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

Commit 6d1d8c0

Browse files
Julien TachoiresCommitfest Bot
Julien Tachoires
authored and
Commitfest Bot
committed
Allow table AMs to define their own reloptions
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
1 parent b006bcd commit 6d1d8c0

File tree

12 files changed

+286
-35
lines changed

12 files changed

+286
-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)