Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * dict_thesaurus.c
4 : * Thesaurus dictionary: phrase to phrase substitution
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/tsearch/dict_thesaurus.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "catalog/namespace.h"
17 : #include "commands/defrem.h"
18 : #include "tsearch/ts_cache.h"
19 : #include "tsearch/ts_locale.h"
20 : #include "tsearch/ts_public.h"
21 : #include "utils/fmgrprotos.h"
22 : #include "utils/regproc.h"
23 :
24 :
25 : /*
26 : * Temporary we use TSLexeme.flags for inner use...
27 : */
28 : #define DT_USEASIS 0x1000
29 :
30 : typedef struct LexemeInfo
31 : {
32 : uint32 idsubst; /* entry's number in DictThesaurus->subst */
33 : uint16 posinsubst; /* pos info in entry */
34 : uint16 tnvariant; /* total num lexemes in one variant */
35 : struct LexemeInfo *nextentry;
36 : struct LexemeInfo *nextvariant;
37 : } LexemeInfo;
38 :
39 : typedef struct
40 : {
41 : char *lexeme;
42 : LexemeInfo *entries;
43 : } TheLexeme;
44 :
45 : typedef struct
46 : {
47 : uint16 lastlexeme; /* number lexemes to substitute */
48 : uint16 reslen;
49 : TSLexeme *res; /* prepared substituted result */
50 : } TheSubstitute;
51 :
52 : typedef struct
53 : {
54 : /* subdictionary to normalize lexemes */
55 : Oid subdictOid;
56 : TSDictionaryCacheEntry *subdict;
57 :
58 : /* Array to search lexeme by exact match */
59 : TheLexeme *wrds;
60 : int nwrds; /* current number of words */
61 : int ntwrds; /* allocated array length */
62 :
63 : /*
64 : * Storage of substituted result, n-th element is for n-th expression
65 : */
66 : TheSubstitute *subst;
67 : int nsubst;
68 : } DictThesaurus;
69 :
70 :
71 : static void
72 210 : newLexeme(DictThesaurus *d, char *b, char *e, uint32 idsubst, uint16 posinsubst)
73 : {
74 : TheLexeme *ptr;
75 :
76 210 : if (d->nwrds >= d->ntwrds)
77 : {
78 14 : if (d->ntwrds == 0)
79 : {
80 14 : d->ntwrds = 16;
81 14 : d->wrds = (TheLexeme *) palloc(sizeof(TheLexeme) * d->ntwrds);
82 : }
83 : else
84 : {
85 0 : d->ntwrds *= 2;
86 0 : d->wrds = (TheLexeme *) repalloc(d->wrds, sizeof(TheLexeme) * d->ntwrds);
87 : }
88 : }
89 :
90 210 : ptr = d->wrds + d->nwrds;
91 210 : d->nwrds++;
92 :
93 210 : ptr->lexeme = palloc(e - b + 1);
94 :
95 210 : memcpy(ptr->lexeme, b, e - b);
96 210 : ptr->lexeme[e - b] = '\0';
97 :
98 210 : ptr->entries = (LexemeInfo *) palloc(sizeof(LexemeInfo));
99 :
100 210 : ptr->entries->nextentry = NULL;
101 210 : ptr->entries->idsubst = idsubst;
102 210 : ptr->entries->posinsubst = posinsubst;
103 210 : }
104 :
105 : static void
106 168 : addWrd(DictThesaurus *d, char *b, char *e, uint32 idsubst, uint16 nwrd, uint16 posinsubst, bool useasis)
107 : {
108 : static int nres = 0;
109 : static int ntres = 0;
110 : TheSubstitute *ptr;
111 :
112 168 : if (nwrd == 0)
113 : {
114 112 : nres = ntres = 0;
115 :
116 112 : if (idsubst >= d->nsubst)
117 : {
118 14 : if (d->nsubst == 0)
119 : {
120 14 : d->nsubst = 16;
121 14 : d->subst = (TheSubstitute *) palloc(sizeof(TheSubstitute) * d->nsubst);
122 : }
123 : else
124 : {
125 0 : d->nsubst *= 2;
126 0 : d->subst = (TheSubstitute *) repalloc(d->subst, sizeof(TheSubstitute) * d->nsubst);
127 : }
128 : }
129 : }
130 :
131 168 : ptr = d->subst + idsubst;
132 :
133 168 : ptr->lastlexeme = posinsubst - 1;
134 :
135 168 : if (nres + 1 >= ntres)
136 : {
137 140 : if (ntres == 0)
138 : {
139 112 : ntres = 2;
140 112 : ptr->res = (TSLexeme *) palloc(sizeof(TSLexeme) * ntres);
141 : }
142 : else
143 : {
144 28 : ntres *= 2;
145 28 : ptr->res = (TSLexeme *) repalloc(ptr->res, sizeof(TSLexeme) * ntres);
146 : }
147 : }
148 :
149 168 : ptr->res[nres].lexeme = palloc(e - b + 1);
150 168 : memcpy(ptr->res[nres].lexeme, b, e - b);
151 168 : ptr->res[nres].lexeme[e - b] = '\0';
152 :
153 168 : ptr->res[nres].nvariant = nwrd;
154 168 : if (useasis)
155 84 : ptr->res[nres].flags = DT_USEASIS;
156 : else
157 84 : ptr->res[nres].flags = 0;
158 :
159 168 : ptr->res[++nres].lexeme = NULL;
160 168 : }
161 :
162 : #define TR_WAITLEX 1
163 : #define TR_INLEX 2
164 : #define TR_WAITSUBS 3
165 : #define TR_INSUBS 4
166 :
167 : static void
168 14 : thesaurusRead(const char *filename, DictThesaurus *d)
169 : {
170 14 : char *real_filename = get_tsearch_config_filename(filename, "ths");
171 : tsearch_readline_state trst;
172 14 : uint32 idsubst = 0;
173 14 : bool useasis = false;
174 : char *line;
175 :
176 14 : if (!tsearch_readline_begin(&trst, real_filename))
177 0 : ereport(ERROR,
178 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
179 : errmsg("could not open thesaurus file \"%s\": %m",
180 : real_filename)));
181 :
182 252 : while ((line = tsearch_readline(&trst)) != NULL)
183 : {
184 : char *ptr;
185 238 : int state = TR_WAITLEX;
186 238 : char *beginwrd = NULL;
187 238 : uint32 posinsubst = 0;
188 238 : uint32 nwrd = 0;
189 :
190 238 : ptr = line;
191 :
192 : /* is it a comment? */
193 266 : while (*ptr && isspace((unsigned char) *ptr))
194 28 : ptr += pg_mblen(ptr);
195 :
196 238 : if (t_iseq(ptr, '#') || *ptr == '\0' ||
197 112 : t_iseq(ptr, '\n') || t_iseq(ptr, '\r'))
198 : {
199 126 : pfree(line);
200 126 : continue;
201 : }
202 :
203 2590 : while (*ptr)
204 : {
205 2478 : if (state == TR_WAITLEX)
206 : {
207 322 : if (t_iseq(ptr, ':'))
208 : {
209 112 : if (posinsubst == 0)
210 0 : ereport(ERROR,
211 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
212 : errmsg("unexpected delimiter")));
213 112 : state = TR_WAITSUBS;
214 : }
215 210 : else if (!isspace((unsigned char) *ptr))
216 : {
217 210 : beginwrd = ptr;
218 210 : state = TR_INLEX;
219 : }
220 : }
221 2156 : else if (state == TR_INLEX)
222 : {
223 1078 : if (t_iseq(ptr, ':'))
224 : {
225 0 : newLexeme(d, beginwrd, ptr, idsubst, posinsubst++);
226 0 : state = TR_WAITSUBS;
227 : }
228 1078 : else if (isspace((unsigned char) *ptr))
229 : {
230 210 : newLexeme(d, beginwrd, ptr, idsubst, posinsubst++);
231 210 : state = TR_WAITLEX;
232 : }
233 : }
234 1078 : else if (state == TR_WAITSUBS)
235 : {
236 280 : if (t_iseq(ptr, '*'))
237 : {
238 84 : useasis = true;
239 84 : state = TR_INSUBS;
240 84 : beginwrd = ptr + pg_mblen(ptr);
241 : }
242 196 : else if (t_iseq(ptr, '\\'))
243 : {
244 0 : useasis = false;
245 0 : state = TR_INSUBS;
246 0 : beginwrd = ptr + pg_mblen(ptr);
247 : }
248 196 : else if (!isspace((unsigned char) *ptr))
249 : {
250 84 : useasis = false;
251 84 : beginwrd = ptr;
252 84 : state = TR_INSUBS;
253 : }
254 : }
255 798 : else if (state == TR_INSUBS)
256 : {
257 798 : if (isspace((unsigned char) *ptr))
258 : {
259 168 : if (ptr == beginwrd)
260 0 : ereport(ERROR,
261 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
262 : errmsg("unexpected end of line or lexeme")));
263 168 : addWrd(d, beginwrd, ptr, idsubst, nwrd++, posinsubst, useasis);
264 168 : state = TR_WAITSUBS;
265 : }
266 : }
267 : else
268 0 : elog(ERROR, "unrecognized thesaurus state: %d", state);
269 :
270 2478 : ptr += pg_mblen(ptr);
271 : }
272 :
273 112 : if (state == TR_INSUBS)
274 : {
275 0 : if (ptr == beginwrd)
276 0 : ereport(ERROR,
277 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
278 : errmsg("unexpected end of line or lexeme")));
279 0 : addWrd(d, beginwrd, ptr, idsubst, nwrd++, posinsubst, useasis);
280 : }
281 :
282 112 : idsubst++;
283 :
284 112 : if (!(nwrd && posinsubst))
285 0 : ereport(ERROR,
286 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
287 : errmsg("unexpected end of line")));
288 :
289 112 : if (nwrd != (uint16) nwrd || posinsubst != (uint16) posinsubst)
290 0 : ereport(ERROR,
291 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
292 : errmsg("too many lexemes in thesaurus entry")));
293 :
294 112 : pfree(line);
295 : }
296 :
297 14 : d->nsubst = idsubst;
298 :
299 14 : tsearch_readline_end(&trst);
300 14 : pfree(real_filename);
301 14 : }
302 :
303 : static TheLexeme *
304 210 : addCompiledLexeme(TheLexeme *newwrds, int *nnw, int *tnm, TSLexeme *lexeme, LexemeInfo *src, uint16 tnvariant)
305 : {
306 210 : if (*nnw >= *tnm)
307 : {
308 0 : *tnm *= 2;
309 0 : newwrds = (TheLexeme *) repalloc(newwrds, sizeof(TheLexeme) * *tnm);
310 : }
311 :
312 210 : newwrds[*nnw].entries = (LexemeInfo *) palloc(sizeof(LexemeInfo));
313 :
314 210 : if (lexeme && lexeme->lexeme)
315 : {
316 196 : newwrds[*nnw].lexeme = pstrdup(lexeme->lexeme);
317 196 : newwrds[*nnw].entries->tnvariant = tnvariant;
318 : }
319 : else
320 : {
321 14 : newwrds[*nnw].lexeme = NULL;
322 14 : newwrds[*nnw].entries->tnvariant = 1;
323 : }
324 :
325 210 : newwrds[*nnw].entries->idsubst = src->idsubst;
326 210 : newwrds[*nnw].entries->posinsubst = src->posinsubst;
327 :
328 210 : newwrds[*nnw].entries->nextentry = NULL;
329 :
330 210 : (*nnw)++;
331 210 : return newwrds;
332 : }
333 :
334 : static int
335 224 : cmpLexemeInfo(LexemeInfo *a, LexemeInfo *b)
336 : {
337 224 : if (a == NULL || b == NULL)
338 0 : return 0;
339 :
340 224 : if (a->idsubst == b->idsubst)
341 : {
342 0 : if (a->posinsubst == b->posinsubst)
343 : {
344 0 : if (a->tnvariant == b->tnvariant)
345 0 : return 0;
346 :
347 0 : return (a->tnvariant > b->tnvariant) ? 1 : -1;
348 : }
349 :
350 0 : return (a->posinsubst > b->posinsubst) ? 1 : -1;
351 : }
352 :
353 224 : return (a->idsubst > b->idsubst) ? 1 : -1;
354 : }
355 :
356 : static int
357 1442 : cmpLexeme(const TheLexeme *a, const TheLexeme *b)
358 : {
359 1442 : if (a->lexeme == NULL)
360 : {
361 164 : if (b->lexeme == NULL)
362 36 : return 0;
363 : else
364 128 : return 1;
365 : }
366 1278 : else if (b->lexeme == NULL)
367 20 : return -1;
368 :
369 1258 : return strcmp(a->lexeme, b->lexeme);
370 : }
371 :
372 : static int
373 588 : cmpLexemeQ(const void *a, const void *b)
374 : {
375 588 : return cmpLexeme((const TheLexeme *) a, (const TheLexeme *) b);
376 : }
377 :
378 : static int
379 658 : cmpTheLexeme(const void *a, const void *b)
380 : {
381 658 : const TheLexeme *la = (const TheLexeme *) a;
382 658 : const TheLexeme *lb = (const TheLexeme *) b;
383 : int res;
384 :
385 658 : if ((res = cmpLexeme(la, lb)) != 0)
386 532 : return res;
387 :
388 126 : return -cmpLexemeInfo(la->entries, lb->entries);
389 : }
390 :
391 : static void
392 14 : compileTheLexeme(DictThesaurus *d)
393 : {
394 : int i,
395 14 : nnw = 0,
396 14 : tnm = 16;
397 14 : TheLexeme *newwrds = (TheLexeme *) palloc(sizeof(TheLexeme) * tnm),
398 : *ptrwrds;
399 :
400 224 : for (i = 0; i < d->nwrds; i++)
401 : {
402 : TSLexeme *ptr;
403 :
404 210 : if (strcmp(d->wrds[i].lexeme, "?") == 0) /* Is stop word marker? */
405 14 : newwrds = addCompiledLexeme(newwrds, &nnw, &tnm, NULL, d->wrds[i].entries, 0);
406 : else
407 : {
408 196 : ptr = (TSLexeme *) DatumGetPointer(FunctionCall4(&(d->subdict->lexize),
409 : PointerGetDatum(d->subdict->dictData),
410 : PointerGetDatum(d->wrds[i].lexeme),
411 : Int32GetDatum(strlen(d->wrds[i].lexeme)),
412 : PointerGetDatum(NULL)));
413 :
414 196 : if (!ptr)
415 0 : ereport(ERROR,
416 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
417 : errmsg("thesaurus sample word \"%s\" isn't recognized by subdictionary (rule %d)",
418 : d->wrds[i].lexeme,
419 : d->wrds[i].entries->idsubst + 1)));
420 196 : else if (!(ptr->lexeme))
421 0 : ereport(ERROR,
422 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
423 : errmsg("thesaurus sample word \"%s\" is a stop word (rule %d)",
424 : d->wrds[i].lexeme,
425 : d->wrds[i].entries->idsubst + 1),
426 : errhint("Use \"?\" to represent a stop word within a sample phrase.")));
427 : else
428 : {
429 392 : while (ptr->lexeme)
430 : {
431 196 : TSLexeme *remptr = ptr + 1;
432 196 : int tnvar = 1;
433 196 : int curvar = ptr->nvariant;
434 :
435 : /* compute n words in one variant */
436 196 : while (remptr->lexeme)
437 : {
438 0 : if (remptr->nvariant != (remptr - 1)->nvariant)
439 0 : break;
440 0 : tnvar++;
441 0 : remptr++;
442 : }
443 :
444 196 : remptr = ptr;
445 392 : while (remptr->lexeme && remptr->nvariant == curvar)
446 : {
447 196 : newwrds = addCompiledLexeme(newwrds, &nnw, &tnm, remptr, d->wrds[i].entries, tnvar);
448 196 : remptr++;
449 : }
450 :
451 196 : ptr = remptr;
452 : }
453 : }
454 : }
455 :
456 210 : pfree(d->wrds[i].lexeme);
457 210 : pfree(d->wrds[i].entries);
458 : }
459 :
460 14 : if (d->wrds)
461 14 : pfree(d->wrds);
462 14 : d->wrds = newwrds;
463 14 : d->nwrds = nnw;
464 14 : d->ntwrds = tnm;
465 :
466 14 : if (d->nwrds > 1)
467 : {
468 14 : qsort(d->wrds, d->nwrds, sizeof(TheLexeme), cmpTheLexeme);
469 :
470 : /* uniq */
471 14 : newwrds = d->wrds;
472 14 : ptrwrds = d->wrds + 1;
473 210 : while (ptrwrds - d->wrds < d->nwrds)
474 : {
475 196 : if (cmpLexeme(ptrwrds, newwrds) == 0)
476 : {
477 98 : if (cmpLexemeInfo(ptrwrds->entries, newwrds->entries))
478 : {
479 98 : ptrwrds->entries->nextentry = newwrds->entries;
480 98 : newwrds->entries = ptrwrds->entries;
481 : }
482 : else
483 0 : pfree(ptrwrds->entries);
484 :
485 98 : if (ptrwrds->lexeme)
486 98 : pfree(ptrwrds->lexeme);
487 : }
488 : else
489 : {
490 98 : newwrds++;
491 98 : *newwrds = *ptrwrds;
492 : }
493 :
494 196 : ptrwrds++;
495 : }
496 :
497 14 : d->nwrds = newwrds - d->wrds + 1;
498 14 : d->wrds = (TheLexeme *) repalloc(d->wrds, sizeof(TheLexeme) * d->nwrds);
499 : }
500 14 : }
501 :
502 : static void
503 14 : compileTheSubstitute(DictThesaurus *d)
504 : {
505 : int i;
506 :
507 126 : for (i = 0; i < d->nsubst; i++)
508 : {
509 112 : TSLexeme *rem = d->subst[i].res,
510 : *outptr,
511 : *inptr;
512 112 : int n = 2;
513 :
514 112 : outptr = d->subst[i].res = (TSLexeme *) palloc(sizeof(TSLexeme) * n);
515 112 : outptr->lexeme = NULL;
516 112 : inptr = rem;
517 :
518 280 : while (inptr && inptr->lexeme)
519 : {
520 : TSLexeme *lexized,
521 : tmplex[2];
522 :
523 168 : if (inptr->flags & DT_USEASIS)
524 : { /* do not lexize */
525 84 : tmplex[0] = *inptr;
526 84 : tmplex[0].flags = 0;
527 84 : tmplex[1].lexeme = NULL;
528 84 : lexized = tmplex;
529 : }
530 : else
531 : {
532 84 : lexized = (TSLexeme *) DatumGetPointer(FunctionCall4(&(d->subdict->lexize),
533 : PointerGetDatum(d->subdict->dictData),
534 : PointerGetDatum(inptr->lexeme),
535 : Int32GetDatum(strlen(inptr->lexeme)),
536 : PointerGetDatum(NULL)));
537 : }
538 :
539 168 : if (lexized && lexized->lexeme)
540 168 : {
541 168 : int toset = (lexized->lexeme && outptr != d->subst[i].res) ? (outptr - d->subst[i].res) : -1;
542 :
543 336 : while (lexized->lexeme)
544 : {
545 168 : if (outptr - d->subst[i].res + 1 >= n)
546 : {
547 28 : int diff = outptr - d->subst[i].res;
548 :
549 28 : n *= 2;
550 28 : d->subst[i].res = (TSLexeme *) repalloc(d->subst[i].res, sizeof(TSLexeme) * n);
551 28 : outptr = d->subst[i].res + diff;
552 : }
553 :
554 168 : *outptr = *lexized;
555 168 : outptr->lexeme = pstrdup(lexized->lexeme);
556 :
557 168 : outptr++;
558 168 : lexized++;
559 : }
560 :
561 168 : if (toset > 0)
562 56 : d->subst[i].res[toset].flags |= TSL_ADDPOS;
563 : }
564 0 : else if (lexized)
565 : {
566 0 : ereport(ERROR,
567 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
568 : errmsg("thesaurus substitute word \"%s\" is a stop word (rule %d)",
569 : inptr->lexeme, i + 1)));
570 : }
571 : else
572 : {
573 0 : ereport(ERROR,
574 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
575 : errmsg("thesaurus substitute word \"%s\" isn't recognized by subdictionary (rule %d)",
576 : inptr->lexeme, i + 1)));
577 : }
578 :
579 168 : if (inptr->lexeme)
580 168 : pfree(inptr->lexeme);
581 168 : inptr++;
582 : }
583 :
584 112 : if (outptr == d->subst[i].res)
585 0 : ereport(ERROR,
586 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
587 : errmsg("thesaurus substitute phrase is empty (rule %d)",
588 : i + 1)));
589 :
590 112 : d->subst[i].reslen = outptr - d->subst[i].res;
591 :
592 112 : pfree(rem);
593 : }
594 14 : }
595 :
596 : Datum
597 14 : thesaurus_init(PG_FUNCTION_ARGS)
598 : {
599 14 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
600 : DictThesaurus *d;
601 14 : char *subdictname = NULL;
602 14 : bool fileloaded = false;
603 : List *namelist;
604 : ListCell *l;
605 :
606 14 : d = (DictThesaurus *) palloc0(sizeof(DictThesaurus));
607 :
608 42 : foreach(l, dictoptions)
609 : {
610 28 : DefElem *defel = (DefElem *) lfirst(l);
611 :
612 28 : if (strcmp(defel->defname, "dictfile") == 0)
613 : {
614 14 : if (fileloaded)
615 0 : ereport(ERROR,
616 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
617 : errmsg("multiple DictFile parameters")));
618 14 : thesaurusRead(defGetString(defel), d);
619 14 : fileloaded = true;
620 : }
621 14 : else if (strcmp(defel->defname, "dictionary") == 0)
622 : {
623 14 : if (subdictname)
624 0 : ereport(ERROR,
625 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
626 : errmsg("multiple Dictionary parameters")));
627 14 : subdictname = pstrdup(defGetString(defel));
628 : }
629 : else
630 : {
631 0 : ereport(ERROR,
632 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
633 : errmsg("unrecognized Thesaurus parameter: \"%s\"",
634 : defel->defname)));
635 : }
636 : }
637 :
638 14 : if (!fileloaded)
639 0 : ereport(ERROR,
640 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
641 : errmsg("missing DictFile parameter")));
642 14 : if (!subdictname)
643 0 : ereport(ERROR,
644 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
645 : errmsg("missing Dictionary parameter")));
646 :
647 14 : namelist = stringToQualifiedNameList(subdictname, NULL);
648 14 : d->subdictOid = get_ts_dict_oid(namelist, false);
649 14 : d->subdict = lookup_ts_dictionary_cache(d->subdictOid);
650 :
651 14 : compileTheLexeme(d);
652 14 : compileTheSubstitute(d);
653 :
654 14 : PG_RETURN_POINTER(d);
655 : }
656 :
657 : static LexemeInfo *
658 192 : findTheLexeme(DictThesaurus *d, char *lexeme)
659 : {
660 : TheLexeme key,
661 : *res;
662 :
663 192 : if (d->nwrds == 0)
664 0 : return NULL;
665 :
666 192 : key.lexeme = lexeme;
667 192 : key.entries = NULL;
668 :
669 192 : res = bsearch(&key, d->wrds, d->nwrds, sizeof(TheLexeme), cmpLexemeQ);
670 :
671 192 : if (res == NULL)
672 54 : return NULL;
673 138 : return res->entries;
674 : }
675 :
676 : static bool
677 288 : matchIdSubst(LexemeInfo *stored, uint32 idsubst)
678 : {
679 288 : bool res = true;
680 :
681 288 : if (stored)
682 : {
683 150 : res = false;
684 :
685 330 : for (; stored; stored = stored->nextvariant)
686 234 : if (stored->idsubst == idsubst)
687 : {
688 54 : res = true;
689 54 : break;
690 : }
691 : }
692 :
693 288 : return res;
694 : }
695 :
696 : static LexemeInfo *
697 138 : findVariant(LexemeInfo *in, LexemeInfo *stored, uint16 curpos, LexemeInfo **newin, int newn)
698 : {
699 : for (;;)
700 192 : {
701 : int i;
702 330 : LexemeInfo *ptr = newin[0];
703 :
704 540 : for (i = 0; i < newn; i++)
705 : {
706 348 : while (newin[i] && newin[i]->idsubst < ptr->idsubst)
707 0 : newin[i] = newin[i]->nextentry;
708 :
709 348 : if (newin[i] == NULL)
710 78 : return in;
711 :
712 270 : if (newin[i]->idsubst > ptr->idsubst)
713 : {
714 0 : ptr = newin[i];
715 0 : i = -1;
716 0 : continue;
717 : }
718 :
719 288 : while (newin[i]->idsubst == ptr->idsubst)
720 : {
721 270 : if (newin[i]->posinsubst == curpos && newin[i]->tnvariant == newn)
722 : {
723 192 : ptr = newin[i];
724 192 : break;
725 : }
726 :
727 78 : newin[i] = newin[i]->nextentry;
728 78 : if (newin[i] == NULL)
729 60 : return in;
730 : }
731 :
732 210 : if (newin[i]->idsubst != ptr->idsubst)
733 : {
734 18 : ptr = newin[i];
735 18 : i = -1;
736 18 : continue;
737 : }
738 : }
739 :
740 192 : if (i == newn && matchIdSubst(stored, ptr->idsubst) && (in == NULL || !matchIdSubst(in, ptr->idsubst)))
741 : { /* found */
742 :
743 192 : ptr->nextvariant = in;
744 192 : in = ptr;
745 : }
746 :
747 : /* step forward */
748 384 : for (i = 0; i < newn; i++)
749 192 : newin[i] = newin[i]->nextentry;
750 : }
751 : }
752 :
753 : static TSLexeme *
754 78 : copyTSLexeme(TheSubstitute *ts)
755 : {
756 : TSLexeme *res;
757 : uint16 i;
758 :
759 78 : res = (TSLexeme *) palloc(sizeof(TSLexeme) * (ts->reslen + 1));
760 180 : for (i = 0; i < ts->reslen; i++)
761 : {
762 102 : res[i] = ts->res[i];
763 102 : res[i].lexeme = pstrdup(ts->res[i].lexeme);
764 : }
765 :
766 78 : res[ts->reslen].lexeme = NULL;
767 :
768 78 : return res;
769 : }
770 :
771 : static TSLexeme *
772 96 : checkMatch(DictThesaurus *d, LexemeInfo *info, uint16 curpos, bool *moreres)
773 : {
774 96 : *moreres = false;
775 126 : while (info)
776 : {
777 : Assert(info->idsubst < d->nsubst);
778 108 : if (info->nextvariant)
779 66 : *moreres = true;
780 108 : if (d->subst[info->idsubst].lastlexeme == curpos)
781 78 : return copyTSLexeme(d->subst + info->idsubst);
782 30 : info = info->nextvariant;
783 : }
784 :
785 18 : return NULL;
786 : }
787 :
788 : Datum
789 204 : thesaurus_lexize(PG_FUNCTION_ARGS)
790 : {
791 204 : DictThesaurus *d = (DictThesaurus *) PG_GETARG_POINTER(0);
792 204 : DictSubState *dstate = (DictSubState *) PG_GETARG_POINTER(3);
793 204 : TSLexeme *res = NULL;
794 : LexemeInfo *stored,
795 204 : *info = NULL;
796 204 : uint16 curpos = 0;
797 204 : bool moreres = false;
798 :
799 204 : if (PG_NARGS() != 4 || dstate == NULL)
800 0 : elog(ERROR, "forbidden call of thesaurus or nested call");
801 :
802 204 : if (dstate->isend)
803 12 : PG_RETURN_POINTER(NULL);
804 192 : stored = (LexemeInfo *) dstate->private_state;
805 :
806 192 : if (stored)
807 60 : curpos = stored->posinsubst + 1;
808 :
809 192 : if (!d->subdict->isvalid)
810 0 : d->subdict = lookup_ts_dictionary_cache(d->subdictOid);
811 :
812 192 : res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(d->subdict->lexize),
813 : PointerGetDatum(d->subdict->dictData),
814 : PG_GETARG_DATUM(1),
815 : PG_GETARG_DATUM(2),
816 : PointerGetDatum(NULL)));
817 :
818 192 : if (res && res->lexeme)
819 156 : {
820 156 : TSLexeme *ptr = res,
821 : *basevar;
822 :
823 312 : while (ptr->lexeme)
824 : {
825 156 : uint16 nv = ptr->nvariant;
826 : uint16 i,
827 156 : nlex = 0;
828 : LexemeInfo **infos;
829 :
830 156 : basevar = ptr;
831 312 : while (ptr->lexeme && nv == ptr->nvariant)
832 : {
833 156 : nlex++;
834 156 : ptr++;
835 : }
836 :
837 156 : infos = (LexemeInfo **) palloc(sizeof(LexemeInfo *) * nlex);
838 258 : for (i = 0; i < nlex; i++)
839 156 : if ((infos[i] = findTheLexeme(d, basevar[i].lexeme)) == NULL)
840 54 : break;
841 :
842 156 : if (i < nlex)
843 : {
844 : /* no chance to find */
845 54 : pfree(infos);
846 54 : continue;
847 : }
848 :
849 102 : info = findVariant(info, stored, curpos, infos, nlex);
850 : }
851 : }
852 36 : else if (res)
853 : { /* stop-word */
854 36 : LexemeInfo *infos = findTheLexeme(d, NULL);
855 :
856 36 : info = findVariant(NULL, stored, curpos, &infos, 1);
857 : }
858 : else
859 : {
860 0 : info = NULL; /* word isn't recognized */
861 : }
862 :
863 192 : dstate->private_state = info;
864 :
865 192 : if (!info)
866 : {
867 96 : dstate->getnext = false;
868 96 : PG_RETURN_POINTER(NULL);
869 : }
870 :
871 96 : if ((res = checkMatch(d, info, curpos, &moreres)) != NULL)
872 : {
873 78 : dstate->getnext = moreres;
874 78 : PG_RETURN_POINTER(res);
875 : }
876 :
877 18 : dstate->getnext = true;
878 :
879 18 : PG_RETURN_POINTER(NULL);
880 : }
|