Header And Logo

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

ltree_io.c

Go to the documentation of this file.
00001 /*
00002  * in/out function for ltree and lquery
00003  * Teodor Sigaev <[email protected]>
00004  * contrib/ltree/ltree_io.c
00005  */
00006 #include "postgres.h"
00007 
00008 #include <ctype.h>
00009 
00010 #include "ltree.h"
00011 #include "crc32.h"
00012 
00013 PG_FUNCTION_INFO_V1(ltree_in);
00014 Datum       ltree_in(PG_FUNCTION_ARGS);
00015 
00016 PG_FUNCTION_INFO_V1(ltree_out);
00017 Datum       ltree_out(PG_FUNCTION_ARGS);
00018 
00019 PG_FUNCTION_INFO_V1(lquery_in);
00020 Datum       lquery_in(PG_FUNCTION_ARGS);
00021 
00022 PG_FUNCTION_INFO_V1(lquery_out);
00023 Datum       lquery_out(PG_FUNCTION_ARGS);
00024 
00025 
00026 #define UNCHAR ereport(ERROR, \
00027                        (errcode(ERRCODE_SYNTAX_ERROR), \
00028                         errmsg("syntax error at position %d", \
00029                         pos)));
00030 
00031 
00032 typedef struct
00033 {
00034     char       *start;
00035     int         len;            /* length in bytes */
00036     int         flag;
00037     int         wlen;           /* length in characters */
00038 } nodeitem;
00039 
00040 #define LTPRS_WAITNAME  0
00041 #define LTPRS_WAITDELIM 1
00042 
00043 Datum
00044 ltree_in(PG_FUNCTION_ARGS)
00045 {
00046     char       *buf = (char *) PG_GETARG_POINTER(0);
00047     char       *ptr;
00048     nodeitem   *list,
00049                *lptr;
00050     int         num = 0,
00051                 totallen = 0;
00052     int         state = LTPRS_WAITNAME;
00053     ltree      *result;
00054     ltree_level *curlevel;
00055     int         charlen;
00056     int         pos = 0;
00057 
00058     ptr = buf;
00059     while (*ptr)
00060     {
00061         charlen = pg_mblen(ptr);
00062         if (charlen == 1 && t_iseq(ptr, '.'))
00063             num++;
00064         ptr += charlen;
00065     }
00066 
00067     list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1));
00068     ptr = buf;
00069     while (*ptr)
00070     {
00071         charlen = pg_mblen(ptr);
00072 
00073         if (state == LTPRS_WAITNAME)
00074         {
00075             if (ISALNUM(ptr))
00076             {
00077                 lptr->start = ptr;
00078                 lptr->wlen = 0;
00079                 state = LTPRS_WAITDELIM;
00080             }
00081             else
00082                 UNCHAR;
00083         }
00084         else if (state == LTPRS_WAITDELIM)
00085         {
00086             if (charlen == 1 && t_iseq(ptr, '.'))
00087             {
00088                 lptr->len = ptr - lptr->start;
00089                 if (lptr->wlen > 255)
00090                     ereport(ERROR,
00091                             (errcode(ERRCODE_NAME_TOO_LONG),
00092                              errmsg("name of level is too long"),
00093                              errdetail("Name length is %d, must "
00094                                        "be < 256, in position %d.",
00095                                        lptr->wlen, pos)));
00096 
00097                 totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
00098                 lptr++;
00099                 state = LTPRS_WAITNAME;
00100             }
00101             else if (!ISALNUM(ptr))
00102                 UNCHAR;
00103         }
00104         else
00105             /* internal error */
00106             elog(ERROR, "internal error in parser");
00107 
00108         ptr += charlen;
00109         lptr->wlen++;
00110         pos++;
00111     }
00112 
00113     if (state == LTPRS_WAITDELIM)
00114     {
00115         lptr->len = ptr - lptr->start;
00116         if (lptr->wlen > 255)
00117             ereport(ERROR,
00118                     (errcode(ERRCODE_NAME_TOO_LONG),
00119                      errmsg("name of level is too long"),
00120                      errdetail("Name length is %d, must "
00121                                "be < 256, in position %d.",
00122                                lptr->wlen, pos)));
00123 
00124         totallen += MAXALIGN(lptr->len + LEVEL_HDRSIZE);
00125         lptr++;
00126     }
00127     else if (!(state == LTPRS_WAITNAME && lptr == list))
00128         ereport(ERROR,
00129                 (errcode(ERRCODE_SYNTAX_ERROR),
00130                  errmsg("syntax error"),
00131                  errdetail("Unexpected end of line.")));
00132 
00133     result = (ltree *) palloc0(LTREE_HDRSIZE + totallen);
00134     SET_VARSIZE(result, LTREE_HDRSIZE + totallen);
00135     result->numlevel = lptr - list;
00136     curlevel = LTREE_FIRST(result);
00137     lptr = list;
00138     while (lptr - list < result->numlevel)
00139     {
00140         curlevel->len = (uint16) lptr->len;
00141         memcpy(curlevel->name, lptr->start, lptr->len);
00142         curlevel = LEVEL_NEXT(curlevel);
00143         lptr++;
00144     }
00145 
00146     pfree(list);
00147     PG_RETURN_POINTER(result);
00148 }
00149 
00150 Datum
00151 ltree_out(PG_FUNCTION_ARGS)
00152 {
00153     ltree      *in = PG_GETARG_LTREE(0);
00154     char       *buf,
00155                *ptr;
00156     int         i;
00157     ltree_level *curlevel;
00158 
00159     ptr = buf = (char *) palloc(VARSIZE(in));
00160     curlevel = LTREE_FIRST(in);
00161     for (i = 0; i < in->numlevel; i++)
00162     {
00163         if (i != 0)
00164         {
00165             *ptr = '.';
00166             ptr++;
00167         }
00168         memcpy(ptr, curlevel->name, curlevel->len);
00169         ptr += curlevel->len;
00170         curlevel = LEVEL_NEXT(curlevel);
00171     }
00172 
00173     *ptr = '\0';
00174     PG_FREE_IF_COPY(in, 0);
00175 
00176     PG_RETURN_POINTER(buf);
00177 }
00178 
00179 #define LQPRS_WAITLEVEL 0
00180 #define LQPRS_WAITDELIM 1
00181 #define LQPRS_WAITOPEN  2
00182 #define LQPRS_WAITFNUM  3
00183 #define LQPRS_WAITSNUM  4
00184 #define LQPRS_WAITND    5
00185 #define LQPRS_WAITCLOSE 6
00186 #define LQPRS_WAITEND   7
00187 #define LQPRS_WAITVAR   8
00188 
00189 
00190 #define GETVAR(x) ( *((nodeitem**)LQL_FIRST(x)) )
00191 #define ITEMSIZE    MAXALIGN(LQL_HDRSIZE+sizeof(nodeitem*))
00192 #define NEXTLEV(x) ( (lquery_level*)( ((char*)(x)) + ITEMSIZE) )
00193 
00194 Datum
00195 lquery_in(PG_FUNCTION_ARGS)
00196 {
00197     char       *buf = (char *) PG_GETARG_POINTER(0);
00198     char       *ptr;
00199     int         num = 0,
00200                 totallen = 0,
00201                 numOR = 0;
00202     int         state = LQPRS_WAITLEVEL;
00203     lquery     *result;
00204     nodeitem   *lptr = NULL;
00205     lquery_level *cur,
00206                *curqlevel,
00207                *tmpql;
00208     lquery_variant *lrptr = NULL;
00209     bool        hasnot = false;
00210     bool        wasbad = false;
00211     int         charlen;
00212     int         pos = 0;
00213 
00214     ptr = buf;
00215     while (*ptr)
00216     {
00217         charlen = pg_mblen(ptr);
00218 
00219         if (charlen == 1)
00220         {
00221             if (t_iseq(ptr, '.'))
00222                 num++;
00223             else if (t_iseq(ptr, '|'))
00224                 numOR++;
00225         }
00226 
00227         ptr += charlen;
00228     }
00229 
00230     num++;
00231     curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num);
00232     ptr = buf;
00233     while (*ptr)
00234     {
00235         charlen = pg_mblen(ptr);
00236 
00237         if (state == LQPRS_WAITLEVEL)
00238         {
00239             if (ISALNUM(ptr))
00240             {
00241                 GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
00242                 lptr->start = ptr;
00243                 state = LQPRS_WAITDELIM;
00244                 curqlevel->numvar = 1;
00245             }
00246             else if (charlen == 1 && t_iseq(ptr, '!'))
00247             {
00248                 GETVAR(curqlevel) = lptr = (nodeitem *) palloc0(sizeof(nodeitem) * (numOR + 1));
00249                 lptr->start = ptr + 1;
00250                 state = LQPRS_WAITDELIM;
00251                 curqlevel->numvar = 1;
00252                 curqlevel->flag |= LQL_NOT;
00253                 hasnot = true;
00254             }
00255             else if (charlen == 1 && t_iseq(ptr, '*'))
00256                 state = LQPRS_WAITOPEN;
00257             else
00258                 UNCHAR;
00259         }
00260         else if (state == LQPRS_WAITVAR)
00261         {
00262             if (ISALNUM(ptr))
00263             {
00264                 lptr++;
00265                 lptr->start = ptr;
00266                 state = LQPRS_WAITDELIM;
00267                 curqlevel->numvar++;
00268             }
00269             else
00270                 UNCHAR;
00271         }
00272         else if (state == LQPRS_WAITDELIM)
00273         {
00274             if (charlen == 1 && t_iseq(ptr, '@'))
00275             {
00276                 if (lptr->start == ptr)
00277                     UNCHAR;
00278                 lptr->flag |= LVAR_INCASE;
00279                 curqlevel->flag |= LVAR_INCASE;
00280             }
00281             else if (charlen == 1 && t_iseq(ptr, '*'))
00282             {
00283                 if (lptr->start == ptr)
00284                     UNCHAR;
00285                 lptr->flag |= LVAR_ANYEND;
00286                 curqlevel->flag |= LVAR_ANYEND;
00287             }
00288             else if (charlen == 1 && t_iseq(ptr, '%'))
00289             {
00290                 if (lptr->start == ptr)
00291                     UNCHAR;
00292                 lptr->flag |= LVAR_SUBLEXEME;
00293                 curqlevel->flag |= LVAR_SUBLEXEME;
00294             }
00295             else if (charlen == 1 && t_iseq(ptr, '|'))
00296             {
00297                 lptr->len = ptr - lptr->start -
00298                     ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
00299                     ((lptr->flag & LVAR_INCASE) ? 1 : 0) -
00300                     ((lptr->flag & LVAR_ANYEND) ? 1 : 0);
00301                 if (lptr->wlen > 255)
00302                     ereport(ERROR,
00303                             (errcode(ERRCODE_NAME_TOO_LONG),
00304                              errmsg("name of level is too long"),
00305                              errdetail("Name length is %d, must "
00306                                        "be < 256, in position %d.",
00307                                        lptr->wlen, pos)));
00308 
00309                 state = LQPRS_WAITVAR;
00310             }
00311             else if (charlen == 1 && t_iseq(ptr, '.'))
00312             {
00313                 lptr->len = ptr - lptr->start -
00314                     ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
00315                     ((lptr->flag & LVAR_INCASE) ? 1 : 0) -
00316                     ((lptr->flag & LVAR_ANYEND) ? 1 : 0);
00317                 if (lptr->wlen > 255)
00318                     ereport(ERROR,
00319                             (errcode(ERRCODE_NAME_TOO_LONG),
00320                              errmsg("name of level is too long"),
00321                              errdetail("Name length is %d, must "
00322                                        "be < 256, in position %d.",
00323                                        lptr->wlen, pos)));
00324 
00325                 state = LQPRS_WAITLEVEL;
00326                 curqlevel = NEXTLEV(curqlevel);
00327             }
00328             else if (ISALNUM(ptr))
00329             {
00330                 if (lptr->flag)
00331                     UNCHAR;
00332             }
00333             else
00334                 UNCHAR;
00335         }
00336         else if (state == LQPRS_WAITOPEN)
00337         {
00338             if (charlen == 1 && t_iseq(ptr, '{'))
00339                 state = LQPRS_WAITFNUM;
00340             else if (charlen == 1 && t_iseq(ptr, '.'))
00341             {
00342                 curqlevel->low = 0;
00343                 curqlevel->high = 0xffff;
00344                 curqlevel = NEXTLEV(curqlevel);
00345                 state = LQPRS_WAITLEVEL;
00346             }
00347             else
00348                 UNCHAR;
00349         }
00350         else if (state == LQPRS_WAITFNUM)
00351         {
00352             if (charlen == 1 && t_iseq(ptr, ','))
00353                 state = LQPRS_WAITSNUM;
00354             else if (t_isdigit(ptr))
00355             {
00356                 curqlevel->low = atoi(ptr);
00357                 state = LQPRS_WAITND;
00358             }
00359             else
00360                 UNCHAR;
00361         }
00362         else if (state == LQPRS_WAITSNUM)
00363         {
00364             if (t_isdigit(ptr))
00365             {
00366                 curqlevel->high = atoi(ptr);
00367                 state = LQPRS_WAITCLOSE;
00368             }
00369             else if (charlen == 1 && t_iseq(ptr, '}'))
00370             {
00371                 curqlevel->high = 0xffff;
00372                 state = LQPRS_WAITEND;
00373             }
00374             else
00375                 UNCHAR;
00376         }
00377         else if (state == LQPRS_WAITCLOSE)
00378         {
00379             if (charlen == 1 && t_iseq(ptr, '}'))
00380                 state = LQPRS_WAITEND;
00381             else if (!t_isdigit(ptr))
00382                 UNCHAR;
00383         }
00384         else if (state == LQPRS_WAITND)
00385         {
00386             if (charlen == 1 && t_iseq(ptr, '}'))
00387             {
00388                 curqlevel->high = curqlevel->low;
00389                 state = LQPRS_WAITEND;
00390             }
00391             else if (charlen == 1 && t_iseq(ptr, ','))
00392                 state = LQPRS_WAITSNUM;
00393             else if (!t_isdigit(ptr))
00394                 UNCHAR;
00395         }
00396         else if (state == LQPRS_WAITEND)
00397         {
00398             if (charlen == 1 && t_iseq(ptr, '.'))
00399             {
00400                 state = LQPRS_WAITLEVEL;
00401                 curqlevel = NEXTLEV(curqlevel);
00402             }
00403             else
00404                 UNCHAR;
00405         }
00406         else
00407             /* internal error */
00408             elog(ERROR, "internal error in parser");
00409 
00410         ptr += charlen;
00411         if (state == LQPRS_WAITDELIM)
00412             lptr->wlen++;
00413         pos++;
00414     }
00415 
00416     if (state == LQPRS_WAITDELIM)
00417     {
00418         if (lptr->start == ptr)
00419             ereport(ERROR,
00420                     (errcode(ERRCODE_SYNTAX_ERROR),
00421                      errmsg("syntax error"),
00422                      errdetail("Unexpected end of line.")));
00423 
00424         lptr->len = ptr - lptr->start -
00425             ((lptr->flag & LVAR_SUBLEXEME) ? 1 : 0) -
00426             ((lptr->flag & LVAR_INCASE) ? 1 : 0) -
00427             ((lptr->flag & LVAR_ANYEND) ? 1 : 0);
00428         if (lptr->len == 0)
00429             ereport(ERROR,
00430                     (errcode(ERRCODE_SYNTAX_ERROR),
00431                      errmsg("syntax error"),
00432                      errdetail("Unexpected end of line.")));
00433 
00434         if (lptr->wlen > 255)
00435             ereport(ERROR,
00436                     (errcode(ERRCODE_NAME_TOO_LONG),
00437                      errmsg("name of level is too long"),
00438                      errdetail("Name length is %d, must "
00439                                "be < 256, in position %d.",
00440                                lptr->wlen, pos)));
00441     }
00442     else if (state == LQPRS_WAITOPEN)
00443         curqlevel->high = 0xffff;
00444     else if (state != LQPRS_WAITEND)
00445         ereport(ERROR,
00446                 (errcode(ERRCODE_SYNTAX_ERROR),
00447                  errmsg("syntax error"),
00448                  errdetail("Unexpected end of line.")));
00449 
00450     curqlevel = tmpql;
00451     totallen = LQUERY_HDRSIZE;
00452     while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
00453     {
00454         totallen += LQL_HDRSIZE;
00455         if (curqlevel->numvar)
00456         {
00457             lptr = GETVAR(curqlevel);
00458             while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
00459             {
00460                 totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
00461                 lptr++;
00462             }
00463         }
00464         else if (curqlevel->low > curqlevel->high)
00465             ereport(ERROR,
00466                     (errcode(ERRCODE_SYNTAX_ERROR),
00467                      errmsg("syntax error"),
00468                      errdetail("Low limit(%d) is greater than upper(%d).",
00469                                curqlevel->low, curqlevel->high)));
00470 
00471         curqlevel = NEXTLEV(curqlevel);
00472     }
00473 
00474     result = (lquery *) palloc0(totallen);
00475     SET_VARSIZE(result, totallen);
00476     result->numlevel = num;
00477     result->firstgood = 0;
00478     result->flag = 0;
00479     if (hasnot)
00480         result->flag |= LQUERY_HASNOT;
00481     cur = LQUERY_FIRST(result);
00482     curqlevel = tmpql;
00483     while ((char *) curqlevel - (char *) tmpql < num * ITEMSIZE)
00484     {
00485         memcpy(cur, curqlevel, LQL_HDRSIZE);
00486         cur->totallen = LQL_HDRSIZE;
00487         if (curqlevel->numvar)
00488         {
00489             lrptr = LQL_FIRST(cur);
00490             lptr = GETVAR(curqlevel);
00491             while (lptr - GETVAR(curqlevel) < curqlevel->numvar)
00492             {
00493                 cur->totallen += MAXALIGN(LVAR_HDRSIZE + lptr->len);
00494                 lrptr->len = lptr->len;
00495                 lrptr->flag = lptr->flag;
00496                 lrptr->val = ltree_crc32_sz(lptr->start, lptr->len);
00497                 memcpy(lrptr->name, lptr->start, lptr->len);
00498                 lptr++;
00499                 lrptr = LVAR_NEXT(lrptr);
00500             }
00501             pfree(GETVAR(curqlevel));
00502             if (cur->numvar > 1 || cur->flag != 0)
00503                 wasbad = true;
00504             else if (wasbad == false)
00505                 (result->firstgood)++;
00506         }
00507         else
00508             wasbad = true;
00509         curqlevel = NEXTLEV(curqlevel);
00510         cur = LQL_NEXT(cur);
00511     }
00512 
00513     pfree(tmpql);
00514     PG_RETURN_POINTER(result);
00515 }
00516 
00517 Datum
00518 lquery_out(PG_FUNCTION_ARGS)
00519 {
00520     lquery     *in = PG_GETARG_LQUERY(0);
00521     char       *buf,
00522                *ptr;
00523     int         i,
00524                 j,
00525                 totallen = 1;
00526     lquery_level *curqlevel;
00527     lquery_variant *curtlevel;
00528 
00529     curqlevel = LQUERY_FIRST(in);
00530     for (i = 0; i < in->numlevel; i++)
00531     {
00532         totallen++;
00533         if (curqlevel->numvar)
00534             totallen += 1 + (curqlevel->numvar * 4) + curqlevel->totallen;
00535         else
00536             totallen += 2 * 11 + 4;
00537         curqlevel = LQL_NEXT(curqlevel);
00538     }
00539 
00540     ptr = buf = (char *) palloc(totallen);
00541     curqlevel = LQUERY_FIRST(in);
00542     for (i = 0; i < in->numlevel; i++)
00543     {
00544         if (i != 0)
00545         {
00546             *ptr = '.';
00547             ptr++;
00548         }
00549         if (curqlevel->numvar)
00550         {
00551             if (curqlevel->flag & LQL_NOT)
00552             {
00553                 *ptr = '!';
00554                 ptr++;
00555             }
00556             curtlevel = LQL_FIRST(curqlevel);
00557             for (j = 0; j < curqlevel->numvar; j++)
00558             {
00559                 if (j != 0)
00560                 {
00561                     *ptr = '|';
00562                     ptr++;
00563                 }
00564                 memcpy(ptr, curtlevel->name, curtlevel->len);
00565                 ptr += curtlevel->len;
00566                 if ((curtlevel->flag & LVAR_SUBLEXEME))
00567                 {
00568                     *ptr = '%';
00569                     ptr++;
00570                 }
00571                 if ((curtlevel->flag & LVAR_INCASE))
00572                 {
00573                     *ptr = '@';
00574                     ptr++;
00575                 }
00576                 if ((curtlevel->flag & LVAR_ANYEND))
00577                 {
00578                     *ptr = '*';
00579                     ptr++;
00580                 }
00581                 curtlevel = LVAR_NEXT(curtlevel);
00582             }
00583         }
00584         else
00585         {
00586             if (curqlevel->low == curqlevel->high)
00587             {
00588                 sprintf(ptr, "*{%d}", curqlevel->low);
00589             }
00590             else if (curqlevel->low == 0)
00591             {
00592                 if (curqlevel->high == 0xffff)
00593                 {
00594                     *ptr = '*';
00595                     *(ptr + 1) = '\0';
00596                 }
00597                 else
00598                     sprintf(ptr, "*{,%d}", curqlevel->high);
00599             }
00600             else if (curqlevel->high == 0xffff)
00601             {
00602                 sprintf(ptr, "*{%d,}", curqlevel->low);
00603             }
00604             else
00605                 sprintf(ptr, "*{%d,%d}", curqlevel->low, curqlevel->high);
00606             ptr = strchr(ptr, '\0');
00607         }
00608 
00609         curqlevel = LQL_NEXT(curqlevel);
00610     }
00611 
00612     *ptr = '\0';
00613     PG_FREE_IF_COPY(in, 0);
00614 
00615     PG_RETURN_POINTER(buf);
00616 }