mirror of
https://github.com/postgres/postgres.git
synced 2025-11-07 19:06:32 +03:00
Promote row expressions to full-fledged citizens of the expression syntax,
rather than allowing them only in a few special cases as before. In particular you can now pass a ROW() construct to a function that accepts a rowtype parameter. Internal generation of RowExprs fixes a number of corner cases that used to not work very well, such as referencing the whole-row result of a JOIN or subquery. This represents a further step in the work I started a month or so back to make rowtype values into first-class citizens.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.113 2004/04/25 18:23:56 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.114 2004/05/10 22:44:44 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -57,9 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes,
|
||||
bool *differentTypes);
|
||||
static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
|
||||
bool *differentTypes);
|
||||
static void subquery_push_qual(Query *subquery, Index rti, Node *qual);
|
||||
static void subquery_push_qual(Query *subquery,
|
||||
RangeTblEntry *rte, Index rti, Node *qual);
|
||||
static void recurse_push_qual(Node *setOp, Query *topquery,
|
||||
Index rti, Node *qual);
|
||||
RangeTblEntry *rte, Index rti, Node *qual);
|
||||
|
||||
|
||||
/*
|
||||
@@ -375,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
|
||||
if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes))
|
||||
{
|
||||
/* Push it down */
|
||||
subquery_push_qual(subquery, rti, clause);
|
||||
subquery_push_qual(subquery, rte, rti, clause);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -778,12 +779,12 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
|
||||
* subquery_push_qual - push down a qual that we have determined is safe
|
||||
*/
|
||||
static void
|
||||
subquery_push_qual(Query *subquery, Index rti, Node *qual)
|
||||
subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual)
|
||||
{
|
||||
if (subquery->setOperations != NULL)
|
||||
{
|
||||
/* Recurse to push it separately to each component query */
|
||||
recurse_push_qual(subquery->setOperations, subquery, rti, qual);
|
||||
recurse_push_qual(subquery->setOperations, subquery, rte, rti, qual);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -797,7 +798,7 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual)
|
||||
* This step also ensures that when we are pushing into a setop tree,
|
||||
* each component query gets its own copy of the qual.
|
||||
*/
|
||||
qual = ResolveNew(qual, rti, 0,
|
||||
qual = ResolveNew(qual, rti, 0, rte,
|
||||
subquery->targetList,
|
||||
CMD_SELECT, 0);
|
||||
subquery->havingQual = make_and_qual(subquery->havingQual,
|
||||
@@ -816,23 +817,23 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual)
|
||||
*/
|
||||
static void
|
||||
recurse_push_qual(Node *setOp, Query *topquery,
|
||||
Index rti, Node *qual)
|
||||
RangeTblEntry *rte, Index rti, Node *qual)
|
||||
{
|
||||
if (IsA(setOp, RangeTblRef))
|
||||
{
|
||||
RangeTblRef *rtr = (RangeTblRef *) setOp;
|
||||
RangeTblEntry *rte = rt_fetch(rtr->rtindex, topquery->rtable);
|
||||
Query *subquery = rte->subquery;
|
||||
RangeTblEntry *subrte = rt_fetch(rtr->rtindex, topquery->rtable);
|
||||
Query *subquery = subrte->subquery;
|
||||
|
||||
Assert(subquery != NULL);
|
||||
subquery_push_qual(subquery, rti, qual);
|
||||
subquery_push_qual(subquery, rte, rti, qual);
|
||||
}
|
||||
else if (IsA(setOp, SetOperationStmt))
|
||||
{
|
||||
SetOperationStmt *op = (SetOperationStmt *) setOp;
|
||||
|
||||
recurse_push_qual(op->larg, topquery, rti, qual);
|
||||
recurse_push_qual(op->rarg, topquery, rti, qual);
|
||||
recurse_push_qual(op->larg, topquery, rte, rti, qual);
|
||||
recurse_push_qual(op->rarg, topquery, rte, rti, qual);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.64 2004/01/05 16:44:40 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.65 2004/05/10 22:44:45 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -27,11 +27,6 @@
|
||||
#include "utils/selfuncs.h"
|
||||
|
||||
|
||||
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
|
||||
#define MAKEBOOLCONST(val,isnull) \
|
||||
((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))
|
||||
|
||||
|
||||
/*
|
||||
* Data structure for accumulating info about possible range-query
|
||||
* clause pairs in clauselist_selectivity.
|
||||
@@ -486,7 +481,7 @@ clause_selectivity(Query *root,
|
||||
s1 = restriction_selectivity(root,
|
||||
BooleanEqualOperator,
|
||||
makeList2(var,
|
||||
MAKEBOOLCONST(true,
|
||||
makeBoolConst(true,
|
||||
false)),
|
||||
varRelid);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.16 2004/01/10 18:13:53 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.17 2004/05/10 22:44:45 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -45,7 +45,8 @@ typedef struct reduce_outer_joins_state
|
||||
|
||||
static bool is_simple_subquery(Query *subquery);
|
||||
static bool has_nullable_targetlist(Query *subquery);
|
||||
static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
|
||||
static void resolvenew_in_jointree(Node *jtnode, int varno,
|
||||
RangeTblEntry *rte, List *subtlist);
|
||||
static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode);
|
||||
static void reduce_outer_joins_pass2(Node *jtnode,
|
||||
reduce_outer_joins_state *state,
|
||||
@@ -154,14 +155,10 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
|
||||
* such expressions; we'd have to figure out how to get the pseudo-
|
||||
* variables evaluated at the right place in the modified plan
|
||||
* tree. Fix it someday.
|
||||
*
|
||||
* Note: even if the subquery itself is simple enough, we can't pull
|
||||
* it up if there is a reference to its whole tuple result.
|
||||
* Perhaps a pseudo-variable is the answer here too.
|
||||
*/
|
||||
if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) &&
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)) &&
|
||||
!contain_whole_tuple_var((Node *) parse, varno, 0))
|
||||
if (rte->rtekind == RTE_SUBQUERY &&
|
||||
is_simple_subquery(subquery) &&
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)))
|
||||
{
|
||||
int rtoffset;
|
||||
List *subtlist;
|
||||
@@ -206,8 +203,7 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
|
||||
* the one above.
|
||||
*/
|
||||
if (is_simple_subquery(subquery) &&
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)) &&
|
||||
!contain_whole_tuple_var((Node *) parse, varno, 0))
|
||||
(!below_outer_join || has_nullable_targetlist(subquery)))
|
||||
{
|
||||
/* good to go */
|
||||
}
|
||||
@@ -247,24 +243,25 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
|
||||
subtlist = subquery->targetList;
|
||||
parse->targetList = (List *)
|
||||
ResolveNew((Node *) parse->targetList,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
resolvenew_in_jointree((Node *) parse->jointree, varno,
|
||||
rte, subtlist);
|
||||
Assert(parse->setOperations == NULL);
|
||||
parse->havingQual =
|
||||
ResolveNew(parse->havingQual,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
parse->in_info_list = (List *)
|
||||
ResolveNew((Node *) parse->in_info_list,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
|
||||
foreach(rt, parse->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
||||
RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt);
|
||||
|
||||
if (rte->rtekind == RTE_JOIN)
|
||||
rte->joinaliasvars = (List *)
|
||||
ResolveNew((Node *) rte->joinaliasvars,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
if (otherrte->rtekind == RTE_JOIN)
|
||||
otherrte->joinaliasvars = (List *)
|
||||
ResolveNew((Node *) otherrte->joinaliasvars,
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -479,7 +476,8 @@ has_nullable_targetlist(Query *subquery)
|
||||
* but there's no other way...
|
||||
*/
|
||||
static void
|
||||
resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
|
||||
resolvenew_in_jointree(Node *jtnode, int varno,
|
||||
RangeTblEntry *rte, List *subtlist)
|
||||
{
|
||||
if (jtnode == NULL)
|
||||
return;
|
||||
@@ -493,18 +491,18 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
|
||||
List *l;
|
||||
|
||||
foreach(l, f->fromlist)
|
||||
resolvenew_in_jointree(lfirst(l), varno, subtlist);
|
||||
resolvenew_in_jointree(lfirst(l), varno, rte, subtlist);
|
||||
f->quals = ResolveNew(f->quals,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
resolvenew_in_jointree(j->larg, varno, subtlist);
|
||||
resolvenew_in_jointree(j->rarg, varno, subtlist);
|
||||
resolvenew_in_jointree(j->larg, varno, rte, subtlist);
|
||||
resolvenew_in_jointree(j->rarg, varno, rte, subtlist);
|
||||
j->quals = ResolveNew(j->quals,
|
||||
varno, 0, subtlist, CMD_SELECT, 0);
|
||||
varno, 0, rte, subtlist, CMD_SELECT, 0);
|
||||
|
||||
/*
|
||||
* We don't bother to update the colvars list, since it won't be
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.169 2004/04/02 23:14:08 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.170 2004/05/10 22:44:45 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -41,10 +41,6 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
|
||||
#define MAKEBOOLCONST(val,isnull) \
|
||||
((Node *) makeConst(BOOLOID, 1, (Datum) (val), (isnull), true))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int nargs;
|
||||
@@ -281,7 +277,7 @@ Expr *
|
||||
make_ands_explicit(List *andclauses)
|
||||
{
|
||||
if (andclauses == NIL)
|
||||
return (Expr *) MAKEBOOLCONST(true, false);
|
||||
return (Expr *) makeBoolConst(true, false);
|
||||
else if (lnext(andclauses) == NIL)
|
||||
return (Expr *) lfirst(andclauses);
|
||||
else
|
||||
@@ -484,6 +480,8 @@ expression_returns_set_walker(Node *node, void *context)
|
||||
return false;
|
||||
if (IsA(node, ArrayExpr))
|
||||
return false;
|
||||
if (IsA(node, RowExpr))
|
||||
return false;
|
||||
if (IsA(node, CoalesceExpr))
|
||||
return false;
|
||||
if (IsA(node, NullIfExpr))
|
||||
@@ -778,6 +776,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
|
||||
if (IsA(node, CaseWhen))
|
||||
return true;
|
||||
/* NB: ArrayExpr might someday be nonstrict */
|
||||
if (IsA(node, RowExpr))
|
||||
return true;
|
||||
if (IsA(node, CoalesceExpr))
|
||||
return true;
|
||||
if (IsA(node, NullIfExpr))
|
||||
@@ -1030,6 +1030,8 @@ set_coercionform_dontcare_walker(Node *node, void *context)
|
||||
((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
|
||||
if (IsA(node, RelabelType))
|
||||
((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
|
||||
if (IsA(node, RowExpr))
|
||||
((RowExpr *) node)->row_format = COERCE_DONTCARE;
|
||||
if (IsA(node, CoerceToDomain))
|
||||
((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
|
||||
return expression_tree_walker(node, set_coercionform_dontcare_walker,
|
||||
@@ -1197,11 +1199,11 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
{
|
||||
/* all nulls? then not distinct */
|
||||
if (all_null_input)
|
||||
return MAKEBOOLCONST(false, false);
|
||||
return makeBoolConst(false, false);
|
||||
|
||||
/* one null? then distinct */
|
||||
if (has_null_input)
|
||||
return MAKEBOOLCONST(true, false);
|
||||
return makeBoolConst(true, false);
|
||||
|
||||
/* otherwise try to evaluate the '=' operator */
|
||||
/* (NOT okay to try to inline it, though!) */
|
||||
@@ -1272,12 +1274,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
newargs = simplify_or_arguments(args,
|
||||
&haveNull, &forceTrue);
|
||||
if (forceTrue)
|
||||
return MAKEBOOLCONST(true, false);
|
||||
return makeBoolConst(true, false);
|
||||
if (haveNull)
|
||||
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
|
||||
newargs = lappend(newargs, makeBoolConst(false, true));
|
||||
/* If all the inputs are FALSE, result is FALSE */
|
||||
if (newargs == NIL)
|
||||
return MAKEBOOLCONST(false, false);
|
||||
return makeBoolConst(false, false);
|
||||
/* If only one nonconst-or-NULL input, it's the result */
|
||||
if (lnext(newargs) == NIL)
|
||||
return (Node *) lfirst(newargs);
|
||||
@@ -1293,12 +1295,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
newargs = simplify_and_arguments(args,
|
||||
&haveNull, &forceFalse);
|
||||
if (forceFalse)
|
||||
return MAKEBOOLCONST(false, false);
|
||||
return makeBoolConst(false, false);
|
||||
if (haveNull)
|
||||
newargs = lappend(newargs, MAKEBOOLCONST(false, true));
|
||||
newargs = lappend(newargs, makeBoolConst(false, true));
|
||||
/* If all the inputs are TRUE, result is TRUE */
|
||||
if (newargs == NIL)
|
||||
return MAKEBOOLCONST(true, false);
|
||||
return makeBoolConst(true, false);
|
||||
/* If only one nonconst-or-NULL input, it's the result */
|
||||
if (lnext(newargs) == NIL)
|
||||
return (Node *) lfirst(newargs);
|
||||
@@ -1313,9 +1315,9 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
|
||||
/* NOT NULL => NULL */
|
||||
if (const_input->constisnull)
|
||||
return MAKEBOOLCONST(false, true);
|
||||
return makeBoolConst(false, true);
|
||||
/* otherwise pretty easy */
|
||||
return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue),
|
||||
return makeBoolConst(!DatumGetBool(const_input->constvalue),
|
||||
false);
|
||||
}
|
||||
else if (not_clause((Node *) lfirst(args)))
|
||||
@@ -1387,7 +1389,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
}
|
||||
if (IsA(node, CaseExpr))
|
||||
{
|
||||
|
||||
/*----------
|
||||
* CASE expressions can be simplified if there are constant
|
||||
* condition clauses:
|
||||
@@ -1546,22 +1547,38 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
|
||||
* We can optimize field selection from a whole-row Var into a
|
||||
* simple Var. (This case won't be generated directly by the
|
||||
* parser, because ParseComplexProjection short-circuits it. But
|
||||
* it can arise while simplifying functions.) If the argument
|
||||
* isn't a whole-row Var, just fall through to do generic
|
||||
* processing.
|
||||
* it can arise while simplifying functions.) Also, we can
|
||||
* optimize field selection from a RowExpr construct.
|
||||
*/
|
||||
FieldSelect *fselect = (FieldSelect *) node;
|
||||
Var *argvar = (Var *) fselect->arg;
|
||||
FieldSelect *newfselect;
|
||||
Node *arg;
|
||||
|
||||
if (argvar && IsA(argvar, Var) &&
|
||||
argvar->varattno == InvalidAttrNumber)
|
||||
arg = eval_const_expressions_mutator((Node *) fselect->arg,
|
||||
active_fns);
|
||||
if (arg && IsA(arg, Var) &&
|
||||
((Var *) arg)->varattno == InvalidAttrNumber)
|
||||
{
|
||||
return (Node *) makeVar(argvar->varno,
|
||||
return (Node *) makeVar(((Var *) arg)->varno,
|
||||
fselect->fieldnum,
|
||||
fselect->resulttype,
|
||||
fselect->resulttypmod,
|
||||
argvar->varlevelsup);
|
||||
((Var *) arg)->varlevelsup);
|
||||
}
|
||||
if (arg && IsA(arg, RowExpr))
|
||||
{
|
||||
RowExpr *rowexpr = (RowExpr *) arg;
|
||||
|
||||
if (fselect->fieldnum > 0 &&
|
||||
fselect->fieldnum <= length(rowexpr->args))
|
||||
return (Node *) nth(fselect->fieldnum - 1, rowexpr->args);
|
||||
}
|
||||
newfselect = makeNode(FieldSelect);
|
||||
newfselect->arg = (Expr *) arg;
|
||||
newfselect->fieldnum = fselect->fieldnum;
|
||||
newfselect->resulttype = fselect->resulttype;
|
||||
newfselect->resulttypmod = fselect->resulttypmod;
|
||||
return (Node *) newfselect;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1759,7 +1776,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
|
||||
bool has_null_input = false;
|
||||
List *arg;
|
||||
FuncExpr *newexpr;
|
||||
char result_typtype;
|
||||
|
||||
/*
|
||||
* Can't simplify if it returns a set.
|
||||
@@ -1796,15 +1812,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args,
|
||||
has_nonconst_input)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Can't simplify functions returning composite types (mainly because
|
||||
* datumCopy() doesn't cope; FIXME someday when we have a saner
|
||||
* representation for whole-tuple results).
|
||||
*/
|
||||
result_typtype = get_typtype(funcform->prorettype);
|
||||
if (result_typtype == 'c')
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* OK, looks like we can simplify this operator/function.
|
||||
*
|
||||
@@ -1850,7 +1857,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple, List *active_fns)
|
||||
{
|
||||
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
char result_typtype;
|
||||
bool polymorphic = false;
|
||||
Oid argtypes[FUNC_MAX_ARGS];
|
||||
char *src;
|
||||
@@ -1877,21 +1883,6 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
funcform->pronargs != length(args))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Forget it if declared return type is not base, domain, or
|
||||
* polymorphic
|
||||
*/
|
||||
result_typtype = get_typtype(funcform->prorettype);
|
||||
if (result_typtype != 'b' &&
|
||||
result_typtype != 'd')
|
||||
{
|
||||
if (funcform->prorettype == ANYARRAYOID ||
|
||||
funcform->prorettype == ANYELEMENTOID)
|
||||
polymorphic = true;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check for recursive function, and give up trying to expand if so */
|
||||
if (oidMember(funcid, active_fns))
|
||||
return NULL;
|
||||
@@ -1912,6 +1903,10 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
}
|
||||
}
|
||||
|
||||
if (funcform->prorettype == ANYARRAYOID ||
|
||||
funcform->prorettype == ANYELEMENTOID)
|
||||
polymorphic = true;
|
||||
|
||||
/*
|
||||
* Setup error traceback support for ereport(). This is so that we
|
||||
* can finger the function that bad information came from.
|
||||
@@ -2483,6 +2478,8 @@ expression_tree_walker(Node *node,
|
||||
break;
|
||||
case T_ArrayExpr:
|
||||
return walker(((ArrayExpr *) node)->elements, context);
|
||||
case T_RowExpr:
|
||||
return walker(((RowExpr *) node)->args, context);
|
||||
case T_CoalesceExpr:
|
||||
return walker(((CoalesceExpr *) node)->args, context);
|
||||
case T_NullIfExpr:
|
||||
@@ -2889,6 +2886,16 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_RowExpr:
|
||||
{
|
||||
RowExpr *rowexpr = (RowExpr *) node;
|
||||
RowExpr *newnode;
|
||||
|
||||
FLATCOPY(newnode, rowexpr, RowExpr);
|
||||
MUTATE(newnode->args, rowexpr->args, List *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_CoalesceExpr:
|
||||
{
|
||||
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.55 2003/11/29 19:51:51 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.56 2004/05/10 22:44:45 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -191,19 +191,6 @@ contain_var_reference_walker(Node *node,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* contain_whole_tuple_var
|
||||
*
|
||||
* Detect whether a parsetree contains any references to the whole
|
||||
* tuple of a given rtable entry (ie, a Var with varattno = 0).
|
||||
*/
|
||||
bool
|
||||
contain_whole_tuple_var(Node *node, int varno, int levelsup)
|
||||
{
|
||||
return contain_var_reference(node, varno, InvalidAttrNumber, levelsup);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* contain_var_clause
|
||||
* Recursively scan a clause to discover whether it contains any Var nodes
|
||||
@@ -486,7 +473,10 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
|
||||
* flatten_join_alias_vars
|
||||
* Replace Vars that reference JOIN outputs with references to the original
|
||||
* relation variables instead. This allows quals involving such vars to be
|
||||
* pushed down.
|
||||
* pushed down. Whole-row Vars that reference JOIN relations are expanded
|
||||
* into RowExpr constructs that name the individual output Vars. This
|
||||
* is necessary since we will not scan the JOIN as a base relation, which
|
||||
* is the only way that the executor can directly handle whole-row Vars.
|
||||
*
|
||||
* NOTE: this is used on not-yet-planned expressions. We do not expect it
|
||||
* to be applied directly to a Query node.
|
||||
@@ -520,8 +510,39 @@ flatten_join_alias_vars_mutator(Node *node,
|
||||
rte = rt_fetch(var->varno, context->root->rtable);
|
||||
if (rte->rtekind != RTE_JOIN)
|
||||
return node;
|
||||
if (var->varattno == InvalidAttrNumber)
|
||||
{
|
||||
/* Must expand whole-row reference */
|
||||
RowExpr *rowexpr;
|
||||
List *fields = NIL;
|
||||
List *l;
|
||||
|
||||
foreach(l, rte->joinaliasvars)
|
||||
{
|
||||
newvar = (Node *) lfirst(l);
|
||||
/*
|
||||
* If we are expanding an alias carried down from an upper
|
||||
* query, must adjust its varlevelsup fields.
|
||||
*/
|
||||
if (context->sublevels_up != 0)
|
||||
{
|
||||
newvar = copyObject(newvar);
|
||||
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
|
||||
}
|
||||
/* Recurse in case join input is itself a join */
|
||||
newvar = flatten_join_alias_vars_mutator(newvar, context);
|
||||
fields = lappend(fields, newvar);
|
||||
}
|
||||
rowexpr = makeNode(RowExpr);
|
||||
rowexpr->args = fields;
|
||||
rowexpr->row_typeid = var->vartype;
|
||||
rowexpr->row_format = COERCE_IMPLICIT_CAST;
|
||||
|
||||
return (Node *) rowexpr;
|
||||
}
|
||||
|
||||
/* Expand join alias reference */
|
||||
Assert(var->varattno > 0);
|
||||
/* Okay, must expand it */
|
||||
newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars);
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user