Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlvaro Herrera2016-03-24 02:01:35 +0000
committerAlvaro Herrera2016-03-24 02:01:35 +0000
commit473b93287040b20017cc25a157cffdc5b978c254 (patch)
tree58f662a65247525b2e5e178b9050feb3f3056590 /src/backend
parent2c6af4f44228d76d3351fe26f68b00b55cdd239a (diff)
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
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/index/amapi.c7
-rw-r--r--src/backend/catalog/dependency.c9
-rw-r--r--src/backend/catalog/objectaddress.c61
-rw-r--r--src/backend/commands/Makefile2
-rw-r--r--src/backend/commands/amcmds.c271
-rw-r--r--src/backend/commands/event_trigger.c3
-rw-r--r--src/backend/commands/opclasscmds.c42
-rw-r--r--src/backend/nodes/copyfuncs.c15
-rw-r--r--src/backend/nodes/equalfuncs.c13
-rw-r--r--src/backend/parser/gram.y25
-rw-r--r--src/backend/parser/parse_utilcmd.c2
-rw-r--r--src/backend/tcop/utility.c18
-rw-r--r--src/backend/utils/adt/selfuncs.c46
13 files changed, 430 insertions, 84 deletions
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
index bda166a9ef8..d347ebcba45 100644
--- a/src/backend/access/index/amapi.c
+++ b/src/backend/access/index/amapi.c
@@ -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 */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index c48e37bf9a2..17f9de1ff94 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -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;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index d2aaa6ded92..cb3ba853f4e 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -110,6 +110,18 @@ typedef struct
static const ObjectPropertyType ObjectProperty[] =
{
{
+ AccessMethodRelationId,
+ AmOidIndexId,
+ AMOID,
+ AMNAME,
+ Anum_pg_am_amname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
+ },
+ {
CastRelationId,
CastOidIndexId,
-1,
@@ -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,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index b1ac704886f..6b3742c0a08 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/commands
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
+OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
dbcommands.o define.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index 00000000000..7a937543916
--- /dev/null
+++ b/src/backend/commands/amcmds.c
@@ -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;
+}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9e32f8d09b1..3f52ad836b4 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -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;
}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 8a661968cd9..ac559fc9b41 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -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;
-}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6b5d1d6efce..6378db8bbea 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 87eb859e05e..854c062d32f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1856,6 +1856,16 @@ _equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStm
}
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)
{
COMPARE_STRING_FIELD(trigname);
@@ -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;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a74fb772e14..12733528eb2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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
@@ -4708,6 +4710,23 @@ row_security_cmd:
/*****************************************************************************
*
+ * 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 :
* CREATE TRIGGER ...
* DROP TRIGGER ...
@@ -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
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index dc431c7de0c..65284941ed9 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 045f7f06ee2..4d0aac979fc 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -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:
{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d396ef142f9..b2c57e87a5e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -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,