the column alias names of the RTE referenced by the Var to the RowExpr.
This is needed to allow ruleutils.c to correctly deparse FieldSelect nodes
referencing such a construct. Per my recent bug report.
Adding a field to RowExpr forces initdb (because of stored rules changes)
so this solution is not back-patchable; which is unfortunate because 8.2
and 8.3 have this issue. But it only affects EXPLAIN for some pretty odd
corner cases, so we can probably live without a solution for the back
branches.
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.406 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.407 2008/10/06 17:39:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_NODE_FIELD(args);
COPY_SCALAR_FIELD(row_typeid);
COPY_SCALAR_FIELD(row_format);
+ COPY_NODE_FIELD(colnames);
COPY_LOCATION_FIELD(location);
return newnode;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.332 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.333 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
b->row_format != COERCE_DONTCARE)
return false;
+ COMPARE_NODE_FIELD(colnames);
COMPARE_LOCATION_FIELD(location);
return true;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.33 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.34 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
case T_ArrayExpr:
return walker(((ArrayExpr *) node)->elements, context);
case T_RowExpr:
+ /* Assume colnames isn't interesting */
return walker(((RowExpr *) node)->args, context);
case T_RowCompareExpr:
{
FLATCOPY(newnode, rowexpr, RowExpr);
MUTATE(newnode->args, rowexpr->args, List *);
+ /* Assume colnames needn't be duplicated */
return (Node *) newnode;
}
break;
}
break;
case T_RowExpr:
+ /* Assume colnames isn't interesting */
return walker(((RowExpr *) node)->args, context);
case T_CoalesceExpr:
return walker(((CoalesceExpr *) node)->args, context);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.340 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.341 2008/10/06 17:39:26 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_FIELD(args);
WRITE_OID_FIELD(row_typeid);
WRITE_ENUM_FIELD(row_format, CoercionForm);
+ WRITE_NODE_FIELD(colnames);
WRITE_LOCATION_FIELD(location);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.215 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.216 2008/10/06 17:39:26 tgl Exp $
*
* NOTES
* Path and Plan nodes do not have any readfuncs support, because we
READ_NODE_FIELD(args);
READ_OID_FIELD(row_typeid);
READ_ENUM_FIELD(row_format, CoercionForm);
+ READ_NODE_FIELD(colnames);
READ_LOCATION_FIELD(location);
READ_DONE();
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.156 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.157 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
rowexpr->row_format = COERCE_IMPLICIT_CAST;
+ rowexpr->colnames = NIL;
rowexpr->location = -1;
return (Node *) rowexpr;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.79 2008/09/01 20:42:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.80 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
rowexpr->row_format = COERCE_IMPLICIT_CAST;
+ rowexpr->colnames = NIL;
rowexpr->location = -1;
return (Node *) rowexpr;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.167 2008/09/10 18:29:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.168 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
rowexpr->args = newargs;
rowexpr->row_typeid = targetTypeId;
rowexpr->row_format = cformat;
+ rowexpr->colnames = NIL; /* not needed for named target type */
rowexpr->location = location;
return (Node *) rowexpr;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.234 2008/09/01 20:42:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.235 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Barring later casting, we consider the type RECORD */
newr->row_typeid = RECORDOID;
newr->row_format = COERCE_IMPLICIT_CAST;
+ newr->colnames = NIL; /* ROW() has anonymous columns */
newr->location = r->location;
return (Node *) newr;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.114 2008/10/04 21:56:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.115 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
/* Must expand whole-tuple reference into RowExpr */
RowExpr *rowexpr;
+ List *colnames;
List *fields;
/*
* If generating an expansion for a var of a named rowtype
* (ie, this is a plain relation RTE), then we must include
* dummy items for dropped columns. If the var is RECORD (ie,
- * this is a JOIN), then omit dropped columns.
+ * this is a JOIN), then omit dropped columns. Either way,
+ * attach column names to the RowExpr for use of ruleutils.c.
*/
expandRTE(context->target_rte,
this_varno, this_varlevelsup, var->location,
(var->vartype != RECORDOID),
- NULL, &fields);
+ &colnames, &fields);
/* Adjust the generated per-field Vars... */
fields = (List *) ResolveNew_mutator((Node *) fields,
context);
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
rowexpr->row_format = COERCE_IMPLICIT_CAST;
+ rowexpr->colnames = colnames;
rowexpr->location = -1;
return (Node *) rowexpr;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.285 2008/10/04 21:56:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.286 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Node *expr;
/*
+ * If it's a RowExpr that was expanded from a whole-row Var, use the
+ * column names attached to it.
+ */
+ if (IsA(var, RowExpr))
+ {
+ RowExpr *r = (RowExpr *) var;
+
+ if (fieldno > 0 && fieldno <= list_length(r->colnames))
+ return strVal(list_nth(r->colnames, fieldno - 1));
+ }
+
+ /*
* If it's a Var of type RECORD, we have to find what the Var refers to;
* if not, we can use get_expr_result_type. If that fails, we try
* lookup_rowtype_tupdesc, which will probably fail too, but will ereport
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.495 2008/10/06 14:13:17 heikki Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.496 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200810062
+#define CATALOG_VERSION_NO 200810063
#endif
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.142 2008/10/04 21:56:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.143 2008/10/06 17:39:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* not RECORD types, since those are built from the RowExpr itself rather
* than vice versa.) It is important not to assume that length(args) is
* the same as the number of columns logically present in the rowtype.
+ *
+ * colnames is NIL in a RowExpr built from an ordinary ROW() expression.
+ * It is provided in cases where we expand a whole-row Var into a RowExpr,
+ * to retain the column alias names of the RTE that the Var referenced
+ * (which would otherwise be very difficult to extract from the parsetree).
+ * Like the args list, it is one-for-one with physical fields of the rowtype.
*/
typedef struct RowExpr
{
* parsetrees. We must assume typmod -1 for a RowExpr node.
*/
CoercionForm row_format; /* how to display this node */
+ List *colnames; /* list of String, or NIL */
int location; /* token location, or -1 if unknown */
} RowExpr;