Skip to content

Commit

Permalink
Relax CAggs JOIN restrictions
Browse files Browse the repository at this point in the history
Remove some Continuous Aggregates JOIN restrictions by allowing:
* INNER/LEFT join
* LATERAL join
* Join between 1 hypertable and N regular tables or materialized views
* Remove restriction of only ONE equality operator on JOIN clause
  • Loading branch information
fabriziomello committed Jul 8, 2024
1 parent 3c58c3f commit 658deaa
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 265 deletions.
174 changes: 48 additions & 126 deletions tsl/src/continuous_aggs/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,17 +667,12 @@ cagg_validate_query(const Query *query, const bool finalized, const char *cagg_s
CAggTimebucketInfo bucket_info = { 0 };
CAggTimebucketInfo bucket_info_parent = { 0 };
Hypertable *ht = NULL, *ht_parent = NULL;
RangeTblRef *rtref = NULL, *rtref_other = NULL;
RangeTblEntry *rte = NULL, *rte_other = NULL;
JoinType jointype = JOIN_FULL;
OpExpr *op = NULL;
List *fromList = NIL;
RangeTblEntry *rte = NULL;
StringInfo hint = makeStringInfo();
StringInfo detail = makeStringInfo();
bool is_hierarchical = false;
Query *prev_query = NULL;
ContinuousAgg *cagg_parent = NULL;
Oid normal_table_id = InvalidOid;

if (!cagg_query_supported(query, hint, detail, finalized))
{
Expand All @@ -688,146 +683,73 @@ cagg_validate_query(const Query *query, const bool finalized, const char *cagg_s
detail->len > 0 ? errdetail("%s", detail->data) : 0));
}

/* Check if there are only two tables in the from list. */
fromList = query->jointree->fromlist;
if (list_length(fromList) > CONTINUOUS_AGG_MAX_JOIN_RELATIONS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("only two tables with one hypertable and one normal table "
"are allowed in continuous aggregate view")));
}
/* Extra checks for joins in Caggs. */
if (list_length(fromList) == CONTINUOUS_AGG_MAX_JOIN_RELATIONS ||
!IsA(linitial(query->jointree->fromlist), RangeTblRef))
int num_tables = 0, num_hypertables = 0;
ListCell *lc;
foreach (lc, query->rtable)
{
if (list_length(fromList) == CONTINUOUS_AGG_MAX_JOIN_RELATIONS)
{
if (!IsA(linitial(fromList), RangeTblRef) || !IsA(lsecond(fromList), RangeTblRef))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail(
"From clause can only have one hypertable and one normal table.")));
RangeTblEntry *inner_rte = lfirst_node(RangeTblEntry, lc);

rtref = linitial_node(RangeTblRef, query->jointree->fromlist);
rte = list_nth(query->rtable, rtref->rtindex - 1);
rtref_other = lsecond_node(RangeTblRef, query->jointree->fromlist);
rte_other = list_nth(query->rtable, rtref_other->rtindex - 1);
jointype = rte->jointype || rte_other->jointype;

if (query->jointree->quals != NULL && IsA(query->jointree->quals, OpExpr))
op = (OpExpr *) query->jointree->quals;
}
else
if (inner_rte->rtekind == RTE_RELATION)
{
ListCell *l;
foreach (l, query->jointree->fromlist)
if (ts_is_hypertable(inner_rte->relid) ||
ts_continuous_agg_find_by_relid(inner_rte->relid))
{
Node *jtnode = (Node *) lfirst(l);
JoinExpr *join = NULL;
if (IsA(jtnode, JoinExpr))
{
join = castNode(JoinExpr, jtnode);
jointype = join->jointype;
op = (OpExpr *) join->quals;
rte = list_nth(query->rtable, ((RangeTblRef *) join->larg)->rtindex - 1);
rte_other = list_nth(query->rtable, ((RangeTblRef *) join->rarg)->rtindex - 1);
if (rte->subquery != NULL || rte_other->subquery != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Sub-queries are not supported in FROM clause.")));
RangeTblEntry *jrte = rt_fetch(join->rtindex, query->rtable);
if (jrte->joinaliasvars == NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view")));
}
num_hypertables++;
if (rte == NULL)
rte = copyObject(inner_rte);
}
else
{
num_tables++;
if (inner_rte->relkind == RELKIND_VIEW)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Views are not supported in continuous aggregates.")));
}

if (inner_rte->inh == false)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("FROM ONLY is not allowed in continuous aggregate.")));
}

/*
* Error out if there is aynthing else than one normal table and one hypertable
* in the from clause, e.g. sub-query, lateral, two hypertables, etc.
*/
if (rte->lateral || rte_other->lateral)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Lateral joins are not supported in FROM clause.")));
if ((rte->relkind == RELKIND_VIEW && ts_is_hypertable(rte_other->relid)) ||
(rte_other->relkind == RELKIND_VIEW && ts_is_hypertable(rte->relid)))
/* Only inner joins are allowed. */
if (inner_rte->jointype != JOIN_INNER && inner_rte->jointype != JOIN_LEFT)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Views are not supported in FROM clause.")));
if (rte->relkind != RELKIND_VIEW && rte_other->relkind != RELKIND_VIEW &&
(ts_is_hypertable(rte->relid) == ts_is_hypertable(rte_other->relid)))
errmsg("only INNER or LEFT joins are supported in continuous aggregates")));

/* Subquery only using LATERAL */
if (inner_rte->subquery && !inner_rte->lateral)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Multiple hypertables or normal tables are not supported in FROM "
"clause.")));
errdetail("Sub-queries are not supported in FROM clause.")));

/* Only inner joins are allowed. */
if (jointype != JOIN_INNER)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("only inner joins are supported in continuous aggregates")));

/* Only equality conditions are permitted on joins. */
if (op && IsA(op, OpExpr) &&
list_length(castNode(OpExpr, op)->args) == CONTINUOUS_AGG_MAX_JOIN_RELATIONS)
{
Oid left_type = exprType(linitial(op->args));
Oid right_type = exprType(lsecond(op->args));
if (!ts_is_equality_operator(op->opno, left_type, right_type))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail(
"Only equality conditions are supported in continuous aggregates.")));
}
else
/* TABLESAMPLE not allowed */
if (inner_rte->tablesample)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Unsupported expression in join clause."),
errhint("Only equality conditions are supported in continuous aggregates.")));
/*
* Record the table oid of the normal table. This is required so
* that we know which one is hypertable to carry out the related
* processing in later parts of code.
*/
if (rte->relkind == RELKIND_VIEW)
normal_table_id = rte_other->relid;
else if (rte_other->relkind == RELKIND_VIEW)
normal_table_id = rte->relid;
else
normal_table_id = ts_is_hypertable(rte->relid) ? rte_other->relid : rte->relid;
if (normal_table_id == rte->relid)
rte = rte_other;
errdetail("TABLESAMPLE is not supported in continuous aggregate.")));
}
else
{
/* Check if we have a hypertable in the FROM clause. */
rtref = linitial_node(RangeTblRef, query->jointree->fromlist);
rte = list_nth(query->rtable, rtref->rtindex - 1);
}
/* FROM only <tablename> sets rte->inh to false. */
if (rte->rtekind != RTE_JOIN)

if (num_hypertables > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("Only one hypertable is allowed in continuous aggregate view.")));

if (rte == NULL)
{
if ((rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_VIEW) ||
rte->tablesample || rte->inh == false)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view")));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid continuous aggregate view"),
errdetail("At least one hypertable should be used in the view definition.")));
}

Ensure(rte->relkind == RELKIND_RELATION || rte->relkind == RELKIND_VIEW,
"invalid continuous aggregate view");

const Dimension *part_dimension = NULL;
int32 parent_mat_hypertable_id = INVALID_HYPERTABLE_ID;
Cache *hcache = ts_hypertable_cache_pin();
Expand Down
8 changes: 6 additions & 2 deletions tsl/test/expected/cagg_errors.out
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ CREATE MATERIALIZED VIEW mat_m1 WITH (timescaledb.continuous, timescaledb.materi
as
select a, count(*) from mat_t1
group by a WITH NO DATA;
ERROR: table "mat_t1" is not a hypertable
ERROR: invalid continuous aggregate view
DETAIL: At least one hypertable should be used in the view definition.
-- no group by
CREATE MATERIALIZED VIEW mat_m1 WITH (timescaledb.continuous, timescaledb.materialized_only=false)
as
Expand Down Expand Up @@ -119,6 +120,7 @@ from
from conditions ) q
group by time_bucket('1week', timec) , location WITH NO DATA;
ERROR: invalid continuous aggregate view
DETAIL: Sub-queries are not supported in FROM clause.
CREATE MATERIALIZED VIEW mat_m1 WITH (timescaledb.continuous, timescaledb.materialized_only=false)
AS
select * from
Expand Down Expand Up @@ -194,13 +196,15 @@ from conditions tablesample bernoulli(0.2)
group by time_bucket('1week', timec) , location
WITH NO DATA;
ERROR: invalid continuous aggregate view
DETAIL: TABLESAMPLE is not supported in continuous aggregate.
-- ONLY in from clause
CREATE MATERIALIZED VIEW mat_m1 WITH (timescaledb.continuous, timescaledb.materialized_only=false)
AS
Select sum(humidity), avg(temperature::int4)
from ONLY conditions
group by time_bucket('1week', timec) , location WITH NO DATA;
ERROR: invalid continuous aggregate view
DETAIL: FROM ONLY is not allowed in continuous aggregate.
--grouping sets and variants
CREATE MATERIALIZED VIEW mat_m1 WITH (timescaledb.continuous, timescaledb.materialized_only=false)
AS
Expand Down Expand Up @@ -645,7 +649,7 @@ ERROR: compression not enabled on "i2980"
-- cagg on normal view should error out
CREATE VIEW v1 AS SELECT now() AS time;
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS SELECT time_bucket('1h',time) FROM v1 GROUP BY 1;
ERROR: invalid continuous aggregate query
ERROR: invalid continuous aggregate view
-- cagg on normal view should error out
CREATE MATERIALIZED VIEW matv1 AS SELECT now() AS time;
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous, timescaledb.materialized_only=false) AS SELECT time_bucket('1h',time) FROM matv1 GROUP BY 1;
Expand Down
Loading

0 comments on commit 658deaa

Please sign in to comment.