PostgreSQL Source Code git master
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, '_'))
31
32 ptr = start;
33 if (ptr >= end)
34 return NULL;
35
36 while (ptr < end && !t_iseq(ptr, '_'))
37 ptr += pg_mblen(ptr);
38
39 *len = ptr - start;
40 return start;
41}
42
43bool
45 ltree_prefix_eq_func prefix_eq, bool anyend)
46{
47 char *endt = t->name + t->len;
48 char *endq = qn + len;
49 char *tn;
50 int lent,
51 lenq;
52 bool isok;
53
54 while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
55 {
56 tn = t->name;
57 isok = false;
58 while ((tn = getlexeme(tn, endt, &lent)) != NULL)
59 {
60 if ((lent == lenq || (lent > lenq && anyend)) &&
61 (*prefix_eq) (qn, lenq, tn, lent))
62 {
63
64 isok = true;
65 break;
66 }
67 tn += lent;
68 }
69
70 if (!isok)
71 return false;
72 qn += lenq;
73 }
74
75 return true;
76}
77
78/*
79 * Check if 'a' is a prefix of 'b'.
80 */
81bool
82ltree_prefix_eq(const char *a, size_t a_sz, const char *b, size_t b_sz)
83{
84 if (a_sz > b_sz)
85 return false;
86 else
87 return (strncmp(a, b, a_sz) == 0);
88}
89
90/*
91 * Case-insensitive check if 'a' is a prefix of 'b'.
92 */
93bool
94ltree_prefix_eq_ci(const char *a, size_t a_sz, const char *b, size_t b_sz)
95{
96 static pg_locale_t locale = NULL;
97 size_t al_sz = a_sz + 1;
98 size_t al_len;
99 char *al = palloc(al_sz);
100 size_t bl_sz = b_sz + 1;
101 size_t bl_len;
102 char *bl = palloc(bl_sz);
103 bool res;
104
105 if (!locale)
107
108 /* casefold both a and b */
109
110 al_len = pg_strfold(al, al_sz, a, a_sz, locale);
111 if (al_len + 1 > al_sz)
112 {
113 /* grow buffer if needed and retry */
114 al_sz = al_len + 1;
115 al = repalloc(al, al_sz);
116 al_len = pg_strfold(al, al_sz, a, a_sz, locale);
117 Assert(al_len + 1 <= al_sz);
118 }
119
120 bl_len = pg_strfold(bl, bl_sz, b, b_sz, locale);
121 if (bl_len + 1 > bl_sz)
122 {
123 /* grow buffer if needed and retry */
124 bl_sz = bl_len + 1;
125 bl = repalloc(bl, bl_sz);
126 bl_len = pg_strfold(bl, bl_sz, b, b_sz, locale);
127 Assert(bl_len + 1 <= bl_sz);
128 }
129
130 if (al_len > bl_len)
131 res = false;
132 else
133 res = (strncmp(al, bl, al_len) == 0);
134
135 pfree(al);
136 pfree(bl);
137
138 return res;
139}
140
141/*
142 * See if an lquery_level matches an ltree_level
143 *
144 * This accounts for all flags including LQL_NOT, but does not
145 * consider repetition counts.
146 */
147static bool
149{
150 lquery_variant *curvar = LQL_FIRST(curq);
151 bool success;
152
153 success = (curq->flag & LQL_NOT) ? false : true;
154
155 /* numvar == 0 means '*' which matches anything */
156 if (curq->numvar == 0)
157 return success;
158
159 for (int i = 0; i < curq->numvar; i++)
160 {
161 ltree_prefix_eq_func prefix_eq;
162
163 prefix_eq = (curvar->flag & LVAR_INCASE) ? ltree_prefix_eq_ci : ltree_prefix_eq;
164
165 if (curvar->flag & LVAR_SUBLEXEME)
166 {
167 if (compare_subnode(curt, curvar->name, curvar->len, prefix_eq,
168 (curvar->flag & LVAR_ANYEND)))
169 return success;
170 }
171 else if ((curvar->len == curt->len ||
172 (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))) &&
173 (*prefix_eq) (curvar->name, curvar->len, curt->name, curt->len))
174 return success;
175
176 curvar = LVAR_NEXT(curvar);
177 }
178 return !success;
179}
180
181/*
182 * Try to match an lquery (of qlen items) to an ltree (of tlen items)
183 */
184static bool
185checkCond(lquery_level *curq, int qlen,
186 ltree_level *curt, int tlen)
187{
188 /* Since this function recurses, it could be driven to stack overflow */
190
191 /* Pathological patterns could take awhile, too */
193
194 /* Loop while we have query items to consider */
195 while (qlen > 0)
196 {
197 int low,
198 high;
199 lquery_level *nextq;
200
201 /*
202 * Get min and max repetition counts for this query item, dealing with
203 * the backwards-compatibility hack that the low/high fields aren't
204 * meaningful for non-'*' items unless LQL_COUNT is set.
205 */
206 if ((curq->flag & LQL_COUNT) || curq->numvar == 0)
207 low = curq->low, high = curq->high;
208 else
209 low = high = 1;
210
211 /*
212 * We may limit "high" to the remaining text length; this avoids
213 * separate tests below.
214 */
215 if (high > tlen)
216 high = tlen;
217
218 /* Fail if a match of required number of items is impossible */
219 if (high < low)
220 return false;
221
222 /*
223 * Recursively check the rest of the pattern against each possible
224 * start point following some of this item's match(es).
225 */
226 nextq = LQL_NEXT(curq);
227 qlen--;
228
229 for (int matchcnt = 0; matchcnt < high; matchcnt++)
230 {
231 /*
232 * If we've consumed an acceptable number of matches of this item,
233 * and the rest of the pattern matches beginning here, we're good.
234 */
235 if (matchcnt >= low && checkCond(nextq, qlen, curt, tlen))
236 return true;
237
238 /*
239 * Otherwise, try to match one more text item to this query item.
240 */
241 if (!checkLevel(curq, curt))
242 return false;
243
244 curt = LEVEL_NEXT(curt);
245 tlen--;
246 }
247
248 /*
249 * Once we've consumed "high" matches, we can succeed only if the rest
250 * of the pattern matches beginning here. Loop around (if you prefer,
251 * think of this as tail recursion).
252 */
253 curq = nextq;
254 }
255
256 /*
257 * Once we're out of query items, we match only if there's no remaining
258 * text either.
259 */
260 return (tlen == 0);
261}
262
263Datum
265{
267 lquery *query = PG_GETARG_LQUERY_P(1);
268 bool res;
269
270 res = checkCond(LQUERY_FIRST(query), query->numlevel,
271 LTREE_FIRST(tree), tree->numlevel);
272
274 PG_FREE_IF_COPY(query, 1);
275 PG_RETURN_BOOL(res);
276}
277
278Datum
280{
284 ));
285}
286
287Datum
289{
291 ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1);
292 lquery *query = (lquery *) ARR_DATA_PTR(_query);
293 bool res = false;
294 int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
295
296 if (ARR_NDIM(_query) > 1)
298 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
299 errmsg("array must be one-dimensional")));
300 if (array_contains_nulls(_query))
302 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
303 errmsg("array must not contain nulls")));
304
305 while (num > 0)
306 {
309 {
310
311 res = true;
312 break;
313 }
314 num--;
315 query = NEXTVAL(query);
316 }
317
319 PG_FREE_IF_COPY(_query, 1);
320 PG_RETURN_BOOL(res);
321}
322
323Datum
325{
329 ));
330}
#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)
Definition: arrayfuncs.c:3768
int ArrayGetNItems(int ndim, const int *dims)
Definition: arrayutils.c:57
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#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:684
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
Assert(PointerIsAligned(start, uint64))
return str start
static bool success
Definition: initdb.c:187
static char * locale
Definition: initdb.c:140
int b
Definition: isn.c:74
int a
Definition: isn.c:73
int i
Definition: isn.c:77
#define NEXTVAL(x)
Definition: lquery_op.c:22
bool compare_subnode(ltree_level *t, char *qn, int len, ltree_prefix_eq_func prefix_eq, bool anyend)
Definition: lquery_op.c:44
PG_FUNCTION_INFO_V1(ltq_regex)
bool ltree_prefix_eq(const char *a, size_t a_sz, const char *b, size_t b_sz)
Definition: lquery_op.c:82
Datum lt_q_rregex(PG_FUNCTION_ARGS)
Definition: lquery_op.c:324
bool ltree_prefix_eq_ci(const char *a, size_t a_sz, const char *b, size_t b_sz)
Definition: lquery_op.c:94
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:185
Datum lt_q_regex(PG_FUNCTION_ARGS)
Definition: lquery_op.c:288
Datum ltq_regex(PG_FUNCTION_ARGS)
Definition: lquery_op.c:264
static bool checkLevel(lquery_level *curq, ltree_level *curt)
Definition: lquery_op.c:148
Datum ltq_rregex(PG_FUNCTION_ARGS)
Definition: lquery_op.c:279
#define LTREE_FIRST(x)
Definition: ltree.h:51
#define LVAR_INCASE
Definition: ltree.h:75
#define PG_GETARG_LQUERY_P(n)
Definition: ltree.h:226
#define LEVEL_NEXT(x)
Definition: ltree.h:40
#define LVAR_ANYEND
Definition: ltree.h:74
bool(* ltree_prefix_eq_func)(const char *, size_t, const char *, size_t)
Definition: ltree.h:160
#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:221
#define LVAR_SUBLEXEME
Definition: ltree.h:76
int pg_mblen(const char *mbstr)
Definition: mbutils.c:1026
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
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:1345
pg_locale_t pg_database_locale(void)
Definition: pg_locale.c:1172
static bool DatumGetBool(Datum X)
Definition: postgres.h:100
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332
uint64_t Datum
Definition: postgres.h:70
tree
Definition: radixtree.h:1828
void check_stack_depth(void)
Definition: stack_depth.c:95
uint16 numvar
Definition: ltree.h:92
uint16 high
Definition: ltree.h:94
uint16 flag
Definition: ltree.h:91
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
Definition: ltree.h:114
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