Instead of supposing (wrongly, in the general case) that the rowtype
authorTom Lane <[email protected]>
Sat, 11 Dec 2004 23:26:51 +0000 (23:26 +0000)
committerTom Lane <[email protected]>
Sat, 11 Dec 2004 23:26:51 +0000 (23:26 +0000)
of an inheritance child table is binary-compatible with the rowtype of
its parent, invent an expression node type that does the conversion
correctly.  Fixes the new bug exhibited by Kris Shannon as well as a
lot of old bugs that would only show up when using multiple inheritance
or after altering the parent table.

15 files changed:
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/primnodes.h
src/pl/plpgsql/src/pl_exec.c

index e0c69aef92f935474ab1670fe45afdfaaceb8544..4152dc8120dbc182dcf2a13a61604c03ddbe80e8 100644 (file)
@@ -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;
index 5e0d54b35d4197d61b0272a014aed3c5d0871084..a02a4bb02acb6e5bfb5292b26858a31e23c40738 100644 (file)
@@ -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;
index 053842b596e3061f4248d80f2257d5082a066b12..64a24a9f461e79fceeda70e430a042aad8780f47 100644 (file)
@@ -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;
index 136ef613b5be4d42b31939ea401e2f370ccf8291..83a52977b4a07a9970408fdb6ab820a5e0ca268f 100644 (file)
@@ -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;
index 301404240f9c61d4667a4c016033f09bead089c6..c0e341df21ea076622d46bbd14be40267109596b 100644 (file)
@@ -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))
index ec05c114552e91945452884ba39716d58de54018..69c2a3dfb1f11f372bede94aa564ddeb7731df66 100644 (file)
@@ -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;
index 9bf95532d326ca7f02c90ed09f09fa05d3e5accb..cb5195f303ec567684cf4cd901158abbe7afedf0 100644 (file)
@@ -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 */
                }
index 5ba831fe5c610f39f0774aa65ea7a7385cde25ec..18fc0b3e1df122ff328b4bd1ae5b7b8b88ef1f74 100644 (file)
@@ -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;
index baf507f2cac6230f94a3e3144a871e52312cce22..fb45079b29fc2f3db367b9d1a8f9980832ec8453 100644 (file)
@@ -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))
index ecdf4b5b9037497a8253c4a88de96591f7aaa0bd..a8b257cec557a5e5897ad9f3136447f11293b9cc 100644 (file)
@@ -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;
index b4c61f2692eced9be9f79822ccf75952aa3529f8..aceb02699c7d0b50c5815369696971451e466f28 100644 (file)
@@ -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;
index b11e91caff138123bcffc8251907c3f2b641ffd5..c9fa470b06e5b79b32daad9d3f50b0a13454f165 100644 (file)
@@ -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
  * ----------------
index 17803278a217a6712a18db648a62b248aad94771..8c428bf171af33653f040ea1722a80872548ae30 100644 (file)
@@ -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,
index ad36d7fe134cbe07f0d6775d042d323f886b1cf0..1ae79f90d705a5980477e9af0cf8f4c503a5672d 100644 (file)
@@ -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
  *
index 8148d51ff47f1e20f95570fe16b1b461618c8dc7..3709498061d02ce4c115dfe975b97705f0848afd 100644 (file)
@@ -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;