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

Generated by: LCOV version 2.0-1