-- records
select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d);
- hstore
-------------------------------------------------
- "f1"=>"1", "f2"=>"foo", "f3"=>"1.2", "f4"=>"3"
+ hstore
+--------------------------------------------
+ "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3"
(1 row)
create domain hstestdom1 as integer not null default 0;
if (rowexpr->row_typeid == RECORDOID)
{
/* generic record, use runtime type assignment */
- rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
+ rstate->tupdesc = ExecTypeFromExprList(rowexpr->args,
+ rowexpr->colnames);
BlessTupleDesc(rstate->tupdesc);
/* we won't need to redo this at runtime */
}
/*
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
*
- * Here we must make up an arbitrary set of field names.
+ * Caller must also supply a list of field names (String nodes).
*/
TupleDesc
-ExecTypeFromExprList(List *exprList)
+ExecTypeFromExprList(List *exprList, List *namesList)
{
TupleDesc typeInfo;
- ListCell *l;
+ ListCell *le;
+ ListCell *ln;
int cur_resno = 1;
- char fldname[NAMEDATALEN];
+
+ Assert(list_length(exprList) == list_length(namesList));
typeInfo = CreateTemplateTupleDesc(list_length(exprList), false);
- foreach(l, exprList)
+ forboth(le, exprList, ln, namesList)
{
- Node *e = lfirst(l);
-
- sprintf(fldname, "f%d", cur_resno);
+ Node *e = lfirst(le);
+ char *n = strVal(lfirst(ln));
TupleDescInitEntry(typeInfo,
cur_resno,
- fldname,
+ n,
exprType(e),
exprTypmod(e),
0);
#include "executor/executor.h"
#include "executor/nodeValuesscan.h"
+#include "parser/parsetree.h"
static TupleTableSlot *ValuesNext(ValuesScanState *node);
ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
{
ValuesScanState *scanstate;
+ RangeTblEntry *rte = rt_fetch(node->scan.scanrelid,
+ estate->es_range_table);
TupleDesc tupdesc;
ListCell *vtl;
int i;
/*
* get info about values list
*/
- tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists));
+ tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists),
+ rte->eref->colnames);
ExecAssignScanType(&scanstate->ss, tupdesc);
* reconstitute the RestrictInfo layer.
*/
childquals = get_all_actual_clauses(rel->baserestrictinfo);
- childquals = (List *) adjust_appendrel_attrs((Node *) childquals,
+ childquals = (List *) adjust_appendrel_attrs(root,
+ (Node *) childquals,
appinfo);
childqual = eval_const_expressions(root, (Node *)
make_ands_explicit(childquals));
* while constructing attr_widths estimates below, though.
*/
childrel->joininfo = (List *)
- adjust_appendrel_attrs((Node *) rel->joininfo,
+ adjust_appendrel_attrs(root,
+ (Node *) rel->joininfo,
appinfo);
childrel->reltargetlist = (List *)
- adjust_appendrel_attrs((Node *) rel->reltargetlist,
+ adjust_appendrel_attrs(root,
+ (Node *) rel->reltargetlist,
appinfo);
/*
Expr *child_expr;
child_expr = (Expr *)
- adjust_appendrel_attrs((Node *) cur_em->em_expr,
+ adjust_appendrel_attrs(root,
+ (Node *) cur_em->em_expr,
appinfo);
(void) add_eq_member(cur_ec, child_expr, child_rel->relids,
true, cur_em->em_datatype);
* then fool around with subquery RTEs.
*/
subroot.parse = (Query *)
- adjust_appendrel_attrs((Node *) parse,
+ adjust_appendrel_attrs(root,
+ (Node *) parse,
appinfo);
/*
#include "utils/selfuncs.h"
+typedef struct
+{
+ PlannerInfo *root;
+ AppendRelInfo *appinfo;
+} adjust_appendrel_attrs_context;
+
static Plan *recurse_set_operations(Node *setOp, PlannerInfo *root,
double tuple_fraction,
List *colTypes, List *colCollations,
static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
List *translated_vars);
static Node *adjust_appendrel_attrs_mutator(Node *node,
- AppendRelInfo *context);
+ adjust_appendrel_attrs_context *context);
static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
static List *adjust_inherited_tlist(List *tlist,
AppendRelInfo *context);
* maybe we should try to fold the two routines together.
*/
Node *
-adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo)
+adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo)
{
Node *result;
+ adjust_appendrel_attrs_context context;
+
+ context.root = root;
+ context.appinfo = appinfo;
/*
* Must be prepared to start with a Query or a bare expression tree.
newnode = query_tree_mutator((Query *) node,
adjust_appendrel_attrs_mutator,
- (void *) appinfo,
+ (void *) &context,
QTW_IGNORE_RC_SUBQUERIES);
if (newnode->resultRelation == appinfo->parent_relid)
{
result = (Node *) newnode;
}
else
- result = adjust_appendrel_attrs_mutator(node, appinfo);
+ result = adjust_appendrel_attrs_mutator(node, &context);
return result;
}
static Node *
-adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
+adjust_appendrel_attrs_mutator(Node *node,
+ adjust_appendrel_attrs_context *context)
{
+ AppendRelInfo *appinfo = context->appinfo;
+
if (node == NULL)
return NULL;
if (IsA(node, Var))
Var *var = (Var *) copyObject(node);
if (var->varlevelsup == 0 &&
- var->varno == context->parent_relid)
+ var->varno == appinfo->parent_relid)
{
- var->varno = context->child_relid;
- var->varnoold = context->child_relid;
+ var->varno = appinfo->child_relid;
+ var->varnoold = appinfo->child_relid;
if (var->varattno > 0)
{
Node *newnode;
- if (var->varattno > list_length(context->translated_vars))
+ if (var->varattno > list_length(appinfo->translated_vars))
elog(ERROR, "attribute %d of relation \"%s\" does not exist",
- var->varattno, get_rel_name(context->parent_reloid));
- newnode = copyObject(list_nth(context->translated_vars,
+ var->varattno, get_rel_name(appinfo->parent_reloid));
+ newnode = copyObject(list_nth(appinfo->translated_vars,
var->varattno - 1));
if (newnode == NULL)
elog(ERROR, "attribute %d of relation \"%s\" does not exist",
- var->varattno, get_rel_name(context->parent_reloid));
+ var->varattno, get_rel_name(appinfo->parent_reloid));
return newnode;
}
else if (var->varattno == 0)
* step to convert the tuple layout to the parent's rowtype.
* Otherwise we have to generate a RowExpr.
*/
- if (OidIsValid(context->child_reltype))
+ if (OidIsValid(appinfo->child_reltype))
{
- Assert(var->vartype == context->parent_reltype);
- if (context->parent_reltype != context->child_reltype)
+ Assert(var->vartype == appinfo->parent_reltype);
+ if (appinfo->parent_reltype != appinfo->child_reltype)
{
ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
r->arg = (Expr *) var;
- r->resulttype = context->parent_reltype;
+ r->resulttype = appinfo->parent_reltype;
r->convertformat = COERCE_IMPLICIT_CAST;
r->location = -1;
/* Make sure the Var node has the right type ID, too */
- var->vartype = context->child_reltype;
+ var->vartype = appinfo->child_reltype;
return (Node *) r;
}
}
{
/*
* Build a RowExpr containing the translated variables.
+ *
+ * In practice var->vartype will always be RECORDOID here,
+ * so we need to come up with some suitable column names.
+ * We use the parent RTE's column names.
+ *
+ * Note: we can't get here for inheritance cases, so there
+ * is no need to worry that translated_vars might contain
+ * some dummy NULLs.
*/
RowExpr *rowexpr;
List *fields;
+ RangeTblEntry *rte;
- fields = (List *) copyObject(context->translated_vars);
+ rte = rt_fetch(appinfo->parent_relid,
+ context->root->parse->rtable);
+ fields = (List *) copyObject(appinfo->translated_vars);
rowexpr = makeNode(RowExpr);
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
rowexpr->row_format = COERCE_IMPLICIT_CAST;
- rowexpr->colnames = NIL;
+ rowexpr->colnames = copyObject(rte->eref->colnames);
rowexpr->location = -1;
return (Node *) rowexpr;
{
CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
- if (cexpr->cvarno == context->parent_relid)
- cexpr->cvarno = context->child_relid;
+ if (cexpr->cvarno == appinfo->parent_relid)
+ cexpr->cvarno = appinfo->child_relid;
return (Node *) cexpr;
}
if (IsA(node, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
- if (rtr->rtindex == context->parent_relid)
- rtr->rtindex = context->child_relid;
+ if (rtr->rtindex == appinfo->parent_relid)
+ rtr->rtindex = appinfo->child_relid;
return (Node *) rtr;
}
if (IsA(node, JoinExpr))
adjust_appendrel_attrs_mutator,
(void *) context);
/* now fix JoinExpr's rtindex (probably never happens) */
- if (j->rtindex == context->parent_relid)
- j->rtindex = context->child_relid;
+ if (j->rtindex == appinfo->parent_relid)
+ j->rtindex = appinfo->child_relid;
return (Node *) j;
}
if (IsA(node, PlaceHolderVar))
/* now fix PlaceHolderVar's relid sets */
if (phv->phlevelsup == 0)
phv->phrels = adjust_relid_set(phv->phrels,
- context->parent_relid,
- context->child_relid);
+ appinfo->parent_relid,
+ appinfo->child_relid);
return (Node *) phv;
}
/* Shouldn't need to handle planner auxiliary nodes here */
/* adjust relid sets too */
newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
- context->parent_relid,
- context->child_relid);
+ appinfo->parent_relid,
+ appinfo->child_relid);
newinfo->required_relids = adjust_relid_set(oldinfo->required_relids,
- context->parent_relid,
- context->child_relid);
+ appinfo->parent_relid,
+ appinfo->child_relid);
newinfo->nullable_relids = adjust_relid_set(oldinfo->nullable_relids,
- context->parent_relid,
- context->child_relid);
+ appinfo->parent_relid,
+ appinfo->child_relid);
newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
- context->parent_relid,
- context->child_relid);
+ appinfo->parent_relid,
+ appinfo->child_relid);
newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
- context->parent_relid,
- context->child_relid);
+ appinfo->parent_relid,
+ appinfo->child_relid);
/*
* Reset cached derivative fields, since these might need to have
/* Must expand whole-row reference */
RowExpr *rowexpr;
List *fields = NIL;
+ List *colnames = NIL;
AttrNumber attnum;
- ListCell *l;
+ ListCell *lv;
+ ListCell *ln;
attnum = 0;
- foreach(l, rte->joinaliasvars)
+ Assert(list_length(rte->joinaliasvars) == list_length(rte->eref->colnames));
+ forboth(lv, rte->joinaliasvars, ln, rte->eref->colnames)
{
- newvar = (Node *) lfirst(l);
+ newvar = (Node *) lfirst(lv);
attnum++;
/* Ignore dropped columns */
if (IsA(newvar, Const))
/* (also takes care of setting inserted_sublink if needed) */
newvar = flatten_join_alias_vars_mutator(newvar, context);
fields = lappend(fields, newvar);
+ /* We need the names of non-dropped columns, too */
+ colnames = lappend(colnames, copyObject((Node *) lfirst(ln)));
}
rowexpr = makeNode(RowExpr);
rowexpr->args = fields;
rowexpr->row_typeid = var->vartype;
rowexpr->row_format = COERCE_IMPLICIT_CAST;
- rowexpr->colnames = NIL;
+ rowexpr->colnames = colnames;
rowexpr->location = var->location;
return (Node *) rowexpr;
RowExpr *r = makeNode(RowExpr);
r->args = $1;
r->row_typeid = InvalidOid; /* not analyzed yet */
+ r->colnames = NIL; /* to be filled in during analysis */
r->location = @1;
$$ = (Node *)r;
}
transformRowExpr(ParseState *pstate, RowExpr *r)
{
RowExpr *newr = makeNode(RowExpr);
+ char fname[16];
+ int fnum;
+ ListCell *lc;
/* Transform the field expressions */
newr->args = transformExpressionList(pstate, r->args);
/* 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 */
+
+ /* ROW() has anonymous columns, so invent some field names */
+ newr->colnames = NIL;
+ fnum = 1;
+ foreach(lc, newr->args)
+ {
+ snprintf(fname, sizeof(fname), "f%d", fnum++);
+ newr->colnames = lappend(newr->colnames, makeString(pstrdup(fname)));
+ }
+
newr->location = r->location;
return (Node *) newr;
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201202131
+#define CATALOG_VERSION_NO 201202141
#endif
TupleDesc tupType);
extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
-extern TupleDesc ExecTypeFromExprList(List *exprList);
+extern TupleDesc ExecTypeFromExprList(List *exprList, List *namesList);
extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
typedef struct TupOutputState
* 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.
+ * colnames provides field names in cases where the names can't easily be
+ * obtained otherwise. Names *must* be provided if row_typeid is RECORDOID.
+ * If row_typeid identifies a known composite type, colnames can be NIL to
+ * indicate the type's cataloged field names apply. Note that colnames can
+ * be non-NIL even for a composite type, and typically is when the RowExpr
+ * was created by expanding a whole-row Var. This is so that we can 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, colnames is one-for-one with physical fields of the rowtype.
*/
typedef struct RowExpr
{
extern void expand_inherited_tables(PlannerInfo *root);
-extern Node *adjust_appendrel_attrs(Node *node, AppendRelInfo *appinfo);
+extern Node *adjust_appendrel_attrs(PlannerInfo *root, Node *node,
+ AppendRelInfo *appinfo);
#endif /* PREP_H */
(1 row)
SELECT array_to_json(array_agg(q),false) from (select x as b, x * 2 as c from generate_series(1,3) x) q;
- array_to_json
----------------------------------------------------
- [{"f1":1,"f2":2},{"f1":2,"f2":4},{"f1":3,"f2":6}]
+ array_to_json
+---------------------------------------------
+ [{"b":1,"c":2},{"b":2,"c":4},{"b":3,"c":6}]
(1 row)
SELECT array_to_json(array_agg(q),true) from (select x as b, x * 2 as c from generate_series(1,3) x) q;
- array_to_json
--------------------
- [{"f1":1,"f2":2},+
- {"f1":2,"f2":4},+
- {"f1":3,"f2":6}]
+ array_to_json
+-----------------
+ [{"b":1,"c":2},+
+ {"b":2,"c":4},+
+ {"b":3,"c":6}]
(1 row)
SELECT array_to_json(array_agg(q),false)
ROW(y.*,ARRAY[4,5,6])] AS z
FROM generate_series(1,2) x,
generate_series(4,5) y) q;
- array_to_json
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- [{"f1":"a1","f2":4,"f3":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]},{"f1":"a1","f2":5,"f3":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]},{"f1":"a2","f2":4,"f3":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]},{"f1":"a2","f2":5,"f3":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}]
+ array_to_json
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ [{"b":"a1","c":4,"z":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]},{"b":"a1","c":5,"z":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]},{"b":"a2","c":4,"z":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]},{"b":"a2","c":5,"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}]
(1 row)
SELECT array_to_json(array_agg(x),false) from generate_series(5,10) x;
ROW(y.*,ARRAY[4,5,6])] AS z
FROM generate_series(1,2) x,
generate_series(4,5) y) q;
- row_to_json
------------------------------------------------------------------------
- {"f1":"a1","f2":4,"f3":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
- {"f1":"a1","f2":5,"f3":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
- {"f1":"a2","f2":4,"f3":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
- {"f1":"a2","f2":5,"f3":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
+ row_to_json
+--------------------------------------------------------------------
+ {"b":"a1","c":4,"z":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
+ {"b":"a1","c":5,"z":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
+ {"b":"a2","c":4,"z":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
+ {"b":"a2","c":5,"z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
(4 rows)
SELECT row_to_json(q,true)
ROW(y.*,ARRAY[4,5,6])] AS z
FROM generate_series(1,2) x,
generate_series(4,5) y) q;
- row_to_json
-------------------------------------------------------
- {"f1":"a1", +
- "f2":4, +
- "f3":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
- {"f1":"a1", +
- "f2":5, +
- "f3":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
- {"f1":"a2", +
- "f2":4, +
- "f3":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
- {"f1":"a2", +
- "f2":5, +
- "f3":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
+ row_to_json
+-----------------------------------------------------
+ {"b":"a1", +
+ "c":4, +
+ "z":[{"f1":1,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
+ {"b":"a1", +
+ "c":5, +
+ "z":[{"f1":1,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
+ {"b":"a2", +
+ "c":4, +
+ "z":[{"f1":2,"f2":[1,2,3]},{"f1":4,"f2":[4,5,6]}]}
+ {"b":"a2", +
+ "c":5, +
+ "z":[{"f1":2,"f2":[1,2,3]},{"f1":5,"f2":[4,5,6]}]}
(4 rows)
CREATE TEMP TABLE rows AS