/* number ranges for compression */
#define MAXNUMRANGE 100
-/* dimension of array */
-#define NDIM 1
-
/* useful macros for accessing int4 arrays */
#define ARRPTR(x) ( (int4 *) ARR_DATA_PTR(x) )
#define ARRNELEMS(x) ArrayGetNItems(ARR_NDIM(x), ARR_DIMS(x))
-/* reject arrays we can't handle; but allow a NULL or empty array */
+/* reject arrays we can't handle; to wit, those containing nulls */
#define CHECKARRVALID(x) \
do { \
- if (x) { \
- if (ARR_NDIM(x) != NDIM && ARR_NDIM(x) != 0) \
- ereport(ERROR, \
- (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), \
- errmsg("array must be one-dimensional"))); \
- if (ARR_HASNULL(x)) \
- ereport(ERROR, \
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \
- errmsg("array must not contain nulls"))); \
- } \
+ if (ARR_HASNULL(x) && array_contains_nulls(x)) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \
+ errmsg("array must not contain nulls"))); \
} while(0)
-#define ARRISVOID(x) ((x) == NULL || ARRNELEMS(x) == 0)
+#define ARRISEMPTY(x) (ARRNELEMS(x) == 0)
+/* sort the elements of the array */
#define SORT(x) \
do { \
- if ( ARRNELEMS( x ) > 1 ) \
- isort( ARRPTR( x ), ARRNELEMS( x ) ); \
+ int _nelems_ = ARRNELEMS(x); \
+ if (_nelems_ > 1) \
+ isort(ARRPTR(x), _nelems_); \
} while(0)
+/* sort the elements of the array and remove duplicates */
#define PREPAREARR(x) \
do { \
- if ( ARRNELEMS( x ) > 1 ) \
- if ( isort( ARRPTR( x ), ARRNELEMS( x ) ) ) \
- x = _int_unique( x ); \
+ int _nelems_ = ARRNELEMS(x); \
+ if (_nelems_ > 1) \
+ if (isort(ARRPTR(x), _nelems_)) \
+ (x) = _int_unique(x); \
} while(0)
/* "wish" function */
#define GETSIGN(x) ( (BITVECP)( (char*)x+GTHDRSIZE ) )
/*
-** types for functions
-*/
+ * types for functions
+ */
typedef ArrayType *(*formarray) (ArrayType *, ArrayType *);
typedef void (*formfloat) (ArrayType *, float *);
/*
-** useful function
-*/
+ * useful functions
+ */
bool isort(int4 *a, int len);
ArrayType *new_intArrayType(int num);
ArrayType *copy_intArrayType(ArrayType *a);
int4 val;
} ITEM;
-typedef struct
+typedef struct QUERYTYPE
{
int32 vl_len_; /* varlena header (do not touch directly!) */
- int4 size;
- char data[1];
+ int4 size; /* number of ITEMs */
+ ITEM items[1]; /* variable length array */
} QUERYTYPE;
-#define HDRSIZEQT (VARHDRSZ + sizeof(int4))
-#define COMPUTESIZE(size) ( HDRSIZEQT + size * sizeof(ITEM) )
-#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
+#define HDRSIZEQT offsetof(QUERYTYPE, items)
+#define COMPUTESIZE(size) ( HDRSIZEQT + (size) * sizeof(ITEM) )
+#define GETQUERY(x) ( (x)->items )
+/* "type" codes for ITEM */
#define END 0
#define ERR 1
#define VAL 2
#define OPEN 4
#define CLOSE 5
+/* fmgr macros for QUERYTYPE objects */
+#define DatumGetQueryTypeP(X) ((QUERYTYPE *) PG_DETOAST_DATUM(X))
+#define DatumGetQueryTypePCopy(X) ((QUERYTYPE *) PG_DETOAST_DATUM_COPY(X))
+#define PG_GETARG_QUERYTYPE_P(n) DatumGetQueryTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_QUERYTYPE_P_COPY(n) DatumGetQueryTypePCopy(PG_GETARG_DATUM(n))
+
bool signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot);
bool execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot);
-bool ginconsistent(QUERYTYPE *query, bool *check);
-int4 shorterquery(ITEM *q, int4 len);
-int compASC(const void *a, const void *b);
+bool gin_bool_consistent(QUERYTYPE *query, bool *check);
+bool query_has_required_values(QUERYTYPE *query);
+int compASC(const void *a, const void *b);
int compDESC(const void *a, const void *b);
-#define QSORT(a, direction) \
-if (ARRNELEMS(a) > 1) \
- qsort((void*)ARRPTR(a), ARRNELEMS(a),sizeof(int4), \
- (direction) ? compASC : compDESC )
+/* sort, either ascending or descending */
+#define QSORT(a, direction) \
+ do { \
+ int _nelems_ = ARRNELEMS(a); \
+ if (_nelems_ > 1) \
+ qsort((void*) ARRPTR(a), _nelems_, sizeof(int4), \
+ (direction) ? compASC : compDESC ); \
+ } while(0)
#endif /* ___INT_H__ */
*/
#include "postgres.h"
+#include "miscadmin.h"
#include "utils/builtins.h"
#include "_int.h"
Datum querytree(PG_FUNCTION_ARGS);
-#define END 0
-#define ERR 1
-#define VAL 2
-#define OPR 3
-#define OPEN 4
-#define CLOSE 5
-
/* parser's states */
#define WAITOPERAND 1
#define WAITENDOPERAND 2
int4 stack[STACKDEPTH];
int4 lenstack = 0;
+ /* since this function recurses, it could be driven to stack overflow */
+ check_stack_depth();
+
while ((type = gettoken(state, &val)) != END)
{
switch (type)
} CHKVAL;
/*
- * is there value 'val' in array or not ?
+ * is there value 'val' in (sorted) array or not ?
*/
static bool
checkcondition_arr(void *checkval, ITEM *item)
}
/*
- * check for boolean condition
+ * evaluate boolean expression, using chkcond() to test the primitive cases
*/
static bool
-execute(ITEM *curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM *item))
+execute(ITEM *curitem, void *checkval, bool calcnot,
+ bool (*chkcond) (void *checkval, ITEM *item))
{
+ /* since this function recurses, it could be driven to stack overflow */
+ check_stack_depth();
if (curitem->type == VAL)
return (*chkcond) (checkval, curitem);
bool
signconsistent(QUERYTYPE *query, BITVEC sign, bool calcnot)
{
- return execute(
- GETQUERY(query) + query->size - 1,
+ return execute(GETQUERY(query) + query->size - 1,
(void *) sign, calcnot,
- checkcondition_bit
- );
+ checkcondition_bit);
}
+/* Array must be sorted! */
bool
execconsistent(QUERYTYPE *query, ArrayType *array, bool calcnot)
{
CHECKARRVALID(array);
chkval.arrb = ARRPTR(array);
chkval.arre = chkval.arrb + ARRNELEMS(array);
- return execute(
- GETQUERY(query) + query->size - 1,
+ return execute(GETQUERY(query) + query->size - 1,
(void *) &chkval, calcnot,
- checkcondition_arr
- );
+ checkcondition_arr);
}
typedef struct
}
bool
-ginconsistent(QUERYTYPE *query, bool *check)
+gin_bool_consistent(QUERYTYPE *query, bool *check)
{
GinChkVal gcv;
ITEM *items = GETQUERY(query);
int i,
j = 0;
- if (query->size < 0)
+ if (query->size <= 0)
return FALSE;
+ /*
+ * Set up data for checkcondition_gin. This must agree with the
+ * query extraction code in ginint4_queryextract.
+ */
gcv.first = items;
gcv.mapped_check = (bool *) palloc(sizeof(bool) * query->size);
for (i = 0; i < query->size; i++)
+ {
if (items[i].type == VAL)
gcv.mapped_check[i] = check[j++];
+ }
- return execute(
- GETQUERY(query) + query->size - 1,
+ return execute(GETQUERY(query) + query->size - 1,
(void *) &gcv, true,
- checkcondition_gin
- );
+ checkcondition_gin);
+}
+
+static bool
+contains_required_value(ITEM *curitem)
+{
+ /* since this function recurses, it could be driven to stack overflow */
+ check_stack_depth();
+
+ if (curitem->type == VAL)
+ return true;
+ else if (curitem->val == (int4) '!')
+ {
+ /*
+ * Assume anything under a NOT is non-required. For some cases with
+ * nested NOTs, we could prove there's a required value, but it seems
+ * unlikely to be worth the trouble.
+ */
+ return false;
+ }
+ else if (curitem->val == (int4) '&')
+ {
+ /* If either side has a required value, we're good */
+ if (contains_required_value(curitem + curitem->left))
+ return true;
+ else
+ return contains_required_value(curitem - 1);
+ }
+ else
+ { /* |-operator */
+ /* Both sides must have required values */
+ if (contains_required_value(curitem + curitem->left))
+ return contains_required_value(curitem - 1);
+ else
+ return false;
+ }
+ return false;
+}
+
+bool
+query_has_required_values(QUERYTYPE *query)
+{
+ if (query->size <= 0)
+ return false;
+ return contains_required_value(GETQUERY(query) + query->size - 1);
}
/*
Datum
rboolop(PG_FUNCTION_ARGS)
{
- return DirectFunctionCall2(
- boolop,
+ /* just reverse the operands */
+ return DirectFunctionCall2(boolop,
PG_GETARG_DATUM(1),
- PG_GETARG_DATUM(0)
- );
+ PG_GETARG_DATUM(0));
}
Datum
boolop(PG_FUNCTION_ARGS)
{
- ArrayType *val = (ArrayType *) PG_DETOAST_DATUM_COPY(PG_GETARG_POINTER(0));
- QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_POINTER(1));
+ ArrayType *val = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ QUERYTYPE *query = PG_GETARG_QUERYTYPE_P(1);
CHKVAL chkval;
bool result;
CHECKARRVALID(val);
- if (ARRISVOID(val))
- {
- pfree(val);
- PG_FREE_IF_COPY(query, 1);
- PG_RETURN_BOOL(false);
- }
-
PREPAREARR(val);
chkval.arrb = ARRPTR(val);
chkval.arre = chkval.arrb + ARRNELEMS(val);
- result = execute(
- GETQUERY(query) + query->size - 1,
+ result = execute(GETQUERY(query) + query->size - 1,
&chkval, true,
- checkcondition_arr
- );
+ checkcondition_arr);
pfree(val);
PG_FREE_IF_COPY(query, 1);
Datum
bqarr_out(PG_FUNCTION_ARGS)
{
- QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
+ QUERYTYPE *query = PG_GETARG_QUERYTYPE_P(0);
INFIX nrm;
if (query->size == 0)
PG_RETURN_POINTER(nrm.buf);
}
-static int4
-countdroptree(ITEM *q, int4 pos)
-{
- if (q[pos].type == VAL)
- return 1;
- else if (q[pos].val == (int4) '!')
- return 1 + countdroptree(q, pos - 1);
- else
- return 1 + countdroptree(q, pos - 1) + countdroptree(q, pos + q[pos].left);
-}
-
-/*
- * common algorithm:
- * result of all '!' will be = 'true', so
- * we can modify query tree for clearing
- */
-int4
-shorterquery(ITEM *q, int4 len)
-{
- int4 index,
- posnot,
- poscor;
- bool notisleft = false;
- int4 drop,
- i;
-
- /* out all '!' */
- do
- {
- index = 0;
- drop = 0;
- /* find ! */
- for (posnot = 0; posnot < len; posnot++)
- if (q[posnot].type == OPR && q[posnot].val == (int4) '!')
- {
- index = 1;
- break;
- }
-
- if (posnot == len)
- return len;
-
- /* last operator is ! */
- if (posnot == len - 1)
- return 0;
-
- /* find operator for this operand */
- for (poscor = posnot + 1; poscor < len; poscor++)
- {
- if (q[poscor].type == OPR)
- {
- if (poscor == posnot + 1)
- {
- notisleft = false;
- break;
- }
- else if (q[poscor].left + poscor == posnot)
- {
- notisleft = true;
- break;
- }
- }
- }
- if (q[poscor].val == (int4) '!')
- {
- drop = countdroptree(q, poscor);
- q[poscor - 1].type = VAL;
- for (i = poscor + 1; i < len; i++)
- if (q[i].type == OPR && q[i].left + i <= poscor)
- q[i].left += drop - 2;
- memcpy((void *) &q[poscor - drop + 1],
- (void *) &q[poscor - 1],
- sizeof(ITEM) * (len - (poscor - 1)));
- len -= drop - 2;
- }
- else if (q[poscor].val == (int4) '|')
- {
- drop = countdroptree(q, poscor);
- q[poscor - 1].type = VAL;
- q[poscor].val = (int4) '!';
- q[poscor].left = -1;
- for (i = poscor + 1; i < len; i++)
- if (q[i].type == OPR && q[i].left + i < poscor)
- q[i].left += drop - 2;
- memcpy((void *) &q[poscor - drop + 1],
- (void *) &q[poscor - 1],
- sizeof(ITEM) * (len - (poscor - 1)));
- len -= drop - 2;
- }
- else
- { /* &-operator */
- if (
- (notisleft && q[poscor - 1].type == OPR &&
- q[poscor - 1].val == (int4) '!') ||
- (!notisleft && q[poscor + q[poscor].left].type == OPR &&
- q[poscor + q[poscor].left].val == (int4) '!')
- )
- { /* drop subtree */
- drop = countdroptree(q, poscor);
- q[poscor - 1].type = VAL;
- q[poscor].val = (int4) '!';
- q[poscor].left = -1;
- for (i = poscor + 1; i < len; i++)
- if (q[i].type == OPR && q[i].left + i < poscor)
- q[i].left += drop - 2;
- memcpy((void *) &q[poscor - drop + 1],
- (void *) &q[poscor - 1],
- sizeof(ITEM) * (len - (poscor - 1)));
- len -= drop - 2;
- }
- else
- { /* drop only operator */
- int4 subtreepos = (notisleft) ?
- poscor - 1 : poscor + q[poscor].left;
- int4 subtreelen = countdroptree(q, subtreepos);
-
- drop = countdroptree(q, poscor);
- for (i = poscor + 1; i < len; i++)
- if (q[i].type == OPR && q[i].left + i < poscor)
- q[i].left += drop - subtreelen;
- memcpy((void *) &q[subtreepos + 1],
- (void *) &q[poscor + 1],
- sizeof(ITEM) * (len - (poscor - 1)));
- memcpy((void *) &q[poscor - drop + 1],
- (void *) &q[subtreepos - subtreelen + 1],
- sizeof(ITEM) * (len - (drop - subtreelen)));
- len -= drop - subtreelen;
- }
- }
- } while (index);
- return len;
-}
-
+/* Useless old "debugging" function for a fundamentally wrong algorithm */
Datum
querytree(PG_FUNCTION_ARGS)
{
- QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
- INFIX nrm;
- text *res;
- ITEM *q;
- int4 len;
-
- if (query->size == 0)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("empty query")));
-
- q = (ITEM *) palloc(sizeof(ITEM) * query->size);
- memcpy((void *) q, GETQUERY(query), sizeof(ITEM) * query->size);
- len = shorterquery(q, query->size);
- PG_FREE_IF_COPY(query, 0);
-
- if (len == 0)
- {
- res = cstring_to_text("T");
- }
- else
- {
- nrm.curpol = q + len - 1;
- nrm.buflen = 32;
- nrm.cur = nrm.buf = (char *) palloc(sizeof(char) * nrm.buflen);
- *(nrm.cur) = '\0';
- infix(&nrm, true);
- res = cstring_to_text_with_len(nrm.buf, nrm.cur - nrm.buf);
- }
- pfree(q);
-
- PG_RETURN_TEXT_P(res);
+ elog(ERROR, "querytree is no longer implemented");
+ PG_RETURN_NULL();
}
*/
#include "postgres.h"
+#include "access/gin.h"
#include "access/gist.h"
#include "access/skey.h"
{
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
+ int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
Datum *res = NULL;
*nentries = 0;
if (strategy == BooleanSearchStrategy)
{
- QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM_COPY(PG_GETARG_POINTER(0));
+ QUERYTYPE *query = PG_GETARG_QUERYTYPE_P(0);
ITEM *items = GETQUERY(query);
int i;
- if (query->size == 0)
+ /* empty query must fail */
+ if (query->size <= 0)
PG_RETURN_POINTER(NULL);
- if (shorterquery(items, query->size) == 0)
- elog(ERROR, "Query requires full scan, GIN doesn't support it");
-
- pfree(query);
-
- query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
- items = GETQUERY(query);
-
+ /*
+ * If the query doesn't have any required primitive values (for
+ * instance, it's something like '! 42'), we have to do a full
+ * index scan.
+ */
+ if (query_has_required_values(query))
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else
+ *searchMode = GIN_SEARCH_MODE_ALL;
+
+ /*
+ * Extract all the VAL items as things we want GIN to check for.
+ */
res = (Datum *) palloc(sizeof(Datum) * query->size);
*nentries = 0;
for (i = 0; i < query->size; i++)
+ {
if (items[i].type == VAL)
{
res[*nentries] = Int32GetDatum(items[i].val);
(*nentries)++;
}
+ }
}
else
{
ArrayType *query = PG_GETARG_ARRAYTYPE_P(0);
- int4 *arr;
- uint32 i;
CHECKARRVALID(query);
*nentries = ARRNELEMS(query);
if (*nentries > 0)
{
+ int4 *arr;
+ int32 i;
+
res = (Datum *) palloc(sizeof(Datum) * (*nentries));
arr = ARRPTR(query);
for (i = 0; i < *nentries; i++)
res[i] = Int32GetDatum(arr[i]);
}
- }
- if (*nentries == 0)
- {
switch (strategy)
{
- case BooleanSearchStrategy:
case RTOverlapStrategyNumber:
- *nentries = -1; /* nobody can be found */
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
+ case RTContainedByStrategyNumber:
+ case RTOldContainedByStrategyNumber:
+ /* empty set is contained in everything */
+ *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
- default: /* require fullscan: GIN can't find void
- * arrays */
+ case RTSameStrategyNumber:
+ if (*nentries > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else
+ *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
+ case RTContainsStrategyNumber:
+ case RTOldContainsStrategyNumber:
+ if (*nentries > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else /* everything contains the empty set */
+ *searchMode = GIN_SEARCH_MODE_ALL;
+ break;
+ default:
+ elog(ERROR, "ginint4_queryextract: unknown strategy number: %d",
+ strategy);
}
}
{
bool *check = (bool *) PG_GETARG_POINTER(0);
StrategyNumber strategy = PG_GETARG_UINT16(1);
-
- /* int32 nkeys = PG_GETARG_INT32(3); */
+ int32 nkeys = PG_GETARG_INT32(3);
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
bool *recheck = (bool *) PG_GETARG_POINTER(5);
bool res = FALSE;
-
- /*
- * we need not check array carefully, it's done by previous
- * ginarrayextract call
- */
+ int32 i;
switch (strategy)
{
res = TRUE;
break;
case RTSameStrategyNumber:
+ /* we will need recheck */
+ *recheck = true;
+ /* Must have all elements in check[] true */
+ res = TRUE;
+ for (i = 0; i < nkeys; i++)
{
- ArrayType *query = PG_GETARG_ARRAYTYPE_P(2);
- int i,
- nentries = ARRNELEMS(query);
-
- /* we will need recheck */
- *recheck = true;
- res = TRUE;
- for (i = 0; i < nentries; i++)
- if (!check[i])
- {
- res = FALSE;
- break;
- }
+ if (!check[i])
+ {
+ res = FALSE;
+ break;
+ }
}
break;
case RTContainsStrategyNumber:
case RTOldContainsStrategyNumber:
+ /* result is not lossy */
+ *recheck = false;
+ /* Must have all elements in check[] true */
+ res = TRUE;
+ for (i = 0; i < nkeys; i++)
{
- ArrayType *query = PG_GETARG_ARRAYTYPE_P(2);
- int i,
- nentries = ARRNELEMS(query);
-
- /* result is not lossy */
- *recheck = false;
- res = TRUE;
- for (i = 0; i < nentries; i++)
- if (!check[i])
- {
- res = FALSE;
- break;
- }
+ if (!check[i])
+ {
+ res = FALSE;
+ break;
+ }
}
break;
case BooleanSearchStrategy:
{
- QUERYTYPE *query = (QUERYTYPE *) PG_DETOAST_DATUM(PG_GETARG_POINTER(2));
+ QUERYTYPE *query = PG_GETARG_QUERYTYPE_P(2);
/* result is not lossy */
*recheck = false;
- res = ginconsistent(query, check);
+ res = gin_bool_consistent(query, check);
}
break;
default:
g_int_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
- ArrayType *query = (ArrayType *) PG_DETOAST_DATUM_COPY(PG_GETARG_POINTER(1));
+ ArrayType *query = PG_GETARG_ARRAYTYPE_P_COPY(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
/* sort query for fast search, key is already sorted */
CHECKARRVALID(query);
- if (ARRISVOID(query))
- {
- pfree(query);
- PG_RETURN_BOOL(false);
- }
PREPAREARR(query);
switch (strategy)
break;
case RTSameStrategyNumber:
if (GIST_LEAF(entry))
- DirectFunctionCall3(
- g_int_same,
+ DirectFunctionCall3(g_int_same,
entry->key,
PointerGetDatum(query),
- PointerGetDatum(&retval)
- );
+ PointerGetDatum(&retval));
else
retval = inner_int_contains((ArrayType *) DatumGetPointer(entry->key),
query);
if (entry->leafkey)
{
- r = (ArrayType *) PG_DETOAST_DATUM_COPY(entry->key);
+ r = DatumGetArrayTypePCopy(entry->key);
CHECKARRVALID(r);
PREPAREARR(r);
* ==true, so now we work only with internal keys
*/
- r = (ArrayType *) PG_DETOAST_DATUM(entry->key);
+ r = DatumGetArrayTypeP(entry->key);
CHECKARRVALID(r);
- if (ARRISVOID(r))
+ if (ARRISEMPTY(r))
{
if (r != (ArrayType *) DatumGetPointer(entry->key))
pfree(r);
if ((len = ARRNELEMS(r)) >= 2 * MAXNUMRANGE)
{ /* compress */
if (r == (ArrayType *) DatumGetPointer(entry->key))
- r = (ArrayType *) PG_DETOAST_DATUM_COPY(entry->key);
+ r = DatumGetArrayTypePCopy(entry->key);
r = resize_intArrayType(r, 2 * (len));
dr = ARRPTR(r);
int i,
j;
- in = (ArrayType *) PG_DETOAST_DATUM(entry->key);
+ in = DatumGetArrayTypeP(entry->key);
CHECKARRVALID(in);
- if (ARRISVOID(in))
+ if (ARRISEMPTY(in))
{
if (in != (ArrayType *) DatumGetPointer(entry->key))
{
Datum
g_int_same(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) PointerGetDatum(PG_GETARG_POINTER(0));
- ArrayType *b = (ArrayType *) PointerGetDatum(PG_GETARG_POINTER(1));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
int4 n = ARRNELEMS(a);
int4 *da,
da = ARRPTR(a);
db = ARRPTR(b);
while (n--)
+ {
if (*da++ != *db++)
{
*result = FALSE;
break;
}
+ }
PG_RETURN_POINTER(result);
}
Datum
_int_contained(PG_FUNCTION_ARGS)
{
- PG_RETURN_BOOL(DatumGetBool(
- DirectFunctionCall2(
- _int_contains,
- PointerGetDatum(PG_GETARG_POINTER(1)),
- PointerGetDatum(PG_GETARG_POINTER(0))
- )
- ));
+ /* just reverse the operands and call _int_contains */
+ return DirectFunctionCall2(_int_contains,
+ PG_GETARG_DATUM(1),
+ PG_GETARG_DATUM(0));
}
Datum
_int_contains(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
+ /* Force copy so we can modify the arrays in-place */
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P_COPY(1);
bool res;
CHECKARRVALID(a);
CHECKARRVALID(b);
- if (ARRISVOID(a) || ARRISVOID(b))
- return FALSE;
-
PREPAREARR(a);
PREPAREARR(b);
res = inner_int_contains(a, b);
Datum
_int_same(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P_COPY(1);
int na,
nb;
int n;
int *da,
*db;
bool result;
- bool avoid;
- bool bvoid;
CHECKARRVALID(a);
CHECKARRVALID(b);
- avoid = ARRISVOID(a);
- bvoid = ARRISVOID(b);
- if (avoid || bvoid)
- return (avoid && bvoid) ? TRUE : FALSE;
-
na = ARRNELEMS(a);
nb = ARRNELEMS(b);
da = ARRPTR(a);
result = TRUE;
for (n = 0; n < na; n++)
+ {
if (da[n] != db[n])
{
result = FALSE;
break;
}
+ }
}
pfree(a);
Datum
_int_overlap(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P_COPY(1);
bool result;
CHECKARRVALID(a);
CHECKARRVALID(b);
- if (ARRISVOID(a) || ARRISVOID(b))
+ if (ARRISEMPTY(a) || ARRISEMPTY(b))
return FALSE;
SORT(a);
Datum
_int_union(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P_COPY(1);
ArrayType *result;
CHECKARRVALID(a);
CHECKARRVALID(b);
- if (!ARRISVOID(a))
- SORT(a);
- if (!ARRISVOID(b))
- SORT(b);
+ SORT(a);
+ SORT(b);
result = inner_int_union(a, b);
- if (a)
- pfree(a);
- if (b)
- pfree(b);
+ pfree(a);
+ pfree(b);
PG_RETURN_POINTER(result);
}
Datum
_int_inter(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P_COPY(1);
ArrayType *result;
CHECKARRVALID(a);
CHECKARRVALID(b);
- if (ARRISVOID(a) || ARRISVOID(b))
- PG_RETURN_POINTER(new_intArrayType(0));
SORT(a);
SORT(b);
Datum
icount(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
int32 count = ARRNELEMS(a);
PG_FREE_IF_COPY(a, 0);
Datum
sort(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
text *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_P(1) : NULL;
int32 dc = (dirstr) ? VARSIZE(dirstr) - VARHDRSZ : 0;
char *d = (dirstr) ? VARDATA(dirstr) : NULL;
int dir = -1;
CHECKARRVALID(a);
- if (ARRISVOID(a) || ARRNELEMS(a) < 2)
+ if (ARRNELEMS(a) < 2)
PG_RETURN_POINTER(a);
if (dirstr == NULL || (dc == 3
Datum
sort_asc(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
CHECKARRVALID(a);
- if (ARRISVOID(a))
- PG_RETURN_POINTER(a);
QSORT(a, 1);
PG_RETURN_POINTER(a);
}
Datum
sort_desc(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
CHECKARRVALID(a);
- if (ARRISVOID(a))
- PG_RETURN_POINTER(a);
QSORT(a, 0);
PG_RETURN_POINTER(a);
}
Datum
uniq(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
CHECKARRVALID(a);
- if (ARRISVOID(a) || ARRNELEMS(a) < 2)
+ if (ARRNELEMS(a) < 2)
PG_RETURN_POINTER(a);
a = _int_unique(a);
PG_RETURN_POINTER(a);
Datum
idx(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
int32 result;
CHECKARRVALID(a);
- result = (ARRISVOID(a)) ? 0 : ARRNELEMS(a);
+ result = ARRNELEMS(a);
if (result)
result = intarray_match_first(a, PG_GETARG_INT32(1));
PG_FREE_IF_COPY(a, 0);
Datum
subarray(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
- ArrayType *result;
- int32 start = (PG_GETARG_INT32(1) > 0) ? PG_GETARG_INT32(1) - 1 : PG_GETARG_INT32(1);
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
+ int32 start = PG_GETARG_INT32(1);
int32 len = (fcinfo->nargs == 3) ? PG_GETARG_INT32(2) : 0;
int32 end = 0;
int32 c;
+ ArrayType *result;
+
+ start = (start > 0) ? start - 1 : start;
CHECKARRVALID(a);
- if (ARRISVOID(a))
+ if (ARRISEMPTY(a))
{
PG_FREE_IF_COPY(a, 0);
PG_RETURN_POINTER(new_intArrayType(0));
PG_RETURN_POINTER(new_intArrayType(0));
}
-
result = new_intArrayType(end - start);
if (end - start > 0)
memcpy(ARRPTR(result), ARRPTR(a) + start, (end - start) * sizeof(int32));
Datum
intarray_push_elem(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *result;
result = intarray_add_elem(a, PG_GETARG_INT32(1));
Datum
intarray_push_array(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P(1);
ArrayType *result;
result = intarray_concat_arrays(a, b);
Datum
intarray_del_elem(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
int32 elem = PG_GETARG_INT32(1);
int32 c;
int32 *aa;
i;
CHECKARRVALID(a);
- if (!ARRISVOID(a))
+ if (!ARRISEMPTY(a))
{
c = ARRNELEMS(a);
aa = ARRPTR(a);
Datum
intset_union_elem(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *result;
result = intarray_add_elem(a, PG_GETARG_INT32(1));
Datum
intset_subtract(PG_FUNCTION_ARGS)
{
- ArrayType *a = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)));
- ArrayType *b = (ArrayType *) DatumGetPointer(PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(1)));
+ ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *b = PG_GETARG_ARRAYTYPE_P_COPY(1);
ArrayType *result;
int32 ca;
int32 cb;
#include "_int.h"
+/* arguments are assumed sorted & unique-ified */
bool
inner_int_contains(ArrayType *a, ArrayType *b)
{
int *da,
*db;
- CHECKARRVALID(a);
- CHECKARRVALID(b);
-
- if (ARRISVOID(a) || ARRISVOID(b))
- return FALSE;
-
na = ARRNELEMS(a);
nb = ARRNELEMS(b);
da = ARRPTR(a);
i = j = n = 0;
while (i < na && j < nb)
+ {
if (da[i] < db[j])
i++;
else if (da[i] == db[j])
j++;
}
else
- break;
+ break; /* db[j] is not in da */
+ }
return (n == nb) ? TRUE : FALSE;
}
+/* arguments are assumed sorted */
bool
inner_int_overlap(ArrayType *a, ArrayType *b)
{
int *da,
*db;
- CHECKARRVALID(a);
- CHECKARRVALID(b);
-
- if (ARRISVOID(a) || ARRISVOID(b))
- return FALSE;
-
na = ARRNELEMS(a);
nb = ARRNELEMS(b);
da = ARRPTR(a);
i = j = 0;
while (i < na && j < nb)
+ {
if (da[i] < db[j])
i++;
else if (da[i] == db[j])
return TRUE;
else
j++;
+ }
return FALSE;
}
CHECKARRVALID(a);
CHECKARRVALID(b);
- if (ARRISVOID(a) && ARRISVOID(b))
+ if (ARRISEMPTY(a) && ARRISEMPTY(b))
return new_intArrayType(0);
- if (ARRISVOID(a))
+ if (ARRISEMPTY(a))
r = copy_intArrayType(b);
- if (ARRISVOID(b))
+ if (ARRISEMPTY(b))
r = copy_intArrayType(a);
if (!r)
int i,
j;
- CHECKARRVALID(a);
- CHECKARRVALID(b);
-
- if (ARRISVOID(a) || ARRISVOID(b))
+ if (ARRISEMPTY(a) || ARRISEMPTY(b))
return new_intArrayType(0);
na = ARRNELEMS(a);
i = j = 0;
while (i < na && j < nb)
+ {
if (da[i] < db[j])
i++;
else if (da[i] == db[j])
}
else
j++;
+ }
if ((dr - ARRPTR(r)) == 0)
{
rt__int_size(ArrayType *a, float *size)
{
*size = (float) ARRNELEMS(a);
-
- return;
}
-
-/* len >= 2 */
+/* Sort the given data (len >= 2). Return true if any duplicates found */
bool
isort(int4 *a, int len)
{
- int4 tmp,
- index;
- int4 *cur,
+ int4 cur,
+ prev;
+ int4 *pcur,
+ *pprev,
*end;
bool r = FALSE;
+ /*
+ * We use a simple insertion sort. While this is O(N^2) in the worst
+ * case, it's quite fast if the input is already sorted or nearly so.
+ * Also, for not-too-large inputs it's faster than more complex methods
+ * anyhow.
+ */
end = a + len;
- do
+ for (pcur = a + 1; pcur < end; pcur++)
{
- index = 0;
- cur = a + 1;
- while (cur < end)
+ cur = *pcur;
+ for (pprev = pcur - 1; pprev >= a; pprev--)
{
- if (*(cur - 1) > *cur)
+ prev = *pprev;
+ if (prev <= cur)
{
- tmp = *(cur - 1);
- *(cur - 1) = *cur;
- *cur = tmp;
- index = 1;
+ if (prev == cur)
+ r = TRUE;
+ break;
}
- else if (!r && *(cur - 1) == *cur)
- r = TRUE;
- cur++;
+ pprev[1] = prev;
}
- } while (index);
+ pprev[1] = cur;
+ }
return r;
}
+/* Create a new int array with room for "num" elements */
ArrayType *
new_intArrayType(int num)
{
ArrayType *r;
- int nbytes = ARR_OVERHEAD_NONULLS(NDIM) + sizeof(int) * num;
+ int nbytes = ARR_OVERHEAD_NONULLS(1) + sizeof(int) * num;
r = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(r, nbytes);
- ARR_NDIM(r) = NDIM;
+ ARR_NDIM(r) = 1;
r->dataoffset = 0; /* marker for no null bitmap */
ARR_ELEMTYPE(r) = INT4OID;
- *((int *) ARR_DIMS(r)) = num;
- *((int *) ARR_LBOUND(r)) = 1;
+ ARR_DIMS(r)[0] = num;
+ ARR_LBOUND(r)[0] = 1;
return r;
}
ArrayType *
resize_intArrayType(ArrayType *a, int num)
{
- int nbytes = ARR_OVERHEAD_NONULLS(NDIM) + sizeof(int) * num;
+ int nbytes = ARR_DATA_OFFSET(a) + sizeof(int) * num;
+ int i;
if (num == ARRNELEMS(a))
return a;
a = (ArrayType *) repalloc(a, nbytes);
SET_VARSIZE(a, nbytes);
- *((int *) ARR_DIMS(a)) = num;
+ /* usually the array should be 1-D already, but just in case ... */
+ for (i = 0; i < ARR_NDIM(a); i++)
+ {
+ ARR_DIMS(a)[i] = num;
+ num = 1;
+ }
return a;
}
copy_intArrayType(ArrayType *a)
{
ArrayType *r;
+ int n = ARRNELEMS(a);
- r = new_intArrayType(ARRNELEMS(a));
- memmove(r, a, VARSIZE(r));
+ r = new_intArrayType(n);
+ memcpy(ARRPTR(r), ARRPTR(a), n * sizeof(int4));
return r;
}
size = 0;
for (i = 0; i < len; i += 2)
+ {
if (!i || a[i] != a[i - 1]) /* do not count repeated range */
size += a[i + 1] - a[i] + 1;
+ }
return size;
}
-/* r is sorted and size of r > 1 */
+/* unique-ify elements of r in-place ... r must be sorted already */
ArrayType *
_int_unique(ArrayType *r)
{
*data;
int num = ARRNELEMS(r);
- CHECKARRVALID(r);
-
if (num < 2)
return r;
data = tmp = dr = ARRPTR(r);
while (tmp - data < num)
+ {
if (*tmp != *dr)
*(++dr) = *tmp++;
else
tmp++;
+ }
return resize_intArrayType(r, dr + 1 - ARRPTR(r));
}
i;
CHECKARRVALID(a);
- if (ARRISVOID(a))
- return 0;
c = ARRNELEMS(a);
aa = ARRPTR(a);
for (i = 0; i < c; i++)
int32 c;
CHECKARRVALID(a);
- c = (ARRISVOID(a)) ? 0 : ARRNELEMS(a);
+ c = ARRNELEMS(a);
result = new_intArrayType(c + 1);
r = ARRPTR(result);
if (c > 0)
intarray_concat_arrays(ArrayType *a, ArrayType *b)
{
ArrayType *result;
- int32 ac = (ARRISVOID(a)) ? 0 : ARRNELEMS(a);
- int32 bc = (ARRISVOID(b)) ? 0 : ARRNELEMS(b);
+ int32 ac = ARRNELEMS(a);
+ int32 bc = ARRNELEMS(b);
CHECKARRVALID(a);
CHECKARRVALID(b);
if (entry->leafkey)
{
GISTENTRY *retval;
- ArrayType *in = (ArrayType *) PG_DETOAST_DATUM(entry->key);
+ ArrayType *in = DatumGetArrayTypeP(entry->key);
int4 *ptr;
int num;
GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
CHECKARRVALID(in);
- if (ARRISVOID(in))
+ if (ARRISEMPTY(in))
{
ptr = NULL;
num = 0;
entry->rel, entry->page,
entry->offset, FALSE);
- if (in != (ArrayType *) PG_DETOAST_DATUM(entry->key))
+ if (in != DatumGetArrayTypeP(entry->key))
pfree(in);
PG_RETURN_POINTER(retval);
g_intbig_consistent(PG_FUNCTION_ARGS)
{
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
- ArrayType *query = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_POINTER(1));
+ ArrayType *query = PG_GETARG_ARRAYTYPE_P(1);
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
/* Oid subtype = PG_GETARG_OID(3); */
}
CHECKARRVALID(query);
- if (ARRISVOID(query))
- {
- PG_FREE_IF_COPY(query, 1);
- PG_RETURN_BOOL(FALSE);
- }
switch (strategy)
{
BITVECP dq,
de;
- CHECKARRVALID(query);
-
memset(qp, 0, sizeof(BITVEC));
while (num--)
BITVECP dq,
de;
- CHECKARRVALID(query);
-
memset(qp, 0, sizeof(BITVEC));
while (num--)
DROP OPERATOR CLASS gin__int_ops USING gin;
-DROP FUNCTION ginint4_queryextract(internal, internal, int2, internal, internal);
+DROP FUNCTION ginint4_queryextract(internal, internal, int2, internal, internal, internal, internal);
-DROP FUNCTION ginint4_consistent(internal, int2, internal, int4, internal, internal);
+DROP FUNCTION ginint4_consistent(internal, int2, internal, int4, internal, internal, internal, internal);
DROP OPERATOR CLASS gist__intbig_ops USING gist;
<para>
The <filename>intarray</> module provides a number of useful functions
- and operators for manipulating one-dimensional arrays of integers.
+ and operators for manipulating null-free arrays of integers.
There is also support for indexed searches using some of the operators.
</para>
+ <para>
+ All of these operations will throw an error if a supplied array contains any
+ NULL elements.
+ </para>
+
+ <para>
+ Many of these operations are only sensible for one-dimensional arrays.
+ Although they will accept input arrays of more dimensions, the data is
+ treated as though it were a linear array in storage order.
+ </para>
+
<sect2>
<title><filename>intarray</> Functions and Operators</title>
</para>
<para>
- The containment operators <literal>@></> and <literal><@</> are
- approximately equivalent to <productname>PostgreSQL</>'s built-in operators
- of the same names, except that they work only on integer arrays while the
- built-in operators work for any array type. An important difference is
- that <filename>intarray</>'s operators do not consider an empty array to be
- contained in anything else. This is consistent with the behavior of
- GIN-indexed queries, but not with the usual mathematical definition of
- containment.
+ The operators <literal>&&</>, <literal>@></> and
+ <literal><@</> are equivalent to <productname>PostgreSQL</>'s built-in
+ operators of the same names, except that they work only on integer arrays
+ that do not contain nulls, while the built-in operators work for any array
+ type. This restriction makes them faster than the built-in operators
+ in many cases.
</para>
<para>