mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Fix lquery's NOT handling, and add ability to quantify non-'*' items.
The existing implementation of the ltree ~ lquery match operator is sufficiently complex and undocumented that it's hard to tell exactly what it does. But one thing it clearly gets wrong is the combination of NOT symbols (!) and '*' symbols. A pattern such as '*.!foo.*' should, by any ordinary understanding of regular expression behavior, match any ltree that has at least one label that's not "foo". As best we can tell by experimentation, what it's actually matching is any ltree in which *no* label is "foo". That's surprising, and not at all what the documentation says. Now, that's arguably a useful behavior, so if we rewrite to fix the bug we should provide some other way to get it. To do so, add the ability to attach lquery quantifiers to non-'*' items as well as '*'s. Then the pattern '!foo{,}' expresses "any ltree in which no label is foo". For backwards compatibility, the default quantifier for non-'*' items has to be "{1}", although the default for '*' items is '{,}'. I wouldn't have done it like that in a green field, but it's not totally horrible. Armed with that, rewrite checkCond() from scratch. Treating '*' and non-'*' items alike makes it simpler, not more complicated, so that the function actually gets a lot shorter than it was. Filip Rembiałkowski, Tom Lane, Nikita Glukhov, per a very ancient bug report from M. Palm Discussion: https://postgr.es/m/CAP_rww=waX2Oo6q+MbMSiZ9ktdj6eaJj0cQzNu=Ry2cCDij5fw@mail.gmail.com
This commit is contained in:
@ -65,14 +65,20 @@ typedef struct
|
||||
/*
|
||||
* In an lquery_level, "flag" contains the union of the variants' flags
|
||||
* along with possible LQL_xxx flags; so those bit sets can't overlap.
|
||||
*
|
||||
* "low" and "high" are nominally the minimum and maximum number of matches.
|
||||
* However, for backwards compatibility with pre-v13 on-disk lqueries,
|
||||
* non-'*' levels (those with numvar > 0) only have valid low/high if the
|
||||
* LQL_COUNT flag is set; otherwise those fields are zero, but the behavior
|
||||
* is as if they were both 1.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint16 totallen; /* total length of this level, in bytes */
|
||||
uint16 flag; /* see LQL_xxx and LVAR_xxx flags */
|
||||
uint16 numvar; /* number of variants; 0 means '*' */
|
||||
uint16 low; /* minimum repeat count for '*' */
|
||||
uint16 high; /* maximum repeat count for '*' */
|
||||
uint16 low; /* minimum repeat count */
|
||||
uint16 high; /* maximum repeat count */
|
||||
/* Array of maxalign'd lquery_variant structs follows: */
|
||||
char variants[FLEXIBLE_ARRAY_MEMBER];
|
||||
} lquery_level;
|
||||
@ -82,6 +88,7 @@ typedef struct
|
||||
#define LQL_FIRST(x) ( (lquery_variant*)( ((char*)(x))+LQL_HDRSIZE ) )
|
||||
|
||||
#define LQL_NOT 0x10 /* level has '!' (NOT) prefix */
|
||||
#define LQL_COUNT 0x20 /* level is non-'*' and has repeat counts */
|
||||
|
||||
#ifdef LOWER_NODE
|
||||
#define FLG_CANLOOKSIGN(x) ( ( (x) & ( LQL_NOT | LVAR_ANYEND | LVAR_SUBLEXEME ) ) == 0 )
|
||||
|
Reference in New Issue
Block a user