1 /*-------------------------------------------------------------------------
4 * Synonym dictionary: replace word by its synonym
6 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
10 * src/backend/tsearch/dict_synonym.c
12 *-------------------------------------------------------------------------
16 #include "commands/defrem.h"
17 #include "tsearch/ts_locale.h"
18 #include "tsearch/ts_utils.h"
19 #include "utils/builtins.h"
31 int len; /* length of syn array */
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 threated as word
41 * character if flags is not null.
44 findwrd(char *in, char **end, uint16 *flags)
49 /* Skip leading spaces */
50 while (*in && t_isspace(in))
53 /* Return NULL on empty lines */
60 lastchar = start = in;
62 /* Find end of word */
63 while (*in && !t_isspace(in))
69 if (in - lastchar == 1 && t_iseq(lastchar, '*') && flags)
85 compareSyn(const void *a, const void *b)
87 return strcmp(((const Syn *) a)->in, ((const Syn *) b)->in);
92 dsynonym_init(PG_FUNCTION_ARGS)
94 List *dictoptions = (List *) PG_GETARG_POINTER(0);
97 char *filename = NULL;
98 bool case_sensitive = false;
99 tsearch_readline_state trst;
107 foreach(l, dictoptions)
109 DefElem *defel = (DefElem *) lfirst(l);
111 if (pg_strcasecmp("Synonyms", defel->defname) == 0)
112 filename = defGetString(defel);
113 else if (pg_strcasecmp("CaseSensitive", defel->defname) == 0)
114 case_sensitive = defGetBoolean(defel);
117 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
118 errmsg("unrecognized synonym parameter: \"%s\"",
124 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
125 errmsg("missing Synonyms parameter")));
127 filename = get_tsearch_config_filename(filename, "syn");
129 if (!tsearch_readline_begin(&trst, filename))
131 (errcode(ERRCODE_CONFIG_FILE_ERROR),
132 errmsg("could not open synonym file \"%s\": %m",
135 d = (DictSyn *) palloc0(sizeof(DictSyn));
137 while ((line = tsearch_readline(&trst)) != NULL)
139 starti = findwrd(line, &end, NULL);
147 /* A line with only one word. Ignore silently. */
152 starto = findwrd(end + 1, &end, &flags);
155 /* A line with only one word (+whitespace). Ignore silently. */
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.
170 d->syn = (Syn *) palloc(sizeof(Syn) * d->len);
175 d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len);
181 d->syn[cur].in = pstrdup(starti);
182 d->syn[cur].out = pstrdup(starto);
186 d->syn[cur].in = lowerstr(starti);
187 d->syn[cur].out = lowerstr(starto);
190 d->syn[cur].outlen = strlen(starto);
191 d->syn[cur].flags = flags;
199 tsearch_readline_end(&trst);
202 qsort(d->syn, d->len, sizeof(Syn), compareSyn);
204 d->case_sensitive = case_sensitive;
206 PG_RETURN_POINTER(d);
210 dsynonym_lexize(PG_FUNCTION_ARGS)
212 DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0);
213 char *in = (char *) PG_GETARG_POINTER(1);
214 int32 len = PG_GETARG_INT32(2);
219 /* note: d->len test protects against Solaris bsearch-of-no-items bug */
220 if (len <= 0 || d->len <= 0)
221 PG_RETURN_POINTER(NULL);
223 if (d->case_sensitive)
224 key.in = pnstrdup(in, len);
226 key.in = lowerstr_with_len(in, len);
230 found = (Syn *) bsearch(&key, d->syn, d->len, sizeof(Syn), compareSyn);
234 PG_RETURN_POINTER(NULL);
236 res = palloc0(sizeof(TSLexeme) * 2);
237 res[0].lexeme = pnstrdup(found->out, found->outlen);
238 res[0].flags = found->flags;
240 PG_RETURN_POINTER(res);