Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * dict_synonym.c
4 : : * Synonym dictionary: replace word by its synonym
5 : : *
6 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/tsearch/dict_synonym.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "catalog/pg_collation_d.h"
17 : : #include "commands/defrem.h"
18 : : #include "tsearch/ts_locale.h"
19 : : #include "tsearch/ts_public.h"
20 : : #include "utils/fmgrprotos.h"
21 : : #include "utils/formatting.h"
22 : :
23 : : typedef struct
24 : : {
25 : : char *in;
26 : : char *out;
27 : : int outlen;
28 : : uint16 flags;
29 : : } Syn;
30 : :
31 : : typedef struct
32 : : {
33 : : int len; /* length of syn array */
34 : : Syn *syn;
35 : : bool case_sensitive;
36 : : } DictSyn;
37 : :
38 : : /*
39 : : * Finds the next whitespace-delimited word within the 'in' string.
40 : : * Returns a pointer to the first character of the word, and a pointer
41 : : * to the next byte after the last character in the word (in *end).
42 : : * Character '*' at the end of word will not be treated as word
43 : : * character if flags is not null.
44 : : */
45 : : static char *
5867 teodor@sigaev.ru 46 :CBC 220 : findwrd(char *in, char **end, uint16 *flags)
47 : : {
48 : : char *start;
49 : : char *lastchar;
50 : :
51 : : /* Skip leading spaces */
263 peter@eisentraut.org 52 [ + - - + ]: 220 : while (*in && isspace((unsigned char) *in))
6591 tgl@sss.pgh.pa.us 53 :UBC 0 : in += pg_mblen(in);
54 : :
55 : : /* Return NULL on empty lines */
6591 tgl@sss.pgh.pa.us 56 [ - + ]:CBC 220 : if (*in == '\0')
57 : : {
6587 tgl@sss.pgh.pa.us 58 :UBC 0 : *end = NULL;
6591 59 : 0 : return NULL;
60 : : }
61 : :
5867 teodor@sigaev.ru 62 :CBC 220 : lastchar = start = in;
63 : :
64 : : /* Find end of word */
263 peter@eisentraut.org 65 [ + - + + ]: 1606 : while (*in && !isspace((unsigned char) *in))
66 : : {
5867 teodor@sigaev.ru 67 : 1386 : lastchar = in;
6591 tgl@sss.pgh.pa.us 68 : 1386 : in += pg_mblen(in);
69 : : }
70 : :
5671 bruce@momjian.us 71 [ + - + + : 220 : if (in - lastchar == 1 && t_iseq(lastchar, '*') && flags)
+ - ]
72 : : {
5867 teodor@sigaev.ru 73 : 22 : *flags = TSL_PREFIX;
74 : 22 : *end = lastchar;
75 : : }
76 : : else
77 : : {
78 [ + + ]: 198 : if (flags)
5671 bruce@momjian.us 79 : 88 : *flags = 0;
5867 teodor@sigaev.ru 80 : 198 : *end = in;
81 : : }
82 : :
6591 tgl@sss.pgh.pa.us 83 : 220 : return start;
84 : : }
85 : :
86 : : static int
87 : 652 : compareSyn(const void *a, const void *b)
88 : : {
5109 peter_e@gmx.net 89 : 652 : return strcmp(((const Syn *) a)->in, ((const Syn *) b)->in);
90 : : }
91 : :
92 : :
93 : : Datum
6591 tgl@sss.pgh.pa.us 94 : 25 : dsynonym_init(PG_FUNCTION_ARGS)
95 : : {
6590 96 : 25 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
97 : : DictSyn *d;
98 : : ListCell *l;
99 : 25 : char *filename = NULL;
6389 100 : 25 : bool case_sensitive = false;
101 : : tsearch_readline_state trst;
102 : : char *starti,
103 : : *starto,
6591 104 : 25 : *end = NULL;
6590 105 : 25 : int cur = 0;
6587 106 : 25 : char *line = NULL;
5867 teodor@sigaev.ru 107 : 25 : uint16 flags = 0;
108 : :
6590 tgl@sss.pgh.pa.us 109 [ + - + + : 66 : foreach(l, dictoptions)
+ + ]
110 : : {
111 : 44 : DefElem *defel = (DefElem *) lfirst(l);
112 : :
2780 113 [ + + ]: 44 : if (strcmp(defel->defname, "synonyms") == 0)
6590 114 : 25 : filename = defGetString(defel);
2780 115 [ + - ]: 19 : else if (strcmp(defel->defname, "casesensitive") == 0)
6389 116 : 19 : case_sensitive = defGetBoolean(defel);
117 : : else
6590 tgl@sss.pgh.pa.us 118 [ # # ]:UBC 0 : ereport(ERROR,
119 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
120 : : errmsg("unrecognized synonym parameter: \"%s\"",
121 : : defel->defname)));
122 : : }
123 : :
6590 tgl@sss.pgh.pa.us 124 [ - + ]:CBC 22 : if (!filename)
6591 tgl@sss.pgh.pa.us 125 [ # # ]:UBC 0 : ereport(ERROR,
126 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
127 : : errmsg("missing Synonyms parameter")));
128 : :
6590 tgl@sss.pgh.pa.us 129 :CBC 22 : filename = get_tsearch_config_filename(filename, "syn");
130 : :
6289 131 [ - + ]: 22 : if (!tsearch_readline_begin(&trst, filename))
6591 tgl@sss.pgh.pa.us 132 [ # # ]:UBC 0 : ereport(ERROR,
133 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
134 : : errmsg("could not open synonym file \"%s\": %m",
135 : : filename)));
136 : :
6591 tgl@sss.pgh.pa.us 137 :CBC 22 : d = (DictSyn *) palloc0(sizeof(DictSyn));
138 : :
6289 139 [ + + ]: 132 : while ((line = tsearch_readline(&trst)) != NULL)
140 : : {
5867 teodor@sigaev.ru 141 : 110 : starti = findwrd(line, &end, NULL);
6587 tgl@sss.pgh.pa.us 142 [ - + ]: 110 : if (!starti)
143 : : {
144 : : /* Empty line */
6587 tgl@sss.pgh.pa.us 145 :UBC 0 : goto skipline;
146 : : }
6587 tgl@sss.pgh.pa.us 147 [ - + ]:CBC 110 : if (*end == '\0')
148 : : {
149 : : /* A line with only one word. Ignore silently. */
6587 tgl@sss.pgh.pa.us 150 :UBC 0 : goto skipline;
151 : : }
6587 tgl@sss.pgh.pa.us 152 :CBC 110 : *end = '\0';
153 : :
5867 teodor@sigaev.ru 154 : 110 : starto = findwrd(end + 1, &end, &flags);
6587 tgl@sss.pgh.pa.us 155 [ - + ]: 110 : if (!starto)
156 : : {
157 : : /* A line with only one word (+whitespace). Ignore silently. */
6587 tgl@sss.pgh.pa.us 158 :UBC 0 : goto skipline;
159 : : }
6587 tgl@sss.pgh.pa.us 160 :CBC 110 : *end = '\0';
161 : :
162 : : /*
163 : : * starti now points to the first word, and starto to the second word
164 : : * on the line, with a \0 terminator at the end of both words.
165 : : */
166 : :
167 [ + + ]: 110 : if (cur >= d->len)
168 : : {
6591 169 [ + - ]: 22 : if (d->len == 0)
170 : : {
6587 171 : 22 : d->len = 64;
6591 172 : 22 : d->syn = (Syn *) palloc(sizeof(Syn) * d->len);
173 : : }
174 : : else
175 : : {
6591 tgl@sss.pgh.pa.us 176 :UBC 0 : d->len *= 2;
177 : 0 : d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len);
178 : : }
179 : : }
180 : :
6389 tgl@sss.pgh.pa.us 181 [ + + ]:CBC 110 : if (case_sensitive)
182 : : {
183 : 30 : d->syn[cur].in = pstrdup(starti);
184 : 30 : d->syn[cur].out = pstrdup(starto);
185 : : }
186 : : else
187 : : {
263 peter@eisentraut.org 188 : 80 : d->syn[cur].in = str_tolower(starti, strlen(starti), DEFAULT_COLLATION_OID);
189 : 80 : d->syn[cur].out = str_tolower(starto, strlen(starto), DEFAULT_COLLATION_OID);
190 : : }
191 : :
5867 teodor@sigaev.ru 192 : 110 : d->syn[cur].outlen = strlen(starto);
5671 bruce@momjian.us 193 : 110 : d->syn[cur].flags = flags;
194 : :
6591 tgl@sss.pgh.pa.us 195 : 110 : cur++;
196 : :
6505 bruce@momjian.us 197 : 110 : skipline:
6587 tgl@sss.pgh.pa.us 198 : 110 : pfree(line);
199 : : }
200 : :
6289 201 : 22 : tsearch_readline_end(&trst);
35 tgl@sss.pgh.pa.us 202 :GNC 22 : pfree(filename);
203 : :
6591 tgl@sss.pgh.pa.us 204 :CBC 22 : d->len = cur;
6587 205 : 22 : qsort(d->syn, d->len, sizeof(Syn), compareSyn);
206 : :
6389 207 : 22 : d->case_sensitive = case_sensitive;
208 : :
6591 209 : 22 : PG_RETURN_POINTER(d);
210 : : }
211 : :
212 : : Datum
213 : 183 : dsynonym_lexize(PG_FUNCTION_ARGS)
214 : : {
215 : 183 : DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0);
216 : 183 : char *in = (char *) PG_GETARG_POINTER(1);
6505 bruce@momjian.us 217 : 183 : int32 len = PG_GETARG_INT32(2);
218 : : Syn key,
219 : : *found;
220 : : TSLexeme *res;
221 : :
222 : : /* note: d->len test protects against Solaris bsearch-of-no-items bug */
6587 tgl@sss.pgh.pa.us 223 [ + - - + ]: 183 : if (len <= 0 || d->len <= 0)
6591 tgl@sss.pgh.pa.us 224 :UBC 0 : PG_RETURN_POINTER(NULL);
225 : :
6389 tgl@sss.pgh.pa.us 226 [ + + ]:CBC 183 : if (d->case_sensitive)
227 : 3 : key.in = pnstrdup(in, len);
228 : : else
263 peter@eisentraut.org 229 : 180 : key.in = str_tolower(in, len, DEFAULT_COLLATION_OID);
230 : :
6591 tgl@sss.pgh.pa.us 231 : 183 : key.out = NULL;
232 : :
233 : 183 : found = (Syn *) bsearch(&key, d->syn, d->len, sizeof(Syn), compareSyn);
234 : 183 : pfree(key.in);
235 : :
236 [ + + ]: 183 : if (!found)
237 : 150 : PG_RETURN_POINTER(NULL);
238 : :
6587 239 : 33 : res = palloc0(sizeof(TSLexeme) * 2);
5867 teodor@sigaev.ru 240 : 33 : res[0].lexeme = pnstrdup(found->out, found->outlen);
241 : 33 : res[0].flags = found->flags;
242 : :
6591 tgl@sss.pgh.pa.us 243 : 33 : PG_RETURN_POINTER(res);
244 : : }
|