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