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 "miscadmin.h" 12 #include "utils/formatting.h" 13 #include "ltree.h" 14 15 PG_FUNCTION_INFO_V1(ltq_regex); 16 PG_FUNCTION_INFO_V1(ltq_rregex); 17 18 PG_FUNCTION_INFO_V1(lt_q_regex); 19 PG_FUNCTION_INFO_V1(lt_q_rregex); 20 21 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) ) 22 23 typedef struct 24 { 25 lquery_level *q; 26 int nq; 27 ltree_level *t; 28 int nt; 29 int posq; 30 int post; 31 } FieldNot; 32 33 static char * 34 getlexeme(char *start, char *end, int *len) 35 { 36 char *ptr; 37 int charlen; 38 39 while (start < end && (charlen = pg_mblen(start)) == 1 && t_iseq(start, '_')) 40 start += charlen; 41 42 ptr = start; 43 if (ptr >= end) 44 return NULL; 45 46 while (ptr < end && !((charlen = pg_mblen(ptr)) == 1 && t_iseq(ptr, '_'))) 47 ptr += charlen; 48 49 *len = ptr - start; 50 return start; 51 } 52 53 bool 54 compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend) 55 { 56 char *endt = t->name + t->len; 57 char *endq = qn + len; 58 char *tn; 59 int lent, 60 lenq; 61 bool isok; 62 63 while ((qn = getlexeme(qn, endq, &lenq)) != NULL) 64 { 65 tn = t->name; 66 isok = false; 67 while ((tn = getlexeme(tn, endt, &lent)) != NULL) 68 { 69 if ( 70 ( 71 lent == lenq || 72 (lent > lenq && anyend) 73 ) && 74 (*cmpptr) (qn, tn, lenq) == 0) 75 { 76 77 isok = true; 78 break; 79 } 80 tn += lent; 81 } 82 83 if (!isok) 84 return false; 85 qn += lenq; 86 } 87 88 return true; 89 } 90 91 int 92 ltree_strncasecmp(const char *a, const char *b, size_t s) 93 { 94 char *al = str_tolower(a, s, DEFAULT_COLLATION_OID); 95 char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID); 96 int res; 97 98 res = strncmp(al, bl, s); 99 100 pfree(al); 101 pfree(bl); 102 103 return res; 104 } 105 106 static bool 107 checkLevel(lquery_level *curq, ltree_level *curt) 108 { 109 int (*cmpptr) (const char *, const char *, size_t); 110 lquery_variant *curvar = LQL_FIRST(curq); 111 int i; 112 113 for (i = 0; i < curq->numvar; i++) 114 { 115 cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp; 116 117 if (curvar->flag & LVAR_SUBLEXEME) 118 { 119 if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND))) 120 return true; 121 } 122 else if ( 123 ( 124 curvar->len == curt->len || 125 (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND)) 126 ) && 127 (*cmpptr) (curvar->name, curt->name, curvar->len) == 0) 128 { 129 130 return true; 131 } 132 curvar = LVAR_NEXT(curvar); 133 } 134 return false; 135 } 136 137 /* 138 void 139 printFieldNot(FieldNot *fn ) { 140 while(fn->q) { 141 elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt); 142 fn++; 143 } 144 } 145 */ 146 147 static struct 148 { 149 bool muse; 150 uint32 high_pos; 151 } SomeStack = 152 153 { 154 false, 0, 155 }; 156 157 static bool 158 checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr) 159 { 160 uint32 low_pos = 0, 161 high_pos = 0, 162 cur_tpos = 0; 163 int tlen = tree_numlevel, 164 qlen = query_numlevel; 165 int isok; 166 lquery_level *prevq = NULL; 167 ltree_level *prevt = NULL; 168 169 /* Since this function recurses, it could be driven to stack overflow */ 170 check_stack_depth(); 171 172 /* Pathological patterns could take awhile, too */ 173 CHECK_FOR_INTERRUPTS(); 174 175 if (SomeStack.muse) 176 { 177 high_pos = SomeStack.high_pos; 178 qlen--; 179 prevq = curq; 180 curq = LQL_NEXT(curq); 181 SomeStack.muse = false; 182 } 183 184 while (tlen > 0 && qlen > 0) 185 { 186 if (curq->numvar) 187 { 188 prevt = curt; 189 while (cur_tpos < low_pos) 190 { 191 curt = LEVEL_NEXT(curt); 192 tlen--; 193 cur_tpos++; 194 if (tlen == 0) 195 return false; 196 if (ptr && ptr->q) 197 ptr->nt++; 198 } 199 200 if (ptr && curq->flag & LQL_NOT) 201 { 202 if (!(prevq && prevq->numvar == 0)) 203 prevq = curq; 204 if (ptr->q == NULL) 205 { 206 ptr->t = prevt; 207 ptr->q = prevq; 208 ptr->nt = 1; 209 ptr->nq = 1 + ((prevq == curq) ? 0 : 1); 210 ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1); 211 ptr->post = cur_tpos; 212 } 213 else 214 { 215 ptr->nt++; 216 ptr->nq++; 217 } 218 219 if (qlen == 1 && ptr->q->numvar == 0) 220 ptr->nt = tree_numlevel - ptr->post; 221 curt = LEVEL_NEXT(curt); 222 tlen--; 223 cur_tpos++; 224 if (high_pos < cur_tpos) 225 high_pos++; 226 } 227 else 228 { 229 isok = false; 230 while (cur_tpos <= high_pos && tlen > 0 && !isok) 231 { 232 isok = checkLevel(curq, curt); 233 curt = LEVEL_NEXT(curt); 234 tlen--; 235 cur_tpos++; 236 if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos) 237 { 238 FieldNot tmpptr; 239 240 if (ptr) 241 memcpy(&tmpptr, ptr, sizeof(FieldNot)); 242 SomeStack.high_pos = high_pos - cur_tpos; 243 SomeStack.muse = true; 244 if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL)) 245 return true; 246 } 247 if (!isok && ptr) 248 ptr->nt++; 249 } 250 if (!isok) 251 return false; 252 253 if (ptr && ptr->q) 254 { 255 if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL)) 256 return false; 257 ptr->q = NULL; 258 } 259 low_pos = cur_tpos; 260 high_pos = cur_tpos; 261 } 262 } 263 else 264 { 265 low_pos = cur_tpos + curq->low; 266 high_pos = cur_tpos + curq->high; 267 if (ptr && ptr->q) 268 { 269 ptr->nq++; 270 if (qlen == 1) 271 ptr->nt = tree_numlevel - ptr->post; 272 } 273 } 274 275 prevq = curq; 276 curq = LQL_NEXT(curq); 277 qlen--; 278 } 279 280 if (low_pos > tree_numlevel || tree_numlevel > high_pos) 281 return false; 282 283 while (qlen > 0) 284 { 285 if (curq->numvar) 286 { 287 if (!(curq->flag & LQL_NOT)) 288 return false; 289 } 290 else 291 { 292 low_pos = cur_tpos + curq->low; 293 high_pos = cur_tpos + curq->high; 294 } 295 296 curq = LQL_NEXT(curq); 297 qlen--; 298 } 299 300 if (low_pos > tree_numlevel || tree_numlevel > high_pos) 301 return false; 302 303 if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL)) 304 return false; 305 306 return true; 307 } 308 309 Datum 310 ltq_regex(PG_FUNCTION_ARGS) 311 { 312 ltree *tree = PG_GETARG_LTREE_P(0); 313 lquery *query = PG_GETARG_LQUERY_P(1); 314 bool res = false; 315 316 if (query->flag & LQUERY_HASNOT) 317 { 318 FieldNot fn; 319 320 fn.q = NULL; 321 322 res = checkCond(LQUERY_FIRST(query), query->numlevel, 323 LTREE_FIRST(tree), tree->numlevel, &fn); 324 } 325 else 326 { 327 res = checkCond(LQUERY_FIRST(query), query->numlevel, 328 LTREE_FIRST(tree), tree->numlevel, NULL); 329 } 330 331 PG_FREE_IF_COPY(tree, 0); 332 PG_FREE_IF_COPY(query, 1); 333 PG_RETURN_BOOL(res); 334 } 335 336 Datum 337 ltq_rregex(PG_FUNCTION_ARGS) 338 { 339 PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex, 340 PG_GETARG_DATUM(1), 341 PG_GETARG_DATUM(0) 342 )); 343 } 344 345 Datum 346 lt_q_regex(PG_FUNCTION_ARGS) 347 { 348 ltree *tree = PG_GETARG_LTREE_P(0); 349 ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1); 350 lquery *query = (lquery *) ARR_DATA_PTR(_query); 351 bool res = false; 352 int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query)); 353 354 if (ARR_NDIM(_query) > 1) 355 ereport(ERROR, 356 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), 357 errmsg("array must be one-dimensional"))); 358 if (array_contains_nulls(_query)) 359 ereport(ERROR, 360 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), 361 errmsg("array must not contain nulls"))); 362 363 while (num > 0) 364 { 365 if (DatumGetBool(DirectFunctionCall2(ltq_regex, 366 PointerGetDatum(tree), PointerGetDatum(query)))) 367 { 368 369 res = true; 370 break; 371 } 372 num--; 373 query = NEXTVAL(query); 374 } 375 376 PG_FREE_IF_COPY(tree, 0); 377 PG_FREE_IF_COPY(_query, 1); 378 PG_RETURN_BOOL(res); 379 } 380 381 Datum 382 lt_q_rregex(PG_FUNCTION_ARGS) 383 { 384 PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex, 385 PG_GETARG_DATUM(1), 386 PG_GETARG_DATUM(0) 387 )); 388 } 389