00001
00002
00003
00004
00005
00006 #include "postgres.h"
00007
00008 #include <ctype.h>
00009
00010 #include "catalog/pg_collation.h"
00011 #include "utils/formatting.h"
00012 #include "ltree.h"
00013
00014 PG_FUNCTION_INFO_V1(ltq_regex);
00015 PG_FUNCTION_INFO_V1(ltq_rregex);
00016
00017 PG_FUNCTION_INFO_V1(lt_q_regex);
00018 PG_FUNCTION_INFO_V1(lt_q_rregex);
00019
00020 #define NEXTVAL(x) ( (lquery*)( (char*)(x) + INTALIGN( VARSIZE(x) ) ) )
00021
00022 typedef struct
00023 {
00024 lquery_level *q;
00025 int nq;
00026 ltree_level *t;
00027 int nt;
00028 int posq;
00029 int post;
00030 } FieldNot;
00031
00032 static char *
00033 getlexeme(char *start, char *end, int *len)
00034 {
00035 char *ptr;
00036 int charlen;
00037
00038 while (start < end && (charlen = pg_mblen(start)) == 1 && t_iseq(start, '_'))
00039 start += charlen;
00040
00041 ptr = start;
00042 if (ptr >= end)
00043 return NULL;
00044
00045 while (ptr < end && !((charlen = pg_mblen(ptr)) == 1 && t_iseq(ptr, '_')))
00046 ptr += charlen;
00047
00048 *len = ptr - start;
00049 return start;
00050 }
00051
00052 bool
00053 compare_subnode(ltree_level *t, char *qn, int len, int (*cmpptr) (const char *, const char *, size_t), bool anyend)
00054 {
00055 char *endt = t->name + t->len;
00056 char *endq = qn + len;
00057 char *tn;
00058 int lent,
00059 lenq;
00060 bool isok;
00061
00062 while ((qn = getlexeme(qn, endq, &lenq)) != NULL)
00063 {
00064 tn = t->name;
00065 isok = false;
00066 while ((tn = getlexeme(tn, endt, &lent)) != NULL)
00067 {
00068 if (
00069 (
00070 lent == lenq ||
00071 (lent > lenq && anyend)
00072 ) &&
00073 (*cmpptr) (qn, tn, lenq) == 0)
00074 {
00075
00076 isok = true;
00077 break;
00078 }
00079 tn += lent;
00080 }
00081
00082 if (!isok)
00083 return false;
00084 qn += lenq;
00085 }
00086
00087 return true;
00088 }
00089
00090 int
00091 ltree_strncasecmp(const char *a, const char *b, size_t s)
00092 {
00093 char *al = str_tolower(a, s, DEFAULT_COLLATION_OID);
00094 char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID);
00095 int res;
00096
00097 res = strncmp(al, bl, s);
00098
00099 pfree(al);
00100 pfree(bl);
00101
00102 return res;
00103 }
00104
00105 static bool
00106 checkLevel(lquery_level *curq, ltree_level *curt)
00107 {
00108 int (*cmpptr) (const char *, const char *, size_t);
00109 lquery_variant *curvar = LQL_FIRST(curq);
00110 int i;
00111
00112 for (i = 0; i < curq->numvar; i++)
00113 {
00114 cmpptr = (curvar->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
00115
00116 if (curvar->flag & LVAR_SUBLEXEME)
00117 {
00118 if (compare_subnode(curt, curvar->name, curvar->len, cmpptr, (curvar->flag & LVAR_ANYEND)))
00119 return true;
00120 }
00121 else if (
00122 (
00123 curvar->len == curt->len ||
00124 (curt->len > curvar->len && (curvar->flag & LVAR_ANYEND))
00125 ) &&
00126 (*cmpptr) (curvar->name, curt->name, curvar->len) == 0)
00127 {
00128
00129 return true;
00130 }
00131 curvar = LVAR_NEXT(curvar);
00132 }
00133 return false;
00134 }
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146 static struct
00147 {
00148 bool muse;
00149 uint32 high_pos;
00150 } SomeStack =
00151
00152 {
00153 false, 0,
00154 };
00155
00156 static bool
00157 checkCond(lquery_level *curq, int query_numlevel, ltree_level *curt, int tree_numlevel, FieldNot *ptr)
00158 {
00159 uint32 low_pos = 0,
00160 high_pos = 0,
00161 cur_tpos = 0;
00162 int tlen = tree_numlevel,
00163 qlen = query_numlevel;
00164 int isok;
00165 lquery_level *prevq = NULL;
00166 ltree_level *prevt = NULL;
00167
00168 if (SomeStack.muse)
00169 {
00170 high_pos = SomeStack.high_pos;
00171 qlen--;
00172 prevq = curq;
00173 curq = LQL_NEXT(curq);
00174 SomeStack.muse = false;
00175 }
00176
00177 while (tlen > 0 && qlen > 0)
00178 {
00179 if (curq->numvar)
00180 {
00181 prevt = curt;
00182 while (cur_tpos < low_pos)
00183 {
00184 curt = LEVEL_NEXT(curt);
00185 tlen--;
00186 cur_tpos++;
00187 if (tlen == 0)
00188 return false;
00189 if (ptr && ptr->q)
00190 ptr->nt++;
00191 }
00192
00193 if (ptr && curq->flag & LQL_NOT)
00194 {
00195 if (!(prevq && prevq->numvar == 0))
00196 prevq = curq;
00197 if (ptr->q == NULL)
00198 {
00199 ptr->t = prevt;
00200 ptr->q = prevq;
00201 ptr->nt = 1;
00202 ptr->nq = 1 + ((prevq == curq) ? 0 : 1);
00203 ptr->posq = query_numlevel - qlen - ((prevq == curq) ? 0 : 1);
00204 ptr->post = cur_tpos;
00205 }
00206 else
00207 {
00208 ptr->nt++;
00209 ptr->nq++;
00210 }
00211
00212 if (qlen == 1 && ptr->q->numvar == 0)
00213 ptr->nt = tree_numlevel - ptr->post;
00214 curt = LEVEL_NEXT(curt);
00215 tlen--;
00216 cur_tpos++;
00217 if (high_pos < cur_tpos)
00218 high_pos++;
00219 }
00220 else
00221 {
00222 isok = false;
00223 while (cur_tpos <= high_pos && tlen > 0 && !isok)
00224 {
00225 isok = checkLevel(curq, curt);
00226 curt = LEVEL_NEXT(curt);
00227 tlen--;
00228 cur_tpos++;
00229 if (isok && prevq && prevq->numvar == 0 && tlen > 0 && cur_tpos <= high_pos)
00230 {
00231 FieldNot tmpptr;
00232
00233 if (ptr)
00234 memcpy(&tmpptr, ptr, sizeof(FieldNot));
00235 SomeStack.high_pos = high_pos - cur_tpos;
00236 SomeStack.muse = true;
00237 if (checkCond(prevq, qlen + 1, curt, tlen, (ptr) ? &tmpptr : NULL))
00238 return true;
00239 }
00240 if (!isok && ptr)
00241 ptr->nt++;
00242 }
00243 if (!isok)
00244 return false;
00245
00246 if (ptr && ptr->q)
00247 {
00248 if (checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
00249 return false;
00250 ptr->q = NULL;
00251 }
00252 low_pos = cur_tpos;
00253 high_pos = cur_tpos;
00254 }
00255 }
00256 else
00257 {
00258 low_pos = cur_tpos + curq->low;
00259 high_pos = cur_tpos + curq->high;
00260 if (ptr && ptr->q)
00261 {
00262 ptr->nq++;
00263 if (qlen == 1)
00264 ptr->nt = tree_numlevel - ptr->post;
00265 }
00266 }
00267
00268 prevq = curq;
00269 curq = LQL_NEXT(curq);
00270 qlen--;
00271 }
00272
00273 if (low_pos > tree_numlevel || tree_numlevel > high_pos)
00274 return false;
00275
00276 while (qlen > 0)
00277 {
00278 if (curq->numvar)
00279 {
00280 if (!(curq->flag & LQL_NOT))
00281 return false;
00282 }
00283 else
00284 {
00285 low_pos = cur_tpos + curq->low;
00286 high_pos = cur_tpos + curq->high;
00287 }
00288
00289 curq = LQL_NEXT(curq);
00290 qlen--;
00291 }
00292
00293 if (low_pos > tree_numlevel || tree_numlevel > high_pos)
00294 return false;
00295
00296 if (ptr && ptr->q && checkCond(ptr->q, ptr->nq, ptr->t, ptr->nt, NULL))
00297 return false;
00298
00299 return true;
00300 }
00301
00302 Datum
00303 ltq_regex(PG_FUNCTION_ARGS)
00304 {
00305 ltree *tree = PG_GETARG_LTREE(0);
00306 lquery *query = PG_GETARG_LQUERY(1);
00307 bool res = false;
00308
00309 if (query->flag & LQUERY_HASNOT)
00310 {
00311 FieldNot fn;
00312
00313 fn.q = NULL;
00314
00315 res = checkCond(LQUERY_FIRST(query), query->numlevel,
00316 LTREE_FIRST(tree), tree->numlevel, &fn);
00317 }
00318 else
00319 {
00320 res = checkCond(LQUERY_FIRST(query), query->numlevel,
00321 LTREE_FIRST(tree), tree->numlevel, NULL);
00322 }
00323
00324 PG_FREE_IF_COPY(tree, 0);
00325 PG_FREE_IF_COPY(query, 1);
00326 PG_RETURN_BOOL(res);
00327 }
00328
00329 Datum
00330 ltq_rregex(PG_FUNCTION_ARGS)
00331 {
00332 PG_RETURN_DATUM(DirectFunctionCall2(ltq_regex,
00333 PG_GETARG_DATUM(1),
00334 PG_GETARG_DATUM(0)
00335 ));
00336 }
00337
00338 Datum
00339 lt_q_regex(PG_FUNCTION_ARGS)
00340 {
00341 ltree *tree = PG_GETARG_LTREE(0);
00342 ArrayType *_query = PG_GETARG_ARRAYTYPE_P(1);
00343 lquery *query = (lquery *) ARR_DATA_PTR(_query);
00344 bool res = false;
00345 int num = ArrayGetNItems(ARR_NDIM(_query), ARR_DIMS(_query));
00346
00347 if (ARR_NDIM(_query) > 1)
00348 ereport(ERROR,
00349 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
00350 errmsg("array must be one-dimensional")));
00351 if (array_contains_nulls(_query))
00352 ereport(ERROR,
00353 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
00354 errmsg("array must not contain nulls")));
00355
00356 while (num > 0)
00357 {
00358 if (DatumGetBool(DirectFunctionCall2(ltq_regex,
00359 PointerGetDatum(tree), PointerGetDatum(query))))
00360 {
00361
00362 res = true;
00363 break;
00364 }
00365 num--;
00366 query = NEXTVAL(query);
00367 }
00368
00369 PG_FREE_IF_COPY(tree, 0);
00370 PG_FREE_IF_COPY(_query, 1);
00371 PG_RETURN_BOOL(res);
00372 }
00373
00374 Datum
00375 lt_q_rregex(PG_FUNCTION_ARGS)
00376 {
00377 PG_RETURN_DATUM(DirectFunctionCall2(lt_q_regex,
00378 PG_GETARG_DATUM(1),
00379 PG_GETARG_DATUM(0)
00380 ));
00381 }