postgresql/contrib/ltree/lquery_op.c

241 lines
4.8 KiB
C

/*
* 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)
) );
}