diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile index c32088f..aea4a14 100644 --- a/src/backend/access/Makefile +++ b/src/backend/access/Makefile @@ -8,6 +8,6 @@ subdir = src/backend/access top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -SUBDIRS = common gin gist hash heap index nbtree rmgrdesc spgist transam +SUBDIRS = common gin gist hash heap index nbtree rmgrdesc spgist transam sequence include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 456d746..14b2e98 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -262,6 +262,7 @@ static bool need_initialization = true; static void initialize_reloptions(void); static void parse_one_reloption(relopt_value *option, char *text_str, int text_len, bool validate); +static bytea *common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate); /* * initialize_reloptions @@ -796,6 +797,9 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions) case RELKIND_INDEX: options = index_reloptions(amoptions, datum, false); break; + case RELKIND_SEQUENCE: + options = sequence_reloptions(amoptions, datum, false); + break; case RELKIND_FOREIGN_TABLE: options = NULL; break; @@ -1203,13 +1207,31 @@ heap_reloptions(char relkind, Datum reloptions, bool validate) /* * Parse options for indexes. + */ +bytea * +index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate) +{ + return common_am_reloptions(amoptions, reloptions, validate); +} + +/* + * Parse options for sequences. + */ +bytea * +sequence_reloptions(RegProcedure amoptions, Datum reloptions, bool validate) +{ + return common_am_reloptions(amoptions, reloptions, validate); +} + +/* + * Parse options for indexes or sequences. * * amoptions Oid of option parser * reloptions options as text[] datum * validate error flag */ -bytea * -index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate) +static bytea * +common_am_reloptions(RegProcedure amoptions, Datum reloptions, bool validate) { FmgrInfo flinfo; FunctionCallInfoData fcinfo; diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile new file mode 100644 index 0000000..f782f7e --- /dev/null +++ b/src/backend/access/sequence/Makefile @@ -0,0 +1,17 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for access/sequence +# +# IDENTIFICATION +# src/backend/access/sequence/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/backend/access/sequence +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +OBJS = seqam.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c new file mode 100644 index 0000000..8b0c1e3 --- /dev/null +++ b/src/backend/access/sequence/seqam.c @@ -0,0 +1,278 @@ +/*------------------------------------------------------------------------- + * + * seqam.c + * sequence access method routines + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/sequence/seqam.c + * + * + * Sequence access method allows the SQLStandard Sequence objects to be + * managed according to either the default access method or a pluggable + * replacement. Each object refers to one access method, though there can + * be many different access methods in use if required. + * + * SQLStandard assumes that each Sequence object is completely controlled + * from the current database node, preventing any form of clustering + * mechanisms from controlling behaviour. Sequence access methods are + * general purpose though designed specifically to address the needs of + * Sequences working as part of a multi-node "cluster", though that is + * not defined here, nor are there dependencies on anything outside of + * this module, nor any particular form of clustering. + * + * The SQL Standard behaviour, also the historical PostgreSQL behaviour, + * is referred to as the "Local" SeqAm. That is also the basic default. + * Local SeqAm assumes that allocations from the sequence will be + * contiguous, so if user1 requests a range of values and is given + * 500-599 as values for their backend then the next user to make a + * request will be given a range starting with 600. + * + * The SeqAm mechanism allows us to override the Local behaviour, for + * use with clustering systems. When multiple masters can request + * ranges of values it would break the assumption of contiguous + * allocation. It seems likely that the SeqAm would also wish to control + * node-level caches for sequences to ensure good performance. + * The CACHE option is not affected by this, since that works per backend. + * + * SeqAm allows calls to allocate a new range of values, reset the + * sequence to a new value and to define options for the AM module. + * The on-disk format of Sequences is the same for all AMs. + * + * We currently assume that there is no persistent state held + * within the SeqAm, so it is safe to ALTER the access method of an + * object without taking any special actions, as long as we hold an + * AccessExclusiveLock while we do that. + * + * SeqAMs work similarly to IndexAMs in many ways. pg_class.relam stores + * the Oid of the SeqAM, just as we do for IndexAm. The relcache stores + * AM information in much the same way for indexes and sequences, and + * management of options is similar also. + * + * Note that the SeqAM API calls are synchronous. It is up to the SeqAM + * to decide how that is handled, for example, whether there is a higher + * level cache at instance level to amortise network traffic in cluster. + * + * The SeqAM is identified by Oid of corresponding tuple in pg_seqam. + * There is no syscache for pg_seqam, though the SeqAm data is stored + * on the relcache entry for the sequence. + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/seqam.h" +#include "access/reloptions.h" +#include "access/relscan.h" +#include "access/transam.h" +#include "catalog/pg_seqam.h" +#include "utils/guc.h" +#include "utils/rel.h" + +char *default_seq_mgr = NULL; + +#define GET_SEQAM_PROCEDURE(pname) \ +do { \ + procedure = &seqRelation->rd_aminfo->pname; \ + if (!OidIsValid(procedure->fn_oid)) \ + elog(ERROR, "invalid %s regproc", CppAsString(pname)); \ +} while(0) + +/*------------------------------------------------------------------------- + * + * Sequence Access Manager API + * + * INTERFACE ROUTINES + * sequence_alloc - allocate a new range of values for the sequence + * sequence_setval - coordinate the reset of a sequence to new value + * + * sequence_reloptions - process options - located in reloptions.c + * + *------------------------------------------------------------------------- + */ +void +sequence_alloc(Relation seqRelation, int64 start, int64 end) +{ + FmgrInfo *procedure; + GET_SEQAM_PROCEDURE(seqamalloc); + +#ifdef UNDEF + /* + * have the seqam's alloc proc do all the work. + */ + return DatumGetBool(FunctionCall6(procedure, + PointerGetDatum(seqRelation), + PointerGetDatum(values), + PointerGetDatum(isnull), + PointerGetDatum(heap_t_ctid), + PointerGetDatum(heapRelation), + Int32GetDatum((int32) checkUnique))); + FmgrInfo *procedure; + Oid seqam = seqrel->relam; + + Assert(RelationIsValid(seqrel)); + + if (!OidIsValid(seqam)) + seqam = default_sequence_am_Oid; + if (!OidIsValid(seqam)) + elog(ERROR, "invalid default_sequence_am_Oid"); +#endif +} + +/* + * sequence_setval allows the SeqAM to disallow resetting of a sequence, + * if required, by returning false. If we return true then the sequence + * value can be reset as specified, if we hear false from the SeqAM + * then we throw an ERROR instead. + * + * If isLocked is set it means we have an AccessExclusiveLock on the + * sequence, otherwise we are being called by a setval() function. + */ +void +sequence_setval(Relation seqRelation, int64 next, bool isLocked) +{ + FmgrInfo *procedure; + bool allowed; + + GET_SEQAM_PROCEDURE(seqamsetval); + + + + if (!allowed) + ereport(ERROR, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("sequence setval incomplete"))); +} + +/*------------------------------------------------------------ + * + * Sequence Access Manager management functions + * + *------------------------------------------------------------ + */ + +/* check_hook: validate new default_sequence_mgr */ +bool +check_default_seq_mgr(char **newval, void **extra, GucSource source) +{ + if (**newval == '\0') + return true; + +#ifdef UNDEF + /* + * If we aren't inside a transaction, we cannot do database access so + * cannot verify the name. Must accept the value on faith. + */ + if (IsTransactionState()) + { + if (!OidIsValid(get_seqam_oid(*newval, true))) + { + /* + * When source == PGC_S_TEST, we are checking the argument of an + * ALTER DATABASE SET or ALTER USER SET command. Value may + * be created later. Because of that, issue a NOTICE if source == + * PGC_S_TEST, but accept the value anyway. + */ + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("sequence manager \"%s\" does not exist", + *newval))); + } + else + { + GUC_check_errdetail("sequence manager \"%s\" does not exist.", + *newval); + return false; + } + } + } +#endif + + return true; +} + +#ifdef UNDEF +/* + * GetDefaultSeqAM -- get the OID of the current default sequence AM + * + * This exists to hide (and possibly optimize the use of) the + * default_tablespace GUC variable. + */ +Oid +GetDefaultTablespace(char relpersistence) +{ + Oid result; + + /* Fast path for default_tablespace == "" */ + if (default_tablespace == NULL || default_tablespace[0] == '\0') + return InvalidOid; + + /* + * It is tempting to cache this lookup for more speed, but then we would + * fail to detect the case where the tablespace was dropped since the GUC + * variable was set. Note also that we don't complain if the value fails + * to refer to an existing tablespace; we just silently return InvalidOid, + * causing the new object to be created in the database's tablespace. + */ + result = get_tablespace_oid(default_tablespace, true); + + /* + * Allow explicit specification of database's default tablespace in + * default_tablespace without triggering permissions checks. + */ + if (result == MyDatabaseTableSpace) + result = InvalidOid; + return result; +} +#endif +/*------------------------------------------------------------ + * + * Sequence Access Manager = LOCAL functions + * + *------------------------------------------------------------ + */ + +/* + * seqam_local_alloc() + * + * Allocate new range of values for a local sequence. + */ +void +seqam_local_alloc(void) +{ + /* No actions required */ +} + +/* + * seqam_local_setval() + * + * Coordinate the setting of a local sequence. + */ +bool +seqam_local_setval(Relation seqRelation, int64 next, bool isLocked) +{ + return true; +} + +/* + * seqam_local_options() + * + * Verify the options of a local sequence. + */ +Datum +seqam_local_options(PG_FUNCTION_ARGS) +{ + Datum reloptions = PG_GETARG_DATUM(0); + bool validate = PG_GETARG_BOOL(1); + bytea *result; + + result = default_reloptions(reloptions, validate, RELOPT_KIND_SEQUENCE); + if (result) + PG_RETURN_BYTEA_P(result); + PG_RETURN_NULL(); +} diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index df6da1f..d923fc3 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_statistic.h pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ + pg_authid.h pg_auth_members.h pg_seqam.h pg_shdepend.h pg_shdescription.h \ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 1f2546d..203993f 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -14,11 +14,14 @@ */ #include "postgres.h" +#include "access/reloptions.h" +#include "access/seqam.h" #include "access/transam.h" #include "access/htup_details.h" #include "access/xlogutils.h" #include "catalog/dependency.h" #include "catalog/namespace.h" +#include "catalog/pg_seqam.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "commands/sequence.h" @@ -93,10 +96,12 @@ static Relation open_share_lock(SeqTable seq); static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel); static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple); -static void init_params(List *options, bool isInit, +static void init_params(List *params, bool isInit, Form_pg_sequence new, List **owned_by); static void do_setval(Oid relid, int64 next, bool iscalled); static void process_owned_by(Relation seqrel, List *owned_by); +static void seqrel_update_relam(Oid seqoid, Oid seqamid); +static Oid init_options(char *accessMethod, List *options); /* @@ -110,6 +115,7 @@ DefineSequence(CreateSeqStmt *seq) List *owned_by; CreateStmt *stmt = makeNode(CreateStmt); Oid seqoid; + Oid seqamid; Relation rel; HeapTuple tuple; TupleDesc tupDesc; @@ -124,8 +130,9 @@ DefineSequence(CreateSeqStmt *seq) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unlogged sequences are not supported"))); - /* Check and set all option values */ - init_params(seq->options, true, &new, &owned_by); + /* Check and set all param values */ + init_params(seq->params, true, &new, &owned_by); + seqamid = init_options(seq->accessMethod, seq->options); /* * Create relation (and fill value[] and null[] for the tuple) @@ -208,7 +215,8 @@ DefineSequence(CreateSeqStmt *seq) stmt->relation = seq->sequence; stmt->inhRelations = NIL; stmt->constraints = NIL; - stmt->options = list_make1(defWithOids(false)); + stmt->options = seq->options; +// stmt->options = list_make1(defWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; stmt->if_not_exists = false; @@ -227,6 +235,14 @@ DefineSequence(CreateSeqStmt *seq) if (owned_by) process_owned_by(rel, owned_by); + /* + * After we've created the sequence's relation in pg_class, update + * the relam to a non-default value, if requested. We perform this + * as a separate update to avoid invasive changes in normal code + * paths and to keep the code similar between CREATE and ALTER. + */ + seqrel_update_relam(seqoid, seqamid); + heap_close(rel, NoLock); return seqoid; @@ -271,6 +287,11 @@ ResetSequence(Oid seq_relid) UnlockReleaseBuffer(buf); /* + * Check whether we can reset the sequence value with lock held. + */ + sequence_setval(seq_rel, seq->start_value, true); + + /* * Modify the copied tuple to execute the restart (compare the RESTART * action in AlterSequence) */ @@ -406,6 +427,7 @@ Oid AlterSequence(AlterSeqStmt *stmt) { Oid relid; + Oid seqamid; SeqTable elm; Relation seqrel; Buffer buf; @@ -434,11 +456,17 @@ AlterSequence(AlterSeqStmt *stmt) /* lock page' buffer and read tuple into new sequence structure */ seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); - /* Copy old values of options into workspace */ + /* Copy old values of params into workspace */ memcpy(&new, seq, sizeof(FormData_pg_sequence)); /* Check and set new values */ - init_params(stmt->options, false, &new, &owned_by); + init_params(stmt->params, false, &new, &owned_by); + seqamid = init_options(stmt->accessMethod, stmt->options); + + /* + * Check whether we can reset the sequence value with lock held. + */ + sequence_setval(seqrel, new.last_value, true); /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ @@ -484,6 +512,11 @@ AlterSequence(AlterSeqStmt *stmt) if (owned_by) process_owned_by(seqrel, owned_by); + /* + * Change the SeqAm, if requested, using a transactional update. + */ + seqrel_update_relam(relid, seqamid); + relation_close(seqrel, NoLock); return relid; @@ -872,6 +905,11 @@ do_setval(Oid relid, int64 next, bool iscalled) bufm, bufx))); } + /* + * Check whether we can reset the sequence value without lock held. + */ + sequence_setval(seqrel, next, false); + /* Set the currval() state only if iscalled = true */ if (iscalled) { @@ -1127,15 +1165,15 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple) } /* - * init_params: process the options list of CREATE or ALTER SEQUENCE, + * init_params: process the params list of CREATE or ALTER SEQUENCE, * and store the values into appropriate fields of *new. Also set - * *owned_by to any OWNED BY option, or to NIL if there is none. + * *owned_by to any OWNED BY param, or to NIL if there is none. * - * If isInit is true, fill any unspecified options with default values; - * otherwise, do not change existing options that aren't explicitly overridden. + * If isInit is true, fill any unspecified params with default values; + * otherwise, do not change existing params that aren't explicitly overridden. */ static void -init_params(List *options, bool isInit, +init_params(List *params, bool isInit, Form_pg_sequence new, List **owned_by) { DefElem *start_value = NULL; @@ -1145,13 +1183,13 @@ init_params(List *options, bool isInit, DefElem *min_value = NULL; DefElem *cache_value = NULL; DefElem *is_cycled = NULL; - ListCell *option; + ListCell *param; *owned_by = NIL; - foreach(option, options) + foreach(param, params) { - DefElem *defel = (DefElem *) lfirst(option); + DefElem *defel = (DefElem *) lfirst(param); if (strcmp(defel->defname, "increment") == 0) { @@ -1396,7 +1434,7 @@ init_params(List *options, bool isInit, } /* - * Process an OWNED BY option for CREATE/ALTER SEQUENCE + * Process an OWNED BY param for CREATE/ALTER SEQUENCE * * Ownership permissions on the sequence are already checked, * but if we are establishing a new owned-by dependency, we must @@ -1544,6 +1582,39 @@ pg_sequence_parameters(PG_FUNCTION_ARGS) return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull)); } +/* + * Update pg_class row for sequence to record change in relam. + * + * Call only while holding AccessExclusiveLock on sequence. + * + * Note that this is a transactional update of pg_class, rather + * than a non-transactional update of the tuple in the sequence's + * heap, as occurs elsewhere in this module. + */ +static void +seqrel_update_relam(Oid seqoid, Oid seqamid) +{ + Relation rd; + HeapTuple ctup; + Form_pg_class pgcform; + + rd = heap_open(RelationRelationId, RowExclusiveLock); + + /* Fetch a copy of the tuple to scribble on */ + ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(seqoid)); + if (!HeapTupleIsValid(ctup)) + elog(ERROR, "pg_class entry for sequence %u unavailable", + seqoid); + pgcform = (Form_pg_class) GETSTRUCT(ctup); + + if (pgcform->relam != seqamid) + { + pgcform->relam = seqamid; + simple_heap_update(rd, &ctup->t_self, ctup); + } + + heap_close(rd, RowExclusiveLock); +} void seq_redo(XLogRecPtr lsn, XLogRecord *record) @@ -1599,3 +1670,34 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record) pfree(localpage); } + +static Oid +init_options(char *accessMethod, List *options) +{ + Oid seqamid; + Datum reloptions; + Form_pg_seqam seqamForm; + HeapTuple tuple; + + /* + * Parse AM-specific options, convert to text array form, + * retrieve the AM-option function and then validate. + */ + reloptions = transformRelOptions((Datum) 0, options, + NULL, NULL, false, false); + /* look up the access method */ + tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(accessMethod)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("access method \"%s\" does not exist", + accessMethod))); + seqamid = HeapTupleGetOid(tuple); + seqamForm = (Form_pg_seqam) GETSTRUCT(tuple); + + (void) sequence_reloptions(seqamForm->seqamoptions, reloptions, true); + + ReleaseSysCache(tuple); + + return seqamid; +} diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cad8311..6977949 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8529,6 +8529,9 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, case RELKIND_INDEX: (void) index_reloptions(rel->rd_am->amoptions, newOptions, true); break; + case RELKIND_SEQUENCE: + (void) sequence_reloptions(rel->rd_am->amoptions, newOptions, true); + break; default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 51fdb63..6598f76 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3237,8 +3237,10 @@ _copyCreateSeqStmt(const CreateSeqStmt *from) CreateSeqStmt *newnode = makeNode(CreateSeqStmt); COPY_NODE_FIELD(sequence); + COPY_NODE_FIELD(params); COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(ownerId); + COPY_STRING_FIELD(accessMethod); return newnode; } @@ -3249,8 +3251,10 @@ _copyAlterSeqStmt(const AlterSeqStmt *from) AlterSeqStmt *newnode = makeNode(AlterSeqStmt); COPY_NODE_FIELD(sequence); + COPY_NODE_FIELD(params); COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(missing_ok); + COPY_STRING_FIELD(accessMethod); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 4b219b3..8fbdc64 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1532,8 +1532,10 @@ static bool _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b) { COMPARE_NODE_FIELD(sequence); + COMPARE_NODE_FIELD(params); COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(ownerId); + COMPARE_STRING_FIELD(accessMethod); return true; } @@ -1542,8 +1544,10 @@ static bool _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b) { COMPARE_NODE_FIELD(sequence); + COMPARE_NODE_FIELD(params); COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_STRING_FIELD(accessMethod); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 76ef11e..f363fcb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -53,6 +53,7 @@ #include "catalog/index.h" #include "catalog/namespace.h" +#include "catalog/pg_seqam.h" #include "catalog/pg_trigger.h" #include "commands/defrem.h" #include "commands/trigger.h" @@ -3195,7 +3196,33 @@ CreateSeqStmt: CreateSeqStmt *n = makeNode(CreateSeqStmt); $4->relpersistence = $2; n->sequence = $4; - n->options = $5; + n->params = $5; + n->accessMethod = DEFAULT_SEQAM; + n->options = NIL; + n->ownerId = InvalidOid; + $$ = (Node *)n; + } + | CREATE OptTemp SEQUENCE qualified_name OptSeqOptList + USING access_method + { + CreateSeqStmt *n = makeNode(CreateSeqStmt); + $4->relpersistence = $2; + n->sequence = $4; + n->params = $5; + n->accessMethod = $7; + n->options = NIL; + n->ownerId = InvalidOid; + $$ = (Node *)n; + } + | CREATE OptTemp SEQUENCE qualified_name OptSeqOptList + USING access_method WITH reloptions + { + CreateSeqStmt *n = makeNode(CreateSeqStmt); + $4->relpersistence = $2; + n->sequence = $4; + n->params = $5; + n->accessMethod = $7; + n->options = $9; n->ownerId = InvalidOid; $$ = (Node *)n; } @@ -3206,7 +3233,31 @@ AlterSeqStmt: { AlterSeqStmt *n = makeNode(AlterSeqStmt); n->sequence = $3; - n->options = $4; + n->params = $4; + n->accessMethod = DEFAULT_SEQAM; + n->options = NIL; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name SeqOptList + USING access_method + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $3; + n->params = $4; + n->accessMethod = $6; + n->options = NIL; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name SeqOptList + USING access_method WITH reloptions + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $3; + n->params = $4; + n->accessMethod = $6; + n->options = $8; n->missing_ok = false; $$ = (Node *)n; } @@ -3214,11 +3265,34 @@ AlterSeqStmt: { AlterSeqStmt *n = makeNode(AlterSeqStmt); n->sequence = $5; - n->options = $6; + n->params = $6; + n->accessMethod = DEFAULT_SEQAM; + n->options = NIL; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name SeqOptList + USING access_method + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $5; + n->params = $6; + n->accessMethod = $8; + n->options = NIL; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name SeqOptList + USING access_method WITH reloptions + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $5; + n->params = $6; + n->accessMethod = $8; + n->options = $10; n->missing_ok = true; $$ = (Node *)n; } - ; OptSeqOptList: SeqOptList { $$ = $1; } @@ -3277,7 +3351,7 @@ SeqOptElem: CACHE NumericOnly { $$ = makeDefElem("restart", (Node *)$3); } - ; + ; opt_by: BY {} | /* empty */ {} diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 10a3be5..e642832 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -391,7 +391,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) */ seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; + seqstmt->params = NIL; /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned @@ -417,7 +417,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) attnamelist = list_make3(makeString(snamespace), makeString(cxt->relation->relname), makeString(column->colname)); - altseqstmt->options = list_make1(makeDefElem("owned_by", + altseqstmt->params = list_make1(makeDefElem("owned_by", (Node *) attnamelist)); cxt->alist = lappend(cxt->alist, altseqstmt); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 25f50e5..c4af2d7 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1005,10 +1005,12 @@ IndexScanOK(CatCache *cache, ScanKey cur_skey) case AMOID: case AMNAME: + case SEQAMOID: + case SEQAMNAME: /* - * Always do heap scans in pg_am, because it's so small there's - * not much point in an indexscan anyway. We *must* do this when + * Always do heap scans in pg_am and pg_seqam, because they are + * too small to benefit from an indexscan. We *must* do this when * initially building critical relcache entries, but we might as * well just always do it. */ diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index bfc3c86..03a9db9 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -47,6 +47,7 @@ #include "catalog/pg_proc.h" #include "catalog/pg_range.h" #include "catalog/pg_rewrite.h" +#include "catalog/pg_seqam.h" #include "catalog/pg_statistic.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_ts_config.h" @@ -624,6 +625,28 @@ static const struct cachedesc cacheinfo[] = { }, 1024 }, + {SeqAccessMethodRelationId, /* SEQAMNAME */ + SeqAmNameIndexId, + 1, + { + Anum_pg_seqam_amname, + 0, + 0, + 0 + }, + 4 + }, + {SeqAccessMethodRelationId, /* SEQAMOID */ + SeqAmOidIndexId, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 4 + }, {StatisticRelationId, /* STATRELATTINH */ StatisticRelidAttnumInhIndexId, 3, diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ac5e4f3..60a8d8f 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -120,6 +120,7 @@ extern bool Log_disconnections; extern int CommitDelay; extern int CommitSiblings; +extern char *default_seq_mgr; extern char *default_tablespace; extern char *temp_tablespaces; extern bool synchronize_seqscans; @@ -2597,6 +2598,17 @@ static struct config_string ConfigureNamesString[] = }, { + {"default_sequence_mgr", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the default sequence manager for any new sequences."), + gettext_noop("An empty string selects the 'local' sequence manager."), + GUC_IS_NAME | GUC_NOT_IN_SAMPLE + }, + &default_seq_mgr, + "", + check_default_seq_mgr, NULL, NULL + }, + + { {"temp_tablespaces", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the tablespace(s) to use for temporary tables and sort files."), NULL, diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index 5a4664b..ef523b7 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -45,8 +45,10 @@ typedef enum relopt_kind RELOPT_KIND_TABLESPACE = (1 << 7), RELOPT_KIND_SPGIST = (1 << 8), RELOPT_KIND_VIEW = (1 << 9), + RELOPT_KIND_SEQUENCE = (1 << 10), + /* if you add a new kind, make sure you update "last_default" too */ - RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_VIEW, + RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_SEQUENCE, /* some compilers treat enums as signed ints, so we can't use 1 << 31 */ RELOPT_KIND_MAX = (1 << 30) } relopt_kind; @@ -270,6 +272,8 @@ extern bytea *default_reloptions(Datum reloptions, bool validate, extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate); extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate); +extern bytea *sequence_reloptions(RegProcedure amoptions, Datum reloptions, + bool validate); extern bytea *attribute_reloptions(Datum reloptions, bool validate); extern bytea *tablespace_reloptions(Datum reloptions, bool validate); diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h new file mode 100644 index 0000000..750cdc2 --- /dev/null +++ b/src/include/access/seqam.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * seqam.h + * Public header file for Sequence access method. + * + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/seqam.h + * + *------------------------------------------------------------------------- + */ +#ifndef SEQAM_H +#define SEQAM_H + +#include "access/genam.h" +#include "utils/relcache.h" + +extern void sequence_alloc(Relation seqRelation, int64 start, int64 end); +extern void sequence_setval(Relation seqRelation, int64 next, bool isLocked); + +extern void seqam_local_alloc(void); +extern bool seqam_local_setval(Relation seqRelation, int64 next, bool isLocked); +extern Datum seqam_local_options(PG_FUNCTION_ARGS); + +#endif /* SEQAM_H */ diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 6251fb8..82eaaf8 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -211,6 +211,11 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops)); #define RewriteRelRulenameIndexId 2693 +DECLARE_UNIQUE_INDEX(pg_seqam_name_index, 3480, on pg_seqam using btree(seqamname name_ops)); +#define SeqAmNameIndexId 3480 +DECLARE_UNIQUE_INDEX(pgseq_am_oid_index, 3481, on pg_seqam using btree(oid oid_ops)); +#define SeqAmOidIndexId 3481 + /* This following index is not used for a cache and is not unique */ DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops)); #define SharedDependDependerIndexId 1232 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 010605d..4048c7d 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4677,6 +4677,12 @@ DESCR("SP-GiST support for quad tree over range"); DATA(insert OID = 3473 ( spg_range_quad_leaf_consistent PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ spg_range_quad_leaf_consistent _null_ _null_ _null_ )); DESCR("SP-GiST support for quad tree over range"); +DATA(insert OID = 3480 ( seqam_local_alloc PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ seqam_local_alloc _null_ _null_ _null_ )); +DESCR("Local SequenceAM allocation"); +DATA(insert OID = 3481 ( seqam_local_setval PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ seqam_local_setval _null_ _null_ _null_ )); +DESCR("Local SequenceAM setval"); +DATA(insert OID = 3482 ( seqam_local_options PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ seqam_local_options _null_ _null_ _null_ )); +DESCR("Local SequenceAM options"); /* * Symbolic values for provolatile column: these indicate whether the result diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h new file mode 100644 index 0000000..44f1c58 --- /dev/null +++ b/src/include/catalog/pg_seqam.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + * + * pg_seqam.h + * definition of the system "sequence access method" relation (pg_seqam) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/pg_seqam.h + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + * XXX do NOT break up DATA() statements into multiple lines! + * the scripts are not as smart as you might think... + * + *------------------------------------------------------------------------- + */ +#ifndef PG_SEQAM_H +#define PG_SEQAM_H + +#include "catalog/genbki.h" + +/* ---------------- + * pg_seqam definition. cpp turns this into + * typedef struct FormData_pg_seqam + * ---------------- + */ +#define SeqAccessMethodRelationId 3819 + +CATALOG(pg_seqam,3819) +{ + NameData seqamname; /* access method name */ + regproc seqamalloc; /* get next allocation of range of values function */ + regproc seqamsetval; /* set value function */ + regproc seqamoptions; /* parse AM-specific parameters */ +} FormData_pg_seqam; + +/* ---------------- + * Form_pg_seqam corresponds to a pointer to a tuple with + * the format of pg_seqam relation. + * ---------------- + */ +typedef FormData_pg_seqam *Form_pg_seqam; + +/* ---------------- + * compiler constants for pg_seqam + * ---------------- + */ +#define Natts_pg_seqam 4 +#define Anum_pg_seqam_amname 1 +#define Anum_pg_seqam_amalloc 2 +#define Anum_pg_seqam_amsetval 3 +#define Anum_pg_seqam_amoptions 4 + +/* ---------------- + * initial contents of pg_seqam + * ---------------- + */ + +DATA(insert OID = 3839 ( local seqam_local_alloc seqam_local_setval seqam_local_options)); +DESCR("local sequence access method"); +#define LOCAL_SEQAM_OID 3839 + +#define DEFAULT_SEQAM "" + +#endif /* PG_SEQAM_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 56cf592..e8d43ee 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1833,16 +1833,20 @@ typedef struct CreateSeqStmt { NodeTag type; RangeVar *sequence; /* the sequence to create */ - List *options; + List *params; /* Pre-9.3, fixed params were called options */ + List *options; /* WITH clause options: a list of DefElem */ Oid ownerId; /* ID of owner, or InvalidOid for default */ + char *accessMethod; /* USING name of access method (eg. Local) */ } CreateSeqStmt; typedef struct AlterSeqStmt { NodeTag type; RangeVar *sequence; /* the sequence to alter */ - List *options; + List *params; /* Pre-9.3, fixed params were called options */ + List *options; /* WITH clause options: a list of DefElem */ bool missing_ok; /* skip error if a role is missing? */ + char *accessMethod; /* USING name of access method (eg. Local) */ } AlterSeqStmt; /* ---------------------- diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 0023c00..e6bcfbf 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -371,6 +371,7 @@ extern void GUC_check_errcode(int sqlerrcode); */ /* in commands/tablespace.c */ +extern bool check_default_seq_mgr(char **newval, void **extra, GucSource source); extern bool check_default_tablespace(char **newval, void **extra, GucSource source); extern bool check_temp_tablespaces(char **newval, void **extra, GucSource source); extern void assign_temp_tablespaces(const char *newval, void *extra); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index bde5f17..c208dfe 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -48,10 +48,12 @@ typedef LockInfoData *LockInfo; /* * Cached lookup information for the index access method functions defined - * by the pg_am row associated with an index relation. + * by the pg_am row associated with an index relation, or the pg_seqam + * row associated with a sequence relation. */ typedef struct RelationAmInfo { + /* pg_am only */ FmgrInfo aminsert; FmgrInfo ambeginscan; FmgrInfo amgettuple; @@ -66,6 +68,12 @@ typedef struct RelationAmInfo FmgrInfo amvacuumcleanup; FmgrInfo amcanreturn; FmgrInfo amcostestimate; + + /* pg_seqam only */ + FmgrInfo seqamalloc; + FmgrInfo seqamsetval; + + /* Common */ FmgrInfo amoptions; } RelationAmInfo; @@ -134,22 +142,22 @@ typedef struct RelationData Form_pg_am rd_am; /* pg_am tuple for index's AM */ /* - * index access support info (used only for an index relation) + * Access support info (used only for index or sequence relations) * * Note: only default support procs for each opclass are cached, namely * those with lefttype and righttype equal to the opclass's opcintype. The * arrays are indexed by support function number, which is a sufficient * identifier given that restriction. * - * Note: rd_amcache is available for index AMs to cache private data about - * an index. This must be just a cache since it may get reset at any time + * Note: rd_amcache is available for AMs to cache private data about + * an object. This must be just a cache since it may get reset at any time * (in particular, it will get reset by a relcache inval message for the - * index). If used, it must point to a single memory chunk palloc'd in + * object). If used, it must point to a single memory chunk palloc'd in * rd_indexcxt. A relcache reset will include freeing that chunk and * setting rd_amcache = NULL. */ MemoryContext rd_indexcxt; /* private memory cxt for this stuff */ - RelationAmInfo *rd_aminfo; /* lookup info for funcs found in pg_am */ + RelationAmInfo *rd_aminfo; /* lookup info for funcs found in pg_am or pg_seqam */ Oid *rd_opfamily; /* OIDs of op families for each index col */ Oid *rd_opcintype; /* OIDs of opclass declared input data types */ RegProcedure *rd_support; /* OIDs of support procedures */ @@ -160,7 +168,7 @@ typedef struct RelationData Oid *rd_exclops; /* OIDs of exclusion operators, if any */ Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */ uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */ - void *rd_amcache; /* available for use by index AM */ + void *rd_amcache; /* available for use by AM */ Oid *rd_indcollation; /* OIDs of index collations */ /* diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index d1d8abe..d47aec1 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -78,6 +78,8 @@ enum SysCacheIdentifier RELNAMENSP, RELOID, RULERELNAME, + SEQAMNAME, + SEQAMOID, STATRELATTINH, TABLESPACEOID, TSCONFIGMAP,