mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Support CREATE ACCESS METHOD
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
This commit is contained in:
parent
2c6af4f442
commit
473b932870
@ -57,6 +57,12 @@
|
||||
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
|
||||
|
@ -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">
|
||||
|
120
doc/src/sgml/ref/create_access_method.sgml
Normal file
120
doc/src/sgml/ref/create_access_method.sgml
Normal file
@ -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>
|
112
doc/src/sgml/ref/drop_access_method.sgml
Normal file
112
doc/src/sgml/ref/drop_access_method.sgml
Normal file
@ -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>
|
@ -80,6 +80,7 @@
|
||||
&commit;
|
||||
&commitPrepared;
|
||||
©Table;
|
||||
&createAccessMethod;
|
||||
&createAggregate;
|
||||
&createCast;
|
||||
&createCollation;
|
||||
@ -122,6 +123,7 @@
|
||||
&delete;
|
||||
&discard;
|
||||
&do;
|
||||
&dropAccessMethod;
|
||||
&dropAggregate;
|
||||
&dropCast;
|
||||
&dropCollation;
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 \
|
||||
|
271
src/backend/commands/amcmds.c
Normal file
271
src/backend/commands/amcmds.c
Normal file
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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)",
|
||||
|
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201603211
|
||||
#define CATALOG_VERSION_NO 201603231
|
||||
|
||||
#endif
|
||||
|
@ -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 */
|
||||
|
@ -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 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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -402,6 +402,7 @@ typedef enum NodeTag
|
||||
T_CreatePolicyStmt,
|
||||
T_AlterPolicyStmt,
|
||||
T_CreateTransformStmt,
|
||||
T_CreateAmStmt,
|
||||
|
||||
/*
|
||||
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
||||
|
@ -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
|
||||
* ----------------------
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
108
src/test/regress/expected/create_am.out
Normal file
108
src/test/regress/expected/create_am.out
Normal file
@ -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;
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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
|
||||
|
73
src/test/regress/sql/create_am.sql
Normal file
73
src/test/regress/sql/create_am.sql
Normal file
@ -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;
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user