tableam: introduce table AM infrastructure.
authorAndres Freund <[email protected]>
Wed, 6 Mar 2019 17:54:38 +0000 (09:54 -0800)
committerAndres Freund <[email protected]>
Wed, 6 Mar 2019 17:54:38 +0000 (09:54 -0800)
This introduces the concept of table access methods, i.e. CREATE
  ACCESS METHOD ... TYPE TABLE and
  CREATE TABLE ... USING (storage-engine).
No table access functionality is delegated to table AMs as of this
commit, that'll be done in following commits.

Subsequent commits will incrementally abstract table access
functionality to be routed through table access methods. That change
is too large to be reviewed & committed at once, so it'll be done
incrementally.

Docs will be updated at the end, as adding them incrementally would
likely make them less coherent, and definitely is a lot more work,
without a lot of benefit.

Table access methods are specified similar to index access methods,
i.e. pg_am.amhandler returns, as INTERNAL, a pointer to a struct with
callbacks. In contrast to index AMs that struct needs to live as long
as a backend, typically that's achieved by just returning a pointer to
a constant struct.

Psql's \d+ now displays a table's access method. That can be disabled
with HIDE_TABLEAM=true, which is mainly useful so regression tests can
be run against different AMs.  It's quite possible that this behaviour
still needs to be fine tuned.

For now it's not allowed to set a table AM for a partitioned table, as
we've not resolved how partitions would inherit that. Disallowing
allows us to introduce, if we decide that's the way forward, such a
behaviour without a compatibility break.

Catversion bumped, to add the heap table AM and references to it.

Author: Haribabu Kommi, Andres Freund, Alvaro Herrera, Dimitri Golgov and others
Discussion:
    https://postgr.es/m/20180703070645[email protected]
    https://postgr.es/m/20160812231527[email protected]
    https://postgr.es/m/20190107235616[email protected]
    https://postgr.es/m/20190304234700[email protected]

50 files changed:
doc/src/sgml/ref/psql-ref.sgml
src/backend/access/heap/Makefile
src/backend/access/heap/heapam_handler.c[new file with mode: 0644]
src/backend/access/table/Makefile
src/backend/access/table/tableam.c[new file with mode: 0644]
src/backend/access/table/tableamapi.c[new file with mode: 0644]
src/backend/bootstrap/bootparse.y
src/backend/catalog/genbki.pl
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/toasting.c
src/backend/commands/amcmds.c
src/backend/commands/cluster.c
src/backend/commands/createas.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/parser/gram.y
src/backend/rewrite/rewriteDefine.c
src/backend/utils/adt/pseudotypes.c
src/backend/utils/cache/relcache.c
src/backend/utils/misc/guc.c
src/bin/psql/describe.c
src/bin/psql/help.c
src/bin/psql/settings.h
src/bin/psql/startup.c
src/include/access/tableam.h[new file with mode: 0644]
src/include/catalog/catversion.h
src/include/catalog/heap.h
src/include/catalog/pg_am.dat
src/include/catalog/pg_am.h
src/include/catalog/pg_class.dat
src/include/catalog/pg_class.h
src/include/catalog/pg_proc.dat
src/include/catalog/pg_type.dat
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h
src/include/utils/rel.h
src/include/utils/relcache.h
src/test/regress/expected/create_am.out
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/psql.out
src/test/regress/expected/sanity_check.out
src/test/regress/expected/type_sanity.out
src/test/regress/pg_regress_main.c
src/test/regress/sql/create_am.sql
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/psql.sql
src/test/regress/sql/type_sanity.sql
src/tools/pgindent/typedefs.list

index d7539ae743948b642d35a83fd8ed28f220db9335..1b5d82ed8eec1df84ef5d553d104424aff48727f 100644 (file)
@@ -3646,6 +3646,17 @@ bar
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>HIDE_TABLEAM</varname></term>
+        <listitem>
+        <para>
+         If this variable is set to <literal>true</literal>, a table's access
+         method details are not displayed. This is mainly useful for
+         regression tests.
+        </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>HISTCONTROL</varname></term>
         <listitem>
index eae36fdbf40c63c4dde3c638e16271d9a6e9d2a6..b2a017249b8b2ab6797a83da27b055498af35a2d 100644 (file)
@@ -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 (file)
index 0000000..518d1df
--- /dev/null
@@ -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);
+}
index ac1de5a52b0813a0d55fd34f19b58d584da9806f..55a0e5efadfe2a84ae192a18162cd7686cd97396 100644 (file)
@@ -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 (file)
index 0000000..84851e4
--- /dev/null
@@ -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 (file)
index 0000000..d49607e
--- /dev/null
@@ -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;
+}
index 913f369b658e82cc76bef490b5af1cc135a6945c..fef6e7c3dc4ff2043f86ea623c93e5586704933a 100644 (file)
@@ -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,
index 4935e00fb27d72d2c212bc3f1e2983564088b142..10c2b24bcf5c5c3e74559618025ebdb8d93fe73e 100644 (file)
@@ -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.
            #
index 7dba4e50ddb2972f8b8a0cfbc310b21bb93e4c9b..c7b5ff62f9fbac93018cd2d4e8788e32745f9e8a 100644 (file)
@@ -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 */
index d16c3d0ea50f28044b18e5740ac5e1d1cc3e280d..1ee1ed289460ddb42685565d3992d4c7e7401b85 100644 (file)
@@ -907,6 +907,7 @@ index_create(Relation heapRelation,
                                tableSpaceId,
                                indexRelationId,
                                relFileNode,
+                               accessMethodObjectId,
                                indexTupDesc,
                                relkind,
                                relpersistence,
index 77be19175a6e9f734ba175c06c9c9370bde6027b..f3306130cdfaaae43611f8a24f4ee3d51a3bd503 100644 (file)
@@ -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,
index c84507b5d039243f2435323b01a39817247f3463..24ca18018e136b5a4efe31fb48eec7e22a9f1fcf 100644 (file)
@@ -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;
 }
index a74af4c171612f0567f458cc31ae0f9eaef43127..4d6453d92412dcd0c996761799ff3de9f3740ad8 100644 (file)
@@ -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,
index 6517ecb738ac3a5db67f9a51500b1d90e0e81b61..36e3d44aad6b758a5d1afc4cb0924b85193bfd26 100644 (file)
@@ -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,
index a93b13c2fe4554ce1922a1297164bd4690e0c07e..788544ec928cf1a1ae9cb9ff0f929c9763d840f0 100644 (file)
@@ -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),
index e15724bb0e52ea500bd739f4414d49ea2c58506b..72f21810fafc445b54c1e78ba96b77c33979e4f1 100644 (file)
@@ -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);
 }
 
index 027901312030c7f3f6257d60d66c6461cf6c4711..753af6073f35e5d37428314beaaf805e46d542a5 100644 (file)
@@ -48,6 +48,7 @@
 #include <ctype.h>
 #include <limits.h>
 
+#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 <str>        OptSchemaName
 %type <list>   OptSchemaEltList
 
+%type <chr>        am_type
+
 %type <boolean> TriggerForSpec TriggerForType
 %type <ival>   TriggerActionTime
 %type <list>   TriggerEvents TriggerOneEvent
@@ -337,7 +340,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 %type <str>        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 <list>   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 :
index 3496e6fef7cefee570fad500298942510eb81600..7ad470d34a9804f96ea53f25c338a7856a7a32ef 100644 (file)
@@ -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;
index 6194dcd2fea9d5eb141c2fb8e2649e94cf40c7db..5c886cfe9630fe9391f934c7a0a349643de11961 100644 (file)
@@ -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);
index 54a40ef00bdc9c02bb520e8ddab4092479a3749e..0b0508c01d8b0283c4a0b6a3b6aa5dd9ce112ce9 100644 (file)
@@ -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);
index 826c189a9670da3705059e0ecd4460262893e8ae..bb6052ab15a6aa236b4695e7b5c74435ff72e416 100644 (file)
@@ -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."),
index 4da6719ce715a316dd0310add2a3a10e415170d7..779e48437cd218e5be9e052c4dfe5384fb29f1c3 100644 (file)
@@ -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 */
index 7c6fa2c590284a5b0b7133b0bf408fd815d2e346..6bac9e47fd7c42ba6b720ff1775de90f7dffe999 100644 (file)
@@ -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"
index 1b90a4771ec05dfcfda0ae79c2fef89fe2e1fcae..5be5091f0e9e1763e00244622afdd68f9d04ebe7 100644 (file)
@@ -127,6 +127,7 @@ typedef struct _psqlSettings
    bool        quiet;
    bool        singleline;
    bool        singlestep;
+   bool        hide_tableam;
    int         fetch_count;
    int         histsize;
    int         ignoreeof;
index f3ceefda9b6d8ce077d3d660fabfd74bfaa4a20b..e1c0754a554632422509c7ac389df14700df7927 100644 (file)
@@ -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 (file)
index 0000000..caeb588
--- /dev/null
@@ -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 */
index 0c4c2e11d59627f216b93bac168542cd2189ebec..f596ea5351116305ab01f949299d5e635507fba8 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201903041
+#define CATALOG_VERSION_NO 201903061
 
 #endif
index 50fb62be9d56c5eed8266ab2a01890365782be85..85076d0743723c9c6ae9fe3ddcaa2738e75c6bbd 100644 (file)
@@ -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,
index 08f331d4e1059e31e32ad2f34af3a9211222e1a1..393b41dd684b81606972641d1caf0dd1353352f6 100644 (file)
@@ -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' },
index eb3495c36a1d7c3c62d2d3cebeba791f829b87ee..706b5e81cba6be3590934dc4e4e794e77b9951fb 100644 (file)
@@ -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 */
 
index cccad25c1484231f665a7a4c8d46238c76a81f45..ef0cf97ab73b7cd5a256cd1b4cf98e96fec5fea4 100644 (file)
@@ -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',
index 5d82ce09a6c1988899a06d95665d4a14ddb5b2e6..ad698c9e84cb4aa08c07cf4951cebcb8a6daacfc 100644 (file)
@@ -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 */
index 5bb56b2c6391df73f0faf05b095d0b530f211021..bce909b05ac2d8df3b1ca1022e29ed0a2dc64758 100644 (file)
   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',
 { 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',
index 4b7750d4398d3f9b6d14c868f689fd4ece2aae92..4497ff6a2c591e6f3244a9cbefe042633e453b35 100644 (file)
   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',
index f9389257c60b52488b1e5c108a436dde58d10691..ffb4cd4bcc45784d937c037f7ba25a3c439824a5 100644 (file)
@@ -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 */
index a7e859dc90ef137865f2abbb0cc38b6487ad0252..fe35783359e4dd031278307a908f893e56a891ef 100644 (file)
@@ -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;
 
index a7efae70381ab6e83401e937609f0298b372d92a..f9b1cf2df72a3472b280f343198af3c5afd3e99c 100644 (file)
@@ -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 */
index 1d05465303916fe31b625f45cde6794e14caebc0..9d805ca23d25cf195c7f9ada8716e68b021ad744 100644 (file)
@@ -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 */
index a80e335374db5f1ddfe4e51ac2a02724fa95681d..8f5bd67649813f7e6952998c588169bc62baa284 100644 (file)
@@ -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,
index 47dd885c4e9b1ff8d1595197bc926c855b7d2fa5..8a63cedb56955e37c70ebbb1ae112a5ecd530263 100644 (file)
@@ -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
index ce25ee044a0c6c9c1433b33751218567bb8afea9..49a0acc0ee449340e3294c55f14361a4702b5a64 100644 (file)
@@ -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 
index 775b127121ee8cd0182852900e79c8c04e3fdc80..aa101de9063efa68ad975df2888360cfa90a5e25 100644 (file)
@@ -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
index 6cd937eb52bdfb57610982cc9df97b39be642e2a..aaaa488b3cdc37cf7fbfd803d7d6bb1db2c3f716 100644 (file)
@@ -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
index b1419d4bc21dbd5b520a2cac69a740205e9dac63..91f17013d644bd7b7e7a261566dff35040f5867f 100644 (file)
@@ -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)
index f274971be31f6c33fe3490bb7a5e9d06e88254a5..f1df7557fa1e9335aae9a32df036f128414337a9 100644 (file)
@@ -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))
index 3e0ac104f3cca3d59f33f83c808b7d2954bc2fc6..516401ddfe804f7791495b1aea151ce0bb2014e6 100644 (file)
@@ -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
index e2014fc2b5e8c593f7bcbb231f354b3193583ccf..1227ef79f0c8ad58352bc92f5e28a6f68223bd48 100644 (file)
@@ -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 ****************
 
index 1bb2a6e16d41d44b1ff6035aead27f2d3dff2299..fb7d17fc76edb00b3d08be8ee8b32a598e36d9e8 100644 (file)
@@ -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
index f9aeea32144f204427830ecf0cf95227a7a1c0fc..821337b00232a5fa3423ee4aabc0b51d539b7bfa 100644 (file)
@@ -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 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 = 'i' AND p1.relam = 0) OR
-    (p1.relkind != 'i' AND p1.relam != 0);
+WHERE p1.relkind IN ('S', 'v', 'f', 'c') and
+    p1.relam != 0;
 
 -- **************** pg_attribute ****************
 
index 3d3c76d2518677d6a308e07e90a3d3c906887e92..7a5d8c47e128f4aef3f4cd56eeb5cf06e8722182 100644 (file)
@@ -2307,6 +2307,7 @@ T_Action
 T_WorkerStatus
 TabStatHashEntry
 TabStatusArray
+TableAmRoutine
 TableDataInfo
 TableFunc
 TableFuncRoutine