mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Fix lquery's behavior for consecutive '*' items.
Something like "*{2}.*{3}" should presumably mean the same as "*{5}", but it didn't. Improve that. Get rid of an undocumented and remarkably ugly (though not, as far as I can tell, actually unsafe) static variable in favor of passing more arguments to checkCond(). Reverse-engineer some commentary. This function, like all of ltree, is still far short of what I would consider the minimum acceptable level of internal documentation, but at least now it has more than zero comments. Although this certainly seems like a bug fix, people might not thank us for changing query behavior in stable branches, so no back-patch. Nikita Glukhov, with cosmetic improvements by me Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
This commit is contained in:
@ -98,6 +98,11 @@ ltree_strncasecmp(const char *a, const char *b, size_t s)
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* See if a (non-star) lquery_level matches an ltree_level
|
||||
*
|
||||
* Does not consider level's possible LQL_NOT flag.
|
||||
*/
|
||||
static bool
|
||||
checkLevel(lquery_level *curq, ltree_level *curt)
|
||||
{
|
||||
@ -136,42 +141,49 @@ printFieldNot(FieldNot *fn ) {
|
||||
}
|
||||
*/
|
||||
|
||||
static struct
|
||||
{
|
||||
bool muse;
|
||||
uint32 high_pos;
|
||||
} SomeStack =
|
||||
|
||||
{
|
||||
false, 0,
|
||||
};
|
||||
|
||||
/*
|
||||
* Try to match an lquery (of query_numlevel items) to an ltree (of
|
||||
* tree_numlevel items)
|
||||
*
|
||||
* If the query contains any NOT flags, "ptr" must point to a FieldNot
|
||||
* workspace initialized with ptr->q == NULL. Otherwise it can be NULL.
|
||||
* (LQL_NOT flags will be ignored if ptr == NULL.)
|
||||
*
|
||||
* high_pos is the last ltree position the first lquery item is allowed
|
||||
* to match at; it should be zero for external calls.
|
||||
*
|
||||
* force_advance must be false except in internal recursive calls.
|
||||
*/
|
||||
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,
|
||||
FieldNot *ptr,
|
||||
uint32 high_pos,
|
||||
bool force_advance)
|
||||
{
|
||||
uint32 low_pos = 0,
|
||||
high_pos = 0,
|
||||
cur_tpos = 0;
|
||||
int tlen = tree_numlevel,
|
||||
uint32 low_pos = 0, /* first allowed ltree position for match */
|
||||
cur_tpos = 0; /* ltree position of curt */
|
||||
int tlen = tree_numlevel, /* counts of remaining items */
|
||||
qlen = query_numlevel;
|
||||
int isok;
|
||||
lquery_level *prevq = NULL;
|
||||
ltree_level *prevt = NULL;
|
||||
|
||||
if (SomeStack.muse)
|
||||
/* advance curq (setting up prevq) if requested */
|
||||
if (force_advance)
|
||||
{
|
||||
high_pos = SomeStack.high_pos;
|
||||
qlen--;
|
||||
Assert(qlen > 0);
|
||||
prevq = curq;
|
||||
curq = LQL_NEXT(curq);
|
||||
SomeStack.muse = false;
|
||||
qlen--;
|
||||
}
|
||||
|
||||
while (tlen > 0 && qlen > 0)
|
||||
{
|
||||
if (curq->numvar)
|
||||
{
|
||||
prevt = curt;
|
||||
/* Current query item is not '*' */
|
||||
ltree_level *prevt = curt;
|
||||
|
||||
/* skip tree items that must be ignored due to prior * items */
|
||||
while (cur_tpos < low_pos)
|
||||
{
|
||||
curt = LEVEL_NEXT(curt);
|
||||
@ -183,8 +195,9 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
|
||||
ptr->nt++;
|
||||
}
|
||||
|
||||
if (ptr && curq->flag & LQL_NOT)
|
||||
if (ptr && (curq->flag & LQL_NOT))
|
||||
{
|
||||
/* Deal with a NOT item */
|
||||
if (!(prevq && prevq->numvar == 0))
|
||||
prevq = curq;
|
||||
if (ptr->q == NULL)
|
||||
@ -212,25 +225,30 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
|
||||
}
|
||||
else
|
||||
{
|
||||
isok = false;
|
||||
/* Not a NOT item, check for normal match */
|
||||
bool 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)
|
||||
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))
|
||||
if (checkCond(prevq, qlen + 1,
|
||||
curt, tlen,
|
||||
(ptr) ? &tmpptr : NULL,
|
||||
high_pos - cur_tpos,
|
||||
true))
|
||||
return true;
|
||||
}
|
||||
if (!isok && ptr)
|
||||
if (!isok && ptr && ptr->q)
|
||||
ptr->nt++;
|
||||
}
|
||||
if (!isok)
|
||||
@ -238,7 +256,11 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
|
||||
|
||||
if (ptr && ptr->q)
|
||||
{
|
||||
if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
|
||||
if (checkCond(ptr->q, ptr->nq,
|
||||
ptr->t, ptr->nt,
|
||||
NULL,
|
||||
0,
|
||||
false))
|
||||
return false;
|
||||
ptr->q = NULL;
|
||||
}
|
||||
@ -248,8 +270,14 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
|
||||
}
|
||||
else
|
||||
{
|
||||
low_pos = cur_tpos + curq->low;
|
||||
high_pos = cur_tpos + curq->high;
|
||||
/* Current query item is '*' */
|
||||
low_pos += curq->low;
|
||||
|
||||
if (low_pos > tree_numlevel)
|
||||
return false;
|
||||
|
||||
high_pos = Min(high_pos + curq->high, tree_numlevel);
|
||||
|
||||
if (ptr && ptr->q)
|
||||
{
|
||||
ptr->nq++;
|
||||
@ -263,9 +291,11 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
|
||||
qlen--;
|
||||
}
|
||||
|
||||
/* Fail if we've already run out of ltree items */
|
||||
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
|
||||
return false;
|
||||
|
||||
/* Remaining lquery items must be NOT or '*' items */
|
||||
while (qlen > 0)
|
||||
{
|
||||
if (curq->numvar)
|
||||
@ -275,18 +305,29 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
|
||||
}
|
||||
else
|
||||
{
|
||||
low_pos = cur_tpos + curq->low;
|
||||
high_pos = cur_tpos + curq->high;
|
||||
low_pos += curq->low;
|
||||
|
||||
if (low_pos > tree_numlevel)
|
||||
return false;
|
||||
|
||||
high_pos = Min(high_pos + curq->high, tree_numlevel);
|
||||
}
|
||||
|
||||
curq = LQL_NEXT(curq);
|
||||
qlen--;
|
||||
}
|
||||
|
||||
/* Fail if trailing '*'s require more ltree items than we have */
|
||||
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))
|
||||
/* Finish pending NOT check, if any */
|
||||
if (ptr && ptr->q &&
|
||||
checkCond(ptr->q, ptr->nq,
|
||||
ptr->t, ptr->nt,
|
||||
NULL,
|
||||
0,
|
||||
false))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -306,12 +347,18 @@ ltq_regex(PG_FUNCTION_ARGS)
|
||||
fn.q = NULL;
|
||||
|
||||
res = checkCond(LQUERY_FIRST(query), query->numlevel,
|
||||
LTREE_FIRST(tree), tree->numlevel, &fn);
|
||||
LTREE_FIRST(tree), tree->numlevel,
|
||||
&fn,
|
||||
0,
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = checkCond(LQUERY_FIRST(query), query->numlevel,
|
||||
LTREE_FIRST(tree), tree->numlevel, NULL);
|
||||
LTREE_FIRST(tree), tree->numlevel,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
}
|
||||
|
||||
PG_FREE_IF_COPY(tree, 0);
|
||||
|
Reference in New Issue
Block a user