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