207 lines
5.7 KiB
C
207 lines
5.7 KiB
C
/*----------------------------------------------------------------------
|
|
*
|
|
* 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);
|
|
|
|
/*
|
|
* Assert that all required callbacks are present. That makes it a bit
|
|
* easier to keep AMs up to date, e.g. when forward porting them to a new
|
|
* major version.
|
|
*/
|
|
Assert(routine->scan_begin != NULL);
|
|
Assert(routine->scan_end != NULL);
|
|
Assert(routine->scan_rescan != NULL);
|
|
|
|
Assert(routine->parallelscan_estimate != NULL);
|
|
Assert(routine->parallelscan_initialize != NULL);
|
|
Assert(routine->parallelscan_reinitialize != NULL);
|
|
|
|
Assert(routine->index_fetch_begin != NULL);
|
|
Assert(routine->index_fetch_reset != NULL);
|
|
Assert(routine->index_fetch_end != NULL);
|
|
Assert(routine->index_fetch_tuple != NULL);
|
|
|
|
Assert(routine->tuple_satisfies_snapshot != NULL);
|
|
|
|
Assert(routine->tuple_insert != NULL);
|
|
|
|
/*
|
|
* Could be made optional, but would require throwing error during
|
|
* parse-analysis.
|
|
*/
|
|
Assert(routine->tuple_insert_speculative != NULL);
|
|
Assert(routine->tuple_complete_speculative != NULL);
|
|
|
|
Assert(routine->tuple_delete != NULL);
|
|
Assert(routine->tuple_update != NULL);
|
|
Assert(routine->tuple_lock != NULL);
|
|
|
|
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;
|
|
TableScanDesc scandesc;
|
|
HeapTuple tuple;
|
|
ScanKeyData entry[1];
|
|
|
|
/*
|
|
* Search pg_am. We use a heapscan here even though there is an index on
|
|
* name, on the theory that pg_am 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 = table_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;
|
|
|
|
table_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;
|
|
}
|