LCOV - differential code coverage report
Current view: top level - src/backend/tsearch - dict_thesaurus.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 86.9 % 381 331 5 7 38 4 65 2 260 8 66
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 17 17 6 1 10 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a