LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsvector.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 165 241 68.5 %
Date: 2019-06-19 14:06:47 Functions: 6 9 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tsvector.c
       4             :  *    I/O functions for tsvector
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/tsvector.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "libpq/pqformat.h"
      18             : #include "tsearch/ts_locale.h"
      19             : #include "tsearch/ts_utils.h"
      20             : #include "utils/builtins.h"
      21             : #include "utils/memutils.h"
      22             : 
      23             : typedef struct
      24             : {
      25             :     WordEntry   entry;          /* must be first! */
      26             :     WordEntryPos *pos;
      27             :     int         poslen;         /* number of elements in pos */
      28             : } WordEntryIN;
      29             : 
      30             : 
      31             : /* Compare two WordEntryPos values for qsort */
      32             : int
      33         496 : compareWordEntryPos(const void *a, const void *b)
      34             : {
      35         496 :     int         apos = WEP_GETPOS(*(const WordEntryPos *) a);
      36         496 :     int         bpos = WEP_GETPOS(*(const WordEntryPos *) b);
      37             : 
      38         496 :     if (apos == bpos)
      39          16 :         return 0;
      40         480 :     return (apos > bpos) ? 1 : -1;
      41             : }
      42             : 
      43             : /*
      44             :  * Removes duplicate pos entries. If there's two entries with same pos
      45             :  * but different weight, the higher weight is retained.
      46             :  *
      47             :  * Returns new length.
      48             :  */
      49             : static int
      50         904 : uniquePos(WordEntryPos *a, int l)
      51             : {
      52             :     WordEntryPos *ptr,
      53             :                *res;
      54             : 
      55         904 :     if (l <= 1)
      56         728 :         return l;
      57             : 
      58         176 :     qsort((void *) a, l, sizeof(WordEntryPos), compareWordEntryPos);
      59             : 
      60         176 :     res = a;
      61         176 :     ptr = a + 1;
      62         792 :     while (ptr - a < l)
      63             :     {
      64         440 :         if (WEP_GETPOS(*ptr) != WEP_GETPOS(*res))
      65             :         {
      66         424 :             res++;
      67         424 :             *res = *ptr;
      68         848 :             if (res - a >= MAXNUMPOS - 1 ||
      69         424 :                 WEP_GETPOS(*res) == MAXENTRYPOS - 1)
      70             :                 break;
      71             :         }
      72          16 :         else if (WEP_GETWEIGHT(*ptr) > WEP_GETWEIGHT(*res))
      73           4 :             WEP_SETWEIGHT(*res, WEP_GETWEIGHT(*ptr));
      74         440 :         ptr++;
      75             :     }
      76             : 
      77         176 :     return res + 1 - a;
      78             : }
      79             : 
      80             : /* Compare two WordEntryIN values for qsort */
      81             : static int
      82      715398 : compareentry(const void *va, const void *vb, void *arg)
      83             : {
      84      715398 :     const WordEntryIN *a = (const WordEntryIN *) va;
      85      715398 :     const WordEntryIN *b = (const WordEntryIN *) vb;
      86      715398 :     char       *BufferStr = (char *) arg;
      87             : 
      88     1430796 :     return tsCompareString(&BufferStr[a->entry.pos], a->entry.len,
      89     1430796 :                            &BufferStr[b->entry.pos], b->entry.len,
      90             :                            false);
      91             : }
      92             : 
      93             : /*
      94             :  * Sort an array of WordEntryIN, remove duplicates.
      95             :  * *outbuflen receives the amount of space needed for strings and positions.
      96             :  */
      97             : static int
      98        2426 : uniqueentry(WordEntryIN *a, int l, char *buf, int *outbuflen)
      99             : {
     100             :     int         buflen;
     101             :     WordEntryIN *ptr,
     102             :                *res;
     103             : 
     104             :     Assert(l >= 1);
     105             : 
     106        2426 :     if (l > 1)
     107        2394 :         qsort_arg((void *) a, l, sizeof(WordEntryIN), compareentry,
     108             :                   (void *) buf);
     109             : 
     110        2426 :     buflen = 0;
     111        2426 :     res = a;
     112        2426 :     ptr = a + 1;
     113      122976 :     while (ptr - a < l)
     114             :     {
     115      235534 :         if (!(ptr->entry.len == res->entry.len &&
     116      117410 :               strncmp(&buf[ptr->entry.pos], &buf[res->entry.pos],
     117      117410 :                       res->entry.len) == 0))
     118             :         {
     119             :             /* done accumulating data into *res, count space needed */
     120      114440 :             buflen += res->entry.len;
     121      114440 :             if (res->entry.haspos)
     122             :             {
     123         620 :                 res->poslen = uniquePos(res->pos, res->poslen);
     124         620 :                 buflen = SHORTALIGN(buflen);
     125         620 :                 buflen += res->poslen * sizeof(WordEntryPos) + sizeof(uint16);
     126             :             }
     127      114440 :             res++;
     128      228880 :             if (res != ptr)
     129       59292 :                 memcpy(res, ptr, sizeof(WordEntryIN));
     130             :         }
     131        3684 :         else if (ptr->entry.haspos)
     132             :         {
     133          28 :             if (res->entry.haspos)
     134             :             {
     135             :                 /* append ptr's positions to res's positions */
     136          24 :                 int         newlen = ptr->poslen + res->poslen;
     137             : 
     138          24 :                 res->pos = (WordEntryPos *)
     139          24 :                     repalloc(res->pos, newlen * sizeof(WordEntryPos));
     140          24 :                 memcpy(&res->pos[res->poslen], ptr->pos,
     141          24 :                        ptr->poslen * sizeof(WordEntryPos));
     142          24 :                 res->poslen = newlen;
     143          24 :                 pfree(ptr->pos);
     144             :             }
     145             :             else
     146             :             {
     147             :                 /* just give ptr's positions to pos */
     148           4 :                 res->entry.haspos = 1;
     149           4 :                 res->pos = ptr->pos;
     150           4 :                 res->poslen = ptr->poslen;
     151             :             }
     152             :         }
     153      118124 :         ptr++;
     154             :     }
     155             : 
     156             :     /* count space needed for last item */
     157        2426 :     buflen += res->entry.len;
     158        2426 :     if (res->entry.haspos)
     159             :     {
     160         284 :         res->poslen = uniquePos(res->pos, res->poslen);
     161         284 :         buflen = SHORTALIGN(buflen);
     162         284 :         buflen += res->poslen * sizeof(WordEntryPos) + sizeof(uint16);
     163             :     }
     164             : 
     165        2426 :     *outbuflen = buflen;
     166        2426 :     return res + 1 - a;
     167             : }
     168             : 
     169             : static int
     170           0 : WordEntryCMP(WordEntry *a, WordEntry *b, char *buf)
     171             : {
     172           0 :     return compareentry(a, b, buf);
     173             : }
     174             : 
     175             : 
     176             : Datum
     177        2458 : tsvectorin(PG_FUNCTION_ARGS)
     178             : {
     179        2458 :     char       *buf = PG_GETARG_CSTRING(0);
     180             :     TSVectorParseState state;
     181             :     WordEntryIN *arr;
     182             :     int         totallen;
     183             :     int         arrlen;         /* allocated size of arr */
     184             :     WordEntry  *inarr;
     185        2458 :     int         len = 0;
     186             :     TSVector    in;
     187             :     int         i;
     188             :     char       *token;
     189             :     int         toklen;
     190             :     WordEntryPos *pos;
     191             :     int         poslen;
     192             :     char       *strbuf;
     193             :     int         stroff;
     194             : 
     195             :     /*
     196             :      * Tokens are appended to tmpbuf, cur is a pointer to the end of used
     197             :      * space in tmpbuf.
     198             :      */
     199             :     char       *tmpbuf;
     200             :     char       *cur;
     201        2458 :     int         buflen = 256;   /* allocated size of tmpbuf */
     202             : 
     203        2458 :     state = init_tsvector_parser(buf, 0);
     204             : 
     205        2458 :     arrlen = 64;
     206        2458 :     arr = (WordEntryIN *) palloc(sizeof(WordEntryIN) * arrlen);
     207        2458 :     cur = tmpbuf = (char *) palloc(buflen);
     208             : 
     209      125466 :     while (gettoken_tsvector(state, &token, &toklen, &pos, &poslen, NULL))
     210             :     {
     211      120550 :         if (toklen >= MAXSTRLEN)
     212           0 :             ereport(ERROR,
     213             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     214             :                      errmsg("word is too long (%ld bytes, max %ld bytes)",
     215             :                             (long) toklen,
     216             :                             (long) (MAXSTRLEN - 1))));
     217             : 
     218      120550 :         if (cur - tmpbuf > MAXSTRPOS)
     219           0 :             ereport(ERROR,
     220             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     221             :                      errmsg("string is too long for tsvector (%ld bytes, max %ld bytes)",
     222             :                             (long) (cur - tmpbuf), (long) MAXSTRPOS)));
     223             : 
     224             :         /*
     225             :          * Enlarge buffers if needed
     226             :          */
     227      120550 :         if (len >= arrlen)
     228             :         {
     229         876 :             arrlen *= 2;
     230         876 :             arr = (WordEntryIN *)
     231         876 :                 repalloc((void *) arr, sizeof(WordEntryIN) * arrlen);
     232             :         }
     233      241100 :         while ((cur - tmpbuf) + toklen >= buflen)
     234             :         {
     235           0 :             int         dist = cur - tmpbuf;
     236             : 
     237           0 :             buflen *= 2;
     238           0 :             tmpbuf = (char *) repalloc((void *) tmpbuf, buflen);
     239           0 :             cur = tmpbuf + dist;
     240             :         }
     241      120550 :         arr[len].entry.len = toklen;
     242      120550 :         arr[len].entry.pos = cur - tmpbuf;
     243      120550 :         memcpy((void *) cur, (void *) token, toklen);
     244      120550 :         cur += toklen;
     245             : 
     246      120550 :         if (poslen != 0)
     247             :         {
     248         928 :             arr[len].entry.haspos = 1;
     249         928 :             arr[len].pos = pos;
     250         928 :             arr[len].poslen = poslen;
     251             :         }
     252             :         else
     253             :         {
     254      119622 :             arr[len].entry.haspos = 0;
     255      119622 :             arr[len].pos = NULL;
     256      119622 :             arr[len].poslen = 0;
     257             :         }
     258      120550 :         len++;
     259             :     }
     260             : 
     261        2458 :     close_tsvector_parser(state);
     262             : 
     263        2458 :     if (len > 0)
     264        2426 :         len = uniqueentry(arr, len, tmpbuf, &buflen);
     265             :     else
     266          32 :         buflen = 0;
     267             : 
     268        2458 :     if (buflen > MAXSTRPOS)
     269           0 :         ereport(ERROR,
     270             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     271             :                  errmsg("string is too long for tsvector (%d bytes, max %d bytes)", buflen, MAXSTRPOS)));
     272             : 
     273        2458 :     totallen = CALCDATASIZE(len, buflen);
     274        2458 :     in = (TSVector) palloc0(totallen);
     275        2458 :     SET_VARSIZE(in, totallen);
     276        2458 :     in->size = len;
     277        2458 :     inarr = ARRPTR(in);
     278        2458 :     strbuf = STRPTR(in);
     279        2458 :     stroff = 0;
     280      119324 :     for (i = 0; i < len; i++)
     281             :     {
     282      116866 :         memcpy(strbuf + stroff, &tmpbuf[arr[i].entry.pos], arr[i].entry.len);
     283      116866 :         arr[i].entry.pos = stroff;
     284      116866 :         stroff += arr[i].entry.len;
     285      116866 :         if (arr[i].entry.haspos)
     286             :         {
     287         904 :             if (arr[i].poslen > 0xFFFF)
     288           0 :                 elog(ERROR, "positions array too long");
     289             : 
     290             :             /* Copy number of positions */
     291         904 :             stroff = SHORTALIGN(stroff);
     292         904 :             *(uint16 *) (strbuf + stroff) = (uint16) arr[i].poslen;
     293         904 :             stroff += sizeof(uint16);
     294             : 
     295             :             /* Copy positions */
     296         904 :             memcpy(strbuf + stroff, arr[i].pos, arr[i].poslen * sizeof(WordEntryPos));
     297         904 :             stroff += arr[i].poslen * sizeof(WordEntryPos);
     298             : 
     299         904 :             pfree(arr[i].pos);
     300             :         }
     301      116866 :         inarr[i] = arr[i].entry;
     302             :     }
     303             : 
     304             :     Assert((strbuf + stroff - (char *) in) == totallen);
     305             : 
     306        2458 :     PG_RETURN_TSVECTOR(in);
     307             : }
     308             : 
     309             : Datum
     310        2480 : tsvectorout(PG_FUNCTION_ARGS)
     311             : {
     312        2480 :     TSVector    out = PG_GETARG_TSVECTOR(0);
     313             :     char       *outbuf;
     314             :     int32       i,
     315        2480 :                 lenbuf = 0,
     316             :                 pp;
     317        2480 :     WordEntry  *ptr = ARRPTR(out);
     318             :     char       *curbegin,
     319             :                *curin,
     320             :                *curout;
     321             : 
     322        2480 :     lenbuf = out->size * 2 /* '' */ + out->size - 1 /* space */ + 2 /* \0 */ ;
     323      119504 :     for (i = 0; i < out->size; i++)
     324             :     {
     325      117024 :         lenbuf += ptr[i].len * 2 * pg_database_encoding_max_length() /* for escape */ ;
     326      117024 :         if (ptr[i].haspos)
     327        1478 :             lenbuf += 1 /* : */ + 7 /* int2 + , + weight */ * POSDATALEN(out, &(ptr[i]));
     328             :     }
     329             : 
     330        2480 :     curout = outbuf = (char *) palloc(lenbuf);
     331      119504 :     for (i = 0; i < out->size; i++)
     332             :     {
     333      117024 :         curbegin = curin = STRPTR(out) + ptr->pos;
     334      117024 :         if (i != 0)
     335      114656 :             *curout++ = ' ';
     336      117024 :         *curout++ = '\'';
     337      472530 :         while (curin - curbegin < ptr->len)
     338             :         {
     339      238482 :             int         len = pg_mblen(curin);
     340             : 
     341      238482 :             if (t_iseq(curin, '\''))
     342          18 :                 *curout++ = '\'';
     343      238464 :             else if (t_iseq(curin, '\\'))
     344          60 :                 *curout++ = '\\';
     345             : 
     346      715446 :             while (len--)
     347      238482 :                 *curout++ = *curin++;
     348             :         }
     349             : 
     350      117024 :         *curout++ = '\'';
     351      117024 :         if ((pp = POSDATALEN(out, ptr)) != 0)
     352             :         {
     353             :             WordEntryPos *wptr;
     354             : 
     355        1478 :             *curout++ = ':';
     356        1478 :             wptr = POSDATAPTR(out, ptr);
     357        4806 :             while (pp)
     358             :             {
     359        1850 :                 curout += sprintf(curout, "%d", WEP_GETPOS(*wptr));
     360        1850 :                 switch (WEP_GETWEIGHT(*wptr))
     361             :                 {
     362             :                     case 3:
     363          76 :                         *curout++ = 'A';
     364          76 :                         break;
     365             :                     case 2:
     366          44 :                         *curout++ = 'B';
     367          44 :                         break;
     368             :                     case 1:
     369         152 :                         *curout++ = 'C';
     370         152 :                         break;
     371             :                     case 0:
     372             :                     default:
     373        1578 :                         break;
     374             :                 }
     375             : 
     376        1850 :                 if (pp > 1)
     377         372 :                     *curout++ = ',';
     378        1850 :                 pp--;
     379        1850 :                 wptr++;
     380             :             }
     381             :         }
     382      117024 :         ptr++;
     383             :     }
     384             : 
     385        2480 :     *curout = '\0';
     386        2480 :     PG_FREE_IF_COPY(out, 0);
     387        2480 :     PG_RETURN_CSTRING(outbuf);
     388             : }
     389             : 
     390             : /*
     391             :  * Binary Input / Output functions. The binary format is as follows:
     392             :  *
     393             :  * uint32   number of lexemes
     394             :  *
     395             :  * for each lexeme:
     396             :  *      lexeme text in client encoding, null-terminated
     397             :  *      uint16  number of positions
     398             :  *      for each position:
     399             :  *          uint16 WordEntryPos
     400             :  */
     401             : 
     402             : Datum
     403           0 : tsvectorsend(PG_FUNCTION_ARGS)
     404             : {
     405           0 :     TSVector    vec = PG_GETARG_TSVECTOR(0);
     406             :     StringInfoData buf;
     407             :     int         i,
     408             :                 j;
     409           0 :     WordEntry  *weptr = ARRPTR(vec);
     410             : 
     411           0 :     pq_begintypsend(&buf);
     412             : 
     413           0 :     pq_sendint32(&buf, vec->size);
     414           0 :     for (i = 0; i < vec->size; i++)
     415             :     {
     416             :         uint16      npos;
     417             : 
     418             :         /*
     419             :          * the strings in the TSVector array are not null-terminated, so we
     420             :          * have to send the null-terminator separately
     421             :          */
     422           0 :         pq_sendtext(&buf, STRPTR(vec) + weptr->pos, weptr->len);
     423           0 :         pq_sendbyte(&buf, '\0');
     424             : 
     425           0 :         npos = POSDATALEN(vec, weptr);
     426           0 :         pq_sendint16(&buf, npos);
     427             : 
     428           0 :         if (npos > 0)
     429             :         {
     430           0 :             WordEntryPos *wepptr = POSDATAPTR(vec, weptr);
     431             : 
     432           0 :             for (j = 0; j < npos; j++)
     433           0 :                 pq_sendint16(&buf, wepptr[j]);
     434             :         }
     435           0 :         weptr++;
     436             :     }
     437             : 
     438           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     439             : }
     440             : 
     441             : Datum
     442           0 : tsvectorrecv(PG_FUNCTION_ARGS)
     443             : {
     444           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     445             :     TSVector    vec;
     446             :     int         i;
     447             :     int32       nentries;
     448             :     int         datalen;        /* number of bytes used in the variable size
     449             :                                  * area after fixed size TSVector header and
     450             :                                  * WordEntries */
     451             :     Size        hdrlen;
     452             :     Size        len;            /* allocated size of vec */
     453           0 :     bool        needSort = false;
     454             : 
     455           0 :     nentries = pq_getmsgint(buf, sizeof(int32));
     456           0 :     if (nentries < 0 || nentries > (MaxAllocSize / sizeof(WordEntry)))
     457           0 :         elog(ERROR, "invalid size of tsvector");
     458             : 
     459           0 :     hdrlen = DATAHDRSIZE + sizeof(WordEntry) * nentries;
     460             : 
     461           0 :     len = hdrlen * 2;           /* times two to make room for lexemes */
     462           0 :     vec = (TSVector) palloc0(len);
     463           0 :     vec->size = nentries;
     464             : 
     465           0 :     datalen = 0;
     466           0 :     for (i = 0; i < nentries; i++)
     467             :     {
     468             :         const char *lexeme;
     469             :         uint16      npos;
     470             :         size_t      lex_len;
     471             : 
     472           0 :         lexeme = pq_getmsgstring(buf);
     473           0 :         npos = (uint16) pq_getmsgint(buf, sizeof(uint16));
     474             : 
     475             :         /* sanity checks */
     476             : 
     477           0 :         lex_len = strlen(lexeme);
     478           0 :         if (lex_len > MAXSTRLEN)
     479           0 :             elog(ERROR, "invalid tsvector: lexeme too long");
     480             : 
     481           0 :         if (datalen > MAXSTRPOS)
     482           0 :             elog(ERROR, "invalid tsvector: maximum total lexeme length exceeded");
     483             : 
     484           0 :         if (npos > MAXNUMPOS)
     485           0 :             elog(ERROR, "unexpected number of tsvector positions");
     486             : 
     487             :         /*
     488             :          * Looks valid. Fill the WordEntry struct, and copy lexeme.
     489             :          *
     490             :          * But make sure the buffer is large enough first.
     491             :          */
     492           0 :         while (hdrlen + SHORTALIGN(datalen + lex_len) +
     493           0 :                (npos + 1) * sizeof(WordEntryPos) >= len)
     494             :         {
     495           0 :             len *= 2;
     496           0 :             vec = (TSVector) repalloc(vec, len);
     497             :         }
     498             : 
     499           0 :         vec->entries[i].haspos = (npos > 0) ? 1 : 0;
     500           0 :         vec->entries[i].len = lex_len;
     501           0 :         vec->entries[i].pos = datalen;
     502             : 
     503           0 :         memcpy(STRPTR(vec) + datalen, lexeme, lex_len);
     504             : 
     505           0 :         datalen += lex_len;
     506             : 
     507           0 :         if (i > 0 && WordEntryCMP(&vec->entries[i],
     508           0 :                                   &vec->entries[i - 1],
     509           0 :                                   STRPTR(vec)) <= 0)
     510           0 :             needSort = true;
     511             : 
     512             :         /* Receive positions */
     513           0 :         if (npos > 0)
     514             :         {
     515             :             uint16      j;
     516             :             WordEntryPos *wepptr;
     517             : 
     518             :             /*
     519             :              * Pad to 2-byte alignment if necessary. Though we used palloc0
     520             :              * for the initial allocation, subsequent repalloc'd memory areas
     521             :              * are not initialized to zero.
     522             :              */
     523           0 :             if (datalen != SHORTALIGN(datalen))
     524             :             {
     525           0 :                 *(STRPTR(vec) + datalen) = '\0';
     526           0 :                 datalen = SHORTALIGN(datalen);
     527             :             }
     528             : 
     529           0 :             memcpy(STRPTR(vec) + datalen, &npos, sizeof(uint16));
     530             : 
     531           0 :             wepptr = POSDATAPTR(vec, &vec->entries[i]);
     532           0 :             for (j = 0; j < npos; j++)
     533             :             {
     534           0 :                 wepptr[j] = (WordEntryPos) pq_getmsgint(buf, sizeof(WordEntryPos));
     535           0 :                 if (j > 0 && WEP_GETPOS(wepptr[j]) <= WEP_GETPOS(wepptr[j - 1]))
     536           0 :                     elog(ERROR, "position information is misordered");
     537             :             }
     538             : 
     539           0 :             datalen += (npos + 1) * sizeof(WordEntry);
     540             :         }
     541             :     }
     542             : 
     543           0 :     SET_VARSIZE(vec, hdrlen + datalen);
     544             : 
     545           0 :     if (needSort)
     546           0 :         qsort_arg((void *) ARRPTR(vec), vec->size, sizeof(WordEntry),
     547           0 :                   compareentry, (void *) STRPTR(vec));
     548             : 
     549           0 :     PG_RETURN_TSVECTOR(vec);
     550             : }

Generated by: LCOV version 1.13