]> granicus.if.org Git - postgresql/blobdiff - src/backend/parser/parse_node.c
Update copyright for 2016
[postgresql] / src / backend / parser / parse_node.c
index 0dea02c71b51af7ff163dfb617453d6c87e59f73..62d2f7105fb61ad40ea001da3a7cab0cbd436581 100644 (file)
@@ -3,18 +3,19 @@
  * parse_node.c
  *       various routines that make nodes for querytrees
  *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.106 2009/10/31 01:41:31 tgl Exp $
+ *       src/backend/parser/parse_node.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/heapam.h"
+#include "access/htup_details.h"
 #include "catalog/pg_type.h"
 #include "mb/pg_wchar.h"
 #include "nodes/makefuncs.h"
@@ -25,6 +26,7 @@
 #include "parser/parse_relation.h"
 #include "utils/builtins.h"
 #include "utils/int8.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/varbit.h"
 
@@ -97,8 +99,8 @@ free_parsestate(ParseState *pstate)
  * is a dummy (always 0, in fact).
  *
  * The locations stored in raw parsetrees are byte offsets into the source
- * string.     We have to convert them to 1-based character indexes for reporting
- * to clients. (We do things this way to avoid unnecessary overhead in the
+ * string.  We have to convert them to 1-based character indexes for reporting
+ * to clients.  (We do things this way to avoid unnecessary overhead in the
  * normal non-error case: computing character indexes would be much more
  * expensive than storing token offsets.)
  */
@@ -127,7 +129,7 @@ parser_errposition(ParseState *pstate, int location)
  * Sometimes the parser calls functions that aren't part of the parser
  * subsystem and can't reasonably be passed a ParseState; yet we would
  * like any errors thrown in those functions to be tagged with a parse
- * error location.     Use this function to set up an error context stack
+ * error location.  Use this function to set up an error context stack
  * entry that will accomplish that.  Usage pattern:
  *
  *             declare a local variable "ParseCallbackState pcbstate"
@@ -143,10 +145,10 @@ setup_parser_errposition_callback(ParseCallbackState *pcbstate,
        /* Setup error traceback support for ereport() */
        pcbstate->pstate = pstate;
        pcbstate->location = location;
-       pcbstate->errcontext.callback = pcb_error_callback;
-       pcbstate->errcontext.arg = (void *) pcbstate;
-       pcbstate->errcontext.previous = error_context_stack;
-       error_context_stack = &pcbstate->errcontext;
+       pcbstate->errcallback.callback = pcb_error_callback;
+       pcbstate->errcallback.arg = (void *) pcbstate;
+       pcbstate->errcallback.previous = error_context_stack;
+       error_context_stack = &pcbstate->errcallback;
 }
 
 /*
@@ -156,7 +158,7 @@ void
 cancel_parser_errposition_callback(ParseCallbackState *pcbstate)
 {
        /* Pop the error context stack */
-       error_context_stack = pcbstate->errcontext.previous;
+       error_context_stack = pcbstate->errcallback.previous;
 }
 
 /*
@@ -188,31 +190,58 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
                                sublevels_up;
        Oid                     vartypeid;
        int32           type_mod;
+       Oid                     varcollid;
 
        vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
-       get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
-       result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
+       get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
+       result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up);
        result->location = location;
        return result;
 }
 
 /*
  * transformArrayType()
- *             Get the element type of an array type in preparation for subscripting
+ *             Identify the types involved in a subscripting operation
+ *
+ * On entry, arrayType/arrayTypmod identify the type of the input value
+ * to be subscripted (which could be a domain type).  These are modified
+ * if necessary to identify the actual array type and typmod, and the
+ * array's element type is returned.  An error is thrown if the input isn't
+ * an array type.
  */
 Oid
-transformArrayType(Oid arrayType)
+transformArrayType(Oid *arrayType, int32 *arrayTypmod)
 {
+       Oid                     origArrayType = *arrayType;
        Oid                     elementType;
        HeapTuple       type_tuple_array;
        Form_pg_type type_struct_array;
 
+       /*
+        * If the input is a domain, smash to base type, and extract the actual
+        * typmod to be applied to the base type.  Subscripting a domain is an
+        * operation that necessarily works on the base array type, not the domain
+        * itself.  (Note that we provide no method whereby the creator of a
+        * domain over an array type could hide its ability to be subscripted.)
+        */
+       *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+
+       /*
+        * We treat int2vector and oidvector as though they were domains over
+        * int2[] and oid[].  This is needed because array slicing could create an
+        * array that doesn't satisfy the dimensionality constraints of the
+        * xxxvector type; so we want the result of a slice operation to be
+        * considered to be of the more general type.
+        */
+       if (*arrayType == INT2VECTOROID)
+               *arrayType = INT2ARRAYOID;
+       else if (*arrayType == OIDVECTOROID)
+               *arrayType = OIDARRAYOID;
+
        /* Get the type tuple for the array */
-       type_tuple_array = SearchSysCache(TYPEOID,
-                                                                         ObjectIdGetDatum(arrayType),
-                                                                         0, 0, 0);
+       type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
        if (!HeapTupleIsValid(type_tuple_array))
-               elog(ERROR, "cache lookup failed for type %u", arrayType);
+               elog(ERROR, "cache lookup failed for type %u", *arrayType);
        type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
 
        /* needn't check typisdefined since this will fail anyway */
@@ -222,7 +251,7 @@ transformArrayType(Oid arrayType)
                ereport(ERROR,
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                                 errmsg("cannot subscript type %s because it is not an array",
-                                               format_type_be(arrayType))));
+                                               format_type_be(origArrayType))));
 
        ReleaseSysCache(type_tuple_array);
 
@@ -240,16 +269,21 @@ transformArrayType(Oid arrayType)
  *
  * In an array assignment, we are given a destination array value plus a
  * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
+ * that array.  We produce an expression that represents the new array value
  * with the source data inserted into the right part of the array.
  *
+ * For both cases, if the source array is of a domain-over-array type,
+ * the result is of the base array type or its element type; essentially,
+ * we must fold a domain to its base type before applying subscripting.
+ * (Note that int2vector and oidvector are treated as domains here.)
+ *
  * pstate              Parse state
  * arrayBase   Already-transformed expression for the array as a whole
- * arrayType   OID of array's datatype (should match type of arrayBase)
+ * arrayType   OID of array's datatype (should match type of arrayBase,
+ *                             or be the base type of arrayBase's domain type)
  * elementType OID of array's element type (fetch with transformArrayType,
  *                             or pass InvalidOid to do it here)
- * elementTypMod typmod to be applied to array elements (if storing) or of
- *                             the source array (if fetching)
+ * arrayTypMod typmod for the array (which is also typmod for the elements)
  * indirection Untransformed list of subscripts (must not be NIL)
  * assignFrom  NULL for array fetch, else transformed expression for source.
  */
@@ -258,7 +292,7 @@ transformArraySubscripts(ParseState *pstate,
                                                 Node *arrayBase,
                                                 Oid arrayType,
                                                 Oid elementType,
-                                                int32 elementTypMod,
+                                                int32 arrayTypMod,
                                                 List *indirection,
                                                 Node *assignFrom)
 {
@@ -268,23 +302,27 @@ transformArraySubscripts(ParseState *pstate,
        ListCell   *idx;
        ArrayRef   *aref;
 
-       /* Caller may or may not have bothered to determine elementType */
+       /*
+        * Caller may or may not have bothered to determine elementType.  Note
+        * that if the caller did do so, arrayType/arrayTypMod must be as modified
+        * by transformArrayType, ie, smash domain to base type.
+        */
        if (!OidIsValid(elementType))
-               elementType = transformArrayType(arrayType);
+               elementType = transformArrayType(&arrayType, &arrayTypMod);
 
        /*
-        * A list containing only single subscripts refers to a single array
-        * element.  If any of the items are double subscripts (lower:upper), then
-        * the subscript expression means an array slice operation. In this case,
-        * we supply a default lower bound of 1 for any items that contain only a
-        * single subscript.  We have to prescan the indirection list to see if
-        * there are any double subscripts.
+        * A list containing only simple subscripts refers to a single array
+        * element.  If any of the items are slice specifiers (lower:upper), then
+        * the subscript expression means an array slice operation.  In this case,
+        * we convert any non-slice items to slices by treating the single
+        * subscript as the upper bound and supplying an assumed lower bound of 1.
+        * We have to prescan the list to see if there are any slice items.
         */
        foreach(idx, indirection)
        {
                A_Indices  *ai = (A_Indices *) lfirst(idx);
 
-               if (ai->lidx != NULL)
+               if (ai->is_slice)
                {
                        isSlice = true;
                        break;
@@ -304,7 +342,7 @@ transformArraySubscripts(ParseState *pstate,
                {
                        if (ai->lidx)
                        {
-                               subexpr = transformExpr(pstate, ai->lidx);
+                               subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
                                /* If it's not int4 already, try to coerce */
                                subexpr = coerce_to_target_type(pstate,
                                                                                                subexpr, exprType(subexpr),
@@ -318,31 +356,49 @@ transformArraySubscripts(ParseState *pstate,
                                                         errmsg("array subscript must have type integer"),
                                                parser_errposition(pstate, exprLocation(ai->lidx))));
                        }
-                       else
+                       else if (!ai->is_slice)
                        {
                                /* Make a constant 1 */
                                subexpr = (Node *) makeConst(INT4OID,
                                                                                         -1,
+                                                                                        InvalidOid,
                                                                                         sizeof(int32),
                                                                                         Int32GetDatum(1),
                                                                                         false,
                                                                                         true);         /* pass by value */
                        }
+                       else
+                       {
+                               /* Slice with omitted lower bound, put NULL into the list */
+                               subexpr = NULL;
+                       }
                        lowerIndexpr = lappend(lowerIndexpr, subexpr);
                }
-               subexpr = transformExpr(pstate, ai->uidx);
-               /* If it's not int4 already, try to coerce */
-               subexpr = coerce_to_target_type(pstate,
-                                                                               subexpr, exprType(subexpr),
-                                                                               INT4OID, -1,
-                                                                               COERCION_ASSIGNMENT,
-                                                                               COERCE_IMPLICIT_CAST,
-                                                                               -1);
-               if (subexpr == NULL)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                        errmsg("array subscript must have type integer"),
-                                        parser_errposition(pstate, exprLocation(ai->uidx))));
+               else
+                       Assert(ai->lidx == NULL && !ai->is_slice);
+
+               if (ai->uidx)
+               {
+                       subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
+                       /* If it's not int4 already, try to coerce */
+                       subexpr = coerce_to_target_type(pstate,
+                                                                                       subexpr, exprType(subexpr),
+                                                                                       INT4OID, -1,
+                                                                                       COERCION_ASSIGNMENT,
+                                                                                       COERCE_IMPLICIT_CAST,
+                                                                                       -1);
+                       if (subexpr == NULL)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("array subscript must have type integer"),
+                                                parser_errposition(pstate, exprLocation(ai->uidx))));
+               }
+               else
+               {
+                       /* Slice with omitted upper bound, put NULL into the list */
+                       Assert(isSlice && ai->is_slice);
+                       subexpr = NULL;
+               }
                upperIndexpr = lappend(upperIndexpr, subexpr);
        }
 
@@ -358,7 +414,7 @@ transformArraySubscripts(ParseState *pstate,
 
                newFrom = coerce_to_target_type(pstate,
                                                                                assignFrom, typesource,
-                                                                               typeneeded, elementTypMod,
+                                                                               typeneeded, arrayTypMod,
                                                                                COERCION_ASSIGNMENT,
                                                                                COERCE_IMPLICIT_CAST,
                                                                                -1);
@@ -380,7 +436,8 @@ transformArraySubscripts(ParseState *pstate,
        aref = makeNode(ArrayRef);
        aref->refarraytype = arrayType;
        aref->refelemtype = elementType;
-       aref->reftypmod = elementTypMod;
+       aref->reftypmod = arrayTypMod;
+       /* refcollid will be set by parse_collate.c */
        aref->refupperindexpr = upperIndexpr;
        aref->reflowerindexpr = lowerIndexpr;
        aref->refexpr = (Expr *) arrayBase;
@@ -501,6 +558,7 @@ make_const(ParseState *pstate, Value *value, int location)
                        /* return a null const */
                        con = makeConst(UNKNOWNOID,
                                                        -1,
+                                                       InvalidOid,
                                                        -2,
                                                        (Datum) 0,
                                                        true,
@@ -515,6 +573,7 @@ make_const(ParseState *pstate, Value *value, int location)
 
        con = makeConst(typeid,
                                        -1,                     /* typmod -1 is OK for all cases */
+                                       InvalidOid, /* all cases are uncollatable types */
                                        typelen,
                                        val,
                                        false,