Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 36c5ab5

Browse files
author
Commitfest Bot
committed
[CF 5631] v4 - Allow table AMs to define their own reloptions
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5631 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/20250526110619.oepaf5dbzq3hjuij@poseidon.home.virt Author(s): Julien Tachoires
2 parents b006bcd + 82ac4c4 commit 36c5ab5

File tree

22 files changed

+1234
-35
lines changed

22 files changed

+1234
-35
lines changed

doc/src/sgml/ref/alter_table.sgml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
7777
CLUSTER ON <replaceable class="parameter">index_name</replaceable>
7878
SET WITHOUT CLUSTER
7979
SET WITHOUT OIDS
80-
SET ACCESS METHOD { <replaceable class="parameter">new_access_method</replaceable> | DEFAULT }
80+
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>'] [, ... ] ) ]
8181
SET TABLESPACE <replaceable class="parameter">new_tablespace</replaceable>
8282
SET { LOGGED | UNLOGGED }
8383
SET ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] )
@@ -758,7 +758,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
758758
</varlistentry>
759759

760760
<varlistentry id="sql-altertable-desc-set-access-method">
761-
<term><literal>SET ACCESS METHOD</literal></term>
761+
<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>
762762
<listitem>
763763
<para>
764764
This form changes the access method of the table by rewriting it
@@ -776,6 +776,15 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
776776
causing future partitions to default to
777777
<varname>default_table_access_method</varname>.
778778
</para>
779+
<para>
780+
Specifying <literal>OPTIONS</literal> allows to change options for
781+
the table when changing the table access method.
782+
<literal>ADD</literal>, <literal>SET</literal>, and
783+
<literal>DROP</literal> specify the action to be performed.
784+
<literal>ADD</literal> is assumed if no operation is explicitly
785+
specified. Option names must be unique; names and values are also
786+
validated using the table access method's library.
787+
</para>
779788
</listitem>
780789
</varlistentry>
781790

doc/src/sgml/ref/create_table.sgml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1552,7 +1552,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
15521552
Storage parameters for
15531553
indexes are documented in <xref linkend="sql-createindex"/>.
15541554
The storage parameters currently
1555-
available for tables are listed below. For many of these parameters, as
1555+
available for tables are listed below. Each table may have different set of storage
1556+
parameters through different access methods. For many of these parameters, as
15561557
shown, there is an additional parameter with the same name prefixed with
15571558
<literal>toast.</literal>, which controls the behavior of the
15581559
table's secondary <acronym>TOAST</acronym> table, if any

src/backend/access/common/reloptions.c

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "access/reloptions.h"
2626
#include "access/spgist_private.h"
2727
#include "catalog/pg_type.h"
28+
#include "catalog/pg_am.h"
2829
#include "commands/defrem.h"
2930
#include "commands/tablespace.h"
3031
#include "nodes/makefuncs.h"
@@ -34,6 +35,7 @@
3435
#include "utils/guc.h"
3536
#include "utils/memutils.h"
3637
#include "utils/rel.h"
38+
#include "utils/syscache.h"
3739

3840
/*
3941
* Contents of pg_class.reloptions
@@ -1388,7 +1390,7 @@ untransformRelOptions(Datum options)
13881390
*/
13891391
bytea *
13901392
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
1391-
amoptions_function amoptions)
1393+
amoptions_function amoptions, reloptions_function reloptsfun)
13921394
{
13931395
bytea *options;
13941396
bool isnull;
@@ -1410,7 +1412,8 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
14101412
case RELKIND_RELATION:
14111413
case RELKIND_TOASTVALUE:
14121414
case RELKIND_MATVIEW:
1413-
options = heap_reloptions(classForm->relkind, datum, false);
1415+
options = table_reloptions(reloptsfun, InvalidOid, classForm->relkind,
1416+
datum, false);
14141417
break;
14151418
case RELKIND_PARTITIONED_TABLE:
14161419
options = partitioned_table_reloptions(datum, false);
@@ -2040,7 +2043,8 @@ view_reloptions(Datum reloptions, bool validate)
20402043
}
20412044

20422045
/*
2043-
* Parse options for heaps, views and toast tables.
2046+
* Parse options for heaps, views and toast tables. This is the implementation
2047+
* of relOptions for the access method heap.
20442048
*/
20452049
bytea *
20462050
heap_reloptions(char relkind, Datum reloptions, bool validate)
@@ -2070,6 +2074,62 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
20702074
}
20712075

20722076

2077+
/*
2078+
* Parse options for tables.
2079+
*
2080+
* reloptsfun Table AM's option parser function. Can be NULL if amid is
2081+
* valid. In this case we load the new table AM and use its option
2082+
* parser function.
2083+
* amid New table AM's Oid if any.
2084+
* relkind relation kind
2085+
* reloptions options as text[] datum
2086+
* validate error flag
2087+
*/
2088+
bytea *
2089+
table_reloptions(reloptions_function reloptsfun, Oid amid, char relkind,
2090+
Datum reloptions, bool validate)
2091+
{
2092+
/* amid and reloptsfun are mutually exclusive */
2093+
Assert((!OidIsValid(amid) && (reloptsfun != NULL)) || \
2094+
(OidIsValid(amid) && (reloptsfun == NULL)));
2095+
2096+
/* Parse/validate options using reloptsfun */
2097+
if (!OidIsValid(amid) && reloptsfun != NULL)
2098+
{
2099+
/* Assume function is strict */
2100+
if (!PointerIsValid(DatumGetPointer(reloptions)))
2101+
return NULL;
2102+
2103+
return reloptsfun(relkind, reloptions, validate);
2104+
}
2105+
/* Parse/validate options using the API of the new Table AM */
2106+
else if (OidIsValid(amid) && (reloptsfun == NULL))
2107+
{
2108+
const TableAmRoutine *routine;
2109+
HeapTuple atuple;
2110+
Form_pg_am aform;
2111+
2112+
atuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amid));
2113+
2114+
if (!HeapTupleIsValid(atuple))
2115+
elog(ERROR, "cache lookup failed for access method %u", amid);
2116+
2117+
aform = (Form_pg_am) GETSTRUCT(atuple);
2118+
routine = GetTableAmRoutine(aform->amhandler);
2119+
ReleaseSysCache(atuple);
2120+
2121+
if (routine->relation_options != NULL)
2122+
return routine->relation_options(relkind, reloptions, validate);
2123+
2124+
return NULL;
2125+
}
2126+
else
2127+
{
2128+
/* Should not happen */
2129+
return NULL;
2130+
}
2131+
}
2132+
20732133
/*
20742134
* Parse options for indexes.
20752135
*

src/backend/access/heap/heapam_handler.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "access/heaptoast.h"
2525
#include "access/multixact.h"
2626
#include "access/rewriteheap.h"
27+
#include "access/reloptions.h"
2728
#include "access/syncscan.h"
2829
#include "access/tableam.h"
2930
#include "access/tsmapi.h"
@@ -2659,6 +2660,7 @@ static const TableAmRoutine heapam_methods = {
26592660
.index_build_range_scan = heapam_index_build_range_scan,
26602661
.index_validate_scan = heapam_index_validate_scan,
26612662

2663+
.relation_options = heap_reloptions,
26622664
.relation_size = table_block_relation_size,
26632665
.relation_needs_toast_table = heapam_relation_needs_toast_table,
26642666
.relation_toast_am = heapam_relation_toast_am,

src/backend/commands/foreigncmds.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ static void import_error_callback(void *arg);
6262
* processing, hence any validation should be done before this
6363
* conversion.
6464
*/
65-
static Datum
65+
Datum
6666
optionListToArray(List *options)
6767
{
6868
ArrayBuildState *astate = NULL;

src/backend/commands/tablecmds.c

Lines changed: 161 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,8 @@ static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
677677
const char *tablespacename, LOCKMODE lockmode);
678678
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
679679
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
680+
static void ATExecSetAccessMethodOptions(Relation rel, List *defList, AlterTableType operation,
681+
LOCKMODE lockmode, Oid newAccessMethodId);
680682
static void ATExecSetRelOptions(Relation rel, List *defList,
681683
AlterTableType operation,
682684
LOCKMODE lockmode);
@@ -926,24 +928,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
926928
if (!OidIsValid(ownerId))
927929
ownerId = GetUserId();
928930

929-
/*
930-
* Parse and validate reloptions, if any.
931-
*/
932-
reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933-
true, false);
934-
935-
switch (relkind)
936-
{
937-
case RELKIND_VIEW:
938-
(void) view_reloptions(reloptions, true);
939-
break;
940-
case RELKIND_PARTITIONED_TABLE:
941-
(void) partitioned_table_reloptions(reloptions, true);
942-
break;
943-
default:
944-
(void) heap_reloptions(relkind, reloptions, true);
945-
}
946-
947931
if (stmt->ofTypename)
948932
{
949933
AclResult aclresult;
@@ -1046,6 +1030,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
10461030
accessMethodId = get_table_am_oid(default_table_access_method, false);
10471031
}
10481032

1033+
/*
1034+
* Parse and validate reloptions, if any.
1035+
*/
1036+
reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
1037+
true, false);
1038+
switch (relkind)
1039+
{
1040+
case RELKIND_VIEW:
1041+
(void) view_reloptions(reloptions, true);
1042+
break;
1043+
case RELKIND_PARTITIONED_TABLE:
1044+
(void) partitioned_table_reloptions(reloptions, true);
1045+
break;
1046+
case RELKIND_RELATION:
1047+
case RELKIND_TOASTVALUE:
1048+
case RELKIND_MATVIEW:
1049+
(void) table_reloptions(NULL, accessMethodId, relkind, reloptions,
1050+
true);
1051+
break;
1052+
default:
1053+
(void) heap_reloptions(relkind, reloptions, true);
1054+
}
1055+
10491056
/*
10501057
* Create the relation. Inherited defaults and CHECK constraints are
10511058
* passed in for immediate handling --- since they don't need parsing,
@@ -5527,6 +5534,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
55275534
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
55285535
tab->chgAccessMethod)
55295536
ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5537+
5538+
ATExecSetAccessMethodOptions(rel, (List *) cmd->def, cmd->subtype,
5539+
lockmode, tab->newAccessMethod);
55305540
break;
55315541
case AT_SetTableSpace: /* SET TABLESPACE */
55325542

@@ -16550,6 +16560,138 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacen
1655016560
tab->newTableSpace = tablespaceId;
1655116561
}
1655216562

16563+
/* SET, ADD or DROP options in ALTER TABLE SET ACCESS METHOD */
16564+
static void
16565+
ATExecSetAccessMethodOptions(Relation rel, List *options, AlterTableType operation,
16566+
LOCKMODE lockmode, Oid newAccessMethodId)
16567+
{
16568+
Oid relid;
16569+
Relation pgclass;
16570+
HeapTuple tuple;
16571+
HeapTuple newtuple;
16572+
Datum datum;
16573+
bool isnull;
16574+
Datum newOptions;
16575+
Datum repl_val[Natts_pg_class];
16576+
bool repl_null[Natts_pg_class];
16577+
bool repl_repl[Natts_pg_class];
16578+
List *resultOptions;
16579+
ListCell *optcell;
16580+
16581+
pgclass = table_open(RelationRelationId, RowExclusiveLock);
16582+
16583+
/* Fetch heap tuple */
16584+
relid = RelationGetRelid(rel);
16585+
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
16586+
if (!HeapTupleIsValid(tuple))
16587+
elog(ERROR, "cache lookup failed for relation %u", relid);
16588+
16589+
/* Get the old reloptions */
16590+
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull);
16591+
16592+
if (isnull)
16593+
datum = PointerGetDatum(NULL);
16594+
16595+
resultOptions = untransformRelOptions(datum);
16596+
16597+
foreach(optcell, options)
16598+
{
16599+
DefElem *od = lfirst(optcell);
16600+
ListCell *cell;
16601+
16602+
/* Search in existing options */
16603+
foreach(cell, resultOptions)
16604+
{
16605+
DefElem *def = lfirst(cell);
16606+
16607+
if (strcmp(def->defname, od->defname) == 0)
16608+
break;
16609+
}
16610+
16611+
/*
16612+
* It is possible to perform multiple SET/DROP actions on the same
16613+
* option. The standard permits this, as long as the options to be
16614+
* added are unique. Note that an unspecified action is taken to be
16615+
* ADD.
16616+
*/
16617+
switch (od->defaction)
16618+
{
16619+
case DEFELEM_DROP:
16620+
if (!cell)
16621+
ereport(ERROR,
16622+
(errcode(ERRCODE_UNDEFINED_OBJECT),
16623+
errmsg("option \"%s\" not found",
16624+
od->defname)));
16625+
resultOptions = list_delete_cell(resultOptions, cell);
16626+
break;
16627+
16628+
case DEFELEM_SET:
16629+
if (!cell)
16630+
ereport(ERROR,
16631+
(errcode(ERRCODE_UNDEFINED_OBJECT),
16632+
errmsg("option \"%s\" not found",
16633+
od->defname)));
16634+
lfirst(cell) = od;
16635+
break;
16636+
16637+
case DEFELEM_ADD:
16638+
case DEFELEM_UNSPEC:
16639+
if (cell)
16640+
ereport(ERROR,
16641+
(errcode(ERRCODE_DUPLICATE_OBJECT),
16642+
errmsg("option \"%s\" provided more than once",
16643+
od->defname)));
16644+
resultOptions = lappend(resultOptions, od);
16645+
break;
16646+
16647+
default:
16648+
elog(ERROR, "unrecognized action %d on option \"%s\"",
16649+
(int) od->defaction, od->defname);
16650+
break;
16651+
}
16652+
}
16653+
16654+
newOptions = optionListToArray(resultOptions);
16655+
16656+
/*
16657+
* If the new table access method was not explicitly defined, then use the
16658+
* default one.
16659+
*/
16660+
if (!OidIsValid(newAccessMethodId))
16661+
newAccessMethodId = get_table_am_oid(default_table_access_method, false);
16662+
16663+
/* Validate new options via the new Table Access Method API */
16664+
(void) table_reloptions(NULL, newAccessMethodId, rel->rd_rel->relkind,
16665+
newOptions, true);
16666+
16667+
/* Initialize buffers for new tuple values */
16668+
memset(repl_val, 0, sizeof(repl_val));
16669+
memset(repl_null, false, sizeof(repl_null));
16670+
memset(repl_repl, false, sizeof(repl_repl));
16671+
16672+
if (newOptions != (Datum) 0)
16673+
repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16674+
else
16675+
repl_null[Anum_pg_class_reloptions - 1] = true;
16676+
16677+
repl_repl[Anum_pg_class_reloptions - 1] = true;
16678+
16679+
/* Everything looks good - update the tuple */
16680+
newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16681+
repl_val, repl_null, repl_repl);
16682+
16683+
CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16684+
16685+
InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel),
16686+
InvalidOid);
16687+
16688+
ReleaseSysCache(tuple);
16689+
16690+
table_close(pgclass, RowExclusiveLock);
16691+
16692+
heap_freetuple(newtuple);
16693+
}
16694+
1655316695
/*
1655416696
* Set, reset, or replace reloptions.
1655516697
*/
@@ -16607,7 +16749,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
1660716749
{
1660816750
case RELKIND_RELATION:
1660916751
case RELKIND_MATVIEW:
16610-
(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16752+
rel->rd_tableam->relation_options(rel->rd_rel->relkind, newOptions, true);
1661116753
break;
1661216754
case RELKIND_PARTITIONED_TABLE:
1661316755
(void) partitioned_table_reloptions(newOptions, true);

0 commit comments

Comments
 (0)