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-2026, 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 : : uint16 flags;
28 : : } Syn;
29 : :
30 : : typedef struct
31 : : {
32 : : int len; /* length of syn array */
33 : : Syn *syn;
34 : : bool case_sensitive;
35 : : } DictSyn;
36 : :
37 : : /*
38 : : * Finds the next whitespace-delimited word within the 'in' string.
39 : : * Returns a pointer to the first character of the word, and a pointer
40 : : * to the next byte after the last character in the word (in *end).
41 : : * Character '*' at the end of word will not be treated as word
42 : : * character if flags is not null.
43 : : */
44 : : static char *
6164 teodor@sigaev.ru 45 :CBC 290 : findwrd(char *in, char **end, uint16 *flags)
46 : : {
47 : : char *start;
48 : : char *lastchar;
49 : :
50 : : /* Skip leading spaces */
560 peter@eisentraut.org 51 [ + - - + ]: 290 : while (*in && isspace((unsigned char) *in))
174 tmunro@postgresql.or 52 :UBC 0 : in += pg_mblen_cstr(in);
53 : :
54 : : /* Return NULL on empty lines */
6888 tgl@sss.pgh.pa.us 55 [ - + ]:CBC 290 : if (*in == '\0')
56 : : {
6884 tgl@sss.pgh.pa.us 57 :UBC 0 : *end = NULL;
6888 58 : 0 : return NULL;
59 : : }
60 : :
6164 teodor@sigaev.ru 61 :CBC 290 : lastchar = start = in;
62 : :
63 : : /* Find end of word */
560 peter@eisentraut.org 64 [ + - + + ]: 2117 : while (*in && !isspace((unsigned char) *in))
65 : : {
6164 teodor@sigaev.ru 66 : 1827 : lastchar = in;
174 tmunro@postgresql.or 67 : 1827 : in += pg_mblen_cstr(in);
68 : : }
69 : :
5968 bruce@momjian.us 70 [ + - + + : 290 : if (in - lastchar == 1 && t_iseq(lastchar, '*') && flags)
+ - ]
71 : : {
6164 teodor@sigaev.ru 72 : 29 : *flags = TSL_PREFIX;
73 : 29 : *end = lastchar;
74 : : }
75 : : else
76 : : {
77 [ + + ]: 261 : if (flags)
5968 bruce@momjian.us 78 : 116 : *flags = 0;
6164 teodor@sigaev.ru 79 : 261 : *end = in;
80 : : }
81 : :
6888 tgl@sss.pgh.pa.us 82 : 290 : return start;
83 : : }
84 : :
85 : : static int
86 : 1010 : compareSyn(const void *a, const void *b)
87 : : {
5406 peter_e@gmx.net 88 : 1010 : return strcmp(((const Syn *) a)->in, ((const Syn *) b)->in);
89 : : }
90 : :
91 : :
92 : : Datum
6888 tgl@sss.pgh.pa.us 93 : 33 : dsynonym_init(PG_FUNCTION_ARGS)
94 : : {
6887 95 : 33 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
96 : : DictSyn *d;
97 : : ListCell *l;
98 : 33 : char *filename = NULL;
6686 99 : 33 : bool case_sensitive = false;
100 : : tsearch_readline_state trst;
101 : : char *starti,
102 : : *starto,
6888 103 : 33 : *end = NULL;
6887 104 : 33 : int cur = 0;
6884 105 : 33 : char *line = NULL;
6164 teodor@sigaev.ru 106 : 33 : uint16 flags = 0;
107 : :
6887 tgl@sss.pgh.pa.us 108 [ + - + + : 87 : foreach(l, dictoptions)
+ + ]
109 : : {
110 : 58 : DefElem *defel = (DefElem *) lfirst(l);
111 : :
3077 112 [ + + ]: 58 : if (strcmp(defel->defname, "synonyms") == 0)
6887 113 : 33 : filename = defGetString(defel);
3077 114 [ + - ]: 25 : else if (strcmp(defel->defname, "casesensitive") == 0)
6686 115 : 25 : case_sensitive = defGetBoolean(defel);
116 : : else
6887 tgl@sss.pgh.pa.us 117 [ # # ]:UBC 0 : ereport(ERROR,
118 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
119 : : errmsg("unrecognized synonym parameter: \"%s\"",
120 : : defel->defname)));
121 : : }
122 : :
6887 tgl@sss.pgh.pa.us 123 [ - + ]:CBC 29 : if (!filename)
6888 tgl@sss.pgh.pa.us 124 [ # # ]:UBC 0 : ereport(ERROR,
125 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
126 : : errmsg("missing Synonyms parameter")));
127 : :
6887 tgl@sss.pgh.pa.us 128 :CBC 29 : filename = get_tsearch_config_filename(filename, "syn");
129 : :
6586 130 [ - + ]: 29 : if (!tsearch_readline_begin(&trst, filename))
6888 tgl@sss.pgh.pa.us 131 [ # # ]:UBC 0 : ereport(ERROR,
132 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
133 : : errmsg("could not open synonym file \"%s\": %m",
134 : : filename)));
135 : :
202 michael@paquier.xyz 136 :GNC 29 : d = palloc0_object(DictSyn);
137 : :
6586 tgl@sss.pgh.pa.us 138 [ + + ]:CBC 174 : while ((line = tsearch_readline(&trst)) != NULL)
139 : : {
6164 teodor@sigaev.ru 140 : 145 : starti = findwrd(line, &end, NULL);
6884 tgl@sss.pgh.pa.us 141 [ - + ]: 145 : if (!starti)
142 : : {
143 : : /* Empty line */
6884 tgl@sss.pgh.pa.us 144 :UBC 0 : goto skipline;
145 : : }
6884 tgl@sss.pgh.pa.us 146 [ - + ]:CBC 145 : if (*end == '\0')
147 : : {
148 : : /* A line with only one word. Ignore silently. */
6884 tgl@sss.pgh.pa.us 149 :UBC 0 : goto skipline;
150 : : }
6884 tgl@sss.pgh.pa.us 151 :CBC 145 : *end = '\0';
152 : :
6164 teodor@sigaev.ru 153 : 145 : starto = findwrd(end + 1, &end, &flags);
6884 tgl@sss.pgh.pa.us 154 [ - + ]: 145 : if (!starto)
155 : : {
156 : : /* A line with only one word (+whitespace). Ignore silently. */
6884 tgl@sss.pgh.pa.us 157 :UBC 0 : goto skipline;
158 : : }
6884 tgl@sss.pgh.pa.us 159 :CBC 145 : *end = '\0';
160 : :
161 : : /*
162 : : * starti now points to the first word, and starto to the second word
163 : : * on the line, with a \0 terminator at the end of both words.
164 : : */
165 : :
166 [ + + ]: 145 : if (cur >= d->len)
167 : : {
6888 168 [ + - ]: 29 : if (d->len == 0)
169 : : {
6884 170 : 29 : d->len = 64;
202 michael@paquier.xyz 171 :GNC 29 : d->syn = palloc_array(Syn, d->len);
172 : : }
173 : : else
174 : : {
6888 tgl@sss.pgh.pa.us 175 :UBC 0 : d->len *= 2;
202 michael@paquier.xyz 176 :UNC 0 : d->syn = repalloc_array(d->syn, Syn, d->len);
177 : : }
178 : : }
179 : :
6686 tgl@sss.pgh.pa.us 180 [ + + ]:CBC 145 : if (case_sensitive)
181 : : {
182 : 40 : d->syn[cur].in = pstrdup(starti);
183 : 40 : d->syn[cur].out = pstrdup(starto);
184 : : }
185 : : else
186 : : {
560 peter@eisentraut.org 187 : 105 : d->syn[cur].in = str_tolower(starti, strlen(starti), DEFAULT_COLLATION_OID);
188 : 105 : d->syn[cur].out = str_tolower(starto, strlen(starto), DEFAULT_COLLATION_OID);
189 : : }
190 : :
5968 bruce@momjian.us 191 : 145 : d->syn[cur].flags = flags;
192 : :
6888 tgl@sss.pgh.pa.us 193 : 145 : cur++;
194 : :
6802 bruce@momjian.us 195 : 145 : skipline:
6884 tgl@sss.pgh.pa.us 196 : 145 : pfree(line);
197 : : }
198 : :
6586 199 : 29 : tsearch_readline_end(&trst);
332 tgl@sss.pgh.pa.us 200 :GNC 29 : pfree(filename);
201 : :
6888 tgl@sss.pgh.pa.us 202 :CBC 29 : d->len = cur;
6884 203 : 29 : qsort(d->syn, d->len, sizeof(Syn), compareSyn);
204 : :
6686 205 : 29 : d->case_sensitive = case_sensitive;
206 : :
6888 207 : 29 : PG_RETURN_POINTER(d);
208 : : }
209 : :
210 : : Datum
211 : 305 : dsynonym_lexize(PG_FUNCTION_ARGS)
212 : : {
213 : 305 : DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0);
214 : 305 : char *in = (char *) PG_GETARG_POINTER(1);
6802 bruce@momjian.us 215 : 305 : int32 len = PG_GETARG_INT32(2);
216 : : Syn key,
217 : : *found;
218 : : TSLexeme *res;
219 : :
220 : : /* note: d->len test protects against Solaris bsearch-of-no-items bug */
6884 tgl@sss.pgh.pa.us 221 [ + - - + ]: 305 : if (len <= 0 || d->len <= 0)
6888 tgl@sss.pgh.pa.us 222 :UBC 0 : PG_RETURN_POINTER(NULL);
223 : :
6686 tgl@sss.pgh.pa.us 224 [ + + ]:CBC 305 : if (d->case_sensitive)
225 : 5 : key.in = pnstrdup(in, len);
226 : : else
560 peter@eisentraut.org 227 : 300 : key.in = str_tolower(in, len, DEFAULT_COLLATION_OID);
228 : :
6888 tgl@sss.pgh.pa.us 229 : 305 : key.out = NULL;
230 : :
231 : 305 : found = (Syn *) bsearch(&key, d->syn, d->len, sizeof(Syn), compareSyn);
232 : 305 : pfree(key.in);
233 : :
234 [ + + ]: 305 : if (!found)
235 : 250 : PG_RETURN_POINTER(NULL);
236 : :
202 michael@paquier.xyz 237 :GNC 55 : res = palloc0_array(TSLexeme, 2);
22 jdavis@postgresql.or 238 :CBC 55 : res[0].lexeme = pstrdup(found->out);
6164 teodor@sigaev.ru 239 : 55 : res[0].flags = found->flags;
240 : :
6888 tgl@sss.pgh.pa.us 241 : 55 : PG_RETURN_POINTER(res);
242 : : }
|