Coverage Report

Created: 2025-10-09 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/common/reloptions.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * reloptions.c
4
 *    Core support for relation options (pg_class.reloptions)
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/access/common/reloptions.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
16
#include "postgres.h"
17
18
#include <float.h>
19
20
#include "access/gist_private.h"
21
#include "access/hash.h"
22
#include "access/heaptoast.h"
23
#include "access/htup_details.h"
24
#include "access/nbtree.h"
25
#include "access/reloptions.h"
26
#include "access/spgist_private.h"
27
#include "catalog/pg_type.h"
28
#include "commands/defrem.h"
29
#include "commands/tablespace.h"
30
#include "nodes/makefuncs.h"
31
#include "utils/array.h"
32
#include "utils/attoptcache.h"
33
#include "utils/builtins.h"
34
#include "utils/guc.h"
35
#include "utils/memutils.h"
36
#include "utils/rel.h"
37
38
/*
39
 * Contents of pg_class.reloptions
40
 *
41
 * To add an option:
42
 *
43
 * (i) decide on a type (bool, integer, real, enum, string), name, default
44
 * value, upper and lower bounds (if applicable); for strings, consider a
45
 * validation routine.
46
 * (ii) add a record below (or use add_<type>_reloption).
47
 * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
48
 * (iv) add it to the appropriate handling routine (perhaps
49
 * default_reloptions)
50
 * (v) make sure the lock level is set correctly for that operation
51
 * (vi) don't forget to document the option
52
 *
53
 * The default choice for any new option should be AccessExclusiveLock.
54
 * In some cases the lock level can be reduced from there, but the lock
55
 * level chosen should always conflict with itself to ensure that multiple
56
 * changes aren't lost when we attempt concurrent changes.
57
 * The choice of lock level depends completely upon how that parameter
58
 * is used within the server, not upon how and when you'd like to change it.
59
 * Safety first. Existing choices are documented here, and elsewhere in
60
 * backend code where the parameters are used.
61
 *
62
 * In general, anything that affects the results obtained from a SELECT must be
63
 * protected by AccessExclusiveLock.
64
 *
65
 * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
66
 * since they are only used by the AV procs and don't change anything
67
 * currently executing.
68
 *
69
 * Fillfactor can be set at ShareUpdateExclusiveLock because it applies only to
70
 * subsequent changes made to data blocks, as documented in hio.c
71
 *
72
 * n_distinct options can be set at ShareUpdateExclusiveLock because they
73
 * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
74
 * so the ANALYZE will not be affected by in-flight changes. Changing those
75
 * values has no effect until the next ANALYZE, so no need for stronger lock.
76
 *
77
 * Planner-related parameters can be set at ShareUpdateExclusiveLock because
78
 * they only affect planning and not the correctness of the execution. Plans
79
 * cannot be changed in mid-flight, so changes here could not easily result in
80
 * new improved plans in any case. So we allow existing queries to continue
81
 * and existing plans to survive, a small price to pay for allowing better
82
 * plans to be introduced concurrently without interfering with users.
83
 *
84
 * Setting parallel_workers at ShareUpdateExclusiveLock is safe, since it acts
85
 * the same as max_parallel_workers_per_gather which is a USERSET parameter
86
 * that doesn't affect existing plans or queries.
87
 *
88
 * vacuum_truncate can be set at ShareUpdateExclusiveLock because it
89
 * is only used during VACUUM, which uses a ShareUpdateExclusiveLock,
90
 * so the VACUUM will not be affected by in-flight changes. Changing its
91
 * value has no effect until the next VACUUM, so no need for stronger lock.
92
 */
93
94
static relopt_bool boolRelOpts[] =
95
{
96
  {
97
    {
98
      "autosummarize",
99
      "Enables automatic summarization on this BRIN index",
100
      RELOPT_KIND_BRIN,
101
      AccessExclusiveLock
102
    },
103
    false
104
  },
105
  {
106
    {
107
      "autovacuum_enabled",
108
      "Enables autovacuum in this relation",
109
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
110
      ShareUpdateExclusiveLock
111
    },
112
    true
113
  },
114
  {
115
    {
116
      "user_catalog_table",
117
      "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
118
      RELOPT_KIND_HEAP,
119
      AccessExclusiveLock
120
    },
121
    false
122
  },
123
  {
124
    {
125
      "fastupdate",
126
      "Enables \"fast update\" feature for this GIN index",
127
      RELOPT_KIND_GIN,
128
      AccessExclusiveLock
129
    },
130
    true
131
  },
132
  {
133
    {
134
      "security_barrier",
135
      "View acts as a row security barrier",
136
      RELOPT_KIND_VIEW,
137
      AccessExclusiveLock
138
    },
139
    false
140
  },
141
  {
142
    {
143
      "security_invoker",
144
      "Privileges on underlying relations are checked as the invoking user, not the view owner",
145
      RELOPT_KIND_VIEW,
146
      AccessExclusiveLock
147
    },
148
    false
149
  },
150
  {
151
    {
152
      "vacuum_truncate",
153
      "Enables vacuum to truncate empty pages at the end of this table",
154
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
155
      ShareUpdateExclusiveLock
156
    },
157
    true
158
  },
159
  {
160
    {
161
      "deduplicate_items",
162
      "Enables \"deduplicate items\" feature for this btree index",
163
      RELOPT_KIND_BTREE,
164
      ShareUpdateExclusiveLock  /* since it applies only to later
165
                     * inserts */
166
    },
167
    true
168
  },
169
  /* list terminator */
170
  {{NULL}}
171
};
172
173
static relopt_int intRelOpts[] =
174
{
175
  {
176
    {
177
      "fillfactor",
178
      "Packs table pages only to this percentage",
179
      RELOPT_KIND_HEAP,
180
      ShareUpdateExclusiveLock  /* since it applies only to later
181
                     * inserts */
182
    },
183
    HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
184
  },
185
  {
186
    {
187
      "fillfactor",
188
      "Packs btree index pages only to this percentage",
189
      RELOPT_KIND_BTREE,
190
      ShareUpdateExclusiveLock  /* since it applies only to later
191
                     * inserts */
192
    },
193
    BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
194
  },
195
  {
196
    {
197
      "fillfactor",
198
      "Packs hash index pages only to this percentage",
199
      RELOPT_KIND_HASH,
200
      ShareUpdateExclusiveLock  /* since it applies only to later
201
                     * inserts */
202
    },
203
    HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
204
  },
205
  {
206
    {
207
      "fillfactor",
208
      "Packs gist index pages only to this percentage",
209
      RELOPT_KIND_GIST,
210
      ShareUpdateExclusiveLock  /* since it applies only to later
211
                     * inserts */
212
    },
213
    GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
214
  },
215
  {
216
    {
217
      "fillfactor",
218
      "Packs spgist index pages only to this percentage",
219
      RELOPT_KIND_SPGIST,
220
      ShareUpdateExclusiveLock  /* since it applies only to later
221
                     * inserts */
222
    },
223
    SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
224
  },
225
  {
226
    {
227
      "autovacuum_vacuum_threshold",
228
      "Minimum number of tuple updates or deletes prior to vacuum",
229
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
230
      ShareUpdateExclusiveLock
231
    },
232
    -1, 0, INT_MAX
233
  },
234
  {
235
    {
236
      "autovacuum_vacuum_max_threshold",
237
      "Maximum number of tuple updates or deletes prior to vacuum",
238
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
239
      ShareUpdateExclusiveLock
240
    },
241
    -2, -1, INT_MAX
242
  },
243
  {
244
    {
245
      "autovacuum_vacuum_insert_threshold",
246
      "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
247
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
248
      ShareUpdateExclusiveLock
249
    },
250
    -2, -1, INT_MAX
251
  },
252
  {
253
    {
254
      "autovacuum_analyze_threshold",
255
      "Minimum number of tuple inserts, updates or deletes prior to analyze",
256
      RELOPT_KIND_HEAP,
257
      ShareUpdateExclusiveLock
258
    },
259
    -1, 0, INT_MAX
260
  },
261
  {
262
    {
263
      "autovacuum_vacuum_cost_limit",
264
      "Vacuum cost amount available before napping, for autovacuum",
265
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
266
      ShareUpdateExclusiveLock
267
    },
268
    -1, 1, 10000
269
  },
270
  {
271
    {
272
      "autovacuum_freeze_min_age",
273
      "Minimum age at which VACUUM should freeze a table row, for autovacuum",
274
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
275
      ShareUpdateExclusiveLock
276
    },
277
    -1, 0, 1000000000
278
  },
279
  {
280
    {
281
      "autovacuum_multixact_freeze_min_age",
282
      "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
283
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
284
      ShareUpdateExclusiveLock
285
    },
286
    -1, 0, 1000000000
287
  },
288
  {
289
    {
290
      "autovacuum_freeze_max_age",
291
      "Age at which to autovacuum a table to prevent transaction ID wraparound",
292
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
293
      ShareUpdateExclusiveLock
294
    },
295
    -1, 100000, 2000000000
296
  },
297
  {
298
    {
299
      "autovacuum_multixact_freeze_max_age",
300
      "Multixact age at which to autovacuum a table to prevent multixact wraparound",
301
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
302
      ShareUpdateExclusiveLock
303
    },
304
    -1, 10000, 2000000000
305
  },
306
  {
307
    {
308
      "autovacuum_freeze_table_age",
309
      "Age at which VACUUM should perform a full table sweep to freeze row versions",
310
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
311
      ShareUpdateExclusiveLock
312
    }, -1, 0, 2000000000
313
  },
314
  {
315
    {
316
      "autovacuum_multixact_freeze_table_age",
317
      "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
318
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
319
      ShareUpdateExclusiveLock
320
    }, -1, 0, 2000000000
321
  },
322
  {
323
    {
324
      "log_autovacuum_min_duration",
325
      "Sets the minimum execution time above which autovacuum actions will be logged",
326
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
327
      ShareUpdateExclusiveLock
328
    },
329
    -1, -1, INT_MAX
330
  },
331
  {
332
    {
333
      "toast_tuple_target",
334
      "Sets the target tuple length at which external columns will be toasted",
335
      RELOPT_KIND_HEAP,
336
      ShareUpdateExclusiveLock
337
    },
338
    TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
339
  },
340
  {
341
    {
342
      "pages_per_range",
343
      "Number of pages that each page range covers in a BRIN index",
344
      RELOPT_KIND_BRIN,
345
      AccessExclusiveLock
346
    }, 128, 1, 131072
347
  },
348
  {
349
    {
350
      "gin_pending_list_limit",
351
      "Maximum size of the pending list for this GIN index, in kilobytes.",
352
      RELOPT_KIND_GIN,
353
      AccessExclusiveLock
354
    },
355
    -1, 64, MAX_KILOBYTES
356
  },
357
  {
358
    {
359
      "effective_io_concurrency",
360
      "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
361
      RELOPT_KIND_TABLESPACE,
362
      ShareUpdateExclusiveLock
363
    },
364
    -1, 0, MAX_IO_CONCURRENCY
365
  },
366
  {
367
    {
368
      "maintenance_io_concurrency",
369
      "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",
370
      RELOPT_KIND_TABLESPACE,
371
      ShareUpdateExclusiveLock
372
    },
373
    -1, 0, MAX_IO_CONCURRENCY
374
  },
375
  {
376
    {
377
      "parallel_workers",
378
      "Number of parallel processes that can be used per executor node for this relation.",
379
      RELOPT_KIND_HEAP,
380
      ShareUpdateExclusiveLock
381
    },
382
    -1, 0, 1024
383
  },
384
385
  /* list terminator */
386
  {{NULL}}
387
};
388
389
static relopt_real realRelOpts[] =
390
{
391
  {
392
    {
393
      "autovacuum_vacuum_cost_delay",
394
      "Vacuum cost delay in milliseconds, for autovacuum",
395
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
396
      ShareUpdateExclusiveLock
397
    },
398
    -1, 0.0, 100.0
399
  },
400
  {
401
    {
402
      "autovacuum_vacuum_scale_factor",
403
      "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
404
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
405
      ShareUpdateExclusiveLock
406
    },
407
    -1, 0.0, 100.0
408
  },
409
  {
410
    {
411
      "autovacuum_vacuum_insert_scale_factor",
412
      "Number of tuple inserts prior to vacuum as a fraction of reltuples",
413
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
414
      ShareUpdateExclusiveLock
415
    },
416
    -1, 0.0, 100.0
417
  },
418
  {
419
    {
420
      "autovacuum_analyze_scale_factor",
421
      "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
422
      RELOPT_KIND_HEAP,
423
      ShareUpdateExclusiveLock
424
    },
425
    -1, 0.0, 100.0
426
  },
427
  {
428
    {
429
      "vacuum_max_eager_freeze_failure_rate",
430
      "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
431
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
432
      ShareUpdateExclusiveLock
433
    },
434
    -1, 0.0, 1.0
435
  },
436
437
  {
438
    {
439
      "seq_page_cost",
440
      "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
441
      RELOPT_KIND_TABLESPACE,
442
      ShareUpdateExclusiveLock
443
    },
444
    -1, 0.0, DBL_MAX
445
  },
446
  {
447
    {
448
      "random_page_cost",
449
      "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
450
      RELOPT_KIND_TABLESPACE,
451
      ShareUpdateExclusiveLock
452
    },
453
    -1, 0.0, DBL_MAX
454
  },
455
  {
456
    {
457
      "n_distinct",
458
      "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
459
      RELOPT_KIND_ATTRIBUTE,
460
      ShareUpdateExclusiveLock
461
    },
462
    0, -1.0, DBL_MAX
463
  },
464
  {
465
    {
466
      "n_distinct_inherited",
467
      "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
468
      RELOPT_KIND_ATTRIBUTE,
469
      ShareUpdateExclusiveLock
470
    },
471
    0, -1.0, DBL_MAX
472
  },
473
  {
474
    {
475
      "vacuum_cleanup_index_scale_factor",
476
      "Deprecated B-Tree parameter.",
477
      RELOPT_KIND_BTREE,
478
      ShareUpdateExclusiveLock
479
    },
480
    -1, 0.0, 1e10
481
  },
482
  /* list terminator */
483
  {{NULL}}
484
};
485
486
/* values from StdRdOptIndexCleanup */
487
static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
488
{
489
  {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
490
  {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
491
  {"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
492
  {"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
493
  {"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
494
  {"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
495
  {"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
496
  {"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
497
  {"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
498
  {(const char *) NULL}   /* list terminator */
499
};
500
501
/* values from GistOptBufferingMode */
502
static relopt_enum_elt_def gistBufferingOptValues[] =
503
{
504
  {"auto", GIST_OPTION_BUFFERING_AUTO},
505
  {"on", GIST_OPTION_BUFFERING_ON},
506
  {"off", GIST_OPTION_BUFFERING_OFF},
507
  {(const char *) NULL}   /* list terminator */
508
};
509
510
/* values from ViewOptCheckOption */
511
static relopt_enum_elt_def viewCheckOptValues[] =
512
{
513
  /* no value for NOT_SET */
514
  {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
515
  {"cascaded", VIEW_OPTION_CHECK_OPTION_CASCADED},
516
  {(const char *) NULL}   /* list terminator */
517
};
518
519
static relopt_enum enumRelOpts[] =
520
{
521
  {
522
    {
523
      "vacuum_index_cleanup",
524
      "Controls index vacuuming and index cleanup",
525
      RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
526
      ShareUpdateExclusiveLock
527
    },
528
    StdRdOptIndexCleanupValues,
529
    STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
530
    gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
531
  },
532
  {
533
    {
534
      "buffering",
535
      "Enables buffering build for this GiST index",
536
      RELOPT_KIND_GIST,
537
      AccessExclusiveLock
538
    },
539
    gistBufferingOptValues,
540
    GIST_OPTION_BUFFERING_AUTO,
541
    gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
542
  },
543
  {
544
    {
545
      "check_option",
546
      "View has WITH CHECK OPTION defined (local or cascaded).",
547
      RELOPT_KIND_VIEW,
548
      AccessExclusiveLock
549
    },
550
    viewCheckOptValues,
551
    VIEW_OPTION_CHECK_OPTION_NOT_SET,
552
    gettext_noop("Valid values are \"local\" and \"cascaded\".")
553
  },
554
  /* list terminator */
555
  {{NULL}}
556
};
557
558
static relopt_string stringRelOpts[] =
559
{
560
  /* list terminator */
561
  {{NULL}}
562
};
563
564
static relopt_gen **relOpts = NULL;
565
static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
566
567
static int  num_custom_options = 0;
568
static relopt_gen **custom_options = NULL;
569
static bool need_initialization = true;
570
571
static void initialize_reloptions(void);
572
static void parse_one_reloption(relopt_value *option, char *text_str,
573
                int text_len, bool validate);
574
575
/*
576
 * Get the length of a string reloption (either default or the user-defined
577
 * value).  This is used for allocation purposes when building a set of
578
 * relation options.
579
 */
580
#define GET_STRING_RELOPTION_LEN(option) \
581
0
  ((option).isset ? strlen((option).values.string_val) : \
582
0
   ((relopt_string *) (option).gen)->default_len)
583
584
/*
585
 * initialize_reloptions
586
 *    initialization routine, must be called before parsing
587
 *
588
 * Initialize the relOpts array and fill each variable's type and name length.
589
 */
590
static void
591
initialize_reloptions(void)
592
0
{
593
0
  int     i;
594
0
  int     j;
595
596
0
  j = 0;
597
0
  for (i = 0; boolRelOpts[i].gen.name; i++)
598
0
  {
599
0
    Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
600
0
                   boolRelOpts[i].gen.lockmode));
601
0
    j++;
602
0
  }
603
0
  for (i = 0; intRelOpts[i].gen.name; i++)
604
0
  {
605
0
    Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
606
0
                   intRelOpts[i].gen.lockmode));
607
0
    j++;
608
0
  }
609
0
  for (i = 0; realRelOpts[i].gen.name; i++)
610
0
  {
611
0
    Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
612
0
                   realRelOpts[i].gen.lockmode));
613
0
    j++;
614
0
  }
615
0
  for (i = 0; enumRelOpts[i].gen.name; i++)
616
0
  {
617
0
    Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
618
0
                   enumRelOpts[i].gen.lockmode));
619
0
    j++;
620
0
  }
621
0
  for (i = 0; stringRelOpts[i].gen.name; i++)
622
0
  {
623
0
    Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
624
0
                   stringRelOpts[i].gen.lockmode));
625
0
    j++;
626
0
  }
627
0
  j += num_custom_options;
628
629
0
  if (relOpts)
630
0
    pfree(relOpts);
631
0
  relOpts = MemoryContextAlloc(TopMemoryContext,
632
0
                 (j + 1) * sizeof(relopt_gen *));
633
634
0
  j = 0;
635
0
  for (i = 0; boolRelOpts[i].gen.name; i++)
636
0
  {
637
0
    relOpts[j] = &boolRelOpts[i].gen;
638
0
    relOpts[j]->type = RELOPT_TYPE_BOOL;
639
0
    relOpts[j]->namelen = strlen(relOpts[j]->name);
640
0
    j++;
641
0
  }
642
643
0
  for (i = 0; intRelOpts[i].gen.name; i++)
644
0
  {
645
0
    relOpts[j] = &intRelOpts[i].gen;
646
0
    relOpts[j]->type = RELOPT_TYPE_INT;
647
0
    relOpts[j]->namelen = strlen(relOpts[j]->name);
648
0
    j++;
649
0
  }
650
651
0
  for (i = 0; realRelOpts[i].gen.name; i++)
652
0
  {
653
0
    relOpts[j] = &realRelOpts[i].gen;
654
0
    relOpts[j]->type = RELOPT_TYPE_REAL;
655
0
    relOpts[j]->namelen = strlen(relOpts[j]->name);
656
0
    j++;
657
0
  }
658
659
0
  for (i = 0; enumRelOpts[i].gen.name; i++)
660
0
  {
661
0
    relOpts[j] = &enumRelOpts[i].gen;
662
0
    relOpts[j]->type = RELOPT_TYPE_ENUM;
663
0
    relOpts[j]->namelen = strlen(relOpts[j]->name);
664
0
    j++;
665
0
  }
666
667
0
  for (i = 0; stringRelOpts[i].gen.name; i++)
668
0
  {
669
0
    relOpts[j] = &stringRelOpts[i].gen;
670
0
    relOpts[j]->type = RELOPT_TYPE_STRING;
671
0
    relOpts[j]->namelen = strlen(relOpts[j]->name);
672
0
    j++;
673
0
  }
674
675
0
  for (i = 0; i < num_custom_options; i++)
676
0
  {
677
0
    relOpts[j] = custom_options[i];
678
0
    j++;
679
0
  }
680
681
  /* add a list terminator */
682
0
  relOpts[j] = NULL;
683
684
  /* flag the work is complete */
685
0
  need_initialization = false;
686
0
}
687
688
/*
689
 * add_reloption_kind
690
 *    Create a new relopt_kind value, to be used in custom reloptions by
691
 *    user-defined AMs.
692
 */
693
relopt_kind
694
add_reloption_kind(void)
695
0
{
696
  /* don't hand out the last bit so that the enum's behavior is portable */
697
0
  if (last_assigned_kind >= RELOPT_KIND_MAX)
698
0
    ereport(ERROR,
699
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
700
0
         errmsg("user-defined relation parameter types limit exceeded")));
701
0
  last_assigned_kind <<= 1;
702
0
  return (relopt_kind) last_assigned_kind;
703
0
}
704
705
/*
706
 * add_reloption
707
 *    Add an already-created custom reloption to the list, and recompute the
708
 *    main parser table.
709
 */
710
static void
711
add_reloption(relopt_gen *newoption)
712
0
{
713
0
  static int  max_custom_options = 0;
714
715
0
  if (num_custom_options >= max_custom_options)
716
0
  {
717
0
    MemoryContext oldcxt;
718
719
0
    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
720
721
0
    if (max_custom_options == 0)
722
0
    {
723
0
      max_custom_options = 8;
724
0
      custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
725
0
    }
726
0
    else
727
0
    {
728
0
      max_custom_options *= 2;
729
0
      custom_options = repalloc(custom_options,
730
0
                    max_custom_options * sizeof(relopt_gen *));
731
0
    }
732
0
    MemoryContextSwitchTo(oldcxt);
733
0
  }
734
0
  custom_options[num_custom_options++] = newoption;
735
736
0
  need_initialization = true;
737
0
}
738
739
/*
740
 * init_local_reloptions
741
 *    Initialize local reloptions that will parsed into bytea structure of
742
 *    'relopt_struct_size'.
743
 */
744
void
745
init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
746
0
{
747
0
  relopts->options = NIL;
748
0
  relopts->validators = NIL;
749
0
  relopts->relopt_struct_size = relopt_struct_size;
750
0
}
751
752
/*
753
 * register_reloptions_validator
754
 *    Register custom validation callback that will be called at the end of
755
 *    build_local_reloptions().
756
 */
757
void
758
register_reloptions_validator(local_relopts *relopts, relopts_validator validator)
759
0
{
760
0
  relopts->validators = lappend(relopts->validators, validator);
761
0
}
762
763
/*
764
 * add_local_reloption
765
 *    Add an already-created custom reloption to the local list.
766
 */
767
static void
768
add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
769
0
{
770
0
  local_relopt *opt = palloc(sizeof(*opt));
771
772
0
  Assert(offset < relopts->relopt_struct_size);
773
774
0
  opt->option = newoption;
775
0
  opt->offset = offset;
776
777
0
  relopts->options = lappend(relopts->options, opt);
778
0
}
779
780
/*
781
 * allocate_reloption
782
 *    Allocate a new reloption and initialize the type-agnostic fields
783
 *    (for types other than string)
784
 */
785
static relopt_gen *
786
allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
787
           LOCKMODE lockmode)
788
0
{
789
0
  MemoryContext oldcxt;
790
0
  size_t    size;
791
0
  relopt_gen *newoption;
792
793
0
  if (kinds != RELOPT_KIND_LOCAL)
794
0
    oldcxt = MemoryContextSwitchTo(TopMemoryContext);
795
0
  else
796
0
    oldcxt = NULL;
797
798
0
  switch (type)
799
0
  {
800
0
    case RELOPT_TYPE_BOOL:
801
0
      size = sizeof(relopt_bool);
802
0
      break;
803
0
    case RELOPT_TYPE_INT:
804
0
      size = sizeof(relopt_int);
805
0
      break;
806
0
    case RELOPT_TYPE_REAL:
807
0
      size = sizeof(relopt_real);
808
0
      break;
809
0
    case RELOPT_TYPE_ENUM:
810
0
      size = sizeof(relopt_enum);
811
0
      break;
812
0
    case RELOPT_TYPE_STRING:
813
0
      size = sizeof(relopt_string);
814
0
      break;
815
0
    default:
816
0
      elog(ERROR, "unsupported reloption type %d", type);
817
0
      return NULL;   /* keep compiler quiet */
818
0
  }
819
820
0
  newoption = palloc(size);
821
822
0
  newoption->name = pstrdup(name);
823
0
  if (desc)
824
0
    newoption->desc = pstrdup(desc);
825
0
  else
826
0
    newoption->desc = NULL;
827
0
  newoption->kinds = kinds;
828
0
  newoption->namelen = strlen(name);
829
0
  newoption->type = type;
830
0
  newoption->lockmode = lockmode;
831
832
0
  if (oldcxt != NULL)
833
0
    MemoryContextSwitchTo(oldcxt);
834
835
0
  return newoption;
836
0
}
837
838
/*
839
 * init_bool_reloption
840
 *    Allocate and initialize a new boolean reloption
841
 */
842
static relopt_bool *
843
init_bool_reloption(bits32 kinds, const char *name, const char *desc,
844
          bool default_val, LOCKMODE lockmode)
845
0
{
846
0
  relopt_bool *newoption;
847
848
0
  newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
849
0
                           name, desc, lockmode);
850
0
  newoption->default_val = default_val;
851
852
0
  return newoption;
853
0
}
854
855
/*
856
 * add_bool_reloption
857
 *    Add a new boolean reloption
858
 */
859
void
860
add_bool_reloption(bits32 kinds, const char *name, const char *desc,
861
           bool default_val, LOCKMODE lockmode)
862
0
{
863
0
  relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
864
0
                         default_val, lockmode);
865
866
0
  add_reloption((relopt_gen *) newoption);
867
0
}
868
869
/*
870
 * add_local_bool_reloption
871
 *    Add a new boolean local reloption
872
 *
873
 * 'offset' is offset of bool-typed field.
874
 */
875
void
876
add_local_bool_reloption(local_relopts *relopts, const char *name,
877
             const char *desc, bool default_val, int offset)
878
0
{
879
0
  relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
880
0
                         name, desc,
881
0
                         default_val, 0);
882
883
0
  add_local_reloption(relopts, (relopt_gen *) newoption, offset);
884
0
}
885
886
887
/*
888
 * init_real_reloption
889
 *    Allocate and initialize a new integer reloption
890
 */
891
static relopt_int *
892
init_int_reloption(bits32 kinds, const char *name, const char *desc,
893
           int default_val, int min_val, int max_val,
894
           LOCKMODE lockmode)
895
0
{
896
0
  relopt_int *newoption;
897
898
0
  newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
899
0
                          name, desc, lockmode);
900
0
  newoption->default_val = default_val;
901
0
  newoption->min = min_val;
902
0
  newoption->max = max_val;
903
904
0
  return newoption;
905
0
}
906
907
/*
908
 * add_int_reloption
909
 *    Add a new integer reloption
910
 */
911
void
912
add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
913
          int min_val, int max_val, LOCKMODE lockmode)
914
0
{
915
0
  relopt_int *newoption = init_int_reloption(kinds, name, desc,
916
0
                         default_val, min_val,
917
0
                         max_val, lockmode);
918
919
0
  add_reloption((relopt_gen *) newoption);
920
0
}
921
922
/*
923
 * add_local_int_reloption
924
 *    Add a new local integer reloption
925
 *
926
 * 'offset' is offset of int-typed field.
927
 */
928
void
929
add_local_int_reloption(local_relopts *relopts, const char *name,
930
            const char *desc, int default_val, int min_val,
931
            int max_val, int offset)
932
0
{
933
0
  relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
934
0
                         name, desc, default_val,
935
0
                         min_val, max_val, 0);
936
937
0
  add_local_reloption(relopts, (relopt_gen *) newoption, offset);
938
0
}
939
940
/*
941
 * init_real_reloption
942
 *    Allocate and initialize a new real reloption
943
 */
944
static relopt_real *
945
init_real_reloption(bits32 kinds, const char *name, const char *desc,
946
          double default_val, double min_val, double max_val,
947
          LOCKMODE lockmode)
948
0
{
949
0
  relopt_real *newoption;
950
951
0
  newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
952
0
                           name, desc, lockmode);
953
0
  newoption->default_val = default_val;
954
0
  newoption->min = min_val;
955
0
  newoption->max = max_val;
956
957
0
  return newoption;
958
0
}
959
960
/*
961
 * add_real_reloption
962
 *    Add a new float reloption
963
 */
964
void
965
add_real_reloption(bits32 kinds, const char *name, const char *desc,
966
           double default_val, double min_val, double max_val,
967
           LOCKMODE lockmode)
968
0
{
969
0
  relopt_real *newoption = init_real_reloption(kinds, name, desc,
970
0
                         default_val, min_val,
971
0
                         max_val, lockmode);
972
973
0
  add_reloption((relopt_gen *) newoption);
974
0
}
975
976
/*
977
 * add_local_real_reloption
978
 *    Add a new local float reloption
979
 *
980
 * 'offset' is offset of double-typed field.
981
 */
982
void
983
add_local_real_reloption(local_relopts *relopts, const char *name,
984
             const char *desc, double default_val,
985
             double min_val, double max_val, int offset)
986
0
{
987
0
  relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
988
0
                         name, desc,
989
0
                         default_val, min_val,
990
0
                         max_val, 0);
991
992
0
  add_local_reloption(relopts, (relopt_gen *) newoption, offset);
993
0
}
994
995
/*
996
 * init_enum_reloption
997
 *    Allocate and initialize a new enum reloption
998
 */
999
static relopt_enum *
1000
init_enum_reloption(bits32 kinds, const char *name, const char *desc,
1001
          relopt_enum_elt_def *members, int default_val,
1002
          const char *detailmsg, LOCKMODE lockmode)
1003
0
{
1004
0
  relopt_enum *newoption;
1005
1006
0
  newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
1007
0
                           name, desc, lockmode);
1008
0
  newoption->members = members;
1009
0
  newoption->default_val = default_val;
1010
0
  newoption->detailmsg = detailmsg;
1011
1012
0
  return newoption;
1013
0
}
1014
1015
1016
/*
1017
 * add_enum_reloption
1018
 *    Add a new enum reloption
1019
 *
1020
 * The members array must have a terminating NULL entry.
1021
 *
1022
 * The detailmsg is shown when unsupported values are passed, and has this
1023
 * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
1024
 *
1025
 * The members array and detailmsg are not copied -- caller must ensure that
1026
 * they are valid throughout the life of the process.
1027
 */
1028
void
1029
add_enum_reloption(bits32 kinds, const char *name, const char *desc,
1030
           relopt_enum_elt_def *members, int default_val,
1031
           const char *detailmsg, LOCKMODE lockmode)
1032
0
{
1033
0
  relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
1034
0
                         members, default_val,
1035
0
                         detailmsg, lockmode);
1036
1037
0
  add_reloption((relopt_gen *) newoption);
1038
0
}
1039
1040
/*
1041
 * add_local_enum_reloption
1042
 *    Add a new local enum reloption
1043
 *
1044
 * 'offset' is offset of int-typed field.
1045
 */
1046
void
1047
add_local_enum_reloption(local_relopts *relopts, const char *name,
1048
             const char *desc, relopt_enum_elt_def *members,
1049
             int default_val, const char *detailmsg, int offset)
1050
0
{
1051
0
  relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
1052
0
                         name, desc,
1053
0
                         members, default_val,
1054
0
                         detailmsg, 0);
1055
1056
0
  add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1057
0
}
1058
1059
/*
1060
 * init_string_reloption
1061
 *    Allocate and initialize a new string reloption
1062
 */
1063
static relopt_string *
1064
init_string_reloption(bits32 kinds, const char *name, const char *desc,
1065
            const char *default_val,
1066
            validate_string_relopt validator,
1067
            fill_string_relopt filler,
1068
            LOCKMODE lockmode)
1069
0
{
1070
0
  relopt_string *newoption;
1071
1072
  /* make sure the validator/default combination is sane */
1073
0
  if (validator)
1074
0
    (validator) (default_val);
1075
1076
0
  newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
1077
0
                           name, desc, lockmode);
1078
0
  newoption->validate_cb = validator;
1079
0
  newoption->fill_cb = filler;
1080
0
  if (default_val)
1081
0
  {
1082
0
    if (kinds == RELOPT_KIND_LOCAL)
1083
0
      newoption->default_val = strdup(default_val);
1084
0
    else
1085
0
      newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
1086
0
    newoption->default_len = strlen(default_val);
1087
0
    newoption->default_isnull = false;
1088
0
  }
1089
0
  else
1090
0
  {
1091
0
    newoption->default_val = "";
1092
0
    newoption->default_len = 0;
1093
0
    newoption->default_isnull = true;
1094
0
  }
1095
1096
0
  return newoption;
1097
0
}
1098
1099
/*
1100
 * add_string_reloption
1101
 *    Add a new string reloption
1102
 *
1103
 * "validator" is an optional function pointer that can be used to test the
1104
 * validity of the values.  It must elog(ERROR) when the argument string is
1105
 * not acceptable for the variable.  Note that the default value must pass
1106
 * the validation.
1107
 */
1108
void
1109
add_string_reloption(bits32 kinds, const char *name, const char *desc,
1110
           const char *default_val, validate_string_relopt validator,
1111
           LOCKMODE lockmode)
1112
0
{
1113
0
  relopt_string *newoption = init_string_reloption(kinds, name, desc,
1114
0
                           default_val,
1115
0
                           validator, NULL,
1116
0
                           lockmode);
1117
1118
0
  add_reloption((relopt_gen *) newoption);
1119
0
}
1120
1121
/*
1122
 * add_local_string_reloption
1123
 *    Add a new local string reloption
1124
 *
1125
 * 'offset' is offset of int-typed field that will store offset of string value
1126
 * in the resulting bytea structure.
1127
 */
1128
void
1129
add_local_string_reloption(local_relopts *relopts, const char *name,
1130
               const char *desc, const char *default_val,
1131
               validate_string_relopt validator,
1132
               fill_string_relopt filler, int offset)
1133
0
{
1134
0
  relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
1135
0
                           name, desc,
1136
0
                           default_val,
1137
0
                           validator, filler,
1138
0
                           0);
1139
1140
0
  add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1141
0
}
1142
1143
/*
1144
 * Transform a relation options list (list of DefElem) into the text array
1145
 * format that is kept in pg_class.reloptions, including only those options
1146
 * that are in the passed namespace.  The output values do not include the
1147
 * namespace.
1148
 *
1149
 * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
1150
 * ALTER TABLE RESET.  In the ALTER cases, oldOptions is the existing
1151
 * reloptions value (possibly NULL), and we replace or remove entries
1152
 * as needed.
1153
 *
1154
 * If acceptOidsOff is true, then we allow oids = false, but throw error when
1155
 * on. This is solely needed for backwards compatibility.
1156
 *
1157
 * Note that this is not responsible for determining whether the options
1158
 * are valid, but it does check that namespaces for all the options given are
1159
 * listed in validnsps.  The NULL namespace is always valid and need not be
1160
 * explicitly listed.  Passing a NULL pointer means that only the NULL
1161
 * namespace is valid.
1162
 *
1163
 * Both oldOptions and the result are text arrays (or NULL for "default"),
1164
 * but we declare them as Datums to avoid including array.h in reloptions.h.
1165
 */
1166
Datum
1167
transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace,
1168
          const char *const validnsps[], bool acceptOidsOff, bool isReset)
1169
0
{
1170
0
  Datum   result;
1171
0
  ArrayBuildState *astate;
1172
0
  ListCell   *cell;
1173
1174
  /* no change if empty list */
1175
0
  if (defList == NIL)
1176
0
    return oldOptions;
1177
1178
  /* We build new array using accumArrayResult */
1179
0
  astate = NULL;
1180
1181
  /* Copy any oldOptions that aren't to be replaced */
1182
0
  if (DatumGetPointer(oldOptions) != NULL)
1183
0
  {
1184
0
    ArrayType  *array = DatumGetArrayTypeP(oldOptions);
1185
0
    Datum    *oldoptions;
1186
0
    int     noldoptions;
1187
0
    int     i;
1188
1189
0
    deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
1190
1191
0
    for (i = 0; i < noldoptions; i++)
1192
0
    {
1193
0
      char     *text_str = VARDATA(DatumGetPointer(oldoptions[i]));
1194
0
      int     text_len = VARSIZE(DatumGetPointer(oldoptions[i])) - VARHDRSZ;
1195
1196
      /* Search for a match in defList */
1197
0
      foreach(cell, defList)
1198
0
      {
1199
0
        DefElem    *def = (DefElem *) lfirst(cell);
1200
0
        int     kw_len;
1201
1202
        /* ignore if not in the same namespace */
1203
0
        if (nameSpace == NULL)
1204
0
        {
1205
0
          if (def->defnamespace != NULL)
1206
0
            continue;
1207
0
        }
1208
0
        else if (def->defnamespace == NULL)
1209
0
          continue;
1210
0
        else if (strcmp(def->defnamespace, nameSpace) != 0)
1211
0
          continue;
1212
1213
0
        kw_len = strlen(def->defname);
1214
0
        if (text_len > kw_len && text_str[kw_len] == '=' &&
1215
0
          strncmp(text_str, def->defname, kw_len) == 0)
1216
0
          break;
1217
0
      }
1218
0
      if (!cell)
1219
0
      {
1220
        /* No match, so keep old option */
1221
0
        astate = accumArrayResult(astate, oldoptions[i],
1222
0
                      false, TEXTOID,
1223
0
                      CurrentMemoryContext);
1224
0
      }
1225
0
    }
1226
0
  }
1227
1228
  /*
1229
   * If CREATE/SET, add new options to array; if RESET, just check that the
1230
   * user didn't say RESET (option=val).  (Must do this because the grammar
1231
   * doesn't enforce it.)
1232
   */
1233
0
  foreach(cell, defList)
1234
0
  {
1235
0
    DefElem    *def = (DefElem *) lfirst(cell);
1236
1237
0
    if (isReset)
1238
0
    {
1239
0
      if (def->arg != NULL)
1240
0
        ereport(ERROR,
1241
0
            (errcode(ERRCODE_SYNTAX_ERROR),
1242
0
             errmsg("RESET must not include values for parameters")));
1243
0
    }
1244
0
    else
1245
0
    {
1246
0
      const char *name;
1247
0
      const char *value;
1248
0
      text     *t;
1249
0
      Size    len;
1250
1251
      /*
1252
       * Error out if the namespace is not valid.  A NULL namespace is
1253
       * always valid.
1254
       */
1255
0
      if (def->defnamespace != NULL)
1256
0
      {
1257
0
        bool    valid = false;
1258
0
        int     i;
1259
1260
0
        if (validnsps)
1261
0
        {
1262
0
          for (i = 0; validnsps[i]; i++)
1263
0
          {
1264
0
            if (strcmp(def->defnamespace, validnsps[i]) == 0)
1265
0
            {
1266
0
              valid = true;
1267
0
              break;
1268
0
            }
1269
0
          }
1270
0
        }
1271
1272
0
        if (!valid)
1273
0
          ereport(ERROR,
1274
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1275
0
               errmsg("unrecognized parameter namespace \"%s\"",
1276
0
                  def->defnamespace)));
1277
0
      }
1278
1279
      /* ignore if not in the same namespace */
1280
0
      if (nameSpace == NULL)
1281
0
      {
1282
0
        if (def->defnamespace != NULL)
1283
0
          continue;
1284
0
      }
1285
0
      else if (def->defnamespace == NULL)
1286
0
        continue;
1287
0
      else if (strcmp(def->defnamespace, nameSpace) != 0)
1288
0
        continue;
1289
1290
      /*
1291
       * Flatten the DefElem into a text string like "name=arg". If we
1292
       * have just "name", assume "name=true" is meant.  Note: the
1293
       * namespace is not output.
1294
       */
1295
0
      name = def->defname;
1296
0
      if (def->arg != NULL)
1297
0
        value = defGetString(def);
1298
0
      else
1299
0
        value = "true";
1300
1301
      /* Insist that name not contain "=", else "a=b=c" is ambiguous */
1302
0
      if (strchr(name, '=') != NULL)
1303
0
        ereport(ERROR,
1304
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1305
0
             errmsg("invalid option name \"%s\": must not contain \"=\"",
1306
0
                name)));
1307
1308
      /*
1309
       * This is not a great place for this test, but there's no other
1310
       * convenient place to filter the option out. As WITH (oids =
1311
       * false) will be removed someday, this seems like an acceptable
1312
       * amount of ugly.
1313
       */
1314
0
      if (acceptOidsOff && def->defnamespace == NULL &&
1315
0
        strcmp(name, "oids") == 0)
1316
0
      {
1317
0
        if (defGetBoolean(def))
1318
0
          ereport(ERROR,
1319
0
              (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1320
0
               errmsg("tables declared WITH OIDS are not supported")));
1321
        /* skip over option, reloptions machinery doesn't know it */
1322
0
        continue;
1323
0
      }
1324
1325
0
      len = VARHDRSZ + strlen(name) + 1 + strlen(value);
1326
      /* +1 leaves room for sprintf's trailing null */
1327
0
      t = (text *) palloc(len + 1);
1328
0
      SET_VARSIZE(t, len);
1329
0
      sprintf(VARDATA(t), "%s=%s", name, value);
1330
1331
0
      astate = accumArrayResult(astate, PointerGetDatum(t),
1332
0
                    false, TEXTOID,
1333
0
                    CurrentMemoryContext);
1334
0
    }
1335
0
  }
1336
1337
0
  if (astate)
1338
0
    result = makeArrayResult(astate, CurrentMemoryContext);
1339
0
  else
1340
0
    result = (Datum) 0;
1341
1342
0
  return result;
1343
0
}
1344
1345
1346
/*
1347
 * Convert the text-array format of reloptions into a List of DefElem.
1348
 * This is the inverse of transformRelOptions().
1349
 */
1350
List *
1351
untransformRelOptions(Datum options)
1352
0
{
1353
0
  List     *result = NIL;
1354
0
  ArrayType  *array;
1355
0
  Datum    *optiondatums;
1356
0
  int     noptions;
1357
0
  int     i;
1358
1359
  /* Nothing to do if no options */
1360
0
  if (DatumGetPointer(options) == NULL)
1361
0
    return result;
1362
1363
0
  array = DatumGetArrayTypeP(options);
1364
1365
0
  deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1366
1367
0
  for (i = 0; i < noptions; i++)
1368
0
  {
1369
0
    char     *s;
1370
0
    char     *p;
1371
0
    Node     *val = NULL;
1372
1373
0
    s = TextDatumGetCString(optiondatums[i]);
1374
0
    p = strchr(s, '=');
1375
0
    if (p)
1376
0
    {
1377
0
      *p++ = '\0';
1378
0
      val = (Node *) makeString(p);
1379
0
    }
1380
0
    result = lappend(result, makeDefElem(s, val, -1));
1381
0
  }
1382
1383
0
  return result;
1384
0
}
1385
1386
/*
1387
 * Extract and parse reloptions from a pg_class tuple.
1388
 *
1389
 * This is a low-level routine, expected to be used by relcache code and
1390
 * callers that do not have a table's relcache entry (e.g. autovacuum).  For
1391
 * other uses, consider grabbing the rd_options pointer from the relcache entry
1392
 * instead.
1393
 *
1394
 * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
1395
 * AM's options parser function in the case of a tuple corresponding to an
1396
 * index, or NULL otherwise.
1397
 */
1398
bytea *
1399
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
1400
          amoptions_function amoptions)
1401
0
{
1402
0
  bytea    *options;
1403
0
  bool    isnull;
1404
0
  Datum   datum;
1405
0
  Form_pg_class classForm;
1406
1407
0
  datum = fastgetattr(tuple,
1408
0
            Anum_pg_class_reloptions,
1409
0
            tupdesc,
1410
0
            &isnull);
1411
0
  if (isnull)
1412
0
    return NULL;
1413
1414
0
  classForm = (Form_pg_class) GETSTRUCT(tuple);
1415
1416
  /* Parse into appropriate format; don't error out here */
1417
0
  switch (classForm->relkind)
1418
0
  {
1419
0
    case RELKIND_RELATION:
1420
0
    case RELKIND_TOASTVALUE:
1421
0
    case RELKIND_MATVIEW:
1422
0
      options = heap_reloptions(classForm->relkind, datum, false);
1423
0
      break;
1424
0
    case RELKIND_PARTITIONED_TABLE:
1425
0
      options = partitioned_table_reloptions(datum, false);
1426
0
      break;
1427
0
    case RELKIND_VIEW:
1428
0
      options = view_reloptions(datum, false);
1429
0
      break;
1430
0
    case RELKIND_INDEX:
1431
0
    case RELKIND_PARTITIONED_INDEX:
1432
0
      options = index_reloptions(amoptions, datum, false);
1433
0
      break;
1434
0
    case RELKIND_FOREIGN_TABLE:
1435
0
      options = NULL;
1436
0
      break;
1437
0
    default:
1438
0
      Assert(false);    /* can't get here */
1439
0
      options = NULL;   /* keep compiler quiet */
1440
0
      break;
1441
0
  }
1442
1443
0
  return options;
1444
0
}
1445
1446
static void
1447
parseRelOptionsInternal(Datum options, bool validate,
1448
            relopt_value *reloptions, int numoptions)
1449
0
{
1450
0
  ArrayType  *array = DatumGetArrayTypeP(options);
1451
0
  Datum    *optiondatums;
1452
0
  int     noptions;
1453
0
  int     i;
1454
1455
0
  deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1456
1457
0
  for (i = 0; i < noptions; i++)
1458
0
  {
1459
0
    char     *text_str = VARDATA(DatumGetPointer(optiondatums[i]));
1460
0
    int     text_len = VARSIZE(DatumGetPointer(optiondatums[i])) - VARHDRSZ;
1461
0
    int     j;
1462
1463
    /* Search for a match in reloptions */
1464
0
    for (j = 0; j < numoptions; j++)
1465
0
    {
1466
0
      int     kw_len = reloptions[j].gen->namelen;
1467
1468
0
      if (text_len > kw_len && text_str[kw_len] == '=' &&
1469
0
        strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
1470
0
      {
1471
0
        parse_one_reloption(&reloptions[j], text_str, text_len,
1472
0
                  validate);
1473
0
        break;
1474
0
      }
1475
0
    }
1476
1477
0
    if (j >= numoptions && validate)
1478
0
    {
1479
0
      char     *s;
1480
0
      char     *p;
1481
1482
0
      s = TextDatumGetCString(optiondatums[i]);
1483
0
      p = strchr(s, '=');
1484
0
      if (p)
1485
0
        *p = '\0';
1486
0
      ereport(ERROR,
1487
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1488
0
           errmsg("unrecognized parameter \"%s\"", s)));
1489
0
    }
1490
0
  }
1491
1492
  /* It's worth avoiding memory leaks in this function */
1493
0
  pfree(optiondatums);
1494
1495
0
  if (((void *) array) != DatumGetPointer(options))
1496
0
    pfree(array);
1497
0
}
1498
1499
/*
1500
 * Interpret reloptions that are given in text-array format.
1501
 *
1502
 * options is a reloption text array as constructed by transformRelOptions.
1503
 * kind specifies the family of options to be processed.
1504
 *
1505
 * The return value is a relopt_value * array on which the options actually
1506
 * set in the options array are marked with isset=true.  The length of this
1507
 * array is returned in *numrelopts.  Options not set are also present in the
1508
 * array; this is so that the caller can easily locate the default values.
1509
 *
1510
 * If there are no options of the given kind, numrelopts is set to 0 and NULL
1511
 * is returned (unless options are illegally supplied despite none being
1512
 * defined, in which case an error occurs).
1513
 *
1514
 * Note: values of type int, bool and real are allocated as part of the
1515
 * returned array.  Values of type string are allocated separately and must
1516
 * be freed by the caller.
1517
 */
1518
static relopt_value *
1519
parseRelOptions(Datum options, bool validate, relopt_kind kind,
1520
        int *numrelopts)
1521
0
{
1522
0
  relopt_value *reloptions = NULL;
1523
0
  int     numoptions = 0;
1524
0
  int     i;
1525
0
  int     j;
1526
1527
0
  if (need_initialization)
1528
0
    initialize_reloptions();
1529
1530
  /* Build a list of expected options, based on kind */
1531
1532
0
  for (i = 0; relOpts[i]; i++)
1533
0
    if (relOpts[i]->kinds & kind)
1534
0
      numoptions++;
1535
1536
0
  if (numoptions > 0)
1537
0
  {
1538
0
    reloptions = palloc(numoptions * sizeof(relopt_value));
1539
1540
0
    for (i = 0, j = 0; relOpts[i]; i++)
1541
0
    {
1542
0
      if (relOpts[i]->kinds & kind)
1543
0
      {
1544
0
        reloptions[j].gen = relOpts[i];
1545
0
        reloptions[j].isset = false;
1546
0
        j++;
1547
0
      }
1548
0
    }
1549
0
  }
1550
1551
  /* Done if no options */
1552
0
  if (DatumGetPointer(options) != NULL)
1553
0
    parseRelOptionsInternal(options, validate, reloptions, numoptions);
1554
1555
0
  *numrelopts = numoptions;
1556
0
  return reloptions;
1557
0
}
1558
1559
/* Parse local unregistered options. */
1560
static relopt_value *
1561
parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
1562
0
{
1563
0
  int     nopts = list_length(relopts->options);
1564
0
  relopt_value *values = palloc(sizeof(*values) * nopts);
1565
0
  ListCell   *lc;
1566
0
  int     i = 0;
1567
1568
0
  foreach(lc, relopts->options)
1569
0
  {
1570
0
    local_relopt *opt = lfirst(lc);
1571
1572
0
    values[i].gen = opt->option;
1573
0
    values[i].isset = false;
1574
1575
0
    i++;
1576
0
  }
1577
1578
0
  if (options != (Datum) 0)
1579
0
    parseRelOptionsInternal(options, validate, values, nopts);
1580
1581
0
  return values;
1582
0
}
1583
1584
/*
1585
 * Subroutine for parseRelOptions, to parse and validate a single option's
1586
 * value
1587
 */
1588
static void
1589
parse_one_reloption(relopt_value *option, char *text_str, int text_len,
1590
          bool validate)
1591
0
{
1592
0
  char     *value;
1593
0
  int     value_len;
1594
0
  bool    parsed;
1595
0
  bool    nofree = false;
1596
1597
0
  if (option->isset && validate)
1598
0
    ereport(ERROR,
1599
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1600
0
         errmsg("parameter \"%s\" specified more than once",
1601
0
            option->gen->name)));
1602
1603
0
  value_len = text_len - option->gen->namelen - 1;
1604
0
  value = (char *) palloc(value_len + 1);
1605
0
  memcpy(value, text_str + option->gen->namelen + 1, value_len);
1606
0
  value[value_len] = '\0';
1607
1608
0
  switch (option->gen->type)
1609
0
  {
1610
0
    case RELOPT_TYPE_BOOL:
1611
0
      {
1612
0
        parsed = parse_bool(value, &option->values.bool_val);
1613
0
        if (validate && !parsed)
1614
0
          ereport(ERROR,
1615
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1616
0
               errmsg("invalid value for boolean option \"%s\": %s",
1617
0
                  option->gen->name, value)));
1618
0
      }
1619
0
      break;
1620
0
    case RELOPT_TYPE_INT:
1621
0
      {
1622
0
        relopt_int *optint = (relopt_int *) option->gen;
1623
1624
0
        parsed = parse_int(value, &option->values.int_val, 0, NULL);
1625
0
        if (validate && !parsed)
1626
0
          ereport(ERROR,
1627
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1628
0
               errmsg("invalid value for integer option \"%s\": %s",
1629
0
                  option->gen->name, value)));
1630
0
        if (validate && (option->values.int_val < optint->min ||
1631
0
                 option->values.int_val > optint->max))
1632
0
          ereport(ERROR,
1633
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1634
0
               errmsg("value %s out of bounds for option \"%s\"",
1635
0
                  value, option->gen->name),
1636
0
               errdetail("Valid values are between \"%d\" and \"%d\".",
1637
0
                     optint->min, optint->max)));
1638
0
      }
1639
0
      break;
1640
0
    case RELOPT_TYPE_REAL:
1641
0
      {
1642
0
        relopt_real *optreal = (relopt_real *) option->gen;
1643
1644
0
        parsed = parse_real(value, &option->values.real_val, 0, NULL);
1645
0
        if (validate && !parsed)
1646
0
          ereport(ERROR,
1647
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1648
0
               errmsg("invalid value for floating point option \"%s\": %s",
1649
0
                  option->gen->name, value)));
1650
0
        if (validate && (option->values.real_val < optreal->min ||
1651
0
                 option->values.real_val > optreal->max))
1652
0
          ereport(ERROR,
1653
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1654
0
               errmsg("value %s out of bounds for option \"%s\"",
1655
0
                  value, option->gen->name),
1656
0
               errdetail("Valid values are between \"%f\" and \"%f\".",
1657
0
                     optreal->min, optreal->max)));
1658
0
      }
1659
0
      break;
1660
0
    case RELOPT_TYPE_ENUM:
1661
0
      {
1662
0
        relopt_enum *optenum = (relopt_enum *) option->gen;
1663
0
        relopt_enum_elt_def *elt;
1664
1665
0
        parsed = false;
1666
0
        for (elt = optenum->members; elt->string_val; elt++)
1667
0
        {
1668
0
          if (pg_strcasecmp(value, elt->string_val) == 0)
1669
0
          {
1670
0
            option->values.enum_val = elt->symbol_val;
1671
0
            parsed = true;
1672
0
            break;
1673
0
          }
1674
0
        }
1675
0
        if (validate && !parsed)
1676
0
          ereport(ERROR,
1677
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1678
0
               errmsg("invalid value for enum option \"%s\": %s",
1679
0
                  option->gen->name, value),
1680
0
               optenum->detailmsg ?
1681
0
               errdetail_internal("%s", _(optenum->detailmsg)) : 0));
1682
1683
        /*
1684
         * If value is not among the allowed string values, but we are
1685
         * not asked to validate, just use the default numeric value.
1686
         */
1687
0
        if (!parsed)
1688
0
          option->values.enum_val = optenum->default_val;
1689
0
      }
1690
0
      break;
1691
0
    case RELOPT_TYPE_STRING:
1692
0
      {
1693
0
        relopt_string *optstring = (relopt_string *) option->gen;
1694
1695
0
        option->values.string_val = value;
1696
0
        nofree = true;
1697
0
        if (validate && optstring->validate_cb)
1698
0
          (optstring->validate_cb) (value);
1699
0
        parsed = true;
1700
0
      }
1701
0
      break;
1702
0
    default:
1703
0
      elog(ERROR, "unsupported reloption type %d", option->gen->type);
1704
0
      parsed = true;   /* quiet compiler */
1705
0
      break;
1706
0
  }
1707
1708
0
  if (parsed)
1709
0
    option->isset = true;
1710
0
  if (!nofree)
1711
0
    pfree(value);
1712
0
}
1713
1714
/*
1715
 * Given the result from parseRelOptions, allocate a struct that's of the
1716
 * specified base size plus any extra space that's needed for string variables.
1717
 *
1718
 * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
1719
 * equivalent).
1720
 */
1721
static void *
1722
allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1723
0
{
1724
0
  Size    size = base;
1725
0
  int     i;
1726
1727
0
  for (i = 0; i < numoptions; i++)
1728
0
  {
1729
0
    relopt_value *optval = &options[i];
1730
1731
0
    if (optval->gen->type == RELOPT_TYPE_STRING)
1732
0
    {
1733
0
      relopt_string *optstr = (relopt_string *) optval->gen;
1734
1735
0
      if (optstr->fill_cb)
1736
0
      {
1737
0
        const char *val = optval->isset ? optval->values.string_val :
1738
0
          optstr->default_isnull ? NULL : optstr->default_val;
1739
1740
0
        size += optstr->fill_cb(val, NULL);
1741
0
      }
1742
0
      else
1743
0
        size += GET_STRING_RELOPTION_LEN(*optval) + 1;
1744
0
    }
1745
0
  }
1746
1747
0
  return palloc0(size);
1748
0
}
1749
1750
/*
1751
 * Given the result of parseRelOptions and a parsing table, fill in the
1752
 * struct (previously allocated with allocateReloptStruct) with the parsed
1753
 * values.
1754
 *
1755
 * rdopts is the pointer to the allocated struct to be filled.
1756
 * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
1757
 * options, of length numoptions, is parseRelOptions' output.
1758
 * elems, of length numelems, is the table describing the allowed options.
1759
 * When validate is true, it is expected that all options appear in elems.
1760
 */
1761
static void
1762
fillRelOptions(void *rdopts, Size basesize,
1763
         relopt_value *options, int numoptions,
1764
         bool validate,
1765
         const relopt_parse_elt *elems, int numelems)
1766
0
{
1767
0
  int     i;
1768
0
  int     offset = basesize;
1769
1770
0
  for (i = 0; i < numoptions; i++)
1771
0
  {
1772
0
    int     j;
1773
0
    bool    found = false;
1774
1775
0
    for (j = 0; j < numelems; j++)
1776
0
    {
1777
0
      if (strcmp(options[i].gen->name, elems[j].optname) == 0)
1778
0
      {
1779
0
        relopt_string *optstring;
1780
0
        char     *itempos = ((char *) rdopts) + elems[j].offset;
1781
0
        char     *string_val;
1782
1783
        /*
1784
         * If isset_offset is provided, store whether the reloption is
1785
         * set there.
1786
         */
1787
0
        if (elems[j].isset_offset > 0)
1788
0
        {
1789
0
          char     *setpos = ((char *) rdopts) + elems[j].isset_offset;
1790
1791
0
          *(bool *) setpos = options[i].isset;
1792
0
        }
1793
1794
0
        switch (options[i].gen->type)
1795
0
        {
1796
0
          case RELOPT_TYPE_BOOL:
1797
0
            *(bool *) itempos = options[i].isset ?
1798
0
              options[i].values.bool_val :
1799
0
              ((relopt_bool *) options[i].gen)->default_val;
1800
0
            break;
1801
0
          case RELOPT_TYPE_INT:
1802
0
            *(int *) itempos = options[i].isset ?
1803
0
              options[i].values.int_val :
1804
0
              ((relopt_int *) options[i].gen)->default_val;
1805
0
            break;
1806
0
          case RELOPT_TYPE_REAL:
1807
0
            *(double *) itempos = options[i].isset ?
1808
0
              options[i].values.real_val :
1809
0
              ((relopt_real *) options[i].gen)->default_val;
1810
0
            break;
1811
0
          case RELOPT_TYPE_ENUM:
1812
0
            *(int *) itempos = options[i].isset ?
1813
0
              options[i].values.enum_val :
1814
0
              ((relopt_enum *) options[i].gen)->default_val;
1815
0
            break;
1816
0
          case RELOPT_TYPE_STRING:
1817
0
            optstring = (relopt_string *) options[i].gen;
1818
0
            if (options[i].isset)
1819
0
              string_val = options[i].values.string_val;
1820
0
            else if (!optstring->default_isnull)
1821
0
              string_val = optstring->default_val;
1822
0
            else
1823
0
              string_val = NULL;
1824
1825
0
            if (optstring->fill_cb)
1826
0
            {
1827
0
              Size    size =
1828
0
                optstring->fill_cb(string_val,
1829
0
                           (char *) rdopts + offset);
1830
1831
0
              if (size)
1832
0
              {
1833
0
                *(int *) itempos = offset;
1834
0
                offset += size;
1835
0
              }
1836
0
              else
1837
0
                *(int *) itempos = 0;
1838
0
            }
1839
0
            else if (string_val == NULL)
1840
0
              *(int *) itempos = 0;
1841
0
            else
1842
0
            {
1843
0
              strcpy((char *) rdopts + offset, string_val);
1844
0
              *(int *) itempos = offset;
1845
0
              offset += strlen(string_val) + 1;
1846
0
            }
1847
0
            break;
1848
0
          default:
1849
0
            elog(ERROR, "unsupported reloption type %d",
1850
0
               options[i].gen->type);
1851
0
            break;
1852
0
        }
1853
0
        found = true;
1854
0
        break;
1855
0
      }
1856
0
    }
1857
0
    if (validate && !found)
1858
0
      elog(ERROR, "reloption \"%s\" not found in parse table",
1859
0
         options[i].gen->name);
1860
0
  }
1861
0
  SET_VARSIZE(rdopts, offset);
1862
0
}
1863
1864
1865
/*
1866
 * Option parser for anything that uses StdRdOptions.
1867
 */
1868
bytea *
1869
default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1870
0
{
1871
0
  static const relopt_parse_elt tab[] = {
1872
0
    {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1873
0
    {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1874
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
1875
0
    {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1876
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
1877
0
    {"autovacuum_vacuum_max_threshold", RELOPT_TYPE_INT,
1878
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_threshold)},
1879
0
    {"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT,
1880
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
1881
0
    {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1882
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
1883
0
    {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1884
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
1885
0
    {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1886
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
1887
0
    {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1888
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
1889
0
    {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1890
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
1891
0
    {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
1892
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
1893
0
    {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
1894
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
1895
0
    {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
1896
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
1897
0
    {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
1898
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
1899
0
    {"toast_tuple_target", RELOPT_TYPE_INT,
1900
0
    offsetof(StdRdOptions, toast_tuple_target)},
1901
0
    {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
1902
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
1903
0
    {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
1904
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
1905
0
    {"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL,
1906
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor)},
1907
0
    {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
1908
0
    offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
1909
0
    {"user_catalog_table", RELOPT_TYPE_BOOL,
1910
0
    offsetof(StdRdOptions, user_catalog_table)},
1911
0
    {"parallel_workers", RELOPT_TYPE_INT,
1912
0
    offsetof(StdRdOptions, parallel_workers)},
1913
0
    {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
1914
0
    offsetof(StdRdOptions, vacuum_index_cleanup)},
1915
0
    {"vacuum_truncate", RELOPT_TYPE_BOOL,
1916
0
    offsetof(StdRdOptions, vacuum_truncate), offsetof(StdRdOptions, vacuum_truncate_set)},
1917
0
    {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
1918
0
    offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
1919
0
  };
1920
1921
0
  return (bytea *) build_reloptions(reloptions, validate, kind,
1922
0
                    sizeof(StdRdOptions),
1923
0
                    tab, lengthof(tab));
1924
0
}
1925
1926
/*
1927
 * build_reloptions
1928
 *
1929
 * Parses "reloptions" provided by the caller, returning them in a
1930
 * structure containing the parsed options.  The parsing is done with
1931
 * the help of a parsing table describing the allowed options, defined
1932
 * by "relopt_elems" of length "num_relopt_elems".
1933
 *
1934
 * "validate" must be true if reloptions value is freshly built by
1935
 * transformRelOptions(), as opposed to being read from the catalog, in which
1936
 * case the values contained in it must already be valid.
1937
 *
1938
 * NULL is returned if the passed-in options did not match any of the options
1939
 * in the parsing table, unless validate is true in which case an error would
1940
 * be reported.
1941
 */
1942
void *
1943
build_reloptions(Datum reloptions, bool validate,
1944
         relopt_kind kind,
1945
         Size relopt_struct_size,
1946
         const relopt_parse_elt *relopt_elems,
1947
         int num_relopt_elems)
1948
0
{
1949
0
  int     numoptions;
1950
0
  relopt_value *options;
1951
0
  void     *rdopts;
1952
1953
  /* parse options specific to given relation option kind */
1954
0
  options = parseRelOptions(reloptions, validate, kind, &numoptions);
1955
0
  Assert(numoptions <= num_relopt_elems);
1956
1957
  /* if none set, we're done */
1958
0
  if (numoptions == 0)
1959
0
  {
1960
0
    Assert(options == NULL);
1961
0
    return NULL;
1962
0
  }
1963
1964
  /* allocate and fill the structure */
1965
0
  rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
1966
0
  fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
1967
0
           validate, relopt_elems, num_relopt_elems);
1968
1969
0
  pfree(options);
1970
1971
0
  return rdopts;
1972
0
}
1973
1974
/*
1975
 * Parse local options, allocate a bytea struct that's of the specified
1976
 * 'base_size' plus any extra space that's needed for string variables,
1977
 * fill its option's fields located at the given offsets and return it.
1978
 */
1979
void *
1980
build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
1981
0
{
1982
0
  int     noptions = list_length(relopts->options);
1983
0
  relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
1984
0
  relopt_value *vals;
1985
0
  void     *opts;
1986
0
  int     i = 0;
1987
0
  ListCell   *lc;
1988
1989
0
  foreach(lc, relopts->options)
1990
0
  {
1991
0
    local_relopt *opt = lfirst(lc);
1992
1993
0
    elems[i].optname = opt->option->name;
1994
0
    elems[i].opttype = opt->option->type;
1995
0
    elems[i].offset = opt->offset;
1996
0
    elems[i].isset_offset = 0;  /* not supported for local relopts yet */
1997
1998
0
    i++;
1999
0
  }
2000
2001
0
  vals = parseLocalRelOptions(relopts, options, validate);
2002
0
  opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
2003
0
  fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
2004
0
           elems, noptions);
2005
2006
0
  if (validate)
2007
0
    foreach(lc, relopts->validators)
2008
0
      ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
2009
2010
0
  if (elems)
2011
0
    pfree(elems);
2012
2013
0
  return opts;
2014
0
}
2015
2016
/*
2017
 * Option parser for partitioned tables
2018
 */
2019
bytea *
2020
partitioned_table_reloptions(Datum reloptions, bool validate)
2021
0
{
2022
0
  if (validate && reloptions)
2023
0
    ereport(ERROR,
2024
0
        errcode(ERRCODE_WRONG_OBJECT_TYPE),
2025
0
        errmsg("cannot specify storage parameters for a partitioned table"),
2026
0
        errhint("Specify storage parameters for its leaf partitions instead."));
2027
0
  return NULL;
2028
0
}
2029
2030
/*
2031
 * Option parser for views
2032
 */
2033
bytea *
2034
view_reloptions(Datum reloptions, bool validate)
2035
0
{
2036
0
  static const relopt_parse_elt tab[] = {
2037
0
    {"security_barrier", RELOPT_TYPE_BOOL,
2038
0
    offsetof(ViewOptions, security_barrier)},
2039
0
    {"security_invoker", RELOPT_TYPE_BOOL,
2040
0
    offsetof(ViewOptions, security_invoker)},
2041
0
    {"check_option", RELOPT_TYPE_ENUM,
2042
0
    offsetof(ViewOptions, check_option)}
2043
0
  };
2044
2045
0
  return (bytea *) build_reloptions(reloptions, validate,
2046
0
                    RELOPT_KIND_VIEW,
2047
0
                    sizeof(ViewOptions),
2048
0
                    tab, lengthof(tab));
2049
0
}
2050
2051
/*
2052
 * Parse options for heaps, views and toast tables.
2053
 */
2054
bytea *
2055
heap_reloptions(char relkind, Datum reloptions, bool validate)
2056
0
{
2057
0
  StdRdOptions *rdopts;
2058
2059
0
  switch (relkind)
2060
0
  {
2061
0
    case RELKIND_TOASTVALUE:
2062
0
      rdopts = (StdRdOptions *)
2063
0
        default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
2064
0
      if (rdopts != NULL)
2065
0
      {
2066
        /* adjust default-only parameters for TOAST relations */
2067
0
        rdopts->fillfactor = 100;
2068
0
        rdopts->autovacuum.analyze_threshold = -1;
2069
0
        rdopts->autovacuum.analyze_scale_factor = -1;
2070
0
      }
2071
0
      return (bytea *) rdopts;
2072
0
    case RELKIND_RELATION:
2073
0
    case RELKIND_MATVIEW:
2074
0
      return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
2075
0
    default:
2076
      /* other relkinds are not supported */
2077
0
      return NULL;
2078
0
  }
2079
0
}
2080
2081
2082
/*
2083
 * Parse options for indexes.
2084
 *
2085
 *  amoptions index AM's option parser function
2086
 *  reloptions  options as text[] datum
2087
 *  validate  error flag
2088
 */
2089
bytea *
2090
index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
2091
0
{
2092
0
  Assert(amoptions != NULL);
2093
2094
  /* Assume function is strict */
2095
0
  if (DatumGetPointer(reloptions) == NULL)
2096
0
    return NULL;
2097
2098
0
  return amoptions(reloptions, validate);
2099
0
}
2100
2101
/*
2102
 * Option parser for attribute reloptions
2103
 */
2104
bytea *
2105
attribute_reloptions(Datum reloptions, bool validate)
2106
0
{
2107
0
  static const relopt_parse_elt tab[] = {
2108
0
    {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
2109
0
    {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
2110
0
  };
2111
2112
0
  return (bytea *) build_reloptions(reloptions, validate,
2113
0
                    RELOPT_KIND_ATTRIBUTE,
2114
0
                    sizeof(AttributeOpts),
2115
0
                    tab, lengthof(tab));
2116
0
}
2117
2118
/*
2119
 * Option parser for tablespace reloptions
2120
 */
2121
bytea *
2122
tablespace_reloptions(Datum reloptions, bool validate)
2123
0
{
2124
0
  static const relopt_parse_elt tab[] = {
2125
0
    {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
2126
0
    {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
2127
0
    {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
2128
0
    {"maintenance_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, maintenance_io_concurrency)}
2129
0
  };
2130
2131
0
  return (bytea *) build_reloptions(reloptions, validate,
2132
0
                    RELOPT_KIND_TABLESPACE,
2133
0
                    sizeof(TableSpaceOpts),
2134
0
                    tab, lengthof(tab));
2135
0
}
2136
2137
/*
2138
 * Determine the required LOCKMODE from an option list.
2139
 *
2140
 * Called from AlterTableGetLockLevel(), see that function
2141
 * for a longer explanation of how this works.
2142
 */
2143
LOCKMODE
2144
AlterTableGetRelOptionsLockLevel(List *defList)
2145
0
{
2146
0
  LOCKMODE  lockmode = NoLock;
2147
0
  ListCell   *cell;
2148
2149
0
  if (defList == NIL)
2150
0
    return AccessExclusiveLock;
2151
2152
0
  if (need_initialization)
2153
0
    initialize_reloptions();
2154
2155
0
  foreach(cell, defList)
2156
0
  {
2157
0
    DefElem    *def = (DefElem *) lfirst(cell);
2158
0
    int     i;
2159
2160
0
    for (i = 0; relOpts[i]; i++)
2161
0
    {
2162
0
      if (strncmp(relOpts[i]->name,
2163
0
            def->defname,
2164
0
            relOpts[i]->namelen + 1) == 0)
2165
0
      {
2166
0
        if (lockmode < relOpts[i]->lockmode)
2167
0
          lockmode = relOpts[i]->lockmode;
2168
0
      }
2169
0
    }
2170
0
  }
2171
2172
0
  return lockmode;
2173
0
}