diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index d7539ae7439..1b5d82ed8ee 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -3646,6 +3646,17 @@ bar
+
+ HIDE_TABLEAM
+
+
+ If this variable is set to true, a table's access
+ method details are not displayed. This is mainly useful for
+ regression tests.
+
+
+
+
HISTCONTROL
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile
index eae36fdbf40..b2a017249b8 100644
--- a/src/backend/access/heap/Makefile
+++ b/src/backend/access/heap/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = heapam.o heapam_visibility.o hio.o pruneheap.o rewriteheap.o \
+OBJS = heapam.o heapam_handler.o heapam_visibility.o hio.o pruneheap.o rewriteheap.o \
syncscan.o tuptoaster.o vacuumlazy.o visibilitymap.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
new file mode 100644
index 00000000000..518d1df84a1
--- /dev/null
+++ b/src/backend/access/heap/heapam_handler.c
@@ -0,0 +1,44 @@
+/*-------------------------------------------------------------------------
+ *
+ * heapam_handler.c
+ * heap table access method code
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/heap/heapam_handler.c
+ *
+ *
+ * NOTES
+ * This files wires up the lower level heapam.c et routines with the
+ * tableam abstraction.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tableam.h"
+#include "utils/builtins.h"
+
+
+static const TableAmRoutine heapam_methods;
+
+
+static const TableAmRoutine heapam_methods = {
+ .type = T_TableAmRoutine,
+};
+
+
+const TableAmRoutine *
+GetHeapamTableAmRoutine(void)
+{
+ return &heapam_methods;
+}
+
+Datum
+heap_tableam_handler(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_POINTER(&heapam_methods);
+}
diff --git a/src/backend/access/table/Makefile b/src/backend/access/table/Makefile
index ac1de5a52b0..55a0e5efadf 100644
--- a/src/backend/access/table/Makefile
+++ b/src/backend/access/table/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/table
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = table.o
+OBJS = table.o tableam.o tableamapi.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
new file mode 100644
index 00000000000..84851e4ff88
--- /dev/null
+++ b/src/backend/access/table/tableam.c
@@ -0,0 +1,18 @@
+/*----------------------------------------------------------------------
+ *
+ * tableam.c
+ * Table access method routines too big to be inline functions.
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/table/tableam.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tableam.h"
+
+
+/* GUC variables */
+char *default_table_access_method = DEFAULT_TABLE_ACCESS_METHOD;
diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c
new file mode 100644
index 00000000000..d49607e7f85
--- /dev/null
+++ b/src/backend/access/table/tableamapi.c
@@ -0,0 +1,173 @@
+/*----------------------------------------------------------------------
+ *
+ * tableamapi.c
+ * Support routines for API for Postgres table access methods
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/table/tableamapi.c
+ *----------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/tableam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "utils/fmgroids.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+
+static Oid get_table_am_oid(const char *tableamname, bool missing_ok);
+
+
+/*
+ * GetTableAmRoutine
+ * Call the specified access method handler routine to get its
+ * TableAmRoutine struct, which will be palloc'd in the caller's
+ * memory context.
+ */
+const TableAmRoutine *
+GetTableAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ const TableAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (TableAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, TableAmRoutine))
+ elog(ERROR, "Table access method handler %u did not return a TableAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/*
+ * GetTableAmRoutineByAmId - look up the handler of the table access
+ * method with the given OID, and get its TableAmRoutine struct.
+ */
+const TableAmRoutine *
+GetTableAmRoutineByAmId(Oid amoid)
+{
+ regproc amhandler;
+ HeapTuple tuple;
+ Form_pg_am amform;
+
+ /* Get handler function OID for the access method */
+ tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ amoid);
+ amform = (Form_pg_am) GETSTRUCT(tuple);
+
+ /* Check that it is a table access method */
+ if (amform->amtype != AMTYPE_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" is not of type %s",
+ NameStr(amform->amname), "TABLE")));
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("table access method \"%s\" does not have a handler",
+ NameStr(amform->amname))));
+
+ ReleaseSysCache(tuple);
+
+ /* And finally, call the handler function to get the API struct. */
+ return GetTableAmRoutine(amhandler);
+}
+
+/*
+ * get_table_am_oid - given a table access method name, look up the OID
+ *
+ * If missing_ok is false, throw an error if table access method name not
+ * found. If true, just return InvalidOid.
+ */
+static Oid
+get_table_am_oid(const char *tableamname, bool missing_ok)
+{
+ Oid result;
+ Relation rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData entry[1];
+
+ /*
+ * Search pg_tablespace. We use a heapscan here even though there is an
+ * index on name, on the theory that pg_tablespace will usually have just
+ * a few entries and so an indexed lookup is a waste of effort.
+ */
+ rel = heap_open(AccessMethodRelationId, AccessShareLock);
+
+ ScanKeyInit(&entry[0],
+ Anum_pg_am_amname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(tableamname));
+ scandesc = heap_beginscan_catalog(rel, 1, entry);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* We assume that there can be at most one matching tuple */
+ if (HeapTupleIsValid(tuple) &&
+ ((Form_pg_am) GETSTRUCT(tuple))->amtype == AMTYPE_TABLE)
+ result = ((Form_pg_am) GETSTRUCT(tuple))->oid;
+ else
+ result = InvalidOid;
+
+ heap_endscan(scandesc);
+ heap_close(rel, AccessShareLock);
+
+ if (!OidIsValid(result) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("table access method \"%s\" does not exist",
+ tableamname)));
+
+ return result;
+}
+
+/* check_hook: validate new default_table_access_method */
+bool
+check_default_table_access_method(char **newval, void **extra, GucSource source)
+{
+ /*
+ * If we aren't inside a transaction, we cannot do database access so
+ * cannot verify the name. Must accept the value on faith.
+ */
+ if (IsTransactionState())
+ {
+ if (**newval != '\0' &&
+ !OidIsValid(get_table_am_oid(*newval, true)))
+ {
+ /*
+ * When source == PGC_S_TEST, don't throw a hard error for a
+ * nonexistent table access method, only a NOTICE. See comments in
+ * guc.h.
+ */
+ if (source == PGC_S_TEST)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("Table access method \"%s\" does not exist",
+ *newval)));
+ }
+ else
+ {
+ GUC_check_errdetail("Table access method \"%s\" does not exist.",
+ *newval);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 913f369b658..fef6e7c3dc4 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -220,6 +220,7 @@ Boot_CreateStmt:
shared_relation ? GLOBALTABLESPACE_OID : 0,
$3,
InvalidOid,
+ HEAP_TABLE_AM_OID,
tupdesc,
RELKIND_RELATION,
RELPERSISTENCE_PERMANENT,
@@ -239,6 +240,7 @@ Boot_CreateStmt:
$6,
InvalidOid,
BOOTSTRAP_SUPERUSERID,
+ HEAP_TABLE_AM_OID,
tupdesc,
NIL,
RELKIND_RELATION,
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 4935e00fb27..10c2b24bcf5 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -160,6 +160,9 @@ my $C_COLLATION_OID =
my $PG_CATALOG_NAMESPACE =
Catalog::FindDefinedSymbolFromData($catalog_data{pg_namespace},
'PG_CATALOG_NAMESPACE');
+my $PG_HEAP_AM =
+ Catalog::FindDefinedSymbolFromData($catalog_data{pg_am},
+ 'HEAP_TABLE_AM_OID');
# Build lookup tables.
@@ -464,6 +467,7 @@ EOM
# (It's intentional that this can apply to parts of a field).
$bki_values{$attname} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g;
$bki_values{$attname} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g;
+ $bki_values{$attname} =~ s/\bPGHEAPAM\b/$PG_HEAP_AM/g;
# Replace OID synonyms with OIDs per the appropriate lookup rule.
#
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 7dba4e50ddb..c7b5ff62f9f 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -45,6 +45,7 @@
#include "catalog/index.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
@@ -293,6 +294,7 @@ heap_create(const char *relname,
Oid reltablespace,
Oid relid,
Oid relfilenode,
+ Oid accessmtd,
TupleDesc tupDesc,
char relkind,
char relpersistence,
@@ -387,6 +389,7 @@ heap_create(const char *relname,
relnamespace,
tupDesc,
relid,
+ accessmtd,
relfilenode,
reltablespace,
shared_relation,
@@ -1063,6 +1066,7 @@ heap_create_with_catalog(const char *relname,
Oid reltypeid,
Oid reloftypeid,
Oid ownerid,
+ Oid accessmtd,
TupleDesc tupdesc,
List *cooked_constraints,
char relkind,
@@ -1210,6 +1214,7 @@ heap_create_with_catalog(const char *relname,
reltablespace,
relid,
InvalidOid,
+ accessmtd,
tupdesc,
relkind,
relpersistence,
@@ -1366,6 +1371,22 @@ heap_create_with_catalog(const char *relname,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+
+ /*
+ * Make a dependency link to force the relation to be deleted if its
+ * access method is. Do this only for relation and materialized views.
+ *
+ * No need to add an explicit dependency for the toast table, as the
+ * main table depends on it.
+ */
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW)
+ {
+ referenced.classId = AccessMethodRelationId;
+ referenced.objectId = accessmtd;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/* Post creation hook for new relation */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index d16c3d0ea50..1ee1ed28946 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -907,6 +907,7 @@ index_create(Relation heapRelation,
tableSpaceId,
indexRelationId,
relFileNode,
+ accessMethodObjectId,
indexTupDesc,
relkind,
relpersistence,
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 77be19175a6..f3306130cdf 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -267,6 +267,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
toast_typid,
InvalidOid,
rel->rd_rel->relowner,
+ rel->rd_rel->relam,
tupdesc,
NIL,
RELKIND_TOASTVALUE,
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d03..24ca18018e1 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -30,7 +30,7 @@
#include "utils/syscache.h"
-static Oid lookup_index_am_handler_func(List *handler_name, char amtype);
+static Oid lookup_am_handler_func(List *handler_name, char amtype);
static const char *get_am_type_string(char amtype);
@@ -74,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
/*
* Get the handler function oid, verifying the AM type while at it.
*/
- amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+ amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
/*
* Insert tuple into pg_am.
@@ -229,6 +229,8 @@ get_am_type_string(char amtype)
{
case AMTYPE_INDEX:
return "INDEX";
+ case AMTYPE_TABLE:
+ return "TABLE";
default:
/* shouldn't happen */
elog(ERROR, "invalid access method type '%c'", amtype);
@@ -243,10 +245,11 @@ get_am_type_string(char amtype)
* This function either return valid function Oid or throw an error.
*/
static Oid
-lookup_index_am_handler_func(List *handler_name, char amtype)
+lookup_am_handler_func(List *handler_name, char amtype)
{
Oid handlerOid;
- static const Oid funcargtypes[1] = {INTERNALOID};
+ Oid funcargtypes[1] = {INTERNALOID};
+ Oid expectedType = InvalidOid;
if (handler_name == NIL)
ereport(ERROR,
@@ -260,16 +263,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
switch (amtype)
{
case AMTYPE_INDEX:
- if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("function %s must return type %s",
- NameListToString(handler_name),
- "index_am_handler")));
+ expectedType = INDEX_AM_HANDLEROID;
+ break;
+ case AMTYPE_TABLE:
+ expectedType = TABLE_AM_HANDLEROID;
break;
default:
elog(ERROR, "unrecognized access method type \"%c\"", amtype);
}
+ if (get_func_rettype(handlerOid) != expectedType)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type %s",
+ get_func_name(handlerOid),
+ format_type_extended(expectedType, -1, 0))));
+
return handlerOid;
}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index a74af4c1716..4d6453d9241 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -682,6 +682,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
InvalidOid,
InvalidOid,
OldHeap->rd_rel->relowner,
+ OldHeap->rd_rel->relam,
OldHeapDesc,
NIL,
RELKIND_RELATION,
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 6517ecb738a..36e3d44aad6 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -108,6 +108,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
create->oncommit = into->onCommit;
create->tablespacename = into->tableSpaceName;
create->if_not_exists = false;
+ create->accessMethod = into->accessMethod;
/*
* Create the relation. (This will error out if there's an existing view,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a93b13c2fe4..788544ec928 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -21,6 +21,7 @@
#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
+#include "access/tableam.h"
#include "access/tupconvert.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -537,6 +538,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Oid ofTypeId;
ObjectAddress address;
LOCKMODE parentLockmode;
+ const char *accessMethod = NULL;
+ Oid accessMethodId = InvalidOid;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -777,6 +780,42 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
attr->attidentity = colDef->identity;
}
+ /*
+ * If the statement hasn't specified an access method, but we're defining
+ * a type of relation that needs one, use the default.
+ */
+ if (stmt->accessMethod != NULL)
+ {
+ accessMethod = stmt->accessMethod;
+
+ if (relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("specifying a table access method is not supported on a partitioned table")));
+
+ }
+ else if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_TOASTVALUE ||
+ relkind == RELKIND_MATVIEW)
+ accessMethod = default_table_access_method;
+
+ /*
+ * look up the access method, verify it can handle the requested features
+ */
+ if (accessMethod != NULL)
+ {
+ HeapTuple tuple;
+
+ tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethod));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("table access method \"%s\" does not exist",
+ accessMethod)));
+ accessMethodId = ((Form_pg_am) GETSTRUCT(tuple))->oid;
+ ReleaseSysCache(tuple);
+ }
+
/*
* Create the relation. Inherited defaults and constraints are passed in
* for immediate handling --- since they don't need parsing, they can be
@@ -789,6 +828,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
InvalidOid,
ofTypeId,
ownerId,
+ accessMethodId,
descriptor,
list_concat(cookedDefaults,
old_constraints),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e15724bb0e5..72f21810faf 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3334,6 +3334,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(oncommit);
COPY_STRING_FIELD(tablespacename);
+ COPY_STRING_FIELD(accessMethod);
COPY_SCALAR_FIELD(if_not_exists);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 02790131203..753af6073f3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -48,6 +48,7 @@
#include
#include
+#include "access/tableam.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_am.h"
@@ -322,6 +323,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type OptSchemaName
%type OptSchemaEltList
+%type am_type
+
%type TriggerForSpec TriggerForType
%type TriggerActionTime
%type TriggerEvents TriggerOneEvent
@@ -337,7 +340,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type copy_file_name
database_name access_method_clause access_method attr_name
- name cursor_name file_name
+ table_access_method_clause name cursor_name file_name
index_name opt_index_name cluster_index_specification
%type func_name handler_name qual_Op qual_all_Op subquery_Op
@@ -3125,7 +3128,8 @@ copy_generic_opt_arg_list_item:
*****************************************************************************/
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
- OptInherit OptPartitionSpec OptWith OnCommitOption OptTableSpace
+ OptInherit OptPartitionSpec table_access_method_clause OptWith
+ OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$4->relpersistence = $2;
@@ -3135,15 +3139,16 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->partspec = $9;
n->ofTypename = NULL;
n->constraints = NIL;
- n->options = $10;
- n->oncommit = $11;
- n->tablespacename = $12;
+ n->accessMethod = $10;
+ n->options = $11;
+ n->oncommit = $12;
+ n->tablespacename = $13;
n->if_not_exists = false;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '('
- OptTableElementList ')' OptInherit OptPartitionSpec OptWith
- OnCommitOption OptTableSpace
+ OptTableElementList ')' OptInherit OptPartitionSpec table_access_method_clause
+ OptWith OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$7->relpersistence = $2;
@@ -3153,15 +3158,16 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->partspec = $12;
n->ofTypename = NULL;
n->constraints = NIL;
- n->options = $13;
- n->oncommit = $14;
- n->tablespacename = $15;
+ n->accessMethod = $13;
+ n->options = $14;
+ n->oncommit = $15;
+ n->tablespacename = $16;
n->if_not_exists = true;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE qualified_name OF any_name
- OptTypedTableElementList OptPartitionSpec OptWith OnCommitOption
- OptTableSpace
+ OptTypedTableElementList OptPartitionSpec table_access_method_clause
+ OptWith OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$4->relpersistence = $2;
@@ -3172,15 +3178,16 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->ofTypename = makeTypeNameFromNameList($6);
n->ofTypename->location = @6;
n->constraints = NIL;
- n->options = $9;
- n->oncommit = $10;
- n->tablespacename = $11;
+ n->accessMethod = $9;
+ n->options = $10;
+ n->oncommit = $11;
+ n->tablespacename = $12;
n->if_not_exists = false;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name
- OptTypedTableElementList OptPartitionSpec OptWith OnCommitOption
- OptTableSpace
+ OptTypedTableElementList OptPartitionSpec table_access_method_clause
+ OptWith OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$7->relpersistence = $2;
@@ -3191,15 +3198,16 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->ofTypename = makeTypeNameFromNameList($9);
n->ofTypename->location = @9;
n->constraints = NIL;
- n->options = $12;
- n->oncommit = $13;
- n->tablespacename = $14;
+ n->accessMethod = $12;
+ n->options = $13;
+ n->oncommit = $14;
+ n->tablespacename = $15;
n->if_not_exists = true;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name
- OptTypedTableElementList PartitionBoundSpec OptPartitionSpec OptWith
- OnCommitOption OptTableSpace
+ OptTypedTableElementList PartitionBoundSpec OptPartitionSpec
+ table_access_method_clause OptWith OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$4->relpersistence = $2;
@@ -3210,15 +3218,16 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->partspec = $10;
n->ofTypename = NULL;
n->constraints = NIL;
- n->options = $11;
- n->oncommit = $12;
- n->tablespacename = $13;
+ n->accessMethod = $11;
+ n->options = $12;
+ n->oncommit = $13;
+ n->tablespacename = $14;
n->if_not_exists = false;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF
qualified_name OptTypedTableElementList PartitionBoundSpec OptPartitionSpec
- OptWith OnCommitOption OptTableSpace
+ table_access_method_clause OptWith OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$7->relpersistence = $2;
@@ -3229,9 +3238,10 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->partspec = $13;
n->ofTypename = NULL;
n->constraints = NIL;
- n->options = $14;
- n->oncommit = $15;
- n->tablespacename = $16;
+ n->accessMethod = $14;
+ n->options = $15;
+ n->oncommit = $16;
+ n->tablespacename = $17;
n->if_not_exists = true;
$$ = (Node *)n;
}
@@ -3876,6 +3886,12 @@ part_elem: ColId opt_collate opt_class
$$ = n;
}
;
+
+table_access_method_clause:
+ USING access_method { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
/* WITHOUT OIDS is legacy only */
OptWith:
WITH reloptions { $$ = $2; }
@@ -3981,14 +3997,16 @@ CreateAsStmt:
;
create_as_target:
- qualified_name opt_column_list OptWith OnCommitOption OptTableSpace
+ qualified_name opt_column_list table_access_method_clause
+ OptWith OnCommitOption OptTableSpace
{
$$ = makeNode(IntoClause);
$$->rel = $1;
$$->colNames = $2;
- $$->options = $3;
- $$->onCommit = $4;
- $$->tableSpaceName = $5;
+ $$->accessMethod = $3;
+ $$->options = $4;
+ $$->onCommit = $5;
+ $$->tableSpaceName = $6;
$$->viewQuery = NULL;
$$->skipData = false; /* might get changed later */
}
@@ -4038,14 +4056,15 @@ CreateMatViewStmt:
;
create_mv_target:
- qualified_name opt_column_list opt_reloptions OptTableSpace
+ qualified_name opt_column_list table_access_method_clause opt_reloptions OptTableSpace
{
$$ = makeNode(IntoClause);
$$->rel = $1;
$$->colNames = $2;
- $$->options = $3;
+ $$->accessMethod = $3;
+ $$->options = $4;
$$->onCommit = ONCOMMIT_NOOP;
- $$->tableSpaceName = $4;
+ $$->tableSpaceName = $5;
$$->viewQuery = NULL; /* filled at analysis time */
$$->skipData = false; /* might get changed later */
}
@@ -5253,16 +5272,21 @@ row_security_cmd:
*
*****************************************************************************/
-CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
{
CreateAmStmt *n = makeNode(CreateAmStmt);
n->amname = $4;
n->handler_name = $8;
- n->amtype = AMTYPE_INDEX;
+ n->amtype = $6;
$$ = (Node *) n;
}
;
+am_type:
+ INDEX { $$ = AMTYPE_INDEX; }
+ | TABLE { $$ = AMTYPE_TABLE; }
+ ;
+
/*****************************************************************************
*
* QUERIES :
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 3496e6fef7c..7ad470d34a9 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -614,6 +614,7 @@ DefineQueryRewrite(const char *rulename,
elog(ERROR, "cache lookup failed for relation %u", event_relid);
classForm = (Form_pg_class) GETSTRUCT(classTup);
+ classForm->relam = InvalidOid;
classForm->reltablespace = InvalidOid;
classForm->relpages = 0;
classForm->reltuples = 0;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 6194dcd2fea..5c886cfe963 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
+PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 54a40ef00bd..0b0508c01d8 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -37,6 +37,7 @@
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/table.h"
+#include "access/tableam.h"
#include "access/tupdesc_details.h"
#include "access/xact.h"
#include "access/xlog.h"
@@ -1137,10 +1138,32 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
}
/*
- * if it's an index, initialize index-related information
+ * initialize access method information
*/
- if (OidIsValid(relation->rd_rel->relam))
- RelationInitIndexAccessInfo(relation);
+ switch (relation->rd_rel->relkind)
+ {
+ case RELKIND_INDEX:
+ case RELKIND_PARTITIONED_INDEX:
+ Assert(relation->rd_rel->relam != InvalidOid);
+ RelationInitIndexAccessInfo(relation);
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_TOASTVALUE:
+ case RELKIND_MATVIEW:
+ Assert(relation->rd_rel->relam != InvalidOid);
+ RelationInitTableAccessMethod(relation);
+ break;
+ case RELKIND_SEQUENCE:
+ Assert(relation->rd_rel->relam == InvalidOid);
+ RelationInitTableAccessMethod(relation);
+ break;
+ case RELKIND_VIEW:
+ case RELKIND_COMPOSITE_TYPE:
+ case RELKIND_FOREIGN_TABLE:
+ case RELKIND_PARTITIONED_TABLE:
+ Assert(relation->rd_rel->relam == InvalidOid);
+ break;
+ }
/* extract reloptions if any */
RelationParseRelOptions(relation, pg_class_tuple);
@@ -1646,6 +1669,65 @@ LookupOpclassInfo(Oid operatorClassOid,
return opcentry;
}
+/*
+ * Fill in the TableAmRoutine for a relation
+ *
+ * relation's rd_amhandler must be valid already.
+ */
+static void
+InitTableAmRoutine(Relation relation)
+{
+ relation->rd_tableam = GetTableAmRoutine(relation->rd_amhandler);
+}
+
+/*
+ * Initialize table access method support for a table like relation relation
+ */
+void
+RelationInitTableAccessMethod(Relation relation)
+{
+ HeapTuple tuple;
+ Form_pg_am aform;
+
+ if (relation->rd_rel->relkind == RELKIND_SEQUENCE)
+ {
+ /*
+ * Sequences are currently accessed like heap tables, but it doesn't
+ * seem prudent to show that in the catalog. So just overwrite it
+ * here.
+ */
+ relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID;
+ }
+ else if (IsCatalogRelation(relation))
+ {
+ /*
+ * Avoid doing a syscache lookup for catalog tables.
+ */
+ Assert(relation->rd_rel->relam == HEAP_TABLE_AM_OID);
+ relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID;
+ }
+ else
+ {
+ /*
+ * Look up the table access method, save the OID of its handler
+ * function.
+ */
+ Assert(relation->rd_rel->relam != InvalidOid);
+ tuple = SearchSysCache1(AMOID,
+ ObjectIdGetDatum(relation->rd_rel->relam));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for access method %u",
+ relation->rd_rel->relam);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
+ ReleaseSysCache(tuple);
+ }
+
+ /*
+ * Now we can fetch the table AM's API struct
+ */
+ InitTableAmRoutine(relation);
+}
/*
* formrdesc
@@ -1732,6 +1814,7 @@ formrdesc(const char *relationName, Oid relationReltype,
relation->rd_rel->relallvisible = 0;
relation->rd_rel->relkind = RELKIND_RELATION;
relation->rd_rel->relnatts = (int16) natts;
+ relation->rd_rel->relam = HEAP_TABLE_AM_OID;
/*
* initialize attribute tuple form
@@ -1799,6 +1882,12 @@ formrdesc(const char *relationName, Oid relationReltype,
*/
RelationInitPhysicalAddr(relation);
+ /*
+ * initialize the table am handler
+ */
+ relation->rd_rel->relam = HEAP_TABLE_AM_OID;
+ relation->rd_tableam = GetHeapamTableAmRoutine();
+
/*
* initialize the rel-has-index flag, using hardwired knowledge
*/
@@ -3032,6 +3121,7 @@ RelationBuildLocalRelation(const char *relname,
Oid relnamespace,
TupleDesc tupDesc,
Oid relid,
+ Oid accessmtd,
Oid relfilenode,
Oid reltablespace,
bool shared_relation,
@@ -3211,6 +3301,14 @@ RelationBuildLocalRelation(const char *relname,
RelationInitPhysicalAddr(rel);
+ rel->rd_rel->relam = accessmtd;
+
+ if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_SEQUENCE ||
+ relkind == RELKIND_TOASTVALUE ||
+ relkind == RELKIND_MATVIEW)
+ RelationInitTableAccessMethod(rel);
+
/*
* Okay to insert into the relcache hash table.
*
@@ -3731,6 +3829,18 @@ RelationCacheInitializePhase3(void)
restart = true;
}
+ if (relation->rd_tableam == NULL &&
+ (relation->rd_rel->relkind == RELKIND_RELATION ||
+ relation->rd_rel->relkind == RELKIND_SEQUENCE ||
+ relation->rd_rel->relkind == RELKIND_TOASTVALUE ||
+ relation->rd_rel->relkind == RELKIND_MATVIEW))
+ {
+ RelationInitTableAccessMethod(relation);
+ Assert(relation->rd_tableam != NULL);
+
+ restart = true;
+ }
+
/* Release hold on the relation */
RelationDecrementReferenceCount(relation);
@@ -5380,6 +5490,13 @@ load_relcache_init_file(bool shared)
if (rel->rd_isnailed)
nailed_rels++;
+ /* Load table AM data */
+ if (rel->rd_rel->relkind == RELKIND_RELATION ||
+ rel->rd_rel->relkind == RELKIND_SEQUENCE ||
+ rel->rd_rel->relkind == RELKIND_TOASTVALUE ||
+ rel->rd_rel->relkind == RELKIND_MATVIEW)
+ RelationInitTableAccessMethod(rel);
+
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
Assert(rel->rd_indexcxt == NULL);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 826c189a967..bb6052ab15a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -29,6 +29,7 @@
#include "access/commit_ts.h"
#include "access/gin.h"
#include "access/rmgr.h"
+#include "access/tableam.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
@@ -3548,6 +3549,17 @@ static struct config_string ConfigureNamesString[] =
check_datestyle, assign_datestyle, NULL
},
+ {
+ {"default_table_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Sets the default table access method for new tables."),
+ NULL,
+ GUC_IS_NAME
+ },
+ &default_table_access_method,
+ DEFAULT_TABLE_ACCESS_METHOD,
+ check_default_table_access_method, NULL, NULL
+ },
+
{
{"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Sets the default tablespace to create tables and indexes in."),
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4da6719ce71..779e48437cd 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1484,6 +1484,7 @@ describeOneTableDetails(const char *schemaname,
char *reloftype;
char relpersistence;
char relreplident;
+ char *relam;
} tableinfo;
bool show_column_details = false;
@@ -1503,9 +1504,10 @@ describeOneTableDetails(const char *schemaname,
"c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
"false AS relhasoids, %s, c.reltablespace, "
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
- "c.relpersistence, c.relreplident\n"
+ "c.relpersistence, c.relreplident, am.amname\n"
"FROM pg_catalog.pg_class c\n "
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
+ "LEFT JOIN pg_catalog.pg_am am ON (c.relam = am.oid)\n"
"WHERE c.oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(c.reloptions || "
@@ -1656,6 +1658,11 @@ describeOneTableDetails(const char *schemaname,
*(PQgetvalue(res, 0, 11)) : 0;
tableinfo.relreplident = (pset.sversion >= 90400) ?
*(PQgetvalue(res, 0, 12)) : 'd';
+ if (pset.sversion >= 120000)
+ tableinfo.relam = PQgetisnull(res, 0, 13) ?
+ (char *) NULL : pg_strdup(PQgetvalue(res, 0, 13));
+ else
+ tableinfo.relam = NULL;
PQclear(res);
res = NULL;
@@ -3141,6 +3148,13 @@ describeOneTableDetails(const char *schemaname,
/* Tablespace info */
add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
true);
+
+ /* Access method info */
+ if (verbose && tableinfo.relam != NULL && !pset.hide_tableam)
+ {
+ printfPQExpBuffer(&buf, _("Access method: %s"), tableinfo.relam);
+ printTableAddFooter(&cont, buf.data);
+ }
}
/* reloptions, if verbose */
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 7c6fa2c5902..6bac9e47fd7 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -366,6 +366,8 @@ helpVariables(unsigned short int pager)
" true if last query failed, else false\n"));
fprintf(output, _(" FETCH_COUNT\n"
" the number of result rows to fetch and display at a time (0 = unlimited)\n"));
+ fprintf(output, _(" HIDE_TABLEAM\n"
+ " if set, table access methods are not displayed\n"));
fprintf(output, _(" HISTCONTROL\n"
" controls command history [ignorespace, ignoredups, ignoreboth]\n"));
fprintf(output, _(" HISTFILE\n"
diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h
index 1b90a4771ec..5be5091f0e9 100644
--- a/src/bin/psql/settings.h
+++ b/src/bin/psql/settings.h
@@ -127,6 +127,7 @@ typedef struct _psqlSettings
bool quiet;
bool singleline;
bool singlestep;
+ bool hide_tableam;
int fetch_count;
int histsize;
int ignoreeof;
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index f3ceefda9b6..e1c0754a554 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -1128,6 +1128,11 @@ show_context_hook(const char *newval)
return true;
}
+static bool
+hide_tableam_hook(const char *newval)
+{
+ return ParseVariableBool(newval, "HIDE_TABLEAM", &pset.hide_tableam);
+}
static void
EstablishVariableSpace(void)
@@ -1191,4 +1196,7 @@ EstablishVariableSpace(void)
SetVariableHooks(pset.vars, "SHOW_CONTEXT",
show_context_substitute_hook,
show_context_hook);
+ SetVariableHooks(pset.vars, "HIDE_TABLEAM",
+ bool_substitute_hook,
+ hide_tableam_hook);
}
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
new file mode 100644
index 00000000000..caeb5887d5d
--- /dev/null
+++ b/src/include/access/tableam.h
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * tableam.h
+ * POSTGRES table access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/tableam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TABLEAM_H
+#define TABLEAM_H
+
+#include "utils/guc.h"
+
+
+#define DEFAULT_TABLE_ACCESS_METHOD "heap"
+
+extern char *default_table_access_method;
+
+
+
+/*
+ * 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
+ * returned by FormData_pg_am.amhandler.
+ */
+typedef struct TableAmRoutine
+{
+ /* this must be set to T_TableAmRoutine */
+ NodeTag type;
+} TableAmRoutine;
+
+
+
+/*
+ * Functions in tableamapi.c
+ */
+extern const TableAmRoutine *GetTableAmRoutine(Oid amhandler);
+extern const TableAmRoutine *GetTableAmRoutineByAmId(Oid amoid);
+extern const TableAmRoutine *GetHeapamTableAmRoutine(void);
+extern bool check_default_table_access_method(char **newval, void **extra,
+ GucSource source);
+
+#endif /* TABLEAM_H */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0c4c2e11d59..f596ea53511 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201903041
+#define CATALOG_VERSION_NO 201903061
#endif
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 50fb62be9d5..85076d07437 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -49,6 +49,7 @@ extern Relation heap_create(const char *relname,
Oid reltablespace,
Oid relid,
Oid relfilenode,
+ Oid accessmtd,
TupleDesc tupDesc,
char relkind,
char relpersistence,
@@ -63,6 +64,7 @@ extern Oid heap_create_with_catalog(const char *relname,
Oid reltypeid,
Oid reloftypeid,
Oid ownerid,
+ Oid accessmtd,
TupleDesc tupdesc,
List *cooked_constraints,
char relkind,
diff --git a/src/include/catalog/pg_am.dat b/src/include/catalog/pg_am.dat
index 08f331d4e10..393b41dd684 100644
--- a/src/include/catalog/pg_am.dat
+++ b/src/include/catalog/pg_am.dat
@@ -12,6 +12,9 @@
[
+{ oid => '2', oid_symbol => 'HEAP_TABLE_AM_OID',
+ descr => 'heap table access method',
+ amname => 'heap', amhandler => 'heap_tableam_handler', amtype => 't' },
{ oid => '403', oid_symbol => 'BTREE_AM_OID',
descr => 'b-tree index access method',
amname => 'btree', amhandler => 'bthandler', amtype => 'i' },
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index eb3495c36a1..706b5e81cba 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -53,6 +53,7 @@ typedef FormData_pg_am *Form_pg_am;
* Allowed values for amtype
*/
#define AMTYPE_INDEX 'i' /* index access method */
+#define AMTYPE_TABLE 't' /* table access method */
#endif /* EXPOSE_TO_CLIENT_CODE */
diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat
index cccad25c148..ef0cf97ab73 100644
--- a/src/include/catalog/pg_class.dat
+++ b/src/include/catalog/pg_class.dat
@@ -22,7 +22,7 @@
{ oid => '1247',
relname => 'pg_type', relnamespace => 'PGNSP', reltype => '71',
- reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0',
+ reloftype => '0', relowner => 'PGUID', relam => 'PGHEAPAM', relfilenode => '0',
reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0',
reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
relpersistence => 'p', relkind => 'r', relnatts => '31', relchecks => '0',
@@ -33,7 +33,7 @@
reloptions => '_null_', relpartbound => '_null_' },
{ oid => '1249',
relname => 'pg_attribute', relnamespace => 'PGNSP', reltype => '75',
- reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0',
+ reloftype => '0', relowner => 'PGUID', relam => 'PGHEAPAM', relfilenode => '0',
reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0',
reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
relpersistence => 'p', relkind => 'r', relnatts => '24', relchecks => '0',
@@ -44,7 +44,7 @@
reloptions => '_null_', relpartbound => '_null_' },
{ oid => '1255',
relname => 'pg_proc', relnamespace => 'PGNSP', reltype => '81',
- reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0',
+ reloftype => '0', relowner => 'PGUID', relam => 'PGHEAPAM', relfilenode => '0',
reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0',
reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
relpersistence => 'p', relkind => 'r', relnatts => '29', relchecks => '0',
@@ -55,7 +55,7 @@
reloptions => '_null_', relpartbound => '_null_' },
{ oid => '1259',
relname => 'pg_class', relnamespace => 'PGNSP', reltype => '83',
- reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0',
+ reloftype => '0', relowner => 'PGUID', relam => 'PGHEAPAM', relfilenode => '0',
reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0',
reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
relpersistence => 'p', relkind => 'r', relnatts => '33', relchecks => '0',
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 5d82ce09a6c..ad698c9e84c 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -36,7 +36,7 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83,Relat
Oid reloftype; /* OID of entry in pg_type for underlying
* composite type */
Oid relowner; /* class owner */
- Oid relam; /* index access method; 0 if not an index */
+ Oid relam; /* access method; 0 if not a table / index */
Oid relfilenode; /* identifier of physical storage file */
/* relfilenode == 0 means it is a "mapped" relation, see relmapper.c */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5bb56b2c639..bce909b05ac 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -867,6 +867,12 @@
proname => 'int4', prorettype => 'int4', proargtypes => 'float4',
prosrc => 'ftoi4' },
+# Table access method handlers
+{ oid => '3', oid_symbol => 'HEAP_TABLE_AM_HANDLER_OID',
+ descr => 'row-oriented heap table access method handler',
+ proname => 'heap_tableam_handler', provolatile => 'v', prorettype => 'table_am_handler',
+ proargtypes => 'internal', prosrc => 'heap_tableam_handler' },
+
# Index access method handlers
{ oid => '330', descr => 'btree index access method handler',
proname => 'bthandler', provolatile => 'v', prorettype => 'index_am_handler',
@@ -6981,6 +6987,13 @@
{ oid => '3312', descr => 'I/O',
proname => 'tsm_handler_out', prorettype => 'cstring',
proargtypes => 'tsm_handler', prosrc => 'tsm_handler_out' },
+{ oid => '267', descr => 'I/O',
+ proname => 'table_am_handler_in', proisstrict => 'f',
+ prorettype => 'table_am_handler', proargtypes => 'cstring',
+ prosrc => 'table_am_handler_in' },
+{ oid => '268', descr => 'I/O',
+ proname => 'table_am_handler_out', prorettype => 'cstring',
+ proargtypes => 'table_am_handler', prosrc => 'table_am_handler_out' },
# tablesample method handlers
{ oid => '3313', descr => 'BERNOULLI tablesample method handler',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 4b7750d4398..4497ff6a2c5 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -580,6 +580,11 @@
typcategory => 'P', typinput => 'tsm_handler_in',
typoutput => 'tsm_handler_out', typreceive => '-', typsend => '-',
typalign => 'i' },
+{ oid => '269',
+ typname => 'table_am_handler', typlen => '4', typbyval => 't', typtype => 'p',
+ typcategory => 'P', typinput => 'table_am_handler_in',
+ typoutput => 'table_am_handler_out', typreceive => '-', typsend => '-',
+ typalign => 'i' },
{ oid => '3831',
descr => 'pseudo-type representing a polymorphic base type that is a range',
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f9389257c60..ffb4cd4bcc4 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -504,6 +504,7 @@ typedef enum NodeTag
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
T_IndexAmRoutine, /* in access/amapi.h */
+ T_TableAmRoutine, /* in access/tableam.h */
T_TsmRoutine, /* in access/tsmapi.h */
T_ForeignKeyCacheInfo, /* in utils/rel.h */
T_CallContext, /* in nodes/parsenodes.h */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a7e859dc90e..fe35783359e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2044,6 +2044,7 @@ typedef struct CreateStmt
List *options; /* options from WITH clause */
OnCommitAction oncommit; /* what do we do at COMMIT? */
char *tablespacename; /* table space to use, or NULL */
+ char *accessMethod; /* table access method */
bool if_not_exists; /* just do nothing if it already exists? */
} CreateStmt;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index a7efae70381..f9b1cf2df72 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -111,6 +111,7 @@ typedef struct IntoClause
RangeVar *rel; /* target relation name */
List *colNames; /* column names to assign, or NIL */
+ char *accessMethod; /* table access method */
List *options; /* options from WITH clause */
OnCommitAction onCommit; /* what do we do at COMMIT? */
char *tableSpaceName; /* table space to use, or NULL */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 1d054653039..9d805ca23d2 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -124,6 +124,20 @@ typedef struct RelationData
*/
bytea *rd_options; /* parsed pg_class.reloptions */
+ /*
+ * Oid of the handler for this relation. For an index this is a function
+ * returning IndexAmRoutine, for table like relations a function returning
+ * TableAmRoutine. This is stored separately from rd_indam, rd_tableam as
+ * its lookup requires syscache access, but during relcache bootstrap we
+ * need to be able to initialize rd_tableam without syscache lookups.
+ */
+ Oid rd_amhandler; /* OID of index AM's handler function */
+
+ /*
+ * Table access method.
+ */
+ const struct TableAmRoutine *rd_tableam;
+
/* These are non-NULL only for an index relation: */
Form_pg_index rd_index; /* pg_index tuple describing this index */
/* use "struct" here to avoid needing to include htup.h: */
@@ -144,7 +158,6 @@ typedef struct RelationData
* rd_indexcxt. A relcache reset will include freeing that chunk and
* setting rd_amcache = NULL.
*/
- Oid rd_amhandler; /* OID of index AM's handler function */
MemoryContext rd_indexcxt; /* private memory cxt for this stuff */
/* use "struct" here to avoid needing to include amapi.h: */
struct IndexAmRoutine *rd_indam; /* index AM's API struct */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index a80e335374d..8f5bd676498 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -75,6 +75,8 @@ extern void RelationInitIndexAccessInfo(Relation relation);
struct PublicationActions;
extern struct PublicationActions *GetRelationPublicationActions(Relation relation);
+extern void RelationInitTableAccessMethod(Relation relation);
+
/*
* Routines to support ereport() reports of relation-related errors
*/
@@ -97,6 +99,7 @@ extern Relation RelationBuildLocalRelation(const char *relname,
Oid relnamespace,
TupleDesc tupDesc,
Oid relid,
+ Oid accessmtd,
Oid relfilenode,
Oid reltablespace,
bool shared_relation,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 47dd885c4e9..8a63cedb569 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -99,3 +99,167 @@ HINT: Use DROP ... CASCADE to drop the dependent objects too.
-- Drop access method cascade
DROP ACCESS METHOD gist2 CASCADE;
NOTICE: drop cascades to index grect2ind2
+--
+-- Test table access methods
+--
+-- Create a heap2 table am handler with heapam handler
+CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler;
+SELECT amname, amhandler, amtype FROM pg_am where amtype = 't' ORDER BY 1, 2;
+ amname | amhandler | amtype
+--------+----------------------+--------
+ heap | heap_tableam_handler | t
+ heap2 | heap_tableam_handler | t
+(2 rows)
+
+-- First create tables employing the new AM using USING
+-- plain CREATE TABLE
+CREATE TABLE tableam_tbl_heap2(f1 int) USING heap2;
+INSERT INTO tableam_tbl_heap2 VALUES(1);
+SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1;
+ f1
+----
+ 1
+(1 row)
+
+-- CREATE TABLE AS
+CREATE TABLE tableam_tblas_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
+SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1;
+ f1
+----
+ 1
+(1 row)
+
+-- SELECT INTO doesn't support USING
+SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2;
+ERROR: syntax error at or near "USING"
+LINE 1: SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tab...
+ ^
+-- CREATE VIEW doesn't support USING
+CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
+ERROR: syntax error at or near "USING"
+LINE 1: CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM ...
+ ^
+-- CREATE SEQUENCE doesn't support USING
+CREATE SEQUENCE tableam_seq_heap2 USING heap2;
+ERROR: syntax error at or near "USING"
+LINE 1: CREATE SEQUENCE tableam_seq_heap2 USING heap2;
+ ^
+-- CREATE MATERIALIZED VIEW does support USING
+CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
+SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1;
+ f1
+----
+ 1
+(1 row)
+
+-- CREATE TABLE .. PARTITION BY doesn't not support 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
+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';
+CREATE TABLE tableam_parted_a_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('a');
+SET default_table_access_method = 'heap2';
+CREATE TABLE tableam_parted_b_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('b');
+RESET default_table_access_method;
+-- but the method can be explicitly specified
+CREATE TABLE tableam_parted_c_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('c') USING heap;
+CREATE TABLE tableam_parted_d_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('d') USING heap2;
+-- List all objects in AM
+SELECT
+ pc.relkind,
+ pa.amname,
+ CASE WHEN relkind = 't' THEN
+ (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid)
+ ELSE
+ relname::regclass::text
+ END AS relname
+FROM pg_class AS pc,
+ pg_am AS pa
+WHERE pa.oid = pc.relam
+ AND pa.amname = 'heap2'
+ORDER BY 3, 1, 2;
+ relkind | amname | relname
+---------+--------+----------------------------------
+ r | heap2 | tableam_parted_b_heap2
+ r | heap2 | tableam_parted_d_heap2
+ r | heap2 | tableam_tblas_heap2
+ r | heap2 | tableam_tbl_heap2
+ m | heap2 | tableam_tblmv_heap2
+ t | heap2 | toast for tableam_parted_b_heap2
+ t | heap2 | toast for tableam_parted_d_heap2
+(7 rows)
+
+-- Show dependencies onto AM - there shouldn't be any for toast
+SELECT pg_describe_object(classid,objid,objsubid) AS obj
+FROM pg_depend, pg_am
+WHERE pg_depend.refclassid = 'pg_am'::regclass
+ AND pg_am.oid = pg_depend.refobjid
+ AND pg_am.amname = 'heap2'
+ORDER BY classid, objid, objsubid;
+ obj
+---------------------------------------
+ table tableam_tbl_heap2
+ table tableam_tblas_heap2
+ materialized view tableam_tblmv_heap2
+ table tableam_parted_b_heap2
+ table tableam_parted_d_heap2
+(5 rows)
+
+-- Second, create objects in the new AM by changing the default AM
+BEGIN;
+SET LOCAL default_table_access_method = 'heap2';
+-- following tests should all respect the default AM
+CREATE TABLE tableam_tbl_heapx(f1 int);
+CREATE TABLE tableam_tblas_heapx AS SELECT * FROM tableam_tbl_heapx;
+SELECT INTO tableam_tblselectinto_heapx FROM tableam_tbl_heapx;
+CREATE MATERIALIZED VIEW tableam_tblmv_heapx USING heap2 AS SELECT * FROM tableam_tbl_heapx;
+CREATE TABLE tableam_parted_heapx (a text, b int) PARTITION BY list (a);
+CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b');
+-- but an explicitly set AM overrides it
+CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap;
+-- sequences, views and foreign servers shouldn't have an AM
+CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx;
+CREATE SEQUENCE tableam_seq_heapx;
+CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator;
+CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ;
+CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2;
+-- Verify that new AM was used for tables, matviews, but not for sequences, views and fdws
+SELECT
+ pc.relkind,
+ pa.amname,
+ CASE WHEN relkind = 't' THEN
+ (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid)
+ ELSE
+ relname::regclass::text
+ END AS relname
+FROM pg_class AS pc
+ LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam)
+WHERE pc.relname LIKE 'tableam_%_heapx'
+ORDER BY 3, 1, 2;
+ relkind | amname | relname
+---------+--------+-----------------------------
+ f | | tableam_fdw_heapx
+ r | heap2 | tableam_parted_1_heapx
+ r | heap | tableam_parted_2_heapx
+ p | | tableam_parted_heapx
+ S | | tableam_seq_heapx
+ r | heap2 | tableam_tblas_heapx
+ r | heap2 | tableam_tbl_heapx
+ m | heap2 | tableam_tblmv_heapx
+ r | heap2 | tableam_tblselectinto_heapx
+ v | | tableam_view_heapx
+(10 rows)
+
+-- don't want to keep those tables, nor the default
+ROLLBACK;
+-- Drop table access method, which fails as objects depends on it
+DROP ACCESS METHOD heap2;
+ERROR: cannot drop access method heap2 because other objects depend on it
+DETAIL: table tableam_tbl_heap2 depends on access method heap2
+table tableam_tblas_heap2 depends on access method heap2
+materialized view tableam_tblmv_heap2 depends on access method heap2
+table tableam_parted_b_heap2 depends on access method heap2
+table tableam_parted_d_heap2 depends on access method heap2
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+-- we intentionally leave the objects created above alive, to verify pg_dump support
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index ce25ee044a0..49a0acc0ee4 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1802,11 +1802,24 @@ WHERE p1.amhandler = 0;
-----+--------
(0 rows)
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
+-- Check for table amhandler functions with the wrong signature
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'table_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
oid | amname | oid | proname
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 775b127121e..aa101de9063 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -2773,6 +2773,45 @@ Argument data types | numeric
Type | func
\pset tuples_only false
+-- check conditional tableam display
+-- Create a heap2 table am handler with heapam handler
+CREATE ACCESS METHOD heap_psql TYPE TABLE HANDLER heap_tableam_handler;
+CREATE TABLE tbl_heap_psql(f1 int, f2 char(100)) using heap_psql;
+CREATE TABLE tbl_heap(f1 int, f2 char(100)) using heap;
+\d+ tbl_heap_psql
+ Table "public.tbl_heap_psql"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+----------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | | | plain | |
+ f2 | character(100) | | | | extended | |
+
+\d+ tbl_heap
+ Table "public.tbl_heap"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+----------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | | | plain | |
+ f2 | character(100) | | | | extended | |
+
+\set HIDE_TABLEAM off
+\d+ tbl_heap_psql
+ Table "public.tbl_heap_psql"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+----------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | | | plain | |
+ f2 | character(100) | | | | extended | |
+Access method: heap_psql
+
+\d+ tbl_heap
+ Table "public.tbl_heap"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+----------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | | | plain | |
+ f2 | character(100) | | | | extended | |
+Access method: heap
+
+\set HIDE_TABLEAM on
+DROP TABLE tbl_heap, tbl_heap_psql;
+DROP ACCESS METHOD heap_psql;
-- test numericlocale (as best we can without control of psql's locale)
\pset format aligned
\pset expanded off
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 6cd937eb52b..aaaa488b3cd 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -186,6 +186,13 @@ sql_sizing|f
sql_sizing_profiles|f
stud_emp|f
student|f
+tableam_parted_a_heap2|f
+tableam_parted_b_heap2|f
+tableam_parted_c_heap2|f
+tableam_parted_d_heap2|f
+tableam_parted_heap2|f
+tableam_tbl_heap2|f
+tableam_tblas_heap2|f
tbl_include_box|t
tbl_include_box_pk|f
tbl_include_pk|t
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index b1419d4bc21..91f17013d64 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -502,11 +502,20 @@ WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p') OR
-----+---------
(0 rows)
--- Indexes should have an access method, others not.
+-- All tables and indexes should have an access method.
SELECT p1.oid, p1.relname
FROM pg_class as p1
-WHERE (p1.relkind = 'i' AND p1.relam = 0) OR
- (p1.relkind != 'i' AND p1.relam != 0);
+WHERE p1.relkind NOT IN ('S', 'v', 'f', 'c') and
+ p1.relam = 0;
+ oid | relname
+-----+---------
+(0 rows)
+
+-- Conversely, sequences, views, types shouldn't have them
+SELECT p1.oid, p1.relname
+FROM pg_class as p1
+WHERE p1.relkind IN ('S', 'v', 'f', 'c') and
+ p1.relam != 0;
oid | relname
-----+---------
(0 rows)
diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c
index f274971be31..f1df7557fa1 100644
--- a/src/test/regress/pg_regress_main.c
+++ b/src/test/regress/pg_regress_main.c
@@ -73,11 +73,16 @@ psql_start_test(const char *testname,
}
}
+ /*
+ * Use HIDE_TABLEAM to hide different AMs to allow to use regression tests
+ * against different AMs without unnecessary differences.
+ */
offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
- "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1",
+ "\"%s%spsql\" -X -a -q -d \"%s\" -v %s < \"%s\" > \"%s\" 2>&1",
bindir ? bindir : "",
bindir ? "/" : "",
dblist->str,
+ "HIDE_TABLEAM=\"on\"",
infile,
outfile);
if (offset >= sizeof(psql_cmd))
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index 3e0ac104f3c..516401ddfe8 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -66,3 +66,119 @@ DROP ACCESS METHOD gist2;
-- Drop access method cascade
DROP ACCESS METHOD gist2 CASCADE;
+
+
+--
+-- Test table access methods
+--
+
+-- Create a heap2 table am handler with heapam handler
+CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler;
+
+SELECT amname, amhandler, amtype FROM pg_am where amtype = 't' ORDER BY 1, 2;
+
+-- First create tables employing the new AM using USING
+
+-- plain CREATE TABLE
+CREATE TABLE tableam_tbl_heap2(f1 int) USING heap2;
+INSERT INTO tableam_tbl_heap2 VALUES(1);
+SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1;
+
+-- CREATE TABLE AS
+CREATE TABLE tableam_tblas_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
+SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1;
+
+-- SELECT INTO doesn't support USING
+SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2;
+
+-- CREATE VIEW doesn't support USING
+CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2;
+
+-- CREATE SEQUENCE doesn't support USING
+CREATE SEQUENCE tableam_seq_heap2 USING heap2;
+
+-- CREATE MATERIALIZED VIEW does support USING
+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 tableam_parted_heap2 (a text, b int) PARTITION BY list (a) USING 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';
+CREATE TABLE tableam_parted_a_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('a');
+SET default_table_access_method = 'heap2';
+CREATE TABLE tableam_parted_b_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('b');
+RESET default_table_access_method;
+-- but the method can be explicitly specified
+CREATE TABLE tableam_parted_c_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('c') USING heap;
+CREATE TABLE tableam_parted_d_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('d') USING heap2;
+
+-- List all objects in AM
+SELECT
+ pc.relkind,
+ pa.amname,
+ CASE WHEN relkind = 't' THEN
+ (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid)
+ ELSE
+ relname::regclass::text
+ END AS relname
+FROM pg_class AS pc,
+ pg_am AS pa
+WHERE pa.oid = pc.relam
+ AND pa.amname = 'heap2'
+ORDER BY 3, 1, 2;
+
+-- Show dependencies onto AM - there shouldn't be any for toast
+SELECT pg_describe_object(classid,objid,objsubid) AS obj
+FROM pg_depend, pg_am
+WHERE pg_depend.refclassid = 'pg_am'::regclass
+ AND pg_am.oid = pg_depend.refobjid
+ AND pg_am.amname = 'heap2'
+ORDER BY classid, objid, objsubid;
+
+
+-- Second, create objects in the new AM by changing the default AM
+BEGIN;
+SET LOCAL default_table_access_method = 'heap2';
+
+-- following tests should all respect the default AM
+CREATE TABLE tableam_tbl_heapx(f1 int);
+CREATE TABLE tableam_tblas_heapx AS SELECT * FROM tableam_tbl_heapx;
+SELECT INTO tableam_tblselectinto_heapx FROM tableam_tbl_heapx;
+CREATE MATERIALIZED VIEW tableam_tblmv_heapx USING heap2 AS SELECT * FROM tableam_tbl_heapx;
+CREATE TABLE tableam_parted_heapx (a text, b int) PARTITION BY list (a);
+CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b');
+
+-- but an explicitly set AM overrides it
+CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap;
+
+-- sequences, views and foreign servers shouldn't have an AM
+CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx;
+CREATE SEQUENCE tableam_seq_heapx;
+CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator;
+CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ;
+CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2;
+
+-- Verify that new AM was used for tables, matviews, but not for sequences, views and fdws
+SELECT
+ pc.relkind,
+ pa.amname,
+ CASE WHEN relkind = 't' THEN
+ (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid)
+ ELSE
+ relname::regclass::text
+ END AS relname
+FROM pg_class AS pc
+ LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam)
+WHERE pc.relname LIKE 'tableam_%_heapx'
+ORDER BY 3, 1, 2;
+
+-- don't want to keep those tables, nor the default
+ROLLBACK;
+
+-- Drop table access method, which fails as objects depends on it
+DROP ACCESS METHOD heap2;
+
+-- we intentionally leave the objects created above alive, to verify pg_dump support
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index e2014fc2b5e..1227ef79f0c 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -1201,15 +1201,25 @@ SELECT p1.oid, p1.amname
FROM pg_am AS p1
WHERE p1.amhandler = 0;
--- Check for amhandler functions with the wrong signature
+-- Check for index amhandler functions with the wrong signature
SELECT p1.oid, p1.amname, p2.oid, p2.proname
FROM pg_am AS p1, pg_proc AS p2
-WHERE p2.oid = p1.amhandler AND
- (p2.prorettype != 'index_am_handler'::regtype OR p2.proretset
+WHERE p2.oid = p1.amhandler AND p1.amtype = 'i' AND
+ (p2.prorettype != 'index_am_handler'::regtype
+ OR p2.proretset
OR p2.pronargs != 1
OR p2.proargtypes[0] != 'internal'::regtype);
+-- Check for table amhandler functions with the wrong signature
+
+SELECT p1.oid, p1.amname, p2.oid, p2.proname
+FROM pg_am AS p1, pg_proc AS p2
+WHERE p2.oid = p1.amhandler AND p1.amtype = 's' AND
+ (p2.prorettype != 'table_am_handler'::regtype
+ OR p2.proretset
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
-- **************** pg_amop ****************
diff --git a/src/test/regress/sql/psql.sql b/src/test/regress/sql/psql.sql
index 1bb2a6e16d4..fb7d17fc76e 100644
--- a/src/test/regress/sql/psql.sql
+++ b/src/test/regress/sql/psql.sql
@@ -448,6 +448,21 @@ select 1 where false;
\df exp
\pset tuples_only false
+-- check conditional tableam display
+
+-- Create a heap2 table am handler with heapam handler
+CREATE ACCESS METHOD heap_psql TYPE TABLE HANDLER heap_tableam_handler;
+CREATE TABLE tbl_heap_psql(f1 int, f2 char(100)) using heap_psql;
+CREATE TABLE tbl_heap(f1 int, f2 char(100)) using heap;
+\d+ tbl_heap_psql
+\d+ tbl_heap
+\set HIDE_TABLEAM off
+\d+ tbl_heap_psql
+\d+ tbl_heap
+\set HIDE_TABLEAM on
+DROP TABLE tbl_heap, tbl_heap_psql;
+DROP ACCESS METHOD heap_psql;
+
-- test numericlocale (as best we can without control of psql's locale)
\pset format aligned
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index f9aeea32144..821337b0023 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -367,12 +367,17 @@ WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p') OR
relpersistence NOT IN ('p', 'u', 't') OR
relreplident NOT IN ('d', 'n', 'f', 'i');
--- Indexes should have an access method, others not.
-
+-- All tables and indexes should have an access method.
SELECT p1.oid, p1.relname
FROM pg_class as p1
-WHERE (p1.relkind = 'i' AND p1.relam = 0) OR
- (p1.relkind != 'i' AND p1.relam != 0);
+WHERE p1.relkind NOT IN ('S', 'v', 'f', 'c') and
+ p1.relam = 0;
+
+-- Conversely, sequences, views, types shouldn't have them
+SELECT p1.oid, p1.relname
+FROM pg_class as p1
+WHERE p1.relkind IN ('S', 'v', 'f', 'c') and
+ p1.relam != 0;
-- **************** pg_attribute ****************
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3d3c76d2518..7a5d8c47e12 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2307,6 +2307,7 @@ T_Action
T_WorkerStatus
TabStatHashEntry
TabStatusArray
+TableAmRoutine
TableDataInfo
TableFunc
TableFuncRoutine