Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * dict_xsyn.c
4 : : * Extended synonym dictionary
5 : : *
6 : : * Copyright (c) 2007-2024, 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 : :
6026 tgl@sss.pgh.pa.us 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 : : {
4599 peter_e@gmx.net 69 : 74 : return strcmp(((const Syn *) a)->key, ((const Syn *) b)->key);
70 : : }
71 : :
72 : : static void
2357 73 : 14 : read_dictionary(DictSyn *d, const char *filename)
74 : : {
5995 bruce@momjian.us 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 : :
5779 tgl@sss.pgh.pa.us 80 [ - + ]: 14 : if (!tsearch_readline_begin(&trst, real_filename))
6026 tgl@sss.pgh.pa.us 81 [ # # ]:UBC 0 : ereport(ERROR,
82 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
83 : : errmsg("could not open synonym file \"%s\": %m",
84 : : real_filename)));
85 : :
5779 tgl@sss.pgh.pa.us 86 [ + + ]:CBC 98 : while ((line = tsearch_readline(&trst)) != NULL)
87 : : {
88 : : char *value;
89 : : char *key;
90 : : char *pos;
91 : : char *end;
92 : :
6026 93 [ - + ]: 84 : if (*line == '\0')
6026 tgl@sss.pgh.pa.us 94 :UBC 0 : continue;
95 : :
6026 tgl@sss.pgh.pa.us 96 :CBC 84 : value = lowerstr(line);
97 : 84 : pfree(line);
98 : :
5366 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)
5366 tgl@sss.pgh.pa.us 107 :UBC 0 : d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len);
108 : : else
5366 tgl@sss.pgh.pa.us 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 : :
5779 131 : 14 : tsearch_readline_end(&trst);
132 : :
6026 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 : : {
5995 bruce@momjian.us 143 : 15 : List *dictoptions = (List *) PG_GETARG_POINTER(0);
144 : : DictSyn *d;
145 : : ListCell *l;
5161 146 : 15 : char *filename = NULL;
147 : :
6026 tgl@sss.pgh.pa.us 148 : 15 : d = (DictSyn *) palloc0(sizeof(DictSyn));
149 : 15 : d->len = 0;
150 : 15 : d->syn = NULL;
5366 151 : 15 : d->matchorig = true;
6026 152 : 15 : d->keeporig = true;
5366 153 : 15 : d->matchsynonyms = false;
154 : 15 : d->keepsynonyms = true;
155 : :
6026 156 [ + + + + : 85 : foreach(l, dictoptions)
+ + ]
157 : : {
5995 bruce@momjian.us 158 : 70 : DefElem *defel = (DefElem *) lfirst(l);
159 : :
2270 tgl@sss.pgh.pa.us 160 [ + + ]: 70 : if (strcmp(defel->defname, "matchorig") == 0)
161 : : {
5366 162 : 14 : d->matchorig = defGetBoolean(defel);
163 : : }
2270 164 [ + + ]: 56 : else if (strcmp(defel->defname, "keeporig") == 0)
165 : : {
6026 166 : 14 : d->keeporig = defGetBoolean(defel);
167 : : }
2270 168 [ + + ]: 42 : else if (strcmp(defel->defname, "matchsynonyms") == 0)
169 : : {
5366 170 : 14 : d->matchsynonyms = defGetBoolean(defel);
171 : : }
2270 172 [ + + ]: 28 : else if (strcmp(defel->defname, "keepsynonyms") == 0)
173 : : {
5366 174 : 14 : d->keepsynonyms = defGetBoolean(defel);
175 : : }
2270 176 [ + - ]: 14 : else if (strcmp(defel->defname, "rules") == 0)
177 : : {
178 : : /* we can't read the rules before parsing all options! */
5366 179 : 14 : filename = defGetString(defel);
180 : : }
181 : : else
182 : : {
6026 tgl@sss.pgh.pa.us 183 [ # # ]:UBC 0 : ereport(ERROR,
184 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
185 : : errmsg("unrecognized xsyn parameter: \"%s\"",
186 : : defel->defname)));
187 : : }
188 : : }
189 : :
5366 tgl@sss.pgh.pa.us 190 [ + + ]:CBC 15 : if (filename)
191 : 14 : read_dictionary(d, filename);
192 : :
6026 193 : 15 : PG_RETURN_POINTER(d);
194 : : }
195 : :
196 : : Datum
197 : 21 : dxsyn_lexize(PG_FUNCTION_ARGS)
198 : : {
5995 bruce@momjian.us 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 : :
6026 tgl@sss.pgh.pa.us 206 [ + - + + ]: 21 : if (!length || d->len == 0)
207 : 3 : PG_RETURN_POINTER(NULL);
208 : :
209 : : /* Create search pattern */
210 : : {
5995 bruce@momjian.us 211 : 18 : char *temp = pnstrdup(in, length);
212 : :
6026 tgl@sss.pgh.pa.us 213 : 18 : word.key = lowerstr(temp);
214 : 18 : pfree(temp);
215 : 18 : word.value = NULL;
216 : : }
217 : :
218 : : /* Look for matching syn */
5995 bruce@momjian.us 219 : 18 : found = (Syn *) bsearch(&word, d->syn, d->len, sizeof(Syn), compare_syn);
6026 tgl@sss.pgh.pa.us 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 : : {
5366 227 : 9 : char *value = found->value;
228 : : char *syn;
229 : : char *pos;
230 : : char *end;
5995 bruce@momjian.us 231 : 9 : int nsyns = 0;
232 : :
5366 tgl@sss.pgh.pa.us 233 : 9 : res = palloc(sizeof(TSLexeme));
234 : :
235 : 9 : pos = value;
236 [ + + ]: 33 : while ((syn = find_word(pos, &end)) != NULL)
237 : : {
5995 bruce@momjian.us 238 : 27 : res = repalloc(res, sizeof(TSLexeme) * (nsyns + 2));
239 : :
240 : : /* The first word is output only if keeporig=true */
5366 tgl@sss.pgh.pa.us 241 [ + + + + ]: 27 : if (pos != value || d->keeporig)
242 : : {
243 : 22 : res[nsyns].lexeme = pnstrdup(syn, end - syn);
4546 244 : 22 : res[nsyns].nvariant = 0;
245 : 22 : res[nsyns].flags = 0;
6026 246 : 22 : nsyns++;
247 : : }
248 : :
5366 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 : :
6026 258 : 9 : PG_RETURN_POINTER(res);
259 : : }
|