Commit 1dedbf2d authored by Bruce Momjian's avatar Bruce Momjian

Add ltree data type to contrib, from Teodor Sigaev and Oleg Bartunov.

parent 210e64fe
# $Header: /cvsroot/pgsql/contrib/Makefile,v 1.33 2002/07/30 16:32:20 momjian Exp $
# $Header: /cvsroot/pgsql/contrib/Makefile,v 1.34 2002/07/30 16:40:34 momjian Exp $
subdir = contrib
top_builddir = ..
......@@ -20,6 +20,7 @@ WANTED_DIRS = \
intarray \
isbn_issn \
lo \
ltree \
miscutil \
noupdate \
oid2name \
......
......@@ -95,6 +95,10 @@ lo -
Large Object maintenance
by Peter Mount <peter@retep.org.uk>
ltree -
Tree-like data structures
by Teodor Sigaev <teodor@stack.net> and Oleg Bartunov <oleg@sai.msu.su>
mSQL-interface -
mSQL API translation library
by Aldrin Leal <aldrin@americasnet.com>
......
subdir = contrib/tree
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
PG_CPPFLAGS = -DLOWER_NODE
MODULE_big = ltree
OBJS = ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \
ltxtquery_io.o ltxtquery_op.o ltree_gist.o _ltree_gist.o
DATA_built = ltree.sql
DOCS = README.ltree
REGRESS = ltree
include $(top_srcdir)/contrib/contrib-global.mk
This diff is collapsed.
This diff is collapsed.
/*
* op function for ltree[]
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
#include "utils/array.h"
PG_FUNCTION_INFO_V1(_ltree_isparent);
PG_FUNCTION_INFO_V1(_ltree_r_isparent);
PG_FUNCTION_INFO_V1(_ltree_risparent);
PG_FUNCTION_INFO_V1(_ltree_r_risparent);
PG_FUNCTION_INFO_V1(_ltq_regex);
PG_FUNCTION_INFO_V1(_ltq_rregex);
PG_FUNCTION_INFO_V1(_ltxtq_exec);
PG_FUNCTION_INFO_V1(_ltxtq_rexec);
Datum _ltree_r_isparent(PG_FUNCTION_ARGS);
Datum _ltree_r_risparent(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(_ltree_extract_isparent);
PG_FUNCTION_INFO_V1(_ltree_extract_risparent);
PG_FUNCTION_INFO_V1(_ltq_extract_regex);
PG_FUNCTION_INFO_V1(_ltxtq_extract_exec);
Datum _ltree_extract_isparent(PG_FUNCTION_ARGS);
Datum _ltree_extract_risparent(PG_FUNCTION_ARGS);
Datum _ltq_extract_regex(PG_FUNCTION_ARGS);
Datum _ltxtq_extract_exec(PG_FUNCTION_ARGS);
typedef Datum (*PGCALL2)(PG_FUNCTION_ARGS);
#define NEXTVAL(x) ( (ltree*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
static bool
array_iterator( ArrayType *la, PGCALL2 callback, void* param, ltree ** found) {
int num=ArrayGetNItems( ARR_NDIM(la), ARR_DIMS(la));
ltree *item = (ltree*)ARR_DATA_PTR(la);
if ( ARR_NDIM(la) !=1 )
elog(ERROR,"Dimension of array != 1");
if ( found )
*found=NULL;
while( num>0 ) {
if ( DatumGetBool( DirectFunctionCall2( callback,
PointerGetDatum(item), PointerGetDatum(param) ) ) ) {
if ( found )
*found = item;
return true;
}
num--;
item = NEXTVAL(item);
}
return false;
}
Datum
_ltree_isparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
bool res = array_iterator( la, ltree_isparent, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltree_r_isparent(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltree_isparent,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltree_risparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
bool res = array_iterator( la, ltree_risparent, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltree_r_risparent(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltree_risparent,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltq_regex(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
lquery *query = PG_GETARG_LQUERY(1);
bool res = array_iterator( la, ltq_regex, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltq_rregex(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltq_regex,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltxtq_exec(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
bool res = array_iterator( la, ltxtq_exec, (void*)query, NULL );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
_ltxtq_rexec(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( _ltxtq_exec,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
Datum
_ltree_extract_isparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
ltree *found,*item;
if ( !array_iterator( la, ltree_isparent, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
Datum
_ltree_extract_risparent(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltree *query = PG_GETARG_LTREE(1);
ltree *found,*item;
if ( !array_iterator( la, ltree_risparent, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
Datum
_ltq_extract_regex(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
lquery *query = PG_GETARG_LQUERY(1);
ltree *found,*item;
if ( !array_iterator( la, ltq_regex, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
Datum
_ltxtq_extract_exec(PG_FUNCTION_ARGS) {
ArrayType *la = (ArrayType *)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
ltree *found,*item;
if ( !array_iterator( la, ltxtq_exec, (void*)query, &found ) ) {
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_NULL();
}
item = (ltree*)palloc( found->len );
memcpy( item, found, found->len );
PG_FREE_IF_COPY(la,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_POINTER(item);
}
/* Both POSIX and CRC32 checksums */
#include <sys/types.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef LOWER_NODE
#include <ctype.h>
#define TOLOWER(x) tolower(x)
#else
#define TOLOWER(x) (x)
#endif
#include "crc32.h"
/*
* This code implements the AUTODIN II polynomial
* The variable corresponding to the macro argument "crc" should
* be an unsigned long.
* Oroginal code by Spencer Garrett <srg@quick.com>
*/
#define _CRC32_(crc, ch) (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff])
/* generated using the AUTODIN II polynomial
* x^32 + x^26 + x^23 + x^22 + x^16 +
* x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
*/
static const unsigned int crc32tab[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
unsigned int
crc32_sz(char *buf, int size)
{
unsigned int crc = ~0;
char *p;
int len,
nr;
len = 0;
nr = size;
for (len += nr, p = buf; nr--; ++p)
_CRC32_(crc, TOLOWER(*p));
return ~crc;
}
#ifndef _CRC32_H
#define _CRC32_H
/* Returns crc32 of data block */
extern unsigned int crc32_sz(char *buf, int size);
/* Returns crc32 of null-terminated string */
#define crc32(buf) crc32_sz((buf),strlen(buf))
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* op function for ltree and lquery
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
PG_FUNCTION_INFO_V1(ltq_regex);
PG_FUNCTION_INFO_V1(ltq_rregex);
typedef struct {
lquery_level *q;
int nq;
ltree_level *t;
int nt;
int posq;
int post;
} FieldNot;
static char *
getlexem(char *start, char *end, int *len) {
char *ptr;
while( start<end && *start == '_' )
start++;
ptr = start;
if ( ptr == end )
return NULL;
while( ptr < end && *ptr != '_')
ptr++;
*len = ptr - start;
return start;
}
bool
compare_subnode( ltree_level *t, char *qn, int len, int (*cmpptr)(const char *,const char *,size_t), bool anyend ) {
char *endt = t->name + t->len;
char *endq = qn + len;
char *tn;
int lent,lenq;
bool isok;
while( (qn=getlexem(qn,endq,&lenq)) != NULL ) {
tn=t->name;
isok = false;
while( (tn=getlexem(tn,endt,&lent)) != NULL ) {
if (
(
lent == lenq ||
( lent > lenq && anyend )
) &&
(*cmpptr)(qn,tn,lenq) == 0 ) {
isok = true;
break;
}
tn += lent;
}
if ( !isok )
return false;
qn += lenq;
}
return true;
}
static bool
checkLevel( lquery_level *curq, ltree_level *curt ) {
int (*cmpptr)(const char *,const char *,size_t);
lquery_variant *curvar = LQL_FIRST(curq);
int i;
for(i=0;i<curq->numvar;i++) {
cmpptr = ( curvar->flag & LVAR_INCASE ) ? strncasecmp : strncmp;
if ( curvar->flag & LVAR_SUBLEXEM ) {
if ( compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND) ) )
return true;
} else if (
(
curvar->len == curt->len ||
( curt->len > curvar->len && (curvar->flag & LVAR_ANYEND) )
) &&
(*cmpptr)( curvar->name, curt->name, curvar->len) == 0 ) {
return true;
}
curvar = LVAR_NEXT(curvar);
}
return false;
}
/*
void
printFieldNot(FieldNot *fn ) {
while(fn->q) {
elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
fn++;
}
}
*/
static bool
checkCond( lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr ) {
uint32 low_pos=0,high_pos=0,cur_tpos=0;
int tlen = tree_numlevel, qlen = query_numlevel;
int isok;
lquery_level *prevq=NULL;
ltree_level *prevt=NULL;
while( tlen >0 && qlen>0 ) {
if ( curq->numvar ) {
prevt = curt;
while ( cur_tpos < low_pos ) {
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if ( tlen==0 )
return false;
if ( ptr && ptr->q )
ptr->nt++;
}
if ( ptr && curq->flag & LQL_NOT ) {
if ( !(prevq && prevq->numvar == 0) )
prevq = curq;
if ( ptr->q == NULL ) {
ptr->t = prevt;
ptr->q = prevq;
ptr->nt=1;
ptr->nq=1 + ( (prevq==curq) ? 0 : 1 );
ptr->posq = query_numlevel - qlen - ( (prevq==curq) ? 0 : 1 );
ptr->post = cur_tpos;
} else {
ptr->nt++;
ptr->nq++;
}
if ( qlen == 1 && ptr->q->numvar==0 )
ptr->nt = tree_numlevel - ptr->post;
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if ( high_pos < cur_tpos )
high_pos++;
} else {
isok = false;
while( cur_tpos <= high_pos && tlen > 0 && !isok) {
isok = checkLevel(curq, curt);
curt = LEVEL_NEXT(curt);
tlen--;
cur_tpos++;
if ( !isok && ptr )
ptr->nt++;
}
if ( !isok )
return false;
if (ptr && ptr->q) {
if ( checkCond(ptr->q,ptr->nq,ptr->t,ptr->nt,NULL) )
return false;
ptr->q = NULL;
}
low_pos=cur_tpos; high_pos=cur_tpos;
}
} else {
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
if ( ptr && ptr->q ) {
ptr->nq++;
if ( qlen==1 )
ptr->nt = tree_numlevel - ptr->post;
}
}
prevq = curq;
curq = LQL_NEXT(curq);
qlen--;
}
if ( low_pos > tree_numlevel || tree_numlevel > high_pos )
return false;
while( qlen>0 ) {
if ( curq->numvar ) {
if ( ! (curq->flag & LQL_NOT) )
return false;
} else {
low_pos = cur_tpos + curq->low;
high_pos = cur_tpos + curq->high;
}
curq = LQL_NEXT(curq);
qlen--;
}
if ( low_pos > tree_numlevel || tree_numlevel > high_pos )
return false;
if ( ptr && ptr->q && checkCond(ptr->q,ptr->nq,ptr->t,ptr->nt,NULL) )
return false;
return true;
}
Datum
ltq_regex(PG_FUNCTION_ARGS) {
ltree *tree = PG_GETARG_LTREE(0);
lquery *query = PG_GETARG_LQUERY(1);
bool res= false;
if ( query->flag & LQUERY_HASNOT ) {
FieldNot fn;
fn.q=NULL;
res = checkCond( LQUERY_FIRST(query), query->numlevel,
LTREE_FIRST(tree), tree->numlevel, &fn );
} else {
res = checkCond( LQUERY_FIRST(query), query->numlevel,
LTREE_FIRST(tree), tree->numlevel, NULL );
}
PG_FREE_IF_COPY(tree,0);
PG_FREE_IF_COPY(query,1);
PG_RETURN_BOOL(res);
}
Datum
ltq_rregex(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( ltq_regex,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
#ifndef __LTREE_H__
#define __LTREE_H__
#include "postgres.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "utils/builtins.h"
typedef struct {
uint8 len;
char name[1];
} ltree_level;
#define LEVEL_HDRSIZE (sizeof(uint8))
#define LEVEL_NEXT(x) ( (ltree_level*)( ((char*)(x)) + ((ltree_level*)(x))->len + LEVEL_HDRSIZE ) )
typedef struct {
int32 len;
uint16 numlevel;
char data[1];
} ltree;
#define LTREE_HDRSIZE ( sizeof(int32) + sizeof(uint16) )
#define LTREE_FIRST(x) ( (ltree_level*)( ((ltree*)(x))->data ) )
/* lquery */
typedef struct {
int4 val;
uint8 len;
uint8 flag;
char name[1];
} lquery_variant;
#define LVAR_HDRSIZE (sizeof(uint8)*2 + sizeof(int4))
#define LVAR_NEXT(x) ( (lquery_variant*)( ((char*)(x)) + ((lquery_variant*)(x))->len + LVAR_HDRSIZE ) )
#define LVAR_ANYEND 0x01
#define LVAR_INCASE 0x02
#define LVAR_SUBLEXEM 0x04
typedef struct {
uint16 totallen;
uint16 flag;
uint16 numvar;
uint16 low;
uint16 high;
char variants[1];
} lquery_level;
#define LQL_HDRSIZE ( sizeof(uint16)*5 )
#define LQL_NEXT(x) ( (lquery_level*)( ((char*)(x)) + ((lquery_level*)(x))->totallen ) )
#define LQL_FIRST(x) ( (lquery_variant*)( ((lquery_level*)(x))->variants ) )
#define LQL_NOT 0x10
#ifdef LOWER_NODE
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEM ) ) == 0 )
#else
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEM | LVAR_INCASE ) ) == 0 )
#endif
#define LQL_CANLOOKSIGN(x) FLG_CANLOOKSIGN( ((lquery_level*)(x))->flag )
typedef struct {
int32 len;
uint16 numlevel;
uint16 firstgood;
uint16 flag;
char data[1];
} lquery;
#define LQUERY_HDRSIZE ( sizeof(int32) + 3*sizeof(uint16) )
#define LQUERY_FIRST(x) ( (lquery_level*)( ((lquery*)(x))->data ) )
#define LQUERY_HASNOT 0x01
#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef min
#define min(a,b) ((a) <= (b) ? (a) : (b))
#endif
#ifndef abs
#define abs(a) ((a) < (0) ? -(a) : (a))
#endif
#define ISALNUM(x) ( isalnum(x) || (x) == '_' )
/* full text query */
/*
* item in polish notation with back link
* to left operand
*/
typedef struct ITEM
{
int2 type;
int2 left;
int4 val;
uint8 flag;
/* user-friendly value */
uint8 length;
uint16 distance;
} ITEM;
/*
*Storage:
* (len)(size)(array of ITEM)(array of operand in user-friendly form)
*/
typedef struct
{
int4 len;
int4 size;
char data[1];
} ltxtquery;
#define HDRSIZEQT ( 2*sizeof(int4) )
#define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + size * sizeof(ITEM) + lenofoperand )
#define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT )
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) )
#define ISOPERATOR(x) ( (x)=='!' || (x)=='&' || (x)=='|' || (x)=='(' || (x)==')' )
#define END 0
#define ERR 1
#define VAL 2
#define OPR 3
#define OPEN 4
#define CLOSE 5
#define VALTRUE 6 /* for stop words */
#define VALFALSE 7
/* use in array iterator */
Datum ltree_isparent(PG_FUNCTION_ARGS);
Datum ltree_risparent(PG_FUNCTION_ARGS);
Datum ltq_regex(PG_FUNCTION_ARGS);
Datum ltq_rregex(PG_FUNCTION_ARGS);
Datum ltxtq_exec(PG_FUNCTION_ARGS);
Datum ltxtq_rexec(PG_FUNCTION_ARGS);
Datum _ltq_regex(PG_FUNCTION_ARGS);
Datum _ltq_rregex(PG_FUNCTION_ARGS);
Datum _ltxtq_exec(PG_FUNCTION_ARGS);
Datum _ltxtq_rexec(PG_FUNCTION_ARGS);
Datum _ltree_isparent(PG_FUNCTION_ARGS);
Datum _ltree_risparent(PG_FUNCTION_ARGS);
/* Concatenation functions */
Datum ltree_addltree(PG_FUNCTION_ARGS);
Datum ltree_addtext(PG_FUNCTION_ARGS);
Datum ltree_textadd(PG_FUNCTION_ARGS);
/* Util function */
Datum ltree_in(PG_FUNCTION_ARGS);
bool execute(ITEM * curitem, void *checkval,
bool calcnot, bool (*chkcond) (void *checkval, ITEM * val));
int ltree_compare(const ltree *a, const ltree *b);
bool inner_isparent(const ltree *c, const ltree *p);
bool compare_subnode( ltree_level *t, char *q, int len,
int (*cmpptr)(const char *,const char *,size_t), bool anyend );
#define PG_GETARG_LTREE(x) ((ltree*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
#define PG_GETARG_LQUERY(x) ((lquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
#define PG_GETARG_LTXTQUERY(x) ((ltxtquery*)DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(x))))
/* GiST support for ltree */
#define BITBYTE 8
#define SIGLENINT 8
#define SIGLEN ( sizeof(int4)*SIGLENINT )
#define SIGLENBIT (SIGLEN*BITBYTE)
typedef unsigned char BITVEC[SIGLEN];
typedef unsigned char *BITVECP;
#define LOOPBYTE(a) \
for(i=0;i<SIGLEN;i++) {\
a;\
}
#define LOOPBIT(a) \
for(i=0;i<SIGLENBIT;i++) {\
a;\
}
#define GETBYTE(x,i) ( *( (BITVECP)(x) + (int)( (i) / BITBYTE ) ) )
#define GETBITBYTE(x,i) ( ((unsigned 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))
/*
* type of index key for ltree. Tree are combined B-Tree and R-Tree
* Storage:
* Leaf pages
* (len)(flag)(ltree)
* Non-Leaf
* (len)(flag)(sign)(left_ltree)(right_ltree)
* ALLTRUE: (len)(flag)(left_ltree)(right_ltree)
*
*/
typedef struct {
int4 len;
uint32 flag;
char data[1];
} ltree_gist;
#define LTG_ONENODE 0x01
#define LTG_ALLTRUE 0x02
#define LTG_NORIGHT 0x04
#define LTG_HDRSIZE ( sizeof(int4) + sizeof(uint32) )
#define LTG_SIGN(x) ( (BITVECP)( ((ltree_gist*)(x))->data ) )
#define LTG_NODE(x) ( (ltree*)( ((ltree_gist*)(x))->data ) )
#define LTG_ISONENODE(x) ( ((ltree_gist*)(x))->flag & LTG_ONENODE )
#define LTG_ISALLTRUE(x) ( ((ltree_gist*)(x))->flag & LTG_ALLTRUE )
#define LTG_ISNORIGHT(x) ( ((ltree_gist*)(x))->flag & LTG_NORIGHT )
#define LTG_LNODE(x) ( (ltree*)( ( (char*)( ((ltree_gist*)(x))->data ) ) + ( LTG_ISALLTRUE(x) ? 0 : SIGLEN ) ) )
#define LTG_RENODE(x) ( (ltree*)( ((char*)LTG_LNODE(x)) + LTG_LNODE(x)->len ) )
#define LTG_RNODE(x) ( LTG_ISNORIGHT(x) ? LTG_LNODE(x) : LTG_RENODE(x) )
#define LTG_GETLNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_LNODE(x) )
#define LTG_GETRNODE(x) ( LTG_ISONENODE(x) ? LTG_NODE(x) : LTG_RNODE(x) )
/* GiST support for ltree[] */
#define ASIGLENINT (2*SIGLENINT)
#define ASIGLEN (sizeof(int4)*ASIGLENINT)
#define ASIGLENBIT (ASIGLEN*BITBYTE)
typedef unsigned char ABITVEC[ASIGLEN];
#define ALOOPBYTE(a) \
for(i=0;i<ASIGLEN;i++) {\
a;\
}
#define ALOOPBIT(a) \
for(i=0;i<ASIGLENBIT;i++) {\
a;\
}
#define AHASHVAL(val) (((unsigned int)(val)) % ASIGLENBIT)
#define AHASH(sign, val) SETBIT((sign), AHASHVAL(val))
/* type of key is the same to ltree_gist */
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
create table test ( path ltree);
insert into test values ('Top');
insert into test values ('Top.Science');
insert into test values ('Top.Science.Astronomy');
insert into test values ('Top.Science.Astronomy.Astrophysics');
insert into test values ('Top.Science.Astronomy.Cosmology');
insert into test values ('Top.Hobbies');
insert into test values ('Top.Hobbies.Amateurs_Astronomy');
insert into test values ('Top.Collections');
insert into test values ('Top.Collections.Pictures');
insert into test values ('Top.Collections.Pictures.Astronomy');
insert into test values ('Top.Collections.Pictures.Astronomy.Stars');
insert into test values ('Top.Collections.Pictures.Astronomy.Galaxies');
insert into test values ('Top.Collections.Pictures.Astronomy.Astronauts');
create index path_gist_idx on test using gist(path);
create index path_idx on test using btree(path);
This diff is collapsed.
/*
* txtquery operations with ltree
* Teodor Sigaev <teodor@stack.net>
*/
#include "ltree.h"
#include <ctype.h>
PG_FUNCTION_INFO_V1(ltxtq_exec);
PG_FUNCTION_INFO_V1(ltxtq_rexec);
/*
* check for boolean condition
*/
bool
execute(ITEM * curitem, void *checkval, bool calcnot, bool (*chkcond) (void *checkval, ITEM * val)) {
if (curitem->type == VAL)
return (*chkcond) (checkval, curitem);
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;
}
typedef struct {
ltree *node;
char *operand;
} CHKVAL;
static bool
checkcondition_str(void* checkval, ITEM * val) {
ltree_level *level = LTREE_FIRST( ((CHKVAL*)checkval)->node );
int tlen = ((CHKVAL*)checkval)->node->numlevel;
char *op = ((CHKVAL*)checkval)->operand + val->distance;
int (*cmpptr)(const char *,const char *,size_t);
cmpptr = ( val->flag & LVAR_INCASE ) ? strncasecmp : strncmp;
while( tlen > 0 ) {
if ( val->flag & LVAR_SUBLEXEM ) {
if ( compare_subnode(level, op, val->length, cmpptr, (val->flag & LVAR_ANYEND) ) )
return true;
} else if (
(
val->length == level->len ||
( level->len > val->length && (val->flag & LVAR_ANYEND) )
) &&
(*cmpptr)( op, level->name, val->length) == 0 )
return true;
tlen--;
level = LEVEL_NEXT(level);
}
return false;
}
Datum
ltxtq_exec(PG_FUNCTION_ARGS) {
ltree *val = PG_GETARG_LTREE(0);
ltxtquery *query = PG_GETARG_LTXTQUERY(1);
CHKVAL chkval;
bool result;
chkval.node = val;
chkval.operand = GETOPERAND(query);
result = execute(
GETQUERY(query),
&chkval,
true,
checkcondition_str
);
PG_FREE_IF_COPY(val, 0);
PG_FREE_IF_COPY(query, 1);
PG_RETURN_BOOL(result);
}
Datum
ltxtq_rexec(PG_FUNCTION_ARGS) {
PG_RETURN_DATUM( DirectFunctionCall2( ltxtq_exec,
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(0)
) );
}
*** ltree.sql.in Tue Jul 23 18:49:12 2002
--- ltree.sql.in.72 Wed Jul 17 18:59:08 2002
***************
*** 177,188 ****
-- B-tree support
! INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opcdefault, opckeytype)
VALUES (
(SELECT oid FROM pg_am WHERE amname = 'btree'),
'ltree_ops',
- (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
- 1, -- UID of superuser is hardwired to 1 as of PG 7.3
(SELECT oid FROM pg_type WHERE typname = 'ltree'),
true,
0);
--- 177,186 ----
-- B-tree support
! INSERT INTO pg_opclass (opcamid, opcname, opcintype, opcdefault, opckeytype)
VALUES (
(SELECT oid FROM pg_am WHERE amname = 'btree'),
'ltree_ops',
(SELECT oid FROM pg_type WHERE typname = 'ltree'),
true,
0);
***************
*** 376,386 ****
create function ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist_ltree_ops',
! (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
! 1, -- UID of superuser is hardwired to 1 as of PG 7.3
! pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = 'ltree' and
pg_am.amname='gist' and
--- 374,381 ----
create function ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist_ltree_ops', pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = 'ltree' and
pg_am.amname='gist' and
***************
*** 720,730 ****
create function _ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function _ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcnamespace, opcowner, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist__ltree_ops',
! (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'),
! 1, -- UID of superuser is hardwired to 1 as of PG 7.3
! pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = '_ltree' and
pg_am.amname='gist' and
--- 715,722 ----
create function _ltree_union(bytea, opaque) returns int4 as 'MODULE_PATHNAME' language 'C';
create function _ltree_same(opaque, opaque, opaque) returns opaque as 'MODULE_PATHNAME' language 'C';
! INSERT INTO pg_opclass (opcamid, opcname, opcintype, opckeytype, opcdefault)
! SELECT pg_am.oid, 'gist__ltree_ops', pg_type.oid, pg_key.oid, true
FROM pg_type, pg_am, pg_type pg_key
WHERE pg_type.typname = '_ltree' and
pg_am.amname='gist' and
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment