Age Owner 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-2023, 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_utils.h"
19 : #include "utils/builtins.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 *
4986 teodor 44 CBC 220 : findwrd(char *in, char **end, uint16 *flags)
45 : {
46 : char *start;
47 : char *lastchar;
48 :
49 : /* Skip leading spaces */
5710 tgl 50 220 : while (*in && t_isspace(in))
5710 tgl 51 UBC 0 : in += pg_mblen(in);
52 :
53 : /* Return NULL on empty lines */
5710 tgl 54 CBC 220 : if (*in == '\0')
55 : {
5706 tgl 56 UBC 0 : *end = NULL;
5710 57 0 : return NULL;
58 : }
59 :
4986 teodor 60 CBC 220 : lastchar = start = in;
61 :
62 : /* Find end of word */
5710 tgl 63 1606 : while (*in && !t_isspace(in))
64 : {
4986 teodor 65 1386 : lastchar = in;
5710 tgl 66 1386 : in += pg_mblen(in);
67 : }
68 :
4790 bruce 69 220 : if (in - lastchar == 1 && t_iseq(lastchar, '*') && flags)
70 : {
4986 teodor 71 22 : *flags = TSL_PREFIX;
72 22 : *end = lastchar;
73 : }
74 : else
75 : {
76 198 : if (flags)
4790 bruce 77 88 : *flags = 0;
4986 teodor 78 198 : *end = in;
79 : }
80 :
5710 tgl 81 220 : return start;
82 : }
83 :
84 : static int
85 652 : compareSyn(const void *a, const void *b)
86 : {
4228 peter_e 87 652 : return strcmp(((const Syn *) a)->in, ((const Syn *) b)->in);
88 : }
89 :
90 :
91 : Datum
5710 tgl 92 25 : dsynonym_init(PG_FUNCTION_ARGS)
93 : {
5709 94 25 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
95 : DictSyn *d;
96 : ListCell *l;
97 25 : char *filename = NULL;
5508 98 25 : bool case_sensitive = false;
99 : tsearch_readline_state trst;
100 : char *starti,
101 : *starto,
5710 102 25 : *end = NULL;
5709 103 25 : int cur = 0;
5706 104 25 : char *line = NULL;
4986 teodor 105 25 : uint16 flags = 0;
106 :
5709 tgl 107 66 : foreach(l, dictoptions)
108 : {
109 44 : DefElem *defel = (DefElem *) lfirst(l);
110 :
1899 111 44 : if (strcmp(defel->defname, "synonyms") == 0)
5709 112 25 : filename = defGetString(defel);
1899 113 19 : else if (strcmp(defel->defname, "casesensitive") == 0)
5508 114 19 : case_sensitive = defGetBoolean(defel);
115 : else
5709 tgl 116 UBC 0 : ereport(ERROR,
117 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
118 : errmsg("unrecognized synonym parameter: \"%s\"",
119 : defel->defname)));
120 : }
121 :
5709 tgl 122 CBC 22 : if (!filename)
5710 tgl 123 UBC 0 : ereport(ERROR,
124 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
125 : errmsg("missing Synonyms parameter")));
126 :
5709 tgl 127 CBC 22 : filename = get_tsearch_config_filename(filename, "syn");
128 :
5408 129 22 : if (!tsearch_readline_begin(&trst, filename))
5710 tgl 130 UBC 0 : ereport(ERROR,
131 : (errcode(ERRCODE_CONFIG_FILE_ERROR),
132 : errmsg("could not open synonym file \"%s\": %m",
133 : filename)));
134 :
5710 tgl 135 CBC 22 : d = (DictSyn *) palloc0(sizeof(DictSyn));
136 :
5408 137 132 : while ((line = tsearch_readline(&trst)) != NULL)
138 : {
4986 teodor 139 110 : starti = findwrd(line, &end, NULL);
5706 tgl 140 110 : if (!starti)
141 : {
142 : /* Empty line */
5706 tgl 143 UBC 0 : goto skipline;
144 : }
5706 tgl 145 CBC 110 : if (*end == '\0')
146 : {
147 : /* A line with only one word. Ignore silently. */
5706 tgl 148 UBC 0 : goto skipline;
149 : }
5706 tgl 150 CBC 110 : *end = '\0';
151 :
4986 teodor 152 110 : starto = findwrd(end + 1, &end, &flags);
5706 tgl 153 110 : if (!starto)
154 : {
155 : /* A line with only one word (+whitespace). Ignore silently. */
5706 tgl 156 UBC 0 : goto skipline;
157 : }
5706 tgl 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 : {
5710 167 22 : if (d->len == 0)
168 : {
5706 169 22 : d->len = 64;
5710 170 22 : d->syn = (Syn *) palloc(sizeof(Syn) * d->len);
171 : }
172 : else
173 : {
5710 tgl 174 UBC 0 : d->len *= 2;
175 0 : d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len);
176 : }
177 : }
178 :
5508 tgl 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 :
4986 teodor 190 110 : d->syn[cur].outlen = strlen(starto);
4790 bruce 191 110 : d->syn[cur].flags = flags;
192 :
5710 tgl 193 110 : cur++;
194 :
5624 bruce 195 110 : skipline:
5706 tgl 196 110 : pfree(line);
197 : }
198 :
5408 199 22 : tsearch_readline_end(&trst);
200 :
5710 201 22 : d->len = cur;
5706 202 22 : qsort(d->syn, d->len, sizeof(Syn), compareSyn);
203 :
5508 204 22 : d->case_sensitive = case_sensitive;
205 :
5710 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);
5624 bruce 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 */
5706 tgl 220 183 : if (len <= 0 || d->len <= 0)
5710 tgl 221 UBC 0 : PG_RETURN_POINTER(NULL);
222 :
5508 tgl 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 :
5710 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 :
5706 236 33 : res = palloc0(sizeof(TSLexeme) * 2);
4986 teodor 237 33 : res[0].lexeme = pnstrdup(found->out, found->outlen);
238 33 : res[0].flags = found->flags;
239 :
5710 tgl 240 33 : PG_RETURN_POINTER(res);
241 : }
|