=== Applying patches on top of PostgreSQL commit ID c0962a113d1f2f94cb7222a7ca025a67e9ce3860 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Fri Apr 4 13:40:23 UTC 2025 On branch cf/4688 nothing to commit, working tree clean === using 'git am' to apply patch ./v-13-0001-New-options-engine.patch === Applying: 1/1] New options engine Using index info to reconstruct a base tree... M contrib/dblink/dblink.c M contrib/file_fdw/file_fdw.c M src/backend/access/brin/brin.c M src/backend/access/common/reloptions.c M src/backend/access/gist/gist.c M src/backend/access/nbtree/nbtree.c M src/backend/access/nbtree/nbtutils.c M src/backend/commands/indexcmds.c M src/backend/commands/tablecmds.c M src/backend/parser/parse_utilcmd.c M src/backend/utils/cache/relcache.c M src/include/access/nbtree.h M src/include/access/reloptions.h Falling back to patching base and 3-way merge... Auto-merging src/include/access/reloptions.h CONFLICT (content): Merge conflict in src/include/access/reloptions.h Auto-merging src/include/access/nbtree.h Auto-merging src/backend/utils/cache/relcache.c Auto-merging src/backend/parser/parse_utilcmd.c Auto-merging src/backend/commands/tablecmds.c Auto-merging src/backend/commands/indexcmds.c Auto-merging src/backend/access/nbtree/nbtutils.c Auto-merging src/backend/access/nbtree/nbtree.c Auto-merging src/backend/access/gist/gist.c Auto-merging src/backend/access/common/reloptions.c CONFLICT (content): Merge conflict in src/backend/access/common/reloptions.c Auto-merging src/backend/access/brin/brin.c Auto-merging contrib/file_fdw/file_fdw.c Auto-merging contrib/dblink/dblink.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 1/1] New options engine When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". Unstaged changes after reset: M contrib/bloom/bloom.h M contrib/bloom/blutils.c M contrib/dblink/dblink.c M contrib/file_fdw/file_fdw.c M contrib/postgres_fdw/option.c M contrib/test_decoding/expected/twophase.out M doc/src/sgml/indexam.sgml M src/backend/access/brin/brin.c M src/backend/access/common/Makefile M src/backend/access/common/meson.build M src/backend/access/common/reloptions.c M src/backend/access/gin/ginutil.c M src/backend/access/gist/gist.c M src/backend/access/gist/gistutil.c M src/backend/access/hash/hash.c M src/backend/access/hash/hashutil.c M src/backend/access/nbtree/nbtree.c M src/backend/access/nbtree/nbtutils.c M src/backend/access/spgist/spgutils.c M src/backend/commands/createas.c M src/backend/commands/foreigncmds.c M src/backend/commands/indexcmds.c M src/backend/commands/tablecmds.c M src/backend/commands/tablespace.c M src/backend/foreign/foreign.c M src/backend/parser/parse_utilcmd.c M src/backend/tcop/utility.c M src/backend/utils/cache/attoptcache.c M src/backend/utils/cache/relcache.c M src/backend/utils/cache/spccache.c M src/include/access/amapi.h M src/include/access/brin.h M src/include/access/brin_internal.h M src/include/access/gin_private.h M src/include/access/gist_private.h M src/include/access/hash.h M src/include/access/nbtree.h M src/include/access/reloptions.h M src/include/access/spgist.h M src/include/access/spgist_private.h M src/include/commands/tablecmds.h M src/test/modules/dummy_index_am/dummy_index_am.c M src/test/modules/test_oat_hooks/expected/alter_table.out M src/test/regress/expected/reloptions.out M src/test/regress/sql/reloptions.sql Removing src/backend/access/common/options.c Removing src/include/access/options.h === using patch(1) to apply patch ./v-13-0001-New-options-engine.patch === patch: unrecognized option `--no-backup-if-mismatch' usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count] [-r rej-name] [-V t | nil | never | none] [-x number] [-z backup-ext] [--posix] [origfile [patchfile]] patch >>>>>> theirs /* values from StdRdOptIndexCleanup */ - static relopt_enum_elt_def StdRdOptIndexCleanupValues[] = + static opt_enum_elt_def StdRdOptIndexCleanupValues[] = { {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO}, {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON}, @@@ -1703,263 -534,64 +929,210 @@@ get_heap_relopt_spec_set(void } /* - * Given the result from parseRelOptions, allocate a struct that's of the - * specified base size plus any extra space that's needed for string variables. - * - * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or - * equivalent). + * These toast options are can't be set via SQL, but we should set them + * to their defaults in binary representation, to make postgres work properly */ - static void * - allocateReloptStruct(Size base, relopt_value *options, int numoptions) + static void + toast_options_postprocess(void *data, bool validate) { - Size size = base; - int i; - - for (i = 0; i < numoptions; i++) + if (data) { - relopt_value *optval = &options[i]; - - if (optval->gen->type == RELOPT_TYPE_STRING) - { - relopt_string *optstr = (relopt_string *) optval->gen; - - if (optstr->fill_cb) - { - const char *val = optval->isset ? optval->values.string_val : - optstr->default_isnull ? NULL : optstr->default_val; + StdRdOptions *toast_options = (StdRdOptions *) data; - size += optstr->fill_cb(val, NULL); - } - else - size += GET_STRING_RELOPTION_LEN(*optval) + 1; - } + toast_options->fillfactor = 100; + toast_options->autovacuum.analyze_threshold = -1; + toast_options->autovacuum.analyze_scale_factor = -1; } - - return palloc0(size); } - /* - * Given the result of parseRelOptions and a parsing table, fill in the - * struct (previously allocated with allocateReloptStruct) with the parsed - * values. - * - * rdopts is the pointer to the allocated struct to be filled. - * basesize is the sizeof(struct) that was passed to allocateReloptStruct. - * options, of length numoptions, is parseRelOptions' output. - * elems, of length numelems, is the table describing the allowed options. - * When validate is true, it is expected that all options appear in elems. - */ - static void - fillRelOptions(void *rdopts, Size basesize, - relopt_value *options, int numoptions, - bool validate, - const relopt_parse_elt *elems, int numelems) + static options_spec_set *toast_relopt_spec_set = NULL; + options_spec_set * + get_toast_relopt_spec_set(void) { - int i; - int offset = basesize; + if (toast_relopt_spec_set) + return toast_relopt_spec_set; - for (i = 0; i < numoptions; i++) - { - int j; - bool found = false; + toast_relopt_spec_set = get_stdrd_relopt_spec_set(false); + toast_relopt_spec_set->postprocess_fun = toast_options_postprocess; ++<<<<<<< ours + for (j = 0; j < numelems; j++) + { + if (strcmp(options[i].gen->name, elems[j].optname) == 0) + { + relopt_string *optstring; + char *itempos = ((char *) rdopts) + elems[j].offset; + char *string_val; + + /* + * If isset_offset is provided, store whether the reloption is + * set there. + */ + if (elems[j].isset_offset > 0) + { + char *setpos = ((char *) rdopts) + elems[j].isset_offset; + + *(bool *) setpos = options[i].isset; + } + + switch (options[i].gen->type) + { + case RELOPT_TYPE_BOOL: + *(bool *) itempos = options[i].isset ? + options[i].values.bool_val : + ((relopt_bool *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_INT: + *(int *) itempos = options[i].isset ? + options[i].values.int_val : + ((relopt_int *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_REAL: + *(double *) itempos = options[i].isset ? + options[i].values.real_val : + ((relopt_real *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_ENUM: + *(int *) itempos = options[i].isset ? + options[i].values.enum_val : + ((relopt_enum *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_STRING: + optstring = (relopt_string *) options[i].gen; + if (options[i].isset) + string_val = options[i].values.string_val; + else if (!optstring->default_isnull) + string_val = optstring->default_val; + else + string_val = NULL; + + if (optstring->fill_cb) + { + Size size = + optstring->fill_cb(string_val, + (char *) rdopts + offset); + + if (size) + { + *(int *) itempos = offset; + offset += size; + } + else + *(int *) itempos = 0; + } + else if (string_val == NULL) + *(int *) itempos = 0; + else + { + strcpy((char *) rdopts + offset, string_val); + *(int *) itempos = offset; + offset += strlen(string_val) + 1; + } + break; + default: + elog(ERROR, "unsupported reloption type %d", + options[i].gen->type); + break; + } + found = true; + break; + } + } + if (validate && !found) + elog(ERROR, "reloption \"%s\" not found in parse table", + options[i].gen->name); + } + SET_VARSIZE(rdopts, offset); ++======= + return toast_relopt_spec_set; ++>>>>>>> theirs } /* - * Option parser for anything that uses StdRdOptions. + * Do not allow to set any option on partitioned table */ - bytea * - default_reloptions(Datum reloptions, bool validate, relopt_kind kind) + static void + partitioned_options_postprocess(void *data, bool validate) { ++<<<<<<< ours + static const relopt_parse_elt tab[] = { + {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}, + {"autovacuum_enabled", RELOPT_TYPE_BOOL, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)}, + {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)}, + {"autovacuum_vacuum_max_threshold", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_threshold)}, + {"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)}, + {"autovacuum_analyze_threshold", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)}, + {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)}, + {"autovacuum_freeze_min_age", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)}, + {"autovacuum_freeze_max_age", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)}, + {"autovacuum_freeze_table_age", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)}, + {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)}, + {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)}, + {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)}, + {"log_autovacuum_min_duration", RELOPT_TYPE_INT, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)}, + {"toast_tuple_target", RELOPT_TYPE_INT, + offsetof(StdRdOptions, toast_tuple_target)}, + {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)}, + {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)}, + {"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor)}, + {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL, + offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)}, + {"user_catalog_table", RELOPT_TYPE_BOOL, + offsetof(StdRdOptions, user_catalog_table)}, + {"parallel_workers", RELOPT_TYPE_INT, + offsetof(StdRdOptions, parallel_workers)}, + {"vacuum_index_cleanup", RELOPT_TYPE_ENUM, + offsetof(StdRdOptions, vacuum_index_cleanup)}, + {"vacuum_truncate", RELOPT_TYPE_BOOL, + offsetof(StdRdOptions, vacuum_truncate), offsetof(StdRdOptions, vacuum_truncate_set)}, + {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL, + offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)} + }; + + return (bytea *) build_reloptions(reloptions, validate, kind, + sizeof(StdRdOptions), + tab, lengthof(tab)); ++======= + if (data && validate) + ereport(ERROR, + errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot specify storage parameters for a partitioned table"), + errhint("Specify storage parameters for its leaf partitions instead.")); ++>>>>>>> theirs } - /* - * build_reloptions - * - * Parses "reloptions" provided by the caller, returning them in a - * structure containing the parsed options. The parsing is done with - * the help of a parsing table describing the allowed options, defined - * by "relopt_elems" of length "num_relopt_elems". - * - * "validate" must be true if reloptions value is freshly built by - * transformRelOptions(), as opposed to being read from the catalog, in which - * case the values contained in it must already be valid. - * - * NULL is returned if the passed-in options did not match any of the options - * in the parsing table, unless validate is true in which case an error would - * be reported. - */ - void * - build_reloptions(Datum reloptions, bool validate, - relopt_kind kind, - Size relopt_struct_size, - const relopt_parse_elt *relopt_elems, - int num_relopt_elems) - { - int numoptions; - relopt_value *options; - void *rdopts; - - /* parse options specific to given relation option kind */ - options = parseRelOptions(reloptions, validate, kind, &numoptions); - Assert(numoptions <= num_relopt_elems); - /* if none set, we're done */ - if (numoptions == 0) - { - Assert(options == NULL); - return NULL; - } + static options_spec_set *partitioned_relopt_spec_set = NULL; - /* allocate and fill the structure */ - rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions); - fillRelOptions(rdopts, relopt_struct_size, options, numoptions, - validate, relopt_elems, num_relopt_elems); + options_spec_set * + get_partitioned_relopt_spec_set(void) + { + if (partitioned_relopt_spec_set) + return partitioned_relopt_spec_set; + partitioned_relopt_spec_set = get_stdrd_relopt_spec_set(true); - pfree(options); + /* No options for now, so Spec Set is empty */ + partitioned_relopt_spec_set->postprocess_fun = + partitioned_options_postprocess; - return rdopts; + return partitioned_relopt_spec_set; } /* @@@ -1970,36 -602,41 +1143,48 @@@ void * build_local_reloptions(local_relopts *relopts, Datum options, bool validate) { - int noptions = list_length(relopts->options); - relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions); - relopt_value *vals; void *opts; - int i = 0; ListCell *lc; + List *values; - foreach(lc, relopts->options) + values = optionsTextArrayToRawValues(options); + values = optionsParseRawValues(values, relopts->spec_set, validate); + opts = optionsValuesToBytea(values, relopts->spec_set); + + /* + * Kind of ugly conversion here for backward compatibility. Would be + * removed while moving opclass options to options.c API + */ + + if (validate && relopts->validators) { - local_relopt *opt = lfirst(lc); + int val_count = list_length(values); + int i; + option_value *val_array; ++<<<<<<< ours + elems[i].optname = opt->option->name; + elems[i].opttype = opt->option->type; + elems[i].offset = opt->offset; + elems[i].isset_offset = 0; /* not supported for local relopts yet */ ++======= + val_array = palloc(sizeof(option_value) * val_count); ++>>>>>>> theirs - i++; - } + i = 0; + foreach(lc, values) + { + option_value *val = lfirst(lc); - vals = parseLocalRelOptions(relopts, options, validate); - opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions); - fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate, - elems, noptions); + memcpy(&(val_array[i]), val, sizeof(option_value)); + i++; + } - if (validate) foreach(lc, relopts->validators) - ((relopts_validator) lfirst(lc)) (opts, vals, noptions); + ((relopts_validator) lfirst(lc)) (opts, val_array, val_count); - if (elems) - pfree(elems); + pfree(val_array); + } return opts; } diff --cc src/include/access/reloptions.h index dfbb4c85460,b42bb614e03..00000000000 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@@ -28,151 -23,25 +23,67 @@@ /* reloption namespaces allowed for heaps -- currently only TOAST */ #define HEAP_RELOPT_NAMESPACES { "toast", NULL } - /* generic struct to hold shared data */ - typedef struct relopt_gen - { - const char *name; /* must be first (used as list termination - * marker) */ - const char *desc; - bits32 kinds; - LOCKMODE lockmode; - int namelen; - relopt_type type; - } relopt_gen; - - /* holds a parsed value */ - typedef struct relopt_value - { - relopt_gen *gen; - bool isset; - union - { - bool bool_val; - int int_val; - double real_val; - int enum_val; - char *string_val; /* allocated separately */ - } values; - } relopt_value; - - /* reloptions records for specific variable types */ - typedef struct relopt_bool - { - relopt_gen gen; - bool default_val; - } relopt_bool; - - typedef struct relopt_int - { - relopt_gen gen; - int default_val; - int min; - int max; - } relopt_int; - - typedef struct relopt_real - { - relopt_gen gen; - double default_val; - double min; - double max; - } relopt_real; - /* - * relopt_enum_elt_def -- One member of the array of acceptable values - * of an enum reloption. + * backward compatibility aliases so local reloption code of custom validator + * can work. */ - typedef struct relopt_enum_elt_def - { - const char *string_val; - int symbol_val; - } relopt_enum_elt_def; + typedef option_value relopt_value; + typedef fill_string_option fill_string_relopt; + typedef validate_string_option validate_string_relopt; + #define GET_STRING_RELOPTION(optstruct, member) \ + GET_STRING_OPTION(optstruct, member) - typedef struct relopt_enum - { - relopt_gen gen; - relopt_enum_elt_def *members; - int default_val; - const char *detailmsg; - /* null-terminated array of members */ - } relopt_enum; - /* validation routines for strings */ - typedef void (*validate_string_relopt) (const char *value); - typedef Size (*fill_string_relopt) (const char *value, void *ptr); + /* + * relopts_validator functions is left for backward compatibility for using + * with local reloptions. Should not be used elsewhere + */ /* validation routine for the whole option set */ ++<<<<<<< ours +typedef void (*relopts_validator) (void *parsed_options, relopt_value *vals, int nvals); + +typedef struct relopt_string +{ + relopt_gen gen; + int default_len; + bool default_isnull; + validate_string_relopt validate_cb; + fill_string_relopt fill_cb; + char *default_val; +} relopt_string; + +/* This is the table datatype for build_reloptions() */ +typedef struct +{ + const char *optname; /* option's name */ + relopt_type opttype; /* option's datatype */ + int offset; /* offset of field in result struct */ + + /* + * isset_offset is an optional offset of a field in the result struct that + * stores whether the option is explicitly set for the relation or if it + * just picked up the default value. In most cases, this can be + * accomplished by giving the reloption a special out-of-range default + * value (e.g., some integer reloptions use -2), but this isn't always + * possible. For example, a Boolean reloption cannot be given an + * out-of-range default, so we need another way to discover the source of + * its value. This offset is only used if given a value greater than + * zero. + */ + int isset_offset; +} relopt_parse_elt; + +/* Local reloption definition */ +typedef struct local_relopt +{ + relopt_gen *option; /* option definition */ + int offset; /* offset of parsed value in bytea structure */ +} local_relopt; ++======= + typedef void (*relopts_validator) (void *parsed_options, relopt_value *vals, + int nvals); ++>>>>>>> theirs /* Structure to hold local reloption data for build_local_reloptions() */ typedef struct local_relopts