aboutsummaryrefslogtreecommitdiff
path: root/contrib/ltree/lquery_op.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ltree/lquery_op.c')
-rw-r--r--contrib/ltree/lquery_op.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
new file mode 100644
index 00000000000..83b380d97ae
--- /dev/null
+++ b/contrib/ltree/lquery_op.c
@@ -0,0 +1,240 @@
+/*
+ * 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)
+ ) );
+}