Skip to content

Commit

Permalink
Add support for simple CTEs (#111)
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 authored Oct 5, 2020
1 parent 3c0ad3b commit 2025b32
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 2025b32

Please sign in to comment.