Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * dict_thesaurus.c
4 : : * Thesaurus dictionary: phrase to phrase substitution
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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
3447 tgl@sss.pgh.pa.us 72 :CBC 105 : newLexeme(DictThesaurus *d, char *b, char *e, uint32 idsubst, uint16 posinsubst)
73 : : {
74 : : TheLexeme *ptr;
75 : :
6081 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 : : {
6081 tgl@sss.pgh.pa.us 85 :UBC 0 : d->ntwrds *= 2;
86 : 0 : d->wrds = (TheLexeme *) repalloc(d->wrds, sizeof(TheLexeme) * d->ntwrds);
87 : : }
88 : : }
89 : :
6081 tgl@sss.pgh.pa.us 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
3447 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 : :
6081 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 : : {
6081 tgl@sss.pgh.pa.us 125 :UBC 0 : d->nsubst *= 2;
126 : 0 : d->subst = (TheSubstitute *) repalloc(d->subst, sizeof(TheSubstitute) * d->nsubst);
127 : : }
128 : : }
129 : : }
130 : :
6081 tgl@sss.pgh.pa.us 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
2357 peter_e@gmx.net 168 : 7 : thesaurusRead(const char *filename, DictThesaurus *d)
169 : : {
170 : : tsearch_readline_state trst;
3447 tgl@sss.pgh.pa.us 171 : 7 : uint32 idsubst = 0;
6081 172 : 7 : bool useasis = false;
173 : : char *line;
174 : :
175 : 7 : filename = get_tsearch_config_filename(filename, "ths");
5779 176 [ - + ]: 7 : if (!tsearch_readline_begin(&trst, filename))
6081 tgl@sss.pgh.pa.us 177 [ # # ]:UBC 0 : ereport(ERROR,
178 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
179 : : errmsg("could not open thesaurus file \"%s\": %m",
180 : : filename)));
181 : :
5779 tgl@sss.pgh.pa.us 182 [ + + ]:CBC 126 : while ((line = tsearch_readline(&trst)) != NULL)
183 : : {
184 : : char *ptr;
6081 185 : 119 : int state = TR_WAITLEX;
186 : 119 : char *beginwrd = NULL;
3447 187 : 119 : uint32 posinsubst = 0;
188 : 119 : uint32 nwrd = 0;
189 : :
6077 190 : 119 : ptr = line;
191 : :
192 : : /* is it a comment? */
193 [ + + + + ]: 133 : while (*ptr && t_isspace(ptr))
6081 194 : 14 : ptr += pg_mblen(ptr);
195 : :
6077 196 [ + + + + ]: 119 : if (t_iseq(ptr, '#') || *ptr == '\0' ||
197 [ + - - + ]: 56 : t_iseq(ptr, '\n') || t_iseq(ptr, '\r'))
198 : : {
199 : 63 : pfree(line);
6081 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)
6081 tgl@sss.pgh.pa.us 210 [ # # ]:UBC 0 : ereport(ERROR,
211 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
212 : : errmsg("unexpected delimiter")));
6081 tgl@sss.pgh.pa.us 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 : : {
6081 tgl@sss.pgh.pa.us 225 :UBC 0 : newLexeme(d, beginwrd, ptr, idsubst, posinsubst++);
226 : 0 : state = TR_WAITSUBS;
227 : : }
6081 tgl@sss.pgh.pa.us 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 : : {
6081 tgl@sss.pgh.pa.us 244 :UBC 0 : useasis = false;
245 : 0 : state = TR_INSUBS;
246 : 0 : beginwrd = ptr + pg_mblen(ptr);
247 : : }
6081 tgl@sss.pgh.pa.us 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)
6081 tgl@sss.pgh.pa.us 260 [ # # ]:UBC 0 : ereport(ERROR,
261 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
262 : : errmsg("unexpected end of line or lexeme")));
6081 tgl@sss.pgh.pa.us 263 :CBC 84 : addWrd(d, beginwrd, ptr, idsubst, nwrd++, posinsubst, useasis);
264 : 84 : state = TR_WAITSUBS;
265 : : }
266 : : }
267 : : else
6081 tgl@sss.pgh.pa.us 268 [ # # ]:UBC 0 : elog(ERROR, "unrecognized thesaurus state: %d", state);
269 : :
6081 tgl@sss.pgh.pa.us 270 :CBC 1239 : ptr += pg_mblen(ptr);
271 : : }
272 : :
273 [ - + ]: 56 : if (state == TR_INSUBS)
274 : : {
6081 tgl@sss.pgh.pa.us 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 : :
6081 tgl@sss.pgh.pa.us 282 :CBC 56 : idsubst++;
283 : :
284 [ + - - + ]: 56 : if (!(nwrd && posinsubst))
6081 tgl@sss.pgh.pa.us 285 [ # # ]:UBC 0 : ereport(ERROR,
286 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
287 : : errmsg("unexpected end of line")));
288 : :
3447 tgl@sss.pgh.pa.us 289 [ + - - + ]:CBC 56 : if (nwrd != (uint16) nwrd || posinsubst != (uint16) posinsubst)
3447 tgl@sss.pgh.pa.us 290 [ # # ]:UBC 0 : ereport(ERROR,
291 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
292 : : errmsg("too many lexemes in thesaurus entry")));
293 : :
6077 tgl@sss.pgh.pa.us 294 :CBC 56 : pfree(line);
295 : : }
296 : :
6081 297 : 7 : d->nsubst = idsubst;
298 : :
5779 299 : 7 : tsearch_readline_end(&trst);
6081 300 : 7 : }
301 : :
302 : : static TheLexeme *
5995 bruce@momjian.us 303 : 105 : addCompiledLexeme(TheLexeme *newwrds, int *nnw, int *tnm, TSLexeme *lexeme, LexemeInfo *src, uint16 tnvariant)
304 : : {
6081 tgl@sss.pgh.pa.us 305 [ - + ]: 105 : if (*nnw >= *tnm)
306 : : {
6081 tgl@sss.pgh.pa.us 307 :UBC 0 : *tnm *= 2;
308 : 0 : newwrds = (TheLexeme *) repalloc(newwrds, sizeof(TheLexeme) * *tnm);
309 : : }
310 : :
6081 tgl@sss.pgh.pa.us 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
5995 bruce@momjian.us 334 : 112 : cmpLexemeInfo(LexemeInfo *a, LexemeInfo *b)
335 : : {
6081 tgl@sss.pgh.pa.us 336 [ + - - + ]: 112 : if (a == NULL || b == NULL)
6081 tgl@sss.pgh.pa.us 337 :UBC 0 : return 0;
338 : :
6081 tgl@sss.pgh.pa.us 339 [ - + ]:CBC 112 : if (a->idsubst == b->idsubst)
340 : : {
6081 tgl@sss.pgh.pa.us 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 : :
6081 tgl@sss.pgh.pa.us 352 [ + + ]:CBC 112 : return (a->idsubst > b->idsubst) ? 1 : -1;
353 : : }
354 : :
355 : : static int
4599 peter_e@gmx.net 356 : 721 : cmpLexeme(const TheLexeme *a, const TheLexeme *b)
357 : : {
6081 tgl@sss.pgh.pa.us 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 : : {
4599 peter_e@gmx.net 374 : 294 : return cmpLexeme((const TheLexeme *) a, (const TheLexeme *) b);
375 : : }
376 : :
377 : : static int
6081 tgl@sss.pgh.pa.us 378 : 329 : cmpTheLexeme(const void *a, const void *b)
379 : : {
4326 bruce@momjian.us 380 : 329 : const TheLexeme *la = (const TheLexeme *) a;
381 : 329 : const TheLexeme *lb = (const TheLexeme *) b;
382 : : int res;
383 : :
6081 tgl@sss.pgh.pa.us 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
5995 bruce@momjian.us 391 : 7 : compileTheLexeme(DictThesaurus *d)
392 : : {
393 : : int i,
6081 tgl@sss.pgh.pa.us 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 : :
2489 403 [ + + ]: 105 : if (strcmp(d->wrds[i].lexeme, "?") == 0) /* Is stop word marker? */
6081 404 : 7 : newwrds = addCompiledLexeme(newwrds, &nnw, &tnm, NULL, d->wrds[i].entries, 0);
405 : : else
406 : : {
6000 bruce@momjian.us 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)
5982 tgl@sss.pgh.pa.us 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)));
6000 bruce@momjian.us 419 [ - + ]:CBC 98 : else if (!(ptr->lexeme))
5982 tgl@sss.pgh.pa.us 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 : : {
6000 bruce@momjian.us 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 : : {
6000 bruce@momjian.us 437 [ # # ]:UBC 0 : if (remptr->nvariant != (remptr - 1)->nvariant)
438 : 0 : break;
439 : 0 : tnvar++;
440 : 0 : remptr++;
441 : : }
442 : :
6000 bruce@momjian.us 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 : :
6081 tgl@sss.pgh.pa.us 455 : 105 : pfree(d->wrds[i].lexeme);
456 : 105 : pfree(d->wrds[i].entries);
457 : : }
458 : :
5249 459 [ + - ]: 7 : if (d->wrds)
460 : 7 : pfree(d->wrds);
6081 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
6081 tgl@sss.pgh.pa.us 482 :UBC 0 : pfree(ptrwrds->entries);
483 : :
6081 tgl@sss.pgh.pa.us 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
5995 bruce@momjian.us 502 : 7 : compileTheSubstitute(DictThesaurus *d)
503 : : {
504 : : int i;
505 : :
6081 tgl@sss.pgh.pa.us 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 : : {
1536 alvherre@alvh.no-ip. 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 : :
6081 tgl@sss.pgh.pa.us 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 : : }
6081 tgl@sss.pgh.pa.us 563 [ # # ]:UBC 0 : else if (lexized)
564 : : {
5982 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 : :
6081 tgl@sss.pgh.pa.us 578 [ + - ]:CBC 84 : if (inptr->lexeme)
579 : 84 : pfree(inptr->lexeme);
580 : 84 : inptr++;
581 : : }
582 : :
583 [ - + ]: 56 : if (outptr == d->subst[i].res)
5982 tgl@sss.pgh.pa.us 584 [ # # ]:UBC 0 : ereport(ERROR,
585 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
586 : : errmsg("thesaurus substitute phrase is empty (rule %d)",
587 : : i + 1)));
588 : :
6081 tgl@sss.pgh.pa.us 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 : : {
6080 598 : 7 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
599 : : DictThesaurus *d;
6081 600 : 7 : char *subdictname = NULL;
601 : 7 : bool fileloaded = false;
602 : : List *namelist;
603 : : ListCell *l;
604 : :
605 : 7 : d = (DictThesaurus *) palloc0(sizeof(DictThesaurus));
606 : :
6080 607 [ + - + + : 21 : foreach(l, dictoptions)
+ + ]
608 : : {
609 : 14 : DefElem *defel = (DefElem *) lfirst(l);
610 : :
2270 611 [ + + ]: 14 : if (strcmp(defel->defname, "dictfile") == 0)
612 : : {
6081 613 [ - + ]: 7 : if (fileloaded)
6081 tgl@sss.pgh.pa.us 614 [ # # ]:UBC 0 : ereport(ERROR,
615 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
616 : : errmsg("multiple DictFile parameters")));
6080 tgl@sss.pgh.pa.us 617 :CBC 7 : thesaurusRead(defGetString(defel), d);
6081 618 : 7 : fileloaded = true;
619 : : }
2270 620 [ + - ]: 7 : else if (strcmp(defel->defname, "dictionary") == 0)
621 : : {
6081 622 [ - + ]: 7 : if (subdictname)
6081 tgl@sss.pgh.pa.us 623 [ # # ]:UBC 0 : ereport(ERROR,
624 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
625 : : errmsg("multiple Dictionary parameters")));
6080 tgl@sss.pgh.pa.us 626 :CBC 7 : subdictname = pstrdup(defGetString(defel));
627 : : }
628 : : else
629 : : {
6081 tgl@sss.pgh.pa.us 630 [ # # ]:UBC 0 : ereport(ERROR,
631 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
632 : : errmsg("unrecognized Thesaurus parameter: \"%s\"",
633 : : defel->defname)));
634 : : }
635 : : }
636 : :
6081 tgl@sss.pgh.pa.us 637 [ - + ]:CBC 7 : if (!fileloaded)
6081 tgl@sss.pgh.pa.us 638 [ # # ]:UBC 0 : ereport(ERROR,
639 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
640 : : errmsg("missing DictFile parameter")));
6081 tgl@sss.pgh.pa.us 641 [ - + ]:CBC 7 : if (!subdictname)
6081 tgl@sss.pgh.pa.us 642 [ # # ]:UBC 0 : ereport(ERROR,
643 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
644 : : errmsg("missing Dictionary parameter")));
645 : :
474 tgl@sss.pgh.pa.us 646 :CBC 7 : namelist = stringToQualifiedNameList(subdictname, NULL);
647 : 7 : d->subdictOid = get_ts_dict_oid(namelist, false);
6081 648 : 7 : d->subdict = lookup_ts_dictionary_cache(d->subdictOid);
649 : :
650 : 7 : compileTheLexeme(d);
651 : 7 : compileTheSubstitute(d);
652 : :
653 : 7 : PG_RETURN_POINTER(d);
654 : : }
655 : :
656 : : static LexemeInfo *
5995 bruce@momjian.us 657 : 96 : findTheLexeme(DictThesaurus *d, char *lexeme)
658 : : {
659 : : TheLexeme key,
660 : : *res;
661 : :
6081 tgl@sss.pgh.pa.us 662 [ - + ]: 96 : if (d->nwrds == 0)
6081 tgl@sss.pgh.pa.us 663 :UBC 0 : return NULL;
664 : :
6053 teodor@sigaev.ru 665 :CBC 96 : key.lexeme = lexeme;
666 : 96 : key.entries = NULL;
667 : :
6081 tgl@sss.pgh.pa.us 668 : 96 : res = bsearch(&key, d->wrds, d->nwrds, sizeof(TheLexeme), cmpLexemeQ);
669 : :
670 [ + + ]: 96 : if (res == NULL)
671 : 27 : return NULL;
672 : 69 : return res->entries;
673 : : }
674 : :
675 : : static bool
3447 676 : 144 : matchIdSubst(LexemeInfo *stored, uint32 idsubst)
677 : : {
6081 678 : 144 : bool res = true;
679 : :
680 [ + + ]: 144 : if (stored)
681 : : {
682 : 75 : res = false;
683 : :
684 [ + + ]: 165 : for (; stored; stored = stored->nextvariant)
685 [ + + ]: 117 : if (stored->idsubst == idsubst)
686 : : {
687 : 27 : res = true;
688 : 27 : break;
689 : : }
690 : : }
691 : :
692 : 144 : return res;
693 : : }
694 : :
695 : : static LexemeInfo *
5995 bruce@momjian.us 696 : 69 : findVariant(LexemeInfo *in, LexemeInfo *stored, uint16 curpos, LexemeInfo **newin, int newn)
697 : : {
698 : : for (;;)
6081 tgl@sss.pgh.pa.us 699 : 96 : {
700 : : int i;
701 : 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)
6081 tgl@sss.pgh.pa.us 706 :UBC 0 : newin[i] = newin[i]->nextentry;
707 : :
6081 tgl@sss.pgh.pa.us 708 [ + + ]:CBC 174 : if (newin[i] == NULL)
709 : 39 : return in;
710 : :
711 [ - + ]: 135 : if (newin[i]->idsubst > ptr->idsubst)
712 : : {
6081 tgl@sss.pgh.pa.us 713 :UBC 0 : ptr = newin[i];
714 : 0 : i = -1;
715 : 0 : continue;
716 : : }
717 : :
6081 tgl@sss.pgh.pa.us 718 [ + + ]:CBC 144 : while (newin[i]->idsubst == ptr->idsubst)
719 : : {
720 [ + + + - ]: 135 : if (newin[i]->posinsubst == curpos && newin[i]->tnvariant == newn)
721 : : {
722 : 96 : ptr = newin[i];
723 : 96 : break;
724 : : }
725 : :
726 : 39 : newin[i] = newin[i]->nextentry;
727 [ + + ]: 39 : if (newin[i] == NULL)
728 : 30 : return in;
729 : : }
730 : :
731 [ + + ]: 105 : if (newin[i]->idsubst != ptr->idsubst)
732 : : {
733 : 9 : ptr = newin[i];
734 : 9 : i = -1;
735 : 9 : continue;
736 : : }
737 : : }
738 : :
739 [ + - + - : 96 : if (i == newn && matchIdSubst(stored, ptr->idsubst) && (in == NULL || !matchIdSubst(in, ptr->idsubst)))
+ + + - ]
740 : : { /* found */
741 : :
742 : 96 : ptr->nextvariant = in;
743 : 96 : in = ptr;
744 : : }
745 : :
746 : : /* step forward */
747 [ + + ]: 192 : for (i = 0; i < newn; i++)
748 : 96 : newin[i] = newin[i]->nextentry;
749 : : }
750 : : }
751 : :
752 : : static TSLexeme *
5995 bruce@momjian.us 753 : 39 : copyTSLexeme(TheSubstitute *ts)
754 : : {
755 : : TSLexeme *res;
756 : : uint16 i;
757 : :
6081 tgl@sss.pgh.pa.us 758 : 39 : res = (TSLexeme *) palloc(sizeof(TSLexeme) * (ts->reslen + 1));
759 [ + + ]: 90 : for (i = 0; i < ts->reslen; i++)
760 : : {
761 : 51 : res[i] = ts->res[i];
762 : 51 : res[i].lexeme = pstrdup(ts->res[i].lexeme);
763 : : }
764 : :
765 : 39 : res[ts->reslen].lexeme = NULL;
766 : :
767 : 39 : return res;
768 : : }
769 : :
770 : : static TSLexeme *
5995 bruce@momjian.us 771 : 48 : checkMatch(DictThesaurus *d, LexemeInfo *info, uint16 curpos, bool *moreres)
772 : : {
6081 tgl@sss.pgh.pa.us 773 : 48 : *moreres = false;
774 [ + + ]: 63 : while (info)
775 : : {
776 [ - + ]: 54 : Assert(info->idsubst < d->nsubst);
777 [ + + ]: 54 : if (info->nextvariant)
778 : 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;
782 : : }
783 : :
784 : 9 : return NULL;
785 : : }
786 : :
787 : : Datum
788 : 102 : thesaurus_lexize(PG_FUNCTION_ARGS)
789 : : {
790 : 102 : DictThesaurus *d = (DictThesaurus *) PG_GETARG_POINTER(0);
791 : 102 : DictSubState *dstate = (DictSubState *) PG_GETARG_POINTER(3);
792 : 102 : TSLexeme *res = NULL;
793 : : LexemeInfo *stored,
794 : 102 : *info = NULL;
795 : 102 : uint16 curpos = 0;
796 : 102 : bool moreres = false;
797 : :
5982 798 [ + - - + ]: 102 : if (PG_NARGS() != 4 || dstate == NULL)
6081 tgl@sss.pgh.pa.us 799 [ # # ]:UBC 0 : elog(ERROR, "forbidden call of thesaurus or nested call");
800 : :
6081 tgl@sss.pgh.pa.us 801 [ + + ]:CBC 102 : if (dstate->isend)
802 : 6 : PG_RETURN_POINTER(NULL);
5386 peter_e@gmx.net 803 : 96 : stored = (LexemeInfo *) dstate->private_state;
804 : :
6081 tgl@sss.pgh.pa.us 805 [ + + ]: 96 : if (stored)
806 : 30 : curpos = stored->posinsubst + 1;
807 : :
808 [ - + ]: 96 : if (!d->subdict->isvalid)
6081 tgl@sss.pgh.pa.us 809 :UBC 0 : d->subdict = lookup_ts_dictionary_cache(d->subdictOid);
810 : :
6081 tgl@sss.pgh.pa.us 811 :CBC 96 : res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(d->subdict->lexize),
812 : : PointerGetDatum(d->subdict->dictData),
813 : : PG_GETARG_DATUM(1),
814 : : PG_GETARG_DATUM(2),
815 : : PointerGetDatum(NULL)));
816 : :
817 [ + - + + ]: 96 : if (res && res->lexeme)
818 : 78 : {
819 : 78 : TSLexeme *ptr = res,
820 : : *basevar;
821 : :
822 [ + + ]: 156 : while (ptr->lexeme)
823 : : {
824 : 78 : uint16 nv = ptr->nvariant;
825 : : uint16 i,
826 : 78 : nlex = 0;
827 : : LexemeInfo **infos;
828 : :
829 : 78 : basevar = ptr;
830 [ + + + - ]: 156 : while (ptr->lexeme && nv == ptr->nvariant)
831 : : {
832 : 78 : nlex++;
833 : 78 : ptr++;
834 : : }
835 : :
836 : 78 : infos = (LexemeInfo **) palloc(sizeof(LexemeInfo *) * nlex);
837 [ + + ]: 129 : for (i = 0; i < nlex; i++)
838 [ + + ]: 78 : if ((infos[i] = findTheLexeme(d, basevar[i].lexeme)) == NULL)
839 : 27 : break;
840 : :
841 [ + + ]: 78 : if (i < nlex)
842 : : {
843 : : /* no chance to find */
844 : 27 : pfree(infos);
845 : 27 : continue;
846 : : }
847 : :
848 : 51 : info = findVariant(info, stored, curpos, infos, nlex);
849 : : }
850 : : }
851 [ + - ]: 18 : else if (res)
852 : : { /* stop-word */
853 : 18 : LexemeInfo *infos = findTheLexeme(d, NULL);
854 : :
855 : 18 : info = findVariant(NULL, stored, curpos, &infos, 1);
856 : : }
857 : : else
858 : : {
6081 tgl@sss.pgh.pa.us 859 :UBC 0 : info = NULL; /* word isn't recognized */
860 : : }
861 : :
5386 peter_e@gmx.net 862 :CBC 96 : dstate->private_state = (void *) info;
863 : :
6081 tgl@sss.pgh.pa.us 864 [ + + ]: 96 : if (!info)
865 : : {
866 : 48 : dstate->getnext = false;
867 : 48 : PG_RETURN_POINTER(NULL);
868 : : }
869 : :
870 [ + + ]: 48 : if ((res = checkMatch(d, info, curpos, &moreres)) != NULL)
871 : : {
872 : 39 : dstate->getnext = moreres;
873 : 39 : PG_RETURN_POINTER(res);
874 : : }
875 : :
876 : 9 : dstate->getnext = true;
877 : :
878 : 9 : PG_RETURN_POINTER(NULL);
879 : : }
|