From c92da4953318c60c3fd427e821f4590cc8b070ad Mon Sep 17 00:00:00 2001 From: Yugo Nagata Date: Wed, 16 Sep 2020 03:33:25 +0900 Subject: [PATCH] Add support for simple CTEs 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 --- .../sgml/ref/create_materialized_view.sgml | 8 +- doc/src/sgml/rules.sgml | 32 +++++- src/backend/commands/createas.c | 90 +++++++++++------ src/backend/commands/indexcmds.c | 4 +- src/backend/commands/matview.c | 26 +++-- src/backend/commands/tablecmds.c | 2 +- src/backend/optimizer/plan/subselect.c | 3 +- src/backend/parser/parse_relation.c | 4 +- src/include/commands/createas.h | 2 +- src/include/commands/matview.h | 2 +- src/include/optimizer/optimizer.h | 6 ++ .../regress/expected/incremental_matview.out | 99 ++++++++++++++++++- src/test/regress/sql/incremental_matview.sql | 48 ++++++++- 13 files changed, 273 insertions(+), 53 deletions(-) diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml index 72ed56de257..9fef7cef131 100644 --- a/doc/src/sgml/ref/create_materialized_view.sgml +++ b/doc/src/sgml/ref/create_materialized_view.sgml @@ -208,6 +208,12 @@ a.i > 5; + + + Simple CTEs which do not contain aggregates or DISTINCT. + + + Some of aggregations (count, sum, avg, min, max) without HAVING @@ -235,7 +241,7 @@ a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i; - CTE, WINDOW, LIMIT and OFFSET clause. + WINDOW, LIMIT and OFFSET clause. diff --git a/doc/src/sgml/rules.sgml b/doc/src/sgml/rules.sgml index a978677f44f..8c84482467c 100644 --- a/doc/src/sgml/rules.sgml +++ b/doc/src/sgml/rules.sgml @@ -1377,7 +1377,7 @@ Time: 16386.245 ms (00:16.386) Subqueries Currently, subqueries using EXISTS and simple - subqueries in FROM clause are supported. + subqueries in FROM clause are supported. @@ -1444,6 +1444,34 @@ Time: 16386.245 ms (00:16.386) + +CTEs + + Currently, simple CTE in FROM clause are supported. + + + + Restrictions on CTEs + + There are the following restrictions: + + + + Aggregate functions cannot be used in a CTE. + + + + + + DISTINCT cannot be contained in a CTE. + + + + + + + + Other General Restrictions @@ -1451,7 +1479,7 @@ Time: 16386.245 ms (00:16.386) - CTE or window functions cannot be used. + window functions cannot be used. diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index a3031045cc6..381cf184dcf 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -270,6 +270,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt, List *rewritten; PlannedStmt *plan; QueryDesc *queryDesc; + Query *query_immv = NULL; if (stmt->if_not_exists) { @@ -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) @@ -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); @@ -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) @@ -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)); } /* @@ -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; @@ -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; @@ -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), @@ -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, @@ -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); @@ -1086,14 +1117,14 @@ 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) { @@ -1101,9 +1132,10 @@ 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"))); ctx->has_subquery = true; + check_ivm_restriction_walker((Node *) rte->subquery, ctx, depth + 1); } } @@ -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))); @@ -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), diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index af6c76bf99d..53cdba4655c 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -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"))); @@ -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"))); diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index db69e02664e..867802730c2 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -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); } @@ -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(); @@ -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) { @@ -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; @@ -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; @@ -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; @@ -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); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cf7d3b84120..bb6ce1005b1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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"))); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index b02fcb9bfe7..d31899fc070 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -87,7 +87,6 @@ static bool contain_dml(Node *node); static bool contain_dml_walker(Node *node, void *context); static bool contain_outer_selfref(Node *node); static bool contain_outer_selfref_walker(Node *node, Index *depth); -static void inline_cte(PlannerInfo *root, CommonTableExpr *cte); static bool inline_cte_walker(Node *node, inline_cte_walker_context *context); static bool simplify_EXISTS_query(PlannerInfo *root, Query *query); static Query *convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect, @@ -1085,7 +1084,7 @@ contain_outer_selfref_walker(Node *node, Index *depth) /* * inline_cte: convert RTE_CTE references to given CTE into RTE_SUBQUERYs */ -static void +void inline_cte(PlannerInfo *root, CommonTableExpr *cte) { struct inline_cte_walker_context context; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 8b32719c004..b0338b5c9ce 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2899,7 +2899,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, { Form_pg_attribute attr = TupleDescAttr(tupdesc, varattno); - if (is_ivm && isIvmColumn(NameStr(attr->attname)) && !MatViewIncrementalMaintenanceIsEnabled()) + if (is_ivm && isIvmName(NameStr(attr->attname)) && !MatViewIncrementalMaintenanceIsEnabled()) continue; if (attr->attisdropped) @@ -3048,7 +3048,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, TargetEntry *te; /* if transform * into columnlist with IMMV, remove IVM columns */ - if (rte->relisivm && isIvmColumn(label) && !MatViewIncrementalMaintenanceIsEnabled()) + if (rte->relisivm && isIvmName(label) && !MatViewIncrementalMaintenanceIsEnabled()) continue; te = makeTargetEntry((Expr *) varnode, diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h index 5941d86abf6..31350afb40b 100644 --- a/src/include/commands/createas.h +++ b/src/include/commands/createas.h @@ -26,7 +26,7 @@ extern ObjectAddress ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *st ParamListInfo params, QueryEnvironment *queryEnv, QueryCompletion *qc); -extern void CreateIvmTriggersOnBaseTables(Query *qry, Node *jtnode, Oid matviewOid, Relids *relids); +extern void CreateIvmTriggersOnBaseTables(Query *qry, Node *node, Oid matviewOid, Relids *relids); extern Query *rewriteQueryForIMMV(Query *query, List *colNames); diff --git a/src/include/commands/matview.h b/src/include/commands/matview.h index 59bb483236f..1554de746c0 100644 --- a/src/include/commands/matview.h +++ b/src/include/commands/matview.h @@ -38,6 +38,6 @@ extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS); extern Query* rewrite_query_for_exists_subquery(Query *query); extern void AtAbort_IVM(void); extern char *getColumnNameStartWith(RangeTblEntry *rte, char *str, int *attnum); -extern bool isIvmColumn(const char *s); +extern bool isIvmName(const char *s); #endif /* MATVIEW_H */ diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h index 3e4171056e8..d5010e08f2a 100644 --- a/src/include/optimizer/optimizer.h +++ b/src/include/optimizer/optimizer.h @@ -121,6 +121,11 @@ extern void extract_query_dependencies(Node *query, List **invalItems, bool *hasRowSecurity); +/* in plan/subselect.c: */ + +extern void +inline_cte(PlannerInfo *root, CommonTableExpr *cte); + /* in prep/prepqual.c: */ extern Node *negate_clause(Node *node); @@ -187,4 +192,5 @@ extern int locate_var_of_level(Node *node, int levelsup); extern List *pull_var_clause(Node *node, int flags); extern Node *flatten_join_alias_vars(Query *query, Node *node); + #endif /* OPTIMIZER_H */ diff --git a/src/test/regress/expected/incremental_matview.out b/src/test/regress/expected/incremental_matview.out index 90607f0a11a..8ddb4331c97 100644 --- a/src/test/regress/expected/incremental_matview.out +++ b/src/test/regress/expected/incremental_matview.out @@ -635,6 +635,100 @@ SELECT * FROM mv_ivm_join_subquery ORDER BY i,j,k; 3 | 30 | 133 (8 rows) +ROLLBACK; +-- support simple CTE +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; + i | j +---+---- + 1 | 10 + 2 | 20 + 2 | 20 + 3 | 30 + 3 | 30 + 4 | 40 +(6 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH a AS (SELECT * FROM mv_base_a), b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM a, b WHERE a.i = b.i; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; + i | j +---+---- + 1 | 10 + 2 | 20 + 2 | 20 + 3 | 30 + 3 | 30 + 4 | 40 +(6 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT v.i,v.j FROM (WITH a AS (SELECT * FROM mv_base_a) SELECT a.i,a.j FROM a, b WHERE a.i = b.i) v; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; + i | j +---+---- + 1 | 10 + 2 | 20 + 2 | 20 + 3 | 30 + 3 | 30 + 4 | 40 +(6 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + SELECT * FROM (WITH a AS (SELECT * FROM mv_base_a), b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM a, b WHERE a.i = b.i) v; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; + i | j +---+---- + 1 | 10 + 2 | 20 + 2 | 20 + 3 | 30 + 3 | 30 + 4 | 40 +(6 rows) + +ROLLBACK; +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH x AS ( SELECT i, a.j, b.k FROM mv_base_b b INNER JOIN mv_base_a a USING(i)) SELECT * FROM x; +WITH + ai AS (INSERT INTO mv_base_a VALUES (1,11),(2,22) RETURNING 0), + bi AS (INSERT INTO mv_base_b VALUES (1,111),(3,133) RETURNING 0), + bd AS (DELETE FROM mv_base_b WHERE i = 4 RETURNING 0) +SELECT; +-- +(1 row) + +SELECT * FROM mv_cte ORDER BY i,j,k; + i | j | k +---+----+----- + 1 | 10 | 101 + 1 | 10 | 111 + 1 | 11 | 101 + 1 | 11 | 111 + 2 | 20 | 102 + 2 | 22 | 102 + 3 | 30 | 103 + 3 | 30 | 133 +(8 rows) + ROLLBACK; -- views including NULL BEGIN; @@ -5576,7 +5670,7 @@ HINT: aggregate is not supported with outer join -- subquery is not supported with outer join CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN (SELECT * FROM mv_base_b) b ON a.i=b.i; ERROR: this query is not allowed on incrementally maintainable materialized view -HINT: subquery is not supported with outer join +HINT: subquery or CTE is not supported with outer join CREATE INCREMENTAL MATERIALIZED VIEW mv(a,b) AS SELECT a.i, b.i FROM mv_base_a a LEFT JOIN mv_base_b b ON a.i=b.i WHERE EXISTS (SELECT 1 FROM mv_base_b b2 WHERE a.j = b.k); ERROR: this query is not allowed on incrementally maintainable materialized view HINT: subquery with outer join is not supported @@ -5592,9 +5686,6 @@ HINT: subquery in WHERE clause only supports subquery with EXISTS clause CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a; ERROR: this query is not allowed on incrementally maintainable materialized view HINT: subquery in WHERE clause only supports subquery with EXISTS clause --- contain CTE -CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm06 AS WITH b AS (SELECT i,k FROM mv_base_b WHERE k < 103) SELECT a.i,a.j FROM mv_base_a a,b WHERE a.i = b.i; -ERROR: CTE is not supported on incrementally maintainable materialized view -- contain ORDER BY CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k; ERROR: ORDER BY clause is not supported on incrementally maintainable materialized view diff --git a/src/test/regress/sql/incremental_matview.sql b/src/test/regress/sql/incremental_matview.sql index 6fea2b5d50d..6b13f5f7ae0 100644 --- a/src/test/regress/sql/incremental_matview.sql +++ b/src/test/regress/sql/incremental_matview.sql @@ -187,6 +187,7 @@ UPDATE ri1 SET i=10 where i=1; DELETE FROM ri1 WHERE i=2; SELECT * FROM mv_ri ORDER BY i2; ROLLBACK; + -- support subquery for using EXISTS() BEGIN; CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_exists_subquery AS SELECT a.i, a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i); @@ -224,6 +225,7 @@ SELECT *, __ivm_exists_count_0__ FROM mv_ivm_exists_subquery2 ORDER BY i, j; SELECT *, __ivm_exists_count_0__, __ivm_exists_count_1__ FROM mv_ivm_exists_subquery3 ORDER BY i, j; SELECT *, __ivm_exists_count_0__, __ivm_exists_count_1__ FROM mv_ivm_exists_subquery4 ORDER BY i, j; ROLLBACK; + -- support simple subquery in FROM cluase BEGIN; CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm_subquery AS SELECT a.i,a.j FROM mv_base_a a,( SELECT * FROM mv_base_b) b WHERE a.i = b.i; @@ -245,6 +247,50 @@ SELECT * FROM mv_ivm_join_subquery ORDER BY i,j,k; ROLLBACK; +-- support simple CTE +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; +ROLLBACK; + +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH a AS (SELECT * FROM mv_base_a), b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM a, b WHERE a.i = b.i; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; +ROLLBACK; + +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH b AS ( SELECT * FROM mv_base_b) SELECT v.i,v.j FROM (WITH a AS (SELECT * FROM mv_base_a) SELECT a.i,a.j FROM a, b WHERE a.i = b.i) v; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; +ROLLBACK; + +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + SELECT * FROM (WITH a AS (SELECT * FROM mv_base_a), b AS ( SELECT * FROM mv_base_b) SELECT a.i,a.j FROM a, b WHERE a.i = b.i) v; +INSERT INTO mv_base_a VALUES(2,20); +INSERT INTO mv_base_b VALUES(3,300); +SELECT * FROM mv_cte ORDER BY i,j; +ROLLBACK; + +BEGIN; +CREATE INCREMENTAL MATERIALIZED VIEW mv_cte AS + WITH x AS ( SELECT i, a.j, b.k FROM mv_base_b b INNER JOIN mv_base_a a USING(i)) SELECT * FROM x; +WITH + ai AS (INSERT INTO mv_base_a VALUES (1,11),(2,22) RETURNING 0), + bi AS (INSERT INTO mv_base_b VALUES (1,111),(3,133) RETURNING 0), + bd AS (DELETE FROM mv_base_b WHERE i = 4 RETURNING 0) +SELECT; +SELECT * FROM mv_cte ORDER BY i,j,k; +ROLLBACK; + -- views including NULL BEGIN; CREATE TABLE base_t (i int, v int); @@ -1592,8 +1638,6 @@ CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm02 AS SELECT i,j FROM mv_base_a WHER -- targetlist or WHERE clause without EXISTS contain subquery CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm03 AS SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 ); CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm05 AS SELECT i,j, (SELECT k FROM mv_base_b b WHERE a.i = b.i) FROM mv_base_a a; --- contain CTE -CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm06 AS WITH b AS (SELECT i,k FROM mv_base_b WHERE k < 103) SELECT a.i,a.j FROM mv_base_a a,b WHERE a.i = b.i; -- contain ORDER BY CREATE INCREMENTAL MATERIALIZED VIEW mv_ivm07 AS SELECT i,j,k FROM mv_base_a a INNER JOIN mv_base_b b USING(i) ORDER BY i,j,k; -- contain HAVING