diff options
Diffstat (limited to 'src')
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 |