Thread
-
[PATCH 1/2] Allow table AMs to define their own reloptions
Julien Tachoires <julien@tachoires.me> — 2025-03-01T16:59:49Z
With the help of the new routine 'relation_options', table access methods can now define their own reloptions. These options can be set via the following commands: 1. CREATE TABLE ... USING table_am WITH (option1='value1', option2='value2'); 2. ALTER TABLE ... SET (option1 'value1', option2 'value2'); 3. ALTER TABLE ... SET ACCESS METHOD table_am OPTIONS (option1 'value1', option2 'value2'); When changing table's access method, the settings from the former table AM can be dropped (if not supported by the new table AM) via: DROP option, or, updated via: SET option 'value'. Before this commit, tables using different table AMs than heap were able to use heap's reloptions (fillfactor, toast_tuple_target, etc...). Now, this is not the case anymore: if the table AM needs to have access to settings similar to heap ones, they must explicitly define them. This work is directly derived from SadhuPrasad's patch named: v4-0001-PATCH-V4-Per-table-storage-parameters-for-TableAM.patch --- doc/src/sgml/ref/alter_table.sgml | 13 +- doc/src/sgml/ref/create_table.sgml | 3 +- src/backend/access/common/reloptions.c | 66 ++++++++- src/backend/access/heap/heapam_handler.c | 2 + src/backend/commands/foreigncmds.c | 2 +- src/backend/commands/tablecmds.c | 180 ++++++++++++++++++++--- src/backend/parser/gram.y | 9 ++ src/backend/postmaster/autovacuum.c | 18 ++- src/backend/utils/cache/relcache.c | 11 +- src/include/access/reloptions.h | 6 +- src/include/access/tableam.h | 10 ++ src/include/commands/defrem.h | 1 + 12 files changed, 286 insertions(+), 35 deletions(-) diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 11d1bc7dbe1..1b4dd023877 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -77,7 +77,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> CLUSTER ON <replaceable class="parameter">index_name</replaceable> SET WITHOUT CLUSTER SET WITHOUT OIDS - SET ACCESS METHOD { <replaceable class="parameter">new_access_method</replaceable> | DEFAULT } + SET ACCESS METHOD { <replaceable class="parameter">new_access_method</replaceable> | DEFAULT } [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] ) ] SET TABLESPACE <replaceable class="parameter">new_tablespace</replaceable> SET { LOGGED | UNLOGGED } SET ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) @@ -755,7 +755,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM </varlistentry> <varlistentry id="sql-altertable-desc-set-access-method"> - <term><literal>SET ACCESS METHOD</literal></term> + <term><literal>SET ACCESS METHOD { <replaceable class="parameter">new_access_method</replaceable> | DEFAULT } [ OPTIONS ( [ ADD | SET | DROP ] <replaceable class="parameter">option</replaceable> ['<replaceable class="parameter">value</replaceable>'] [, ... ] ) ]</literal></term> <listitem> <para> This form changes the access method of the table by rewriting it @@ -773,6 +773,15 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM causing future partitions to default to <varname>default_table_access_method</varname>. </para> + <para> + Specifying <literal>OPTIONS</literal> allows to change options for + the table when changing the table access method. + <literal>ADD</literal>, <literal>SET</literal>, and + <literal>DROP</literal> specify the action to be performed. + <literal>ADD</literal> is assumed if no operation is explicitly + specified. Option names must be unique; names and values are also + validated using the table access method's library. + </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index e5c034d724e..4420d4c83cd 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -1552,7 +1552,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM Storage parameters for indexes are documented in <xref linkend="sql-createindex"/>. The storage parameters currently - available for tables are listed below. For many of these parameters, as + available for tables are listed below. Each table may have different set of storage + parameters through different access methods. For many of these parameters, as shown, there is an additional parameter with the same name prefixed with <literal>toast.</literal>, which controls the behavior of the table's secondary <acronym>TOAST</acronym> table, if any diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 645b5c00467..8de07d3d266 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -25,6 +25,7 @@ #include "access/reloptions.h" #include "access/spgist_private.h" #include "catalog/pg_type.h" +#include "catalog/pg_am.h" #include "commands/defrem.h" #include "commands/tablespace.h" #include "nodes/makefuncs.h" @@ -34,6 +35,7 @@ #include "utils/guc.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/syscache.h" /* * Contents of pg_class.reloptions @@ -1396,7 +1398,7 @@ untransformRelOptions(Datum options) */ bytea * extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, - amoptions_function amoptions) + amoptions_function amoptions, reloptions_function reloptsfun) { bytea *options; bool isnull; @@ -1418,7 +1420,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, case RELKIND_RELATION: case RELKIND_TOASTVALUE: case RELKIND_MATVIEW: - options = heap_reloptions(classForm->relkind, datum, false); + options = table_reloptions(reloptsfun, InvalidOid, classForm->relkind, + datum, false); break; case RELKIND_PARTITIONED_TABLE: options = partitioned_table_reloptions(datum, false); @@ -2048,7 +2051,8 @@ view_reloptions(Datum reloptions, bool validate) } /* - * Parse options for heaps, views and toast tables. + * Parse options for heaps, views and toast tables. This is the implementation + * of relOptions for the access method heap. */ bytea * heap_reloptions(char relkind, Datum reloptions, bool validate) @@ -2078,6 +2082,62 @@ heap_reloptions(char relkind, Datum reloptions, bool validate) } +/* + * Parse options for tables. + * + * reloptsfun Table AM's option parser function. Can be NULL if amid is + * valid. In this case we load the new table AM and use its option + * parser function. + * amid New table AM's Oid if any. + * relkind relation kind + * reloptions options as text[] datum + * validate error flag + */ +bytea * +table_reloptions(reloptions_function reloptsfun, Oid amid, char relkind, + Datum reloptions, bool validate) +{ + /* amid and reloptsfun are mutually exclusive */ + Assert((!OidIsValid(amid) && (reloptsfun != NULL)) || \ + (OidIsValid(amid) && (reloptsfun == NULL))); + + /* Parse/validate options using reloptsfun */ + if (!OidIsValid(amid) && reloptsfun != NULL) + { + /* Assume function is strict */ + if (!PointerIsValid(DatumGetPointer(reloptions))) + return NULL; + + return reloptsfun(relkind, reloptions, validate); + } + /* Parse/validate options using the API of the new Table AM */ + else if (OidIsValid(amid) && (reloptsfun == NULL)) + { + const TableAmRoutine *routine; + HeapTuple atuple; + Form_pg_am aform; + + atuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amid)); + + if (!HeapTupleIsValid(atuple)) + elog(ERROR, "cache lookup failed for access method %u", amid); + + aform = (Form_pg_am) GETSTRUCT(atuple); + routine = GetTableAmRoutine(aform->amhandler); + ReleaseSysCache(atuple); + + if (routine->relation_options != NULL) + return routine->relation_options(relkind, reloptions, validate); + + return NULL; + } + else + { + /* Should not happen */ + return NULL; + } +} + /* * Parse options for indexes. * diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 24d3765aa20..e9a1cb4ba1e 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -24,6 +24,7 @@ #include "access/heaptoast.h" #include "access/multixact.h" #include "access/rewriteheap.h" +#include "access/reloptions.h" #include "access/syncscan.h" #include "access/tableam.h" #include "access/tsmapi.h" @@ -2701,6 +2702,7 @@ static const TableAmRoutine heapam_methods = { .index_build_range_scan = heapam_index_build_range_scan, .index_validate_scan = heapam_index_validate_scan, + .relation_options = heap_reloptions, .relation_size = table_block_relation_size, .relation_needs_toast_table = heapam_relation_needs_toast_table, .relation_toast_am = heapam_relation_toast_am, diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index c14e038d54f..9dab5dfb999 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -62,7 +62,7 @@ static void import_error_callback(void *arg); * processing, hence any validation should be done before this * conversion. */ -static Datum +Datum optionListToArray(List *options) { ArrayBuildState *astate = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 10624353b0a..d067b9a0be9 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -657,6 +657,8 @@ static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode); static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode); static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace); +static void ATExecSetAccessMethodOptions(Relation rel, List *defList, AlterTableType operation, + LOCKMODE lockmode, Oid newAccessMethodId); static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode); @@ -906,24 +908,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (!OidIsValid(ownerId)) ownerId = GetUserId(); - /* - * Parse and validate reloptions, if any. - */ - reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps, - true, false); - - switch (relkind) - { - case RELKIND_VIEW: - (void) view_reloptions(reloptions, true); - break; - case RELKIND_PARTITIONED_TABLE: - (void) partitioned_table_reloptions(reloptions, true); - break; - default: - (void) heap_reloptions(relkind, reloptions, true); - } - if (stmt->ofTypename) { AclResult aclresult; @@ -1026,6 +1010,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, accessMethodId = get_table_am_oid(default_table_access_method, false); } + /* + * Parse and validate reloptions, if any. + */ + reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps, + true, false); + switch (relkind) + { + case RELKIND_VIEW: + (void) view_reloptions(reloptions, true); + break; + case RELKIND_PARTITIONED_TABLE: + (void) partitioned_table_reloptions(reloptions, true); + break; + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + (void) table_reloptions(NULL, accessMethodId, relkind, reloptions, + true); + break; + default: + (void) heap_reloptions(relkind, reloptions, true); + } + /* * Create the relation. Inherited defaults and CHECK constraints are * passed in for immediate handling --- since they don't need parsing, @@ -5507,6 +5514,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && tab->chgAccessMethod) ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod); + + ATExecSetAccessMethodOptions(rel, (List *) cmd->def, cmd->subtype, + lockmode, tab->newAccessMethod); break; case AT_SetTableSpace: /* SET TABLESPACE */ @@ -16024,6 +16034,138 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacen tab->newTableSpace = tablespaceId; } +/* SET, ADD or DROP options in ALTER TABLE SET ACCESS METHOD */ +static void +ATExecSetAccessMethodOptions(Relation rel, List *options, AlterTableType operation, + LOCKMODE lockmode, Oid newAccessMethodId) +{ + Oid relid; + Relation pgclass; + HeapTuple tuple; + HeapTuple newtuple; + Datum datum; + bool isnull; + Datum newOptions; + Datum repl_val[Natts_pg_class]; + bool repl_null[Natts_pg_class]; + bool repl_repl[Natts_pg_class]; + List *resultOptions; + ListCell *optcell; + + pgclass = table_open(RelationRelationId, RowExclusiveLock); + + /* Fetch heap tuple */ + relid = RelationGetRelid(rel); + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + /* Get the old reloptions */ + datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull); + + if (isnull) + datum = PointerGetDatum(NULL); + + resultOptions = untransformRelOptions(datum); + + foreach(optcell, options) + { + DefElem *od = lfirst(optcell); + ListCell *cell; + + /* Search in existing options */ + foreach(cell, resultOptions) + { + DefElem *def = lfirst(cell); + + if (strcmp(def->defname, od->defname) == 0) + break; + } + + /* + * It is possible to perform multiple SET/DROP actions on the same + * option. The standard permits this, as long as the options to be + * added are unique. Note that an unspecified action is taken to be + * ADD. + */ + switch (od->defaction) + { + case DEFELEM_DROP: + if (!cell) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("option \"%s\" not found", + od->defname))); + resultOptions = list_delete_cell(resultOptions, cell); + break; + + case DEFELEM_SET: + if (!cell) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("option \"%s\" not found", + od->defname))); + lfirst(cell) = od; + break; + + case DEFELEM_ADD: + case DEFELEM_UNSPEC: + if (cell) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("option \"%s\" provided more than once", + od->defname))); + resultOptions = lappend(resultOptions, od); + break; + + default: + elog(ERROR, "unrecognized action %d on option \"%s\"", + (int) od->defaction, od->defname); + break; + } + } + + newOptions = optionListToArray(resultOptions); + + /* + * If the new table access method was not explicitly defined, then use the + * default one. + */ + if (!OidIsValid(newAccessMethodId)) + newAccessMethodId = get_table_am_oid(default_table_access_method, false); + + /* Validate new options via the new Table Access Method API */ + (void) table_reloptions(NULL, newAccessMethodId, rel->rd_rel->relkind, + newOptions, true); + + /* Initialize buffers for new tuple values */ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + + if (newOptions != (Datum) 0) + repl_val[Anum_pg_class_reloptions - 1] = newOptions; + else + repl_null[Anum_pg_class_reloptions - 1] = true; + + repl_repl[Anum_pg_class_reloptions - 1] = true; + + /* Everything looks good - update the tuple */ + newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass), + repl_val, repl_null, repl_repl); + + CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple); + + InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), + InvalidOid); + + ReleaseSysCache(tuple); + + table_close(pgclass, RowExclusiveLock); + + heap_freetuple(newtuple); +} + /* * Set, reset, or replace reloptions. */ @@ -16081,7 +16223,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, { case RELKIND_RELATION: case RELKIND_MATVIEW: - (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true); + rel->rd_tableam->relation_options(rel->rd_rel->relkind, newOptions, true); break; case RELKIND_PARTITIONED_TABLE: (void) partitioned_table_reloptions(newOptions, true); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0fc502a3a40..16ac2ea8260 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -2919,6 +2919,15 @@ alter_table_cmd: n->name = $4; $$ = (Node *) n; } + /* ALTER TABLE <name> SET ACCESS METHOD <amname> [OPTIONS]*/ + | SET ACCESS METHOD name alter_generic_options + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetAccessMethod; + n->name = $4; + n->def = (Node *) $5; + $$ = (Node *)n; + } /* ALTER TABLE <name> SET TABLESPACE <tablespacename> */ | SET TABLESPACE name { diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 2513a8ef8a6..aff14a71585 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -332,6 +332,7 @@ static void FreeWorkerInfo(int code, Datum arg); static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map, TupleDesc pg_class_desc, + reloptions_function reloptions, int effective_multixact_freeze_max_age); static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts, Form_pg_class classForm, @@ -346,7 +347,7 @@ static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts, static void autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy); static AutoVacOpts *extract_autovac_opts(HeapTuple tup, - TupleDesc pg_class_desc); + TupleDesc pg_class_desc, reloptions_function reloptions); static void perform_work_item(AutoVacuumWorkItem *workitem); static void autovac_report_activity(autovac_table *tab); static void autovac_report_workitem(AutoVacuumWorkItem *workitem, @@ -2033,7 +2034,8 @@ do_autovacuum(void) } /* Fetch reloptions and the pgstat entry for this table */ - relopts = extract_autovac_opts(tuple, pg_class_desc); + relopts = extract_autovac_opts(tuple, pg_class_desc, + classRel->rd_tableam->relation_options); tabentry = pgstat_fetch_stat_tabentry_ext(classForm->relisshared, relid); @@ -2106,7 +2108,8 @@ do_autovacuum(void) * fetch reloptions -- if this toast table does not have them, try the * main rel */ - relopts = extract_autovac_opts(tuple, pg_class_desc); + relopts = extract_autovac_opts(tuple, pg_class_desc, + classRel->rd_tableam->relation_options); if (relopts == NULL) { av_relation *hentry; @@ -2364,6 +2367,7 @@ do_autovacuum(void) */ MemoryContextSwitchTo(AutovacMemCxt); tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc, + classRel->rd_tableam->relation_options, effective_multixact_freeze_max_age); if (tab == NULL) { @@ -2689,7 +2693,8 @@ deleted2: * be a risk; fortunately, it doesn't. */ static AutoVacOpts * -extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) +extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc, + reloptions_function reloptions) { bytea *relopts; AutoVacOpts *av; @@ -2698,7 +2703,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW || ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE); - relopts = extractRelOptions(tup, pg_class_desc, NULL); + relopts = extractRelOptions(tup, pg_class_desc, NULL, reloptions); if (relopts == NULL) return NULL; @@ -2721,6 +2726,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc) static autovac_table * table_recheck_autovac(Oid relid, HTAB *table_toast_map, TupleDesc pg_class_desc, + reloptions_function reloptions, int effective_multixact_freeze_max_age) { Form_pg_class classForm; @@ -2741,7 +2747,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, * Get the applicable reloptions. If it is a TOAST table, try to get the * main table reloptions if the toast table itself doesn't have. */ - avopts = extract_autovac_opts(classTup, pg_class_desc); + avopts = extract_autovac_opts(classTup, pg_class_desc, reloptions); if (classForm->relkind == RELKIND_TOASTVALUE && avopts == NULL && table_toast_map != NULL) { diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 9f54a9e72b7..8771e8d9846 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -469,6 +469,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple) { bytea *options; amoptions_function amoptsfn; + reloptions_function reloptsfn; relation->rd_options = NULL; @@ -480,13 +481,18 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple) { case RELKIND_RELATION: case RELKIND_TOASTVALUE: - case RELKIND_VIEW: case RELKIND_MATVIEW: + reloptsfn = relation->rd_tableam->relation_options; + amoptsfn = NULL; + break; + case RELKIND_VIEW: case RELKIND_PARTITIONED_TABLE: + reloptsfn = NULL; amoptsfn = NULL; break; case RELKIND_INDEX: case RELKIND_PARTITIONED_INDEX: + reloptsfn = NULL; amoptsfn = relation->rd_indam->amoptions; break; default: @@ -498,7 +504,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple) * we might not have any other for pg_class yet (consider executing this * code for pg_class itself) */ - options = extractRelOptions(tuple, GetPgClassDescriptor(), amoptsfn); + options = extractRelOptions(tuple, GetPgClassDescriptor(), + amoptsfn, reloptsfn); /* * Copy parsed data into CacheMemoryContext. To guard against the diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index dfbb4c85460..37f51d0f1c2 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -21,6 +21,7 @@ #include "access/amapi.h" #include "access/htup.h" +#include "access/tableam.h" #include "access/tupdesc.h" #include "nodes/pg_list.h" #include "storage/lock.h" @@ -237,7 +238,8 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList, bool acceptOidsOff, bool isReset); extern List *untransformRelOptions(Datum options); extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, - amoptions_function amoptions); + amoptions_function amoptions, + reloptions_function reloptsfun); extern void *build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, @@ -251,6 +253,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate, extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate); extern bytea *view_reloptions(Datum reloptions, bool validate); extern bytea *partitioned_table_reloptions(Datum reloptions, bool validate); +extern bytea *table_reloptions(reloptions_function reloptsfun, Oid amid, char relkind, + Datum reloptions, bool validate); extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate); extern bytea *attribute_reloptions(Datum reloptions, bool validate); diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h index b8cb1e744ad..f7ec0ed57bc 100644 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@ -276,6 +276,14 @@ typedef void (*IndexBuildCallback) (Relation index, bool tupleIsAlive, void *state); +/* + * Callback in charge of parsing and validating the table reloptions. + * It returns parsed options in bytea format. + */ +typedef bytea *(*reloptions_function) (char relkind, + Datum reloptions, + bool validate); + /* * API struct for a table AM. Note this must be allocated in a * server-lifetime manner, typically as a static const struct, which then gets @@ -715,6 +723,8 @@ typedef struct TableAmRoutine * ------------------------------------------------------------------------ */ + reloptions_function relation_options; + /* * See table_relation_size(). * diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index dd22b5efdfd..8e42f394107 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -136,6 +136,7 @@ extern ObjectAddress AlterUserMapping(AlterUserMappingStmt *stmt); extern Oid RemoveUserMapping(DropUserMappingStmt *stmt); extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid); extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt); +extern Datum optionListToArray(List *options); extern Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, -- 2.39.5 --jr4jupeetksdtvhn Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v3-0002-Add-the-dummy_table_am-test-module.patch"