Hi all,
Here is my attempt to fix a 12-years old ltree bug (which is a todo item).
I see it's not backward-compatible, but in my understanding that's
what is documented. Previous behavior was inconsistent with
documentation (where single asterisk should match zero or more
labels).
http://archives.postgresql.org/pgsql-bugs/2007-11/msg00044.php
diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out
index 8226930905..b1878a99b3 100644
--- a/contrib/ltree/expected/ltree.out
+++ b/contrib/ltree/expected/ltree.out
@@ -676,7 +676,7 @@ SELECT 'a.b.c.d.e'::ltree ~ '*.a.*.d.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.!d.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.!d';
@@ -706,7 +706,7 @@ SELECT 'a.b.c.d.e'::ltree ~ '*.!e';
SELECT 'a.b.c.d.e'::ltree ~ '*.!e.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ 'a.*.!e';
@@ -724,7 +724,7 @@ SELECT 'a.b.c.d.e'::ltree ~ 'a.*.!d';
SELECT 'a.b.c.d.e'::ltree ~ 'a.*.!d.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ 'a.*.!f.*';
@@ -742,7 +742,7 @@ SELECT 'a.b.c.d.e'::ltree ~ '*.a.*.!f.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.a.*.!d.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.a.!d.*';
@@ -766,13 +766,13 @@ SELECT 'a.b.c.d.e'::ltree ~ 'a.!d.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.a.*.!d.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.c.*';
@@ -784,7 +784,7 @@ SELECT 'a.b.c.d.e'::ltree ~ '*.!b.c.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.c.*';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '!b.*.c.*';
@@ -832,31 +832,31 @@ SELECT 'a.b.c.d.e'::ltree ~ '*{1}.!b.*.!c.*.e';
SELECT 'a.b.c.d.e'::ltree ~ '*{1}.!b.*{1}.!c.*.e';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ 'a.!b.*{1}.!c.*.e';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*.e';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*.e';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*.e';
?column?
----------
- f
+ t
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '!b.!c.*';
@@ -886,19 +886,19 @@ SELECT 'a.b.c.d.e'::ltree ~ '*{1}.!b.*.!c.*';
SELECT 'a.b.c.d.e'::ltree ~ '*{1}.!b.*{1}.!c.*';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ 'a.!b.*{1}.!c.*';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*';
?column?
----------
- t
+ f
(1 row)
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*';
@@ -909,6 +909,18 @@ SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*';
?column?
+----------
+ t
+(1 row)
+
+SELECT '5.0.1.0'::ltree ~ '5.!0.!0.0';
+ ?column?
+----------
+ f
+(1 row)
+
+SELECT 'a.b'::ltree ~ '!a.!a';
+ ?column?
----------
f
(1 row)
diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c
index b6d2deb1af..392c193fd8 100644
--- a/contrib/ltree/lquery_op.c
+++ b/contrib/ltree/lquery_op.c
@@ -19,16 +19,6 @@ PG_FUNCTION_INFO_V1(lt_q_rregex);
#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
-typedef struct
-{
- lquery_level *q;
- int nq;
- ltree_level *t;
- int nt;
- int posq;
- int post;
-} FieldNot;
-
static char *
getlexeme(char *start, char *end, int *len)
{
@@ -50,7 +40,7 @@ getlexeme(char *start, char *end, int *len)
}
bool
- compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
+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;
@@ -108,6 +98,9 @@ checkLevel(lquery_level *curq, ltree_level *curt)
int (*cmpptr) (const char *, const char *, size_t);
lquery_variant *curvar = LQL_FIRST(curq);
int i;
+ bool success;
+
+ success = (curq->flag & LQL_NOT) ? false : true;
for (i = 0; i < curq->numvar; i++)
{
@@ -115,8 +108,9 @@ checkLevel(lquery_level *curq, ltree_level *curt)
if (curvar->flag & LVAR_SUBLEXEME)
{
- if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
- return true;
+ if (compare_subnode(curt, curvar->name, curvar->len, cmpptr,
+ (curvar->flag & LVAR_ANYEND)))
+ return success;
}
else if (
(
@@ -126,22 +120,12 @@ checkLevel(lquery_level *curq, ltree_level *curt)
(*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
{
- return true;
+ return success;
}
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++;
- }
+ return !success;
}
-*/
static struct
{
@@ -154,7 +138,7 @@ static struct
};
static bool
-checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr)
+checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel)
{
uint32 low_pos = 0,
high_pos = 0,
@@ -163,7 +147,6 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
qlen = query_numlevel;
int isok;
lquery_level *prevq = NULL;
- ltree_level *prevt = NULL;
if (SomeStack.muse)
{
@@ -178,7 +161,6 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
{
if (curq->numvar)
{
- prevt = curt;
while (cur_tpos < low_pos)
{
curt = LEVEL_NEXT(curt);
@@ -186,83 +168,33 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
cur_tpos++;
if (tlen == 0)
return false;
- if (ptr && ptr->q)
- ptr->nt++;
}
- if (ptr && curq->flag & LQL_NOT)
+ isok = false;
+ while (cur_tpos <= high_pos && tlen > 0 && !isok)
{
- 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;
+ isok = checkLevel(curq, curt);
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 && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
- {
- FieldNot tmpptr;
-
- if (ptr)
- memcpy(&tmpptr, ptr, sizeof(FieldNot));
- SomeStack.high_pos = high_pos - cur_tpos;
- SomeStack.muse = true;
- if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
- return true;
- }
- if (!isok && ptr)
- ptr->nt++;
- }
- if (!isok)
- return false;
-
- if (ptr && ptr->q)
+ if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
{
- if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
- return false;
- ptr->q = NULL;
+ SomeStack.high_pos = high_pos - cur_tpos;
+ SomeStack.muse = true;
+ if (checkCond(prevq, qlen + 1, curt, tlen))
+ return true;
}
- low_pos = cur_tpos;
- high_pos = cur_tpos;
}
+ if (!isok)
+ return false;
+
+ 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;
@@ -293,9 +225,6 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
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;
}
@@ -306,20 +235,8 @@ ltq_regex(PG_FUNCTION_ARGS)
lquery *query = PG_GETARG_LQUERY_P(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);
- }
+ res = checkCond(LQUERY_FIRST(query), query->numlevel,
+ LTREE_FIRST(tree), tree->numlevel);
PG_FREE_IF_COPY(tree, 0);
PG_FREE_IF_COPY(query, 1);
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index e4b8c84fa6..f3c6501309 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -78,8 +78,6 @@ typedef struct
#define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) )
#define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
-#define LQUERY_HASNOT 0x01
-
#define ISALNUM(x) ( t_isalpha(x) || t_isdigit(x) || ( pg_mblen(x) == 1 && t_iseq((x), '_') ) )
/* full text query */
diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c
index f54f037443..2e95e8a1e0 100644
--- a/contrib/ltree/ltree_io.c
+++ b/contrib/ltree/ltree_io.c
@@ -205,7 +205,6 @@ lquery_in(PG_FUNCTION_ARGS)
*curqlevel,
*tmpql;
lquery_variant *lrptr = NULL;
- bool hasnot = false;
bool wasbad = false;
int charlen;
int pos = 0;
@@ -254,7 +253,6 @@ lquery_in(PG_FUNCTION_ARGS)
state = LQPRS_WAITDELIM;
curqlevel->numvar = 1;
curqlevel->flag |= LQL_NOT;
- hasnot = true;
}
else if (charlen == 1 && t_iseq(ptr, '*'))
state = LQPRS_WAITOPEN;
@@ -480,8 +478,6 @@ lquery_in(PG_FUNCTION_ARGS)
result->numlevel = num;
result->firstgood = 0;
result->flag = 0;
- if (hasnot)
- result->flag |= LQUERY_HASNOT;
cur = LQUERY_FIRST(result);
curqlevel = tmpql;
while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql
index 846b04e48e..4f734953e2 100644
--- a/contrib/ltree/sql/ltree.sql
+++ b/contrib/ltree/sql/ltree.sql
@@ -168,7 +168,8 @@ SELECT 'a.b.c.d.e'::ltree ~ 'a.!b.*{1}.!c.*';
SELECT 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*';
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*';
-
+SELECT '5.0.1.0'::ltree ~ '5.!0.!0.0';
+SELECT 'a.b'::ltree ~ '!a.!a';
SELECT 'QWER_TY'::ltree ~ 'q%@*';
SELECT 'QWER_TY'::ltree ~ 'Q_t%@*';
diff --git a/doc/src/sgml/ltree.sgml b/doc/src/sgml/ltree.sgml
index 3ddd335b8c..df8b4b042f 100644
--- a/doc/src/sgml/ltree.sgml
+++ b/doc/src/sgml/ltree.sgml
@@ -598,7 +598,7 @@ ltreetest=> SELECT path FROM test WHERE path ~ '*.Astronomy.*';
Top.Collections.Pictures.Astronomy.Astronauts
(7 rows)
-ltreetest=> SELECT path FROM test WHERE path ~ '*.!pictures@.*.Astronomy.*';
+ltreetest=> SELECT path FROM test WHERE path ~ '*[email protected].*';
path
------------------------------------
Top.Science.Astronomy