summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/nodes/makefuncs.c88
-rw-r--r--src/backend/optimizer/prep/preptlist.c8
-rw-r--r--src/backend/parser/parse_expr.c76
-rw-r--r--src/include/nodes/makefuncs.h4
-rw-r--r--src/test/regress/expected/rowtypes.out38
-rw-r--r--src/test/regress/sql/rowtypes.sql40
6 files changed, 175 insertions, 79 deletions
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 5550f706b18..c9344f724d3 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -17,6 +17,7 @@
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/lsyscache.h"
@@ -91,6 +92,93 @@ makeVar(Index varno,
}
/*
+ * makeWholeRowVar -
+ * creates a Var node representing a whole row of the specified RTE
+ *
+ * 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. This function
+ * encapsulates the logic for determining the correct rowtype OID to use.
+ */
+Var *
+makeWholeRowVar(RangeTblEntry *rte,
+ Index varno,
+ Index varlevelsup)
+{
+ Var *result;
+ Oid toid;
+
+ switch (rte->rtekind)
+ {
+ case RTE_RELATION:
+ /* relation: the rowtype is a named composite type */
+ toid = get_rel_type_id(rte->relid);
+ if (!OidIsValid(toid))
+ elog(ERROR, "could not find type OID for relation %u",
+ rte->relid);
+ result = makeVar(varno,
+ InvalidAttrNumber,
+ toid,
+ -1,
+ varlevelsup);
+ break;
+ case RTE_FUNCTION:
+ toid = exprType(rte->funcexpr);
+ if (type_is_rowtype(toid))
+ {
+ /* func returns composite; same as relation case */
+ result = makeVar(varno,
+ InvalidAttrNumber,
+ toid,
+ -1,
+ varlevelsup);
+ }
+ else
+ {
+ /*
+ * func returns scalar; instead of making a whole-row Var,
+ * just reference the function's scalar output. (XXX this
+ * seems a tad inconsistent, especially if "f.*" was
+ * explicitly written ...)
+ */
+ result = makeVar(varno,
+ 1,
+ toid,
+ -1,
+ varlevelsup);
+ }
+ break;
+ case RTE_VALUES:
+ toid = RECORDOID;
+ /* returns composite; same as relation case */
+ result = makeVar(varno,
+ InvalidAttrNumber,
+ toid,
+ -1,
+ varlevelsup);
+ break;
+ default:
+
+ /*
+ * 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 = makeVar(varno,
+ InvalidAttrNumber,
+ RECORDOID,
+ -1,
+ varlevelsup);
+ break;
+ }
+
+ return result;
+}
+
+/*
* makeTargetEntry -
* creates a TargetEntry node
*/
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index abbf42cb625..2ea01a7f08d 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -170,11 +170,9 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
else
{
/* Not a table, so we need the whole row as a junk var */
- var = makeVar(rc->rti,
- InvalidAttrNumber,
- RECORDOID,
- -1,
- 0);
+ var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
+ rc->rti,
+ 0);
snprintf(resname, sizeof(resname), "wholerow%u", rc->rti);
tle = makeTargetEntry((Expr *) var,
list_length(tlist) + 1,
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index f839a965a77..a0cfad00c2b 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -2015,12 +2015,6 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr)
/*
* Construct a whole-row reference to represent the notation "relation.*".
- *
- * 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.
*/
static Node *
transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
@@ -2028,80 +2022,14 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
Var *result;
int vnum;
int sublevels_up;
- Oid toid;
/* Find the RTE's rangetable location */
-
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
/* Build the appropriate referencing node */
+ result = makeWholeRowVar(rte, vnum, sublevels_up);
- switch (rte->rtekind)
- {
- case RTE_RELATION:
- /* relation: the rowtype is a named composite type */
- toid = get_rel_type_id(rte->relid);
- if (!OidIsValid(toid))
- elog(ERROR, "could not find type OID for relation %u",
- rte->relid);
- result = makeVar(vnum,
- InvalidAttrNumber,
- toid,
- -1,
- sublevels_up);
- break;
- case RTE_FUNCTION:
- toid = exprType(rte->funcexpr);
- if (type_is_rowtype(toid))
- {
- /* func returns composite; same as relation case */
- result = makeVar(vnum,
- InvalidAttrNumber,
- toid,
- -1,
- sublevels_up);
- }
- else
- {
- /*
- * func returns scalar; instead of making a whole-row Var,
- * just reference the function's scalar output. (XXX this
- * seems a tad inconsistent, especially if "f.*" was
- * explicitly written ...)
- */
- result = makeVar(vnum,
- 1,
- toid,
- -1,
- sublevels_up);
- }
- break;
- case RTE_VALUES:
- toid = RECORDOID;
- /* returns composite; same as relation case */
- result = makeVar(vnum,
- InvalidAttrNumber,
- toid,
- -1,
- sublevels_up);
- break;
- default:
-
- /*
- * 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 = makeVar(vnum,
- InvalidAttrNumber,
- RECORDOID,
- -1,
- sublevels_up);
- break;
- }
-
- /* location is not filled in by makeVar */
+ /* location is not filled in by makeWholeRowVar */
result->location = location;
/* mark relation as requiring whole-row SELECT access */
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index c4e7385c48d..6d009a2bbab 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -29,6 +29,10 @@ extern Var *makeVar(Index varno,
int32 vartypmod,
Index varlevelsup);
+extern Var *makeWholeRowVar(RangeTblEntry *rte,
+ Index varno,
+ Index varlevelsup);
+
extern TargetEntry *makeTargetEntry(Expr *expr,
AttrNumber resno,
char *resname,
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index ee392909302..a21f7b8c06b 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -286,3 +286,41 @@ select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
f
(1 row)
+--
+-- Test case derived from bug #5716: check multiple uses of a rowtype result
+--
+BEGIN;
+CREATE TABLE price (
+ id SERIAL PRIMARY KEY,
+ active BOOLEAN NOT NULL,
+ price NUMERIC
+);
+NOTICE: CREATE TABLE will create implicit sequence "price_id_seq" for serial column "price.id"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "price_pkey" for table "price"
+CREATE TYPE price_input AS (
+ id INTEGER,
+ price NUMERIC
+);
+CREATE TYPE price_key AS (
+ id INTEGER
+);
+CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$
+ SELECT $1.id
+$$ LANGUAGE SQL;
+CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$
+ SELECT $1.id
+$$ LANGUAGE SQL;
+insert into price values (1,false,42), (10,false,100), (11,true,17.99);
+UPDATE price
+ SET active = true, price = input_prices.price
+ FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices
+ WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*);
+select * from price;
+ id | active | price
+----+--------+--------
+ 1 | f | 42
+ 10 | t | 123.00
+ 11 | t | 99.99
+(3 rows)
+
+rollback;
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 51c66f0d04f..e5a77f79f65 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -117,3 +117,43 @@ select array[ row(1,2), row(3,4), row(5,6) ];
-- Check ability to compare an anonymous row to elements of an array
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]);
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
+
+--
+-- Test case derived from bug #5716: check multiple uses of a rowtype result
+--
+
+BEGIN;
+
+CREATE TABLE price (
+ id SERIAL PRIMARY KEY,
+ active BOOLEAN NOT NULL,
+ price NUMERIC
+);
+
+CREATE TYPE price_input AS (
+ id INTEGER,
+ price NUMERIC
+);
+
+CREATE TYPE price_key AS (
+ id INTEGER
+);
+
+CREATE FUNCTION price_key_from_table(price) RETURNS price_key AS $$
+ SELECT $1.id
+$$ LANGUAGE SQL;
+
+CREATE FUNCTION price_key_from_input(price_input) RETURNS price_key AS $$
+ SELECT $1.id
+$$ LANGUAGE SQL;
+
+insert into price values (1,false,42), (10,false,100), (11,true,17.99);
+
+UPDATE price
+ SET active = true, price = input_prices.price
+ FROM unnest(ARRAY[(10, 123.00), (11, 99.99)]::price_input[]) input_prices
+ WHERE price_key_from_table(price.*) = price_key_from_input(input_prices.*);
+
+select * from price;
+
+rollback;