Fix oversight in previous error-reporting patch; mustn't pfree path string
[PostgreSQL.git] / src / backend / tsearch / dict_synonym.c
blob443476cd2e958423e7b7119bd2cf90243980292f
1 /*-------------------------------------------------------------------------
3 * dict_synonym.c
4 * Synonym dictionary: replace word by its synonym
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
9 * IDENTIFICATION
10 * $PostgreSQL$
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
16 #include "commands/defrem.h"
17 #include "tsearch/ts_locale.h"
18 #include "tsearch/ts_public.h"
19 #include "tsearch/ts_utils.h"
20 #include "utils/builtins.h"
22 typedef struct
24 char *in;
25 char *out;
26 } Syn;
28 typedef struct
30 int len; /* length of syn array */
31 Syn *syn;
32 bool case_sensitive;
33 } DictSyn;
36 * Finds the next whitespace-delimited word within the 'in' string.
37 * Returns a pointer to the first character of the word, and a pointer
38 * to the next byte after the last character in the word (in *end).
40 static char *
41 findwrd(char *in, char **end)
43 char *start;
45 /* Skip leading spaces */
46 while (*in && t_isspace(in))
47 in += pg_mblen(in);
49 /* Return NULL on empty lines */
50 if (*in == '\0')
52 *end = NULL;
53 return NULL;
56 start = in;
58 /* Find end of word */
59 while (*in && !t_isspace(in))
60 in += pg_mblen(in);
62 *end = in;
63 return start;
66 static int
67 compareSyn(const void *a, const void *b)
69 return strcmp(((Syn *) a)->in, ((Syn *) b)->in);
73 Datum
74 dsynonym_init(PG_FUNCTION_ARGS)
76 List *dictoptions = (List *) PG_GETARG_POINTER(0);
77 DictSyn *d;
78 ListCell *l;
79 char *filename = NULL;
80 bool case_sensitive = false;
81 tsearch_readline_state trst;
82 char *starti,
83 *starto,
84 *end = NULL;
85 int cur = 0;
86 char *line = NULL;
88 foreach(l, dictoptions)
90 DefElem *defel = (DefElem *) lfirst(l);
92 if (pg_strcasecmp("Synonyms", defel->defname) == 0)
93 filename = defGetString(defel);
94 else if (pg_strcasecmp("CaseSensitive", defel->defname) == 0)
95 case_sensitive = defGetBoolean(defel);
96 else
97 ereport(ERROR,
98 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
99 errmsg("unrecognized synonym parameter: \"%s\"",
100 defel->defname)));
103 if (!filename)
104 ereport(ERROR,
105 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
106 errmsg("missing Synonyms parameter")));
108 filename = get_tsearch_config_filename(filename, "syn");
110 if (!tsearch_readline_begin(&trst, filename))
111 ereport(ERROR,
112 (errcode(ERRCODE_CONFIG_FILE_ERROR),
113 errmsg("could not open synonym file \"%s\": %m",
114 filename)));
116 d = (DictSyn *) palloc0(sizeof(DictSyn));
118 while ((line = tsearch_readline(&trst)) != NULL)
120 starti = findwrd(line, &end);
121 if (!starti)
123 /* Empty line */
124 goto skipline;
126 if (*end == '\0')
128 /* A line with only one word. Ignore silently. */
129 goto skipline;
131 *end = '\0';
133 starto = findwrd(end + 1, &end);
134 if (!starto)
136 /* A line with only one word (+whitespace). Ignore silently. */
137 goto skipline;
139 *end = '\0';
142 * starti now points to the first word, and starto to the second word
143 * on the line, with a \0 terminator at the end of both words.
146 if (cur >= d->len)
148 if (d->len == 0)
150 d->len = 64;
151 d->syn = (Syn *) palloc(sizeof(Syn) * d->len);
153 else
155 d->len *= 2;
156 d->syn = (Syn *) repalloc(d->syn, sizeof(Syn) * d->len);
160 if (case_sensitive)
162 d->syn[cur].in = pstrdup(starti);
163 d->syn[cur].out = pstrdup(starto);
165 else
167 d->syn[cur].in = lowerstr(starti);
168 d->syn[cur].out = lowerstr(starto);
171 cur++;
173 skipline:
174 pfree(line);
177 tsearch_readline_end(&trst);
179 d->len = cur;
180 qsort(d->syn, d->len, sizeof(Syn), compareSyn);
182 d->case_sensitive = case_sensitive;
184 PG_RETURN_POINTER(d);
187 Datum
188 dsynonym_lexize(PG_FUNCTION_ARGS)
190 DictSyn *d = (DictSyn *) PG_GETARG_POINTER(0);
191 char *in = (char *) PG_GETARG_POINTER(1);
192 int32 len = PG_GETARG_INT32(2);
193 Syn key,
194 *found;
195 TSLexeme *res;
197 /* note: d->len test protects against Solaris bsearch-of-no-items bug */
198 if (len <= 0 || d->len <= 0)
199 PG_RETURN_POINTER(NULL);
201 if (d->case_sensitive)
202 key.in = pnstrdup(in, len);
203 else
204 key.in = lowerstr_with_len(in, len);
206 key.out = NULL;
208 found = (Syn *) bsearch(&key, d->syn, d->len, sizeof(Syn), compareSyn);
209 pfree(key.in);
211 if (!found)
212 PG_RETURN_POINTER(NULL);
214 res = palloc0(sizeof(TSLexeme) * 2);
215 res[0].lexeme = pstrdup(found->out);
217 PG_RETURN_POINTER(res);