diff options
Diffstat (limited to 'src')
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. |