Allow specifying an access method for partitioned tables

It's now possible to specify a table access method via
CREATE TABLE ... USING for a partitioned table, as well change it with
ALTER TABLE ... SET ACCESS METHOD.  Specifying an AM for a partitioned
table lets the value be used for all future partitions created under it,
closely mirroring the behavior of the TABLESPACE option for partitioned
tables.  Existing partitions are not modified.

For a partitioned table with no AM specified, any new partitions are
created with the default_table_access_method.

Also add ALTER TABLE ... SET ACCESS METHOD DEFAULT, which reverts to the
original state of using the default for new partitions.

The relcache of partitioned tables is not changed: rd_tableam is not
set, even if a partitioned table has a relam set.

Author: Justin Pryzby <pryzby@telsasoft.com>
Author: Soumyadeep Chakraborty <soumyadeep2007@gmail.com>
Author: Michaël Paquier <michael@paquier.xyz>
Reviewed-by: The authors themselves
Discussion: https://postgr.es/m/CAE-ML+9zM4wJCGCBGv01k96qQ3gFv4WFcFy=zqPHKeaEFwwv6A@mail.gmail.com
Discussion: https://postgr.es/m/20210308010707.GA29832%40telsasoft.com
This commit is contained in:
Alvaro Herrera 2024-03-25 16:30:36 +01:00
parent b8528fe026
commit 374c7a2290
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
12 changed files with 469 additions and 49 deletions

View File

@ -1988,9 +1988,13 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
(references <link linkend="catalog-pg-am"><structname>pg_am</structname></link>.<structfield>oid</structfield>)
</para>
<para>
If this is a table or an index, the access method used (heap,
B-tree, hash, etc.); otherwise zero (zero occurs for sequences,
as well as relations without storage, such as views)
The access method used to access this table or index.
Not meaningful if the relation is a sequence or
has no on-disk file,
except for partitioned tables, where, if set, it takes
precedence over <varname>default_table_access_method</varname>
when determining the access method to use for partitions created
when one is not specified in the creation command.
</para></entry>
</row>

View File

@ -732,10 +732,20 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<term><literal>SET ACCESS METHOD</literal></term>
<listitem>
<para>
This form changes the access method of the table by rewriting it. See
<xref linkend="tableam"/> for more information. Writing
<literal>DEFAULT</literal> changes the access method of the table
to <xref linkend="guc-default-table-access-method"/>.
This form changes the access method of the table by rewriting it
using the indicated access method; specifying
<literal>DEFAULT</literal> selects the access method set as the
<xref linkend="guc-default-table-access-method"/> configuration
parameter.
See <xref linkend="tableam"/> for more information.
</para>
<para>
When applied to a partitioned table, there is no data to rewrite,
but partitions created afterwards will default to the given access
method unless overridden by a <literal>USING</literal> clause.
Specifying <varname>DEFAULT</varname> removes a previous value,
causing future partitions to default to
<varname>default_table_access_method</varname>.
</para>
</listitem>
</varlistentry>

View File

@ -1365,6 +1365,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
method is chosen for the new table. See <xref
linkend="guc-default-table-access-method"/> for more information.
</para>
<para>
When creating a partition, the table access method is the access method
of its partitioned table, if set.
</para>
</listitem>
</varlistentry>

View File

@ -184,7 +184,9 @@ typedef struct AlteredTableInfo
List *afterStmts; /* List of utility command parsetrees */
bool verify_new_notnull; /* T if we should recheck NOT NULL */
int rewrite; /* Reason for forced rewrite, if any */
Oid newAccessMethod; /* new access method; 0 means no change */
bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
Oid newAccessMethod; /* new access method; 0 means no change,
* if above is true */
Oid newTableSpace; /* new tablespace; 0 means no change */
bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
char newrelpersistence; /* if above is true */
@ -595,6 +597,7 @@ static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
LOCKMODE lockmode);
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
static bool ATPrepChangePersistence(Relation rel, bool toLogged);
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
const char *tablespacename, LOCKMODE lockmode);
@ -709,7 +712,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Oid ofTypeId;
ObjectAddress address;
LOCKMODE parentLockmode;
const char *accessMethod = NULL;
Oid accessMethodId = InvalidOid;
/*
@ -954,24 +956,22 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
}
/*
* If the statement hasn't specified an access method, but we're defining
* a type of relation that needs one, use the default.
* Select access method to use: an explicitly indicated one, or (in the
* case of a partitioned table) the parent's, if it has one.
*/
if (stmt->accessMethod != NULL)
accessMethodId = get_table_am_oid(stmt->accessMethod, false);
else if (stmt->partbound)
{
accessMethod = stmt->accessMethod;
if (partitioned)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("specifying a table access method is not supported on a partitioned table")));
Assert(list_length(inheritOids) == 1);
accessMethodId = get_rel_relam(linitial_oid(inheritOids));
}
else if (RELKIND_HAS_TABLE_AM(relkind))
accessMethod = default_table_access_method;
else
accessMethodId = InvalidOid;
/* look up the access method, verify it is for a table */
if (accessMethod != NULL)
accessMethodId = get_table_am_oid(accessMethod, false);
/* still nothing? use the default */
if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
accessMethodId = get_table_am_oid(default_table_access_method, false);
/*
* Create the relation. Inherited defaults and constraints are passed in
@ -5047,14 +5047,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetAccessMethod: /* SET ACCESS METHOD */
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
/* partitioned tables don't have an access method */
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change access method of a partitioned table")));
/* check if another access method change was already requested */
if (OidIsValid(tab->newAccessMethod))
if (tab->chgAccessMethod)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
@ -5408,7 +5402,14 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
/* nothing to do here, oid columns don't exist anymore */
break;
case AT_SetAccessMethod: /* SET ACCESS METHOD */
/* handled specially in Phase 3 */
/*
* Only do this for partitioned tables, for which this is just a
* catalog change. Tables with storage are handled by Phase 3.
*/
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
tab->chgAccessMethod)
ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
break;
case AT_SetTableSpace: /* SET TABLESPACE */
@ -5814,7 +5815,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
* Select destination access method (same as original unless user
* requested a change)
*/
if (OidIsValid(tab->newAccessMethod))
if (tab->chgAccessMethod)
NewAccessMethod = tab->newAccessMethod;
else
NewAccessMethod = OldHeap->rd_rel->relam;
@ -6402,6 +6403,7 @@ ATGetQueueEntry(List **wqueue, Relation rel)
tab->relkind = rel->rd_rel->relkind;
tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
tab->newAccessMethod = InvalidOid;
tab->chgAccessMethod = false;
tab->newTableSpace = InvalidOid;
tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
tab->chgPersistence = false;
@ -15343,25 +15345,128 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
/*
* Preparation phase for SET ACCESS METHOD
*
* Check that access method exists. If it is the same as the table's current
* access method, it is a no-op. Otherwise, a table rewrite is necessary.
* If amname is NULL, select default_table_access_method as access method.
* Check that the access method exists and determine whether a change is
* actually needed.
*/
static void
ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
{
Oid amoid;
/* Check that the table access method exists */
amoid = get_table_am_oid(amname ? amname : default_table_access_method,
false);
/*
* Look up the access method name and check that it differs from the
* table's current AM. If DEFAULT was specified for a partitioned table
* (amname is NULL), set it to InvalidOid to reset the catalogued AM.
*/
if (amname != NULL)
amoid = get_table_am_oid(amname, false);
else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
amoid = InvalidOid;
else
amoid = get_table_am_oid(default_table_access_method, false);
/* if it's a match, phase 3 doesn't need to do anything */
if (rel->rd_rel->relam == amoid)
return;
/* Save info for Phase 3 to do the real work */
tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
tab->newAccessMethod = amoid;
tab->chgAccessMethod = true;
}
/*
* Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
* storage that have an interest in preserving AM.
*
* Since these have no storage, setting the access method is a catalog only
* operation.
*/
static void
ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
{
Relation pg_class;
Oid oldAccessMethodId;
HeapTuple tuple;
Form_pg_class rd_rel;
Oid reloid = RelationGetRelid(rel);
/*
* Shouldn't be called on relations having storage; these are processed in
* phase 3.
*/
Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
/* Get a modifiable copy of the relation's pg_class row. */
pg_class = table_open(RelationRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", reloid);
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
/* Update the pg_class row. */
oldAccessMethodId = rd_rel->relam;
rd_rel->relam = newAccessMethodId;
/* Leave if no update required */
if (rd_rel->relam == oldAccessMethodId)
{
heap_freetuple(tuple);
table_close(pg_class, RowExclusiveLock);
return;
}
CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
/*
* Update the dependency on the new access method. No dependency is added
* if the new access method is InvalidOid (default case). Be very careful
* that this has to compare the previous value stored in pg_class with the
* new one.
*/
if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
{
ObjectAddress relobj,
referenced;
/*
* New access method is defined and there was no dependency
* previously, so record a new one.
*/
ObjectAddressSet(relobj, RelationRelationId, reloid);
ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
}
else if (OidIsValid(oldAccessMethodId) &&
!OidIsValid(rd_rel->relam))
{
/*
* There was an access method defined, and no new one, so just remove
* the existing dependency.
*/
deleteDependencyRecordsForClass(RelationRelationId, reloid,
AccessMethodRelationId,
DEPENDENCY_NORMAL);
}
else
{
Assert(OidIsValid(oldAccessMethodId) &&
OidIsValid(rd_rel->relam));
/* Both are valid, so update the dependency */
changeDependencyFor(RelationRelationId, reloid,
AccessMethodRelationId,
oldAccessMethodId, rd_rel->relam);
}
/* make the relam and dependency changes visible */
CommandCounterIncrement();
InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
heap_freetuple(tuple);
table_close(pg_class, RowExclusiveLock);
}
/*

View File

@ -2069,6 +2069,28 @@ get_rel_persistence(Oid relid)
return result;
}
/*
* get_rel_relam
*
* Returns the relam associated with a given relation.
*/
Oid
get_rel_relam(Oid relid)
{
HeapTuple tp;
Form_pg_class reltup;
Oid result;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for relation %u", relid);
reltup = (Form_pg_class) GETSTRUCT(tp);
result = reltup->relam;
ReleaseSysCache(tp);
return result;
}
/* ---------- TRANSFORM CACHE ---------- */

View File

@ -1208,6 +1208,13 @@ retry:
else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) ||
relation->rd_rel->relkind == RELKIND_SEQUENCE)
RelationInitTableAccessMethod(relation);
else if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
/*
* Do nothing: access methods are a setting that partitions can
* inherit.
*/
}
else
Assert(relation->rd_rel->relam == InvalidOid);

View File

@ -16656,7 +16656,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
tablespace = tbinfo->reltablespace;
if (RELKIND_HAS_TABLE_AM(tbinfo->relkind))
if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
tableam = tbinfo->amname;
ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,

View File

@ -4587,6 +4587,41 @@ my %tests = (
no_table_access_method => 1,
only_dump_measurement => 1,
},
},
# CREATE TABLE with partitioned table and various AMs. One
# partition uses the same default as the parent, and a second
# uses its own AM.
'CREATE TABLE regress_pg_dump_table_part' => {
create_order => 19,
create_sql => '
CREATE TABLE dump_test.regress_pg_dump_table_am_parent (id int) PARTITION BY LIST (id);
ALTER TABLE dump_test.regress_pg_dump_table_am_parent SET ACCESS METHOD regress_table_am;
CREATE TABLE dump_test.regress_pg_dump_table_am_child_1
PARTITION OF dump_test.regress_pg_dump_table_am_parent FOR VALUES IN (1) USING heap;
CREATE TABLE dump_test.regress_pg_dump_table_am_child_2
PARTITION OF dump_test.regress_pg_dump_table_am_parent FOR VALUES IN (2);',
regexp => qr/^
\QSET default_table_access_method = regress_table_am;\E
(\n(?!SET[^;]+;)[^\n]*)*
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_parent (\E
(.*\n)*
\QSET default_table_access_method = heap;\E
(\n(?!SET[^;]+;)[^\n]*)*
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_child_1 (\E
(.*\n)*
\QSET default_table_access_method = regress_table_am;\E
(\n(?!SET[^;]+;)[^\n]*)*
\n\QCREATE TABLE dump_test.regress_pg_dump_table_am_child_2 (\E
(.*\n)*/xm,
like => {
%full_runs, %dump_test_schema_runs, section_pre_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
no_table_access_method => 1,
only_dump_measurement => 1,
},
});
#########################################

View File

@ -219,7 +219,9 @@ MAKE_SYSCACHE(RELNAMENSP, pg_class_relname_nsp_index, 128);
/*
* Relation kinds with a table access method (rd_tableam). Although sequences
* use the heap table AM, they are enough of a special case in most uses that
* they are not included here.
* they are not included here. Likewise, partitioned tables can have an access
* method defined so that their partitions can inherit it, but they do not set
* rd_tableam; hence, this is handled specially outside of this macro.
*/
#define RELKIND_HAS_TABLE_AM(relkind) \
((relkind) == RELKIND_RELATION || \

View File

@ -139,6 +139,7 @@ extern char get_rel_relkind(Oid relid);
extern bool get_rel_relispartition(Oid relid);
extern Oid get_rel_tablespace(Oid relid);
extern char get_rel_persistence(Oid relid);
extern Oid get_rel_relam(Oid relid);
extern Oid get_transform_fromsql(Oid typid, Oid langid, List *trftypes);
extern Oid get_transform_tosql(Oid typid, Oid langid, List *trftypes);
extern bool get_typisdefined(Oid typid);

View File

@ -176,9 +176,16 @@ SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1;
1
(1 row)
-- CREATE TABLE .. PARTITION BY doesn't not support USING
-- CREATE TABLE .. PARTITION BY supports USING.
CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a) USING heap2;
ERROR: specifying a table access method is not supported on a partitioned table
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'tableam_parted_heap2' AND a.oid = c.relam;
amname
--------
heap2
(1 row)
DROP TABLE tableam_parted_heap2;
CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a);
-- new partitions will inherit from the current default, rather the partition root
SET default_table_access_method = 'heap';
@ -336,12 +343,151 @@ ALTER MATERIALIZED VIEW heapmv SET ACCESS METHOD heap, SET ACCESS METHOD heap2;
ERROR: cannot have multiple SET ACCESS METHOD subcommands
DROP MATERIALIZED VIEW heapmv;
DROP TABLE heaptable;
-- No support for partitioned tables.
CREATE TABLE am_partitioned(x INT, y INT)
PARTITION BY hash (x);
-- Partition hierarchies with access methods
BEGIN;
SET LOCAL default_table_access_method = 'heap';
CREATE TABLE am_partitioned(x INT, y INT) PARTITION BY hash (x);
-- pg_class.relam is 0, no dependency recorded between the AM and the
-- partitioned table.
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
0
(1 row)
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as refobj
FROM pg_depend, pg_am
WHERE pg_depend.refclassid = 'pg_am'::regclass
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
obj | refobj
-----+--------
(0 rows)
-- New default is set, with dependency added.
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
ERROR: cannot change access method of a partitioned table
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
--------
heap2
(1 row)
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as refobj
FROM pg_depend, pg_am
WHERE pg_depend.refclassid = 'pg_am'::regclass
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
obj | refobj
----------------------+---------------------
table am_partitioned | access method heap2
(1 row)
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
ALTER TABLE am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
--------
heap
(1 row)
-- Dependency pinned, hence removed.
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as refobj
FROM pg_depend, pg_am
WHERE pg_depend.refclassid = 'pg_am'::regclass
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
obj | refobj
-----+--------
(0 rows)
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
amname
--------
heap2
(1 row)
-- Reset to default
ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
0
(1 row)
-- Upon ALTER TABLE SET ACCESS METHOD on a partitioned table, new partitions
-- will inherit the AM set. Existing partitioned are unchanged.
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
0
(1 row)
SET LOCAL default_table_access_method = 'heap';
CREATE TABLE am_partitioned_0 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 0);
SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
relam
-------
0
(1 row)
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
-- time.
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
CREATE TABLE am_partitioned_6p1 PARTITION OF am_partitioned_6p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SELECT c.relname, a.amname FROM pg_class c, pg_am a
WHERE c.relam = a.oid AND
c.relname LIKE 'am_partitioned%'
UNION ALL
SELECT c.relname, 'default' FROM pg_class c
WHERE c.relam = 0
AND c.relname LIKE 'am_partitioned%' ORDER BY 1;
relname | amname
--------------------+---------
am_partitioned | heap2
am_partitioned_0 | heap
am_partitioned_1 | heap2
am_partitioned_2 | heap2
am_partitioned_3 | heap
am_partitioned_5p | default
am_partitioned_5p1 | heap
am_partitioned_6p | heap2
am_partitioned_6p1 | heap2
(9 rows)
DROP TABLE am_partitioned;
COMMIT;
-- Second, create objects in the new AM by changing the default AM
BEGIN;
SET LOCAL default_table_access_method = 'heap2';

View File

@ -124,8 +124,11 @@ CREATE SEQUENCE tableam_seq_heap2 USING heap2;
CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1;
-- CREATE TABLE .. PARTITION BY doesn't not support USING
-- CREATE TABLE .. PARTITION BY supports USING.
CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a) USING heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'tableam_parted_heap2' AND a.oid = c.relam;
DROP TABLE tableam_parted_heap2;
CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a);
-- new partitions will inherit from the current default, rather the partition root
@ -213,11 +216,91 @@ ALTER TABLE heaptable SET ACCESS METHOD DEFAULT, SET ACCESS METHOD heap2;
ALTER MATERIALIZED VIEW heapmv SET ACCESS METHOD heap, SET ACCESS METHOD heap2;
DROP MATERIALIZED VIEW heapmv;
DROP TABLE heaptable;
-- No support for partitioned tables.
CREATE TABLE am_partitioned(x INT, y INT)
PARTITION BY hash (x);
-- Partition hierarchies with access methods
BEGIN;
SET LOCAL default_table_access_method = 'heap';
CREATE TABLE am_partitioned(x INT, y INT) PARTITION BY hash (x);
-- pg_class.relam is 0, no dependency recorded between the AM and the
-- partitioned table.
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as refobj
FROM pg_depend, pg_am
WHERE pg_depend.refclassid = 'pg_am'::regclass
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
-- New default is set, with dependency added.
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as refobj
FROM pg_depend, pg_am
WHERE pg_depend.refclassid = 'pg_am'::regclass
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default is set, with dependency updated.
SET LOCAL default_table_access_method = 'heap2';
ALTER TABLE am_partitioned SET ACCESS METHOD heap;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Dependency pinned, hence removed.
SELECT pg_describe_object(classid, objid, objsubid) AS obj,
pg_describe_object(refclassid, refobjid, refobjsubid) as refobj
FROM pg_depend, pg_am
WHERE pg_depend.refclassid = 'pg_am'::regclass
AND pg_am.oid = pg_depend.refobjid
AND pg_depend.objid = 'am_partitioned'::regclass;
-- Default and AM set in the clause are the same, relam should be set.
SET LOCAL default_table_access_method = 'heap2';
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
SELECT a.amname FROM pg_class c, pg_am a
WHERE c.relname = 'am_partitioned' AND a.oid = c.relam;
-- Reset to default
ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
-- Upon ALTER TABLE SET ACCESS METHOD on a partitioned table, new partitions
-- will inherit the AM set. Existing partitioned are unchanged.
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
SET LOCAL default_table_access_method = 'heap';
CREATE TABLE am_partitioned_0 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 0);
SET LOCAL default_table_access_method = 'heap2';
CREATE TABLE am_partitioned_1 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SET LOCAL default_table_access_method = 'heap';
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_2 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 2);
ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
SELECT relam FROM pg_class WHERE relname = 'am_partitioned';
CREATE TABLE am_partitioned_3 PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 3);
-- Partitioned table with relam at 0
ALTER TABLE am_partitioned SET ACCESS METHOD DEFAULT;
CREATE TABLE am_partitioned_5p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 5) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit default AM at creation
-- time.
CREATE TABLE am_partitioned_5p1 PARTITION OF am_partitioned_5p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
-- Partitioned table with relam set.
ALTER TABLE am_partitioned SET ACCESS METHOD heap2;
CREATE TABLE am_partitioned_6p PARTITION OF am_partitioned
FOR VALUES WITH (MODULUS 10, REMAINDER 6) PARTITION BY hash(y);
-- Partitions of this partitioned table inherit its AM.
CREATE TABLE am_partitioned_6p1 PARTITION OF am_partitioned_6p
FOR VALUES WITH (MODULUS 10, REMAINDER 1);
SELECT c.relname, a.amname FROM pg_class c, pg_am a
WHERE c.relam = a.oid AND
c.relname LIKE 'am_partitioned%'
UNION ALL
SELECT c.relname, 'default' FROM pg_class c
WHERE c.relam = 0
AND c.relname LIKE 'am_partitioned%' ORDER BY 1;
DROP TABLE am_partitioned;
COMMIT;
-- Second, create objects in the new AM by changing the default AM
BEGIN;