diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-09-10 20:14:20 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-09-10 20:14:20 +0000 |
commit | f5b4d9a9e08199e6bcdb050ef42ea7ec0f7525ca (patch) | |
tree | 4d161947e80cc2c1f55b08a7093744bfaa1d229b /src/backend/utils/adt/arrayfuncs.c | |
parent | 69b7c99265a495365a49d89692fafe12d2a69d13 (diff) | |
download | postgresql-f5b4d9a9e08199e6bcdb050ef42ea7ec0f7525ca.tar.gz postgresql-f5b4d9a9e08199e6bcdb050ef42ea7ec0f7525ca.zip |
If we're going to advertise the array overlap/containment operators,
we probably should make them work reliably for all arrays. Fix code
to handle NULLs and multidimensional arrays, move it into arrayfuncs.c.
GIN is still restricted to indexing arrays with no null elements, however.
Diffstat (limited to 'src/backend/utils/adt/arrayfuncs.c')
-rw-r--r-- | src/backend/utils/adt/arrayfuncs.c | 228 |
1 files changed, 227 insertions, 1 deletions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 58dfb0fc085..8b0c11c163d 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.130 2006/07/14 14:52:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.131 2006/09/10 20:14:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3345,6 +3345,232 @@ array_cmp(FunctionCallInfo fcinfo) } +/*----------------------------------------------------------------------------- + * array overlap/containment comparisons + * These use the same methods of comparing array elements as array_eq. + * We consider only the elements of the arrays, ignoring dimensionality. + *---------------------------------------------------------------------------- + */ + +/* + * array_contain_compare : + * compares two arrays for overlap/containment + * + * When matchall is true, return true if all members of array1 are in array2. + * When matchall is false, return true if any members of array1 are in array2. + */ +static bool +array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall, + void **fn_extra) +{ + bool result = matchall; + Oid element_type = ARR_ELEMTYPE(array1); + TypeCacheEntry *typentry; + int nelems1; + Datum *values2; + bool *nulls2; + int nelems2; + int typlen; + bool typbyval; + char typalign; + char *ptr1; + bits8 *bitmap1; + int bitmask; + int i; + int j; + FunctionCallInfoData locfcinfo; + + if (element_type != ARR_ELEMTYPE(array2)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot compare arrays of different element types"))); + + /* + * We arrange to look up the equality function only once per series of + * calls, assuming the element type doesn't change underneath us. The + * typcache is used so that we have no memory leakage when being used + * as an index support function. + */ + typentry = (TypeCacheEntry *) *fn_extra; + if (typentry == NULL || + typentry->type_id != element_type) + { + typentry = lookup_type_cache(element_type, + TYPECACHE_EQ_OPR_FINFO); + if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify an equality operator for type %s", + format_type_be(element_type)))); + *fn_extra = (void *) typentry; + } + typlen = typentry->typlen; + typbyval = typentry->typbyval; + typalign = typentry->typalign; + + /* + * Since we probably will need to scan array2 multiple times, it's + * worthwhile to use deconstruct_array on it. We scan array1 the hard way + * however, since we very likely won't need to look at all of it. + */ + deconstruct_array(array2, element_type, typlen, typbyval, typalign, + &values2, &nulls2, &nelems2); + + /* + * Apply the comparison operator to each pair of array elements. + */ + InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2, + NULL, NULL); + + /* Loop over source data */ + nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1)); + ptr1 = ARR_DATA_PTR(array1); + bitmap1 = ARR_NULLBITMAP(array1); + bitmask = 1; + + for (i = 0; i < nelems1; i++) + { + Datum elt1; + bool isnull1; + + /* Get element, checking for NULL */ + if (bitmap1 && (*bitmap1 & bitmask) == 0) + { + isnull1 = true; + elt1 = (Datum) 0; + } + else + { + isnull1 = false; + elt1 = fetch_att(ptr1, typbyval, typlen); + ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1)); + ptr1 = (char *) att_align(ptr1, typalign); + } + + /* advance bitmap pointer if any */ + bitmask <<= 1; + if (bitmask == 0x100) + { + if (bitmap1) + bitmap1++; + bitmask = 1; + } + + /* + * We assume that the comparison operator is strict, so a NULL + * can't match anything. XXX this diverges from the "NULL=NULL" + * behavior of array_eq, should we act like that? + */ + if (isnull1) + { + if (matchall) + { + result = false; + break; + } + continue; + } + + for (j = 0; j < nelems2; j++) + { + Datum elt2 = values2[j]; + bool isnull2 = nulls2[j]; + bool oprresult; + + if (isnull2) + continue; /* can't match */ + + /* + * 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) + break; + } + + if (j < nelems2) + { + /* found a match for elt1 */ + if (!matchall) + { + result = true; + break; + } + } + else + { + /* no match for elt1 */ + if (matchall) + { + result = false; + break; + } + } + } + + pfree(values2); + pfree(nulls2); + + return result; +} + +Datum +arrayoverlap(PG_FUNCTION_ARGS) +{ + ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); + ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); + bool result; + + result = array_contain_compare(array1, array2, false, + &fcinfo->flinfo->fn_extra); + + /* Avoid leaking memory when handed toasted input. */ + PG_FREE_IF_COPY(array1, 0); + PG_FREE_IF_COPY(array2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +arraycontains(PG_FUNCTION_ARGS) +{ + ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); + ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); + bool result; + + result = array_contain_compare(array2, array1, true, + &fcinfo->flinfo->fn_extra); + + /* Avoid leaking memory when handed toasted input. */ + PG_FREE_IF_COPY(array1, 0); + PG_FREE_IF_COPY(array2, 1); + + PG_RETURN_BOOL(result); +} + +Datum +arraycontained(PG_FUNCTION_ARGS) +{ + ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); + ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); + bool result; + + result = array_contain_compare(array1, array2, true, + &fcinfo->flinfo->fn_extra); + + /* Avoid leaking memory when handed toasted input. */ + PG_FREE_IF_COPY(array1, 0); + PG_FREE_IF_COPY(array2, 1); + + PG_RETURN_BOOL(result); +} + + /***************************************************************************/ /******************| Support Routines |*****************/ /***************************************************************************/ |