OSDN Git Service

8.4 pgindent run, with new combined Linux/FreeBSD/MinGW typedef list
[pg-rex/syncrep.git] / src / backend / utils / adt / array_userfuncs.c
index fd83025..c30d6b4 100644 (file)
@@ -3,21 +3,20 @@
  * array_userfuncs.c
  *       Misc user-visible array support functions
  *
- * Copyright (c) 2003-2005, PostgreSQL Global Development Group
+ * Copyright (c) 2003-2009, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.15 2005/01/01 20:44:17 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.30 2009/06/11 14:49:03 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
+#include "nodes/execnodes.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
-#include "utils/syscache.h"
+
 
 /*-----------------------------------------------------------------------------
  * array_push :
@@ -29,11 +28,11 @@ array_push(PG_FUNCTION_ARGS)
 {
        ArrayType  *v;
        Datum           newelem;
+       bool            isNull;
        int                *dimv,
                           *lb;
        ArrayType  *result;
        int                     indx;
-       bool            isNull;
        Oid                     element_type;
        int16           typlen;
        bool            typbyval;
@@ -54,15 +53,27 @@ array_push(PG_FUNCTION_ARGS)
 
        if (arg0_elemid != InvalidOid)
        {
-               v = PG_GETARG_ARRAYTYPE_P(0);
-               element_type = ARR_ELEMTYPE(v);
-               newelem = PG_GETARG_DATUM(1);
+               if (PG_ARGISNULL(0))
+                       v = construct_empty_array(arg0_elemid);
+               else
+                       v = PG_GETARG_ARRAYTYPE_P(0);
+               isNull = PG_ARGISNULL(1);
+               if (isNull)
+                       newelem = (Datum) 0;
+               else
+                       newelem = PG_GETARG_DATUM(1);
        }
        else if (arg1_elemid != InvalidOid)
        {
-               v = PG_GETARG_ARRAYTYPE_P(1);
-               element_type = ARR_ELEMTYPE(v);
-               newelem = PG_GETARG_DATUM(0);
+               if (PG_ARGISNULL(1))
+                       v = construct_empty_array(arg1_elemid);
+               else
+                       v = PG_GETARG_ARRAYTYPE_P(1);
+               isNull = PG_ARGISNULL(0);
+               if (isNull)
+                       newelem = (Datum) 0;
+               else
+                       newelem = PG_GETARG_DATUM(0);
        }
        else
        {
@@ -73,6 +84,8 @@ array_push(PG_FUNCTION_ARGS)
                PG_RETURN_NULL();               /* keep compiler quiet */
        }
 
+       element_type = ARR_ELEMTYPE(v);
+
        if (ARR_NDIM(v) == 1)
        {
                lb = ARR_LBOUND(v);
@@ -84,11 +97,21 @@ array_push(PG_FUNCTION_ARGS)
                        int                     ub = dimv[0] + lb[0] - 1;
 
                        indx = ub + 1;
+                       /* overflow? */
+                       if (indx < ub)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                                errmsg("integer out of range")));
                }
                else
                {
                        /* prepend newelem */
                        indx = lb[0] - 1;
+                       /* overflow? */
+                       if (indx > lb[0])
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+                                                errmsg("integer out of range")));
                }
        }
        else if (ARR_NDIM(v) == 0)
@@ -96,19 +119,19 @@ array_push(PG_FUNCTION_ARGS)
        else
                ereport(ERROR,
                                (errcode(ERRCODE_DATA_EXCEPTION),
-                        errmsg("argument must be empty or one-dimensional array")));
+                                errmsg("argument must be empty or one-dimensional array")));
 
        /*
-        * We arrange to look up info about element type only once per series
-        * of calls, assuming the element type doesn't change underneath us.
+        * We arrange to look up info about element type only once per series of
+        * calls, assuming the element type doesn't change underneath us.
         */
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
        if (my_extra == NULL)
        {
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-                                                                                                sizeof(ArrayMetaState));
+                                                                                                         sizeof(ArrayMetaState));
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
-               my_extra->element_type = InvalidOid;
+               my_extra->element_type = ~element_type;
        }
 
        if (my_extra->element_type != element_type)
@@ -124,8 +147,15 @@ array_push(PG_FUNCTION_ARGS)
        typbyval = my_extra->typbyval;
        typalign = my_extra->typalign;
 
-       result = array_set(v, 1, &indx, newelem, -1,
-                                          typlen, typbyval, typalign, &isNull);
+       result = array_set(v, 1, &indx, newelem, isNull,
+                                          -1, typlen, typbyval, typalign);
+
+       /*
+        * Readjust result's LB to match the input's.  This does nothing in the
+        * append case, but it's the simplest way to implement the prepend case.
+        */
+       if (ARR_NDIM(v) == 1)
+               ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
 
        PG_RETURN_ARRAYTYPE_P(result);
 }
@@ -141,26 +171,46 @@ array_cat(PG_FUNCTION_ARGS)
 {
        ArrayType  *v1,
                           *v2;
+       ArrayType  *result;
        int                *dims,
                           *lbs,
                                ndims,
+                               nitems,
                                ndatabytes,
                                nbytes;
        int                *dims1,
                           *lbs1,
                                ndims1,
+                               nitems1,
                                ndatabytes1;
        int                *dims2,
                           *lbs2,
                                ndims2,
+                               nitems2,
                                ndatabytes2;
        int                     i;
        char       *dat1,
                           *dat2;
+       bits8      *bitmap1,
+                          *bitmap2;
        Oid                     element_type;
        Oid                     element_type1;
        Oid                     element_type2;
-       ArrayType  *result;
+       int32           dataoffset;
+
+       /* Concatenating a null array is a no-op, just return the other input */
+       if (PG_ARGISNULL(0))
+       {
+               if (PG_ARGISNULL(1))
+                       PG_RETURN_NULL();
+               result = PG_GETARG_ARRAYTYPE_P(1);
+               PG_RETURN_ARRAYTYPE_P(result);
+       }
+       if (PG_ARGISNULL(1))
+       {
+               result = PG_GETARG_ARRAYTYPE_P(0);
+               PG_RETURN_ARRAYTYPE_P(result);
+       }
 
        v1 = PG_GETARG_ARRAYTYPE_P(0);
        v2 = PG_GETARG_ARRAYTYPE_P(1);
@@ -194,8 +244,8 @@ array_cat(PG_FUNCTION_ARGS)
        ndims2 = ARR_NDIM(v2);
 
        /*
-        * short circuit - if one input array is empty, and the other is not,
-        * we return the non-empty one as the result
+        * short circuit - if one input array is empty, and the other is not, we
+        * return the non-empty one as the result
         *
         * if both are empty, return the first one
         */
@@ -223,8 +273,12 @@ array_cat(PG_FUNCTION_ARGS)
        dims2 = ARR_DIMS(v2);
        dat1 = ARR_DATA_PTR(v1);
        dat2 = ARR_DATA_PTR(v2);
-       ndatabytes1 = ARR_SIZE(v1) - ARR_OVERHEAD(ndims1);
-       ndatabytes2 = ARR_SIZE(v2) - ARR_OVERHEAD(ndims2);
+       bitmap1 = ARR_NULLBITMAP(v1);
+       bitmap2 = ARR_NULLBITMAP(v2);
+       nitems1 = ArrayGetNItems(ndims1, dims1);
+       nitems2 = ArrayGetNItems(ndims2, dims2);
+       ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
+       ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
 
        if (ndims1 == ndims2)
        {
@@ -245,8 +299,8 @@ array_cat(PG_FUNCTION_ARGS)
                                ereport(ERROR,
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                                                 errmsg("cannot concatenate incompatible arrays"),
-                               errdetail("Arrays with differing element dimensions are "
-                                                 "not compatible for concatenation.")));
+                                       errdetail("Arrays with differing element dimensions are "
+                                                         "not compatible for concatenation.")));
 
                        dims[i] = dims1[i];
                        lbs[i] = lbs1[i];
@@ -255,9 +309,8 @@ array_cat(PG_FUNCTION_ARGS)
        else if (ndims1 == ndims2 - 1)
        {
                /*
-                * resulting array has the second argument as the outer array,
-                * with the first argument appended to the front of the outer
-                * dimension
+                * resulting array has the second argument as the outer array, with
+                * the first argument inserted at the front of the outer dimension
                 */
                ndims = ndims2;
                dims = (int *) palloc(ndims * sizeof(int));
@@ -268,9 +321,6 @@ array_cat(PG_FUNCTION_ARGS)
                /* increment number of elements in outer array */
                dims[0] += 1;
 
-               /* decrement outer array lower bound */
-               lbs[0] -= 1;
-
                /* make sure the added element matches our existing elements */
                for (i = 0; i < ndims1; i++)
                {
@@ -278,8 +328,8 @@ array_cat(PG_FUNCTION_ARGS)
                                ereport(ERROR,
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                                                 errmsg("cannot concatenate incompatible arrays"),
-                                       errdetail("Arrays with differing dimensions are not "
-                                                         "compatible for concatenation.")));
+                                                errdetail("Arrays with differing dimensions are not "
+                                                                  "compatible for concatenation.")));
                }
        }
        else
@@ -287,8 +337,8 @@ array_cat(PG_FUNCTION_ARGS)
                /*
                 * (ndims1 == ndims2 + 1)
                 *
-                * resulting array has the first argument as the outer array, with
-                * the second argument appended to the end of the outer dimension
+                * resulting array has the first argument as the outer array, with the
+                * second argument appended to the end of the outer dimension
                 */
                ndims = ndims1;
                dims = (int *) palloc(ndims * sizeof(int));
@@ -306,25 +356,46 @@ array_cat(PG_FUNCTION_ARGS)
                                ereport(ERROR,
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
                                                 errmsg("cannot concatenate incompatible arrays"),
-                                       errdetail("Arrays with differing dimensions are not "
-                                                         "compatible for concatenation.")));
+                                                errdetail("Arrays with differing dimensions are not "
+                                                                  "compatible for concatenation.")));
                }
        }
 
+       /* Do this mainly for overflow checking */
+       nitems = ArrayGetNItems(ndims, dims);
+
        /* build the result array */
        ndatabytes = ndatabytes1 + ndatabytes2;
-       nbytes = ndatabytes + ARR_OVERHEAD(ndims);
+       if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
+       {
+               dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
+               nbytes = ndatabytes + dataoffset;
+       }
+       else
+       {
+               dataoffset = 0;                 /* marker for no null bitmap */
+               nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
+       }
        result = (ArrayType *) palloc(nbytes);
-
-       result->size = nbytes;
+       SET_VARSIZE(result, nbytes);
        result->ndim = ndims;
-       result->flags = 0;
+       result->dataoffset = dataoffset;
        result->elemtype = element_type;
        memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
        memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
        /* data area is arg1 then arg2 */
        memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
        memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
+       /* handle the null bitmap if needed */
+       if (ARR_HASNULL(result))
+       {
+               array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+                                                 bitmap1, 0,
+                                                 nitems1);
+               array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
+                                                 bitmap2, 0,
+                                                 nitems2);
+       }
 
        PG_RETURN_ARRAYTYPE_P(result);
 }
@@ -348,10 +419,6 @@ create_singleton_array(FunctionCallInfo fcinfo,
        int                     i;
        ArrayMetaState *my_extra;
 
-       if (element_type == 0)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                       errmsg("invalid array element type OID: %u", element_type)));
        if (ndims < 1)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -371,16 +438,16 @@ create_singleton_array(FunctionCallInfo fcinfo,
        }
 
        /*
-        * We arrange to look up info about element type only once per series
-        * of calls, assuming the element type doesn't change underneath us.
+        * We arrange to look up info about element type only once per series of
+        * calls, assuming the element type doesn't change underneath us.
         */
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
        if (my_extra == NULL)
        {
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-                                                                                                sizeof(ArrayMetaState));
+                                                                                                         sizeof(ArrayMetaState));
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
-               my_extra->element_type = InvalidOid;
+               my_extra->element_type = ~element_type;
        }
 
        if (my_extra->element_type != element_type)
@@ -396,6 +463,84 @@ create_singleton_array(FunctionCallInfo fcinfo,
        typbyval = my_extra->typbyval;
        typalign = my_extra->typalign;
 
-       return construct_md_array(dvalues, ndims, dims, lbs, element_type,
+       return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
                                                          typlen, typbyval, typalign);
 }
+
+
+/*
+ * ARRAY_AGG aggregate function
+ */
+Datum
+array_agg_transfn(PG_FUNCTION_ARGS)
+{
+       Oid                     arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+       MemoryContext aggcontext;
+       ArrayBuildState *state;
+       Datum           elem;
+
+       if (arg1_typeid == InvalidOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("could not determine input data type")));
+
+       if (fcinfo->context && IsA(fcinfo->context, AggState))
+               aggcontext = ((AggState *) fcinfo->context)->aggcontext;
+       else if (fcinfo->context && IsA(fcinfo->context, WindowAggState))
+               aggcontext = ((WindowAggState *) fcinfo->context)->wincontext;
+       else
+       {
+               /* cannot be called directly because of internal-type argument */
+               elog(ERROR, "array_agg_transfn called in non-aggregate context");
+               aggcontext = NULL;              /* keep compiler quiet */
+       }
+
+       state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+       elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
+       state = accumArrayResult(state,
+                                                        elem,
+                                                        PG_ARGISNULL(1),
+                                                        arg1_typeid,
+                                                        aggcontext);
+
+       /*
+        * The transition type for array_agg() is declared to be "internal", which
+        * is a pass-by-value type the same size as a pointer.  So we can safely
+        * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
+        */
+       PG_RETURN_POINTER(state);
+}
+
+Datum
+array_agg_finalfn(PG_FUNCTION_ARGS)
+{
+       Datum           result;
+       ArrayBuildState *state;
+       int                     dims[1];
+       int                     lbs[1];
+
+       /*
+        * Test for null before Asserting we are in right context.      This is to
+        * avoid possible Assert failure in 8.4beta installations, where it is
+        * possible for users to create NULL constants of type internal.
+        */
+       if (PG_ARGISNULL(0))
+               PG_RETURN_NULL();               /* returns null iff no input values */
+
+       /* cannot be called directly because of internal-type argument */
+       Assert(fcinfo->context &&
+                  (IsA(fcinfo->context, AggState) ||
+                       IsA(fcinfo->context, WindowAggState)));
+
+       state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+       dims[0] = state->nelems;
+       lbs[0] = 1;
+
+       /* Release working state if regular aggregate, but not if window agg */
+       result = makeMdArrayResult(state, 1, dims, lbs,
+                                                          CurrentMemoryContext,
+                                                          IsA(fcinfo->context, AggState));
+
+       PG_RETURN_DATUM(result);
+}