I have applied the attached patch to HEAD in order to fix the problems
discussed in this thread:
http://archives.postgresql.org/pgsql-hackers/2004-12/msg00187.php
regards, tom lane
Index: src/backend/executor/execQual.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/executor/execQual.c,v
retrieving revision 1.169
diff -c -r1.169 execQual.c
*** src/backend/executor/execQual.c 22 Sep 2004 17:41:50 -0000 1.169
--- src/backend/executor/execQual.c 11 Dec 2004 16:26:02 -0000
***************
*** 87,92 ****
--- 87,95 ----
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
+ ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
***************
*** 428,434 ****
*
* Returns a Datum whose value is the value of a range
* variable with respect to given expression context.
! * ---------------------------------------------------------------- */
static Datum
ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
--- 431,438 ----
*
* Returns a Datum whose value is the value of a range
* variable with respect to given expression context.
! * ----------------------------------------------------------------
! */
static Datum
ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
***************
*** 1844,1849 ****
--- 1848,1922 ----
return BoolGetDatum(!AnyNull);
}
+ /* ----------------------------------------------------------------
+ * ExecEvalConvertRowtype
+ *
+ * Evaluate a rowtype coercion operation. This may require
+ * rearranging field positions.
+ * ----------------------------------------------------------------
+ */
+ static Datum
+ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
+ ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+ {
+ HeapTuple result;
+ Datum tupDatum;
+ HeapTupleHeader tuple;
+ HeapTupleData tmptup;
+ AttrNumber *attrMap = cstate->attrMap;
+ Datum *invalues = cstate->invalues;
+ char *innulls = cstate->innulls;
+ Datum *outvalues = cstate->outvalues;
+ char *outnulls = cstate->outnulls;
+ int i;
+ int outnatts = cstate->outdesc->natts;
+
+ tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
+
+ /* this test covers the isDone exception too: */
+ if (*isNull)
+ return tupDatum;
+
+ tuple = DatumGetHeapTupleHeader(tupDatum);
+
+ Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
+ Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
+
+ /*
+ * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
+ */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ tmptup.t_data = tuple;
+
+ /*
+ * Extract all the values of the old tuple, offsetting the arrays
+ * so that invalues[0] is NULL and invalues[1] is the first
+ * source attribute; this exactly matches the numbering convention
+ * in attrMap.
+ */
+ heap_deformtuple(&tmptup, cstate->indesc, invalues + 1, innulls + 1);
+ invalues[0] = (Datum) 0;
+ innulls[0] = 'n';
+
+ /*
+ * Transpose into proper fields of the new tuple.
+ */
+ for (i = 0; i < outnatts; i++)
+ {
+ int j = attrMap[i];
+
+ outvalues[i] = invalues[j];
+ outnulls[i] = innulls[j];
+ }
+
+ /*
+ * Now form the new tuple.
+ */
+ result = heap_formtuple(cstate->outdesc, outvalues, outnulls);
+
+ return HeapTupleGetDatum(result);
+ }
/* ----------------------------------------------------------------
* ExecEvalCase
***************
*** 2969,2974 ****
--- 3042,3109 ----
state = (ExprState *) gstate;
}
break;
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+ ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
+ int i;
+ int n;
+
+ cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
+ cstate->arg = ExecInitExpr(convert->arg, parent);
+ /* save copies of needed tuple descriptors */
+ cstate->indesc = lookup_rowtype_tupdesc(exprType((Node *) convert->arg), -1);
+ cstate->indesc = CreateTupleDescCopy(cstate->indesc);
+ cstate->outdesc = lookup_rowtype_tupdesc(convert->resulttype, -1);
+ cstate->outdesc = CreateTupleDescCopy(cstate->outdesc);
+ /* prepare map from old to new attribute numbers */
+ n = cstate->outdesc->natts;
+ cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
+ for (i = 0; i < n; i++)
+ {
+ Form_pg_attribute att = cstate->outdesc->attrs[i];
+ char *attname;
+ Oid atttypid;
+ int32 atttypmod;
+ int j;
+
+ if (att->attisdropped)
+ continue; /* attrMap[i] is already 0 */
+ attname = NameStr(att->attname);
+ atttypid = att->atttypid;
+ atttypmod = att->atttypmod;
+ for (j = 0; j < cstate->indesc->natts; j++)
+ {
+ att = cstate->indesc->attrs[j];
+ if (att->attisdropped)
+ continue;
+ if (strcmp(attname, NameStr(att->attname)) == 0)
+ {
+ /* Found it, check type */
+ if (atttypid != att->atttypid || atttypmod != att->atttypmod)
+ elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of
type%s",
+ attname,
+ format_type_be(cstate->indesc->tdtypeid),
+ format_type_be(cstate->outdesc->tdtypeid));
+ cstate->attrMap[i] = (AttrNumber) (j + 1);
+ break;
+ }
+ }
+ if (cstate->attrMap[i] == 0)
+ elog(ERROR, "attribute \"%s\" of type %s does not exist",
+ attname,
+ format_type_be(cstate->indesc->tdtypeid));
+ }
+ /* preallocate workspace for Datum arrays */
+ n = cstate->indesc->natts + 1; /* +1 for NULL */
+ cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
+ cstate->innulls = (char *) palloc(n * sizeof(char));
+ n = cstate->outdesc->natts;
+ cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
+ cstate->outnulls = (char *) palloc(n * sizeof(char));
+ state = (ExprState *) cstate;
+ }
+ break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.293
diff -c -r1.293 copyfuncs.c
*** src/backend/nodes/copyfuncs.c 5 Nov 2004 19:15:59 -0000 1.293
--- src/backend/nodes/copyfuncs.c 11 Dec 2004 16:26:02 -0000
***************
*** 877,882 ****
--- 877,897 ----
}
/*
+ * _copyConvertRowtypeExpr
+ */
+ static ConvertRowtypeExpr *
+ _copyConvertRowtypeExpr(ConvertRowtypeExpr *from)
+ {
+ ConvertRowtypeExpr *newnode = makeNode(ConvertRowtypeExpr);
+
+ COPY_NODE_FIELD(arg);
+ COPY_SCALAR_FIELD(resulttype);
+ COPY_SCALAR_FIELD(convertformat);
+
+ return newnode;
+ }
+
+ /*
* _copyCaseExpr
*/
static CaseExpr *
***************
*** 2696,2701 ****
--- 2711,2719 ----
case T_RelabelType:
retval = _copyRelabelType(from);
break;
+ case T_ConvertRowtypeExpr:
+ retval = _copyConvertRowtypeExpr(from);
+ break;
case T_CaseExpr:
retval = _copyCaseExpr(from);
break;
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.232
diff -c -r1.232 equalfuncs.c
*** src/backend/nodes/equalfuncs.c 5 Nov 2004 19:15:59 -0000 1.232
--- src/backend/nodes/equalfuncs.c 11 Dec 2004 16:26:02 -0000
***************
*** 381,386 ****
--- 381,404 ----
}
static bool
+ _equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b)
+ {
+ COMPARE_NODE_FIELD(arg);
+ COMPARE_SCALAR_FIELD(resulttype);
+
+ /*
+ * Special-case COERCE_DONTCARE, so that planner can build coercion
+ * nodes that are equal() to both explicit and implicit coercions.
+ */
+ if (a->convertformat != b->convertformat &&
+ a->convertformat != COERCE_DONTCARE &&
+ b->convertformat != COERCE_DONTCARE)
+ return false;
+
+ return true;
+ }
+
+ static bool
_equalCaseExpr(CaseExpr *a, CaseExpr *b)
{
COMPARE_SCALAR_FIELD(casetype);
***************
*** 1844,1849 ****
--- 1862,1870 ----
case T_RelabelType:
retval = _equalRelabelType(a, b);
break;
+ case T_ConvertRowtypeExpr:
+ retval = _equalConvertRowtypeExpr(a, b);
+ break;
case T_CaseExpr:
retval = _equalCaseExpr(a, b);
break;
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.243
diff -c -r1.243 outfuncs.c
*** src/backend/nodes/outfuncs.c 29 Aug 2004 05:06:43 -0000 1.243
--- src/backend/nodes/outfuncs.c 11 Dec 2004 16:26:02 -0000
***************
*** 768,773 ****
--- 768,783 ----
}
static void
+ _outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node)
+ {
+ WRITE_NODE_TYPE("CONVERTROWTYPEEXPR");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_ENUM_FIELD(convertformat, CoercionForm);
+ }
+
+ static void
_outCaseExpr(StringInfo str, CaseExpr *node)
{
WRITE_NODE_TYPE("CASE");
***************
*** 1728,1733 ****
--- 1738,1746 ----
case T_RelabelType:
_outRelabelType(str, obj);
break;
+ case T_ConvertRowtypeExpr:
+ _outConvertRowtypeExpr(str, obj);
+ break;
case T_CaseExpr:
_outCaseExpr(str, obj);
break;
Index: src/backend/nodes/readfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v
retrieving revision 1.173
diff -c -r1.173 readfuncs.c
*** src/backend/nodes/readfuncs.c 29 Aug 2004 04:12:33 -0000 1.173
--- src/backend/nodes/readfuncs.c 11 Dec 2004 16:26:02 -0000
***************
*** 576,581 ****
--- 576,596 ----
}
/*
+ * _readConvertRowtypeExpr
+ */
+ static ConvertRowtypeExpr *
+ _readConvertRowtypeExpr(void)
+ {
+ READ_LOCALS(ConvertRowtypeExpr);
+
+ READ_NODE_FIELD(arg);
+ READ_OID_FIELD(resulttype);
+ READ_ENUM_FIELD(convertformat, CoercionForm);
+
+ READ_DONE();
+ }
+
+ /*
* _readCaseExpr
*/
static CaseExpr *
***************
*** 971,976 ****
--- 986,993 ----
return_value = _readFieldStore();
else if (MATCH("RELABELTYPE", 11))
return_value = _readRelabelType();
+ else if (MATCH("CONVERTROWTYPEEXPR", 18))
+ return_value = _readConvertRowtypeExpr();
else if (MATCH("CASE", 4))
return_value = _readCaseExpr();
else if (MATCH("WHEN", 4))
Index: src/backend/optimizer/prep/prepjointree.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/prep/prepjointree.c,v
retrieving revision 1.23
diff -c -r1.23 prepjointree.c
*** src/backend/optimizer/prep/prepjointree.c 29 Aug 2004 05:06:44 -0000 1.23
--- src/backend/optimizer/prep/prepjointree.c 11 Dec 2004 16:26:02 -0000
***************
*** 839,844 ****
--- 839,851 ----
result = find_nonnullable_rels((Node *) expr->arg, top_level);
}
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ /* not clear this is useful, but it can't hurt */
+ ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node;
+
+ result = find_nonnullable_rels((Node *) expr->arg, top_level);
+ }
else if (IsA(node, NullTest))
{
NullTest *expr = (NullTest *) node;
Index: src/backend/optimizer/prep/prepunion.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v
retrieving revision 1.117
diff -c -r1.117 prepunion.c
*** src/backend/optimizer/prep/prepunion.c 2 Oct 2004 22:39:47 -0000 1.117
--- src/backend/optimizer/prep/prepunion.c 11 Dec 2004 16:26:03 -0000
***************
*** 40,45 ****
--- 40,47 ----
{
Index old_rt_index;
Index new_rt_index;
+ Oid old_rel_type;
+ Oid new_rel_type;
TupleDesc old_tupdesc;
TupleDesc new_tupdesc;
char *old_rel_name;
***************
*** 826,831 ****
--- 828,835 ----
context.old_rt_index = old_rt_index;
context.new_rt_index = new_rt_index;
+ context.old_rel_type = oldrelation->rd_rel->reltype;
+ context.new_rel_type = newrelation->rd_rel->reltype;
context.old_tupdesc = RelationGetDescr(oldrelation);
context.new_tupdesc = RelationGetDescr(newrelation);
context.old_rel_name = RelationGetRelationName(oldrelation);
***************
*** 910,959 ****
return 0; /* keep compiler quiet */
}
- /*
- * Translate a whole-row Var to be correct for a child table.
- *
- * In general the child will not have a suitable field layout to be used
- * directly, so we translate the simple whole-row Var into a ROW() construct.
- */
- static Node *
- generate_whole_row(Var *var,
- adjust_inherited_attrs_context *context)
- {
- RowExpr *rowexpr;
- List *fields = NIL;
- int oldnatts = context->old_tupdesc->natts;
- int i;
-
- for (i = 0; i < oldnatts; i++)
- {
- Form_pg_attribute att = context->old_tupdesc->attrs[i];
- Var *newvar;
-
- if (att->attisdropped)
- {
- /*
- * can't use atttypid here, but it doesn't really matter what
- * type the Const claims to be.
- */
- newvar = (Var *) makeNullConst(INT4OID);
- }
- else
- newvar = makeVar(context->new_rt_index,
- translate_inherited_attnum(i + 1, context),
- att->atttypid,
- att->atttypmod,
- 0);
- fields = lappend(fields, newvar);
- }
- rowexpr = makeNode(RowExpr);
- rowexpr->args = fields;
- rowexpr->row_typeid = var->vartype; /* report parent's rowtype */
- rowexpr->row_format = COERCE_IMPLICIT_CAST;
-
- return (Node *) rowexpr;
- }
-
static Node *
adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context)
--- 914,919 ----
***************
*** 977,984 ****
}
else if (var->varattno == 0)
{
! /* expand whole-row reference into a ROW() construct */
! return generate_whole_row(var, context);
}
/* system attributes don't need any translation */
}
--- 937,958 ----
}
else if (var->varattno == 0)
{
! /*
! * Whole-row Var: we need to insert a coercion step to convert
! * the tuple layout to the parent's rowtype.
! */
! if (context->old_rel_type != context->new_rel_type)
! {
! ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
!
! r->arg = (Expr *) var;
! r->resulttype = context->old_rel_type;
! r->convertformat = COERCE_IMPLICIT_CAST;
! /* Make sure the Var node has the right type ID, too */
! Assert(var->vartype == context->old_rel_type);
! var->vartype = context->new_rel_type;
! return (Node *) r;
! }
}
/* system attributes don't need any translation */
}
Index: src/backend/optimizer/util/clauses.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v
retrieving revision 1.184
diff -c -r1.184 clauses.c
*** src/backend/optimizer/util/clauses.c 9 Nov 2004 21:42:53 -0000 1.184
--- src/backend/optimizer/util/clauses.c 11 Dec 2004 16:26:03 -0000
***************
*** 1047,1052 ****
--- 1047,1059 ----
if (r->relabelformat == COERCE_IMPLICIT_CAST)
return strip_implicit_coercions((Node *) r->arg);
}
+ else if (IsA(node, ConvertRowtypeExpr))
+ {
+ ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node;
+
+ if (c->convertformat == COERCE_IMPLICIT_CAST)
+ return strip_implicit_coercions((Node *) c->arg);
+ }
else if (IsA(node, CoerceToDomain))
{
CoerceToDomain *c = (CoerceToDomain *) node;
***************
*** 1082,1092 ****
return false;
if (IsA(node, FuncExpr))
((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,
context);
--- 1089,1101 ----
return false;
if (IsA(node, FuncExpr))
((FuncExpr *) node)->funcformat = COERCE_DONTCARE;
! else if (IsA(node, RelabelType))
((RelabelType *) node)->relabelformat = COERCE_DONTCARE;
! else if (IsA(node, ConvertRowtypeExpr))
! ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE;
! else if (IsA(node, RowExpr))
((RowExpr *) node)->row_format = COERCE_DONTCARE;
! else if (IsA(node, CoerceToDomain))
((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE;
return expression_tree_walker(node, set_coercionform_dontcare_walker,
context);
***************
*** 2647,2652 ****
--- 2656,2663 ----
break;
case T_RelabelType:
return walker(((RelabelType *) node)->arg, context);
+ case T_ConvertRowtypeExpr:
+ return walker(((ConvertRowtypeExpr *) node)->arg, context);
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
***************
*** 3057,3062 ****
--- 3068,3083 ----
return (Node *) newnode;
}
break;
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
+ ConvertRowtypeExpr *newnode;
+
+ FLATCOPY(newnode, convexpr, ConvertRowtypeExpr);
+ MUTATE(newnode->arg, convexpr->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v
retrieving revision 2.124
diff -c -r2.124 parse_coerce.c
*** src/backend/parser/parse_coerce.c 6 Nov 2004 17:46:33 -0000 2.124
--- src/backend/parser/parse_coerce.c 11 Dec 2004 16:26:03 -0000
***************
*** 321,333 ****
if (typeInheritsFrom(inputTypeId, targetTypeId))
{
/*
! * Input class type is a subclass of target, so nothing to do ---
! * except relabel the type. This is binary compatibility for
! * complex types.
*/
! return (Node *) makeRelabelType((Expr *) node,
! targetTypeId, -1,
! cformat);
}
/* If we get here, caller blew it */
elog(ERROR, "failed to find conversion function from %s to %s",
--- 321,336 ----
if (typeInheritsFrom(inputTypeId, targetTypeId))
{
/*
! * Input class type is a subclass of target, so generate an
! * appropriate runtime conversion (removing unneeded columns
! * and possibly rearranging the ones that are wanted).
*/
! ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
!
! r->arg = (Expr *) node;
! r->resulttype = targetTypeId;
! r->convertformat = cformat;
! return (Node *) r;
}
/* If we get here, caller blew it */
elog(ERROR, "failed to find conversion function from %s to %s",
***************
*** 567,572 ****
--- 570,577 ----
((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, RelabelType))
((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST;
+ else if (IsA(node, ConvertRowtypeExpr))
+ ((ConvertRowtypeExpr *) node)->convertformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, RowExpr))
((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST;
else if (IsA(node, CoerceToDomain))
Index: src/backend/parser/parse_expr.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v
retrieving revision 1.176
diff -c -r1.176 parse_expr.c
*** src/backend/parser/parse_expr.c 29 Aug 2004 05:06:44 -0000 1.176
--- src/backend/parser/parse_expr.c 11 Dec 2004 16:26:03 -0000
***************
*** 940,945 ****
--- 940,946 ----
case T_FieldSelect:
case T_FieldStore:
case T_RelabelType:
+ case T_ConvertRowtypeExpr:
case T_CaseTestExpr:
case T_CoerceToDomain:
case T_CoerceToDomainValue:
***************
*** 1406,1411 ****
--- 1407,1415 ----
case T_RelabelType:
type = ((RelabelType *) expr)->resulttype;
break;
+ case T_ConvertRowtypeExpr:
+ type = ((ConvertRowtypeExpr *) expr)->resulttype;
+ break;
case T_CaseExpr:
type = ((CaseExpr *) expr)->casetype;
break;
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.185
diff -c -r1.185 ruleutils.c
*** src/backend/utils/adt/ruleutils.c 5 Nov 2004 19:16:11 -0000 1.185
--- src/backend/utils/adt/ruleutils.c 11 Dec 2004 16:26:03 -0000
***************
*** 2622,2627 ****
--- 2622,2630 ----
case T_RelabelType:
return isSimpleNode((Node *) ((RelabelType *) node)->arg,
node, prettyFlags);
+ case T_ConvertRowtypeExpr:
+ return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
+ node, prettyFlags);
case T_OpExpr:
{
***************
*** 3133,3138 ****
--- 3136,3165 ----
}
break;
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+ Node *arg = (Node *) convert->arg;
+
+ if (convert->convertformat == COERCE_IMPLICIT_CAST &&
+ !showimplicit)
+ {
+ /* don't show the implicit cast */
+ get_rule_expr_paren(arg, context, false, node);
+ }
+ else
+ {
+ if (!PRETTY_PAREN(context))
+ appendStringInfoChar(buf, '(');
+ get_rule_expr_paren(arg, context, false, node);
+ if (!PRETTY_PAREN(context))
+ appendStringInfoChar(buf, ')');
+ appendStringInfo(buf, "::%s",
+ format_type_with_typemod(convert->resulttype, -1));
+ }
+ }
+ break;
+
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
Index: src/include/nodes/execnodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/execnodes.h,v
retrieving revision 1.120
diff -c -r1.120 execnodes.h
*** src/include/nodes/execnodes.h 7 Oct 2004 18:38:51 -0000 1.120
--- src/include/nodes/execnodes.h 11 Dec 2004 16:26:03 -0000
***************
*** 579,584 ****
--- 579,601 ----
} FieldStoreState;
/* ----------------
+ * ConvertRowtypeExprState node
+ * ----------------
+ */
+ typedef struct ConvertRowtypeExprState
+ {
+ ExprState xprstate;
+ ExprState *arg; /* input tuple value */
+ TupleDesc indesc; /* tupdesc for source rowtype */
+ TupleDesc outdesc; /* tupdesc for result rowtype */
+ AttrNumber *attrMap; /* indexes of input fields, or 0 for null */
+ Datum *invalues; /* workspace for deconstructing source */
+ char *innulls;
+ Datum *outvalues; /* workspace for constructing result */
+ char *outnulls;
+ } ConvertRowtypeExprState;
+
+ /* ----------------
* CaseExprState node
* ----------------
*/
Index: src/include/nodes/nodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/nodes.h,v
retrieving revision 1.161
diff -c -r1.161 nodes.h
*** src/include/nodes/nodes.h 14 Sep 2004 03:21:25 -0000 1.161
--- src/include/nodes/nodes.h 11 Dec 2004 16:26:03 -0000
***************
*** 112,117 ****
--- 112,118 ----
T_FieldSelect,
T_FieldStore,
T_RelabelType,
+ T_ConvertRowtypeExpr,
T_CaseExpr,
T_CaseWhen,
T_CaseTestExpr,
***************
*** 145,150 ****
--- 146,152 ----
T_SubPlanState,
T_FieldSelectState,
T_FieldStoreState,
+ T_ConvertRowtypeExprState,
T_CaseExprState,
T_CaseWhenState,
T_ArrayExprState,
Index: src/include/nodes/primnodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/primnodes.h,v
retrieving revision 1.104
diff -c -r1.104 primnodes.h
*** src/include/nodes/primnodes.h 29 Aug 2004 05:06:57 -0000 1.104
--- src/include/nodes/primnodes.h 11 Dec 2004 16:26:03 -0000
***************
*** 591,596 ****
--- 591,617 ----
CoercionForm relabelformat; /* how to display this node */
} RelabelType;
+ /* ----------------
+ * ConvertRowtypeExpr
+ *
+ * ConvertRowtypeExpr represents a type coercion from one composite type
+ * to another, where the source type is guaranteed to contain all the columns
+ * needed for the destination type plus possibly others; the columns need not
+ * be in the same positions, but are matched up by name. This is primarily
+ * used to convert a whole-row value of an inheritance child table into a
+ * valid whole-row value of its parent table's rowtype.
+ * ----------------
+ */
+
+ typedef struct ConvertRowtypeExpr
+ {
+ Expr xpr;
+ Expr *arg; /* input expression */
+ Oid resulttype; /* output type (always a composite type) */
+ /* result typmod is not stored, but must be -1; see RowExpr comments */
+ CoercionForm convertformat; /* how to display this node */
+ } ConvertRowtypeExpr;
+
/*----------
* CaseExpr - a CASE expression
*
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.123
diff -c -r1.123 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c 30 Nov 2004 03:50:29 -0000 1.123
--- src/pl/plpgsql/src/pl_exec.c 11 Dec 2004 16:26:03 -0000
***************
*** 4024,4029 ****
--- 4024,4032 ----
case T_RelabelType:
return exec_simple_check_node((Node *) ((RelabelType *) node)->arg);
+ case T_ConvertRowtypeExpr:
+ return exec_simple_check_node((Node *) ((ConvertRowtypeExpr *) node)->arg);
+
case T_CaseExpr:
{
CaseExpr *expr = (CaseExpr *) node;