1 : /*-------------------------------------------------------------------------
2 : *
3 : * reloptions.c
4 : * Core support for relation options (pg_class.reloptions)
5 : *
6 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.7 2007/12/01 23:44:44 tgl Exp $
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include "access/reloptions.h"
19 : #include "catalog/pg_type.h"
20 : #include "commands/defrem.h"
21 : #include "nodes/makefuncs.h"
22 : #include "utils/array.h"
23 : #include "utils/builtins.h"
24 : #include "utils/rel.h"
25 :
26 :
27 : /*
28 : * Transform a relation options list (list of DefElem) into the text array
29 : * format that is kept in pg_class.reloptions.
30 : *
31 : * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
32 : * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
33 : * reloptions value (possibly NULL), and we replace or remove entries
34 : * as needed.
35 : *
36 : * If ignoreOids is true, then we should ignore any occurrence of "oids"
37 : * in the list (it will be or has been handled by interpretOidsOption()).
38 : *
39 : * Note that this is not responsible for determining whether the options
40 : * are valid.
41 : *
42 : * Both oldOptions and the result are text arrays (or NULL for "default"),
43 : * but we declare them as Datums to avoid including array.h in reloptions.h.
44 : */
45 : Datum
46 : transformRelOptions(Datum oldOptions, List *defList,
47 : bool ignoreOids, bool isReset)
48 1078 : {
49 : Datum result;
50 : ArrayBuildState *astate;
51 : ListCell *cell;
52 :
53 : /* no change if empty list */
54 1078 : if (defList == NIL)
55 843 : return oldOptions;
56 :
57 : /* We build new array using accumArrayResult */
58 235 : astate = NULL;
59 :
60 : /* Copy any oldOptions that aren't to be replaced */
61 235 : if (oldOptions != (Datum) 0)
62 : {
63 0 : ArrayType *array = DatumGetArrayTypeP(oldOptions);
64 : Datum *oldoptions;
65 : int noldoptions;
66 : int i;
67 :
68 : Assert(ARR_ELEMTYPE(array) == TEXTOID);
69 :
70 0 : deconstruct_array(array, TEXTOID, -1, false, 'i',
71 : &oldoptions, NULL, &noldoptions);
72 :
73 0 : for (i = 0; i < noldoptions; i++)
74 : {
75 0 : text *oldoption = DatumGetTextP(oldoptions[i]);
76 0 : char *text_str = VARDATA(oldoption);
77 0 : int text_len = VARSIZE(oldoption) - VARHDRSZ;
78 :
79 : /* Search for a match in defList */
80 0 : foreach(cell, defList)
81 : {
82 0 : DefElem *def = lfirst(cell);
83 0 : int kw_len = strlen(def->defname);
84 :
85 0 : if (text_len > kw_len && text_str[kw_len] == '=' &&
86 : pg_strncasecmp(text_str, def->defname, kw_len) == 0)
87 0 : break;
88 : }
89 0 : if (!cell)
90 : {
91 : /* No match, so keep old option */
92 0 : astate = accumArrayResult(astate, oldoptions[i],
93 : false, TEXTOID,
94 : CurrentMemoryContext);
95 : }
96 : }
97 : }
98 :
99 : /*
100 : * If CREATE/SET, add new options to array; if RESET, just check that the
101 : * user didn't say RESET (option=val). (Must do this because the grammar
102 : * doesn't enforce it.)
103 : */
104 470 : foreach(cell, defList)
105 : {
106 235 : DefElem *def = lfirst(cell);
107 :
108 235 : if (isReset)
109 : {
110 0 : if (def->arg != NULL)
111 0 : ereport(ERROR,
112 : (errcode(ERRCODE_SYNTAX_ERROR),
113 : errmsg("RESET must not include values for parameters")));
114 : }
115 : else
116 : {
117 : text *t;
118 : const char *value;
119 : Size len;
120 :
121 235 : if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
122 235 : continue;
123 :
124 : /*
125 : * Flatten the DefElem into a text string like "name=arg". If we
126 : * have just "name", assume "name=true" is meant.
127 : */
128 0 : if (def->arg != NULL)
129 0 : value = defGetString(def);
130 : else
131 0 : value = "true";
132 0 : len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
133 : /* +1 leaves room for sprintf's trailing null */
134 0 : t = (text *) palloc(len + 1);
135 0 : SET_VARSIZE(t, len);
136 0 : sprintf(VARDATA(t), "%s=%s", def->defname, value);
137 :
138 0 : astate = accumArrayResult(astate, PointerGetDatum(t),
139 : false, TEXTOID,
140 : CurrentMemoryContext);
141 : }
142 : }
143 :
144 235 : if (astate)
145 0 : result = makeArrayResult(astate, CurrentMemoryContext);
146 : else
147 235 : result = (Datum) 0;
148 :
149 235 : return result;
150 : }
151 :
152 :
153 : /*
154 : * Convert the text-array format of reloptions into a List of DefElem.
155 : * This is the inverse of transformRelOptions().
156 : */
157 : List *
158 : untransformRelOptions(Datum options)
159 0 : {
160 0 : List *result = NIL;
161 : ArrayType *array;
162 : Datum *optiondatums;
163 : int noptions;
164 : int i;
165 :
166 : /* Nothing to do if no options */
167 0 : if (options == (Datum) 0)
168 0 : return result;
169 :
170 0 : array = DatumGetArrayTypeP(options);
171 :
172 : Assert(ARR_ELEMTYPE(array) == TEXTOID);
173 :
174 0 : deconstruct_array(array, TEXTOID, -1, false, 'i',
175 : &optiondatums, NULL, &noptions);
176 :
177 0 : for (i = 0; i < noptions; i++)
178 : {
179 : char *s;
180 : char *p;
181 0 : Node *val = NULL;
182 :
183 0 : s = DatumGetCString(DirectFunctionCall1(textout, optiondatums[i]));
184 0 : p = strchr(s, '=');
185 0 : if (p)
186 : {
187 0 : *p++ = '\0';
188 0 : val = (Node *) makeString(pstrdup(p));
189 : }
190 0 : result = lappend(result, makeDefElem(pstrdup(s), val));
191 : }
192 :
193 0 : return result;
194 : }
195 :
196 :
197 : /*
198 : * Interpret reloptions that are given in text-array format.
199 : *
200 : * options: array of "keyword=value" strings, as built by transformRelOptions
201 : * numkeywords: number of legal keywords
202 : * keywords: the allowed keywords
203 : * values: output area
204 : * validate: if true, throw error for unrecognized keywords.
205 : *
206 : * The keywords and values arrays must both be of length numkeywords.
207 : * The values entry corresponding to a keyword is set to a palloc'd string
208 : * containing the corresponding value, or NULL if the keyword does not appear.
209 : */
210 : void
211 : parseRelOptions(Datum options, int numkeywords, const char *const * keywords,
212 : char **values, bool validate)
213 792 : {
214 : ArrayType *array;
215 : Datum *optiondatums;
216 : int noptions;
217 : int i;
218 :
219 : /* Initialize to "all defaulted" */
220 792 : MemSet(values, 0, numkeywords * sizeof(char *));
221 :
222 : /* Done if no options */
223 792 : if (options == (Datum) 0)
224 792 : return;
225 :
226 0 : array = DatumGetArrayTypeP(options);
227 :
228 : Assert(ARR_ELEMTYPE(array) == TEXTOID);
229 :
230 0 : deconstruct_array(array, TEXTOID, -1, false, 'i',
231 : &optiondatums, NULL, &noptions);
232 :
233 0 : for (i = 0; i < noptions; i++)
234 : {
235 0 : text *optiontext = DatumGetTextP(optiondatums[i]);
236 0 : char *text_str = VARDATA(optiontext);
237 0 : int text_len = VARSIZE(optiontext) - VARHDRSZ;
238 : int j;
239 :
240 : /* Search for a match in keywords */
241 0 : for (j = 0; j < numkeywords; j++)
242 : {
243 0 : int kw_len = strlen(keywords[j]);
244 :
245 0 : if (text_len > kw_len && text_str[kw_len] == '=' &&
246 : pg_strncasecmp(text_str, keywords[j], kw_len) == 0)
247 : {
248 : char *value;
249 : int value_len;
250 :
251 0 : if (values[j] && validate)
252 0 : ereport(ERROR,
253 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
254 : errmsg("parameter \"%s\" specified more than once",
255 : keywords[j])));
256 0 : value_len = text_len - kw_len - 1;
257 0 : value = (char *) palloc(value_len + 1);
258 0 : memcpy(value, text_str + kw_len + 1, value_len);
259 0 : value[value_len] = '\0';
260 0 : values[j] = value;
261 0 : break;
262 : }
263 : }
264 0 : if (j >= numkeywords && validate)
265 : {
266 : char *s;
267 : char *p;
268 :
269 0 : s = DatumGetCString(DirectFunctionCall1(textout, optiondatums[i]));
270 0 : p = strchr(s, '=');
271 0 : if (p)
272 0 : *p = '\0';
273 0 : ereport(ERROR,
274 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
275 : errmsg("unrecognized parameter \"%s\"", s)));
276 : }
277 : }
278 : }
279 :
280 :
281 : /*
282 : * Parse reloptions for anything using StdRdOptions (ie, fillfactor only)
283 : */
284 : bytea *
285 : default_reloptions(Datum reloptions, bool validate,
286 : int minFillfactor, int defaultFillfactor)
287 792 : {
288 : static const char *const default_keywords[1] = {"fillfactor"};
289 : char *values[1];
290 : int32 fillfactor;
291 : StdRdOptions *result;
292 :
293 792 : parseRelOptions(reloptions, 1, default_keywords, values, validate);
294 :
295 : /*
296 : * If no options, we can just return NULL rather than doing anything.
297 : * (defaultFillfactor is thus not used, but we require callers to pass it
298 : * anyway since we would need it if more options were added.)
299 : */
300 792 : if (values[0] == NULL)
301 792 : return NULL;
302 :
303 0 : fillfactor = pg_atoi(values[0], sizeof(int32), 0);
304 0 : if (fillfactor < minFillfactor || fillfactor > 100)
305 : {
306 0 : if (validate)
307 0 : ereport(ERROR,
308 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
309 : errmsg("fillfactor=%d is out of range (should be between %d and 100)",
310 : fillfactor, minFillfactor)));
311 0 : return NULL;
312 : }
313 :
314 0 : result = (StdRdOptions *) palloc(sizeof(StdRdOptions));
315 0 : SET_VARSIZE(result, sizeof(StdRdOptions));
316 :
317 0 : result->fillfactor = fillfactor;
318 :
319 0 : return (bytea *) result;
320 : }
321 :
322 :
323 : /*
324 : * Parse options for heaps (and perhaps someday toast tables).
325 : */
326 : bytea *
327 : heap_reloptions(char relkind, Datum reloptions, bool validate)
328 792 : {
329 792 : return default_reloptions(reloptions, validate,
330 : HEAP_MIN_FILLFACTOR,
331 : HEAP_DEFAULT_FILLFACTOR);
332 : }
333 :
334 :
335 : /*
336 : * Parse options for indexes.
337 : *
338 : * amoptions Oid of option parser
339 : * reloptions options as text[] datum
340 : * validate error flag
341 : */
342 : bytea *
343 : index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
344 286 : {
345 : FmgrInfo flinfo;
346 : FunctionCallInfoData fcinfo;
347 : Datum result;
348 :
349 : Assert(RegProcedureIsValid(amoptions));
350 :
351 : /* Assume function is strict */
352 286 : if (reloptions == (Datum) 0)
353 286 : return NULL;
354 :
355 : /* Can't use OidFunctionCallN because we might get a NULL result */
356 0 : fmgr_info(amoptions, &flinfo);
357 :
358 0 : InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
359 :
360 0 : fcinfo.arg[0] = reloptions;
361 0 : fcinfo.arg[1] = BoolGetDatum(validate);
362 0 : fcinfo.argnull[0] = false;
363 0 : fcinfo.argnull[1] = false;
364 :
365 0 : result = FunctionCallInvoke(&fcinfo);
366 :
367 0 : if (fcinfo.isnull || DatumGetPointer(result) == NULL)
368 0 : return NULL;
369 :
370 0 : return DatumGetByteaP(result);
371 : }
|