/****************************************************************************** This file contains routines that can be bound to a Postgres backend and called by the backend in the process of processing queries. The calling format for these routines is dictated by Postgres architecture. ******************************************************************************/ /* #define BS_DEBUG #define GIST_DEBUG #define GIST_QUERY_DEBUG */ #include "postgres.h" #include #include "access/gist.h" #include "access/itup.h" #include "access/rtree.h" #include "utils/elog.h" #include "utils/palloc.h" #include "utils/array.h" #include "utils/builtins.h" #include "storage/bufpage.h" /* number ranges for compression */ #define MAXNUMRANGE 100 #define max(a,b) ((a) > (b) ? (a) : (b)) #define min(a,b) ((a) <= (b) ? (a) : (b)) #define abs(a) ((a) < (0) ? -(a) : (a)) /* dimension of array */ #define NDIM 1 /* * flags for gist__int_ops, use ArrayType->flags * which is unused (see array.h) */ #define LEAFKEY (1<<31) #define ISLEAFKEY(x) ( ((ArrayType*)(x))->flags & LEAFKEY ) /* useful macros for accessing int4 arrays */ #define ARRPTR(x) ( (int4 *) ARR_DATA_PTR(x) ) #define ARRNELEMS(x) ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x)) #define ARRISVOID(x) ( (x) ? ( ( ARR_NDIM(x) == NDIM ) ? ( ( ARRNELEMS( x ) ) ? 0 : 1 ) : ( ( ARR_NDIM(x) ) ? (elog(ERROR,"Array is not one-dimensional: %d dimensions",ARRNELEMS( x )),1) : 0 ) ) : 0 ) #define SORT(x) \ do { \ if ( ARRNELEMS( x ) > 1 ) \ isort( ARRPTR( x ), ARRNELEMS( x ) ); \ } while(0) #define PREPAREARR(x) \ do { \ if ( ARRNELEMS( x ) > 1 ) \ if ( isort( ARRPTR( x ), ARRNELEMS( x ) ) ) \ x = _int_unique( x ); \ } while(0) /* "wish" function */ #define WISH_F(a,b,c) (double)( -(double)(((a)-(b))*((a)-(b))*((a)-(b)))*(c) ) /* bigint defines */ #define BITBYTE 8 #define SIGLENINT 64 /* >122 => key will toast, so very slow!!! */ #define SIGLEN ( sizeof(int)*SIGLENINT ) #define SIGLENBIT (SIGLEN*BITBYTE) typedef char BITVEC[SIGLEN]; typedef char *BITVECP; #define SIGPTR(x) ( (BITVECP) ARR_DATA_PTR(x) ) #define LOOPBYTE(a) \ for(i=0;i> (i)) & 0x01 ) #define CLRBIT(x,i) GETBYTE(x,i) &= ~( 0x01 << ( (i) % BITBYTE ) ) #define SETBIT(x,i) GETBYTE(x,i) |= ( 0x01 << ( (i) % BITBYTE ) ) #define GETBIT(x,i) ( (GETBYTE(x,i) >> ( (i) % BITBYTE )) & 0x01 ) #define HASHVAL(val) (((unsigned int)(val)) % SIGLENBIT) #define HASH(sign, val) SETBIT((sign), HASHVAL(val)) #ifdef GIST_DEBUG static void printarr(ArrayType *a, int num) { char bbb[16384]; char *cur; int l; int *d; d = ARRPTR(a); *bbb = '\0'; cur = bbb; for (l = 0; l < min(num, ARRNELEMS(a)); l++) { sprintf(cur, "%d ", d[l]); cur = strchr(cur, '\0'); } elog(NOTICE, "\t\t%s", bbb); } static void printbitvec(BITVEC bv) { int i; char str[SIGLENBIT + 1]; str[SIGLENBIT] = '\0'; LOOPBIT(str[i] = (GETBIT(bv, i)) ? '1' : '0'); elog(NOTICE, "BV: %s", str); } #endif /* ** types for functions */ typedef ArrayType *(*formarray) (ArrayType *, ArrayType *); typedef void (*formfloat) (ArrayType *, float *); /* ** usefull function */ static bool isort(int4 *a, const int len); static ArrayType *new_intArrayType(int num); static ArrayType *copy_intArrayType(ArrayType *a); static ArrayType *resize_intArrayType(ArrayType *a, int num); static int internal_size(int *a, int len); static ArrayType *_int_unique(ArrayType *a); /* common GiST function*/ static GIST_SPLITVEC *_int_common_picksplit(bytea *entryvec, GIST_SPLITVEC *v, formarray unionf, formarray interf, formfloat sizef, float coef); static float *_int_common_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, formarray unionf, formfloat sizef); static ArrayType *_int_common_union(bytea *entryvec, int *sizep, formarray unionf); /* ** GiST support methods */ PG_FUNCTION_INFO_V1( g_int_consistent ); PG_FUNCTION_INFO_V1( g_int_compress ); PG_FUNCTION_INFO_V1( g_int_decompress ); PG_FUNCTION_INFO_V1( g_int_penalty ); PG_FUNCTION_INFO_V1( g_int_picksplit ); PG_FUNCTION_INFO_V1( g_int_union ); PG_FUNCTION_INFO_V1( g_int_same ); Datum g_int_consistent(PG_FUNCTION_ARGS); Datum g_int_compress(PG_FUNCTION_ARGS); Datum g_int_decompress(PG_FUNCTION_ARGS); Datum g_int_penalty(PG_FUNCTION_ARGS); Datum g_int_picksplit(PG_FUNCTION_ARGS); Datum g_int_union(PG_FUNCTION_ARGS); Datum g_int_same(PG_FUNCTION_ARGS); /* ** R-tree support functions */ static bool inner_int_contains(ArrayType *a, ArrayType *b); static bool inner_int_overlap(ArrayType *a, ArrayType *b); static ArrayType *inner_int_union(ArrayType *a, ArrayType *b); static ArrayType *inner_int_inter(ArrayType *a, ArrayType *b); static void rt__int_size(ArrayType *a, float *sz); PG_FUNCTION_INFO_V1( _int_different ); PG_FUNCTION_INFO_V1( _int_same ); PG_FUNCTION_INFO_V1( _int_contains ); PG_FUNCTION_INFO_V1( _int_contained ); PG_FUNCTION_INFO_V1( _int_overlap ); PG_FUNCTION_INFO_V1( _int_union ); PG_FUNCTION_INFO_V1( _int_inter ); Datum _int_different(PG_FUNCTION_ARGS); Datum _int_same(PG_FUNCTION_ARGS); Datum _int_contains(PG_FUNCTION_ARGS); Datum _int_contained(PG_FUNCTION_ARGS); Datum _int_overlap(PG_FUNCTION_ARGS); Datum _int_union(PG_FUNCTION_ARGS); Datum _int_inter(PG_FUNCTION_ARGS); /* ** _intbig methods */ PG_FUNCTION_INFO_V1( g_intbig_consistent ); PG_FUNCTION_INFO_V1( g_intbig_compress ); PG_FUNCTION_INFO_V1( g_intbig_decompress ); PG_FUNCTION_INFO_V1( g_intbig_penalty ); PG_FUNCTION_INFO_V1( g_intbig_picksplit ); PG_FUNCTION_INFO_V1( g_intbig_union ); PG_FUNCTION_INFO_V1( g_intbig_same ); Datum g_intbig_consistent(PG_FUNCTION_ARGS); Datum g_intbig_compress(PG_FUNCTION_ARGS); Datum g_intbig_decompress(PG_FUNCTION_ARGS); Datum g_intbig_penalty(PG_FUNCTION_ARGS); Datum g_intbig_picksplit(PG_FUNCTION_ARGS); Datum g_intbig_union(PG_FUNCTION_ARGS); Datum g_intbig_same(PG_FUNCTION_ARGS); static bool _intbig_contains(ArrayType *a, ArrayType *b); static bool _intbig_overlap(ArrayType *a, ArrayType *b); static ArrayType *_intbig_union(ArrayType *a, ArrayType *b); static ArrayType * _intbig_inter(ArrayType *a, ArrayType *b); static void rt__intbig_size(ArrayType *a, float *sz); /***************************************************************************** * Boolean Search *****************************************************************************/ #define BooleanSearchStrategy 20 /* * item in polish notation with back link * to left operand */ typedef struct ITEM { int2 type; int2 left; int4 val; } ITEM; typedef struct { int4 len; int4 size; char data[1]; } QUERYTYPE; #define HDRSIZEQT ( 2*sizeof(int4) ) #define COMPUTESIZE(size) ( HDRSIZEQT + size * sizeof(ITEM) ) #define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT ) PG_FUNCTION_INFO_V1(bqarr_in); PG_FUNCTION_INFO_V1(bqarr_out); Datum bqarr_in(PG_FUNCTION_ARGS); Datum bqarr_out(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(boolop); Datum boolop(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(rboolop); Datum rboolop(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(querytree); Datum querytree(PG_FUNCTION_ARGS); static bool signconsistent( QUERYTYPE *query, BITVEC sign, bool leaf ); static bool execconsistent( QUERYTYPE *query, ArrayType *array, bool leaf ); /***************************************************************************** * GiST functions *****************************************************************************/ /* ** The GiST Consistent method for _intments ** Should return false if for all data items x below entry, ** the predicate x op query == FALSE, where op is the oper ** corresponding to strategy in the pg_amop table. */ Datum g_int_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); ArrayType *query = ( ArrayType * )PG_GETARG_POINTER(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool retval; if ( strategy == BooleanSearchStrategy ) PG_RETURN_BOOL(execconsistent( (QUERYTYPE*)query, (ArrayType *) DatumGetPointer(entry->key), ISLEAFKEY( (ArrayType *) DatumGetPointer(entry->key) ) ) ); /* XXX are we sure it's safe to scribble on the query object here? */ /* XXX what about toasted input? */ /* sort query for fast search, key is already sorted */ if ( ARRISVOID( query ) ) PG_RETURN_BOOL(false); PREPAREARR(query); switch (strategy) { case RTOverlapStrategyNumber: retval = inner_int_overlap((ArrayType *) DatumGetPointer(entry->key), query); break; case RTSameStrategyNumber: case RTContainsStrategyNumber: retval = inner_int_contains((ArrayType *) DatumGetPointer(entry->key), query); break; case RTContainedByStrategyNumber: if ( GIST_LEAF(entry) ) retval = inner_int_contains(query, (ArrayType *) DatumGetPointer(entry->key) ); else retval = inner_int_overlap((ArrayType *) DatumGetPointer(entry->key), query); break; default: retval = FALSE; } PG_RETURN_BOOL(retval); } Datum g_int_union(PG_FUNCTION_ARGS) { PG_RETURN_POINTER( _int_common_union( (bytea *) PG_GETARG_POINTER(0), (int *) PG_GETARG_POINTER(1), inner_int_union ) ); } /* ** GiST Compress and Decompress methods */ Datum g_int_compress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); GISTENTRY *retval; ArrayType *r; int len; int *dr; int i, min, cand; if (entry->leafkey) { r = (ArrayType *) PG_DETOAST_DATUM_COPY(entry->key); PREPAREARR(r); r->flags |= LEAFKEY; retval = palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, VARSIZE(r), FALSE); PG_RETURN_POINTER(retval); } r = (ArrayType *) PG_DETOAST_DATUM(entry->key); if ( ISLEAFKEY( r ) || ARRISVOID(r) ) { if ( r != (ArrayType*)DatumGetPointer(entry->key) ) pfree(r); PG_RETURN_POINTER(entry); } if ( (len=ARRNELEMS(r)) >= 2 * MAXNUMRANGE) { /* compress */ if ( r == (ArrayType*)DatumGetPointer( entry->key) ) r = (ArrayType *) PG_DETOAST_DATUM_COPY(entry->key); r = resize_intArrayType(r, 2 * (len)); dr = ARRPTR(r); for (i = len - 1; i >= 0; i--) dr[2 * i] = dr[2 * i + 1] = dr[i]; len *= 2; cand = 1; while (len > MAXNUMRANGE * 2) { min = 0x7fffffff; for (i = 2; i < len; i += 2) if (min > (dr[i] - dr[i - 1])) { min = (dr[i] - dr[i - 1]); cand = i; } memmove((void *) &dr[cand - 1], (void *) &dr[cand + 1], (len - cand - 1) * sizeof(int)); len -= 2; } r = resize_intArrayType(r, len); retval = palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, VARSIZE(r), FALSE); PG_RETURN_POINTER(retval); } else { PG_RETURN_POINTER(entry); } PG_RETURN_POINTER(entry); } Datum g_int_decompress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); GISTENTRY *retval; ArrayType *r; int *dr, lenr; ArrayType *in; int lenin; int *din; int i, j; in = (ArrayType *) PG_DETOAST_DATUM(entry->key); if ( ARRISVOID(in) ) { PG_RETURN_POINTER(entry); } lenin = ARRNELEMS(in); if (lenin < 2 * MAXNUMRANGE || ISLEAFKEY( in ) ) { /* not comressed value */ if ( in != (ArrayType *) DatumGetPointer(entry->key)) { retval = palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(in), entry->rel, entry->page, entry->offset, VARSIZE(in), FALSE); PG_RETURN_POINTER(retval); } PG_RETURN_POINTER(entry); } din = ARRPTR(in); lenr = internal_size(din, lenin); r = new_intArrayType(lenr); dr = ARRPTR(r); for (i = 0; i < lenin; i += 2) for (j = din[i]; j <= din[i + 1]; j++) if ((!i) || *(dr - 1) != j) *dr++ = j; if (in != (ArrayType *) DatumGetPointer(entry->key)) pfree(in); retval = palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, VARSIZE(r), FALSE); PG_RETURN_POINTER(retval); } /* ** The GiST Penalty method for _intments */ Datum g_int_penalty(PG_FUNCTION_ARGS) { PG_RETURN_POINTER( _int_common_penalty( (GISTENTRY *)PG_GETARG_POINTER(0), (GISTENTRY *)PG_GETARG_POINTER(1), (float *) PG_GETARG_POINTER(2), inner_int_union, rt__int_size ) ); } Datum g_int_picksplit(PG_FUNCTION_ARGS) { PG_RETURN_POINTER( _int_common_picksplit( (bytea *)PG_GETARG_POINTER(0), (GIST_SPLITVEC *)PG_GETARG_POINTER(1), inner_int_union, inner_int_inter, rt__int_size, 0.01 ) ); } /* ** Equality methods */ Datum g_int_same(PG_FUNCTION_ARGS) { ArrayType *a = (ArrayType*)PointerGetDatum(PG_GETARG_POINTER(0)); ArrayType *b = (ArrayType*)PointerGetDatum(PG_GETARG_POINTER(1)); bool *result = (bool *)PG_GETARG_POINTER(2); int4 n = ARRNELEMS(a); int4 *da, *db; if ( n != ARRNELEMS(b) ) { *result = false; PG_RETURN_POINTER(result); } *result = TRUE; 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)) ) )); } 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))); bool res; if (ARRISVOID(a) || ARRISVOID(b)) return FALSE; PREPAREARR(a); PREPAREARR(b); res = inner_int_contains(a, b); pfree(a); pfree(b); PG_RETURN_BOOL( res ); } static bool inner_int_contains(ArrayType *a, ArrayType *b) { int na, nb; int i, j, n; int *da, *db; if (ARRISVOID(a) || ARRISVOID(b)) return FALSE; na = ARRNELEMS(a); nb = ARRNELEMS(b); da = ARRPTR(a); db = ARRPTR(b); #ifdef GIST_DEBUG elog(NOTICE, "contains %d %d", na, nb); #endif i = j = n = 0; while (i < na && j < nb) if (da[i] < db[j]) i++; else if (da[i] == db[j]) { n++; i++; j++; } else j++; return (n == nb) ? TRUE : FALSE; } /***************************************************************************** * Operator class for R-tree indexing *****************************************************************************/ Datum _int_different(PG_FUNCTION_ARGS) { PG_RETURN_BOOL( ! DatumGetBool( DirectFunctionCall2( _int_same, PointerGetDatum(PG_GETARG_POINTER(0)), PointerGetDatum(PG_GETARG_POINTER(1)) ) )); } 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))); int na, nb; int n; int *da, *db; bool result; bool avoid = ARRISVOID(a); bool bvoid = ARRISVOID(b); if (avoid || bvoid) return (avoid && bvoid) ? TRUE : FALSE; SORT(a); SORT(b); na = ARRNELEMS(a); nb = ARRNELEMS(b); da = ARRPTR(a); db = ARRPTR(b); result = FALSE; if (na == nb) { result = TRUE; for (n = 0; n < na; n++) if (da[n] != db[n]) { result = FALSE; break; } } pfree(a); pfree(b); PG_RETURN_BOOL(result); } /* _int_overlap -- does a overlap b? */ 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))); bool result; if (ARRISVOID(a) || ARRISVOID(b)) return FALSE; SORT(a); SORT(b); result = inner_int_overlap(a, b); pfree(a); pfree(b); PG_RETURN_BOOL( result ); } static bool inner_int_overlap(ArrayType *a, ArrayType *b) { int na, nb; int i, j; int *da, *db; if (ARRISVOID(a) || ARRISVOID(b)) return FALSE; na = ARRNELEMS(a); nb = ARRNELEMS(b); da = ARRPTR(a); db = ARRPTR(b); #ifdef GIST_DEBUG elog(NOTICE, "g_int_overlap"); #endif 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; } 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 *result; if (!ARRISVOID(a)) SORT(a); if (!ARRISVOID(b)) SORT(b); result = inner_int_union(a, b); if (a) pfree(a); if (b) pfree(b); PG_RETURN_POINTER( result ); } static ArrayType * inner_int_union(ArrayType *a, ArrayType *b) { ArrayType *r = NULL; int na, nb; int *da, *db, *dr; int i, j; if (ARRISVOID(a) && ARRISVOID(b)) return new_intArrayType(0); if (ARRISVOID(a)) r = copy_intArrayType(b); if (ARRISVOID(b)) r = copy_intArrayType(a); if (r) dr = ARRPTR(r); else { na = ARRNELEMS(a); nb = ARRNELEMS(b); da = ARRPTR(a); db = ARRPTR(b); r = new_intArrayType(na + nb); dr = ARRPTR(r); /* union */ i = j = 0; while (i < na && j < nb) if (da[i] < db[j]) *dr++ = da[i++]; else *dr++ = db[j++]; while (i < na) *dr++ = da[i++]; while (j < nb) *dr++ = db[j++]; } if (ARRNELEMS(r) > 1) r = _int_unique(r); return r; } 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 *result; if (ARRISVOID(a) || ARRISVOID(b)) PG_RETURN_POINTER(new_intArrayType(0)); SORT(a); SORT(b); result = inner_int_inter(a, b); pfree(a); pfree(b); PG_RETURN_POINTER( result ); } static ArrayType * inner_int_inter(ArrayType *a, ArrayType *b) { ArrayType *r; int na, nb; int *da, *db, *dr; int i, j; if (ARRISVOID(a) || ARRISVOID(b)) return new_intArrayType(0); na = ARRNELEMS(a); nb = ARRNELEMS(b); da = ARRPTR(a); db = ARRPTR(b); r = new_intArrayType(min(na, nb)); dr = ARRPTR(r); i = j = 0; while (i < na && j < nb) if (da[i] < db[j]) i++; else if (da[i] == db[j]) { if (i + j == 0 || (i + j > 0 && *(dr - 1) != db[j])) *dr++ = db[j]; i++; j++; } else j++; if ((dr - ARRPTR(r)) == 0) { pfree(r); return new_intArrayType(0); } else return resize_intArrayType(r, dr - ARRPTR(r)); } static void rt__int_size(ArrayType *a, float *size) { *size = (float) ARRNELEMS(a); return; } /***************************************************************************** * Miscellaneous operators and functions *****************************************************************************/ /* len >= 2 */ static bool isort(int4 *a, int len) { int4 tmp, index; int4 *cur, *end; bool r = FALSE; end = a + len; do { index = 0; cur = a + 1; while (cur < end) { if (*(cur - 1) > *cur) { tmp = *(cur - 1); *(cur - 1) = *cur; *cur = tmp; index = 1; } else if (!r && *(cur - 1) == *cur) r = TRUE; cur++; } } while (index); return r; } static ArrayType * new_intArrayType(int num) { ArrayType *r; int nbytes = ARR_OVERHEAD(NDIM) + sizeof(int) * num; r = (ArrayType *) palloc(nbytes); MemSet(r, 0, nbytes); r->size = nbytes; r->ndim = NDIM; r->flags &= ~LEAFKEY; *((int *) ARR_DIMS(r)) = num; *((int *) ARR_LBOUND(r)) = 1; return r; } static ArrayType * resize_intArrayType(ArrayType *a, int num) { int nbytes = ARR_OVERHEAD(NDIM) + sizeof(int) * num; if (num == ARRNELEMS(a)) return a; a = (ArrayType *) repalloc(a, nbytes); a->size = nbytes; *((int *) ARR_DIMS(a)) = num; return a; } static ArrayType * copy_intArrayType(ArrayType *a) { ArrayType *r; r = new_intArrayType(ARRNELEMS(a)); memmove(r, a, VARSIZE(a)); return r; } /* num for compressed key */ static int internal_size(int *a, int len) { int i, 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 */ static ArrayType * _int_unique(ArrayType *r) { int *tmp, *dr, *data; int num = ARRNELEMS(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)); } /********************************************************************* ** intbig functions *********************************************************************/ static void gensign(BITVEC sign, int *a, int len) { int i; /* we assume that the sign vector is previously zeroed */ for (i = 0; i < len; i++) { HASH(sign, *a); a++; } } static bool _intbig_overlap(ArrayType *a, ArrayType *b) { int i; BITVECP da, db; da = SIGPTR(a); db = SIGPTR(b); LOOPBYTE(if (da[i] & db[i]) return TRUE); return FALSE; } static bool _intbig_contains(ArrayType *a, ArrayType *b) { int i; BITVECP da, db; da = SIGPTR(a); db = SIGPTR(b); LOOPBYTE(if (db[i] & ~da[i]) return FALSE); return TRUE; } static void rt__intbig_size(ArrayType *a, float *sz) { int i, len = 0; BITVECP bv = SIGPTR(a); LOOPBYTE( len += GETBITBYTE(bv,0) + GETBITBYTE(bv,1) + GETBITBYTE(bv,2) + GETBITBYTE(bv,3) + GETBITBYTE(bv,4) + GETBITBYTE(bv,5) + GETBITBYTE(bv,6) + GETBITBYTE(bv,7) ; bv = (BITVECP) ( ((char*)bv) + 1 ); ); *sz = (float) len; return; } static ArrayType * _intbig_union(ArrayType *a, ArrayType *b) { ArrayType *r; BITVECP da, db, dr; int i; r = new_intArrayType(SIGLENINT); da = SIGPTR(a); db = SIGPTR(b); dr = SIGPTR(r); LOOPBYTE(dr[i] = da[i] | db[i]); return r; } static ArrayType * _intbig_inter(ArrayType *a, ArrayType *b) { ArrayType *r; BITVECP da, db, dr; int i; r = new_intArrayType(SIGLENINT); da = SIGPTR(a); db = SIGPTR(b); dr = SIGPTR(r); LOOPBYTE(dr[i] = da[i] & db[i]); return r; } Datum g_intbig_same(PG_FUNCTION_ARGS) { ArrayType *a = (ArrayType *)PG_GETARG_POINTER(0); ArrayType *b = (ArrayType *)PG_GETARG_POINTER(1); bool *result = (bool *)PG_GETARG_POINTER(2); BITVECP da, db; int i; da = SIGPTR(a); db = SIGPTR(b); LOOPBYTE( if (da[i] != db[i]) { *result = FALSE; PG_RETURN_POINTER( result ); } ); *result = TRUE; PG_RETURN_POINTER( result ); } Datum g_intbig_compress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); GISTENTRY *retval; ArrayType *r, *in; bool maycompress = true; int i; if (DatumGetPointer(entry->key) != NULL) in = (ArrayType *) PG_DETOAST_DATUM(entry->key); else in = NULL; if (!entry->leafkey) { LOOPBYTE( if ( ( ((char*)ARRPTR(in))[i] & 0xff ) != 0xff ) { maycompress = false; break; } ); if ( maycompress ) { retval = palloc(sizeof(GISTENTRY)); r = new_intArrayType(1); gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, VARSIZE(r), FALSE); PG_RETURN_POINTER( retval ); } PG_RETURN_POINTER( entry ); } retval = palloc(sizeof(GISTENTRY)); r = new_intArrayType( SIGLENINT ); if (ARRISVOID(in)) { gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, VARSIZE(r), FALSE); if (in != (ArrayType *) DatumGetPointer(entry->key)) pfree(in); PG_RETURN_POINTER (retval); } gensign(SIGPTR(r), ARRPTR(in), ARRNELEMS(in)); LOOPBYTE( if( ( ((char*)ARRPTR(in))[i] & 0xff ) != 0xff ) { maycompress = false; break; } ); if ( maycompress ) { pfree(r); r = new_intArrayType(1); } gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, entry->offset, VARSIZE(r), FALSE); if ( in != (ArrayType *) DatumGetPointer(entry->key)) pfree(in); PG_RETURN_POINTER (retval); } Datum g_intbig_decompress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); ArrayType *key; key = (ArrayType *) PG_DETOAST_DATUM(entry->key); if ( key != (ArrayType *) DatumGetPointer(entry->key)) { GISTENTRY *retval; retval = palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(key), entry->rel, entry->page, entry->offset, (key) ? VARSIZE(key) : 0, FALSE); PG_RETURN_POINTER( retval ); } if ( ARRNELEMS(key) == 1 ) { GISTENTRY *retval; ArrayType *newkey; retval = palloc(sizeof(GISTENTRY)); newkey = new_intArrayType(SIGLENINT); MemSet( (void*)ARRPTR(newkey), 0xff, SIGLEN ); gistentryinit(*retval, PointerGetDatum(newkey), entry->rel, entry->page, entry->offset, VARSIZE(newkey), FALSE); PG_RETURN_POINTER( retval ); } PG_RETURN_POINTER( entry ); } Datum g_intbig_picksplit(PG_FUNCTION_ARGS) { PG_RETURN_POINTER( _int_common_picksplit( (bytea *)PG_GETARG_POINTER(0), (GIST_SPLITVEC *)PG_GETARG_POINTER(1), _intbig_union, _intbig_inter, rt__intbig_size, 0.1 ) ); } Datum g_intbig_union(PG_FUNCTION_ARGS) { PG_RETURN_POINTER( _int_common_union( (bytea *) PG_GETARG_POINTER(0), (int *) PG_GETARG_POINTER(1), _intbig_union ) ); } Datum g_intbig_penalty(PG_FUNCTION_ARGS) { PG_RETURN_POINTER( _int_common_penalty( (GISTENTRY *)PG_GETARG_POINTER(0), (GISTENTRY *)PG_GETARG_POINTER(1), (float *) PG_GETARG_POINTER(2), _intbig_union, rt__intbig_size ) ); } Datum g_intbig_consistent(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *)PG_GETARG_POINTER(0); ArrayType *query = ( ArrayType * )PG_GETARG_POINTER(1); StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); bool retval; ArrayType *q; if ( strategy == BooleanSearchStrategy ) PG_RETURN_BOOL(signconsistent( (QUERYTYPE*)query, SIGPTR((ArrayType *) DatumGetPointer(entry->key)), false ) ); /* XXX what about toasted input? */ if (ARRISVOID(query)) return FALSE; q = new_intArrayType(SIGLENINT); gensign(SIGPTR(q), ARRPTR(query), ARRNELEMS(query)); switch (strategy) { case RTOverlapStrategyNumber: retval = _intbig_overlap((ArrayType *) DatumGetPointer(entry->key), q); break; case RTSameStrategyNumber: case RTContainsStrategyNumber: retval = _intbig_contains((ArrayType *) DatumGetPointer(entry->key), q); break; case RTContainedByStrategyNumber: retval = _intbig_overlap((ArrayType *) DatumGetPointer(entry->key), q); break; default: retval = FALSE; } pfree(q); PG_RETURN_BOOL(retval); } /***************************************************************** ** Common GiST Method *****************************************************************/ /* ** The GiST Union method for _intments ** returns the minimal set that encloses all the entries in entryvec */ static ArrayType * _int_common_union(bytea *entryvec, int *sizep, formarray unionf) { int numranges, i; ArrayType *out = (ArrayType *) NULL; ArrayType *tmp; #ifdef GIST_DEBUG elog(NOTICE, "_int_common_union in"); #endif numranges = (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY); tmp = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[0].key); for (i = 1; i < numranges; i++) { out = (*unionf) (tmp, (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[i].key)); if (i > 1 && tmp) pfree(tmp); tmp = out; } out->flags &= ~LEAFKEY; *sizep = VARSIZE(out); if (*sizep == 0) { pfree(out); #ifdef GIST_DEBUG elog(NOTICE, "_int_common_union out1"); #endif return NULL; } #ifdef GIST_DEBUG elog(NOTICE, "_int_common_union out"); #endif return (out); } /***************************************** * The GiST Penalty method for _intments * *****************************************/ static float * _int_common_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result, formarray unionf, formfloat sizef) { ArrayType *ud; float tmp1, tmp2; #ifdef GIST_DEBUG elog(NOTICE, "penalty"); #endif ud = (*unionf) ((ArrayType *) DatumGetPointer(origentry->key), (ArrayType *) DatumGetPointer(newentry->key)); (*sizef) (ud, &tmp1); (*sizef) ((ArrayType *) DatumGetPointer(origentry->key), &tmp2); *result = tmp1 - tmp2; pfree(ud); #ifdef GIST_DEBUG elog(NOTICE, "--penalty\t%g", *result); #endif return (result); } typedef struct { OffsetNumber pos; float cost; } SPLITCOST; static int comparecost( const void *a, const void *b ) { if ( ((SPLITCOST*)a)->cost == ((SPLITCOST*)b)->cost ) return 0; else return ( ((SPLITCOST*)a)->cost > ((SPLITCOST*)b)->cost ) ? 1 : -1; } /* ** The GiST PickSplit method for _intments ** We use Guttman's poly time split algorithm */ static GIST_SPLITVEC * _int_common_picksplit(bytea *entryvec, GIST_SPLITVEC *v, formarray unionf, formarray interf, formfloat sizef, float coef) { OffsetNumber i, j; ArrayType *datum_alpha, *datum_beta; ArrayType *datum_l, *datum_r; ArrayType *union_d, *union_dl, *union_dr; ArrayType *inter_d; bool firsttime; float size_alpha, size_beta, size_union, size_inter; float size_waste, waste; float size_l, size_r; int nbytes; OffsetNumber seed_1 = 0, seed_2 = 0; OffsetNumber *left, *right; OffsetNumber maxoff; SPLITCOST *costvector; #ifdef GIST_DEBUG elog(NOTICE, "--------picksplit %d", (VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)); #endif maxoff = ((VARSIZE(entryvec) - VARHDRSZ) / sizeof(GISTENTRY)) - 2; nbytes = (maxoff + 2) * sizeof(OffsetNumber); v->spl_left = (OffsetNumber *) palloc(nbytes); v->spl_right = (OffsetNumber *) palloc(nbytes); firsttime = true; waste = 0.0; for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) { datum_alpha = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[i].key); for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) { datum_beta = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[j].key); /* compute the wasted space by unioning these guys */ /* size_waste = size_union - size_inter; */ union_d = (*unionf) (datum_alpha, datum_beta); (*sizef) (union_d, &size_union); inter_d = (*interf) (datum_alpha, datum_beta); (*sizef) (inter_d, &size_inter); size_waste = size_union - size_inter; pfree(union_d); if (inter_d != (ArrayType *) NULL) pfree(inter_d); /* * are these a more promising split that what we've already * seen? */ if (size_waste > waste || firsttime) { waste = size_waste; seed_1 = i; seed_2 = j; firsttime = false; } } } left = v->spl_left; v->spl_nleft = 0; right = v->spl_right; v->spl_nright = 0; if ( seed_1 == 0 || seed_2 == 0 ) { seed_1 = 1; seed_2 = 2; } datum_alpha = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[seed_1].key); datum_l = copy_intArrayType(datum_alpha); (*sizef) (datum_l, &size_l); datum_beta = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[seed_2].key); datum_r = copy_intArrayType(datum_beta); (*sizef) (datum_r, &size_r); maxoff = OffsetNumberNext(maxoff); /* * sort entries */ costvector=(SPLITCOST*)palloc( sizeof(SPLITCOST)*maxoff ); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { costvector[i-1].pos = i; datum_alpha = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[i].key); union_d = (*unionf)(datum_l, datum_alpha); (*sizef)(union_d, &size_alpha); pfree( union_d ); union_d = (*unionf)(datum_r, datum_alpha); (*sizef)(union_d, &size_beta); pfree( union_d ); costvector[i-1].cost = abs( (size_alpha - size_l) - (size_beta - size_r) ); } qsort( (void*)costvector, maxoff, sizeof(SPLITCOST), comparecost ); /* * Now split up the regions between the two seeds. An important * property of this split algorithm is that the split vector v has the * indices of items to be split in order in its left and right * vectors. We exploit this property by doing a merge in the code * that actually splits the page. * * For efficiency, we also place the new index tuple in this loop. This * is handled at the very end, when we have placed all the existing * tuples and i == maxoff + 1. */ for (j = 0; j < maxoff; j++) { i = costvector[j].pos; /* * If we've already decided where to place this item, just put it * on the right list. Otherwise, we need to figure out which page * needs the least enlargement in order to store the item. */ if (i == seed_1) { *left++ = i; v->spl_nleft++; continue; } else if (i == seed_2) { *right++ = i; v->spl_nright++; continue; } /* okay, which page needs least enlargement? */ datum_alpha = (ArrayType *) DatumGetPointer(((GISTENTRY *) VARDATA(entryvec))[i].key); union_dl = (*unionf) (datum_l, datum_alpha); union_dr = (*unionf) (datum_r, datum_alpha); (*sizef) (union_dl, &size_alpha); (*sizef) (union_dr, &size_beta); /* pick which page to add it to */ if (size_alpha - size_l < size_beta - size_r + WISH_F(v->spl_nleft, v->spl_nright, coef)) { if (datum_l) pfree(datum_l); if (union_dr) pfree(union_dr); datum_l = union_dl; size_l = size_alpha; *left++ = i; v->spl_nleft++; } else { if (datum_r) pfree(datum_r); if (union_dl) pfree(union_dl); datum_r = union_dr; size_r = size_beta; *right++ = i; v->spl_nright++; } } pfree( costvector ); *right = *left = FirstOffsetNumber; datum_l->flags &= ~LEAFKEY; datum_r->flags &= ~LEAFKEY; v->spl_ldatum = PointerGetDatum(datum_l); v->spl_rdatum = PointerGetDatum(datum_r); #ifdef GIST_DEBUG elog(NOTICE, "--------ENDpicksplit %d %d", v->spl_nleft, v->spl_nright); #endif return v; } /***************************************************************************** * BoolSearch *****************************************************************************/ #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 #define WAITOPERATOR 3 /* * node of query tree, also used * for storing polish notation in parser */ typedef struct NODE { int4 type; int4 val; struct NODE *next; } NODE; typedef struct { char *buf; int4 state; int4 count; /* reverse polish notation in list (for temprorary usage)*/ NODE *str; /* number in str */ int4 num; } WORKSTATE; /* * get token from query string */ static int4 gettoken( WORKSTATE* state, int4* val ) { char nnn[16], *curnnn; curnnn=nnn; while(1) { switch(state->state) { case WAITOPERAND: curnnn=nnn; if ( (*(state->buf)>='0' && *(state->buf)<='9') || *(state->buf)=='-' ) { state->state = WAITENDOPERAND; *curnnn = *(state->buf); curnnn++; } else if ( *(state->buf) == '!' ) { (state->buf)++; *val = (int4)'!'; return OPR; } else if ( *(state->buf) == '(' ) { state->count++; (state->buf)++; return OPEN; } else if ( *(state->buf) != ' ' ) return ERR; break; case WAITENDOPERAND: if ( *(state->buf)>='0' && *(state->buf)<='9' ) { *curnnn = *(state->buf); curnnn++; } else { *curnnn = '\0'; *val=(int4)atoi( nnn ); state->state = WAITOPERATOR; return ( state->count && *(state->buf) == '\0' ) ? ERR : VAL; } break; case WAITOPERATOR: if ( *(state->buf) == '&' || *(state->buf) == '|' ) { state->state = WAITOPERAND; *val = (int4) *(state->buf); (state->buf)++; return OPR; } else if ( *(state->buf) == ')' ) { (state->buf)++; state->count--; return ( state->count <0 ) ? ERR : CLOSE; } else if ( *(state->buf) == '\0' ) { return ( state->count ) ? ERR : END; } else if ( *(state->buf) != ' ' ) return ERR; break; default: return ERR; break; } (state->buf)++; } return END; } /* * push new one in polish notation reverse view */ static void pushquery( WORKSTATE *state, int4 type, int4 val ) { NODE *tmp = (NODE*)palloc(sizeof(NODE)); tmp->type=type; tmp->val =val; tmp->next = state->str; state->str = tmp; state->num++; } #define STACKDEPTH 16 /* * make polish notaion of query */ static int4 makepol(WORKSTATE *state) { int4 val,type; int4 stack[STACKDEPTH]; int4 lenstack=0; while( (type=gettoken(state, &val))!=END ) { switch(type) { case VAL: pushquery(state, type, val); while ( lenstack && (stack[ lenstack-1 ] == (int4)'&' || stack[ lenstack-1 ] == (int4)'!') ) { lenstack--; pushquery(state, OPR, stack[ lenstack ]); } break; case OPR: if ( lenstack && val == (int4) '|' ) { pushquery(state, OPR, val); } else { if ( lenstack == STACKDEPTH ) elog(ERROR,"Stack too short"); stack[ lenstack ] = val; lenstack++; } break; case OPEN: if ( makepol( state ) == ERR ) return ERR; if ( lenstack && (stack[ lenstack-1 ] == (int4)'&' || stack[ lenstack-1 ] == (int4)'!') ) { lenstack--; pushquery(state, OPR, stack[ lenstack ]); } break; case CLOSE: while ( lenstack ) { lenstack--; pushquery(state, OPR, stack[ lenstack ]); }; return END; break; case ERR: default: elog(ERROR,"Syntax error"); return ERR; } } while (lenstack) { lenstack--; pushquery(state, OPR, stack[ lenstack ]); }; return END; } typedef struct { int4 *arrb; int4 *arre; } CHKVAL; /* * is there value 'val' in array or not ? */ static bool checkcondition_arr( void *checkval, int4 val ) { int4 *StopLow = ((CHKVAL*)checkval)->arrb; int4 *StopHigh = ((CHKVAL*)checkval)->arre; int4 *StopMiddle; /* Loop invariant: StopLow <= val < StopHigh */ while (StopLow < StopHigh) { StopMiddle = StopLow + (StopHigh - StopLow) / 2; if (*StopMiddle == val) return (true); else if (*StopMiddle < val ) StopLow = StopMiddle + 1; else StopHigh = StopMiddle; } return false; } static bool checkcondition_bit( void *checkval, int4 val ) { return GETBIT( checkval, HASHVAL( val ) ); } /* * check for boolean condition */ static bool execute( ITEM* curitem, void *checkval, bool calcnot, bool (*chkcond)(void *checkval, int4 val )) { if ( curitem->type == VAL ) { return (*chkcond)( checkval, curitem->val ); } else if ( curitem->val == (int4)'!' ) { return ( calcnot ) ? ( ( execute(curitem - 1, checkval, calcnot, chkcond) ) ? false : true ) : true; } else if ( curitem->val == (int4)'&' ) { if ( execute(curitem + curitem->left, checkval, calcnot, chkcond) ) return execute(curitem - 1, checkval, calcnot, chkcond); else return false; } else { /* |-operator */ if ( execute(curitem + curitem->left, checkval, calcnot, chkcond) ) return true; else return execute(curitem - 1, checkval, calcnot, chkcond); } return false; } /* * signconsistent & execconsistent called by *_consistent */ static bool signconsistent( QUERYTYPE *query, BITVEC sign, bool calcnot ) { return execute( GETQUERY(query) + query->size-1 , (void*)sign, calcnot, checkcondition_bit ); } static bool execconsistent( QUERYTYPE *query, ArrayType *array, bool calcnot ) { CHKVAL chkval; chkval.arrb = ARRPTR(array); chkval.arre = chkval.arrb + ARRNELEMS(array); return execute( GETQUERY(query) + query->size-1 , (void*)&chkval, calcnot, checkcondition_arr ); } /* * boolean operations */ Datum rboolop(PG_FUNCTION_ARGS) { return DirectFunctionCall2( boolop, PG_GETARG_DATUM(1), 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)); CHKVAL chkval; bool result; 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 , &chkval, true, checkcondition_arr ); pfree(val); PG_FREE_IF_COPY(query,1); PG_RETURN_BOOL( result ); } static void findoprnd( ITEM *ptr, int4 *pos ) { #ifdef BS_DEBUG elog(NOTICE, ( ptr[*pos].type == OPR ) ? "%d %c" : "%d %d ", *pos, ptr[*pos].val ); #endif if ( ptr[*pos].type == VAL ) { ptr[*pos].left = 0; (*pos)--; } else if ( ptr[*pos].val == (int4)'!' ) { ptr[*pos].left = -1; (*pos)--; findoprnd( ptr, pos ); } else { ITEM *curitem = &ptr[*pos]; int4 tmp = *pos; (*pos)--; findoprnd(ptr,pos); curitem->left = *pos - tmp; findoprnd(ptr,pos); } } /* * input */ Datum bqarr_in(PG_FUNCTION_ARGS) { char *buf=(char*)PG_GETARG_POINTER(0); WORKSTATE state; int4 i; QUERYTYPE *query; int4 commonlen; ITEM *ptr; NODE *tmp; int4 pos=0; #ifdef BS_DEBUG char pbuf[16384],*cur; #endif state.buf = buf; state.state = WAITOPERAND; state.count = 0; state.num = 0; state.str=NULL; /* make polish notation (postfix, but in reverse order) */ makepol( &state ); if (!state.num) elog( ERROR,"Empty query"); commonlen = COMPUTESIZE(state.num); query = (QUERYTYPE*) palloc( commonlen ); query->len = commonlen; query->size = state.num; ptr = GETQUERY(query); for(i=state.num-1; i>=0; i-- ) { ptr[i].type = state.str->type; ptr[i].val = state.str->val; tmp = state.str->next; pfree( state.str ); state.str = tmp; } pos = query->size-1; findoprnd( ptr, &pos ); #ifdef BS_DEBUG cur = pbuf; *cur = '\0'; for( i=0;isize;i++ ) { if ( ptr[i].type == OPR ) sprintf(cur, "%c(%d) ", ptr[i].val, ptr[i].left); else sprintf(cur, "%d ", ptr[i].val ); cur = strchr(cur,'\0'); } elog(NOTICE,"POR: %s", pbuf); #endif PG_RETURN_POINTER( query ); } /* * out function */ typedef struct { ITEM *curpol; char *buf; char *cur; int4 buflen; } INFIX; #define RESIZEBUF(inf,addsize) while( ( inf->cur - inf->buf ) + addsize + 1 >= inf->buflen ) { \ int4 len = inf->cur - inf->buf; \ inf->buflen *= 2; \ inf->buf = (char*) repalloc( (void*)inf->buf, inf->buflen ); \ inf->cur = inf->buf + len; \ } static void infix(INFIX *in, bool first) { if ( in->curpol->type == VAL ) { RESIZEBUF(in, 11); sprintf(in->cur, "%d", in->curpol->val ); in->cur = strchr( in->cur, '\0' ); in->curpol--; } else if ( in->curpol->val == (int4)'!' ) { bool isopr = false; RESIZEBUF(in, 1); *(in->cur) = '!'; in->cur++; *(in->cur) = '\0'; in->curpol--; if ( in->curpol->type == OPR ) { isopr = true; RESIZEBUF(in, 2); sprintf(in->cur, "( "); in->cur = strchr( in->cur, '\0' ); } infix( in, isopr ); if ( isopr ) { RESIZEBUF(in, 2); sprintf(in->cur, " )"); in->cur = strchr( in->cur, '\0' ); } } else { int4 op = in->curpol->val; INFIX nrm; in->curpol--; if ( op == (int4)'|' && ! first) { RESIZEBUF(in, 2); sprintf(in->cur, "( "); in->cur = strchr( in->cur, '\0' ); } nrm.curpol = in->curpol; nrm.buflen = 16; nrm.cur = nrm.buf = (char*)palloc( sizeof(char) * nrm.buflen ); /* get right operand */ infix( &nrm, false ); /* get & print left operand */ in->curpol = nrm.curpol; infix( in, false ); /* print operator & right operand*/ RESIZEBUF(in, 3 + (nrm.cur - nrm.buf) ); sprintf(in->cur, " %c %s", op, nrm.buf); in->cur = strchr( in->cur, '\0' ); pfree( nrm.buf ); if ( op == (int4)'|' && ! first) { RESIZEBUF(in, 2); sprintf(in->cur, " )"); in->cur = strchr( in->cur, '\0' ); } } } Datum bqarr_out(PG_FUNCTION_ARGS) { QUERYTYPE *query = (QUERYTYPE*)PG_DETOAST_DATUM(PG_GETARG_POINTER(0)); INFIX nrm; if ( query->size == 0 ) elog(ERROR,"Empty"); nrm.curpol = GETQUERY(query) + query->size - 1; nrm.buflen = 32; nrm.cur = nrm.buf = (char*)palloc( sizeof(char) * nrm.buflen ); *(nrm.cur) = '\0'; infix( &nrm, true ); PG_FREE_IF_COPY(query,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 */ static 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; poscorsize == 0 ) elog(ERROR,"Empty"); 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 = (text*) palloc( 1 + VARHDRSZ ); VARATT_SIZEP(res) = 1 + VARHDRSZ; *((char*)VARDATA(res)) = '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 = (text*) palloc( nrm.cur-nrm.buf + VARHDRSZ ); VARATT_SIZEP(res) = nrm.cur-nrm.buf + VARHDRSZ; strncpy( VARDATA(res), nrm.buf, nrm.cur-nrm.buf ); } pfree(q); PG_RETURN_POINTER( res ); }