00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "postgres.h"
00017
00018 #include "access/gist_private.h"
00019 #include "access/hash.h"
00020 #include "access/htup_details.h"
00021 #include "access/nbtree.h"
00022 #include "access/reloptions.h"
00023 #include "access/spgist.h"
00024 #include "catalog/pg_type.h"
00025 #include "commands/defrem.h"
00026 #include "commands/tablespace.h"
00027 #include "nodes/makefuncs.h"
00028 #include "utils/array.h"
00029 #include "utils/attoptcache.h"
00030 #include "utils/builtins.h"
00031 #include "utils/guc.h"
00032 #include "utils/memutils.h"
00033 #include "utils/rel.h"
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053 static relopt_bool boolRelOpts[] =
00054 {
00055 {
00056 {
00057 "autovacuum_enabled",
00058 "Enables autovacuum in this relation",
00059 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00060 },
00061 true
00062 },
00063 {
00064 {
00065 "fastupdate",
00066 "Enables \"fast update\" feature for this GIN index",
00067 RELOPT_KIND_GIN
00068 },
00069 true
00070 },
00071 {
00072 {
00073 "security_barrier",
00074 "View acts as a row security barrier",
00075 RELOPT_KIND_VIEW
00076 },
00077 false
00078 },
00079
00080 {{NULL}}
00081 };
00082
00083 static relopt_int intRelOpts[] =
00084 {
00085 {
00086 {
00087 "fillfactor",
00088 "Packs table pages only to this percentage",
00089 RELOPT_KIND_HEAP
00090 },
00091 HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
00092 },
00093 {
00094 {
00095 "fillfactor",
00096 "Packs btree index pages only to this percentage",
00097 RELOPT_KIND_BTREE
00098 },
00099 BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
00100 },
00101 {
00102 {
00103 "fillfactor",
00104 "Packs hash index pages only to this percentage",
00105 RELOPT_KIND_HASH
00106 },
00107 HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
00108 },
00109 {
00110 {
00111 "fillfactor",
00112 "Packs gist index pages only to this percentage",
00113 RELOPT_KIND_GIST
00114 },
00115 GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
00116 },
00117 {
00118 {
00119 "fillfactor",
00120 "Packs spgist index pages only to this percentage",
00121 RELOPT_KIND_SPGIST
00122 },
00123 SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
00124 },
00125 {
00126 {
00127 "autovacuum_vacuum_threshold",
00128 "Minimum number of tuple updates or deletes prior to vacuum",
00129 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00130 },
00131 -1, 0, INT_MAX
00132 },
00133 {
00134 {
00135 "autovacuum_analyze_threshold",
00136 "Minimum number of tuple inserts, updates or deletes prior to analyze",
00137 RELOPT_KIND_HEAP
00138 },
00139 -1, 0, INT_MAX
00140 },
00141 {
00142 {
00143 "autovacuum_vacuum_cost_delay",
00144 "Vacuum cost delay in milliseconds, for autovacuum",
00145 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00146 },
00147 -1, 0, 100
00148 },
00149 {
00150 {
00151 "autovacuum_vacuum_cost_limit",
00152 "Vacuum cost amount available before napping, for autovacuum",
00153 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00154 },
00155 -1, 1, 10000
00156 },
00157 {
00158 {
00159 "autovacuum_freeze_min_age",
00160 "Minimum age at which VACUUM should freeze a table row, for autovacuum",
00161 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00162 },
00163 -1, 0, 1000000000
00164 },
00165 {
00166 {
00167 "autovacuum_freeze_max_age",
00168 "Age at which to autovacuum a table to prevent transaction ID wraparound",
00169 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00170 },
00171 -1, 100000000, 2000000000
00172 },
00173 {
00174 {
00175 "autovacuum_freeze_table_age",
00176 "Age at which VACUUM should perform a full table sweep to replace old Xid values with FrozenXID",
00177 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00178 }, -1, 0, 2000000000
00179 },
00180
00181 {{NULL}}
00182 };
00183
00184 static relopt_real realRelOpts[] =
00185 {
00186 {
00187 {
00188 "autovacuum_vacuum_scale_factor",
00189 "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
00190 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00191 },
00192 -1, 0.0, 100.0
00193 },
00194 {
00195 {
00196 "autovacuum_analyze_scale_factor",
00197 "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
00198 RELOPT_KIND_HEAP
00199 },
00200 -1, 0.0, 100.0
00201 },
00202 {
00203 {
00204 "seq_page_cost",
00205 "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
00206 RELOPT_KIND_TABLESPACE
00207 },
00208 -1, 0.0, DBL_MAX
00209 },
00210 {
00211 {
00212 "random_page_cost",
00213 "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
00214 RELOPT_KIND_TABLESPACE
00215 },
00216 -1, 0.0, DBL_MAX
00217 },
00218 {
00219 {
00220 "n_distinct",
00221 "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
00222 RELOPT_KIND_ATTRIBUTE
00223 },
00224 0, -1.0, DBL_MAX
00225 },
00226 {
00227 {
00228 "n_distinct_inherited",
00229 "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
00230 RELOPT_KIND_ATTRIBUTE
00231 },
00232 0, -1.0, DBL_MAX
00233 },
00234
00235 {{NULL}}
00236 };
00237
00238 static relopt_string stringRelOpts[] =
00239 {
00240 {
00241 {
00242 "buffering",
00243 "Enables buffering build for this GiST index",
00244 RELOPT_KIND_GIST
00245 },
00246 4,
00247 false,
00248 gistValidateBufferingOption,
00249 "auto"
00250 },
00251
00252 {{NULL}}
00253 };
00254
00255 static relopt_gen **relOpts = NULL;
00256 static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
00257
00258 static int num_custom_options = 0;
00259 static relopt_gen **custom_options = NULL;
00260 static bool need_initialization = true;
00261
00262 static void initialize_reloptions(void);
00263 static void parse_one_reloption(relopt_value *option, char *text_str,
00264 int text_len, bool validate);
00265
00266
00267
00268
00269
00270
00271
00272 static void
00273 initialize_reloptions(void)
00274 {
00275 int i;
00276 int j;
00277
00278 j = 0;
00279 for (i = 0; boolRelOpts[i].gen.name; i++)
00280 j++;
00281 for (i = 0; intRelOpts[i].gen.name; i++)
00282 j++;
00283 for (i = 0; realRelOpts[i].gen.name; i++)
00284 j++;
00285 for (i = 0; stringRelOpts[i].gen.name; i++)
00286 j++;
00287 j += num_custom_options;
00288
00289 if (relOpts)
00290 pfree(relOpts);
00291 relOpts = MemoryContextAlloc(TopMemoryContext,
00292 (j + 1) * sizeof(relopt_gen *));
00293
00294 j = 0;
00295 for (i = 0; boolRelOpts[i].gen.name; i++)
00296 {
00297 relOpts[j] = &boolRelOpts[i].gen;
00298 relOpts[j]->type = RELOPT_TYPE_BOOL;
00299 relOpts[j]->namelen = strlen(relOpts[j]->name);
00300 j++;
00301 }
00302
00303 for (i = 0; intRelOpts[i].gen.name; i++)
00304 {
00305 relOpts[j] = &intRelOpts[i].gen;
00306 relOpts[j]->type = RELOPT_TYPE_INT;
00307 relOpts[j]->namelen = strlen(relOpts[j]->name);
00308 j++;
00309 }
00310
00311 for (i = 0; realRelOpts[i].gen.name; i++)
00312 {
00313 relOpts[j] = &realRelOpts[i].gen;
00314 relOpts[j]->type = RELOPT_TYPE_REAL;
00315 relOpts[j]->namelen = strlen(relOpts[j]->name);
00316 j++;
00317 }
00318
00319 for (i = 0; stringRelOpts[i].gen.name; i++)
00320 {
00321 relOpts[j] = &stringRelOpts[i].gen;
00322 relOpts[j]->type = RELOPT_TYPE_STRING;
00323 relOpts[j]->namelen = strlen(relOpts[j]->name);
00324 j++;
00325 }
00326
00327 for (i = 0; i < num_custom_options; i++)
00328 {
00329 relOpts[j] = custom_options[i];
00330 j++;
00331 }
00332
00333
00334 relOpts[j] = NULL;
00335
00336
00337 need_initialization = false;
00338 }
00339
00340
00341
00342
00343
00344
00345 relopt_kind
00346 add_reloption_kind(void)
00347 {
00348
00349 if (last_assigned_kind >= RELOPT_KIND_MAX)
00350 ereport(ERROR,
00351 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00352 errmsg("user-defined relation parameter types limit exceeded")));
00353 last_assigned_kind <<= 1;
00354 return (relopt_kind) last_assigned_kind;
00355 }
00356
00357
00358
00359
00360
00361
00362 static void
00363 add_reloption(relopt_gen *newoption)
00364 {
00365 static int max_custom_options = 0;
00366
00367 if (num_custom_options >= max_custom_options)
00368 {
00369 MemoryContext oldcxt;
00370
00371 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
00372
00373 if (max_custom_options == 0)
00374 {
00375 max_custom_options = 8;
00376 custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
00377 }
00378 else
00379 {
00380 max_custom_options *= 2;
00381 custom_options = repalloc(custom_options,
00382 max_custom_options * sizeof(relopt_gen *));
00383 }
00384 MemoryContextSwitchTo(oldcxt);
00385 }
00386 custom_options[num_custom_options++] = newoption;
00387
00388 need_initialization = true;
00389 }
00390
00391
00392
00393
00394
00395
00396 static relopt_gen *
00397 allocate_reloption(bits32 kinds, int type, char *name, char *desc)
00398 {
00399 MemoryContext oldcxt;
00400 size_t size;
00401 relopt_gen *newoption;
00402
00403 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
00404
00405 switch (type)
00406 {
00407 case RELOPT_TYPE_BOOL:
00408 size = sizeof(relopt_bool);
00409 break;
00410 case RELOPT_TYPE_INT:
00411 size = sizeof(relopt_int);
00412 break;
00413 case RELOPT_TYPE_REAL:
00414 size = sizeof(relopt_real);
00415 break;
00416 case RELOPT_TYPE_STRING:
00417 size = sizeof(relopt_string);
00418 break;
00419 default:
00420 elog(ERROR, "unsupported option type");
00421 return NULL;
00422 }
00423
00424 newoption = palloc(size);
00425
00426 newoption->name = pstrdup(name);
00427 if (desc)
00428 newoption->desc = pstrdup(desc);
00429 else
00430 newoption->desc = NULL;
00431 newoption->kinds = kinds;
00432 newoption->namelen = strlen(name);
00433 newoption->type = type;
00434
00435 MemoryContextSwitchTo(oldcxt);
00436
00437 return newoption;
00438 }
00439
00440
00441
00442
00443
00444 void
00445 add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
00446 {
00447 relopt_bool *newoption;
00448
00449 newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
00450 name, desc);
00451 newoption->default_val = default_val;
00452
00453 add_reloption((relopt_gen *) newoption);
00454 }
00455
00456
00457
00458
00459
00460 void
00461 add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
00462 int min_val, int max_val)
00463 {
00464 relopt_int *newoption;
00465
00466 newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
00467 name, desc);
00468 newoption->default_val = default_val;
00469 newoption->min = min_val;
00470 newoption->max = max_val;
00471
00472 add_reloption((relopt_gen *) newoption);
00473 }
00474
00475
00476
00477
00478
00479 void
00480 add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
00481 double min_val, double max_val)
00482 {
00483 relopt_real *newoption;
00484
00485 newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
00486 name, desc);
00487 newoption->default_val = default_val;
00488 newoption->min = min_val;
00489 newoption->max = max_val;
00490
00491 add_reloption((relopt_gen *) newoption);
00492 }
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503 void
00504 add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
00505 validate_string_relopt validator)
00506 {
00507 relopt_string *newoption;
00508
00509
00510 if (validator)
00511 (validator) (default_val);
00512
00513 newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
00514 name, desc);
00515 newoption->validate_cb = validator;
00516 if (default_val)
00517 {
00518 newoption->default_val = MemoryContextStrdup(TopMemoryContext,
00519 default_val);
00520 newoption->default_len = strlen(default_val);
00521 newoption->default_isnull = false;
00522 }
00523 else
00524 {
00525 newoption->default_val = "";
00526 newoption->default_len = 0;
00527 newoption->default_isnull = true;
00528 }
00529
00530 add_reloption((relopt_gen *) newoption);
00531 }
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556 Datum
00557 transformRelOptions(Datum oldOptions, List *defList, char *namspace,
00558 char *validnsps[], bool ignoreOids, bool isReset)
00559 {
00560 Datum result;
00561 ArrayBuildState *astate;
00562 ListCell *cell;
00563
00564
00565 if (defList == NIL)
00566 return oldOptions;
00567
00568
00569 astate = NULL;
00570
00571
00572 if (PointerIsValid(DatumGetPointer(oldOptions)))
00573 {
00574 ArrayType *array = DatumGetArrayTypeP(oldOptions);
00575 Datum *oldoptions;
00576 int noldoptions;
00577 int i;
00578
00579 Assert(ARR_ELEMTYPE(array) == TEXTOID);
00580
00581 deconstruct_array(array, TEXTOID, -1, false, 'i',
00582 &oldoptions, NULL, &noldoptions);
00583
00584 for (i = 0; i < noldoptions; i++)
00585 {
00586 text *oldoption = DatumGetTextP(oldoptions[i]);
00587 char *text_str = VARDATA(oldoption);
00588 int text_len = VARSIZE(oldoption) - VARHDRSZ;
00589
00590
00591 foreach(cell, defList)
00592 {
00593 DefElem *def = (DefElem *) lfirst(cell);
00594 int kw_len;
00595
00596
00597 if (namspace == NULL)
00598 {
00599 if (def->defnamespace != NULL)
00600 continue;
00601 }
00602 else if (def->defnamespace == NULL)
00603 continue;
00604 else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
00605 continue;
00606
00607 kw_len = strlen(def->defname);
00608 if (text_len > kw_len && text_str[kw_len] == '=' &&
00609 pg_strncasecmp(text_str, def->defname, kw_len) == 0)
00610 break;
00611 }
00612 if (!cell)
00613 {
00614
00615 astate = accumArrayResult(astate, oldoptions[i],
00616 false, TEXTOID,
00617 CurrentMemoryContext);
00618 }
00619 }
00620 }
00621
00622
00623
00624
00625
00626
00627 foreach(cell, defList)
00628 {
00629 DefElem *def = (DefElem *) lfirst(cell);
00630
00631 if (isReset)
00632 {
00633 if (def->arg != NULL)
00634 ereport(ERROR,
00635 (errcode(ERRCODE_SYNTAX_ERROR),
00636 errmsg("RESET must not include values for parameters")));
00637 }
00638 else
00639 {
00640 text *t;
00641 const char *value;
00642 Size len;
00643
00644
00645
00646
00647
00648 if (def->defnamespace != NULL)
00649 {
00650 bool valid = false;
00651 int i;
00652
00653 if (validnsps)
00654 {
00655 for (i = 0; validnsps[i]; i++)
00656 {
00657 if (pg_strcasecmp(def->defnamespace,
00658 validnsps[i]) == 0)
00659 {
00660 valid = true;
00661 break;
00662 }
00663 }
00664 }
00665
00666 if (!valid)
00667 ereport(ERROR,
00668 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00669 errmsg("unrecognized parameter namespace \"%s\"",
00670 def->defnamespace)));
00671 }
00672
00673 if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
00674 continue;
00675
00676
00677 if (namspace == NULL)
00678 {
00679 if (def->defnamespace != NULL)
00680 continue;
00681 }
00682 else if (def->defnamespace == NULL)
00683 continue;
00684 else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
00685 continue;
00686
00687
00688
00689
00690
00691
00692 if (def->arg != NULL)
00693 value = defGetString(def);
00694 else
00695 value = "true";
00696 len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
00697
00698 t = (text *) palloc(len + 1);
00699 SET_VARSIZE(t, len);
00700 sprintf(VARDATA(t), "%s=%s", def->defname, value);
00701
00702 astate = accumArrayResult(astate, PointerGetDatum(t),
00703 false, TEXTOID,
00704 CurrentMemoryContext);
00705 }
00706 }
00707
00708 if (astate)
00709 result = makeArrayResult(astate, CurrentMemoryContext);
00710 else
00711 result = (Datum) 0;
00712
00713 return result;
00714 }
00715
00716
00717
00718
00719
00720
00721 List *
00722 untransformRelOptions(Datum options)
00723 {
00724 List *result = NIL;
00725 ArrayType *array;
00726 Datum *optiondatums;
00727 int noptions;
00728 int i;
00729
00730
00731 if (!PointerIsValid(DatumGetPointer(options)))
00732 return result;
00733
00734 array = DatumGetArrayTypeP(options);
00735
00736 Assert(ARR_ELEMTYPE(array) == TEXTOID);
00737
00738 deconstruct_array(array, TEXTOID, -1, false, 'i',
00739 &optiondatums, NULL, &noptions);
00740
00741 for (i = 0; i < noptions; i++)
00742 {
00743 char *s;
00744 char *p;
00745 Node *val = NULL;
00746
00747 s = TextDatumGetCString(optiondatums[i]);
00748 p = strchr(s, '=');
00749 if (p)
00750 {
00751 *p++ = '\0';
00752 val = (Node *) makeString(pstrdup(p));
00753 }
00754 result = lappend(result, makeDefElem(pstrdup(s), val));
00755 }
00756
00757 return result;
00758 }
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771 bytea *
00772 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
00773 {
00774 bytea *options;
00775 bool isnull;
00776 Datum datum;
00777 Form_pg_class classForm;
00778
00779 datum = fastgetattr(tuple,
00780 Anum_pg_class_reloptions,
00781 tupdesc,
00782 &isnull);
00783 if (isnull)
00784 return NULL;
00785
00786 classForm = (Form_pg_class) GETSTRUCT(tuple);
00787
00788
00789 switch (classForm->relkind)
00790 {
00791 case RELKIND_RELATION:
00792 case RELKIND_TOASTVALUE:
00793 case RELKIND_VIEW:
00794 case RELKIND_MATVIEW:
00795 options = heap_reloptions(classForm->relkind, datum, false);
00796 break;
00797 case RELKIND_INDEX:
00798 options = index_reloptions(amoptions, datum, false);
00799 break;
00800 case RELKIND_FOREIGN_TABLE:
00801 options = NULL;
00802 break;
00803 default:
00804 Assert(false);
00805 options = NULL;
00806 break;
00807 }
00808
00809 return options;
00810 }
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830 relopt_value *
00831 parseRelOptions(Datum options, bool validate, relopt_kind kind,
00832 int *numrelopts)
00833 {
00834 relopt_value *reloptions;
00835 int numoptions = 0;
00836 int i;
00837 int j;
00838
00839 if (need_initialization)
00840 initialize_reloptions();
00841
00842
00843
00844 for (i = 0; relOpts[i]; i++)
00845 if (relOpts[i]->kinds & kind)
00846 numoptions++;
00847
00848 if (numoptions == 0)
00849 {
00850 *numrelopts = 0;
00851 return NULL;
00852 }
00853
00854 reloptions = palloc(numoptions * sizeof(relopt_value));
00855
00856 for (i = 0, j = 0; relOpts[i]; i++)
00857 {
00858 if (relOpts[i]->kinds & kind)
00859 {
00860 reloptions[j].gen = relOpts[i];
00861 reloptions[j].isset = false;
00862 j++;
00863 }
00864 }
00865
00866
00867 if (PointerIsValid(DatumGetPointer(options)))
00868 {
00869 ArrayType *array;
00870 Datum *optiondatums;
00871 int noptions;
00872
00873 array = DatumGetArrayTypeP(options);
00874
00875 Assert(ARR_ELEMTYPE(array) == TEXTOID);
00876
00877 deconstruct_array(array, TEXTOID, -1, false, 'i',
00878 &optiondatums, NULL, &noptions);
00879
00880 for (i = 0; i < noptions; i++)
00881 {
00882 text *optiontext = DatumGetTextP(optiondatums[i]);
00883 char *text_str = VARDATA(optiontext);
00884 int text_len = VARSIZE(optiontext) - VARHDRSZ;
00885 int j;
00886
00887
00888 for (j = 0; j < numoptions; j++)
00889 {
00890 int kw_len = reloptions[j].gen->namelen;
00891
00892 if (text_len > kw_len && text_str[kw_len] == '=' &&
00893 pg_strncasecmp(text_str, reloptions[j].gen->name,
00894 kw_len) == 0)
00895 {
00896 parse_one_reloption(&reloptions[j], text_str, text_len,
00897 validate);
00898 break;
00899 }
00900 }
00901
00902 if (j >= numoptions && validate)
00903 {
00904 char *s;
00905 char *p;
00906
00907 s = TextDatumGetCString(optiondatums[i]);
00908 p = strchr(s, '=');
00909 if (p)
00910 *p = '\0';
00911 ereport(ERROR,
00912 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00913 errmsg("unrecognized parameter \"%s\"", s)));
00914 }
00915 }
00916 }
00917
00918 *numrelopts = numoptions;
00919 return reloptions;
00920 }
00921
00922
00923
00924
00925
00926 static void
00927 parse_one_reloption(relopt_value *option, char *text_str, int text_len,
00928 bool validate)
00929 {
00930 char *value;
00931 int value_len;
00932 bool parsed;
00933 bool nofree = false;
00934
00935 if (option->isset && validate)
00936 ereport(ERROR,
00937 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00938 errmsg("parameter \"%s\" specified more than once",
00939 option->gen->name)));
00940
00941 value_len = text_len - option->gen->namelen - 1;
00942 value = (char *) palloc(value_len + 1);
00943 memcpy(value, text_str + option->gen->namelen + 1, value_len);
00944 value[value_len] = '\0';
00945
00946 switch (option->gen->type)
00947 {
00948 case RELOPT_TYPE_BOOL:
00949 {
00950 parsed = parse_bool(value, &option->values.bool_val);
00951 if (validate && !parsed)
00952 ereport(ERROR,
00953 (errmsg("invalid value for boolean option \"%s\": %s",
00954 option->gen->name, value)));
00955 }
00956 break;
00957 case RELOPT_TYPE_INT:
00958 {
00959 relopt_int *optint = (relopt_int *) option->gen;
00960
00961 parsed = parse_int(value, &option->values.int_val, 0, NULL);
00962 if (validate && !parsed)
00963 ereport(ERROR,
00964 (errmsg("invalid value for integer option \"%s\": %s",
00965 option->gen->name, value)));
00966 if (validate && (option->values.int_val < optint->min ||
00967 option->values.int_val > optint->max))
00968 ereport(ERROR,
00969 (errmsg("value %s out of bounds for option \"%s\"",
00970 value, option->gen->name),
00971 errdetail("Valid values are between \"%d\" and \"%d\".",
00972 optint->min, optint->max)));
00973 }
00974 break;
00975 case RELOPT_TYPE_REAL:
00976 {
00977 relopt_real *optreal = (relopt_real *) option->gen;
00978
00979 parsed = parse_real(value, &option->values.real_val);
00980 if (validate && !parsed)
00981 ereport(ERROR,
00982 (errmsg("invalid value for floating point option \"%s\": %s",
00983 option->gen->name, value)));
00984 if (validate && (option->values.real_val < optreal->min ||
00985 option->values.real_val > optreal->max))
00986 ereport(ERROR,
00987 (errmsg("value %s out of bounds for option \"%s\"",
00988 value, option->gen->name),
00989 errdetail("Valid values are between \"%f\" and \"%f\".",
00990 optreal->min, optreal->max)));
00991 }
00992 break;
00993 case RELOPT_TYPE_STRING:
00994 {
00995 relopt_string *optstring = (relopt_string *) option->gen;
00996
00997 option->values.string_val = value;
00998 nofree = true;
00999 if (validate && optstring->validate_cb)
01000 (optstring->validate_cb) (value);
01001 parsed = true;
01002 }
01003 break;
01004 default:
01005 elog(ERROR, "unsupported reloption type %d", option->gen->type);
01006 parsed = true;
01007 break;
01008 }
01009
01010 if (parsed)
01011 option->isset = true;
01012 if (!nofree)
01013 pfree(value);
01014 }
01015
01016
01017
01018
01019
01020
01021
01022
01023 void *
01024 allocateReloptStruct(Size base, relopt_value *options, int numoptions)
01025 {
01026 Size size = base;
01027 int i;
01028
01029 for (i = 0; i < numoptions; i++)
01030 if (options[i].gen->type == RELOPT_TYPE_STRING)
01031 size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
01032
01033 return palloc0(size);
01034 }
01035
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047 void
01048 fillRelOptions(void *rdopts, Size basesize,
01049 relopt_value *options, int numoptions,
01050 bool validate,
01051 const relopt_parse_elt *elems, int numelems)
01052 {
01053 int i;
01054 int offset = basesize;
01055
01056 for (i = 0; i < numoptions; i++)
01057 {
01058 int j;
01059 bool found = false;
01060
01061 for (j = 0; j < numelems; j++)
01062 {
01063 if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
01064 {
01065 relopt_string *optstring;
01066 char *itempos = ((char *) rdopts) + elems[j].offset;
01067 char *string_val;
01068
01069 switch (options[i].gen->type)
01070 {
01071 case RELOPT_TYPE_BOOL:
01072 *(bool *) itempos = options[i].isset ?
01073 options[i].values.bool_val :
01074 ((relopt_bool *) options[i].gen)->default_val;
01075 break;
01076 case RELOPT_TYPE_INT:
01077 *(int *) itempos = options[i].isset ?
01078 options[i].values.int_val :
01079 ((relopt_int *) options[i].gen)->default_val;
01080 break;
01081 case RELOPT_TYPE_REAL:
01082 *(double *) itempos = options[i].isset ?
01083 options[i].values.real_val :
01084 ((relopt_real *) options[i].gen)->default_val;
01085 break;
01086 case RELOPT_TYPE_STRING:
01087 optstring = (relopt_string *) options[i].gen;
01088 if (options[i].isset)
01089 string_val = options[i].values.string_val;
01090 else if (!optstring->default_isnull)
01091 string_val = optstring->default_val;
01092 else
01093 string_val = NULL;
01094
01095 if (string_val == NULL)
01096 *(int *) itempos = 0;
01097 else
01098 {
01099 strcpy((char *) rdopts + offset, string_val);
01100 *(int *) itempos = offset;
01101 offset += strlen(string_val) + 1;
01102 }
01103 break;
01104 default:
01105 elog(ERROR, "unrecognized reloption type %c",
01106 options[i].gen->type);
01107 break;
01108 }
01109 found = true;
01110 break;
01111 }
01112 }
01113 if (validate && !found)
01114 elog(ERROR, "reloption \"%s\" not found in parse table",
01115 options[i].gen->name);
01116 }
01117 SET_VARSIZE(rdopts, offset);
01118 }
01119
01120
01121
01122
01123
01124
01125 bytea *
01126 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
01127 {
01128 relopt_value *options;
01129 StdRdOptions *rdopts;
01130 int numoptions;
01131 static const relopt_parse_elt tab[] = {
01132 {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
01133 {"autovacuum_enabled", RELOPT_TYPE_BOOL,
01134 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, enabled)},
01135 {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
01136 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_threshold)},
01137 {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
01138 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_threshold)},
01139 {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
01140 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_delay)},
01141 {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
01142 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_limit)},
01143 {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
01144 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_min_age)},
01145 {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
01146 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
01147 {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
01148 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)},
01149 {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
01150 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
01151 {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
01152 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
01153 {"security_barrier", RELOPT_TYPE_BOOL,
01154 offsetof(StdRdOptions, security_barrier)},
01155 };
01156
01157 options = parseRelOptions(reloptions, validate, kind, &numoptions);
01158
01159
01160 if (numoptions == 0)
01161 return NULL;
01162
01163 rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
01164
01165 fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
01166 validate, tab, lengthof(tab));
01167
01168 pfree(options);
01169
01170 return (bytea *) rdopts;
01171 }
01172
01173
01174
01175
01176 bytea *
01177 heap_reloptions(char relkind, Datum reloptions, bool validate)
01178 {
01179 StdRdOptions *rdopts;
01180
01181 switch (relkind)
01182 {
01183 case RELKIND_TOASTVALUE:
01184 rdopts = (StdRdOptions *)
01185 default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
01186 if (rdopts != NULL)
01187 {
01188
01189 rdopts->fillfactor = 100;
01190 rdopts->autovacuum.analyze_threshold = -1;
01191 rdopts->autovacuum.analyze_scale_factor = -1;
01192 }
01193 return (bytea *) rdopts;
01194 case RELKIND_RELATION:
01195 case RELKIND_MATVIEW:
01196 return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
01197 case RELKIND_VIEW:
01198 return default_reloptions(reloptions, validate, RELOPT_KIND_VIEW);
01199 default:
01200
01201 return NULL;
01202 }
01203 }
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213 bytea *
01214 index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
01215 {
01216 FmgrInfo flinfo;
01217 FunctionCallInfoData fcinfo;
01218 Datum result;
01219
01220 Assert(RegProcedureIsValid(amoptions));
01221
01222
01223 if (!PointerIsValid(DatumGetPointer(reloptions)))
01224 return NULL;
01225
01226
01227 fmgr_info(amoptions, &flinfo);
01228
01229 InitFunctionCallInfoData(fcinfo, &flinfo, 2, InvalidOid, NULL, NULL);
01230
01231 fcinfo.arg[0] = reloptions;
01232 fcinfo.arg[1] = BoolGetDatum(validate);
01233 fcinfo.argnull[0] = false;
01234 fcinfo.argnull[1] = false;
01235
01236 result = FunctionCallInvoke(&fcinfo);
01237
01238 if (fcinfo.isnull || DatumGetPointer(result) == NULL)
01239 return NULL;
01240
01241 return DatumGetByteaP(result);
01242 }
01243
01244
01245
01246
01247 bytea *
01248 attribute_reloptions(Datum reloptions, bool validate)
01249 {
01250 relopt_value *options;
01251 AttributeOpts *aopts;
01252 int numoptions;
01253 static const relopt_parse_elt tab[] = {
01254 {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
01255 {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
01256 };
01257
01258 options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
01259 &numoptions);
01260
01261
01262 if (numoptions == 0)
01263 return NULL;
01264
01265 aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
01266
01267 fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
01268 validate, tab, lengthof(tab));
01269
01270 pfree(options);
01271
01272 return (bytea *) aopts;
01273 }
01274
01275
01276
01277
01278 bytea *
01279 tablespace_reloptions(Datum reloptions, bool validate)
01280 {
01281 relopt_value *options;
01282 TableSpaceOpts *tsopts;
01283 int numoptions;
01284 static const relopt_parse_elt tab[] = {
01285 {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
01286 {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)}
01287 };
01288
01289 options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
01290 &numoptions);
01291
01292
01293 if (numoptions == 0)
01294 return NULL;
01295
01296 tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
01297
01298 fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
01299 validate, tab, lengthof(tab));
01300
01301 pfree(options);
01302
01303 return (bytea *) tsopts;
01304 }