Header And Logo

PostgreSQL
| The world's most advanced open source database.

lquery_op.c

Go to the documentation of this file.
00001 /*
00002  * op function for ltree and lquery
00003  * Teodor Sigaev <[email protected]>
00004  * contrib/ltree/lquery_op.c
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 void
00138 printFieldNot(FieldNot *fn ) {
00139     while(fn->q) {
00140         elog(NOTICE,"posQ:%d lenQ:%d posT:%d lenT:%d", fn->posq,fn->nq,fn->post,fn->nt);
00141         fn++;
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 }