diff options
author | Andres Freund | 2019-03-06 17:54:38 +0000 |
---|---|---|
committer | Andres Freund | 2019-03-06 17:54:38 +0000 |
commit | 8586bf7ed8889f39a59dd99b292014b73be85342 (patch) | |
tree | 95910aef0fd4f0d271e31e03ba717184166f7c3d /src/backend | |
parent | f21776185648537a7bb82dfdf89991fb2e0b9ca5 (diff) |
tableam: introduce table AM infrastructure.
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.wchpu5muyto5n647@alap3.anarazel.de
https://postgr.es/m/20160812231527.GA690404@alvherre.pgsql
https://postgr.es/m/20190107235616.6lur25ph22u5u5av@alap3.anarazel.de
https://postgr.es/m/20190304234700.w5tmhducs5wxgzls@alap3.anarazel.de
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/heap/Makefile | 2 | ||||
-rw-r--r-- | src/backend/access/heap/heapam_handler.c | 44 | ||||
-rw-r--r-- | src/backend/access/table/Makefile | 2 | ||||
-rw-r--r-- | src/backend/access/table/tableam.c | 18 | ||||
-rw-r--r-- | src/backend/access/table/tableamapi.c | 173 | ||||
-rw-r--r-- | src/backend/bootstrap/bootparse.y | 2 | ||||
-rw-r--r-- | src/backend/catalog/genbki.pl | 4 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 21 | ||||
-rw-r--r-- | src/backend/catalog/index.c | 1 | ||||
-rw-r--r-- | src/backend/catalog/toasting.c | 1 | ||||
-rw-r--r-- | src/backend/commands/amcmds.c | 28 | ||||
-rw-r--r-- | src/backend/commands/cluster.c | 1 | ||||
-rw-r--r-- | src/backend/commands/createas.c | 1 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 40 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 1 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 100 | ||||
-rw-r--r-- | src/backend/rewrite/rewriteDefine.c | 1 | ||||
-rw-r--r-- | src/backend/utils/adt/pseudotypes.c | 1 | ||||
-rw-r--r-- | src/backend/utils/cache/relcache.c | 123 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 12 |
20 files changed, 523 insertions, 53 deletions
diff --git a/src/backend/access/heap/Makefile b/src/backend/access/heap/Makefile index eae36fdbf40..b2a017249b8 100644 --- a/src/backend/access/heap/Makefile +++ b/src/backend/access/heap/Makefile @@ -12,7 +12,7 @@ subdir = src/backend/access/heap top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = heapam.o heapam_visibility.o hio.o pruneheap.o rewriteheap.o \ +OBJS = heapam.o heapam_handler.o heapam_visibility.o hio.o pruneheap.o rewriteheap.o \ syncscan.o tuptoaster.o vacuumlazy.o visibilitymap.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c new file mode 100644 index 00000000000..518d1df84a1 --- /dev/null +++ b/src/backend/access/heap/heapam_handler.c @@ -0,0 +1,44 @@ +/*------------------------------------------------------------------------- + * + * heapam_handler.c + * heap table access method code + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/heap/heapam_handler.c + * + * + * NOTES + * This files wires up the lower level heapam.c et routines with the + * tableam abstraction. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/tableam.h" +#include "utils/builtins.h" + + +static const TableAmRoutine heapam_methods; + + +static const TableAmRoutine heapam_methods = { + .type = T_TableAmRoutine, +}; + + +const TableAmRoutine * +GetHeapamTableAmRoutine(void) +{ + return &heapam_methods; +} + +Datum +heap_tableam_handler(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(&heapam_methods); +} diff --git a/src/backend/access/table/Makefile b/src/backend/access/table/Makefile index ac1de5a52b0..55a0e5efadf 100644 --- a/src/backend/access/table/Makefile +++ b/src/backend/access/table/Makefile @@ -12,6 +12,6 @@ subdir = src/backend/access/table top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = table.o +OBJS = table.o tableam.o tableamapi.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c new file mode 100644 index 00000000000..84851e4ff88 --- /dev/null +++ b/src/backend/access/table/tableam.c @@ -0,0 +1,18 @@ +/*---------------------------------------------------------------------- + * + * tableam.c + * Table access method routines too big to be inline functions. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/access/table/tableam.c + *---------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/tableam.h" + + +/* GUC variables */ +char *default_table_access_method = DEFAULT_TABLE_ACCESS_METHOD; diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c new file mode 100644 index 00000000000..d49607e7f85 --- /dev/null +++ b/src/backend/access/table/tableamapi.c @@ -0,0 +1,173 @@ +/*---------------------------------------------------------------------- + * + * tableamapi.c + * Support routines for API for Postgres table access methods + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/backend/access/table/tableamapi.c + *---------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/htup_details.h" +#include "access/tableam.h" +#include "access/xact.h" +#include "catalog/pg_am.h" +#include "catalog/pg_proc.h" +#include "utils/fmgroids.h" +#include "utils/memutils.h" +#include "utils/syscache.h" + + +static Oid get_table_am_oid(const char *tableamname, bool missing_ok); + + +/* + * GetTableAmRoutine + * Call the specified access method handler routine to get its + * TableAmRoutine struct, which will be palloc'd in the caller's + * memory context. + */ +const TableAmRoutine * +GetTableAmRoutine(Oid amhandler) +{ + Datum datum; + const TableAmRoutine *routine; + + datum = OidFunctionCall0(amhandler); + routine = (TableAmRoutine *) DatumGetPointer(datum); + + if (routine == NULL || !IsA(routine, TableAmRoutine)) + elog(ERROR, "Table access method handler %u did not return a TableAmRoutine struct", + amhandler); + + return routine; +} + +/* + * GetTableAmRoutineByAmId - look up the handler of the table access + * method with the given OID, and get its TableAmRoutine struct. + */ +const TableAmRoutine * +GetTableAmRoutineByAmId(Oid amoid) +{ + regproc amhandler; + HeapTuple tuple; + Form_pg_am amform; + + /* Get handler function OID for the access method */ + tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for access method %u", + amoid); + amform = (Form_pg_am) GETSTRUCT(tuple); + + /* Check that it is a table access method */ + if (amform->amtype != AMTYPE_TABLE) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("access method \"%s\" is not of type %s", + NameStr(amform->amname), "TABLE"))); + + amhandler = amform->amhandler; + + /* Complain if handler OID is invalid */ + if (!RegProcedureIsValid(amhandler)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("table access method \"%s\" does not have a handler", + NameStr(amform->amname)))); + + ReleaseSysCache(tuple); + + /* And finally, call the handler function to get the API struct. */ + return GetTableAmRoutine(amhandler); +} + +/* + * get_table_am_oid - given a table access method name, look up the OID + * + * If missing_ok is false, throw an error if table access method name not + * found. If true, just return InvalidOid. + */ +static Oid +get_table_am_oid(const char *tableamname, bool missing_ok) +{ + Oid result; + Relation rel; + HeapScanDesc scandesc; + HeapTuple tuple; + ScanKeyData entry[1]; + + /* + * Search pg_tablespace. We use a heapscan here even though there is an + * index on name, on the theory that pg_tablespace will usually have just + * a few entries and so an indexed lookup is a waste of effort. + */ + rel = heap_open(AccessMethodRelationId, AccessShareLock); + + ScanKeyInit(&entry[0], + Anum_pg_am_amname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(tableamname)); + scandesc = heap_beginscan_catalog(rel, 1, entry); + tuple = heap_getnext(scandesc, ForwardScanDirection); + + /* We assume that there can be at most one matching tuple */ + if (HeapTupleIsValid(tuple) && + ((Form_pg_am) GETSTRUCT(tuple))->amtype == AMTYPE_TABLE) + result = ((Form_pg_am) GETSTRUCT(tuple))->oid; + else + result = InvalidOid; + + heap_endscan(scandesc); + heap_close(rel, AccessShareLock); + + if (!OidIsValid(result) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("table access method \"%s\" does not exist", + tableamname))); + + return result; +} + +/* check_hook: validate new default_table_access_method */ +bool +check_default_table_access_method(char **newval, void **extra, GucSource source) +{ + /* + * If we aren't inside a transaction, we cannot do database access so + * cannot verify the name. Must accept the value on faith. + */ + if (IsTransactionState()) + { + if (**newval != '\0' && + !OidIsValid(get_table_am_oid(*newval, true))) + { + /* + * When source == PGC_S_TEST, don't throw a hard error for a + * nonexistent table access method, only a NOTICE. See comments in + * guc.h. + */ + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("Table access method \"%s\" does not exist", + *newval))); + } + else + { + GUC_check_errdetail("Table access method \"%s\" does not exist.", + *newval); + return false; + } + } + } + + return true; +} diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 913f369b658..fef6e7c3dc4 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -220,6 +220,7 @@ Boot_CreateStmt: shared_relation ? GLOBALTABLESPACE_OID : 0, $3, InvalidOid, + HEAP_TABLE_AM_OID, tupdesc, RELKIND_RELATION, RELPERSISTENCE_PERMANENT, @@ -239,6 +240,7 @@ Boot_CreateStmt: $6, InvalidOid, BOOTSTRAP_SUPERUSERID, + HEAP_TABLE_AM_OID, tupdesc, NIL, RELKIND_RELATION, diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index 4935e00fb27..10c2b24bcf5 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -160,6 +160,9 @@ my $C_COLLATION_OID = my $PG_CATALOG_NAMESPACE = Catalog::FindDefinedSymbolFromData($catalog_data{pg_namespace}, 'PG_CATALOG_NAMESPACE'); +my $PG_HEAP_AM = + Catalog::FindDefinedSymbolFromData($catalog_data{pg_am}, + 'HEAP_TABLE_AM_OID'); # Build lookup tables. @@ -464,6 +467,7 @@ EOM # (It's intentional that this can apply to parts of a field). $bki_values{$attname} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g; $bki_values{$attname} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g; + $bki_values{$attname} =~ s/\bPGHEAPAM\b/$PG_HEAP_AM/g; # Replace OID synonyms with OIDs per the appropriate lookup rule. # diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 7dba4e50ddb..c7b5ff62f9f 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -45,6 +45,7 @@ #include "catalog/index.h" #include "catalog/objectaccess.h" #include "catalog/partition.h" +#include "catalog/pg_am.h" #include "catalog/pg_attrdef.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" @@ -293,6 +294,7 @@ heap_create(const char *relname, Oid reltablespace, Oid relid, Oid relfilenode, + Oid accessmtd, TupleDesc tupDesc, char relkind, char relpersistence, @@ -387,6 +389,7 @@ heap_create(const char *relname, relnamespace, tupDesc, relid, + accessmtd, relfilenode, reltablespace, shared_relation, @@ -1063,6 +1066,7 @@ heap_create_with_catalog(const char *relname, Oid reltypeid, Oid reloftypeid, Oid ownerid, + Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, @@ -1210,6 +1214,7 @@ heap_create_with_catalog(const char *relname, reltablespace, relid, InvalidOid, + accessmtd, tupdesc, relkind, relpersistence, @@ -1366,6 +1371,22 @@ heap_create_with_catalog(const char *relname, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + + /* + * Make a dependency link to force the relation to be deleted if its + * access method is. Do this only for relation and materialized views. + * + * No need to add an explicit dependency for the toast table, as the + * main table depends on it. + */ + if (relkind == RELKIND_RELATION || + relkind == RELKIND_MATVIEW) + { + referenced.classId = AccessMethodRelationId; + referenced.objectId = accessmtd; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* Post creation hook for new relation */ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index d16c3d0ea50..1ee1ed28946 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -907,6 +907,7 @@ index_create(Relation heapRelation, tableSpaceId, indexRelationId, relFileNode, + accessMethodObjectId, indexTupDesc, relkind, relpersistence, diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 77be19175a6..f3306130cdf 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -267,6 +267,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, toast_typid, InvalidOid, rel->rd_rel->relowner, + rel->rd_rel->relam, tupdesc, NIL, RELKIND_TOASTVALUE, diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c index c84507b5d03..24ca18018e1 100644 --- a/src/backend/commands/amcmds.c +++ b/src/backend/commands/amcmds.c @@ -30,7 +30,7 @@ #include "utils/syscache.h" -static Oid lookup_index_am_handler_func(List *handler_name, char amtype); +static Oid lookup_am_handler_func(List *handler_name, char amtype); static const char *get_am_type_string(char amtype); @@ -74,7 +74,7 @@ CreateAccessMethod(CreateAmStmt *stmt) /* * Get the handler function oid, verifying the AM type while at it. */ - amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype); + amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype); /* * Insert tuple into pg_am. @@ -229,6 +229,8 @@ get_am_type_string(char amtype) { case AMTYPE_INDEX: return "INDEX"; + case AMTYPE_TABLE: + return "TABLE"; default: /* shouldn't happen */ elog(ERROR, "invalid access method type '%c'", amtype); @@ -243,10 +245,11 @@ get_am_type_string(char amtype) * This function either return valid function Oid or throw an error. */ static Oid -lookup_index_am_handler_func(List *handler_name, char amtype) +lookup_am_handler_func(List *handler_name, char amtype) { Oid handlerOid; - static const Oid funcargtypes[1] = {INTERNALOID}; + Oid funcargtypes[1] = {INTERNALOID}; + Oid expectedType = InvalidOid; if (handler_name == NIL) ereport(ERROR, @@ -260,16 +263,21 @@ lookup_index_am_handler_func(List *handler_name, char amtype) switch (amtype) { case AMTYPE_INDEX: - if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("function %s must return type %s", - NameListToString(handler_name), - "index_am_handler"))); + expectedType = INDEX_AM_HANDLEROID; + break; + case AMTYPE_TABLE: + expectedType = TABLE_AM_HANDLEROID; break; default: elog(ERROR, "unrecognized access method type \"%c\"", amtype); } + if (get_func_rettype(handlerOid) != expectedType) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("function %s must return type %s", + get_func_name(handlerOid), + format_type_extended(expectedType, -1, 0)))); + return handlerOid; } diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index a74af4c1716..4d6453d9241 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -682,6 +682,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence, InvalidOid, InvalidOid, OldHeap->rd_rel->relowner, + OldHeap->rd_rel->relam, OldHeapDesc, NIL, RELKIND_RELATION, diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index 6517ecb738a..36e3d44aad6 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -108,6 +108,7 @@ create_ctas_internal(List *attrList, IntoClause *into) create->oncommit = into->onCommit; create->tablespacename = into->tableSpaceName; create->if_not_exists = false; + create->accessMethod = into->accessMethod; /* * Create the relation. (This will error out if there's an existing view, diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a93b13c2fe4..788544ec928 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -21,6 +21,7 @@ #include "access/reloptions.h" #include "access/relscan.h" #include "access/sysattr.h" +#include "access/tableam.h" #include "access/tupconvert.h" #include "access/xact.h" #include "access/xlog.h" @@ -537,6 +538,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, Oid ofTypeId; ObjectAddress address; LOCKMODE parentLockmode; + const char *accessMethod = NULL; + Oid accessMethodId = InvalidOid; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -778,6 +781,42 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } /* + * If the statement hasn't specified an access method, but we're defining + * a type of relation that needs one, use the default. + */ + 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 * stored immediately. @@ -789,6 +828,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, InvalidOid, ofTypeId, ownerId, + accessMethodId, descriptor, list_concat(cookedDefaults, old_constraints), diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e15724bb0e5..72f21810faf 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3334,6 +3334,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode) COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_STRING_FIELD(accessMethod); COPY_SCALAR_FIELD(if_not_exists); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 02790131203..753af6073f3 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -48,6 +48,7 @@ #include <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 : diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 3496e6fef7c..7ad470d34a9 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -614,6 +614,7 @@ DefineQueryRewrite(const char *rulename, elog(ERROR, "cache lookup failed for relation %u", event_relid); classForm = (Form_pg_class) GETSTRUCT(classTup); + classForm->relam = InvalidOid; classForm->reltablespace = InvalidOid; classForm->relpages = 0; classForm->reltuples = 0; diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 6194dcd2fea..5c886cfe963 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -418,3 +418,4 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(internal); PSEUDOTYPE_DUMMY_IO_FUNCS(opaque); PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement); PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray); +PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 54a40ef00bd..0b0508c01d8 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -37,6 +37,7 @@ #include "access/reloptions.h" #include "access/sysattr.h" #include "access/table.h" +#include "access/tableam.h" #include "access/tupdesc_details.h" #include "access/xact.h" #include "access/xlog.h" @@ -1137,10 +1138,32 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) } /* - * if it's an index, initialize index-related information + * initialize access method information */ - if (OidIsValid(relation->rd_rel->relam)) - RelationInitIndexAccessInfo(relation); + switch (relation->rd_rel->relkind) + { + case RELKIND_INDEX: + case RELKIND_PARTITIONED_INDEX: + Assert(relation->rd_rel->relam != InvalidOid); + RelationInitIndexAccessInfo(relation); + break; + case RELKIND_RELATION: + case RELKIND_TOASTVALUE: + case RELKIND_MATVIEW: + Assert(relation->rd_rel->relam != InvalidOid); + RelationInitTableAccessMethod(relation); + break; + case RELKIND_SEQUENCE: + Assert(relation->rd_rel->relam == InvalidOid); + RelationInitTableAccessMethod(relation); + break; + case RELKIND_VIEW: + case RELKIND_COMPOSITE_TYPE: + case RELKIND_FOREIGN_TABLE: + case RELKIND_PARTITIONED_TABLE: + Assert(relation->rd_rel->relam == InvalidOid); + break; + } /* extract reloptions if any */ RelationParseRelOptions(relation, pg_class_tuple); @@ -1646,6 +1669,65 @@ LookupOpclassInfo(Oid operatorClassOid, return opcentry; } +/* + * Fill in the TableAmRoutine for a relation + * + * relation's rd_amhandler must be valid already. + */ +static void +InitTableAmRoutine(Relation relation) +{ + relation->rd_tableam = GetTableAmRoutine(relation->rd_amhandler); +} + +/* + * Initialize table access method support for a table like relation relation + */ +void +RelationInitTableAccessMethod(Relation relation) +{ + HeapTuple tuple; + Form_pg_am aform; + + if (relation->rd_rel->relkind == RELKIND_SEQUENCE) + { + /* + * Sequences are currently accessed like heap tables, but it doesn't + * seem prudent to show that in the catalog. So just overwrite it + * here. + */ + relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID; + } + else if (IsCatalogRelation(relation)) + { + /* + * Avoid doing a syscache lookup for catalog tables. + */ + Assert(relation->rd_rel->relam == HEAP_TABLE_AM_OID); + relation->rd_amhandler = HEAP_TABLE_AM_HANDLER_OID; + } + else + { + /* + * Look up the table access method, save the OID of its handler + * function. + */ + Assert(relation->rd_rel->relam != InvalidOid); + tuple = SearchSysCache1(AMOID, + ObjectIdGetDatum(relation->rd_rel->relam)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for access method %u", + relation->rd_rel->relam); + aform = (Form_pg_am) GETSTRUCT(tuple); + relation->rd_amhandler = aform->amhandler; + ReleaseSysCache(tuple); + } + + /* + * Now we can fetch the table AM's API struct + */ + InitTableAmRoutine(relation); +} /* * formrdesc @@ -1732,6 +1814,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_rel->relallvisible = 0; relation->rd_rel->relkind = RELKIND_RELATION; relation->rd_rel->relnatts = (int16) natts; + relation->rd_rel->relam = HEAP_TABLE_AM_OID; /* * initialize attribute tuple form @@ -1800,6 +1883,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 */ if (IsBootstrapProcessingMode()) @@ -3032,6 +3121,7 @@ RelationBuildLocalRelation(const char *relname, Oid relnamespace, TupleDesc tupDesc, Oid relid, + Oid accessmtd, Oid relfilenode, Oid reltablespace, bool shared_relation, @@ -3211,6 +3301,14 @@ RelationBuildLocalRelation(const char *relname, RelationInitPhysicalAddr(rel); + rel->rd_rel->relam = accessmtd; + + if (relkind == RELKIND_RELATION || + relkind == RELKIND_SEQUENCE || + relkind == RELKIND_TOASTVALUE || + relkind == RELKIND_MATVIEW) + RelationInitTableAccessMethod(rel); + /* * Okay to insert into the relcache hash table. * @@ -3731,6 +3829,18 @@ RelationCacheInitializePhase3(void) restart = true; } + if (relation->rd_tableam == NULL && + (relation->rd_rel->relkind == RELKIND_RELATION || + relation->rd_rel->relkind == RELKIND_SEQUENCE || + relation->rd_rel->relkind == RELKIND_TOASTVALUE || + relation->rd_rel->relkind == RELKIND_MATVIEW)) + { + RelationInitTableAccessMethod(relation); + Assert(relation->rd_tableam != NULL); + + restart = true; + } + /* Release hold on the relation */ RelationDecrementReferenceCount(relation); @@ -5380,6 +5490,13 @@ load_relcache_init_file(bool shared) if (rel->rd_isnailed) nailed_rels++; + /* Load table AM data */ + if (rel->rd_rel->relkind == RELKIND_RELATION || + rel->rd_rel->relkind == RELKIND_SEQUENCE || + rel->rd_rel->relkind == RELKIND_TOASTVALUE || + rel->rd_rel->relkind == RELKIND_MATVIEW) + RelationInitTableAccessMethod(rel); + Assert(rel->rd_index == NULL); Assert(rel->rd_indextuple == NULL); Assert(rel->rd_indexcxt == NULL); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 826c189a967..bb6052ab15a 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -29,6 +29,7 @@ #include "access/commit_ts.h" #include "access/gin.h" #include "access/rmgr.h" +#include "access/tableam.h" #include "access/transam.h" #include "access/twophase.h" #include "access/xact.h" @@ -3549,6 +3550,17 @@ static struct config_string ConfigureNamesString[] = }, { + {"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."), gettext_noop("An empty string selects the database's default tablespace."), |