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
<!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">
<!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">
--- /dev/null
+<!--
+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>
--- /dev/null
+<!--
+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>
&commit;
&commitPrepared;
©Table;
+ &createAccessMethod;
&createAggregate;
&createCast;
&createCollation;
&delete;
&discard;
&do;
+ &dropAccessMethod;
&dropAggregate;
&dropCast;
&dropCollation;
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 */
#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"
OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
+ AccessMethodRelationId, /* OCLASS_AM */
AccessMethodOperatorRelationId, /* OCLASS_AMOP */
AccessMethodProcedureRelationId, /* OCLASS_AMPROC */
RewriteRelationId, /* OCLASS_REWRITE */
RemoveOpFamilyById(object->objectId);
break;
+ case OCLASS_AM:
+ RemoveAccessMethodById(object->objectId);
+ break;
+
case OCLASS_AMOP:
RemoveAmOpEntryById(object->objectId);
break;
case OperatorFamilyRelationId:
return OCLASS_OPFAMILY;
+ case AccessMethodRelationId:
+ return OCLASS_AM;
+
case AccessMethodOperatorRelationId:
return OCLASS_AMOP;
static const ObjectPropertyType ObjectProperty[] =
{
+ {
+ AccessMethodRelationId,
+ AmOidIndexId,
+ AMOID,
+ AMNAME,
+ Anum_pg_am_amname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
{
CastRelationId,
CastOidIndexId,
{
"operator family", OBJECT_OPFAMILY
},
+ /* OCLASS_AM */
+ {
+ "access method", OBJECT_ACCESS_METHOD
+ },
/* OCLASS_AMOP */
{
"operator of access method", OBJECT_AMOP
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;
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;
/* 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);
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)
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,
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,
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_AM:
+ appendStringInfoString(&buffer, "access method");
+ break;
+
default:
appendStringInfo(&buffer, "unrecognized %u", object->classId);
break;
}
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,
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 \
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
/* XXX merge this with ObjectTypeMap? */
static event_trigger_support_data event_trigger_support[] = {
+ {"ACCESS METHOD", true},
{"AGGREGATE", true},
{"CAST", true},
{"CONSTRAINT", true},
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:
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_POLICY:
+ case OCLASS_AM:
return true;
}
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;
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? */
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
*
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;
-}
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)
{
case T_CreateTransformStmt:
retval = _copyCreateTransformStmt(from);
break;
+ case T_CreateAmStmt:
+ retval = _copyCreateAmStmt(from);
+ break;
case T_CreateTrigStmt:
retval = _copyCreateTrigStmt(from);
break;
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)
{
case T_CreateTransformStmt:
retval = _equalCreateTransformStmt(a, b);
break;
+ case T_CreateAmStmt:
+ retval = _equalCreateAmStmt(a, b);
+ break;
case T_CreateTrigStmt:
retval = _equalCreateTrigStmt(a, b);
break;
#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"
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
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
| CommentStmt
| ConstraintsSetStmt
| CopyStmt
+ | CreateAmStmt
| CreateAsStmt
| CreateAssertStmt
| CreateCastStmt
| 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 :
| 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; }
| MATCH
| MATERIALIZED
| MAXVALUE
+ | METHOD
| MINUTE_P
| MINVALUE
| MODE
* 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),
address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
break;
+ case T_CreateAmStmt:
+ address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(parsetree));
case OBJECT_TRANSFORM:
tag = "DROP TRANSFORM";
break;
+ case OBJECT_ACCESS_METHOD:
+ tag = "DROP ACCESS METHOD";
+ break;
default:
tag = "???";
}
case OBJECT_COLLATION:
tag = "CREATE COLLATION";
break;
+ case OBJECT_ACCESS_METHOD:
+ tag = "CREATE ACCESS METHOD";
+ break;
default:
tag = "???";
}
tag = "ALTER POLICY";
break;
+ case T_CreateAmStmt:
+ tag = "CREATE ACCESS METHOD";
+ break;
+
case T_PrepareStmt:
tag = "PREPARE";
break;
lev = LOGSTMT_DDL;
break;
+ case T_CreateAmStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
/* already-planned queries */
case T_PlannedStmt:
{
*-------------------------------------------------------------------------
*/
-/*
- * 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;
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,
int numProcLangs;
int numCasts;
int numTransforms;
+ int numAccessMethods;
int numOpclasses;
int numOpfamilies;
int numConversions;
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);
#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"
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);
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
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
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;
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
case DO_FUNC:
case DO_AGG:
case DO_OPERATOR:
+ case DO_ACCESS_METHOD:
case DO_OPCLASS:
case DO_OPFAMILY:
case DO_COLLATION:
DO_FUNC,
DO_AGG,
DO_OPERATOR,
+ DO_ACCESS_METHOD,
DO_OPCLASS,
DO_OPFAMILY,
DO_COLLATION,
Oid oprcode;
} OprInfo;
+typedef struct _accessMethodInfo
+{
+ DumpableObject dobj;
+ char amtype;
+ char *amhandler;
+} AccessMethodInfo;
+
typedef struct _opclassInfo
{
DumpableObject dobj;
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);
* 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,
2, /* DO_FUNC */
3, /* DO_AGG */
3, /* DO_OPERATOR */
+ 3, /* DO_ACCESS_METHOD */
4, /* DO_OPCLASS */
4, /* DO_OPFAMILY */
4, /* DO_COLLATION */
6, /* DO_FUNC */
7, /* DO_AGG */
8, /* DO_OPERATOR */
+ 8, /* DO_ACCESS_METHOD */
9, /* DO_OPCLASS */
9, /* DO_OPFAMILY */
3, /* DO_COLLATION */
"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)",
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201603211
+#define CATALOG_VERSION_NO 201603231
#endif
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 */
{
NameData amname; /* access method name */
regproc amhandler; /* handler function */
+ char amtype; /* see AMTYPE_xxx constants below */
} FormData_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 i ));
DESCR("hash index access method");
#define HASH_AM_OID 405
-DATA(insert OID = 783 ( gist gisthandler ));
+DATA(insert OID = 783 ( gist gisthandler i ));
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 i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
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);
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);
T_CreatePolicyStmt,
T_AlterPolicyStmt,
T_CreateTransformStmt,
+ T_CreateAmStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
typedef enum ObjectType
{
+ OBJECT_ACCESS_METHOD,
OBJECT_AGGREGATE,
OBJECT_AMOP,
OBJECT_AMPROC,
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
* ----------------------
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)
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,
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,
--- /dev/null
+--
+-- 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;
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[]),
-- 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
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
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
emp|f
equipment_r|f
f_star|f
-fast_emp4000|t
+fast_emp4000|f
float4_tbl|f
float8_tbl|f
func_index_heap|t
# ----------
# 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 *
test: updatable_views
test: rolenames
test: roleattributes
+test: create_am
test: sanity_check
test: errors
test: select
--- /dev/null
+--
+-- 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;
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
-- 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