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;