timescaledb/src/compression_with_clause.c
gayyappan b1b840f00e Use timescaledb prefix for compression errors
Modify compression parameter related error messages
to make them consistent.
2020-01-28 09:08:58 -05:00

297 lines
7.4 KiB
C

/*
* This file and its contents are licensed under the Apache License 2.0.
* Please see the included NOTICE for copyright information and
* LICENSE-APACHE for a copy of the license.
*/
#include <postgres.h>
#include <fmgr.h>
#include <access/htup_details.h>
#include <catalog/dependency.h>
#include <catalog/namespace.h>
#include <catalog/pg_type.h>
#include <catalog/pg_trigger.h>
#include <commands/trigger.h>
#include <storage/lmgr.h>
#include <utils/builtins.h>
#include <utils/lsyscache.h>
#include <parser/parser.h>
#include "compat.h"
#include "compression_with_clause.h"
static const WithClauseDefinition compress_hypertable_with_clause_def[] = {
[CompressEnabled] = {
.arg_name = "compress",
.type_id = BOOLOID,
.default_val = BoolGetDatum(false),
},
[CompressSegmentBy] = {
.arg_name = "compress_segmentby",
.type_id = TEXTOID,
},
[CompressOrderBy] = {
.arg_name = "compress_orderby",
.type_id = TEXTOID,
},
};
WithClauseResult *
ts_compress_hypertable_set_clause_parse(const List *defelems)
{
return ts_with_clauses_parse(defelems,
compress_hypertable_with_clause_def,
TS_ARRAY_LEN(compress_hypertable_with_clause_def));
}
static inline void
throw_segment_by_error(char *segment_by)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unable to parse timescaledb.compress_segmentby option '%s'", segment_by),
errhint("timescaledb.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, Hypertable *hypertable)
{
StringInfoData buf;
List *parsed;
ListCell *lc;
SelectStmt *select;
short index = 0;
List *collist = NIL;
#if !PG96
RawStmt *raw;
#endif
if (strlen(inpstr) == 0)
return NIL;
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();
{
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;
}
static inline void
throw_order_by_error(char *order_by)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unable to parse timescaledb.compress_orderby option '%s'", order_by),
errhint("timescaledb.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 is parsed same as order by in select queries */
static List *
parse_order_collist(char *inpstr, Hypertable *hypertable)
{
StringInfoData buf;
List *parsed;
ListCell *lc;
SelectStmt *select;
short index = 0;
List *collist = NIL;
#if !PG96
RawStmt *raw;
#endif
if (strlen(inpstr) == 0)
return NIL;
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();
{
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;
}
/* returns List of CompressedParsedCol
* compress_segmentby = `col1,col2,col3`
*/
List *
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), hypertable);
}
else
return NIL;
}
/* returns List of CompressedParsedCol
* E.g. timescaledb.compress_orderby = 'col1 asc nulls first,col2 desc,col3'
*/
List *
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), hypertable);
}
else
return NIL;
}