diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index d7539ae743..1b5d82ed8e 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 eae36fdbf4..b2a017249b 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 0000000000..518d1df84a --- /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 ac1de5a52b..55a0e5efad 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 0000000000..84851e4ff8 --- /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 0000000000..d49607e7f8 --- /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 913f369b65..fef6e7c3dc 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 4935e00fb2..10c2b24bcf 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 7dba4e50dd..c7b5ff62f9 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 d16c3d0ea5..1ee1ed2894 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 77be19175a..f3306130cd 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 c84507b5d0..24ca18018e 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 a74af4c171..4d6453d924 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 6517ecb738..36e3d44aad 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 a93b13c2fe..788544ec92 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 e15724bb0e..72f21810fa 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 0279013120..753af6073f 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 3496e6fef7..7ad470d34a 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 6194dcd2fe..5c886cfe96 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 54a40ef00b..0b0508c01d 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 826c189a96..bb6052ab15 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 4da6719ce7..779e48437c 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 7c6fa2c590..6bac9e47fd 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 1b90a4771e..5be5091f0e 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 f3ceefda9b..e1c0754a55 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 0000000000..caeb5887d5 --- /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 0c4c2e11d5..f596ea5351 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 50fb62be9d..85076d0743 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 08f331d4e1..393b41dd68 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 eb3495c36a..706b5e81cb 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 cccad25c14..ef0cf97ab7 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 5d82ce09a6..ad698c9e84 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 5bb56b2c63..bce909b05a 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 4b7750d439..4497ff6a2c 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 f9389257c6..ffb4cd4bcc 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 a7e859dc90..fe35783359 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 a7efae7038..f9b1cf2df7 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 1d05465303..9d805ca23d 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 a80e335374..8f5bd67649 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 47dd885c4e..8a63cedb56 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 ce25ee044a..49a0acc0ee 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 775b127121..aa101de906 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 6cd937eb52..aaaa488b3c 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 b1419d4bc2..91f17013d6 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 f274971be3..f1df7557fa 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 3e0ac104f3..516401ddfe 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 e2014fc2b5..1227ef79f0 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 1bb2a6e16d..fb7d17fc76 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 f9aeea3214..821337b002 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 3d3c76d251..7a5d8c47e1 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