Fix and cleanup source code

And add some comments and documents.
This commit is contained in:
thoshiai 2023-06-25 05:18:49 +09:00
parent f29aa802b8
commit c0f8a22fa1
4 changed files with 37 additions and 25 deletions

View file

@ -188,7 +188,7 @@ Time: 3224.741 ms (00:03.225)
## Supported View Definitions and Restriction ## Supported View Definitions and Restriction
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. Currently, IMMV's view definition can contain inner joins, DISTINCT clause, some built-in aggregate functions, simple sub-queries in `FROM` clause, EXISTS sub-queries, 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.
The base tables must be simple tables. Views, materialized views, inheritance parent tables, partitioned tables, partitions, and foreign tables can not be used. The base tables must be simple tables. Views, materialized views, inheritance parent tables, partitioned tables, partitions, and foreign tables can not be used.
@ -216,11 +216,11 @@ Targetlist cannot contain expressions which contain an aggregate in it.
### Subqueries ### Subqueries
Simple subqueries in `FROM` clause are supported. Simple subqueries in `FROM` clause and EXISTS subqueries in 'WHERE' clause are supported.
#### Restrictions on Subqueries #### Restrictions on Subqueries
Subqueries can be used only in `FROM` clause. Subqueries in target list or `WHERE` clause are not supported. Subqueries using EXISTS and simple subqueries in FROM clause are supported. EXISTS subqueries with condition other than 'AND' and Subqueries in targetlist are not supported.
Subqueries containing an aggregate function or `DISTINCT` are not supported. Subqueries containing an aggregate function or `DISTINCT` are not supported.

View file

@ -166,7 +166,7 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
check_ivm_restriction((Node *) query); check_ivm_restriction((Node *) query);
/* For IMMV, we need to rewrite matview query */ /* For IMMV, we need to rewrite matview query */
query = rewriteQueryForIMMV(query, into->colNames); query = rewriteQueryForIMMV(viewQuery, into->colNames);
} }
@ -337,10 +337,9 @@ rewriteQueryForIMMV(Query *query, List *colNames)
columnName = getColumnNameStartWith(rte, "__ivm_exists", &attnum); columnName = getColumnNameStartWith(rte, "__ivm_exists", &attnum);
if (columnName == NULL) if (columnName == NULL)
continue; continue;
countCol = (Node *)makeVar(varno, attnum, countCol = (Node *) makeVar(varno, attnum,
INT8OID, -1, InvalidOid, 0); INT8OID, -1, InvalidOid, 0);
if (countCol != NULL) if (countCol != NULL)
{ {
tle = makeTargetEntry((Expr *) countCol, tle = makeTargetEntry((Expr *) countCol,
@ -893,7 +892,12 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
query_tree_walker(qry, check_ivm_restriction_walker, (void *) context, QTW_IGNORE_RT_SUBQUERIES); query_tree_walker(qry, check_ivm_restriction_walker, (void *) context, QTW_IGNORE_RT_SUBQUERIES);
/* additional restriction checks for exists subquery */ /*
* additional restriction checks for exists subquery
*
* When contain EXISTS clauses, and it has a column refernces
* a table outside, its column must be included by target list.
*/
if (context->exists_qual_vars != NIL && context->sublevels_up == 0) if (context->exists_qual_vars != NIL && context->sublevels_up == 0)
{ {
ListCell *lc; ListCell *lc;
@ -1015,7 +1019,9 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
case T_Var: case T_Var:
{ {
Var *variable = (Var *) node; Var *variable = (Var *) node;
/* If EXISTS subquery refers to vars of the upper query, collect these vars */ /* Currently, only EXISTS clause is allowed here.
* If EXISTS subquery refers to vars of the upper query, collect these vars.
*/
if (variable->varlevelsup > 0 && context->in_exists_subquery) if (variable->varlevelsup > 0 && context->in_exists_subquery)
context->exists_qual_vars = lappend(context->exists_qual_vars, node); context->exists_qual_vars = lappend(context->exists_qual_vars, node);
break; break;
@ -1023,6 +1029,7 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
case T_SubLink: case T_SubLink:
{ {
/* Now, EXISTS clause is supported only */ /* Now, EXISTS clause is supported only */
Query *subselect;
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
if (sublink->subLinkType != EXISTS_SUBLINK) if (sublink->subLinkType != EXISTS_SUBLINK)
ereport(ERROR, ereport(ERROR,
@ -1034,6 +1041,13 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("nested subquery is not supported on incrementally maintainable materialized view"))); errmsg("nested subquery is not supported on incrementally maintainable materialized view")));
subselect = (Query *)sublink->subselect;
/* raise ERROR if the sublink has CTE */
if (subselect->cteList)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CTE in EXIST clause is not supported on incrementally maintainable materialized view")));
context->in_exists_subquery = true; context->in_exists_subquery = true;
context->sublevels_up++; context->sublevels_up++;
check_ivm_restriction_walker(sublink->subselect, context); check_ivm_restriction_walker(sublink->subselect, context);

View file

@ -1160,6 +1160,8 @@ SELECT create_immv('mv_cte_fail', 'WITH b AS (SELECT i, COUNT(*) FROM mv_base_b
ERROR: aggregate functions in nested query are not supported on incrementally maintainable materialized view ERROR: aggregate functions in nested query are not supported on incrementally maintainable materialized view
SELECT create_immv('mv_cte_fail', 'WITH b AS (SELECT DISTINCT i FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i'); SELECT create_immv('mv_cte_fail', 'WITH b AS (SELECT DISTINCT i FROM mv_base_b) SELECT a.i,a.j FROM mv_base_a a, b WHERE a.i = b.i');
ERROR: DISTINCT clause in nested query are not supported on incrementally maintainable materialized view ERROR: DISTINCT clause in nested query are not supported on incrementally maintainable materialized view
SELECT create_immv('mv_cte_fail', 'WITH a AS (SELECT i, j FROM mv_base_a) SELECT a.i,a.j FROM a WHERE EXISTS(WITH b AS (SELECT i FROM mv_base_b) SELECT 1 FROM b WHERE a.i = b.i)');
ERROR: CTE in EXIST clause is not supported on incrementally maintainable materialized view
-- unreferenced CTE -- unreferenced CTE
SELECT create_immv('mv_cte_fail', 'WITH b AS (SELECT * FROM mv_base_b) SELECT * FROM mv_base_a a'); SELECT create_immv('mv_cte_fail', 'WITH b AS (SELECT * FROM mv_base_b) SELECT * FROM mv_base_a a');
ERROR: Ureferenced WITH query is not supported on incrementally maintainable materialized view ERROR: Ureferenced WITH query is not supported on incrementally maintainable materialized view

View file

@ -1623,9 +1623,14 @@ rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
case AND_EXPR: case AND_EXPR:
foreach(lc, ((BoolExpr *)node)->args) foreach(lc, ((BoolExpr *)node)->args)
{ {
/* If simple EXISTS subquery is used, rewrite LATERAL subquery */
Node *opnode = (Node *)lfirst(lc); Node *opnode = (Node *)lfirst(lc);
query = rewrite_exists_subquery_walker(query, opnode, count); query = rewrite_exists_subquery_walker(query, opnode, count);
/* overwrite SubLink node if it is contained in AND_EXPR */ /*
* overwrite SubLink node to true condition if it is contained in AND_EXPR.
* EXISTS clause have already overwritten to LATERAL, so original EXISTS clause
* is not necessory.
*/
if (IsA(opnode, SubLink)) if (IsA(opnode, SubLink))
lfirst(lc) = makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true); lfirst(lc) = makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true);
} }
@ -1659,21 +1664,8 @@ rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
Expr *opexpr; Expr *opexpr;
SubLink *sublink = (SubLink *)node; SubLink *sublink = (SubLink *)node;
/* raise ERROR if there is non-EXISTS sublink */
if (sublink->subLinkType != EXISTS_SUBLINK)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("this query is not allowed on incrementally maintainable materialized view"),
errhint("subquery in WHERE clause only supports subquery with EXISTS clause")));
subselect = (Query *)sublink->subselect; subselect = (Query *)sublink->subselect;
/* raise ERROR if the sublink has CTE */
if (subselect->cteList)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CTE is not supported on incrementally maintainable materialized view")));
pstate = make_parsestate(NULL); pstate = make_parsestate(NULL);
pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET; pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
@ -1685,7 +1677,11 @@ rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
snprintf(columnName, sizeof(columnName), "__ivm_exists_count_%d__", *count); snprintf(columnName, sizeof(columnName), "__ivm_exists_count_%d__", *count);
/* add COUNT(*) for counting rows that meet exists condition */ /* add COUNT(*) for counting rows that meet exists condition */
fn = makeFuncCall(list_make1(makeString("count")), NIL, COERCE_EXPLICIT_CALL, -1); #if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 140000)
fn = makeFuncCall(SystemFuncName("count"), NIL, COERCE_EXPLICIT_CALL, -1);
#else
fn = makeFuncCall(SystemFuncName("count"), NIL, -1);
#endif
fn->agg_star = true; fn->agg_star = true;
fn_node = ParseFuncOrColumn(pstate, fn->funcname, NIL, NULL, fn, false, -1); fn_node = ParseFuncOrColumn(pstate, fn->funcname, NIL, NULL, fn, false, -1);
tle_count = makeTargetEntry((Expr *) fn_node, tle_count = makeTargetEntry((Expr *) fn_node,
@ -1711,7 +1707,7 @@ rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
* EXISTS condition is converted to HAVING count(*) > 0. * EXISTS condition is converted to HAVING count(*) > 0.
* We use make_opcllause() to get int84gt( '>' operator). We might be able to use make_op(). * We use make_opcllause() to get int84gt( '>' operator). We might be able to use make_op().
*/ */
opId = OpernameGetOprid(list_make1(makeString(">")), INT8OID, INT4OID); opId = OpernameGetOprid(list_make2(makeString("pg_catalog"), makeString(">")), INT8OID, INT4OID);
opexpr = make_opclause(opId, BOOLOID, false, opexpr = make_opclause(opId, BOOLOID, false,
(Expr *)fn_node, (Expr *)fn_node,
(Expr *)makeConst(INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(0), false, true), (Expr *)makeConst(INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(0), false, true),