diff --git a/src/compression_with_clause.c b/src/compression_with_clause.c index c6243c958..7f357c2ca 100644 --- a/src/compression_with_clause.c +++ b/src/compression_with_clause.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "compat.h" @@ -43,142 +44,216 @@ ts_compress_hypertable_set_clause_parse(const List *defelems) compress_hypertable_with_clause_def, TS_ARRAY_LEN(compress_hypertable_with_clause_def)); } -/* strip double quotes from tokens that are names of columns */ -static char * -strip_name_token(char *token) + +static inline void +throw_segment_by_error(char *segment_by) { - int len; - if (token == NULL) - return NULL; - len = strlen(token); - if (token[0] == '"' && (len > 1) && (token[len - 1] == '"')) - { - char *newtok = palloc0(sizeof(char) * (len - 1)); - strncpy(newtok, &token[1], len - 2); - return newtok; - } - return token; + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unable to parse the compress_segmentby option '%s'", segment_by), + errhint("The compress_segmentby option should be a comma separated list of column " + "names."))); +} + +static bool +select_stmt_as_expected(SelectStmt *stmt) +{ + /* The only parts of the select stmt that are allowed to be set are the order by or group by. + * Check that no other fields are set */ + if (stmt->distinctClause != NIL || stmt->intoClause != NULL || stmt->targetList != NIL || + stmt->whereClause != NULL || stmt->havingClause != NULL || stmt->windowClause != NIL || + stmt->valuesLists != NULL || stmt->limitOffset != NULL || stmt->limitCount != NULL || + stmt->lockingClause != NIL || stmt->withClause != NULL || stmt->op != 0 || + stmt->all != false || stmt->larg != NULL || stmt->rarg != NULL) + return false; + return true; } static List * -parse_segment_collist(char *inpstr, const char *delim) +parse_segment_collist(char *inpstr, Hypertable *hypertable) { - List *collist = NIL; - char *saveptr = NULL; - char *token = strtok_r(inpstr, delim, &saveptr); + StringInfoData buf; + List *parsed; + ListCell *lc; + SelectStmt *select; short index = 0; - while (token) + List *collist = NIL; +#if !PG96 + RawStmt *raw; +#endif + + initStringInfo(&buf); + + /* parse the segment by list exactly how you would a group by */ + appendStringInfo(&buf, + "SELECT FROM %s.%s GROUP BY %s", + quote_identifier(hypertable->fd.schema_name.data), + quote_identifier(hypertable->fd.table_name.data), + inpstr); + + PG_TRY(); { - char *namtoken = NULL; - CompressedParsedCol *col = (CompressedParsedCol *) palloc(sizeof(CompressedParsedCol)); - col->index = index; - namtoken = strip_name_token(token); - namestrcpy(&col->colname, namtoken); - index++; - // elog(INFO, "colname is %s %d", col->colname, col->index); - collist = lappend(collist, (void *) col); - token = strtok_r(NULL, delim, &saveptr); + parsed = raw_parser(buf.data); } + PG_CATCH(); + { + throw_segment_by_error(inpstr); + PG_RE_THROW(); + } + PG_END_TRY(); + + if (list_length(parsed) != 1) + throw_segment_by_error(inpstr); +#if PG96 + if (!IsA(linitial(parsed), SelectStmt)) + throw_segment_by_error(inpstr); + select = linitial(parsed); +#else + if (!IsA(linitial(parsed), RawStmt)) + throw_segment_by_error(inpstr); + raw = linitial(parsed); + + if (!IsA(raw->stmt, SelectStmt)) + throw_segment_by_error(inpstr); + select = (SelectStmt *) raw->stmt; +#endif + + if (!select_stmt_as_expected(select)) + throw_segment_by_error(inpstr); + + if (select->sortClause != NIL) + throw_segment_by_error(inpstr); + + foreach (lc, select->groupClause) + { + ColumnRef *cf; + CompressedParsedCol *col = (CompressedParsedCol *) palloc(sizeof(*col)); + + if (!IsA(lfirst(lc), ColumnRef)) + throw_segment_by_error(inpstr); + cf = lfirst(lc); + if (list_length(cf->fields) != 1) + throw_segment_by_error(inpstr); + + if (!IsA(linitial(cf->fields), String)) + throw_segment_by_error(inpstr); + + col->index = index; + index++; + namestrcpy(&col->colname, strVal(linitial(cf->fields))); + collist = lappend(collist, (void *) col); + } + return collist; } -#define CHKTOKEN(token, str) \ - (token && (strncmp(token, str, strlen(str)) == 0) && (strlen(str) == strlen(token))) -#define PRINT_UNEXPECTED_TOKEN_MSG(token2) \ - ereport(ERROR, \ - (errcode(ERRCODE_SYNTAX_ERROR), \ - errmsg("unexpected token %s in compress_orderby list ", token2))) - -static CompressedParsedCol * -parse_orderelement(char *elttoken, const char *spcdelim, short index) +static inline void +throw_order_by_error(char *order_by) { - bool getnext = false; - bool neednullstok = false; - CompressedParsedCol *col = (CompressedParsedCol *) palloc(sizeof(CompressedParsedCol)); - char *saveptr2 = NULL; - char *namtoken; - char *token2 = strtok_r(elttoken, spcdelim, &saveptr2); - col->index = index; - namtoken = strip_name_token(token2); - namestrcpy(&col->colname, namtoken); - /* default for sort is asc and nulls first */ - col->asc = true; - col->nullsfirst = true; - - token2 = strtok_r(NULL, spcdelim, &saveptr2); - if (CHKTOKEN(token2, "asc")) - { - col->asc = true; - getnext = true; - } - else if (CHKTOKEN(token2, "desc")) - { - col->asc = false; - /* if we have desceneding then nulls last is default unless user specifies otherwise */ - col->nullsfirst = false; - getnext = true; - } - if (getnext) - { - token2 = strtok_r(NULL, spcdelim, &saveptr2); - } - if (CHKTOKEN(token2, "nulls")) - { - token2 = strtok_r(NULL, spcdelim, &saveptr2); - neednullstok = true; - } - else if (token2) // we have a token but not nay of the expected ones - { - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unexpected token %s in compress_orderby list ", token2))); - } - if (CHKTOKEN(token2, "first")) - { - col->nullsfirst = true; - } - else if (CHKTOKEN(token2, "last")) - { - col->nullsfirst = false; - } - else if (neednullstok) - { - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("expect first/last after nulls in compress_orderby list "))); - } - else if (token2) // we have a token but not nay of the expected ones - { - PRINT_UNEXPECTED_TOKEN_MSG(token2); - } - // any more tokens left? - token2 = strtok_r(NULL, spcdelim, &saveptr2); - if (token2) - { - PRINT_UNEXPECTED_TOKEN_MSG(token2); - } - - return col; + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unable to parse the compress_orderby option '%s'", order_by), + errhint("The compress_orderby option should be a comma separated list of column " + "names with sort options. It is the same format as an ORDER BY clause."))); } -/* compress_orderby = `,:...' - = [asc|desc] [nulls (first|last)] - */ +/* compress_orderby is parsed same as order by in select queries */ static List * -parse_order_collist(char *inpstr, const char *delim) +parse_order_collist(char *inpstr, Hypertable *hypertable) { - List *collist = NIL; - char *saveptr = NULL; - char *elttoken = strtok_r(inpstr, delim, &saveptr); + StringInfoData buf; + List *parsed; + ListCell *lc; + SelectStmt *select; short index = 0; - char spcdelim = ' '; - while (elttoken) + List *collist = NIL; +#if !PG96 + RawStmt *raw; +#endif + + initStringInfo(&buf); + + /* parse the segment by list exactly how you would a order by by */ + appendStringInfo(&buf, + "SELECT FROM %s.%s ORDER BY %s", + quote_identifier(hypertable->fd.schema_name.data), + quote_identifier(hypertable->fd.table_name.data), + inpstr); + + PG_TRY(); { - CompressedParsedCol *col = parse_orderelement(elttoken, &spcdelim, index); - collist = lappend(collist, (void *) col); - elttoken = strtok_r(NULL, delim, &saveptr); - index++; + parsed = raw_parser(buf.data); } + PG_CATCH(); + { + throw_order_by_error(inpstr); + PG_RE_THROW(); + } + PG_END_TRY(); + + if (list_length(parsed) != 1) + throw_order_by_error(inpstr); +#if PG96 + if (!IsA(linitial(parsed), SelectStmt)) + throw_order_by_error(inpstr); + select = linitial(parsed); +#else + if (!IsA(linitial(parsed), RawStmt)) + throw_order_by_error(inpstr); + raw = linitial(parsed); + if (!IsA(raw->stmt, SelectStmt)) + throw_order_by_error(inpstr); + select = (SelectStmt *) raw->stmt; +#endif + + if (!select_stmt_as_expected(select)) + throw_order_by_error(inpstr); + + if (select->groupClause != NIL) + throw_order_by_error(inpstr); + + foreach (lc, select->sortClause) + { + SortBy *sort_by; + ColumnRef *cf; + CompressedParsedCol *col = (CompressedParsedCol *) palloc(sizeof(*col)); + + if (!IsA(lfirst(lc), SortBy)) + throw_order_by_error(inpstr); + sort_by = lfirst(lc); + + if (!IsA(sort_by->node, ColumnRef)) + throw_order_by_error(inpstr); + cf = (ColumnRef *) sort_by->node; + + if (list_length(cf->fields) != 1) + throw_order_by_error(inpstr); + + if (!IsA(linitial(cf->fields), String)) + throw_order_by_error(inpstr); + + col->index = index; + index++; + namestrcpy(&col->colname, strVal(linitial(cf->fields))); + + if (sort_by->sortby_dir != SORTBY_ASC && sort_by->sortby_dir != SORTBY_DESC && + sort_by->sortby_dir != SORTBY_DEFAULT) + throw_order_by_error(inpstr); + col->asc = sort_by->sortby_dir == SORTBY_ASC || sort_by->sortby_dir == SORTBY_DEFAULT; + + if (sort_by->sortby_nulls == SORTBY_NULLS_DEFAULT) + { + /* default null ordering is LAST for ASC, FIRST for DESC */ + col->nullsfirst = !col->asc; + } + else + { + col->nullsfirst = sort_by->sortby_nulls == SORTBY_NULLS_FIRST; + } + + collist = lappend(collist, (void *) col); + } + return collist; } @@ -186,12 +261,12 @@ parse_order_collist(char *inpstr, const char *delim) * compress_segmentby = `col1,col2,col3` */ List * -ts_compress_hypertable_parse_segment_by(WithClauseResult *parsed_options) +ts_compress_hypertable_parse_segment_by(WithClauseResult *parsed_options, Hypertable *hypertable) { if (parsed_options[CompressSegmentBy].is_default == false) { Datum textarg = parsed_options[CompressSegmentBy].parsed; - return parse_segment_collist(TextDatumGetCString(textarg), ","); + return parse_segment_collist(TextDatumGetCString(textarg), hypertable); } else return NIL; @@ -201,12 +276,12 @@ ts_compress_hypertable_parse_segment_by(WithClauseResult *parsed_options) * E.g. timescaledb.compress_orderby = 'col1 asc nulls first,col2 desc,col3' */ List * -ts_compress_hypertable_parse_order_by(WithClauseResult *parsed_options) +ts_compress_hypertable_parse_order_by(WithClauseResult *parsed_options, Hypertable *hypertable) { if (parsed_options[CompressOrderBy].is_default == false) { Datum textarg = parsed_options[CompressOrderBy].parsed; - return parse_order_collist(TextDatumGetCString(textarg), ","); + return parse_order_collist(TextDatumGetCString(textarg), hypertable); } else return NIL; diff --git a/src/compression_with_clause.h b/src/compression_with_clause.h index fd8f8c6e7..06d80380f 100644 --- a/src/compression_with_clause.h +++ b/src/compression_with_clause.h @@ -29,7 +29,9 @@ typedef struct } CompressedParsedCol; WithClauseResult *ts_compress_hypertable_set_clause_parse(const List *defelems); -extern TSDLLEXPORT List *ts_compress_hypertable_parse_segment_by(WithClauseResult *parsed_options); -extern TSDLLEXPORT List *ts_compress_hypertable_parse_order_by(WithClauseResult *parsed_options); +extern TSDLLEXPORT List *ts_compress_hypertable_parse_segment_by(WithClauseResult *parsed_options, + Hypertable *hypertable); +extern TSDLLEXPORT List *ts_compress_hypertable_parse_order_by(WithClauseResult *parsed_options, + Hypertable *hypertable); #endif diff --git a/tsl/src/compression/create.c b/tsl/src/compression/create.c index 529bc86d2..bc5fac9c5 100644 --- a/tsl/src/compression/create.c +++ b/tsl/src/compression/create.c @@ -355,8 +355,8 @@ tsl_process_compress_table(AlterTableCmd *cmd, Hypertable *ht, struct CompressColInfo compress_cols; Oid ownerid = ts_rel_get_owner(ht->main_table_relid); - List *segmentby_cols = ts_compress_hypertable_parse_segment_by(with_clause_options); - List *orderby_cols = ts_compress_hypertable_parse_order_by(with_clause_options); + List *segmentby_cols = ts_compress_hypertable_parse_segment_by(with_clause_options, ht); + List *orderby_cols = ts_compress_hypertable_parse_order_by(with_clause_options, ht); /* we need an AccessShare lock on the hypertable so that there are * no DDL changes while we create the compressed hypertable */ diff --git a/tsl/test/expected/compression.out b/tsl/test/expected/compression.out index 881d749d4..65c205e26 100644 --- a/tsl/test/expected/compression.out +++ b/tsl/test/expected/compression.out @@ -29,7 +29,7 @@ select * from _timescaledb_catalog.hypertable_compression order by hypertable_id ---------------+---------+--------------------------+------------------------+----------------------+-------------+-------------------- 1 | a | 0 | 1 | | | 1 | b | 0 | 2 | | | - 1 | c | 4 | | 1 | f | f + 1 | c | 4 | | 1 | f | t 1 | d | 4 | | 2 | t | f (4 rows) diff --git a/tsl/test/expected/compression_errors.out b/tsl/test/expected/compression_errors.out index 8524ff4af..b6719f9c0 100644 --- a/tsl/test/expected/compression_errors.out +++ b/tsl/test/expected/compression_errors.out @@ -11,6 +11,14 @@ NOTICE: adding not-null constraint to column "a" foo2 (1 row) +create table foo3 (a integer, "bacB toD" integer, c integer, d integer); +select table_name from create_hypertable('foo3', 'a', chunk_time_interval=> 10); +NOTICE: adding not-null constraint to column "a" + table_name +------------ + foo3 +(1 row) + create table non_compressed (a integer, "bacB toD" integer, c integer, d integer); select table_name from create_hypertable('non_compressed', 'a', chunk_time_interval=> 10); NOTICE: adding not-null constraint to column "a" @@ -23,15 +31,19 @@ insert into non_compressed values( 3 , 16 , 20, 4); ALTER TABLE foo2 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'c'); ERROR: cannot use the same column c in compress_orderby and compress_segmentby ALTER TABLE foo2 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'd'); +--TODO: allow changing the options if not chunks compressed +ALTER TABLE foo2 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'd DESC'); +ERROR: duplicate key value violates unique constraint "hypertable_compression_pkey" select * from _timescaledb_catalog.hypertable_compression order by attname; hypertable_id | attname | compression_algorithm_id | segmentby_column_index | orderby_column_index | orderby_asc | orderby_nullsfirst ---------------+----------+--------------------------+------------------------+----------------------+-------------+-------------------- 1 | a | 4 | | | | 1 | bacB toD | 0 | 1 | | | 1 | c | 0 | 2 | | | - 1 | d | 4 | | 1 | t | t + 1 | d | 4 | | 1 | t | f (4 rows) +ALTER TABLE foo3 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'd DeSc NullS lAsT'); -- Negative test cases --- create table reserved_column_prefix (a integer, _ts_meta_foo integer, "bacB toD" integer, c integer, d integer); select table_name from create_hypertable('reserved_column_prefix', 'a', chunk_time_interval=> 10); @@ -44,7 +56,7 @@ NOTICE: adding not-null constraint to column "a" ALTER TABLE reserved_column_prefix set (timescaledb.compress); ERROR: cannot compress tables with reserved column prefix '_ts_meta_' --basic test with count -create table foo (a integer, b integer, c integer); +create table foo (a integer, b integer, c integer, t text); select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10); NOTICE: adding not-null constraint to column "a" table_name @@ -71,26 +83,50 @@ ERROR: column d in compress_segmentby list does not exist ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'd'); ERROR: column d in compress_orderby list does not exist ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c desc nulls'); -ERROR: expect first/last after nulls in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c desc nulls' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c desc nulls thirsty'); -ERROR: expect first/last after nulls in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c desc nulls thirsty' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c climb nulls first'); -ERROR: unexpected token climb in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c climb nulls first' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c nulls first asC'); -ERROR: unexpected token asC in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c nulls first asC' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c desc nulls first asc'); -ERROR: unexpected token asc in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c desc nulls first asc' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c desc hurry'); -ERROR: unexpected token hurry in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c desc hurry' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c descend'); -ERROR: unexpected token descend in compress_orderby list +ERROR: unable to parse the compress_orderby option 'c descend' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c; SELECT 1'); +ERROR: unable to parse the compress_orderby option 'c; SELECT 1' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = '1,2'); +ERROR: unable to parse the compress_orderby option '1,2' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c + 1'); +ERROR: unable to parse the compress_orderby option 'c + 1' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'random()'); +ERROR: unable to parse the compress_orderby option 'random()' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c LIMIT 1'); +ERROR: unable to parse the compress_orderby option 'c LIMIT 1' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c USING <'); +ERROR: unable to parse the compress_orderby option 'c USING <' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 't COLLATE "en_US"'); +ERROR: unable to parse the compress_orderby option 't COLLATE "en_US"' ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c asc' , timescaledb.compress_orderby = 'c'); -ERROR: column c asc in compress_segmentby list does not exist +ERROR: unable to parse the compress_segmentby option 'c asc' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c nulls last'); +ERROR: unable to parse the compress_segmentby option 'c nulls last' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + 1'); +ERROR: unable to parse the compress_segmentby option 'c + 1' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'random()'); +ERROR: unable to parse the compress_segmentby option 'random()' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c LIMIT 1'); +ERROR: unable to parse the compress_segmentby option 'c LIMIT 1' +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + b'); +ERROR: unable to parse the compress_segmentby option 'c + b' --should succeed ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a'); select decompress_chunk(ch1.schema_name|| '.' || ch1.table_name) FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'foo' limit 1; -ERROR: chunk "_hyper_5_2_chunk" is not a compressed +ERROR: chunk "_hyper_8_2_chunk" is not a compressed --should succeed select compress_chunk(ch1.schema_name|| '.' || ch1.table_name) FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'foo' limit 1; diff --git a/tsl/test/sql/compression_errors.sql b/tsl/test/sql/compression_errors.sql index 3205a7a87..f8f23deea 100644 --- a/tsl/test/sql/compression_errors.sql +++ b/tsl/test/sql/compression_errors.sql @@ -8,14 +8,22 @@ create table foo2 (a integer, "bacB toD" integer, c integer, d integer); select table_name from create_hypertable('foo2', 'a', chunk_time_interval=> 10); +create table foo3 (a integer, "bacB toD" integer, c integer, d integer); +select table_name from create_hypertable('foo3', 'a', chunk_time_interval=> 10); + create table non_compressed (a integer, "bacB toD" integer, c integer, d integer); select table_name from create_hypertable('non_compressed', 'a', chunk_time_interval=> 10); insert into non_compressed values( 3 , 16 , 20, 4); ALTER TABLE foo2 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'c'); ALTER TABLE foo2 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'd'); +--TODO: allow changing the options if not chunks compressed +ALTER TABLE foo2 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'd DESC'); + select * from _timescaledb_catalog.hypertable_compression order by attname; +ALTER TABLE foo3 set (timescaledb.compress, timescaledb.compress_segmentby = '"bacB toD",c' , timescaledb.compress_orderby = 'd DeSc NullS lAsT'); + -- Negative test cases --- create table reserved_column_prefix (a integer, _ts_meta_foo integer, "bacB toD" integer, c integer, d integer); @@ -23,7 +31,7 @@ select table_name from create_hypertable('reserved_column_prefix', 'a', chunk_ti ALTER TABLE reserved_column_prefix set (timescaledb.compress); --basic test with count -create table foo (a integer, b integer, c integer); +create table foo (a integer, b integer, c integer, t text); select table_name from create_hypertable('foo', 'a', chunk_time_interval=> 10); insert into foo values( 3 , 16 , 20); @@ -46,7 +54,20 @@ ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c nul ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c desc nulls first asc'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c desc hurry'); ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c descend'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c; SELECT 1'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = '1,2'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c + 1'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'random()'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c LIMIT 1'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'c USING <'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 't COLLATE "en_US"'); + ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c asc' , timescaledb.compress_orderby = 'c'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c nulls last'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + 1'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'random()'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c LIMIT 1'); +ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_segmentby = 'c + b'); --should succeed ALTER TABLE foo set (timescaledb.compress, timescaledb.compress_orderby = 'a');