Skip to content

Commit

Permalink
Add support for simple CTEs
Browse files Browse the repository at this point in the history
Simple CTEs which does not contain aggregates or DISTINCT are now
supported similarly to simple sub-queries.

Before a view is maintained, all CTEs are converted to corresponding
subqueries to enable to treat CTEs as same as subqueries. For this
end, inline_cte in optimizer/plan/subselect.c was export to public.

Related issue #8
  • Loading branch information
yugo-n committed Oct 5, 2020
1 parent 3c0ad3b commit c92da49
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 53 deletions.
8 changes: 7 additions & 1 deletion doc/src/sgml/ref/create_materialized_view.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ a.i > 5;
</para>
</listitem>

<listitem>
<para>
Simple CTEs which do not contain aggregates or DISTINCT.
</para>
</listitem>

<listitem>
<para>
Some of aggregations (count, sum, avg, min, max) without HAVING
Expand Down Expand Up @@ -235,7 +241,7 @@ a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i;
</listitem>
<listitem>
<para>
CTE, WINDOW, LIMIT and OFFSET clause.
WINDOW, LIMIT and OFFSET clause.
</para>
</listitem>
</itemizedlist>
Expand Down
32 changes: 30 additions & 2 deletions doc/src/sgml/rules.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -1377,7 +1377,7 @@ Time: 16386.245 ms (00:16.386)
<title>Subqueries</title>
<para>
Currently, subqueries using <literal>EXISTS</literal> and simple
subqueries in <literal>FROM</literal> clause are supported.
subqueries in <literal>FROM</literal> clause are supported.
</para>

<sect4>
Expand Down Expand Up @@ -1444,14 +1444,42 @@ Time: 16386.245 ms (00:16.386)

</sect3>

<sect3>
<title>CTEs</title>
<para>
Currently, simple <literal>CTE</literal> in <literal>FROM</literal> clause are supported.
</para>

<sect4>
<title>Restrictions on CTEs</title>
<para>
There are the following restrictions:
<itemizedlist>
<listitem>
<para>
Aggregate functions cannot be used in a CTE.
</para>
</listitem>

<listitem>
<para>
<literal>DISTINCT</literal> cannot be contained in a CTE.
</para>
</listitem>

</itemizedlist>
</para>
</sect4>
</sect3>

<sect3>
<title>Other General Restrictions</title>
<para>
There are other restrictions which generally apply to <acronym>IMMV</acronym>:
<itemizedlist>
<listitem>
<para>
<literal>CTE</literal> or window functions cannot be used.
window functions cannot be used.
</para>
</listitem>

Expand Down
90 changes: 61 additions & 29 deletions src/backend/commands/createas.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
List *rewritten;
PlannedStmt *plan;
QueryDesc *queryDesc;
Query *query_immv = NULL;

if (stmt->if_not_exists)
{
Expand Down Expand Up @@ -338,6 +339,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,

check_ivm_restriction_walker((Node *) query, &ctx, 0);
query = rewriteQueryForIMMV(query, into->colNames);
query_immv = copyObject(query);
}

if (into->skipData)
Expand Down Expand Up @@ -438,7 +440,8 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,

if (!into->skipData)
{
CreateIvmTriggersOnBaseTables(query, (Node *)query->jointree, matviewOid, &relids);
Assert(query_immv != NULL);
CreateIvmTriggersOnBaseTables(query_immv, (Node *)query_immv, matviewOid, &relids);
bms_free(relids);
}
table_close(matviewRel, NoLock);
Expand Down Expand Up @@ -636,13 +639,26 @@ rewriteQueryForIMMV(Query *query, List *colNames)
* CreateIvmTriggersOnBaseTables -- create IVM triggers on all base tables
*/
void
CreateIvmTriggersOnBaseTables(Query *qry, Node *jtnode, Oid matviewOid, Relids *relids)
CreateIvmTriggersOnBaseTables(Query *qry, Node *node, Oid matviewOid, Relids *relids)
{
if (jtnode == NULL)
if (node == NULL)
return;
if (IsA(jtnode, RangeTblRef))
if (IsA(node, Query))
{
Query *query = (Query *) node;
ListCell *lc;

CreateIvmTriggersOnBaseTables(qry, (Node *)query->jointree, matviewOid, relids);
foreach(lc, query->cteList)
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
Assert(IsA(cte->ctequery, Query));
CreateIvmTriggersOnBaseTables((Query *) cte->ctequery, cte->ctequery, matviewOid, relids);
}
}
else if (IsA(node, RangeTblRef))
{
int rti = ((RangeTblRef *) jtnode)->rtindex;
int rti = ((RangeTblRef *) node)->rtindex;
RangeTblEntry *rte = rt_fetch(rti, qry->rtable);

if (rte->rtekind == RTE_RELATION)
Expand All @@ -664,26 +680,26 @@ CreateIvmTriggersOnBaseTables(Query *qry, Node *jtnode, Oid matviewOid, Relids *
Query *subquery = rte->subquery;
Assert(rte->subquery != NULL);

CreateIvmTriggersOnBaseTables(subquery, (Node *)subquery->jointree, matviewOid, relids);
CreateIvmTriggersOnBaseTables(subquery, (Node *)subquery, matviewOid, relids);
}
}
else if (IsA(jtnode, FromExpr))
else if (IsA(node, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode;
FromExpr *f = (FromExpr *) node;
ListCell *l;

foreach(l, f->fromlist)
CreateIvmTriggersOnBaseTables(qry, lfirst(l), matviewOid, relids);
}
else if (IsA(jtnode, JoinExpr))
else if (IsA(node, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode;
JoinExpr *j = (JoinExpr *) node;

CreateIvmTriggersOnBaseTables(qry, j->larg, matviewOid, relids);
CreateIvmTriggersOnBaseTables(qry, j->rarg, matviewOid, relids);
}
else
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(jtnode));
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
}

/*
Expand Down Expand Up @@ -964,7 +980,7 @@ CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing)
if (type == TRIGGER_TYPE_INSERT || type == TRIGGER_TYPE_UPDATE)
{
TriggerTransition *n = makeNode(TriggerTransition);
n->name = "ivm_newtable";
n->name = "__ivm_newtable";
n->isNew = true;
n->isTable = true;

Expand All @@ -973,7 +989,7 @@ CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing)
if (type == TRIGGER_TYPE_DELETE || type == TRIGGER_TYPE_UPDATE)
{
TriggerTransition *n = makeNode(TriggerTransition);
n->name = "ivm_oldtable";
n->name = "__ivm_oldtable";
n->isNew = false;
n->isTable = true;

Expand Down Expand Up @@ -1022,11 +1038,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *ctx, int
{
Query *qry = (Query *)node;
ListCell *lc;
/* if contained CTE, return error */
if (qry->cteList != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CTE is not supported on incrementally maintainable materialized view")));

if (qry->havingQual != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
Expand Down Expand Up @@ -1064,6 +1076,25 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *ctx, int
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("FOR UPDATE/SHARE clause is not supported on incrementally maintainable materialized view")));

/* CTE restrictions */
if (qry->hasRecursive)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("recursive CTE is not supported on incrementally maintainable materialized view")));

foreach(lc, qry->cteList)
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
Query *subquery = (Query *) cte->ctequery;;

if (isIvmName(cte->ctename))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CTE name %s is not supported on incrementally maintainable materialized view", cte->ctename)));

check_ivm_restriction_walker((Node *) subquery, ctx, depth + 1);
}

/* subquery restrictions */
if (depth > 0 && qry->distinctClause != NIL)
ereport(ERROR,
Expand All @@ -1076,7 +1107,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *ctx, int

ctx->has_agg = qry->hasAggs;

/* if contained VIEW or subquery into RTE, return error */
/* restrictions for rtable */
foreach(lc, qry->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
Expand All @@ -1086,24 +1117,25 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *ctx, int
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("TABLESAMPLE clause is not supported on incrementally maintainable materialized view")));
if (rte->relkind == RELKIND_RELATION && find_inheritance_children(rte->relid, NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("inheritance parent is not supported on incrementally maintainable materialized view")));
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("inheritance parent is not supported on incrementally maintainable materialized view")));
if (rte->relkind == RELKIND_VIEW ||
rte->relkind == RELKIND_MATVIEW)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view")));
rte->relkind == RELKIND_MATVIEW)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VIEW or MATERIALIZED VIEW is not supported on incrementally maintainable materialized view")));

if (rte->rtekind == RTE_SUBQUERY)
{
if (ctx->has_outerjoin)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("this query is not allowed on incrementally maintainable materialized view"),
errhint("subquery is not supported with outer join")));
errhint("subquery or CTE is not supported with outer join")));

ctx->has_subquery = true;

check_ivm_restriction_walker((Node *) rte->subquery, ctx, depth + 1);
}
}
Expand All @@ -1115,7 +1147,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *ctx, int
foreach(lc, qry->targetList)
{
TargetEntry *tle = (TargetEntry *) lfirst(lc);
if (isIvmColumn(tle->resname))
if (isIvmName(tle->resname))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("column name %s is not supported on incrementally maintainable materialized view", tle->resname)));
Expand Down Expand Up @@ -1201,7 +1233,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *ctx, int
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("this query is not allowed on incrementally maintainable materialized view"),
errhint("subquery is not supported with outer join")));
errhint("subquery or CTE is not supported with outer join")));
if (ctx->has_agg)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
Expand Down
4 changes: 2 additions & 2 deletions src/backend/commands/indexcmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ DefineIndex(Oid relationId,
if (attno > 0)
{
char *name = NameStr(TupleDescAttr(rel->rd_att, attno - 1)->attname);
if (name && isIvmColumn(name))
if (name && isIvmName(name))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unique index creation on IVM columns is not supported")));
Expand All @@ -1063,7 +1063,7 @@ DefineIndex(Oid relationId,
{
int attno = varno + FirstLowInvalidHeapAttributeNumber;
char *name = NameStr(TupleDescAttr(rel->rd_att, attno - 1)->attname);
if (name && isIvmColumn(name))
if (name && isIvmName(name))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unique index creation on IVM columns is not supported")));
Expand Down
26 changes: 20 additions & 6 deletions src/backend/commands/matview.c
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
{
Relids relids = NULL;

CreateIvmTriggersOnBaseTables(dataQuery, (Node *)dataQuery->jointree, matviewOid, &relids);
CreateIvmTriggersOnBaseTables(dataQuery, (Node *)dataQuery, matviewOid, &relids);
bms_free(relids);
}

Expand Down Expand Up @@ -1728,6 +1728,7 @@ rewrite_query_for_preupdate_state(Query *query, List *tables,
int num_rte = list_length(query->rtable);
int i;


/* This can recurse, so check for excessive recursion */
check_stack_depth();

Expand All @@ -1738,6 +1739,20 @@ rewrite_query_for_preupdate_state(Query *query, List *tables,
// XXX: Is necessary? Is this right timing?
AcquireRewriteLocks(query, true, false);

/* convert CTEs to subqueries */
foreach (lc, query->cteList)
{
PlannerInfo root;
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

if (cte->cterefcount == 0)
continue;

root.parse = query;
inline_cte(&root, cte);
}
query->cteList = NIL;

i = 1;
foreach(lc, query->rtable)
{
Expand Down Expand Up @@ -1960,7 +1975,7 @@ make_delta_enr_name(const char *prefix, Oid relid, int count)
char buf[NAMEDATALEN];
char *name;

snprintf(buf, NAMEDATALEN, "%s_%u_%u", prefix, relid, count);
snprintf(buf, NAMEDATALEN, "__ivm_%s_%u_%u", prefix, relid, count);
name = pstrdup(buf);

return name;
Expand Down Expand Up @@ -2141,7 +2156,6 @@ rewrite_query_for_counting_and_aggregates(Query *query, ParseState *pstate)
int attnum;

/* search ivm_exists_count_X__ column in RangeTblEntry */
pstate->p_rtable = query->rtable;
columnName = getColumnNameStartWith(rte, "__ivm_exists", &attnum);
if (columnName == NULL)
continue;
Expand Down Expand Up @@ -2261,7 +2275,7 @@ rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
if (subselect->cteList)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CTE is not supported on incrementally maintainable materialized view")));
errmsg("CTE in EXIST clause is not supported on incrementally maintainable materialized view")));

pstate = make_parsestate(NULL);
pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
Expand Down Expand Up @@ -4532,12 +4546,12 @@ getColumnNameStartWith(RangeTblEntry *rte, char *str, int *attnum)
}

/*
* isIvmColumn
* isIvmName
*
* Check if this is a IVM hidden column from the name.
*/
bool
isIvmColumn(const char *s)
isIvmName(const char *s)
{
if (s)
return (strncmp(s, "__ivm_", 6) == 0);
Expand Down
2 changes: 1 addition & 1 deletion src/backend/commands/tablecmds.c
Original file line number Diff line number Diff line change
Expand Up @@ -3053,7 +3053,7 @@ renameatt_internal(Oid myrelid,
/*
* Don't rename IVM columns.
*/
if (RelationIsIVM(targetrelation) && isIvmColumn(oldattname))
if (RelationIsIVM(targetrelation) && isIvmName(oldattname))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("IVM column can not be renamed")));
Expand Down
Loading

0 comments on commit c92da49

Please sign in to comment.