aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands')
-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
4 files changed, 282 insertions, 36 deletions
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;
-}