aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/brin/Makefile2
-rw-r--r--src/backend/access/brin/brin.c161
-rw-r--r--src/backend/access/brin/brin_validate.c152
-rw-r--r--src/backend/access/common/reloptions.c35
-rw-r--r--src/backend/access/gin/Makefile2
-rw-r--r--src/backend/access/gin/ginget.c10
-rw-r--r--src/backend/access/gin/gininsert.c33
-rw-r--r--src/backend/access/gin/ginscan.c41
-rw-r--r--src/backend/access/gin/ginutil.c57
-rw-r--r--src/backend/access/gin/ginvacuum.c21
-rw-r--r--src/backend/access/gin/ginvalidate.c145
-rw-r--r--src/backend/access/gist/Makefile2
-rw-r--r--src/backend/access/gist/gist.c76
-rw-r--r--src/backend/access/gist/gistbuild.c9
-rw-r--r--src/backend/access/gist/gistget.c35
-rw-r--r--src/backend/access/gist/gistscan.c41
-rw-r--r--src/backend/access/gist/gistutil.c11
-rw-r--r--src/backend/access/gist/gistvacuum.c21
-rw-r--r--src/backend/access/gist/gistvalidate.c133
-rw-r--r--src/backend/access/hash/Makefile2
-rw-r--r--src/backend/access/hash/hash.c167
-rw-r--r--src/backend/access/hash/hashutil.c14
-rw-r--r--src/backend/access/hash/hashvalidate.c157
-rw-r--r--src/backend/access/index/Makefile2
-rw-r--r--src/backend/access/index/amapi.c114
-rw-r--r--src/backend/access/index/indexam.c167
-rw-r--r--src/backend/access/nbtree/Makefile2
-rw-r--r--src/backend/access/nbtree/nbtree.c165
-rw-r--r--src/backend/access/nbtree/nbtutils.c13
-rw-r--r--src/backend/access/nbtree/nbtvalidate.c204
-rw-r--r--src/backend/access/spgist/Makefile2
-rw-r--r--src/backend/access/spgist/spginsert.c33
-rw-r--r--src/backend/access/spgist/spgscan.c67
-rw-r--r--src/backend/access/spgist/spgutils.c60
-rw-r--r--src/backend/access/spgist/spgvacuum.c21
-rw-r--r--src/backend/access/spgist/spgvalidate.c129
-rw-r--r--src/backend/catalog/index.c37
-rw-r--r--src/backend/catalog/objectaddress.c1
-rw-r--r--src/backend/catalog/toasting.c1
-rw-r--r--src/backend/commands/cluster.c4
-rw-r--r--src/backend/commands/indexcmds.c22
-rw-r--r--src/backend/commands/opclasscmds.c41
-rw-r--r--src/backend/commands/tablecmds.c6
-rw-r--r--src/backend/commands/typecmds.c1
-rw-r--r--src/backend/executor/execAmi.c16
-rw-r--r--src/backend/executor/nodeIndexscan.c5
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/optimizer/path/costsize.c14
-rw-r--r--src/backend/optimizer/util/plancat.c24
-rw-r--r--src/backend/parser/parse_clause.c1
-rw-r--r--src/backend/parser/parse_utilcmd.c14
-rw-r--r--src/backend/postmaster/autovacuum.c2
-rw-r--r--src/backend/utils/adt/pseudotypes.c27
-rw-r--r--src/backend/utils/adt/ruleutils.c8
-rw-r--r--src/backend/utils/adt/selfuncs.c98
-rw-r--r--src/backend/utils/cache/lsyscache.c1
-rw-r--r--src/backend/utils/cache/relcache.c86
-rw-r--r--src/backend/utils/cache/syscache.c1
-rw-r--r--src/backend/utils/cache/typcache.c1
-rw-r--r--src/backend/utils/sort/sortsupport.c1
-rw-r--r--src/backend/utils/sort/tuplesort.c1
-rw-r--r--src/include/access/amapi.h174
-rw-r--r--src/include/access/brin.h15
-rw-r--r--src/include/access/brin_internal.h34
-rw-r--r--src/include/access/gin_private.h36
-rw-r--r--src/include/access/gist_private.h30
-rw-r--r--src/include/access/gistscan.h11
-rw-r--r--src/include/access/hash.h36
-rw-r--r--src/include/access/nbtree.h44
-rw-r--r--src/include/access/reloptions.h5
-rw-r--r--src/include/access/spgist.h44
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_am.h78
-rw-r--r--src/include/catalog/pg_proc.h198
-rw-r--r--src/include/catalog/pg_type.h2
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/relation.h6
-rw-r--r--src/include/utils/builtins.h2
-rw-r--r--src/include/utils/index_selfuncs.h68
-rw-r--r--src/include/utils/rel.h23
-rw-r--r--src/include/utils/selfuncs.h11
-rw-r--r--src/test/regress/expected/oidjoins.out136
-rw-r--r--src/test/regress/expected/opr_sanity.out128
-rw-r--r--src/test/regress/sql/oidjoins.sql70
-rw-r--r--src/test/regress/sql/opr_sanity.sql115
-rw-r--r--src/tools/findoidjoins/README33
86 files changed, 2347 insertions, 1676 deletions
diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile
index f4572d80a89..5aef925ed42 100644
--- a/src/backend/access/brin/Makefile
+++ b/src/backend/access/brin/Makefile
@@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = brin.o brin_pageops.o brin_revmap.o brin_tuple.o brin_xlog.o \
- brin_minmax.o brin_inclusion.o
+ brin_minmax.o brin_inclusion.o brin_validate.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 60d405ef176..c7409529234 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -16,22 +16,21 @@
#include "postgres.h"
#include "access/brin.h"
-#include "access/brin_internal.h"
#include "access/brin_page.h"
#include "access/brin_pageops.h"
#include "access/brin_xlog.h"
#include "access/reloptions.h"
#include "access/relscan.h"
-#include "access/xact.h"
#include "access/xloginsert.h"
#include "catalog/index.h"
+#include "catalog/pg_am.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
+#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "utils/rel.h"
-#include "utils/snapmgr.h"
/*
@@ -72,6 +71,50 @@ static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
/*
+ * BRIN handler function: return IndexAmRoutine with access method parameters
+ * and callbacks.
+ */
+Datum
+brinhandler(PG_FUNCTION_ARGS)
+{
+ IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+ amroutine->amstrategies = 0;
+ amroutine->amsupport = BRIN_LAST_OPTIONAL_PROCNUM;
+ amroutine->amcanorder = false;
+ amroutine->amcanorderbyop = false;
+ amroutine->amcanbackward = false;
+ amroutine->amcanunique = false;
+ amroutine->amcanmulticol = true;
+ amroutine->amoptionalkey = true;
+ amroutine->amsearcharray = false;
+ amroutine->amsearchnulls = true;
+ amroutine->amstorage = true;
+ amroutine->amclusterable = false;
+ amroutine->ampredlocks = false;
+ amroutine->amkeytype = InvalidOid;
+
+ amroutine->ambuild = brinbuild;
+ amroutine->ambuildempty = brinbuildempty;
+ amroutine->aminsert = brininsert;
+ amroutine->ambulkdelete = brinbulkdelete;
+ amroutine->amvacuumcleanup = brinvacuumcleanup;
+ amroutine->amcanreturn = NULL;
+ amroutine->amcostestimate = brincostestimate;
+ amroutine->amoptions = brinoptions;
+ amroutine->amvalidate = brinvalidate;
+ amroutine->ambeginscan = brinbeginscan;
+ amroutine->amrescan = brinrescan;
+ amroutine->amgettuple = NULL;
+ amroutine->amgetbitmap = bringetbitmap;
+ amroutine->amendscan = brinendscan;
+ amroutine->ammarkpos = NULL;
+ amroutine->amrestrpos = NULL;
+
+ PG_RETURN_POINTER(amroutine);
+}
+
+/*
* A tuple in the heap is being inserted. To keep a brin index up to date,
* we need to obtain the relevant index tuple and compare its stored values
* with those of the new tuple. If the tuple values are not consistent with
@@ -80,15 +123,11 @@ static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
* If the range is not currently summarized (i.e. the revmap returns NULL for
* it), there's nothing to do.
*/
-Datum
-brininsert(PG_FUNCTION_ARGS)
+bool
+brininsert(Relation idxRel, Datum *values, bool *nulls,
+ ItemPointer heaptid, Relation heapRel,
+ IndexUniqueCheck checkUnique)
{
- Relation idxRel = (Relation) PG_GETARG_POINTER(0);
- Datum *values = (Datum *) PG_GETARG_POINTER(1);
- bool *nulls = (bool *) PG_GETARG_POINTER(2);
- ItemPointer heaptid = (ItemPointer) PG_GETARG_POINTER(3);
-
- /* we ignore the rest of our arguments */
BlockNumber pagesPerRange;
BrinDesc *bdesc = NULL;
BrinRevmap *revmap;
@@ -226,7 +265,7 @@ brininsert(PG_FUNCTION_ARGS)
MemoryContextDelete(tupcxt);
}
- return BoolGetDatum(false);
+ return false;
}
/*
@@ -236,12 +275,9 @@ brininsert(PG_FUNCTION_ARGS)
* index was built with. Note that since this cannot be changed while we're
* holding lock on index, it's not necessary to recompute it during brinrescan.
*/
-Datum
-brinbeginscan(PG_FUNCTION_ARGS)
+IndexScanDesc
+brinbeginscan(Relation r, int nkeys, int norderbys)
{
- Relation r = (Relation) PG_GETARG_POINTER(0);
- int nkeys = PG_GETARG_INT32(1);
- int norderbys = PG_GETARG_INT32(2);
IndexScanDesc scan;
BrinOpaque *opaque;
@@ -252,7 +288,7 @@ brinbeginscan(PG_FUNCTION_ARGS)
opaque->bo_bdesc = brin_build_desc(r);
scan->opaque = opaque;
- PG_RETURN_POINTER(scan);
+ return scan;
}
/*
@@ -267,11 +303,9 @@ brinbeginscan(PG_FUNCTION_ARGS)
* unsummarized. Pages in those ranges need to be returned regardless of scan
* keys.
*/
-Datum
-bringetbitmap(PG_FUNCTION_ARGS)
+int64
+bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
Relation idxRel = scan->indexRelation;
Buffer buf = InvalidBuffer;
BrinDesc *bdesc;
@@ -451,20 +485,16 @@ bringetbitmap(PG_FUNCTION_ARGS)
* returns, but we don't have a precise idea of the number of heap tuples
* involved.
*/
- PG_RETURN_INT64(totalpages * 10);
+ return totalpages * 10;
}
/*
* Re-initialize state for a BRIN index scan
*/
-Datum
-brinrescan(PG_FUNCTION_ARGS)
+void
+brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
-
- /* other arguments ignored */
-
/*
* Other index AMs preprocess the scan keys at this point, or sometime
* early during the scan; this lets them optimize by removing redundant
@@ -476,38 +506,19 @@ brinrescan(PG_FUNCTION_ARGS)
if (scankey && scan->numberOfKeys > 0)
memmove(scan->keyData, scankey,
scan->numberOfKeys * sizeof(ScanKeyData));
-
- PG_RETURN_VOID();
}
/*
* Close down a BRIN index scan
*/
-Datum
-brinendscan(PG_FUNCTION_ARGS)
+void
+brinendscan(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
BrinOpaque *opaque = (BrinOpaque *) scan->opaque;
brinRevmapTerminate(opaque->bo_rmAccess);
brin_free_desc(opaque->bo_bdesc);
pfree(opaque);
-
- PG_RETURN_VOID();
-}
-
-Datum
-brinmarkpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "BRIN does not support mark/restore");
- PG_RETURN_VOID();
-}
-
-Datum
-brinrestrpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "BRIN does not support mark/restore");
- PG_RETURN_VOID();
}
/*
@@ -579,12 +590,9 @@ brinbuildCallback(Relation index,
/*
* brinbuild() -- build a new BRIN index.
*/
-Datum
-brinbuild(PG_FUNCTION_ARGS)
+IndexBuildResult *
+brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
- Relation heap = (Relation) PG_GETARG_POINTER(0);
- Relation index = (Relation) PG_GETARG_POINTER(1);
- IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
double reltuples;
double idxtuples;
@@ -663,13 +671,12 @@ brinbuild(PG_FUNCTION_ARGS)
result->heap_tuples = reltuples;
result->index_tuples = idxtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
-Datum
-brinbuildempty(PG_FUNCTION_ARGS)
+void
+brinbuildempty(Relation index)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
Buffer metabuf;
/* An empty BRIN index has a metapage only. */
@@ -686,8 +693,6 @@ brinbuildempty(PG_FUNCTION_ARGS)
END_CRIT_SECTION();
UnlockReleaseBuffer(metabuf);
-
- PG_RETURN_VOID();
}
/*
@@ -699,35 +704,29 @@ brinbuildempty(PG_FUNCTION_ARGS)
* tuple is deleted), meaning the need to re-run summarization on the affected
* range. Would need to add an extra flag in brintuples for that.
*/
-Datum
-brinbulkdelete(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+brinbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback, void *callback_state)
{
- /* other arguments are not currently used */
- IndexBulkDeleteResult *stats =
- (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
-
/* allocate stats if first time through, else re-use existing struct */
if (stats == NULL)
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
* This routine is in charge of "vacuuming" a BRIN index: we just summarize
* ranges that are currently unsummarized.
*/
-Datum
-brinvacuumcleanup(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats =
- (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
Relation heapRel;
/* No-op in ANALYZE ONLY mode */
if (info->analyze_only)
- PG_RETURN_POINTER(stats);
+ return stats;
if (!stats)
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
@@ -744,17 +743,15 @@ brinvacuumcleanup(PG_FUNCTION_ARGS)
heap_close(heapRel, AccessShareLock);
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
* reloptions processor for BRIN indexes
*/
-Datum
-brinoptions(PG_FUNCTION_ARGS)
+bytea *
+brinoptions(Datum reloptions, bool validate)
{
- Datum reloptions = PG_GETARG_DATUM(0);
- bool validate = PG_GETARG_BOOL(1);
relopt_value *options;
BrinOptions *rdopts;
int numoptions;
@@ -767,7 +764,7 @@ brinoptions(PG_FUNCTION_ARGS)
/* if none set, we're done */
if (numoptions == 0)
- PG_RETURN_NULL();
+ return NULL;
rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
@@ -776,7 +773,7 @@ brinoptions(PG_FUNCTION_ARGS)
pfree(options);
- PG_RETURN_BYTEA_P(rdopts);
+ return (bytea *) rdopts;
}
/*
diff --git a/src/backend/access/brin/brin_validate.c b/src/backend/access/brin/brin_validate.c
new file mode 100644
index 00000000000..956ecb95a01
--- /dev/null
+++ b/src/backend/access/brin/brin_validate.c
@@ -0,0 +1,152 @@
+/*-------------------------------------------------------------------------
+ *
+ * brin_validate.c
+ * Opclass validator for BRIN.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/brin/brin_validate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/brin_internal.h"
+#include "access/htup_details.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_opclass.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Validator for a BRIN opclass.
+ */
+bool
+brinvalidate(Oid opclassoid)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ int numclassops;
+ int32 classfuncbits;
+ CatCList *proclist,
+ *oprlist;
+ int i,
+ j;
+
+ /* Fetch opclass information */
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+ ReleaseSysCache(classtup);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ /* We'll track the ops and functions belonging to the named opclass */
+ numclassops = 0;
+ classfuncbits = 0;
+
+ /* Check support functions */
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* Check that only allowed procedure numbers exist */
+ if (procform->amprocnum < 1 ||
+ procform->amprocnum > BRIN_LAST_OPTIONAL_PROCNUM)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("brin opfamily %u contains invalid support number %d for procedure %u",
+ opfamilyoid,
+ procform->amprocnum, procform->amproc)));
+
+ /* Remember functions that are specifically for the named opclass */
+ if (procform->amproclefttype == opcintype &&
+ procform->amprocrighttype == opcintype)
+ classfuncbits |= (1 << procform->amprocnum);
+ }
+
+ /* Check operators */
+ for (i = 0; i < oprlist->n_members; i++)
+ {
+ HeapTuple oprtup = &oprlist->members[i]->tuple;
+ Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ bool found = false;
+
+ /* TODO: Check that only allowed strategy numbers exist */
+ if (oprform->amopstrategy < 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("brin opfamily %u contains invalid strategy number %d for operator %u",
+ opfamilyoid,
+ oprform->amopstrategy, oprform->amopopr)));
+
+ /* TODO: check more thoroughly for missing support functions */
+ for (j = 0; j < proclist->n_members; j++)
+ {
+ HeapTuple proctup = &proclist->members[j]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* note only the operator's lefttype matters */
+ if (procform->amproclefttype == oprform->amoplefttype &&
+ procform->amprocrighttype == oprform->amoplefttype)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("brin opfamily %u lacks support function for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* brin doesn't support ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH ||
+ OidIsValid(oprform->amopsortfamily))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("brin opfamily %u contains invalid ORDER BY specification for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* Count operators that are specifically for the named opclass */
+ if (oprform->amoplefttype == opcintype &&
+ oprform->amoprighttype == opcintype)
+ numclassops++;
+ }
+
+ /* Check that the named opclass is complete */
+ if (numclassops == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("brin opclass %u is missing operator(s)",
+ opclassoid)));
+ for (i = 1; i <= BRIN_MANDATORY_NPROCS; i++)
+ {
+ if ((classfuncbits & (1 << i)) != 0)
+ continue; /* got it */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("brin opclass %u is missing required support function %d",
+ opclassoid, i)));
+ }
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+
+ return true;
+}
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 45fba905859..86b9ae1c3ce 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -887,11 +887,13 @@ untransformRelOptions(Datum options)
* other uses, consider grabbing the rd_options pointer from the relcache entry
* instead.
*
- * tupdesc is pg_class' tuple descriptor. amoptions is the amoptions regproc
- * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
+ * tupdesc is pg_class' tuple descriptor. amoptions is a pointer to the index
+ * AM's options parser function in the case of a tuple corresponding to an
+ * index, or NULL otherwise.
*/
bytea *
-extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
+extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
+ amoptions_function amoptions)
{
bytea *options;
bool isnull;
@@ -1374,39 +1376,20 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
/*
* Parse options for indexes.
*
- * amoptions Oid of option parser
+ * amoptions index AM's option parser function
* reloptions options as text[] datum
* validate error flag
*/
bytea *
-index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
{
- FmgrInfo flinfo;
- FunctionCallInfoData fcinfo;
- Datum result;
-
- Assert(RegProcedureIsValid(amoptions));
+ Assert(amoptions != NULL);
/* Assume function is strict */
if (!PointerIsValid(DatumGetPointer(reloptions)))
return NULL;
- /* Can't use OidFunctionCallN because we might get a NULL result */
- fmgr_info(amoptions, &flinfo);
-
- InitFunctionCallInfoData(fcinfo, &flinfo, 2, InvalidOid, NULL, NULL);
-
- fcinfo.arg[0] = reloptions;
- fcinfo.arg[1] = BoolGetDatum(validate);
- fcinfo.argnull[0] = false;
- fcinfo.argnull[1] = false;
-
- result = FunctionCallInvoke(&fcinfo);
-
- if (fcinfo.isnull || DatumGetPointer(result) == NULL)
- return NULL;
-
- return DatumGetByteaP(result);
+ return amoptions(reloptions, validate);
}
/*
diff --git a/src/backend/access/gin/Makefile b/src/backend/access/gin/Makefile
index db4f496a4db..0895003cd1a 100644
--- a/src/backend/access/gin/Makefile
+++ b/src/backend/access/gin/Makefile
@@ -14,6 +14,6 @@ include $(top_builddir)/src/Makefile.global
OBJS = ginutil.o gininsert.o ginxlog.o ginentrypage.o gindatapage.o \
ginbtree.o ginscan.o ginget.o ginvacuum.o ginarrayproc.o \
- ginbulk.o ginfast.o ginpostinglist.o ginlogic.o
+ ginbulk.o ginfast.o ginpostinglist.o ginlogic.o ginvalidate.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 5bb8f1f0927..a6756d594d2 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1772,11 +1772,9 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
#define GinIsVoidRes(s) ( ((GinScanOpaque) scan->opaque)->isVoidRes )
-Datum
-gingetbitmap(PG_FUNCTION_ARGS)
+int64
+gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
GinScanOpaque so = (GinScanOpaque) scan->opaque;
int64 ntids;
ItemPointerData iptr;
@@ -1790,7 +1788,7 @@ gingetbitmap(PG_FUNCTION_ARGS)
ginNewScanKey(scan);
if (GinIsVoidRes(scan))
- PG_RETURN_INT64(0);
+ return 0;
ntids = 0;
@@ -1827,5 +1825,5 @@ gingetbitmap(PG_FUNCTION_ARGS)
ntids++;
}
- PG_RETURN_INT64(ntids);
+ return ntids;
}
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index 8bcd1590327..cd21e0e6552 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -306,12 +306,9 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
MemoryContextSwitchTo(oldCtx);
}
-Datum
-ginbuild(PG_FUNCTION_ARGS)
+IndexBuildResult *
+ginbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
- Relation heap = (Relation) PG_GETARG_POINTER(0);
- Relation index = (Relation) PG_GETARG_POINTER(1);
- IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
double reltuples;
GinBuildState buildstate;
@@ -429,16 +426,15 @@ ginbuild(PG_FUNCTION_ARGS)
result->heap_tuples = reltuples;
result->index_tuples = buildstate.indtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
* ginbuildempty() -- build an empty gin index in the initialization fork
*/
-Datum
-ginbuildempty(PG_FUNCTION_ARGS)
+void
+ginbuildempty(Relation index)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
Buffer RootBuffer,
MetaBuffer;
@@ -463,8 +459,6 @@ ginbuildempty(PG_FUNCTION_ARGS)
/* Unlock and release the buffers. */
UnlockReleaseBuffer(MetaBuffer);
UnlockReleaseBuffer(RootBuffer);
-
- PG_RETURN_VOID();
}
/*
@@ -489,18 +483,11 @@ ginHeapTupleInsert(GinState *ginstate, OffsetNumber attnum,
item, 1, NULL);
}
-Datum
-gininsert(PG_FUNCTION_ARGS)
+bool
+gininsert(Relation index, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
- Datum *values = (Datum *) PG_GETARG_POINTER(1);
- bool *isnull = (bool *) PG_GETARG_POINTER(2);
- ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
-
-#ifdef NOT_USED
- Relation heapRel = (Relation) PG_GETARG_POINTER(4);
- IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
-#endif
GinState ginstate;
MemoryContext oldCtx;
MemoryContext insertCtx;
@@ -541,5 +528,5 @@ gininsert(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(insertCtx);
- PG_RETURN_BOOL(false);
+ return false;
}
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 458540415f1..3449a30e419 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -21,12 +21,9 @@
#include "utils/rel.h"
-Datum
-ginbeginscan(PG_FUNCTION_ARGS)
+IndexScanDesc
+ginbeginscan(Relation rel, int nkeys, int norderbys)
{
- Relation rel = (Relation) PG_GETARG_POINTER(0);
- int nkeys = PG_GETARG_INT32(1);
- int norderbys = PG_GETARG_INT32(2);
IndexScanDesc scan;
GinScanOpaque so;
@@ -53,7 +50,7 @@ ginbeginscan(PG_FUNCTION_ARGS)
scan->opaque = so;
- PG_RETURN_POINTER(scan);
+ return scan;
}
/*
@@ -417,13 +414,10 @@ ginNewScanKey(IndexScanDesc scan)
pgstat_count_index_scan(scan->indexRelation);
}
-Datum
-ginrescan(PG_FUNCTION_ARGS)
+void
+ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
-
- /* remaining arguments are ignored */
GinScanOpaque so = (GinScanOpaque) scan->opaque;
ginFreeScanKeys(so);
@@ -433,15 +427,12 @@ ginrescan(PG_FUNCTION_ARGS)
memmove(scan->keyData, scankey,
scan->numberOfKeys * sizeof(ScanKeyData));
}
-
- PG_RETURN_VOID();
}
-Datum
-ginendscan(PG_FUNCTION_ARGS)
+void
+ginendscan(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GinScanOpaque so = (GinScanOpaque) scan->opaque;
ginFreeScanKeys(so);
@@ -450,20 +441,4 @@ ginendscan(PG_FUNCTION_ARGS)
MemoryContextDelete(so->keyCtx);
pfree(so);
-
- PG_RETURN_VOID();
-}
-
-Datum
-ginmarkpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "GIN does not support mark/restore");
- PG_RETURN_VOID();
-}
-
-Datum
-ginrestrpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "GIN does not support mark/restore");
- PG_RETURN_VOID();
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index c1c69cef1d1..94502678abb 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* ginutil.c
- * utilities routines for the postgres inverted index access method.
+ * Utility routines for the Postgres inverted index access method.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
@@ -22,9 +22,54 @@
#include "miscadmin.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
+#include "utils/index_selfuncs.h"
/*
+ * GIN handler function: return IndexAmRoutine with access method parameters
+ * and callbacks.
+ */
+Datum
+ginhandler(PG_FUNCTION_ARGS)
+{
+ IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+ amroutine->amstrategies = 0;
+ amroutine->amsupport = 6;
+ amroutine->amcanorder = false;
+ amroutine->amcanorderbyop = false;
+ amroutine->amcanbackward = false;
+ amroutine->amcanunique = false;
+ amroutine->amcanmulticol = true;
+ amroutine->amoptionalkey = true;
+ amroutine->amsearcharray = false;
+ amroutine->amsearchnulls = false;
+ amroutine->amstorage = true;
+ amroutine->amclusterable = false;
+ amroutine->ampredlocks = false;
+ amroutine->amkeytype = InvalidOid;
+
+ amroutine->ambuild = ginbuild;
+ amroutine->ambuildempty = ginbuildempty;
+ amroutine->aminsert = gininsert;
+ amroutine->ambulkdelete = ginbulkdelete;
+ amroutine->amvacuumcleanup = ginvacuumcleanup;
+ amroutine->amcanreturn = NULL;
+ amroutine->amcostestimate = gincostestimate;
+ amroutine->amoptions = ginoptions;
+ amroutine->amvalidate = ginvalidate;
+ amroutine->ambeginscan = ginbeginscan;
+ amroutine->amrescan = ginrescan;
+ amroutine->amgettuple = NULL;
+ amroutine->amgetbitmap = gingetbitmap;
+ amroutine->amendscan = ginendscan;
+ amroutine->ammarkpos = NULL;
+ amroutine->amrestrpos = NULL;
+
+ PG_RETURN_POINTER(amroutine);
+}
+
+/*
* initGinState: fill in an empty GinState struct to describe the index
*
* Note: assorted subsidiary data is allocated in the CurrentMemoryContext.
@@ -516,11 +561,9 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-Datum
-ginoptions(PG_FUNCTION_ARGS)
+bytea *
+ginoptions(Datum reloptions, bool validate)
{
- Datum reloptions = PG_GETARG_DATUM(0);
- bool validate = PG_GETARG_BOOL(1);
relopt_value *options;
GinOptions *rdopts;
int numoptions;
@@ -535,7 +578,7 @@ ginoptions(PG_FUNCTION_ARGS)
/* if none set, we're done */
if (numoptions == 0)
- PG_RETURN_NULL();
+ return NULL;
rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
@@ -544,7 +587,7 @@ ginoptions(PG_FUNCTION_ARGS)
pfree(options);
- PG_RETURN_BYTEA_P(rdopts);
+ return (bytea *) rdopts;
}
/*
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index 9e496dc736f..f0a246f3f3c 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -513,13 +513,10 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
return (tmppage == origpage) ? NULL : tmppage;
}
-Datum
-ginbulkdelete(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback, void *callback_state)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- void *callback_state = (void *) PG_GETARG_POINTER(3);
Relation index = info->index;
BlockNumber blkno = GIN_ROOT_BLKNO;
GinVacuumState gvs;
@@ -634,14 +631,12 @@ ginbulkdelete(PG_FUNCTION_ARGS)
MemoryContextDelete(gvs.tmpCxt);
- PG_RETURN_POINTER(gvs.result);
+ return gvs.result;
}
-Datum
-ginvacuumcleanup(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
Relation index = info->index;
bool needLock;
BlockNumber npages,
@@ -661,7 +656,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
initGinState(&ginstate, index);
ginInsertCleanup(&ginstate, true, true, stats);
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
@@ -746,5 +741,5 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
if (needLock)
UnlockRelationForExtension(index, ExclusiveLock);
- PG_RETURN_POINTER(stats);
+ return stats;
}
diff --git a/src/backend/access/gin/ginvalidate.c b/src/backend/access/gin/ginvalidate.c
new file mode 100644
index 00000000000..e934fe824b5
--- /dev/null
+++ b/src/backend/access/gin/ginvalidate.c
@@ -0,0 +1,145 @@
+/*-------------------------------------------------------------------------
+ *
+ * ginvalidate.c
+ * Opclass validator for GIN.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/gin/ginvalidate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gin_private.h"
+#include "access/htup_details.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_opclass.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Validator for a GIN opclass.
+ */
+bool
+ginvalidate(Oid opclassoid)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ int numclassops;
+ int32 classfuncbits;
+ CatCList *proclist,
+ *oprlist;
+ int i;
+
+ /* Fetch opclass information */
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+ ReleaseSysCache(classtup);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ /* We'll track the ops and functions belonging to the named opclass */
+ numclassops = 0;
+ classfuncbits = 0;
+
+ /* Check support functions */
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* Check that only allowed procedure numbers exist */
+ if (procform->amprocnum < 1 ||
+ procform->amprocnum > GINNProcs)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gin opfamily %u contains invalid support number %d for procedure %u",
+ opfamilyoid,
+ procform->amprocnum, procform->amproc)));
+
+ /* Remember functions that are specifically for the named opclass */
+ if (procform->amproclefttype == opcintype &&
+ procform->amprocrighttype == opcintype)
+ classfuncbits |= (1 << procform->amprocnum);
+ }
+
+ /* Check operators */
+ for (i = 0; i < oprlist->n_members; i++)
+ {
+ HeapTuple oprtup = &oprlist->members[i]->tuple;
+ Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+
+ /* TODO: Check that only allowed strategy numbers exist */
+ if (oprform->amopstrategy < 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gin opfamily %u contains invalid strategy number %d for operator %u",
+ opfamilyoid,
+ oprform->amopstrategy, oprform->amopopr)));
+
+ /* gin doesn't support ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH ||
+ OidIsValid(oprform->amopsortfamily))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gin opfamily %u contains invalid ORDER BY specification for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* Count operators that are specifically for the named opclass */
+ if (oprform->amoplefttype == opcintype &&
+ oprform->amoprighttype == opcintype)
+ numclassops++;
+ }
+
+ /* Check that the named opclass is complete */
+
+ /* XXX needs work: we need to detect applicability of ANYARRAY operators */
+#ifdef NOT_USED
+ if (numclassops == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gin opclass %u is missing operator(s)",
+ opclassoid)));
+#endif
+
+ for (i = 1; i <= GINNProcs; i++)
+ {
+ if ((classfuncbits & (1 << i)) != 0)
+ continue; /* got it */
+ if (i == GIN_COMPARE_PARTIAL_PROC)
+ continue; /* optional method */
+ if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
+ continue; /* don't need to have both, see check below
+ * loop */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gin opclass %u is missing required support function %d",
+ opclassoid, i)));
+ }
+ if ((classfuncbits & (1 << GIN_CONSISTENT_PROC)) == 0 &&
+ (classfuncbits & (1 << GIN_TRICONSISTENT_PROC)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gin opclass %u is missing required support function",
+ opclassoid)));
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+
+ return true;
+}
diff --git a/src/backend/access/gist/Makefile b/src/backend/access/gist/Makefile
index cc9468ffb19..21875bf7aee 100644
--- a/src/backend/access/gist/Makefile
+++ b/src/backend/access/gist/Makefile
@@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = gist.o gistutil.o gistxlog.o gistvacuum.o gistget.o gistscan.o \
- gistproc.o gistsplit.o gistbuild.o gistbuildbuffers.o
+ gistproc.o gistsplit.o gistbuild.o gistbuildbuffers.o gistvalidate.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 834d9d33358..996363c2ded 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -14,17 +14,15 @@
*/
#include "postgres.h"
-#include "access/genam.h"
#include "access/gist_private.h"
-#include "access/xloginsert.h"
-#include "catalog/index.h"
+#include "access/gistscan.h"
#include "catalog/pg_collation.h"
#include "miscadmin.h"
-#include "storage/bufmgr.h"
-#include "storage/indexfsm.h"
+#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+
/* non-export function prototypes */
static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate);
static bool gistinserttuple(GISTInsertState *state, GISTInsertStack *stack,
@@ -50,6 +48,50 @@ static void gistvacuumpage(Relation rel, Page page, Buffer buffer);
/*
+ * GiST handler function: return IndexAmRoutine with access method parameters
+ * and callbacks.
+ */
+Datum
+gisthandler(PG_FUNCTION_ARGS)
+{
+ IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+ amroutine->amstrategies = 0;
+ amroutine->amsupport = 9;
+ amroutine->amcanorder = false;
+ amroutine->amcanorderbyop = true;
+ amroutine->amcanbackward = false;
+ amroutine->amcanunique = false;
+ amroutine->amcanmulticol = true;
+ amroutine->amoptionalkey = true;
+ amroutine->amsearcharray = false;
+ amroutine->amsearchnulls = true;
+ amroutine->amstorage = true;
+ amroutine->amclusterable = true;
+ amroutine->ampredlocks = false;
+ amroutine->amkeytype = InvalidOid;
+
+ amroutine->ambuild = gistbuild;
+ amroutine->ambuildempty = gistbuildempty;
+ amroutine->aminsert = gistinsert;
+ amroutine->ambulkdelete = gistbulkdelete;
+ amroutine->amvacuumcleanup = gistvacuumcleanup;
+ amroutine->amcanreturn = gistcanreturn;
+ amroutine->amcostestimate = gistcostestimate;
+ amroutine->amoptions = gistoptions;
+ amroutine->amvalidate = gistvalidate;
+ amroutine->ambeginscan = gistbeginscan;
+ amroutine->amrescan = gistrescan;
+ amroutine->amgettuple = gistgettuple;
+ amroutine->amgetbitmap = gistgetbitmap;
+ amroutine->amendscan = gistendscan;
+ amroutine->ammarkpos = NULL;
+ amroutine->amrestrpos = NULL;
+
+ PG_RETURN_POINTER(amroutine);
+}
+
+/*
* Create and return a temporary memory context for use by GiST. We
* _always_ invoke user-provided methods in a temporary memory
* context, so that memory leaks in those functions cannot cause
@@ -70,10 +112,9 @@ createTempGistContext(void)
/*
* gistbuildempty() -- build an empty gist index in the initialization fork
*/
-Datum
-gistbuildempty(PG_FUNCTION_ARGS)
+void
+gistbuildempty(Relation index)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
Buffer buffer;
/* Initialize the root page */
@@ -89,8 +130,6 @@ gistbuildempty(PG_FUNCTION_ARGS)
/* Unlock and release the buffer */
UnlockReleaseBuffer(buffer);
-
- PG_RETURN_VOID();
}
/*
@@ -99,18 +138,11 @@ gistbuildempty(PG_FUNCTION_ARGS)
* This is the public interface routine for tuple insertion in GiSTs.
* It doesn't do any work; just locks the relation and passes the buck.
*/
-Datum
-gistinsert(PG_FUNCTION_ARGS)
+bool
+gistinsert(Relation r, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique)
{
- Relation r = (Relation) PG_GETARG_POINTER(0);
- Datum *values = (Datum *) PG_GETARG_POINTER(1);
- bool *isnull = (bool *) PG_GETARG_POINTER(2);
- ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
-
-#ifdef NOT_USED
- Relation heapRel = (Relation) PG_GETARG_POINTER(4);
- IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
-#endif
IndexTuple itup;
GISTSTATE *giststate;
MemoryContext oldCxt;
@@ -136,7 +168,7 @@ gistinsert(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldCxt);
freeGISTstate(giststate);
- PG_RETURN_BOOL(false);
+ return false;
}
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index 98ea0cb2d14..4e43a6932a4 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -109,12 +109,9 @@ static BlockNumber gistGetParent(GISTBuildState *buildstate, BlockNumber child);
* but switches to more efficient buffering build algorithm after a certain
* number of tuples (unless buffering mode is disabled).
*/
-Datum
-gistbuild(PG_FUNCTION_ARGS)
+IndexBuildResult *
+gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
- Relation heap = (Relation) PG_GETARG_POINTER(0);
- Relation index = (Relation) PG_GETARG_POINTER(1);
- IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
double reltuples;
GISTBuildState buildstate;
@@ -232,7 +229,7 @@ gistbuild(PG_FUNCTION_ARGS)
result->heap_tuples = reltuples;
result->index_tuples = (double) buildstate.indtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 21f9de511ff..41b83431b61 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -618,18 +618,16 @@ getNextNearest(IndexScanDesc scan)
/*
* gistgettuple() -- Get the next tuple in the scan
*/
-Datum
-gistgettuple(PG_FUNCTION_ARGS)
+bool
+gistgettuple(IndexScanDesc scan, ScanDirection dir)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
if (dir != ForwardScanDirection)
elog(ERROR, "GiST only supports forward scan direction");
if (!so->qual_ok)
- PG_RETURN_BOOL(false);
+ return false;
if (so->firstCall)
{
@@ -651,7 +649,7 @@ gistgettuple(PG_FUNCTION_ARGS)
if (scan->numberOfOrderBys > 0)
{
/* Must fetch tuples in strict distance order */
- PG_RETURN_BOOL(getNextNearest(scan));
+ return getNextNearest(scan);
}
else
{
@@ -688,7 +686,7 @@ gistgettuple(PG_FUNCTION_ARGS)
so->curPageData++;
- PG_RETURN_BOOL(true);
+ return true;
}
/*
@@ -726,7 +724,7 @@ gistgettuple(PG_FUNCTION_ARGS)
item = getNextGISTSearchItem(so);
if (!item)
- PG_RETURN_BOOL(false);
+ return false;
CHECK_FOR_INTERRUPTS();
@@ -750,17 +748,15 @@ gistgettuple(PG_FUNCTION_ARGS)
/*
* gistgetbitmap() -- Get a bitmap of all heap tuple locations
*/
-Datum
-gistgetbitmap(PG_FUNCTION_ARGS)
+int64
+gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
int64 ntids = 0;
GISTSearchItem fakeItem;
if (!so->qual_ok)
- PG_RETURN_INT64(0);
+ return 0;
pgstat_count_index_scan(scan->indexRelation);
@@ -791,7 +787,7 @@ gistgetbitmap(PG_FUNCTION_ARGS)
pfree(item);
}
- PG_RETURN_INT64(ntids);
+ return ntids;
}
/*
@@ -799,14 +795,11 @@ gistgetbitmap(PG_FUNCTION_ARGS)
*
* Opclasses that implement a fetch function support index-only scans.
*/
-Datum
-gistcanreturn(PG_FUNCTION_ARGS)
+bool
+gistcanreturn(Relation index, int attno)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
- int attno = PG_GETARG_INT32(1);
-
if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
- PG_RETURN_BOOL(true);
+ return true;
else
- PG_RETURN_BOOL(false);
+ return false;
}
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
index 8ab41838361..31758a1c8fe 100644
--- a/src/backend/access/gist/gistscan.c
+++ b/src/backend/access/gist/gistscan.c
@@ -54,12 +54,9 @@ pairingheap_GISTSearchItem_cmp(const pairingheap_node *a, const pairingheap_node
* Index AM API functions for scanning GiST indexes
*/
-Datum
-gistbeginscan(PG_FUNCTION_ARGS)
+IndexScanDesc
+gistbeginscan(Relation r, int nkeys, int norderbys)
{
- Relation r = (Relation) PG_GETARG_POINTER(0);
- int nkeys = PG_GETARG_INT32(1);
- int norderbys = PG_GETARG_INT32(2);
IndexScanDesc scan;
GISTSTATE *giststate;
GISTScanOpaque so;
@@ -107,16 +104,13 @@ gistbeginscan(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldCxt);
- PG_RETURN_POINTER(scan);
+ return scan;
}
-Datum
-gistrescan(PG_FUNCTION_ARGS)
+void
+gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
+ ScanKey orderbys, int norderbys)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanKey key = (ScanKey) PG_GETARG_POINTER(1);
- ScanKey orderbys = (ScanKey) PG_GETARG_POINTER(3);
-
/* nkeys and norderbys arguments are ignored */
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
bool first_time;
@@ -314,28 +308,11 @@ gistrescan(PG_FUNCTION_ARGS)
if (!first_time)
pfree(fn_extras);
}
-
- PG_RETURN_VOID();
-}
-
-Datum
-gistmarkpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "GiST does not support mark/restore");
- PG_RETURN_VOID();
}
-Datum
-gistrestrpos(PG_FUNCTION_ARGS)
+void
+gistendscan(IndexScanDesc scan)
{
- elog(ERROR, "GiST does not support mark/restore");
- PG_RETURN_VOID();
-}
-
-Datum
-gistendscan(PG_FUNCTION_ARGS)
-{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
/*
@@ -343,6 +320,4 @@ gistendscan(PG_FUNCTION_ARGS)
* as well as the queueCxt if there is a separate context for it.
*/
freeGISTstate(so->giststate);
-
- PG_RETURN_VOID();
}
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index 0995e0355cf..fac166d4c29 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -808,11 +808,9 @@ gistNewBuffer(Relation r)
return buffer;
}
-Datum
-gistoptions(PG_FUNCTION_ARGS)
+bytea *
+gistoptions(Datum reloptions, bool validate)
{
- Datum reloptions = PG_GETARG_DATUM(0);
- bool validate = PG_GETARG_BOOL(1);
relopt_value *options;
GiSTOptions *rdopts;
int numoptions;
@@ -826,7 +824,7 @@ gistoptions(PG_FUNCTION_ARGS)
/* if none set, we're done */
if (numoptions == 0)
- PG_RETURN_NULL();
+ return NULL;
rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
@@ -835,8 +833,7 @@ gistoptions(PG_FUNCTION_ARGS)
pfree(options);
- PG_RETURN_BYTEA_P(rdopts);
-
+ return (bytea *) rdopts;
}
/*
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index 346f6d96f4d..7947ff9dbe6 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -25,11 +25,9 @@
/*
* VACUUM cleanup: update FSM
*/
-Datum
-gistvacuumcleanup(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
Relation rel = info->index;
BlockNumber npages,
blkno;
@@ -38,7 +36,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
/* No-op in ANALYZE ONLY mode */
if (info->analyze_only)
- PG_RETURN_POINTER(stats);
+ return stats;
/* Set up all-zero stats if gistbulkdelete wasn't called */
if (stats == NULL)
@@ -98,7 +96,7 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock);
- PG_RETURN_POINTER(stats);
+ return stats;
}
typedef struct GistBDItem
@@ -137,13 +135,10 @@ pushStackIfSplited(Page page, GistBDItem *stack)
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-gistbulkdelete(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback, void *callback_state)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- void *callback_state = (void *) PG_GETARG_POINTER(3);
Relation rel = info->index;
GistBDItem *stack,
*ptr;
@@ -276,5 +271,5 @@ gistbulkdelete(PG_FUNCTION_ARGS)
vacuum_delay_point();
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
diff --git a/src/backend/access/gist/gistvalidate.c b/src/backend/access/gist/gistvalidate.c
new file mode 100644
index 00000000000..86b5aeaec5c
--- /dev/null
+++ b/src/backend/access/gist/gistvalidate.c
@@ -0,0 +1,133 @@
+/*-------------------------------------------------------------------------
+ *
+ * gistvalidate.c
+ * Opclass validator for GiST.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/gist/gistvalidate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gist_private.h"
+#include "access/htup_details.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_opclass.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Validator for a GiST opclass.
+ */
+bool
+gistvalidate(Oid opclassoid)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ int numclassops;
+ int32 classfuncbits;
+ CatCList *proclist,
+ *oprlist;
+ int i;
+
+ /* Fetch opclass information */
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+ ReleaseSysCache(classtup);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ /* We'll track the ops and functions belonging to the named opclass */
+ numclassops = 0;
+ classfuncbits = 0;
+
+ /* Check support functions */
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* Check that only allowed procedure numbers exist */
+ if (procform->amprocnum < 1 ||
+ procform->amprocnum > GISTNProcs)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gist opfamily %u contains invalid support number %d for procedure %u",
+ opfamilyoid,
+ procform->amprocnum, procform->amproc)));
+
+ /* Remember functions that are specifically for the named opclass */
+ if (procform->amproclefttype == opcintype &&
+ procform->amprocrighttype == opcintype)
+ classfuncbits |= (1 << procform->amprocnum);
+ }
+
+ /* Check operators */
+ for (i = 0; i < oprlist->n_members; i++)
+ {
+ HeapTuple oprtup = &oprlist->members[i]->tuple;
+ Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+
+ /* TODO: Check that only allowed strategy numbers exist */
+ if (oprform->amopstrategy < 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gist opfamily %u contains invalid strategy number %d for operator %u",
+ opfamilyoid,
+ oprform->amopstrategy, oprform->amopopr)));
+
+ /* GiST supports ORDER BY operators, but must have distance proc */
+ if (oprform->amoppurpose != AMOP_SEARCH &&
+ oprform->amoplefttype == opcintype &&
+ oprform->amoprighttype == opcintype &&
+ (classfuncbits & (1 << GIST_DISTANCE_PROC)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gist opfamily %u contains unsupported ORDER BY specification for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* Count operators that are specifically for the named opclass */
+ /* XXX we consider only lefttype here */
+ if (oprform->amoplefttype == opcintype)
+ numclassops++;
+ }
+
+ /* Check that the named opclass is complete */
+ if (numclassops == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gist opclass %u is missing operator(s)",
+ opclassoid)));
+ for (i = 1; i <= GISTNProcs; i++)
+ {
+ if ((classfuncbits & (1 << i)) != 0)
+ continue; /* got it */
+ if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC)
+ continue; /* optional methods */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("gist opclass %u is missing required support function %d",
+ opclassoid, i)));
+ }
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+
+ return true;
+}
diff --git a/src/backend/access/hash/Makefile b/src/backend/access/hash/Makefile
index 82297606dc1..5d3bd94d3ee 100644
--- a/src/backend/access/hash/Makefile
+++ b/src/backend/access/hash/Makefile
@@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = hash.o hashfunc.o hashinsert.o hashovfl.o hashpage.o hashscan.o \
- hashsearch.o hashsort.o hashutil.o
+ hashsearch.o hashsort.o hashutil.o hashvalidate.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 1d21ea155b4..3d48c4f0310 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -22,9 +22,8 @@
#include "access/relscan.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
-#include "optimizer/cost.h"
#include "optimizer/plancat.h"
-#include "storage/bufmgr.h"
+#include "utils/index_selfuncs.h"
#include "utils/rel.h"
@@ -44,14 +43,55 @@ static void hashbuildCallback(Relation index,
/*
- * hashbuild() -- build a new hash index.
+ * Hash handler function: return IndexAmRoutine with access method parameters
+ * and callbacks.
*/
Datum
-hashbuild(PG_FUNCTION_ARGS)
+hashhandler(PG_FUNCTION_ARGS)
+{
+ IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+ amroutine->amstrategies = 1;
+ amroutine->amsupport = 1;
+ amroutine->amcanorder = false;
+ amroutine->amcanorderbyop = false;
+ amroutine->amcanbackward = true;
+ amroutine->amcanunique = false;
+ amroutine->amcanmulticol = false;
+ amroutine->amoptionalkey = false;
+ amroutine->amsearcharray = false;
+ amroutine->amsearchnulls = false;
+ amroutine->amstorage = false;
+ amroutine->amclusterable = false;
+ amroutine->ampredlocks = false;
+ amroutine->amkeytype = INT4OID;
+
+ amroutine->ambuild = hashbuild;
+ amroutine->ambuildempty = hashbuildempty;
+ amroutine->aminsert = hashinsert;
+ amroutine->ambulkdelete = hashbulkdelete;
+ amroutine->amvacuumcleanup = hashvacuumcleanup;
+ amroutine->amcanreturn = NULL;
+ amroutine->amcostestimate = hashcostestimate;
+ amroutine->amoptions = hashoptions;
+ amroutine->amvalidate = hashvalidate;
+ amroutine->ambeginscan = hashbeginscan;
+ amroutine->amrescan = hashrescan;
+ amroutine->amgettuple = hashgettuple;
+ amroutine->amgetbitmap = hashgetbitmap;
+ amroutine->amendscan = hashendscan;
+ amroutine->ammarkpos = NULL;
+ amroutine->amrestrpos = NULL;
+
+ PG_RETURN_POINTER(amroutine);
+}
+
+/*
+ * hashbuild() -- build a new hash index.
+ */
+IndexBuildResult *
+hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
- Relation heap = (Relation) PG_GETARG_POINTER(0);
- Relation index = (Relation) PG_GETARG_POINTER(1);
- IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
BlockNumber relpages;
double reltuples;
@@ -112,20 +152,16 @@ hashbuild(PG_FUNCTION_ARGS)
result->heap_tuples = reltuples;
result->index_tuples = buildstate.indtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
* hashbuildempty() -- build an empty hash index in the initialization fork
*/
-Datum
-hashbuildempty(PG_FUNCTION_ARGS)
+void
+hashbuildempty(Relation index)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
-
_hash_metapinit(index, 0, INIT_FORKNUM);
-
- PG_RETURN_VOID();
}
/*
@@ -167,18 +203,11 @@ hashbuildCallback(Relation index,
* Hash on the heap tuple's key, form an index tuple with hash code.
* Find the appropriate location for the new tuple, and put it there.
*/
-Datum
-hashinsert(PG_FUNCTION_ARGS)
+bool
+hashinsert(Relation rel, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique)
{
- Relation rel = (Relation) PG_GETARG_POINTER(0);
- Datum *values = (Datum *) PG_GETARG_POINTER(1);
- bool *isnull = (bool *) PG_GETARG_POINTER(2);
- ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
-
-#ifdef NOT_USED
- Relation heapRel = (Relation) PG_GETARG_POINTER(4);
- IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
-#endif
IndexTuple itup;
/*
@@ -191,7 +220,7 @@ hashinsert(PG_FUNCTION_ARGS)
* chosen in 1986, not of the way nulls are handled here.
*/
if (isnull[0])
- PG_RETURN_BOOL(false);
+ return false;
/* generate an index tuple */
itup = _hash_form_tuple(rel, values, isnull);
@@ -201,18 +230,16 @@ hashinsert(PG_FUNCTION_ARGS)
pfree(itup);
- PG_RETURN_BOOL(false);
+ return false;
}
/*
* hashgettuple() -- Get the next tuple in the scan.
*/
-Datum
-hashgettuple(PG_FUNCTION_ARGS)
+bool
+hashgettuple(IndexScanDesc scan, ScanDirection dir)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
HashScanOpaque so = (HashScanOpaque) scan->opaque;
Relation rel = scan->indexRelation;
Buffer buf;
@@ -314,18 +341,16 @@ hashgettuple(PG_FUNCTION_ARGS)
/* Return current heap TID on success */
scan->xs_ctup.t_self = so->hashso_heappos;
- PG_RETURN_BOOL(res);
+ return res;
}
/*
* hashgetbitmap() -- get all tuples at once
*/
-Datum
-hashgetbitmap(PG_FUNCTION_ARGS)
+int64
+hashgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
HashScanOpaque so = (HashScanOpaque) scan->opaque;
bool res;
int64 ntids = 0;
@@ -362,19 +387,16 @@ hashgetbitmap(PG_FUNCTION_ARGS)
res = _hash_next(scan, ForwardScanDirection);
}
- PG_RETURN_INT64(ntids);
+ return ntids;
}
/*
* hashbeginscan() -- start a scan on a hash index
*/
-Datum
-hashbeginscan(PG_FUNCTION_ARGS)
+IndexScanDesc
+hashbeginscan(Relation rel, int nkeys, int norderbys)
{
- Relation rel = (Relation) PG_GETARG_POINTER(0);
- int nkeys = PG_GETARG_INT32(1);
- int norderbys = PG_GETARG_INT32(2);
IndexScanDesc scan;
HashScanOpaque so;
@@ -396,19 +418,16 @@ hashbeginscan(PG_FUNCTION_ARGS)
/* register scan in case we change pages it's using */
_hash_regscan(scan);
- PG_RETURN_POINTER(scan);
+ return scan;
}
/*
* hashrescan() -- rescan an index relation
*/
-Datum
-hashrescan(PG_FUNCTION_ARGS)
+void
+hashrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
-
- /* remaining arguments are ignored */
HashScanOpaque so = (HashScanOpaque) scan->opaque;
Relation rel = scan->indexRelation;
@@ -434,17 +453,14 @@ hashrescan(PG_FUNCTION_ARGS)
scan->numberOfKeys * sizeof(ScanKeyData));
so->hashso_bucket_valid = false;
}
-
- PG_RETURN_VOID();
}
/*
* hashendscan() -- close down a scan
*/
-Datum
-hashendscan(PG_FUNCTION_ARGS)
+void
+hashendscan(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
HashScanOpaque so = (HashScanOpaque) scan->opaque;
Relation rel = scan->indexRelation;
@@ -463,28 +479,6 @@ hashendscan(PG_FUNCTION_ARGS)
pfree(so);
scan->opaque = NULL;
-
- PG_RETURN_VOID();
-}
-
-/*
- * hashmarkpos() -- save current scan position
- */
-Datum
-hashmarkpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "hash does not support mark/restore");
- PG_RETURN_VOID();
-}
-
-/*
- * hashrestrpos() -- restore scan to last saved position
- */
-Datum
-hashrestrpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "hash does not support mark/restore");
- PG_RETURN_VOID();
}
/*
@@ -494,13 +488,10 @@ hashrestrpos(PG_FUNCTION_ARGS)
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-hashbulkdelete(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+hashbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback, void *callback_state)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- void *callback_state = (void *) PG_GETARG_POINTER(3);
Relation rel = info->index;
double tuples_removed;
double num_index_tuples;
@@ -670,7 +661,7 @@ loop_top:
stats->tuples_removed += tuples_removed;
/* hashvacuumcleanup will fill in num_pages */
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
@@ -678,24 +669,22 @@ loop_top:
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-hashvacuumcleanup(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+hashvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
Relation rel = info->index;
BlockNumber num_pages;
/* If hashbulkdelete wasn't called, return NULL signifying no change */
/* Note: this covers the analyze_only case too */
if (stats == NULL)
- PG_RETURN_POINTER(NULL);
+ return NULL;
/* update statistics */
num_pages = RelationGetNumberOfBlocks(rel);
stats->num_pages = num_pages;
- PG_RETURN_POINTER(stats);
+ return stats;
}
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 86037d47277..456954b0631 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -217,18 +217,10 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
}
}
-Datum
-hashoptions(PG_FUNCTION_ARGS)
+bytea *
+hashoptions(Datum reloptions, bool validate)
{
- Datum reloptions = PG_GETARG_DATUM(0);
- bool validate = PG_GETARG_BOOL(1);
- bytea *result;
-
- result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
-
- if (result)
- PG_RETURN_BYTEA_P(result);
- PG_RETURN_NULL();
+ return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
}
/*
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
new file mode 100644
index 00000000000..abd678483c8
--- /dev/null
+++ b/src/backend/access/hash/hashvalidate.c
@@ -0,0 +1,157 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashvalidate.c
+ * Opclass validator for hash.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/hash/hashvalidate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/htup_details.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_opclass.h"
+#include "utils/builtins.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Validator for a hash opclass.
+ *
+ * Some of the checks done here cover the whole opfamily, and therefore are
+ * redundant when checking each opclass in a family. But they don't run long
+ * enough to be much of a problem, so we accept the duplication rather than
+ * complicate the amvalidate API.
+ *
+ * Some of the code here relies on the fact that hash has only one operator
+ * strategy and support function; we don't have to check for incomplete sets.
+ */
+bool
+hashvalidate(Oid opclassoid)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ int numclassops;
+ int32 classfuncbits;
+ CatCList *proclist,
+ *oprlist;
+ int i,
+ j;
+
+ /* Fetch opclass information */
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+ ReleaseSysCache(classtup);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ /* We'll track the ops and functions belonging to the named opclass */
+ numclassops = 0;
+ classfuncbits = 0;
+
+ /* Check support functions */
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* Check that only allowed procedure numbers exist */
+ if (procform->amprocnum != HASHPROC)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash opfamily %u contains invalid support number %d for procedure %u",
+ opfamilyoid,
+ procform->amprocnum, procform->amproc)));
+
+ /* Remember functions that are specifically for the named opclass */
+ if (procform->amproclefttype == opcintype &&
+ procform->amprocrighttype == opcintype)
+ classfuncbits |= (1 << procform->amprocnum);
+ }
+
+ /* Check operators */
+ for (i = 0; i < oprlist->n_members; i++)
+ {
+ HeapTuple oprtup = &oprlist->members[i]->tuple;
+ Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ bool leftFound = false,
+ rightFound = false;
+
+ /* Check that only allowed strategy numbers exist */
+ if (oprform->amopstrategy < 1 ||
+ oprform->amopstrategy > HTMaxStrategyNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash opfamily %u contains invalid strategy number %d for operator %u",
+ opfamilyoid,
+ oprform->amopstrategy, oprform->amopopr)));
+
+ /*
+ * There should be relevant hash procedures for each operator
+ */
+ for (j = 0; j < proclist->n_members; j++)
+ {
+ HeapTuple proctup = &proclist->members[j]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ if (procform->amproclefttype == oprform->amoplefttype)
+ leftFound = true;
+ if (procform->amproclefttype == oprform->amoprighttype)
+ rightFound = true;
+ }
+
+ if (!leftFound || !rightFound)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash opfamily %u lacks support function for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* hash doesn't support ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH ||
+ OidIsValid(oprform->amopsortfamily))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash opfamily %u contains invalid ORDER BY specification for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* Count operators that are specifically for the named opclass */
+ if (oprform->amoplefttype == opcintype &&
+ oprform->amoprighttype == opcintype)
+ numclassops++;
+ }
+
+ /* Check that the named opclass is complete */
+ if (numclassops != HTMaxStrategyNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash opclass %u is missing operator(s)",
+ opclassoid)));
+ if ((classfuncbits & (1 << HASHPROC)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("hash opclass %u is missing required support function",
+ opclassoid)));
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+
+ return true;
+}
diff --git a/src/backend/access/index/Makefile b/src/backend/access/index/Makefile
index 96490db032c..b82e5d727f8 100644
--- a/src/backend/access/index/Makefile
+++ b/src/backend/access/index/Makefile
@@ -12,6 +12,6 @@ subdir = src/backend/access/index
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = genam.o indexam.o
+OBJS = amapi.o genam.o indexam.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index 00000000000..bda166a9ef8
--- /dev/null
+++ b/src/backend/access/index/amapi.c
@@ -0,0 +1,114 @@
+/*-------------------------------------------------------------------------
+ *
+ * amapi.c
+ * Support routines for API for Postgres index access methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/access/index/amapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/amapi.h"
+#include "access/htup_details.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_opclass.h"
+#include "utils/syscache.h"
+
+
+/*
+ * GetIndexAmRoutine - call the specified access method handler routine to get
+ * its IndexAmRoutine struct, which will be palloc'd in the caller's context.
+ *
+ * Note that if the amhandler function is built-in, this will not involve
+ * any catalog access. It's therefore safe to use this while bootstrapping
+ * indexes for the system catalogs. relcache.c relies on that.
+ */
+IndexAmRoutine *
+GetIndexAmRoutine(Oid amhandler)
+{
+ Datum datum;
+ IndexAmRoutine *routine;
+
+ datum = OidFunctionCall0(amhandler);
+ routine = (IndexAmRoutine *) DatumGetPointer(datum);
+
+ if (routine == NULL || !IsA(routine, IndexAmRoutine))
+ elog(ERROR, "index access method handler function %u did not return an IndexAmRoutine struct",
+ amhandler);
+
+ return routine;
+}
+
+/*
+ * GetIndexAmRoutineByAmId - look up the handler of the index access method
+ * with the given OID, and get its IndexAmRoutine struct.
+ */
+IndexAmRoutine *
+GetIndexAmRoutineByAmId(Oid amoid)
+{
+ HeapTuple tuple;
+ Form_pg_am amform;
+ regproc amhandler;
+
+ /* 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);
+
+ amhandler = amform->amhandler;
+
+ /* Complain if handler OID is invalid */
+ if (!RegProcedureIsValid(amhandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("index 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 GetIndexAmRoutine(amhandler);
+}
+
+
+/*
+ * Ask appropriate access method to validate the specified opclass.
+ */
+Datum
+amvalidate(PG_FUNCTION_ARGS)
+{
+ Oid opclassoid = PG_GETARG_OID(0);
+ bool result;
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid amoid;
+ IndexAmRoutine *amroutine;
+
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ amoid = classform->opcmethod;
+
+ ReleaseSysCache(classtup);
+
+ amroutine = GetIndexAmRoutineByAmId(amoid);
+
+ if (amroutine->amvalidate == NULL)
+ elog(ERROR, "function amvalidate is not defined for index access method %u",
+ amoid);
+
+ result = amroutine->amvalidate(opclassoid);
+
+ pfree(amroutine);
+
+ PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 9352423305d..54b71cb2f77 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -65,12 +65,12 @@
#include "postgres.h"
+#include "access/amapi.h"
#include "access/relscan.h"
#include "access/transam.h"
#include "access/xlog.h"
-
-#include "catalog/index.h"
#include "catalog/catalog.h"
+#include "catalog/index.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
@@ -94,7 +94,7 @@
#define RELATION_CHECKS \
( \
AssertMacro(RelationIsValid(indexRelation)), \
- AssertMacro(PointerIsValid(indexRelation->rd_am)), \
+ AssertMacro(PointerIsValid(indexRelation->rd_amroutine)), \
AssertMacro(!ReindexIsProcessingIndex(RelationGetRelid(indexRelation))) \
)
@@ -102,38 +102,21 @@
( \
AssertMacro(IndexScanIsValid(scan)), \
AssertMacro(RelationIsValid(scan->indexRelation)), \
- AssertMacro(PointerIsValid(scan->indexRelation->rd_am)) \
+ AssertMacro(PointerIsValid(scan->indexRelation->rd_amroutine)) \
)
-#define GET_REL_PROCEDURE(pname) \
-do { \
- procedure = &indexRelation->rd_aminfo->pname; \
- if (!OidIsValid(procedure->fn_oid)) \
- { \
- RegProcedure procOid = indexRelation->rd_am->pname; \
- if (!RegProcedureIsValid(procOid)) \
- elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
- fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
- } \
-} while(0)
-
-#define GET_UNCACHED_REL_PROCEDURE(pname) \
+#define CHECK_REL_PROCEDURE(pname) \
do { \
- if (!RegProcedureIsValid(indexRelation->rd_am->pname)) \
- elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
- fmgr_info(indexRelation->rd_am->pname, &procedure); \
+ if (indexRelation->rd_amroutine->pname == NULL) \
+ elog(ERROR, "function %s is not defined for index %s", \
+ CppAsString(pname), RelationGetRelationName(indexRelation)); \
} while(0)
-#define GET_SCAN_PROCEDURE(pname) \
+#define CHECK_SCAN_PROCEDURE(pname) \
do { \
- procedure = &scan->indexRelation->rd_aminfo->pname; \
- if (!OidIsValid(procedure->fn_oid)) \
- { \
- RegProcedure procOid = scan->indexRelation->rd_am->pname; \
- if (!RegProcedureIsValid(procOid)) \
- elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
- fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
- } \
+ if (scan->indexRelation->rd_amroutine->pname == NULL) \
+ elog(ERROR, "function %s is not defined for index %s", \
+ CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
} while(0)
static IndexScanDesc index_beginscan_internal(Relation indexRelation,
@@ -210,26 +193,17 @@ index_insert(Relation indexRelation,
Relation heapRelation,
IndexUniqueCheck checkUnique)
{
- FmgrInfo *procedure;
-
RELATION_CHECKS;
- GET_REL_PROCEDURE(aminsert);
+ CHECK_REL_PROCEDURE(aminsert);
- if (!(indexRelation->rd_am->ampredlocks))
+ if (!(indexRelation->rd_amroutine->ampredlocks))
CheckForSerializableConflictIn(indexRelation,
(HeapTuple) NULL,
InvalidBuffer);
- /*
- * have the am's insert proc do all the work.
- */
- return DatumGetBool(FunctionCall6(procedure,
- PointerGetDatum(indexRelation),
- PointerGetDatum(values),
- PointerGetDatum(isnull),
- PointerGetDatum(heap_t_ctid),
- PointerGetDatum(heapRelation),
- Int32GetDatum((int32) checkUnique)));
+ return indexRelation->rd_amroutine->aminsert(indexRelation, values, isnull,
+ heap_t_ctid, heapRelation,
+ checkUnique);
}
/*
@@ -288,13 +262,10 @@ static IndexScanDesc
index_beginscan_internal(Relation indexRelation,
int nkeys, int norderbys, Snapshot snapshot)
{
- IndexScanDesc scan;
- FmgrInfo *procedure;
-
RELATION_CHECKS;
- GET_REL_PROCEDURE(ambeginscan);
+ CHECK_REL_PROCEDURE(ambeginscan);
- if (!(indexRelation->rd_am->ampredlocks))
+ if (!(indexRelation->rd_amroutine->ampredlocks))
PredicateLockRelation(indexRelation, snapshot);
/*
@@ -305,13 +276,8 @@ index_beginscan_internal(Relation indexRelation,
/*
* Tell the AM to open a scan.
*/
- scan = (IndexScanDesc)
- DatumGetPointer(FunctionCall3(procedure,
- PointerGetDatum(indexRelation),
- Int32GetDatum(nkeys),
- Int32GetDatum(norderbys)));
-
- return scan;
+ return indexRelation->rd_amroutine->ambeginscan(indexRelation, nkeys,
+ norderbys);
}
/* ----------------
@@ -331,10 +297,8 @@ index_rescan(IndexScanDesc scan,
ScanKey keys, int nkeys,
ScanKey orderbys, int norderbys)
{
- FmgrInfo *procedure;
-
SCAN_CHECKS;
- GET_SCAN_PROCEDURE(amrescan);
+ CHECK_SCAN_PROCEDURE(amrescan);
Assert(nkeys == scan->numberOfKeys);
Assert(norderbys == scan->numberOfOrderBys);
@@ -350,12 +314,8 @@ index_rescan(IndexScanDesc scan,
scan->kill_prior_tuple = false; /* for safety */
- FunctionCall5(procedure,
- PointerGetDatum(scan),
- PointerGetDatum(keys),
- Int32GetDatum(nkeys),
- PointerGetDatum(orderbys),
- Int32GetDatum(norderbys));
+ scan->indexRelation->rd_amroutine->amrescan(scan, keys, nkeys,
+ orderbys, norderbys);
}
/* ----------------
@@ -365,10 +325,8 @@ index_rescan(IndexScanDesc scan,
void
index_endscan(IndexScanDesc scan)
{
- FmgrInfo *procedure;
-
SCAN_CHECKS;
- GET_SCAN_PROCEDURE(amendscan);
+ CHECK_SCAN_PROCEDURE(amendscan);
/* Release any held pin on a heap page */
if (BufferIsValid(scan->xs_cbuf))
@@ -378,7 +336,7 @@ index_endscan(IndexScanDesc scan)
}
/* End the AM's scan */
- FunctionCall1(procedure, PointerGetDatum(scan));
+ scan->indexRelation->rd_amroutine->amendscan(scan);
/* Release index refcount acquired by index_beginscan */
RelationDecrementReferenceCount(scan->indexRelation);
@@ -394,12 +352,10 @@ index_endscan(IndexScanDesc scan)
void
index_markpos(IndexScanDesc scan)
{
- FmgrInfo *procedure;
-
SCAN_CHECKS;
- GET_SCAN_PROCEDURE(ammarkpos);
+ CHECK_SCAN_PROCEDURE(ammarkpos);
- FunctionCall1(procedure, PointerGetDatum(scan));
+ scan->indexRelation->rd_amroutine->ammarkpos(scan);
}
/* ----------------
@@ -421,18 +377,16 @@ index_markpos(IndexScanDesc scan)
void
index_restrpos(IndexScanDesc scan)
{
- FmgrInfo *procedure;
-
Assert(IsMVCCSnapshot(scan->xs_snapshot));
SCAN_CHECKS;
- GET_SCAN_PROCEDURE(amrestrpos);
+ CHECK_SCAN_PROCEDURE(amrestrpos);
scan->xs_continue_hot = false;
scan->kill_prior_tuple = false; /* for safety */
- FunctionCall1(procedure, PointerGetDatum(scan));
+ scan->indexRelation->rd_amroutine->amrestrpos(scan);
}
/* ----------------
@@ -445,11 +399,10 @@ index_restrpos(IndexScanDesc scan)
ItemPointer
index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
{
- FmgrInfo *procedure;
bool found;
SCAN_CHECKS;
- GET_SCAN_PROCEDURE(amgettuple);
+ CHECK_SCAN_PROCEDURE(amgettuple);
Assert(TransactionIdIsValid(RecentGlobalXmin));
@@ -459,9 +412,7 @@ index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
* scan->xs_recheck and possibly scan->xs_itup, though we pay no attention
* to those fields here.
*/
- found = DatumGetBool(FunctionCall2(procedure,
- PointerGetDatum(scan),
- Int32GetDatum(direction)));
+ found = scan->indexRelation->rd_amroutine->amgettuple(scan, direction);
/* Reset kill flag immediately for safety */
scan->kill_prior_tuple = false;
@@ -635,12 +586,10 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
int64
index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
{
- FmgrInfo *procedure;
int64 ntids;
- Datum d;
SCAN_CHECKS;
- GET_SCAN_PROCEDURE(amgetbitmap);
+ CHECK_SCAN_PROCEDURE(amgetbitmap);
/* just make sure this is false... */
scan->kill_prior_tuple = false;
@@ -648,16 +597,7 @@ index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
/*
* have the am's getbitmap proc do all the work.
*/
- d = FunctionCall2(procedure,
- PointerGetDatum(scan),
- PointerGetDatum(bitmap));
-
- ntids = DatumGetInt64(d);
-
- /* If int8 is pass-by-ref, must free the result to avoid memory leak */
-#ifndef USE_FLOAT8_BYVAL
- pfree(DatumGetPointer(d));
-#endif
+ ntids = scan->indexRelation->rd_amroutine->amgetbitmap(scan, bitmap);
pgstat_count_index_tuples(scan->indexRelation, ntids);
@@ -680,20 +620,12 @@ index_bulk_delete(IndexVacuumInfo *info,
void *callback_state)
{
Relation indexRelation = info->index;
- FmgrInfo procedure;
- IndexBulkDeleteResult *result;
RELATION_CHECKS;
- GET_UNCACHED_REL_PROCEDURE(ambulkdelete);
-
- result = (IndexBulkDeleteResult *)
- DatumGetPointer(FunctionCall4(&procedure,
- PointerGetDatum(info),
- PointerGetDatum(stats),
- PointerGetDatum((Pointer) callback),
- PointerGetDatum(callback_state)));
+ CHECK_REL_PROCEDURE(ambulkdelete);
- return result;
+ return indexRelation->rd_amroutine->ambulkdelete(info, stats,
+ callback, callback_state);
}
/* ----------------
@@ -707,18 +639,11 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats)
{
Relation indexRelation = info->index;
- FmgrInfo procedure;
- IndexBulkDeleteResult *result;
RELATION_CHECKS;
- GET_UNCACHED_REL_PROCEDURE(amvacuumcleanup);
-
- result = (IndexBulkDeleteResult *)
- DatumGetPointer(FunctionCall2(&procedure,
- PointerGetDatum(info),
- PointerGetDatum(stats)));
+ CHECK_REL_PROCEDURE(amvacuumcleanup);
- return result;
+ return indexRelation->rd_amroutine->amvacuumcleanup(info, stats);
}
/* ----------------
@@ -731,19 +656,13 @@ index_vacuum_cleanup(IndexVacuumInfo *info,
bool
index_can_return(Relation indexRelation, int attno)
{
- FmgrInfo *procedure;
-
RELATION_CHECKS;
/* amcanreturn is optional; assume FALSE if not provided by AM */
- if (!RegProcedureIsValid(indexRelation->rd_am->amcanreturn))
+ if (indexRelation->rd_amroutine->amcanreturn == NULL)
return false;
- GET_REL_PROCEDURE(amcanreturn);
-
- return DatumGetBool(FunctionCall2(procedure,
- PointerGetDatum(indexRelation),
- Int32GetDatum(attno)));
+ return indexRelation->rd_amroutine->amcanreturn(indexRelation, attno);
}
/* ----------------
@@ -781,7 +700,7 @@ index_getprocid(Relation irel,
int nproc;
int procindex;
- nproc = irel->rd_am->amsupport;
+ nproc = irel->rd_amroutine->amsupport;
Assert(procnum > 0 && procnum <= (uint16) nproc);
@@ -815,7 +734,7 @@ index_getprocinfo(Relation irel,
int nproc;
int procindex;
- nproc = irel->rd_am->amsupport;
+ nproc = irel->rd_amroutine->amsupport;
Assert(procnum > 0 && procnum <= (uint16) nproc);
diff --git a/src/backend/access/nbtree/Makefile b/src/backend/access/nbtree/Makefile
index 2d76d648e01..bbb21d235c0 100644
--- a/src/backend/access/nbtree/Makefile
+++ b/src/backend/access/nbtree/Makefile
@@ -13,6 +13,6 @@ top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = nbtcompare.o nbtinsert.o nbtpage.o nbtree.o nbtsearch.o \
- nbtutils.o nbtsort.o nbtxlog.o
+ nbtutils.o nbtsort.o nbtvalidate.o nbtxlog.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 752e3b5dd12..f2905cb734e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -28,7 +28,7 @@
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
-#include "tcop/tcopprot.h"
+#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
@@ -77,14 +77,55 @@ static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
/*
- * btbuild() -- build a new btree index.
+ * Btree handler function: return IndexAmRoutine with access method parameters
+ * and callbacks.
*/
Datum
-btbuild(PG_FUNCTION_ARGS)
+bthandler(PG_FUNCTION_ARGS)
+{
+ IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+ amroutine->amstrategies = 5;
+ amroutine->amsupport = 2;
+ amroutine->amcanorder = true;
+ amroutine->amcanorderbyop = false;
+ amroutine->amcanbackward = true;
+ amroutine->amcanunique = true;
+ amroutine->amcanmulticol = true;
+ amroutine->amoptionalkey = true;
+ amroutine->amsearcharray = true;
+ amroutine->amsearchnulls = true;
+ amroutine->amstorage = false;
+ amroutine->amclusterable = true;
+ amroutine->ampredlocks = true;
+ amroutine->amkeytype = InvalidOid;
+
+ amroutine->ambuild = btbuild;
+ amroutine->ambuildempty = btbuildempty;
+ amroutine->aminsert = btinsert;
+ amroutine->ambulkdelete = btbulkdelete;
+ amroutine->amvacuumcleanup = btvacuumcleanup;
+ amroutine->amcanreturn = btcanreturn;
+ amroutine->amcostestimate = btcostestimate;
+ amroutine->amoptions = btoptions;
+ amroutine->amvalidate = btvalidate;
+ amroutine->ambeginscan = btbeginscan;
+ amroutine->amrescan = btrescan;
+ amroutine->amgettuple = btgettuple;
+ amroutine->amgetbitmap = btgetbitmap;
+ amroutine->amendscan = btendscan;
+ amroutine->ammarkpos = btmarkpos;
+ amroutine->amrestrpos = btrestrpos;
+
+ PG_RETURN_POINTER(amroutine);
+}
+
+/*
+ * btbuild() -- build a new btree index.
+ */
+IndexBuildResult *
+btbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
- Relation heap = (Relation) PG_GETARG_POINTER(0);
- Relation index = (Relation) PG_GETARG_POINTER(1);
- IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
double reltuples;
BTBuildState buildstate;
@@ -156,7 +197,7 @@ btbuild(PG_FUNCTION_ARGS)
result->heap_tuples = reltuples;
result->index_tuples = buildstate.indtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
@@ -191,10 +232,9 @@ btbuildCallback(Relation index,
/*
* btbuildempty() -- build an empty btree index in the initialization fork
*/
-Datum
-btbuildempty(PG_FUNCTION_ARGS)
+void
+btbuildempty(Relation index)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
Page metapage;
/* Construct metapage. */
@@ -215,8 +255,6 @@ btbuildempty(PG_FUNCTION_ARGS)
* checkpoint may have moved the redo pointer past our xlog record.
*/
smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
-
- PG_RETURN_VOID();
}
/*
@@ -225,15 +263,11 @@ btbuildempty(PG_FUNCTION_ARGS)
* Descend the tree recursively, find the appropriate location for our
* new tuple, and put it there.
*/
-Datum
-btinsert(PG_FUNCTION_ARGS)
+bool
+btinsert(Relation rel, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique)
{
- Relation rel = (Relation) PG_GETARG_POINTER(0);
- Datum *values = (Datum *) PG_GETARG_POINTER(1);
- bool *isnull = (bool *) PG_GETARG_POINTER(2);
- ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
- Relation heapRel = (Relation) PG_GETARG_POINTER(4);
- IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
bool result;
IndexTuple itup;
@@ -245,17 +279,15 @@ btinsert(PG_FUNCTION_ARGS)
pfree(itup);
- PG_RETURN_BOOL(result);
+ return result;
}
/*
* btgettuple() -- Get the next tuple in the scan.
*/
-Datum
-btgettuple(PG_FUNCTION_ARGS)
+bool
+btgettuple(IndexScanDesc scan, ScanDirection dir)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
bool res;
@@ -271,7 +303,7 @@ btgettuple(PG_FUNCTION_ARGS)
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
- PG_RETURN_BOOL(false);
+ return false;
_bt_start_array_keys(scan, dir);
}
@@ -321,17 +353,15 @@ btgettuple(PG_FUNCTION_ARGS)
/* ... otherwise see if we have more array keys to deal with */
} while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
- PG_RETURN_BOOL(res);
+ return res;
}
/*
* btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
*/
-Datum
-btgetbitmap(PG_FUNCTION_ARGS)
+int64
+btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
int64 ntids = 0;
ItemPointer heapTid;
@@ -343,7 +373,7 @@ btgetbitmap(PG_FUNCTION_ARGS)
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
- PG_RETURN_INT64(ntids);
+ return ntids;
_bt_start_array_keys(scan, ForwardScanDirection);
}
@@ -381,18 +411,15 @@ btgetbitmap(PG_FUNCTION_ARGS)
/* Now see if we have more array keys to deal with */
} while (so->numArrayKeys && _bt_advance_array_keys(scan, ForwardScanDirection));
- PG_RETURN_INT64(ntids);
+ return ntids;
}
/*
* btbeginscan() -- start a scan on a btree index
*/
-Datum
-btbeginscan(PG_FUNCTION_ARGS)
+IndexScanDesc
+btbeginscan(Relation rel, int nkeys, int norderbys)
{
- Relation rel = (Relation) PG_GETARG_POINTER(0);
- int nkeys = PG_GETARG_INT32(1);
- int norderbys = PG_GETARG_INT32(2);
IndexScanDesc scan;
BTScanOpaque so;
@@ -430,19 +457,16 @@ btbeginscan(PG_FUNCTION_ARGS)
scan->opaque = so;
- PG_RETURN_POINTER(scan);
+ return scan;
}
/*
* btrescan() -- rescan an index relation
*/
-Datum
-btrescan(PG_FUNCTION_ARGS)
+void
+btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
-
- /* remaining arguments are ignored */
BTScanOpaque so = (BTScanOpaque) scan->opaque;
/* we aren't holding any read locks, but gotta drop the pins */
@@ -493,17 +517,14 @@ btrescan(PG_FUNCTION_ARGS)
/* If any keys are SK_SEARCHARRAY type, set up array-key info */
_bt_preprocess_array_keys(scan);
-
- PG_RETURN_VOID();
}
/*
* btendscan() -- close down a scan
*/
-Datum
-btendscan(PG_FUNCTION_ARGS)
+void
+btendscan(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
/* we aren't holding any read locks, but gotta drop the pins */
@@ -532,17 +553,14 @@ btendscan(PG_FUNCTION_ARGS)
pfree(so->currTuples);
/* so->markTuples should not be pfree'd, see btrescan */
pfree(so);
-
- PG_RETURN_VOID();
}
/*
* btmarkpos() -- save current scan position
*/
-Datum
-btmarkpos(PG_FUNCTION_ARGS)
+void
+btmarkpos(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
/* There may be an old mark with a pin (but no lock). */
@@ -565,17 +583,14 @@ btmarkpos(PG_FUNCTION_ARGS)
/* Also record the current positions of any array keys */
if (so->numArrayKeys)
_bt_mark_array_keys(scan);
-
- PG_RETURN_VOID();
}
/*
* btrestrpos() -- restore scan to last saved position
*/
-Datum
-btrestrpos(PG_FUNCTION_ARGS)
+void
+btrestrpos(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
BTScanOpaque so = (BTScanOpaque) scan->opaque;
/* Restore the marked positions of any array keys */
@@ -643,8 +658,6 @@ btrestrpos(PG_FUNCTION_ARGS)
else
BTScanPosInvalidate(so->currPos);
}
-
- PG_RETURN_VOID();
}
/*
@@ -654,13 +667,10 @@ btrestrpos(PG_FUNCTION_ARGS)
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-btbulkdelete(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback, void *callback_state)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *volatile stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- void *callback_state = (void *) PG_GETARG_POINTER(3);
Relation rel = info->index;
BTCycleId cycleid;
@@ -679,7 +689,7 @@ btbulkdelete(PG_FUNCTION_ARGS)
PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
_bt_end_vacuum(rel);
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
@@ -687,15 +697,12 @@ btbulkdelete(PG_FUNCTION_ARGS)
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-btvacuumcleanup(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+btvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
-
/* No-op in ANALYZE ONLY mode */
if (info->analyze_only)
- PG_RETURN_POINTER(stats);
+ return stats;
/*
* If btbulkdelete was called, we need not do anything, just return the
@@ -727,7 +734,7 @@ btvacuumcleanup(PG_FUNCTION_ARGS)
stats->num_index_tuples = info->num_heap_tuples;
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
@@ -1148,8 +1155,8 @@ restart:
*
* btrees always do, so this is trivial.
*/
-Datum
-btcanreturn(PG_FUNCTION_ARGS)
+bool
+btcanreturn(Relation index, int attno)
{
- PG_RETURN_BOOL(true);
+ return true;
}
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 555f7df3f93..c850b4804fe 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -2058,15 +2058,8 @@ BTreeShmemInit(void)
Assert(found);
}
-Datum
-btoptions(PG_FUNCTION_ARGS)
+bytea *
+btoptions(Datum reloptions, bool validate)
{
- Datum reloptions = PG_GETARG_DATUM(0);
- bool validate = PG_GETARG_BOOL(1);
- bytea *result;
-
- result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
- if (result)
- PG_RETURN_BYTEA_P(result);
- PG_RETURN_NULL();
+ return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
}
diff --git a/src/backend/access/nbtree/nbtvalidate.c b/src/backend/access/nbtree/nbtvalidate.c
new file mode 100644
index 00000000000..b814b54a36b
--- /dev/null
+++ b/src/backend/access/nbtree/nbtvalidate.c
@@ -0,0 +1,204 @@
+/*-------------------------------------------------------------------------
+ *
+ * nbtvalidate.c
+ * Opclass validator for btree.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/nbtree/nbtvalidate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_opclass.h"
+#include "utils/builtins.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Validator for a btree opclass.
+ *
+ * Some of the checks done here cover the whole opfamily, and therefore are
+ * redundant when checking each opclass in a family. But they don't run long
+ * enough to be much of a problem, so we accept the duplication rather than
+ * complicate the amvalidate API.
+ */
+bool
+btvalidate(Oid opclassoid)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ int numclassops;
+ int32 classfuncbits;
+ CatCList *proclist,
+ *oprlist;
+ Oid lastlefttype,
+ lastrighttype;
+ int numOps;
+ int i,
+ j;
+
+ /* Fetch opclass information */
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+ ReleaseSysCache(classtup);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ /* We rely on the oprlist to be ordered */
+ if (!oprlist->ordered)
+ elog(ERROR, "cannot validate btree opclass without ordered data");
+
+ /* We'll track the ops and functions belonging to the named opclass */
+ numclassops = 0;
+ classfuncbits = 0;
+
+ /* Check support functions */
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* Check that only allowed procedure numbers exist */
+ if (procform->amprocnum != BTORDER_PROC &&
+ procform->amprocnum != BTSORTSUPPORT_PROC)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opfamily %u contains invalid support number %d for procedure %u",
+ opfamilyoid,
+ procform->amprocnum, procform->amproc)));
+
+ /* Remember functions that are specifically for the named opclass */
+ if (procform->amproclefttype == opcintype &&
+ procform->amprocrighttype == opcintype)
+ classfuncbits |= (1 << procform->amprocnum);
+ }
+
+ /* Check operators */
+ lastlefttype = lastrighttype = InvalidOid;
+ numOps = 0;
+ for (i = 0; i < oprlist->n_members; i++)
+ {
+ HeapTuple oprtup = &oprlist->members[i]->tuple;
+ Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+
+ /* Check that only allowed strategy numbers exist */
+ if (oprform->amopstrategy < 1 ||
+ oprform->amopstrategy > BTMaxStrategyNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opfamily %u contains invalid strategy number %d for operator %u",
+ opfamilyoid,
+ oprform->amopstrategy, oprform->amopopr)));
+
+ /*
+ * Check that we have all strategies for each supported datatype
+ * combination. This is easy since the list will be sorted in
+ * datatype order and there can't be duplicate strategy numbers.
+ */
+ if (oprform->amoplefttype == lastlefttype &&
+ oprform->amoprighttype == lastrighttype)
+ numOps++;
+ else
+ {
+ /* reached a group boundary, so check ... */
+ if (numOps > 0 && numOps != BTMaxStrategyNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opfamily %u has a partial set of operators for datatypes %s and %s",
+ opfamilyoid,
+ format_type_be(lastlefttype),
+ format_type_be(lastrighttype))));
+ /* ... and reset for new group */
+ lastlefttype = oprform->amoplefttype;
+ lastrighttype = oprform->amoprighttype;
+ numOps = 1;
+ }
+
+ /*
+ * There should be a relevant support function for each operator, but
+ * we only need to check this once per pair of datatypes.
+ */
+ if (numOps == 1)
+ {
+ bool found = false;
+
+ for (j = 0; j < proclist->n_members; j++)
+ {
+ HeapTuple proctup = &proclist->members[j]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ if (procform->amprocnum == BTORDER_PROC &&
+ procform->amproclefttype == oprform->amoplefttype &&
+ procform->amprocrighttype == oprform->amoprighttype)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opfamily %u lacks support function for operator %u",
+ opfamilyoid, oprform->amopopr)));
+ }
+
+ /* btree doesn't support ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH ||
+ OidIsValid(oprform->amopsortfamily))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opfamily %u contains invalid ORDER BY specification for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* Count operators that are specifically for the named opclass */
+ if (oprform->amoplefttype == opcintype &&
+ oprform->amoprighttype == opcintype)
+ numclassops++;
+ }
+
+ /* don't forget to check the last batch of operators for completeness */
+ if (numOps > 0 && numOps != BTMaxStrategyNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opfamily %u has a partial set of operators for datatypes %s and %s",
+ opfamilyoid,
+ format_type_be(lastlefttype),
+ format_type_be(lastrighttype))));
+
+ /* Check that the named opclass is complete */
+ if (numclassops != BTMaxStrategyNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opclass %u is missing operator(s)",
+ opclassoid)));
+ if ((classfuncbits & (1 << BTORDER_PROC)) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("btree opclass %u is missing required support function",
+ opclassoid)));
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+
+ return true;
+}
diff --git a/src/backend/access/spgist/Makefile b/src/backend/access/spgist/Makefile
index 918da1fccaf..14948a531ee 100644
--- a/src/backend/access/spgist/Makefile
+++ b/src/backend/access/spgist/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/spgist
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = spgutils.o spginsert.o spgscan.o spgvacuum.o \
+OBJS = spgutils.o spginsert.o spgscan.o spgvacuum.o spgvalidate.o \
spgdoinsert.o spgxlog.o \
spgtextproc.o spgquadtreeproc.o spgkdtreeproc.o
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
index 0e6a180cc9e..44fd644e421 100644
--- a/src/backend/access/spgist/spginsert.c
+++ b/src/backend/access/spgist/spginsert.c
@@ -65,12 +65,9 @@ spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
/*
* Build an SP-GiST index.
*/
-Datum
-spgbuild(PG_FUNCTION_ARGS)
+IndexBuildResult *
+spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
- Relation heap = (Relation) PG_GETARG_POINTER(0);
- Relation index = (Relation) PG_GETARG_POINTER(1);
- IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
IndexBuildResult *result;
double reltuples;
SpGistBuildState buildstate;
@@ -151,16 +148,15 @@ spgbuild(PG_FUNCTION_ARGS)
result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
result->heap_tuples = result->index_tuples = reltuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
* Build an empty SPGiST index in the initialization fork
*/
-Datum
-spgbuildempty(PG_FUNCTION_ARGS)
+void
+spgbuildempty(Relation index)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
Page page;
/* Construct metapage. */
@@ -201,25 +197,16 @@ spgbuildempty(PG_FUNCTION_ARGS)
* checkpoint may have moved the redo pointer past our xlog record.
*/
smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
-
- PG_RETURN_VOID();
}
/*
* Insert one new tuple into an SPGiST index.
*/
-Datum
-spginsert(PG_FUNCTION_ARGS)
+bool
+spginsert(Relation index, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
- Datum *values = (Datum *) PG_GETARG_POINTER(1);
- bool *isnull = (bool *) PG_GETARG_POINTER(2);
- ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
-
-#ifdef NOT_USED
- Relation heapRel = (Relation) PG_GETARG_POINTER(4);
- IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
-#endif
SpGistState spgstate;
MemoryContext oldCtx;
MemoryContext insertCtx;
@@ -251,5 +238,5 @@ spginsert(PG_FUNCTION_ARGS)
MemoryContextDelete(insertCtx);
/* return false since we've not done any unique check */
- PG_RETURN_BOOL(false);
+ return false;
}
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index 48e678c970e..620e7461998 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -173,13 +173,9 @@ spgPrepareScanKeys(IndexScanDesc scan)
}
}
-Datum
-spgbeginscan(PG_FUNCTION_ARGS)
+IndexScanDesc
+spgbeginscan(Relation rel, int keysz, int orderbysz)
{
- Relation rel = (Relation) PG_GETARG_POINTER(0);
- int keysz = PG_GETARG_INT32(1);
-
- /* ScanKey scankey = (ScanKey) PG_GETARG_POINTER(2); */
IndexScanDesc scan;
SpGistScanOpaque so;
@@ -202,15 +198,14 @@ spgbeginscan(PG_FUNCTION_ARGS)
scan->opaque = so;
- PG_RETURN_POINTER(scan);
+ return scan;
}
-Datum
-spgrescan(PG_FUNCTION_ARGS)
+void
+spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
- ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1);
/* copy scankeys into local storage */
if (scankey && scan->numberOfKeys > 0)
@@ -224,33 +219,14 @@ spgrescan(PG_FUNCTION_ARGS)
/* set up starting stack entries */
resetSpGistScanOpaque(so);
-
- PG_RETURN_VOID();
}
-Datum
-spgendscan(PG_FUNCTION_ARGS)
+void
+spgendscan(IndexScanDesc scan)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
MemoryContextDelete(so->tempCxt);
-
- PG_RETURN_VOID();
-}
-
-Datum
-spgmarkpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "SPGiST does not support mark/restore");
- PG_RETURN_VOID();
-}
-
-Datum
-spgrestrpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "SPGiST does not support mark/restore");
- PG_RETURN_VOID();
}
/*
@@ -571,11 +547,9 @@ storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
so->ntids++;
}
-Datum
-spggetbitmap(PG_FUNCTION_ARGS)
+int64
+spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
/* Copy want_itup to *so so we don't need to pass it around separately */
@@ -586,7 +560,7 @@ spggetbitmap(PG_FUNCTION_ARGS)
spgWalk(scan->indexRelation, so, true, storeBitmap);
- PG_RETURN_INT64(so->ntids);
+ return so->ntids;
}
/* storeRes subroutine for gettuple case */
@@ -610,11 +584,9 @@ storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
so->nPtrs++;
}
-Datum
-spggettuple(PG_FUNCTION_ARGS)
+bool
+spggettuple(IndexScanDesc scan, ScanDirection dir)
{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
if (dir != ForwardScanDirection)
@@ -632,7 +604,7 @@ spggettuple(PG_FUNCTION_ARGS)
scan->xs_recheck = so->recheck[so->iPtr];
scan->xs_itup = so->indexTups[so->iPtr];
so->iPtr++;
- PG_RETURN_BOOL(true);
+ return true;
}
if (so->want_itup)
@@ -651,19 +623,16 @@ spggettuple(PG_FUNCTION_ARGS)
break; /* must have completed scan */
}
- PG_RETURN_BOOL(false);
+ return false;
}
-Datum
-spgcanreturn(PG_FUNCTION_ARGS)
+bool
+spgcanreturn(Relation index, int attno)
{
- Relation index = (Relation) PG_GETARG_POINTER(0);
-
- /* int i = PG_GETARG_INT32(1); */
SpGistCache *cache;
/* We can do it if the opclass config function says so */
cache = spgGetCache(index);
- PG_RETURN_BOOL(cache->config.canReturnData);
+ return cache->config.canReturnData;
}
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index 9ba077686a9..201203f91a3 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -15,7 +15,6 @@
#include "postgres.h"
-#include "access/genam.h"
#include "access/reloptions.h"
#include "access/spgist_private.h"
#include "access/transam.h"
@@ -23,9 +22,54 @@
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
+#include "utils/index_selfuncs.h"
#include "utils/lsyscache.h"
+/*
+ * SP-GiST handler function: return IndexAmRoutine with access method parameters
+ * and callbacks.
+ */
+Datum
+spghandler(PG_FUNCTION_ARGS)
+{
+ IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+
+ amroutine->amstrategies = 0;
+ amroutine->amsupport = 5;
+ amroutine->amcanorder = false;
+ amroutine->amcanorderbyop = false;
+ amroutine->amcanbackward = false;
+ amroutine->amcanunique = false;
+ amroutine->amcanmulticol = false;
+ amroutine->amoptionalkey = true;
+ amroutine->amsearcharray = false;
+ amroutine->amsearchnulls = true;
+ amroutine->amstorage = false;
+ amroutine->amclusterable = false;
+ amroutine->ampredlocks = false;
+ amroutine->amkeytype = InvalidOid;
+
+ amroutine->ambuild = spgbuild;
+ amroutine->ambuildempty = spgbuildempty;
+ amroutine->aminsert = spginsert;
+ amroutine->ambulkdelete = spgbulkdelete;
+ amroutine->amvacuumcleanup = spgvacuumcleanup;
+ amroutine->amcanreturn = spgcanreturn;
+ amroutine->amcostestimate = spgcostestimate;
+ amroutine->amoptions = spgoptions;
+ amroutine->amvalidate = spgvalidate;
+ amroutine->ambeginscan = spgbeginscan;
+ amroutine->amrescan = spgrescan;
+ amroutine->amgettuple = spggettuple;
+ amroutine->amgetbitmap = spggetbitmap;
+ amroutine->amendscan = spgendscan;
+ amroutine->ammarkpos = NULL;
+ amroutine->amrestrpos = NULL;
+
+ PG_RETURN_POINTER(amroutine);
+}
+
/* Fill in a SpGistTypeDesc struct with info about the specified data type */
static void
fillTypeDesc(SpGistTypeDesc *desc, Oid type)
@@ -489,18 +533,10 @@ SpGistInitMetapage(Page page)
/*
* reloptions processing for SPGiST
*/
-Datum
-spgoptions(PG_FUNCTION_ARGS)
+bytea *
+spgoptions(Datum reloptions, bool validate)
{
- Datum reloptions = PG_GETARG_DATUM(0);
- bool validate = PG_GETARG_BOOL(1);
- bytea *result;
-
- result = default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
-
- if (result)
- PG_RETURN_BYTEA_P(result);
- PG_RETURN_NULL();
+ return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
}
/*
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index c2d4f0a68a6..15b867f24cb 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -881,13 +881,10 @@ spgvacuumscan(spgBulkDeleteState *bds)
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-spgbulkdelete(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback, void *callback_state)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- void *callback_state = (void *) PG_GETARG_POINTER(3);
spgBulkDeleteState bds;
/* allocate stats if first time through, else re-use existing struct */
@@ -900,7 +897,7 @@ spgbulkdelete(PG_FUNCTION_ARGS)
spgvacuumscan(&bds);
- PG_RETURN_POINTER(stats);
+ return stats;
}
/* Dummy callback to delete no tuples during spgvacuumcleanup */
@@ -915,17 +912,15 @@ dummy_callback(ItemPointer itemptr, void *state)
*
* Result: a palloc'd struct containing statistical info for VACUUM displays.
*/
-Datum
-spgvacuumcleanup(PG_FUNCTION_ARGS)
+IndexBulkDeleteResult *
+spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
{
- IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
Relation index = info->index;
spgBulkDeleteState bds;
/* No-op in ANALYZE ONLY mode */
if (info->analyze_only)
- PG_RETURN_POINTER(stats);
+ return stats;
/*
* We don't need to scan the index if there was a preceding bulkdelete
@@ -959,5 +954,5 @@ spgvacuumcleanup(PG_FUNCTION_ARGS)
stats->num_index_tuples = info->num_heap_tuples;
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
diff --git a/src/backend/access/spgist/spgvalidate.c b/src/backend/access/spgist/spgvalidate.c
new file mode 100644
index 00000000000..c2d2d466d94
--- /dev/null
+++ b/src/backend/access/spgist/spgvalidate.c
@@ -0,0 +1,129 @@
+/*-------------------------------------------------------------------------
+ *
+ * spgvalidate.c
+ * Opclass validator for SP-GiST.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/access/spgist/spgvalidate.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/spgist_private.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_opclass.h"
+#include "utils/catcache.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Validator for an SP-GiST opclass.
+ */
+bool
+spgvalidate(Oid opclassoid)
+{
+ HeapTuple classtup;
+ Form_pg_opclass classform;
+ Oid opfamilyoid;
+ Oid opcintype;
+ int numclassops;
+ int32 classfuncbits;
+ CatCList *proclist,
+ *oprlist;
+ int i;
+
+ /* Fetch opclass information */
+ classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ if (!HeapTupleIsValid(classtup))
+ elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+ opfamilyoid = classform->opcfamily;
+ opcintype = classform->opcintype;
+
+ ReleaseSysCache(classtup);
+
+ /* Fetch all operators and support functions of the opfamily */
+ oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
+ proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
+
+ /* We'll track the ops and functions belonging to the named opclass */
+ numclassops = 0;
+ classfuncbits = 0;
+
+ /* Check support functions */
+ for (i = 0; i < proclist->n_members; i++)
+ {
+ HeapTuple proctup = &proclist->members[i]->tuple;
+ Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
+
+ /* Check that only allowed procedure numbers exist */
+ if (procform->amprocnum < 1 ||
+ procform->amprocnum > SPGISTNProc)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("spgist opfamily %u contains invalid support number %d for procedure %u",
+ opfamilyoid,
+ procform->amprocnum, procform->amproc)));
+
+ /* Remember functions that are specifically for the named opclass */
+ if (procform->amproclefttype == opcintype &&
+ procform->amprocrighttype == opcintype)
+ classfuncbits |= (1 << procform->amprocnum);
+ }
+
+ /* Check operators */
+ for (i = 0; i < oprlist->n_members; i++)
+ {
+ HeapTuple oprtup = &oprlist->members[i]->tuple;
+ Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+
+ /* TODO: Check that only allowed strategy numbers exist */
+ if (oprform->amopstrategy < 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("spgist opfamily %u contains invalid strategy number %d for operator %u",
+ opfamilyoid,
+ oprform->amopstrategy, oprform->amopopr)));
+
+ /* spgist doesn't support ORDER BY operators */
+ if (oprform->amoppurpose != AMOP_SEARCH ||
+ OidIsValid(oprform->amopsortfamily))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("spgist opfamily %u contains invalid ORDER BY specification for operator %u",
+ opfamilyoid, oprform->amopopr)));
+
+ /* Count operators that are specifically for the named opclass */
+ if (oprform->amoplefttype == opcintype &&
+ oprform->amoprighttype == opcintype)
+ numclassops++;
+ }
+
+ /* Check that the named opclass is complete */
+ if (numclassops == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("spgist opclass %u is missing operator(s)",
+ opclassoid)));
+ for (i = 1; i <= SPGISTNProc; i++)
+ {
+ if ((classfuncbits & (1 << i)) != 0)
+ continue; /* got it */
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("spgist opclass %u is missing required support function %d",
+ opclassoid, i)));
+ }
+
+ ReleaseCatCacheList(proclist);
+ ReleaseCatCacheList(oprlist);
+
+ return true;
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 401c59f9a1a..313ee9c4edc 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -23,6 +23,7 @@
#include <unistd.h>
+#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/sysattr.h"
@@ -36,6 +37,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h"
@@ -279,20 +281,14 @@ ConstructTupleDescriptor(Relation heapRelation,
int numatts = indexInfo->ii_NumIndexAttrs;
ListCell *colnames_item = list_head(indexColNames);
ListCell *indexpr_item = list_head(indexInfo->ii_Expressions);
- HeapTuple amtuple;
- Form_pg_am amform;
+ IndexAmRoutine *amroutine;
TupleDesc heapTupDesc;
TupleDesc indexTupDesc;
int natts; /* #atts in heap rel --- for error checks */
int i;
- /* We need access to the index AM's pg_am tuple */
- amtuple = SearchSysCache1(AMOID,
- ObjectIdGetDatum(accessMethodObjectId));
- if (!HeapTupleIsValid(amtuple))
- elog(ERROR, "cache lookup failed for access method %u",
- accessMethodObjectId);
- amform = (Form_pg_am) GETSTRUCT(amtuple);
+ /* We need access to the index AM's API struct */
+ amroutine = GetIndexAmRoutineByAmId(accessMethodObjectId);
/* ... and to the table's tuple descriptor */
heapTupDesc = RelationGetDescr(heapRelation);
@@ -439,7 +435,7 @@ ConstructTupleDescriptor(Relation heapRelation,
if (OidIsValid(opclassTup->opckeytype))
keyType = opclassTup->opckeytype;
else
- keyType = amform->amkeytype;
+ keyType = amroutine->amkeytype;
ReleaseSysCache(tuple);
if (OidIsValid(keyType) && keyType != to->atttypid)
@@ -461,7 +457,7 @@ ConstructTupleDescriptor(Relation heapRelation,
}
}
- ReleaseSysCache(amtuple);
+ pfree(amroutine);
return indexTupDesc;
}
@@ -1990,7 +1986,6 @@ index_build(Relation heapRelation,
bool isprimary,
bool isreindex)
{
- RegProcedure procedure;
IndexBuildResult *stats;
Oid save_userid;
int save_sec_context;
@@ -2000,10 +1995,9 @@ index_build(Relation heapRelation,
* sanity checks
*/
Assert(RelationIsValid(indexRelation));
- Assert(PointerIsValid(indexRelation->rd_am));
-
- procedure = indexRelation->rd_am->ambuild;
- Assert(RegProcedureIsValid(procedure));
+ Assert(PointerIsValid(indexRelation->rd_amroutine));
+ Assert(PointerIsValid(indexRelation->rd_amroutine->ambuild));
+ Assert(PointerIsValid(indexRelation->rd_amroutine->ambuildempty));
ereport(DEBUG1,
(errmsg("building index \"%s\" on table \"%s\"",
@@ -2023,11 +2017,8 @@ index_build(Relation heapRelation,
/*
* Call the access method's build procedure
*/
- stats = (IndexBuildResult *)
- DatumGetPointer(OidFunctionCall3(procedure,
- PointerGetDatum(heapRelation),
- PointerGetDatum(indexRelation),
- PointerGetDatum(indexInfo)));
+ stats = indexRelation->rd_amroutine->ambuild(heapRelation, indexRelation,
+ indexInfo);
Assert(PointerIsValid(stats));
/*
@@ -2040,11 +2031,9 @@ index_build(Relation heapRelation,
if (indexRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
!smgrexists(indexRelation->rd_smgr, INIT_FORKNUM))
{
- RegProcedure ambuildempty = indexRelation->rd_am->ambuildempty;
-
RelationOpenSmgr(indexRelation);
smgrcreate(indexRelation->rd_smgr, INIT_FORKNUM, false);
- OidFunctionCall1(ambuildempty, PointerGetDatum(indexRelation));
+ indexRelation->rd_amroutine->ambuildempty(indexRelation);
}
/*
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 65cf3ed9fe8..0232e0d8678 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -20,6 +20,7 @@
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/objectaddress.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 0aba9e2cccd..f40a005f225 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -21,6 +21,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 23a69dc0aa2..5cb28cfa735 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -17,6 +17,7 @@
*/
#include "postgres.h"
+#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
@@ -24,6 +25,7 @@
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "access/xlog.h"
+#include "catalog/pg_am.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
@@ -433,7 +435,7 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMOD
RelationGetRelationName(OldHeap))));
/* Index AM must allow clustering */
- if (!OldIndex->rd_am->amclusterable)
+ if (!OldIndex->rd_amroutine->amclusterable)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 7b3a2f498b8..13b04e68f01 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -15,12 +15,14 @@
#include "postgres.h"
+#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_tablespace.h"
@@ -125,6 +127,7 @@ CheckIndexCompatible(Oid oldId,
HeapTuple tuple;
Form_pg_index indexForm;
Form_pg_am accessMethodForm;
+ IndexAmRoutine *amRoutine;
bool amcanorder;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -160,9 +163,11 @@ CheckIndexCompatible(Oid oldId,
accessMethodName)));
accessMethodId = HeapTupleGetOid(tuple);
accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
- amcanorder = accessMethodForm->amcanorder;
+ amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
ReleaseSysCache(tuple);
+ amcanorder = amRoutine->amcanorder;
+
/*
* Compute the operator classes, collations, and exclusion operators for
* the new index, so we can test whether it's compatible with the existing
@@ -315,8 +320,9 @@ DefineIndex(Oid relationId,
Relation indexRelation;
HeapTuple tuple;
Form_pg_am accessMethodForm;
+ IndexAmRoutine *amRoutine;
bool amcanorder;
- RegProcedure amoptions;
+ amoptions_function amoptions;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -489,31 +495,33 @@ DefineIndex(Oid relationId,
}
accessMethodId = HeapTupleGetOid(tuple);
accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
+ amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
if (strcmp(accessMethodName, "hash") == 0 &&
RelationNeedsWAL(rel))
ereport(WARNING,
(errmsg("hash indexes are not WAL-logged and their use is discouraged")));
- if (stmt->unique && !accessMethodForm->amcanunique)
+ if (stmt->unique && !amRoutine->amcanunique)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support unique indexes",
accessMethodName)));
- if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
+ if (numberOfAttributes > 1 && !amRoutine->amcanmulticol)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support multicolumn indexes",
accessMethodName)));
- if (stmt->excludeOpNames && !OidIsValid(accessMethodForm->amgettuple))
+ if (stmt->excludeOpNames && amRoutine->amgettuple == NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support exclusion constraints",
accessMethodName)));
- amcanorder = accessMethodForm->amcanorder;
- amoptions = accessMethodForm->amoptions;
+ amcanorder = amRoutine->amcanorder;
+ amoptions = amRoutine->amoptions;
+ pfree(amRoutine);
ReleaseSysCache(tuple);
/*
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index a0ca2114d1b..8a661968cd9 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -26,6 +26,7 @@
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/opfam_internal.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_namespace.h"
@@ -334,7 +335,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
ListCell *l;
Relation rel;
HeapTuple tup;
- Form_pg_am pg_am;
+ IndexAmRoutine *amroutine;
Datum values[Natts_pg_opclass];
bool nulls[Natts_pg_opclass];
AclResult aclresult;
@@ -361,18 +362,18 @@ DefineOpClass(CreateOpClassStmt *stmt)
stmt->amname)));
amoid = HeapTupleGetOid(tup);
- pg_am = (Form_pg_am) GETSTRUCT(tup);
- maxOpNumber = pg_am->amstrategies;
+ amroutine = GetIndexAmRoutineByAmId(amoid);
+ ReleaseSysCache(tup);
+
+ maxOpNumber = amroutine->amstrategies;
/* if amstrategies is zero, just enforce that op numbers fit in int16 */
if (maxOpNumber <= 0)
maxOpNumber = SHRT_MAX;
- maxProcNumber = pg_am->amsupport;
- amstorage = pg_am->amstorage;
+ maxProcNumber = amroutine->amsupport;
+ amstorage = amroutine->amstorage;
/* XXX Should we make any privilege check against the AM? */
- ReleaseSysCache(tup);
-
/*
* The question of appropriate permissions for CREATE OPERATOR CLASS is
* interesting. Creating an opclass is tantamount to granting public
@@ -776,7 +777,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
int maxOpNumber, /* amstrategies value */
maxProcNumber; /* amsupport value */
HeapTuple tup;
- Form_pg_am pg_am;
+ IndexAmRoutine *amroutine;
/* Get necessary info about access method */
tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
@@ -787,17 +788,17 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
stmt->amname)));
amoid = HeapTupleGetOid(tup);
- pg_am = (Form_pg_am) GETSTRUCT(tup);
- maxOpNumber = pg_am->amstrategies;
+ amroutine = GetIndexAmRoutineByAmId(amoid);
+ ReleaseSysCache(tup);
+
+ maxOpNumber = amroutine->amstrategies;
/* if amstrategies is zero, just enforce that op numbers fit in int16 */
if (maxOpNumber <= 0)
maxOpNumber = SHRT_MAX;
- maxProcNumber = pg_am->amsupport;
+ maxProcNumber = amroutine->amsupport;
/* XXX Should we make any privilege check against the AM? */
- ReleaseSysCache(tup);
-
/* Look up the opfamily */
opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
@@ -1099,21 +1100,13 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
* the family has been created but not yet populated with the required
* operators.)
*/
- HeapTuple amtup;
- Form_pg_am pg_am;
+ IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid);
- amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
- if (amtup == NULL)
- elog(ERROR, "cache lookup failed for access method %u", amoid);
- pg_am = (Form_pg_am) GETSTRUCT(amtup);
-
- if (!pg_am->amcanorderbyop)
+ if (!amroutine->amcanorderbyop)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("access method \"%s\" does not support ordering operators",
- NameStr(pg_am->amname))));
-
- ReleaseSysCache(amtup);
+ get_am_name(amoid))));
}
else
{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0b4a3346316..eeda3b4697b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -29,6 +29,7 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
@@ -9401,7 +9402,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
(void) view_reloptions(newOptions, true);
break;
case RELKIND_INDEX:
- (void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
+ (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
break;
default:
ereport(ERROR,
@@ -11011,7 +11012,8 @@ ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode
RelationGetRelationName(indexRel),
RelationGetRelationName(rel))));
/* The AM must support uniqueness, and the index must in fact be unique. */
- if (!indexRel->rd_am->amcanunique || !indexRel->rd_index->indisunique)
+ if (!indexRel->rd_amroutine->amcanunique ||
+ !indexRel->rd_index->indisunique)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot use non-unique index \"%s\" as replica identity",
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index f0b293ceece..4f41766eef5 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -37,6 +37,7 @@
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index ada54a102d8..35864c16813 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -12,6 +12,7 @@
*/
#include "postgres.h"
+#include "access/amapi.h"
#include "access/htup_details.h"
#include "executor/execdebug.h"
#include "executor/nodeAgg.h"
@@ -542,9 +543,8 @@ IndexSupportsBackwardScan(Oid indexid)
{
bool result;
HeapTuple ht_idxrel;
- HeapTuple ht_am;
Form_pg_class idxrelrec;
- Form_pg_am amrec;
+ IndexAmRoutine *amroutine;
/* Fetch the pg_class tuple of the index relation */
ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
@@ -552,17 +552,13 @@ IndexSupportsBackwardScan(Oid indexid)
elog(ERROR, "cache lookup failed for relation %u", indexid);
idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
- /* Fetch the pg_am tuple of the index' access method */
- ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
- if (!HeapTupleIsValid(ht_am))
- elog(ERROR, "cache lookup failed for access method %u",
- idxrelrec->relam);
- amrec = (Form_pg_am) GETSTRUCT(ht_am);
+ /* Fetch the index AM's API struct */
+ amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam);
- result = amrec->amcanbackward;
+ result = amroutine->amcanbackward;
+ pfree(amroutine);
ReleaseSysCache(ht_idxrel);
- ReleaseSysCache(ht_am);
return result;
}
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 46146613cf7..bf16cb1b57e 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -27,6 +27,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
+#include "catalog/pg_am.h"
#include "executor/execdebug.h"
#include "executor/nodeIndexscan.h"
#include "lib/pairingheap.h"
@@ -1053,7 +1054,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* can have either constant or non-constant comparison values.
*
* 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). If the index
- * has rd_am->amsearcharray, we handle these the same as simple operators,
+ * supports amsearcharray, we handle these the same as simple operators,
* setting the SK_SEARCHARRAY flag to tell the AM to handle them. Otherwise,
* we create a ScanKey with everything filled in except the comparison value,
* and set up an IndexArrayKeyInfo struct to drive processing of the qual.
@@ -1436,7 +1437,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
Assert(rightop != NULL);
- if (index->rd_am->amsearcharray)
+ if (index->rd_amroutine->amsearcharray)
{
/* Index AM will handle this like a simple operator */
flags |= SK_SEARCHARRAY;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index d95e15181ad..f1e22e5fb94 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1935,7 +1935,7 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node)
WRITE_BOOL_FIELD(unique);
WRITE_BOOL_FIELD(immediate);
WRITE_BOOL_FIELD(hypothetical);
- /* we don't bother with fields copied from the pg_am entry */
+ /* we don't bother with fields copied from the index AM's API struct */
}
static void
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 4e17fac6c13..8fb483aaebd 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -75,6 +75,7 @@
#endif
#include <math.h>
+#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/tsmapi.h"
#include "executor/executor.h"
@@ -364,6 +365,7 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count)
IndexOptInfo *index = path->indexinfo;
RelOptInfo *baserel = index->rel;
bool indexonly = (path->path.pathtype == T_IndexOnlyScan);
+ amcostestimate_function amcostestimate;
List *qpquals;
Cost startup_cost = 0;
Cost run_cost = 0;
@@ -419,14 +421,10 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count)
* the fraction of main-table tuples we will have to retrieve) and its
* correlation to the main-table tuple order.
*/
- OidFunctionCall7(index->amcostestimate,
- PointerGetDatum(root),
- PointerGetDatum(path),
- Float8GetDatum(loop_count),
- PointerGetDatum(&indexStartupCost),
- PointerGetDatum(&indexTotalCost),
- PointerGetDatum(&indexSelectivity),
- PointerGetDatum(&indexCorrelation));
+ amcostestimate = index->amcostestimate; /* cast to proper type */
+ amcostestimate(root, path, loop_count,
+ &indexStartupCost, &indexTotalCost,
+ &indexSelectivity, &indexCorrelation);
/*
* Save amcostestimate's results for possible use in bitmap scan planning.
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index d5528e03818..0ea9fcf7c20 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -27,6 +27,7 @@
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
+#include "catalog/pg_am.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
@@ -163,6 +164,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
Oid indexoid = lfirst_oid(l);
Relation indexRelation;
Form_pg_index index;
+ IndexAmRoutine *amroutine;
IndexOptInfo *info;
int ncolumns;
int i;
@@ -223,13 +225,17 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
}
info->relam = indexRelation->rd_rel->relam;
- info->amcostestimate = indexRelation->rd_am->amcostestimate;
- info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
- info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
- info->amsearcharray = indexRelation->rd_am->amsearcharray;
- info->amsearchnulls = indexRelation->rd_am->amsearchnulls;
- info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple);
- info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
+
+ /* We copy just the fields we need, not all of rd_amroutine */
+ amroutine = indexRelation->rd_amroutine;
+ info->amcanorderbyop = amroutine->amcanorderbyop;
+ info->amoptionalkey = amroutine->amoptionalkey;
+ info->amsearcharray = amroutine->amsearcharray;
+ info->amsearchnulls = amroutine->amsearchnulls;
+ info->amhasgettuple = (amroutine->amgettuple != NULL);
+ info->amhasgetbitmap = (amroutine->amgetbitmap != NULL);
+ info->amcostestimate = amroutine->amcostestimate;
+ Assert(info->amcostestimate != NULL);
/*
* Fetch the ordering information for the index, if any.
@@ -240,7 +246,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
* If it's a btree index, we can use its opfamily OIDs
* directly as the sort ordering opfamily OIDs.
*/
- Assert(indexRelation->rd_am->amcanorder);
+ Assert(amroutine->amcanorder);
info->sortopfamily = info->opfamily;
info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
@@ -254,7 +260,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
}
}
- else if (indexRelation->rd_am->amcanorder)
+ else if (amroutine->amcanorder)
{
/*
* Otherwise, identify the corresponding btree opfamilies by
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 8396048b5b8..7ea455cf043 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -21,6 +21,7 @@
#include "access/tsmapi.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e319d57acab..a65b2977281 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -26,12 +26,14 @@
#include "postgres.h"
+#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_opclass.h"
@@ -1072,6 +1074,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
Form_pg_attribute *attrs = RelationGetDescr(source_idx)->attrs;
HeapTuple ht_idxrel;
HeapTuple ht_idx;
+ HeapTuple ht_am;
Form_pg_class idxrelrec;
Form_pg_index idxrec;
Form_pg_am amrec;
@@ -1100,8 +1103,12 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
indrelid = idxrec->indrelid;
- /* Fetch pg_am tuple for source index from relcache entry */
- amrec = source_idx->rd_am;
+ /* Fetch the pg_am tuple of the index' access method */
+ ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
+ if (!HeapTupleIsValid(ht_am))
+ elog(ERROR, "cache lookup failed for access method %u",
+ idxrelrec->relam);
+ amrec = (Form_pg_am) GETSTRUCT(ht_am);
/* Extract indcollation from the pg_index tuple */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
@@ -1299,7 +1306,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
/* Adjust options if necessary */
- if (amrec->amcanorder)
+ if (source_idx->rd_amroutine->amcanorder)
{
/*
* If it supports sort ordering, copy DESC and NULLS opts. Don't
@@ -1361,6 +1368,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
/* Clean up */
ReleaseSysCache(ht_idxrel);
+ ReleaseSysCache(ht_am);
return index;
}
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 4fa66222d9d..e2859df41d6 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2420,7 +2420,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
- relopts = extractRelOptions(tup, pg_class_desc, InvalidOid);
+ relopts = extractRelOptions(tup, pg_class_desc, NULL);
if (relopts == NULL)
return NULL;
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index f309ad3cbee..dd447cf4e86 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -374,6 +374,33 @@ fdw_handler_out(PG_FUNCTION_ARGS)
/*
+ * index_am_handler_in - input routine for pseudo-type INDEX_AM_HANDLER.
+ */
+Datum
+index_am_handler_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type index_am_handler")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * index_am_handler_out - output routine for pseudo-type INDEX_AM_HANDLER.
+ */
+Datum
+index_am_handler_out(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot display a value of type index_am_handler")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+
+/*
* tsm_handler_in - input routine for pseudo-type TSM_HANDLER.
*/
Datum
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 023d651899a..4efd2988e79 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -19,11 +19,13 @@
#include <unistd.h>
#include <fcntl.h>
+#include "access/amapi.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_aggregate.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
@@ -1019,6 +1021,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Form_pg_index idxrec;
Form_pg_class idxrelrec;
Form_pg_am amrec;
+ IndexAmRoutine *amroutine;
List *indexprs;
ListCell *indexpr_item;
List *context;
@@ -1079,6 +1082,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
idxrelrec->relam);
amrec = (Form_pg_am) GETSTRUCT(ht_am);
+ /* Fetch the index AM's API struct */
+ amroutine = GetIndexAmRoutine(amrec->amhandler);
+
/*
* Get the index expressions, if any. (NOTE: we do not use the relcache
* versions of the expressions and predicate, because we want to display
@@ -1190,7 +1196,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
get_opclass_name(indclass->values[keyno], keycoltype, &buf);
/* Add options if relevant */
- if (amrec->amcanorder)
+ if (amroutine->amcanorder)
{
/* if it supports sort ordering, report DESC and NULLS opts */
if (opt & INDOPTION_DESC)
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index ebb03aae475..46c95b089ed 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -7,8 +7,8 @@
* Selectivity routines are registered in the pg_operator catalog
* in the "oprrest" and "oprjoin" attributes.
*
- * Index cost functions are registered in the pg_am catalog
- * in the "amcostestimate" attribute.
+ * Index cost functions are located via the index AM's API struct,
+ * which is obtained from the handler function registered in pg_am.
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -104,6 +104,7 @@
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "catalog/index.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
@@ -129,6 +130,7 @@
#include "utils/date.h"
#include "utils/datum.h"
#include "utils/fmgroids.h"
+#include "utils/index_selfuncs.h"
#include "utils/lsyscache.h"
#include "utils/nabstime.h"
#include "utils/pg_locale.h"
@@ -6443,16 +6445,11 @@ add_predicate_to_quals(IndexOptInfo *index, List *indexQuals)
}
-Datum
-btcostestimate(PG_FUNCTION_ARGS)
+void
+btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ Cost *indexStartupCost, Cost *indexTotalCost,
+ Selectivity *indexSelectivity, double *indexCorrelation)
{
- PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
- double loop_count = PG_GETARG_FLOAT8(2);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs;
@@ -6741,20 +6738,13 @@ btcostestimate(PG_FUNCTION_ARGS)
*indexTotalCost = costs.indexTotalCost;
*indexSelectivity = costs.indexSelectivity;
*indexCorrelation = costs.indexCorrelation;
-
- PG_RETURN_VOID();
}
-Datum
-hashcostestimate(PG_FUNCTION_ARGS)
+void
+hashcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ Cost *indexStartupCost, Cost *indexTotalCost,
+ Selectivity *indexSelectivity, double *indexCorrelation)
{
- PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
- double loop_count = PG_GETARG_FLOAT8(2);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
List *qinfos;
GenericCosts costs;
@@ -6794,20 +6784,13 @@ hashcostestimate(PG_FUNCTION_ARGS)
*indexTotalCost = costs.indexTotalCost;
*indexSelectivity = costs.indexSelectivity;
*indexCorrelation = costs.indexCorrelation;
-
- PG_RETURN_VOID();
}
-Datum
-gistcostestimate(PG_FUNCTION_ARGS)
+void
+gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ Cost *indexStartupCost, Cost *indexTotalCost,
+ Selectivity *indexSelectivity, double *indexCorrelation)
{
- PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
- double loop_count = PG_GETARG_FLOAT8(2);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs;
@@ -6860,20 +6843,13 @@ gistcostestimate(PG_FUNCTION_ARGS)
*indexTotalCost = costs.indexTotalCost;
*indexSelectivity = costs.indexSelectivity;
*indexCorrelation = costs.indexCorrelation;
-
- PG_RETURN_VOID();
}
-Datum
-spgcostestimate(PG_FUNCTION_ARGS)
+void
+spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ Cost *indexStartupCost, Cost *indexTotalCost,
+ Selectivity *indexSelectivity, double *indexCorrelation)
{
- PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
- double loop_count = PG_GETARG_FLOAT8(2);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
IndexOptInfo *index = path->indexinfo;
List *qinfos;
GenericCosts costs;
@@ -6926,8 +6902,6 @@ spgcostestimate(PG_FUNCTION_ARGS)
*indexTotalCost = costs.indexTotalCost;
*indexSelectivity = costs.indexSelectivity;
*indexCorrelation = costs.indexCorrelation;
-
- PG_RETURN_VOID();
}
@@ -7222,16 +7196,11 @@ gincost_scalararrayopexpr(PlannerInfo *root,
/*
* GIN has search behavior completely different from other index types
*/
-Datum
-gincostestimate(PG_FUNCTION_ARGS)
+void
+gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ Cost *indexStartupCost, Cost *indexTotalCost,
+ Selectivity *indexSelectivity, double *indexCorrelation)
{
- PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
- double loop_count = PG_GETARG_FLOAT8(2);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
IndexOptInfo *index = path->indexinfo;
List *indexQuals = path->indexquals;
List *indexOrderBys = path->indexorderbys;
@@ -7418,7 +7387,7 @@ gincostestimate(PG_FUNCTION_ARGS)
*indexStartupCost = 0;
*indexTotalCost = 0;
*indexSelectivity = 0;
- PG_RETURN_VOID();
+ return;
}
if (counts.haveFullScan || indexQuals == NIL)
@@ -7545,23 +7514,16 @@ gincostestimate(PG_FUNCTION_ARGS)
*indexStartupCost += qual_arg_cost;
*indexTotalCost += qual_arg_cost;
*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost + qual_op_cost);
-
- PG_RETURN_VOID();
}
/*
* BRIN has search behavior completely different from other index types
*/
-Datum
-brincostestimate(PG_FUNCTION_ARGS)
+void
+brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
+ Cost *indexStartupCost, Cost *indexTotalCost,
+ Selectivity *indexSelectivity, double *indexCorrelation)
{
- PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- IndexPath *path = (IndexPath *) PG_GETARG_POINTER(1);
- double loop_count = PG_GETARG_FLOAT8(2);
- Cost *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- Cost *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- double *indexCorrelation = (double *) PG_GETARG_POINTER(6);
IndexOptInfo *index = path->indexinfo;
List *indexQuals = path->indexquals;
List *indexOrderBys = path->indexorderbys;
@@ -7614,6 +7576,4 @@ brincostestimate(PG_FUNCTION_ARGS)
*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost + qual_op_cost);
/* XXX what about pages_per_range? */
-
- PG_RETURN_VOID();
}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 5c9333c296c..a180d2b507a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -20,6 +20,7 @@
#include "access/nbtree.h"
#include "bootstrap/bootstrap.h"
#include "catalog/namespace.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_collation.h"
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fc5b9d99340..130c06d81c8 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -34,13 +34,13 @@
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
-#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
@@ -267,6 +267,7 @@ static void AttrDefaultFetch(Relation relation);
static void CheckConstraintFetch(Relation relation);
static int CheckConstraintCmp(const void *a, const void *b);
static List *insert_ordered_oid(List *list, Oid datum);
+static void InitIndexAmRoutine(Relation relation);
static void IndexSupportInitialize(oidvector *indclass,
RegProcedure *indexSupport,
Oid *opFamily,
@@ -417,7 +418,7 @@ AllocateRelationDesc(Form_pg_class relp)
*
* tuple is the real pg_class tuple (not rd_rel!) for relation
*
- * Note: rd_rel and (if an index) rd_am must be valid already
+ * Note: rd_rel and (if an index) rd_amroutine must be valid already
*/
static void
RelationParseRelOptions(Relation relation, HeapTuple tuple)
@@ -447,7 +448,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
options = extractRelOptions(tuple,
GetPgClassDescriptor(),
relation->rd_rel->relkind == RELKIND_INDEX ?
- relation->rd_am->amoptions : InvalidOid);
+ relation->rd_amroutine->amoptions : NULL);
/*
* Copy parsed data into CacheMemoryContext. To guard against the
@@ -1162,6 +1163,32 @@ RelationInitPhysicalAddr(Relation relation)
}
/*
+ * Fill in the IndexAmRoutine for an index relation.
+ *
+ * relation's rd_amhandler and rd_indexcxt must be valid already.
+ */
+static void
+InitIndexAmRoutine(Relation relation)
+{
+ IndexAmRoutine *cached,
+ *tmp;
+
+ /*
+ * Call the amhandler in current, short-lived memory context, just in case
+ * it leaks anything (it probably won't, but let's be paranoid).
+ */
+ tmp = GetIndexAmRoutine(relation->rd_amhandler);
+
+ /* OK, now transfer the data into relation's rd_indexcxt. */
+ cached = (IndexAmRoutine *) MemoryContextAlloc(relation->rd_indexcxt,
+ sizeof(IndexAmRoutine));
+ memcpy(cached, tmp, sizeof(IndexAmRoutine));
+ relation->rd_amroutine = cached;
+
+ pfree(tmp);
+}
+
+/*
* Initialize index-access-method support data for an index relation
*/
void
@@ -1198,22 +1225,20 @@ RelationInitIndexAccessInfo(Relation relation)
ReleaseSysCache(tuple);
/*
- * Make a copy of the pg_am entry for the index's access method
+ * Look up the index's access method, save the OID of its handler function
*/
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) MemoryContextAlloc(CacheMemoryContext, sizeof *aform);
- memcpy(aform, GETSTRUCT(tuple), sizeof *aform);
+ aform = (Form_pg_am) GETSTRUCT(tuple);
+ relation->rd_amhandler = aform->amhandler;
ReleaseSysCache(tuple);
- relation->rd_am = aform;
natts = relation->rd_rel->relnatts;
if (natts != relation->rd_index->indnatts)
elog(ERROR, "relnatts disagrees with indnatts for index %u",
RelationGetRelid(relation));
- amsupport = aform->amsupport;
/*
* Make the private context to hold index access info. The reason we need
@@ -1231,16 +1256,19 @@ RelationInitIndexAccessInfo(Relation relation)
relation->rd_indexcxt = indexcxt;
/*
- * Allocate arrays to hold data
+ * Now we can fetch the index AM's API struct
*/
- relation->rd_aminfo = (RelationAmInfo *)
- MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
+ InitIndexAmRoutine(relation);
+ /*
+ * Allocate arrays to hold data
+ */
relation->rd_opfamily = (Oid *)
MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
relation->rd_opcintype = (Oid *)
MemoryContextAllocZero(indexcxt, natts * sizeof(Oid));
+ amsupport = relation->rd_amroutine->amsupport;
if (amsupport > 0)
{
int nsupport = natts * amsupport;
@@ -2011,8 +2039,6 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
pfree(relation->rd_options);
if (relation->rd_indextuple)
pfree(relation->rd_indextuple);
- if (relation->rd_am)
- pfree(relation->rd_am);
if (relation->rd_indexcxt)
MemoryContextDelete(relation->rd_indexcxt);
if (relation->rd_rulescxt)
@@ -4746,7 +4772,6 @@ load_relcache_init_file(bool shared)
/* If it's an index, there's more to do */
if (rel->rd_rel->relkind == RELKIND_INDEX)
{
- Form_pg_am am;
MemoryContext indexcxt;
Oid *opfamily;
Oid *opcintype;
@@ -4771,15 +4796,6 @@ load_relcache_init_file(bool shared)
rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
- /* next, read the access method tuple form */
- if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
- goto read_failed;
-
- am = (Form_pg_am) palloc(len);
- if (fread(am, 1, len, fp) != len)
- goto read_failed;
- rel->rd_am = am;
-
/*
* prepare index info context --- parameters should match
* RelationInitIndexAccessInfo
@@ -4791,6 +4807,14 @@ load_relcache_init_file(bool shared)
ALLOCSET_SMALL_MAXSIZE);
rel->rd_indexcxt = indexcxt;
+ /*
+ * Now we can fetch the index AM's API struct. (We can't store
+ * that in the init file, since it contains function pointers that
+ * might vary across server executions. Fortunately, it should be
+ * safe to call the amhandler even while bootstrapping indexes.)
+ */
+ InitIndexAmRoutine(rel);
+
/* next, read the vector of opfamily OIDs */
if (fread(&len, 1, sizeof(len), fp) != sizeof(len))
goto read_failed;
@@ -4840,10 +4864,8 @@ load_relcache_init_file(bool shared)
rel->rd_indoption = indoption;
- /* set up zeroed fmgr-info vectors */
- rel->rd_aminfo = (RelationAmInfo *)
- MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
- nsupport = relform->relnatts * am->amsupport;
+ /* set up zeroed fmgr-info vector */
+ nsupport = relform->relnatts * rel->rd_amroutine->amsupport;
rel->rd_supportinfo = (FmgrInfo *)
MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
}
@@ -4855,9 +4877,8 @@ load_relcache_init_file(bool shared)
Assert(rel->rd_index == NULL);
Assert(rel->rd_indextuple == NULL);
- Assert(rel->rd_am == NULL);
Assert(rel->rd_indexcxt == NULL);
- Assert(rel->rd_aminfo == NULL);
+ Assert(rel->rd_amroutine == NULL);
Assert(rel->rd_opfamily == NULL);
Assert(rel->rd_opcintype == NULL);
Assert(rel->rd_support == NULL);
@@ -5101,17 +5122,12 @@ write_relcache_init_file(bool shared)
/* If it's an index, there's more to do */
if (rel->rd_rel->relkind == RELKIND_INDEX)
{
- Form_pg_am am = rel->rd_am;
-
/* write the pg_index tuple */
/* we assume this was created by heap_copytuple! */
write_item(rel->rd_indextuple,
HEAPTUPLESIZE + rel->rd_indextuple->t_len,
fp);
- /* next, write the access method tuple form */
- write_item(am, sizeof(FormData_pg_am), fp);
-
/* next, write the vector of opfamily OIDs */
write_item(rel->rd_opfamily,
relform->relnatts * sizeof(Oid),
@@ -5124,7 +5140,7 @@ write_relcache_init_file(bool shared)
/* next, write the vector of support procedure OIDs */
write_item(rel->rd_support,
- relform->relnatts * (am->amsupport * sizeof(RegProcedure)),
+ relform->relnatts * (rel->rd_amroutine->amsupport * sizeof(RegProcedure)),
fp);
/* next, write the vector of collation OIDs */
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 6eb2ac69a13..65ffe844093 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -24,6 +24,7 @@
#include "access/sysattr.h"
#include "catalog/indexing.h"
#include "catalog/pg_aggregate.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_auth_members.h"
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 4ad787bfa45..ea6f787a527 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -47,6 +47,7 @@
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "catalog/indexing.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_operator.h"
diff --git a/src/backend/utils/sort/sortsupport.c b/src/backend/utils/sort/sortsupport.c
index e193372d2e5..e9eeb18b7ec 100644
--- a/src/backend/utils/sort/sortsupport.c
+++ b/src/backend/utils/sort/sortsupport.c
@@ -16,6 +16,7 @@
#include "postgres.h"
#include "access/nbtree.h"
+#include "catalog/pg_am.h"
#include "fmgr.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 3d5b8ed9070..a30e1707a79 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -103,6 +103,7 @@
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "catalog/index.h"
+#include "catalog/pg_am.h"
#include "commands/tablespace.h"
#include "executor/executor.h"
#include "miscadmin.h"
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
new file mode 100644
index 00000000000..35f1061b3a1
--- /dev/null
+++ b/src/include/access/amapi.h
@@ -0,0 +1,174 @@
+/*-------------------------------------------------------------------------
+ *
+ * amapi.h
+ * API for Postgres index access methods.
+ *
+ * Copyright (c) 2015-2016, PostgreSQL Global Development Group
+ *
+ * src/include/access/amapi.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef AMAPI_H
+#define AMAPI_H
+
+#include "access/genam.h"
+
+/*
+ * We don't wish to include planner header files here, since most of an index
+ * AM's implementation isn't concerned with those data structures. To allow
+ * declaring amcostestimate_function here, use forward struct references.
+ */
+struct PlannerInfo;
+struct IndexPath;
+
+/* Likewise, this file shouldn't depend on execnodes.h. */
+struct IndexInfo;
+
+
+/*
+ * Callback function signatures --- see indexam.sgml for more info.
+ */
+
+/* build new index */
+typedef IndexBuildResult *(*ambuild_function) (Relation heapRelation,
+ Relation indexRelation,
+ struct IndexInfo *indexInfo);
+
+/* build empty index */
+typedef void (*ambuildempty_function) (Relation indexRelation);
+
+/* insert this tuple */
+typedef bool (*aminsert_function) (Relation indexRelation,
+ Datum *values,
+ bool *isnull,
+ ItemPointer heap_tid,
+ Relation heapRelation,
+ IndexUniqueCheck checkUnique);
+
+/* bulk delete */
+typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+
+/* post-VACUUM cleanup */
+typedef IndexBulkDeleteResult *(*amvacuumcleanup_function) (IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
+
+/* can indexscan return IndexTuples? */
+typedef bool (*amcanreturn_function) (Relation indexRelation, int attno);
+
+/* estimate cost of an indexscan */
+typedef void (*amcostestimate_function) (struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+
+/* parse index reloptions */
+typedef bytea *(*amoptions_function) (Datum reloptions,
+ bool validate);
+
+/* validate definition of an opclass for this AM */
+typedef bool (*amvalidate_function) (Oid opclassoid);
+
+/* prepare for index scan */
+typedef IndexScanDesc (*ambeginscan_function) (Relation indexRelation,
+ int nkeys,
+ int norderbys);
+
+/* (re)start index scan */
+typedef void (*amrescan_function) (IndexScanDesc scan,
+ ScanKey keys,
+ int nkeys,
+ ScanKey orderbys,
+ int norderbys);
+
+/* next valid tuple */
+typedef bool (*amgettuple_function) (IndexScanDesc scan,
+ ScanDirection direction);
+
+/* fetch all valid tuples */
+typedef int64 (*amgetbitmap_function) (IndexScanDesc scan,
+ TIDBitmap *tbm);
+
+/* end index scan */
+typedef void (*amendscan_function) (IndexScanDesc scan);
+
+/* mark current scan position */
+typedef void (*ammarkpos_function) (IndexScanDesc scan);
+
+/* restore marked scan position */
+typedef void (*amrestrpos_function) (IndexScanDesc scan);
+
+
+/*
+ * API struct for an index AM. Note this must be stored in a single palloc'd
+ * chunk of memory.
+ */
+typedef struct IndexAmRoutine
+{
+ NodeTag type;
+
+ /*
+ * Total number of strategies (operators) by which we can traverse/search
+ * this AM. Zero if AM does not have a fixed set of strategy assignments.
+ */
+ uint16 amstrategies;
+ /* total number of support functions that this AM uses */
+ uint16 amsupport;
+ /* does AM support ORDER BY indexed column's value? */
+ bool amcanorder;
+ /* does AM support ORDER BY result of an operator on indexed column? */
+ bool amcanorderbyop;
+ /* does AM support backward scanning? */
+ bool amcanbackward;
+ /* does AM support UNIQUE indexes? */
+ bool amcanunique;
+ /* does AM support multi-column indexes? */
+ bool amcanmulticol;
+ /* does AM require scans to have a constraint on the first index column? */
+ bool amoptionalkey;
+ /* does AM handle ScalarArrayOpExpr quals? */
+ bool amsearcharray;
+ /* does AM handle IS NULL/IS NOT NULL quals? */
+ bool amsearchnulls;
+ /* can index storage data type differ from column data type? */
+ bool amstorage;
+ /* can an index of this type be clustered on? */
+ bool amclusterable;
+ /* does AM handle predicate locks? */
+ bool ampredlocks;
+ /* type of data stored in index, or InvalidOid if variable */
+ Oid amkeytype;
+
+ /* interface functions */
+ ambuild_function ambuild;
+ ambuildempty_function ambuildempty;
+ aminsert_function aminsert;
+ ambulkdelete_function ambulkdelete;
+ amvacuumcleanup_function amvacuumcleanup;
+ amcanreturn_function amcanreturn; /* can be NULL */
+ amcostestimate_function amcostestimate;
+ amoptions_function amoptions;
+ amvalidate_function amvalidate;
+ ambeginscan_function ambeginscan;
+ amrescan_function amrescan;
+ amgettuple_function amgettuple; /* can be NULL */
+ amgetbitmap_function amgetbitmap; /* can be NULL */
+ amendscan_function amendscan;
+ ammarkpos_function ammarkpos; /* can be NULL */
+ amrestrpos_function amrestrpos; /* can be NULL */
+} IndexAmRoutine;
+
+
+/* Functions in access/index/amapi.c */
+extern IndexAmRoutine *GetIndexAmRoutine(Oid amhandler);
+extern IndexAmRoutine *GetIndexAmRoutineByAmId(Oid amoid);
+
+extern Datum amvalidate(PG_FUNCTION_ARGS);
+
+#endif /* AMAPI_H */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 111de82e046..99bf5330bbd 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -18,19 +18,8 @@
/*
* prototypes for functions in brin.c (external entry points for BRIN)
*/
-extern Datum brinbuild(PG_FUNCTION_ARGS);
-extern Datum brinbuildempty(PG_FUNCTION_ARGS);
-extern Datum brininsert(PG_FUNCTION_ARGS);
-extern Datum brinbeginscan(PG_FUNCTION_ARGS);
-extern Datum bringetbitmap(PG_FUNCTION_ARGS);
-extern Datum brinrescan(PG_FUNCTION_ARGS);
-extern Datum brinendscan(PG_FUNCTION_ARGS);
-extern Datum brinmarkpos(PG_FUNCTION_ARGS);
-extern Datum brinrestrpos(PG_FUNCTION_ARGS);
-extern Datum brinbulkdelete(PG_FUNCTION_ARGS);
-extern Datum brinvacuumcleanup(PG_FUNCTION_ARGS);
-extern Datum brincostestimate(PG_FUNCTION_ARGS);
-extern Datum brinoptions(PG_FUNCTION_ARGS);
+extern Datum brinhandler(PG_FUNCTION_ARGS);
+extern Datum brin_summarize_new_values(PG_FUNCTION_ARGS);
/*
* Storage type for BRIN's reloptions
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 5ec47f4c84c..47317af43c8 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -11,11 +11,8 @@
#ifndef BRIN_INTERNAL_H
#define BRIN_INTERNAL_H
-#include "fmgr.h"
-#include "storage/buf.h"
+#include "access/amapi.h"
#include "storage/bufpage.h"
-#include "storage/off.h"
-#include "utils/relcache.h"
#include "utils/typcache.h"
@@ -64,14 +61,17 @@ typedef struct BrinDesc
/*
* Globally-known function support numbers for BRIN indexes. Individual
- * opclasses define their own function support numbers, which must not collide
- * with the definitions here.
+ * opclasses can define more function support numbers, which must fall into
+ * BRIN_FIRST_OPTIONAL_PROCNUM .. BRIN_LAST_OPTIONAL_PROCNUM.
*/
#define BRIN_PROCNUM_OPCINFO 1
#define BRIN_PROCNUM_ADDVALUE 2
#define BRIN_PROCNUM_CONSISTENT 3
#define BRIN_PROCNUM_UNION 4
+#define BRIN_MANDATORY_NPROCS 4
/* procedure numbers up to 10 are reserved for BRIN future expansion */
+#define BRIN_FIRST_OPTIONAL_PROCNUM 11
+#define BRIN_LAST_OPTIONAL_PROCNUM 15
#undef BRIN_DEBUG
@@ -84,6 +84,26 @@ typedef struct BrinDesc
/* brin.c */
extern BrinDesc *brin_build_desc(Relation rel);
extern void brin_free_desc(BrinDesc *bdesc);
-extern Datum brin_summarize_new_values(PG_FUNCTION_ARGS);
+extern IndexBuildResult *brinbuild(Relation heap, Relation index,
+ struct IndexInfo *indexInfo);
+extern void brinbuildempty(Relation index);
+extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
+ ItemPointer heaptid, Relation heapRel,
+ IndexUniqueCheck checkUnique);
+extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
+extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys);
+extern void brinendscan(IndexScanDesc scan);
+extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
+extern bytea *brinoptions(Datum reloptions, bool validate);
+
+/* brin_validate.c */
+extern bool brinvalidate(Oid opclassoid);
#endif /* BRIN_INTERNAL_H */
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 2ba8a21124f..fe04aaa6ef2 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -10,7 +10,7 @@
#ifndef GIN_PRIVATE_H
#define GIN_PRIVATE_H
-#include "access/genam.h"
+#include "access/amapi.h"
#include "access/gin.h"
#include "access/itup.h"
#include "fmgr.h"
@@ -593,7 +593,8 @@ typedef struct ginxlogDeleteListPages
/* ginutil.c */
-extern Datum ginoptions(PG_FUNCTION_ARGS);
+extern Datum ginhandler(PG_FUNCTION_ARGS);
+extern bytea *ginoptions(Datum reloptions, bool validate);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -614,9 +615,12 @@ extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
/* gininsert.c */
-extern Datum ginbuild(PG_FUNCTION_ARGS);
-extern Datum ginbuildempty(PG_FUNCTION_ARGS);
-extern Datum gininsert(PG_FUNCTION_ARGS);
+extern IndexBuildResult *ginbuild(Relation heap, Relation index,
+ struct IndexInfo *indexInfo);
+extern void ginbuildempty(Relation index);
+extern bool gininsert(Relation index, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique);
extern void ginEntryInsert(GinState *ginstate,
OffsetNumber attnum, Datum key, GinNullCategory category,
ItemPointerData *items, uint32 nitem,
@@ -867,26 +871,32 @@ typedef struct GinScanOpaqueData
typedef GinScanOpaqueData *GinScanOpaque;
-extern Datum ginbeginscan(PG_FUNCTION_ARGS);
-extern Datum ginendscan(PG_FUNCTION_ARGS);
-extern Datum ginrescan(PG_FUNCTION_ARGS);
-extern Datum ginmarkpos(PG_FUNCTION_ARGS);
-extern Datum ginrestrpos(PG_FUNCTION_ARGS);
+extern IndexScanDesc ginbeginscan(Relation rel, int nkeys, int norderbys);
+extern void ginendscan(IndexScanDesc scan);
+extern void ginrescan(IndexScanDesc scan, ScanKey key, int nscankeys,
+ ScanKey orderbys, int norderbys);
extern void ginNewScanKey(IndexScanDesc scan);
extern void ginFreeScanKeys(GinScanOpaque so);
/* ginget.c */
-extern Datum gingetbitmap(PG_FUNCTION_ARGS);
+extern int64 gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
/* ginlogic.c */
extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key);
/* ginvacuum.c */
-extern Datum ginbulkdelete(PG_FUNCTION_ARGS);
-extern Datum ginvacuumcleanup(PG_FUNCTION_ARGS);
+extern IndexBulkDeleteResult *ginbulkdelete(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+extern IndexBulkDeleteResult *ginvacuumcleanup(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
extern ItemPointer ginVacuumItemPointers(GinVacuumState *gvs,
ItemPointerData *items, int nitem, int *nremaining);
+/* ginvalidate.c */
+extern bool ginvalidate(Oid opclassoid);
+
/* ginbulk.c */
typedef struct GinEntryAccumulator
{
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 71f4b5ef633..f9732ba7fb9 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -14,6 +14,7 @@
#ifndef GIST_PRIVATE_H
#define GIST_PRIVATE_H
+#include "access/amapi.h"
#include "access/gist.h"
#include "access/itup.h"
#include "access/xlogreader.h"
@@ -426,9 +427,11 @@ typedef struct GiSTOptions
} GiSTOptions;
/* gist.c */
-extern Datum gistbuildempty(PG_FUNCTION_ARGS);
-extern Datum gistinsert(PG_FUNCTION_ARGS);
-extern Datum gistcanreturn(PG_FUNCTION_ARGS);
+extern Datum gisthandler(PG_FUNCTION_ARGS);
+extern void gistbuildempty(Relation index);
+extern bool gistinsert(Relation r, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique);
extern MemoryContext createTempGistContext(void);
extern GISTSTATE *initGISTstate(Relation index);
extern void freeGISTstate(GISTSTATE *giststate);
@@ -474,8 +477,12 @@ extern XLogRecPtr gistXLogSplit(RelFileNode node,
Buffer leftchild, bool markfollowright);
/* gistget.c */
-extern Datum gistgettuple(PG_FUNCTION_ARGS);
-extern Datum gistgetbitmap(PG_FUNCTION_ARGS);
+extern bool gistgettuple(IndexScanDesc scan, ScanDirection dir);
+extern int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+extern bool gistcanreturn(Relation index, int attno);
+
+/* gistvalidate.c */
+extern bool gistvalidate(Oid opclassoid);
/* gistutil.c */
@@ -485,7 +492,7 @@ extern Datum gistgetbitmap(PG_FUNCTION_ARGS);
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern Datum gistoptions(PG_FUNCTION_ARGS);
+extern bytea *gistoptions(Datum reloptions, bool validate);
extern bool gistfitpage(IndexTuple *itvec, int len);
extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
extern void gistcheckpage(Relation rel, Buffer buf);
@@ -534,8 +541,12 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
extern XLogRecPtr gistGetFakeLSN(Relation rel);
/* gistvacuum.c */
-extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
-extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);
+extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+extern IndexBulkDeleteResult *gistvacuumcleanup(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
/* gistsplit.c */
extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
@@ -544,7 +555,8 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
int attno);
/* gistbuild.c */
-extern Datum gistbuild(PG_FUNCTION_ARGS);
+extern IndexBuildResult *gistbuild(Relation heap, Relation index,
+ struct IndexInfo *indexInfo);
extern void gistValidateBufferingOption(char *value);
/* gistbuildbuffers.c */
diff --git a/src/include/access/gistscan.h b/src/include/access/gistscan.h
index a7d498758c4..4acaebdc9ed 100644
--- a/src/include/access/gistscan.h
+++ b/src/include/access/gistscan.h
@@ -14,12 +14,11 @@
#ifndef GISTSCAN_H
#define GISTSCAN_H
-#include "fmgr.h"
+#include "access/amapi.h"
-extern Datum gistbeginscan(PG_FUNCTION_ARGS);
-extern Datum gistrescan(PG_FUNCTION_ARGS);
-extern Datum gistmarkpos(PG_FUNCTION_ARGS);
-extern Datum gistrestrpos(PG_FUNCTION_ARGS);
-extern Datum gistendscan(PG_FUNCTION_ARGS);
+extern IndexScanDesc gistbeginscan(Relation r, int nkeys, int norderbys);
+extern void gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
+ ScanKey orderbys, int norderbys);
+extern void gistendscan(IndexScanDesc scan);
#endif /* GISTSCAN_H */
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 6dde6a71b3a..3a683904739 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -17,7 +17,7 @@
#ifndef HASH_H
#define HASH_H
-#include "access/genam.h"
+#include "access/amapi.h"
#include "access/itup.h"
#include "access/sdir.h"
#include "access/xlogreader.h"
@@ -243,19 +243,27 @@ typedef HashMetaPageData *HashMetaPage;
/* public routines */
-extern Datum hashbuild(PG_FUNCTION_ARGS);
-extern Datum hashbuildempty(PG_FUNCTION_ARGS);
-extern Datum hashinsert(PG_FUNCTION_ARGS);
-extern Datum hashbeginscan(PG_FUNCTION_ARGS);
-extern Datum hashgettuple(PG_FUNCTION_ARGS);
-extern Datum hashgetbitmap(PG_FUNCTION_ARGS);
-extern Datum hashrescan(PG_FUNCTION_ARGS);
-extern Datum hashendscan(PG_FUNCTION_ARGS);
-extern Datum hashmarkpos(PG_FUNCTION_ARGS);
-extern Datum hashrestrpos(PG_FUNCTION_ARGS);
-extern Datum hashbulkdelete(PG_FUNCTION_ARGS);
-extern Datum hashvacuumcleanup(PG_FUNCTION_ARGS);
-extern Datum hashoptions(PG_FUNCTION_ARGS);
+extern Datum hashhandler(PG_FUNCTION_ARGS);
+extern IndexBuildResult *hashbuild(Relation heap, Relation index,
+ struct IndexInfo *indexInfo);
+extern void hashbuildempty(Relation index);
+extern bool hashinsert(Relation rel, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique);
+extern bool hashgettuple(IndexScanDesc scan, ScanDirection dir);
+extern int64 hashgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+extern IndexScanDesc hashbeginscan(Relation rel, int nkeys, int norderbys);
+extern void hashrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys);
+extern void hashendscan(IndexScanDesc scan);
+extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
+extern bytea *hashoptions(Datum reloptions, bool validate);
+extern bool hashvalidate(Oid opclassoid);
/*
* Datatype-specific hash functions in hashfunc.c.
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index b76083323b7..06822fac3c4 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -14,7 +14,7 @@
#ifndef NBTREE_H
#define NBTREE_H
-#include "access/genam.h"
+#include "access/amapi.h"
#include "access/itup.h"
#include "access/sdir.h"
#include "access/xlogreader.h"
@@ -654,20 +654,28 @@ typedef BTScanOpaqueData *BTScanOpaque;
/*
* prototypes for functions in nbtree.c (external entry points for btree)
*/
-extern Datum btbuild(PG_FUNCTION_ARGS);
-extern Datum btbuildempty(PG_FUNCTION_ARGS);
-extern Datum btinsert(PG_FUNCTION_ARGS);
-extern Datum btbeginscan(PG_FUNCTION_ARGS);
-extern Datum btgettuple(PG_FUNCTION_ARGS);
-extern Datum btgetbitmap(PG_FUNCTION_ARGS);
-extern Datum btrescan(PG_FUNCTION_ARGS);
-extern Datum btendscan(PG_FUNCTION_ARGS);
-extern Datum btmarkpos(PG_FUNCTION_ARGS);
-extern Datum btrestrpos(PG_FUNCTION_ARGS);
-extern Datum btbulkdelete(PG_FUNCTION_ARGS);
-extern Datum btvacuumcleanup(PG_FUNCTION_ARGS);
-extern Datum btcanreturn(PG_FUNCTION_ARGS);
-extern Datum btoptions(PG_FUNCTION_ARGS);
+extern Datum bthandler(PG_FUNCTION_ARGS);
+extern IndexBuildResult *btbuild(Relation heap, Relation index,
+ struct IndexInfo *indexInfo);
+extern void btbuildempty(Relation index);
+extern bool btinsert(Relation rel, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique);
+extern IndexScanDesc btbeginscan(Relation rel, int nkeys, int norderbys);
+extern bool btgettuple(IndexScanDesc scan, ScanDirection dir);
+extern int64 btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+extern void btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys);
+extern void btendscan(IndexScanDesc scan);
+extern void btmarkpos(IndexScanDesc scan);
+extern void btrestrpos(IndexScanDesc scan);
+extern IndexBulkDeleteResult *btbulkdelete(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+extern IndexBulkDeleteResult *btvacuumcleanup(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
+extern bool btcanreturn(Relation index, int attno);
/*
* prototypes for functions in nbtinsert.c
@@ -738,6 +746,12 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
+extern bytea *btoptions(Datum reloptions, bool validate);
+
+/*
+ * prototypes for functions in nbtvalidate.c
+ */
+extern bool btvalidate(Oid opclassoid);
/*
* prototypes for functions in nbtsort.c
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index ca6b519c9e1..469ac677e30 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -19,6 +19,7 @@
#ifndef RELOPTIONS_H
#define RELOPTIONS_H
+#include "access/amapi.h"
#include "access/htup.h"
#include "access/tupdesc.h"
#include "nodes/pg_list.h"
@@ -258,7 +259,7 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList,
bool ignoreOids, bool isReset);
extern List *untransformRelOptions(Datum options);
extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- Oid amoptions);
+ amoptions_function amoptions);
extern relopt_value *parseRelOptions(Datum options, bool validate,
relopt_kind kind, int *numrelopts);
extern void *allocateReloptStruct(Size base, relopt_value *options,
@@ -272,7 +273,7 @@ extern bytea *default_reloptions(Datum reloptions, bool validate,
relopt_kind kind);
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
+extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
bool validate);
extern bytea *attribute_reloptions(Datum reloptions, bool validate);
extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 5936af6f4e7..1994f718eb1 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -14,7 +14,7 @@
#ifndef SPGIST_H
#define SPGIST_H
-#include "access/skey.h"
+#include "access/amapi.h"
#include "access/xlogreader.h"
#include "fmgr.h"
#include "lib/stringinfo.h"
@@ -174,27 +174,37 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
+/* spgutils.c */
+extern Datum spghandler(PG_FUNCTION_ARGS);
+extern bytea *spgoptions(Datum reloptions, bool validate);
+
/* spginsert.c */
-extern Datum spgbuild(PG_FUNCTION_ARGS);
-extern Datum spgbuildempty(PG_FUNCTION_ARGS);
-extern Datum spginsert(PG_FUNCTION_ARGS);
+extern IndexBuildResult *spgbuild(Relation heap, Relation index,
+ struct IndexInfo *indexInfo);
+extern void spgbuildempty(Relation index);
+extern bool spginsert(Relation index, Datum *values, bool *isnull,
+ ItemPointer ht_ctid, Relation heapRel,
+ IndexUniqueCheck checkUnique);
/* spgscan.c */
-extern Datum spgbeginscan(PG_FUNCTION_ARGS);
-extern Datum spgendscan(PG_FUNCTION_ARGS);
-extern Datum spgrescan(PG_FUNCTION_ARGS);
-extern Datum spgmarkpos(PG_FUNCTION_ARGS);
-extern Datum spgrestrpos(PG_FUNCTION_ARGS);
-extern Datum spggetbitmap(PG_FUNCTION_ARGS);
-extern Datum spggettuple(PG_FUNCTION_ARGS);
-extern Datum spgcanreturn(PG_FUNCTION_ARGS);
-
-/* spgutils.c */
-extern Datum spgoptions(PG_FUNCTION_ARGS);
+extern IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz);
+extern void spgendscan(IndexScanDesc scan);
+extern void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
+ ScanKey orderbys, int norderbys);
+extern int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
+extern bool spggettuple(IndexScanDesc scan, ScanDirection dir);
+extern bool spgcanreturn(Relation index, int attno);
/* spgvacuum.c */
-extern Datum spgbulkdelete(PG_FUNCTION_ARGS);
-extern Datum spgvacuumcleanup(PG_FUNCTION_ARGS);
+extern IndexBulkDeleteResult *spgbulkdelete(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats,
+ IndexBulkDeleteCallback callback,
+ void *callback_state);
+extern IndexBulkDeleteResult *spgvacuumcleanup(IndexVacuumInfo *info,
+ IndexBulkDeleteResult *stats);
+
+/* spgvalidate.c */
+extern bool spgvalidate(Oid opclassoid);
/* spgxlog.c */
extern void spg_redo(XLogReaderState *record);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 62e08a9674a..54b9944c415 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201601091
+#define CATALOG_VERSION_NO 201601171
#endif
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index 0c7fcdc02f1..f801c3ee577 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -34,39 +34,7 @@
CATALOG(pg_am,2601)
{
NameData amname; /* access method name */
- int16 amstrategies; /* total number of strategies (operators) by
- * which we can traverse/search this AM. Zero
- * if AM does not have a fixed set of strategy
- * assignments. */
- int16 amsupport; /* total number of support functions that this
- * AM uses */
- bool amcanorder; /* does AM support order by column value? */
- bool amcanorderbyop; /* does AM support order by operator result? */
- bool amcanbackward; /* does AM support backward scan? */
- bool amcanunique; /* does AM support UNIQUE indexes? */
- bool amcanmulticol; /* does AM support multi-column indexes? */
- bool amoptionalkey; /* can query omit key for the first column? */
- bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
- bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
- bool amstorage; /* can storage type differ from column type? */
- bool amclusterable; /* does AM support cluster command? */
- bool ampredlocks; /* does AM handle predicate locks? */
- Oid amkeytype; /* type of data in index, or InvalidOid */
- regproc aminsert; /* "insert this tuple" function */
- regproc ambeginscan; /* "prepare for index scan" function */
- regproc amgettuple; /* "next valid tuple" function, or 0 */
- regproc amgetbitmap; /* "fetch all valid tuples" function, or 0 */
- regproc amrescan; /* "(re)start index scan" function */
- regproc amendscan; /* "end index scan" function */
- regproc ammarkpos; /* "mark current scan position" function */
- regproc amrestrpos; /* "restore marked scan position" function */
- regproc ambuild; /* "build new index" function */
- regproc ambuildempty; /* "build empty index" function */
- regproc ambulkdelete; /* bulk-delete function */
- regproc amvacuumcleanup; /* post-VACUUM cleanup function */
- regproc amcanreturn; /* can indexscan return IndexTuples? */
- regproc amcostestimate; /* estimate cost of an indexscan */
- regproc amoptions; /* parse AM-specific parameters */
+ regproc amhandler; /* handler function */
} FormData_pg_am;
/* ----------------
@@ -80,59 +48,31 @@ typedef FormData_pg_am *Form_pg_am;
* compiler constants for pg_am
* ----------------
*/
-#define Natts_pg_am 30
+#define Natts_pg_am 2
#define Anum_pg_am_amname 1
-#define Anum_pg_am_amstrategies 2
-#define Anum_pg_am_amsupport 3
-#define Anum_pg_am_amcanorder 4
-#define Anum_pg_am_amcanorderbyop 5
-#define Anum_pg_am_amcanbackward 6
-#define Anum_pg_am_amcanunique 7
-#define Anum_pg_am_amcanmulticol 8
-#define Anum_pg_am_amoptionalkey 9
-#define Anum_pg_am_amsearcharray 10
-#define Anum_pg_am_amsearchnulls 11
-#define Anum_pg_am_amstorage 12
-#define Anum_pg_am_amclusterable 13
-#define Anum_pg_am_ampredlocks 14
-#define Anum_pg_am_amkeytype 15
-#define Anum_pg_am_aminsert 16
-#define Anum_pg_am_ambeginscan 17
-#define Anum_pg_am_amgettuple 18
-#define Anum_pg_am_amgetbitmap 19
-#define Anum_pg_am_amrescan 20
-#define Anum_pg_am_amendscan 21
-#define Anum_pg_am_ammarkpos 22
-#define Anum_pg_am_amrestrpos 23
-#define Anum_pg_am_ambuild 24
-#define Anum_pg_am_ambuildempty 25
-#define Anum_pg_am_ambulkdelete 26
-#define Anum_pg_am_amvacuumcleanup 27
-#define Anum_pg_am_amcanreturn 28
-#define Anum_pg_am_amcostestimate 29
-#define Anum_pg_am_amoptions 30
+#define Anum_pg_am_amhandler 2
/* ----------------
* initial contents of pg_am
* ----------------
*/
-DATA(insert OID = 403 ( btree 5 2 t f t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcanreturn btcostestimate btoptions ));
+DATA(insert OID = 403 ( btree bthandler ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
-DATA(insert OID = 405 ( hash 1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
+DATA(insert OID = 405 ( hash hashhandler ));
DESCR("hash index access method");
#define HASH_AM_OID 405
-DATA(insert OID = 783 ( gist 0 9 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup gistcanreturn gistcostestimate gistoptions ));
+DATA(insert OID = 783 ( gist gisthandler ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
-DATA(insert OID = 2742 ( gin 0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
+DATA(insert OID = 2742 ( gin ginhandler ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742
-DATA(insert OID = 4000 ( spgist 0 5 f f f f f t f t f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions ));
+DATA(insert OID = 4000 ( spgist spghandler ));
DESCR("SP-GiST index access method");
#define SPGIST_AM_OID 4000
-DATA(insert OID = 3580 ( brin 0 15 f f f f t t f t t f f 0 brininsert brinbeginscan - bringetbitmap brinrescan brinendscan brinmarkpos brinrestrpos brinbuild brinbuildempty brinbulkdelete brinvacuumcleanup - brincostestimate brinoptions ));
+DATA(insert OID = 3580 ( brin brinhandler ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f58672e4a7c..3df5ac50b60 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -548,63 +548,22 @@ DESCR("convert int4 to float4");
DATA(insert OID = 319 ( int4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ ftoi4 _null_ _null_ _null_ ));
DESCR("convert float4 to int4");
-DATA(insert OID = 330 ( btgettuple PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ btgettuple _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 636 ( btgetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_ btgetbitmap _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 331 ( btinsert PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btinsert _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 333 ( btbeginscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btbeginscan _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 334 ( btrescan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btrescan _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 335 ( btendscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btendscan _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 336 ( btmarkpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btmarkpos _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 337 ( btrestrpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btrestrpos _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 338 ( btbuild PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btbuild _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 328 ( btbuildempty PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btbuildempty _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 332 ( btbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btbulkdelete _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 972 ( btvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 276 ( btcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 1268 ( btcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-DATA(insert OID = 2785 ( btoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ btoptions _null_ _null_ _null_ ));
-DESCR("btree(internal)");
-
-DATA(insert OID = 3789 ( bringetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_ bringetbitmap _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3790 ( brininsert PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brininsert _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3791 ( brinbeginscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinbeginscan _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3792 ( brinrescan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinrescan _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3793 ( brinendscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinendscan _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3794 ( brinmarkpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinmarkpos _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3795 ( brinrestrpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinrestrpos _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3796 ( brinbuild PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinbuild _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3797 ( brinbuildempty PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinbuildempty _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3798 ( brinbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinbulkdelete _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3799 ( brinvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ brinvacuumcleanup _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3800 ( brincostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brincostestimate _null_ _null_ _null_ ));
-DESCR("brin(internal)");
-DATA(insert OID = 3801 ( brinoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ brinoptions _null_ _null_ _null_ ));
-DESCR("brin(internal)");
+/* Index access method handlers */
+DATA(insert OID = 330 ( bthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ bthandler _null_ _null_ _null_ ));
+DESCR("btree index access method handler");
+DATA(insert OID = 331 ( hashhandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ hashhandler _null_ _null_ _null_ ));
+DESCR("hash index access method handler");
+DATA(insert OID = 332 ( gisthandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ gisthandler _null_ _null_ _null_ ));
+DESCR("gist index access method handler");
+DATA(insert OID = 333 ( ginhandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ ginhandler _null_ _null_ _null_ ));
+DESCR("gin index access method handler");
+DATA(insert OID = 334 ( spghandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ spghandler _null_ _null_ _null_ ));
+DESCR("spgist index access method handler");
+DATA(insert OID = 335 ( brinhandler PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 325 "2281" _null_ _null_ _null_ _null_ _null_ brinhandler _null_ _null_ _null_ ));
+DESCR("brin index access method handler");
+
+DATA(insert OID = 338 ( amvalidate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 16 "26" _null_ _null_ _null_ _null_ _null_ amvalidate _null_ _null_ _null_ ));
+DESCR("validate an operator class");
DATA(insert OID = 3952 ( brin_summarize_new_values PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 23 "2205" _null_ _null_ _null_ _null_ _null_ brin_summarize_new_values _null_ _null_ _null_ ));
DESCR("brin: standalone scan new table pages");
@@ -695,35 +654,6 @@ DESCR("convert name to char(n)");
DATA(insert OID = 409 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 19 "1042" _null_ _null_ _null_ _null_ _null_ bpchar_name _null_ _null_ _null_ ));
DESCR("convert char(n) to name");
-DATA(insert OID = 440 ( hashgettuple PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ hashgettuple _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 637 ( hashgetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_ hashgetbitmap _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 441 ( hashinsert PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashinsert _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 443 ( hashbeginscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashbeginscan _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 444 ( hashrescan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashrescan _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 445 ( hashendscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashendscan _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 446 ( hashmarkpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashmarkpos _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 447 ( hashrestrpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashrestrpos _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 448 ( hashbuild PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashbuild _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 327 ( hashbuildempty PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashbuildempty _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 442 ( hashbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashbulkdelete _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 425 ( hashvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ hashvacuumcleanup _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 438 ( hashcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashcostestimate _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-DATA(insert OID = 2786 ( hashoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ hashoptions _null_ _null_ _null_ ));
-DESCR("hash(internal)");
-
DATA(insert OID = 449 ( hashint2 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "21" _null_ _null_ _null_ _null_ _null_ hashint2 _null_ _null_ _null_ ));
DESCR("hash");
DATA(insert OID = 450 ( hashint4 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "23" _null_ _null_ _null_ _null_ _null_ hashint4 _null_ _null_ _null_ ));
@@ -979,37 +909,6 @@ DESCR("larger of two");
DATA(insert OID = 771 ( int2smaller PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 21 "21 21" _null_ _null_ _null_ _null_ _null_ int2smaller _null_ _null_ _null_ ));
DESCR("smaller of two");
-DATA(insert OID = 774 ( gistgettuple PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ gistgettuple _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 638 ( gistgetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_ gistgetbitmap _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 775 ( gistinsert PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistinsert _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 777 ( gistbeginscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistbeginscan _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 778 ( gistrescan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistrescan _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 779 ( gistendscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistendscan _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 780 ( gistmarkpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistmarkpos _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 781 ( gistrestrpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistrestrpos _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 782 ( gistbuild PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistbuild _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 326 ( gistbuildempty PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistbuildempty _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 776 ( gistbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistbulkdelete _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 2561 ( gistvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 3280 ( gistcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ _null_ gistcanreturn _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 772 ( gistcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-DATA(insert OID = 2787 ( gistoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ gistoptions _null_ _null_ _null_ ));
-DESCR("gist(internal)");
-
DATA(insert OID = 784 ( tintervaleq PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "704 704" _null_ _null_ _null_ _null_ _null_ tintervaleq _null_ _null_ _null_ ));
DATA(insert OID = 785 ( tintervalne PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "704 704" _null_ _null_ _null_ _null_ _null_ tintervalne _null_ _null_ _null_ ));
DATA(insert OID = 786 ( tintervallt PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "704 704" _null_ _null_ _null_ _null_ _null_ tintervallt _null_ _null_ _null_ ));
@@ -3744,6 +3643,10 @@ DATA(insert OID = 3116 ( fdw_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s
DESCR("I/O");
DATA(insert OID = 3117 ( fdw_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3115" _null_ _null_ _null_ _null_ _null_ fdw_handler_out _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 326 ( index_am_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 325 "2275" _null_ _null_ _null_ _null_ _null_ index_am_handler_in _null_ _null_ _null_ ));
+DESCR("I/O");
+DATA(insert OID = 327 ( index_am_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
+DESCR("I/O");
DATA(insert OID = 3311 ( tsm_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 3310 "2275" _null_ _null_ _null_ _null_ _null_ tsm_handler_in _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 3312 ( tsm_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
@@ -4204,34 +4107,6 @@ DESCR("GiST support");
DATA(insert OID = 3288 ( gist_bbox_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ _null_ gist_bbox_distance _null_ _null_ _null_ ));
DESCR("GiST support");
-/* GIN */
-DATA(insert OID = 2731 ( gingetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_ gingetbitmap _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2732 ( gininsert PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gininsert _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2733 ( ginbeginscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginbeginscan _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2734 ( ginrescan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginrescan _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2735 ( ginendscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginendscan _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2736 ( ginmarkpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginmarkpos _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2737 ( ginrestrpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginrestrpos _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2738 ( ginbuild PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginbuild _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 325 ( ginbuildempty PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginbuildempty _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2739 ( ginbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginbulkdelete _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2740 ( ginvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ ginvacuumcleanup _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2741 ( gincostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gincostestimate _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-DATA(insert OID = 2788 ( ginoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ ginoptions _null_ _null_ _null_ ));
-DESCR("gin(internal)");
-
/* GIN array support */
DATA(insert OID = 2743 ( ginarrayextract PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "2277 2281 2281" _null_ _null_ _null_ _null_ _null_ ginarrayextract _null_ _null_ _null_ ));
DESCR("GIN array support");
@@ -5129,38 +5004,6 @@ DESCR("construct timestamp with time zone");
DATA(insert OID = 3464 ( make_interval PGNSP PGUID 12 1 0 0 0 f f f f t f i s 7 0 1186 "23 23 23 23 23 23 701" _null_ _null_ "{years,months,weeks,days,hours,mins,secs}" _null_ _null_ make_interval _null_ _null_ _null_ ));
DESCR("construct interval");
-/* spgist support functions */
-DATA(insert OID = 4001 ( spggettuple PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_ spggettuple _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4002 ( spggetbitmap PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_ spggetbitmap _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4003 ( spginsert PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spginsert _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4004 ( spgbeginscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgbeginscan _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4005 ( spgrescan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgrescan _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4006 ( spgendscan PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgendscan _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4007 ( spgmarkpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgmarkpos _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4008 ( spgrestrpos PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgrestrpos _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4009 ( spgbuild PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgbuild _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4010 ( spgbuildempty PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgbuildempty _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4011 ( spgbulkdelete PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgbulkdelete _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4012 ( spgvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ spgvacuumcleanup _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4032 ( spgcanreturn PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4013 ( spgcostestimate PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-DATA(insert OID = 4014 ( spgoptions PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ spgoptions _null_ _null_ _null_ ));
-DESCR("spgist(internal)");
-
/* spgist opclasses */
DATA(insert OID = 4018 ( spg_quad_config PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_ _null_ spg_quad_config _null_ _null_ _null_ ));
DESCR("SP-GiST support for quad tree over point");
@@ -5343,6 +5186,7 @@ DESCR("get an individual replication origin's replication progress");
DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
DESCR("get progress for all replication origins");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 16e0d917841..2c90b76fe1b 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -694,6 +694,8 @@ DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in
#define ANYENUMOID 3500
DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+#define INDEX_AM_HANDLEROID 325
DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2b734830988..cf09db4e5fe 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -454,6 +454,7 @@ typedef enum NodeTag
T_TIDBitmap, /* in nodes/tidbitmap.h */
T_InlineCodeBlock, /* in nodes/parsenodes.h */
T_FdwRoutine, /* in foreign/fdwapi.h */
+ T_IndexAmRoutine, /* in access/amapi.h */
T_TsmRoutine /* in access/tsmapi.h */
} NodeTag;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 61519bb6fae..6deda54c143 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -554,8 +554,6 @@ typedef struct IndexOptInfo
* index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */
- RegProcedure amcostestimate; /* OID of the access method's cost fcn */
-
List *indexprs; /* expressions for non-simple index columns */
List *indpred; /* predicate if a partial index, else NIL */
@@ -565,12 +563,16 @@ typedef struct IndexOptInfo
bool unique; /* true if a unique index */
bool immediate; /* is uniqueness enforced immediately? */
bool hypothetical; /* true if index doesn't really exist */
+
+ /* Remaining fields are copied from the index AM's API struct: */
bool amcanorderbyop; /* does AM support order by operator result? */
bool amoptionalkey; /* can query omit key for the first column? */
bool amsearcharray; /* can AM handle ScalarArrayOpExpr quals? */
bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */
bool amhasgettuple; /* does AM have amgettuple interface? */
bool amhasgetbitmap; /* does AM have amgetbitmap interface? */
+ /* Rather than include amapi.h here, we declare amcostestimate like this */
+ void (*amcostestimate) (); /* AM's cost estimator */
} IndexOptInfo;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index b35d20626b7..477fde1f81d 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -566,6 +566,8 @@ extern Datum language_handler_in(PG_FUNCTION_ARGS);
extern Datum language_handler_out(PG_FUNCTION_ARGS);
extern Datum fdw_handler_in(PG_FUNCTION_ARGS);
extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
+extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
+extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
extern Datum internal_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/index_selfuncs.h b/src/include/utils/index_selfuncs.h
new file mode 100644
index 00000000000..a03e12f518f
--- /dev/null
+++ b/src/include/utils/index_selfuncs.h
@@ -0,0 +1,68 @@
+/*-------------------------------------------------------------------------
+ *
+ * index_selfuncs.h
+ * Index cost estimation functions for standard index access methods.
+ *
+ *
+ * Note: this is split out of selfuncs.h mainly to avoid importing all of the
+ * planner's data structures into the non-planner parts of the index AMs.
+ * If you make it depend on anything besides access/amapi.h, that's likely
+ * a mistake.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/index_selfuncs.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef INDEX_SELFUNCS_H
+#define INDEX_SELFUNCS_H
+
+#include "access/amapi.h"
+
+/* Functions in selfuncs.c */
+extern void brincostestimate(struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+extern void btcostestimate(struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+extern void hashcostestimate(struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+extern void gistcostestimate(struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+extern void spgcostestimate(struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+extern void gincostestimate(struct PlannerInfo *root,
+ struct IndexPath *path,
+ double loop_count,
+ Cost *indexStartupCost,
+ Cost *indexTotalCost,
+ Selectivity *indexSelectivity,
+ double *indexCorrelation);
+
+#endif /* INDEX_SELFUNCS_H */
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index ff5672d2654..f2bebf2c3dd 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -15,7 +15,6 @@
#define REL_H
#include "access/tupdesc.h"
-#include "catalog/pg_am.h"
#include "catalog/pg_class.h"
#include "catalog/pg_index.h"
#include "fmgr.h"
@@ -47,23 +46,6 @@ typedef LockInfoData *LockInfo;
/*
- * Cached lookup information for the frequently used index access method
- * functions, defined by the pg_am row associated with an index relation.
- */
-typedef struct RelationAmInfo
-{
- FmgrInfo aminsert;
- FmgrInfo ambeginscan;
- FmgrInfo amgettuple;
- FmgrInfo amgetbitmap;
- FmgrInfo amrescan;
- FmgrInfo amendscan;
- FmgrInfo ammarkpos;
- FmgrInfo amrestrpos;
- FmgrInfo amcanreturn;
-} RelationAmInfo;
-
-/*
* Here are the contents of a relation cache entry.
*/
@@ -128,7 +110,6 @@ typedef struct RelationData
Form_pg_index rd_index; /* pg_index tuple describing this index */
/* use "struct" here to avoid needing to include htup.h: */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
- Form_pg_am rd_am; /* pg_am tuple for index's AM */
/*
* index access support info (used only for an index relation)
@@ -145,8 +126,10 @@ 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 */
- RelationAmInfo *rd_aminfo; /* lookup info for funcs found in pg_am */
+ /* use "struct" here to avoid needing to include amapi.h: */
+ struct IndexAmRoutine *rd_amroutine; /* index AM's API struct */
Oid *rd_opfamily; /* OIDs of op families for each index col */
Oid *rd_opcintype; /* OIDs of opclass declared input data types */
RegProcedure *rd_support; /* OIDs of support procedures */
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
index 38fe731f81e..06fbca719b5 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -1,8 +1,8 @@
/*-------------------------------------------------------------------------
*
* selfuncs.h
- * Selectivity functions and index cost estimation functions for
- * standard operators and index access methods.
+ * Selectivity functions for standard operators, and assorted
+ * infrastructure for selectivity and cost estimation.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
@@ -191,13 +191,6 @@ extern double estimate_num_groups(PlannerInfo *root, List *groupExprs,
extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
double nbuckets);
-extern Datum brincostestimate(PG_FUNCTION_ARGS);
-extern Datum btcostestimate(PG_FUNCTION_ARGS);
-extern Datum hashcostestimate(PG_FUNCTION_ARGS);
-extern Datum gistcostestimate(PG_FUNCTION_ARGS);
-extern Datum spgcostestimate(PG_FUNCTION_ARGS);
-extern Datum gincostestimate(PG_FUNCTION_ARGS);
-
/* Functions in array_selfuncs.c */
extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index d85bc83e11e..f10e007f58c 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -73,131 +73,11 @@ WHERE aggmtranstype != 0 AND
------+---------------
(0 rows)
-SELECT ctid, amkeytype
+SELECT ctid, amhandler
FROM pg_catalog.pg_am fk
-WHERE amkeytype != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amkeytype);
- ctid | amkeytype
-------+-----------
-(0 rows)
-
-SELECT ctid, aminsert
-FROM pg_catalog.pg_am fk
-WHERE aminsert != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aminsert);
- ctid | aminsert
-------+----------
-(0 rows)
-
-SELECT ctid, ambeginscan
-FROM pg_catalog.pg_am fk
-WHERE ambeginscan != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
- ctid | ambeginscan
-------+-------------
-(0 rows)
-
-SELECT ctid, amgettuple
-FROM pg_catalog.pg_am fk
-WHERE amgettuple != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
- ctid | amgettuple
-------+------------
-(0 rows)
-
-SELECT ctid, amgetbitmap
-FROM pg_catalog.pg_am fk
-WHERE amgetbitmap != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
- ctid | amgetbitmap
-------+-------------
-(0 rows)
-
-SELECT ctid, amrescan
-FROM pg_catalog.pg_am fk
-WHERE amrescan != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrescan);
- ctid | amrescan
-------+----------
-(0 rows)
-
-SELECT ctid, amendscan
-FROM pg_catalog.pg_am fk
-WHERE amendscan != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amendscan);
- ctid | amendscan
-------+-----------
-(0 rows)
-
-SELECT ctid, ammarkpos
-FROM pg_catalog.pg_am fk
-WHERE ammarkpos != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ammarkpos);
- ctid | ammarkpos
-------+-----------
-(0 rows)
-
-SELECT ctid, amrestrpos
-FROM pg_catalog.pg_am fk
-WHERE amrestrpos != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrestrpos);
- ctid | amrestrpos
-------+------------
-(0 rows)
-
-SELECT ctid, ambuild
-FROM pg_catalog.pg_am fk
-WHERE ambuild != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
- ctid | ambuild
-------+---------
-(0 rows)
-
-SELECT ctid, ambuildempty
-FROM pg_catalog.pg_am fk
-WHERE ambuildempty != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
- ctid | ambuildempty
-------+--------------
-(0 rows)
-
-SELECT ctid, ambulkdelete
-FROM pg_catalog.pg_am fk
-WHERE ambulkdelete != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambulkdelete);
- ctid | ambulkdelete
-------+--------------
-(0 rows)
-
-SELECT ctid, amvacuumcleanup
-FROM pg_catalog.pg_am fk
-WHERE amvacuumcleanup != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amvacuumcleanup);
- ctid | amvacuumcleanup
-------+-----------------
-(0 rows)
-
-SELECT ctid, amcanreturn
-FROM pg_catalog.pg_am fk
-WHERE amcanreturn != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcanreturn);
- ctid | amcanreturn
-------+-------------
-(0 rows)
-
-SELECT ctid, amcostestimate
-FROM pg_catalog.pg_am fk
-WHERE amcostestimate != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcostestimate);
- ctid | amcostestimate
-------+----------------
-(0 rows)
-
-SELECT ctid, amoptions
-FROM pg_catalog.pg_am fk
-WHERE amoptions != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amoptions);
- ctid | amoptions
+WHERE amhandler != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amhandler);
+ ctid | amhandler
------+-----------
(0 rows)
@@ -809,6 +689,14 @@ WHERE opfowner != 0 AND
------+----------
(0 rows)
+SELECT ctid, polrelid
+FROM pg_catalog.pg_policy fk
+WHERE polrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.polrelid);
+ ctid | polrelid
+------+----------
+(0 rows)
+
SELECT ctid, pronamespace
FROM pg_catalog.pg_proc fk
WHERE pronamespace != 0 AND
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index f786962488c..79c13211ec4 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1565,6 +1565,32 @@ WHERE p1.oid != p2.oid AND
-----+-----
(0 rows)
+-- Ask access methods to validate opclasses
+SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate(oid);
+ oid | opcname
+-----+---------
+(0 rows)
+
+-- **************** pg_am ****************
+-- Look for illegal values in pg_am fields
+SELECT p1.oid, p1.amname
+FROM pg_am AS p1
+WHERE p1.amhandler = 0;
+ oid | amname
+-----+--------
+(0 rows)
+
+-- Check for 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
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+ oid | amname | oid | proname
+-----+--------+-----+---------
+(0 rows)
+
-- **************** pg_amop ****************
-- Look for illegal values in pg_amop fields
SELECT p1.amopfamily, p1.amopstrategy
@@ -1610,49 +1636,6 @@ WHERE p1.amopsortfamily <> 0 AND NOT EXISTS
------------+--------------
(0 rows)
--- check for ordering operators not supported by parent AM
-SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
-FROM pg_amop AS p1, pg_am AS p2
-WHERE p1.amopmethod = p2.oid AND
- p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
- amopfamily | amopopr | oid | amname
-------------+---------+-----+--------
-(0 rows)
-
--- Cross-check amopstrategy index against parent AM
-SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
-FROM pg_amop AS p1, pg_am AS p2
-WHERE p1.amopmethod = p2.oid AND
- p1.amopstrategy > p2.amstrategies AND p2.amstrategies <> 0;
- amopfamily | amopopr | oid | amname
-------------+---------+-----+--------
-(0 rows)
-
--- Detect missing pg_amop entries: should have as many strategy operators
--- as AM expects for each datatype combination supported by the opfamily.
--- We can't check this for AMs with variable strategy sets.
-SELECT p1.amname, p2.amoplefttype, p2.amoprighttype
-FROM pg_am AS p1, pg_amop AS p2
-WHERE p2.amopmethod = p1.oid AND
- p1.amstrategies <> 0 AND
- p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
- WHERE p3.amopfamily = p2.amopfamily AND
- p3.amoplefttype = p2.amoplefttype AND
- p3.amoprighttype = p2.amoprighttype AND
- p3.amoppurpose = 's');
- amname | amoplefttype | amoprighttype
---------+--------------+---------------
-(0 rows)
-
--- Currently, none of the AMs with fixed strategy sets support ordering ops.
-SELECT p1.amname, p2.amopfamily, p2.amopstrategy
-FROM pg_am AS p1, pg_amop AS p2
-WHERE p2.amopmethod = p1.oid AND
- p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
- amname | amopfamily | amopstrategy
---------+------------+--------------
-(0 rows)
-
-- Check that amopopr points at a reasonable-looking operator, ie a binary
-- operator. If it's a search operator it had better yield boolean,
-- otherwise an input type of its sort opfamily.
@@ -1935,65 +1918,6 @@ WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
--------------+-----------
(0 rows)
--- Cross-check amprocnum index against parent AM
-SELECT p1.amprocfamily, p1.amprocnum, p2.oid, p2.amname
-FROM pg_amproc AS p1, pg_am AS p2, pg_opfamily AS p3
-WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
- p1.amprocnum > p2.amsupport;
- amprocfamily | amprocnum | oid | amname
---------------+-----------+-----+--------
-(0 rows)
-
--- Detect missing pg_amproc entries: should have as many support functions
--- as AM expects for each datatype combination supported by the opfamily.
-SELECT * FROM (
- SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype,
- array_agg(p3.amprocnum ORDER BY amprocnum) AS procnums
- FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
- WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid
- GROUP BY p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
-) AS t
-WHERE NOT (
- -- btree has one mandatory and one optional support function.
- -- hash has one support function, which is mandatory.
- -- GiST has eight support functions, one of which is optional.
- -- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
- -- at least one of 4 and 6 must be given.
- -- SP-GiST has five support functions, all mandatory
- -- BRIN has four mandatory support functions, and a bunch of optionals
- amname = 'btree' AND procnums @> '{1}' OR
- amname = 'hash' AND procnums = '{1}' OR
- amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
- amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
- amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
- amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
-);
- amname | opfname | amproclefttype | amprocrighttype | procnums
---------+---------+----------------+-----------------+----------
-(0 rows)
-
--- Also, check if there are any pg_opclass entries that don't seem to have
--- pg_amproc support.
-SELECT * FROM (
- SELECT amname, opcname, array_agg(amprocnum ORDER BY amprocnum) as procnums
- FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
- LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
- amproclefttype = amprocrighttype AND amproclefttype = opcintype
- GROUP BY amname, opcname, amprocfamily
-) AS t
-WHERE NOT (
- -- same per-AM rules as above
- amname = 'btree' AND procnums @> '{1}' OR
- amname = 'hash' AND procnums = '{1}' OR
- amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
- amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
- amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
- amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
-);
- amname | opcname | procnums
---------+---------+----------
-(0 rows)
-
-- Unfortunately, we can't check the amproc link very well because the
-- signature of the function may be different for different support routines
-- or different base data types.
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index 2fa628d0f73..9b7c47060b6 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -37,70 +37,10 @@ SELECT ctid, aggmtranstype
FROM pg_catalog.pg_aggregate fk
WHERE aggmtranstype != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggmtranstype);
-SELECT ctid, amkeytype
+SELECT ctid, amhandler
FROM pg_catalog.pg_am fk
-WHERE amkeytype != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amkeytype);
-SELECT ctid, aminsert
-FROM pg_catalog.pg_am fk
-WHERE aminsert != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aminsert);
-SELECT ctid, ambeginscan
-FROM pg_catalog.pg_am fk
-WHERE ambeginscan != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
-SELECT ctid, amgettuple
-FROM pg_catalog.pg_am fk
-WHERE amgettuple != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
-SELECT ctid, amgetbitmap
-FROM pg_catalog.pg_am fk
-WHERE amgetbitmap != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
-SELECT ctid, amrescan
-FROM pg_catalog.pg_am fk
-WHERE amrescan != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrescan);
-SELECT ctid, amendscan
-FROM pg_catalog.pg_am fk
-WHERE amendscan != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amendscan);
-SELECT ctid, ammarkpos
-FROM pg_catalog.pg_am fk
-WHERE ammarkpos != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ammarkpos);
-SELECT ctid, amrestrpos
-FROM pg_catalog.pg_am fk
-WHERE amrestrpos != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrestrpos);
-SELECT ctid, ambuild
-FROM pg_catalog.pg_am fk
-WHERE ambuild != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
-SELECT ctid, ambuildempty
-FROM pg_catalog.pg_am fk
-WHERE ambuildempty != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
-SELECT ctid, ambulkdelete
-FROM pg_catalog.pg_am fk
-WHERE ambulkdelete != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambulkdelete);
-SELECT ctid, amvacuumcleanup
-FROM pg_catalog.pg_am fk
-WHERE amvacuumcleanup != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amvacuumcleanup);
-SELECT ctid, amcanreturn
-FROM pg_catalog.pg_am fk
-WHERE amcanreturn != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcanreturn);
-SELECT ctid, amcostestimate
-FROM pg_catalog.pg_am fk
-WHERE amcostestimate != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcostestimate);
-SELECT ctid, amoptions
-FROM pg_catalog.pg_am fk
-WHERE amoptions != 0 AND
- NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amoptions);
+WHERE amhandler != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amhandler);
SELECT ctid, amopfamily
FROM pg_catalog.pg_amop fk
WHERE amopfamily != 0 AND
@@ -405,6 +345,10 @@ SELECT ctid, opfowner
FROM pg_catalog.pg_opfamily fk
WHERE opfowner != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.opfowner);
+SELECT ctid, polrelid
+FROM pg_catalog.pg_policy fk
+WHERE polrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.polrelid);
SELECT ctid, pronamespace
FROM pg_catalog.pg_proc fk
WHERE pronamespace != 0 AND
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9a55aea5d30..257a4a2765b 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -50,6 +50,7 @@ SELECT ($1 = $2) OR
(select typtype from pg_catalog.pg_type where oid = $1) = 'r')
$$ language sql strict stable;
+
-- **************** pg_proc ****************
-- Look for illegal values in pg_proc fields.
@@ -1001,6 +1002,7 @@ SELECT p.oid, proname
FROM pg_proc AS p JOIN pg_aggregate AS a ON a.aggfnoid = p.oid
WHERE proisagg AND provariadic != 0 AND a.aggkind = 'n';
+
-- **************** pg_opfamily ****************
-- Look for illegal values in pg_opfamily fields
@@ -1009,6 +1011,7 @@ SELECT p1.oid
FROM pg_opfamily as p1
WHERE p1.opfmethod = 0 OR p1.opfnamespace = 0;
+
-- **************** pg_opclass ****************
-- Look for illegal values in pg_opclass fields
@@ -1033,6 +1036,29 @@ WHERE p1.oid != p2.oid AND
p1.opcmethod = p2.opcmethod AND p1.opcintype = p2.opcintype AND
p1.opcdefault AND p2.opcdefault;
+-- Ask access methods to validate opclasses
+
+SELECT oid, opcname FROM pg_opclass WHERE NOT amvalidate(oid);
+
+
+-- **************** pg_am ****************
+
+-- Look for illegal values in pg_am fields
+
+SELECT p1.oid, p1.amname
+FROM pg_am AS p1
+WHERE p1.amhandler = 0;
+
+-- Check for 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
+ OR p2.pronargs != 1
+ OR p2.proargtypes[0] != 'internal'::regtype);
+
+
-- **************** pg_amop ****************
-- Look for illegal values in pg_amop fields
@@ -1068,41 +1094,6 @@ WHERE p1.amopsortfamily <> 0 AND NOT EXISTS
(SELECT 1 from pg_opfamily op WHERE op.oid = p1.amopsortfamily
AND op.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree'));
--- check for ordering operators not supported by parent AM
-
-SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
-FROM pg_amop AS p1, pg_am AS p2
-WHERE p1.amopmethod = p2.oid AND
- p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
-
--- Cross-check amopstrategy index against parent AM
-
-SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
-FROM pg_amop AS p1, pg_am AS p2
-WHERE p1.amopmethod = p2.oid AND
- p1.amopstrategy > p2.amstrategies AND p2.amstrategies <> 0;
-
--- Detect missing pg_amop entries: should have as many strategy operators
--- as AM expects for each datatype combination supported by the opfamily.
--- We can't check this for AMs with variable strategy sets.
-
-SELECT p1.amname, p2.amoplefttype, p2.amoprighttype
-FROM pg_am AS p1, pg_amop AS p2
-WHERE p2.amopmethod = p1.oid AND
- p1.amstrategies <> 0 AND
- p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
- WHERE p3.amopfamily = p2.amopfamily AND
- p3.amoplefttype = p2.amoplefttype AND
- p3.amoprighttype = p2.amoprighttype AND
- p3.amoppurpose = 's');
-
--- Currently, none of the AMs with fixed strategy sets support ordering ops.
-
-SELECT p1.amname, p2.amopfamily, p2.amopstrategy
-FROM pg_am AS p1, pg_amop AS p2
-WHERE p2.amopmethod = p1.oid AND
- p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
-
-- Check that amopopr points at a reasonable-looking operator, ie a binary
-- operator. If it's a search operator it had better yield boolean,
-- otherwise an input type of its sort opfamily.
@@ -1249,59 +1240,6 @@ FROM pg_amproc as p1
WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
OR p1.amprocnum < 1 OR p1.amproc = 0;
--- Cross-check amprocnum index against parent AM
-
-SELECT p1.amprocfamily, p1.amprocnum, p2.oid, p2.amname
-FROM pg_amproc AS p1, pg_am AS p2, pg_opfamily AS p3
-WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
- p1.amprocnum > p2.amsupport;
-
--- Detect missing pg_amproc entries: should have as many support functions
--- as AM expects for each datatype combination supported by the opfamily.
-
-SELECT * FROM (
- SELECT p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype,
- array_agg(p3.amprocnum ORDER BY amprocnum) AS procnums
- FROM pg_am AS p1, pg_opfamily AS p2, pg_amproc AS p3
- WHERE p2.opfmethod = p1.oid AND p3.amprocfamily = p2.oid
- GROUP BY p1.amname, p2.opfname, p3.amproclefttype, p3.amprocrighttype
-) AS t
-WHERE NOT (
- -- btree has one mandatory and one optional support function.
- -- hash has one support function, which is mandatory.
- -- GiST has eight support functions, one of which is optional.
- -- GIN has six support functions. 1-3 are mandatory, 5 is optional, and
- -- at least one of 4 and 6 must be given.
- -- SP-GiST has five support functions, all mandatory
- -- BRIN has four mandatory support functions, and a bunch of optionals
- amname = 'btree' AND procnums @> '{1}' OR
- amname = 'hash' AND procnums = '{1}' OR
- amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
- amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
- amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
- amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
-);
-
--- Also, check if there are any pg_opclass entries that don't seem to have
--- pg_amproc support.
-
-SELECT * FROM (
- SELECT amname, opcname, array_agg(amprocnum ORDER BY amprocnum) as procnums
- FROM pg_am am JOIN pg_opclass op ON opcmethod = am.oid
- LEFT JOIN pg_amproc p ON amprocfamily = opcfamily AND
- amproclefttype = amprocrighttype AND amproclefttype = opcintype
- GROUP BY amname, opcname, amprocfamily
-) AS t
-WHERE NOT (
- -- same per-AM rules as above
- amname = 'btree' AND procnums @> '{1}' OR
- amname = 'hash' AND procnums = '{1}' OR
- amname = 'gist' AND procnums @> '{1, 2, 3, 4, 5, 6, 7}' OR
- amname = 'gin' AND (procnums @> '{1, 2, 3}' AND (procnums && '{4, 6}')) OR
- amname = 'spgist' AND procnums = '{1, 2, 3, 4, 5}' OR
- amname = 'brin' AND procnums @> '{1, 2, 3, 4}'
-);
-
-- Unfortunately, we can't check the amproc link very well because the
-- signature of the function may be different for different support routines
-- or different base data types.
@@ -1395,6 +1333,7 @@ WHERE p1.amproc = p2.oid AND
p1.amproclefttype != p1.amprocrighttype AND
p2.provolatile = 'v';
+
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.
diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README
index c46f9fc0f82..f7a88227ac8 100644
--- a/src/tools/findoidjoins/README
+++ b/src/tools/findoidjoins/README
@@ -5,7 +5,7 @@ findoidjoins
This program scans a database and prints oid fields (also reg* fields)
and the tables they join to. It is normally used to check the system
-catalog join relationships (shown below for 9.4devel as of 2014-04-16).
+catalog join relationships (shown below for 9.6devel as of 2016-01-16).
Historically this has been run against an empty database such as template1,
but there's a problem with that approach: some of the catalogs are empty
@@ -16,7 +16,7 @@ catalogs in interesting ways.
Note that unexpected matches may indicate bogus entries in system tables;
don't accept a peculiar match without question. In particular, a field
shown as joining to more than one target table is probably messed up.
-In 9.4devel, the *only* fields that should join to more than one target
+In 9.6devel, the *only* fields that should join to more than one target
table are pg_description.objoid, pg_depend.objid, pg_depend.refobjid,
pg_shdescription.objoid, pg_shdepend.objid, and pg_shdepend.refobjid.
(Running make_oidjoins_check is an easy way to spot fields joining to more
@@ -33,7 +33,7 @@ regression test. The oidjoins test should be updated after any
revision in the patterns of cross-links between system tables.
(Typically we update it at the end of each development cycle.)
-NOTE: as of 9.4devel, make_oidjoins_check produces two bogus join checks:
+NOTE: as of 9.6devel, make_oidjoins_check produces two bogus join checks:
Join pg_catalog.pg_class.relfilenode => pg_catalog.pg_class.oid
Join pg_catalog.pg_database.datlastsysoid => pg_catalog.pg_database.oid
These are artifacts and should not be added to the oidjoins regression test.
@@ -51,22 +51,7 @@ Join pg_catalog.pg_aggregate.aggmfinalfn => pg_catalog.pg_proc.oid
Join pg_catalog.pg_aggregate.aggsortop => pg_catalog.pg_operator.oid
Join pg_catalog.pg_aggregate.aggtranstype => pg_catalog.pg_type.oid
Join pg_catalog.pg_aggregate.aggmtranstype => pg_catalog.pg_type.oid
-Join pg_catalog.pg_am.amkeytype => pg_catalog.pg_type.oid
-Join pg_catalog.pg_am.aminsert => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.ambeginscan => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amgettuple => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amgetbitmap => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amrescan => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amendscan => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.ammarkpos => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amrestrpos => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.ambuild => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.ambuildempty => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.ambulkdelete => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amvacuumcleanup => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amcanreturn => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amcostestimate => pg_catalog.pg_proc.oid
-Join pg_catalog.pg_am.amoptions => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_am.amhandler => pg_catalog.pg_proc.oid
Join pg_catalog.pg_amop.amopfamily => pg_catalog.pg_opfamily.oid
Join pg_catalog.pg_amop.amoplefttype => pg_catalog.pg_type.oid
Join pg_catalog.pg_amop.amoprighttype => pg_catalog.pg_type.oid
@@ -110,6 +95,9 @@ Join pg_catalog.pg_description.classoid => pg_catalog.pg_class.oid
Join pg_catalog.pg_enum.enumtypid => pg_catalog.pg_type.oid
Join pg_catalog.pg_extension.extowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_extension.extnamespace => pg_catalog.pg_namespace.oid
+Join pg_catalog.pg_foreign_data_wrapper.fdwowner => pg_catalog.pg_authid.oid
+Join pg_catalog.pg_foreign_server.srvowner => pg_catalog.pg_authid.oid
+Join pg_catalog.pg_foreign_server.srvfdw => pg_catalog.pg_foreign_data_wrapper.oid
Join pg_catalog.pg_index.indexrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_index.indrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_inherits.inhrelid => pg_catalog.pg_class.oid
@@ -118,6 +106,8 @@ Join pg_catalog.pg_language.lanowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_language.lanplcallfoid => pg_catalog.pg_proc.oid
Join pg_catalog.pg_language.laninline => pg_catalog.pg_proc.oid
Join pg_catalog.pg_language.lanvalidator => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_largeobject.loid => pg_catalog.pg_largeobject_metadata.oid
+Join pg_catalog.pg_largeobject_metadata.lomowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_namespace.nspowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_opclass.opcmethod => pg_catalog.pg_am.oid
Join pg_catalog.pg_opclass.opcnamespace => pg_catalog.pg_namespace.oid
@@ -138,6 +128,7 @@ Join pg_catalog.pg_operator.oprjoin => pg_catalog.pg_proc.oid
Join pg_catalog.pg_opfamily.opfmethod => pg_catalog.pg_am.oid
Join pg_catalog.pg_opfamily.opfnamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_opfamily.opfowner => pg_catalog.pg_authid.oid
+Join pg_catalog.pg_policy.polrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_proc.pronamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_proc.proowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_proc.prolang => pg_catalog.pg_language.oid
@@ -160,6 +151,10 @@ Join pg_catalog.pg_statistic.staop3 => pg_catalog.pg_operator.oid
Join pg_catalog.pg_statistic.staop4 => pg_catalog.pg_operator.oid
Join pg_catalog.pg_statistic.staop5 => pg_catalog.pg_operator.oid
Join pg_catalog.pg_tablespace.spcowner => pg_catalog.pg_authid.oid
+Join pg_catalog.pg_transform.trftype => pg_catalog.pg_type.oid
+Join pg_catalog.pg_transform.trflang => pg_catalog.pg_language.oid
+Join pg_catalog.pg_transform.trffromsql => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_transform.trftosql => pg_catalog.pg_proc.oid
Join pg_catalog.pg_trigger.tgrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_trigger.tgfoid => pg_catalog.pg_proc.oid
Join pg_catalog.pg_trigger.tgconstrrelid => pg_catalog.pg_class.oid