mirror of https://github.com/postgres/postgres
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
This commit is contained in:
parent
f217761856
commit
8586bf7ed8
|
@ -3646,6 +3646,17 @@ bar
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>HIDE_TABLEAM</varname></term>
|
||||
<listitem>
|
||||
<para>
|
||||
If this variable is set to <literal>true</literal>, a table's access
|
||||
method details are not displayed. This is mainly useful for
|
||||
regression tests.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>HISTCONTROL</varname></term>
|
||||
<listitem>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -907,6 +907,7 @@ index_create(Relation heapRelation,
|
|||
tableSpaceId,
|
||||
indexRelationId,
|
||||
relFileNode,
|
||||
accessMethodObjectId,
|
||||
indexTupDesc,
|
||||
relkind,
|
||||
relpersistence,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 :
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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."),
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -127,6 +127,7 @@ typedef struct _psqlSettings
|
|||
bool quiet;
|
||||
bool singleline;
|
||||
bool singlestep;
|
||||
bool hide_tableam;
|
||||
int fetch_count;
|
||||
int histsize;
|
||||
int ignoreeof;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
|
@ -53,6 +53,6 @@
|
|||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201903041
|
||||
#define CATALOG_VERSION_NO 201903061
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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' },
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ****************
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ****************
|
||||
|
||||
|
|
|
@ -2307,6 +2307,7 @@ T_Action
|
|||
T_WorkerStatus
|
||||
TabStatHashEntry
|
||||
TabStatusArray
|
||||
TableAmRoutine
|
||||
TableDataInfo
|
||||
TableFunc
|
||||
TableFuncRoutine
|
||||
|
|
Loading…
Reference in New Issue