diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
+ Index access access methods can be defined and dropped using
+ and
+ SQL commands respectively.
+
+
+
An index access method handler function must be declared to accept a
single argument of type internal> and to return the
pseudo-type index_am_handler>. The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
+
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
+
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...f255ecc
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+
+
+
+
+ CREATE ACCESS METHOD
+
+
+
+ CREATE ACCESS METHOD
+ 7
+ SQL - Language Statements
+
+
+
+ CREATE ACCESS METHOD
+ define a new access method
+
+
+
+
+ CREATE ACCESS METHOD name
+ TYPE INDEX
+ HANDLER handler_function
+
+
+
+
+ Description
+
+
+ CREATE ACCESS METHOD creates a new access method.
+
+
+
+ The access method name must be unique within the database.
+
+
+
+ Only superusers can define new access methods.
+
+
+
+
+ Parameters
+
+
+
+ name
+
+
+ The name of the access method to be created.
+
+
+
+
+
+ TYPE INDEX
+
+
+ This clause specifies type of access method to define.
+ For now, there are only index access methods. But intentionally
+ there would be other types of access methods.
+
+
+
+
+
+ HANDLER handler_function
+
+ handler_function 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 internal>, and its return type must be
+ index_am_handler.
+
+
+
+ See for index access methods API.
+
+
+
+
+
+
+
+ Examples
+
+
+ Create an access method bloom> with
+ handler function blhandler>:
+
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+
+
+
+
+
+ Compatibility
+
+
+ CREATE ACCESS METHOD is a
+ PostgreSQL> extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
+
+
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+
+
+
+
+ DROP ACCESS METHOD
+
+
+
+ DROP ACCESS METHOD
+ 7
+ SQL - Language Statements
+
+
+
+ DROP ACCESS METHOD
+ remove an access method
+
+
+
+
+ DROP ACCESS METHOD [ IF EXISTS ] name [ CASCADE | RESTRICT ]
+
+
+
+
+ Description
+
+
+ DROP ACCESS METHOD removes an existing access method.
+ Only superusers can drop access methods.
+
+
+
+
+
+
+
+ Parameters
+
+
+
+ IF EXISTS
+
+
+ Do not throw an error if the access method does not exist.
+ A notice is issued in this case.
+
+
+
+
+
+ name
+
+
+ The name of an existing access method.
+
+
+
+
+
+ CASCADE
+
+
+ Automatically drop objects that depend on the access method
+ (such as operator classes, operator families, indexes).
+
+
+
+
+
+ RESTRICT
+
+
+ Refuse to drop the access method if any objects depend on it.
+ This is the default.
+
+
+
+
+
+
+
+ Examples
+
+
+ Drop the access method bloom>:
+
+ DROP ACCESS METHOD bloom;
+
+
+
+
+ Compatibility
+
+
+ DROP ACCESS METHOD is a
+ PostgreSQL> extension.
+
+
+
+
+ See Also
+
+
+
+
+
+
+
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
&commit;
&commitPrepared;
©Table;
+ &createAccessMethod;
&createAggregate;
&createCast;
&createCollation;
***************
*** 122,127 ****
--- 123,129 ----
&delete;
&discard;
&do;
+ &dropAccessMethod;
&dropAggregate;
&dropCast;
&dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..e9b9b3f
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
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\" type is not index",
+ NameStr(amform->amname))));
+
amhandler = amform->amhandler;
/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
#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"
*************** static const Oid object_classes[] = {
*** 160,166 ****
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
PolicyRelationId, /* OCLASS_POLICY */
! TransformRelationId /* OCLASS_TRANSFORM */
};
--- 161,168 ----
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
PolicyRelationId, /* OCLASS_POLICY */
! TransformRelationId, /* OCLASS_TRANSFORM */
! AccessMethodRelationId /* OCLASS_AM */
};
*************** doDeletion(const ObjectAddress *object,
*** 1270,1275 ****
--- 1272,1280 ----
case OCLASS_TRANSFORM:
DropTransformById(object->objectId);
+
+ case OCLASS_AM:
+ RemoveAccessMethodById(object->objectId);
break;
default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
case TransformRelationId:
return OCLASS_TRANSFORM;
+
+ case AccessMethodRelationId:
+ return OCLASS_AM;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..78eff1e
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
Anum_pg_type_typacl,
ACL_KIND_TYPE,
true
+ },
+ {
+ AccessMethodRelationId,
+ AmOidIndexId,
+ AMOID,
+ AMNAME,
+ Anum_pg_am_amname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ true
}
};
*************** static const struct object_type_map
*** 640,645 ****
--- 652,661 ----
/* OCLASS_TRANSFORM */
{
"transform", OBJECT_TRANSFORM
+ },
+ /* OCLASS_AM */
+ {
+ "access method", OBJECT_ACCESS_METHOD
}
};
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 690,697 ----
List *objargs, bool missing_ok);
static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ bool missing_ok);
static const ObjectPropertyType *get_object_property_data(Oid class_id);
static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 931,939 ----
address = get_object_address_defacl(objname, objargs,
missing_ok);
break;
+ case OBJECT_ACCESS_METHOD:
+ address = get_object_address_am(objtype, objname, missing_ok);
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
ObjectAddress address;
/* XXX no missing_ok support here */
! amoid = get_am_oid(strVal(linitial(objname)), false);
objname = list_copy_tail(objname, 1);
switch (objtype)
--- 1510,1516 ----
ObjectAddress address;
/* XXX no missing_ok support here */
! amoid = get_am_oid(strVal(linitial(objname)), AMTYPE_INDEX, false);
objname = list_copy_tail(objname, 1);
switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1537,1601 ----
}
/*
+ * Find the ObjectAddress for an access method.
+ */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ ObjectAddress address;
+ char *amname, *catalogname;
+ Type tup;
+
+ switch (list_length(objname))
+ {
+ case 1:
+ amname = strVal(linitial(objname));
+ break;
+ case 2:
+ catalogname = strVal(linitial(objname));
+ amname = strVal(lsecond(objname));
+
+ /*
+ * We check the catalog name and then ignore it.
+ */
+ if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(objname))));
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper access method name (too many dotted names): %s",
+ NameListToString(objname))));
+ break;
+ }
+
+ address.classId = AccessMethodRelationId;
+ address.objectId = InvalidOid;
+ address.objectSubId = 0;
+
+ tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ if (!HeapTupleIsValid(tup))
+ {
+ /* Access method is missing, report error if needed */
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("access method \"%s\" does not exist",
+ amname)));
+ return address;
+ }
+
+ /* Access method is found, return its oid */
+ address.objectId = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+
+ return address;
+ }
+
+ /*
* Find the ObjectAddress for an opclass/opfamily member.
*
* (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2259,2265 ----
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,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3210,3230 ----
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,
*************** getObjectTypeDescription(const ObjectAdd
*** 3610,3615 ****
--- 3706,3715 ----
appendStringInfoString(&buffer, "transform");
break;
+ case OCLASS_AM:
+ appendStringInfoString(&buffer, "access method");
+ break;
+
default:
appendStringInfo(&buffer, "unrecognized %u", object->classId);
break;
*************** getObjectIdentityParts(const ObjectAddre
*** 4566,4571 ****
--- 4666,4685 ----
}
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
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
! OBJS = 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 \
--- 12,18 ----
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
! 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 ...3d7f5ec
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,277 ----
+ /*-------------------------------------------------------------------------
+ *
+ * amcmds.c
+ * Routines for SQL commands that manipulate access methods.
+ *
+ * Portions Copyright (c) 1996-2014, 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/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+
+ /*
+ * Convert a handler function name passed from the parser to an Oid. This
+ * function either return valid function Oid or throw an error.
+ */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ Oid handlerOid;
+ Oid funcargtypes[1] = {INTERNALOID};
+
+ if (handler_name == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("handler function is not specified")));
+
+ /* handlers have no arguments */
+ handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+
+ /* check that handler has correct return type */
+ if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("function %s must return type \"index_am_handler\"",
+ NameListToString(handler_name))));
+
+ return handlerOid;
+ }
+
+
+ /*
+ * 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 access method.")));
+
+ /* Check if name is busy */
+ amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ if (OidIsValid(amoid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("access method \"%s\" already exists", stmt->amname)));
+ }
+
+ /*
+ * Get handler function oid. Handler signature depends on access method
+ * type.
+ */
+ switch(stmt->amtype)
+ {
+ case AMTYPE_INDEX:
+ amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ break;
+ }
+
+ /*
+ * 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 dependecy 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);
+ }
+
+ /*
+ * 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:
+ elog(ERROR, "invalid access method type '%c'", amtype);
+ }
+ }
+
+ /*
+ * get_am_oid - given an access method name and type, 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, 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 (amform->amtype != amtype)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("access method \"%s\" type is not %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_am_name - given an access method OID name and type, look up the name
+ *
+ * Access method type is not required for lookup. However it's useful to check
+ * the type to ensure it is what we're looking for.
+ */
+ 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;
+ }
+
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
/* XXX merge this with ObjectTypeMap? */
static event_trigger_support_data event_trigger_support[] = {
+ {"ACCESS METHOD", true},
{"AGGREGATE", true},
{"CAST", true},
{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
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:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
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
new file mode 100644
index 8a66196..19dac98
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
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;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
get_namespace_name(namespaceoid));
/* Get access method OID, throwing an error if it doesn't exist. */
! amoid = get_am_oid(stmt->amname, false);
/* XXX Should we make any privilege check against the AM? */
--- 749,755 ----
get_namespace_name(namespaceoid));
/* Get access method OID, throwing an error if it doesn't exist. */
! amoid = get_am_oid(stmt->amname, AMTYPE_INDEX, false);
/* XXX Should we make any privilege check against the AM? */
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
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
*
--- 1669,1674 ----
*************** IsThereOpFamilyInNamespace(const char *o
*** 1723,1744 ****
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;
- }
--- 1714,1716 ----
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index df7c2fa..37529e1
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
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)
{
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
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
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
}
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);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
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
new file mode 100644
index b307b48..ce541c4
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 51,56 ****
--- 51,57 ----
#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"
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
DeallocateStmt PrepareStmt ExecuteStmt
DropOwnedStmt ReassignOwnedStmt
AlterTSConfigurationStmt AlterTSDictionaryStmt
! CreateMatViewStmt RefreshMatViewStmt
%type select_no_parens select_with_parens select_clause
simple_select values_clause
--- 264,270 ----
DeallocateStmt PrepareStmt ExecuteStmt
DropOwnedStmt ReassignOwnedStmt
AlterTSConfigurationStmt AlterTSDictionaryStmt
! CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
%type select_no_parens select_with_parens select_clause
simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
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
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 605,611 ----
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
! 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
*************** stmt :
*** 789,794 ****
--- 790,796 ----
| CommentStmt
| ConstraintsSetStmt
| CopyStmt
+ | CreateAmStmt
| CreateAsStmt
| CreateAssertStmt
| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 825,831 ----
| DeleteStmt
| DiscardStmt
| DoStmt
+ | DropAmStmt
| DropAssertStmt
| DropCastStmt
| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4711,4765 ----
/*****************************************************************************
*
+ * 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;
+ }
+ ;
+
+ /*****************************************************************************
+ *
+ * QUERY :
+ * DROP ACCESS METHOD name
+ *
+ ****************************************************************************/
+
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_ACCESS_METHOD;
+ n->objects = list_make1(list_make1(makeString($4)));
+ n->arguments = NIL;
+ n->missing_ok = false;
+ n->behavior = $5;
+ n->concurrent = false;
+ $$ = (Node *) n;
+ }
+ | DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ {
+ DropStmt *n = makeNode(DropStmt);
+ n->removeType = OBJECT_ACCESS_METHOD;
+ n->objects = list_make1(list_make1(makeString($6)));
+ n->arguments = NIL;
+ n->missing_ok = true;
+ n->behavior = $7;
+ n->concurrent = false;
+ $$ = (Node *) n;
+ }
+ ;
+
+
+ /*****************************************************************************
+ *
* QUERIES :
* CREATE TRIGGER ...
* DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13830,13836 ----
| MATCH
| MATERIALIZED
| MAXVALUE
+ | METHOD
| MINUTE_P
| MINVALUE
| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..92d1baa
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
* 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))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
* 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, AMTYPE_INDEX, 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
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
break;
+ case T_CreateAmStmt:
+ address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
case OBJECT_TRANSFORM:
tag = "DROP TRANSFORM";
break;
+ case OBJECT_ACCESS_METHOD:
+ tag = "DROP ACCESS METHOD";
+ break;
default:
tag = "???";
}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
case OBJECT_COLLATION:
tag = "CREATE COLLATION";
break;
+ case OBJECT_ACCESS_METHOD:
+ tag = "CREATE ACCESS METHOD";
+ break;
default:
tag = "???";
}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
tag = "ALTER POLICY";
break;
+ case T_CreateAmStmt:
+ tag = "CREATE ACCESS METHOD";
+ break;
+
case T_PrepareStmt:
tag = "PREPARE";
break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
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
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
*-------------------------------------------------------------------------
*/
! /*
! * 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 *
deconstruct_indexquals(IndexPath *path)
{
List *result = NIL;
--- 6012,6018 ----
*-------------------------------------------------------------------------
*/
! List *
deconstruct_indexquals(IndexPath *path)
{
List *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
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
genericcostestimate(PlannerInfo *root,
IndexPath *path,
double loop_count,
--- 6162,6168 ----
return qual_arg_cost;
}
! void
genericcostestimate(PlannerInfo *root,
IndexPath *path,
double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
int numProcLangs;
int numCasts;
int numTransforms;
+ int numAccessMethods;
int numOpclasses;
int numOpfamilies;
int numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
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);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..9061afa
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
***************
*** 45,50 ****
--- 45,51 ----
#include "access/attnum.h"
#include "access/sysattr.h"
#include "access/transam.h"
+ #include "catalog/pg_am.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_default_acl.h"
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 174,180 ----
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);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4103,4187 ----
}
/*
+ * 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 "
+ "WHERE oid >= 10000");
+
+ 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 */
+ selectDumpableObject(&(aminfo[i].dobj), dopt);
+ }
+
+ PQclear(res);
+
+ destroyPQExpBuffer(query);
+
+ return aminfo;
+ }
+
+
+ /*
* getOpclasses:
* read all opclasses in the system catalogs and return them in the
* OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8489,8497 ----
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;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11530,11603 ----
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
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16379,16385 ----
case DO_FUNC:
case DO_AGG:
case DO_OPERATOR:
+ case DO_ACCESS_METHOD:
case DO_OPCLASS:
case DO_OPFAMILY:
case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..66e6931
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
DO_FUNC,
DO_AGG,
DO_OPERATOR,
+ DO_ACCESS_METHOD,
DO_OPCLASS,
DO_OPFAMILY,
DO_COLLATION,
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,180 ----
Oid oprcode;
} OprInfo;
+ typedef struct _accessMethodInfo
+ {
+ DumpableObject dobj;
+ char amtype;
+ char *amhandler;
+ } AccessMethodInfo;
+
typedef struct _opclassInfo
{
DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 556,562 ----
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);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..52d5625
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
2, /* DO_FUNC */
3, /* DO_AGG */
3, /* DO_OPERATOR */
+ 3, /* DO_ACCESS_METHOD */
4, /* DO_OPCLASS */
4, /* DO_OPFAMILY */
4, /* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
6, /* DO_FUNC */
7, /* DO_AGG */
8, /* DO_OPERATOR */
+ 8, /* DO_ACCESS_METHOD */
9, /* DO_OPCLASS */
9, /* DO_OPFAMILY */
3, /* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1341 ----
"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)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
OCLASS_EXTENSION, /* pg_extension */
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
OCLASS_POLICY, /* pg_policy */
! OCLASS_TRANSFORM /* pg_transform */
} ObjectClass;
! #define LAST_OCLASS OCLASS_TRANSFORM
/* in dependency.c */
--- 153,163 ----
OCLASS_EXTENSION, /* pg_extension */
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
OCLASS_POLICY, /* pg_policy */
! OCLASS_TRANSFORM, /* pg_transform */
! OCLASS_AM, /* pg_am */
} ObjectClass;
! #define LAST_OCLASS OCLASS_AM
/* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..6fba1cd
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,41 ----
{
NameData amname; /* access method name */
regproc amhandler; /* handler function */
+ char amtype; /* see AMTYPE_xxx constants below */
} FormData_pg_am;
/* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 48,78 ****
* compiler constants for pg_am
* ----------------
*/
! #define Natts_pg_am 2
#define Anum_pg_am_amname 1
#define Anum_pg_am_amhandler 2
/* ----------------
* initial contents of pg_am
* ----------------
*/
! DATA(insert OID = 403 ( btree bthandler ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
! DATA(insert OID = 405 ( hash hashhandler ));
DESCR("hash index access method");
#define HASH_AM_OID 405
! DATA(insert OID = 783 ( gist gisthandler ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
! DATA(insert OID = 2742 ( gin ginhandler ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742
! DATA(insert OID = 4000 ( spgist spghandler ));
DESCR("SP-GiST index access method");
#define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 ( brin brinhandler ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
--- 49,86 ----
* compiler constants for pg_am
* ----------------
*/
! #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 i ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
! DATA(insert OID = 405 ( hash hashhandler i ));
DESCR("hash index access method");
#define HASH_AM_OID 405
! DATA(insert OID = 783 ( gist gisthandler i ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
! DATA(insert OID = 2742 ( gin ginhandler i ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742
! DATA(insert OID = 4000 ( spgist spghandler i ));
DESCR("SP-GiST index access method");
#define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 ( brin brinhandler i ));
DESCR("block range index (BRIN) access method");
#define BRIN_AM_OID 3580
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..1e21f03
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
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);
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
List *options,
Oid fdwvalidator);
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid);
+
/* support routines in commands/define.c */
extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index fad9988..8cd1d89
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 401,406 ****
--- 401,407 ----
T_CreatePolicyStmt,
T_AlterPolicyStmt,
T_CreateTransformStmt,
+ T_CreateAmStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
typedef enum ObjectType
{
+ OBJECT_ACCESS_METHOD,
OBJECT_AGGREGATE,
OBJECT_AMOP,
OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
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
* ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
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)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
} Pattern_Prefix_Status;
+ /*
+ * deconstruct_indexquals is a simple function to examine the indexquals
+ * attached to a proposed IndexPath. It returns a list of IndexQualInfo
+ * structs, one per qual expression.
+ */
+ typedef struct
+ {
+ RestrictInfo *rinfo; /* the indexqual itself */
+ int indexcol; /* zero-based index column number */
+ bool varonleft; /* true if index column is on left of qual */
+ Oid clause_op; /* qual's operator OID, if relevant */
+ Node *other_operand; /* non-index operand of qual's operator */
+ } IndexQualInfo;
+
+ /*
+ * genericcostestimate is a general-purpose estimator that can be used for
+ * most index types. In some cases we use genericcostestimate as the base
+ * code and then incorporate additional index-type-specific knowledge in
+ * the type-specific calling function. To avoid code duplication, we make
+ * genericcostestimate return a number of intermediate values as well as
+ * its preliminary estimates of the output cost values. The GenericCosts
+ * struct includes all these values.
+ *
+ * Callers should initialize all fields of GenericCosts to zero. In addition,
+ * they can set numIndexTuples to some positive value if they have a better
+ * than default way of estimating the number of leaf index tuples visited.
+ */
+ typedef struct
+ {
+ /* These are the values the cost estimator must return to the planner */
+ Cost indexStartupCost; /* index-related startup cost */
+ Cost indexTotalCost; /* total index-related scan cost */
+ Selectivity indexSelectivity; /* selectivity of index */
+ double indexCorrelation; /* order correlation of index */
+
+ /* Intermediate values we obtain along the way */
+ double numIndexPages; /* number of leaf pages visited */
+ double numIndexTuples; /* number of leaf tuples visited */
+ double spc_random_page_cost; /* relevant random_page_cost value */
+ double num_sa_scans; /* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+
/* Hooks for plugins to get control when we ask for stats */
typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
double nbuckets);
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ double loop_count,
+ List *qinfos,
+ GenericCosts *costs);
+
/* Functions in array_selfuncs.c */
extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 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;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
emp|f
equipment_r|f
f_star|f
! fast_emp4000|t
float4_tbl|f
float8_tbl|f
func_index_heap|t
--- 44,50 ----
emp|f
equipment_r|f
f_star|f
! fast_emp4000|f
float4_tbl|f
float8_tbl|f
func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
# ----------
# 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
# ----------
# sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
# ----------
# 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 create_am
# ----------
# sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
test: updatable_views
test: rolenames
test: roleattributes
+ test: create_am
test: sanity_check
test: errors
test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 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;