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