diff options
Diffstat (limited to 'src')
31 files changed, 1116 insertions, 511 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index b27e86122bc..d44f580be02 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.158 2004/04/01 21:28:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.159 2004/05/10 22:44:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,6 +42,7 @@ #include "executor/execdebug.h" #include "executor/functions.h" #include "executor/nodeSubplan.h" +#include "funcapi.h" #include "miscadmin.h" #include "optimizer/planmain.h" #include "parser/parse_expr.h" @@ -93,6 +94,9 @@ static Datum ExecEvalCaseTestExpr(ExprState *exprstate, static Datum ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalRow(RowExprState *rstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -2102,6 +2106,54 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, } /* ---------------------------------------------------------------- + * ExecEvalRow - ROW() expressions + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalRow(RowExprState *rstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + HeapTuple tuple; + Datum *values; + char *nulls; + int nargs; + List *arg; + int i; + + /* Set default values for result flags: non-null, not a set result */ + *isNull = false; + if (isDone) + *isDone = ExprSingleResult; + + /* Allocate workspace */ + nargs = length(rstate->args); + if (nargs == 0) /* avoid palloc(0) if no fields */ + nargs = 1; + values = (Datum *) palloc(nargs * sizeof(Datum)); + nulls = (char *) palloc(nargs * sizeof(char)); + + /* Evaluate field values */ + i = 0; + foreach(arg, rstate->args) + { + ExprState *e = (ExprState *) lfirst(arg); + bool eisnull; + + values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL); + nulls[i] = eisnull ? 'n' : ' '; + i++; + } + + tuple = heap_formtuple(rstate->tupdesc, values, nulls); + + pfree(values); + pfree(nulls); + + return HeapTupleGetDatum(tuple); +} + +/* ---------------------------------------------------------------- * ExecEvalCoalesce * ---------------------------------------------------------------- */ @@ -2822,6 +2874,39 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) astate; } break; + case T_RowExpr: + { + RowExpr *rowexpr = (RowExpr *) node; + RowExprState *rstate = makeNode(RowExprState); + List *outlist; + List *inlist; + + rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow; + outlist = NIL; + foreach(inlist, rowexpr->args) + { + Expr *e = (Expr *) lfirst(inlist); + ExprState *estate; + + estate = ExecInitExpr(e, parent); + outlist = lappend(outlist, estate); + } + rstate->args = outlist; + /* Build tupdesc to describe result tuples */ + if (rowexpr->row_typeid == RECORDOID) + { + /* generic record, use runtime type assignment */ + rstate->tupdesc = ExecTypeFromExprList(rowexpr->args); + rstate->tupdesc = BlessTupleDesc(rstate->tupdesc); + } + else + { + /* it's been cast to a named type, use that */ + rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); + } + state = (ExprState *) rstate; + } + break; case T_CoalesceExpr: { CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index faf910b736f..725b8fea0ff 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.76 2004/04/01 21:28:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.77 2004/05/10 22:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -111,6 +111,7 @@ #include "access/heapam.h" #include "catalog/pg_type.h" #include "executor/executor.h" +#include "parser/parse_expr.h" #include "utils/lsyscache.h" #include "utils/typcache.h" @@ -118,6 +119,7 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk); + /* ---------------------------------------------------------------- * tuple table create/delete functions * ---------------------------------------------------------------- @@ -596,6 +598,38 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) } /* + * ExecTypeFromExprList - build a tuple descriptor from a list of Exprs + * + * Here we must make up an arbitrary set of field names. + */ +TupleDesc +ExecTypeFromExprList(List *exprList) +{ + TupleDesc typeInfo; + List *l; + int cur_resno = 1; + char fldname[NAMEDATALEN]; + + typeInfo = CreateTemplateTupleDesc(length(exprList), false); + + foreach(l, exprList) + { + Node *e = lfirst(l); + + sprintf(fldname, "f%d", cur_resno); + + TupleDescInitEntry(typeInfo, + cur_resno++, + fldname, + exprType(e), + exprTypmod(e), + 0); + } + + return typeInfo; +} + +/* * BlessTupleDesc - make a completed tuple descriptor useful for SRFs * * Rowtype Datums returned by a function must contain valid type information. diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 1466be98cbb..91fb3b08343 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.280 2004/05/05 04:48:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.281 2004/05/10 22:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -999,6 +999,21 @@ _copyArrayExpr(ArrayExpr *from) } /* + * _copyRowExpr + */ +static RowExpr * +_copyRowExpr(RowExpr *from) +{ + RowExpr *newnode = makeNode(RowExpr); + + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(row_typeid); + COPY_SCALAR_FIELD(row_format); + + return newnode; +} + +/* * _copyCoalesceExpr */ static CoalesceExpr * @@ -2674,6 +2689,9 @@ copyObject(void *from) case T_ArrayExpr: retval = _copyArrayExpr(from); break; + case T_RowExpr: + retval = _copyRowExpr(from); + break; case T_CoalesceExpr: retval = _copyCoalesceExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index eab30d122c2..19ffbb1be70 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.219 2004/05/05 04:48:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.220 2004/05/10 22:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -424,6 +424,24 @@ _equalArrayExpr(ArrayExpr *a, ArrayExpr *b) } static bool +_equalRowExpr(RowExpr *a, RowExpr *b) +{ + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(row_typeid); + + /* + * Special-case COERCE_DONTCARE, so that planner can build coercion + * nodes that are equal() to both explicit and implicit coercions. + */ + if (a->row_format != b->row_format && + a->row_format != COERCE_DONTCARE && + b->row_format != COERCE_DONTCARE) + return false; + + return true; +} + +static bool _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b) { COMPARE_SCALAR_FIELD(coalescetype); @@ -1748,6 +1766,9 @@ equal(void *a, void *b) case T_ArrayExpr: retval = _equalArrayExpr(a, b); break; + case T_RowExpr: + retval = _equalRowExpr(a, b); + break; case T_CoalesceExpr: retval = _equalCoalesceExpr(a, b); break; diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 230910aca0f..8842bd4aa3b 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -9,12 +9,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.42 2003/11/29 19:51:49 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.43 2004/05/10 22:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "utils/lsyscache.h" @@ -171,6 +172,17 @@ makeNullConst(Oid consttype) } /* + * makeBoolConst - + * creates a Const node representing a boolean value (can be NULL too) + */ +Node * +makeBoolConst(bool value, bool isnull) +{ + /* note that pg_type.h hardwires size of bool as 1 ... duplicate it */ + return (Node *) makeConst(BOOLOID, 1, BoolGetDatum(value), isnull, true); +} + +/* * makeBoolExpr - * creates a BoolExpr node */ diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 9139a9bec54..e919a851940 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.235 2004/05/08 21:21:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.236 2004/05/10 22:44:44 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -829,6 +829,16 @@ _outArrayExpr(StringInfo str, ArrayExpr *node) } static void +_outRowExpr(StringInfo str, RowExpr *node) +{ + WRITE_NODE_TYPE("ROW"); + + WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(row_typeid); + WRITE_ENUM_FIELD(row_format, CoercionForm); +} + +static void _outCoalesceExpr(StringInfo str, CoalesceExpr *node) { WRITE_NODE_TYPE("COALESCE"); @@ -1719,6 +1729,9 @@ _outNode(StringInfo str, void *obj) case T_ArrayExpr: _outArrayExpr(str, obj); break; + case T_RowExpr: + _outRowExpr(str, obj); + break; case T_CoalesceExpr: _outCoalesceExpr(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index ebd3c636c29..253b5a3eafa 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.168 2004/05/08 21:21:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.169 2004/05/10 22:44:44 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -629,6 +629,21 @@ _readArrayExpr(void) } /* + * _readRowExpr + */ +static RowExpr * +_readRowExpr(void) +{ + READ_LOCALS(RowExpr); + + READ_NODE_FIELD(args); + READ_OID_FIELD(row_typeid); + READ_ENUM_FIELD(row_format, CoercionForm); + + READ_DONE(); +} + +/* * _readCoalesceExpr */ static CoalesceExpr * @@ -978,6 +993,8 @@ parseNodeString(void) return_value = _readCaseTestExpr(); else if (MATCH("ARRAY", 5)) return_value = _readArrayExpr(); + else if (MATCH("ROW", 3)) + return_value = _readRowExpr(); else if (MATCH("COALESCE", 8)) return_value = _readCoalesceExpr(); else if (MATCH("NULLIFEXPR", 10)) diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 5d92fc4c5d1..217f06a6b2b 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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 { diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 2cd445961e9..96ce94c8fcb 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -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); } diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 033aaba494e..0ddf1ccd67f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -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 diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f653640ee8c..4dabbf50dac 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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. @@ -1797,15 +1813,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args, 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. * * Build a new FuncExpr node containing the already-simplified arguments. @@ -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; diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index dac90809de1..47b1fdbf70e 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -192,19 +192,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 * (of the current query level). @@ -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); /* diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 64825893bc1..b9becf91dbb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.453 2004/05/05 04:48:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.454 2004/05/10 22:44:45 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -80,11 +80,9 @@ static Node *makeStringConst(char *str, TypeName *typename); static Node *makeIntConst(int val); static Node *makeFloatConst(char *str); static Node *makeAConst(Value *v); -static Node *makeRowExpr(List *opr, List *largs, List *rargs); -static Node *makeDistinctExpr(List *largs, List *rargs); -static Node *makeRowNullTest(NullTestType test, List *args); +static Node *makeRowNullTest(NullTestType test, RowExpr *row); static DefElem *makeDefElem(char *name, Node *arg); -static A_Const *makeBoolConst(bool state); +static A_Const *makeBoolAConst(bool state); static FuncCall *makeOverlaps(List *largs, List *rargs); static List *extractArgTypes(List *parameters); static SelectStmt *findLeftmostSelect(SelectStmt *node); @@ -277,9 +275,9 @@ static void doNegateFloat(Value *v); %type <node> columnDef %type <defelt> def_elem %type <node> def_arg columnElem where_clause insert_column_item - a_expr b_expr c_expr r_expr AexprConst + a_expr b_expr c_expr AexprConst in_expr having_clause func_table array_expr -%type <list> row row_descriptor type_list array_expr_list +%type <list> row type_list array_expr_list %type <node> case_expr case_arg when_clause case_default %type <list> when_clause_list %type <ival> sub_type @@ -5710,163 +5708,6 @@ opt_interval: * *****************************************************************************/ -/* Expressions using row descriptors - * Define row_descriptor to allow yacc to break the reduce/reduce conflict - * with singleton expressions. Use SQL99's ROW keyword to allow rows of - * one element. - */ -r_expr: row IN_P select_with_parens - { - SubLink *n = makeNode(SubLink); - n->subLinkType = ANY_SUBLINK; - n->lefthand = $1; - n->operName = makeList1(makeString("=")); - n->subselect = $3; - $$ = (Node *)n; - } - | row NOT IN_P select_with_parens - { - /* Make an IN node */ - SubLink *n = makeNode(SubLink); - n->subLinkType = ANY_SUBLINK; - n->lefthand = $1; - n->operName = makeList1(makeString("=")); - n->subselect = $4; - /* Stick a NOT on top */ - $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n); - } - | row subquery_Op sub_type select_with_parens - %prec Op - { - SubLink *n = makeNode(SubLink); - n->subLinkType = $3; - n->lefthand = $1; - n->operName = $2; - n->subselect = $4; - $$ = (Node *)n; - } - | row subquery_Op select_with_parens - %prec Op - { - SubLink *n = makeNode(SubLink); - n->subLinkType = MULTIEXPR_SUBLINK; - n->lefthand = $1; - n->operName = $2; - n->subselect = $3; - $$ = (Node *)n; - } - | row subquery_Op row - %prec Op - { - $$ = makeRowExpr($2, $1, $3); - } - | row IS NULL_P - { - $$ = makeRowNullTest(IS_NULL, $1); - } - | row IS NOT NULL_P - { - $$ = makeRowNullTest(IS_NOT_NULL, $1); - } - | row OVERLAPS row - { - $$ = (Node *)makeOverlaps($1, $3); - } - | row IS DISTINCT FROM row - %prec IS - { - /* IS DISTINCT FROM has the following rules for non-array types: - * a) the row lengths must be equal - * b) if both rows are zero-length, then they are not distinct - * c) if any element is distinct, the rows are distinct - * The rules for an element being distinct: - * a) if the elements are both NULL, then they are not distinct - * b) if the elements compare to be equal, then they are not distinct - * c) otherwise, they are distinct - */ - List *largs = $1; - List *rargs = $5; - /* lengths don't match? then complain */ - if (length(largs) != length(rargs)) - { - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unequal number of entries in row expression"))); - } - /* both are zero-length rows? then they are not distinct */ - else if (length(largs) <= 0) - { - $$ = (Node *)makeBoolConst(FALSE); - } - /* otherwise, we need to compare each element */ - else - { - $$ = (Node *)makeDistinctExpr(largs, rargs); - } - } - ; - -/* Explicit row production. - * SQL99 allows an optional ROW keyword, so we can now do single-element productions - * without conflicting with the parenthesized a_expr production. - */ -row: ROW '(' row_descriptor ')' { $$ = $3; } - | ROW '(' a_expr ')' { $$ = makeList1($3); } - | ROW '(' ')' { $$ = NULL; } - | '(' row_descriptor ')' { $$ = $2; } - ; - -row_descriptor: expr_list ',' a_expr { $$ = lappend($1, $3); } - ; - -sub_type: ANY { $$ = ANY_SUBLINK; } - | SOME { $$ = ANY_SUBLINK; } - | ALL { $$ = ALL_SUBLINK; } - ; - -all_Op: Op { $$ = $1; } - | MathOp { $$ = $1; } - ; - -MathOp: '+' { $$ = "+"; } - | '-' { $$ = "-"; } - | '*' { $$ = "*"; } - | '/' { $$ = "/"; } - | '%' { $$ = "%"; } - | '^' { $$ = "^"; } - | '<' { $$ = "<"; } - | '>' { $$ = ">"; } - | '=' { $$ = "="; } - ; - -qual_Op: Op - { $$ = makeList1(makeString($1)); } - | OPERATOR '(' any_operator ')' { $$ = $3; } - ; - -qual_all_Op: - all_Op - { $$ = makeList1(makeString($1)); } - | OPERATOR '(' any_operator ')' { $$ = $3; } - ; - -subquery_Op: - all_Op { $$ = makeList1(makeString($1)); } - | OPERATOR '(' any_operator ')' { $$ = $3; } - | LIKE { $$ = makeList1(makeString("~~")); } - | NOT LIKE { $$ = makeList1(makeString("!~~")); } - | ILIKE { $$ = makeList1(makeString("~~*")); } - | NOT ILIKE { $$ = makeList1(makeString("!~~*")); } -/* cannot put SIMILAR TO here, because SIMILAR TO is a hack. - * the regular expression is preprocessed by a function (similar_escape), - * and the ~ operator for posix regular expressions is used. - * x SIMILAR TO y -> x ~ similar_escape(y) - * this transformation is made on the fly by the parser upwards. - * however the SubLink structure which handles any/some/all stuff - * is not ready for such a thing. - */ - ; - /* * General expressions * This is the heart of the expression syntax. @@ -6046,31 +5887,55 @@ a_expr: c_expr { $$ = $1; } */ | a_expr ISNULL { - NullTest *n = makeNode(NullTest); - n->arg = (Expr *) $1; - n->nulltesttype = IS_NULL; - $$ = (Node *)n; + if (IsA($1, RowExpr)) + $$ = makeRowNullTest(IS_NULL, (RowExpr *) $1); + else + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NULL; + $$ = (Node *)n; + } } | a_expr IS NULL_P { - NullTest *n = makeNode(NullTest); - n->arg = (Expr *) $1; - n->nulltesttype = IS_NULL; - $$ = (Node *)n; + if (IsA($1, RowExpr)) + $$ = makeRowNullTest(IS_NULL, (RowExpr *) $1); + else + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NULL; + $$ = (Node *)n; + } } | a_expr NOTNULL { - NullTest *n = makeNode(NullTest); - n->arg = (Expr *) $1; - n->nulltesttype = IS_NOT_NULL; - $$ = (Node *)n; + if (IsA($1, RowExpr)) + $$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1); + else + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NOT_NULL; + $$ = (Node *)n; + } } | a_expr IS NOT NULL_P { - NullTest *n = makeNode(NullTest); - n->arg = (Expr *) $1; - n->nulltesttype = IS_NOT_NULL; - $$ = (Node *)n; + if (IsA($1, RowExpr)) + $$ = makeRowNullTest(IS_NOT_NULL, (RowExpr *) $1); + else + { + NullTest *n = makeNode(NullTest); + n->arg = (Expr *) $1; + n->nulltesttype = IS_NOT_NULL; + $$ = (Node *)n; + } + } + | row OVERLAPS row + { + $$ = (Node *)makeOverlaps($1, $3); } | a_expr IS TRUE_P { @@ -6115,7 +5980,9 @@ a_expr: c_expr { $$ = $1; } $$ = (Node *)b; } | a_expr IS DISTINCT FROM a_expr %prec IS - { $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5); } + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5); + } | a_expr IS OF '(' type_list ')' %prec IS { $$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5); @@ -6143,7 +6010,10 @@ a_expr: c_expr { $$ = $1; } { SubLink *n = (SubLink *)$3; n->subLinkType = ANY_SUBLINK; - n->lefthand = makeList1($1); + if (IsA($1, RowExpr)) + n->lefthand = ((RowExpr *) $1)->args; + else + n->lefthand = makeList1($1); n->operName = makeList1(makeString("=")); $$ = (Node *)n; } @@ -6171,7 +6041,10 @@ a_expr: c_expr { $$ = $1; } /* Make an IN node */ SubLink *n = (SubLink *)$4; n->subLinkType = ANY_SUBLINK; - n->lefthand = makeList1($1); + if (IsA($1, RowExpr)) + n->lefthand = ((RowExpr *) $1)->args; + else + n->lefthand = makeList1($1); n->operName = makeList1(makeString("=")); /* Stick a NOT on top */ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n); @@ -6196,7 +6069,10 @@ a_expr: c_expr { $$ = $1; } { SubLink *n = makeNode(SubLink); n->subLinkType = $3; - n->lefthand = makeList1($1); + if (IsA($1, RowExpr)) + n->lefthand = ((RowExpr *) $1)->args; + else + n->lefthand = makeList1($1); n->operName = $2; n->subselect = $4; $$ = (Node *)n; @@ -6223,8 +6099,6 @@ a_expr: c_expr { $$ = $1; } (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("UNIQUE predicate is not yet implemented"))); } - | r_expr - { $$ = $1; } ; /* @@ -6277,7 +6151,9 @@ b_expr: c_expr | b_expr qual_Op %prec POSTFIXOP { $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL); } | b_expr IS DISTINCT FROM b_expr %prec IS - { $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5); } + { + $$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5); + } | b_expr IS OF '(' type_list ')' %prec IS { $$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5); @@ -6819,12 +6695,86 @@ c_expr: columnref { $$ = (Node *) $1; } } | ARRAY array_expr { $$ = $2; } + | row + { + RowExpr *r = makeNode(RowExpr); + r->args = $1; + r->row_typeid = InvalidOid; /* not analyzed yet */ + $$ = (Node *)r; + } ; /* * Supporting nonterminals for expressions. */ +/* Explicit row production. + * + * SQL99 allows an optional ROW keyword, so we can now do single-element rows + * without conflicting with the parenthesized a_expr production. Without the + * ROW keyword, there must be more than one a_expr inside the parens. + */ +row: ROW '(' expr_list ')' { $$ = $3; } + | ROW '(' ')' { $$ = NIL; } + | '(' expr_list ',' a_expr ')' { $$ = lappend($2, $4); } + ; + +sub_type: ANY { $$ = ANY_SUBLINK; } + | SOME { $$ = ANY_SUBLINK; } + | ALL { $$ = ALL_SUBLINK; } + ; + +all_Op: Op { $$ = $1; } + | MathOp { $$ = $1; } + ; + +MathOp: '+' { $$ = "+"; } + | '-' { $$ = "-"; } + | '*' { $$ = "*"; } + | '/' { $$ = "/"; } + | '%' { $$ = "%"; } + | '^' { $$ = "^"; } + | '<' { $$ = "<"; } + | '>' { $$ = ">"; } + | '=' { $$ = "="; } + ; + +qual_Op: Op + { $$ = makeList1(makeString($1)); } + | OPERATOR '(' any_operator ')' + { $$ = $3; } + ; + +qual_all_Op: + all_Op + { $$ = makeList1(makeString($1)); } + | OPERATOR '(' any_operator ')' + { $$ = $3; } + ; + +subquery_Op: + all_Op + { $$ = makeList1(makeString($1)); } + | OPERATOR '(' any_operator ')' + { $$ = $3; } + | LIKE + { $$ = makeList1(makeString("~~")); } + | NOT LIKE + { $$ = makeList1(makeString("!~~")); } + | ILIKE + { $$ = makeList1(makeString("~~*")); } + | NOT ILIKE + { $$ = makeList1(makeString("!~~*")); } +/* cannot put SIMILAR TO here, because SIMILAR TO is a hack. + * the regular expression is preprocessed by a function (similar_escape), + * and the ~ operator for posix regular expressions is used. + * x SIMILAR TO y -> x ~ similar_escape(y) + * this transformation is made on the fly by the parser upwards. + * however the SubLink structure which handles any/some/all stuff + * is not ready for such a thing. + */ + ; + opt_indirection: opt_indirection '[' a_expr ']' { @@ -7358,11 +7308,11 @@ AexprConst: Iconst } | TRUE_P { - $$ = (Node *)makeBoolConst(TRUE); + $$ = (Node *)makeBoolAConst(TRUE); } | FALSE_P { - $$ = (Node *)makeBoolConst(FALSE); + $$ = (Node *)makeBoolAConst(FALSE); } | NULL_P { @@ -7892,11 +7842,11 @@ makeDefElem(char *name, Node *arg) return f; } -/* makeBoolConst() +/* makeBoolAConst() * Create an A_Const node and initialize to a boolean constant. */ static A_Const * -makeBoolConst(bool state) +makeBoolAConst(bool state) { A_Const *n = makeNode(A_Const); n->val.type = T_String; @@ -7905,119 +7855,41 @@ makeBoolConst(bool state) return n; } -/* makeRowExpr() - * Generate separate operator nodes for a single row descriptor expression. - * Perhaps this should go deeper in the parser someday... - * - thomas 1997-12-22 +/* makeRowNullTest() + * Generate separate operator nodes for a single row descriptor test. + * + * Eventually this should be eliminated in favor of making the NullTest + * node type capable of handling it directly. */ static Node * -makeRowExpr(List *opr, List *largs, List *rargs) +makeRowNullTest(NullTestType test, RowExpr *row) { - Node *expr = NULL; - Node *larg, *rarg; - char *oprname; + Node *result = NULL; + List *arg; - if (length(largs) != length(rargs)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unequal number of entries in row expression"))); - - if (lnext(largs) != NIL) - expr = makeRowExpr(opr, lnext(largs), lnext(rargs)); - - larg = lfirst(largs); - rarg = lfirst(rargs); + foreach(arg, row->args) + { + NullTest *n; - oprname = strVal(llast(opr)); + n = makeNode(NullTest); + n->arg = (Expr *) lfirst(arg); + n->nulltesttype = test; - if ((strcmp(oprname, "=") == 0) || - (strcmp(oprname, "<") == 0) || - (strcmp(oprname, "<=") == 0) || - (strcmp(oprname, ">") == 0) || - (strcmp(oprname, ">=") == 0)) - { - if (expr == NULL) - expr = (Node *) makeA_Expr(AEXPR_OP, opr, larg, rarg); - else - expr = (Node *) makeA_Expr(AEXPR_AND, NIL, expr, - (Node *) makeA_Expr(AEXPR_OP, opr, - larg, rarg)); - } - else if (strcmp(oprname, "<>") == 0) - { - if (expr == NULL) - expr = (Node *) makeA_Expr(AEXPR_OP, opr, larg, rarg); + if (result == NULL) + result = (Node *) n; + else if (test == IS_NOT_NULL) + result = (Node *) makeA_Expr(AEXPR_OR, NIL, result, (Node *)n); else - expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, - (Node *) makeA_Expr(AEXPR_OP, opr, - larg, rarg)); + result = (Node *) makeA_Expr(AEXPR_AND, NIL, result, (Node *)n); } - else + + if (result == NULL) { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("operator %s is not supported for row expressions", - oprname))); + /* zero-length rows? Generate constant TRUE or FALSE */ + result = (Node *) makeBoolAConst(test == IS_NULL); } - return expr; -} - -/* makeDistinctExpr() - * Generate separate operator nodes for a single row descriptor expression. - * Same comments as for makeRowExpr(). - */ -static Node * -makeDistinctExpr(List *largs, List *rargs) -{ - Node *expr = NULL; - Node *larg, *rarg; - - if (length(largs) != length(rargs)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unequal number of entries in row expression"))); - - if (lnext(largs) != NIL) - expr = makeDistinctExpr(lnext(largs), lnext(rargs)); - - larg = lfirst(largs); - rarg = lfirst(rargs); - - if (expr == NULL) - expr = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", larg, rarg); - else - expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, - (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", - larg, rarg)); - - return expr; -} - -/* makeRowNullTest() - * Generate separate operator nodes for a single row descriptor test. - */ -static Node * -makeRowNullTest(NullTestType test, List *args) -{ - Node *expr = NULL; - NullTest *n; - - if (lnext(args) != NIL) - expr = makeRowNullTest(test, lnext(args)); - - n = makeNode(NullTest); - n->arg = (Expr *) lfirst(args); - n->nulltesttype = test; - - if (expr == NULL) - expr = (Node *) n; - else if (test == IS_NOT_NULL) - expr = (Node *) makeA_Expr(AEXPR_OR, NIL, expr, (Node *)n); - else - expr = (Node *) makeA_Expr(AEXPR_AND, NIL, expr, (Node *)n); - - return expr; + return result; } /* makeOverlaps() diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index a5bff31c89f..c7a8c3c83bb 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.114 2004/03/15 01:13:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.115 2004/05/10 22:44:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,19 +19,26 @@ #include "nodes/makefuncs.h" #include "nodes/params.h" #include "optimizer/clauses.h" +#include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" +#include "parser/parse_relation.h" #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" +#include "utils/typcache.h" static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, bool isExplicit); +static Node *coerce_record_to_complex(ParseState *pstate, Node *node, + Oid targetTypeId, + CoercionContext ccontext, + CoercionForm cformat); /* @@ -279,6 +286,13 @@ coerce_type(ParseState *pstate, Node *node, } return result; } + if (inputTypeId == RECORDOID && + ISCOMPLEX(targetTypeId)) + { + /* Coerce a RECORD to a specific complex type */ + return coerce_record_to_complex(pstate, node, targetTypeId, + ccontext, cformat); + } if (typeInheritsFrom(inputTypeId, targetTypeId)) { /* @@ -360,6 +374,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, continue; /* + * If input is RECORD and target is a composite type, assume + * we can coerce (may need tighter checking here) + */ + if (inputTypeId == RECORDOID && + ISCOMPLEX(targetTypeId)) + continue; + + /* * If input is a class type that inherits from target, accept */ if (typeInheritsFrom(inputTypeId, targetTypeId)) @@ -506,6 +528,103 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, return node; } +/* + * coerce_record_to_complex + * Coerce a RECORD to a specific composite type. + * + * Currently we only support this for inputs that are RowExprs or whole-row + * Vars. + */ +static Node * +coerce_record_to_complex(ParseState *pstate, Node *node, + Oid targetTypeId, + CoercionContext ccontext, + CoercionForm cformat) +{ + RowExpr *rowexpr; + TupleDesc tupdesc; + List *args = NIL; + List *newargs; + int i; + List *arg; + + if (node && IsA(node, RowExpr)) + { + args = ((RowExpr *) node)->args; + } + else if (node && IsA(node, Var) && + ((Var *) node)->varattno == InvalidAttrNumber) + { + RangeTblEntry *rte; + AttrNumber nfields; + AttrNumber nf; + + rte = GetRTEByRangeTablePosn(pstate, + ((Var *) node)->varno, + ((Var *) node)->varlevelsup); + nfields = length(rte->eref->colnames); + for (nf = 1; nf <= nfields; nf++) + { + Oid vartype; + int32 vartypmod; + + get_rte_attribute_type(rte, nf, &vartype, &vartypmod); + args = lappend(args, + makeVar(((Var *) node)->varno, + nf, + vartype, + vartypmod, + ((Var *) node)->varlevelsup)); + } + } + else + ereport(ERROR, + (errcode(ERRCODE_CANNOT_COERCE), + errmsg("cannot cast type %s to %s", + format_type_be(RECORDOID), + format_type_be(targetTypeId)))); + + tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1); + if (length(args) != tupdesc->natts) + ereport(ERROR, + (errcode(ERRCODE_CANNOT_COERCE), + errmsg("cannot cast type %s to %s", + format_type_be(RECORDOID), + format_type_be(targetTypeId)), + errdetail("Input has wrong number of columns."))); + newargs = NIL; + i = 0; + foreach(arg, args) + { + Node *expr = (Node *) lfirst(arg); + Oid exprtype = exprType(expr); + + expr = coerce_to_target_type(pstate, + expr, exprtype, + tupdesc->attrs[i]->atttypid, + tupdesc->attrs[i]->atttypmod, + ccontext, + COERCE_IMPLICIT_CAST); + if (expr == NULL) + ereport(ERROR, + (errcode(ERRCODE_CANNOT_COERCE), + errmsg("cannot cast type %s to %s", + format_type_be(RECORDOID), + format_type_be(targetTypeId)), + errdetail("Cannot cast type %s to %s in column %d.", + format_type_be(exprtype), + format_type_be(tupdesc->attrs[i]->atttypid), + i + 1))); + newargs = lappend(newargs, expr); + i++; + } + + rowexpr = makeNode(RowExpr); + rowexpr->args = newargs; + rowexpr->row_typeid = targetTypeId; + rowexpr->row_format = cformat; + return (Node *) rowexpr; +} /* coerce_to_boolean() * Coerce an argument of a construct that requires boolean input diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 2747ec3ed43..d4a8cdcc8b6 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.169 2004/04/18 18:12:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.170 2004/05/10 22:44:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,13 +37,19 @@ bool Transform_null_equals = false; -static Node *typecast_expression(ParseState *pstate, Node *expr, - TypeName *typename); static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); static Node *transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname); static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); +static Node *typecast_expression(ParseState *pstate, Node *expr, + TypeName *typename); +static Node *make_row_op(ParseState *pstate, List *opname, + Node *ltree, Node *rtree); +static Node *make_row_distinct_op(ParseState *pstate, List *opname, + Node *ltree, Node *rtree); +static Expr *make_distinct_op(ParseState *pstate, List *opname, + Node *ltree, Node *rtree); /* @@ -202,6 +208,9 @@ transformExpr(ParseState *pstate, Node *expr) { case AEXPR_OP: { + Node *lexpr = a->lexpr; + Node *rexpr = a->rexpr; + /* * Special-case "foo = NULL" and "NULL = foo" * for compatibility with standards-broken @@ -211,27 +220,51 @@ transformExpr(ParseState *pstate, Node *expr) if (Transform_null_equals && length(a->name) == 1 && strcmp(strVal(lfirst(a->name)), "=") == 0 && - (exprIsNullConstant(a->lexpr) || - exprIsNullConstant(a->rexpr))) + (exprIsNullConstant(lexpr) || + exprIsNullConstant(rexpr))) { NullTest *n = makeNode(NullTest); n->nulltesttype = IS_NULL; - if (exprIsNullConstant(a->lexpr)) - n->arg = (Expr *) a->rexpr; + if (exprIsNullConstant(lexpr)) + n->arg = (Expr *) rexpr; else - n->arg = (Expr *) a->lexpr; + n->arg = (Expr *) lexpr; result = transformExpr(pstate, (Node *) n); } + else if (lexpr && IsA(lexpr, RowExpr) && + rexpr && IsA(rexpr, SubLink) && + ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK) + { + /* + * Convert "row op subselect" into a + * MULTIEXPR sublink. Formerly the grammar + * did this, but now that a row construct is + * allowed anywhere in expressions, it's + * easier to do it here. + */ + SubLink *s = (SubLink *) rexpr; + + s->subLinkType = MULTIEXPR_SUBLINK; + s->lefthand = ((RowExpr *) lexpr)->args; + s->operName = a->name; + result = transformExpr(pstate, (Node *) s); + } + else if (lexpr && IsA(lexpr, RowExpr) && + rexpr && IsA(rexpr, RowExpr)) + { + /* "row op row" */ + result = make_row_op(pstate, a->name, + lexpr, rexpr); + } else { - Node *lexpr = transformExpr(pstate, - a->lexpr); - Node *rexpr = transformExpr(pstate, - a->rexpr); + /* Ordinary scalar operator */ + lexpr = transformExpr(pstate, lexpr); + rexpr = transformExpr(pstate, rexpr); result = (Node *) make_op(pstate, a->name, @@ -311,25 +344,27 @@ transformExpr(ParseState *pstate, Node *expr) break; case AEXPR_DISTINCT: { - Node *lexpr = transformExpr(pstate, - a->lexpr); - Node *rexpr = transformExpr(pstate, - a->rexpr); + Node *lexpr = a->lexpr; + Node *rexpr = a->rexpr; - result = (Node *) make_op(pstate, - a->name, - lexpr, - rexpr); - if (((OpExpr *) result)->opresulttype != BOOLOID) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("IS DISTINCT FROM requires = operator to yield boolean"))); + if (lexpr && IsA(lexpr, RowExpr) && + rexpr && IsA(rexpr, RowExpr)) + { + /* "row op row" */ + result = make_row_distinct_op(pstate, a->name, + lexpr, rexpr); + } + else + { + /* Ordinary scalar operator */ + lexpr = transformExpr(pstate, lexpr); + rexpr = transformExpr(pstate, rexpr); - /* - * We rely on DistinctExpr and OpExpr being - * same struct - */ - NodeSetTag(result, T_DistinctExpr); + result = (Node *) make_distinct_op(pstate, + a->name, + lexpr, + rexpr); + } } break; case AEXPR_NULLIF: @@ -787,6 +822,32 @@ transformExpr(ParseState *pstate, Node *expr) break; } + case T_RowExpr: + { + RowExpr *r = (RowExpr *) expr; + RowExpr *newr = makeNode(RowExpr); + List *newargs = NIL; + List *arg; + + /* Transform the field expressions */ + foreach(arg, r->args) + { + Node *e = (Node *) lfirst(arg); + Node *newe; + + newe = transformExpr(pstate, e); + newargs = lappend(newargs, newe); + } + newr->args = newargs; + + /* Barring later casting, we consider the type RECORD */ + newr->row_typeid = RECORDOID; + newr->row_format = COERCE_IMPLICIT_CAST; + + result = (Node *) newr; + break; + } + case T_CoalesceExpr: { CoalesceExpr *c = (CoalesceExpr *) expr; @@ -1113,14 +1174,11 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* * Construct a whole-row reference to represent the notation "relation.*". * - * In simple cases, this will be a Var with varno set to the correct range + * A whole-row reference is a Var with varno set to the correct range * table entry, and varattno == 0 to signal that it references the whole * tuple. (Use of zero here is unclean, since it could easily be confused * with error cases, but it's not worth changing now.) The vartype indicates * a rowtype; either a named composite type, or RECORD. - * - * We also need the ability to build a row-constructor expression, but the - * infrastructure for that doesn't exist just yet. */ static Node * transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname) @@ -1185,12 +1243,10 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname) break; default: /* - * RTE is a join or subselect. For the moment we represent this - * as a whole-row Var of RECORD type, but this will not actually - * work; need a row-constructor expression instead. - * - * XXX after fixing, be sure that unknown_attribute still - * does the right thing. + * RTE is a join or subselect. We represent this as a whole-row + * Var of RECORD type. (Note that in most cases the Var will + * be expanded to a RowExpr during planning, but that is not + * our concern here.) */ result = (Node *) makeVar(vnum, InvalidAttrNumber, @@ -1266,8 +1322,8 @@ exprType(Node *expr) if (sublink->subLinkType == EXPR_SUBLINK) type = tent->resdom->restype; else -/* ARRAY_SUBLINK */ { + /* ARRAY_SUBLINK */ type = get_array_type(tent->resdom->restype); if (!OidIsValid(type)) ereport(ERROR, @@ -1305,8 +1361,8 @@ exprType(Node *expr) if (subplan->subLinkType == EXPR_SUBLINK) type = tent->resdom->restype; else -/* ARRAY_SUBLINK */ { + /* ARRAY_SUBLINK */ type = get_array_type(tent->resdom->restype); if (!OidIsValid(type)) ereport(ERROR, @@ -1340,6 +1396,9 @@ exprType(Node *expr) case T_ArrayExpr: type = ((ArrayExpr *) expr)->array_typeid; break; + case T_RowExpr: + type = ((RowExpr *) expr)->row_typeid; + break; case T_CoalesceExpr: type = ((CoalesceExpr *) expr)->coalescetype; break; @@ -1573,3 +1632,166 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename) return expr; } + +/* + * Transform a "row op row" construct + */ +static Node * +make_row_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree) +{ + Node *result = NULL; + RowExpr *lrow, + *rrow; + List *largs, + *rargs; + List *largl, + *rargl; + char *oprname; + BoolExprType boolop; + + /* Inputs are untransformed RowExprs */ + lrow = (RowExpr *) transformExpr(pstate, ltree); + rrow = (RowExpr *) transformExpr(pstate, rtree); + Assert(IsA(lrow, RowExpr)); + Assert(IsA(rrow, RowExpr)); + largs = lrow->args; + rargs = rrow->args; + + if (length(largs) != length(rargs)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unequal number of entries in row expression"))); + + /* + * XXX it's really wrong to generate a simple AND combination for < <= + * > >=. We probably need to invent a new runtime node type to handle + * those correctly. For the moment, though, keep on doing this ... + */ + oprname = strVal(llast(opname)); + + if ((strcmp(oprname, "=") == 0) || + (strcmp(oprname, "<") == 0) || + (strcmp(oprname, "<=") == 0) || + (strcmp(oprname, ">") == 0) || + (strcmp(oprname, ">=") == 0)) + { + boolop = AND_EXPR; + } + else if (strcmp(oprname, "<>") == 0) + { + boolop = OR_EXPR; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("operator %s is not supported for row expressions", + oprname))); + boolop = 0; /* keep compiler quiet */ + } + + /* XXX use forboth */ + rargl = rargs; + foreach(largl, largs) + { + Node *larg = (Node *) lfirst(largl); + Node *rarg = (Node *) lfirst(rargl); + Node *cmp; + + rargl = lnext(rargl); + cmp = (Node *) make_op(pstate, opname, larg, rarg); + cmp = coerce_to_boolean(pstate, cmp, "row comparison"); + if (result == NULL) + result = cmp; + else + result = (Node *) makeBoolExpr(boolop, + makeList2(result, cmp)); + } + + if (result == NULL) + { + /* zero-length rows? Generate constant TRUE or FALSE */ + if (boolop == AND_EXPR) + result = makeBoolConst(true, false); + else + result = makeBoolConst(false, false); + } + + return result; +} + +/* + * Transform a "row IS DISTINCT FROM row" construct + */ +static Node * +make_row_distinct_op(ParseState *pstate, List *opname, + Node *ltree, Node *rtree) +{ + Node *result = NULL; + RowExpr *lrow, + *rrow; + List *largs, + *rargs; + List *largl, + *rargl; + + /* Inputs are untransformed RowExprs */ + lrow = (RowExpr *) transformExpr(pstate, ltree); + rrow = (RowExpr *) transformExpr(pstate, rtree); + Assert(IsA(lrow, RowExpr)); + Assert(IsA(rrow, RowExpr)); + largs = lrow->args; + rargs = rrow->args; + + if (length(largs) != length(rargs)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unequal number of entries in row expression"))); + + /* XXX use forboth */ + rargl = rargs; + foreach(largl, largs) + { + Node *larg = (Node *) lfirst(largl); + Node *rarg = (Node *) lfirst(rargl); + Node *cmp; + + rargl = lnext(rargl); + cmp = (Node *) make_distinct_op(pstate, opname, larg, rarg); + if (result == NULL) + result = cmp; + else + result = (Node *) makeBoolExpr(OR_EXPR, + makeList2(result, cmp)); + } + + if (result == NULL) + { + /* zero-length rows? Generate constant FALSE */ + result = makeBoolConst(false, false); + } + + return result; +} + +/* + * make the node for an IS DISTINCT FROM operator + */ +static Expr * +make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree) +{ + Expr *result; + + result = make_op(pstate, opname, ltree, rtree); + if (((OpExpr *) result)->opresulttype != BOOLOID) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("IS DISTINCT FROM requires = operator to yield boolean"))); + /* + * We rely on DistinctExpr and OpExpr being + * same struct + */ + NodeSetTag(result, T_DistinctExpr); + + return result; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index c9c44ac2389..fbee22b37d3 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.116 2004/04/02 19:06:58 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.117 2004/05/10 22:44:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -643,6 +643,10 @@ FigureColnameInternal(Node *node, char **name) /* make ARRAY[] act like a function */ *name = "array"; return 2; + case T_RowExpr: + /* make ROW() act like a function */ + *name = "row"; + return 2; case T_CoalesceExpr: /* make coalesce() act like a regular function */ *name = "coalesce"; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index d83e8ac4580..3b898b973d8 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.134 2004/04/01 21:28:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.135 2004/05/10 22:44:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -212,6 +212,8 @@ rewriteRuleAction(Query *parsetree, sub_action = (Query *) ResolveNew((Node *) sub_action, new_varno, 0, + rt_fetch(new_varno, + sub_action->rtable), parsetree->targetList, event, current_varno); @@ -947,6 +949,7 @@ CopyAndAddInvertedQual(Query *parsetree, new_qual = ResolveNew(new_qual, PRS2_NEW_VARNO, 0, + rt_fetch(rt_index, parsetree->rtable), parsetree->targetList, event, rt_index); diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 5210b99fd7e..45b60715966 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.81 2003/11/29 19:51:55 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.82 2004/05/10 22:44:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -850,6 +850,10 @@ AddInvertedQual(Query *parsetree, Node *qual) * If not, we either change the unmatched Var's varno to update_varno * (when event == CMD_UPDATE) or replace it with a constant NULL. * + * The caller must also provide target_rte, the RTE describing the target + * relation. This is needed to handle whole-row Vars referencing the target. + * We expand such Vars into RowExpr constructs. + * * Note: the business with inserted_sublink is needed to update hasSubLinks * in subqueries when the replacement adds a subquery inside a subquery. * Messy, isn't it? We do not need to do similar pushups for hasAggs, @@ -861,6 +865,7 @@ typedef struct { int target_varno; int sublevels_up; + RangeTblEntry *target_rte; List *targetlist; int event; int update_varno; @@ -868,6 +873,45 @@ typedef struct } ResolveNew_context; static Node * +resolve_one_var(Var *var, ResolveNew_context *context) +{ + TargetEntry *tle; + + tle = get_tle_by_resno(context->targetlist, var->varattno); + + if (tle == NULL) + { + /* Failed to find column in insert/update tlist */ + if (context->event == CMD_UPDATE) + { + /* For update, just change unmatched var's varno */ + var = (Var *) copyObject(var); + var->varno = context->update_varno; + var->varnoold = context->update_varno; + return (Node *) var; + } + else + { + /* Otherwise replace unmatched var with a null */ + return (Node *) makeNullConst(var->vartype); + } + } + else + { + /* Make a copy of the tlist item to return */ + Node *n = copyObject(tle->expr); + + /* Adjust varlevelsup if tlist item is from higher query */ + if (var->varlevelsup > 0) + IncrementVarSublevelsUp(n, var->varlevelsup, 0); + /* Report it if we are adding a sublink to query */ + if (!context->inserted_sublink) + context->inserted_sublink = checkExprHasSubLink(n); + return n; + } +} + +static Node * ResolveNew_mutator(Node *node, ResolveNew_context *context) { if (node == NULL) @@ -881,45 +925,41 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) if (this_varno == context->target_varno && this_varlevelsup == context->sublevels_up) { - TargetEntry *tle; - - /* band-aid: don't do the wrong thing with a whole-tuple Var */ if (var->varattno == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot handle whole-row reference"))); - - tle = get_tle_by_resno(context->targetlist, var->varattno); - - if (tle == NULL) { - if (context->event == CMD_UPDATE) + /* Must expand whole-tuple reference into RowExpr */ + RangeTblEntry *rte = context->target_rte; + RowExpr *rowexpr; + List *fields = NIL; + AttrNumber nfields = length(rte->eref->colnames); + AttrNumber nf; + + for (nf = 1; nf <= nfields; nf++) { - /* For update, just change unmatched var's varno */ - var = (Var *) copyObject(node); - var->varno = context->update_varno; - var->varnoold = context->update_varno; - return (Node *) var; + Oid vartype; + int32 vartypmod; + Var *newvar; + + get_rte_attribute_type(rte, nf, &vartype, &vartypmod); + newvar = makeVar(this_varno, + nf, + vartype, + vartypmod, + this_varlevelsup); + fields = lappend(fields, + resolve_one_var(newvar, context)); } - else - { - /* Otherwise replace unmatched var with a null */ - return (Node *) makeNullConst(var->vartype); - } - } - else - { - /* Make a copy of the tlist item to return */ - Node *n = copyObject(tle->expr); - - /* Adjust varlevelsup if tlist item is from higher query */ - if (this_varlevelsup > 0) - IncrementVarSublevelsUp(n, this_varlevelsup, 0); - /* Report it if we are adding a sublink to query */ - if (!context->inserted_sublink) - context->inserted_sublink = checkExprHasSubLink(n); - return n; + + rowexpr = makeNode(RowExpr); + rowexpr->args = fields; + rowexpr->row_typeid = var->vartype; + rowexpr->row_format = COERCE_IMPLICIT_CAST; + + return (Node *) rowexpr; } + + /* Normal case for scalar variable */ + return resolve_one_var(var, context); } /* otherwise fall through to copy the var normally */ } @@ -948,12 +988,14 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) Node * ResolveNew(Node *node, int target_varno, int sublevels_up, + RangeTblEntry *target_rte, List *targetlist, int event, int update_varno) { ResolveNew_context context; context.target_varno = target_varno; context.sublevels_up = sublevels_up; + context.target_rte = target_rte; context.targetlist = targetlist; context.event = event; context.update_varno = update_varno; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 9cb90444a87..95e457c261f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.165 2004/05/07 03:19:44 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.166 2004/05/10 22:44:46 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -2432,6 +2432,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_ArrayRef: case T_ArrayExpr: + case T_RowExpr: case T_CoalesceExpr: case T_NullIfExpr: case T_Aggref: @@ -2528,6 +2529,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_BoolExpr: /* lower precedence */ case T_ArrayRef: /* other separators */ case T_ArrayExpr: /* other separators */ + case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ @@ -2574,6 +2576,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) } case T_ArrayRef: /* other separators */ case T_ArrayExpr: /* other separators */ + case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ case T_NullIfExpr: /* other separators */ case T_Aggref: /* own parentheses */ @@ -2942,11 +2945,9 @@ get_rule_expr(Node *node, deparse_context *context, * arg.fieldname, but most cases where FieldSelect is used * are *not* simple. So, always use parenthesized syntax. */ - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); + appendStringInfoChar(buf, '('); get_rule_expr_paren((Node *) fselect->arg, context, true, node); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); + appendStringInfoChar(buf, ')'); appendStringInfo(buf, ".%s", quote_identifier(fieldname)); } break; @@ -3051,6 +3052,33 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_RowExpr: + { + RowExpr *rowexpr = (RowExpr *) node; + List *arg; + char *sep; + + /* + * SQL99 allows "ROW" to be omitted when length(args) > 1, + * but for simplicity we always print it. + */ + appendStringInfo(buf, "ROW("); + sep = ""; + foreach(arg, rowexpr->args) + { + Node *e = (Node *) lfirst(arg); + + appendStringInfo(buf, sep); + get_rule_expr(e, context, true); + sep = ", "; + } + appendStringInfo(buf, ")"); + if (rowexpr->row_format == COERCE_EXPLICIT_CAST) + appendStringInfo(buf, "::%s", + format_type_with_typemod(rowexpr->row_typeid, -1)); + } + break; + case T_CoalesceExpr: { CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 46a8a2d06f3..0e957db82ca 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.228 2004/05/08 21:21:18 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.229 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200405081 +#define CATALOG_VERSION_NO 200405101 #endif diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index a3088ca7f62..52873e86469 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.109 2004/04/01 21:28:46 tgl Exp $ + * $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.110 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -175,6 +175,7 @@ extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); +extern TupleDesc ExecTypeFromExprList(List *exprList); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); typedef struct TupOutputState diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 8a0fbf7be0f..325bf876800 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.115 2004/04/01 21:28:46 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.116 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -614,6 +614,17 @@ typedef struct ArrayExprState } ArrayExprState; /* ---------------- + * RowExprState node + * ---------------- + */ +typedef struct RowExprState +{ + ExprState xprstate; + List *args; /* the arguments */ + TupleDesc tupdesc; /* descriptor for result tuples */ +} RowExprState; + +/* ---------------- * CoalesceExprState node * ---------------- */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 7562e1a82b6..085e76d03bf 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.48 2003/11/29 22:41:06 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/nodes/makefuncs.h,v 1.49 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +45,8 @@ extern Const *makeConst(Oid consttype, extern Const *makeNullConst(Oid consttype); +extern Node *makeBoolConst(bool value, bool isnull); + extern Expr *makeBoolExpr(BoolExprType boolop, List *args); extern Alias *makeAlias(const char *aliasname, List *colnames); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index a776607ec66..20c1b08f5c5 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.153 2004/05/05 04:48:47 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.154 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -115,6 +115,7 @@ typedef enum NodeTag T_CaseWhen, T_CaseTestExpr, T_ArrayExpr, + T_RowExpr, T_CoalesceExpr, T_NullIfExpr, T_NullTest, @@ -145,6 +146,7 @@ typedef enum NodeTag T_CaseExprState, T_CaseWhenState, T_ArrayExprState, + T_RowExprState, T_CoalesceExprState, T_CoerceToDomainState, T_DomainConstraintState, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 567310fa1c3..d5819eab7bb 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.97 2004/04/01 21:28:46 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.98 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -633,6 +633,23 @@ typedef struct ArrayExpr } ArrayExpr; /* + * RowExpr - a ROW() expression + */ +typedef struct RowExpr +{ + Expr xpr; + List *args; /* the fields */ + Oid row_typeid; /* RECORDOID or a composite type's ID */ + /* + * Note: we deliberately do NOT store a typmod. Although a typmod + * will be associated with specific RECORD types at runtime, it will + * differ for different backends, and so cannot safely be stored in + * stored parsetrees. We must assume typmod -1 for a RowExpr node. + */ + CoercionForm row_format; /* how to display this node */ +} RowExpr; + +/* * CoalesceExpr - a COALESCE expression */ typedef struct CoalesceExpr diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h index 087489a0a75..0f0920d0639 100644 --- a/src/include/optimizer/var.h +++ b/src/include/optimizer/var.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.29 2003/11/29 22:41:07 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.30 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,6 @@ extern Relids pull_varnos(Node *node); extern bool contain_var_reference(Node *node, int varno, int varattno, int levelsup); -extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup); extern bool contain_var_clause(Node *node); extern bool contain_vars_of_level(Node *node, int levelsup); extern bool contain_vars_above_level(Node *node, int levelsup); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index d2c694bb4b2..ce4702de35b 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.34 2003/11/29 22:41:11 pgsql Exp $ + * $PostgreSQL: pgsql/src/include/rewrite/rewriteManip.h,v 1.35 2004/05/10 22:44:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ extern bool checkExprHasAggs(Node *node); extern bool checkExprHasSubLink(Node *node); extern Node *ResolveNew(Node *node, int target_varno, int sublevels_up, - List *targetlist, int event, int update_varno); + RangeTblEntry *target_rte, + List *targetlist, int event, int update_varno); #endif /* REWRITEMANIP_H */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index b71a71bbaac..3a90b464851 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.99 2004/04/01 21:28:46 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.100 2004/05/10 22:44:49 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -3752,6 +3752,16 @@ exec_simple_check_node(Node *node) return TRUE; } + case T_RowExpr: + { + RowExpr *expr = (RowExpr *) node; + + if (!exec_simple_check_node((Node *) expr->args)) + return FALSE; + + return TRUE; + } + case T_CoalesceExpr: { CoalesceExpr *expr = (CoalesceExpr *) node; diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source index ebf13626c7f..cfaaea95131 100644 --- a/src/test/regress/input/misc.source +++ b/src/test/regress/input/misc.source @@ -219,6 +219,17 @@ SELECT hobbies_by_name('basketball'); SELECT name, overpaid(emp.*) FROM emp; -- +-- Try a few cases with SQL-spec row constructor expressions +-- +SELECT * FROM equipment(ROW('skywalking', 'mer')); + +SELECT name(equipment(ROW('skywalking', 'mer'))); + +SELECT *, name(equipment(h.*)) FROM hobbies_r h; + +SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h; + +-- -- check that old-style C functions work properly with TOASTed values -- create table oldstyle_test(i int4, t text); diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index 52ecabbd908..2237e0c5966 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -45,9 +45,9 @@ SELECT '' AS four, * FROM DEFAULTEXPR_TBL; -- syntax errors -- test for extraneous comma CREATE TABLE error_tbl (i int DEFAULT (100, )); -ERROR: syntax error at or near "," at character 43 +ERROR: syntax error at or near ")" at character 45 LINE 1: CREATE TABLE error_tbl (i int DEFAULT (100, )); - ^ + ^ -- this will fail because gram.y uses b_expr not a_expr for defaults, -- to avoid a shift/reduce conflict that arises from NOT NULL being -- part of the column definition syntax: diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source index 3173f718c6c..0c1ed5deaf4 100644 --- a/src/test/regress/output/misc.source +++ b/src/test/regress/output/misc.source @@ -687,6 +687,45 @@ SELECT name, overpaid(emp.*) FROM emp; (6 rows) -- +-- Try a few cases with SQL-spec row constructor expressions +-- +SELECT * FROM equipment(ROW('skywalking', 'mer')); + name | hobby +------+------------ + guts | skywalking +(1 row) + +SELECT name(equipment(ROW('skywalking', 'mer'))); + name +------ + guts +(1 row) + +SELECT *, name(equipment(h.*)) FROM hobbies_r h; + name | person | name +-------------+--------+--------------- + posthacking | mike | advil + posthacking | mike | peet's coffee + posthacking | jeff | advil + posthacking | jeff | peet's coffee + basketball | joe | hightops + basketball | sally | hightops + skywalking | | guts +(7 rows) + +SELECT *, (equipment(CAST((h.*) AS hobbies_r))).name FROM hobbies_r h; + name | person | name +-------------+--------+--------------- + posthacking | mike | advil + posthacking | mike | peet's coffee + posthacking | jeff | advil + posthacking | jeff | peet's coffee + basketball | joe | hightops + basketball | sally | hightops + skywalking | | guts +(7 rows) + +-- -- check that old-style C functions work properly with TOASTed values -- create table oldstyle_test(i int4, t text); |