]> granicus.if.org Git - postgresql/commitdiff
Support CREATE ACCESS METHOD
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 24 Mar 2016 02:01:35 +0000 (23:01 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 24 Mar 2016 02:01:35 +0000 (23:01 -0300)
This enables external code to create access methods.  This is useful so
that extensions can add their own access methods which can be formally
tracked for dependencies, so that DROP operates correctly.  Also, having
explicit support makes pg_dump work correctly.

Currently only index AMs are supported, but we expect different types to
be added in the future.

Authors: Alexander Korotkov, Petr Jelínek
Reviewed-By: Teodor Sigaev, Petr Jelínek, Jim Nasby
Commitfest-URL: https://commitfest.postgresql.org/9/353/
Discussion: https://www.postgresql.org/message-id/CAPpHfdsXwZmojm6Dx+TJnpYk27kT4o7Ri6X_4OSWcByu1Rm+VA@mail.gmail.com

37 files changed:
doc/src/sgml/indexam.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/create_access_method.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_access_method.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
src/backend/access/index/amapi.c
src/backend/catalog/dependency.c
src/backend/catalog/objectaddress.c
src/backend/commands/Makefile
src/backend/commands/amcmds.c [new file with mode: 0644]
src/backend/commands/event_trigger.c
src/backend/commands/opclasscmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/backend/tcop/utility.c
src/backend/utils/adt/selfuncs.c
src/bin/pg_dump/common.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump_sort.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/pg_am.h
src/include/commands/defrem.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/include/utils/selfuncs.h
src/test/regress/expected/create_am.out [new file with mode: 0644]
src/test/regress/expected/object_address.out
src/test/regress/expected/sanity_check.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/create_am.sql [new file with mode: 0644]
src/test/regress/sql/object_address.sql

index 5f7befb858856fd75f6d598a62e9262f6ba681f7..b36889b856b275cd70d7b86204b824351c4e48a5 100644 (file)
    to insert an appropriate row for themselves.
   </para>
 
+  <para>
+   Index access access methods can be defined and dropped using
+   <xref linkend="sql-create-access-method"> and
+    <xref linkend="sql-drop-access-method"> SQL commands respectively.
+  </para>
+
   <para>
    An index access method handler function must be declared to accept a
    single argument of type <type>internal</> and to return the
index bf95453b6c6d597ad8c66c8ccb04c35b5f68fd24..77667bdebd1e68028658a0274da3ad32e89b4def 100644 (file)
@@ -52,6 +52,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY commit             SYSTEM "commit.sgml">
 <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
 <!ENTITY copyTable          SYSTEM "copy.sgml">
+<!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
 <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
 <!ENTITY createCast         SYSTEM "create_cast.sgml">
 <!ENTITY createCollation    SYSTEM "create_collation.sgml">
@@ -94,6 +95,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY delete             SYSTEM "delete.sgml">
 <!ENTITY discard            SYSTEM "discard.sgml">
 <!ENTITY do                 SYSTEM "do.sgml">
+<!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
 <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
 <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
 <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644 (file)
index 0000000..3c091f8
--- /dev/null
@@ -0,0 +1,120 @@
+<!--
+doc/src/sgml/ref/create_access_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-create-access-method">
+ <indexterm zone="sql-create-access-method">
+  <primary>CREATE ACCESS METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE ACCESS METHOD</refname>
+  <refpurpose>define a new access method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+    TYPE <replaceable class="parameter">access_method_type</replaceable>
+    HANDLER <replaceable class="parameter">handler_function</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE ACCESS METHOD</command> creates a new access method.
+  </para>
+
+  <para>
+   The access method name must be unique within the database.
+  </para>
+
+  <para>
+   Only superusers can define new access methods.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the access method to be created.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>access_method_type</literal></term>
+    <listitem>
+     <para>
+      This clause specifies type of access method to define.
+      Only <literal>INDEX</literal> is supported at present.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+    <listitem>
+     <para><replaceable class="parameter">handler_function</replaceable> is the
+      name of a previously registered function that will be called to
+      retrieve the struct which contains required parameters and functions
+      of access method to the core.  The handler function must take single
+      argument of type <type>internal</>, and its return type depends on the
+      type of access method; for <literal>INDEX</literal> access methods, it
+      must be <type>index_am_handler</type>.
+     </para>
+
+     <para>
+      See <xref linkend="index-api"> for index access methods API.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Create an access method <literal>heptree</> with
+   handler function <literal>heptree_handler</>:
+<programlisting>
+CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE ACCESS METHOD</command> is a
+   <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-drop-access-method"></member>
+   <member><xref linkend="sql-createopclass"></member>
+   <member><xref linkend="sql-createopfamily"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644 (file)
index 0000000..97ed77e
--- /dev/null
@@ -0,0 +1,112 @@
+<!--
+doc/src/sgml/ref/drop_access_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-drop-access-method">
+ <indexterm zone="sql-drop-access-method">
+  <primary>DROP ACCESS METHOD</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP ACCESS METHOD</refname>
+  <refpurpose>remove an access method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP ACCESS METHOD</command> removes an existing access method.
+   Only superusers can drop access methods.
+  </para>
+
+  <para>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the access method does not exist.
+      A notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the access method
+      (such as operator classes, operator families, indexes).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the access method if any objects depend on it.
+      This is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Drop the access method <literal>heptree</>:
+<programlisting>
+DROP ACCESS METHOD heptree;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>DROP ACCESS METHOD</command> is a
+   <productname>PostgreSQL</> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-create-access-method"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
index 03020dfec42ace90b4ad1695b66f7c475ad3ca46..8acdff1393fd2d1460f5e707c8ada4840bdf8620 100644 (file)
@@ -80,6 +80,7 @@
    &commit;
    &commitPrepared;
    &copyTable;
+   &createAccessMethod;
    &createAggregate;
    &createCast;
    &createCollation;
    &delete;
    &discard;
    &do;
+   &dropAccessMethod;
    &dropAggregate;
    &dropCast;
    &dropCollation;
index bda166a9ef8ccfdeba31a776d266b8e3cbe455c6..d347ebcba45f7d8fa71ed98a7a496a65cbdba834 100644 (file)
@@ -62,6 +62,13 @@ GetIndexAmRoutineByAmId(Oid amoid)
                         amoid);
        amform = (Form_pg_am) GETSTRUCT(tuple);
 
+       /* Check if it's index access method */
+       if (amform->amtype != AMTYPE_INDEX)
+               ereport(ERROR,
+                               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                errmsg("access method \"%s\" is not of type %s",
+                                               NameStr(amform->amname), "INDEX")));
+
        amhandler = amform->amhandler;
 
        /* Complain if handler OID is invalid */
index c48e37bf9a2424f892e1ddee71677ec66558ff29..17f9de1ff942f325fcb27aad45c1effe9efbc8f0 100644 (file)
@@ -20,6 +20,7 @@
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
 #include "catalog/pg_attrdef.h"
@@ -141,6 +142,7 @@ static const Oid object_classes[] = {
        OperatorRelationId,                     /* OCLASS_OPERATOR */
        OperatorClassRelationId,        /* OCLASS_OPCLASS */
        OperatorFamilyRelationId,       /* OCLASS_OPFAMILY */
+       AccessMethodRelationId,         /* OCLASS_AM */
        AccessMethodOperatorRelationId,         /* OCLASS_AMOP */
        AccessMethodProcedureRelationId,        /* OCLASS_AMPROC */
        RewriteRelationId,                      /* OCLASS_REWRITE */
@@ -1199,6 +1201,10 @@ doDeletion(const ObjectAddress *object, int flags)
                        RemoveOpFamilyById(object->objectId);
                        break;
 
+               case OCLASS_AM:
+                       RemoveAccessMethodById(object->objectId);
+                       break;
+
                case OCLASS_AMOP:
                        RemoveAmOpEntryById(object->objectId);
                        break;
@@ -2356,6 +2362,9 @@ getObjectClass(const ObjectAddress *object)
                case OperatorFamilyRelationId:
                        return OCLASS_OPFAMILY;
 
+               case AccessMethodRelationId:
+                       return OCLASS_AM;
+
                case AccessMethodOperatorRelationId:
                        return OCLASS_AMOP;
 
index d2aaa6ded92e6e2231d290c6a75884b1fe9cf3aa..cb3ba853f4ede38e341d6e8d4964e4b5030e915b 100644 (file)
@@ -109,6 +109,18 @@ typedef struct
 
 static const ObjectPropertyType ObjectProperty[] =
 {
+       {
+               AccessMethodRelationId,
+               AmOidIndexId,
+               AMOID,
+               AMNAME,
+               Anum_pg_am_amname,
+               InvalidAttrNumber,
+               InvalidAttrNumber,
+               InvalidAttrNumber,
+               -1,
+               true
+       },
        {
                CastRelationId,
                CastOidIndexId,
@@ -561,6 +573,10 @@ static const struct object_type_map
        {
                "operator family", OBJECT_OPFAMILY
        },
+       /* OCLASS_AM */
+       {
+               "access method", OBJECT_ACCESS_METHOD
+       },
        /* OCLASS_AMOP */
        {
                "operator of access method", OBJECT_AMOP
@@ -795,6 +811,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
                        case OBJECT_FDW:
                        case OBJECT_FOREIGN_SERVER:
                        case OBJECT_EVENT_TRIGGER:
+                       case OBJECT_ACCESS_METHOD:
                                address = get_object_address_unqualified(objtype,
                                                                                                                 objname, missing_ok);
                                break;
@@ -1019,6 +1036,9 @@ get_object_address_unqualified(ObjectType objtype,
 
                switch (objtype)
                {
+                       case OBJECT_ACCESS_METHOD:
+                               msg = gettext_noop("access method name cannot be qualified");
+                               break;
                        case OBJECT_DATABASE:
                                msg = gettext_noop("database name cannot be qualified");
                                break;
@@ -1061,6 +1081,11 @@ get_object_address_unqualified(ObjectType objtype,
        /* Translate name to OID. */
        switch (objtype)
        {
+               case OBJECT_ACCESS_METHOD:
+                       address.classId = AccessMethodRelationId;
+                       address.objectId = get_am_oid(name, missing_ok);
+                       address.objectSubId = 0;
+                       break;
                case OBJECT_DATABASE:
                        address.classId = DatabaseRelationId;
                        address.objectId = get_database_oid(name, missing_ok);
@@ -1489,7 +1514,7 @@ get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
        ObjectAddress address;
 
        /* XXX no missing_ok support here */
-       amoid = get_am_oid(strVal(linitial(objname)), false);
+       amoid = get_index_am_oid(strVal(linitial(objname)), false);
        objname = list_copy_tail(objname, 1);
 
        switch (objtype)
@@ -2179,6 +2204,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
                        break;
                case OBJECT_TSPARSER:
                case OBJECT_TSTEMPLATE:
+               case OBJECT_ACCESS_METHOD:
                        /* We treat these object types as being owned by superusers */
                        if (!superuser_arg(roleid))
                                ereport(ERROR,
@@ -3129,6 +3155,21 @@ getObjectDescription(const ObjectAddress *object)
                                break;
                        }
 
+               case OCLASS_AM:
+                       {
+                               HeapTuple       tup;
+
+                               tup = SearchSysCache1(AMOID,
+                                                                         ObjectIdGetDatum(object->objectId));
+                               if (!HeapTupleIsValid(tup))
+                                       elog(ERROR, "cache lookup failed for access method %u",
+                                                object->objectId);
+                               appendStringInfo(&buffer, _("access method %s"),
+                                                        NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+                               ReleaseSysCache(tup);
+                               break;
+                       }
+
                default:
                        appendStringInfo(&buffer, "unrecognized object %u %u %d",
                                                         object->classId,
@@ -3610,6 +3651,10 @@ getObjectTypeDescription(const ObjectAddress *object)
                        appendStringInfoString(&buffer, "transform");
                        break;
 
+               case OCLASS_AM:
+                       appendStringInfoString(&buffer, "access method");
+                       break;
+
                default:
                        appendStringInfo(&buffer, "unrecognized %u", object->classId);
                        break;
@@ -4566,6 +4611,20 @@ getObjectIdentityParts(const ObjectAddress *object,
                        }
                        break;
 
+               case OCLASS_AM:
+                       {
+                               char       *amname;
+
+                               amname = get_am_name(object->objectId);
+                               if (!amname)
+                                       elog(ERROR, "cache lookup failed for access method %u",
+                                                object->objectId);
+                               appendStringInfoString(&buffer, quote_identifier(amname));
+                               if (objname)
+                                       *objname = list_make1(amname);
+                       }
+                       break;
+
                default:
                        appendStringInfo(&buffer, "unrecognized object %u %u %d",
                                                         object->classId,
index b1ac704886feb047a8f6e89e4d3fbc9b1569fde9..6b3742c0a0847196b7696ee5cdb9698cd611ed86 100644 (file)
@@ -12,7 +12,7 @@ subdir = src/backend/commands
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
+OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
        collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
        dbcommands.o define.o discard.o dropcmds.o \
        event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644 (file)
index 0000000..7a93754
--- /dev/null
@@ -0,0 +1,271 @@
+/*-------------------------------------------------------------------------
+ *
+ * amcmds.c
+ *       Routines for SQL commands that manipulate access methods.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/commands/amcmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+static Oid     lookup_index_am_handler_func(List *handler_name, char amtype);
+static char *get_am_type_string(char amtype);
+
+
+/*
+ * CreateAcessMethod
+ *             Registers a new access method.
+ */
+ObjectAddress
+CreateAccessMethod(CreateAmStmt *stmt)
+{
+       Relation        rel;
+       ObjectAddress myself;
+       ObjectAddress referenced;
+       Oid                     amoid;
+       Oid                     amhandler;
+       bool            nulls[Natts_pg_am];
+       Datum           values[Natts_pg_am];
+       HeapTuple       tup;
+
+       rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+
+       /* Must be super user */
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("permission denied to create access method \"%s\"",
+                                               stmt->amname),
+                                errhint("Must be superuser to create an access method.")));
+
+       /* Check if name is used */
+       amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+       if (OidIsValid(amoid))
+       {
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("access method \"%s\" already exists",
+                                               stmt->amname)));
+       }
+
+       /*
+        * Get the handler function oid, verifying the AM type while at it.
+        */
+       amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+
+       /*
+        * Insert tuple into pg_am.
+        */
+       memset(values, 0, sizeof(values));
+       memset(nulls, false, sizeof(nulls));
+
+       values[Anum_pg_am_amname - 1] =
+               DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+       values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+       values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+
+       tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+       amoid = simple_heap_insert(rel, tup);
+       CatalogUpdateIndexes(rel, tup);
+       heap_freetuple(tup);
+
+       myself.classId = AccessMethodRelationId;
+       myself.objectId = amoid;
+       myself.objectSubId = 0;
+
+       /* Record dependency on handler function */
+       referenced.classId = ProcedureRelationId;
+       referenced.objectId = amhandler;
+       referenced.objectSubId = 0;
+
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+       recordDependencyOnCurrentExtension(&myself, false);
+
+       heap_close(rel, RowExclusiveLock);
+
+       return myself;
+}
+
+/*
+ * Guts of access method deletion.
+ */
+void
+RemoveAccessMethodById(Oid amOid)
+{
+       Relation        relation;
+       HeapTuple       tup;
+
+       if (!superuser())
+               ereport(ERROR,
+                               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                errmsg("must be superuser to drop access methods")));
+
+       relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+
+       tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+       if (!HeapTupleIsValid(tup))
+               elog(ERROR, "cache lookup failed for access method %u", amOid);
+
+       simple_heap_delete(relation, &tup->t_self);
+
+       ReleaseSysCache(tup);
+
+       heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * get_am_type_oid
+ *             Worker for various get_am_*_oid variants
+ *
+ * If missing_ok is false, throw an error if access method not found.  If
+ * true, just return InvalidOid.
+ *
+ * If amtype is not '\0', an error is raised if the AM found is not of the
+ * given type.
+ */
+static Oid
+get_am_type_oid(const char *amname, char amtype, bool missing_ok)
+{
+       HeapTuple       tup;
+       Oid                     oid = InvalidOid;
+
+       tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+       if (HeapTupleIsValid(tup))
+       {
+               Form_pg_am      amform = (Form_pg_am) GETSTRUCT(tup);
+
+               if (amtype != '\0' &&
+                       amform->amtype != amtype)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                                        errmsg("access method \"%s\" is not of type %s",
+                                                       NameStr(amform->amname),
+                                                       get_am_type_string(amtype))));
+
+               oid = HeapTupleGetOid(tup);
+               ReleaseSysCache(tup);
+       }
+
+       if (!OidIsValid(oid) && !missing_ok)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                errmsg("access method \"%s\" does not exist", amname)));
+       return oid;
+}
+
+/*
+ * get_index_am_oid - given an access method name, look up its OID
+ *             and verify it corresponds to an index AM.
+ */
+Oid
+get_index_am_oid(const char *amname, bool missing_ok)
+{
+       return get_am_type_oid(amname, AMTYPE_INDEX, missing_ok);
+}
+
+/*
+ * get_am_oid - given an access method name, look up its OID.
+ *             The type is not checked.
+ */
+Oid
+get_am_oid(const char *amname, bool missing_ok)
+{
+       return get_am_type_oid(amname, '\0', missing_ok);
+}
+
+/*
+ * get_am_name - given an access method OID name and type, look up its name.
+ */
+char *
+get_am_name(Oid amOid)
+{
+       HeapTuple       tup;
+       char       *result = NULL;
+
+       tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+       if (HeapTupleIsValid(tup))
+       {
+               Form_pg_am      amform = (Form_pg_am) GETSTRUCT(tup);
+
+               result = pstrdup(NameStr(amform->amname));
+               ReleaseSysCache(tup);
+       }
+       return result;
+}
+
+/*
+ * Convert single charater access method type into string for error reporting.
+ */
+static char *
+get_am_type_string(char amtype)
+{
+       switch (amtype)
+       {
+               case AMTYPE_INDEX:
+                       return "INDEX";
+               default:
+                       /* shouldn't happen */
+                       elog(ERROR, "invalid access method type '%c'", amtype);
+       }
+}
+
+/*
+ * Convert a handler function name to an Oid.  If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+lookup_index_am_handler_func(List *handler_name, char amtype)
+{
+       Oid                     handlerOid;
+       static const Oid funcargtypes[1] = {INTERNALOID};
+
+       if (handler_name == NIL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("handler function is not specified")));
+
+       /* handlers have one argument of type internal */
+       handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+
+       /* check that handler has the correct return type */
+       switch (amtype)
+       {
+               case AMTYPE_INDEX:
+                       if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                                errmsg("function %s must return type \"%s\"",
+                                                               NameListToString(handler_name),
+                                                               "index_am_handler")));
+                       break;
+               default:
+                       elog(ERROR, "unrecognized access method type \"%c\"", amtype);
+       }
+
+       return handlerOid;
+}
index 9e32f8d09b1fb746ecb65ddf1aebb5d8258d8bb1..3f52ad836b4a2be1d95047f0949607d8cbbb283d 100644 (file)
@@ -86,6 +86,7 @@ typedef enum
 
 /* XXX merge this with ObjectTypeMap? */
 static event_trigger_support_data event_trigger_support[] = {
+       {"ACCESS METHOD", true},
        {"AGGREGATE", true},
        {"CAST", true},
        {"CONSTRAINT", true},
@@ -1078,6 +1079,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
                case OBJECT_EVENT_TRIGGER:
                        /* no support for event triggers on event triggers */
                        return false;
+               case OBJECT_ACCESS_METHOD:
                case OBJECT_AGGREGATE:
                case OBJECT_AMOP:
                case OBJECT_AMPROC:
@@ -1167,6 +1169,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
                case OCLASS_DEFACL:
                case OCLASS_EXTENSION:
                case OCLASS_POLICY:
+               case OCLASS_AM:
                        return true;
        }
 
index 8a661968cd9f3bd32ccb2dc65aeab267a1760cfb..ac559fc9b41c3f324b8339ec16f52dd8cd4fdf9c 100644 (file)
@@ -678,6 +678,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
        myself.objectId = opclassoid;
        myself.objectSubId = 0;
 
+       /* dependency on access method */
+       referenced.classId = AccessMethodRelationId;
+       referenced.objectId = amoid;
+       referenced.objectSubId = 0;
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
        /* dependency on namespace */
        referenced.classId = NamespaceRelationId;
        referenced.objectId = namespaceoid;
@@ -743,7 +749,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
                                           get_namespace_name(namespaceoid));
 
        /* Get access method OID, throwing an error if it doesn't exist. */
-       amoid = get_am_oid(stmt->amname, false);
+       amoid = get_index_am_oid(stmt->amname, false);
 
        /* XXX Should we make any privilege check against the AM? */
 
@@ -1663,21 +1669,6 @@ RemoveAmProcEntryById(Oid entryOid)
        heap_close(rel, RowExclusiveLock);
 }
 
-char *
-get_am_name(Oid amOid)
-{
-       HeapTuple       tup;
-       char       *result = NULL;
-
-       tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
-       if (HeapTupleIsValid(tup))
-       {
-               result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
-               ReleaseSysCache(tup);
-       }
-       return result;
-}
-
 /*
  * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
  *
@@ -1723,22 +1714,3 @@ IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
                                                get_am_name(opfmethod),
                                                get_namespace_name(opfnamespace))));
 }
-
-/*
- * get_am_oid - given an access method name, look up the OID
- *
- * If missing_ok is false, throw an error if access method not found.  If
- * true, just return InvalidOid.
- */
-Oid
-get_am_oid(const char *amname, bool missing_ok)
-{
-       Oid                     oid;
-
-       oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
-       if (!OidIsValid(oid) && !missing_ok)
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("access method \"%s\" does not exist", amname)));
-       return oid;
-}
index 6b5d1d6efce66e7949d18e176c0f148b932fc28b..6378db8bbea1006f447190c8f23987f36a8d02e0 100644 (file)
@@ -3831,6 +3831,18 @@ _copyCreateTransformStmt(const CreateTransformStmt *from)
        return newnode;
 }
 
+static CreateAmStmt *
+_copyCreateAmStmt(const CreateAmStmt *from)
+{
+       CreateAmStmt *newnode = makeNode(CreateAmStmt);
+
+       COPY_STRING_FIELD(amname);
+       COPY_NODE_FIELD(handler_name);
+       COPY_SCALAR_FIELD(amtype);
+
+       return newnode;
+}
+
 static CreateTrigStmt *
 _copyCreateTrigStmt(const CreateTrigStmt *from)
 {
@@ -4822,6 +4834,9 @@ copyObject(const void *from)
                case T_CreateTransformStmt:
                        retval = _copyCreateTransformStmt(from);
                        break;
+               case T_CreateAmStmt:
+                       retval = _copyCreateAmStmt(from);
+                       break;
                case T_CreateTrigStmt:
                        retval = _copyCreateTrigStmt(from);
                        break;
index 87eb859e05e81004bb4eafb917d738d4a6a96d7b..854c062d32ff858b9cc371f027ec6b10a69b753e 100644 (file)
@@ -1855,6 +1855,16 @@ _equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStm
        return true;
 }
 
+static bool
+_equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+{
+       COMPARE_STRING_FIELD(amname);
+       COMPARE_NODE_FIELD(handler_name);
+       COMPARE_SCALAR_FIELD(amtype);
+
+       return true;
+}
+
 static bool
 _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 {
@@ -3147,6 +3157,9 @@ equal(const void *a, const void *b)
                case T_CreateTransformStmt:
                        retval = _equalCreateTransformStmt(a, b);
                        break;
+               case T_CreateAmStmt:
+                       retval = _equalCreateAmStmt(a, b);
+                       break;
                case T_CreateTrigStmt:
                        retval = _equalCreateTrigStmt(a, b);
                        break;
index a74fb772e1496fcad618d16dcfca4491db618db9..12733528eb28b8181381ee5a618efad8bfa5e757 100644 (file)
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -263,7 +264,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
                DeallocateStmt PrepareStmt ExecuteStmt
                DropOwnedStmt ReassignOwnedStmt
                AlterTSConfigurationStmt AlterTSDictionaryStmt
-               CreateMatViewStmt RefreshMatViewStmt
+               CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
 
 %type <node>   select_no_parens select_with_parens select_clause
                                simple_select values_clause
@@ -604,7 +605,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
        LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
        LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
 
-       MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
+       MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
 
        NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
        NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
@@ -789,6 +790,7 @@ stmt :
                        | CommentStmt
                        | ConstraintsSetStmt
                        | CopyStmt
+                       | CreateAmStmt
                        | CreateAsStmt
                        | CreateAssertStmt
                        | CreateCastStmt
@@ -4706,6 +4708,23 @@ row_security_cmd:
                |       DELETE_P                { $$ = "delete"; }
                ;
 
+/*****************************************************************************
+ *
+ *             QUERY:
+ *             CREATE ACCESS METHOD name HANDLER handler_name
+ *
+ *****************************************************************************/
+
+CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+                               {
+                                       CreateAmStmt *n = makeNode(CreateAmStmt);
+                                       n->amname = $4;
+                                       n->handler_name = $8;
+                                       n->amtype = AMTYPE_INDEX;
+                                       $$ = (Node *) n;
+                               }
+               ;
+
 /*****************************************************************************
  *
  *             QUERIES :
@@ -5612,6 +5631,7 @@ drop_type:        TABLE                                                                   { $$ = OBJECT_TABLE; }
                        | MATERIALIZED VIEW                                             { $$ = OBJECT_MATVIEW; }
                        | INDEX                                                                 { $$ = OBJECT_INDEX; }
                        | FOREIGN TABLE                                                 { $$ = OBJECT_FOREIGN_TABLE; }
+                       | ACCESS METHOD                                                 { $$ = OBJECT_ACCESS_METHOD; }
                        | EVENT TRIGGER                                                 { $$ = OBJECT_EVENT_TRIGGER; }
                        | COLLATION                                                             { $$ = OBJECT_COLLATION; }
                        | CONVERSION_P                                                  { $$ = OBJECT_CONVERSION; }
@@ -13778,6 +13798,7 @@ unreserved_keyword:
                        | MATCH
                        | MATERIALIZED
                        | MAXVALUE
+                       | METHOD
                        | MINUTE_P
                        | MINVALUE
                        | MODE
index dc431c7de0c4581019986ba555852728e313bbde..65284941ed98e6367277fd68a6ac5a9a942ee2e7 100644 (file)
@@ -1709,7 +1709,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
                 * else dump and reload will produce a different index (breaking
                 * pg_upgrade in particular).
                 */
-               if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
+               if (index_rel->rd_rel->relam != get_index_am_oid(DEFAULT_INDEX_TYPE, false))
                        ereport(ERROR,
                                        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                         errmsg("index \"%s\" is not a btree", index_name),
index 045f7f06ee27ffa517bd4576f3bd3e5af8dac286..4d0aac979fcc799507609cb83438a0ca927aa99e 100644 (file)
@@ -1520,6 +1520,10 @@ ProcessUtilitySlow(Node *parsetree,
                                address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
                                break;
 
+                       case T_CreateAmStmt:
+                               address = CreateAccessMethod((CreateAmStmt *) parsetree);
+                               break;
+
                        default:
                                elog(ERROR, "unrecognized node type: %d",
                                         (int) nodeTag(parsetree));
@@ -2160,6 +2164,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_TRANSFORM:
                                        tag = "DROP TRANSFORM";
                                        break;
+                               case OBJECT_ACCESS_METHOD:
+                                       tag = "DROP ACCESS METHOD";
+                                       break;
                                default:
                                        tag = "???";
                        }
@@ -2256,6 +2263,9 @@ CreateCommandTag(Node *parsetree)
                                case OBJECT_COLLATION:
                                        tag = "CREATE COLLATION";
                                        break;
+                               case OBJECT_ACCESS_METHOD:
+                                       tag = "CREATE ACCESS METHOD";
+                                       break;
                                default:
                                        tag = "???";
                        }
@@ -2519,6 +2529,10 @@ CreateCommandTag(Node *parsetree)
                        tag = "ALTER POLICY";
                        break;
 
+               case T_CreateAmStmt:
+                       tag = "CREATE ACCESS METHOD";
+                       break;
+
                case T_PrepareStmt:
                        tag = "PREPARE";
                        break;
@@ -3076,6 +3090,10 @@ GetCommandLogLevel(Node *parsetree)
                        lev = LOGSTMT_DDL;
                        break;
 
+               case T_CreateAmStmt:
+                       lev = LOGSTMT_DDL;
+                       break;
+
                        /* already-planned queries */
                case T_PlannedStmt:
                        {
index d396ef142f9563ce5f620152983a873c082439c8..b2c57e87a5e5a0a8b3248db979506f9cb9290e3d 100644 (file)
@@ -6013,21 +6013,7 @@ string_to_bytea_const(const char *str, size_t str_len)
  *-------------------------------------------------------------------------
  */
 
-/*
- * deconstruct_indexquals is a simple function to examine the indexquals
- * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
- * structs, one per qual expression.
- */
-typedef struct
-{
-       RestrictInfo *rinfo;            /* the indexqual itself */
-       int                     indexcol;               /* zero-based index column number */
-       bool            varonleft;              /* true if index column is on left of qual */
-       Oid                     clause_op;              /* qual's operator OID, if relevant */
-       Node       *other_operand;      /* non-index operand of qual's operator */
-} IndexQualInfo;
-
-static List *
+List *
 deconstruct_indexquals(IndexPath *path)
 {
        List       *result = NIL;
@@ -6177,35 +6163,7 @@ orderby_operands_eval_cost(PlannerInfo *root, IndexPath *path)
        return qual_arg_cost;
 }
 
-/*
- * genericcostestimate is a general-purpose estimator that can be used for
- * most index types.  In some cases we use genericcostestimate as the base
- * code and then incorporate additional index-type-specific knowledge in
- * the type-specific calling function.  To avoid code duplication, we make
- * genericcostestimate return a number of intermediate values as well as
- * its preliminary estimates of the output cost values.  The GenericCosts
- * struct includes all these values.
- *
- * Callers should initialize all fields of GenericCosts to zero.  In addition,
- * they can set numIndexTuples to some positive value if they have a better
- * than default way of estimating the number of leaf index tuples visited.
- */
-typedef struct
-{
-       /* These are the values the cost estimator must return to the planner */
-       Cost            indexStartupCost;               /* index-related startup cost */
-       Cost            indexTotalCost; /* total index-related scan cost */
-       Selectivity indexSelectivity;           /* selectivity of index */
-       double          indexCorrelation;               /* order correlation of index */
-
-       /* Intermediate values we obtain along the way */
-       double          numIndexPages;  /* number of leaf pages visited */
-       double          numIndexTuples; /* number of leaf tuples visited */
-       double          spc_random_page_cost;   /* relevant random_page_cost value */
-       double          num_sa_scans;   /* # indexscans from ScalarArrayOps */
-} GenericCosts;
-
-static void
+void
 genericcostestimate(PlannerInfo *root,
                                        IndexPath *path,
                                        double loop_count,
index f798b15e3c47ee6edf1902f9f6c7701c655ea43b..1acd91ab4449208185aa6924975cbcbbf43607f0 100644 (file)
@@ -98,6 +98,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
        int                     numProcLangs;
        int                     numCasts;
        int                     numTransforms;
+       int                     numAccessMethods;
        int                     numOpclasses;
        int                     numOpfamilies;
        int                     numConversions;
@@ -168,6 +169,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
        oprinfo = getOperators(fout, &numOperators);
        oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
 
+       if (g_verbose)
+               write_msg(NULL, "reading user-defined access methods\n");
+       getAccessMethods(fout, &numAccessMethods);
+
        if (g_verbose)
                write_msg(NULL, "reading user-defined operator classes\n");
        getOpclasses(fout, &numOpclasses);
index 64c2673f9a598d748b611e6781a964c483ea6515..b3ef201a3ae2e45b78a493d8e6922284c54f8e73 100644 (file)
@@ -45,6 +45,7 @@
 #include "access/attnum.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_default_acl.h"
@@ -173,6 +174,7 @@ static void dumpFunc(Archive *fout, FuncInfo *finfo);
 static void dumpCast(Archive *fout, CastInfo *cast);
 static void dumpTransform(Archive *fout, TransformInfo *transform);
 static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
 static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
 static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
 static void dumpCollation(Archive *fout, CollInfo *convinfo);
@@ -1468,6 +1470,26 @@ selectDumpableProcLang(ProcLangInfo *plang, DumpOptions *dopt)
                plang->dobj.dump = dopt->include_everything;
 }
 
+/*
+ * selectDumpableAccessMethod: policy-setting subroutine
+ *             Mark an access method as to be dumped or not
+ *
+ * Access methods do not belong to any particular namespace.  To identify
+ * built-in access methods, we must resort to checking whether the
+ * method's OID is in the range reserved for initdb.
+ */
+static void
+selectDumpableAccessMethod(AccessMethodInfo *method, DumpOptions *dopt)
+{
+       if (checkExtensionMembership(&method->dobj, dopt))
+               return;                                 /* extension membership overrides all else */
+
+       if (method->dobj.catId.oid < (Oid) FirstNormalObjectId)
+               method->dobj.dump = false;
+       else
+               method->dobj.dump = dopt->include_everything;
+}
+
 /*
  * selectDumpableExtension: policy-setting subroutine
  *             Mark an extension as to be dumped or not
@@ -4100,6 +4122,84 @@ getConversions(Archive *fout, int *numConversions)
        return convinfo;
 }
 
+/*
+ * getAccessMethods:
+ *       read all user-defined access methods in the system catalogs and return
+ *       them in the AccessMethodInfo* structure
+ *
+ *     numAccessMethods is set to the number of access methods read in
+ */
+AccessMethodInfo *
+getAccessMethods(Archive *fout, int *numAccessMethods)
+{
+       DumpOptions *dopt = fout->dopt;
+       PGresult   *res;
+       int                     ntups;
+       int                     i;
+       PQExpBuffer query;
+       AccessMethodInfo *aminfo;
+       int                     i_tableoid;
+       int                     i_oid;
+       int                     i_amname;
+       int                     i_amhandler;
+       int                     i_amtype;
+
+       /* Before 9.6, there are no user-defined access methods */
+       if (fout->remoteVersion < 90600)
+       {
+               *numAccessMethods = 0;
+               return NULL;
+       }
+
+       query = createPQExpBuffer();
+
+       /* Make sure we are in proper schema */
+       selectSourceSchema(fout, "pg_catalog");
+
+       /*
+        * Select only user-defined access methods assuming all built-in access
+        * methods have oid < 10000.
+        */
+       appendPQExpBuffer(query, "SELECT tableoid, oid, amname, amtype, "
+                                         "amhandler::pg_catalog.regproc AS amhandler "
+                                         "FROM pg_am");
+
+       res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+       ntups = PQntuples(res);
+       *numAccessMethods = ntups;
+
+       aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+
+       i_tableoid = PQfnumber(res, "tableoid");
+       i_oid = PQfnumber(res, "oid");
+       i_amname = PQfnumber(res, "amname");
+       i_amhandler = PQfnumber(res, "amhandler");
+       i_amtype = PQfnumber(res, "amtype");
+
+       for (i = 0; i < ntups; i++)
+       {
+               aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+               aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+               aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+               AssignDumpId(&aminfo[i].dobj);
+               aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+               aminfo[i].dobj.namespace = NULL;
+               aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+               aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
+
+               /* Decide whether we want to dump it */
+               selectDumpableAccessMethod(&(aminfo[i]), dopt);
+       }
+
+       PQclear(res);
+
+       destroyPQExpBuffer(query);
+
+       return aminfo;
+}
+
+
 /*
  * getOpclasses:
  *       read all opclasses in the system catalogs and return them in the
@@ -8408,6 +8508,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
                case DO_OPERATOR:
                        dumpOpr(fout, (OprInfo *) dobj);
                        break;
+               case DO_ACCESS_METHOD:
+                       dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+                       break;
                case DO_OPCLASS:
                        dumpOpclass(fout, (OpclassInfo *) dobj);
                        break;
@@ -11446,6 +11549,74 @@ convertTSFunction(Archive *fout, Oid funcOid)
        return result;
 }
 
+/*
+ * dumpAccessMethod
+ *       write out a single access method definition
+ */
+static void
+dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+{
+       DumpOptions *dopt = fout->dopt;
+       PQExpBuffer q;
+       PQExpBuffer delq;
+       PQExpBuffer labelq;
+       char       *qamname;
+
+       /* Skip if not to be dumped */
+       if (!aminfo->dobj.dump || dopt->dataOnly)
+               return;
+
+       q = createPQExpBuffer();
+       delq = createPQExpBuffer();
+       labelq = createPQExpBuffer();
+
+       qamname = pg_strdup(fmtId(aminfo->dobj.name));
+
+       appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
+
+       switch (aminfo->amtype)
+       {
+               case AMTYPE_INDEX:
+                       appendPQExpBuffer(q, "TYPE INDEX ");
+                       break;
+               default:
+                       write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
+                                         aminfo->amtype, qamname);
+                       destroyPQExpBuffer(q);
+                       destroyPQExpBuffer(delq);
+                       destroyPQExpBuffer(labelq);
+                       return;
+       }
+
+       appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
+
+       appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+                                         qamname);
+
+       appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+                                         qamname);
+
+       ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+                                aminfo->dobj.name,
+                                NULL,
+                                NULL,
+                                "",
+                                false, "ACCESS METHOD", SECTION_PRE_DATA,
+                                q->data, delq->data, NULL,
+                                NULL, 0,
+                                NULL, NULL);
+
+       /* Dump Access Method Comments */
+       dumpComment(fout, labelq->data,
+                               NULL, "",
+                               aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+
+       free(qamname);
+
+       destroyPQExpBuffer(q);
+       destroyPQExpBuffer(delq);
+       destroyPQExpBuffer(labelq);
+}
 
 /*
  * dumpOpclass
@@ -16227,6 +16398,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
                        case DO_FUNC:
                        case DO_AGG:
                        case DO_OPERATOR:
+                       case DO_ACCESS_METHOD:
                        case DO_OPCLASS:
                        case DO_OPFAMILY:
                        case DO_COLLATION:
index 9a1d8f863cc3d3ec83c9fe7a0d83ef46bbcf3ff1..66e693183ae40ef2d7806eb517745e82fcb1db0a 100644 (file)
@@ -48,6 +48,7 @@ typedef enum
        DO_FUNC,
        DO_AGG,
        DO_OPERATOR,
+       DO_ACCESS_METHOD,
        DO_OPCLASS,
        DO_OPFAMILY,
        DO_COLLATION,
@@ -167,6 +168,13 @@ typedef struct _oprInfo
        Oid                     oprcode;
 } OprInfo;
 
+typedef struct _accessMethodInfo
+{
+       DumpableObject dobj;
+       char            amtype;
+       char       *amhandler;
+} AccessMethodInfo;
+
 typedef struct _opclassInfo
 {
        DumpableObject dobj;
@@ -548,6 +556,7 @@ extern TypeInfo *getTypes(Archive *fout, int *numTypes);
 extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
 extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
 extern OprInfo *getOperators(Archive *fout, int *numOperators);
+extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
 extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
 extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
 extern CollInfo *getCollations(Archive *fout, int *numCollations);
index 78ff59c342998295ceaee04f1a074b90787842c7..36de6b625739c8da40dc5e5df61aa2aaa14259dc 100644 (file)
@@ -28,8 +28,8 @@ static const char *modulename = gettext_noop("sorter");
  * by OID.  (This is a relatively crude hack to provide semi-reasonable
  * behavior for old databases without full dependency info.)  Note: collations,
  * extensions, text search, foreign-data, materialized view, event trigger,
- * policies, transforms, and default ACL objects can't really happen here, so the rather
- * bogus priorities for them don't matter.
+ * policies, transforms, access methods and default ACL objects can't really
+ * happen here, so the rather bogus priorities for them don't matter.
  *
  * NOTE: object-type priorities must match the section assignments made in
  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
@@ -45,6 +45,7 @@ static const int oldObjectTypePriority[] =
        2,                                                      /* DO_FUNC */
        3,                                                      /* DO_AGG */
        3,                                                      /* DO_OPERATOR */
+       3,                                                      /* DO_ACCESS_METHOD */
        4,                                                      /* DO_OPCLASS */
        4,                                                      /* DO_OPFAMILY */
        4,                                                      /* DO_COLLATION */
@@ -95,6 +96,7 @@ static const int newObjectTypePriority[] =
        6,                                                      /* DO_FUNC */
        7,                                                      /* DO_AGG */
        8,                                                      /* DO_OPERATOR */
+       8,                                                      /* DO_ACCESS_METHOD */
        9,                                                      /* DO_OPCLASS */
        9,                                                      /* DO_OPFAMILY */
        3,                                                      /* DO_COLLATION */
@@ -1329,6 +1331,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
                                         "OPERATOR %s  (ID %d OID %u)",
                                         obj->name, obj->dumpId, obj->catId.oid);
                        return;
+               case DO_ACCESS_METHOD:
+                       snprintf(buf, bufsize,
+                                        "ACCESS METHOD %s  (ID %d OID %u)",
+                                        obj->name, obj->dumpId, obj->catId.oid);
+                       return;
                case DO_OPCLASS:
                        snprintf(buf, bufsize,
                                         "OPERATOR CLASS %s  (ID %d OID %u)",
index 3568cb27e814705045424fa17c8866f465a69967..b690dc6b34f466e84f69026e928e69d48729ec9b 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201603211
+#define CATALOG_VERSION_NO     201603231
 
 #endif
index 049bf9f5a2e90939957d9f8ddc23ba70e1937c77..d41abc4e48091891f32e56e34bbb269629ca8fac 100644 (file)
@@ -134,6 +134,7 @@ typedef enum ObjectClass
        OCLASS_OPERATOR,                        /* pg_operator */
        OCLASS_OPCLASS,                         /* pg_opclass */
        OCLASS_OPFAMILY,                        /* pg_opfamily */
+       OCLASS_AM,                                      /* pg_am */
        OCLASS_AMOP,                            /* pg_amop */
        OCLASS_AMPROC,                          /* pg_amproc */
        OCLASS_REWRITE,                         /* pg_rewrite */
index f801c3ee5779a8675bed504c7936e858dd2fe1bc..11169237991e8a941813bbc5ea5f9f03c7e31291 100644 (file)
@@ -35,6 +35,7 @@ CATALOG(pg_am,2601)
 {
        NameData        amname;                 /* access method name */
        regproc         amhandler;              /* handler function */
+       char            amtype;                 /* see AMTYPE_xxx constants below */
 } FormData_pg_am;
 
 /* ----------------
@@ -48,31 +49,38 @@ typedef FormData_pg_am *Form_pg_am;
  *             compiler constants for pg_am
  * ----------------
  */
-#define Natts_pg_am                                            2
+#define Natts_pg_am                                            3
 #define Anum_pg_am_amname                              1
 #define Anum_pg_am_amhandler                   2
+#define Anum_pg_am_amtype                              3
+
+/* ----------------
+ *             compiler constant for amtype
+ * ----------------
+ */
+#define AMTYPE_INDEX                                   'i'             /* index access method */
 
 /* ----------------
  *             initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree         bthandler ));
+DATA(insert OID = 403 (  btree         bthandler       i ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash          hashhandler ));
+DATA(insert OID = 405 (  hash          hashhandler ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist          gisthandler ));
+DATA(insert OID = 783 (  gist          gisthandler ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin          ginhandler ));
+DATA(insert OID = 2742 (  gin          ginhandler      i ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
-DATA(insert OID = 4000 (  spgist       spghandler ));
+DATA(insert OID = 4000 (  spgist       spghandler      i ));
 DESCR("SP-GiST index access method");
 #define SPGIST_AM_OID 4000
-DATA(insert OID = 3580 (  brin         brinhandler ));
+DATA(insert OID = 3580 (  brin         brinhandler ));
 DESCR("block range index (BRIN) access method");
 #define BRIN_AM_OID 3580
 
index 54f67e9eea62a1d75c94f6c5fcd5ddbcddd15b1c..b064eb4836a74b57cd7a07a26738781b09285762 100644 (file)
@@ -91,8 +91,6 @@ extern void IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
                                                  Oid opcnamespace);
 extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
                                                   Oid opfnamespace);
-extern Oid     get_am_oid(const char *amname, bool missing_ok);
-extern char *get_am_name(Oid amOid);
 extern Oid     get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
 extern Oid     get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
 
@@ -137,6 +135,13 @@ extern Datum transformGenericOptions(Oid catalogId,
                                                List *options,
                                                Oid fdwvalidator);
 
+/* commands/amcmds.c */
+extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+extern void RemoveAccessMethodById(Oid amOid);
+extern Oid     get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid     get_am_oid(const char *amname, bool missing_ok);
+extern char *get_am_name(Oid amOid);
+
 /* support routines in commands/define.c */
 
 extern char *defGetString(DefElem *def);
index 42c958258b698a952bba43c3f99ff68e1268a4c0..734df771eb2bb0f76b3fd9f3d22a5239f2154859 100644 (file)
@@ -402,6 +402,7 @@ typedef enum NodeTag
        T_CreatePolicyStmt,
        T_AlterPolicyStmt,
        T_CreateTransformStmt,
+       T_CreateAmStmt,
 
        /*
         * TAGS FOR PARSE TREE NODES (parsenodes.h)
index 2fd06295e5c230783d1ecd0a2ff4227455fa51b5..8b958b422cbcb9f13f4ac880207bd79e0d8dfd02 100644 (file)
@@ -1379,6 +1379,7 @@ typedef struct SetOperationStmt
 
 typedef enum ObjectType
 {
+       OBJECT_ACCESS_METHOD,
        OBJECT_AGGREGATE,
        OBJECT_AMOP,
        OBJECT_AMPROC,
@@ -2070,6 +2071,18 @@ typedef struct AlterPolicyStmt
        Node       *with_check;         /* the policy's WITH CHECK condition. */
 } AlterPolicyStmt;
 
+/*----------------------
+ *             Create ACCESS METHOD Statement
+ *----------------------
+ */
+typedef struct CreateAmStmt
+{
+       NodeTag         type;
+       char       *amname;                     /* access method name */
+       List       *handler_name;       /* handler function name */
+       char            amtype;                 /* type of access method */
+} CreateAmStmt;
+
 /* ----------------------
  *             Create TRIGGER Statement
  * ----------------------
index 6e1e82027cbbcf4cd18abc451d3113b595e4dd6e..7de3404aa227f01b64cd4e5bb21dca281a3bf77a 100644 (file)
@@ -239,6 +239,7 @@ PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
 PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
 PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
 PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
 PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
 PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
index 06fbca719b56563cb4864ccd946508e8355f1de7..8e0d317468748d1ecb243206e51610dace2549c3 100644 (file)
@@ -95,6 +95,48 @@ typedef enum
        Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
 } Pattern_Prefix_Status;
 
+/*
+ * deconstruct_indexquals is a simple function to examine the indexquals
+ * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+ * structs, one per qual expression.
+ */
+typedef struct
+{
+       RestrictInfo *rinfo;            /* the indexqual itself */
+       int                     indexcol;               /* zero-based index column number */
+       bool            varonleft;              /* true if index column is on left of qual */
+       Oid                     clause_op;              /* qual's operator OID, if relevant */
+       Node       *other_operand;      /* non-index operand of qual's operator */
+} IndexQualInfo;
+
+/*
+ * genericcostestimate is a general-purpose estimator that can be used for
+ * most index types.  In some cases we use genericcostestimate as the base
+ * code and then incorporate additional index-type-specific knowledge in
+ * the type-specific calling function.  To avoid code duplication, we make
+ * genericcostestimate return a number of intermediate values as well as
+ * its preliminary estimates of the output cost values.  The GenericCosts
+ * struct includes all these values.
+ *
+ * Callers should initialize all fields of GenericCosts to zero.  In addition,
+ * they can set numIndexTuples to some positive value if they have a better
+ * than default way of estimating the number of leaf index tuples visited.
+ */
+typedef struct
+{
+       /* These are the values the cost estimator must return to the planner */
+       Cost            indexStartupCost;               /* index-related startup cost */
+       Cost            indexTotalCost; /* total index-related scan cost */
+       Selectivity indexSelectivity;           /* selectivity of index */
+       double          indexCorrelation;               /* order correlation of index */
+
+       /* Intermediate values we obtain along the way */
+       double          numIndexPages;  /* number of leaf pages visited */
+       double          numIndexTuples; /* number of leaf tuples visited */
+       double          spc_random_page_cost;   /* relevant random_page_cost value */
+       double          num_sa_scans;   /* # indexscans from ScalarArrayOps */
+} GenericCosts;
+
 /* Hooks for plugins to get control when we ask for stats */
 typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
                                                                                                                  RangeTblEntry *rte,
@@ -191,6 +233,12 @@ extern double estimate_num_groups(PlannerInfo *root, List *groupExprs,
 extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
                                                 double nbuckets);
 
+extern List *deconstruct_indexquals(IndexPath *path);
+extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+                                       double loop_count,
+                                       List *qinfos,
+                                       GenericCosts *costs);
+
 /* Functions in array_selfuncs.c */
 
 extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644 (file)
index 0000000..47d6024
--- /dev/null
@@ -0,0 +1,108 @@
+--
+-- Create access method tests
+--
+-- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+-- Drop old index on fast_emp4000
+DROP INDEX grect2ind;
+-- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ERROR:  data type box has no default operator class for access method "gist2"
+HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+-- Make operator class for boxes using gist2
+CREATE OPERATOR CLASS box_ops DEFAULT
+       FOR TYPE box USING gist2 AS
+       OPERATOR 1      <<,
+       OPERATOR 2      &<,
+       OPERATOR 3      &&,
+       OPERATOR 4      &>,
+       OPERATOR 5      >>,
+       OPERATOR 6      ~=,
+       OPERATOR 7      @>,
+       OPERATOR 8      <@,
+       OPERATOR 9      &<|,
+       OPERATOR 10     <<|,
+       OPERATOR 11     |>>,
+       OPERATOR 12     |&>,
+       OPERATOR 13     ~,
+       OPERATOR 14     @,
+       FUNCTION 1      gist_box_consistent(internal, box, smallint, oid, internal),
+       FUNCTION 2      gist_box_union(internal, internal),
+       FUNCTION 3      gist_box_compress(internal),
+       FUNCTION 4      gist_box_decompress(internal),
+       FUNCTION 5      gist_box_penalty(internal, internal, internal),
+       FUNCTION 6      gist_box_picksplit(internal, internal),
+       FUNCTION 7      gist_box_same(box, box, internal),
+       FUNCTION 9      gist_box_fetch(internal);
+-- Create gist2 index on fast_emp4000
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+-- Now check the results from plain indexscan
+SET enable_seqscan = OFF;
+SET enable_indexscan = ON;
+SET enable_bitmapscan = OFF;
+EXPLAIN (COSTS OFF)
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+                           QUERY PLAN                           
+----------------------------------------------------------------
+ Sort
+   Sort Key: ((home_base[0])[0])
+   ->  Index Only Scan using grect2ind on fast_emp4000
+         Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+(4 rows)
+
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+       home_base       
+-----------------------
+ (337,455),(240,359)
+ (1444,403),(1346,344)
+(2 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                         QUERY PLAN                          
+-------------------------------------------------------------
+ Aggregate
+   ->  Index Only Scan using grect2ind on fast_emp4000
+         Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+(3 rows)
+
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ count 
+-------
+     2
+(1 row)
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                      QUERY PLAN                       
+-------------------------------------------------------
+ Aggregate
+   ->  Index Only Scan using grect2ind on fast_emp4000
+         Index Cond: (home_base IS NULL)
+(3 rows)
+
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ count 
+-------
+   278
+(1 row)
+
+-- Try to drop access method: fail because of depending objects
+DROP ACCESS METHOD gist2;
+ERROR:  cannot drop access method gist2 because other objects depend on it
+DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+index grect2ind depends on operator class box_ops for access method gist2
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+-- Drop access method cascade
+DROP ACCESS METHOD gist2 CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to operator class box_ops for access method gist2
+drop cascades to index grect2ind
+-- Reset optimizer options
+RESET enable_seqscan;
+RESET enable_indexscan;
+RESET enable_bitmapscan;
index 75751bebeff157d21297592d201b73fd19b2739b..5310c956febef25f2db578ba91a536d38c47282c 100644 (file)
@@ -325,6 +325,10 @@ SELECT pg_get_object_address('event trigger', '{one}', '{}');
 ERROR:  event trigger "one" does not exist
 SELECT pg_get_object_address('event trigger', '{one,two}', '{}');
 ERROR:  event trigger name cannot be qualified
+SELECT pg_get_object_address('access method', '{one}', '{}');
+ERROR:  access method "one" does not exist
+SELECT pg_get_object_address('access method', '{one,two}', '{}');
+ERROR:  access method name cannot be qualified
 -- test successful cases
 WITH objects (type, name, args) AS (VALUES
                                ('table', '{addr_nsp, gentable}'::text[], '{}'::text[]),
@@ -373,7 +377,8 @@ WITH objects (type, name, args) AS (VALUES
                                -- extension
                                -- event trigger
                                ('policy', '{addr_nsp, gentable, genpol}', '{}'),
-                               ('transform', '{int}', '{sql}')
+                               ('transform', '{int}', '{sql}'),
+                               ('access method', '{btree}', '{}')
         )
 SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
        -- test roundtrip through pg_identify_object_as_address
@@ -405,6 +410,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  server                    |            | addr_fserv        | addr_fserv                                                           | t
  user mapping              |            |                   | regtest_addr_user on server integer                                  | t
  foreign-data wrapper      |            | addr_fdw          | addr_fdw                                                             | t
+ access method             |            | btree             | btree                                                                | t
  operator of access method |            |                   | operator 1 (integer, integer) of pg_catalog.integer_ops USING btree  | t
  function of access method |            |                   | function 2 (integer, integer) of pg_catalog.integer_ops USING btree  | t
  default value             |            |                   | for addr_nsp.gentable.b                                              | t
@@ -426,7 +432,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
  text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
  text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
-(41 rows)
+(42 rows)
 
 ---
 --- Cleanup resources
index eb0bc88ef1fb27daee22dd4a2de684df35507417..2c5be4bae4bb0fa80db1b7d151e647023aa5a7f8 100644 (file)
@@ -44,7 +44,7 @@ e_star|f
 emp|f
 equipment_r|f
 f_star|f
-fast_emp4000|t
+fast_emp4000|f
 float4_tbl|f
 float8_tbl|f
 func_index_heap|t
index bec03165a73213c5e916b4eb02febe96ff67d0b0..8be4b831a1efc8dd7cbf7f0edde19a333d7ee80b 100644 (file)
@@ -60,7 +60,7 @@ test: create_index create_view
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
+test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
 
 # ----------
 # sanity_check does a vacuum, affecting the sort order of SELECT *
index 7e9b319b0f7091ccd865df2f83ed21d1a76d8a41..1de3da8bac0a985bcaddd354cf3dee487e2f5fbd 100644 (file)
@@ -75,6 +75,7 @@ test: drop_if_exists
 test: updatable_views
 test: rolenames
 test: roleattributes
+test: create_am
 test: sanity_check
 test: errors
 test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644 (file)
index 0000000..e2051c5
--- /dev/null
@@ -0,0 +1,73 @@
+--
+-- Create access method tests
+--
+
+-- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+
+-- Drop old index on fast_emp4000
+DROP INDEX grect2ind;
+
+-- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+
+-- Make operator class for boxes using gist2
+CREATE OPERATOR CLASS box_ops DEFAULT
+       FOR TYPE box USING gist2 AS
+       OPERATOR 1      <<,
+       OPERATOR 2      &<,
+       OPERATOR 3      &&,
+       OPERATOR 4      &>,
+       OPERATOR 5      >>,
+       OPERATOR 6      ~=,
+       OPERATOR 7      @>,
+       OPERATOR 8      <@,
+       OPERATOR 9      &<|,
+       OPERATOR 10     <<|,
+       OPERATOR 11     |>>,
+       OPERATOR 12     |&>,
+       OPERATOR 13     ~,
+       OPERATOR 14     @,
+       FUNCTION 1      gist_box_consistent(internal, box, smallint, oid, internal),
+       FUNCTION 2      gist_box_union(internal, internal),
+       FUNCTION 3      gist_box_compress(internal),
+       FUNCTION 4      gist_box_decompress(internal),
+       FUNCTION 5      gist_box_penalty(internal, internal, internal),
+       FUNCTION 6      gist_box_picksplit(internal, internal),
+       FUNCTION 7      gist_box_same(box, box, internal),
+       FUNCTION 9      gist_box_fetch(internal);
+
+-- Create gist2 index on fast_emp4000
+CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+
+-- Now check the results from plain indexscan
+SET enable_seqscan = OFF;
+SET enable_indexscan = ON;
+SET enable_bitmapscan = OFF;
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+SELECT * FROM fast_emp4000
+    WHERE home_base @ '(200,200),(2000,1000)'::box
+    ORDER BY (home_base[0])[0];
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+
+EXPLAIN (COSTS OFF)
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+
+-- Try to drop access method: fail because of depending objects
+DROP ACCESS METHOD gist2;
+
+-- Drop access method cascade
+DROP ACCESS METHOD gist2 CASCADE;
+
+-- Reset optimizer options
+RESET enable_seqscan;
+RESET enable_indexscan;
+RESET enable_bitmapscan;
index 68e7cb0eb3a55c66dd6b39b3f46afffa17da95de..b5662349bcd01250a421f65c2a809ec5a862ce7e 100644 (file)
@@ -117,6 +117,8 @@ SELECT pg_get_object_address('extension', '{one}', '{}');
 SELECT pg_get_object_address('extension', '{one,two}', '{}');
 SELECT pg_get_object_address('event trigger', '{one}', '{}');
 SELECT pg_get_object_address('event trigger', '{one,two}', '{}');
+SELECT pg_get_object_address('access method', '{one}', '{}');
+SELECT pg_get_object_address('access method', '{one,two}', '{}');
 
 -- test successful cases
 WITH objects (type, name, args) AS (VALUES
@@ -166,7 +168,8 @@ WITH objects (type, name, args) AS (VALUES
                                -- extension
                                -- event trigger
                                ('policy', '{addr_nsp, gentable, genpol}', '{}'),
-                               ('transform', '{int}', '{sql}')
+                               ('transform', '{int}', '{sql}'),
+                               ('access method', '{btree}', '{}')
         )
 SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
        -- test roundtrip through pg_identify_object_as_address