mirror of https://github.com/postgres/postgres
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.depull/39/head
parent
f217761856
commit
8586bf7ed8
@ -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); |
||||||
|
} |
@ -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; |
@ -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; |
||||||
|
} |
@ -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 */ |
Loading…
Reference in new issue