diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 086df14642..9aec096561 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.169 2004/09/22 17:41:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.170 2004/12/11 23:26:29 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -87,6 +87,9 @@ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, 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,7 +431,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, * * 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,6 +1848,75 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, 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,6 +3042,68 @@ ExecInitExpr(Expr *node, PlanState *parent) 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; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 093965a517..abe0ebe065 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.293 2004/11/05 19:15:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.294 2004/12/11 23:26:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -876,6 +876,21 @@ _copyRelabelType(RelabelType *from) return newnode; } +/* + * _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 */ @@ -2696,6 +2711,9 @@ copyObject(void *from) case T_RelabelType: retval = _copyRelabelType(from); break; + case T_ConvertRowtypeExpr: + retval = _copyConvertRowtypeExpr(from); + break; case T_CaseExpr: retval = _copyCaseExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 6099f42cca..f47fbdd5a4 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.232 2004/11/05 19:15:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.233 2004/12/11 23:26:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -380,6 +380,24 @@ _equalRelabelType(RelabelType *a, RelabelType *b) return true; } +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) { @@ -1844,6 +1862,9 @@ equal(void *a, void *b) case T_RelabelType: retval = _equalRelabelType(a, b); break; + case T_ConvertRowtypeExpr: + retval = _equalConvertRowtypeExpr(a, b); + break; case T_CaseExpr: retval = _equalCaseExpr(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b80cee4944..292b608c22 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.243 2004/08/29 05:06:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.244 2004/12/11 23:26:33 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -767,6 +767,16 @@ _outRelabelType(StringInfo str, RelabelType *node) WRITE_ENUM_FIELD(relabelformat, CoercionForm); } +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) { @@ -1728,6 +1738,9 @@ _outNode(StringInfo str, void *obj) case T_RelabelType: _outRelabelType(str, obj); break; + case T_ConvertRowtypeExpr: + _outConvertRowtypeExpr(str, obj); + break; case T_CaseExpr: _outCaseExpr(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index cf2502c715..7928c04de3 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.173 2004/08/29 04:12:33 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.174 2004/12/11 23:26:34 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -575,6 +575,21 @@ _readRelabelType(void) READ_DONE(); } +/* + * _readConvertRowtypeExpr + */ +static ConvertRowtypeExpr * +_readConvertRowtypeExpr(void) +{ + READ_LOCALS(ConvertRowtypeExpr); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(resulttype); + READ_ENUM_FIELD(convertformat, CoercionForm); + + READ_DONE(); +} + /* * _readCaseExpr */ @@ -971,6 +986,8 @@ parseNodeString(void) 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)) diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index af53c4592c..cd461d237f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.23 2004/08/29 05:06:44 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.24 2004/12/11 23:26:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -839,6 +839,13 @@ find_nonnullable_rels(Node *node, bool top_level) 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; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index ba1de1b886..1f4753aa60 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.117 2004/10/02 22:39:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.118 2004/12/11 23:26:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,8 @@ typedef struct { 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,6 +828,8 @@ adjust_inherited_attrs(Node *node, 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,50 +914,6 @@ translate_inherited_attnum(AttrNumber old_attno, 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) @@ -977,8 +937,22 @@ adjust_inherited_attrs_mutator(Node *node, } else if (var->varattno == 0) { - /* expand whole-row reference into a ROW() construct */ - return generate_whole_row(var, context); + /* + * 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 */ } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 6bf9990ccf..39a8c8c56f 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.184 2004/11/09 21:42:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.185 2004/12/11 23:26:39 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1047,6 +1047,13 @@ strip_implicit_coercions(Node *node) 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,11 +1089,13 @@ set_coercionform_dontcare_walker(Node *node, void *context) return false; if (IsA(node, FuncExpr)) ((FuncExpr *) node)->funcformat = COERCE_DONTCARE; - if (IsA(node, RelabelType)) + else if (IsA(node, RelabelType)) ((RelabelType *) node)->relabelformat = COERCE_DONTCARE; - if (IsA(node, RowExpr)) + else if (IsA(node, ConvertRowtypeExpr)) + ((ConvertRowtypeExpr *) node)->convertformat = COERCE_DONTCARE; + else if (IsA(node, RowExpr)) ((RowExpr *) node)->row_format = COERCE_DONTCARE; - if (IsA(node, CoerceToDomain)) + else if (IsA(node, CoerceToDomain)) ((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE; return expression_tree_walker(node, set_coercionform_dontcare_walker, context); @@ -2647,6 +2656,8 @@ expression_tree_walker(Node *node, 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,6 +3068,16 @@ expression_tree_mutator(Node *node, 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; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 4ac4ec84ec..d4407ec373 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.124 2004/11/06 17:46:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.125 2004/12/11 23:26:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -321,13 +321,16 @@ coerce_type(ParseState *pstate, Node *node, 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. + * 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). */ - return (Node *) makeRelabelType((Expr *) node, - targetTypeId, -1, - cformat); + 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,6 +570,8 @@ hide_coercion_node(Node *node) ((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)) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 73d74a0535..f8ce13d7cf 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.176 2004/08/29 05:06:44 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.177 2004/12/11 23:26:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -940,6 +940,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_FieldSelect: case T_FieldStore: case T_RelabelType: + case T_ConvertRowtypeExpr: case T_CaseTestExpr: case T_CoerceToDomain: case T_CoerceToDomainValue: @@ -1406,6 +1407,9 @@ exprType(Node *expr) case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; + case T_ConvertRowtypeExpr: + type = ((ConvertRowtypeExpr *) expr)->resulttype; + break; case T_CaseExpr: type = ((CaseExpr *) expr)->casetype; break; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 36e7208dc1..44fdafcded 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.185 2004/11/05 19:16:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.186 2004/12/11 23:26:45 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -2622,6 +2622,9 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) 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,6 +3136,30 @@ get_rule_expr(Node *node, deparse_context *context, } 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; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 0717695203..7b5a75868b 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.120 2004/10/07 18:38:51 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.121 2004/12/11 23:26:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -578,6 +578,23 @@ typedef struct FieldStoreState TupleDesc argdesc; /* tupdesc for most recent input */ } 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 * ---------------- diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 4a7cf05e5c..5cd4108858 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.161 2004/09/14 03:21:25 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.162 2004/12/11 23:26:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -112,6 +112,7 @@ typedef enum NodeTag T_FieldSelect, T_FieldStore, T_RelabelType, + T_ConvertRowtypeExpr, T_CaseExpr, T_CaseWhen, T_CaseTestExpr, @@ -145,6 +146,7 @@ typedef enum NodeTag T_SubPlanState, T_FieldSelectState, T_FieldStoreState, + T_ConvertRowtypeExprState, T_CaseExprState, T_CaseWhenState, T_ArrayExprState, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 9eb1514c28..789cc83893 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.104 2004/08/29 05:06:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.105 2004/12/11 23:26:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -591,6 +591,27 @@ typedef struct RelabelType 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 * diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 9156c8fc92..f8563b3e0b 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.123 2004/11/30 03:50:29 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.124 2004/12/11 23:26:51 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -4024,6 +4024,9 @@ exec_simple_check_node(Node *node) 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;