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;
---------------------------(end of broadcast)--------------------------- TIP 4: Don't 'kill -9' the postmaster