diff options
author | Andres Freund | 2013-05-20 14:18:35 +0000 |
---|---|---|
committer | Andres Freund | 2013-11-05 17:24:48 +0000 |
commit | dd36779511ed0c52926cd44cb52657f73d43d891 (patch) | |
tree | b2cb1801721fa5adf100fee9d13b472c19a0cd3b | |
parent | 732758db4c8226b74a6ea7a90bc8c3cd15f5fe86 (diff) |
seqam 0.2seqam-0.2
30 files changed, 1104 insertions, 355 deletions
diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile index c32088f81d..aea4a149df 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 b5fd30a4f9..950c291836 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -274,6 +274,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 @@ -809,6 +810,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; @@ -1219,13 +1223,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 0000000000..f782f7e4f2 --- /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 0000000000..3325351694 --- /dev/null +++ b/src/backend/access/sequence/seqam.c @@ -0,0 +1,251 @@ +/*------------------------------------------------------------------------- + * + * 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 SQL Standard Sequence objects to be + * managed according to either the default access method or a pluggable + * replacement. Each sequence can only use one access method, but different + * sequences can use different access methods at the same time. + * + * The SQL Standard 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 "access/xact.h" +#include "catalog/pg_seqam.h" +#include "utils/guc.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +char *default_seqam = NULL; + +#define GET_SEQAM_PROCEDURE(pname) \ +do { \ + procedure = &seqRelation->rd_aminfo->pname; \ + if (!OidIsValid(procedure->fn_oid)) \ + { \ + RegProcedure procOid = seqRelation->rd_seqam->pname; \ + if (!RegProcedureIsValid(procOid)) \ + elog(ERROR, "invalid %s regproc", CppAsString(pname)); \ + fmgr_info_cxt(procOid, procedure, seqRelation->rd_indexcxt); \ + } \ +} while(0) + +/*------------------------------------------------------------------------- + * + * Sequence Access Manager API + * + * INTERFACE ROUTINES + * sequence_reset - reset definition of sequence + * 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 + * + *------------------------------------------------------------------------- + */ + +/* + * sequence_alloc - allocate sequence values in a sequence + */ +void +sequence_alloc(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup) +{ + FmgrInfo *procedure; + + Assert(RelationIsValid(seqRelation)); + Assert(PointerIsValid(seqRelation->rd_seqam)); + Assert(OidIsValid(seqRelation->rd_rel->relam)); + + GET_SEQAM_PROCEDURE(seqamalloc); + + /* + * have the seqam's alloc proc do all the work. + */ + FunctionCall4(procedure, + PointerGetDatum(seqRelation), + PointerGetDatum(seq_elem), + Int32GetDatum(buf), + PointerGetDatum(tup)); +} + +/* + * sequence_alloc - allocate sequence values in a sequence + */ +void +sequence_setval(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup, int64 next, bool iscalled) +{ + FmgrInfo *procedure; + + Assert(RelationIsValid(seqRelation)); + Assert(PointerIsValid(seqRelation->rd_seqam)); + Assert(OidIsValid(seqRelation->rd_rel->relam)); + + GET_SEQAM_PROCEDURE(seqamsetval); + + /* + * have the seqam's setval proc do all the work. + */ + FunctionCall6(procedure, + PointerGetDatum(seqRelation), + PointerGetDatum(seq_elem), + Int32GetDatum(buf), + PointerGetDatum(tup), + Int64GetDatum(next), + BoolGetDatum(iscalled)); +} + +/*------------------------------------------------------------ + * + * Sequence Access Manager management functions + * + *------------------------------------------------------------ + */ + +/* check_hook: validate new default_sequenceam */ +bool +check_default_seqam(char **newval, void **extra, GucSource source) +{ + if (**newval == '\0') + return true; + + /* + * 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; + } + } + } + return true; +} + +/* + * GetDefaultSeqAM -- get the OID of the current default sequence AM + * + * This exists to hide (and possibly optimize the use of) the + * default_seqam GUC variable. + */ +Oid +GetDefaultSeqAM(void) +{ + /* Fast path for default_tablespace == "" */ + if (default_seqam == NULL || default_seqam[0] == '\0') + return LOCAL_SEQAM_OID; + + return get_seqam_oid(default_seqam, false); +} + +/* + * 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, + * just return InvalidOid. + */ +Oid +get_seqam_oid(const char *amname, bool missing_ok) +{ + Oid result; + HeapTuple tuple; + + /* look up the access method */ + tuple = SearchSysCache1(SEQAMNAME, PointerGetDatum(amname)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("sequence access method \"%s\" does not exist", + amname))); + + /* We assume that there can be at most one matching tuple */ + if (HeapTupleIsValid(tuple)) + { + result = HeapTupleGetOid(tuple); + ReleaseSysCache(tuple); + } + else + result = InvalidOid; + + if (!OidIsValid(result) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("squence am \"%s\" does not exist", + amname))); + return result; +} diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index a974bd5260..c85b5d19e2 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -35,7 +35,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 f3344c6207..5ca29a189c 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -14,13 +14,19 @@ */ #include "postgres.h" +#include "access/reloptions.h" +#include "access/seqam.h" +#include "access/transam.h" #include "access/htup_details.h" #include "access/multixact.h" #include "access/transam.h" +#include "access/xact.h" #include "access/xlogutils.h" #include "catalog/dependency.h" +#include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_seqam.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "commands/sequence.h" @@ -55,32 +61,6 @@ typedef struct sequence_magic uint32 magic; } sequence_magic; -/* - * We store a SeqTable item for every sequence we have touched in the current - * session. This is needed to hold onto nextval/currval state. (We can't - * rely on the relcache, since it's only, well, a cache, and may decide to - * discard entries.) - * - * XXX We use linear search to find pre-existing SeqTable entries. This is - * good when only a small number of sequences are touched in a session, but - * would suck with many different sequences. Perhaps use a hashtable someday. - */ -typedef struct SeqTableData -{ - struct SeqTableData *next; /* link to next SeqTable object */ - Oid relid; /* pg_class OID of this sequence */ - Oid filenode; /* last seen relfilenode of this sequence */ - LocalTransactionId lxid; /* xact in which we last did a seq op */ - bool last_valid; /* do we have a valid "last" value? */ - int64 last; /* value last returned by nextval */ - int64 cached; /* last value already cached for nextval */ - /* if last != cached, we have not used up all the cached values */ - int64 increment; /* copy of sequence's increment field */ - /* note that increment is zero until we first do read_seq_tuple() */ -} SeqTableData; - -typedef SeqTableData *SeqTable; - static SeqTable seqtab = NULL; /* Head of list of SeqTable items */ /* @@ -92,13 +72,12 @@ static SeqTableData *last_used_seq = NULL; static void fill_seq_with_data(Relation rel, HeapTuple tuple); static int64 nextval_internal(Oid relid); 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); /* @@ -112,6 +91,7 @@ DefineSequence(CreateSeqStmt *seq) List *owned_by; CreateStmt *stmt = makeNode(CreateStmt); Oid seqoid; + Oid seqamid; Relation rel; HeapTuple tuple; TupleDesc tupDesc; @@ -119,6 +99,9 @@ DefineSequence(CreateSeqStmt *seq) bool null[SEQ_COL_LASTCOL]; int i; NameData name; + HeapTupleData seqtuple; + SeqTable elm; + Buffer buf; /* Unlogged sequences are not implemented -- not clear if useful. */ if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED) @@ -126,8 +109,9 @@ DefineSequence(CreateSeqStmt *seq) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unlogged sequences are not supported"))); - /* Check and set all option values */ + /* Check and set all param values */ init_params(seq->options, true, &new, &owned_by); + seqamid = init_options(seq->accessMethod, seq->amoptions); /* * Create relation (and fill value[] and null[] for the tuple) @@ -203,6 +187,11 @@ DefineSequence(CreateSeqStmt *seq) coldef->colname = "is_called"; value[i - 1] = BoolGetDatum(false); break; + case SEQ_COL_AMDATA: + coldef->typeName = makeTypeNameFromOid(BYTEAOID, -1); + coldef->colname = "amdata"; + null[i - 1] = true; + break; } stmt->tableElts = lappend(stmt->tableElts, coldef); } @@ -218,6 +207,14 @@ DefineSequence(CreateSeqStmt *seq) seqoid = DefineRelation(stmt, RELKIND_SEQUENCE, seq->ownerId); Assert(seqoid != InvalidOid); + /* + * 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); + rel = heap_open(seqoid, AccessExclusiveLock); tupDesc = RelationGetDescr(rel); @@ -231,6 +228,19 @@ DefineSequence(CreateSeqStmt *seq) heap_close(rel, NoLock); + /* + * reopen relation, read sequence for the benfit of the AM. It would be + * nice to do this with less repetitive work. + */ + init_sequence(seqoid, &elm, &rel); + (void) read_seq_tuple(elm, rel, &buf, &seqtuple); + + /* call into AM */ + sequence_setval(rel, elm, buf, &seqtuple, new.start_value, false); + + UnlockReleaseBuffer(buf); + heap_close(rel, NoLock); + return seqoid; } @@ -273,15 +283,6 @@ ResetSequence(Oid seq_relid) UnlockReleaseBuffer(buf); /* - * Modify the copied tuple to execute the restart (compare the RESTART - * action in AlterSequence) - */ - seq = (Form_pg_sequence) GETSTRUCT(tuple); - seq->last_value = seq->start_value; - seq->is_called = false; - seq->log_cnt = 0; - - /* * Create a new storage file for the sequence. We want to keep the * sequence's relfrozenxid at 0, since it won't contain any unfrozen XIDs. * Same with relminmxid, since a sequence will never contain multixacts. @@ -290,14 +291,25 @@ ResetSequence(Oid seq_relid) InvalidMultiXactId); /* - * Insert the modified tuple into the new storage file. + * Insert the modified tuple into the new storage file. This will log + * superflously log the old values, but this isn't a performance critical + * path... */ fill_seq_with_data(seq_rel, tuple); + /* read from the new relfilenode, pointing into buffer again */ + seq = read_seq_tuple(elm, seq_rel, &buf, &seqtuple); + + /* call into the sequence am, let it do whatever it wants */ + sequence_setval(seq_rel, elm, buf, &seqtuple, seq->start_value, false); + /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ elm->cached = elm->last; + /* And now done with the old page */ + UnlockReleaseBuffer(buf); + relation_close(seq_rel, NoLock); } @@ -344,6 +356,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple) * the right xmin, and (b) REDO of the heap_insert record would re-init * page and sequence magic number would be lost. This means two log * records instead of one :-( + * XXX: Why don't we just pass HEAP_INSERT_SKIP_WAL to heap_insert? */ LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); @@ -373,27 +386,7 @@ fill_seq_with_data(Relation rel, HeapTuple tuple) MarkBufferDirty(buf); /* XLOG stuff */ - if (RelationNeedsWAL(rel)) - { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - - xlrec.node = rel->rd_node; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].buffer = InvalidBuffer; - rdata[0].next = &(rdata[1]); - - rdata[1].data = (char *) tuple->t_data; - rdata[1].len = tuple->t_len; - rdata[1].buffer = InvalidBuffer; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata); - - PageSetLSN(page, recptr); - } + log_sequence_tuple(rel, tuple, page); END_CRIT_SECTION(); @@ -409,12 +402,14 @@ Oid AlterSequence(AlterSeqStmt *stmt) { Oid relid; + Oid seqamid; SeqTable elm; Relation seqrel; Buffer buf; HeapTupleData seqtuple; + HeapTuple newtuple; Form_pg_sequence seq; - FormData_pg_sequence new; + Form_pg_sequence new; List *owned_by; /* Open and lock sequence. */ @@ -437,11 +432,16 @@ 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 */ - memcpy(&new, seq, sizeof(FormData_pg_sequence)); + /* + * copy tuple so init_params can scribble on it and error out later without + * causing problems. + */ + newtuple = heap_copytuple(&seqtuple); + new = (Form_pg_sequence) GETSTRUCT(newtuple); /* Check and set new values */ - init_params(stmt->options, false, &new, &owned_by); + init_params(stmt->options, false, new, &owned_by); + seqamid = init_options(stmt->accessMethod, stmt->amoptions); /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ @@ -450,33 +450,15 @@ AlterSequence(AlterSeqStmt *stmt) /* Now okay to update the on-disk tuple */ START_CRIT_SECTION(); - memcpy(seq, &new, sizeof(FormData_pg_sequence)); + /* we copy back the tuple, contains new settings but is not yet reset via AM*/ + Assert(newtuple->t_len == seqtuple.t_len); + memcpy(seqtuple.t_data, newtuple->t_data, newtuple->t_len); MarkBufferDirty(buf); - /* XLOG stuff */ - if (RelationNeedsWAL(seqrel)) - { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - Page page = BufferGetPage(buf); - - xlrec.node = seqrel->rd_node; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].buffer = InvalidBuffer; - rdata[0].next = &(rdata[1]); - - rdata[1].data = (char *) seqtuple.t_data; - rdata[1].len = seqtuple.t_len; - rdata[1].buffer = InvalidBuffer; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata); - - PageSetLSN(page, recptr); - } + /* call SeqAM, in a crit section since we already modified the tuple */ + sequence_setval(seqrel, elm, buf, &seqtuple, + seq->last_value, seq->is_called); END_CRIT_SECTION(); @@ -486,6 +468,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); + InvokeObjectPostAlterHook(RelationRelationId, relid, 0); relation_close(seqrel, NoLock); @@ -529,26 +516,20 @@ nextval_oid(PG_FUNCTION_ARGS) PG_RETURN_INT64(nextval_internal(relid)); } +/* + * Sequence AM independent part of nextval() that does permission checking, + * returns cached values and then calls out to the SeqAM specific nextval part. + */ static int64 nextval_internal(Oid relid) { SeqTable elm; Relation seqrel; Buffer buf; - Page page; HeapTupleData seqtuple; - Form_pg_sequence seq; - int64 incby, - maxv, - minv, - cache, - log, - fetch, - last; - int64 result, - next, - rescnt = 0; - bool logit = false; + int64 result; + + /* common code path for all sequence AMs */ /* open and AccessShareLock sequence */ init_sequence(relid, &elm, &seqrel); @@ -564,7 +545,8 @@ nextval_internal(Oid relid) if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("nextval()"); - if (elm->last != elm->cached) /* some numbers were cached */ + /* return cached values without entering the sequence am again */ + if (elm->last != elm->cached) { Assert(elm->last_valid); Assert(elm->increment != 0); @@ -575,178 +557,14 @@ nextval_internal(Oid relid) } /* lock page' buffer and read tuple */ - seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); - page = BufferGetPage(buf); - - last = next = result = seq->last_value; - incby = seq->increment_by; - maxv = seq->max_value; - minv = seq->min_value; - fetch = cache = seq->cache_value; - log = seq->log_cnt; - - if (!seq->is_called) - { - rescnt++; /* return last_value if not is_called */ - fetch--; - } - - /* - * Decide whether we should emit a WAL log record. If so, force up the - * fetch count to grab SEQ_LOG_VALS more values than we actually need to - * cache. (These will then be usable without logging.) - * - * If this is the first nextval after a checkpoint, we must force a new - * WAL record to be written anyway, else replay starting from the - * checkpoint would fail to advance the sequence past the logged values. - * In this case we may as well fetch extra values. - */ - if (log < fetch || !seq->is_called) - { - /* forced log to satisfy local demand for values */ - fetch = log = fetch + SEQ_LOG_VALS; - logit = true; - } - else - { - XLogRecPtr redoptr = GetRedoRecPtr(); - - if (PageGetLSN(page) <= redoptr) - { - /* last update of seq was before checkpoint */ - fetch = log = fetch + SEQ_LOG_VALS; - logit = true; - } - } - - while (fetch) /* try to fetch cache [+ log ] numbers */ - { - /* - * Check MAXVALUE for ascending sequences and MINVALUE for descending - * sequences - */ - if (incby > 0) - { - /* ascending sequence */ - if ((maxv >= 0 && next > maxv - incby) || - (maxv < 0 && next + incby > maxv)) - { - if (rescnt > 0) - break; /* stop fetching */ - if (!seq->is_cycled) - { - char buf[100]; + read_seq_tuple(elm, seqrel, &buf, &seqtuple); - snprintf(buf, sizeof(buf), INT64_FORMAT, maxv); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("nextval: reached maximum value of sequence \"%s\" (%s)", - RelationGetRelationName(seqrel), buf))); - } - next = minv; - } - else - next += incby; - } - else - { - /* descending sequence */ - if ((minv < 0 && next < minv - incby) || - (minv >= 0 && next + incby < minv)) - { - if (rescnt > 0) - break; /* stop fetching */ - if (!seq->is_cycled) - { - char buf[100]; - - snprintf(buf, sizeof(buf), INT64_FORMAT, minv); - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("nextval: reached minimum value of sequence \"%s\" (%s)", - RelationGetRelationName(seqrel), buf))); - } - next = maxv; - } - else - next += incby; - } - fetch--; - if (rescnt < cache) - { - log--; - rescnt++; - last = next; - if (rescnt == 1) /* if it's first result - */ - result = next; /* it's what to return */ - } - } - - log -= fetch; /* adjust for any unfetched numbers */ - Assert(log >= 0); - - /* save info in local cache */ - elm->last = result; /* last returned number */ - elm->cached = last; /* last fetched number */ - elm->last_valid = true; + /* call into AM specific code */ + sequence_alloc(seqrel, elm, buf, &seqtuple); last_used_seq = elm; - /* ready to change the on-disk (or really, in-buffer) tuple */ - START_CRIT_SECTION(); - - /* - * We must mark the buffer dirty before doing XLogInsert(); see notes in - * SyncOneBuffer(). However, we don't apply the desired changes just yet. - * This looks like a violation of the buffer update protocol, but it is in - * fact safe because we hold exclusive lock on the buffer. Any other - * process, including a checkpoint, that tries to examine the buffer - * contents will block until we release the lock, and then will see the - * final state that we install below. - */ - MarkBufferDirty(buf); - - /* XLOG stuff */ - if (logit && RelationNeedsWAL(seqrel)) - { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - - /* - * We don't log the current state of the tuple, but rather the state - * as it would appear after "log" more fetches. This lets us skip - * that many future WAL records, at the cost that we lose those - * sequence values if we crash. - */ - - /* set values that will be saved in xlog */ - seq->last_value = next; - seq->is_called = true; - seq->log_cnt = 0; - - xlrec.node = seqrel->rd_node; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].buffer = InvalidBuffer; - rdata[0].next = &(rdata[1]); - - rdata[1].data = (char *) seqtuple.t_data; - rdata[1].len = seqtuple.t_len; - rdata[1].buffer = InvalidBuffer; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata); - - PageSetLSN(page, recptr); - } - - /* Now update sequence tuple to the intended final state */ - seq->last_value = last; /* last fetched number */ - seq->is_called = true; - seq->log_cnt = log; /* how much is logged */ - - END_CRIT_SECTION(); + result = elm->last; UnlockReleaseBuffer(buf); @@ -875,51 +693,22 @@ do_setval(Oid relid, int64 next, bool iscalled) bufm, bufx))); } - /* Set the currval() state only if iscalled = true */ - if (iscalled) - { - elm->last = next; /* last returned number */ - elm->last_valid = true; - } - /* In any case, forget any future cached numbers */ elm->cached = elm->last; - /* ready to change the on-disk (or really, in-buffer) tuple */ - START_CRIT_SECTION(); + sequence_setval(seqrel, elm, buf, &seqtuple, next, iscalled); - seq->last_value = next; /* last fetched number */ - seq->is_called = iscalled; - seq->log_cnt = 0; + Assert(seq->is_called == iscalled); - MarkBufferDirty(buf); + /* common logic we don't have to duplicate in every AM implementation */ - /* XLOG stuff */ - if (RelationNeedsWAL(seqrel)) + /* Set the currval() state only if iscalled = true */ + if (iscalled) { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - Page page = BufferGetPage(buf); - - xlrec.node = seqrel->rd_node; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].buffer = InvalidBuffer; - rdata[0].next = &(rdata[1]); - - rdata[1].data = (char *) seqtuple.t_data; - rdata[1].len = seqtuple.t_len; - rdata[1].buffer = InvalidBuffer; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata); - - PageSetLSN(page, recptr); + elm->last = next; /* last returned number */ + elm->last_valid = true; } - END_CRIT_SECTION(); - UnlockReleaseBuffer(buf); relation_close(seqrel, NoLock); @@ -1002,7 +791,7 @@ open_share_lock(SeqTable seq) * Given a relation OID, open and lock the sequence. p_elm and p_rel are * output parameters. */ -static void +void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel) { SeqTable elm; @@ -1039,6 +828,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel) elm->last_valid = false; elm->last = elm->cached = elm->increment = 0; elm->next = seqtab; + elm->am_private = PointerGetDatum(NULL); seqtab = elm; } @@ -1079,7 +869,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel) * * Function's return value points to the data payload of the tuple */ -static Form_pg_sequence +Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple) { Page page; @@ -1130,15 +920,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; @@ -1148,13 +938,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) { @@ -1399,7 +1189,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 @@ -1415,8 +1205,7 @@ process_owned_by(Relation seqrel, List *owned_by) nnames = list_length(owned_by); Assert(nnames > 0); - if (nnames == 1) - { + if (nnames == 1) { /* Must be OWNED BY NONE */ if (strcmp(strVal(linitial(owned_by)), "none") != 0) ereport(ERROR, @@ -1548,6 +1337,42 @@ 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); + CatalogUpdateIndexes(rd, ctup); + } + + heap_freetuple(ctup); + heap_close(rd, RowExclusiveLock); + CommandCounterIncrement(); +} void seq_redo(XLogRecPtr lsn, XLogRecord *record) @@ -1603,6 +1428,7 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record) pfree(localpage); } + /* * Flush cached sequence information. */ @@ -1620,3 +1446,279 @@ ResetSequenceCaches(void) last_used_seq = NULL; } + +static Oid +init_options(char *accessMethod, List *options) +{ + Oid seqamid; + Datum reloptions; + Form_pg_seqam seqamForm; + HeapTuple tuple = NULL; + char *validnsps[] = {NULL, NULL}; + + if (accessMethod == NULL || strcmp(accessMethod, DEFAULT_SEQAM) == 0) + seqamid = GetDefaultSeqAM(); + else + seqamid = get_seqam_oid(accessMethod, false); + + tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", seqamid); + + seqamForm = (Form_pg_seqam) GETSTRUCT(tuple); + + /* allow am specific options */ + validnsps[0] = NameStr(seqamForm->seqamname); + + /* + * Parse AM-specific options, convert to text array form, + * retrieve the AM-option function and then validate. + */ + reloptions = transformRelOptions((Datum) NULL, options, + NULL, validnsps, false, false); + + (void) sequence_reloptions(seqamForm->seqamoptions, reloptions, true); + + ReleaseSysCache(tuple); + + return seqamid; +} + +void +log_sequence_tuple(Relation seqrel, HeapTuple tup, Page page) +{ + xl_seq_rec xlrec; + XLogRecPtr recptr; + XLogRecData rdata[2]; + + xlrec.node = seqrel->rd_node; + rdata[0].data = (char *) &xlrec; + rdata[0].len = sizeof(xl_seq_rec); + rdata[0].buffer = InvalidBuffer; + rdata[0].next = &(rdata[1]); + + rdata[1].data = (char *) tup->t_data; + rdata[1].len = tup->t_len; + rdata[1].buffer = InvalidBuffer; + rdata[1].next = NULL; + + recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG, rdata); + + PageSetLSN(page, recptr); +} + + +/*------------------------------------------------------------ + * + * Sequence Access Manager = LOCAL functions + * + *------------------------------------------------------------ + */ + +/* + * seqam_local_alloc() + * + * Allocate new range of values for a local sequence. + */ +void +sequence_local_alloc(PG_FUNCTION_ARGS) +{ + Relation seqrel = (Relation) PG_GETARG_POINTER(0); + SeqTable elm = (SeqTable) PG_GETARG_POINTER(1); + Buffer buf = (Buffer) PG_GETARG_INT32(2); + HeapTuple seqtuple = (HeapTuple) PG_GETARG_POINTER(3); + Page page; + Form_pg_sequence seq; + bool logit = false; + int64 incby, + maxv, + minv, + cache, + log, + fetch, + last; + int64 result, + next; + int64 rescnt = 0; /* how many values did we fetch up to now */ + + page = BufferGetPage(buf); + seq = (Form_pg_sequence) GETSTRUCT(seqtuple); + + last = next = result = seq->last_value; + incby = seq->increment_by; + maxv = seq->max_value; + minv = seq->min_value; + fetch = cache = seq->cache_value; + log = seq->log_cnt; + + if (!seq->is_called) + { + rescnt++; /* return current last_value if not is_called */ + fetch--; + logit = true; + } + + /* check whether value can be satisfied without logging again */ + if (log < fetch || !seq->is_called || PageGetLSN(page) <= GetRedoRecPtr()) + { + /* forced log to satisfy local demand for values */ + fetch = log = fetch + SEQ_LOG_VALS; + logit = true; + } + + while (fetch) /* try to fetch cache [+ log ] numbers */ + { + /* + * Check MAXVALUE for ascending sequences and MINVALUE for descending + * sequences + */ + if (incby > 0) + { + /* ascending sequence */ + if ((maxv >= 0 && next > maxv - incby) || + (maxv < 0 && next + incby > maxv)) + { + /* it's ok, only additional cached values exceed maximum */ + if (rescnt > 0) + break; + if (!seq->is_cycled) + { + char buf[100]; + + snprintf(buf, sizeof(buf), INT64_FORMAT, maxv); + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("nextval: reached maximum value of sequence \"%s\" (%s)", + RelationGetRelationName(seqrel), buf))); + } + next = minv; + } + else + next += incby; + } + else + { + /* descending sequence */ + if ((minv < 0 && next < minv - incby) || + (minv >= 0 && next + incby < minv)) + { + if (rescnt > 0) + break; /* stop fetching */ + if (!seq->is_cycled) + { + char buf[100]; + + snprintf(buf, sizeof(buf), INT64_FORMAT, minv); + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("nextval: reached minimum value of sequence \"%s\" (%s)", + RelationGetRelationName(seqrel), buf))); + } + next = maxv; + } + else + next += incby; + } + + fetch--; + if (rescnt < cache) + { + log--; + rescnt++; + last = next; + if (rescnt == 1) /* if it's first result - */ + result = next; /* it's what to return */ + } + } + + elm->last = result; + elm->cached = last; + elm->last_valid = true; + + /* ready to change the on-disk (or really, in-buffer) tuple */ + START_CRIT_SECTION(); + + /* + * We must mark the buffer dirty before doing XLogInsert(); see notes in + * SyncOneBuffer(). However, we don't apply the desired changes just yet. + * This looks like a violation of the buffer update protocol, but it is + * in fact safe because we hold exclusive lock on the buffer. Any other + * process, including a checkpoint, that tries to examine the buffer + * contents will block until we release the lock, and then will see the + * final state that we install below. + */ + MarkBufferDirty(buf); + + if (logit) + { + /* + * We don't log the current state of the tuple, but rather the state + * as it would appear after "log" more fetches. This lets us skip + * that many future WAL records, at the cost that we lose those + * sequence values if we crash. + */ + + seq->last_value = next; + seq->is_called = true; + seq->log_cnt = 0; + log_sequence_tuple(seqrel, seqtuple, page); + } + + /* Now update sequence tuple to the intended final state */ + seq->last_value = elm->last; /* last fetched number */ + seq->is_called = true; + seq->log_cnt = log; /* how much is logged */ + + result = elm->last; + + END_CRIT_SECTION(); +} + +/* + * seqam_local_setval() + * + * Coordinate the setting of a local sequence. + */ +void +sequence_local_setval(PG_FUNCTION_ARGS) +{ + Relation seqrel = (Relation) PG_GETARG_POINTER(0); + Buffer buf = (Buffer) PG_GETARG_INT32(2); + HeapTuple seqtuple = (HeapTuple) PG_GETARG_POINTER(3); + int64 next = PG_GETARG_INT64(4); + bool iscalled = PG_GETARG_BOOL(5); + Page page = BufferGetPage(buf); + Form_pg_sequence seq = (Form_pg_sequence) GETSTRUCT(seqtuple); + + /* ready to change the on-disk (or really, in-buffer) tuple */ + START_CRIT_SECTION(); + + /* set is_called, all AMs should need to do this */ + seq->is_called = iscalled; + seq->last_value = next; /* last fetched number */ + seq->log_cnt = 0; + + MarkBufferDirty(buf); + + log_sequence_tuple(seqrel, seqtuple, page); + + END_CRIT_SECTION(); +} + +/* + * seqam_local_options() + * + * Verify the options of a local sequence. + */ +Datum +sequence_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/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 670af18f4c..4a6e0f28ab 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8772,6 +8772,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 65f3b984d6..fc309e2c72 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3277,7 +3277,9 @@ _copyCreateSeqStmt(const CreateSeqStmt *from) COPY_NODE_FIELD(sequence); COPY_NODE_FIELD(options); + COPY_NODE_FIELD(amoptions); COPY_SCALAR_FIELD(ownerId); + COPY_STRING_FIELD(accessMethod); return newnode; } @@ -3289,7 +3291,9 @@ _copyAlterSeqStmt(const AlterSeqStmt *from) COPY_NODE_FIELD(sequence); COPY_NODE_FIELD(options); + COPY_NODE_FIELD(amoptions); 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 4c9b05e1e4..258c09e6b7 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1542,7 +1542,9 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b) { COMPARE_NODE_FIELD(sequence); COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(amoptions); COMPARE_SCALAR_FIELD(ownerId); + COMPARE_STRING_FIELD(accessMethod); return true; } @@ -1552,7 +1554,9 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b) { COMPARE_NODE_FIELD(sequence); COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(amoptions); 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 363c603848..4ac8d940aa 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -51,6 +51,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" @@ -3331,7 +3332,33 @@ CreateSeqStmt: CreateSeqStmt *n = makeNode(CreateSeqStmt); $4->relpersistence = $2; n->sequence = $4; + n->accessMethod = DEFAULT_SEQAM; n->options = $5; + n->amoptions = 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->accessMethod = $7; + n->options = $5; + n->amoptions = 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->accessMethod = $7; + n->options = $5; + n->amoptions = $9; n->ownerId = InvalidOid; $$ = (Node *)n; } @@ -3342,7 +3369,31 @@ AlterSeqStmt: { AlterSeqStmt *n = makeNode(AlterSeqStmt); n->sequence = $3; + n->accessMethod = DEFAULT_SEQAM; n->options = $4; + n->amoptions = NIL; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name SeqOptList + USING access_method + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $3; + n->accessMethod = $6; + n->options = $4; + n->amoptions = 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->accessMethod = $6; + n->options = $4; + n->amoptions = $8; n->missing_ok = false; $$ = (Node *)n; } @@ -3350,11 +3401,34 @@ AlterSeqStmt: { AlterSeqStmt *n = makeNode(AlterSeqStmt); n->sequence = $5; + n->accessMethod = DEFAULT_SEQAM; n->options = $6; + n->amoptions = NIL; + n->missing_ok = true; + $$ = (Node *)n; + } + | ALTER SEQUENCE IF_P EXISTS qualified_name SeqOptList + USING access_method + { + AlterSeqStmt *n = makeNode(AlterSeqStmt); + n->sequence = $5; + n->accessMethod = $8; + n->options = $6; + n->amoptions = 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->accessMethod = $8; + n->options = $6; + n->amoptions = $10; n->missing_ok = true; $$ = (Node *)n; } - ; OptSeqOptList: SeqOptList { $$ = $1; } @@ -3413,7 +3487,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 19d19e5f39..a16c9bbe32 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -399,6 +399,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) seqstmt = makeNode(CreateSeqStmt); seqstmt->sequence = makeRangeVar(snamespace, sname, -1); seqstmt->options = NIL; + seqstmt->amoptions = NIL; /* * If this is ALTER ADD COLUMN, make sure the sequence will be owned diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index c467f11ac8..1f123f2a20 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1043,10 +1043,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/relcache.c b/src/backend/utils/cache/relcache.c index b4cc6ad221..ea5eb1a633 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -50,6 +50,7 @@ #include "catalog/pg_opclass.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" +#include "catalog/pg_seqam.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -241,6 +242,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple); static void RelationBuildTupleDesc(Relation relation); static Relation RelationBuildDesc(Oid targetRelId, bool insertIt); static void RelationInitPhysicalAddr(Relation relation); +static void RelationInitSequenceAccessInfo(Relation relation); static void load_critical_index(Oid indexoid, Oid heapoid); static TupleDesc GetPgClassDescriptor(void); static TupleDesc GetPgIndexDescriptor(void); @@ -932,11 +934,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) else relation->trigdesc = NULL; - /* - * if it's an index, initialize index-related information - */ - if (OidIsValid(relation->rd_rel->relam)) + /* if it's an index, initialize index-related information */ + if (relation->rd_rel->relkind == RELKIND_INDEX && + OidIsValid(relation->rd_rel->relam)) RelationInitIndexAccessInfo(relation); + /* same for sequences */ + else if (relation->rd_rel->relkind == RELKIND_SEQUENCE && + OidIsValid(relation->rd_rel->relam)) + RelationInitSequenceAccessInfo(relation); /* extract reloptions if any */ RelationParseRelOptions(relation, pg_class_tuple); @@ -1370,6 +1375,39 @@ LookupOpclassInfo(Oid operatorClassOid, return opcentry; } +/* + * Initialize sequence-access-method support data for an index relation + */ +static void +RelationInitSequenceAccessInfo(Relation rel) +{ + HeapTuple amtuple; + MemoryContext indexcxt; + Form_pg_seqam amform; + + indexcxt = AllocSetContextCreate(CacheMemoryContext, + RelationGetRelationName(rel), + ALLOCSET_SMALL_MINSIZE, + ALLOCSET_SMALL_INITSIZE, + ALLOCSET_SMALL_MAXSIZE); + rel->rd_indexcxt = indexcxt; + + rel->rd_aminfo = (RelationAmInfo *) + MemoryContextAllocZero(rel->rd_indexcxt, + sizeof(RelationAmInfo)); + + /* + * Make a copy of the pg_am entry for the index's access method + */ + amtuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(rel->rd_rel->relam)); + if (!HeapTupleIsValid(amtuple)) + elog(ERROR, "cache lookup failed for access method %u", + rel->rd_rel->relam); + amform = (Form_pg_seqam) MemoryContextAlloc(rel->rd_indexcxt, sizeof(*amform)); + memcpy(amform, GETSTRUCT(amtuple), sizeof(*amform)); + ReleaseSysCache(amtuple); + rel->rd_seqam = amform; +} /* * formrdesc @@ -4375,6 +4413,22 @@ load_relcache_init_file(bool shared) rel->rd_supportinfo = (FmgrInfo *) MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo)); } + else if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + { + MemoryContext indexcxt; + Assert(!rel->rd_isnailed); + Assert(false); + + indexcxt = AllocSetContextCreate(CacheMemoryContext, + RelationGetRelationName(rel), + ALLOCSET_SMALL_MINSIZE, + ALLOCSET_SMALL_INITSIZE, + ALLOCSET_SMALL_MAXSIZE); + rel->rd_indexcxt = indexcxt; + /* set up zeroed fmgr-info vectors */ + rel->rd_aminfo = (RelationAmInfo *) + MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo)); + } else { /* Count nailed rels to ensure we have 'em all */ diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index e9bdfeae21..2fddb27ef7 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -54,6 +54,7 @@ #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" #include "catalog/pg_shseclabel.h" +#include "catalog/pg_seqam.h" #include "catalog/pg_statistic.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_ts_config.h" @@ -631,6 +632,28 @@ static const struct cachedesc cacheinfo[] = { }, 8 }, + {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 538d027606..a3e3d0d502 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -27,6 +27,7 @@ #endif #include "access/gin.h" +#include "access/seqam.h" #include "access/transam.h" #include "access/twophase.h" #include "access/xact.h" @@ -2663,6 +2664,17 @@ static struct config_string ConfigureNamesString[] = }, { + {"default_sequenceam", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the default sequence am for any new sequences."), + gettext_noop("An empty string selects the 'local' sequence am."), + GUC_IS_NAME | GUC_NOT_IN_SAMPLE + }, + &default_seqam, + "", + check_default_seqam, 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 5a4664bb05..ef523b72ec 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 0000000000..61a1d1bebd --- /dev/null +++ b/src/include/access/seqam.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + * + * 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 "fmgr.h" + +#include "access/htup.h" +#include "commands/sequence.h" +#include "utils/relcache.h" +#include "storage/buf.h" +#include "storage/bufpage.h" + +/* + * We store a SeqTable item for every sequence we have touched in the current + * session. This is needed to hold onto nextval/currval state. (We can't + * rely on the relcache, since it's only, well, a cache, and may decide to + * discard entries.) + * + * XXX We use linear search to find pre-existing SeqTable entries. This is + * good when only a small number of sequences are touched in a session, but + * would suck with many different sequences. Perhaps use a hashtable someday. + */ +typedef struct SeqTableData +{ + struct SeqTableData *next; /* link to next SeqTable object */ + Oid relid; /* pg_class OID of this sequence */ + Oid filenode; /* last seen relfilenode of this sequence */ + LocalTransactionId lxid; /* xact in which we last did a seq op */ + bool last_valid; /* do we have a valid "last" value? */ + int64 last; /* value last returned by nextval */ + int64 cached; /* last value already cached for nextval */ + /* if last != cached, we have not used up all the cached values */ + int64 increment; /* copy of sequence's increment field */ + /* note that increment is zero until we first do read_seq_tuple() */ + Datum am_private; /* private data of the SeqAm */ +} SeqTableData; + +typedef SeqTableData *SeqTable; + +extern char *default_seqam; + +Oid GetDefaultSeqAM(void); + +extern void sequence_alloc(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup); +extern void sequence_setval(Relation seqRelation, SeqTable seq_elem, Buffer buf, HeapTuple tup, int64 next, bool iscalled); + + +extern void sequence_local_alloc(PG_FUNCTION_ARGS); +extern void sequence_local_setval(PG_FUNCTION_ARGS); +extern Datum sequence_local_options(PG_FUNCTION_ARGS); + +extern Oid get_seqam_oid(const char *sequencename, bool missing_ok); + +extern void log_sequence_tuple(Relation seqrel, HeapTuple tup, Page page); +extern void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel); +extern Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel, + Buffer *buf, HeapTuple seqtuple); +#endif /* SEQAM_H */ diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 4860e98ca5..bb3c6e687c 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -213,6 +213,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(pg_seqam_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 ca4fc62bcd..54d4e13483 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4746,6 +4746,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 = 3939 ( sequence_local_alloc PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ sequence_local_alloc _null_ _null_ _null_ )); +DESCR("Local SequenceAM allocation"); +DATA(insert OID = 3940 ( sequence_local_setval PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 2281 2281 20 16" _null_ _null_ _null_ _null_ sequence_local_setval _null_ _null_ _null_ )); +DESCR("Local SequenceAM setval"); +DATA(insert OID = 3936 ( sequence_local_options PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ sequence_local_options _null_ _null_ _null_ )); +DESCR("Local SequenceAM options"); /* event triggers */ DATA(insert OID = 3566 ( pg_event_trigger_dropped_objects PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ )); diff --git a/src/include/catalog/pg_seqam.h b/src/include/catalog/pg_seqam.h new file mode 100644 index 0000000000..00a6f036ee --- /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 32 + +CATALOG(pg_seqam,32) +{ + 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 = 3971 ( local sequence_local_alloc sequence_local_setval sequence_local_options)); +DESCR("local sequence access method"); +#define LOCAL_SEQAM_OID 3971 + +#define DEFAULT_SEQAM "" + +#endif /* PG_SEQAM_H */ diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index ffafe6c52f..9b4ac6ee99 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -31,6 +31,7 @@ typedef struct FormData_pg_sequence int64 log_cnt; bool is_cycled; bool is_called; + bytea amdata; } FormData_pg_sequence; typedef FormData_pg_sequence *Form_pg_sequence; @@ -49,9 +50,10 @@ typedef FormData_pg_sequence *Form_pg_sequence; #define SEQ_COL_LOG 8 #define SEQ_COL_CYCLE 9 #define SEQ_COL_CALLED 10 +#define SEQ_COL_AMDATA 11 #define SEQ_COL_FIRSTCOL SEQ_COL_NAME -#define SEQ_COL_LASTCOL SEQ_COL_CALLED +#define SEQ_COL_LASTCOL SEQ_COL_AMDATA /* XLOG stuff */ #define XLOG_SEQ_LOG 0x00 diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index e5235cbf40..89a1727f0c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1886,16 +1886,20 @@ typedef struct CreateSeqStmt { NodeTag type; RangeVar *sequence; /* the sequence to create */ - List *options; + List *options; /* standard sequence options */ + List *amoptions; /* am specific options */ 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 *options; /* standard sequence options */ + List *amoptions; /* am specific options */ 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 3e981b3e94..6e93258cce 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -376,6 +376,7 @@ extern void GUC_check_errcode(int sqlerrcode); */ /* in commands/tablespace.c */ +extern bool check_default_seqam(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 589c9a81b6..d930ac956f 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -18,6 +18,7 @@ #include "catalog/pg_am.h" #include "catalog/pg_class.h" #include "catalog/pg_index.h" +#include "catalog/pg_seqam.h" #include "fmgr.h" #include "nodes/bitmapset.h" #include "rewrite/prs2lock.h" @@ -48,10 +49,12 @@ typedef LockInfoData *LockInfo; /* * Cached lookup information for the frequently used index access method - * functions, defined by the pg_am row associated with an index relation. + * functions, defined 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; @@ -61,6 +64,14 @@ typedef struct RelationAmInfo FmgrInfo ammarkpos; FmgrInfo amrestrpos; FmgrInfo amcanreturn; + FmgrInfo amcostestimate; + + /* pg_seqam only */ + FmgrInfo seqamalloc; + FmgrInfo seqamsetval; + + /* Common */ + FmgrInfo amoptions; } RelationAmInfo; @@ -123,23 +134,25 @@ typedef struct RelationData struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */ Form_pg_am rd_am; /* pg_am tuple for index's AM */ + Form_pg_seqam rd_seqam; /* pg_seqam tuple for sequence'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 */ @@ -150,7 +163,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 e41b3d2e38..9b6f434ee8 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, diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 57d614f651..b8e822ebb1 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -51,9 +51,11 @@ WHERE p1.prolang = 0 OR p1.prorettype = 0 OR 0::oid = ANY (p1.proargtypes) OR procost <= 0 OR CASE WHEN proretset THEN prorows <= 0 ELSE prorows != 0 END; - oid | proname ------+--------- -(0 rows) + oid | proname +------+----------------------- + 3939 | sequence_local_alloc + 3940 | sequence_local_setval +(2 rows) -- prosrc should never be null or empty SELECT p1.oid, p1.proname diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index a62a3e3ebc..fe6c3fefa4 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -122,6 +122,7 @@ pg_proc|t pg_range|t pg_rewrite|t pg_seclabel|t +pg_seqam|t pg_shdepend|t pg_shdescription|t pg_shseclabel|t diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out index 8fcb70015f..39e3e9d431 100644 --- a/src/test/regress/expected/sequence.out +++ b/src/test/regress/expected/sequence.out @@ -171,9 +171,9 @@ DROP SEQUENCE sequence_test; CREATE SEQUENCE foo_seq; ALTER TABLE foo_seq RENAME TO foo_seq_new; SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called | amdata +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+-------- + foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f | (1 row) SELECT nextval('foo_seq_new'); @@ -189,9 +189,9 @@ SELECT nextval('foo_seq_new'); (1 row) SELECT * FROM foo_seq_new; - sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called ----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- - foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t + sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called | amdata +---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------+-------- + foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t | (1 row) DROP SEQUENCE foo_seq_new; diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index c725bba8f3..e2ee303cf6 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -117,6 +117,7 @@ SELECT table_name, column_name, is_updatable ro_view20 | log_cnt | NO ro_view20 | is_cycled | NO ro_view20 | is_called | NO + ro_view20 | amdata | NO ro_view21 | a | NO ro_view21 | b | NO ro_view21 | g | NO @@ -140,7 +141,7 @@ SELECT table_name, column_name, is_updatable rw_view16 | a | YES rw_view16 | b | YES rw_view16 | aa | YES -(48 rows) +(49 rows) -- Read-only views DELETE FROM ro_view1; diff --git a/src/test/regress/sql/namespace.sql b/src/test/regress/sql/namespace.sql index 879b6c35b0..2bb9134398 100644 --- a/src/test/regress/sql/namespace.sql +++ b/src/test/regress/sql/namespace.sql @@ -1,7 +1,6 @@ -- -- Regression tests for schemas (namespaces) -- - CREATE SCHEMA test_schema_1 CREATE UNIQUE INDEX abc_a_idx ON abc (a) |