Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * tsvector.c
4 : : * I/O functions for tsvector
5 : : *
6 : : * Portions Copyright (c) 1996-2026, 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 "common/int.h"
18 : : #include "libpq/pqformat.h"
19 : : #include "nodes/miscnodes.h"
20 : : #include "tsearch/ts_locale.h"
21 : : #include "tsearch/ts_utils.h"
22 : : #include "utils/fmgrprotos.h"
23 : : #include "utils/memutils.h"
24 : : #include "varatt.h"
25 : :
26 : : typedef struct
27 : : {
28 : : WordEntry entry; /* must be first, see compareentry */
29 : : WordEntryPos *pos;
30 : : int poslen; /* number of elements in pos */
31 : : } WordEntryIN;
32 : :
33 : :
34 : : /* Compare two WordEntryPos values for qsort */
35 : : int
3793 teodor@sigaev.ru 36 :CBC 674 : compareWordEntryPos(const void *a, const void *b)
37 : : {
6860 bruce@momjian.us 38 : 674 : int apos = WEP_GETPOS(*(const WordEntryPos *) a);
39 : 674 : int bpos = WEP_GETPOS(*(const WordEntryPos *) b);
40 : :
923 nathan@postgresql.or 41 : 674 : return pg_cmp_s32(apos, bpos);
42 : : }
43 : :
44 : : /*
45 : : * Removes duplicate pos entries. If there's two entries with same pos but
46 : : * different weight, the higher weight is retained, so we can't use
47 : : * qunique here.
48 : : *
49 : : * Returns new length.
50 : : */
51 : : static int
6860 bruce@momjian.us 52 : 6404 : uniquePos(WordEntryPos *a, int l)
53 : : {
54 : : WordEntryPos *ptr,
55 : : *res;
56 : :
6929 teodor@sigaev.ru 57 [ + + ]: 6404 : if (l <= 1)
6946 tgl@sss.pgh.pa.us 58 : 6048 : return l;
59 : :
1297 peter@eisentraut.org 60 : 356 : qsort(a, l, sizeof(WordEntryPos), compareWordEntryPos);
61 : :
6883 tgl@sss.pgh.pa.us 62 : 356 : res = a;
6946 63 : 356 : ptr = a + 1;
64 [ + + ]: 968 : while (ptr - a < l)
65 : : {
66 [ + + ]: 612 : if (WEP_GETPOS(*ptr) != WEP_GETPOS(*res))
67 : : {
68 : 596 : res++;
69 : 596 : *res = *ptr;
6883 70 [ + - ]: 596 : if (res - a >= MAXNUMPOS - 1 ||
71 [ + - ]: 596 : WEP_GETPOS(*res) == MAXENTRYPOS - 1)
72 : : break;
73 : : }
6946 74 [ + + ]: 16 : else if (WEP_GETWEIGHT(*ptr) > WEP_GETWEIGHT(*res))
75 : 4 : WEP_SETWEIGHT(*res, WEP_GETWEIGHT(*ptr));
76 : 612 : ptr++;
77 : : }
78 : :
79 : 356 : return res + 1 - a;
80 : : }
81 : :
82 : : /*
83 : : * Compare two WordEntry structs for qsort_arg. This can also be used on
84 : : * WordEntryIN structs, since those have WordEntry as their first field.
85 : : */
86 : : static int
6929 teodor@sigaev.ru 87 : 715003 : compareentry(const void *va, const void *vb, void *arg)
88 : : {
512 tgl@sss.pgh.pa.us 89 : 715003 : const WordEntry *a = (const WordEntry *) va;
90 : 715003 : const WordEntry *b = (const WordEntry *) vb;
6946 91 : 715003 : char *BufferStr = (char *) arg;
92 : :
512 93 : 1430006 : return tsCompareString(&BufferStr[a->pos], a->len,
94 : 715003 : &BufferStr[b->pos], b->len,
95 : : false);
96 : : }
97 : :
98 : : /*
99 : : * Sort an array of WordEntryIN, remove duplicates.
100 : : * *outbuflen receives the amount of space needed for strings and positions.
101 : : */
102 : : static int
6860 bruce@momjian.us 103 : 2470 : uniqueentry(WordEntryIN *a, int l, char *buf, int *outbuflen)
104 : : {
105 : : int buflen;
106 : : WordEntryIN *ptr,
107 : : *res;
108 : :
6929 teodor@sigaev.ru 109 [ - + ]: 2470 : Assert(l >= 1);
110 : :
6883 tgl@sss.pgh.pa.us 111 [ + + ]: 2470 : if (l > 1)
1297 peter@eisentraut.org 112 : 2402 : qsort_arg(a, l, sizeof(WordEntryIN), compareentry, buf);
113 : :
6883 tgl@sss.pgh.pa.us 114 : 2470 : buflen = 0;
6929 teodor@sigaev.ru 115 : 2470 : res = a;
6946 tgl@sss.pgh.pa.us 116 : 2470 : ptr = a + 1;
117 [ + + ]: 120552 : while (ptr - a < l)
118 : : {
119 [ + + ]: 118082 : if (!(ptr->entry.len == res->entry.len &&
6883 120 : 117367 : strncmp(&buf[ptr->entry.pos], &buf[res->entry.pos],
121 [ + + ]: 117367 : res->entry.len) == 0))
122 : : {
123 : : /* done accumulating data into *res, count space needed */
124 : 114398 : buflen += res->entry.len;
6946 125 [ + + ]: 114398 : if (res->entry.haspos)
126 : : {
6929 teodor@sigaev.ru 127 : 5996 : res->poslen = uniquePos(res->pos, res->poslen);
6883 tgl@sss.pgh.pa.us 128 : 5996 : buflen = SHORTALIGN(buflen);
129 : 5996 : buflen += res->poslen * sizeof(WordEntryPos) + sizeof(uint16);
130 : : }
6946 131 : 114398 : res++;
4556 heikki.linnakangas@i 132 [ + + ]: 114398 : if (res != ptr)
133 : 59048 : memcpy(res, ptr, sizeof(WordEntryIN));
134 : : }
6946 tgl@sss.pgh.pa.us 135 [ + + ]: 3684 : else if (ptr->entry.haspos)
136 : : {
137 [ + + ]: 212 : if (res->entry.haspos)
138 : : {
139 : : /* append ptr's positions to res's positions */
6860 bruce@momjian.us 140 : 208 : int newlen = ptr->poslen + res->poslen;
141 : :
10 michael@paquier.xyz 142 :GNC 208 : res->pos = repalloc_array(res->pos, WordEntryPos, newlen);
6883 tgl@sss.pgh.pa.us 143 :CBC 208 : memcpy(&res->pos[res->poslen], ptr->pos,
144 : 208 : ptr->poslen * sizeof(WordEntryPos));
6929 teodor@sigaev.ru 145 : 208 : res->poslen = newlen;
6946 tgl@sss.pgh.pa.us 146 : 208 : pfree(ptr->pos);
147 : : }
148 : : else
149 : : {
150 : : /* just give ptr's positions to pos */
151 : 4 : res->entry.haspos = 1;
152 : 4 : res->pos = ptr->pos;
6883 153 : 4 : res->poslen = ptr->poslen;
154 : : }
155 : : }
6946 156 : 118082 : ptr++;
157 : : }
158 : :
159 : : /* count space needed for last item */
6883 160 : 2470 : buflen += res->entry.len;
6946 161 [ + + ]: 2470 : if (res->entry.haspos)
162 : : {
6929 teodor@sigaev.ru 163 : 408 : res->poslen = uniquePos(res->pos, res->poslen);
6883 tgl@sss.pgh.pa.us 164 : 408 : buflen = SHORTALIGN(buflen);
165 : 408 : buflen += res->poslen * sizeof(WordEntryPos) + sizeof(uint16);
166 : : }
167 : :
168 : 2470 : *outbuflen = buflen;
6946 169 : 2470 : return res + 1 - a;
170 : : }
171 : :
172 : :
173 : : Datum
174 : 2514 : tsvectorin(PG_FUNCTION_ARGS)
175 : : {
176 : 2514 : char *buf = PG_GETARG_CSTRING(0);
1339 177 : 2514 : Node *escontext = fcinfo->context;
178 : : TSVectorParseState state;
179 : : WordEntryIN *arr;
180 : : int totallen;
181 : : int arrlen; /* allocated size of arr */
182 : : WordEntry *inarr;
6929 teodor@sigaev.ru 183 : 2514 : int len = 0;
184 : : TSVector in;
185 : : int i;
186 : : char *token;
187 : : int toklen;
188 : : WordEntryPos *pos;
189 : : int poslen;
190 : : char *strbuf;
191 : : int stroff;
192 : :
193 : : /*
194 : : * Tokens are appended to tmpbuf, cur is a pointer to the end of used
195 : : * space in tmpbuf.
196 : : */
197 : : char *tmpbuf;
198 : : char *cur;
6860 bruce@momjian.us 199 : 2514 : int buflen = 256; /* allocated size of tmpbuf */
200 : :
1339 tgl@sss.pgh.pa.us 201 : 2514 : state = init_tsvector_parser(buf, 0, escontext);
202 : :
6929 teodor@sigaev.ru 203 : 2514 : arrlen = 64;
260 michael@paquier.xyz 204 : 2514 : arr = palloc_array(WordEntryIN, arrlen);
205 : 2514 : cur = tmpbuf = palloc_array(char, buflen);
206 : :
6929 teodor@sigaev.ru 207 [ + + ]: 123066 : while (gettoken_tsvector(state, &token, &toklen, &pos, &poslen, NULL))
208 : : {
14 tgl@sss.pgh.pa.us 209 [ - + ]: 120552 : if (toklen > MAXSTRLEN)
1339 tgl@sss.pgh.pa.us 210 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
211 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
212 : : errmsg("word is too long (%d bytes, max %d bytes)",
213 : : toklen,
214 : : MAXSTRLEN)));
215 : :
6946 tgl@sss.pgh.pa.us 216 [ - + ]:CBC 120552 : if (cur - tmpbuf > MAXSTRPOS)
1339 tgl@sss.pgh.pa.us 217 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
218 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
219 : : errmsg("string is too long for tsvector (%zu bytes, max %zu bytes)",
220 : : (size_t) (cur - tmpbuf), (size_t) MAXSTRPOS)));
221 : :
222 : : /*
223 : : * Enlarge buffers if needed
224 : : */
6929 teodor@sigaev.ru 225 [ + + ]:CBC 120552 : if (len >= arrlen)
226 : : {
227 : 876 : arrlen *= 2;
10 michael@paquier.xyz 228 :GNC 876 : arr = repalloc_array(arr, WordEntryIN, arrlen);
229 : : }
6929 teodor@sigaev.ru 230 [ - + ]:CBC 120552 : while ((cur - tmpbuf) + toklen >= buflen)
231 : : {
6860 bruce@momjian.us 232 :UBC 0 : int dist = cur - tmpbuf;
233 : :
6929 teodor@sigaev.ru 234 : 0 : buflen *= 2;
1297 peter@eisentraut.org 235 : 0 : tmpbuf = (char *) repalloc(tmpbuf, buflen);
6929 teodor@sigaev.ru 236 : 0 : cur = tmpbuf + dist;
237 : : }
6929 teodor@sigaev.ru 238 :CBC 120552 : arr[len].entry.len = toklen;
6946 tgl@sss.pgh.pa.us 239 : 120552 : arr[len].entry.pos = cur - tmpbuf;
1297 peter@eisentraut.org 240 : 120552 : memcpy(cur, token, toklen);
6929 teodor@sigaev.ru 241 : 120552 : cur += toklen;
242 : :
243 [ + + ]: 120552 : if (poslen != 0)
244 : : {
6946 tgl@sss.pgh.pa.us 245 : 6612 : arr[len].entry.haspos = 1;
6929 teodor@sigaev.ru 246 : 6612 : arr[len].pos = pos;
247 : 6612 : arr[len].poslen = poslen;
248 : : }
249 : : else
250 : : {
6946 tgl@sss.pgh.pa.us 251 : 113940 : arr[len].entry.haspos = 0;
6883 252 : 113940 : arr[len].pos = NULL;
253 : 113940 : arr[len].poslen = 0;
254 : : }
6946 255 : 120552 : len++;
256 : : }
257 : :
6929 teodor@sigaev.ru 258 : 2510 : close_tsvector_parser(state);
259 : :
260 : : /* Did gettoken_tsvector fail? */
1339 tgl@sss.pgh.pa.us 261 [ + + + - : 2510 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
262 : 8 : PG_RETURN_NULL();
263 : :
6946 264 [ + + ]: 2502 : if (len > 0)
265 : 2470 : len = uniqueentry(arr, len, tmpbuf, &buflen);
266 : : else
267 : 32 : buflen = 0;
268 : :
6883 269 [ - + ]: 2502 : if (buflen > MAXSTRPOS)
1339 tgl@sss.pgh.pa.us 270 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
271 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
272 : : errmsg("string is too long for tsvector (%zu bytes, max %zu bytes)",
273 : : (size_t) buflen, (size_t) MAXSTRPOS)));
274 : :
6946 tgl@sss.pgh.pa.us 275 :CBC 2502 : totallen = CALCDATASIZE(len, buflen);
276 : 2502 : in = (TSVector) palloc0(totallen);
277 : 2502 : SET_VARSIZE(in, totallen);
278 : 2502 : in->size = len;
279 : 2502 : inarr = ARRPTR(in);
6883 280 : 2502 : strbuf = STRPTR(in);
281 : 2502 : stroff = 0;
6946 282 [ + + ]: 119370 : for (i = 0; i < len; i++)
283 : : {
6883 284 : 116868 : memcpy(strbuf + stroff, &tmpbuf[arr[i].entry.pos], arr[i].entry.len);
285 : 116868 : arr[i].entry.pos = stroff;
286 : 116868 : stroff += arr[i].entry.len;
6946 287 [ + + ]: 116868 : if (arr[i].entry.haspos)
288 : : {
289 : : /* This should be unreachable because of MAXNUMPOS restrictions */
6883 290 [ - + ]: 6404 : if (arr[i].poslen > 0xFFFF)
6929 teodor@sigaev.ru 291 [ # # ]:UBC 0 : elog(ERROR, "positions array too long");
292 : :
293 : : /* Copy number of positions */
6883 tgl@sss.pgh.pa.us 294 :CBC 6404 : stroff = SHORTALIGN(stroff);
295 : 6404 : *(uint16 *) (strbuf + stroff) = (uint16) arr[i].poslen;
296 : 6404 : stroff += sizeof(uint16);
297 : :
298 : : /* Copy positions */
299 : 6404 : memcpy(strbuf + stroff, arr[i].pos, arr[i].poslen * sizeof(WordEntryPos));
300 : 6404 : stroff += arr[i].poslen * sizeof(WordEntryPos);
301 : :
6946 302 : 6404 : pfree(arr[i].pos);
303 : : }
304 : 116868 : inarr[i] = arr[i].entry;
305 : : }
306 : :
6883 307 [ - + ]: 2502 : Assert((strbuf + stroff - (char *) in) == totallen);
308 : :
6946 309 : 2502 : PG_RETURN_TSVECTOR(in);
310 : : }
311 : :
312 : : Datum
313 : 2487 : tsvectorout(PG_FUNCTION_ARGS)
314 : : {
315 : 2487 : TSVector out = PG_GETARG_TSVECTOR(0);
316 : : char *outbuf;
317 : : int32 i,
318 : : pp;
319 : : size_t lenbuf;
320 : 2487 : WordEntry *ptr = ARRPTR(out);
321 : : char *curin,
322 : : *curout;
323 : : const char *curend;
324 : :
325 : 2487 : lenbuf = out->size * 2 /* '' */ + out->size - 1 /* space */ + 2 /* \0 */ ;
326 [ + + ]: 119484 : for (i = 0; i < out->size; i++)
327 : : {
17 328 : 116997 : lenbuf += ptr[i].len * 2 /* allow for escapes */ ;
6946 329 [ + + ]: 116997 : if (ptr[i].haspos)
330 : 6923 : lenbuf += 1 /* : */ + 7 /* int2 + , + weight */ * POSDATALEN(out, &(ptr[i]));
331 : : }
332 : :
333 : 2487 : curout = outbuf = (char *) palloc(lenbuf);
334 [ + + ]: 119484 : for (i = 0; i < out->size; i++)
335 : : {
232 tmunro@postgresql.or 336 : 116997 : curin = STRPTR(out) + ptr->pos;
337 : 116997 : curend = curin + ptr->len;
6946 tgl@sss.pgh.pa.us 338 [ + + ]: 116997 : if (i != 0)
339 : 114622 : *curout++ = ' ';
340 : 116997 : *curout++ = '\'';
232 tmunro@postgresql.or 341 [ + + ]: 355485 : while (curin < curend)
342 : : {
343 : 238488 : int len = pg_mblen_range(curin, curend);
344 : :
6946 tgl@sss.pgh.pa.us 345 [ + + ]: 238488 : if (t_iseq(curin, '\''))
346 : 17 : *curout++ = '\'';
6859 teodor@sigaev.ru 347 [ + + ]: 238471 : else if (t_iseq(curin, '\\'))
348 : 65 : *curout++ = '\\';
349 : :
6946 tgl@sss.pgh.pa.us 350 [ + + ]: 476976 : while (len--)
351 : 238488 : *curout++ = *curin++;
352 : : }
353 : :
354 : 116997 : *curout++ = '\'';
355 [ + + + + ]: 116997 : if ((pp = POSDATALEN(out, ptr)) != 0)
356 : : {
357 : : WordEntryPos *wptr;
358 : :
359 : 6923 : *curout++ = ':';
360 : 6923 : wptr = POSDATAPTR(out, ptr);
361 [ + + ]: 14402 : while (pp)
362 : : {
363 : 7479 : curout += sprintf(curout, "%d", WEP_GETPOS(*wptr));
364 [ + + + + ]: 7479 : switch (WEP_GETWEIGHT(*wptr))
365 : : {
366 : 76 : case 3:
367 : 76 : *curout++ = 'A';
368 : 76 : break;
369 : 44 : case 2:
370 : 44 : *curout++ = 'B';
371 : 44 : break;
372 : 152 : case 1:
373 : 152 : *curout++ = 'C';
374 : 152 : break;
375 : 7207 : case 0:
376 : : default:
377 : 7207 : break;
378 : : }
379 : :
380 [ + + ]: 7479 : if (pp > 1)
381 : 556 : *curout++ = ',';
382 : 7479 : pp--;
383 : 7479 : wptr++;
384 : : }
385 : : }
386 : 116997 : ptr++;
387 : : }
388 : :
389 : 2487 : *curout = '\0';
390 [ + + ]: 2487 : PG_FREE_IF_COPY(out, 0);
391 : 2487 : PG_RETURN_CSTRING(outbuf);
392 : : }
393 : :
394 : : /*
395 : : * Binary Input / Output functions. The binary format is as follows:
396 : : *
397 : : * uint32 number of lexemes
398 : : *
399 : : * for each lexeme:
400 : : * lexeme text in client encoding, null-terminated
401 : : * uint16 number of positions
402 : : * for each position:
403 : : * uint16 WordEntryPos
404 : : */
405 : :
406 : : Datum
6946 tgl@sss.pgh.pa.us 407 :UBC 0 : tsvectorsend(PG_FUNCTION_ARGS)
408 : : {
409 : 0 : TSVector vec = PG_GETARG_TSVECTOR(0);
410 : : StringInfoData buf;
411 : : int i,
412 : : j;
413 : 0 : WordEntry *weptr = ARRPTR(vec);
414 : :
415 : 0 : pq_begintypsend(&buf);
416 : :
3242 andres@anarazel.de 417 : 0 : pq_sendint32(&buf, vec->size);
6946 tgl@sss.pgh.pa.us 418 [ # # ]: 0 : for (i = 0; i < vec->size; i++)
419 : : {
420 : : uint16 npos;
421 : :
422 : : /*
423 : : * the strings in the TSVector array are not null-terminated, so we
424 : : * have to send the null-terminator separately
425 : : */
6929 teodor@sigaev.ru 426 : 0 : pq_sendtext(&buf, STRPTR(vec) + weptr->pos, weptr->len);
427 : 0 : pq_sendbyte(&buf, '\0');
428 : :
429 [ # # ]: 0 : npos = POSDATALEN(vec, weptr);
3242 andres@anarazel.de 430 : 0 : pq_sendint16(&buf, npos);
431 : :
6860 bruce@momjian.us 432 [ # # ]: 0 : if (npos > 0)
433 : : {
6946 tgl@sss.pgh.pa.us 434 : 0 : WordEntryPos *wepptr = POSDATAPTR(vec, weptr);
435 : :
6929 teodor@sigaev.ru 436 [ # # ]: 0 : for (j = 0; j < npos; j++)
3242 andres@anarazel.de 437 : 0 : pq_sendint16(&buf, wepptr[j]);
438 : : }
6946 tgl@sss.pgh.pa.us 439 : 0 : weptr++;
440 : : }
441 : :
442 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
443 : : }
444 : :
445 : : Datum
446 : 0 : tsvectorrecv(PG_FUNCTION_ARGS)
447 : : {
448 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
449 : : TSVector vec;
450 : : int i;
451 : : int32 nentries;
452 : : int datalen; /* number of bytes used in the variable size
453 : : * area after fixed size TSVector header and
454 : : * WordEntries */
455 : : Size hdrlen;
456 : : Size len; /* allocated size of vec */
6307 teodor@sigaev.ru 457 : 0 : bool needSort = false;
458 : :
6929 459 : 0 : nentries = pq_getmsgint(buf, sizeof(int32));
460 : :
461 : : /* We disallow empty lexemes, so more than MAXSTRPOS of them can't fit */
17 tgl@sss.pgh.pa.us 462 [ # # # # ]: 0 : if (nentries < 0 || nentries > MAXSTRPOS)
6946 463 [ # # ]: 0 : elog(ERROR, "invalid size of tsvector");
464 : :
6929 teodor@sigaev.ru 465 : 0 : hdrlen = DATAHDRSIZE + sizeof(WordEntry) * nentries;
466 : :
17 tgl@sss.pgh.pa.us 467 : 0 : len = hdrlen * 2; /* times two to make some room for lexemes */
6946 468 : 0 : vec = (TSVector) palloc0(len);
6929 teodor@sigaev.ru 469 : 0 : vec->size = nentries;
470 : :
471 : 0 : datalen = 0;
472 [ # # ]: 0 : for (i = 0; i < nentries; i++)
473 : : {
474 : : const char *lexeme;
475 : : uint16 npos;
476 : : size_t lex_len;
477 : :
478 : 0 : lexeme = pq_getmsgstring(buf);
479 : 0 : npos = (uint16) pq_getmsgint(buf, sizeof(uint16));
480 : :
481 : : /* sanity checks */
482 : :
483 : 0 : lex_len = strlen(lexeme);
17 tgl@sss.pgh.pa.us 484 [ # # ]: 0 : if (lex_len == 0)
485 [ # # ]: 0 : elog(ERROR, "invalid tsvector: empty lexeme");
6307 meskes@postgresql.or 486 [ # # ]: 0 : if (lex_len > MAXSTRLEN)
6847 tgl@sss.pgh.pa.us 487 [ # # ]: 0 : elog(ERROR, "invalid tsvector: lexeme too long");
488 : :
6929 teodor@sigaev.ru 489 [ # # ]: 0 : if (datalen > MAXSTRPOS)
6847 tgl@sss.pgh.pa.us 490 [ # # ]: 0 : elog(ERROR, "invalid tsvector: maximum total lexeme length exceeded");
491 : :
6929 teodor@sigaev.ru 492 [ # # ]: 0 : if (npos > MAXNUMPOS)
6847 tgl@sss.pgh.pa.us 493 [ # # ]: 0 : elog(ERROR, "unexpected number of tsvector positions");
494 : :
495 : : /*
496 : : * Looks valid. Fill the WordEntry struct, and copy lexeme.
497 : : *
498 : : * But make sure the buffer is large enough first.
499 : : */
6929 teodor@sigaev.ru 500 : 0 : while (hdrlen + SHORTALIGN(datalen + lex_len) +
1061 tgl@sss.pgh.pa.us 501 [ # # ]: 0 : sizeof(uint16) + npos * sizeof(WordEntryPos) >= len)
502 : : {
6946 503 : 0 : len *= 2;
504 : 0 : vec = (TSVector) repalloc(vec, len);
505 : : }
506 : :
6929 teodor@sigaev.ru 507 : 0 : vec->entries[i].haspos = (npos > 0) ? 1 : 0;
508 : 0 : vec->entries[i].len = lex_len;
509 : 0 : vec->entries[i].pos = datalen;
510 : :
511 : 0 : memcpy(STRPTR(vec) + datalen, lexeme, lex_len);
512 : :
513 : 0 : datalen += lex_len;
514 : :
512 tgl@sss.pgh.pa.us 515 [ # # # # ]: 0 : if (i > 0 && compareentry(&vec->entries[i],
6883 516 : 0 : &vec->entries[i - 1],
517 : 0 : STRPTR(vec)) <= 0)
6307 teodor@sigaev.ru 518 : 0 : needSort = true;
519 : :
520 : : /* Receive positions */
6929 521 [ # # ]: 0 : if (npos > 0)
522 : : {
523 : : uint16 j;
524 : : WordEntryPos *wepptr;
525 : :
526 : : /*
527 : : * Pad to 2-byte alignment if necessary. Though we used palloc0
528 : : * for the initial allocation, subsequent repalloc'd memory areas
529 : : * are not initialized to zero.
530 : : */
531 [ # # ]: 0 : if (datalen != SHORTALIGN(datalen))
532 : : {
533 : 0 : *(STRPTR(vec) + datalen) = '\0';
534 : 0 : datalen = SHORTALIGN(datalen);
535 : : }
536 : :
537 : 0 : memcpy(STRPTR(vec) + datalen, &npos, sizeof(uint16));
538 : :
539 : 0 : wepptr = POSDATAPTR(vec, &vec->entries[i]);
6946 tgl@sss.pgh.pa.us 540 [ # # ]: 0 : for (j = 0; j < npos; j++)
541 : : {
6929 teodor@sigaev.ru 542 : 0 : wepptr[j] = (WordEntryPos) pq_getmsgint(buf, sizeof(WordEntryPos));
6946 tgl@sss.pgh.pa.us 543 [ # # # # ]: 0 : if (j > 0 && WEP_GETPOS(wepptr[j]) <= WEP_GETPOS(wepptr[j - 1]))
6885 544 [ # # ]: 0 : elog(ERROR, "position information is misordered");
545 : : }
546 : :
1061 547 : 0 : datalen += sizeof(uint16) + npos * sizeof(WordEntryPos);
548 : : }
549 : : }
550 : :
551 : : /*
552 : : * Enforce that datalen is still within MAXSTRPOS, ie the last lexeme
553 : : * didn't go past that. We could allow that, since no "pos" field
554 : : * overflowed, but tsvectorrecv shouldn't accept values that other
555 : : * tsvector-constructing routines wouldn't.
556 : : */
17 557 [ # # ]: 0 : if (datalen > MAXSTRPOS)
558 [ # # ]: 0 : elog(ERROR, "invalid tsvector: maximum total lexeme length exceeded");
559 : :
6929 teodor@sigaev.ru 560 : 0 : SET_VARSIZE(vec, hdrlen + datalen);
561 : :
6307 562 [ # # ]: 0 : if (needSort)
1297 peter@eisentraut.org 563 : 0 : qsort_arg(ARRPTR(vec), vec->size, sizeof(WordEntry),
564 : 0 : compareentry, STRPTR(vec));
565 : :
6946 tgl@sss.pgh.pa.us 566 : 0 : PG_RETURN_TSVECTOR(vec);
567 : : }
|