Create a new expression node type RelabelType, which exists solely to
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 20 Feb 2000 21:32:16 +0000 (21:32 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 20 Feb 2000 21:32:16 +0000 (21:32 +0000)
represent the result of a binary-compatible type coercion.  At runtime
it just evaluates its argument --- but during type resolution, exprType
will pick up the output type of the RelabelType node instead of the type
of the argument.  This solves some longstanding problems with dropped
type coercions, an example being 'select now()::abstime::int4' which
used to produce date-formatted output, not an integer, because the
coercion to int4 was dropped on the floor.

13 files changed:
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/freefuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/nodes.h
src/include/nodes/primnodes.h

index 91dbde63419e59c9af574fad32bc5336113740c1..a319e2c2b37c678624d6b66f167765eec752c282 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.67 2000/01/26 05:56:21 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.68 2000/02/20 21:32:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1176,7 +1176,7 @@ ExecEvalExpr(Node *expression,
             bool *isNull,
             bool *isDone)
 {
-   Datum       retDatum = 0;
+   Datum       retDatum;
 
    *isNull = false;
 
@@ -1200,36 +1200,33 @@ ExecEvalExpr(Node *expression,
    switch (nodeTag(expression))
    {
        case T_Var:
-           retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
+           retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
            break;
        case T_Const:
            {
                Const      *con = (Const *) expression;
 
-               if (con->constisnull)
-                   *isNull = true;
                retDatum = con->constvalue;
+               *isNull = con->constisnull;
                break;
            }
        case T_Param:
-           retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull);
+           retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
            break;
        case T_Iter:
-           retDatum = (Datum) ExecEvalIter((Iter *) expression,
-                                           econtext,
-                                           isNull,
-                                           isDone);
+           retDatum = ExecEvalIter((Iter *) expression,
+                                   econtext,
+                                   isNull,
+                                   isDone);
            break;
        case T_Aggref:
-           retDatum = (Datum) ExecEvalAggref((Aggref *) expression,
-                                             econtext,
-                                             isNull);
+           retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
            break;
        case T_ArrayRef:
-           retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression,
-                                               econtext,
-                                               isNull,
-                                               isDone);
+           retDatum = ExecEvalArrayRef((ArrayRef *) expression,
+                                       econtext,
+                                       isNull,
+                                       isDone);
            break;
        case T_Expr:
            {
@@ -1238,37 +1235,48 @@ ExecEvalExpr(Node *expression,
                switch (expr->opType)
                {
                    case OP_EXPR:
-                       retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
+                       retDatum = ExecEvalOper(expr, econtext, isNull);
                        break;
                    case FUNC_EXPR:
-                       retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
+                       retDatum = ExecEvalFunc(expr, econtext,
+                                               isNull, isDone);
                        break;
                    case OR_EXPR:
-                       retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
+                       retDatum = ExecEvalOr(expr, econtext, isNull);
                        break;
                    case AND_EXPR:
-                       retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull);
+                       retDatum = ExecEvalAnd(expr, econtext, isNull);
                        break;
                    case NOT_EXPR:
-                       retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
+                       retDatum = ExecEvalNot(expr, econtext, isNull);
                        break;
                    case SUBPLAN_EXPR:
-                       retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper,
-                                                      expr->args, econtext,
-                                                      isNull);
+                       retDatum = ExecSubPlan((SubPlan *) expr->oper,
+                                              expr->args, econtext,
+                                              isNull);
                        break;
                    default:
-                       elog(ERROR, "ExecEvalExpr: unknown expression type %d", expr->opType);
+                       elog(ERROR, "ExecEvalExpr: unknown expression type %d",
+                            expr->opType);
+                       retDatum = 0; /* keep compiler quiet */
                        break;
                }
                break;
            }
+       case T_RelabelType:
+           retDatum = ExecEvalExpr(((RelabelType *) expression)->arg,
+                                   econtext,
+                                   isNull,
+                                   isDone);
+           break;
        case T_CaseExpr:
-           retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull);
+           retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull);
            break;
 
        default:
-           elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression));
+           elog(ERROR, "ExecEvalExpr: unknown expression type %d",
+                nodeTag(expression));
+           retDatum = 0;       /* keep compiler quiet */
            break;
    }
 
index 5bf01e227228c4d55e9f93a0de964054d06b4c58..fbef91b35d8006b8d28739c65811b0ea5f9faa49 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.106 2000/02/15 20:49:09 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.107 2000/02/20 21:32:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -873,6 +873,26 @@ _copySubLink(SubLink *from)
    return newnode;
 }
 
+/* ----------------
+ *     _copyRelabelType
+ * ----------------
+ */
+static RelabelType *
+_copyRelabelType(RelabelType *from)
+{
+   RelabelType    *newnode = makeNode(RelabelType);
+
+   /* ----------------
+    *  copy remainder of node
+    * ----------------
+    */
+   Node_Copy(from, newnode, arg);
+   newnode->resulttype = from->resulttype;
+   newnode->resulttypmod = from->resulttypmod;
+
+   return newnode;
+}
+
 /* ----------------
  *     _copyCaseExpr
  * ----------------
@@ -1617,6 +1637,9 @@ copyObject(void *from)
        case T_SubLink:
            retval = _copySubLink(from);
            break;
+       case T_RelabelType:
+           retval = _copyRelabelType(from);
+           break;
        case T_CaseExpr:
            retval = _copyCaseExpr(from);
            break;
index fadc282d1add86fcb5aa8255e80f65cf443af725..b4f5fc6285c2ba77e296d151014bd358350f1c6c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.61 2000/02/15 20:49:09 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.62 2000/02/20 21:32:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -262,6 +262,18 @@ _equalSubLink(SubLink *a, SubLink *b)
    return true;
 }
 
+static bool
+_equalRelabelType(RelabelType *a, RelabelType *b)
+{
+   if (!equal(a->arg, b->arg))
+       return false;
+   if (a->resulttype != b->resulttype)
+       return false;
+   if (a->resulttypmod != b->resulttypmod)
+       return false;
+   return true;
+}
+
 static bool
 _equalArray(Array *a, Array *b)
 {
@@ -806,6 +818,9 @@ equal(void *a, void *b)
        case T_SubLink:
            retval = _equalSubLink(a, b);
            break;
+       case T_RelabelType:
+           retval = _equalRelabelType(a, b);
+           break;
        case T_Func:
            retval = _equalFunc(a, b);
            break;
index 8eed80e61ab42d4835136ac4259514d0424b0936..daca4a6d96a18230f27010f93b76434ed531edc2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.36 2000/02/15 20:49:09 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.37 2000/02/20 21:32:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -650,6 +650,22 @@ _freeSubLink(SubLink *node)
    pfree(node);
 }
 
+/* ----------------
+ *     _freeRelabelType
+ * ----------------
+ */
+static void
+_freeRelabelType(RelabelType *node)
+{
+   /* ----------------
+    *  free remainder of node
+    * ----------------
+    */
+   freeObject(node->arg);
+
+   pfree(node);
+}
+
 /* ----------------
  *     _freeCaseExpr
  * ----------------
@@ -1241,6 +1257,9 @@ freeObject(void *node)
        case T_SubLink:
            _freeSubLink(node);
            break;
+       case T_RelabelType:
+           _freeRelabelType(node);
+           break;
        case T_CaseExpr:
            _freeCaseExpr(node);
            break;
index c40ca9ff9cbdf1e1a8afc969dbbde086477824cc..db785afab9fa9ea4a91ef279d32eb18f3a29c815 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.108 2000/02/15 20:49:09 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.109 2000/02/20 21:32:05 tgl Exp $
  *
  * NOTES
  *   Every (plan) node in POSTGRES has an associated "out" routine which
@@ -770,6 +770,19 @@ _outSubLink(StringInfo str, SubLink *node)
    _outNode(str, node->subselect);
 }
 
+/*
+ * RelabelType
+ */
+static void
+_outRelabelType(StringInfo str, RelabelType *node)
+{
+   appendStringInfo(str, " RELABELTYPE :arg ");
+   _outNode(str, node->arg);
+
+   appendStringInfo(str, " :resulttype %u :resulttypmod %d ",
+                    node->resulttype, node->resulttypmod);
+}
+
 /*
  * Array is a subclass of Expr
  */
@@ -1496,6 +1509,9 @@ _outNode(StringInfo str, void *obj)
            case T_SubLink:
                _outSubLink(str, obj);
                break;
+           case T_RelabelType:
+               _outRelabelType(str, obj);
+               break;
            case T_Array:
                _outArray(str, obj);
                break;
index 7d1e0b4cccf22ca551325c917bd9dce570baf099..dfbdbef9884f16d05d06db1e81588772ce2170b5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.84 2000/02/15 20:49:12 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.85 2000/02/20 21:32:05 tgl Exp $
  *
  * NOTES
  *   Most of the read functions for plan nodes are tested. (In fact, they
@@ -1191,6 +1191,35 @@ _readSubLink()
    return local_node;
 }
 
+/* ----------------
+ *     _readRelabelType
+ *
+ * RelabelType is a subclass of Node
+ * ----------------
+ */
+static RelabelType *
+_readRelabelType()
+{
+   RelabelType *local_node;
+   char       *token;
+   int         length;
+
+   local_node = makeNode(RelabelType);
+
+   token = lsptok(NULL, &length);      /* eat :arg */
+   local_node->arg = nodeRead(true);   /* now read it */
+
+   token = lsptok(NULL, &length);      /* eat :resulttype */
+   token = lsptok(NULL, &length);      /* get resulttype */
+   local_node->resulttype = (Oid) atol(token);
+
+   token = lsptok(NULL, &length);      /* eat :resulttypmod */
+   token = lsptok(NULL, &length);      /* get resulttypmod */
+   local_node->resulttypmod = atoi(token);
+
+   return local_node;
+}
+
 /*
  * Stuff from execnodes.h
  */
@@ -1820,6 +1849,8 @@ parsePlanString(void)
        return_value = _readAggref();
    else if (length == 7 && strncmp(token, "SUBLINK", length) == 0)
        return_value = _readSubLink();
+   else if (length == 11 && strncmp(token, "RELABELTYPE", length) == 0)
+       return_value = _readRelabelType();
    else if (length == 3 && strncmp(token, "AGG", length) == 0)
        return_value = _readAgg();
    else if (length == 4 && strncmp(token, "HASH", length) == 0)
index 664a0dfaaa57e2c652e48ec7506de7756239a8f7..4bb84f1521463375055c07da42b45ec52e4fe20b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.59 2000/02/15 03:37:36 thomas Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.60 2000/02/20 21:32:06 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -1150,6 +1150,37 @@ eval_const_expressions_mutator (Node *node, void *context)
         newexpr->args = args;
         return (Node *) newexpr;
    }
+   if (IsA(node, RelabelType))
+   {
+       /*
+        * If we can simplify the input to a constant, then we don't need
+        * the RelabelType node anymore: just change the type field of
+        * the Const node.  Otherwise keep the RelabelType node.
+        *
+        * XXX if relabel has a nondefault resulttypmod, do we need to
+        * keep it to show that?  At present I don't think so.
+        */
+       RelabelType *relabel = (RelabelType *) node;
+       Node       *arg;
+
+       arg = eval_const_expressions_mutator(relabel->arg, context);
+       if (arg && IsA(arg, Const))
+       {
+           Const  *con = (Const *) arg;
+
+           con->consttype = relabel->resulttype;
+           return (Node *) con;
+       }
+       else
+       {
+           RelabelType *newrelabel = makeNode(RelabelType);
+
+           newrelabel->arg = arg;
+           newrelabel->resulttype = relabel->resulttype;
+           newrelabel->resulttypmod = relabel->resulttypmod;
+           return (Node *) newrelabel;
+       }
+   }
    if (IsA(node, CaseExpr))
    {
        /*
@@ -1392,6 +1423,8 @@ expression_tree_walker(Node *node, bool (*walker) (), void *context)
                    return true;
            }
            break;
+       case T_RelabelType:
+           return walker(((RelabelType *) node)->arg, context);
        case T_CaseExpr:
            {
                CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -1603,6 +1636,16 @@ expression_tree_mutator(Node *node, Node * (*mutator) (), void *context)
                return (Node *) newnode;
            }
            break;
+       case T_RelabelType:
+           {
+               RelabelType *relabel = (RelabelType *) node;
+               RelabelType *newnode;
+
+               FLATCOPY(newnode, relabel, RelabelType);
+               MUTATE(newnode->arg, relabel->arg, Node *);
+               return (Node *) newnode;
+           }
+           break;
        case T_CaseExpr:
            {
                CaseExpr   *caseexpr = (CaseExpr *) node;
index dbb6cbc581627450a1540c19d7c90aa724f569b6..646f1a046b324a9cb74a2491cf6a0f414ce680d0 100644 (file)
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * parse_coerce.c
- *     handle type coersions/conversions for parser
+ *     handle type coercions/conversions for parser
  *
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.31 2000/02/20 06:28:42 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.32 2000/02/20 21:32:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@ Node *
 coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
            Oid targetTypeId, int32 atttypmod)
 {
-   Node       *result = NULL;
+   Node       *result;
 
    if (targetTypeId == InvalidOid ||
        targetTypeId == inputTypeId)
@@ -44,11 +44,6 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
        /* no conversion needed */
        result = node;
    }
-   else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
-   {
-       /* no work if one of the known-good transparent conversions */
-       result = node;
-   }
    else if (inputTypeId == UNKNOWNOID && IsA(node, Const))
    {
        /*
@@ -87,6 +82,27 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 
        result = (Node *) newcon;
    }
+   else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
+   {
+       /*
+        * We don't really need to do a conversion, but we do need to attach
+        * a RelabelType node so that the expression will be seen to have
+        * the intended type when inspected by higher-level code.
+        */
+       RelabelType *relabel = makeNode(RelabelType);
+
+       relabel->arg = node;
+       relabel->resulttype = targetTypeId;
+       /*
+        * XXX could we label result with exprTypmod(node) instead of
+        * default -1 typmod, to save a possible length-coercion later?
+        * Would work if both types have same interpretation of typmod,
+        * which is likely but not certain.
+        */
+       relabel->resulttypmod = -1;
+
+       result = (Node *) relabel;
+   }
    else
    {
        /*
index 365378f40720180f62627c1adf36d250246d44d9..3fd3370672ff40db0af42520cb4f3360c516fb92 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.68 2000/02/15 03:37:47 thomas Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.69 2000/02/20 21:32:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -516,6 +516,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
        case T_Param:
        case T_Aggref:
        case T_ArrayRef:
+       case T_RelabelType:
            {
                result = (Node *) expr;
                break;
@@ -627,6 +628,9 @@ exprType(Node *expr)
        case T_Param:
            type = ((Param *) expr)->paramtype;
            break;
+       case T_RelabelType:
+           type = ((RelabelType *) expr)->resulttype;
+           break;
        case T_SubLink:
            {
                SubLink    *sublink = (SubLink *) expr;
@@ -697,6 +701,9 @@ exprTypmod(Node *expr)
                }
            }
            break;
+       case T_RelabelType:
+           return ((RelabelType *) expr)->resulttypmod;
+           break;
        default:
            break;
    }
index 939d7a91ec3c220d0aa04b00ed02ce50e0285bae..71cda760874d7ac40ac481ddaf93a8b922101d9e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.70 2000/02/20 06:35:08 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.71 2000/02/20 21:32:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -163,7 +163,10 @@ agg_get_candidates(char *aggname,
 }  /* agg_get_candidates() */
 
 /* agg_select_candidate()
- * Try to choose only one candidate aggregate function from a list of possibles.
+ *
+ * Try to choose only one candidate aggregate function from a list of
+ * possible matches.  Return value is Oid of input type of aggregate
+ * if successful, else InvalidOid.
  */
 static Oid
 agg_select_candidate(Oid typeid, CandidateList candidates)
@@ -175,10 +178,12 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
    CATEGORY    category,
                current_category;
 
-/*
- * First look for exact matches or binary compatible matches.
- * (Of course exact matches shouldn't even get here, but anyway.)
- */
+   /*
+    * First look for exact matches or binary compatible matches.
+    * (Of course exact matches shouldn't even get here, but anyway.)
+    */
+   ncandidates = 0;
+   last_candidate = NULL;
    for (current_candidate = candidates;
         current_candidate != NULL;
         current_candidate = current_candidate->next)
@@ -188,15 +193,17 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
        if (current_typeid == typeid
             || IS_BINARY_COMPATIBLE(current_typeid, typeid))
        {
-            /* we're home free */
-            return current_typeid;
+           last_candidate = current_candidate;
+           ncandidates++;
         }
     }
+   if (ncandidates == 1)
+       return last_candidate->args[0];
 
-/*
* If no luck that way, look for candidates which allow coersion
- * and have a preferred type. Keep all candidates if none match.
- */
+   /*
   * If no luck that way, look for candidates which allow coercion
   * and have a preferred type. Keep all candidates if none match.
   */
    category = TypeCategory(typeid);
    ncandidates = 0;
    last_candidate = NULL;
@@ -232,7 +239,10 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
    if (last_candidate)         /* terminate rebuilt list */
        last_candidate->next = NULL;
 
-   return ((ncandidates == 1) ? candidates->args[0] : 0);
+   if (ncandidates == 1)
+       return candidates->args[0];
+
+   return InvalidOid;
 }  /* agg_select_candidate() */
 
 
@@ -471,10 +481,9 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
    /*
     * See if this is a single argument function with the function
     * name also a type name and the input argument and type name
-    * binary compatible... This means that you are trying for a
-    * type conversion which does not need to take place, so we'll
-    * just pass through the argument itself. (make this clearer
-    * with some extra brackets - thomas 1998-12-05)
+    * binary compatible.  If so, we do not need to do any real
+    * conversion, but we do need to build a RelabelType node
+    * so that exprType() sees the result as being of the output type.
     */
    if (nargs == 1)
    {
@@ -486,8 +495,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
        if (HeapTupleIsValid(tp) &&
            IS_BINARY_COMPATIBLE(typeTypeId(tp), exprType(lfirst(fargs))))
        {
-           /* XXX FIXME: probably need to change expression's marked type? */
-           return (Node *) lfirst(fargs);
+           RelabelType *relabel = makeNode(RelabelType);
+
+           relabel->arg = (Node *) lfirst(fargs);
+           relabel->resulttype = typeTypeId(tp);
+           relabel->resulttypmod = -1;
+
+           return (Node *) relabel;
        }
    }
 
@@ -1128,7 +1142,7 @@ func_get_detail(char *funcname,
  *                      inheritance properties of the supplied argv.
  *
  *     This function is used to disambiguate among functions with the
- *     same name but different signatures.  It takes an array of eight
+ *     same name but different signatures.  It takes an array of input
  *     type ids.  For each type id in the array that's a complex type
  *     (a class), it walks up the inheritance tree, finding all
  *     superclasses of that type.  A vector of new Oid type arrays
@@ -1342,7 +1356,7 @@ gen_cross_product(InhPaths *arginh, int nargs)
  * 2) the input type can be typecast into the function type
  * Right now, we only typecast unknowns, and that is all we check for.
  *
- * func_get_detail() now can find coersions for function arguments which
+ * func_get_detail() now can find coercions for function arguments which
  * will make this function executable. So, we need to recover these
  * results here too.
  * - thomas 1998-03-25
@@ -1361,7 +1375,7 @@ make_arguments(ParseState *pstate,
         i < nargs;
         i++, current_fargs = lnext(current_fargs))
    {
-       /* types don't match? then force coersion using a function call... */
+       /* types don't match? then force coercion using a function call... */
        if (input_typeids[i] != function_typeids[i])
        {
            lfirst(current_fargs) = coerce_type(pstate,
index d18cff2789cf62a1d94554dab2da69d708f15623..0267fc2c576f804a50c1eb09ee24d7ccc18e3ac6 100644 (file)
@@ -3,7 +3,7 @@
  *           out of its tuple
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.41 2000/02/15 08:24:12 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.42 2000/02/20 21:32:12 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -1006,7 +1006,7 @@ get_select_query_def(Query *query, deparse_context *context)
                        appendStringInfo(buf, "%s",
                                         quote_identifier(strVal(lfirst(col))));
                    }
-                   appendStringInfo(buf, ")");
+                   appendStringInfoChar(buf, ')');
                }
            }
        }
@@ -1127,7 +1127,7 @@ get_insert_query_def(Query *query, deparse_context *context)
            sep = ", ";
            get_tle_expr(tle, context);
        }
-       appendStringInfo(buf, ")");
+       appendStringInfoChar(buf, ')');
    }
    else
        get_select_query_def(query, context);
@@ -1281,7 +1281,7 @@ get_rule_expr(Node *node, deparse_context *context)
                switch (expr->opType)
                {
                    case OP_EXPR:
-                       appendStringInfo(buf, "(");
+                       appendStringInfoChar(buf, '(');
                        if (length(args) == 2)
                        {
                            /* binary operator */
@@ -1320,35 +1320,35 @@ get_rule_expr(Node *node, deparse_context *context)
                                    elog(ERROR, "get_rule_expr: bogus oprkind");
                            }
                        }
-                       appendStringInfo(buf, ")");
+                       appendStringInfoChar(buf, ')');
                        break;
 
                    case OR_EXPR:
-                       appendStringInfo(buf, "(");
+                       appendStringInfoChar(buf, '(');
                        get_rule_expr((Node *) lfirst(args), context);
                        while ((args = lnext(args)) != NIL)
                        {
                            appendStringInfo(buf, " OR ");
                            get_rule_expr((Node *) lfirst(args), context);
                        }
-                       appendStringInfo(buf, ")");
+                       appendStringInfoChar(buf, ')');
                        break;
 
                    case AND_EXPR:
-                       appendStringInfo(buf, "(");
+                       appendStringInfoChar(buf, '(');
                        get_rule_expr((Node *) lfirst(args), context);
                        while ((args = lnext(args)) != NIL)
                        {
                            appendStringInfo(buf, " AND ");
                            get_rule_expr((Node *) lfirst(args), context);
                        }
-                       appendStringInfo(buf, ")");
+                       appendStringInfoChar(buf, ')');
                        break;
 
                    case NOT_EXPR:
                        appendStringInfo(buf, "(NOT ");
                        get_rule_expr((Node *) lfirst(args), context);
-                       appendStringInfo(buf, ")");
+                       appendStringInfoChar(buf, ')');
                        break;
 
                    case FUNC_EXPR:
@@ -1373,7 +1373,7 @@ get_rule_expr(Node *node, deparse_context *context)
                    appendStringInfo(buf, "*");
                else
                    get_rule_expr(aggref->target, context);
-               appendStringInfo(buf, ")");
+               appendStringInfoChar(buf, ')');
            }
            break;
 
@@ -1405,6 +1405,28 @@ get_rule_expr(Node *node, deparse_context *context)
            }
            break;
 
+       case T_RelabelType:
+           {
+               RelabelType *relabel = (RelabelType *) node;
+               HeapTuple   typetup;
+               Form_pg_type typeStruct;
+               char       *extval;
+
+               appendStringInfoChar(buf, '(');
+               get_rule_expr(relabel->arg, context);
+               typetup = SearchSysCacheTuple(TYPEOID,
+                                   ObjectIdGetDatum(relabel->resulttype),
+                                             0, 0, 0);
+               if (!HeapTupleIsValid(typetup))
+                   elog(ERROR, "cache lookup of type %u failed",
+                        relabel->resulttype);
+               typeStruct = (Form_pg_type) GETSTRUCT(typetup);
+               extval = pstrdup(NameStr(typeStruct->typname));
+               appendStringInfo(buf, ")::%s", quote_identifier(extval));
+               pfree(extval);
+           }
+           break;
+
        case T_CaseExpr:
            {
                CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -1474,14 +1496,14 @@ get_func_expr(Expr *expr, deparse_context *context)
    {
        if (!strcmp(proname, "nullvalue"))
        {
-           appendStringInfo(buf, "(");
+           appendStringInfoChar(buf, '(');
            get_rule_expr((Node *) lfirst(expr->args), context);
            appendStringInfo(buf, " ISNULL)");
            return;
        }
        if (!strcmp(proname, "nonnullvalue"))
        {
-           appendStringInfo(buf, "(");
+           appendStringInfoChar(buf, '(');
            get_rule_expr((Node *) lfirst(expr->args), context);
            appendStringInfo(buf, " NOTNULL)");
            return;
@@ -1500,7 +1522,7 @@ get_func_expr(Expr *expr, deparse_context *context)
        sep = ", ";
        get_rule_expr((Node *) lfirst(l), context);
    }
-   appendStringInfo(buf, ")");
+   appendStringInfoChar(buf, ')');
 }
 
 
@@ -1712,13 +1734,13 @@ get_sublink_expr(Node *node, deparse_context *context)
    Oper       *oper;
    bool        need_paren;
 
-   appendStringInfo(buf, "(");
+   appendStringInfoChar(buf, '(');
 
    if (sublink->lefthand != NIL)
    {
        need_paren = (length(sublink->lefthand) > 1);
        if (need_paren)
-           appendStringInfo(buf, "(");
+           appendStringInfoChar(buf, '(');
 
        sep = "";
        foreach(l, sublink->lefthand)
@@ -1731,7 +1753,7 @@ get_sublink_expr(Node *node, deparse_context *context)
        if (need_paren)
            appendStringInfo(buf, ") ");
        else
-           appendStringInfo(buf, " ");
+           appendStringInfoChar(buf, ' ');
    }
 
    need_paren = true;
@@ -1768,14 +1790,14 @@ get_sublink_expr(Node *node, deparse_context *context)
    }
 
    if (need_paren)
-       appendStringInfo(buf, "(");
+       appendStringInfoChar(buf, '(');
 
    get_query_def(query, buf, context->rangetables);
 
    if (need_paren)
        appendStringInfo(buf, "))");
    else
-       appendStringInfo(buf, ")");
+       appendStringInfoChar(buf, ')');
 }
 
 /* ----------
index 8dd893e3f13cc2ef79b260f6a132d27ca4dec2d1..f42f615c7a6603e499bd2ba42dc9d5c2c4f0ae66 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.65 2000/02/18 09:29:43 inoue Exp $
+ * $Id: nodes.h,v 1.66 2000/02/20 21:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,6 +67,7 @@ typedef enum NodeTag
    T_Array,
    T_ArrayRef,
    T_Iter,
+   T_RelabelType,
 
    /*---------------------
     * TAGS FOR PLANNER NODES (relation.h)
index bdeff737b63516b091a4409ef010e8d323756e89..7c3e0c6c4b843ba5585a21599f852ce2fb9b8e00 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.39 2000/01/26 05:58:16 momjian Exp $
+ * $Id: primnodes.h,v 1.40 2000/02/20 21:32:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -467,4 +467,28 @@ typedef struct ArrayRef
    Node       *refassgnexpr;
 } ArrayRef;
 
+/* ----------------
+ * RelabelType
+ *     arg             - input expression
+ *     resulttype      - output type of coercion expression
+ *     resulttypmod    - output typmod (usually -1)
+ *
+ * RelabelType represents a "dummy" type coercion between two binary-
+ * compatible datatypes, such as reinterpreting the result of an OID
+ * expression as an int4.  It is a no-op at runtime; we only need it
+ * to provide a place to store the correct type to be attributed to
+ * the expression result during type resolution.  (We can't get away
+ * with just overwriting the type field of the input expression node,
+ * so we need a separate node to show the coercion's result type.)
+ * ----------------
+ */
+
+typedef struct RelabelType
+{
+   NodeTag     type;
+   Node       *arg;
+   Oid         resulttype;
+   int32       resulttypmod;
+} RelabelType;
+
 #endif  /* PRIMNODES_H */