Instead of supposing (wrongly, in the general case) that the rowtype
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 11 Dec 2004 23:26:51 +0000 (23:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
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 086df146420c2c8409870675e50982a0d27dc7e4..9aec09656108a5688ecbdc2acbcc05cb9312516a 100644 (file)
@@ -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;
index 093965a51745463d3fe3c4be62ffea2fd7249715..abe0ebe065d327458457a52a5f8eae13bcf4d2ed 100644 (file)
@@ -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;
index 6099f42cca749d3d9a7981c723dc1ecb28a43594..f47fbdd5a41939dbb7779170ff123d46d2387a25 100644 (file)
@@ -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;
index b80cee4944c3daa42711f253bf051f0ef660ff42..292b608c22f8dca03dda1a1e6cbcb2713ccd8524 100644 (file)
@@ -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;
index cf2502c715165e4d48780e3a8cce3a8ba4f78c69..7928c04de35fdedec2251356b82085a374dc06e9 100644 (file)
@@ -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))
index af53c4592cc4ea7bec2039160f026c34ed242f2c..cd461d237f315f340af77f27d3d546528073b369 100644 (file)
@@ -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;
index ba1de1b886041640aefed019a3a80e772cbb8f3a..1f4753aa6041124a7b3a20b2b99f2f6d3165a4c0 100644 (file)
@@ -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 */
        }
index 6bf9990ccf50bcc0094b0018f7ad0c02fc02b5b5..39a8c8c56fbcdcbae3e1b78912334a9bd9faac5d 100644 (file)
@@ -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;
index 4ac4ec84ecebc0fc53b7d87ef73d0497d26ab6b9..d4407ec37325141ff52907fa3c7a74b71f8d7d3c 100644 (file)
@@ -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))
index 73d74a0535ff5eef7215ce3582b85cbd9d57be1f..f8ce13d7cf4360563d306a534a452aadd447d041 100644 (file)
@@ -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;
index 36e7208dc1bd08f46d459a65c08a3e78c7452970..44fdafcded86b0b931e12606321757ce74342262 100644 (file)
@@ -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;
index 07176952032d38568ad3c7714660c80d99cc6e15..7b5a75868ba85d05150b12a4f86292ecd66efb2d 100644 (file)
@@ -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
  * ----------------
index 4a7cf05e5c5ca85de1099674e7ee10ae0ac13c0d..5cd41088589aa0602d17291009d3421b9399fc55 100644 (file)
@@ -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,
index 9eb1514c2881409421951e62c227ab4fbdba83b9..789cc83893f428a51fe7fe09c750f02e7e8a4c87 100644 (file)
@@ -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
  *
index 9156c8fc9224626b6deac6ee652f0e2d3b9c74c0..f8563b3e0bf814834c7a661373660afd528cc2c0 100644 (file)
@@ -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;