92288a1cf9
o Change all current CVS messages of NOTICE to WARNING. We were going to do this just before 7.3 beta but it has to be done now, as you will see below. o Change current INFO messages that should be controlled by client_min_messages to NOTICE. o Force remaining INFO messages, like from EXPLAIN, VACUUM VERBOSE, etc. to always go to the client. o Remove INFO from the client_min_messages options and add NOTICE. Seems we do need three non-ERROR elog levels to handle the various behaviors we need for these messages. Regression passed.
2199 lines
47 KiB
C
2199 lines
47 KiB
C
/******************************************************************************
|
|
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 <float.h>
|
|
|
|
#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<SIGLEN;i++) {\
|
|
a;\
|
|
}
|
|
|
|
#define LOOPBIT(a) \
|
|
for(i=0;i<SIGLENBIT;i++) {\
|
|
a;\
|
|
}
|
|
|
|
/* beware of multiple evaluation of arguments to these macros! */
|
|
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
|
|
#define GETBITBYTE(x,i) ( (*((char*)(x)) >> (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(DEBUG3, "\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(DEBUG3, "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(DEBUG3, "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(DEBUG3, "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(DEBUG3, "_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(DEBUG3, "_int_common_union out1");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
#ifdef GIST_DEBUG
|
|
elog(DEBUG3, "_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(DEBUG3, "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(DEBUG3, "--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(DEBUG3, "--------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(DEBUG3, "--------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(DEBUG3, ( 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;i<query->size;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(DEBUG3,"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; 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;
|
|
}
|
|
|
|
|
|
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 )
|
|
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 );
|
|
}
|