Support exists_subquery
Simple EXISTS clause is supported by this patch. A query is not supported when WHERE clause has OR-condition or JOIN-condition is not contained by targetlist.
This commit is contained in:
parent
0587e78651
commit
f29aa802b8
5 changed files with 533 additions and 20 deletions
114
createas.c
114
createas.c
|
|
@ -66,6 +66,8 @@ typedef struct
|
||||||
{
|
{
|
||||||
bool has_agg;
|
bool has_agg;
|
||||||
bool has_subquery;
|
bool has_subquery;
|
||||||
|
bool in_exists_subquery; /* true, if it is in a exists subquery */
|
||||||
|
List *exists_qual_vars;
|
||||||
int sublevels_up;
|
int sublevels_up;
|
||||||
} check_ivm_restriction_context;
|
} check_ivm_restriction_context;
|
||||||
|
|
||||||
|
|
@ -261,7 +263,7 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
|
||||||
CreateIndexOnIMMV(viewQuery, matviewRel, true);
|
CreateIndexOnIMMV(viewQuery, matviewRel, true);
|
||||||
|
|
||||||
/* Create triggers on incremental maintainable materialized view */
|
/* Create triggers on incremental maintainable materialized view */
|
||||||
CreateIvmTriggersOnBaseTables(viewQuery, matviewOid, true);
|
CreateIvmTriggersOnBaseTables(query, matviewOid, true);
|
||||||
|
|
||||||
/* Create triggers to prevent IMMV from beeing changed */
|
/* Create triggers to prevent IMMV from beeing changed */
|
||||||
CreateChangePreventTrigger(matviewOid);
|
CreateChangePreventTrigger(matviewOid);
|
||||||
|
|
@ -278,6 +280,11 @@ ExecCreateImmv(ParseState *pstate, CreateTableAsStmt *stmt,
|
||||||
*
|
*
|
||||||
* count(*) is added for counting distinct tuples in views.
|
* count(*) is added for counting distinct tuples in views.
|
||||||
* Also, additional hidden columns are added for aggregate values.
|
* Also, additional hidden columns are added for aggregate values.
|
||||||
|
*
|
||||||
|
* EXISTS sublink is rewritten to LATERAL subquery with HAVING
|
||||||
|
* clause to check count(*) > 0. In addition, a counting column
|
||||||
|
* referring to count(*) in this subquery is added to the original
|
||||||
|
* target list.
|
||||||
*/
|
*/
|
||||||
Query *
|
Query *
|
||||||
rewriteQueryForIMMV(Query *query, List *colNames)
|
rewriteQueryForIMMV(Query *query, List *colNames)
|
||||||
|
|
@ -301,6 +308,51 @@ rewriteQueryForIMMV(Query *query, List *colNames)
|
||||||
rewritten = copyObject(query);
|
rewritten = copyObject(query);
|
||||||
pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
|
pstate->p_expr_kind = EXPR_KIND_SELECT_TARGET;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this query has EXISTS clause, rewrite query and
|
||||||
|
* add __ivm_exists_count_X__ column.
|
||||||
|
*/
|
||||||
|
if (rewritten->hasSubLinks)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
int varno = 0;
|
||||||
|
|
||||||
|
/* rewrite EXISTS sublink to LATERAL subquery */
|
||||||
|
rewrite_query_for_exists_subquery(rewritten);
|
||||||
|
|
||||||
|
/* Add counting column referring to count(*) in EXISTS clause */
|
||||||
|
foreach(lc, rewritten->rtable)
|
||||||
|
{
|
||||||
|
char *columnName;
|
||||||
|
int attnum;
|
||||||
|
Node *countCol = NULL;
|
||||||
|
varno++;
|
||||||
|
|
||||||
|
rte = (RangeTblEntry *) lfirst(lc);
|
||||||
|
if (!rte->subquery || !rte->lateral)
|
||||||
|
continue;
|
||||||
|
pstate->p_rtable = rewritten->rtable;
|
||||||
|
|
||||||
|
columnName = getColumnNameStartWith(rte, "__ivm_exists", &attnum);
|
||||||
|
if (columnName == NULL)
|
||||||
|
continue;
|
||||||
|
countCol = (Node *)makeVar(varno, attnum,
|
||||||
|
INT8OID, -1, InvalidOid, 0);
|
||||||
|
|
||||||
|
|
||||||
|
if (countCol != NULL)
|
||||||
|
{
|
||||||
|
tle = makeTargetEntry((Expr *) countCol,
|
||||||
|
list_length(rewritten->targetList) + 1,
|
||||||
|
pstrdup(columnName),
|
||||||
|
false);
|
||||||
|
rewritten->targetList = list_concat(rewritten->targetList, list_make1(tle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* group keys must be in targetlist */
|
/* group keys must be in targetlist */
|
||||||
if (rewritten->groupClause)
|
if (rewritten->groupClause)
|
||||||
{
|
{
|
||||||
|
|
@ -691,7 +743,7 @@ CreateIvmTrigger(Oid relOid, Oid viewOid, int16 type, int16 timing, bool ex_lock
|
||||||
static void
|
static void
|
||||||
check_ivm_restriction(Node *node)
|
check_ivm_restriction(Node *node)
|
||||||
{
|
{
|
||||||
check_ivm_restriction_context context = {false, false};
|
check_ivm_restriction_context context = {false, false, false, NIL, 0};
|
||||||
|
|
||||||
check_ivm_restriction_walker(node, &context);
|
check_ivm_restriction_walker(node, &context);
|
||||||
}
|
}
|
||||||
|
|
@ -841,6 +893,39 @@ 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 */
|
||||||
|
if (context->exists_qual_vars != NIL && context->sublevels_up == 0)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
foreach (lc, context->exists_qual_vars)
|
||||||
|
{
|
||||||
|
Var *var = (Var *) lfirst(lc);
|
||||||
|
ListCell *lc2;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
foreach(lc2, qry->targetList)
|
||||||
|
{
|
||||||
|
TargetEntry *tle = lfirst(lc2);
|
||||||
|
Var *var2;
|
||||||
|
|
||||||
|
if (!IsA(tle->expr, Var))
|
||||||
|
continue;
|
||||||
|
var2 = (Var *) tle->expr;
|
||||||
|
if (var->varno == var2->varno && var->varattno == var2->varattno)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
||||||
|
errhint("targetlist must contain vars that are referred to in EXISTS subquery")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_CommonTableExpr:
|
case T_CommonTableExpr:
|
||||||
|
|
@ -927,12 +1012,33 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
||||||
expression_tree_walker(node, check_ivm_restriction_walker, (void *) context);
|
expression_tree_walker(node, check_ivm_restriction_walker, (void *) context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case T_Var:
|
||||||
|
{
|
||||||
|
Var *variable = (Var *) node;
|
||||||
|
/* If EXISTS subquery refers to vars of the upper query, collect these vars */
|
||||||
|
if (variable->varlevelsup > 0 && context->in_exists_subquery)
|
||||||
|
context->exists_qual_vars = lappend(context->exists_qual_vars, node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case T_SubLink:
|
case T_SubLink:
|
||||||
{
|
{
|
||||||
|
/* Now, EXISTS clause is supported only */
|
||||||
|
SubLink *sublink = (SubLink *) node;
|
||||||
|
if (sublink->subLinkType != EXISTS_SUBLINK)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("unsupported subquery on incrementally maintainable materialized view"),
|
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
||||||
errhint("Only simple subquery in FROM clause is supported.")));
|
errhint("subquery in WHERE clause only supports subquery with EXISTS clause")));
|
||||||
|
if (context->sublevels_up > 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("nested subquery is not supported on incrementally maintainable materialized view")));
|
||||||
|
|
||||||
|
context->in_exists_subquery = true;
|
||||||
|
context->sublevels_up++;
|
||||||
|
check_ivm_restriction_walker(sublink->subselect, context);
|
||||||
|
context->sublevels_up--;
|
||||||
|
context->in_exists_subquery = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -661,6 +661,139 @@ SELECT * FROM mv_ri ORDER BY i2;
|
||||||
10 | 10
|
10 | 10
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
ROLLBACK;
|
||||||
|
-- support subquery for using EXISTS()
|
||||||
|
BEGIN;
|
||||||
|
SELECT create_immv('mv_ivm_exists_subquery', 'SELECT a.i, a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i)');
|
||||||
|
NOTICE: could not create an index on immv "mv_ivm_exists_subquery" automatically
|
||||||
|
DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
|
||||||
|
HINT: Create an index on the immv for efficient incremental maintenance.
|
||||||
|
create_immv
|
||||||
|
-------------
|
||||||
|
4
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_immv('mv_ivm_exists_subquery2', 'SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) AND a.i > 2');
|
||||||
|
NOTICE: could not create an index on immv "mv_ivm_exists_subquery2" automatically
|
||||||
|
DETAIL: This target list does not have all the primary key columns, or this view does not contain GROUP BY or DISTINCT clause.
|
||||||
|
HINT: Create an index on the immv for efficient incremental maintenance.
|
||||||
|
create_immv
|
||||||
|
-------------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+----+------------------------
|
||||||
|
1 | 10 | 1
|
||||||
|
2 | 20 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+----+------------------------
|
||||||
|
3 | 30 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
INSERT INTO mv_base_a VALUES(1,10),(6,60),(3,30),(3,300);
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
1 | 10 | 1
|
||||||
|
1 | 10 | 1
|
||||||
|
2 | 20 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(7 rows)
|
||||||
|
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
INSERT INTO mv_base_b VALUES(1,101);
|
||||||
|
INSERT INTO mv_base_b VALUES(1,111);
|
||||||
|
INSERT INTO mv_base_b VALUES(2,102);
|
||||||
|
INSERT INTO mv_base_b VALUES(6,106);
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
1 | 10 | 3
|
||||||
|
1 | 10 | 3
|
||||||
|
2 | 20 | 2
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
6 | 60 | 1
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
6 | 60 | 1
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
UPDATE mv_base_a SET i = 1 WHERE j =60;
|
||||||
|
UPDATE mv_base_b SET i = 10 WHERE k = 101;
|
||||||
|
UPDATE mv_base_b SET k = 1002 WHERE k = 102;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
1 | 10 | 1
|
||||||
|
1 | 10 | 1
|
||||||
|
1 | 60 | 1
|
||||||
|
2 | 20 | 2
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(8 rows)
|
||||||
|
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
DELETE FROM mv_base_a WHERE (i,j) = (1,60);
|
||||||
|
DELETE FROM mv_base_b WHERE i = 2;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
1 | 10 | 1
|
||||||
|
1 | 10 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
i | j | __ivm_exists_count_0__
|
||||||
|
---+-----+------------------------
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 30 | 1
|
||||||
|
3 | 300 | 1
|
||||||
|
4 | 40 | 1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- support simple subquery in FROM clause
|
-- support simple subquery in FROM clause
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
@ -688,25 +821,28 @@ SELECT * FROM mv_ivm_subquery ORDER BY i,j;
|
||||||
|
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- disallow non-simple subqueries
|
-- disallow non-simple subqueries
|
||||||
SELECT create_immv('mv_ivm_exists_subquery', 'SELECT a.i, a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i)');
|
|
||||||
ERROR: unsupported subquery on incrementally maintainable materialized view
|
|
||||||
HINT: Only simple subquery in FROM clause is supported.
|
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i');
|
||||||
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_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT DISTINCT i FROM mv_base_b) b WHERE a.i = b.i');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT DISTINCT i FROM mv_base_b) 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_ivm_subquery', 'SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 )');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 )');
|
||||||
ERROR: unsupported subquery on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: Only simple subquery in FROM clause is supported.
|
HINT: subquery in WHERE clause only supports subquery with EXISTS clause
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) FROM mv_base_a a');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) FROM mv_base_a a');
|
||||||
ERROR: unsupported subquery on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: Only simple subquery in FROM clause is supported.
|
HINT: subquery in WHERE clause only supports subquery with EXISTS clause
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) + 1 FROM mv_base_a a');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) + 1 FROM mv_base_a a');
|
||||||
ERROR: unsupported subquery on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: Only simple subquery in FROM clause is supported.
|
HINT: subquery in WHERE clause only supports subquery with EXISTS clause
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM generate_series(1, (SELECT k FROM mv_base_b LIMIT 1)) AS v');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM generate_series(1, (SELECT k FROM mv_base_b LIMIT 1)) AS v');
|
||||||
ERROR: unsupported subquery on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: Only simple subquery in FROM clause is supported.
|
HINT: subquery in WHERE clause only supports subquery with EXISTS clause
|
||||||
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i)');
|
||||||
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
|
HINT: targetlist must contain vars that are referred to in EXISTS subquery
|
||||||
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 2');
|
||||||
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
|
HINT: OR or NOT conditions and EXISTS condition are not used together
|
||||||
-- support join subquery in FROM clause
|
-- support join subquery in FROM clause
|
||||||
BEGIN;
|
BEGIN;
|
||||||
SELECT create_immv('mv_ivm_join_subquery', 'SELECT i, j, k FROM ( SELECT i, a.j, b.k FROM mv_base_b b INNER JOIN mv_base_a a USING(i)) tmp');
|
SELECT create_immv('mv_ivm_join_subquery', 'SELECT i, j, k FROM ( SELECT i, a.j, b.k FROM mv_base_b b INNER JOIN mv_base_a a USING(i)) tmp');
|
||||||
|
|
|
||||||
246
matview.c
246
matview.c
|
|
@ -29,6 +29,7 @@
|
||||||
#include "executor/tstoreReceiver.h"
|
#include "executor/tstoreReceiver.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "optimizer/optimizer.h"
|
#include "optimizer/optimizer.h"
|
||||||
#include "parser/analyze.h"
|
#include "parser/analyze.h"
|
||||||
#include "parser/parse_clause.h"
|
#include "parser/parse_clause.h"
|
||||||
|
|
@ -37,6 +38,7 @@
|
||||||
#include "parser/parser.h"
|
#include "parser/parser.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "rewrite/rewriteHandler.h"
|
#include "rewrite/rewriteHandler.h"
|
||||||
|
#include "rewrite/rewriteManip.h"
|
||||||
#include "rewrite/rowsecurity.h"
|
#include "rewrite/rowsecurity.h"
|
||||||
#include "storage/lmgr.h"
|
#include "storage/lmgr.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
|
|
@ -973,6 +975,10 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Rewrite for the EXISTS clause */
|
||||||
|
if (rewritten->hasSubLinks)
|
||||||
|
rewrite_query_for_exists_subquery(rewritten);
|
||||||
|
|
||||||
/* Set all tables in the query to pre-update state */
|
/* Set all tables in the query to pre-update state */
|
||||||
rewritten = rewrite_query_for_preupdate_state(rewritten, entry->tables,
|
rewritten = rewrite_query_for_preupdate_state(rewritten, entry->tables,
|
||||||
pstate, NIL, matviewOid);
|
pstate, NIL, matviewOid);
|
||||||
|
|
@ -1034,15 +1040,40 @@ IVM_immediate_maintenance(PG_FUNCTION_ARGS)
|
||||||
foreach(lc2, table->rte_paths)
|
foreach(lc2, table->rte_paths)
|
||||||
{
|
{
|
||||||
List *rte_path = lfirst(lc2);
|
List *rte_path = lfirst(lc2);
|
||||||
|
int i;
|
||||||
|
Query *querytree = rewritten;
|
||||||
|
RangeTblEntry *rte;
|
||||||
TupleDesc tupdesc_old;
|
TupleDesc tupdesc_old;
|
||||||
TupleDesc tupdesc_new;
|
TupleDesc tupdesc_new;
|
||||||
bool use_count = false;
|
bool use_count = false;
|
||||||
char *count_colname = NULL;
|
char *count_colname = NULL;
|
||||||
|
|
||||||
count_colname = pstrdup("__ivm_count__");
|
/* check if the modified table is in EXISTS clause. */
|
||||||
|
for (i = 0; i< list_length(rte_path); i++)
|
||||||
|
{
|
||||||
|
int index = lfirst_int(list_nth_cell(rte_path, i));
|
||||||
|
rte = (RangeTblEntry *)lfirst(list_nth_cell(querytree->rtable, index - 1));
|
||||||
|
|
||||||
if (query->hasAggs || query->distinctClause)
|
if (rte != NULL && rte->rtekind == RTE_SUBQUERY)
|
||||||
|
{
|
||||||
|
querytree = rte->subquery;
|
||||||
|
if (rte->lateral)
|
||||||
|
{
|
||||||
|
int attnum;
|
||||||
|
count_colname = getColumnNameStartWith(rte, "__ivm_exists", &attnum);
|
||||||
|
if (count_colname)
|
||||||
|
{
|
||||||
use_count = true;
|
use_count = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count_colname == NULL && (query->hasAggs || query->distinctClause))
|
||||||
|
{
|
||||||
|
count_colname = pstrdup("__ivm_count__");
|
||||||
|
use_count = true;
|
||||||
|
}
|
||||||
|
|
||||||
/* calculate delta tables */
|
/* calculate delta tables */
|
||||||
calc_delta(table, rte_path, rewritten, dest_old, dest_new,
|
calc_delta(table, rte_path, rewritten, dest_old, dest_new,
|
||||||
|
|
@ -1486,6 +1517,8 @@ rewrite_query_for_distinct_and_aggregates(Query *query, ParseState *pstate)
|
||||||
TargetEntry *tle_count;
|
TargetEntry *tle_count;
|
||||||
FuncCall *fn;
|
FuncCall *fn;
|
||||||
Node *node;
|
Node *node;
|
||||||
|
int varno = 0;
|
||||||
|
ListCell *tbl_lc;
|
||||||
|
|
||||||
/* For aggregate views */
|
/* For aggregate views */
|
||||||
if (query->hasAggs)
|
if (query->hasAggs)
|
||||||
|
|
@ -1504,6 +1537,34 @@ rewrite_query_for_distinct_and_aggregates(Query *query, ParseState *pstate)
|
||||||
query->targetList = list_concat(query->targetList, aggs);
|
query->targetList = list_concat(query->targetList, aggs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add count(*) used for EXISTS clause */
|
||||||
|
foreach(tbl_lc, query->rtable)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = (RangeTblEntry *)lfirst(tbl_lc);
|
||||||
|
varno++;
|
||||||
|
if (rte->subquery)
|
||||||
|
{
|
||||||
|
char *columnName;
|
||||||
|
int attnum;
|
||||||
|
|
||||||
|
/* search ivm_exists_count_X__ column in RangeTblEntry */
|
||||||
|
columnName = getColumnNameStartWith(rte, "__ivm_exists", &attnum);
|
||||||
|
if (columnName == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
node = (Node *)makeVar(varno ,attnum,
|
||||||
|
INT8OID, -1, InvalidOid, 0);
|
||||||
|
|
||||||
|
if (node == NULL)
|
||||||
|
continue;
|
||||||
|
tle_count = makeTargetEntry((Expr *) node,
|
||||||
|
list_length(query->targetList) + 1,
|
||||||
|
pstrdup(columnName),
|
||||||
|
false);
|
||||||
|
query->targetList = lappend(query->targetList, tle_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Add count(*) for counting distinct tuples in views */
|
/* Add count(*) for counting distinct tuples in views */
|
||||||
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 140000)
|
#if defined(PG_VERSION_NUM) && (PG_VERSION_NUM >= 140000)
|
||||||
fn = makeFuncCall(list_make1(makeString("count")), NIL, COERCE_EXPLICIT_CALL, -1);
|
fn = makeFuncCall(list_make1(makeString("count")), NIL, COERCE_EXPLICIT_CALL, -1);
|
||||||
|
|
@ -1526,6 +1587,161 @@ rewrite_query_for_distinct_and_aggregates(Query *query, ParseState *pstate)
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rewrite_query_for_exists_subquery
|
||||||
|
*
|
||||||
|
* Rewrite EXISTS sublink in WHERE to LATERAL subquery
|
||||||
|
*/
|
||||||
|
static Query *
|
||||||
|
rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
|
||||||
|
{
|
||||||
|
/* This can recurse, so check for excessive recursion */
|
||||||
|
check_stack_depth();
|
||||||
|
|
||||||
|
switch (nodeTag(node))
|
||||||
|
{
|
||||||
|
case T_Query:
|
||||||
|
{
|
||||||
|
FromExpr *fromexpr;
|
||||||
|
|
||||||
|
/* get subquery in WHERE clause */
|
||||||
|
fromexpr = (FromExpr *)query->jointree;
|
||||||
|
query = rewrite_exists_subquery_walker(query, fromexpr->quals, count);
|
||||||
|
/* drop subquery in WHERE clause */
|
||||||
|
if (IsA(fromexpr->quals, SubLink))
|
||||||
|
fromexpr->quals = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case T_BoolExpr:
|
||||||
|
{
|
||||||
|
BoolExprType type;
|
||||||
|
|
||||||
|
type = ((BoolExpr *) node)->boolop;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
case AND_EXPR:
|
||||||
|
foreach(lc, ((BoolExpr *)node)->args)
|
||||||
|
{
|
||||||
|
Node *opnode = (Node *)lfirst(lc);
|
||||||
|
query = rewrite_exists_subquery_walker(query, opnode, count);
|
||||||
|
/* overwrite SubLink node if it is contained in AND_EXPR */
|
||||||
|
if (IsA(opnode, SubLink))
|
||||||
|
lfirst(lc) = makeConst(BOOLOID, -1, InvalidOid, sizeof(bool), BoolGetDatum(true), false, true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OR_EXPR:
|
||||||
|
case NOT_EXPR:
|
||||||
|
if (checkExprHasSubLink(node))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
||||||
|
errhint("OR or NOT conditions and EXISTS condition are not used together")));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case T_SubLink:
|
||||||
|
{
|
||||||
|
char aliasName[NAMEDATALEN];
|
||||||
|
char columnName[NAMEDATALEN];
|
||||||
|
Query *subselect;
|
||||||
|
ParseState *pstate;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
RangeTblRef *rtr;
|
||||||
|
Alias *alias;
|
||||||
|
Oid opId;
|
||||||
|
ParseNamespaceItem *nsitem;
|
||||||
|
|
||||||
|
TargetEntry *tle_count;
|
||||||
|
FuncCall *fn;
|
||||||
|
Node *fn_node;
|
||||||
|
Expr *opexpr;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/* 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->p_expr_kind = EXPR_KIND_SELECT_TARGET;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* convert EXISTS subquery into LATERAL subquery in FROM clause.
|
||||||
|
*/
|
||||||
|
|
||||||
|
snprintf(aliasName, sizeof(aliasName), "__ivm_exists_subquery_%d__", *count);
|
||||||
|
snprintf(columnName, sizeof(columnName), "__ivm_exists_count_%d__", *count);
|
||||||
|
|
||||||
|
/* add COUNT(*) for counting rows that meet exists condition */
|
||||||
|
fn = makeFuncCall(list_make1(makeString("count")), NIL, COERCE_EXPLICIT_CALL, -1);
|
||||||
|
fn->agg_star = true;
|
||||||
|
fn_node = ParseFuncOrColumn(pstate, fn->funcname, NIL, NULL, fn, false, -1);
|
||||||
|
tle_count = makeTargetEntry((Expr *) fn_node,
|
||||||
|
list_length(subselect->targetList) + 1,
|
||||||
|
columnName,
|
||||||
|
false);
|
||||||
|
/* add __ivm_exists_count__ column */
|
||||||
|
subselect->targetList = list_concat(subselect->targetList, list_make1(tle_count));
|
||||||
|
subselect->hasAggs = true;
|
||||||
|
|
||||||
|
/* add a sub-query whth LATERAL into from clause */
|
||||||
|
alias = makeAlias(aliasName, NIL);
|
||||||
|
nsitem = addRangeTableEntryForSubquery(pstate, subselect, alias, true, true);
|
||||||
|
rte = nsitem->p_rte;
|
||||||
|
query->rtable = lappend(query->rtable, rte);
|
||||||
|
|
||||||
|
/* assume the new RTE is at the end */
|
||||||
|
rtr = makeNode(RangeTblRef);
|
||||||
|
rtr->rtindex = list_length(query->rtable);
|
||||||
|
((FromExpr *)query->jointree)->fromlist = lappend(((FromExpr *)query->jointree)->fromlist, rtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EXISTS condition is converted to HAVING count(*) > 0.
|
||||||
|
* We use make_opcllause() to get int84gt( '>' operator). We might be able to use make_op().
|
||||||
|
*/
|
||||||
|
opId = OpernameGetOprid(list_make1(makeString(">")), INT8OID, INT4OID);
|
||||||
|
opexpr = make_opclause(opId, BOOLOID, false,
|
||||||
|
(Expr *)fn_node,
|
||||||
|
(Expr *)makeConst(INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(0), false, true),
|
||||||
|
InvalidOid, InvalidOid);
|
||||||
|
fix_opfuncids((Node *) opexpr);
|
||||||
|
query->hasSubLinks = false;
|
||||||
|
|
||||||
|
subselect->havingQual = (Node *)opexpr;
|
||||||
|
(*count)++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
Query *
|
||||||
|
rewrite_query_for_exists_subquery(Query *query)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (query->hasAggs)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
||||||
|
errhint("aggregate function and EXISTS condition are not supported at the same time")));
|
||||||
|
|
||||||
|
return rewrite_exists_subquery_walker(query, (Node *)query, &count);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* calc_delta
|
* calc_delta
|
||||||
*
|
*
|
||||||
|
|
@ -2948,6 +3164,32 @@ clean_up_IVM_hash_entry(MV_TriggerHashEntry *entry, bool is_abort)
|
||||||
hash_search(mv_trigger_info, (void *) &entry->matview_id, HASH_REMOVE, &found);
|
hash_search(mv_trigger_info, (void *) &entry->matview_id, HASH_REMOVE, &found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getColumnNameStartWith
|
||||||
|
*
|
||||||
|
* Search a column name which starts with the given string from the given RTE,
|
||||||
|
* and return the first found one or NULL if not found.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
getColumnNameStartWith(RangeTblEntry *rte, char *str, int *attnum)
|
||||||
|
{
|
||||||
|
char *colname;
|
||||||
|
ListCell *lc;
|
||||||
|
Alias *alias = rte->eref;
|
||||||
|
|
||||||
|
(*attnum) = 0;
|
||||||
|
foreach(lc, alias->colnames)
|
||||||
|
{
|
||||||
|
(*attnum)++;
|
||||||
|
if (strncmp(strVal(lfirst(lc)), str, strlen(str)) == 0)
|
||||||
|
{
|
||||||
|
colname = pstrdup(strVal(lfirst(lc)));
|
||||||
|
return colname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* isIvmName
|
* isIvmName
|
||||||
*
|
*
|
||||||
|
|
|
||||||
2
pg_ivm.h
2
pg_ivm.h
|
|
@ -52,8 +52,10 @@ extern bool ImmvIncrementalMaintenanceIsEnabled(void);
|
||||||
extern Query *get_immv_query(Relation matviewRel);
|
extern Query *get_immv_query(Relation matviewRel);
|
||||||
extern Datum IVM_immediate_before(PG_FUNCTION_ARGS);
|
extern Datum IVM_immediate_before(PG_FUNCTION_ARGS);
|
||||||
extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
|
extern Datum IVM_immediate_maintenance(PG_FUNCTION_ARGS);
|
||||||
|
extern Query* rewrite_query_for_exists_subquery(Query *query);
|
||||||
extern Datum ivm_visible_in_prestate(PG_FUNCTION_ARGS);
|
extern Datum ivm_visible_in_prestate(PG_FUNCTION_ARGS);
|
||||||
extern void AtAbort_IVM(void);
|
extern void AtAbort_IVM(void);
|
||||||
|
extern char *getColumnNameStartWith(RangeTblEntry *rte, char *str, int *attnum);
|
||||||
extern bool isIvmName(const char *s);
|
extern bool isIvmName(const char *s);
|
||||||
|
|
||||||
/* ruleutils.c */
|
/* ruleutils.c */
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,32 @@ DELETE FROM ri1 WHERE i=2;
|
||||||
SELECT * FROM mv_ri ORDER BY i2;
|
SELECT * FROM mv_ri ORDER BY i2;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- support subquery for using EXISTS()
|
||||||
|
BEGIN;
|
||||||
|
SELECT create_immv('mv_ivm_exists_subquery', 'SELECT a.i, a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i)');
|
||||||
|
SELECT create_immv('mv_ivm_exists_subquery2', 'SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) AND a.i > 2');
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
INSERT INTO mv_base_a VALUES(1,10),(6,60),(3,30),(3,300);
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
INSERT INTO mv_base_b VALUES(1,101);
|
||||||
|
INSERT INTO mv_base_b VALUES(1,111);
|
||||||
|
INSERT INTO mv_base_b VALUES(2,102);
|
||||||
|
INSERT INTO mv_base_b VALUES(6,106);
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
UPDATE mv_base_a SET i = 1 WHERE j =60;
|
||||||
|
UPDATE mv_base_b SET i = 10 WHERE k = 101;
|
||||||
|
UPDATE mv_base_b SET k = 1002 WHERE k = 102;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
DELETE FROM mv_base_a WHERE (i,j) = (1,60);
|
||||||
|
DELETE FROM mv_base_b WHERE i = 2;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery ORDER BY i, j;
|
||||||
|
SELECT * FROM mv_ivm_exists_subquery2 ORDER BY i, j;
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
-- support simple subquery in FROM clause
|
-- support simple subquery in FROM clause
|
||||||
BEGIN;
|
BEGIN;
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a,( SELECT * FROM mv_base_b) b WHERE a.i = b.i');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a,( SELECT * FROM mv_base_b) b WHERE a.i = b.i');
|
||||||
|
|
@ -236,13 +262,14 @@ SELECT * FROM mv_ivm_subquery ORDER BY i,j;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
-- disallow non-simple subqueries
|
-- disallow non-simple subqueries
|
||||||
SELECT create_immv('mv_ivm_exists_subquery', 'SELECT a.i, a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i)');
|
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT i, COUNT(*) FROM mv_base_b GROUP BY i) b WHERE a.i = b.i');
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT DISTINCT i FROM mv_base_b) b WHERE a.i = b.i');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a, (SELECT DISTINCT i FROM mv_base_b) b WHERE a.i = b.i');
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 )');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j FROM mv_base_a WHERE i IN (SELECT i FROM mv_base_b WHERE k < 103 )');
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) FROM mv_base_a a');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) FROM mv_base_a a');
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) + 1 FROM mv_base_a a');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT i,j, (SELECT k FROM mv_base_b LIMIT 1) + 1 FROM mv_base_a a');
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM generate_series(1, (SELECT k FROM mv_base_b LIMIT 1)) AS v');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM generate_series(1, (SELECT k FROM mv_base_b LIMIT 1)) AS v');
|
||||||
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i)');
|
||||||
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.i,a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) OR a.i > 2');
|
||||||
|
|
||||||
-- support join subquery in FROM clause
|
-- support join subquery in FROM clause
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue