Coverage Report

Created: 2026-09-28 06:55

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