LCOV - code coverage report
Current view: top level - src/backend/tsearch - ts_parse.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 254 274 92.7 %
Date: 2020-06-01 00:06:26 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * ts_parse.c
       4             :  *      main parse functions for tsearch
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/tsearch/ts_parse.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "tsearch/ts_cache.h"
      18             : #include "tsearch/ts_utils.h"
      19             : 
      20             : #define IGNORE_LONGLEXEME   1
      21             : 
      22             : /*
      23             :  * Lexize subsystem
      24             :  */
      25             : 
      26             : typedef struct ParsedLex
      27             : {
      28             :     int         type;
      29             :     char       *lemm;
      30             :     int         lenlemm;
      31             :     struct ParsedLex *next;
      32             : } ParsedLex;
      33             : 
      34             : typedef struct ListParsedLex
      35             : {
      36             :     ParsedLex  *head;
      37             :     ParsedLex  *tail;
      38             : } ListParsedLex;
      39             : 
      40             : typedef struct
      41             : {
      42             :     TSConfigCacheEntry *cfg;
      43             :     Oid         curDictId;
      44             :     int         posDict;
      45             :     DictSubState dictState;
      46             :     ParsedLex  *curSub;
      47             :     ListParsedLex towork;       /* current list to work */
      48             :     ListParsedLex waste;        /* list of lexemes that already lexized */
      49             : 
      50             :     /*
      51             :      * fields to store last variant to lexize (basically, thesaurus or similar
      52             :      * to, which wants  several lexemes
      53             :      */
      54             : 
      55             :     ParsedLex  *lastRes;
      56             :     TSLexeme   *tmpRes;
      57             : } LexizeData;
      58             : 
      59             : static void
      60        3132 : LexizeInit(LexizeData *ld, TSConfigCacheEntry *cfg)
      61             : {
      62        3132 :     ld->cfg = cfg;
      63        3132 :     ld->curDictId = InvalidOid;
      64        3132 :     ld->posDict = 0;
      65        3132 :     ld->towork.head = ld->towork.tail = ld->curSub = NULL;
      66        3132 :     ld->waste.head = ld->waste.tail = NULL;
      67        3132 :     ld->lastRes = NULL;
      68        3132 :     ld->tmpRes = NULL;
      69        3132 : }
      70             : 
      71             : static void
      72       31840 : LPLAddTail(ListParsedLex *list, ParsedLex *newpl)
      73             : {
      74       31840 :     if (list->tail)
      75             :     {
      76         148 :         list->tail->next = newpl;
      77         148 :         list->tail = newpl;
      78             :     }
      79             :     else
      80       31692 :         list->head = list->tail = newpl;
      81       31840 :     newpl->next = NULL;
      82       31840 : }
      83             : 
      84             : static ParsedLex *
      85       15920 : LPLRemoveHead(ListParsedLex *list)
      86             : {
      87       15920 :     ParsedLex  *res = list->head;
      88             : 
      89       15920 :     if (list->head)
      90       15920 :         list->head = list->head->next;
      91             : 
      92       15920 :     if (list->head == NULL)
      93       15836 :         list->tail = NULL;
      94             : 
      95       15920 :     return res;
      96             : }
      97             : 
      98             : static void
      99       15920 : LexizeAddLemm(LexizeData *ld, int type, char *lemm, int lenlemm)
     100             : {
     101       15920 :     ParsedLex  *newpl = (ParsedLex *) palloc(sizeof(ParsedLex));
     102             : 
     103       15920 :     newpl->type = type;
     104       15920 :     newpl->lemm = lemm;
     105       15920 :     newpl->lenlemm = lenlemm;
     106       15920 :     LPLAddTail(&ld->towork, newpl);
     107       15920 :     ld->curSub = ld->towork.tail;
     108       15920 : }
     109             : 
     110             : static void
     111       15920 : RemoveHead(LexizeData *ld)
     112             : {
     113       15920 :     LPLAddTail(&ld->waste, LPLRemoveHead(&ld->towork));
     114             : 
     115       15920 :     ld->posDict = 0;
     116       15920 : }
     117             : 
     118             : static void
     119       23682 : setCorrLex(LexizeData *ld, ParsedLex **correspondLexem)
     120             : {
     121       23682 :     if (correspondLexem)
     122             :     {
     123        6624 :         *correspondLexem = ld->waste.head;
     124             :     }
     125             :     else
     126             :     {
     127             :         ParsedLex  *tmp,
     128       17058 :                    *ptr = ld->waste.head;
     129             : 
     130       28518 :         while (ptr)
     131             :         {
     132       11460 :             tmp = ptr->next;
     133       11460 :             pfree(ptr);
     134       11460 :             ptr = tmp;
     135             :         }
     136             :     }
     137       23682 :     ld->waste.head = ld->waste.tail = NULL;
     138       23682 : }
     139             : 
     140             : static void
     141          32 : moveToWaste(LexizeData *ld, ParsedLex *stop)
     142             : {
     143          32 :     bool        go = true;
     144             : 
     145         120 :     while (ld->towork.head && go)
     146             :     {
     147          88 :         if (ld->towork.head == stop)
     148             :         {
     149          32 :             ld->curSub = stop->next;
     150          32 :             go = false;
     151             :         }
     152          88 :         RemoveHead(ld);
     153             :     }
     154          32 : }
     155             : 
     156             : static void
     157          32 : setNewTmpRes(LexizeData *ld, ParsedLex *lex, TSLexeme *res)
     158             : {
     159          32 :     if (ld->tmpRes)
     160             :     {
     161             :         TSLexeme   *ptr;
     162             : 
     163          16 :         for (ptr = ld->tmpRes; ptr->lexeme; ptr++)
     164           8 :             pfree(ptr->lexeme);
     165           8 :         pfree(ld->tmpRes);
     166             :     }
     167          32 :     ld->tmpRes = res;
     168          32 :     ld->lastRes = lex;
     169          32 : }
     170             : 
     171             : static TSLexeme *
     172       23714 : LexizeExec(LexizeData *ld, ParsedLex **correspondLexem)
     173             : {
     174             :     int         i;
     175             :     ListDictionary *map;
     176             :     TSDictionaryCacheEntry *dict;
     177             :     TSLexeme   *res;
     178             : 
     179       23714 :     if (ld->curDictId == InvalidOid)
     180             :     {
     181             :         /*
     182             :          * usual mode: dictionary wants only one word, but we should keep in
     183             :          * mind that we should go through all stack
     184             :          */
     185             : 
     186       31700 :         while (ld->towork.head)
     187             :         {
     188       15864 :             ParsedLex  *curVal = ld->towork.head;
     189       15864 :             char       *curValLemm = curVal->lemm;
     190       15864 :             int         curValLenLemm = curVal->lenlemm;
     191             : 
     192       15864 :             map = ld->cfg->map + curVal->type;
     193             : 
     194       15864 :             if (curVal->type == 0 || curVal->type >= ld->cfg->lenmap || map->len == 0)
     195             :             {
     196             :                 /* skip this type of lexeme */
     197        8102 :                 RemoveHead(ld);
     198        8102 :                 continue;
     199             :             }
     200             : 
     201        8090 :             for (i = ld->posDict; i < map->len; i++)
     202             :             {
     203        8090 :                 dict = lookup_ts_dictionary_cache(map->dictIds[i]);
     204             : 
     205        8090 :                 ld->dictState.isend = ld->dictState.getnext = false;
     206        8090 :                 ld->dictState.private_state = NULL;
     207        8090 :                 res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(dict->lexize),
     208             :                                                                  PointerGetDatum(dict->dictData),
     209             :                                                                  PointerGetDatum(curValLemm),
     210             :                                                                  Int32GetDatum(curValLenLemm),
     211             :                                                                  PointerGetDatum(&ld->dictState)));
     212             : 
     213        8090 :                 if (ld->dictState.getnext)
     214             :                 {
     215             :                     /*
     216             :                      * dictionary wants next word, so setup and store current
     217             :                      * position and go to multiword mode
     218             :                      */
     219             : 
     220          32 :                     ld->curDictId = DatumGetObjectId(map->dictIds[i]);
     221          32 :                     ld->posDict = i + 1;
     222          32 :                     ld->curSub = curVal->next;
     223          32 :                     if (res)
     224          24 :                         setNewTmpRes(ld, curVal, res);
     225          32 :                     return LexizeExec(ld, correspondLexem);
     226             :                 }
     227             : 
     228        8058 :                 if (!res)       /* dictionary doesn't know this lexeme */
     229         328 :                     continue;
     230             : 
     231        7730 :                 if (res->flags & TSL_FILTER)
     232             :                 {
     233           0 :                     curValLemm = res->lexeme;
     234           0 :                     curValLenLemm = strlen(res->lexeme);
     235           0 :                     continue;
     236             :                 }
     237             : 
     238        7730 :                 RemoveHead(ld);
     239        7730 :                 setCorrLex(ld, correspondLexem);
     240        7730 :                 return res;
     241             :             }
     242             : 
     243           0 :             RemoveHead(ld);
     244             :         }
     245             :     }
     246             :     else
     247             :     {                           /* curDictId is valid */
     248         116 :         dict = lookup_ts_dictionary_cache(ld->curDictId);
     249             : 
     250             :         /*
     251             :          * Dictionary ld->curDictId asks  us about following words
     252             :          */
     253             : 
     254         168 :         while (ld->curSub)
     255             :         {
     256          84 :             ParsedLex  *curVal = ld->curSub;
     257             : 
     258          84 :             map = ld->cfg->map + curVal->type;
     259             : 
     260          84 :             if (curVal->type != 0)
     261             :             {
     262          80 :                 bool        dictExists = false;
     263             : 
     264          80 :                 if (curVal->type >= ld->cfg->lenmap || map->len == 0)
     265             :                 {
     266             :                     /* skip this type of lexeme */
     267          40 :                     ld->curSub = curVal->next;
     268          40 :                     continue;
     269             :                 }
     270             : 
     271             :                 /*
     272             :                  * We should be sure that current type of lexeme is recognized
     273             :                  * by our dictionary: we just check is it exist in list of
     274             :                  * dictionaries ?
     275             :                  */
     276         120 :                 for (i = 0; i < map->len && !dictExists; i++)
     277          80 :                     if (ld->curDictId == DatumGetObjectId(map->dictIds[i]))
     278          40 :                         dictExists = true;
     279             : 
     280          40 :                 if (!dictExists)
     281             :                 {
     282             :                     /*
     283             :                      * Dictionary can't work with current type of lexeme,
     284             :                      * return to basic mode and redo all stored lexemes
     285             :                      */
     286           0 :                     ld->curDictId = InvalidOid;
     287           0 :                     return LexizeExec(ld, correspondLexem);
     288             :                 }
     289             :             }
     290             : 
     291          44 :             ld->dictState.isend = (curVal->type == 0) ? true : false;
     292          44 :             ld->dictState.getnext = false;
     293             : 
     294          44 :             res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(dict->lexize),
     295             :                                                              PointerGetDatum(dict->dictData),
     296             :                                                              PointerGetDatum(curVal->lemm),
     297             :                                                              Int32GetDatum(curVal->lenlemm),
     298             :                                                              PointerGetDatum(&ld->dictState)));
     299             : 
     300          44 :             if (ld->dictState.getnext)
     301             :             {
     302             :                 /* Dictionary wants one more */
     303          12 :                 ld->curSub = curVal->next;
     304          12 :                 if (res)
     305           8 :                     setNewTmpRes(ld, curVal, res);
     306          12 :                 continue;
     307             :             }
     308             : 
     309          32 :             if (res || ld->tmpRes)
     310             :             {
     311             :                 /*
     312             :                  * Dictionary normalizes lexemes, so we remove from stack all
     313             :                  * used lexemes, return to basic mode and redo end of stack
     314             :                  * (if it exists)
     315             :                  */
     316          32 :                 if (res)
     317             :                 {
     318          16 :                     moveToWaste(ld, ld->curSub);
     319             :                 }
     320             :                 else
     321             :                 {
     322          16 :                     res = ld->tmpRes;
     323          16 :                     moveToWaste(ld, ld->lastRes);
     324             :                 }
     325             : 
     326             :                 /* reset to initial state */
     327          32 :                 ld->curDictId = InvalidOid;
     328          32 :                 ld->posDict = 0;
     329          32 :                 ld->lastRes = NULL;
     330          32 :                 ld->tmpRes = NULL;
     331          32 :                 setCorrLex(ld, correspondLexem);
     332          32 :                 return res;
     333             :             }
     334             : 
     335             :             /*
     336             :              * Dict don't want next lexem and didn't recognize anything, redo
     337             :              * from ld->towork.head
     338             :              */
     339           0 :             ld->curDictId = InvalidOid;
     340           0 :             return LexizeExec(ld, correspondLexem);
     341             :         }
     342             :     }
     343             : 
     344       15920 :     setCorrLex(ld, correspondLexem);
     345       15920 :     return NULL;
     346             : }
     347             : 
     348             : /*
     349             :  * Parse string and lexize words.
     350             :  *
     351             :  * prs will be filled in.
     352             :  */
     353             : void
     354        2918 : parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
     355             : {
     356             :     int         type,
     357             :                 lenlemm;
     358        2918 :     char       *lemm = NULL;
     359             :     LexizeData  ldata;
     360             :     TSLexeme   *norms;
     361             :     TSConfigCacheEntry *cfg;
     362             :     TSParserCacheEntry *prsobj;
     363             :     void       *prsdata;
     364             : 
     365        2918 :     cfg = lookup_ts_config_cache(cfgId);
     366        2918 :     prsobj = lookup_ts_parser_cache(cfg->prsId);
     367             : 
     368        2918 :     prsdata = (void *) DatumGetPointer(FunctionCall2(&prsobj->prsstart,
     369             :                                                      PointerGetDatum(buf),
     370             :                                                      Int32GetDatum(buflen)));
     371             : 
     372        2918 :     LexizeInit(&ldata, cfg);
     373             : 
     374             :     do
     375             :     {
     376       11460 :         type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken),
     377             :                                            PointerGetDatum(prsdata),
     378             :                                            PointerGetDatum(&lemm),
     379             :                                            PointerGetDatum(&lenlemm)));
     380             : 
     381       11460 :         if (type > 0 && lenlemm >= MAXSTRLEN)
     382             :         {
     383             : #ifdef IGNORE_LONGLEXEME
     384           0 :             ereport(NOTICE,
     385             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     386             :                      errmsg("word is too long to be indexed"),
     387             :                      errdetail("Words longer than %d characters are ignored.",
     388             :                                MAXSTRLEN)));
     389           0 :             continue;
     390             : #else
     391             :             ereport(ERROR,
     392             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     393             :                      errmsg("word is too long to be indexed"),
     394             :                      errdetail("Words longer than %d characters are ignored.",
     395             :                                MAXSTRLEN)));
     396             : #endif
     397             :         }
     398             : 
     399       11460 :         LexizeAddLemm(&ldata, type, lemm, lenlemm);
     400             : 
     401       17058 :         while ((norms = LexizeExec(&ldata, NULL)) != NULL)
     402             :         {
     403        5598 :             TSLexeme   *ptr = norms;
     404             : 
     405        5598 :             prs->pos++;          /* set pos */
     406             : 
     407       10392 :             while (ptr->lexeme)
     408             :             {
     409        4794 :                 if (prs->curwords == prs->lenwords)
     410             :                 {
     411         222 :                     prs->lenwords *= 2;
     412         222 :                     prs->words = (ParsedWord *) repalloc((void *) prs->words, prs->lenwords * sizeof(ParsedWord));
     413             :                 }
     414             : 
     415        4794 :                 if (ptr->flags & TSL_ADDPOS)
     416          16 :                     prs->pos++;
     417        4794 :                 prs->words[prs->curwords].len = strlen(ptr->lexeme);
     418        4794 :                 prs->words[prs->curwords].word = ptr->lexeme;
     419        4794 :                 prs->words[prs->curwords].nvariant = ptr->nvariant;
     420        4794 :                 prs->words[prs->curwords].flags = ptr->flags & TSL_PREFIX;
     421        4794 :                 prs->words[prs->curwords].alen = 0;
     422        4794 :                 prs->words[prs->curwords].pos.pos = LIMITPOS(prs->pos);
     423        4794 :                 ptr++;
     424        4794 :                 prs->curwords++;
     425             :             }
     426        5598 :             pfree(norms);
     427             :         }
     428       11460 :     } while (type > 0);
     429             : 
     430        2918 :     FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata));
     431        2918 : }
     432             : 
     433             : /*
     434             :  * Headline framework
     435             :  */
     436             : static void
     437        4246 : hladdword(HeadlineParsedText *prs, char *buf, int buflen, int type)
     438             : {
     439        4274 :     while (prs->curwords >= prs->lenwords)
     440             :     {
     441          28 :         prs->lenwords *= 2;
     442          28 :         prs->words = (HeadlineWordEntry *) repalloc((void *) prs->words, prs->lenwords * sizeof(HeadlineWordEntry));
     443             :     }
     444        4246 :     memset(&(prs->words[prs->curwords]), 0, sizeof(HeadlineWordEntry));
     445        4246 :     prs->words[prs->curwords].type = (uint8) type;
     446        4246 :     prs->words[prs->curwords].len = buflen;
     447        4246 :     prs->words[prs->curwords].word = palloc(buflen);
     448        4246 :     memcpy(prs->words[prs->curwords].word, buf, buflen);
     449        4246 :     prs->curwords++;
     450        4246 : }
     451             : 
     452             : static void
     453        1444 : hlfinditem(HeadlineParsedText *prs, TSQuery query, int32 pos, char *buf, int buflen)
     454             : {
     455             :     int         i;
     456        1444 :     QueryItem  *item = GETQUERY(query);
     457             :     HeadlineWordEntry *word;
     458             : 
     459        1492 :     while (prs->curwords + query->size >= prs->lenwords)
     460             :     {
     461          48 :         prs->lenwords *= 2;
     462          48 :         prs->words = (HeadlineWordEntry *) repalloc((void *) prs->words, prs->lenwords * sizeof(HeadlineWordEntry));
     463             :     }
     464             : 
     465        1444 :     word = &(prs->words[prs->curwords - 1]);
     466        1444 :     word->pos = LIMITPOS(pos);
     467        6528 :     for (i = 0; i < query->size; i++)
     468             :     {
     469        8348 :         if (item->type == QI_VAL &&
     470        3264 :             tsCompareString(GETOPERAND(query) + item->qoperand.distance, item->qoperand.length,
     471        3264 :                             buf, buflen, item->qoperand.prefix) == 0)
     472             :         {
     473         290 :             if (word->item)
     474             :             {
     475           0 :                 memcpy(&(prs->words[prs->curwords]), word, sizeof(HeadlineWordEntry));
     476           0 :                 prs->words[prs->curwords].item = &item->qoperand;
     477           0 :                 prs->words[prs->curwords].repeated = 1;
     478           0 :                 prs->curwords++;
     479             :             }
     480             :             else
     481         290 :                 word->item = &item->qoperand;
     482             :         }
     483        5084 :         item++;
     484             :     }
     485        1444 : }
     486             : 
     487             : static void
     488        6624 : addHLParsedLex(HeadlineParsedText *prs, TSQuery query, ParsedLex *lexs, TSLexeme *norms)
     489             : {
     490             :     ParsedLex  *tmplexs;
     491             :     TSLexeme   *ptr;
     492             :     int32       savedpos;
     493             : 
     494       11084 :     while (lexs)
     495             :     {
     496        4460 :         if (lexs->type > 0)
     497        4246 :             hladdword(prs, lexs->lemm, lexs->lenlemm, lexs->type);
     498             : 
     499        4460 :         ptr = norms;
     500        4460 :         savedpos = prs->vectorpos;
     501        5904 :         while (ptr && ptr->lexeme)
     502             :         {
     503        1444 :             if (ptr->flags & TSL_ADDPOS)
     504           0 :                 savedpos++;
     505        1444 :             hlfinditem(prs, query, savedpos, ptr->lexeme, strlen(ptr->lexeme));
     506        1444 :             ptr++;
     507             :         }
     508             : 
     509        4460 :         tmplexs = lexs->next;
     510        4460 :         pfree(lexs);
     511        4460 :         lexs = tmplexs;
     512             :     }
     513             : 
     514        6624 :     if (norms)
     515             :     {
     516        2164 :         ptr = norms;
     517        3608 :         while (ptr->lexeme)
     518             :         {
     519        1444 :             if (ptr->flags & TSL_ADDPOS)
     520           0 :                 prs->vectorpos++;
     521        1444 :             pfree(ptr->lexeme);
     522        1444 :             ptr++;
     523             :         }
     524        2164 :         pfree(norms);
     525             :     }
     526        6624 : }
     527             : 
     528             : void
     529         214 : hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
     530             : {
     531             :     int         type,
     532             :                 lenlemm;
     533         214 :     char       *lemm = NULL;
     534             :     LexizeData  ldata;
     535             :     TSLexeme   *norms;
     536             :     ParsedLex  *lexs;
     537             :     TSConfigCacheEntry *cfg;
     538             :     TSParserCacheEntry *prsobj;
     539             :     void       *prsdata;
     540             : 
     541         214 :     cfg = lookup_ts_config_cache(cfgId);
     542         214 :     prsobj = lookup_ts_parser_cache(cfg->prsId);
     543             : 
     544         214 :     prsdata = (void *) DatumGetPointer(FunctionCall2(&(prsobj->prsstart),
     545             :                                                      PointerGetDatum(buf),
     546             :                                                      Int32GetDatum(buflen)));
     547             : 
     548         214 :     LexizeInit(&ldata, cfg);
     549             : 
     550             :     do
     551             :     {
     552        4460 :         type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken),
     553             :                                            PointerGetDatum(prsdata),
     554             :                                            PointerGetDatum(&lemm),
     555             :                                            PointerGetDatum(&lenlemm)));
     556             : 
     557        4460 :         if (type > 0 && lenlemm >= MAXSTRLEN)
     558             :         {
     559             : #ifdef IGNORE_LONGLEXEME
     560           0 :             ereport(NOTICE,
     561             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     562             :                      errmsg("word is too long to be indexed"),
     563             :                      errdetail("Words longer than %d characters are ignored.",
     564             :                                MAXSTRLEN)));
     565           0 :             continue;
     566             : #else
     567             :             ereport(ERROR,
     568             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     569             :                      errmsg("word is too long to be indexed"),
     570             :                      errdetail("Words longer than %d characters are ignored.",
     571             :                                MAXSTRLEN)));
     572             : #endif
     573             :         }
     574             : 
     575        4460 :         LexizeAddLemm(&ldata, type, lemm, lenlemm);
     576             : 
     577             :         do
     578             :         {
     579        6624 :             if ((norms = LexizeExec(&ldata, &lexs)) != NULL)
     580             :             {
     581        2164 :                 prs->vectorpos++;
     582        2164 :                 addHLParsedLex(prs, query, lexs, norms);
     583             :             }
     584             :             else
     585        4460 :                 addHLParsedLex(prs, query, lexs, NULL);
     586        6624 :         } while (norms);
     587             : 
     588        4460 :     } while (type > 0);
     589             : 
     590         214 :     FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata));
     591         214 : }
     592             : 
     593             : text *
     594         214 : generateHeadline(HeadlineParsedText *prs)
     595             : {
     596             :     text       *out;
     597             :     char       *ptr;
     598         214 :     int         len = 128;
     599         214 :     int         numfragments = 0;
     600         214 :     int16       infrag = 0;
     601             : 
     602         214 :     HeadlineWordEntry *wrd = prs->words;
     603             : 
     604         214 :     out = (text *) palloc(len);
     605         214 :     ptr = ((char *) out) + VARHDRSZ;
     606             : 
     607        4460 :     while (wrd - prs->words < prs->curwords)
     608             :     {
     609        4286 :         while (wrd->len + prs->stopsellen + prs->startsellen + prs->fragdelimlen + (ptr - ((char *) out)) >= len)
     610             :         {
     611          40 :             int         dist = ptr - ((char *) out);
     612             : 
     613          40 :             len *= 2;
     614          40 :             out = (text *) repalloc(out, len);
     615          40 :             ptr = ((char *) out) + dist;
     616             :         }
     617             : 
     618        4246 :         if (wrd->in && !wrd->repeated)
     619             :         {
     620        2510 :             if (!infrag)
     621             :             {
     622             : 
     623             :                 /* start of a new fragment */
     624         222 :                 infrag = 1;
     625         222 :                 numfragments++;
     626             :                 /* add a fragment delimiter if this is after the first one */
     627         222 :                 if (numfragments > 1)
     628             :                 {
     629           8 :                     memcpy(ptr, prs->fragdelim, prs->fragdelimlen);
     630           8 :                     ptr += prs->fragdelimlen;
     631             :                 }
     632             : 
     633             :             }
     634        5020 :             if (wrd->replace)
     635             :             {
     636           0 :                 *ptr = ' ';
     637           0 :                 ptr++;
     638             :             }
     639        2510 :             else if (!wrd->skip)
     640             :             {
     641        2506 :                 if (wrd->selected)
     642             :                 {
     643         254 :                     memcpy(ptr, prs->startsel, prs->startsellen);
     644         254 :                     ptr += prs->startsellen;
     645             :                 }
     646        2506 :                 memcpy(ptr, wrd->word, wrd->len);
     647        2506 :                 ptr += wrd->len;
     648        2506 :                 if (wrd->selected)
     649             :                 {
     650         254 :                     memcpy(ptr, prs->stopsel, prs->stopsellen);
     651         254 :                     ptr += prs->stopsellen;
     652             :                 }
     653             :             }
     654             :         }
     655        1736 :         else if (!wrd->repeated)
     656             :         {
     657        1736 :             if (infrag)
     658          56 :                 infrag = 0;
     659        1736 :             pfree(wrd->word);
     660             :         }
     661             : 
     662        4246 :         wrd++;
     663             :     }
     664             : 
     665         214 :     SET_VARSIZE(out, ptr - ((char *) out));
     666         214 :     return out;
     667             : }

Generated by: LCOV version 1.13