Change restriction handling and flags for EXISTS subquery
This patch is reported by yugo-n. This is adding a new boolean member allow_context into check_ivm_restriction_context. This member means whether EXISTS subquery is allowed in the current node being examined in check_ivm_restriction_walker or not.
This commit is contained in:
parent
7eb8a1da38
commit
b3603c776d
3 changed files with 79 additions and 77 deletions
105
createas.c
105
createas.c
|
|
@ -65,14 +65,11 @@ typedef struct
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
bool has_agg;
|
bool has_agg; /* the query has an aggregate */
|
||||||
bool has_sublinks;
|
bool allow_exists; /* EXISTS subquery is allowed in the current node */
|
||||||
bool has_subquery;
|
bool in_exists_subquery; /* true, if under an EXISTS subquery */
|
||||||
bool in_exists_subquery; /* true, if it is in a exists subquery */
|
List *exists_qual_vars; /* Vars used in EXISTS subqueries */
|
||||||
bool in_jointree; /* true, if it is in a join tree */
|
int sublevels_up; /* (current) nesting depth */
|
||||||
bool is_simple_jointree; /* true, if it is in a simple join tree or boolexpr directly under jointree */
|
|
||||||
List *exists_qual_vars;
|
|
||||||
int sublevels_up;
|
|
||||||
} check_ivm_restriction_context;
|
} check_ivm_restriction_context;
|
||||||
|
|
||||||
static void CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
|
static void CreateIvmTriggersOnBaseTablesRecurse(Query *qry, Node *node, Oid matviewOid,
|
||||||
|
|
@ -742,7 +739,13 @@ 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, false, false, false, false, NIL, 0};
|
check_ivm_restriction_context context;
|
||||||
|
|
||||||
|
context.has_agg = false;
|
||||||
|
context.allow_exists = false;
|
||||||
|
context.in_exists_subquery = false;
|
||||||
|
context.exists_qual_vars = NIL;
|
||||||
|
context.sublevels_up = 0;
|
||||||
|
|
||||||
check_ivm_restriction_walker(node, &context);
|
check_ivm_restriction_walker(node, &context);
|
||||||
}
|
}
|
||||||
|
|
@ -750,6 +753,10 @@ check_ivm_restriction(Node *node)
|
||||||
static bool
|
static bool
|
||||||
check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
||||||
{
|
{
|
||||||
|
/* EXISTS is allowed only in this node */
|
||||||
|
bool allow_exists = context->allow_exists;
|
||||||
|
context->allow_exists = false;
|
||||||
|
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -809,8 +816,6 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("recursive query is not supported on incrementally maintainable materialized view")));
|
errmsg("recursive query is not supported on incrementally maintainable materialized view")));
|
||||||
|
|
||||||
context->has_sublinks |= qry->hasSubLinks;
|
|
||||||
|
|
||||||
/* system column restrictions */
|
/* system column restrictions */
|
||||||
vars = pull_vars_of_level((Node *) qry, 0);
|
vars = pull_vars_of_level((Node *) qry, 0);
|
||||||
foreach(lc, vars)
|
foreach(lc, vars)
|
||||||
|
|
@ -904,10 +909,8 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
||||||
|
|
||||||
if (rte->rtekind == RTE_SUBQUERY)
|
if (rte->rtekind == RTE_SUBQUERY)
|
||||||
{
|
{
|
||||||
context->has_subquery = true;
|
|
||||||
|
|
||||||
context->sublevels_up++;
|
context->sublevels_up++;
|
||||||
check_ivm_restriction_walker((Node *)rte->subquery, context);
|
check_ivm_restriction_walker((Node *) rte->subquery, context);
|
||||||
context->sublevels_up--;
|
context->sublevels_up--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -997,38 +1000,17 @@ 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_List:
|
case T_FromExpr:
|
||||||
{
|
{
|
||||||
ListCell *temp;
|
FromExpr *from = (FromExpr *) node;
|
||||||
foreach(temp, (List *) node)
|
|
||||||
{
|
check_ivm_restriction_walker((Node *) from->fromlist, context);
|
||||||
check_ivm_restriction_walker(lfirst(temp), context);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case T_FromExpr:
|
|
||||||
{
|
|
||||||
FromExpr *from = (FromExpr *) node;
|
|
||||||
|
|
||||||
check_ivm_restriction_walker((Node *)from->fromlist, context);
|
|
||||||
/*
|
/*
|
||||||
* check the sublink restrictions.
|
* EXIEST is allowed directly under FROM clause
|
||||||
* Currently, EXISTS subqueries with condition other than 'AND' is not supported.
|
|
||||||
*/
|
*/
|
||||||
if (from->quals == NULL)
|
context->allow_exists = true;
|
||||||
break;
|
|
||||||
if (context->has_sublinks && !context->in_exists_subquery &&
|
|
||||||
!IsA(from->quals, SubLink) && !IsA(from->quals, BoolExpr))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
|
||||||
errhint("sublink only supports simple conditions with EXISTS clause in WHERE clause")));
|
|
||||||
|
|
||||||
context->in_jointree = true;
|
|
||||||
context->is_simple_jointree = true;
|
|
||||||
check_ivm_restriction_walker(from->quals, context);
|
check_ivm_restriction_walker(from->quals, context);
|
||||||
context->is_simple_jointree = false;
|
|
||||||
context->in_jointree = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_JoinExpr:
|
case T_JoinExpr:
|
||||||
|
|
@ -1086,20 +1068,51 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
||||||
case T_BoolExpr:
|
case T_BoolExpr:
|
||||||
{
|
{
|
||||||
BoolExpr *expr = (BoolExpr *) node;
|
BoolExpr *expr = (BoolExpr *) node;
|
||||||
expression_tree_walker((Node *) expr->args, check_ivm_restriction_walker, (void *) context);
|
BoolExprType type = ((BoolExpr *) node)->boolop;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case AND_EXPR:
|
||||||
|
foreach(lc, expr->args)
|
||||||
|
{
|
||||||
|
Node *opnode = (Node *) lfirst(lc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EXIEST is allowed under AND expression only if it is
|
||||||
|
* directly under WHERE.
|
||||||
|
*/
|
||||||
|
if (allow_exists)
|
||||||
|
context->allow_exists = true;
|
||||||
|
check_ivm_restriction_walker(opnode, context);
|
||||||
|
}
|
||||||
|
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 can not be used together")));
|
||||||
|
|
||||||
|
expression_tree_walker((Node *) expr->args, check_ivm_restriction_walker, (void *) context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_SubLink:
|
case T_SubLink:
|
||||||
{
|
{
|
||||||
/* Currently, EXISTS clause is supported only */
|
|
||||||
Query *subselect;
|
Query *subselect;
|
||||||
SubLink *sublink = (SubLink *) node;
|
SubLink *sublink = (SubLink *) node;
|
||||||
if (!context->in_jointree || sublink->subLinkType != EXISTS_SUBLINK)
|
|
||||||
|
/* Only EXISTS clause is supported if it is directly under WHERE */
|
||||||
|
if (!allow_exists || sublink->subLinkType != EXISTS_SUBLINK)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
errmsg("this query is not allowed on incrementally maintainable materialized view"),
|
||||||
errhint("sublink only supports subquery with EXISTS clause in WHERE clause")));
|
errhint("sublink only supports subquery with EXISTS clause in WHERE clause")));
|
||||||
if (context->sublevels_up > 0 || (context->in_jointree && !context->is_simple_jointree))
|
|
||||||
|
if (context->sublevels_up > 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("nested sublink is not supported on incrementally maintainable materialized view")));
|
errmsg("nested sublink is not supported on incrementally maintainable materialized view")));
|
||||||
|
|
@ -1119,8 +1132,6 @@ check_ivm_restriction_walker(Node *node, check_ivm_restriction_context *context)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// not a simple query, if a condition don't match these
|
|
||||||
context->is_simple_jointree = false;
|
|
||||||
expression_tree_walker(node, check_ivm_restriction_walker, (void *) context);
|
expression_tree_walker(node, check_ivm_restriction_walker, (void *) context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -876,7 +876,7 @@ ERROR: this query is not allowed on incrementally maintainable materialized vie
|
||||||
HINT: targetlist must contain vars that are referred to in EXISTS subquery
|
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');
|
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
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: OR or NOT conditions and EXISTS condition are not used together
|
HINT: OR or NOT conditions and EXISTS condition can not be used together
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_a a2 WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a2.i = b.i))');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT a.j FROM mv_base_a a WHERE EXISTS(SELECT 1 FROM mv_base_a a2 WHERE EXISTS(SELECT 1 FROM mv_base_b b WHERE a2.i = b.i))');
|
||||||
ERROR: nested sublink is not supported on incrementally maintainable materialized view
|
ERROR: nested sublink is not supported on incrementally maintainable materialized view
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT EXISTS(SELECT 1 from mv_base_b) FROM mv_base_a a');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT EXISTS(SELECT 1 from mv_base_b) FROM mv_base_a a');
|
||||||
|
|
@ -884,15 +884,16 @@ ERROR: this query is not allowed on incrementally maintainable materialized vie
|
||||||
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT false OR EXISTS(SELECT 1 FROM mv_base_a) FROM mv_base_b');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT false OR EXISTS(SELECT 1 FROM mv_base_a) FROM mv_base_b');
|
||||||
ERROR: this query is not allowed on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
HINT: OR or NOT conditions and EXISTS condition can not be used together
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM generate_series(1, CASE EXISTS(SELECT 1 FROM mv_base_a) WHEN true THEN 100 ELSE 10 END), mv_base_b');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM generate_series(1, CASE EXISTS(SELECT 1 FROM mv_base_a) WHEN true THEN 100 ELSE 10 END), mv_base_b');
|
||||||
ERROR: this query is not allowed on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM mv_base_a a WHERE CASE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) WHEN true THEN false ELSE true END');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM mv_base_a a WHERE CASE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) WHEN true THEN false ELSE true END');
|
||||||
ERROR: this query is not allowed on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
HINT: sublink only supports simple conditions with EXISTS clause in WHERE clause
|
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
||||||
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM mv_base_a a WHERE true and CASE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) WHEN true THEN false ELSE true END');
|
SELECT create_immv('mv_ivm_subquery', 'SELECT * FROM mv_base_a a WHERE true and CASE EXISTS(SELECT 1 FROM mv_base_b b WHERE a.i = b.i) WHEN true THEN false ELSE true END');
|
||||||
ERROR: nested sublink is not supported on incrementally maintainable materialized view
|
ERROR: this query is not allowed on incrementally maintainable materialized view
|
||||||
|
HINT: sublink only supports subquery with EXISTS clause in WHERE clause
|
||||||
-- 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');
|
||||||
|
|
|
||||||
42
matview.c
42
matview.c
|
|
@ -1614,35 +1614,25 @@ rewrite_exists_subquery_walker(Query *query, Node *node, int *count)
|
||||||
}
|
}
|
||||||
case T_BoolExpr:
|
case T_BoolExpr:
|
||||||
{
|
{
|
||||||
BoolExprType type;
|
BoolExprType type = ((BoolExpr *) node)->boolop;
|
||||||
|
|
||||||
type = ((BoolExpr *) node)->boolop;
|
if (type == AND_EXPR)
|
||||||
switch (type)
|
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
case AND_EXPR:
|
foreach(lc, ((BoolExpr *) node)->args)
|
||||||
foreach(lc, ((BoolExpr *)node)->args)
|
{
|
||||||
{
|
/* If simple EXISTS subquery is used, rewrite LATERAL subquery */
|
||||||
/* 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 to true condition 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
|
* EXISTS clause have already overwritten to LATERAL, so original EXISTS clause
|
||||||
* is not necessory.
|
* 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);
|
||||||
}
|
}
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue