mirror of
https://github.com/postgres/postgres.git
synced 2025-11-07 19:06:32 +03:00
Get rid of long-since-vestigial Iter node type, in favor of adding a
returns-set boolean field in Func and Oper nodes. This allows cleaner, more reliable tests for expressions returning sets in the planner and parser. For example, a WHERE clause returning a set is now detected and complained of in the parser, not only at runtime.
This commit is contained in:
@@ -42,7 +42,8 @@ base rels of the query.
|
||||
Possible Paths for a primitive table relation include plain old sequential
|
||||
scan, plus index scans for any indexes that exist on the table. A subquery
|
||||
base relation just has one Path, a "SubqueryScan" path (which links to the
|
||||
subplan that was built by a recursive invocation of the planner).
|
||||
subplan that was built by a recursive invocation of the planner). Likewise
|
||||
a function-RTE base relation has only one possible Path.
|
||||
|
||||
Joins always occur using two RelOptInfos. One is outer, the other inner.
|
||||
Outers drive lookups of values in the inner. In a nested loop, lookups of
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.85 2002/05/12 23:43:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -312,8 +312,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
|
||||
* checking that seems more work than it's worth. In any case, a
|
||||
* plain DISTINCT is safe to push down past.)
|
||||
*
|
||||
* 3. If the subquery has any ITER nodes (ie, functions returning sets)
|
||||
* in its target list, we do not push down any quals, since the quals
|
||||
* 3. If the subquery has any functions returning sets in its target list,
|
||||
* we do not push down any quals, since the quals
|
||||
* might refer to those tlist items, which would mean we'd introduce
|
||||
* functions-returning-sets into the subquery's WHERE/HAVING quals.
|
||||
* (It'd be sufficient to not push down quals that refer to those
|
||||
@@ -333,7 +333,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
|
||||
subquery->limitOffset == NULL &&
|
||||
subquery->limitCount == NULL &&
|
||||
!has_distinct_on_clause(subquery) &&
|
||||
!contain_iter_clause((Node *) subquery->targetList))
|
||||
!expression_returns_set((Node *) subquery->targetList))
|
||||
{
|
||||
/* OK to consider pushing down individual quals */
|
||||
List *upperrestrictlist = NIL;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.116 2002/04/16 23:08:10 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.117 2002/05/12 23:43:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1316,7 +1316,8 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
|
||||
*/
|
||||
test_oper = makeOper(test_op, /* opno */
|
||||
InvalidOid, /* opid */
|
||||
BOOLOID); /* opresulttype */
|
||||
BOOLOID, /* opresulttype */
|
||||
false); /* opretset */
|
||||
replace_opid(test_oper);
|
||||
test_expr = make_opclause(test_oper,
|
||||
(Var *) clause_const,
|
||||
@@ -2020,7 +2021,7 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "prefix_quals: no = operator for type %u", datatype);
|
||||
con = string_to_const(prefix, datatype);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID, false);
|
||||
expr = make_opclause(op, leftop, (Var *) con);
|
||||
result = makeList1(expr);
|
||||
return result;
|
||||
@@ -2035,7 +2036,7 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "prefix_quals: no >= operator for type %u", datatype);
|
||||
con = string_to_const(prefix, datatype);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID, false);
|
||||
expr = make_opclause(op, leftop, (Var *) con);
|
||||
result = makeList1(expr);
|
||||
|
||||
@@ -2051,7 +2052,7 @@ prefix_quals(Var *leftop, Oid expr_op,
|
||||
if (oproid == InvalidOid)
|
||||
elog(ERROR, "prefix_quals: no < operator for type %u", datatype);
|
||||
con = string_to_const(greaterstr, datatype);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID);
|
||||
op = makeOper(oproid, InvalidOid, BOOLOID, false);
|
||||
expr = make_opclause(op, leftop, (Var *) con);
|
||||
result = lappend(result, expr);
|
||||
pfree(greaterstr);
|
||||
@@ -2116,7 +2117,7 @@ network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop)
|
||||
|
||||
opr1right = network_scan_first(rightop);
|
||||
|
||||
op = makeOper(opr1oid, InvalidOid, BOOLOID);
|
||||
op = makeOper(opr1oid, InvalidOid, BOOLOID, false);
|
||||
expr = make_opclause(op, leftop,
|
||||
(Var *) makeConst(datatype, -1, opr1right,
|
||||
false, false, false, false));
|
||||
@@ -2131,7 +2132,7 @@ network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop)
|
||||
|
||||
opr2right = network_scan_last(rightop);
|
||||
|
||||
op = makeOper(opr2oid, InvalidOid, BOOLOID);
|
||||
op = makeOper(opr2oid, InvalidOid, BOOLOID, false);
|
||||
expr = make_opclause(op, leftop,
|
||||
(Var *) makeConst(datatype, -1, opr2right,
|
||||
false, false, false, false));
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.38 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -518,7 +518,8 @@ build_index_pathkeys(Query *root,
|
||||
List *funcargs = NIL;
|
||||
|
||||
funcnode->funcid = index->indproc;
|
||||
funcnode->functype = get_func_rettype(index->indproc);
|
||||
funcnode->funcresulttype = get_func_rettype(index->indproc);
|
||||
funcnode->funcretset = false; /* can never be a set */
|
||||
funcnode->func_fcache = NULL;
|
||||
|
||||
while (*indexkeys != 0)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.69 2002/04/28 19:54:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.70 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -764,9 +764,10 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
||||
clause = makeNode(Expr);
|
||||
clause->typeOid = BOOLOID;
|
||||
clause->opType = OP_EXPR;
|
||||
clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
|
||||
clause->oper = (Node *) makeOper(oprid(eq_operator),/* opno */
|
||||
InvalidOid, /* opid */
|
||||
BOOLOID); /* operator result type */
|
||||
BOOLOID, /* opresulttype */
|
||||
false); /* opretset */
|
||||
clause->args = makeList2(item1, item2);
|
||||
|
||||
ReleaseSysCache(eq_operator);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.116 2002/04/28 19:54:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.117 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -498,7 +498,7 @@ is_simple_subquery(Query *subquery)
|
||||
* set-returning functions into places where they mustn't go,
|
||||
* such as quals of higher queries.
|
||||
*/
|
||||
if (contain_iter_clause((Node *) subquery->targetList))
|
||||
if (expression_returns_set((Node *) subquery->targetList))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.30 2001/10/25 05:49:33 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.31 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -502,7 +502,8 @@ push_nots(Expr *qual)
|
||||
{
|
||||
Oper *op = (Oper *) makeOper(negator,
|
||||
InvalidOid,
|
||||
oper->opresulttype);
|
||||
oper->opresulttype,
|
||||
oper->opretset);
|
||||
|
||||
return make_opclause(op, get_leftop(qual), get_rightop(qual));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.98 2002/05/12 20:10:03 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.99 2002/05/12 23:43:03 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -47,7 +47,7 @@ typedef struct
|
||||
|
||||
static bool contain_agg_clause_walker(Node *node, void *context);
|
||||
static bool pull_agg_clause_walker(Node *node, List **listptr);
|
||||
static bool contain_iter_clause_walker(Node *node, void *context);
|
||||
static bool expression_returns_set_walker(Node *node, void *context);
|
||||
static bool contain_subplans_walker(Node *node, void *context);
|
||||
static bool pull_subplans_walker(Node *node, List **listptr);
|
||||
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
|
||||
@@ -74,7 +74,7 @@ make_clause(int type, Node *oper, List *args)
|
||||
expr->typeOid = ((Oper *) oper)->opresulttype;
|
||||
break;
|
||||
case FUNC_EXPR:
|
||||
expr->typeOid = ((Func *) oper)->functype;
|
||||
expr->typeOid = ((Func *) oper)->funcresulttype;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "make_clause: unsupported type %d", type);
|
||||
@@ -195,7 +195,7 @@ make_funcclause(Func *func, List *funcargs)
|
||||
{
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
expr->typeOid = func->functype;
|
||||
expr->typeOid = func->funcresulttype;
|
||||
expr->opType = FUNC_EXPR;
|
||||
expr->oper = (Node *) func;
|
||||
expr->args = funcargs;
|
||||
@@ -453,36 +453,61 @@ pull_agg_clause_walker(Node *node, List **listptr)
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Iter clause manipulation
|
||||
* Support for expressions returning sets
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* contain_iter_clause
|
||||
* Recursively search for Iter nodes within a clause.
|
||||
* expression_returns_set
|
||||
* Test whethe an expression returns a set result.
|
||||
*
|
||||
* Returns true if any Iter found.
|
||||
*
|
||||
* XXX Iter is a crock. It'd be better to look directly at each function
|
||||
* or operator to see if it can return a set. However, that would require
|
||||
* a lot of extra cycles as things presently stand. The return-type info
|
||||
* for function and operator nodes should be extended to include whether
|
||||
* the return is a set.
|
||||
* Because we use expression_tree_walker(), this can also be applied to
|
||||
* whole targetlists; it'll produce TRUE if any one of the tlist items
|
||||
* returns a set.
|
||||
*/
|
||||
bool
|
||||
contain_iter_clause(Node *clause)
|
||||
expression_returns_set(Node *clause)
|
||||
{
|
||||
return contain_iter_clause_walker(clause, NULL);
|
||||
return expression_returns_set_walker(clause, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
contain_iter_clause_walker(Node *node, void *context)
|
||||
expression_returns_set_walker(Node *node, void *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, Iter))
|
||||
return true; /* abort the tree traversal and return
|
||||
* true */
|
||||
return expression_tree_walker(node, contain_iter_clause_walker, context);
|
||||
if (IsA(node, Expr))
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
|
||||
switch (expr->opType)
|
||||
{
|
||||
case OP_EXPR:
|
||||
if (((Oper *) expr->oper)->opretset)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
break;
|
||||
case FUNC_EXPR:
|
||||
if (((Func *) expr->oper)->funcretset)
|
||||
return true;
|
||||
/* else fall through to check args */
|
||||
break;
|
||||
case OR_EXPR:
|
||||
case AND_EXPR:
|
||||
case NOT_EXPR:
|
||||
/* Booleans can't return a set, so no need to recurse */
|
||||
return false;
|
||||
case SUBPLAN_EXPR:
|
||||
/* Subplans can't presently return sets either */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Avoid recursion for some other cases that can't return a set */
|
||||
if (IsA(node, Aggref))
|
||||
return false;
|
||||
if (IsA(node, SubLink))
|
||||
return false;
|
||||
return expression_tree_walker(node, expression_returns_set_walker,
|
||||
context);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
@@ -1043,7 +1068,8 @@ CommuteClause(Expr *clause)
|
||||
|
||||
commu = makeOper(optup->t_data->t_oid,
|
||||
commuTup->oprcode,
|
||||
commuTup->oprresult);
|
||||
commuTup->oprresult,
|
||||
((Oper *) clause->oper)->opretset);
|
||||
|
||||
ReleaseSysCache(optup);
|
||||
|
||||
@@ -1073,8 +1099,7 @@ CommuteClause(Expr *clause)
|
||||
* results even with constant inputs, "nextval()" being the classic
|
||||
* example. Functions that are not marked "immutable" in pg_proc
|
||||
* will not be pre-evaluated here, although we will reduce their
|
||||
* arguments as far as possible. Functions that are the arguments
|
||||
* of Iter nodes are also not evaluated.
|
||||
* arguments as far as possible.
|
||||
*
|
||||
* We assume that the tree has already been type-checked and contains
|
||||
* only operators and functions that are reasonable to try to execute.
|
||||
@@ -1398,37 +1423,6 @@ eval_const_expressions_mutator(Node *node, void *context)
|
||||
newcase->defresult = defresult;
|
||||
return (Node *) newcase;
|
||||
}
|
||||
if (IsA(node, Iter))
|
||||
{
|
||||
/*
|
||||
* The argument of an Iter is normally a function call. We must
|
||||
* not try to eliminate the function, but we can try to simplify
|
||||
* its arguments. If, by chance, the arg is NOT a function then
|
||||
* we go ahead and try to simplify it (by falling into
|
||||
* expression_tree_mutator). Is that the right thing?
|
||||
*/
|
||||
Iter *iter = (Iter *) node;
|
||||
|
||||
if (is_funcclause(iter->iterexpr))
|
||||
{
|
||||
Expr *func = (Expr *) iter->iterexpr;
|
||||
Expr *newfunc;
|
||||
Iter *newiter;
|
||||
|
||||
newfunc = makeNode(Expr);
|
||||
newfunc->typeOid = func->typeOid;
|
||||
newfunc->opType = func->opType;
|
||||
newfunc->oper = func->oper;
|
||||
newfunc->args = (List *)
|
||||
expression_tree_mutator((Node *) func->args,
|
||||
eval_const_expressions_mutator,
|
||||
(void *) context);
|
||||
newiter = makeNode(Iter);
|
||||
newiter->iterexpr = (Node *) newfunc;
|
||||
newiter->itertype = iter->itertype;
|
||||
return (Node *) newiter;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For any node type not handled above, we recurse using
|
||||
@@ -1501,8 +1495,9 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
* Get the function procedure's OID and look to see whether it is
|
||||
* marked immutable.
|
||||
*
|
||||
* XXX would it be better to take the result type from the pg_proc tuple,
|
||||
* rather than the Oper or Func node?
|
||||
* Note we take the result type from the Oper or Func node, not the
|
||||
* pg_proc tuple; probably necessary for binary-compatibility cases.
|
||||
*
|
||||
*/
|
||||
if (expr->opType == OP_EXPR)
|
||||
{
|
||||
@@ -1517,7 +1512,7 @@ simplify_op_or_func(Expr *expr, List *args)
|
||||
Func *func = (Func *) expr->oper;
|
||||
|
||||
funcid = func->funcid;
|
||||
result_typeid = func->functype;
|
||||
result_typeid = func->funcresulttype;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1747,8 +1742,6 @@ expression_tree_walker(Node *node,
|
||||
break;
|
||||
case T_Aggref:
|
||||
return walker(((Aggref *) node)->target, context);
|
||||
case T_Iter:
|
||||
return walker(((Iter *) node)->iterexpr, context);
|
||||
case T_ArrayRef:
|
||||
{
|
||||
ArrayRef *aref = (ArrayRef *) node;
|
||||
@@ -2083,16 +2076,6 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_Iter:
|
||||
{
|
||||
Iter *iter = (Iter *) node;
|
||||
Iter *newnode;
|
||||
|
||||
FLATCOPY(newnode, iter, Iter);
|
||||
MUTATE(newnode->iterexpr, iter->iterexpr, Node *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_ArrayRef:
|
||||
{
|
||||
ArrayRef *arrayref = (ArrayRef *) node;
|
||||
|
||||
Reference in New Issue
Block a user