PostgreSQL Source Code git master
Loading...
Searching...
No Matches
lquery_op.c
Go to the documentation of this file.
1/*
2 * op function for ltree and lquery
3 * Teodor Sigaev <teodor@stack.net>
4 * contrib/ltree/lquery_op.c
5 */
6#include "postgres.h"
7
8#include <ctype.h>
9
11#include "ltree.h"
12#include "miscadmin.h"
13#include "utils/array.h"
14#include "utils/formatting.h"
15
18
21
22#define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
23
24static char *
25getlexeme(char *start, char *end, int *len)
26{
27 char *ptr;
28
29 while (start < end && t_iseq(start, '_'))
30 start += pg_mblen_range(start, end);
31
32 ptr = start;
33 if (ptr >= end)
34 return NULL;
35
36 while (ptr < end && !t_iseq(ptr, '_'))
37 ptr += pg_mblen_range(ptr, end);
38
39 *len = ptr - start;
40 return start;
41}
42
43bool
44compare_subnode(ltree_level *t, char *qn, int len, bool prefix, bool ci)
45{
46 char *endt = t->name + t->len;
47 char *endq = qn + len;
48 char *tn;
49 int lent,
50 lenq;
51 bool isok;
52
53 while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
54 {
55 tn = t->name;
56 isok = false;
57 while ((tn = getlexeme(tn, endt, &lent)) != NULL)
58 {
59 if (ltree_label_match(qn, lenq, tn, lent, prefix, ci))
60 {
61 isok = true;
62 break;
63 }
64 tn += lent;
65 }
66
67 if (!isok)
68 return false;
69 qn += lenq;
70 }
71
72 return true;
73}
74
75/*
76 * Check if the label matches the predicate string. If 'prefix' is true, then
77 * the predicate string is treated as a prefix. If 'ci' is true, then the
78 * predicate string is case-insensitive (and locale-aware).
79 */
80bool
81ltree_label_match(const char *pred, size_t pred_len, const char *label,
82 size_t label_len, bool prefix, bool ci)
83{
84 static pg_locale_t locale = NULL;
85 char *fpred; /* casefolded predicate */
86 size_t fpred_len = pred_len;
87 char *flabel; /* casefolded label */
88 size_t flabel_len = label_len;
89 size_t len;
90 bool res;
91
92 /* fast path for binary match or binary prefix match */
93 if ((pred_len == label_len || (prefix && pred_len < label_len)) &&
94 strncmp(pred, label, pred_len) == 0)
95 return true;
96 else if (!ci)
97 return false;
98
99 /*
100 * Slow path for case-insensitive comparison: case fold and then compare.
101 * This path is necessary even if pred_len > label_len, because the byte
102 * lengths may change after casefolding.
103 */
104 if (!locale)
105 locale = pg_database_locale();
106
107 fpred = palloc(fpred_len + 1);
108 len = pg_strfold(fpred, fpred_len + 1, pred, pred_len, locale);
109 if (len > fpred_len)
110 {
111 /* grow buffer if needed and retry */
112 fpred_len = len;
114 len = pg_strfold(fpred, fpred_len + 1, pred, pred_len, locale);
115 }
116 Assert(len <= fpred_len);
117 fpred_len = len;
118
119 flabel = palloc(flabel_len + 1);
120 len = pg_strfold(flabel, flabel_len + 1, label, label_len, locale);
121 if (len > flabel_len)
122 {
123 /* grow buffer if needed and retry */
124 flabel_len = len;
126 len = pg_strfold(flabel, flabel_len + 1, label, label_len, locale);
127 }
129 flabel_len = len;
130
131 if ((fpred_len == flabel_len || (prefix && fpred_len < flabel_len)) &&
133 res = true;
134 else
135 res = false;
136
137 pfree(fpred);
138 pfree(flabel);
139
140 return res;
141}
142
143/*
144 * See if an lquery_level matches an ltree_level
145 *
146 * This accounts for all flags including LQL_NOT, but does not
147 * consider repetition counts.
148 */
149static bool
151{
152 lquery_variant *curvar = LQL_FIRST(curq);
153 bool success;
154
155 success = (curq->flag & LQL_NOT) ? false : true;
156
157 /* numvar == 0 means '*' which matches anything */
158 if (curq->numvar == 0)
159 return success;
160
161 for (int i = 0; i < curq->numvar; i++)
162 {
163 bool prefix = (curvar->flag & LVAR_ANYEND);
164 bool ci = (curvar->flag & LVAR_INCASE);
165
166 if (curvar->flag & LVAR_SUBLEXEME)
167 {
168 if (compare_subnode(curt, curvar->name, curvar->len, prefix, ci))
169 return success;
170 }
171 else if (ltree_label_match(curvar->name, curvar->len, curt->name,
172 curt->len, prefix, ci))
173 return success;
174
175 curvar = LVAR_NEXT(curvar);
176 }
177 return !success;
178}
179
180/*
181 * Try to match an lquery (of qlen items) to an ltree (of tlen items)
182 */
183static bool
185 ltree_level *curt, int tlen)
186{
187 /* Since this function recurses, it could be driven to stack overflow */
189
190 /* Pathological patterns could take awhile, too */
192
193 /* Loop while we have query items to consider */
194 while (qlen > 0)
195 {
196 int low,
197 high;
199
200 /*
201 * Get min and max repetition counts for this query item, dealing with
202 * the backwards-compatibility hack that the low/high fields aren't
203 * meaningful for non-'*' items unless LQL_COUNT is set.
204 */
205 if ((curq->flag & LQL_COUNT) || curq->numvar == 0)
206 low = curq->low, high = curq->high;
207 else
208 low = high = 1;
209
210 /*
211 * We may limit "high" to the remaining text length; this avoids
212 * separate tests below.
213 */
214 if (high > tlen)
215 high = tlen;
216
217 /* Fail if a match of required number of items is impossible */
218 if (high < low)
219 return false;
220
221 /*
222 * Recursively check the rest of the pattern against each possible
223 * start point following some of this item's match(es).
224 */
226 qlen--;
227
228 for (int matchcnt = 0; matchcnt < high; matchcnt++)
229 {
230 /*
231 * If we've consumed an acceptable number of matches of this item,
232 * and the rest of the pattern matches beginning here, we're good.
233 */
234 if (matchcnt >= low && checkCond(nextq, qlen, curt, tlen))
235 return true;
236
237 /*
238 * Otherwise, try to match one more text item to this query item.
239 */
240 if (!checkLevel(curq, curt))
241 return false;
242
244 tlen--;
245 }
246
247 /*
248 * Once we've consumed "high" matches, we can succeed only if the rest
249 * of the pattern matches beginning here. Loop around (if you prefer,
250 * think of this as tail recursion).
251 */
252 curq = nextq;
253 }
254
255 /*
256 * Once we're out of query items, we match only if there's no remaining
257 * text either.
258 */
259 return (tlen == 0);
260}
261
262Datum
264{
266 lquery *query = PG_GETARG_LQUERY_P(1);
267 bool res;
268
269 res = checkCond(LQUERY_FIRST(query), query->numlevel,
270 LTREE_FIRST(tree), tree->numlevel);
271
273 PG_FREE_IF_COPY(query, 1);
274 PG_RETURN_BOOL(res);
275}
276
277Datum
285
286Datum
288{
291 lquery *query = (lquery *) ARR_DATA_PTR(_query);
292 bool res = false;
294
295 if (ARR_NDIM(_query) > 1)
298 errmsg("array must be one-dimensional")));
302 errmsg("array must not contain nulls")));
303
304 while (num > 0)
305 {
308 {
309
310 res = true;
311 break;
312 }
313 num--;
314 query = NEXTVAL(query);
315 }
316
319 PG_RETURN_BOOL(res);
320}
321
322Datum
#define ARR_NDIM(a)
Definition array.h:290
#define PG_GETARG_ARRAYTYPE_P(n)
Definition array.h:263
#define ARR_DATA_PTR(a)
Definition array.h:322
#define ARR_DIMS(a)
Definition array.h:294
bool array_contains_nulls(const ArrayType *array)
int ArrayGetNItems(int ndim, const int *dims)
Definition arrayutils.c:57
#define Assert(condition)
Definition c.h:906
int errcode(int sqlerrcode)
Definition elog.c:874
int errmsg(const char *fmt,...)
Definition elog.c:1093
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
#define PG_FREE_IF_COPY(ptr, n)
Definition fmgr.h:260
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:686
#define PG_GETARG_DATUM(n)
Definition fmgr.h:268
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_RETURN_DATUM(x)
Definition fmgr.h:354
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition fmgr.h:360
return str start
static bool success
Definition initdb.c:187
int i
Definition isn.c:77
#define NEXTVAL(x)
Definition lquery_op.c:22
Datum lt_q_rregex(PG_FUNCTION_ARGS)
Definition lquery_op.c:323
static char * getlexeme(char *start, char *end, int *len)
Definition lquery_op.c:25
static bool checkCond(lquery_level *curq, int qlen, ltree_level *curt, int tlen)
Definition lquery_op.c:184
bool compare_subnode(ltree_level *t, char *qn, int len, bool prefix, bool ci)
Definition lquery_op.c:44
Datum lt_q_regex(PG_FUNCTION_ARGS)
Definition lquery_op.c:287
Datum ltq_regex(PG_FUNCTION_ARGS)
Definition lquery_op.c:263
static bool checkLevel(lquery_level *curq, ltree_level *curt)
Definition lquery_op.c:150
bool ltree_label_match(const char *pred, size_t pred_len, const char *label, size_t label_len, bool prefix, bool ci)
Definition lquery_op.c:81
Datum ltq_rregex(PG_FUNCTION_ARGS)
Definition lquery_op.c:278
#define LTREE_FIRST(x)
Definition ltree.h:51
#define LVAR_INCASE
Definition ltree.h:75
#define PG_GETARG_LQUERY_P(n)
Definition ltree.h:224
#define LEVEL_NEXT(x)
Definition ltree.h:40
#define LVAR_ANYEND
Definition ltree.h:74
#define LQL_FIRST(x)
Definition ltree.h:101
#define LVAR_NEXT(x)
Definition ltree.h:72
#define LQL_NEXT(x)
Definition ltree.h:100
#define LQL_COUNT
Definition ltree.h:104
#define LQUERY_FIRST(x)
Definition ltree.h:124
#define LQL_NOT
Definition ltree.h:103
#define PG_GETARG_LTREE_P(n)
Definition ltree.h:219
#define LVAR_SUBLEXEME
Definition ltree.h:76
int pg_mblen_range(const char *mbstr, const char *end)
Definition mbutils.c:1084
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc(Size size)
Definition mcxt.c:1387
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
static char * label
const void size_t len
size_t pg_strfold(char *dst, size_t dstsize, const char *src, ssize_t srclen, pg_locale_t locale)
Definition pg_locale.c:1348
pg_locale_t pg_database_locale(void)
Definition pg_locale.c:1175
static bool DatumGetBool(Datum X)
Definition postgres.h:100
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
uint64_t Datum
Definition postgres.h:70
static int fb(int x)
tree
Definition radixtree.h:1828
void check_stack_depth(void)
Definition stack_depth.c:95
uint16 low
Definition ltree.h:93
uint8 flag
Definition ltree.h:62
uint16 len
Definition ltree.h:61
char name[FLEXIBLE_ARRAY_MEMBER]
Definition ltree.h:63
uint16 numlevel
Definition ltree.h:116
char name[FLEXIBLE_ARRAY_MEMBER]
Definition ltree.h:36
uint16 len
Definition ltree.h:35
Definition ltree.h:43
#define t_iseq(x, c)
Definition ts_locale.h:38