LCOV - code coverage report
Current view: top level - src/backend/tsearch - to_tsany.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 219 228 96.1 %
Date: 2017-09-29 15:12:54 Functions: 16 18 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * to_tsany.c
       4             :  *      to_ts* function definitions
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/tsearch/to_tsany.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "tsearch/ts_cache.h"
      17             : #include "tsearch/ts_utils.h"
      18             : #include "utils/builtins.h"
      19             : #include "utils/jsonapi.h"
      20             : 
      21             : 
      22             : typedef struct MorphOpaque
      23             : {
      24             :     Oid         cfg_id;
      25             :     int         qoperator;      /* query operator */
      26             : } MorphOpaque;
      27             : 
      28             : typedef struct TSVectorBuildState
      29             : {
      30             :     ParsedText *prs;
      31             :     Oid         cfgId;
      32             : } TSVectorBuildState;
      33             : 
      34             : static void add_to_tsvector(void *_state, char *elem_value, int elem_len);
      35             : 
      36             : 
      37             : Datum
      38           0 : get_current_ts_config(PG_FUNCTION_ARGS)
      39             : {
      40           0 :     PG_RETURN_OID(getTSCurrentConfig(true));
      41             : }
      42             : 
      43             : /*
      44             :  * to_tsvector
      45             :  */
      46             : static int
      47        1799 : compareWORD(const void *a, const void *b)
      48             : {
      49             :     int         res;
      50             : 
      51        3598 :     res = tsCompareString(
      52        1799 :                           ((const ParsedWord *) a)->word, ((const ParsedWord *) a)->len,
      53        1799 :                           ((const ParsedWord *) b)->word, ((const ParsedWord *) b)->len,
      54             :                           false);
      55             : 
      56        1799 :     if (res == 0)
      57             :     {
      58         166 :         if (((const ParsedWord *) a)->pos.pos == ((const ParsedWord *) b)->pos.pos)
      59           4 :             return 0;
      60             : 
      61         162 :         res = (((const ParsedWord *) a)->pos.pos > ((const ParsedWord *) b)->pos.pos) ? 1 : -1;
      62             :     }
      63             : 
      64        1795 :     return res;
      65             : }
      66             : 
      67             : static int
      68          70 : uniqueWORD(ParsedWord *a, int32 l)
      69             : {
      70             :     ParsedWord *ptr,
      71             :                *res;
      72             :     int         tmppos;
      73             : 
      74          70 :     if (l == 1)
      75             :     {
      76           3 :         tmppos = LIMITPOS(a->pos.pos);
      77           3 :         a->alen = 2;
      78           3 :         a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
      79           3 :         a->pos.apos[0] = 1;
      80           3 :         a->pos.apos[1] = tmppos;
      81           3 :         return l;
      82             :     }
      83             : 
      84          67 :     res = a;
      85          67 :     ptr = a + 1;
      86             : 
      87             :     /*
      88             :      * Sort words with its positions
      89             :      */
      90          67 :     qsort((void *) a, l, sizeof(ParsedWord), compareWORD);
      91             : 
      92             :     /*
      93             :      * Initialize first word and its first position
      94             :      */
      95          67 :     tmppos = LIMITPOS(a->pos.pos);
      96          67 :     a->alen = 2;
      97          67 :     a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
      98          67 :     a->pos.apos[0] = 1;
      99          67 :     a->pos.apos[1] = tmppos;
     100             : 
     101             :     /*
     102             :      * Summarize position information for each word
     103             :      */
     104         595 :     while (ptr - a < l)
     105             :     {
     106         697 :         if (!(ptr->len == res->len &&
     107         236 :               strncmp(ptr->word, res->word, res->len) == 0))
     108             :         {
     109             :             /*
     110             :              * Got a new word, so put it in result
     111             :              */
     112         361 :             res++;
     113         361 :             res->len = ptr->len;
     114         361 :             res->word = ptr->word;
     115         361 :             tmppos = LIMITPOS(ptr->pos.pos);
     116         361 :             res->alen = 2;
     117         361 :             res->pos.apos = (uint16 *) palloc(sizeof(uint16) * res->alen);
     118         361 :             res->pos.apos[0] = 1;
     119         361 :             res->pos.apos[1] = tmppos;
     120             :         }
     121             :         else
     122             :         {
     123             :             /*
     124             :              * The word already exists, so adjust position information. But
     125             :              * before we should check size of position's array, max allowed
     126             :              * value for position and uniqueness of position
     127             :              */
     128         100 :             pfree(ptr->word);
     129         200 :             if (res->pos.apos[0] < MAXNUMPOS - 1 && res->pos.apos[res->pos.apos[0]] != MAXENTRYPOS - 1 &&
     130         100 :                 res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos))
     131             :             {
     132          96 :                 if (res->pos.apos[0] + 1 >= res->alen)
     133             :                 {
     134          75 :                     res->alen *= 2;
     135          75 :                     res->pos.apos = (uint16 *) repalloc(res->pos.apos, sizeof(uint16) * res->alen);
     136             :                 }
     137          96 :                 if (res->pos.apos[0] == 0 || res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos))
     138             :                 {
     139          96 :                     res->pos.apos[res->pos.apos[0] + 1] = LIMITPOS(ptr->pos.pos);
     140          96 :                     res->pos.apos[0]++;
     141             :                 }
     142             :             }
     143             :         }
     144         461 :         ptr++;
     145             :     }
     146             : 
     147          67 :     return res + 1 - a;
     148             : }
     149             : 
     150             : /*
     151             :  * make value of tsvector, given parsed text
     152             :  *
     153             :  * Note: frees prs->words and subsidiary data.
     154             :  */
     155             : TSVector
     156          80 : make_tsvector(ParsedText *prs)
     157             : {
     158             :     int         i,
     159             :                 j,
     160          80 :                 lenstr = 0,
     161             :                 totallen;
     162             :     TSVector    in;
     163             :     WordEntry  *ptr;
     164             :     char       *str;
     165             :     int         stroff;
     166             : 
     167             :     /* Merge duplicate words */
     168          80 :     if (prs->curwords > 0)
     169          70 :         prs->curwords = uniqueWORD(prs->words, prs->curwords);
     170             : 
     171             :     /* Determine space needed */
     172         511 :     for (i = 0; i < prs->curwords; i++)
     173             :     {
     174         431 :         lenstr += prs->words[i].len;
     175         431 :         if (prs->words[i].alen)
     176             :         {
     177         431 :             lenstr = SHORTALIGN(lenstr);
     178         431 :             lenstr += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos);
     179             :         }
     180             :     }
     181             : 
     182          80 :     if (lenstr > MAXSTRPOS)
     183           0 :         ereport(ERROR,
     184             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     185             :                  errmsg("string is too long for tsvector (%d bytes, max %d bytes)", lenstr, MAXSTRPOS)));
     186             : 
     187          80 :     totallen = CALCDATASIZE(prs->curwords, lenstr);
     188          80 :     in = (TSVector) palloc0(totallen);
     189          80 :     SET_VARSIZE(in, totallen);
     190          80 :     in->size = prs->curwords;
     191             : 
     192          80 :     ptr = ARRPTR(in);
     193          80 :     str = STRPTR(in);
     194          80 :     stroff = 0;
     195         511 :     for (i = 0; i < prs->curwords; i++)
     196             :     {
     197         431 :         ptr->len = prs->words[i].len;
     198         431 :         ptr->pos = stroff;
     199         431 :         memcpy(str + stroff, prs->words[i].word, prs->words[i].len);
     200         431 :         stroff += prs->words[i].len;
     201         431 :         pfree(prs->words[i].word);
     202         431 :         if (prs->words[i].alen)
     203             :         {
     204         431 :             int         k = prs->words[i].pos.apos[0];
     205             :             WordEntryPos *wptr;
     206             : 
     207         431 :             if (k > 0xFFFF)
     208           0 :                 elog(ERROR, "positions array too long");
     209             : 
     210         431 :             ptr->haspos = 1;
     211         431 :             stroff = SHORTALIGN(stroff);
     212         431 :             *(uint16 *) (str + stroff) = (uint16) k;
     213         431 :             wptr = POSDATAPTR(in, ptr);
     214         958 :             for (j = 0; j < k; j++)
     215             :             {
     216         527 :                 WEP_SETWEIGHT(wptr[j], 0);
     217         527 :                 WEP_SETPOS(wptr[j], prs->words[i].pos.apos[j + 1]);
     218             :             }
     219         431 :             stroff += sizeof(uint16) + k * sizeof(WordEntryPos);
     220         431 :             pfree(prs->words[i].pos.apos);
     221             :         }
     222             :         else
     223           0 :             ptr->haspos = 0;
     224         431 :         ptr++;
     225             :     }
     226             : 
     227          80 :     if (prs->words)
     228          74 :         pfree(prs->words);
     229             : 
     230          80 :     return in;
     231             : }
     232             : 
     233             : Datum
     234          63 : to_tsvector_byid(PG_FUNCTION_ARGS)
     235             : {
     236          63 :     Oid         cfgId = PG_GETARG_OID(0);
     237          63 :     text       *in = PG_GETARG_TEXT_PP(1);
     238             :     ParsedText  prs;
     239             :     TSVector    out;
     240             : 
     241          63 :     prs.lenwords = VARSIZE_ANY_EXHDR(in) / 6;   /* just estimation of word's
     242             :                                                  * number */
     243          63 :     if (prs.lenwords < 2)
     244          42 :         prs.lenwords = 2;
     245          63 :     prs.curwords = 0;
     246          63 :     prs.pos = 0;
     247          63 :     prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords);
     248             : 
     249          63 :     parsetext(cfgId, &prs, VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in));
     250             : 
     251          63 :     PG_FREE_IF_COPY(in, 1);
     252             : 
     253          63 :     out = make_tsvector(&prs);
     254             : 
     255          63 :     PG_RETURN_TSVECTOR(out);
     256             : }
     257             : 
     258             : Datum
     259           9 : to_tsvector(PG_FUNCTION_ARGS)
     260             : {
     261           9 :     text       *in = PG_GETARG_TEXT_PP(0);
     262             :     Oid         cfgId;
     263             : 
     264           9 :     cfgId = getTSCurrentConfig(true);
     265           9 :     PG_RETURN_DATUM(DirectFunctionCall2(to_tsvector_byid,
     266             :                                         ObjectIdGetDatum(cfgId),
     267             :                                         PointerGetDatum(in)));
     268             : }
     269             : 
     270             : Datum
     271           7 : jsonb_to_tsvector_byid(PG_FUNCTION_ARGS)
     272             : {
     273           7 :     Oid         cfgId = PG_GETARG_OID(0);
     274           7 :     Jsonb      *jb = PG_GETARG_JSONB(1);
     275             :     TSVector    result;
     276             :     TSVectorBuildState state;
     277             :     ParsedText  prs;
     278             : 
     279           7 :     prs.words = NULL;
     280           7 :     prs.curwords = 0;
     281           7 :     state.prs = &prs;
     282           7 :     state.cfgId = cfgId;
     283             : 
     284           7 :     iterate_jsonb_string_values(jb, &state, add_to_tsvector);
     285             : 
     286           7 :     PG_FREE_IF_COPY(jb, 1);
     287             : 
     288           7 :     result = make_tsvector(&prs);
     289             : 
     290           7 :     PG_RETURN_TSVECTOR(result);
     291             : }
     292             : 
     293             : Datum
     294           5 : jsonb_to_tsvector(PG_FUNCTION_ARGS)
     295             : {
     296           5 :     Jsonb      *jb = PG_GETARG_JSONB(0);
     297             :     Oid         cfgId;
     298             : 
     299           5 :     cfgId = getTSCurrentConfig(true);
     300           5 :     PG_RETURN_DATUM(DirectFunctionCall2(jsonb_to_tsvector_byid,
     301             :                                         ObjectIdGetDatum(cfgId),
     302             :                                         JsonbGetDatum(jb)));
     303             : }
     304             : 
     305             : Datum
     306           7 : json_to_tsvector_byid(PG_FUNCTION_ARGS)
     307             : {
     308           7 :     Oid         cfgId = PG_GETARG_OID(0);
     309           7 :     text       *json = PG_GETARG_TEXT_P(1);
     310             :     TSVector    result;
     311             :     TSVectorBuildState state;
     312             :     ParsedText  prs;
     313             : 
     314           7 :     prs.words = NULL;
     315           7 :     prs.curwords = 0;
     316           7 :     state.prs = &prs;
     317           7 :     state.cfgId = cfgId;
     318             : 
     319           7 :     iterate_json_string_values(json, &state, add_to_tsvector);
     320             : 
     321           7 :     PG_FREE_IF_COPY(json, 1);
     322             : 
     323           7 :     result = make_tsvector(&prs);
     324             : 
     325           7 :     PG_RETURN_TSVECTOR(result);
     326             : }
     327             : 
     328             : Datum
     329           5 : json_to_tsvector(PG_FUNCTION_ARGS)
     330             : {
     331           5 :     text       *json = PG_GETARG_TEXT_P(0);
     332             :     Oid         cfgId;
     333             : 
     334           5 :     cfgId = getTSCurrentConfig(true);
     335           5 :     PG_RETURN_DATUM(DirectFunctionCall2(json_to_tsvector_byid,
     336             :                                         ObjectIdGetDatum(cfgId),
     337             :                                         PointerGetDatum(json)));
     338             : }
     339             : 
     340             : /*
     341             :  * Parse lexemes in an element of a json(b) value, add to TSVectorBuildState.
     342             :  */
     343             : static void
     344          20 : add_to_tsvector(void *_state, char *elem_value, int elem_len)
     345             : {
     346          20 :     TSVectorBuildState *state = (TSVectorBuildState *) _state;
     347          20 :     ParsedText *prs = state->prs;
     348             :     int32       prevwords;
     349             : 
     350          20 :     if (prs->words == NULL)
     351             :     {
     352             :         /*
     353             :          * First time through: initialize words array to a reasonable size.
     354             :          * (parsetext() will realloc it bigger as needed.)
     355             :          */
     356           8 :         prs->lenwords = Max(elem_len / 6, 64);
     357           8 :         prs->words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs->lenwords);
     358           8 :         prs->curwords = 0;
     359           8 :         prs->pos = 0;
     360             :     }
     361             : 
     362          20 :     prevwords = prs->curwords;
     363             : 
     364          20 :     parsetext(state->cfgId, prs, elem_value, elem_len);
     365             : 
     366             :     /*
     367             :      * If we extracted any words from this JSON element, advance pos to create
     368             :      * an artificial break between elements.  This is because we don't want
     369             :      * phrase searches to think that the last word in this element is adjacent
     370             :      * to the first word in the next one.
     371             :      */
     372          20 :     if (prs->curwords > prevwords)
     373          18 :         prs->pos += 1;
     374          20 : }
     375             : 
     376             : 
     377             : /*
     378             :  * to_tsquery
     379             :  */
     380             : 
     381             : 
     382             : /*
     383             :  * This function is used for morph parsing.
     384             :  *
     385             :  * The value is passed to parsetext which will call the right dictionary to
     386             :  * lexize the word. If it turns out to be a stopword, we push a QI_VALSTOP
     387             :  * to the stack.
     388             :  *
     389             :  * All words belonging to the same variant are pushed as an ANDed list,
     390             :  * and different variants are ORed together.
     391             :  */
     392             : static void
     393         312 : pushval_morph(Datum opaque, TSQueryParserState state, char *strval, int lenval, int16 weight, bool prefix)
     394             : {
     395         312 :     int32       count = 0;
     396             :     ParsedText  prs;
     397             :     uint32      variant,
     398         312 :                 pos = 0,
     399         312 :                 cntvar = 0,
     400         312 :                 cntpos = 0,
     401         312 :                 cnt = 0;
     402         312 :     MorphOpaque *data = (MorphOpaque *) DatumGetPointer(opaque);
     403             : 
     404         312 :     prs.lenwords = 4;
     405         312 :     prs.curwords = 0;
     406         312 :     prs.pos = 0;
     407         312 :     prs.words = (ParsedWord *) palloc(sizeof(ParsedWord) * prs.lenwords);
     408             : 
     409         312 :     parsetext(data->cfg_id, &prs, strval, lenval);
     410             : 
     411         312 :     if (prs.curwords > 0)
     412             :     {
     413         763 :         while (count < prs.curwords)
     414             :         {
     415             :             /*
     416             :              * Were any stop words removed? If so, fill empty positions with
     417             :              * placeholders linked by an appropriate operator.
     418             :              */
     419         271 :             if (pos > 0 && pos + 1 < prs.words[count].pos.pos)
     420             :             {
     421          18 :                 while (pos + 1 < prs.words[count].pos.pos)
     422             :                 {
     423             :                     /* put placeholders for each missing stop word */
     424           8 :                     pushStop(state);
     425           8 :                     if (cntpos)
     426           8 :                         pushOperator(state, data->qoperator, 1);
     427           8 :                     cntpos++;
     428           8 :                     pos++;
     429             :                 }
     430             :             }
     431             : 
     432             :             /* save current word's position */
     433         271 :             pos = prs.words[count].pos.pos;
     434             : 
     435             :             /* Go through all variants obtained from this token */
     436         271 :             cntvar = 0;
     437         825 :             while (count < prs.curwords && pos == prs.words[count].pos.pos)
     438             :             {
     439         283 :                 variant = prs.words[count].nvariant;
     440             : 
     441             :                 /* Push all words belonging to the same variant */
     442         283 :                 cnt = 0;
     443        1217 :                 while (count < prs.curwords &&
     444         663 :                        pos == prs.words[count].pos.pos &&
     445         319 :                        variant == prs.words[count].nvariant)
     446             :                 {
     447         921 :                     pushValue(state,
     448         307 :                               prs.words[count].word,
     449         307 :                               prs.words[count].len,
     450             :                               weight,
     451         307 :                               ((prs.words[count].flags & TSL_PREFIX) || prefix));
     452         307 :                     pfree(prs.words[count].word);
     453         307 :                     if (cnt)
     454          24 :                         pushOperator(state, OP_AND, 0);
     455         307 :                     cnt++;
     456         307 :                     count++;
     457             :                 }
     458             : 
     459         283 :                 if (cntvar)
     460          12 :                     pushOperator(state, OP_OR, 0);
     461         283 :                 cntvar++;
     462             :             }
     463             : 
     464         271 :             if (cntpos)
     465             :             {
     466             :                 /* distance may be useful */
     467          25 :                 pushOperator(state, data->qoperator, 1);
     468             :             }
     469             : 
     470         271 :             cntpos++;
     471             :         }
     472             : 
     473         246 :         pfree(prs.words);
     474             : 
     475             :     }
     476             :     else
     477          66 :         pushStop(state);
     478         312 : }
     479             : 
     480             : Datum
     481         114 : to_tsquery_byid(PG_FUNCTION_ARGS)
     482             : {
     483         114 :     text       *in = PG_GETARG_TEXT_PP(1);
     484             :     TSQuery     query;
     485             :     MorphOpaque data;
     486             : 
     487         114 :     data.cfg_id = PG_GETARG_OID(0);
     488         114 :     data.qoperator = OP_AND;
     489             : 
     490         114 :     query = parse_tsquery(text_to_cstring(in),
     491             :                           pushval_morph,
     492             :                           PointerGetDatum(&data),
     493             :                           false);
     494             : 
     495         114 :     PG_RETURN_TSQUERY(query);
     496             : }
     497             : 
     498             : Datum
     499          22 : to_tsquery(PG_FUNCTION_ARGS)
     500             : {
     501          22 :     text       *in = PG_GETARG_TEXT_PP(0);
     502             :     Oid         cfgId;
     503             : 
     504          22 :     cfgId = getTSCurrentConfig(true);
     505          22 :     PG_RETURN_DATUM(DirectFunctionCall2(to_tsquery_byid,
     506             :                                         ObjectIdGetDatum(cfgId),
     507             :                                         PointerGetDatum(in)));
     508             : }
     509             : 
     510             : Datum
     511          10 : plainto_tsquery_byid(PG_FUNCTION_ARGS)
     512             : {
     513          10 :     text       *in = PG_GETARG_TEXT_PP(1);
     514             :     TSQuery     query;
     515             :     MorphOpaque data;
     516             : 
     517          10 :     data.cfg_id = PG_GETARG_OID(0);
     518          10 :     data.qoperator = OP_AND;
     519             : 
     520          10 :     query = parse_tsquery(text_to_cstring(in),
     521             :                           pushval_morph,
     522             :                           PointerGetDatum(&data),
     523             :                           true);
     524             : 
     525          10 :     PG_RETURN_POINTER(query);
     526             : }
     527             : 
     528             : Datum
     529           2 : plainto_tsquery(PG_FUNCTION_ARGS)
     530             : {
     531           2 :     text       *in = PG_GETARG_TEXT_PP(0);
     532             :     Oid         cfgId;
     533             : 
     534           2 :     cfgId = getTSCurrentConfig(true);
     535           2 :     PG_RETURN_DATUM(DirectFunctionCall2(plainto_tsquery_byid,
     536             :                                         ObjectIdGetDatum(cfgId),
     537             :                                         PointerGetDatum(in)));
     538             : }
     539             : 
     540             : 
     541             : Datum
     542           5 : phraseto_tsquery_byid(PG_FUNCTION_ARGS)
     543             : {
     544           5 :     text       *in = PG_GETARG_TEXT_PP(1);
     545             :     TSQuery     query;
     546             :     MorphOpaque data;
     547             : 
     548           5 :     data.cfg_id = PG_GETARG_OID(0);
     549           5 :     data.qoperator = OP_PHRASE;
     550             : 
     551           5 :     query = parse_tsquery(text_to_cstring(in),
     552             :                           pushval_morph,
     553             :                           PointerGetDatum(&data),
     554             :                           true);
     555             : 
     556           5 :     PG_RETURN_TSQUERY(query);
     557             : }
     558             : 
     559             : Datum
     560           0 : phraseto_tsquery(PG_FUNCTION_ARGS)
     561             : {
     562           0 :     text       *in = PG_GETARG_TEXT_PP(0);
     563             :     Oid         cfgId;
     564             : 
     565           0 :     cfgId = getTSCurrentConfig(true);
     566           0 :     PG_RETURN_DATUM(DirectFunctionCall2(phraseto_tsquery_byid,
     567             :                                         ObjectIdGetDatum(cfgId),
     568             :                                         PointerGetDatum(in)));
     569             : }

Generated by: LCOV version 1.11