aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execQual.c87
-rw-r--r--src/backend/executor/execTuples.c36
-rw-r--r--src/backend/nodes/copyfuncs.c20
-rw-r--r--src/backend/nodes/equalfuncs.c23
-rw-r--r--src/backend/nodes/makefuncs.c14
-rw-r--r--src/backend/nodes/outfuncs.c15
-rw-r--r--src/backend/nodes/readfuncs.c19
-rw-r--r--src/backend/optimizer/path/allpaths.c27
-rw-r--r--src/backend/optimizer/path/clausesel.c9
-rw-r--r--src/backend/optimizer/prep/prepjointree.c50
-rw-r--r--src/backend/optimizer/util/clauses.c109
-rw-r--r--src/backend/optimizer/util/var.c53
-rw-r--r--src/backend/parser/gram.y458
-rw-r--r--src/backend/parser/parse_coerce.c121
-rw-r--r--src/backend/parser/parse_expr.c304
-rw-r--r--src/backend/parser/parse_target.c6
-rw-r--r--src/backend/rewrite/rewriteHandler.c5
-rw-r--r--src/backend/rewrite/rewriteManip.c112
-rw-r--r--src/backend/utils/adt/ruleutils.c38
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/executor/executor.h3
-rw-r--r--src/include/nodes/execnodes.h13
-rw-r--r--src/include/nodes/makefuncs.h4
-rw-r--r--src/include/nodes/nodes.h4
-rw-r--r--src/include/nodes/primnodes.h19
-rw-r--r--src/include/optimizer/var.h3
-rw-r--r--src/include/rewrite/rewriteManip.h5
-rw-r--r--src/pl/plpgsql/src/pl_exec.c12
-rw-r--r--src/test/regress/input/misc.source11
-rw-r--r--src/test/regress/output/constraints.source4
-rw-r--r--src/test/regress/output/misc.source39
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);