LCOV - code coverage report
Current view: top level - src/backend/tsearch - dict_thesaurus.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 330 380 86.8 %
Date: 2017-09-29 15:12:54 Functions: 17 17 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * dict_thesaurus.c
       4             :  *      Thesaurus dictionary: phrase to phrase substitution
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/tsearch/dict_thesaurus.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "catalog/namespace.h"
      17             : #include "commands/defrem.h"
      18             : #include "tsearch/ts_cache.h"
      19             : #include "tsearch/ts_locale.h"
      20             : #include "tsearch/ts_utils.h"
      21             : #include "utils/builtins.h"
      22             : #include "utils/regproc.h"
      23             : 
      24             : 
      25             : /*
      26             :  * Temporary we use TSLexeme.flags for inner use...
      27             :  */
      28             : #define DT_USEASIS      0x1000
      29             : 
      30             : typedef struct LexemeInfo
      31             : {
      32             :     uint32      idsubst;        /* entry's number in DictThesaurus->subst */
      33             :     uint16      posinsubst;     /* pos info in entry */
      34             :     uint16      tnvariant;      /* total num lexemes in one variant */
      35             :     struct LexemeInfo *nextentry;
      36             :     struct LexemeInfo *nextvariant;
      37             : } LexemeInfo;
      38             : 
      39             : typedef struct
      40             : {
      41             :     char       *lexeme;
      42             :     LexemeInfo *entries;
      43             : } TheLexeme;
      44             : 
      45             : typedef struct
      46             : {
      47             :     uint16      lastlexeme;     /* number lexemes to substitute */
      48             :     uint16      reslen;
      49             :     TSLexeme   *res;            /* prepared substituted result */
      50             : } TheSubstitute;
      51             : 
      52             : typedef struct
      53             : {
      54             :     /* subdictionary to normalize lexemes */
      55             :     Oid         subdictOid;
      56             :     TSDictionaryCacheEntry *subdict;
      57             : 
      58             :     /* Array to search lexeme by exact match */
      59             :     TheLexeme  *wrds;
      60             :     int         nwrds;          /* current number of words */
      61             :     int         ntwrds;         /* allocated array length */
      62             : 
      63             :     /*
      64             :      * Storage of substituted result, n-th element is for n-th expression
      65             :      */
      66             :     TheSubstitute *subst;
      67             :     int         nsubst;
      68             : } DictThesaurus;
      69             : 
      70             : 
      71             : static void
      72          30 : newLexeme(DictThesaurus *d, char *b, char *e, uint32 idsubst, uint16 posinsubst)
      73             : {
      74             :     TheLexeme  *ptr;
      75             : 
      76          30 :     if (d->nwrds >= d->ntwrds)
      77             :     {
      78           2 :         if (d->ntwrds == 0)
      79             :         {
      80           2 :             d->ntwrds = 16;
      81           2 :             d->wrds = (TheLexeme *) palloc(sizeof(TheLexeme) * d->ntwrds);
      82             :         }
      83             :         else
      84             :         {
      85           0 :             d->ntwrds *= 2;
      86           0 :             d->wrds = (TheLexeme *) repalloc(d->wrds, sizeof(TheLexeme) * d->ntwrds);
      87             :         }
      88             :     }
      89             : 
      90          30 :     ptr = d->wrds + d->nwrds;
      91          30 :     d->nwrds++;
      92             : 
      93          30 :     ptr->lexeme = palloc(e - b + 1);
      94             : 
      95          30 :     memcpy(ptr->lexeme, b, e - b);
      96          30 :     ptr->lexeme[e - b] = '\0';
      97             : 
      98          30 :     ptr->entries = (LexemeInfo *) palloc(sizeof(LexemeInfo));
      99             : 
     100          30 :     ptr->entries->nextentry = NULL;
     101          30 :     ptr->entries->idsubst = idsubst;
     102          30 :     ptr->entries->posinsubst = posinsubst;
     103          30 : }
     104             : 
     105             : static void
     106          24 : addWrd(DictThesaurus *d, char *b, char *e, uint32 idsubst, uint16 nwrd, uint16 posinsubst, bool useasis)
     107             : {
     108             :     static int  nres = 0;
     109             :     static int  ntres = 0;
     110             :     TheSubstitute *ptr;
     111             : 
     112          24 :     if (nwrd == 0)
     113             :     {
     114          16 :         nres = ntres = 0;
     115             : 
     116          16 :         if (idsubst >= d->nsubst)
     117             :         {
     118           2 :             if (d->nsubst == 0)
     119             :             {
     120           2 :                 d->nsubst = 16;
     121           2 :                 d->subst = (TheSubstitute *) palloc(sizeof(TheSubstitute) * d->nsubst);
     122             :             }
     123             :             else
     124             :             {
     125           0 :                 d->nsubst *= 2;
     126           0 :                 d->subst = (TheSubstitute *) repalloc(d->subst, sizeof(TheSubstitute) * d->nsubst);
     127             :             }
     128             :         }
     129             :     }
     130             : 
     131          24 :     ptr = d->subst + idsubst;
     132             : 
     133          24 :     ptr->lastlexeme = posinsubst - 1;
     134             : 
     135          24 :     if (nres + 1 >= ntres)
     136             :     {
     137          20 :         if (ntres == 0)
     138             :         {
     139          16 :             ntres = 2;
     140          16 :             ptr->res = (TSLexeme *) palloc(sizeof(TSLexeme) * ntres);
     141             :         }
     142             :         else
     143             :         {
     144           4 :             ntres *= 2;
     145           4 :             ptr->res = (TSLexeme *) repalloc(ptr->res, sizeof(TSLexeme) * ntres);
     146             :         }
     147             :     }
     148             : 
     149          24 :     ptr->res[nres].lexeme = palloc(e - b + 1);
     150          24 :     memcpy(ptr->res[nres].lexeme, b, e - b);
     151          24 :     ptr->res[nres].lexeme[e - b] = '\0';
     152             : 
     153          24 :     ptr->res[nres].nvariant = nwrd;
     154          24 :     if (useasis)
     155          12 :         ptr->res[nres].flags = DT_USEASIS;
     156             :     else
     157          12 :         ptr->res[nres].flags = 0;
     158             : 
     159          24 :     ptr->res[++nres].lexeme = NULL;
     160          24 : }
     161             : 
     162             : #define TR_WAITLEX  1
     163             : #define TR_INLEX    2
     164             : #define TR_WAITSUBS 3
     165             : #define TR_INSUBS   4
     166             : 
     167             : static void
     168           2 : thesaurusRead(char *filename, DictThesaurus *d)
     169             : {
     170             :     tsearch_readline_state trst;
     171           2 :     uint32      idsubst = 0;
     172           2 :     bool        useasis = false;
     173             :     char       *line;
     174             : 
     175           2 :     filename = get_tsearch_config_filename(filename, "ths");
     176           2 :     if (!tsearch_readline_begin(&trst, filename))
     177           0 :         ereport(ERROR,
     178             :                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
     179             :                  errmsg("could not open thesaurus file \"%s\": %m",
     180             :                         filename)));
     181             : 
     182          38 :     while ((line = tsearch_readline(&trst)) != NULL)
     183             :     {
     184             :         char       *ptr;
     185          34 :         int         state = TR_WAITLEX;
     186          34 :         char       *beginwrd = NULL;
     187          34 :         uint32      posinsubst = 0;
     188          34 :         uint32      nwrd = 0;
     189             : 
     190          34 :         ptr = line;
     191             : 
     192             :         /* is it a comment? */
     193          72 :         while (*ptr && t_isspace(ptr))
     194           4 :             ptr += pg_mblen(ptr);
     195             : 
     196          50 :         if (t_iseq(ptr, '#') || *ptr == '\0' ||
     197          32 :             t_iseq(ptr, '\n') || t_iseq(ptr, '\r'))
     198             :         {
     199          18 :             pfree(line);
     200          18 :             continue;
     201             :         }
     202             : 
     203         386 :         while (*ptr)
     204             :         {
     205         354 :             if (state == TR_WAITLEX)
     206             :             {
     207          46 :                 if (t_iseq(ptr, ':'))
     208             :                 {
     209          16 :                     if (posinsubst == 0)
     210           0 :                         ereport(ERROR,
     211             :                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
     212             :                                  errmsg("unexpected delimiter")));
     213          16 :                     state = TR_WAITSUBS;
     214             :                 }
     215          30 :                 else if (!t_isspace(ptr))
     216             :                 {
     217          30 :                     beginwrd = ptr;
     218          30 :                     state = TR_INLEX;
     219             :                 }
     220             :             }
     221         308 :             else if (state == TR_INLEX)
     222             :             {
     223         154 :                 if (t_iseq(ptr, ':'))
     224             :                 {
     225           0 :                     newLexeme(d, beginwrd, ptr, idsubst, posinsubst++);
     226           0 :                     state = TR_WAITSUBS;
     227             :                 }
     228         154 :                 else if (t_isspace(ptr))
     229             :                 {
     230          30 :                     newLexeme(d, beginwrd, ptr, idsubst, posinsubst++);
     231          30 :                     state = TR_WAITLEX;
     232             :                 }
     233             :             }
     234         154 :             else if (state == TR_WAITSUBS)
     235             :             {
     236          40 :                 if (t_iseq(ptr, '*'))
     237             :                 {
     238          12 :                     useasis = true;
     239          12 :                     state = TR_INSUBS;
     240          12 :                     beginwrd = ptr + pg_mblen(ptr);
     241             :                 }
     242          28 :                 else if (t_iseq(ptr, '\\'))
     243             :                 {
     244           0 :                     useasis = false;
     245           0 :                     state = TR_INSUBS;
     246           0 :                     beginwrd = ptr + pg_mblen(ptr);
     247             :                 }
     248          28 :                 else if (!t_isspace(ptr))
     249             :                 {
     250          12 :                     useasis = false;
     251          12 :                     beginwrd = ptr;
     252          12 :                     state = TR_INSUBS;
     253             :                 }
     254             :             }
     255         114 :             else if (state == TR_INSUBS)
     256             :             {
     257         114 :                 if (t_isspace(ptr))
     258             :                 {
     259          24 :                     if (ptr == beginwrd)
     260           0 :                         ereport(ERROR,
     261             :                                 (errcode(ERRCODE_CONFIG_FILE_ERROR),
     262             :                                  errmsg("unexpected end of line or lexeme")));
     263          24 :                     addWrd(d, beginwrd, ptr, idsubst, nwrd++, posinsubst, useasis);
     264          24 :                     state = TR_WAITSUBS;
     265             :                 }
     266             :             }
     267             :             else
     268           0 :                 elog(ERROR, "unrecognized thesaurus state: %d", state);
     269             : 
     270         354 :             ptr += pg_mblen(ptr);
     271             :         }
     272             : 
     273          16 :         if (state == TR_INSUBS)
     274             :         {
     275           0 :             if (ptr == beginwrd)
     276           0 :                 ereport(ERROR,
     277             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
     278             :                          errmsg("unexpected end of line or lexeme")));
     279           0 :             addWrd(d, beginwrd, ptr, idsubst, nwrd++, posinsubst, useasis);
     280             :         }
     281             : 
     282          16 :         idsubst++;
     283             : 
     284          16 :         if (!(nwrd && posinsubst))
     285           0 :             ereport(ERROR,
     286             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     287             :                      errmsg("unexpected end of line")));
     288             : 
     289             :         /*
     290             :          * Note: currently, tsearch_readline can't return lines exceeding 4KB,
     291             :          * so overflow of the word counts is impossible.  But that may not
     292             :          * always be true, so let's check.
     293             :          */
     294          16 :         if (nwrd != (uint16) nwrd || posinsubst != (uint16) posinsubst)
     295           0 :             ereport(ERROR,
     296             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     297             :                      errmsg("too many lexemes in thesaurus entry")));
     298             : 
     299          16 :         pfree(line);
     300             :     }
     301             : 
     302           2 :     d->nsubst = idsubst;
     303             : 
     304           2 :     tsearch_readline_end(&trst);
     305           2 : }
     306             : 
     307             : static TheLexeme *
     308          30 : addCompiledLexeme(TheLexeme *newwrds, int *nnw, int *tnm, TSLexeme *lexeme, LexemeInfo *src, uint16 tnvariant)
     309             : {
     310          30 :     if (*nnw >= *tnm)
     311             :     {
     312           0 :         *tnm *= 2;
     313           0 :         newwrds = (TheLexeme *) repalloc(newwrds, sizeof(TheLexeme) * *tnm);
     314             :     }
     315             : 
     316          30 :     newwrds[*nnw].entries = (LexemeInfo *) palloc(sizeof(LexemeInfo));
     317             : 
     318          30 :     if (lexeme && lexeme->lexeme)
     319             :     {
     320          28 :         newwrds[*nnw].lexeme = pstrdup(lexeme->lexeme);
     321          28 :         newwrds[*nnw].entries->tnvariant = tnvariant;
     322             :     }
     323             :     else
     324             :     {
     325           2 :         newwrds[*nnw].lexeme = NULL;
     326           2 :         newwrds[*nnw].entries->tnvariant = 1;
     327             :     }
     328             : 
     329          30 :     newwrds[*nnw].entries->idsubst = src->idsubst;
     330          30 :     newwrds[*nnw].entries->posinsubst = src->posinsubst;
     331             : 
     332          30 :     newwrds[*nnw].entries->nextentry = NULL;
     333             : 
     334          30 :     (*nnw)++;
     335          30 :     return newwrds;
     336             : }
     337             : 
     338             : static int
     339          32 : cmpLexemeInfo(LexemeInfo *a, LexemeInfo *b)
     340             : {
     341          32 :     if (a == NULL || b == NULL)
     342           0 :         return 0;
     343             : 
     344          32 :     if (a->idsubst == b->idsubst)
     345             :     {
     346           0 :         if (a->posinsubst == b->posinsubst)
     347             :         {
     348           0 :             if (a->tnvariant == b->tnvariant)
     349           0 :                 return 0;
     350             : 
     351           0 :             return (a->tnvariant > b->tnvariant) ? 1 : -1;
     352             :         }
     353             : 
     354           0 :         return (a->posinsubst > b->posinsubst) ? 1 : -1;
     355             :     }
     356             : 
     357          32 :     return (a->idsubst > b->idsubst) ? 1 : -1;
     358             : }
     359             : 
     360             : static int
     361         220 : cmpLexeme(const TheLexeme *a, const TheLexeme *b)
     362             : {
     363         220 :     if (a->lexeme == NULL)
     364             :     {
     365          26 :         if (b->lexeme == NULL)
     366           6 :             return 0;
     367             :         else
     368          20 :             return 1;
     369             :     }
     370         194 :     else if (b->lexeme == NULL)
     371           3 :         return -1;
     372             : 
     373         191 :     return strcmp(a->lexeme, b->lexeme);
     374             : }
     375             : 
     376             : static int
     377          98 : cmpLexemeQ(const void *a, const void *b)
     378             : {
     379          98 :     return cmpLexeme((const TheLexeme *) a, (const TheLexeme *) b);
     380             : }
     381             : 
     382             : static int
     383          94 : cmpTheLexeme(const void *a, const void *b)
     384             : {
     385          94 :     const TheLexeme *la = (const TheLexeme *) a;
     386          94 :     const TheLexeme *lb = (const TheLexeme *) b;
     387             :     int         res;
     388             : 
     389          94 :     if ((res = cmpLexeme(la, lb)) != 0)
     390          76 :         return res;
     391             : 
     392          18 :     return -cmpLexemeInfo(la->entries, lb->entries);
     393             : }
     394             : 
     395             : static void
     396           2 : compileTheLexeme(DictThesaurus *d)
     397             : {
     398             :     int         i,
     399           2 :                 nnw = 0,
     400           2 :                 tnm = 16;
     401           2 :     TheLexeme  *newwrds = (TheLexeme *) palloc(sizeof(TheLexeme) * tnm),
     402             :                *ptrwrds;
     403             : 
     404          32 :     for (i = 0; i < d->nwrds; i++)
     405             :     {
     406             :         TSLexeme   *ptr;
     407             : 
     408          30 :         if (strcmp(d->wrds[i].lexeme, "?") == 0)   /* Is stop word marker? */
     409           2 :             newwrds = addCompiledLexeme(newwrds, &nnw, &tnm, NULL, d->wrds[i].entries, 0);
     410             :         else
     411             :         {
     412          28 :             ptr = (TSLexeme *) DatumGetPointer(FunctionCall4(&(d->subdict->lexize),
     413             :                                                              PointerGetDatum(d->subdict->dictData),
     414             :                                                              PointerGetDatum(d->wrds[i].lexeme),
     415             :                                                              Int32GetDatum(strlen(d->wrds[i].lexeme)),
     416             :                                                              PointerGetDatum(NULL)));
     417             : 
     418          28 :             if (!ptr)
     419           0 :                 ereport(ERROR,
     420             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
     421             :                          errmsg("thesaurus sample word \"%s\" isn't recognized by subdictionary (rule %d)",
     422             :                                 d->wrds[i].lexeme,
     423             :                                 d->wrds[i].entries->idsubst + 1)));
     424          28 :             else if (!(ptr->lexeme))
     425           0 :                 ereport(ERROR,
     426             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
     427             :                          errmsg("thesaurus sample word \"%s\" is a stop word (rule %d)",
     428             :                                 d->wrds[i].lexeme,
     429             :                                 d->wrds[i].entries->idsubst + 1),
     430             :                          errhint("Use \"?\" to represent a stop word within a sample phrase.")));
     431             :             else
     432             :             {
     433          84 :                 while (ptr->lexeme)
     434             :                 {
     435          28 :                     TSLexeme   *remptr = ptr + 1;
     436          28 :                     int         tnvar = 1;
     437          28 :                     int         curvar = ptr->nvariant;
     438             : 
     439             :                     /* compute n words in one variant */
     440          56 :                     while (remptr->lexeme)
     441             :                     {
     442           0 :                         if (remptr->nvariant != (remptr - 1)->nvariant)
     443           0 :                             break;
     444           0 :                         tnvar++;
     445           0 :                         remptr++;
     446             :                     }
     447             : 
     448          28 :                     remptr = ptr;
     449          84 :                     while (remptr->lexeme && remptr->nvariant == curvar)
     450             :                     {
     451          28 :                         newwrds = addCompiledLexeme(newwrds, &nnw, &tnm, remptr, d->wrds[i].entries, tnvar);
     452          28 :                         remptr++;
     453             :                     }
     454             : 
     455          28 :                     ptr = remptr;
     456             :                 }
     457             :             }
     458             :         }
     459             : 
     460          30 :         pfree(d->wrds[i].lexeme);
     461          30 :         pfree(d->wrds[i].entries);
     462             :     }
     463             : 
     464           2 :     if (d->wrds)
     465           2 :         pfree(d->wrds);
     466           2 :     d->wrds = newwrds;
     467           2 :     d->nwrds = nnw;
     468           2 :     d->ntwrds = tnm;
     469             : 
     470           2 :     if (d->nwrds > 1)
     471             :     {
     472           2 :         qsort(d->wrds, d->nwrds, sizeof(TheLexeme), cmpTheLexeme);
     473             : 
     474             :         /* uniq */
     475           2 :         newwrds = d->wrds;
     476           2 :         ptrwrds = d->wrds + 1;
     477          32 :         while (ptrwrds - d->wrds < d->nwrds)
     478             :         {
     479          28 :             if (cmpLexeme(ptrwrds, newwrds) == 0)
     480             :             {
     481          14 :                 if (cmpLexemeInfo(ptrwrds->entries, newwrds->entries))
     482             :                 {
     483          14 :                     ptrwrds->entries->nextentry = newwrds->entries;
     484          14 :                     newwrds->entries = ptrwrds->entries;
     485             :                 }
     486             :                 else
     487           0 :                     pfree(ptrwrds->entries);
     488             : 
     489          14 :                 if (ptrwrds->lexeme)
     490          14 :                     pfree(ptrwrds->lexeme);
     491             :             }
     492             :             else
     493             :             {
     494          14 :                 newwrds++;
     495          14 :                 *newwrds = *ptrwrds;
     496             :             }
     497             : 
     498          28 :             ptrwrds++;
     499             :         }
     500             : 
     501           2 :         d->nwrds = newwrds - d->wrds + 1;
     502           2 :         d->wrds = (TheLexeme *) repalloc(d->wrds, sizeof(TheLexeme) * d->nwrds);
     503             :     }
     504           2 : }
     505             : 
     506             : static void
     507           2 : compileTheSubstitute(DictThesaurus *d)
     508             : {
     509             :     int         i;
     510             : 
     511          18 :     for (i = 0; i < d->nsubst; i++)
     512             :     {
     513          16 :         TSLexeme   *rem = d->subst[i].res,
     514             :                    *outptr,
     515             :                    *inptr;
     516          16 :         int         n = 2;
     517             : 
     518          16 :         outptr = d->subst[i].res = (TSLexeme *) palloc(sizeof(TSLexeme) * n);
     519          16 :         outptr->lexeme = NULL;
     520          16 :         inptr = rem;
     521             : 
     522          56 :         while (inptr && inptr->lexeme)
     523             :         {
     524             :             TSLexeme   *lexized,
     525             :                         tmplex[2];
     526             : 
     527          24 :             if (inptr->flags & DT_USEASIS)
     528             :             {                   /* do not lexize */
     529          12 :                 tmplex[0] = *inptr;
     530          12 :                 tmplex[0].flags = 0;
     531          12 :                 tmplex[1].lexeme = NULL;
     532          12 :                 lexized = tmplex;
     533             :             }
     534             :             else
     535             :             {
     536          12 :                 lexized = (TSLexeme *) DatumGetPointer(
     537             :                                                        FunctionCall4(
     538             :                                                                      &(d->subdict->lexize),
     539             :                                                                      PointerGetDatum(d->subdict->dictData),
     540             :                                                                      PointerGetDatum(inptr->lexeme),
     541             :                                                                      Int32GetDatum(strlen(inptr->lexeme)),
     542             :                                                                      PointerGetDatum(NULL)
     543             :                                                                      )
     544             :                     );
     545             :             }
     546             : 
     547          24 :             if (lexized && lexized->lexeme)
     548          24 :             {
     549          24 :                 int         toset = (lexized->lexeme && outptr != d->subst[i].res) ? (outptr - d->subst[i].res) : -1;
     550             : 
     551          72 :                 while (lexized->lexeme)
     552             :                 {
     553          24 :                     if (outptr - d->subst[i].res + 1 >= n)
     554             :                     {
     555           4 :                         int         diff = outptr - d->subst[i].res;
     556             : 
     557           4 :                         n *= 2;
     558           4 :                         d->subst[i].res = (TSLexeme *) repalloc(d->subst[i].res, sizeof(TSLexeme) * n);
     559           4 :                         outptr = d->subst[i].res + diff;
     560             :                     }
     561             : 
     562          24 :                     *outptr = *lexized;
     563          24 :                     outptr->lexeme = pstrdup(lexized->lexeme);
     564             : 
     565          24 :                     outptr++;
     566          24 :                     lexized++;
     567             :                 }
     568             : 
     569          24 :                 if (toset > 0)
     570           8 :                     d->subst[i].res[toset].flags |= TSL_ADDPOS;
     571             :             }
     572           0 :             else if (lexized)
     573             :             {
     574           0 :                 ereport(ERROR,
     575             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
     576             :                          errmsg("thesaurus substitute word \"%s\" is a stop word (rule %d)",
     577             :                                 inptr->lexeme, i + 1)));
     578             :             }
     579             :             else
     580             :             {
     581           0 :                 ereport(ERROR,
     582             :                         (errcode(ERRCODE_CONFIG_FILE_ERROR),
     583             :                          errmsg("thesaurus substitute word \"%s\" isn't recognized by subdictionary (rule %d)",
     584             :                                 inptr->lexeme, i + 1)));
     585             :             }
     586             : 
     587          24 :             if (inptr->lexeme)
     588          24 :                 pfree(inptr->lexeme);
     589          24 :             inptr++;
     590             :         }
     591             : 
     592          16 :         if (outptr == d->subst[i].res)
     593           0 :             ereport(ERROR,
     594             :                     (errcode(ERRCODE_CONFIG_FILE_ERROR),
     595             :                      errmsg("thesaurus substitute phrase is empty (rule %d)",
     596             :                             i + 1)));
     597             : 
     598          16 :         d->subst[i].reslen = outptr - d->subst[i].res;
     599             : 
     600          16 :         pfree(rem);
     601             :     }
     602           2 : }
     603             : 
     604             : Datum
     605           2 : thesaurus_init(PG_FUNCTION_ARGS)
     606             : {
     607           2 :     List       *dictoptions = (List *) PG_GETARG_POINTER(0);
     608             :     DictThesaurus *d;
     609           2 :     char       *subdictname = NULL;
     610           2 :     bool        fileloaded = false;
     611             :     ListCell   *l;
     612             : 
     613           2 :     d = (DictThesaurus *) palloc0(sizeof(DictThesaurus));
     614             : 
     615           6 :     foreach(l, dictoptions)
     616             :     {
     617           4 :         DefElem    *defel = (DefElem *) lfirst(l);
     618             : 
     619           4 :         if (pg_strcasecmp("DictFile", defel->defname) == 0)
     620             :         {
     621           2 :             if (fileloaded)
     622           0 :                 ereport(ERROR,
     623             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     624             :                          errmsg("multiple DictFile parameters")));
     625           2 :             thesaurusRead(defGetString(defel), d);
     626           2 :             fileloaded = true;
     627             :         }
     628           2 :         else if (pg_strcasecmp("Dictionary", defel->defname) == 0)
     629             :         {
     630           2 :             if (subdictname)
     631           0 :                 ereport(ERROR,
     632             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     633             :                          errmsg("multiple Dictionary parameters")));
     634           2 :             subdictname = pstrdup(defGetString(defel));
     635             :         }
     636             :         else
     637             :         {
     638           0 :             ereport(ERROR,
     639             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     640             :                      errmsg("unrecognized Thesaurus parameter: \"%s\"",
     641             :                             defel->defname)));
     642             :         }
     643             :     }
     644             : 
     645           2 :     if (!fileloaded)
     646           0 :         ereport(ERROR,
     647             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     648             :                  errmsg("missing DictFile parameter")));
     649           2 :     if (!subdictname)
     650           0 :         ereport(ERROR,
     651             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     652             :                  errmsg("missing Dictionary parameter")));
     653             : 
     654           2 :     d->subdictOid = get_ts_dict_oid(stringToQualifiedNameList(subdictname), false);
     655           2 :     d->subdict = lookup_ts_dictionary_cache(d->subdictOid);
     656             : 
     657           2 :     compileTheLexeme(d);
     658           2 :     compileTheSubstitute(d);
     659             : 
     660           2 :     PG_RETURN_POINTER(d);
     661             : }
     662             : 
     663             : static LexemeInfo *
     664          32 : findTheLexeme(DictThesaurus *d, char *lexeme)
     665             : {
     666             :     TheLexeme   key,
     667             :                *res;
     668             : 
     669          32 :     if (d->nwrds == 0)
     670           0 :         return NULL;
     671             : 
     672          32 :     key.lexeme = lexeme;
     673          32 :     key.entries = NULL;
     674             : 
     675          32 :     res = bsearch(&key, d->wrds, d->nwrds, sizeof(TheLexeme), cmpLexemeQ);
     676             : 
     677          32 :     if (res == NULL)
     678           9 :         return NULL;
     679          23 :     return res->entries;
     680             : }
     681             : 
     682             : static bool
     683          48 : matchIdSubst(LexemeInfo *stored, uint32 idsubst)
     684             : {
     685          48 :     bool        res = true;
     686             : 
     687          48 :     if (stored)
     688             :     {
     689          25 :         res = false;
     690             : 
     691          55 :         for (; stored; stored = stored->nextvariant)
     692          39 :             if (stored->idsubst == idsubst)
     693             :             {
     694           9 :                 res = true;
     695           9 :                 break;
     696             :             }
     697             :     }
     698             : 
     699          48 :     return res;
     700             : }
     701             : 
     702             : static LexemeInfo *
     703          55 : findVariant(LexemeInfo *in, LexemeInfo *stored, uint16 curpos, LexemeInfo **newin, int newn)
     704             : {
     705             :     for (;;)
     706             :     {
     707             :         int         i;
     708          55 :         LexemeInfo *ptr = newin[0];
     709             : 
     710          90 :         for (i = 0; i < newn; i++)
     711             :         {
     712         116 :             while (newin[i] && newin[i]->idsubst < ptr->idsubst)
     713           0 :                 newin[i] = newin[i]->nextentry;
     714             : 
     715          58 :             if (newin[i] == NULL)
     716          13 :                 return in;
     717             : 
     718          45 :             if (newin[i]->idsubst > ptr->idsubst)
     719             :             {
     720           0 :                 ptr = newin[i];
     721           0 :                 i = -1;
     722           0 :                 continue;
     723             :             }
     724             : 
     725          93 :             while (newin[i]->idsubst == ptr->idsubst)
     726             :             {
     727          45 :                 if (newin[i]->posinsubst == curpos && newin[i]->tnvariant == newn)
     728             :                 {
     729          32 :                     ptr = newin[i];
     730          32 :                     break;
     731             :                 }
     732             : 
     733          13 :                 newin[i] = newin[i]->nextentry;
     734          13 :                 if (newin[i] == NULL)
     735          10 :                     return in;
     736             :             }
     737             : 
     738          35 :             if (newin[i]->idsubst != ptr->idsubst)
     739             :             {
     740           3 :                 ptr = newin[i];
     741           3 :                 i = -1;
     742           3 :                 continue;
     743             :             }
     744             :         }
     745             : 
     746          32 :         if (i == newn && matchIdSubst(stored, ptr->idsubst) && (in == NULL || !matchIdSubst(in, ptr->idsubst)))
     747             :         {                       /* found */
     748             : 
     749          32 :             ptr->nextvariant = in;
     750          32 :             in = ptr;
     751             :         }
     752             : 
     753             :         /* step forward */
     754          64 :         for (i = 0; i < newn; i++)
     755          32 :             newin[i] = newin[i]->nextentry;
     756          32 :     }
     757             : }
     758             : 
     759             : static TSLexeme *
     760          13 : copyTSLexeme(TheSubstitute *ts)
     761             : {
     762             :     TSLexeme   *res;
     763             :     uint16      i;
     764             : 
     765          13 :     res = (TSLexeme *) palloc(sizeof(TSLexeme) * (ts->reslen + 1));
     766          30 :     for (i = 0; i < ts->reslen; i++)
     767             :     {
     768          17 :         res[i] = ts->res[i];
     769          17 :         res[i].lexeme = pstrdup(ts->res[i].lexeme);
     770             :     }
     771             : 
     772          13 :     res[ts->reslen].lexeme = NULL;
     773             : 
     774          13 :     return res;
     775             : }
     776             : 
     777             : static TSLexeme *
     778          16 : checkMatch(DictThesaurus *d, LexemeInfo *info, uint16 curpos, bool *moreres)
     779             : {
     780          16 :     *moreres = false;
     781          37 :     while (info)
     782             :     {
     783          18 :         Assert(info->idsubst < d->nsubst);
     784          18 :         if (info->nextvariant)
     785          11 :             *moreres = true;
     786          18 :         if (d->subst[info->idsubst].lastlexeme == curpos)
     787          13 :             return copyTSLexeme(d->subst + info->idsubst);
     788           5 :         info = info->nextvariant;
     789             :     }
     790             : 
     791           3 :     return NULL;
     792             : }
     793             : 
     794             : Datum
     795          34 : thesaurus_lexize(PG_FUNCTION_ARGS)
     796             : {
     797          34 :     DictThesaurus *d = (DictThesaurus *) PG_GETARG_POINTER(0);
     798          34 :     DictSubState *dstate = (DictSubState *) PG_GETARG_POINTER(3);
     799          34 :     TSLexeme   *res = NULL;
     800             :     LexemeInfo *stored,
     801          34 :                *info = NULL;
     802          34 :     uint16      curpos = 0;
     803          34 :     bool        moreres = false;
     804             : 
     805          34 :     if (PG_NARGS() != 4 || dstate == NULL)
     806           0 :         elog(ERROR, "forbidden call of thesaurus or nested call");
     807             : 
     808          34 :     if (dstate->isend)
     809           2 :         PG_RETURN_POINTER(NULL);
     810          32 :     stored = (LexemeInfo *) dstate->private_state;
     811             : 
     812          32 :     if (stored)
     813          10 :         curpos = stored->posinsubst + 1;
     814             : 
     815          32 :     if (!d->subdict->isvalid)
     816           0 :         d->subdict = lookup_ts_dictionary_cache(d->subdictOid);
     817             : 
     818          32 :     res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(d->subdict->lexize),
     819             :                                                      PointerGetDatum(d->subdict->dictData),
     820             :                                                      PG_GETARG_DATUM(1),
     821             :                                                      PG_GETARG_DATUM(2),
     822             :                                                      PointerGetDatum(NULL)));
     823             : 
     824          32 :     if (res && res->lexeme)
     825          26 :     {
     826          26 :         TSLexeme   *ptr = res,
     827             :                    *basevar;
     828             : 
     829          78 :         while (ptr->lexeme)
     830             :         {
     831          26 :             uint16      nv = ptr->nvariant;
     832             :             uint16      i,
     833          26 :                         nlex = 0;
     834             :             LexemeInfo **infos;
     835             : 
     836          26 :             basevar = ptr;
     837          78 :             while (ptr->lexeme && nv == ptr->nvariant)
     838             :             {
     839          26 :                 nlex++;
     840          26 :                 ptr++;
     841             :             }
     842             : 
     843          26 :             infos = (LexemeInfo **) palloc(sizeof(LexemeInfo *) * nlex);
     844          43 :             for (i = 0; i < nlex; i++)
     845          26 :                 if ((infos[i] = findTheLexeme(d, basevar[i].lexeme)) == NULL)
     846           9 :                     break;
     847             : 
     848          26 :             if (i < nlex)
     849             :             {
     850             :                 /* no chance to find */
     851           9 :                 pfree(infos);
     852           9 :                 continue;
     853             :             }
     854             : 
     855          17 :             info = findVariant(info, stored, curpos, infos, nlex);
     856             :         }
     857             :     }
     858           6 :     else if (res)
     859             :     {                           /* stop-word */
     860           6 :         LexemeInfo *infos = findTheLexeme(d, NULL);
     861             : 
     862           6 :         info = findVariant(NULL, stored, curpos, &infos, 1);
     863             :     }
     864             :     else
     865             :     {
     866           0 :         info = NULL;            /* word isn't recognized */
     867             :     }
     868             : 
     869          32 :     dstate->private_state = (void *) info;
     870             : 
     871          32 :     if (!info)
     872             :     {
     873          16 :         dstate->getnext = false;
     874          16 :         PG_RETURN_POINTER(NULL);
     875             :     }
     876             : 
     877          16 :     if ((res = checkMatch(d, info, curpos, &moreres)) != NULL)
     878             :     {
     879          13 :         dstate->getnext = moreres;
     880          13 :         PG_RETURN_POINTER(res);
     881             :     }
     882             : 
     883           3 :     dstate->getnext = true;
     884             : 
     885           3 :     PG_RETURN_POINTER(NULL);
     886             : }

Generated by: LCOV version 1.11