aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/array_userfuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-06-27 00:33:26 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-06-27 00:33:26 +0000
commitb3c0551edaf390ab7bde4ebcc2299d1b0da686c5 (patch)
tree46512d8ab2d8eeb570722878904f367effb1383d /src/backend/utils/adt/array_userfuncs.c
parent0c985ab5a86b4ca9d8e312bfd0db5536b2be121e (diff)
downloadpostgresql-b3c0551edaf390ab7bde4ebcc2299d1b0da686c5.tar.gz
postgresql-b3c0551edaf390ab7bde4ebcc2299d1b0da686c5.zip
Create real array comparison functions (that use the element datatype's
comparison functions), replacing the highly bogus bitwise array_eq. Create a btree index opclass for ANYARRAY --- it is now possible to create indexes on array columns. Arrange to cache the results of catalog lookups across multiple array operations, instead of repeating the lookups on every call. Add string_to_array and array_to_string functions. Remove singleton_array, array_accum, array_assign, and array_subscript functions, since these were for proof-of-concept and not intended to become supported functions. Minor adjustments to behavior in some corner cases with empty or zero-dimensional arrays. Joe Conway (with some editorializing by Tom Lane).
Diffstat (limited to 'src/backend/utils/adt/array_userfuncs.c')
-rw-r--r--src/backend/utils/adt/array_userfuncs.c277
1 files changed, 93 insertions, 184 deletions
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 519ac8d1885..3aa70b0d332 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -6,7 +6,7 @@
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.3 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.4 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -18,35 +18,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-
-/*-----------------------------------------------------------------------------
- * singleton_array :
- * Form a multi-dimensional array given one starting element.
- *
- * - first argument is the datum with which to build the array
- * - second argument is the number of dimensions the array should have;
- * defaults to 1 if no second argument is provided
- *----------------------------------------------------------------------------
- */
-Datum
-singleton_array(PG_FUNCTION_ARGS)
-{
- Oid elem_type = get_fn_expr_argtype(fcinfo, 0);
- int ndims;
-
- if (elem_type == InvalidOid)
- elog(ERROR, "Cannot determine input datatype");
-
- if (PG_NARGS() == 2)
- ndims = PG_GETARG_INT32(1);
- else
- ndims = 1;
-
- PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
- PG_GETARG_DATUM(0),
- ndims));
-}
-
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
Oid arg0_elemid;
Oid arg1_elemid;
+ ArrayMetaState *my_extra;
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
elog(ERROR, "array_push: cannot determine input data types");
@@ -95,25 +67,54 @@ array_push(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); /* keep compiler quiet */
}
- /* Sanity check: do we have a one-dimensional array */
- if (ARR_NDIM(v) != 1)
- elog(ERROR, "Arrays greater than one-dimension are not supported");
-
- lb = ARR_LBOUND(v);
- dimv = ARR_DIMS(v);
- if (arg0_elemid != InvalidOid)
+ if (ARR_NDIM(v) == 1)
{
- /* append newelem */
- int ub = dimv[0] + lb[0] - 1;
- indx = ub + 1;
+ lb = ARR_LBOUND(v);
+ dimv = ARR_DIMS(v);
+
+ if (arg0_elemid != InvalidOid)
+ {
+ /* append newelem */
+ int ub = dimv[0] + lb[0] - 1;
+ indx = ub + 1;
+ }
+ else
+ {
+ /* prepend newelem */
+ indx = lb[0] - 1;
+ }
}
+ else if (ARR_NDIM(v) == 0)
+ indx = 1;
else
+ elog(ERROR, "only empty and one-dimensional arrays are supported");
+
+
+ /*
+ * 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)
{
- /* prepend newelem */
- indx = lb[0] - 1;
+ fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(ArrayMetaState));
+ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+ my_extra->element_type = InvalidOid;
}
- get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+ if (my_extra->element_type != element_type)
+ {
+ /* Get info about element type */
+ get_typlenbyvalalign(element_type,
+ &my_extra->typlen,
+ &my_extra->typbyval,
+ &my_extra->typalign);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
result = array_set(v, 1, &indx, newelem, -1,
typlen, typbyval, typalign, &isNull);
@@ -145,13 +146,28 @@ array_cat(PG_FUNCTION_ARGS)
/*
* We must have one of the following combinations of inputs:
- * 1) two arrays with ndims1 == ndims2
- * 2) ndims1 == ndims2 - 1
- * 3) ndims1 == ndims2 + 1
+ * 1) one empty array, and one non-empty array
+ * 2) both arrays empty
+ * 3) two arrays with ndims1 == ndims2
+ * 4) ndims1 == ndims2 - 1
+ * 5) ndims1 == ndims2 + 1
*/
ndims1 = ARR_NDIM(v1);
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
+ *
+ * if both are empty, return the first one
+ */
+ if (ndims1 == 0 && ndims2 > 0)
+ PG_RETURN_ARRAYTYPE_P(v2);
+
+ if (ndims2 == 0)
+ PG_RETURN_ARRAYTYPE_P(v1);
+
+ /* the rest fall into combo 2, 3, or 4 */
if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
"%d dimensions", ndims1, ndims2);
@@ -266,147 +282,15 @@ array_cat(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(result);
}
-/*----------------------------------------------------------------------------
- * array_accum :
- * accumulator to build a 1-D array from input values -- this can be used
- * to create custom aggregates.
- *
- * This function is not marked strict, so we have to be careful about nulls.
- *----------------------------------------------------------------------------
- */
-Datum
-array_accum(PG_FUNCTION_ARGS)
-{
- /* return NULL if both arguments are NULL */
- if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
- PG_RETURN_NULL();
-
- /* create a new 1-D array from the new element if the array is NULL */
- if (PG_ARGISNULL(0))
- {
- Oid tgt_type = get_fn_expr_rettype(fcinfo);
- Oid tgt_elem_type;
-
- if (tgt_type == InvalidOid)
- elog(ERROR, "Cannot determine target array type");
- tgt_elem_type = get_element_type(tgt_type);
- if (tgt_elem_type == InvalidOid)
- elog(ERROR, "Target type is not an array");
-
- PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
- PG_GETARG_DATUM(1),
- 1));
- }
-
- /* return the array if the new element is NULL */
- if (PG_ARGISNULL(1))
- PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
-
- /*
- * Otherwise this is equivalent to array_push. We hack the call a little
- * so that array_push can see the fn_expr information.
- */
- return array_push(fcinfo);
-}
-
-/*-----------------------------------------------------------------------------
- * array_assign :
- * assign an element of an array to a new value and return the
- * redefined array
- *----------------------------------------------------------------------------
- */
-Datum
-array_assign(PG_FUNCTION_ARGS)
-{
- ArrayType *v;
- int idx_to_chg;
- Datum newelem;
- int *dimv,
- *lb, ub;
- ArrayType *result;
- bool isNull;
- Oid element_type;
- int16 typlen;
- bool typbyval;
- char typalign;
-
- v = PG_GETARG_ARRAYTYPE_P(0);
- idx_to_chg = PG_GETARG_INT32(1);
- newelem = PG_GETARG_DATUM(2);
-
- /* Sanity check: do we have a one-dimensional array */
- if (ARR_NDIM(v) != 1)
- elog(ERROR, "Arrays greater than one-dimension are not supported");
-
- lb = ARR_LBOUND(v);
- dimv = ARR_DIMS(v);
- ub = dimv[0] + lb[0] - 1;
- if (idx_to_chg < lb[0] || idx_to_chg > ub)
- elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
-
- element_type = ARR_ELEMTYPE(v);
- /* Sanity check: do we have a non-zero element type */
- if (element_type == 0)
- elog(ERROR, "Invalid array element type: %u", element_type);
-
- get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
- result = array_set(v, 1, &idx_to_chg, newelem, -1,
- typlen, typbyval, typalign, &isNull);
-
- PG_RETURN_ARRAYTYPE_P(result);
-}
-
-/*-----------------------------------------------------------------------------
- * array_subscript :
- * return specific element of an array
- *----------------------------------------------------------------------------
- */
-Datum
-array_subscript(PG_FUNCTION_ARGS)
-{
- ArrayType *v;
- int idx;
- int *dimv,
- *lb, ub;
- Datum result;
- bool isNull;
- Oid element_type;
- int16 typlen;
- bool typbyval;
- char typalign;
-
- v = PG_GETARG_ARRAYTYPE_P(0);
- idx = PG_GETARG_INT32(1);
-
- /* Sanity check: do we have a one-dimensional array */
- if (ARR_NDIM(v) != 1)
- elog(ERROR, "Arrays greater than one-dimension are not supported");
-
- lb = ARR_LBOUND(v);
- dimv = ARR_DIMS(v);
- ub = dimv[0] + lb[0] - 1;
- if (idx < lb[0] || idx > ub)
- elog(ERROR, "Cannot return nonexistent array element: %d", idx);
-
- element_type = ARR_ELEMTYPE(v);
- /* Sanity check: do we have a non-zero element type */
- if (element_type == 0)
- elog(ERROR, "Invalid array element type: %u", element_type);
-
- get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
-
- result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
-
- PG_RETURN_DATUM(result);
-}
/*
- * actually does the work for singleton_array(), and array_accum() if it is
- * given a null input array.
+ * used by text_to_array() in varlena.c
*/
ArrayType *
-create_singleton_array(Oid element_type, Datum element, int ndims)
+create_singleton_array(FunctionCallInfo fcinfo,
+ Oid element_type,
+ Datum element,
+ int ndims)
{
Datum dvalues[1];
int16 typlen;
@@ -415,6 +299,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
int dims[MAXDIM];
int lbs[MAXDIM];
int i;
+ ArrayMetaState *my_extra;
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
@@ -429,7 +314,31 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
lbs[i] = 1;
}
- get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
+ /*
+ * 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));
+ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
+ my_extra->element_type = InvalidOid;
+ }
+
+ if (my_extra->element_type != element_type)
+ {
+ /* Get info about element type */
+ get_typlenbyvalalign(element_type,
+ &my_extra->typlen,
+ &my_extra->typbyval,
+ &my_extra->typalign);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
return construct_md_array(dvalues, ndims, dims, lbs, element_type,
typlen, typbyval, typalign);