static SeqTableData *last_used_seq = NULL;
static void fill_seq_with_data(Relation rel, HeapTuple tuple);
-static Relation open_share_lock(SeqTable seq);
+static Relation lock_and_open_sequence(SeqTable seq);
static void create_seq_hashtable(void);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
static Form_pg_sequence_data read_seq_tuple(Relation rel,
Buffer *buf, HeapTuple seqdatatuple);
+static LOCKMODE alter_sequence_get_lock_level(List *options);
static void init_params(ParseState *pstate, List *options, bool for_identity,
bool isInit,
Form_pg_sequence seqform,
HeapTuple tuple;
/* Open and lock sequence. */
- relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
+ relid = RangeVarGetRelid(stmt->sequence,
+ alter_sequence_get_lock_level(stmt->options),
+ stmt->missing_ok);
if (relid == InvalidOid)
{
ereport(NOTICE,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
stmt->sequence->relname);
- /* lock page' buffer and read tuple into new sequence structure */
- seqdata = read_seq_tuple(seqrel, &buf, &seqdatatuple);
-
- /* Copy old sequence data into workspace */
- memcpy(&newseqdata, seqdata, sizeof(FormData_pg_sequence_data));
-
rel = heap_open(SequenceRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopy1(SEQRELID,
ObjectIdGetDatum(relid));
seqform = (Form_pg_sequence) GETSTRUCT(tuple);
+ /* lock page's buffer and read tuple into new sequence structure */
+ seqdata = read_seq_tuple(seqrel, &buf, &seqdatatuple);
+
+ /* Copy old sequence data into workspace */
+ memcpy(&newseqdata, seqdata, sizeof(FormData_pg_sequence_data));
+
/* Check and set new values */
init_params(pstate, stmt->options, stmt->for_identity, false, seqform, &changed_seqform, &newseqdata, &owned_by);
ObjectAddressSet(address, RelationRelationId, relid);
- relation_close(seqrel, NoLock);
-
if (changed_seqform)
CatalogTupleUpdate(rel, &tuple->t_self, tuple);
heap_close(rel, RowExclusiveLock);
+ relation_close(seqrel, NoLock);
+
return address;
}
bool cycle;
bool logit = false;
- /* open and AccessShareLock sequence */
+ /* open and lock sequence */
init_sequence(relid, &elm, &seqrel);
if (check_permissions &&
SeqTable elm;
Relation seqrel;
- /* open and AccessShareLock sequence */
+ /* open and lock sequence */
init_sequence(relid, &elm, &seqrel);
if (pg_class_aclcheck(elm->relid, GetUserId(),
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("lastval is not yet defined in this session")));
- seqrel = open_share_lock(last_used_seq);
+ seqrel = lock_and_open_sequence(last_used_seq);
/* nextval() must have already been called for this sequence */
Assert(last_used_seq->last_valid);
int64 maxv,
minv;
- /* open and AccessShareLock sequence */
+ /* open and lock sequence */
init_sequence(relid, &elm, &seqrel);
if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
/*
- * Open the sequence and acquire AccessShareLock if needed
+ * Open the sequence and acquire lock if needed
*
* If we haven't touched the sequence already in this transaction,
- * we need to acquire AccessShareLock. We arrange for the lock to
+ * we need to acquire a lock. We arrange for the lock to
* be owned by the top transaction, so that we don't need to do it
* more than once per xact.
*/
static Relation
-open_share_lock(SeqTable seq)
+lock_and_open_sequence(SeqTable seq)
{
LocalTransactionId thislxid = MyProc->lxid;
PG_TRY();
{
CurrentResourceOwner = TopTransactionResourceOwner;
- LockRelationOid(seq->relid, AccessShareLock);
+ LockRelationOid(seq->relid, RowExclusiveLock);
}
PG_CATCH();
{
seq->lxid = thislxid;
}
- /* We now know we have AccessShareLock, and can safely open the rel */
+ /* We now know we have the lock, and can safely open the rel */
return relation_open(seq->relid, NoLock);
}
/*
* Open the sequence relation.
*/
- seqrel = open_share_lock(elm);
+ seqrel = lock_and_open_sequence(elm);
if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
ereport(ERROR,
return seq;
}
+/*
+ * Check the sequence options list and return the appropriate lock level for
+ * ALTER SEQUENCE.
+ *
+ * Most sequence option changes require a self-exclusive lock and should block
+ * concurrent nextval() et al. But RESTART does not, because it's not
+ * transactional. Also take a lower lock if no option at all is present.
+ */
+static LOCKMODE
+alter_sequence_get_lock_level(List *options)
+{
+ ListCell *option;
+
+ foreach(option, options)
+ {
+ DefElem *defel = (DefElem *) lfirst(option);
+
+ if (strcmp(defel->defname, "restart") != 0)
+ return ShareRowExclusiveLock;
+ }
+
+ return RowExclusiveLock;
+}
+
/*
* init_params: process the options list of CREATE or ALTER SEQUENCE, and
* store the values into appropriate fields of seqform, for changes that go
bool is_called;
int64 result;
- /* open and AccessShareLock sequence */
+ /* open and lock sequence */
init_sequence(relid, &elm, &seqrel);
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
--- /dev/null
+Parsed test spec with 2 sessions
+
+starting permutation: s1alter s1commit s2nv
+step s1alter: ALTER SEQUENCE seq1 MAXVALUE 10;
+step s1commit: COMMIT;
+step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15);
+ERROR: nextval: reached maximum value of sequence "seq1" (10)
+
+starting permutation: s1alter s2nv s1commit
+step s1alter: ALTER SEQUENCE seq1 MAXVALUE 10;
+step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15); <waiting ...>
+step s1commit: COMMIT;
+step s2nv: <... completed>
+error in steps s1commit s2nv: ERROR: nextval: reached maximum value of sequence "seq1" (10)
+
+starting permutation: s2begin s2nv s1alter2 s2commit s1commit
+step s2begin: BEGIN;
+step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15);
+nextval
+
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+step s1alter2: ALTER SEQUENCE seq1 MAXVALUE 20; <waiting ...>
+step s2commit: COMMIT;
+step s1alter2: <... completed>
+step s1commit: COMMIT;
+
+starting permutation: s1restart s2nv s1commit
+step s1restart: ALTER SEQUENCE seq1 RESTART WITH 5;
+step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15);
+nextval
+
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+step s1commit: COMMIT;
+
+starting permutation: s2begin s2nv s1restart s2commit s1commit
+step s2begin: BEGIN;
+step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15);
+nextval
+
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+step s1restart: ALTER SEQUENCE seq1 RESTART WITH 5;
+step s2commit: COMMIT;
+step s1commit: COMMIT;