aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execQual.c8
-rw-r--r--src/backend/executor/nodeSubplan.c122
-rw-r--r--src/backend/parser/parse_coerce.c10
-rw-r--r--src/backend/parser/parse_expr.c4
-rw-r--r--src/backend/parser/parse_oper.c122
-rw-r--r--src/backend/utils/adt/acl.c33
-rw-r--r--src/backend/utils/adt/array_userfuncs.c277
-rw-r--r--src/backend/utils/adt/arrayfuncs.c747
-rw-r--r--src/backend/utils/adt/varlena.c181
-rw-r--r--src/backend/utils/cache/lsyscache.c52
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/pg_amop.h11
-rw-r--r--src/include/catalog/pg_amproc.h3
-rw-r--r--src/include/catalog/pg_opclass.h4
-rw-r--r--src/include/catalog/pg_operator.h14
-rw-r--r--src/include/catalog/pg_proc.h33
-rw-r--r--src/include/parser/parse_oper.h3
-rw-r--r--src/include/utils/acl.h3
-rw-r--r--src/include/utils/array.h55
-rw-r--r--src/include/utils/builtins.h4
-rw-r--r--src/include/utils/lsyscache.h19
-rw-r--r--src/interfaces/ecpg/preproc/preproc.y8
-rw-r--r--src/interfaces/ecpg/preproc/type.c2
-rw-r--r--src/interfaces/ecpg/preproc/variable.c12
-rw-r--r--src/test/regress/expected/arrays.out49
-rw-r--r--src/test/regress/sql/arrays.sql22
26 files changed, 1255 insertions, 547 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 6e0a46e0e7f..ee29c195ae7 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.132 2003/06/25 21:30:28 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.133 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
{
/* Check other sub-arrays are compatible */
if (elem_ndims != ARR_NDIM(array))
- elog(ERROR, "Multiple dimension arrays must have array "
+ elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching number of dimensions");
if (memcmp(elem_dims, ARR_DIMS(array),
elem_ndims * sizeof(int)) != 0)
- elog(ERROR, "Multiple dimension arrays must have array "
+ elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching dimensions");
if (memcmp(elem_lbs, ARR_LBOUND(array),
elem_ndims * sizeof(int)) != 0)
- elog(ERROR, "Multiple dimension arrays must have array "
+ elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching dimensions");
}
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 64567377684..5999ef70770 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.49 2003/06/25 21:30:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.50 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,22 +29,6 @@
#include "utils/lsyscache.h"
-typedef struct ArrayBuildState
-{
- MemoryContext mcontext; /* where all the temp stuff is kept */
- Datum *dvalues; /* array of accumulated Datums */
- /*
- * The allocated size of dvalues[] is always a multiple of
- * ARRAY_ELEMS_CHUNKSIZE
- */
-#define ARRAY_ELEMS_CHUNKSIZE 64
- int nelems; /* number of valid Datums in dvalues[] */
- Oid element_type; /* data type of the Datums */
- int16 typlen; /* needed info about datatype */
- bool typbyval;
- char typalign;
-} ArrayBuildState;
-
static Datum ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
@@ -54,12 +38,6 @@ static Datum ExecScanSubPlan(SubPlanState *node,
static void buildSubPlanHash(SubPlanState *node);
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
static bool tupleAllNulls(HeapTuple tuple);
-static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
- Datum dvalue, bool disnull,
- Oid element_type,
- MemoryContext rcontext);
-static Datum makeArrayResult(ArrayBuildState *astate,
- MemoryContext rcontext);
/* ----------------------------------------------------------------
@@ -1099,101 +1077,3 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
parent->chgParam = bms_add_member(parent->chgParam, paramid);
}
}
-
-/*
- * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
- *
- * astate is working state (NULL on first call)
- * rcontext is where to keep working state
- */
-static ArrayBuildState *
-accumArrayResult(ArrayBuildState *astate,
- Datum dvalue, bool disnull,
- Oid element_type,
- MemoryContext rcontext)
-{
- MemoryContext arr_context,
- oldcontext;
-
- if (astate == NULL)
- {
- /* First time through --- initialize */
-
- /* Make a temporary context to hold all the junk */
- arr_context = AllocSetContextCreate(rcontext,
- "ARRAY_SUBLINK Result",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- oldcontext = MemoryContextSwitchTo(arr_context);
- astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
- astate->mcontext = arr_context;
- astate->dvalues = (Datum *)
- palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
- astate->nelems = 0;
- astate->element_type = element_type;
- get_typlenbyvalalign(element_type,
- &astate->typlen,
- &astate->typbyval,
- &astate->typalign);
- }
- else
- {
- oldcontext = MemoryContextSwitchTo(astate->mcontext);
- Assert(astate->element_type == element_type);
- /* enlarge dvalues[] if needed */
- if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
- astate->dvalues = (Datum *)
- repalloc(astate->dvalues,
- (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
- }
-
- if (disnull)
- elog(ERROR, "NULL elements not allowed in Arrays");
-
- /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
- astate->dvalues[astate->nelems++] =
- datumCopy(dvalue, astate->typbyval, astate->typlen);
-
- MemoryContextSwitchTo(oldcontext);
-
- return astate;
-}
-
-/*
- * makeArrayResult - produce final result of ARRAY_SUBLINK
- *
- * astate is working state (not NULL)
- * rcontext is where to construct result
- */
-static Datum
-makeArrayResult(ArrayBuildState *astate,
- MemoryContext rcontext)
-{
- ArrayType *result;
- int dims[1];
- int lbs[1];
- MemoryContext oldcontext;
-
- /* Build the final array result in rcontext */
- oldcontext = MemoryContextSwitchTo(rcontext);
-
- dims[0] = astate->nelems;
- lbs[0] = 1;
-
- result = construct_md_array(astate->dvalues,
- 1,
- dims,
- lbs,
- astate->element_type,
- astate->typlen,
- astate->typbyval,
- astate->typalign);
-
- MemoryContextSwitchTo(oldcontext);
-
- /* Clean up all the junk */
- MemoryContextDelete(astate->mcontext);
-
- return PointerGetDatum(result);
-}
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 987d129027f..eba6f5a1333 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.100 2003/06/25 21:30:31 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.101 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1145,7 +1145,8 @@ IsPreferredType(CATEGORY category, Oid type)
* invokable, no-function-needed pg_cast entry. Also, a domain is always
* binary-coercible to its base type, though *not* vice versa (in the other
* direction, one must apply domain constraint checks before accepting the
- * value as legitimate).
+ * value as legitimate). We also need to special-case the polymorphic
+ * ANYARRAY type.
*
* This function replaces IsBinaryCompatible(), which was an inherently
* symmetric test. Since the pg_cast entries aren't necessarily symmetric,
@@ -1170,6 +1171,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
+ /* Also accept any array type as coercible to ANYARRAY */
+ if (targettype == ANYARRAYOID)
+ if (get_element_type(srctype) != InvalidOid)
+ return true;
+
/* Else look in pg_cast */
tuple = SearchSysCache(CASTSOURCETARGET,
ObjectIdGetDatum(srctype),
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 1e655217103..415242bc7a8 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.150 2003/06/25 21:30:31 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.151 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -743,7 +743,7 @@ transformExpr(ParseState *pstate, Node *expr)
ArrayExpr *e = (ArrayExpr *) lfirst(element);
if (!IsA(e, ArrayExpr))
- elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
+ elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
if (ndims == 0)
ndims = e->ndims;
else if (e->ndims != ndims)
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 69edb4b85bd..983041eb45b 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.66 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.67 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -137,28 +137,50 @@ Operator
equality_oper(Oid argtype, bool noError)
{
Operator optup;
+ Oid elem_type;
/*
- * Look for an "=" operator for the datatype. We require it to be
- * an exact or binary-compatible match, since most callers are not
- * prepared to cope with adding any run-time type coercion steps.
+ * If the datatype is an array, then we can use array_eq ... but only
+ * if there is a suitable equality operator for the element type.
+ * (We must run this test first, since compatible_oper will find
+ * array_eq, but would not notice the lack of an element operator.)
*/
- optup = compatible_oper(makeList1(makeString("=")),
- argtype, argtype, true);
- if (optup != NULL)
+ elem_type = get_element_type(argtype);
+ if (OidIsValid(elem_type))
+ {
+ optup = equality_oper(elem_type, true);
+ if (optup != NULL)
+ {
+ ReleaseSysCache(optup);
+ return SearchSysCache(OPEROID,
+ ObjectIdGetDatum(ARRAY_EQ_OP),
+ 0, 0, 0);
+ }
+ }
+ else
{
/*
- * Only believe that it's equality if it's mergejoinable,
- * hashjoinable, or uses eqsel() as oprrest.
+ * Look for an "=" operator for the datatype. We require it to be
+ * an exact or binary-compatible match, since most callers are not
+ * prepared to cope with adding any run-time type coercion steps.
*/
- Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
+ optup = compatible_oper(makeList1(makeString("=")),
+ argtype, argtype, true);
+ if (optup != NULL)
+ {
+ /*
+ * Only believe that it's equality if it's mergejoinable,
+ * hashjoinable, or uses eqsel() as oprrest.
+ */
+ Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
- if (OidIsValid(pgopform->oprlsortop) ||
- pgopform->oprcanhash ||
- pgopform->oprrest == F_EQSEL)
- return optup;
+ if (OidIsValid(pgopform->oprlsortop) ||
+ pgopform->oprcanhash ||
+ pgopform->oprrest == F_EQSEL)
+ return optup;
- ReleaseSysCache(optup);
+ ReleaseSysCache(optup);
+ }
}
if (!noError)
elog(ERROR, "Unable to identify an equality operator for type %s",
@@ -175,27 +197,50 @@ Operator
ordering_oper(Oid argtype, bool noError)
{
Operator optup;
+ Oid elem_type;
/*
- * Find the type's equality operator, and use its lsortop (it *must*
- * be mergejoinable). We use this definition because for sorting and
- * grouping purposes, it's important that the equality and ordering
- * operators are consistent.
+ * If the datatype is an array, then we can use array_lt ... but only
+ * if there is a suitable ordering operator for the element type.
+ * (We must run this test first, since the code below would find
+ * array_lt if there's an element = operator, but would not notice the
+ * lack of an element < operator.)
*/
- optup = equality_oper(argtype, noError);
- if (optup != NULL)
+ elem_type = get_element_type(argtype);
+ if (OidIsValid(elem_type))
{
- Oid lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
-
- ReleaseSysCache(optup);
-
- if (OidIsValid(lsortop))
+ optup = ordering_oper(elem_type, true);
+ if (optup != NULL)
{
- optup = SearchSysCache(OPEROID,
- ObjectIdGetDatum(lsortop),
- 0, 0, 0);
- if (optup != NULL)
- return optup;
+ ReleaseSysCache(optup);
+ return SearchSysCache(OPEROID,
+ ObjectIdGetDatum(ARRAY_LT_OP),
+ 0, 0, 0);
+ }
+ }
+ else
+ {
+ /*
+ * Find the type's equality operator, and use its lsortop (it *must*
+ * be mergejoinable). We use this definition because for sorting and
+ * grouping purposes, it's important that the equality and ordering
+ * operators are consistent.
+ */
+ optup = equality_oper(argtype, noError);
+ if (optup != NULL)
+ {
+ Oid lsortop;
+
+ lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
+ ReleaseSysCache(optup);
+ if (OidIsValid(lsortop))
+ {
+ optup = SearchSysCache(OPEROID,
+ ObjectIdGetDatum(lsortop),
+ 0, 0, 0);
+ if (optup != NULL)
+ return optup;
+ }
}
}
if (!noError)
@@ -237,6 +282,21 @@ ordering_oper_opid(Oid argtype)
return result;
}
+/*
+ * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+ */
+Oid
+ordering_oper_funcid(Oid argtype)
+{
+ Operator optup;
+ Oid result;
+
+ optup = ordering_oper(argtype, false);
+ result = oprfuncid(optup);
+ ReleaseSysCache(optup);
+ return result;
+}
+
/* given operator tuple, return the operator OID */
Oid
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 415a086b7dd..878f4ebe2a1 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.90 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.91 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,7 +34,7 @@ static const char *getid(const char *s, char *n);
static void putid(char *p, const char *s);
static Acl *allocacl(int n);
static const char *aclparse(const char *s, AclItem *aip);
-static bool aclitemeq(const AclItem *a1, const AclItem *a2);
+static bool aclitem_match(const AclItem *a1, const AclItem *a2);
static Acl *recursive_revoke(Acl *acl, AclId grantee,
AclMode revoke_privs, DropBehavior behavior);
@@ -415,18 +415,33 @@ aclitemout(PG_FUNCTION_ARGS)
}
/*
- * aclitemeq
- * Two AclItems are considered equal iff they have the same
+ * aclitem_match
+ * Two AclItems are considered to match iff they have the same
* grantee and grantor; the privileges are ignored.
*/
static bool
-aclitemeq(const AclItem *a1, const AclItem *a2)
+aclitem_match(const AclItem *a1, const AclItem *a2)
{
return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
a1->ai_grantee == a2->ai_grantee &&
a1->ai_grantor == a2->ai_grantor;
}
+/*
+ * aclitem equality operator
+ */
+Datum
+aclitem_eq(PG_FUNCTION_ARGS)
+{
+ AclItem *a1 = PG_GETARG_ACLITEM_P(0);
+ AclItem *a2 = PG_GETARG_ACLITEM_P(1);
+ bool result;
+
+ result = a1->ai_privs == a2->ai_privs &&
+ a1->ai_grantee == a2->ai_grantee &&
+ a1->ai_grantor == a2->ai_grantor;
+ PG_RETURN_BOOL(result);
+}
/*
* acldefault() --- create an ACL describing default access permissions
@@ -535,7 +550,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
for (dst = 0; dst < num; ++dst)
{
- if (aclitemeq(mod_aip, old_aip + dst))
+ if (aclitem_match(mod_aip, old_aip + dst))
{
/* found a match, so modify existing item */
new_acl = allocacl(num);
@@ -685,8 +700,10 @@ aclremove(PG_FUNCTION_ARGS)
old_aip = ACL_DAT(old_acl);
/* Search for the matching entry */
- for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
- ;
+ for (dst = 0;
+ dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
+ ++dst)
+ /* continue */ ;
if (dst >= old_num)
{
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);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index c03d8c861d1..808353a63d4 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.91 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.92 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,8 +21,10 @@
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_oper.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -70,16 +72,6 @@
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
-/* I/O function selector for system_cache_lookup */
-typedef enum IOFuncSelector
-{
- IOFunc_input,
- IOFunc_output,
- IOFunc_receive,
- IOFunc_send
-} IOFuncSelector;
-
-
static int ArrayCount(char *str, int *dim, char typdelim);
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typelem, int32 typmod,
@@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
static void CopyArrayEls(char *p, Datum *values, int nitems,
int typlen, bool typbyval, char typalign,
bool freedata);
-static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
- int *typlen, bool *typbyval,
- char *typdelim, Oid *typelem,
- Oid *proc, char *typalign);
static Datum ArrayCast(char *value, bool byval, int len);
static int ArrayCastAndSet(Datum src,
int typlen, bool typbyval, char typalign,
@@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
char *destPtr,
int *st, int *endp, char *srcPtr,
int typlen, bool typbyval, char typalign);
-
+static int array_cmp(FunctionCallInfo fcinfo);
/*---------------------------------------------------------------------
* array_in :
@@ -139,12 +127,11 @@ array_in(PG_FUNCTION_ARGS)
* elements */
int typlen;
bool typbyval;
+ char typalign;
char typdelim;
- Oid typinput;
Oid typelem;
char *string_save,
*p;
- FmgrInfo inputproc;
int i,
nitems;
int32 nbytes;
@@ -153,13 +140,38 @@ array_in(PG_FUNCTION_ARGS)
int ndim,
dim[MAXDIM],
lBound[MAXDIM];
- char typalign;
+ ArrayMetaState *my_extra;
- /* Get info about element type, including its input conversion proc */
- system_cache_lookup(element_type, IOFunc_input,
- &typlen, &typbyval, &typdelim,
- &typelem, &typinput, &typalign);
- fmgr_info(typinput, &inputproc);
+ /*
+ * We arrange to look up info about element type, including its input
+ * conversion proc, 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, including its input conversion proc */
+ get_type_io_data(element_type, IOFunc_input,
+ &my_extra->typlen, &my_extra->typbyval,
+ &my_extra->typalign, &my_extra->typdelim,
+ &my_extra->typelem, &my_extra->typiofunc);
+ fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+ fcinfo->flinfo->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+ typdelim = my_extra->typdelim;
+ typelem = my_extra->typelem;
/* Make a modifiable copy of the input */
/* XXX why are we allocating an extra 2 bytes here? */
@@ -262,7 +274,7 @@ array_in(PG_FUNCTION_ARGS)
if (*p != '{')
elog(ERROR, "array_in: missing left brace");
- dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
+ dataPtr = ReadArrayStr(p, nitems, ndim, dim, &my_extra->proc, typelem,
typmod, typdelim, typlen, typbyval, typalign,
&nbytes);
nbytes += ARR_OVERHEAD(ndim);
@@ -618,11 +630,9 @@ array_out(PG_FUNCTION_ARGS)
Oid element_type;
int typlen;
bool typbyval;
- char typdelim;
- Oid typoutput,
- typelem;
- FmgrInfo outputproc;
char typalign;
+ char typdelim;
+ Oid typelem;
char *p,
*tmp,
*retval,
@@ -636,12 +646,40 @@ array_out(PG_FUNCTION_ARGS)
indx[MAXDIM];
int ndim,
*dim;
+ ArrayMetaState *my_extra;
element_type = ARR_ELEMTYPE(v);
- system_cache_lookup(element_type, IOFunc_output,
- &typlen, &typbyval, &typdelim,
- &typelem, &typoutput, &typalign);
- fmgr_info(typoutput, &outputproc);
+
+ /*
+ * We arrange to look up info about element type, including its output
+ * conversion proc, 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, including its output conversion proc */
+ get_type_io_data(element_type, IOFunc_output,
+ &my_extra->typlen, &my_extra->typbyval,
+ &my_extra->typalign, &my_extra->typdelim,
+ &my_extra->typelem, &my_extra->typiofunc);
+ fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+ fcinfo->flinfo->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+ typdelim = my_extra->typdelim;
+ typelem = my_extra->typelem;
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
@@ -668,7 +706,7 @@ array_out(PG_FUNCTION_ARGS)
bool nq;
itemvalue = fetch_att(p, typbyval, typlen);
- values[i] = DatumGetCString(FunctionCall3(&outputproc,
+ values[i] = DatumGetCString(FunctionCall3(&my_extra->proc,
itemvalue,
ObjectIdGetDatum(typelem),
Int32GetDatum(-1)));
@@ -786,10 +824,8 @@ array_recv(PG_FUNCTION_ARGS)
Oid element_type;
int typlen;
bool typbyval;
- char typdelim;
- Oid typreceive;
+ char typalign;
Oid typelem;
- FmgrInfo receiveproc;
int i,
nitems;
int32 nbytes;
@@ -799,7 +835,7 @@ array_recv(PG_FUNCTION_ARGS)
flags,
dim[MAXDIM],
lBound[MAXDIM];
- char typalign;
+ ArrayMetaState *my_extra;
/* Get the array header information */
ndim = pq_getmsgint(buf, 4);
@@ -831,16 +867,40 @@ array_recv(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(retval);
}
- /* Get info about element type, including its receive conversion proc */
- system_cache_lookup(element_type, IOFunc_receive,
- &typlen, &typbyval, &typdelim,
- &typelem, &typreceive, &typalign);
- if (!OidIsValid(typreceive))
- elog(ERROR, "No binary input function available for type %s",
- format_type_be(element_type));
- fmgr_info(typreceive, &receiveproc);
+ /*
+ * We arrange to look up info about element type, including its receive
+ * conversion proc, 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, including its receive proc */
+ get_type_io_data(element_type, IOFunc_receive,
+ &my_extra->typlen, &my_extra->typbyval,
+ &my_extra->typalign, &my_extra->typdelim,
+ &my_extra->typelem, &my_extra->typiofunc);
+ if (!OidIsValid(my_extra->typiofunc))
+ elog(ERROR, "No binary input function available for type %s",
+ format_type_be(element_type));
+ fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+ fcinfo->flinfo->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+ typelem = my_extra->typelem;
- dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
+ dataPtr = ReadArrayBinary(buf, nitems, &my_extra->proc, typelem,
typlen, typbyval, typalign,
&nbytes);
nbytes += ARR_OVERHEAD(ndim);
@@ -965,26 +1025,51 @@ array_send(PG_FUNCTION_ARGS)
Oid element_type;
int typlen;
bool typbyval;
- char typdelim;
- Oid typsend,
- typelem;
- FmgrInfo sendproc;
char typalign;
+ Oid typelem;
char *p;
int nitems,
i;
int ndim,
*dim;
StringInfoData buf;
+ ArrayMetaState *my_extra;
/* Get information about the element type and the array dimensions */
element_type = ARR_ELEMTYPE(v);
- system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
- &typdelim, &typelem, &typsend, &typalign);
- if (!OidIsValid(typsend))
- elog(ERROR, "No binary output function available for type %s",
- format_type_be(element_type));
- fmgr_info(typsend, &sendproc);
+
+ /*
+ * We arrange to look up info about element type, including its send
+ * conversion proc, 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, including its send proc */
+ get_type_io_data(element_type, IOFunc_send,
+ &my_extra->typlen, &my_extra->typbyval,
+ &my_extra->typalign, &my_extra->typdelim,
+ &my_extra->typelem, &my_extra->typiofunc);
+ if (!OidIsValid(my_extra->typiofunc))
+ elog(ERROR, "No binary output function available for type %s",
+ format_type_be(element_type));
+ fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+ fcinfo->flinfo->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+ typelem = my_extra->typelem;
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
@@ -1011,7 +1096,7 @@ array_send(PG_FUNCTION_ARGS)
itemvalue = fetch_att(p, typbyval, typlen);
- outputbytes = DatumGetByteaP(FunctionCall2(&sendproc,
+ outputbytes = DatumGetByteaP(FunctionCall2(&my_extra->proc,
itemvalue,
ObjectIdGetDatum(typelem)));
/* We assume the result will not have been toasted */
@@ -1476,6 +1561,26 @@ array_set(ArrayType *array,
array = DatumGetArrayTypeP(PointerGetDatum(array));
ndim = ARR_NDIM(array);
+
+ /*
+ * if number of dims is zero, i.e. an empty array, create an array
+ * with nSubscripts dimensions, and set the lower bounds to the supplied
+ * subscripts
+ */
+ if (ndim == 0)
+ {
+ Oid elmtype = ARR_ELEMTYPE(array);
+
+ for (i = 0; i < nSubscripts; i++)
+ {
+ dim[i] = 1;
+ lb[i] = indx[i];
+ }
+
+ return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
+ elmlen, elmbyval, elmalign);
+ }
+
if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
elog(ERROR, "Invalid array subscripts");
@@ -1632,6 +1737,31 @@ array_set_slice(ArrayType *array,
/* note: we assume srcArray contains no toasted elements */
ndim = ARR_NDIM(array);
+
+ /*
+ * if number of dims is zero, i.e. an empty array, create an array
+ * with nSubscripts dimensions, and set the upper and lower bounds
+ * to the supplied subscripts
+ */
+ if (ndim == 0)
+ {
+ Datum *dvalues;
+ int nelems;
+ Oid elmtype = ARR_ELEMTYPE(array);
+
+ deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
+ &dvalues, &nelems);
+
+ for (i = 0; i < nSubscripts; i++)
+ {
+ dim[i] = 1 + upperIndx[i] - lowerIndx[i];
+ lb[i] = lowerIndx[i];
+ }
+
+ return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
+ elmlen, elmbyval, elmalign);
+ }
+
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
elog(ERROR, "Invalid array subscripts");
@@ -1807,10 +1937,14 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
int typlen;
bool typbyval;
char typalign;
- char typdelim;
- Oid typelem;
- Oid proc;
char *s;
+ typedef struct {
+ ArrayMetaState inp_extra;
+ ArrayMetaState ret_extra;
+ } am_extra;
+ am_extra *my_extra;
+ ArrayMetaState *inp_extra;
+ ArrayMetaState *ret_extra;
/* Get input array */
if (fcinfo->nargs < 1)
@@ -1829,11 +1963,51 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
if (nitems <= 0)
PG_RETURN_ARRAYTYPE_P(v);
- /* Lookup source and result types. Unneeded variables are reused. */
- system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
- &typdelim, &typelem, &proc, &inp_typalign);
- system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
- &typdelim, &typelem, &proc, &typalign);
+ /*
+ * We arrange to look up info about input and return element types only
+ * once per series of calls, assuming the element type doesn't change
+ * underneath us.
+ */
+ my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
+ if (my_extra == NULL)
+ {
+ fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(am_extra));
+ my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
+ inp_extra = &my_extra->inp_extra;
+ inp_extra->element_type = InvalidOid;
+ ret_extra = &my_extra->ret_extra;
+ ret_extra->element_type = InvalidOid;
+ }
+ else
+ {
+ inp_extra = &my_extra->inp_extra;
+ ret_extra = &my_extra->ret_extra;
+ }
+
+ if (inp_extra->element_type != inpType)
+ {
+ get_typlenbyvalalign(inpType,
+ &inp_extra->typlen,
+ &inp_extra->typbyval,
+ &inp_extra->typalign);
+ inp_extra->element_type = inpType;
+ }
+ inp_typlen = inp_extra->typlen;
+ inp_typbyval = inp_extra->typbyval;
+ inp_typalign = inp_extra->typalign;
+
+ if (ret_extra->element_type != retType)
+ {
+ get_typlenbyvalalign(retType,
+ &ret_extra->typlen,
+ &ret_extra->typbyval,
+ &ret_extra->typalign);
+ ret_extra->element_type = retType;
+ }
+ typlen = ret_extra->typlen;
+ typbyval = ret_extra->typbyval;
+ typalign = ret_extra->typalign;
/* Allocate temporary array for new values */
values = (Datum *) palloc(nitems * sizeof(Datum));
@@ -2049,8 +2223,6 @@ deconstruct_array(ArrayType *array,
* compares two arrays for equality
* result :
* returns true if the arrays are equal, false otherwise.
- *
- * XXX bitwise equality is pretty bogus ...
*-----------------------------------------------------------------------------
*/
Datum
@@ -2058,12 +2230,101 @@ array_eq(PG_FUNCTION_ARGS)
{
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
+ char *p1 = (char *) ARR_DATA_PTR(array1);
+ char *p2 = (char *) ARR_DATA_PTR(array2);
+ int ndims1 = ARR_NDIM(array1);
+ int ndims2 = ARR_NDIM(array2);
+ int *dims1 = ARR_DIMS(array1);
+ int *dims2 = ARR_DIMS(array2);
+ int nitems1 = ArrayGetNItems(ndims1, dims1);
+ int nitems2 = ArrayGetNItems(ndims2, dims2);
+ Oid element_type = ARR_ELEMTYPE(array1);
+ FmgrInfo *ae_fmgr_info = fcinfo->flinfo;
bool result = true;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ ArrayMetaState *my_extra;
+ FunctionCallInfoData locfcinfo;
- if (ARR_SIZE(array1) != ARR_SIZE(array2))
- result = false;
- else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
+ if (element_type != ARR_ELEMTYPE(array2))
+ elog(ERROR, "cannot compare arrays of different element types");
+
+ /* fast path if the arrays do not have the same number of elements */
+ if (nitems1 != nitems2)
result = false;
+ else
+ {
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us.
+ */
+ my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+ if (my_extra == NULL)
+ {
+ ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
+ sizeof(ArrayMetaState));
+ my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
+ my_extra->element_type = InvalidOid;
+ }
+
+ if (my_extra->element_type != element_type)
+ {
+ Oid opfuncid = equality_oper_funcid(element_type);
+
+ get_typlenbyvalalign(element_type,
+ &my_extra->typlen,
+ &my_extra->typbyval,
+ &my_extra->typalign);
+ fmgr_info_cxt(opfuncid, &my_extra->proc,
+ ae_fmgr_info->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+
+ /*
+ * apply the operator to each pair of array elements.
+ */
+ MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+ locfcinfo.flinfo = &my_extra->proc;
+ locfcinfo.nargs = 2;
+
+ /* Loop over source data */
+ for (i = 0; i < nitems1; i++)
+ {
+ Datum elt1;
+ Datum elt2;
+ bool oprresult;
+
+ /* Get element pair */
+ elt1 = fetch_att(p1, typbyval, typlen);
+ elt2 = fetch_att(p2, typbyval, typlen);
+
+ p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
+ p1 = (char *) att_align(p1, typalign);
+
+ p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
+ p2 = (char *) att_align(p2, typalign);
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elt2;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (!oprresult)
+ {
+ result = false;
+ break;
+ }
+ }
+ }
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(array1, 0);
@@ -2073,54 +2334,172 @@ array_eq(PG_FUNCTION_ARGS)
}
-/***************************************************************************/
-/******************| Support Routines |*****************/
-/***************************************************************************/
+/*-----------------------------------------------------------------------------
+ * array-array bool operators:
+ * Given two arrays, iterate comparison operators
+ * over the array. Uses logic similar to text comparison
+ * functions, except element-by-element instead of
+ * character-by-character.
+ *----------------------------------------------------------------------------
+ */
+Datum
+array_ne(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
+}
-static void
-system_cache_lookup(Oid element_type,
- IOFuncSelector which_func,
- int *typlen,
- bool *typbyval,
- char *typdelim,
- Oid *typelem,
- Oid *proc,
- char *typalign)
+Datum
+array_lt(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
+}
+
+Datum
+array_gt(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
+}
+
+Datum
+array_le(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
+}
+
+Datum
+array_ge(PG_FUNCTION_ARGS)
{
- HeapTuple typeTuple;
- Form_pg_type typeStruct;
-
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(element_type),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "cache lookup failed for type %u", element_type);
- typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
- *typlen = typeStruct->typlen;
- *typbyval = typeStruct->typbyval;
- *typdelim = typeStruct->typdelim;
- *typelem = typeStruct->typelem;
- *typalign = typeStruct->typalign;
- switch (which_func)
- {
- case IOFunc_input:
- *proc = typeStruct->typinput;
- break;
- case IOFunc_output:
- *proc = typeStruct->typoutput;
- break;
- case IOFunc_receive:
- *proc = typeStruct->typreceive;
- break;
- case IOFunc_send:
- *proc = typeStruct->typsend;
- break;
- }
- ReleaseSysCache(typeTuple);
+ PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
+}
+
+Datum
+btarraycmp(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_INT32(array_cmp(fcinfo));
}
/*
+ * array_cmp()
+ * Internal comparison function for arrays.
+ *
+ * Returns -1, 0 or 1
+ */
+static int
+array_cmp(FunctionCallInfo fcinfo)
+{
+ ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
+ ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
+ FmgrInfo *ac_fmgr_info = fcinfo->flinfo;
+ Datum opresult;
+ int result = 0;
+ Oid element_type = InvalidOid;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ Datum *dvalues1;
+ int nelems1;
+ Datum *dvalues2;
+ int nelems2;
+ int min_nelems;
+ int i;
+ typedef struct
+ {
+ Oid element_type;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ FmgrInfo eqproc;
+ FmgrInfo ordproc;
+ } ac_extra;
+ ac_extra *my_extra;
+
+ element_type = ARR_ELEMTYPE(array1);
+ if (element_type != ARR_ELEMTYPE(array2))
+ elog(ERROR, "cannot compare arrays of different element types");
+
+ /*
+ * We arrange to look up the element type info and related functions
+ * only once per series of calls, assuming the element type doesn't
+ * change underneath us.
+ */
+ my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
+ if (my_extra == NULL)
+ {
+ ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
+ sizeof(ac_extra));
+ my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
+ my_extra->element_type = InvalidOid;
+ }
+
+ if (my_extra->element_type != element_type)
+ {
+ Oid eqfuncid = equality_oper_funcid(element_type);
+ Oid ordfuncid = ordering_oper_funcid(element_type);
+
+ get_typlenbyvalalign(element_type,
+ &my_extra->typlen,
+ &my_extra->typbyval,
+ &my_extra->typalign);
+ fmgr_info_cxt(eqfuncid, &my_extra->eqproc,
+ ac_fmgr_info->fn_mcxt);
+ fmgr_info_cxt(ordfuncid, &my_extra->ordproc,
+ ac_fmgr_info->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+
+ /* extract a C array of arg array datums */
+ deconstruct_array(array1, element_type, typlen, typbyval, typalign,
+ &dvalues1, &nelems1);
+
+ deconstruct_array(array2, element_type, typlen, typbyval, typalign,
+ &dvalues2, &nelems2);
+
+ min_nelems = Min(nelems1, nelems2);
+ for (i = 0; i < min_nelems; i++)
+ {
+ /* are they equal */
+ opresult = FunctionCall2(&my_extra->eqproc,
+ dvalues1[i], dvalues2[i]);
+
+ if (!DatumGetBool(opresult))
+ {
+ /* nope, see if arg1 is less than arg2 */
+ opresult = FunctionCall2(&my_extra->ordproc,
+ dvalues1[i], dvalues2[i]);
+ if (DatumGetBool(opresult))
+ {
+ /* arg1 is less than arg2 */
+ result = -1;
+ break;
+ }
+ else
+ {
+ /* arg1 is greater than arg2 */
+ result = 1;
+ break;
+ }
+ }
+ }
+
+ if ((result == 0) && (nelems1 != nelems2))
+ result = (nelems1 < nelems2) ? -1 : 1;
+
+ /* Avoid leaking memory when handed toasted input. */
+ PG_FREE_IF_COPY(array1, 0);
+ PG_FREE_IF_COPY(array2, 1);
+
+ return result;
+}
+
+
+/***************************************************************************/
+/******************| Support Routines |*****************/
+/***************************************************************************/
+
+/*
* Fetch array element at pointer, converted correctly to a Datum
*/
static Datum
@@ -2423,6 +2802,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
if (tgt_elem_type == InvalidOid)
elog(ERROR, "Target type is not an array");
+ /*
+ * We don't deal with domain constraints yet, so bail out.
+ * This isn't currently a problem, because we also don't
+ * support arrays of domain type elements either. But in the
+ * future we might. At that point consideration should be given
+ * to removing the check below and adding a domain constraints
+ * check to the coercion.
+ */
+ if (getBaseType(tgt_elem_type) != tgt_elem_type)
+ elog(ERROR, "array coercion to domain type elements not " \
+ "currently supported");
+
if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
COERCION_EXPLICIT, &funcId))
{
@@ -2439,10 +2830,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
}
/*
- * If it's binary-compatible, return the array unmodified.
+ * If it's binary-compatible, modify the element type in the array header,
+ * but otherwise leave the array as we received it.
*/
if (my_extra->coerce_finfo.fn_oid == InvalidOid)
- PG_RETURN_ARRAYTYPE_P(src);
+ {
+ ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
+
+ ARR_ELEMTYPE(result) = my_extra->desttype;
+ PG_RETURN_ARRAYTYPE_P(result);
+ }
/*
* Use array_map to apply the function to each array element.
@@ -2454,3 +2851,121 @@ array_type_coerce(PG_FUNCTION_ARGS)
return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
}
+
+/*
+ * accumArrayResult - accumulate one (more) Datum for an array result
+ *
+ * astate is working state (NULL on first call)
+ * rcontext is where to keep working state
+ */
+ArrayBuildState *
+accumArrayResult(ArrayBuildState *astate,
+ Datum dvalue, bool disnull,
+ Oid element_type,
+ MemoryContext rcontext)
+{
+ MemoryContext arr_context,
+ oldcontext;
+
+ if (astate == NULL)
+ {
+ /* First time through --- initialize */
+
+ /* Make a temporary context to hold all the junk */
+ arr_context = AllocSetContextCreate(rcontext,
+ "accumArrayResult",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(arr_context);
+ astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
+ astate->mcontext = arr_context;
+ astate->dvalues = (Datum *)
+ palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
+ astate->nelems = 0;
+ astate->element_type = element_type;
+ get_typlenbyvalalign(element_type,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+ }
+ else
+ {
+ oldcontext = MemoryContextSwitchTo(astate->mcontext);
+ Assert(astate->element_type == element_type);
+ /* enlarge dvalues[] if needed */
+ if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
+ astate->dvalues = (Datum *)
+ repalloc(astate->dvalues,
+ (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
+ }
+
+ if (disnull)
+ elog(ERROR, "NULL elements not allowed in Arrays");
+
+ /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
+ astate->dvalues[astate->nelems++] =
+ datumCopy(dvalue, astate->typbyval, astate->typlen);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return astate;
+}
+
+/*
+ * makeArrayResult - produce 1-D final result of accumArrayResult
+ *
+ * astate is working state (not NULL)
+ * rcontext is where to construct result
+ */
+Datum
+makeArrayResult(ArrayBuildState *astate,
+ MemoryContext rcontext)
+{
+ int dims[1];
+ int lbs[1];
+
+ dims[0] = astate->nelems;
+ lbs[0] = 1;
+
+ return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
+}
+
+/*
+ * makeMdArrayResult - produce multi-D final result of accumArrayResult
+ *
+ * beware: no check that specified dimensions match the number of values
+ * accumulated.
+ *
+ * astate is working state (not NULL)
+ * rcontext is where to construct result
+ */
+Datum
+makeMdArrayResult(ArrayBuildState *astate,
+ int ndims,
+ int *dims,
+ int *lbs,
+ MemoryContext rcontext)
+{
+ ArrayType *result;
+ MemoryContext oldcontext;
+
+ /* Build the final array result in rcontext */
+ oldcontext = MemoryContextSwitchTo(rcontext);
+
+ result = construct_md_array(astate->dvalues,
+ ndims,
+ dims,
+ lbs,
+ astate->element_type,
+ astate->typlen,
+ astate->typbyval,
+ astate->typalign);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Clean up all the junk */
+ MemoryContextDelete(astate->mcontext);
+
+ return PointerGetDatum(result);
+}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index ce6443647e2..37413bcd59f 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.100 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.101 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,11 +19,14 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "access/tuptoaster.h"
+#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "libpq/crypt.h"
#include "libpq/pqformat.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/pg_locale.h"
+#include "utils/lsyscache.h"
typedef struct varlena unknown;
@@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
if (fldnum == 1) /* first field - just return the input
* string */
PG_RETURN_TEXT_P(inputstring);
- else
-/* otherwise return an empty string */
+ else /* otherwise return an empty string */
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
}
@@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
if (fldnum == 1) /* first field - just return the input
* string */
PG_RETURN_TEXT_P(inputstring);
- else
-/* otherwise return an empty string */
+ else /* otherwise return an empty string */
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
}
else if ((start_posn != 0) && (end_posn == 0))
@@ -2028,6 +2029,176 @@ split_text(PG_FUNCTION_ARGS)
}
}
+/*
+ * text_to_array
+ * parse input string
+ * return text array of elements
+ * based on provided field separator
+ */
+Datum
+text_to_array(PG_FUNCTION_ARGS)
+{
+ text *inputstring = PG_GETARG_TEXT_P(0);
+ int inputstring_len = TEXTLEN(inputstring);
+ text *fldsep = PG_GETARG_TEXT_P(1);
+ int fldsep_len = TEXTLEN(fldsep);
+ int fldnum;
+ int start_posn = 0;
+ int end_posn = 0;
+ text *result_text = NULL;
+ ArrayBuildState *astate = NULL;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ /* return NULL for empty input string */
+ if (inputstring_len < 1)
+ PG_RETURN_NULL();
+
+ /* empty field separator
+ * return one element, 1D, array using the input string */
+ if (fldsep_len < 1)
+ PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+ CStringGetDatum(inputstring), 1));
+
+ /* start with end position holding the initial start position */
+ end_posn = 0;
+ for (fldnum=1;;fldnum++) /* field number is 1 based */
+ {
+ Datum dvalue;
+ bool disnull = false;
+
+ start_posn = end_posn;
+ end_posn = text_position(PointerGetDatum(inputstring),
+ PointerGetDatum(fldsep),
+ fldnum);
+
+ if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */
+ {
+ if (fldnum == 1)
+ {
+ /* first element
+ * return one element, 1D, array using the input string */
+ PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
+ CStringGetDatum(inputstring), 1));
+ }
+ else
+ {
+ /* otherwise create array and exit */
+ PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
+ }
+ }
+ else if ((start_posn != 0) && (end_posn == 0))
+ {
+ /* last field requested */
+ result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
+ }
+ else if ((start_posn == 0) && (end_posn != 0))
+ {
+ /* first field requested */
+ result_text = LEFT(inputstring, fldsep);
+ }
+ else
+ {
+ /* prior to last field requested */
+ result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
+ }
+
+ /* stash away current value */
+ dvalue = PointerGetDatum(result_text);
+ astate = accumArrayResult(astate, dvalue,
+ disnull, TEXTOID, oldcontext);
+
+ }
+
+ /* never reached -- keep compiler quiet */
+ PG_RETURN_NULL();
+}
+
+/*
+ * array_to_text
+ * concatenate Cstring representation of input array elements
+ * using provided field separator
+ */
+Datum
+array_to_text(PG_FUNCTION_ARGS)
+{
+ ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
+ char *fldsep = PG_TEXTARG_GET_STR(1);
+ int nitems, *dims, ndims;
+ char *p;
+ Oid element_type;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ Oid typelem;
+ StringInfo result_str = makeStringInfo();
+ int i;
+ ArrayMetaState *my_extra;
+
+ p = ARR_DATA_PTR(v);
+ ndims = ARR_NDIM(v);
+ dims = ARR_DIMS(v);
+ nitems = ArrayGetNItems(ndims, dims);
+
+ /* if there are no elements, return an empty string */
+ if (nitems == 0)
+ PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
+
+ element_type = ARR_ELEMTYPE(v);
+
+ /*
+ * We arrange to look up info about element type, including its output
+ * conversion proc, 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, including its output conversion proc */
+ get_type_io_data(element_type, IOFunc_output,
+ &my_extra->typlen, &my_extra->typbyval,
+ &my_extra->typalign, &my_extra->typdelim,
+ &my_extra->typelem, &my_extra->typiofunc);
+ fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
+ fcinfo->flinfo->fn_mcxt);
+ my_extra->element_type = element_type;
+ }
+ typlen = my_extra->typlen;
+ typbyval = my_extra->typbyval;
+ typalign = my_extra->typalign;
+ typelem = my_extra->typelem;
+
+ for (i = 0; i < nitems; i++)
+ {
+ Datum itemvalue;
+ char *value;
+
+ itemvalue = fetch_att(p, typbyval, typlen);
+
+ value = DatumGetCString(FunctionCall3(&my_extra->proc,
+ itemvalue,
+ ObjectIdGetDatum(typelem),
+ Int32GetDatum(-1)));
+
+ if (i > 0)
+ appendStringInfo(result_str, "%s%s", fldsep, value);
+ else
+ appendStringInfo(result_str, "%s", value);
+
+ p = att_addlength(p, typlen, PointerGetDatum(p));
+ p = (char *) att_align(p, typalign);
+ }
+
+ PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
+}
+
#define HEXBASE 16
/*
* Convert a int32 to a string containing a base 16 (hex) representation of
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 479f23090fb..6da53870c47 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.99 2003/06/25 21:30:32 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.100 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@@ -1090,6 +1090,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
ReleaseSysCache(tp);
}
+/*
+ * get_type_io_data
+ *
+ * A six-fer: given the type OID, return typlen, typbyval, typalign,
+ * typdelim, typelem, and IO function OID. The IO function
+ * returned is controlled by IOFuncSelector
+ */
+void
+get_type_io_data(Oid typid,
+ IOFuncSelector which_func,
+ int16 *typlen,
+ bool *typbyval,
+ char *typalign,
+ char *typdelim,
+ Oid *typelem,
+ Oid *func)
+{
+ HeapTuple typeTuple;
+ Form_pg_type typeStruct;
+
+ typeTuple = SearchSysCache(TYPEOID,
+ ObjectIdGetDatum(typid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "cache lookup failed for type %u", typid);
+ typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
+
+ *typlen = typeStruct->typlen;
+ *typbyval = typeStruct->typbyval;
+ *typalign = typeStruct->typalign;
+ *typdelim = typeStruct->typdelim;
+ *typelem = typeStruct->typelem;
+ switch (which_func)
+ {
+ case IOFunc_input:
+ *func = typeStruct->typinput;
+ break;
+ case IOFunc_output:
+ *func = typeStruct->typoutput;
+ break;
+ case IOFunc_receive:
+ *func = typeStruct->typreceive;
+ break;
+ case IOFunc_send:
+ *func = typeStruct->typsend;
+ break;
+ }
+ ReleaseSysCache(typeTuple);
+}
+
#ifdef NOT_USED
char
get_typalign(Oid typid)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 8bba8c953db..e8ce5d131a5 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: catversion.h,v 1.200 2003/06/25 01:26:16 momjian Exp $
+ * $Id: catversion.h,v 1.201 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200306241
+#define CATALOG_VERSION_NO 200306261
#endif
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index 4b6107ce53e..e2816b3c214 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -16,7 +16,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_amop.h,v 1.52 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_amop.h,v 1.53 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -418,6 +418,15 @@ DATA(insert ( 2098 3 f 2334 ));
DATA(insert ( 2098 4 f 2335 ));
DATA(insert ( 2098 5 f 2336 ));
+/*
+ * btree array_ops
+ */
+
+DATA(insert ( 397 1 f 1072 ));
+DATA(insert ( 397 2 f 1074 ));
+DATA(insert ( 397 3 f 1070 ));
+DATA(insert ( 397 4 f 1075 ));
+DATA(insert ( 397 5 f 1073 ));
/*
* hash index _ops
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 6febdecffcd..79f181cd695 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -14,7 +14,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_amproc.h,v 1.40 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_amproc.h,v 1.41 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -78,6 +78,7 @@ DATA(insert ( 1993 3 199 ));
/* btree */
+DATA(insert ( 397 1 382 ));
DATA(insert ( 421 1 357 ));
DATA(insert ( 423 1 1596 ));
DATA(insert ( 424 1 1693 ));
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index 88d2aac1a7d..5d635523ca9 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -26,7 +26,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_opclass.h,v 1.53 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_opclass.h,v 1.54 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -87,6 +87,8 @@ typedef FormData_pg_opclass *Form_pg_opclass;
*/
DATA(insert OID = 421 ( 403 abstime_ops PGNSP PGUID 702 t 0 ));
+DATA(insert OID = 397 ( 403 array_ops PGNSP PGUID 2277 t 0 ));
+#define ARRAY_BTREE_OPS_OID 397
DATA(insert OID = 422 ( 402 bigbox_ops PGNSP PGUID 603 f 0 ));
DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 0 ));
DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 ));
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 2bbef276c00..a964ac1b3dd 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_operator.h,v 1.117 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_operator.h,v 1.118 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -116,7 +116,6 @@ DATA(insert OID = 96 ( "=" PGNSP PGUID b t 23 23 16 96 518 97 97 97 521 int
DATA(insert OID = 97 ( "<" PGNSP PGUID b f 23 23 16 521 525 0 0 0 0 int4lt scalarltsel scalarltjoinsel ));
DATA(insert OID = 98 ( "=" PGNSP PGUID b t 25 25 16 98 531 664 664 664 666 texteq eqsel eqjoinsel ));
-DATA(insert OID = 329 ( "=" PGNSP PGUID b f 2277 2277 16 329 0 0 0 0 0 array_eq eqsel eqjoinsel ));
DATA(insert OID = 349 ( "||" PGNSP PGUID b f 2277 2283 2277 0 0 0 0 0 0 array_append - - ));
DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 array_prepend - - ));
DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - ));
@@ -425,6 +424,7 @@ DATA(insert OID = 965 ( "^" PGNSP PGUID b f 701 701 701 0 0 0 0 0 0 dpow -
DATA(insert OID = 966 ( "+" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
DATA(insert OID = 967 ( "-" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
DATA(insert OID = 968 ( "~" PGNSP PGUID b f 1034 1033 16 0 0 0 0 0 0 aclcontains - - ));
+DATA(insert OID = 974 ( "=" PGNSP PGUID b f 1033 1033 16 974 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
/* additional geometric operators - thomas 1997-07-09 */
DATA(insert OID = 969 ( "@@" PGNSP PGUID l f 0 601 600 0 0 0 0 0 0 lseg_center - - ));
@@ -441,6 +441,16 @@ DATA(insert OID = 1059 ( "<=" PGNSP PGUID b f 1042 1042 16 1061 1060 0 0 0
DATA(insert OID = 1060 ( ">" PGNSP PGUID b f 1042 1042 16 1058 1059 0 0 0 0 bpchargt scalargtsel scalargtjoinsel ));
DATA(insert OID = 1061 ( ">=" PGNSP PGUID b f 1042 1042 16 1059 1058 0 0 0 0 bpcharge scalargtsel scalargtjoinsel ));
+/* generic array comparison operators */
+DATA(insert OID = 1070 ( "=" PGNSP PGUID b f 2277 2277 16 1070 1071 1072 1072 1072 1073 array_eq eqsel eqjoinsel ));
+#define ARRAY_EQ_OP 1070
+DATA(insert OID = 1071 ( "<>" PGNSP PGUID b f 2277 2277 16 1071 1070 0 0 0 0 array_ne neqsel neqjoinsel ));
+DATA(insert OID = 1072 ( "<" PGNSP PGUID b f 2277 2277 16 1073 1075 0 0 0 0 array_lt scalarltsel scalarltjoinsel ));
+#define ARRAY_LT_OP 1072
+DATA(insert OID = 1073 ( ">" PGNSP PGUID b f 2277 2277 16 1072 1074 0 0 0 0 array_gt scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1074 ( "<=" PGNSP PGUID b f 2277 2277 16 1075 1073 0 0 0 0 array_le scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1075 ( ">=" PGNSP PGUID b f 2277 2277 16 1074 1072 0 0 0 0 array_ge scalargtsel scalargtjoinsel ));
+
/* date operators */
DATA(insert OID = 1076 ( "+" PGNSP PGUID b f 1082 1186 1114 0 0 0 0 0 0 date_pl_interval - - ));
DATA(insert OID = 1077 ( "-" PGNSP PGUID b f 1082 1186 1114 0 0 0 0 0 0 date_mi_interval - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index bae6f644fc5..578a6b7d42a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_proc.h,v 1.307 2003/06/25 21:30:32 momjian Exp $
+ * $Id: pg_proc.h,v 1.308 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@@ -758,6 +758,8 @@ DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 f f t f i 2 23 "19 19" btn
DESCR("btree less-equal-greater");
DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ ));
DESCR("btree less-equal-greater");
+DATA(insert OID = 382 ( btarraycmp PGNSP PGUID 12 f f t f i 2 23 "2277 2277" btarraycmp - _null_ ));
+DESCR("btree less-equal-greater");
DATA(insert OID = 361 ( lseg_distance PGNSP PGUID 12 f f t f i 2 701 "601 601" lseg_distance - _null_ ));
DESCR("distance between");
@@ -988,14 +990,23 @@ DESCR("greater-than");
DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 f f t f i 2 16 "25 25" text_ge - _null_ ));
DESCR("greater-than-or-equal");
-DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
-DESCR("array equal");
-
DATA(insert OID = 745 ( current_user PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
DESCR("current user name");
DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
DESCR("session user name");
+DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
+DESCR("array equal");
+DATA(insert OID = 390 ( array_ne PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
+DESCR("array not equal");
+DATA(insert OID = 391 ( array_lt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
+DESCR("array less than");
+DATA(insert OID = 392 ( array_gt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
+DESCR("array greater than");
+DATA(insert OID = 393 ( array_le PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
+DESCR("array less than or equal");
+DATA(insert OID = 396 ( array_ge PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
+DESCR("array greater than or equal");
DATA(insert OID = 747 ( array_dims PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
DESCR("array dimensions");
DATA(insert OID = 750 ( array_in PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23" array_in - _null_ ));
@@ -1006,22 +1017,18 @@ DATA(insert OID = 2091 ( array_lower PGNSP PGUID 12 f f t f i 2 23 "2277 23"
DESCR("array lower dimension");
DATA(insert OID = 2092 ( array_upper PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ ));
DESCR("array upper dimension");
-DATA(insert OID = 377 ( singleton_array PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
-DESCR("create array from single element");
DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ ));
DESCR("append element onto end of array");
DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ ));
DESCR("prepend element onto front of array");
-DATA(insert OID = 380 ( array_accum PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
-DESCR("push element onto end of array, creating array if needed");
-DATA(insert OID = 381 ( array_assign PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
-DESCR("assign specific array element");
-DATA(insert OID = 382 ( array_subscript PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
-DESCR("return specific array element");
DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
DESCR("concatenate two arrays");
DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
DESCR("coerce array type to another array type");
+DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
+DESCR("split delimited text into text[]");
+DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
+DESCR("concatenate array elements, using delimiter, into text");
DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ ));
DESCR("I/O");
@@ -1322,6 +1329,8 @@ DATA(insert OID = 1036 ( aclremove PGNSP PGUID 12 f f t f s 2 1034 "1034 10
DESCR("remove ACL item");
DATA(insert OID = 1037 ( aclcontains PGNSP PGUID 12 f f t f s 2 16 "1034 1033" aclcontains - _null_ ));
DESCR("does ACL contain item?");
+DATA(insert OID = 1062 ( aclitemeq PGNSP PGUID 12 f f t f s 2 16 "1033 1033" aclitem_eq - _null_ ));
+DESCR("equality operator for ACL items");
DATA(insert OID = 1365 ( makeaclitem PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16" makeaclitem - _null_ ));
DESCR("make ACL item");
DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ ));
diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h
index 13642f5a348..66e12583645 100644
--- a/src/include/parser/parse_oper.h
+++ b/src/include/parser/parse_oper.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_oper.h,v 1.27 2003/06/25 21:30:33 momjian Exp $
+ * $Id: parse_oper.h,v 1.28 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -45,6 +45,7 @@ extern Operator ordering_oper(Oid argtype, bool noError);
extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
extern Oid equality_oper_funcid(Oid argtype);
extern Oid ordering_oper_opid(Oid argtype);
+extern Oid ordering_oper_funcid(Oid argtype);
/* Extract operator OID or underlying-function OID from an Operator tuple */
extern Oid oprid(Operator op);
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 9cf35618ad7..8352f8180e1 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: acl.h,v 1.54 2003/06/25 21:30:33 momjian Exp $
+ * $Id: acl.h,v 1.55 2003/06/27 00:33:26 tgl Exp $
*
* NOTES
* For backward-compatibility purposes we have to allow there
@@ -192,6 +192,7 @@ extern Datum aclinsert(PG_FUNCTION_ARGS);
extern Datum aclremove(PG_FUNCTION_ARGS);
extern Datum aclcontains(PG_FUNCTION_ARGS);
extern Datum makeaclitem(PG_FUNCTION_ARGS);
+extern Datum aclitem_eq(PG_FUNCTION_ARGS);
/*
* prototypes for functions in aclchk.c
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 23a32d3459e..0d99816c246 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: array.h,v 1.40 2003/06/25 21:30:33 momjian Exp $
+ * $Id: array.h,v 1.41 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,6 +32,37 @@ typedef struct
Oid elemtype; /* element type OID */
} ArrayType;
+typedef struct ArrayBuildState
+{
+ MemoryContext mcontext; /* where all the temp stuff is kept */
+ Datum *dvalues; /* array of accumulated Datums */
+ /*
+ * The allocated size of dvalues[] is always a multiple of
+ * ARRAY_ELEMS_CHUNKSIZE
+ */
+#define ARRAY_ELEMS_CHUNKSIZE 64
+ int nelems; /* number of valid Datums in dvalues[] */
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+} ArrayBuildState;
+
+/*
+ * structure to cache type metadata needed for array manipulation
+ */
+typedef struct ArrayMetaState
+{
+ Oid element_type;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+ Oid typelem;
+ Oid typiofunc;
+ FmgrInfo proc;
+} ArrayMetaState;
+
/*
* fmgr macros for array objects
*/
@@ -86,11 +117,15 @@ extern Datum array_recv(PG_FUNCTION_ARGS);
extern Datum array_send(PG_FUNCTION_ARGS);
extern Datum array_length_coerce(PG_FUNCTION_ARGS);
extern Datum array_eq(PG_FUNCTION_ARGS);
+extern Datum array_ne(PG_FUNCTION_ARGS);
+extern Datum array_lt(PG_FUNCTION_ARGS);
+extern Datum array_gt(PG_FUNCTION_ARGS);
+extern Datum array_le(PG_FUNCTION_ARGS);
+extern Datum array_ge(PG_FUNCTION_ARGS);
+extern Datum btarraycmp(PG_FUNCTION_ARGS);
extern Datum array_dims(PG_FUNCTION_ARGS);
extern Datum array_lower(PG_FUNCTION_ARGS);
extern Datum array_upper(PG_FUNCTION_ARGS);
-extern Datum array_assign(PG_FUNCTION_ARGS);
-extern Datum array_subscript(PG_FUNCTION_ARGS);
extern Datum array_type_coerce(PG_FUNCTION_ARGS);
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
@@ -124,7 +159,14 @@ extern void deconstruct_array(ArrayType *array,
Oid elmtype,
int elmlen, bool elmbyval, char elmalign,
Datum **elemsp, int *nelemsp);
-
+extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
+ Datum dvalue, bool disnull,
+ Oid element_type,
+ MemoryContext rcontext);
+extern Datum makeArrayResult(ArrayBuildState *astate,
+ MemoryContext rcontext);
+extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
+ int *dims, int *lbs, MemoryContext rcontext);
/*
* prototypes for functions defined in arrayutils.c
@@ -141,12 +183,11 @@ extern int mda_next_tuple(int n, int *curr, int *span);
/*
* prototypes for functions defined in array_userfuncs.c
*/
-extern Datum singleton_array(PG_FUNCTION_ARGS);
extern Datum array_push(PG_FUNCTION_ARGS);
-extern Datum array_accum(PG_FUNCTION_ARGS);
extern Datum array_cat(PG_FUNCTION_ARGS);
-extern ArrayType *create_singleton_array(Oid element_type,
+extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
+ Oid element_type,
Datum element,
int ndims);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 958021eb1f0..52b67ac88a9 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: builtins.h,v 1.222 2003/06/25 21:30:33 momjian Exp $
+ * $Id: builtins.h,v 1.223 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -530,6 +530,8 @@ extern bool SplitIdentifierString(char *rawstring, char separator,
List **namelist);
extern Datum replace_text(PG_FUNCTION_ARGS);
extern Datum split_text(PG_FUNCTION_ARGS);
+extern Datum text_to_array(PG_FUNCTION_ARGS);
+extern Datum array_to_text(PG_FUNCTION_ARGS);
extern Datum to_hex32(PG_FUNCTION_ARGS);
extern Datum to_hex64(PG_FUNCTION_ARGS);
extern Datum md5_text(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index d7d3bba9d97..4d8749befc6 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.74 2003/06/25 21:30:33 momjian Exp $
+ * $Id: lsyscache.h,v 1.75 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -15,6 +15,15 @@
#include "access/htup.h"
+/* I/O function selector for get_type_io_data */
+typedef enum IOFuncSelector
+{
+ IOFunc_input,
+ IOFunc_output,
+ IOFunc_receive,
+ IOFunc_send
+} IOFuncSelector;
+
extern bool op_in_opclass(Oid opno, Oid opclass);
extern bool op_requires_recheck(Oid opno, Oid opclass);
extern Oid get_opclass_member(Oid opclass, int16 strategy);
@@ -56,6 +65,14 @@ extern bool get_typbyval(Oid typid);
extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
char *typalign);
+extern void get_type_io_data(Oid typid,
+ IOFuncSelector which_func,
+ int16 *typlen,
+ bool *typbyval,
+ char *typalign,
+ char *typdelim,
+ Oid *typelem,
+ Oid *func);
extern char get_typstorage(Oid typid);
extern int32 get_typtypmod(Oid typid);
extern Node *get_typdefault(Oid typid);
diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y
index 2d799705edc..6b79bd17747 100644
--- a/src/interfaces/ecpg/preproc/preproc.y
+++ b/src/interfaces/ecpg/preproc/preproc.y
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.240 2003/06/26 11:37:05 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.241 2003/06/27 00:33:26 tgl Exp $ */
/* Copyright comment */
%{
@@ -4597,7 +4597,7 @@ type_declaration: S_TYPEDEF
$3.type_enum != ECPGt_char &&
$3.type_enum != ECPGt_unsigned_char &&
atoi(this->type->type_index) >= 0)
- mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+ mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
types = this;
}
@@ -5417,7 +5417,7 @@ ECPGTypedef: TYPE_P
$5.type_enum != ECPGt_char &&
$5.type_enum != ECPGt_unsigned_char &&
atoi(this->type->type_index) >= 0)
- mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+ mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
types = this;
}
@@ -5484,7 +5484,7 @@ ECPGVar: SQL_VAR
default:
if (atoi(length) >= 0)
- mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
+ mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
if (atoi(dimension) < 0)
type = ECPGmake_simple_type($5.type_enum, make_str("1"));
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 5f2dd86bb55..80406bbccc5 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -504,7 +504,7 @@ ECPGfree_type(struct ECPGtype * type)
switch (type->u.element->type)
{
case ECPGt_array:
- yyerror("internal error, found multi-dimensional array\n");
+ yyerror("internal error, found multidimensional array\n");
break;
case ECPGt_struct:
case ECPGt_union:
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index be96e18c0c5..9fa2ec8a6c9 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -436,7 +436,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
if (atoi(type_index) >= 0)
{
if (atoi(*length) >= 0)
- mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+ mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
*length = type_index;
}
@@ -444,7 +444,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
if (atoi(type_dimension) >= 0)
{
if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
- mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+ mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
if (atoi(*dimension) >= 0)
*length = *dimension;
@@ -463,10 +463,10 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
- mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+ mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
- mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
+ mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
switch (type_enum)
{
@@ -480,7 +480,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
}
if (atoi(*length) >= 0)
- mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
+ mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
break;
case ECPGt_varchar:
@@ -525,7 +525,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
}
if (atoi(*length) >= 0)
- mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
+ mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
break;
}
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 617cf09a9a0..9822294d19a 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -178,19 +178,13 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
(1 row)
-- functions
-SELECT singleton_array(42) AS "{42}";
- {42}
-------
- {42}
-(1 row)
-
-SELECT array_append(singleton_array(42), 6) AS "{42,6}";
+SELECT array_append(array[42], 6) AS "{42,6}";
{42,6}
--------
{42,6}
(1 row)
-SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
+SELECT array_prepend(6, array[42]) AS "{6,42}";
{6,42}
--------
{6,42}
@@ -214,24 +208,6 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
{{3,4},{5,6},{1,2}}
(1 row)
-SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
- 1.2
------
- 1.2
-(1 row)
-
-SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
- {1.1,9.99,1.3}
-----------------
- {1.1,9.99,1.3}
-(1 row)
-
-SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
- 9.99
-------
- 9.99
-(1 row)
-
-- operators
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
a
@@ -318,3 +294,24 @@ SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"
{{{{{{a,bb,ccc}}}}}}
(1 row)
+-- test indexes on arrays
+create temp table arr_tbl (f1 int[] unique);
+NOTICE: CREATE TABLE / UNIQUE will create implicit index 'arr_tbl_f1_key' for table 'arr_tbl'
+insert into arr_tbl values ('{1,2,3}');
+insert into arr_tbl values ('{1,2}');
+-- failure expected:
+insert into arr_tbl values ('{1,2,3}');
+ERROR: Cannot insert a duplicate key into unique index arr_tbl_f1_key
+insert into arr_tbl values ('{2,3,4}');
+insert into arr_tbl values ('{1,5,3}');
+insert into arr_tbl values ('{1,2,10}');
+set enable_seqscan to off;
+select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
+ f1
+----------
+ {1,2,10}
+ {1,5,3}
+(2 rows)
+
+-- note: if above select doesn't produce the expected tuple order,
+-- then you didn't get an indexscan plan, and something is busted.
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 82eff24125d..8b18ffb9eb8 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -130,15 +130,11 @@ SELECT ARRAY[ARRAY['hello'],ARRAY['world']];
SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
-- functions
-SELECT singleton_array(42) AS "{42}";
-SELECT array_append(singleton_array(42), 6) AS "{42,6}";
-SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
+SELECT array_append(array[42], 6) AS "{42,6}";
+SELECT array_prepend(6, array[42]) AS "{6,42}";
SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
-SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
-SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
-SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
-- operators
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
@@ -157,3 +153,17 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE";
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
+
+-- test indexes on arrays
+create temp table arr_tbl (f1 int[] unique);
+insert into arr_tbl values ('{1,2,3}');
+insert into arr_tbl values ('{1,2}');
+-- failure expected:
+insert into arr_tbl values ('{1,2,3}');
+insert into arr_tbl values ('{2,3,4}');
+insert into arr_tbl values ('{1,5,3}');
+insert into arr_tbl values ('{1,2,10}');
+set enable_seqscan to off;
+select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
+-- note: if above select doesn't produce the expected tuple order,
+-- then you didn't get an indexscan plan, and something is busted.