#include "access/nbtree.h"
#include "catalog/namespace.h"
+#include "catalog/pg_am.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#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"
#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"
<para>
The catalog <structname>pg_am</structname> 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
- <xref linkend="indexam">.
+ the system. The requirements for index access methods are discussed in
+ detail in <xref linkend="indexam">.
</para>
<table>
</row>
<row>
- <entry><structfield>amstrategies</structfield></entry>
- <entry><type>int2</type></entry>
- <entry></entry>
- <entry>Number of operator strategies for this access method,
- or zero if access method does not have a fixed set of operator
- strategies</entry>
- </row>
-
- <row>
- <entry><structfield>amsupport</structfield></entry>
- <entry><type>int2</type></entry>
- <entry></entry>
- <entry>Number of support routines for this access method</entry>
- </row>
-
- <row>
- <entry><structfield>amcanorder</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support ordered scans sorted by the
- indexed column's value?</entry>
- </row>
-
- <row>
- <entry><structfield>amcanorderbyop</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support ordered scans sorted by the result
- of an operator on the indexed column?</entry>
- </row>
-
- <row>
- <entry><structfield>amcanbackward</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support backward scanning?</entry>
- </row>
-
- <row>
- <entry><structfield>amcanunique</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support unique indexes?</entry>
- </row>
-
- <row>
- <entry><structfield>amcanmulticol</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support multicolumn indexes?</entry>
- </row>
-
- <row>
- <entry><structfield>amoptionalkey</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support a scan without any constraint
- for the first index column?</entry>
- </row>
-
- <row>
- <entry><structfield>amsearcharray</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support <literal>ScalarArrayOpExpr</> searches?</entry>
- </row>
-
- <row>
- <entry><structfield>amsearchnulls</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does the access method support <literal>IS NULL</>/<literal>NOT NULL</> searches?</entry>
- </row>
-
- <row>
- <entry><structfield>amstorage</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Can index storage data type differ from column data type?</entry>
- </row>
-
- <row>
- <entry><structfield>amclusterable</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Can an index of this type be clustered on?</entry>
- </row>
-
- <row>
- <entry><structfield>ampredlocks</structfield></entry>
- <entry><type>bool</type></entry>
- <entry></entry>
- <entry>Does an index of this type manage fine-grained predicate locks?</entry>
- </row>
-
- <row>
- <entry><structfield>amkeytype</structfield></entry>
+ <entry><structfield>amhandler</structfield></entry>
<entry><type>oid</type></entry>
- <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
- <entry>Type of data stored in index, or zero if not a fixed type</entry>
- </row>
-
- <row>
- <entry><structfield>aminsert</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Insert this tuple</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>ambeginscan</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Prepare for index scan</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>amgettuple</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Next valid tuple</quote> function, or zero if none</entry>
- </row>
-
- <row>
- <entry><structfield>amgetbitmap</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Fetch all valid tuples</quote> function, or zero if none</entry>
- </row>
-
- <row>
- <entry><structfield>amrescan</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>(Re)start index scan</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>amendscan</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Clean up after index scan</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>ammarkpos</structfield></entry>
- <entry><type>regproc</type></entry>
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Mark current scan position</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>amrestrpos</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Restore marked scan position</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>ambuild</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Build new index</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>ambuildempty</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry><quote>Build empty index</quote> function</entry>
- </row>
-
- <row>
- <entry><structfield>ambulkdelete</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>Bulk-delete function</entry>
- </row>
-
- <row>
- <entry><structfield>amvacuumcleanup</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>Post-<command>VACUUM</command> cleanup function</entry>
- </row>
-
- <row>
- <entry><structfield>amcanreturn</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>Function to check whether an index column supports index-only
- scans. Can be zero if index-only scans are never supported.</entry>
- </row>
-
- <row>
- <entry><structfield>amcostestimate</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>Function to estimate cost of an index scan</entry>
- </row>
-
- <row>
- <entry><structfield>amoptions</structfield></entry>
- <entry><type>regproc</type></entry>
- <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
- <entry>Function to parse and validate <structfield>reloptions</> for an index</entry>
+ <entry>
+ OID of a handler function that is responsible for supplying information
+ about the access method
+ </entry>
</row>
</tbody>
<primary>fdw_handler</primary>
</indexterm>
+ <indexterm zone="datatype-pseudo">
+ <primary>index_am_handler</primary>
+ </indexterm>
+
<indexterm zone="datatype-pseudo">
<primary>tsm_handler</primary>
</indexterm>
<entry>A foreign-data wrapper handler is declared to return <type>fdw_handler</>.</entry>
</row>
+ <row>
+ <entry><type>index_am_handler</></entry>
+ <entry>An index access method handler is declared to return <type>index_am_handler</>.</entry>
+ </row>
+
<row>
<entry><type>tsm_handler</></entry>
<entry>A tablesample method handler is declared to return <type>tsm_handler</>.</entry>
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 <xref
- linkend="storage-page-layout">, 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.)
</para>
are reclaimed.
</para>
- <sect1 id="index-catalog">
- <title>Catalog Entries for Indexes</title>
+ <sect1 id="index-api">
+ <title>Basic API Structure for Indexes</title>
<para>
Each index access method is described by a row in the
- <structname>pg_am</structname> system catalog (see
- <xref linkend="catalog-pg-am">). The principal contents of a
- <structname>pg_am</structname> row are references to
- <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
- 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
- <structname>pg_am</structname> 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
+ <link linkend="catalog-pg-am"><structname>pg_am</structname></link>
+ system catalog. The <structname>pg_am</structname> entry
+ specifies a name and a <firstterm>handler function</> for the access
+ method. There is not currently any special support
for creating or deleting <structname>pg_am</structname> entries;
anyone able to write a new access method is expected to be competent
to insert an appropriate row for themselves.
</para>
+ <para>
+ An index access method handler function must be declared to accept a
+ single argument of type <type>internal</> and to return the
+ pseudo-type <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 <structname>IndexAmRoutine</structname>, which contains everything
+ that the core code needs to know to make use of the index access method.
+ The <structname>IndexAmRoutine</structname> struct, also called the access
+ method's <firstterm>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 <xref linkend="index-functions">.
+ </para>
+
+ <para>
+ The structure <structname>IndexAmRoutine</structname> is defined thus:
+<programlisting>
+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;
+</programlisting>
+ </para>
+
<para>
To be useful, an index access method must also have one or more
<firstterm>operator families</> and
</para>
<para>
- Some of the flag columns of <structname>pg_am</structname> have nonobvious
+ Some of the flag fields of <structname>IndexAmRoutine</> have nonobvious
implications. The requirements of <structfield>amcanunique</structfield>
are discussed in <xref linkend="index-unique-checks">.
The <structfield>amcanmulticol</structfield> flag asserts that the
<para>
The index construction and maintenance functions that an index access
- method must provide are:
+ method must provide in <structname>IndexAmRoutine</structname> are:
</para>
<para>
ambuildempty (Relation indexRelation);
</programlisting>
Build an empty index, and write it to the initialization fork (<symbol>INIT_FORKNUM</symbol>)
- 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.
</para>
<literal>isnull</> arrays give the key values to be indexed, and
<literal>heap_tid</> is the TID to be indexed.
If the access method supports unique indexes (its
- <structname>pg_am</>.<structfield>amcanunique</> flag is true) then
+ <structfield>amcanunique</> flag is true) then
<literal>checkUnique</> indicates the type of uniqueness check to
perform. This varies depending on whether the unique constraint is
deferrable; see <xref linkend="index-unique-checks"> for details.
the form of an <structname>IndexTuple</structname>. 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 <structfield>amcanreturn</> field in its <structname>pg_am</> row can
- be set to zero.
+ the <structfield>amcanreturn</> field in its <structname>IndexAmRoutine</>
+ struct can be set to NULL.
</para>
<para>
It is OK to return NULL if default behavior is wanted.
</para>
+ <para>
+<programlisting>
+bool
+amvalidate (Oid opclassoid);
+</programlisting>
+ 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 <function>amvalidate</> function must return false if the opclass is
+ invalid. Problems should be reported with <function>ereport</> messages.
+ </para>
+
+
<para>
The purpose of an index, of course, is to support scans for tuples matching
an indexable <literal>WHERE</> condition, often called a
<para>
The <function>amgettuple</> function need only be provided if the access
method supports <quote>plain</> index scans. If it doesn't, the
- <structfield>amgettuple</> field in its <structname>pg_am</> row must
- be set to zero.
+ <structfield>amgettuple</> field in its <structname>IndexAmRoutine</>
+ struct must be set to NULL.
</para>
<para>
<para>
The <function>amgetbitmap</> function need only be provided if the access
method supports <quote>bitmap</> index scans. If it doesn't, the
- <structfield>amgetbitmap</> field in its <structname>pg_am</> row must
- be set to zero.
+ <structfield>amgetbitmap</> field in its <structname>IndexAmRoutine</>
+ struct must be set to NULL.
</para>
<para>
remembered scan position per scan.
</para>
+ <para>
+ The <function>ammarkpos</> function need only be provided if the access
+ method supports ordered scans. If it doesn't,
+ the <structfield>ammarkpos</> field in its <structname>IndexAmRoutine</>
+ struct may be set to NULL.
+ </para>
+
<para>
<programlisting>
void
</para>
<para>
- By convention, the <literal>pg_proc</literal> entry for an index
- access method function should show the correct number of arguments,
- but declare them all as type <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
- <type>void</>, <type>internal</>, or <type>boolean</> as appropriate.
- The only exception is <function>amoptions</>, which should be correctly
- declared as taking <type>text[]</> and <type>bool</> and returning
- <type>bytea</>. This provision allows client code to execute
- <function>amoptions</> to test validity of options settings.
+ The <function>amrestrpos</> function need only be provided if the access
+ method supports ordered scans. If it doesn't,
+ the <structfield>amrestrpos</> field in its <structname>IndexAmRoutine</>
+ struct may be set to NULL.
</para>
-
</sect1>
<sect1 id="index-scanning">
<para>
Access methods that always return entries in the natural ordering
of their data (such as btree) should set
- <structname>pg_am</>.<structfield>amcanorder</> to true.
+ <structfield>amcanorder</> to true.
Currently, such access methods must use btree-compatible strategy
numbers for their equality and ordering operators.
</para>
<listitem>
<para>
Access methods that support ordering operators should set
- <structname>pg_am</>.<structfield>amcanorderbyop</> to true.
+ <structfield>amcanorderbyop</> to true.
This indicates that the index is capable of returning entries in
an order satisfying <literal>ORDER BY</> <replaceable>index_key</>
<replaceable>operator</> <replaceable>constant</>. Scan modifiers
methods that set <structfield>amcanorder</> to true.) After the
first call, <function>amgettuple</> must be prepared to advance the scan in
either direction from the most recently returned entry. (But if
- <structname>pg_am</>.<structfield>amcanbackward</> is false, all subsequent
+ <structfield>amcanbackward</> is false, all subsequent
calls will have the same direction as the first one.)
</para>
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 <function>ammarkpos</> call overrides the
- previously marked position. An access method that does not support
- ordered scans should still provide mark and restore functions in
- <structname>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 <function>ammarkpos</> and <function>amrestrpos</>
+ functions in <structname>IndexAmRoutine</>; set those pointers to NULL
+ instead.
</para>
<para>
<productname>PostgreSQL</productname> enforces SQL uniqueness constraints
using <firstterm>unique indexes</>, which are indexes that disallow
multiple entries with identical keys. An access method that supports this
- feature sets <structname>pg_am</>.<structfield>amcanunique</> true.
+ feature sets <structfield>amcanunique</> true.
(At present, only b-tree supports it.)
</para>
regular access to tables is built into
<productname>PostgreSQL</productname>, but all index methods are
described in <classname>pg_am</classname>. 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 <classname>pg_am</classname> — but that is
beyond the scope of this chapter (see <xref linkend="indexam">).
</para>
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
#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"
/*
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
* 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;
MemoryContextDelete(tupcxt);
}
- return BoolGetDatum(false);
+ return false;
}
/*
* 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;
opaque->bo_bdesc = brin_build_desc(r);
scan->opaque = opaque;
- PG_RETURN_POINTER(scan);
+ return scan;
}
/*
* 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;
* 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
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();
}
/*
/*
* 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;
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. */
END_CRIT_SECTION();
UnlockReleaseBuffer(metabuf);
-
- PG_RETURN_VOID();
}
/*
* 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));
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;
/* if none set, we're done */
if (numoptions == 0)
- PG_RETURN_NULL();
+ return NULL;
rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
pfree(options);
- PG_RETURN_BYTEA_P(rdopts);
+ return (bytea *) rdopts;
}
/*
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
* 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;
/*
* 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);
}
/*
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
#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;
ginNewScanKey(scan);
if (GinIsVoidRes(scan))
- PG_RETURN_INT64(0);
+ return 0;
ntids = 0;
ntids++;
}
- PG_RETURN_INT64(ntids);
+ return ntids;
}
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;
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;
/* Unlock and release the buffers. */
UnlockReleaseBuffer(MetaBuffer);
UnlockReleaseBuffer(RootBuffer);
-
- PG_RETURN_VOID();
}
/*
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;
MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(insertCtx);
- PG_RETURN_BOOL(false);
+ return false;
}
#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;
scan->opaque = so;
- PG_RETURN_POINTER(scan);
+ return 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);
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);
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();
}
/*-------------------------------------------------------------------------
*
* 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
#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
*
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;
/* if none set, we're done */
if (numoptions == 0)
- PG_RETURN_NULL();
+ return NULL;
rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
pfree(options);
- PG_RETURN_BYTEA_P(rdopts);
+ return (bytea *) rdopts;
}
/*
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;
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,
initGinState(&ginstate, index);
ginInsertCleanup(&ginstate, true, true, stats);
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
if (needLock)
UnlockRelationForExtension(index, ExclusiveLock);
- PG_RETURN_POINTER(stats);
+ return stats;
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
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
*/
#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,
} 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
/*
* 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 */
/* Unlock and release the buffer */
UnlockReleaseBuffer(buffer);
-
- PG_RETURN_VOID();
}
/*
* 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;
MemoryContextSwitchTo(oldCxt);
freeGISTstate(giststate);
- PG_RETURN_BOOL(false);
+ return false;
}
* 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;
result->heap_tuples = reltuples;
result->index_tuples = (double) buildstate.indtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
/*
* 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)
{
if (scan->numberOfOrderBys > 0)
{
/* Must fetch tuples in strict distance order */
- PG_RETURN_BOOL(getNextNearest(scan));
+ return getNextNearest(scan);
}
else
{
so->curPageData++;
- PG_RETURN_BOOL(true);
+ return true;
}
/*
item = getNextGISTSearchItem(so);
if (!item)
- PG_RETURN_BOOL(false);
+ return false;
CHECK_FOR_INTERRUPTS();
/*
* 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);
pfree(item);
}
- PG_RETURN_INT64(ntids);
+ return ntids;
}
/*
*
* 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;
}
* 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;
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;
if (!first_time)
pfree(fn_extras);
}
-
- PG_RETURN_VOID();
-}
-
-Datum
-gistmarkpos(PG_FUNCTION_ARGS)
-{
- elog(ERROR, "GiST does not support mark/restore");
- PG_RETURN_VOID();
}
-Datum
-gistrestrpos(PG_FUNCTION_ARGS)
+void
+gistendscan(IndexScanDesc scan)
{
- elog(ERROR, "GiST does not support mark/restore");
- PG_RETURN_VOID();
-}
-
-Datum
-gistendscan(PG_FUNCTION_ARGS)
-{
- IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
/*
* as well as the queueCxt if there is a separate context for it.
*/
freeGISTstate(so->giststate);
-
- PG_RETURN_VOID();
}
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;
/* if none set, we're done */
if (numoptions == 0)
- PG_RETURN_NULL();
+ return NULL;
rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
pfree(options);
- PG_RETURN_BYTEA_P(rdopts);
-
+ return (bytea *) rdopts;
}
/*
/*
* 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;
/* 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)
if (needLock)
UnlockRelationForExtension(rel, ExclusiveLock);
- PG_RETURN_POINTER(stats);
+ return stats;
}
typedef struct GistBDItem
*
* 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;
vacuum_delay_point();
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
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
#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"
/*
- * 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;
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();
}
/*
* 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;
/*
* 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);
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;
/* 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;
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;
/* 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;
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;
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();
}
/*
*
* 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;
stats->tuples_removed += tuples_removed;
/* hashvacuumcleanup will fill in num_pages */
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
*
* 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;
}
}
}
-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);
}
/*
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
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
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+}
#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"
#define RELATION_CHECKS \
( \
AssertMacro(RelationIsValid(indexRelation)), \
- AssertMacro(PointerIsValid(indexRelation->rd_am)), \
+ AssertMacro(PointerIsValid(indexRelation->rd_amroutine)), \
AssertMacro(!ReindexIsProcessingIndex(RelationGetRelid(indexRelation))) \
)
( \
AssertMacro(IndexScanIsValid(scan)), \
AssertMacro(RelationIsValid(scan->indexRelation)), \
- AssertMacro(PointerIsValid(scan->indexRelation->rd_am)) \
+ AssertMacro(PointerIsValid(scan->indexRelation->rd_amroutine)) \
)
-#define GET_REL_PROCEDURE(pname) \
-do { \
- procedure = &indexRelation->rd_aminfo->pname; \
- if (!OidIsValid(procedure->fn_oid)) \
- { \
- RegProcedure procOid = indexRelation->rd_am->pname; \
- if (!RegProcedureIsValid(procOid)) \
- elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
- fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
- } \
-} while(0)
-
-#define GET_UNCACHED_REL_PROCEDURE(pname) \
+#define CHECK_REL_PROCEDURE(pname) \
do { \
- if (!RegProcedureIsValid(indexRelation->rd_am->pname)) \
- elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
- fmgr_info(indexRelation->rd_am->pname, &procedure); \
+ if (indexRelation->rd_amroutine->pname == NULL) \
+ elog(ERROR, "function %s is not defined for index %s", \
+ CppAsString(pname), RelationGetRelationName(indexRelation)); \
} while(0)
-#define GET_SCAN_PROCEDURE(pname) \
+#define CHECK_SCAN_PROCEDURE(pname) \
do { \
- procedure = &scan->indexRelation->rd_aminfo->pname; \
- if (!OidIsValid(procedure->fn_oid)) \
- { \
- RegProcedure procOid = scan->indexRelation->rd_am->pname; \
- if (!RegProcedureIsValid(procOid)) \
- elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
- fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
- } \
+ if (scan->indexRelation->rd_amroutine->pname == NULL) \
+ elog(ERROR, "function %s is not defined for index %s", \
+ CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
} while(0)
static IndexScanDesc index_beginscan_internal(Relation indexRelation,
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);
}
/*
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);
/*
/*
* 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);
}
/* ----------------
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);
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);
}
/* ----------------
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))
}
/* 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);
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);
}
/* ----------------
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);
}
/* ----------------
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));
* 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;
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;
/*
* 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);
void *callback_state)
{
Relation indexRelation = info->index;
- FmgrInfo procedure;
- IndexBulkDeleteResult *result;
RELATION_CHECKS;
- GET_UNCACHED_REL_PROCEDURE(ambulkdelete);
-
- result = (IndexBulkDeleteResult *)
- DatumGetPointer(FunctionCall4(&procedure,
- PointerGetDatum(info),
- PointerGetDatum(stats),
- PointerGetDatum((Pointer) callback),
- PointerGetDatum(callback_state)));
+ CHECK_REL_PROCEDURE(ambulkdelete);
- return result;
+ return indexRelation->rd_amroutine->ambulkdelete(info, stats,
+ callback, callback_state);
}
/* ----------------
IndexBulkDeleteResult *stats)
{
Relation indexRelation = info->index;
- FmgrInfo procedure;
- IndexBulkDeleteResult *result;
RELATION_CHECKS;
- GET_UNCACHED_REL_PROCEDURE(amvacuumcleanup);
-
- result = (IndexBulkDeleteResult *)
- DatumGetPointer(FunctionCall2(&procedure,
- PointerGetDatum(info),
- PointerGetDatum(stats)));
+ CHECK_REL_PROCEDURE(amvacuumcleanup);
- return result;
+ return indexRelation->rd_amroutine->amvacuumcleanup(info, stats);
}
/* ----------------
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);
}
/* ----------------
int nproc;
int procindex;
- nproc = irel->rd_am->amsupport;
+ nproc = irel->rd_amroutine->amsupport;
Assert(procnum > 0 && procnum <= (uint16) nproc);
int nproc;
int procindex;
- nproc = irel->rd_am->amsupport;
+ nproc = irel->rd_amroutine->amsupport;
Assert(procnum > 0 && procnum <= (uint16) nproc);
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
#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"
/*
- * 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;
result->heap_tuples = reltuples;
result->index_tuples = buildstate.indtuples;
- PG_RETURN_POINTER(result);
+ return result;
}
/*
/*
* 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. */
* checkpoint may have moved the redo pointer past our xlog record.
*/
smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
-
- PG_RETURN_VOID();
}
/*
* 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;
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;
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
- PG_RETURN_BOOL(false);
+ return false;
_bt_start_array_keys(scan, dir);
}
/* ... 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;
{
/* punt if we have any unsatisfiable array keys */
if (so->numArrayKeys < 0)
- PG_RETURN_INT64(ntids);
+ return ntids;
_bt_start_array_keys(scan, ForwardScanDirection);
}
/* 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;
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 */
/* 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 */
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). */
/* 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 */
else
BTScanPosInvalidate(so->currPos);
}
-
- PG_RETURN_VOID();
}
/*
*
* 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;
PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
_bt_end_vacuum(rel);
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
*
* 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
stats->num_index_tuples = info->num_heap_tuples;
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
/*
*
* btrees always do, so this is trivial.
*/
-Datum
-btcanreturn(PG_FUNCTION_ARGS)
+bool
+btcanreturn(Relation index, int attno)
{
- PG_RETURN_BOOL(true);
+ return true;
}
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);
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
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
/*
* 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;
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. */
* 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;
MemoryContextDelete(insertCtx);
/* return false since we've not done any unique check */
- PG_RETURN_BOOL(false);
+ return false;
}
}
}
-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;
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)
/* 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();
}
/*
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 */
spgWalk(scan->indexRelation, so, true, storeBitmap);
- PG_RETURN_INT64(so->ntids);
+ return so->ntids;
}
/* storeRes subroutine for gettuple case */
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)
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)
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;
}
#include "postgres.h"
-#include "access/genam.h"
#include "access/reloptions.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#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)
/*
* 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);
}
/*
*
* 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 */
spgvacuumscan(&bds);
- PG_RETURN_POINTER(stats);
+ return stats;
}
/* Dummy callback to delete no tuples during spgvacuumcleanup */
*
* 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
stats->num_index_tuples = info->num_heap_tuples;
}
- PG_RETURN_POINTER(stats);
+ return stats;
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
#include <unistd.h>
+#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#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"
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);
if (OidIsValid(opclassTup->opckeytype))
keyType = opclassTup->opckeytype;
else
- keyType = amform->amkeytype;
+ keyType = amroutine->amkeytype;
ReleaseSysCache(tuple);
if (OidIsValid(keyType) && keyType != to->atttypid)
}
}
- ReleaseSysCache(amtuple);
+ pfree(amroutine);
return indexTupDesc;
}
bool isprimary,
bool isreindex)
{
- RegProcedure procedure;
IndexBuildResult *stats;
Oid save_userid;
int save_sec_context;
* 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\"",
/*
* 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));
/*
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);
}
/*
#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"
#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"
*/
#include "postgres.h"
+#include "access/amapi.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/rewriteheap.h"
#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"
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",
#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"
HeapTuple tuple;
Form_pg_index indexForm;
Form_pg_am accessMethodForm;
+ IndexAmRoutine *amRoutine;
bool amcanorder;
int16 *coloptions;
IndexInfo *indexInfo;
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
Relation indexRelation;
HeapTuple tuple;
Form_pg_am accessMethodForm;
+ IndexAmRoutine *amRoutine;
bool amcanorder;
- RegProcedure amoptions;
+ amoptions_function amoptions;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
}
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);
/*
#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"
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;
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;
<