Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * dict_xsyn.c
4 : : * Extended synonym dictionary
5 : : *
6 : : * Copyright (c) 2007-2025, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * contrib/dict_xsyn/dict_xsyn.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #include <ctype.h>
16 : :
17 : : #include "catalog/pg_collation_d.h"
18 : : #include "commands/defrem.h"
19 : : #include "tsearch/ts_locale.h"
20 : : #include "tsearch/ts_public.h"
21 : : #include "utils/formatting.h"
22 : :
164 tgl@sss.pgh.pa.us 23 :CBC 1 : PG_MODULE_MAGIC_EXT(
24 : : .name = "dict_xsyn",
25 : : .version = PG_VERSION
26 : : );
27 : :
28 : : typedef struct
29 : : {
30 : : char *key; /* Word */
31 : : char *value; /* Unparsed list of synonyms, including the
32 : : * word itself */
33 : : } Syn;
34 : :
35 : : typedef struct
36 : : {
37 : : int len;
38 : : Syn *syn;
39 : :
40 : : bool matchorig;
41 : : bool keeporig;
42 : : bool matchsynonyms;
43 : : bool keepsynonyms;
44 : : } DictSyn;
45 : :
46 : :
6536 47 : 2 : PG_FUNCTION_INFO_V1(dxsyn_init);
48 : 2 : PG_FUNCTION_INFO_V1(dxsyn_lexize);
49 : :
50 : : static char *
51 : 149 : find_word(char *in, char **end)
52 : : {
53 : : char *start;
54 : :
55 : 149 : *end = NULL;
263 peter@eisentraut.org 56 [ + + + + ]: 205 : while (*in && isspace((unsigned char) *in))
6536 tgl@sss.pgh.pa.us 57 : 56 : in += pg_mblen(in);
58 : :
59 [ + + + + ]: 149 : if (!*in || *in == '#')
60 : 84 : return NULL;
61 : 65 : start = in;
62 : :
263 peter@eisentraut.org 63 [ + - + + ]: 412 : while (*in && !isspace((unsigned char) *in))
6536 tgl@sss.pgh.pa.us 64 : 347 : in += pg_mblen(in);
65 : :
66 : 65 : *end = in;
67 : :
68 : 65 : return start;
69 : : }
70 : :
71 : : static int
72 : 74 : compare_syn(const void *a, const void *b)
73 : : {
5109 peter_e@gmx.net 74 : 74 : return strcmp(((const Syn *) a)->key, ((const Syn *) b)->key);
75 : : }
76 : :
77 : : static void
2867 78 : 14 : read_dictionary(DictSyn *d, const char *filename)
79 : : {
6505 bruce@momjian.us 80 : 14 : char *real_filename = get_tsearch_config_filename(filename, "rules");
81 : : tsearch_readline_state trst;
82 : : char *line;
83 : 14 : int cur = 0;
84 : :
6289 tgl@sss.pgh.pa.us 85 [ - + ]: 14 : if (!tsearch_readline_begin(&trst, real_filename))
6536 tgl@sss.pgh.pa.us 86 [ # # ]:UBC 0 : ereport(ERROR,
87 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
88 : : errmsg("could not open synonym file \"%s\": %m",
89 : : real_filename)));
90 : :
6289 tgl@sss.pgh.pa.us 91 [ + + ]:CBC 98 : while ((line = tsearch_readline(&trst)) != NULL)
92 : : {
93 : : char *value;
94 : : char *key;
95 : : char *pos;
96 : : char *end;
97 : :
6536 98 [ - + ]: 84 : if (*line == '\0')
6536 tgl@sss.pgh.pa.us 99 :UBC 0 : continue;
100 : :
263 peter@eisentraut.org 101 :CBC 84 : value = str_tolower(line, strlen(line), DEFAULT_COLLATION_OID);
6536 tgl@sss.pgh.pa.us 102 : 84 : pfree(line);
103 : :
5876 104 : 84 : pos = value;
105 [ + + ]: 116 : while ((key = find_word(pos, &end)) != NULL)
106 : : {
107 : : /* Enlarge syn structure if full */
108 [ + + ]: 38 : if (cur == d->len)
109 : : {
110 [ - + ]: 14 : d->len = (d->len > 0) ? 2 * d->len : 16;
111 [ - + ]: 14 : if (d->syn)
5876 tgl@sss.pgh.pa.us 112 :UBC 0 : d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len);
113 : : else
5876 tgl@sss.pgh.pa.us 114 :CBC 14 : d->syn = (Syn *) palloc(sizeof(Syn) * d->len);
115 : : }
116 : :
117 : : /* Save first word only if we will match it */
118 [ + + + + ]: 38 : if (pos != value || d->matchorig)
119 : : {
120 : 34 : d->syn[cur].key = pnstrdup(key, end - key);
121 : 34 : d->syn[cur].value = pstrdup(value);
122 : :
123 : 34 : cur++;
124 : : }
125 : :
126 : 38 : pos = end;
127 : :
128 : : /* Don't bother scanning synonyms if we will not match them */
129 [ + + ]: 38 : if (!d->matchsynonyms)
130 : 6 : break;
131 : : }
132 : :
133 : 84 : pfree(value);
134 : : }
135 : :
6289 136 : 14 : tsearch_readline_end(&trst);
137 : :
6536 138 : 14 : d->len = cur;
139 [ + + ]: 14 : if (cur > 1)
140 : 8 : qsort(d->syn, d->len, sizeof(Syn), compare_syn);
141 : :
142 : 14 : pfree(real_filename);
143 : 14 : }
144 : :
145 : : Datum
146 : 15 : dxsyn_init(PG_FUNCTION_ARGS)
147 : : {
6505 bruce@momjian.us 148 : 15 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
149 : : DictSyn *d;
150 : : ListCell *l;
5671 151 : 15 : char *filename = NULL;
152 : :
6536 tgl@sss.pgh.pa.us 153 : 15 : d = (DictSyn *) palloc0(sizeof(DictSyn));
154 : 15 : d->len = 0;
155 : 15 : d->syn = NULL;
5876 156 : 15 : d->matchorig = true;
6536 157 : 15 : d->keeporig = true;
5876 158 : 15 : d->matchsynonyms = false;
159 : 15 : d->keepsynonyms = true;
160 : :
6536 161 [ + + + + : 85 : foreach(l, dictoptions)
+ + ]
162 : : {
6505 bruce@momjian.us 163 : 70 : DefElem *defel = (DefElem *) lfirst(l);
164 : :
2780 tgl@sss.pgh.pa.us 165 [ + + ]: 70 : if (strcmp(defel->defname, "matchorig") == 0)
166 : : {
5876 167 : 14 : d->matchorig = defGetBoolean(defel);
168 : : }
2780 169 [ + + ]: 56 : else if (strcmp(defel->defname, "keeporig") == 0)
170 : : {
6536 171 : 14 : d->keeporig = defGetBoolean(defel);
172 : : }
2780 173 [ + + ]: 42 : else if (strcmp(defel->defname, "matchsynonyms") == 0)
174 : : {
5876 175 : 14 : d->matchsynonyms = defGetBoolean(defel);
176 : : }
2780 177 [ + + ]: 28 : else if (strcmp(defel->defname, "keepsynonyms") == 0)
178 : : {
5876 179 : 14 : d->keepsynonyms = defGetBoolean(defel);
180 : : }
2780 181 [ + - ]: 14 : else if (strcmp(defel->defname, "rules") == 0)
182 : : {
183 : : /* we can't read the rules before parsing all options! */
5876 184 : 14 : filename = defGetString(defel);
185 : : }
186 : : else
187 : : {
6536 tgl@sss.pgh.pa.us 188 [ # # ]:UBC 0 : ereport(ERROR,
189 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
190 : : errmsg("unrecognized xsyn parameter: \"%s\"",
191 : : defel->defname)));
192 : : }
193 : : }
194 : :
5876 tgl@sss.pgh.pa.us 195 [ + + ]:CBC 15 : if (filename)
196 : 14 : read_dictionary(d, filename);
197 : :
6536 198 : 15 : PG_RETURN_POINTER(d);
199 : : }
200 : :
201 : : Datum
202 : 21 : dxsyn_lexize(PG_FUNCTION_ARGS)
203 : : {
6505 bruce@momjian.us 204 : 21 : DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0);
205 : 21 : char *in = (char *) PG_GETARG_POINTER(1);
206 : 21 : int length = PG_GETARG_INT32(2);
207 : : Syn word;
208 : : Syn *found;
209 : 21 : TSLexeme *res = NULL;
210 : :
6536 tgl@sss.pgh.pa.us 211 [ + - + + ]: 21 : if (!length || d->len == 0)
212 : 3 : PG_RETURN_POINTER(NULL);
213 : :
214 : : /* Create search pattern */
215 : : {
6505 bruce@momjian.us 216 : 18 : char *temp = pnstrdup(in, length);
217 : :
263 peter@eisentraut.org 218 : 18 : word.key = str_tolower(temp, length, DEFAULT_COLLATION_OID);
6536 tgl@sss.pgh.pa.us 219 : 18 : pfree(temp);
220 : 18 : word.value = NULL;
221 : : }
222 : :
223 : : /* Look for matching syn */
6505 bruce@momjian.us 224 : 18 : found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn);
6536 tgl@sss.pgh.pa.us 225 : 18 : pfree(word.key);
226 : :
227 [ + + ]: 18 : if (!found)
228 : 9 : PG_RETURN_POINTER(NULL);
229 : :
230 : : /* Parse string of synonyms and return array of words */
231 : : {
5876 232 : 9 : char *value = found->value;
233 : : char *syn;
234 : : char *pos;
235 : : char *end;
6505 bruce@momjian.us 236 : 9 : int nsyns = 0;
237 : :
5876 tgl@sss.pgh.pa.us 238 : 9 : res = palloc(sizeof(TSLexeme));
239 : :
240 : 9 : pos = value;
241 [ + + ]: 33 : while ((syn = find_word(pos, &end)) != NULL)
242 : : {
6505 bruce@momjian.us 243 : 27 : res = repalloc(res, sizeof(TSLexeme) * (nsyns + 2));
244 : :
245 : : /* The first word is output only if keeporig=true */
5876 tgl@sss.pgh.pa.us 246 [ + + + + ]: 27 : if (pos != value || d->keeporig)
247 : : {
248 : 22 : res[nsyns].lexeme = pnstrdup(syn, end - syn);
5056 249 : 22 : res[nsyns].nvariant = 0;
250 : 22 : res[nsyns].flags = 0;
6536 251 : 22 : nsyns++;
252 : : }
253 : :
5876 254 : 27 : pos = end;
255 : :
256 : : /* Stop if we are not to output the synonyms */
257 [ + + ]: 27 : if (!d->keepsynonyms)
258 : 3 : break;
259 : : }
260 : 9 : res[nsyns].lexeme = NULL;
261 : : }
262 : :
6536 263 : 9 : PG_RETURN_POINTER(res);
264 : : }
|