Age Owner TLA Line data Source code
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 :
10 : #include "catalog/pg_collation.h"
11 : #include "ltree.h"
12 : #include "miscadmin.h"
13 : #include "utils/array.h"
14 : #include "utils/formatting.h"
15 :
7558 bruce 16 GIC 3 : PG_FUNCTION_INFO_V1(ltq_regex);
7558 bruce 17 CBC 2 : PG_FUNCTION_INFO_V1(ltq_rregex);
7558 bruce 18 ECB :
7354 bruce 19 GIC 3 : PG_FUNCTION_INFO_V1(lt_q_regex);
7354 bruce 20 CBC 2 : PG_FUNCTION_INFO_V1(lt_q_rregex);
7354 bruce 21 ECB :
22 : #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
23 :
24 : static char *
6248 neilc 25 GIC 36 : getlexeme(char *start, char *end, int *len)
7522 bruce 26 ECB : {
27 : char *ptr;
28 :
185 tgl 29 GNC 44 : while (start < end && t_iseq(start, '_'))
30 8 : start += pg_mblen(start);
31 :
7558 bruce 32 CBC 36 : ptr = start;
5396 teodor 33 36 : if (ptr >= end)
7558 bruce 34 8 : return NULL;
35 :
185 tgl 36 GNC 114 : while (ptr < end && !t_iseq(ptr, '_'))
37 86 : ptr += pg_mblen(ptr);
38 :
7558 bruce 39 CBC 28 : *len = ptr - start;
40 28 : return start;
41 : }
42 :
43 : bool
1418 tgl 44 8 : compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
45 : {
7522 bruce 46 8 : char *endt = t->name + t->len;
47 8 : char *endq = qn + len;
48 : char *tn;
49 : int lent,
50 : lenq;
51 : bool isok;
52 :
6248 neilc 53 16 : while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
54 : {
7522 bruce 55 12 : tn = t->name;
7558 56 12 : isok = false;
6248 neilc 57 20 : while ((tn = getlexeme(tn, endt, &lent)) != NULL)
58 : {
1165 alvherre 59 32 : if ((lent == lenq || (lent > lenq && anyend)) &&
7522 bruce 60 16 : (*cmpptr) (qn, tn, lenq) == 0)
61 : {
62 :
63 8 : isok = true;
7558 64 8 : break;
65 : }
66 8 : tn += lent;
67 : }
68 :
7522 69 12 : if (!isok)
7558 70 4 : return false;
71 8 : qn += lenq;
72 : }
73 :
74 4 : return true;
75 : }
76 :
77 : int
5396 teodor 78 26 : ltree_strncasecmp(const char *a, const char *b, size_t s)
79 : {
4443 peter_e 80 26 : char *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
81 26 : char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
82 : int res;
83 :
5050 bruce 84 26 : res = strncmp(al, bl, s);
85 :
5396 teodor 86 26 : pfree(al);
87 26 : pfree(bl);
88 :
89 26 : return res;
90 : }
91 :
92 : /*
93 : * See if an lquery_level matches an ltree_level
94 : *
95 : * This accounts for all flags including LQL_NOT, but does not
96 : * consider repetition counts.
97 : */
98 : static bool
5050 bruce 99 216918 : checkLevel(lquery_level *curq, ltree_level *curt)
100 : {
7558 101 216918 : lquery_variant *curvar = LQL_FIRST(curq);
102 : bool success;
103 :
1104 tgl 104 216918 : success = (curq->flag & LQL_NOT) ? false : true;
105 :
106 : /* numvar == 0 means '*' which matches anything */
107 216918 : if (curq->numvar == 0)
108 83195 : return success;
109 :
110 260897 : for (int i = 0; i < curq->numvar; i++)
111 : {
112 : int (*cmpptr) (const char *, const char *, size_t);
113 :
5396 teodor 114 133728 : cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
115 :
6248 neilc 116 133728 : if (curvar->flag & LVAR_SUBLEXEME)
117 : {
1104 tgl 118 4 : if (compare_subnode(curt, curvar->name, curvar->len, cmpptr,
119 4 : (curvar->flag & LVAR_ANYEND)))
120 3 : return success;
121 : }
1165 alvherre 122 133724 : else if ((curvar->len == curt->len ||
123 133727 : (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))) &&
7522 bruce 124 55166 : (*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
1104 tgl 125 6551 : return success;
126 :
7522 bruce 127 127174 : curvar = LVAR_NEXT(curvar);
128 : }
1104 tgl 129 127169 : return !success;
130 : }
131 :
132 : /*
133 : * Try to match an lquery (of qlen items) to an ltree (of tlen items)
134 : */
135 : static bool
136 143589 : checkCond(lquery_level *curq, int qlen,
137 : ltree_level *curt, int tlen)
138 : {
139 : /* Since this function recurses, it could be driven to stack overflow */
140 143589 : check_stack_depth();
141 :
142 : /* Pathological patterns could take awhile, too */
143 143589 : CHECK_FOR_INTERRUPTS();
144 :
145 : /* Loop while we have query items to consider */
146 162868 : while (qlen > 0)
147 : {
148 : int low,
149 : high;
150 : lquery_level *nextq;
151 :
152 : /*
153 : * Get min and max repetition counts for this query item, dealing with
154 : * the backwards-compatibility hack that the low/high fields aren't
155 : * meaningful for non-'*' items unless LQL_COUNT is set.
156 : */
157 159155 : if ((curq->flag & LQL_COUNT) || curq->numvar == 0)
158 13307 : low = curq->low, high = curq->high;
159 : else
160 145848 : low = high = 1;
161 :
162 : /*
163 : * We may limit "high" to the remaining text length; this avoids
164 : * separate tests below.
165 : */
166 159155 : if (high > tlen)
167 24988 : high = tlen;
168 :
169 : /* Fail if a match of required number of items is impossible */
170 159155 : if (high < low)
171 12173 : return false;
172 :
173 : /*
174 : * Recursively check the rest of the pattern against each possible
175 : * start point following some of this item's match(es).
176 : */
177 146982 : nextq = LQL_NEXT(curq);
7558 bruce 178 146982 : qlen--;
179 :
1104 tgl 180 236791 : for (int matchcnt = 0; matchcnt < high; matchcnt++)
181 : {
182 : /*
183 : * If we've consumed an acceptable number of matches of this item,
184 : * and the rest of the pattern matches beginning here, we're good.
185 : */
186 217512 : if (matchcnt >= low && checkCond(nextq, qlen, curt, tlen))
187 594 : return true;
188 :
189 : /*
190 : * Otherwise, try to match one more text item to this query item.
191 : */
192 216918 : if (!checkLevel(curq, curt))
1107 193 127109 : return false;
194 :
1104 195 89809 : curt = LEVEL_NEXT(curt);
196 89809 : tlen--;
197 : }
198 :
199 : /*
200 : * Once we've consumed "high" matches, we can succeed only if the rest
201 : * of the pattern matches beginning here. Loop around (if you prefer,
202 : * think of this as tail recursion).
203 : */
204 19279 : curq = nextq;
205 : }
206 :
207 : /*
208 : * Once we're out of query items, we match only if there's no remaining
209 : * text either.
210 : */
211 3713 : return (tlen == 0);
212 : }
213 :
214 : Datum
7522 bruce 215 60271 : ltq_regex(PG_FUNCTION_ARGS)
216 : {
2029 tgl 217 60271 : ltree *tree = PG_GETARG_LTREE_P(0);
218 60271 : lquery *query = PG_GETARG_LQUERY_P(1);
219 : bool res;
220 :
1104 221 60271 : res = checkCond(LQUERY_FIRST(query), query->numlevel,
222 60271 : LTREE_FIRST(tree), tree->numlevel);
223 :
7522 bruce 224 60271 : PG_FREE_IF_COPY(tree, 0);
225 60271 : PG_FREE_IF_COPY(query, 1);
226 60271 : PG_RETURN_BOOL(res);
227 : }
228 :
229 : Datum
7522 bruce 230 UBC 0 : ltq_rregex(PG_FUNCTION_ARGS)
231 : {
232 0 : PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
233 : PG_GETARG_DATUM(1),
234 : PG_GETARG_DATUM(0)
235 : ));
236 : }
237 :
238 : Datum
7354 bruce 239 CBC 1600 : lt_q_regex(PG_FUNCTION_ARGS)
240 : {
2029 tgl 241 1600 : ltree *tree = PG_GETARG_LTREE_P(0);
7188 bruce 242 1600 : ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1);
243 1600 : lquery *query = (lquery *) ARR_DATA_PTR(_query);
244 1600 : bool res = false;
245 1600 : int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
246 :
4792 tgl 247 1600 : if (ARR_NDIM(_query) > 1)
7188 bruce 248 UBC 0 : ereport(ERROR,
249 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
250 : errmsg("array must be one-dimensional")));
4473 tgl 251 CBC 1600 : if (array_contains_nulls(_query))
6350 tgl 252 UBC 0 : ereport(ERROR,
253 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
254 : errmsg("array must not contain nulls")));
255 :
7188 bruce 256 CBC 4775 : while (num > 0)
257 : {
7354 258 3189 : if (DatumGetBool(DirectFunctionCall2(ltq_regex,
259 : PointerGetDatum(tree), PointerGetDatum(query))))
260 : {
261 :
262 14 : res = true;
263 14 : break;
264 : }
265 3175 : num--;
266 3175 : query = NEXTVAL(query);
267 : }
268 :
269 1600 : PG_FREE_IF_COPY(tree, 0);
270 1600 : PG_FREE_IF_COPY(_query, 1);
271 1600 : PG_RETURN_BOOL(res);
272 : }
273 :
274 : Datum
7354 bruce 275 UBC 0 : lt_q_rregex(PG_FUNCTION_ARGS)
276 : {
277 0 : PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
278 : PG_GETARG_DATUM(1),
279 : PG_GETARG_DATUM(0)
280 : ));
281 : }
|