Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 9950c8a

Browse files
committed
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
1 parent 95f7ddf commit 9950c8a

File tree

4 files changed

+123
-46
lines changed

4 files changed

+123
-46
lines changed

contrib/ltree/expected/ltree.out

+24
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,30 @@ SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*';
959959
f
960960
(1 row)
961961

962+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{2}.*{2}';
963+
?column?
964+
----------
965+
t
966+
(1 row)
967+
968+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{1}.*{2}.e';
969+
?column?
970+
----------
971+
t
972+
(1 row)
973+
974+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{1}.*{4}';
975+
?column?
976+
----------
977+
f
978+
(1 row)
979+
980+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{5}.*';
981+
?column?
982+
----------
983+
f
984+
(1 row)
985+
962986
SELECT 'QWER_TY'::ltree ~ 'q%@*';
963987
?column?
964988
----------

contrib/ltree/lquery_op.c

+84-37
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ ltree_strncasecmp(const char *a, const char *b, size_t s)
9898
return res;
9999
}
100100

101+
/*
102+
* See if a (non-star) lquery_level matches an ltree_level
103+
*
104+
* Does not consider level's possible LQL_NOT flag.
105+
*/
101106
static bool
102107
checkLevel(lquery_level *curq, ltree_level *curt)
103108
{
@@ -136,42 +141,49 @@ printFieldNot(FieldNot *fn ) {
136141
}
137142
*/
138143

139-
static struct
140-
{
141-
bool muse;
142-
uint32 high_pos;
143-
} SomeStack =
144-
145-
{
146-
false, 0,
147-
};
148-
144+
/*
145+
* Try to match an lquery (of query_numlevel items) to an ltree (of
146+
* tree_numlevel items)
147+
*
148+
* If the query contains any NOT flags, "ptr" must point to a FieldNot
149+
* workspace initialized with ptr->q == NULL. Otherwise it can be NULL.
150+
* (LQL_NOT flags will be ignored if ptr == NULL.)
151+
*
152+
* high_pos is the last ltree position the first lquery item is allowed
153+
* to match at; it should be zero for external calls.
154+
*
155+
* force_advance must be false except in internal recursive calls.
156+
*/
149157
static bool
150-
checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr)
158+
checkCond(lquery_level *curq, int query_numlevel,
159+
ltree_level *curt, int tree_numlevel,
160+
FieldNot *ptr,
161+
uint32 high_pos,
162+
bool force_advance)
151163
{
152-
uint32 low_pos = 0,
153-
high_pos = 0,
154-
cur_tpos = 0;
155-
int tlen = tree_numlevel,
164+
uint32 low_pos = 0, /* first allowed ltree position for match */
165+
cur_tpos = 0; /* ltree position of curt */
166+
int tlen = tree_numlevel, /* counts of remaining items */
156167
qlen = query_numlevel;
157-
int isok;
158168
lquery_level *prevq = NULL;
159-
ltree_level *prevt = NULL;
160169

161-
if (SomeStack.muse)
170+
/* advance curq (setting up prevq) if requested */
171+
if (force_advance)
162172
{
163-
high_pos = SomeStack.high_pos;
164-
qlen--;
173+
Assert(qlen > 0);
165174
prevq = curq;
166175
curq = LQL_NEXT(curq);
167-
SomeStack.muse = false;
176+
qlen--;
168177
}
169178

170179
while (tlen > 0 && qlen > 0)
171180
{
172181
if (curq->numvar)
173182
{
174-
prevt = curt;
183+
/* Current query item is not '*' */
184+
ltree_level *prevt = curt;
185+
186+
/* skip tree items that must be ignored due to prior * items */
175187
while (cur_tpos < low_pos)
176188
{
177189
curt = LEVEL_NEXT(curt);
@@ -183,8 +195,9 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
183195
ptr->nt++;
184196
}
185197

186-
if (ptr && curq->flag & LQL_NOT)
198+
if (ptr && (curq->flag & LQL_NOT))
187199
{
200+
/* Deal with a NOT item */
188201
if (!(prevq && prevq->numvar == 0))
189202
prevq = curq;
190203
if (ptr->q == NULL)
@@ -212,33 +225,42 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
212225
}
213226
else
214227
{
215-
isok = false;
228+
/* Not a NOT item, check for normal match */
229+
bool isok = false;
230+
216231
while (cur_tpos <= high_pos && tlen > 0 && !isok)
217232
{
218233
isok = checkLevel(curq, curt);
219234
curt = LEVEL_NEXT(curt);
220235
tlen--;
221236
cur_tpos++;
222-
if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
237+
if (isok && prevq && prevq->numvar == 0 &&
238+
tlen > 0 && cur_tpos <= high_pos)
223239
{
224240
FieldNot tmpptr;
225241

226242
if (ptr)
227243
memcpy(&tmpptr, ptr, sizeof(FieldNot));
228-
SomeStack.high_pos = high_pos - cur_tpos;
229-
SomeStack.muse = true;
230-
if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
244+
if (checkCond(prevq, qlen + 1,
245+
curt, tlen,
246+
(ptr) ? &tmpptr : NULL,
247+
high_pos - cur_tpos,
248+
true))
231249
return true;
232250
}
233-
if (!isok && ptr)
251+
if (!isok && ptr && ptr->q)
234252
ptr->nt++;
235253
}
236254
if (!isok)
237255
return false;
238256

239257
if (ptr && ptr->q)
240258
{
241-
if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
259+
if (checkCond(ptr->q, ptr->nq,
260+
ptr->t, ptr->nt,
261+
NULL,
262+
0,
263+
false))
242264
return false;
243265
ptr->q = NULL;
244266
}
@@ -248,8 +270,14 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
248270
}
249271
else
250272
{
251-
low_pos = cur_tpos + curq->low;
252-
high_pos = cur_tpos + curq->high;
273+
/* Current query item is '*' */
274+
low_pos += curq->low;
275+
276+
if (low_pos > tree_numlevel)
277+
return false;
278+
279+
high_pos = Min(high_pos + curq->high, tree_numlevel);
280+
253281
if (ptr && ptr->q)
254282
{
255283
ptr->nq++;
@@ -263,9 +291,11 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
263291
qlen--;
264292
}
265293

294+
/* Fail if we've already run out of ltree items */
266295
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
267296
return false;
268297

298+
/* Remaining lquery items must be NOT or '*' items */
269299
while (qlen > 0)
270300
{
271301
if (curq->numvar)
@@ -275,18 +305,29 @@ checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_nu
275305
}
276306
else
277307
{
278-
low_pos = cur_tpos + curq->low;
279-
high_pos = cur_tpos + curq->high;
308+
low_pos += curq->low;
309+
310+
if (low_pos > tree_numlevel)
311+
return false;
312+
313+
high_pos = Min(high_pos + curq->high, tree_numlevel);
280314
}
281315

282316
curq = LQL_NEXT(curq);
283317
qlen--;
284318
}
285319

320+
/* Fail if trailing '*'s require more ltree items than we have */
286321
if (low_pos > tree_numlevel || tree_numlevel > high_pos)
287322
return false;
288323

289-
if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
324+
/* Finish pending NOT check, if any */
325+
if (ptr && ptr->q &&
326+
checkCond(ptr->q, ptr->nq,
327+
ptr->t, ptr->nt,
328+
NULL,
329+
0,
330+
false))
290331
return false;
291332

292333
return true;
@@ -306,12 +347,18 @@ ltq_regex(PG_FUNCTION_ARGS)
306347
fn.q = NULL;
307348

308349
res = checkCond(LQUERY_FIRST(query), query->numlevel,
309-
LTREE_FIRST(tree), tree->numlevel, &fn);
350+
LTREE_FIRST(tree), tree->numlevel,
351+
&fn,
352+
0,
353+
false);
310354
}
311355
else
312356
{
313357
res = checkCond(LQUERY_FIRST(query), query->numlevel,
314-
LTREE_FIRST(tree), tree->numlevel, NULL);
358+
LTREE_FIRST(tree), tree->numlevel,
359+
NULL,
360+
0,
361+
false);
315362
}
316363

317364
PG_FREE_IF_COPY(tree, 0);

contrib/ltree/ltree.h

+11-8
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ typedef struct
3434
{
3535
int32 val;
3636
uint16 len;
37-
uint8 flag;
37+
uint8 flag; /* see LVAR_xxx flags below */
3838
char name[FLEXIBLE_ARRAY_MEMBER];
3939
} lquery_variant;
4040

@@ -47,11 +47,12 @@ typedef struct
4747

4848
typedef struct
4949
{
50-
uint16 totallen;
51-
uint16 flag;
52-
uint16 numvar;
53-
uint16 low;
54-
uint16 high;
50+
uint16 totallen; /* total length of this level, in bytes */
51+
uint16 flag; /* see LQL_xxx flags below */
52+
uint16 numvar; /* number of variants; 0 means '*' */
53+
uint16 low; /* minimum repeat count for '*' */
54+
uint16 high; /* maximum repeat count for '*' */
55+
/* Array of maxalign'd lquery_variant structs follows: */
5556
char variants[FLEXIBLE_ARRAY_MEMBER];
5657
} lquery_level;
5758

@@ -60,6 +61,7 @@ typedef struct
6061
#define LQL_FIRST(x) ( (lquery_variant*)( ((char*)(x))+LQL_HDRSIZE ) )
6162

6263
#define LQL_NOT 0x10
64+
6365
#ifdef LOWER_NODE
6466
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEME ) ) == 0 )
6567
#else
@@ -70,9 +72,10 @@ typedef struct
7072
typedef struct
7173
{
7274
int32 vl_len_; /* varlena header (do not touch directly!) */
73-
uint16 numlevel;
75+
uint16 numlevel; /* number of lquery_levels */
7476
uint16 firstgood;
75-
uint16 flag;
77+
uint16 flag; /* see LQUERY_xxx flags below */
78+
/* Array of maxalign'd lquery_level structs follows: */
7679
char data[FLEXIBLE_ARRAY_MEMBER];
7780
} lquery;
7881

contrib/ltree/sql/ltree.sql

+4-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ SELECT 'a.b.c.d.e'::ltree ~ 'a.!b.*{1}.!c.*';
179179
SELECT 'a.b.c.d.e'::ltree ~ '!b.*{1}.!c.*';
180180
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*{1}.!c.*';
181181
SELECT 'a.b.c.d.e'::ltree ~ '*.!b.*.!c.*';
182-
182+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{2}.*{2}';
183+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{1}.*{2}.e';
184+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{1}.*{4}';
185+
SELECT 'a.b.c.d.e'::ltree ~ 'a.*{5}.*';
183186

184187
SELECT 'QWER_TY'::ltree ~ 'q%@*';
185188
SELECT 'QWER_TY'::ltree ~ 'Q_t%@*';

0 commit comments

Comments
 (0)