Skip to content

Commit b928e32

Browse files
authored
Add CTE support (#47)
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, codes of the static function inline_cte in the core (optimizer/plan/subselect.c) was imported. Prohibit Unreferenced CTE is prohibited. When a table in a unreferenced CTE is TRUNCATEd, the contents of the IMMV is not affected so it must not be truncated. For confirming it at the maintenance time, we have to check if the modified table used in a CTE is actually referenced. Although it would possible, we just disallow to create such IMMVs for now since such unreferenced CTE is useless unless it doesn't contain modifying commands, that is already prohibited.
1 parent 7997f3e commit b928e32

File tree

8 files changed

+584
-16
lines changed

8 files changed

+584
-16
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ OBJS = \
66
createas.o \
77
matview.o \
88
pg_ivm.o \
9-
ruleutils.o
9+
ruleutils.o \
10+
subselect.o
1011
PGFILEDESC = "pg_ivm - incremental view maintenance on PostgreSQL"
1112

1213
EXTENSION = pg_ivm

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ Time: 3224.741 ms (00:03.225)
188188

189189
## Supported View Definitions and Restriction
190190

191-
Currently, IMMV's view definition can contain inner joins, DISTINCT clause, some built-in aggregate functions, and simple sub-queries in `FROM` clause. Inner joins including self-join are supported, but outer joins are not supported. Supported aggregate functions are count, sum, avg, min and max. Other aggregates, sub-queries which contain an aggregate or `DISTINCT` clause, sub-queries in other than `FROM` clause, CTEs, window functions, `HAVING`, `ORDER BY`, `LIMIT`/`OFFSET`, `UNION`/`INTERSECT`/`EXCEPT`, `DISTINCT ON`, `TABLESAMPLE`, `VALUES`, and `FOR UPDATE`/`SHARE` can not be used in view definition.
191+
Currently, IMMV's view definition can contain inner joins, DISTINCT clause, some built-in aggregate functions, simple sub-queries in `FROM` clause, and simple CTE (`WITH` query). Inner joins including self-join are supported, but outer joins are not supported. Supported aggregate functions are count, sum, avg, min and max. Other aggregates, sub-queries which contain an aggregate or `DISTINCT` clause, sub-queries in other than `FROM` clause, window functions, `HAVING`, `ORDER BY`, `LIMIT`/`OFFSET`, `UNION`/`INTERSECT`/`EXCEPT`, `DISTINCT ON`, `TABLESAMPLE`, `VALUES`, and `FOR UPDATE`/`SHARE` can not be used in view definition.
192192

193193
The base tables must be simple tables. Views, materialized views, inheritance parent tables, partitioned tables, partitions, and foreign tables can not be used.
194194

@@ -224,6 +224,16 @@ Subqueries can be used only in `FROM` clause. Subqueries in target list or `WHER
224224

225225
Subqueries containing an aggregate function or `DISTINCT` are not supported.
226226

227+
### CTE
228+
229+
Simple CTEs (`WITH` queries) are supported.
230+
231+
#### Restrictions on Subqueries
232+
233+
`WITH` queries containing an aggregate function or `DISTINCT` are not supported.
234+
235+
Recursive queries (`WITH RECURSIVE`) are not allowed. Unreferenced CTEs are not allowed either, that is, a CTE must be referenced at least once in the view definition query.
236+
227237
### DISTINCT
228238

229239
`DISTINCT` is allowed in IMMV's definition queries. Suppose an IMMV defined with DISTINCT on a base table containing duplicate tuples. When tuples are deleted from the base table, a tuple in the view is deleted if and only if the multiplicity of the tuple becomes zero. Moreover, when tuples are inserted into the base table, a tuple is inserted into the view only if the same tuple doesn't already exist in it.

createas.c

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -522,8 +522,15 @@ CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
522522
case T_Query:
523523
{
524524
Query *query = (Query *) node;
525+
ListCell *lc;
525526

526527
CreateIvmTriggersOnBaseTablesRecurse(qry, (Node *)query->jointree, matviewOid, relids, ex_lock);
528+
foreach(lc, query->cteList)
529+
{
530+
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
531+
Assert(IsA(cte->ctequery, Query));
532+
CreateIvmTriggersOnBaseTablesRecurse((Query *) cte->ctequery, cte->ctequery, matviewOid, relids, ex_lock);
533+
}
527534
}
528535
break;
529536

@@ -706,11 +713,6 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
706713
ListCell *lc;
707714
List *vars;
708715

709-
/* if contained CTE, return error */
710-
if (qry->cteList != NIL)
711-
ereport(ERROR,
712-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
713-
errmsg("CTE is not supported on incrementally maintainable materialized view")));
714716
if (qry->groupClause != NIL && !qry->hasAggs)
715717
ereport(ERROR,
716718
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -751,6 +753,10 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
751753
ereport(ERROR,
752754
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
753755
errmsg("FOR UPDATE/SHARE clause is not supported on incrementally maintainable materialized view")));
756+
if (qry->hasRecursive)
757+
ereport(ERROR,
758+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
759+
errmsg("recursive query is not supported on incrementally maintainable materialized view")));
754760

755761
/* system column restrictions */
756762
vars = pull_vars_of_level((Node *) qry, 0);
@@ -835,6 +841,34 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
835841

836842
query_tree_walker(qry, check_ivm_restriction_walker, (void *) context, QTW_IGNORE_RT_SUBQUERIES);
837843

844+
break;
845+
}
846+
case T_CommonTableExpr:
847+
{
848+
CommonTableExpr *cte = (CommonTableExpr *) node;
849+
850+
if (isIvmName(cte->ctename))
851+
ereport(ERROR,
852+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
853+
errmsg("WITH query name %s is not supported on incrementally maintainable materialized view", cte->ctename)));
854+
855+
/*
856+
* When a table in a unreferenced CTE is TRUNCATEd, the contents of the
857+
* IMMV is not affected so it must not be truncated. For confirming it
858+
* at the maintenance time, we have to check if the modified table used
859+
* in a CTE is actually referenced. Although it would be possible, we
860+
* just disallow to create such IMMVs for now since such unreferenced
861+
* CTE is useless unless it doesn't contain modifying commands, that is
862+
* already prohibited.
863+
*/
864+
if (cte->cterefcount == 0)
865+
ereport(ERROR,
866+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
867+
errmsg("Ureferenced WITH query is not supported on incrementally maintainable materialized view")));
868+
869+
context->sublevels_up++;
870+
check_ivm_restriction_walker(cte->ctequery, (void *) context);
871+
context->sublevels_up--;
838872
break;
839873
}
840874
case T_TargetEntry:
@@ -1315,6 +1349,21 @@ get_primary_key_attnos_from_query(Query *query, List **constraintList, bool is_c
13151349
Bitmapset *keys = NULL;
13161350
Relids rels_in_from;
13171351

1352+
/* convert CTEs to subqueries */
1353+
query = copyObject(query);
1354+
foreach (lc, query->cteList)
1355+
{
1356+
PlannerInfo root;
1357+
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
1358+
1359+
if (cte->cterefcount == 0)
1360+
continue;
1361+
1362+
root.parse = query;
1363+
inline_cte(&root, cte);
1364+
}
1365+
query->cteList = NIL;
1366+
13181367
/*
13191368
* Collect primary key attributes from all tables used in query. The key attributes
13201369
* sets for each table are stored in key_attnos_list in order by RTE index.

0 commit comments

Comments
 (0)