>From 149d44f96a6da30024b23129b87363495e2736dc Mon Sep 17 00:00:00 2001 From: Petr Jelinek Date: Sun, 15 Feb 2015 03:00:24 +0100 Subject: [PATCH 2/3] seqam ddl v5 --- doc/src/sgml/catalogs.sgml | 6 + doc/src/sgml/ref/allfiles.sgml | 2 + doc/src/sgml/ref/create_seqam.sgml | 163 ++++++++++++++++++ doc/src/sgml/ref/drop_seqam.sgml | 87 ++++++++++ doc/src/sgml/reference.sgml | 2 + src/backend/access/sequence/seqam.c | 308 +++++++++++++++++++++++++++++++++++ src/backend/catalog/dependency.c | 11 +- src/backend/catalog/objectaddress.c | 66 +++++++- src/backend/commands/dropcmds.c | 4 + src/backend/commands/event_trigger.c | 3 + src/backend/parser/gram.y | 13 +- src/backend/tcop/utility.c | 12 ++ src/bin/pg_dump/common.c | 5 + src/bin/pg_dump/pg_dump.c | 158 +++++++++++++++++- src/bin/pg_dump/pg_dump.h | 9 +- src/bin/pg_dump/pg_dump_sort.c | 11 +- src/include/access/seqam.h | 3 + src/include/catalog/dependency.h | 1 + src/include/nodes/parsenodes.h | 1 + src/include/parser/kwlist.h | 1 + 20 files changed, 858 insertions(+), 8 deletions(-) create mode 100644 doc/src/sgml/ref/create_seqam.sgml create mode 100644 doc/src/sgml/ref/drop_seqam.sgml diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 8faa87b..0a48cc8 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -5677,6 +5677,12 @@ installed on the system. + + The contents of this catalog can be manipulated by + CREATE ACCESS METHOD FOR SEQUENCES and + DROP ACCESS METHOD FOR SEQUENCES. + + <structname>pg_seqam</> Columns diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index bf95453..2fef865 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -74,6 +74,7 @@ Complete list of usable sgml source files in this directory. + @@ -117,6 +118,7 @@ Complete list of usable sgml source files in this directory. + diff --git a/doc/src/sgml/ref/create_seqam.sgml b/doc/src/sgml/ref/create_seqam.sgml new file mode 100644 index 0000000..e95409f --- /dev/null +++ b/doc/src/sgml/ref/create_seqam.sgml @@ -0,0 +1,163 @@ + + + + + CREATE ACCESS METHOD FOR SEQUENCES + + + + CREATE ACCESS METHOD FOR SEQUENCES + 7 + SQL - Language Statements + + + + CREATE ACCESS METHOD FOR SEQUENCES + define custom sequence access method + + + + +CREATE ACCESS METHOD FOR SEQUENCES name ( + RELOPTIONS = reloptions_function , + INIT = init_function , + ALLOC = alloc_function , + SETVAL = setval_function , + GETSTATE = getstate_function, + SETSTATE = setstate_function +) + + + + + Description + + + CREATE ACCESS METHOD FOR SEQUENCES creates a sequence + access method. A sequence access method changes how values for SEQUENCEs + are generated. + + + + You must be a superuser to use CREATE ACCESS METHOD FOR SEQUENCES. + + + + + Parameters + + + + name + + + The name of the sequence access method to be created. This name must be + unique within the database. + + + + + + reloptions_function + + + The name of the function for parsing reloptions for the sequence access + method. + + + + + + init_function + + + The name of the init function for the sequence access method. + + + + + + alloc_function + + + The name of the function which allocates new sequence id. + + + + + + setval_function + + + The name of the function which handles the setval + function call. + + + + + + getstate_function + + + The name of the function used for dumping current state of the sequence + to a string. This function will be called by + pg_dump. + + + + + + setstate_function + + + The name of the function used for restoring the state of the sequence + from a string. Calls to this function will be present in a database dumps + created by pg_dump. + + + + + + + + The function names can be schema-qualified if necessary. Argument types + are not given, since the argument list for each type of function is + predetermined. All functions are required. + + + + The arguments can appear in any order, not only the one shown above. + + + + + Examples + + + See the contrib/gapless_seq for example on how to write new + sequence access methods and how to use this command. + + + + + + Compatibility + + + There is no + CREATE ACCESS METHOD FOR SEQUENCES statement in the SQL + standard. + + + + + See Also + + + + + + diff --git a/doc/src/sgml/ref/drop_seqam.sgml b/doc/src/sgml/ref/drop_seqam.sgml new file mode 100644 index 0000000..959b777 --- /dev/null +++ b/doc/src/sgml/ref/drop_seqam.sgml @@ -0,0 +1,87 @@ + + + + + DROP ACCESS METHOD FOR SEQUENCES + + + + DROP ACCESS METHOD FOR SEQUENCES + 7 + SQL - Language Statements + + + + DROP ACCESS METHOD FOR SEQUENCES + remove a custom sequence access method + + + + +DROP ACCESS METHOD FOR SEQUENCES [ IF EXISTS ] name + + + + + Description + + + DROP ACCESS METHOD FOR SEQUENCES drop an existing + sequence access method. + + + + You must be a superuser to use the + DROP ACCESS METHOD FOR SEQUENCES. + + + + + Parameters + + + + + IF EXISTS + + + Do not throw an error if the sequence access method does not exist. + A notice is issued in this case. + + + + + + name + + + The name of an existing sequence access method to be removed. + + + + + + + + + + Compatibility + + + There is no + DDROP ACCESS METHOD FOR SEQUENCES statement in the SQL + standard. + + + + + See Also + + + + + + diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 03020df..35c0c05 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -102,6 +102,7 @@ &createRole; &createRule; &createSchema; + &createSeqAM; &createSequence; &createServer; &createTable; @@ -145,6 +146,7 @@ &dropRole; &dropRule; &dropSchema; + &dropSeqAM; &dropSequence; &dropServer; &dropTable; diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c index 54d6e9d..bd9f2da 100644 --- a/src/backend/access/sequence/seqam.c +++ b/src/backend/access/sequence/seqam.c @@ -67,8 +67,20 @@ #include "access/seqam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/dependency.h" +#include "catalog/objectaccess.h" +#include "catalog/objectaddress.h" +#include "catalog/indexing.h" +#include "catalog/pg_proc.h" #include "catalog/pg_seqam.h" +#include "catalog/pg_type.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "parser/parse_func.h" +#include "parser/parse_type.h" +#include "utils/builtins.h" #include "utils/guc.h" +#include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" @@ -238,6 +250,302 @@ seqam_set_state(Relation seqrel, SequenceHandle *seqh, ArrayType *statearr) /* + * Find seqam function by name and validate it. + */ +static Datum +get_seqam_func(DefElem *defel, int attnum) +{ + List *funcName = defGetQualifiedName(defel); + Oid typeId[6]; + Oid retTypeId; + int nargs; + Oid procOid; + + typeId[0] = INTERNALOID; + + switch (attnum) + { + case Anum_pg_seqam_seqamreloptions: + nargs = 2; + typeId[1] = BOOLOID; + retTypeId = BYTEAOID; + break; + + case Anum_pg_seqam_seqaminit: + nargs = 5; + typeId[1] = INTERNALOID; + typeId[2] = INT8OID; + typeId[3] = BOOLOID; + typeId[4] = BOOLOID; + retTypeId = BYTEAOID; + break; + + case Anum_pg_seqam_seqamalloc: + nargs = 4; + typeId[1] = INTERNALOID; + typeId[2] = INT8OID; + typeId[3] = INTERNALOID; + retTypeId = INT8OID; + break; + + case Anum_pg_seqam_seqamsetval: + nargs = 3; + typeId[1] = INTERNALOID; + typeId[2] = INT8OID; + retTypeId = VOIDOID; + break; + + case Anum_pg_seqam_seqamgetstate: + nargs = 2; + typeId[1] = INTERNALOID; + retTypeId = TEXTARRAYOID; + break; + + case Anum_pg_seqam_seqamsetstate: + nargs = 3; + typeId[1] = INTERNALOID; + typeId[2] = TEXTARRAYOID; + retTypeId = VOIDOID; + break; + + default: + /* should not be here */ + elog(ERROR, "unrecognized attribute for sequence access method: %d", + attnum); + nargs = 0; /* keep compiler quiet */ + } + + procOid = LookupFuncName(funcName, nargs, typeId, false); + if (get_func_rettype(procOid) != retTypeId) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("function %s should return type %s", + func_signature_string(funcName, nargs, NIL, typeId), + format_type_be(retTypeId)))); + + return ObjectIdGetDatum(procOid); +} + + +/* + * make pg_depend entries for a new pg_seqam entry + */ +static void +makeSeqAMDependencies(HeapTuple tuple) +{ + Form_pg_seqam seqam = (Form_pg_seqam) GETSTRUCT(tuple); + ObjectAddress myself, + referenced; + + + myself.classId = SeqAccessMethodRelationId; + myself.objectId = HeapTupleGetOid(tuple); + myself.objectSubId = 0; + + /* Dependency on extension. */ + recordDependencyOnCurrentExtension(&myself, false); + + /* Dependencies on functions. */ + referenced.classId = ProcedureRelationId; + referenced.objectSubId = 0; + + referenced.objectId = seqam->seqamreloptions; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.objectId = seqam->seqaminit; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.objectId = seqam->seqamalloc; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.objectId = seqam->seqamsetval; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.objectId = seqam->seqamgetstate; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + referenced.objectId = seqam->seqamsetstate; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); +} + +/* + * Create a sequence access method record in pg_seqam catalog. + * + * Only superusers can create a sequence access methods. + */ +Oid +DefineSeqAM(List *names, List* definition) +{ + char *seqamname = strVal(linitial(names)); + Oid seqamoid; + ListCell *pl; + Relation rel; + Datum values[Natts_pg_seqam]; + bool nulls[Natts_pg_seqam]; + HeapTuple tuple; + + /* Must be super user. */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to create sequence access method \"%s\"", + seqamname), + errhint("Must be superuser to create a sequence access method."))); + + /* Must not already exist. */ + seqamoid = get_seqam_oid(seqamname, true); + if (OidIsValid(seqamoid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("sequence access method \"%s\" already exists", + seqamname))); + + /* Initialize the values. */ + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + + values[Anum_pg_seqam_seqamname - 1] = + DirectFunctionCall1(namein, CStringGetDatum(seqamname)); + + /* + * Loop over the definition list and extract the information we need. + */ + foreach(pl, definition) + { + DefElem *defel = (DefElem *) lfirst(pl); + + if (pg_strcasecmp(defel->defname, "reloptions") == 0) + { + values[Anum_pg_seqam_seqamreloptions - 1] = + get_seqam_func(defel, Anum_pg_seqam_seqamreloptions); + } + else if (pg_strcasecmp(defel->defname, "init") == 0) + { + values[Anum_pg_seqam_seqaminit - 1] = + get_seqam_func(defel, Anum_pg_seqam_seqaminit); + } + else if (pg_strcasecmp(defel->defname, "alloc") == 0) + { + values[Anum_pg_seqam_seqamalloc - 1] = + get_seqam_func(defel, Anum_pg_seqam_seqamalloc); + } + else if (pg_strcasecmp(defel->defname, "setval") == 0) + { + values[Anum_pg_seqam_seqamsetval - 1] = + get_seqam_func(defel, Anum_pg_seqam_seqamsetval); + } + else if (pg_strcasecmp(defel->defname, "getstate") == 0) + { + values[Anum_pg_seqam_seqamgetstate - 1] = + get_seqam_func(defel, Anum_pg_seqam_seqamgetstate); + } + else if (pg_strcasecmp(defel->defname, "setstate") == 0) + { + values[Anum_pg_seqam_seqamsetstate - 1] = + get_seqam_func(defel, Anum_pg_seqam_seqamsetstate); + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("sequence access method parameter \"%s\" not recognized", + defel->defname))); + } + + /* + * Validation. + */ + if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamreloptions - 1]))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("sequence access method reloptions function is required"))); + + if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqaminit - 1]))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("sequence access method init function is required"))); + + if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamalloc - 1]))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("sequence access method alloc function is required"))); + + if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetval - 1]))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("sequence access method setval function is required"))); + + if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamgetstate - 1]))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("sequence access method getstate function is required"))); + + if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetstate - 1]))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("sequence access method setstate function is required"))); + + /* + * Insert tuple into pg_seqam. + */ + rel = heap_open(SeqAccessMethodRelationId, RowExclusiveLock); + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + + seqamoid = simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); + + makeSeqAMDependencies(tuple); + + heap_freetuple(tuple); + + /* Post creation hook */ + InvokeObjectPostCreateHook(SeqAccessMethodRelationId, seqamoid, 0); + + heap_close(rel, RowExclusiveLock); + + return seqamoid; +} + +/* + * Drop a sequence access method. + */ +void +RemoveSeqAMById(Oid seqamoid) +{ + Relation rel; + HeapTuple tuple; + Form_pg_seqam seqam; + + /* + * Find the target tuple + */ + rel = heap_open(SeqAccessMethodRelationId, RowExclusiveLock); + + tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for sequence access method %u", + seqamoid); + + seqam = (Form_pg_seqam) GETSTRUCT(tuple); + /* Can't drop builtin local sequence access method. */ + if (seqamoid == LOCAL_SEQAM_OID) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for sequence access method %s", + NameStr(seqam->seqamname)))); + + /* + * Remove the pg_seqam tuple (this will roll back if we fail below) + */ + simple_heap_delete(rel, &tuple->t_self); + + ReleaseSysCache(tuple); + + heap_close(rel, RowExclusiveLock); +} + +/* * get_seqam_oid - given a sequence AM name, look up the OID * * If missing_ok is false, throw an error if SeqAM name not found. If true, diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index ec4ba39..d268dc5 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -16,6 +16,7 @@ #include "access/htup_details.h" #include "access/xact.h" +#include "access/seqam.h" #include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" @@ -158,7 +159,8 @@ static const Oid object_classes[MAX_OCLASS] = { DefaultAclRelationId, /* OCLASS_DEFACL */ ExtensionRelationId, /* OCLASS_EXTENSION */ EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */ - PolicyRelationId /* OCLASS_POLICY */ + PolicyRelationId, /* OCLASS_POLICY */ + SeqAccessMethodRelationId /* OCLASS_SEQAM */ }; @@ -1270,6 +1272,10 @@ doDeletion(const ObjectAddress *object, int flags) DropTransformById(object->objectId); break; + case OCLASS_SEQAM: + RemoveSeqAMById(object->objectId); + break; + default: elog(ERROR, "unrecognized object class: %u", object->classId); @@ -2399,6 +2405,9 @@ getObjectClass(const ObjectAddress *object) case TransformRelationId: return OCLASS_TRANSFORM; + + case SeqAccessMethodRelationId: + return OCLASS_SEQAM; } /* shouldn't get here */ diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index cbb0f5f..b3ddefc 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -437,6 +437,18 @@ static const ObjectPropertyType ObjectProperty[] = Anum_pg_type_typacl, ACL_KIND_TYPE, true + }, + { + SeqAccessMethodRelationId, + SeqAMOidIndexId, + SEQAMOID, + SEQAMNAME, + Anum_pg_seqam_seqamname, + InvalidAttrNumber, + InvalidAttrNumber, + InvalidAttrNumber, + -1, + true } }; @@ -536,7 +548,9 @@ ObjectTypeMap[] = /* OCLASS_EVENT_TRIGGER */ { "event trigger", OBJECT_EVENT_TRIGGER }, /* OCLASS_POLICY */ - { "policy", OBJECT_POLICY } + { "policy", OBJECT_POLICY }, + /* OCLASS_SEQAM */ + { "sequence access method", OBJECT_SEQAM } }; const ObjectAddress InvalidObjectAddress = @@ -691,6 +705,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, case OBJECT_FDW: case OBJECT_FOREIGN_SERVER: case OBJECT_EVENT_TRIGGER: + case OBJECT_SEQAM: address = get_object_address_unqualified(objtype, objname, missing_ok); break; @@ -942,6 +957,9 @@ get_object_address_unqualified(ObjectType objtype, case OBJECT_EVENT_TRIGGER: msg = gettext_noop("event trigger name cannot be qualified"); break; + case OBJECT_SEQAM: + msg = gettext_noop("sequence access method name cannot be qualified"); + break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); msg = NULL; /* placate compiler */ @@ -1002,6 +1020,11 @@ get_object_address_unqualified(ObjectType objtype, address.objectId = get_event_trigger_oid(name, missing_ok); address.objectSubId = 0; break; + case OBJECT_SEQAM: + address.classId = SeqAccessMethodRelationId; + address.objectId = get_seqam_oid(name, missing_ok); + address.objectSubId = 0; + break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); /* placate compiler, which doesn't know elog won't return */ @@ -2074,6 +2097,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, break; case OBJECT_TSPARSER: case OBJECT_TSTEMPLATE: + case OBJECT_SEQAM: /* We treat these object types as being owned by superusers */ if (!superuser_arg(roleid)) ereport(ERROR, @@ -3024,6 +3048,21 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_SEQAM: + { + HeapTuple tup; + + tup = SearchSysCache1(SEQAMOID, + ObjectIdGetDatum(object->objectId)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for sequence access method %u", + object->objectId); + appendStringInfo(&buffer, _("sequence access method %s"), + NameStr(((Form_pg_seqam) GETSTRUCT(tup))->seqamname)); + ReleaseSysCache(tup); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, @@ -3501,6 +3540,10 @@ getObjectTypeDescription(const ObjectAddress *object) appendStringInfoString(&buffer, "policy"); break; + case OCLASS_SEQAM: + appendStringInfoString(&buffer, "sequence access method"); + break; + default: appendStringInfo(&buffer, "unrecognized %u", object->classId); break; @@ -4423,6 +4466,27 @@ getObjectIdentityParts(const ObjectAddress *object, break; } + case OCLASS_SEQAM: + { + char *seqamname; + HeapTuple tup; + Form_pg_seqam seqamForm; + + tup = SearchSysCache1(SEQAMOID, + ObjectIdGetDatum(object->objectId)); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for sequence access method %u", + object->objectId); + seqamForm = (Form_pg_seqam) GETSTRUCT(tup); + seqamname = pstrdup(NameStr(seqamForm->seqamname)); + ReleaseSysCache(tup); + appendStringInfoString(&buffer, + quote_identifier(seqamname)); + if (objname) + *objname = list_make1(seqamname); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index 80a0eb6..1067ced 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -437,6 +437,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) } } break; + case OBJECT_SEQAM: + msg = gettext_noop("sequence access method \"%s\" does not exist, skipping"); + name = NameListToString(objname); + break; default: elog(ERROR, "unexpected object type (%d)", (int) objtype); break; diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 7658c06..e7177c9 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -84,6 +84,7 @@ typedef enum /* XXX merge this with ObjectTypeMap? */ static event_trigger_support_data event_trigger_support[] = { + {"ACCESS METHOD FOR SEQUENCES", true}, {"AGGREGATE", true}, {"CAST", true}, {"CONSTRAINT", true}, @@ -1102,6 +1103,7 @@ EventTriggerSupportsObjectType(ObjectType obtype) case OBJECT_POLICY: case OBJECT_RULE: case OBJECT_SCHEMA: + case OBJECT_SEQAM: case OBJECT_SEQUENCE: case OBJECT_TABCONSTRAINT: case OBJECT_TABLE: @@ -1164,6 +1166,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass) case OCLASS_DEFACL: case OCLASS_EXTENSION: case OCLASS_POLICY: + case OCLASS_SEQAM: return true; case MAX_OCLASS: diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 60b855a..9e3734d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -596,7 +596,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED - MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE + MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P + MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF @@ -5194,6 +5195,14 @@ DefineStmt: n->definition = list_make1(makeDefElem("from", (Node *) $5)); $$ = (Node *)n; } + | CREATE ACCESS METHOD FOR SEQUENCES name AS definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_SEQAM; + n->defnames = list_make1(makeString($6)); + n->definition = $8; + $$ = (Node *)n; + } ; definition: '(' def_list ')' { $$ = $2; } @@ -5648,6 +5657,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; } | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } | TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; } + | ACCESS METHOD FOR SEQUENCES { $$ = OBJECT_SEQAM; } ; any_name_list: @@ -13634,6 +13644,7 @@ unreserved_keyword: | MATCH | MATERIALIZED | MAXVALUE + | METHOD | MINUTE_P | MINVALUE | MODE diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index ce712d8..630e28d 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -18,6 +18,7 @@ #include "access/htup_details.h" #include "access/reloptions.h" +#include "access/seqam.h" #include "access/twophase.h" #include "access/xact.h" #include "access/xlog.h" @@ -1217,6 +1218,11 @@ ProcessUtilitySlow(Node *parsetree, address = DefineCollation(stmt->defnames, stmt->definition); break; + case OBJECT_SEQAM: + Assert(list_length(stmt->defnames) == 1); + Assert(stmt->args == NIL); + DefineSeqAM(stmt->defnames, stmt->definition); + break; default: elog(ERROR, "unrecognized define stmt type: %d", (int) stmt->kind); @@ -2154,6 +2160,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_TRANSFORM: tag = "DROP TRANSFORM"; break; + case OBJECT_SEQAM: + tag = "DROP ACCESS METHOD FOR SEQUENCES"; + break; default: tag = "???"; } @@ -2250,6 +2259,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_COLLATION: tag = "CREATE COLLATION"; break; + case OBJECT_SEQAM: + tag = "CREATE ACCESS METHOD FOR SEQUENCES"; + break; default: tag = "???"; } diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index d100514..fc76858 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -104,6 +104,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr) int numForeignServers; int numDefaultACLs; int numEventTriggers; + int numSeqAMs; if (g_verbose) write_msg(NULL, "reading schemas\n"); @@ -256,6 +257,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr) write_msg(NULL, "reading policies\n"); getPolicies(fout, tblinfo, numTables); + if (g_verbose) + write_msg(NULL, "reading sequence access methods\n"); + getSeqAMs(fout, &numSeqAMs); + *numTablesPtr = numTables; return tblinfo; } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b0ee6e5..3968f04 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -179,6 +179,7 @@ static void dumpEventTrigger(Archive *fout, DumpOptions *dopt, EventTriggerInfo static void dumpTable(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo); static void dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo); static void dumpAttrDef(Archive *fout, DumpOptions *dopt, AttrDefInfo *adinfo); +static void dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *tbinfo); static void dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo); static void dumpSequenceData(Archive *fout, TableDataInfo *tdinfo); static void dumpIndex(Archive *fout, DumpOptions *dopt, IndxInfo *indxinfo); @@ -5996,6 +5997,71 @@ getRules(Archive *fout, int *numRules) } /* + * getSeqAMs: + * read all sequence access methods in the system catalogs and return them + * in the SeqAMInfo* structure + * + * numSeqAMs is set to the number of access methods read in + */ +SeqAMInfo * +getSeqAMs(Archive *fout, int *numSeqAMs) +{ + PGresult *res; + int ntups; + int i; + PQExpBuffer query; + SeqAMInfo *seqminfo; + int i_tableoid, + i_oid, + i_seqamname; + + /* Before 9.5, there were no sequence access methods */ + if (fout->remoteVersion < 90500) + { + *numSeqAMs = 0; + return NULL; + } + + query = createPQExpBuffer(); + + appendPQExpBuffer(query, + "SELECT tableoid, oid, seqamname " + "FROM pg_catalog.pg_seqam " + "WHERE oid != '%u'::pg_catalog.oid", + LOCAL_SEQAM_OID); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + *numSeqAMs = ntups; + + seqminfo = (SeqAMInfo *) pg_malloc(ntups * sizeof(SeqAMInfo)); + + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_seqamname = PQfnumber(res, "seqamname"); + + for (i = 0; i < ntups; i++) + { + seqminfo[i].dobj.objType = DO_SEQAM; + seqminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); + seqminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&seqminfo[i].dobj); + seqminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_seqamname)); + seqminfo[i].dobj.namespace = NULL; + + /* Decide whether we want to dump it */ + selectDumpableObject(&(seqminfo[i].dobj)); + } + + PQclear(res); + + destroyPQExpBuffer(query); + + return seqminfo; +} + +/* * getTriggers * get information about every trigger on a dumpable table * @@ -8355,6 +8421,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj) case DO_POLICY: dumpPolicy(fout, dopt, (PolicyInfo *) dobj); break; + case DO_SEQAM: + dumpSeqAM(fout, dopt, (SeqAMInfo *) dobj); + break; case DO_PRE_DATA_BOUNDARY: case DO_POST_DATA_BOUNDARY: /* never dumped, nothing to do */ @@ -14812,6 +14881,90 @@ findLastBuiltinOid_V70(Archive *fout) } /* + * dumpSeqAM + * write the declaration of one user-defined sequence access method + */ +static void +dumpSeqAM(Archive *fout, DumpOptions *dopt, SeqAMInfo *seqaminfo) +{ + PGresult *res; + PQExpBuffer q; + PQExpBuffer delq; + PQExpBuffer labelq; + PQExpBuffer query; + char *seqamreloptions; + char *seqaminit; + char *seqamalloc; + char *seqamsetval; + char *seqamgetstate; + char *seqamsetstate; + + /* Skip if not to be dumped */ + if (!seqaminfo->dobj.dump || dopt->dataOnly) + return; + + q = createPQExpBuffer(); + delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); + query = createPQExpBuffer(); + + appendPQExpBuffer(query, "SELECT seqamreloptions, seqaminit, " + "seqamalloc, seqamsetval, " + "seqamgetstate, seqamsetstate " + "FROM pg_catalog.pg_seqam " + "WHERE oid = '%u'::pg_catalog.oid", + seqaminfo->dobj.catId.oid); + + res = ExecuteSqlQueryForSingleRow(fout, query->data); + + seqamreloptions = PQgetvalue(res, 0, PQfnumber(res, "seqamreloptions")); + seqaminit = PQgetvalue(res, 0, PQfnumber(res, "seqaminit")); + seqamalloc = PQgetvalue(res, 0, PQfnumber(res, "seqamalloc")); + seqamsetval = PQgetvalue(res, 0, PQfnumber(res, "seqamsetval")); + seqamgetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamgetstate")); + seqamsetstate = PQgetvalue(res, 0, PQfnumber(res, "seqamsetstate")); + + appendPQExpBuffer(q, "CREATE ACCESS METHOD FOR SEQUENCES %s AS (\n", + fmtId(seqaminfo->dobj.name)); + + appendPQExpBuffer(q, " RELOPTIONS = %s,\n", seqamreloptions); + appendPQExpBuffer(q, " INIT = %s,\n", seqaminit); + appendPQExpBuffer(q, " ALLOC = %s,\n", seqamalloc); + appendPQExpBuffer(q, " SETVAL = %s,\n", seqamsetval); + appendPQExpBuffer(q, " GETSTATE = %s,\n", seqamgetstate); + appendPQExpBuffer(q, " SETSTATE = %s\n);\n", seqamsetstate); + + appendPQExpBuffer(delq, "DROP ACCESS METHOD FOR SEQUENCES %s", + fmtId(seqaminfo->dobj.name)); + + appendPQExpBuffer(labelq, "ACCESS METHOD FOR SEQUENCES %s", + fmtId(seqaminfo->dobj.name)); + + if (dopt->binary_upgrade) + binary_upgrade_extension_member(q, &seqaminfo->dobj, labelq->data); + + ArchiveEntry(fout, seqaminfo->dobj.catId, seqaminfo->dobj.dumpId, + seqaminfo->dobj.name, + NULL, + NULL, + "", + false, "ACCESS METHOD FOR SEQUENCES", SECTION_PRE_DATA, + q->data, delq->data, NULL, + NULL, 0, + NULL, NULL); + + /* Dump Parser Comments */ + dumpComment(fout, dopt, labelq->data, + NULL, "", + seqaminfo->dobj.catId, 0, seqaminfo->dobj.dumpId); + + PQclear(res); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); +} + +/* * dumpSequence * write the declaration (not data) of one user-defined sequence */ @@ -14913,8 +15066,8 @@ dumpSequence(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo) { PGresult *res2; - printfPQExpBuffer(query, "SELECT a.seqamname\n" - "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c\n" + printfPQExpBuffer(query, "SELECT a.seqamname " + "FROM pg_catalog.pg_seqam a, pg_catalog.pg_class c " "WHERE c.relam = a.oid AND c.oid = %u", tbinfo->dobj.catId.oid); @@ -16019,6 +16172,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs, case DO_FOREIGN_SERVER: case DO_TRANSFORM: case DO_BLOB: + case DO_SEQAM: /* Pre-data objects: must come before the pre-data boundary */ addObjectDependency(preDataBound, dobj->dumpId); break; diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 3d2b66a..141c684 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -77,7 +77,8 @@ typedef enum DO_POST_DATA_BOUNDARY, DO_EVENT_TRIGGER, DO_REFRESH_MATVIEW, - DO_POLICY + DO_POLICY, + DO_SEQAM } DumpableObjectType; typedef struct _dumpableObject @@ -305,6 +306,11 @@ typedef struct _ruleInfo /* reloptions is only set if we need to dump the options with the rule */ } RuleInfo; +typedef struct _seqamInfo +{ + DumpableObject dobj; +} SeqAMInfo; + typedef struct _triggerInfo { DumpableObject dobj; @@ -561,5 +567,6 @@ extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionIn int numExtensions); extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers); extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables); +extern SeqAMInfo *getSeqAMs(Archive *fout, int *numSeqAMs); #endif /* PG_DUMP_H */ diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index aa3e20a..ad81f0d 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -74,7 +74,8 @@ static const int oldObjectTypePriority[] = 13, /* DO_POST_DATA_BOUNDARY */ 20, /* DO_EVENT_TRIGGER */ 15, /* DO_REFRESH_MATVIEW */ - 21 /* DO_POLICY */ + 21, /* DO_POLICY */ + 5 /* DO_SEQAM */ }; /* @@ -124,7 +125,8 @@ static const int newObjectTypePriority[] = 25, /* DO_POST_DATA_BOUNDARY */ 32, /* DO_EVENT_TRIGGER */ 33, /* DO_REFRESH_MATVIEW */ - 34 /* DO_POLICY */ + 34, /* DO_POLICY */ + 17 /* DO_SEQAM */ }; static DumpId preDataBoundId; @@ -1469,6 +1471,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize) "POLICY (ID %d OID %u)", obj->dumpId, obj->catId.oid); return; + case DO_SEQAM: + snprintf(buf, bufsize, + "ACCESS METHOD FOR SEQUENCES %s (ID %d OID %u)", + obj->name, obj->dumpId, obj->catId.oid); + return; case DO_PRE_DATA_BOUNDARY: snprintf(buf, bufsize, "PRE-DATA BOUNDARY (ID %d)", diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h index 7baa47c..b6e1474 100644 --- a/src/include/access/seqam.h +++ b/src/include/access/seqam.h @@ -26,6 +26,9 @@ struct SequenceHandle; typedef struct SequenceHandle SequenceHandle; +extern Oid DefineSeqAM(List *names, List *definition); +extern void RemoveSeqAMById(Oid seqamoid); + extern HeapTuple seqam_init(Relation seqrel, HeapTuple tuple, int64 restart_value, bool restart_requested, bool is_init); diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 5da18c2..42a2fa4 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -149,6 +149,7 @@ typedef enum ObjectClass OCLASS_EVENT_TRIGGER, /* pg_event_trigger */ OCLASS_POLICY, /* pg_policy */ OCLASS_TRANSFORM, /* pg_transform */ + OCLASS_SEQAM, /* pg_seqam */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 10e2a1e..e4d81a7 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1298,6 +1298,7 @@ typedef enum ObjectType OBJECT_ROLE, OBJECT_RULE, OBJECT_SCHEMA, + OBJECT_SEQAM, OBJECT_SEQUENCE, OBJECT_TABCONSTRAINT, OBJECT_TABLE, diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index faea991..0ec3cb3 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -237,6 +237,7 @@ PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD) PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD) PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD) PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD) +PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD) PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD) PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD) PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD) -- 1.9.1