From 65c5fcd353a859da9e61bfb2b92a99f12937de3b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 17 Jan 2016 19:36:59 -0500 Subject: [PATCH] Restructure index access method API to hide most of it at the C level. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch reduces pg_am to just two columns, a name and a handler function. All the data formerly obtained from pg_am is now provided in a C struct returned by the handler function. This is similar to the designs we've adopted for FDWs and tablesample methods. There are multiple advantages. For one, the index AM's support functions are now simple C functions, making them faster to call and much less error-prone, since the C compiler can now check function signatures. For another, this will make it far more practical to define index access methods in installable extensions. A disadvantage is that SQL-level code can no longer see attributes of index AMs; in particular, some of the crosschecks in the opr_sanity regression test are no longer possible from SQL. We've addressed that by adding a facility for the index AM to perform such checks instead. (Much more could be done in that line, but for now we're content if the amvalidate functions more or less replace what opr_sanity used to do.) We might also want to expose some sort of reporting functionality, but this patch doesn't do that. Alexander Korotkov, reviewed by Petr JelĂ­nek, and rather heavily editorialized on by me. --- contrib/pageinspect/btreefuncs.c | 1 + contrib/pgstattuple/pgstatindex.c | 1 + contrib/pgstattuple/pgstattuple.c | 1 + doc/src/sgml/catalogs.sgml | 213 +---------------------- doc/src/sgml/datatype.sgml | 9 + doc/src/sgml/indexam.sgml | 169 +++++++++++++----- doc/src/sgml/xindex.sgml | 2 +- src/backend/access/brin/Makefile | 2 +- src/backend/access/brin/brin.c | 161 +++++++++-------- src/backend/access/brin/brin_validate.c | 152 ++++++++++++++++ src/backend/access/common/reloptions.c | 35 +--- src/backend/access/gin/Makefile | 2 +- src/backend/access/gin/ginget.c | 10 +- src/backend/access/gin/gininsert.c | 33 ++-- src/backend/access/gin/ginscan.c | 41 +---- src/backend/access/gin/ginutil.c | 57 +++++- src/backend/access/gin/ginvacuum.c | 21 +-- src/backend/access/gin/ginvalidate.c | 145 +++++++++++++++ src/backend/access/gist/Makefile | 2 +- src/backend/access/gist/gist.c | 76 +++++--- src/backend/access/gist/gistbuild.c | 9 +- src/backend/access/gist/gistget.c | 35 ++-- src/backend/access/gist/gistscan.c | 41 +---- src/backend/access/gist/gistutil.c | 11 +- src/backend/access/gist/gistvacuum.c | 21 +-- src/backend/access/gist/gistvalidate.c | 133 ++++++++++++++ src/backend/access/hash/Makefile | 2 +- src/backend/access/hash/hash.c | 167 +++++++++--------- src/backend/access/hash/hashutil.c | 14 +- src/backend/access/hash/hashvalidate.c | 157 +++++++++++++++++ src/backend/access/index/Makefile | 2 +- src/backend/access/index/amapi.c | 114 ++++++++++++ src/backend/access/index/indexam.c | 167 +++++------------- src/backend/access/nbtree/Makefile | 2 +- src/backend/access/nbtree/nbtree.c | 165 +++++++++--------- src/backend/access/nbtree/nbtutils.c | 13 +- src/backend/access/nbtree/nbtvalidate.c | 204 ++++++++++++++++++++++ src/backend/access/spgist/Makefile | 2 +- src/backend/access/spgist/spginsert.c | 33 ++-- src/backend/access/spgist/spgscan.c | 67 ++----- src/backend/access/spgist/spgutils.c | 60 +++++-- src/backend/access/spgist/spgvacuum.c | 21 +-- src/backend/access/spgist/spgvalidate.c | 129 ++++++++++++++ src/backend/catalog/index.c | 37 ++-- src/backend/catalog/objectaddress.c | 1 + src/backend/catalog/toasting.c | 1 + src/backend/commands/cluster.c | 4 +- src/backend/commands/indexcmds.c | 22 ++- src/backend/commands/opclasscmds.c | 41 ++--- src/backend/commands/tablecmds.c | 6 +- src/backend/commands/typecmds.c | 1 + src/backend/executor/execAmi.c | 16 +- src/backend/executor/nodeIndexscan.c | 5 +- src/backend/nodes/outfuncs.c | 2 +- src/backend/optimizer/path/costsize.c | 14 +- src/backend/optimizer/util/plancat.c | 24 ++- src/backend/parser/parse_clause.c | 1 + src/backend/parser/parse_utilcmd.c | 14 +- src/backend/postmaster/autovacuum.c | 2 +- src/backend/utils/adt/pseudotypes.c | 27 +++ src/backend/utils/adt/ruleutils.c | 8 +- src/backend/utils/adt/selfuncs.c | 98 +++-------- src/backend/utils/cache/lsyscache.c | 1 + src/backend/utils/cache/relcache.c | 86 +++++---- src/backend/utils/cache/syscache.c | 1 + src/backend/utils/cache/typcache.c | 1 + src/backend/utils/sort/sortsupport.c | 1 + src/backend/utils/sort/tuplesort.c | 1 + src/include/access/amapi.h | 174 ++++++++++++++++++ src/include/access/brin.h | 15 +- src/include/access/brin_internal.h | 34 +++- src/include/access/gin_private.h | 36 ++-- src/include/access/gist_private.h | 30 +++- src/include/access/gistscan.h | 11 +- src/include/access/hash.h | 36 ++-- src/include/access/nbtree.h | 44 +++-- src/include/access/reloptions.h | 5 +- src/include/access/spgist.h | 44 +++-- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_am.h | 78 +-------- src/include/catalog/pg_proc.h | 196 +++------------------ src/include/catalog/pg_type.h | 2 + src/include/nodes/nodes.h | 1 + src/include/nodes/relation.h | 6 +- src/include/utils/builtins.h | 2 + src/include/utils/index_selfuncs.h | 68 ++++++++ src/include/utils/rel.h | 23 +-- src/include/utils/selfuncs.h | 11 +- src/test/regress/expected/oidjoins.out | 136 ++------------- src/test/regress/expected/opr_sanity.out | 128 +++----------- src/test/regress/sql/oidjoins.sql | 70 +------- src/test/regress/sql/opr_sanity.sql | 115 +++--------- src/tools/findoidjoins/README | 33 ++-- 93 files changed, 2493 insertions(+), 1924 deletions(-) create mode 100644 src/backend/access/brin/brin_validate.c create mode 100644 src/backend/access/gin/ginvalidate.c create mode 100644 src/backend/access/gist/gistvalidate.c create mode 100644 src/backend/access/hash/hashvalidate.c create mode 100644 src/backend/access/index/amapi.c create mode 100644 src/backend/access/nbtree/nbtvalidate.c create mode 100644 src/backend/access/spgist/spgvalidate.c create mode 100644 src/include/access/amapi.h create mode 100644 src/include/utils/index_selfuncs.h diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c index c1e83f301b..d088ce5719 100644 --- a/contrib/pageinspect/btreefuncs.c +++ b/contrib/pageinspect/btreefuncs.c @@ -29,6 +29,7 @@ #include "access/nbtree.h" #include "catalog/namespace.h" +#include "catalog/pg_am.h" #include "funcapi.h" #include "miscadmin.h" #include "utils/builtins.h" diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index a2ea5d709c..8048a2cbe7 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -32,6 +32,7 @@ #include "access/htup_details.h" #include "access/nbtree.h" #include "catalog/namespace.h" +#include "catalog/pg_am.h" #include "funcapi.h" #include "miscadmin.h" #include "storage/bufmgr.h" diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index 4e221c3682..c1122b496a 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -29,6 +29,7 @@ #include "access/nbtree.h" #include "access/relscan.h" #include "catalog/namespace.h" +#include "catalog/pg_am.h" #include "funcapi.h" #include "miscadmin.h" #include "storage/bufmgr.h" diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 97ef618606..412c8450ba 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -516,8 +516,8 @@ The catalog pg_am stores information about index access methods. There is one row for each index access method supported by - the system. The contents of this catalog are discussed in detail in - . + the system. The requirements for index access methods are discussed in + detail in . @@ -549,212 +549,13 @@ - amstrategies - int2 - - Number of operator strategies for this access method, - or zero if access method does not have a fixed set of operator - strategies - - - - amsupport - int2 - - Number of support routines for this access method - - - - amcanorder - bool - - Does the access method support ordered scans sorted by the - indexed column's value? - - - - amcanorderbyop - bool - - Does the access method support ordered scans sorted by the result - of an operator on the indexed column? - - - - amcanbackward - bool - - Does the access method support backward scanning? - - - - amcanunique - bool - - Does the access method support unique indexes? - - - - amcanmulticol - bool - - Does the access method support multicolumn indexes? - - - - amoptionalkey - bool - - Does the access method support a scan without any constraint - for the first index column? - - - - amsearcharray - bool - - Does the access method support ScalarArrayOpExpr searches? - - - - amsearchnulls - bool - - Does the access method support IS NULL/NOT NULL searches? - - - - amstorage - bool - - Can index storage data type differ from column data type? - - - - amclusterable - bool - - Can an index of this type be clustered on? - - - - ampredlocks - bool - - Does an index of this type manage fine-grained predicate locks? - - - - amkeytype + amhandler oid - pg_type.oid - Type of data stored in index, or zero if not a fixed type - - - - aminsert - regproc pg_proc.oid - Insert this tuple function - - - - ambeginscan - regproc - pg_proc.oid - Prepare for index scan function - - - - amgettuple - regproc - pg_proc.oid - Next valid tuple function, or zero if none - - - - amgetbitmap - regproc - pg_proc.oid - Fetch all valid tuples function, or zero if none - - - - amrescan - regproc - pg_proc.oid - (Re)start index scan function - - - - amendscan - regproc - pg_proc.oid - Clean up after index scan function - - - - ammarkpos - regproc - pg_proc.oid - Mark current scan position function - - - - amrestrpos - regproc - pg_proc.oid - Restore marked scan position function - - - - ambuild - regproc - pg_proc.oid - Build new index function - - - - ambuildempty - regproc - pg_proc.oid - Build empty index function - - - - ambulkdelete - regproc - pg_proc.oid - Bulk-delete function - - - - amvacuumcleanup - regproc - pg_proc.oid - Post-VACUUM cleanup function - - - - amcanreturn - regproc - pg_proc.oid - Function to check whether an index column supports index-only - scans. Can be zero if index-only scans are never supported. - - - - amcostestimate - regproc - pg_proc.oid - Function to estimate cost of an index scan - - - - amoptions - regproc - pg_proc.oid - Function to parse and validate reloptions for an index + + OID of a handler function that is responsible for supplying information + about the access method + diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index d1db0d26a2..3b3e903b03 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -4632,6 +4632,10 @@ SELECT * FROM pg_attribute fdw_handler + + index_am_handler + + tsm_handler @@ -4730,6 +4734,11 @@ SELECT * FROM pg_attribute A foreign-data wrapper handler is declared to return fdw_handler. + + index_am_handler + An index access method handler is declared to return index_am_handler. + + tsm_handler A tablesample method handler is declared to return tsm_handler. diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index 1c09bae395..5f7befb858 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -22,7 +22,7 @@ pages so that they can use the regular storage manager and buffer manager to access the index contents. (All the existing index access methods furthermore use the standard page layout described in , and they all use the same format for index + linkend="storage-page-layout">, and most use the same format for index tuple headers; but these decisions are not forced on an access method.) @@ -43,26 +43,98 @@ are reclaimed. - - Catalog Entries for Indexes + + Basic API Structure for Indexes Each index access method is described by a row in the - pg_am system catalog (see - ). The principal contents of a - pg_am row are references to - pg_proc - entries that identify the index access - functions supplied by the access method. The APIs for these functions - are defined later in this chapter. In addition, the - pg_am row specifies a few fixed properties of - the access method, such as whether it can support multicolumn indexes. - There is not currently any special support + pg_am + system catalog. The pg_am entry + specifies a name and a handler function for the access + method. There is not currently any special support for creating or deleting pg_am entries; anyone able to write a new access method is expected to be competent to insert an appropriate row for themselves. + + An index access method handler function must be declared to accept a + single argument of type internal and to return the + pseudo-type index_am_handler. The argument is a dummy value that + simply serves to prevent handler functions from being called directly from + SQL commands. The result of the function must be a palloc'd struct of + type IndexAmRoutine, which contains everything + that the core code needs to know to make use of the index access method. + The IndexAmRoutine struct, also called the access + method's API struct, includes fields specifying assorted + fixed properties of the access method, such as whether it can support + multicolumn indexes. More importantly, it contains pointers to support + functions for the access method, which do all of the real work to access + indexes. These support functions are plain C functions and are not + visible or callable at the SQL level. The support functions are described + in . + + + + The structure IndexAmRoutine is defined thus: + +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; + + + To be useful, an index access method must also have one or more operator families and @@ -96,7 +168,7 @@ - Some of the flag columns of pg_am have nonobvious + Some of the flag fields of IndexAmRoutine have nonobvious implications. The requirements of amcanunique are discussed in . The amcanmulticol flag asserts that the @@ -143,7 +215,7 @@ The index construction and maintenance functions that an index access - method must provide are: + method must provide in IndexAmRoutine are: @@ -169,7 +241,7 @@ void ambuildempty (Relation indexRelation); Build an empty index, and write it to the initialization fork (INIT_FORKNUM) - of the given relation. This method is called only for unlogged tables; the + of the given relation. This method is called only for unlogged indexes; the empty index written to the initialization fork will be copied over the main relation fork on each server restart. @@ -188,7 +260,7 @@ aminsert (Relation indexRelation, isnull arrays give the key values to be indexed, and heap_tid is the TID to be indexed. If the access method supports unique indexes (its - pg_am.amcanunique flag is true) then + amcanunique flag is true) then checkUnique indicates the type of uniqueness check to perform. This varies depending on whether the unique constraint is deferrable; see for details. @@ -281,8 +353,8 @@ amcanreturn (Relation indexRelation, int attno); the form of an IndexTuple. The attribute number is 1-based, i.e. the first columns attno is 1. Returns TRUE if supported, else FALSE. If the access method does not support index-only scans at all, - the amcanreturn field in its pg_am row can - be set to zero. + the amcanreturn field in its IndexAmRoutine + struct can be set to NULL. @@ -325,6 +397,19 @@ amoptions (ArrayType *reloptions, It is OK to return NULL if default behavior is wanted. + + +bool +amvalidate (Oid opclassoid); + + Validate the catalog entries for the specified operator class, so far as + the access method can reasonably do that. For example, this might include + testing that all required support functions are provided. + The amvalidate function must return false if the opclass is + invalid. Problems should be reported with ereport messages. + + + The purpose of an index, of course, is to support scans for tuples matching an indexable WHERE condition, often called a @@ -414,8 +499,8 @@ amgettuple (IndexScanDesc scan, The amgettuple function need only be provided if the access method supports plain index scans. If it doesn't, the - amgettuple field in its pg_am row must - be set to zero. + amgettuple field in its IndexAmRoutine + struct must be set to NULL. @@ -445,8 +530,8 @@ amgetbitmap (IndexScanDesc scan, The amgetbitmap function need only be provided if the access method supports bitmap index scans. If it doesn't, the - amgetbitmap field in its pg_am row must - be set to zero. + amgetbitmap field in its IndexAmRoutine + struct must be set to NULL. @@ -468,6 +553,13 @@ ammarkpos (IndexScanDesc scan); remembered scan position per scan. + + The ammarkpos function need only be provided if the access + method supports ordered scans. If it doesn't, + the ammarkpos field in its IndexAmRoutine + struct may be set to NULL. + + void @@ -477,18 +569,11 @@ amrestrpos (IndexScanDesc scan); - By convention, the pg_proc entry for an index - access method function should show the correct number of arguments, - but declare them all as type internal (since most of the arguments - have types that are not known to SQL, and we don't want users calling - the functions directly anyway). The return type is declared as - void, internal, or boolean as appropriate. - The only exception is amoptions, which should be correctly - declared as taking text[] and bool and returning - bytea. This provision allows client code to execute - amoptions to test validity of options settings. + The amrestrpos function need only be provided if the access + method supports ordered scans. If it doesn't, + the amrestrpos field in its IndexAmRoutine + struct may be set to NULL. - @@ -548,7 +633,7 @@ amrestrpos (IndexScanDesc scan); Access methods that always return entries in the natural ordering of their data (such as btree) should set - pg_am.amcanorder to true. + amcanorder to true. Currently, such access methods must use btree-compatible strategy numbers for their equality and ordering operators. @@ -556,7 +641,7 @@ amrestrpos (IndexScanDesc scan); Access methods that support ordering operators should set - pg_am.amcanorderbyop to true. + amcanorderbyop to true. This indicates that the index is capable of returning entries in an order satisfying ORDER BY index_key operator constant. Scan modifiers @@ -579,7 +664,7 @@ amrestrpos (IndexScanDesc scan); methods that set amcanorder to true.) After the first call, amgettuple must be prepared to advance the scan in either direction from the most recently returned entry. (But if - pg_am.amcanbackward is false, all subsequent + amcanbackward is false, all subsequent calls will have the same direction as the first one.) @@ -588,10 +673,10 @@ amrestrpos (IndexScanDesc scan); position in a scan and later returning to the marked position. The same position might be restored multiple times. However, only one position need be remembered per scan; a new ammarkpos call overrides the - previously marked position. An access method that does not support - ordered scans should still provide mark and restore functions in - pg_am, but it is sufficient to have them throw errors if - called. + previously marked position. An access method that does not support ordered + scans need not provide ammarkpos and amrestrpos + functions in IndexAmRoutine; set those pointers to NULL + instead. @@ -766,7 +851,7 @@ amrestrpos (IndexScanDesc scan); PostgreSQL enforces SQL uniqueness constraints using unique indexes, which are indexes that disallow multiple entries with identical keys. An access method that supports this - feature sets pg_am.amcanunique true. + feature sets amcanunique true. (At present, only b-tree supports it.) diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml index 178278251e..aaf9d8cd1f 100644 --- a/doc/src/sgml/xindex.sgml +++ b/doc/src/sgml/xindex.sgml @@ -34,7 +34,7 @@ regular access to tables is built into PostgreSQL, but all index methods are described in pg_am. It is possible to add a - new index method by defining the required interface routines and + new index access method by writing the necessary code and then creating a row in pg_am — but that is beyond the scope of this chapter (see ). diff --git a/src/backend/access/brin/Makefile b/src/backend/access/brin/Makefile index f4572d80a8..5aef925ed4 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 60d405ef17..c740952923 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" /* @@ -71,6 +70,50 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a, 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 @@ -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 0000000000..956ecb95a0 --- /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 45fba90585..86b9ae1c3c 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 db4f496a4d..0895003cd1 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 5bb8f1f092..a6756d594d 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 8bcd159032..cd21e0e655 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 458540415f..3449a30e41 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 c1c69cef1d..94502678ab 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,8 +22,53 @@ #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 * @@ -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 9e496dc736..f0a246f3f3 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 0000000000..e934fe824b --- /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 cc9468ffb1..21875bf7ae 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 834d9d3335..996363c2de 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, @@ -49,6 +47,50 @@ static void gistvacuumpage(Relation rel, Page page, Buffer buffer); } while(0) +/* + * 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 @@ -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 98ea0cb2d1..4e43a6932a 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 21f9de511f..41b83431b6 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 8ab4183836..31758a1c8f 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) +void +gistendscan(IndexScanDesc scan) { - elog(ERROR, "GiST does not support mark/restore"); - PG_RETURN_VOID(); -} - -Datum -gistrestrpos(PG_FUNCTION_ARGS) -{ - 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 0995e0355c..fac166d4c2 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 346f6d96f4..7947ff9dbe 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 0000000000..86b5aeaec5 --- /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 82297606dc..5d3bd94d3e 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 1d21ea155b..3d48c4f031 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 86037d4727..456954b063 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 0000000000..abd678483c --- /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 96490db032..b82e5d727f 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 0000000000..bda166a9ef --- /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 9352423305..54b71cb2f7 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) \ +#define CHECK_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); \ - } \ + if (indexRelation->rd_amroutine->pname == NULL) \ + elog(ERROR, "function %s is not defined for index %s", \ + CppAsString(pname), RelationGetRelationName(indexRelation)); \ } while(0) -#define GET_UNCACHED_REL_PROCEDURE(pname) \ +#define CHECK_SCAN_PROCEDURE(pname) \ do { \ - if (!RegProcedureIsValid(indexRelation->rd_am->pname)) \ - elog(ERROR, "invalid %s regproc", CppAsString(pname)); \ - fmgr_info(indexRelation->rd_am->pname, &procedure); \ -} while(0) - -#define GET_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); + CHECK_REL_PROCEDURE(ambulkdelete); - result = (IndexBulkDeleteResult *) - DatumGetPointer(FunctionCall4(&procedure, - PointerGetDatum(info), - PointerGetDatum(stats), - PointerGetDatum((Pointer) callback), - PointerGetDatum(callback_state))); - - 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); + CHECK_REL_PROCEDURE(amvacuumcleanup); - result = (IndexBulkDeleteResult *) - DatumGetPointer(FunctionCall2(&procedure, - PointerGetDatum(info), - PointerGetDatum(stats))); - - 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 2d76d648e0..bbb21d235c 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 752e3b5dd1..f2905cb734 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 555f7df3f9..c850b4804f 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 0000000000..b814b54a36 --- /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 918da1fcca..14948a531e 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 0e6a180cc9..44fd644e42 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 48e678c970..620e746199 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 9ba077686a..201203f91a 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 c2d4f0a68a..15b867f24c 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 0000000000..c2d2d466d9 --- /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 401c59f9a1..313ee9c4ed 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -23,6 +23,7 @@ #include +#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 65cf3ed9fe..0232e0d867 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 0aba9e2ccc..f40a005f22 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 23a69dc0aa..5cb28cfa73 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 7b3a2f498b..13b04e68f0 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 a0ca2114d1..8a661968cd 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 0b4a334631..eeda3b4697 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 f0b293ceec..4f41766eef 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 ada54a102d..35864c1681 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 46146613cf..bf16cb1b57 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 d95e15181a..f1e22e5fb9 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 4e17fac6c1..8fb483aaeb 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -75,6 +75,7 @@ #endif #include +#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 d5528e0381..0ea9fcf7c2 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 8396048b5b..7ea455cf04 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 e319d57aca..a65b297728 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 4fa66222d9..e2859df41d 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 f309ad3cbe..dd447cf4e8 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -373,6 +373,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. */ diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 023d651899..4efd2988e7 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -19,11 +19,13 @@ #include #include +#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 ebb03aae47..46c95b089e 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 5c9333c296..a180d2b507 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 fc5b9d9934..130c06d81c 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 @@ -1161,6 +1162,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 */ @@ -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 @@ -1230,17 +1255,20 @@ RelationInitIndexAccessInfo(Relation relation) ALLOCSET_SMALL_MAXSIZE); relation->rd_indexcxt = indexcxt; + /* + * Now we can fetch the index AM's API struct + */ + InitIndexAmRoutine(relation); + /* * Allocate arrays to hold data */ - relation->rd_aminfo = (RelationAmInfo *) - MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo)); - 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 6eb2ac69a1..65ffe84409 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 4ad787bfa4..ea6f787a52 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 e193372d2e..e9eeb18b7e 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 3d5b8ed907..a30e1707a7 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 0000000000..35f1061b3a --- /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 111de82e04..99bf5330bb 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 5ec47f4c84..47317af43c 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 2ba8a21124..fe04aaa6ef 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 71f4b5ef63..f9732ba7fb 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 a7d498758c..4acaebdc9e 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 6dde6a71b3..3a68390473 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 b76083323b..06822fac3c 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 ca6b519c9e..469ac677e3 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 5936af6f4e..1994f718eb 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 62e08a9674..54b9944c41 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 0c7fcdc02f..f801c3ee57 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 f58672e4a7..3df5ac50b6 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)"); +/* 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 = 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)"); +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 16e0d91784..2c90b76fe1 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 2b73483098..cf09db4e5f 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 61519bb6fa..6deda54c14 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 b35d20626b..477fde1f81 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 0000000000..a03e12f518 --- /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 ff5672d265..f2bebf2c3d 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" @@ -46,23 +45,6 @@ typedef struct LockInfoData 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 38fe731f81..06fbca719b 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 d85bc83e11..f10e007f58 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 f786962488..79c13211ec 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 2fa628d0f7..9b7c47060b 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 9a55aea5d3..257a4a2765 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 c46f9fc0f8..f7a88227ac 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