mirror of
https://github.com/postgres/postgres.git
synced 2025-06-11 20:28:21 +03:00
Make ALTER SEQUENCE, including RESTART, fully transactional.
Previously the changes to the "data" part of the sequence, i.e. the one containing the current value, were not transactional, whereas the definition, including minimum and maximum value were. That leads to odd behaviour if a schema change is rolled back, with the potential that out-of-bound sequence values can be returned. To avoid the issue create a new relfilenode fork whenever ALTER SEQUENCE is executed, similar to how TRUNCATE ... RESTART IDENTITY already is already handled. This commit also makes ALTER SEQUENCE RESTART transactional, as it seems to be too confusing to have some forms of ALTER SEQUENCE behave transactionally, some forms not. This way setval() and nextval() are not transactional, but DDL is, which seems to make sense. This commit also rolls back parts of the changes made in3d092fe540
andf8dc1985f
as they're now not needed anymore. Author: Andres Freund Discussion: https://postgr.es/m/20170522154227.nvafbsm62sjpbxvd@alap3.anarazel.de Backpatch: Bug is in master/v10 only
This commit is contained in:
@ -171,7 +171,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
|
|||||||
<para>
|
<para>
|
||||||
The optional clause <literal>RESTART [ WITH <replaceable
|
The optional clause <literal>RESTART [ WITH <replaceable
|
||||||
class="parameter">restart</replaceable> ]</literal> changes the
|
class="parameter">restart</replaceable> ]</literal> changes the
|
||||||
current value of the sequence. This is equivalent to calling the
|
current value of the sequence. This is similar to calling the
|
||||||
<function>setval</> function with <literal>is_called</literal> =
|
<function>setval</> function with <literal>is_called</literal> =
|
||||||
<literal>false</>: the specified value will be returned by the
|
<literal>false</>: the specified value will be returned by the
|
||||||
<emphasis>next</> call of <function>nextval</>.
|
<emphasis>next</> call of <function>nextval</>.
|
||||||
@ -182,11 +182,11 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Like a <function>setval</function> call, a <literal>RESTART</literal>
|
In contrast to a <function>setval</function> call,
|
||||||
operation on a sequence is never rolled back, to avoid blocking of
|
a <literal>RESTART</literal> operation on a sequence is transactional
|
||||||
concurrent transactions that obtain numbers from the same sequence.
|
and blocks concurrent transactions from obtaining numbers from the
|
||||||
(The other clauses cause ordinary catalog updates that can be rolled
|
same sequence. If that's not the desired mode of
|
||||||
back.)
|
operation, <function>setval</> should be used.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -307,8 +307,7 @@ ALTER SEQUENCE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> S
|
|||||||
<para>
|
<para>
|
||||||
<command>ALTER SEQUENCE</command> blocks
|
<command>ALTER SEQUENCE</command> blocks
|
||||||
concurrent <function>nextval</function>, <function>currval</function>,
|
concurrent <function>nextval</function>, <function>currval</function>,
|
||||||
<function>lastval</function>, and <command>setval</command> calls, except
|
<function>lastval</function>, and <command>setval</command> calls.
|
||||||
if only the <literal>RESTART</literal> clause is used.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -98,11 +98,9 @@ static void create_seq_hashtable(void);
|
|||||||
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
|
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
|
||||||
static Form_pg_sequence_data read_seq_tuple(Relation rel,
|
static Form_pg_sequence_data read_seq_tuple(Relation rel,
|
||||||
Buffer *buf, HeapTuple seqdatatuple);
|
Buffer *buf, HeapTuple seqdatatuple);
|
||||||
static LOCKMODE alter_sequence_get_lock_level(List *options);
|
|
||||||
static void init_params(ParseState *pstate, List *options, bool for_identity,
|
static void init_params(ParseState *pstate, List *options, bool for_identity,
|
||||||
bool isInit,
|
bool isInit,
|
||||||
Form_pg_sequence seqform,
|
Form_pg_sequence seqform,
|
||||||
bool *changed_seqform,
|
|
||||||
Form_pg_sequence_data seqdataform, List **owned_by);
|
Form_pg_sequence_data seqdataform, List **owned_by);
|
||||||
static void do_setval(Oid relid, int64 next, bool iscalled);
|
static void do_setval(Oid relid, int64 next, bool iscalled);
|
||||||
static void process_owned_by(Relation seqrel, List *owned_by, bool for_identity);
|
static void process_owned_by(Relation seqrel, List *owned_by, bool for_identity);
|
||||||
@ -117,7 +115,6 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
|
|||||||
{
|
{
|
||||||
FormData_pg_sequence seqform;
|
FormData_pg_sequence seqform;
|
||||||
FormData_pg_sequence_data seqdataform;
|
FormData_pg_sequence_data seqdataform;
|
||||||
bool changed_seqform = false; /* not used here */
|
|
||||||
List *owned_by;
|
List *owned_by;
|
||||||
CreateStmt *stmt = makeNode(CreateStmt);
|
CreateStmt *stmt = makeNode(CreateStmt);
|
||||||
Oid seqoid;
|
Oid seqoid;
|
||||||
@ -156,7 +153,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check and set all option values */
|
/* Check and set all option values */
|
||||||
init_params(pstate, seq->options, seq->for_identity, true, &seqform, &changed_seqform, &seqdataform, &owned_by);
|
init_params(pstate, seq->options, seq->for_identity, true, &seqform, &seqdataform, &owned_by);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create relation (and fill value[] and null[] for the tuple)
|
* Create relation (and fill value[] and null[] for the tuple)
|
||||||
@ -417,19 +414,18 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
|
|||||||
SeqTable elm;
|
SeqTable elm;
|
||||||
Relation seqrel;
|
Relation seqrel;
|
||||||
Buffer buf;
|
Buffer buf;
|
||||||
HeapTupleData seqdatatuple;
|
HeapTupleData datatuple;
|
||||||
Form_pg_sequence seqform;
|
Form_pg_sequence seqform;
|
||||||
Form_pg_sequence_data seqdata;
|
Form_pg_sequence_data newdataform;
|
||||||
FormData_pg_sequence_data newseqdata;
|
|
||||||
bool changed_seqform = false;
|
|
||||||
List *owned_by;
|
List *owned_by;
|
||||||
ObjectAddress address;
|
ObjectAddress address;
|
||||||
Relation rel;
|
Relation rel;
|
||||||
HeapTuple tuple;
|
HeapTuple seqtuple;
|
||||||
|
HeapTuple newdatatuple;
|
||||||
|
|
||||||
/* Open and lock sequence. */
|
/* Open and lock sequence. */
|
||||||
relid = RangeVarGetRelid(stmt->sequence,
|
relid = RangeVarGetRelid(stmt->sequence,
|
||||||
alter_sequence_get_lock_level(stmt->options),
|
ShareRowExclusiveLock,
|
||||||
stmt->missing_ok);
|
stmt->missing_ok);
|
||||||
if (relid == InvalidOid)
|
if (relid == InvalidOid)
|
||||||
{
|
{
|
||||||
@ -447,22 +443,26 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
|
|||||||
stmt->sequence->relname);
|
stmt->sequence->relname);
|
||||||
|
|
||||||
rel = heap_open(SequenceRelationId, RowExclusiveLock);
|
rel = heap_open(SequenceRelationId, RowExclusiveLock);
|
||||||
tuple = SearchSysCacheCopy1(SEQRELID,
|
seqtuple = SearchSysCacheCopy1(SEQRELID,
|
||||||
ObjectIdGetDatum(relid));
|
ObjectIdGetDatum(relid));
|
||||||
if (!HeapTupleIsValid(tuple))
|
if (!HeapTupleIsValid(seqtuple))
|
||||||
elog(ERROR, "cache lookup failed for sequence %u",
|
elog(ERROR, "cache lookup failed for sequence %u",
|
||||||
relid);
|
relid);
|
||||||
|
|
||||||
seqform = (Form_pg_sequence) GETSTRUCT(tuple);
|
seqform = (Form_pg_sequence) GETSTRUCT(seqtuple);
|
||||||
|
|
||||||
/* lock page's buffer and read tuple into new sequence structure */
|
/* lock page's buffer and read tuple into new sequence structure */
|
||||||
seqdata = read_seq_tuple(seqrel, &buf, &seqdatatuple);
|
(void) read_seq_tuple(seqrel, &buf, &datatuple);
|
||||||
|
|
||||||
/* Copy old sequence data into workspace */
|
/* copy the existing sequence data tuple, so it can be modified localy */
|
||||||
memcpy(&newseqdata, seqdata, sizeof(FormData_pg_sequence_data));
|
newdatatuple = heap_copytuple(&datatuple);
|
||||||
|
newdataform = (Form_pg_sequence_data) GETSTRUCT(newdatatuple);
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buf);
|
||||||
|
|
||||||
/* Check and set new values */
|
/* Check and set new values */
|
||||||
init_params(pstate, stmt->options, stmt->for_identity, false, seqform, &changed_seqform, &newseqdata, &owned_by);
|
init_params(pstate, stmt->options, stmt->for_identity, false, seqform,
|
||||||
|
newdataform, &owned_by);
|
||||||
|
|
||||||
/* Clear local cache so that we don't think we have cached numbers */
|
/* Clear local cache so that we don't think we have cached numbers */
|
||||||
/* Note that we do not change the currval() state */
|
/* Note that we do not change the currval() state */
|
||||||
@ -472,36 +472,19 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
|
|||||||
if (RelationNeedsWAL(seqrel))
|
if (RelationNeedsWAL(seqrel))
|
||||||
GetTopTransactionId();
|
GetTopTransactionId();
|
||||||
|
|
||||||
/* Now okay to update the on-disk tuple */
|
/*
|
||||||
START_CRIT_SECTION();
|
* Create a new storage file for the sequence, making the state changes
|
||||||
|
* transactional. 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.
|
||||||
|
*/
|
||||||
|
RelationSetNewRelfilenode(seqrel, seqrel->rd_rel->relpersistence,
|
||||||
|
InvalidTransactionId, InvalidMultiXactId);
|
||||||
|
|
||||||
memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data));
|
/*
|
||||||
|
* Insert the modified tuple into the new storage file.
|
||||||
MarkBufferDirty(buf);
|
*/
|
||||||
|
fill_seq_with_data(seqrel, newdatatuple);
|
||||||
/* XLOG stuff */
|
|
||||||
if (RelationNeedsWAL(seqrel))
|
|
||||||
{
|
|
||||||
xl_seq_rec xlrec;
|
|
||||||
XLogRecPtr recptr;
|
|
||||||
Page page = BufferGetPage(buf);
|
|
||||||
|
|
||||||
XLogBeginInsert();
|
|
||||||
XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
|
|
||||||
|
|
||||||
xlrec.node = seqrel->rd_node;
|
|
||||||
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
|
|
||||||
|
|
||||||
XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
|
|
||||||
|
|
||||||
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
|
|
||||||
|
|
||||||
PageSetLSN(page, recptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
END_CRIT_SECTION();
|
|
||||||
|
|
||||||
UnlockReleaseBuffer(buf);
|
|
||||||
|
|
||||||
/* process OWNED BY if given */
|
/* process OWNED BY if given */
|
||||||
if (owned_by)
|
if (owned_by)
|
||||||
@ -511,10 +494,9 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
|
|||||||
|
|
||||||
ObjectAddressSet(address, RelationRelationId, relid);
|
ObjectAddressSet(address, RelationRelationId, relid);
|
||||||
|
|
||||||
if (changed_seqform)
|
CatalogTupleUpdate(rel, &seqtuple->t_self, seqtuple);
|
||||||
CatalogTupleUpdate(rel, &tuple->t_self, tuple);
|
|
||||||
heap_close(rel, RowExclusiveLock);
|
|
||||||
|
|
||||||
|
heap_close(rel, RowExclusiveLock);
|
||||||
relation_close(seqrel, NoLock);
|
relation_close(seqrel, NoLock);
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
@ -1219,30 +1201,6 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
|
|||||||
return seq;
|
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
|
* init_params: process the options list of CREATE or ALTER SEQUENCE, and
|
||||||
* store the values into appropriate fields of seqform, for changes that go
|
* store the values into appropriate fields of seqform, for changes that go
|
||||||
@ -1258,7 +1216,6 @@ static void
|
|||||||
init_params(ParseState *pstate, List *options, bool for_identity,
|
init_params(ParseState *pstate, List *options, bool for_identity,
|
||||||
bool isInit,
|
bool isInit,
|
||||||
Form_pg_sequence seqform,
|
Form_pg_sequence seqform,
|
||||||
bool *changed_seqform,
|
|
||||||
Form_pg_sequence_data seqdataform,
|
Form_pg_sequence_data seqdataform,
|
||||||
List **owned_by)
|
List **owned_by)
|
||||||
{
|
{
|
||||||
@ -1378,8 +1335,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
defel->defname);
|
defel->defname);
|
||||||
}
|
}
|
||||||
|
|
||||||
*changed_seqform = false;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must reset log_cnt when isInit or when changing any parameters that
|
* We must reset log_cnt when isInit or when changing any parameters that
|
||||||
* would affect future nextval allocations.
|
* would affect future nextval allocations.
|
||||||
@ -1420,19 +1375,16 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
}
|
}
|
||||||
|
|
||||||
seqform->seqtypid = newtypid;
|
seqform->seqtypid = newtypid;
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
else if (isInit)
|
else if (isInit)
|
||||||
{
|
{
|
||||||
seqform->seqtypid = INT8OID;
|
seqform->seqtypid = INT8OID;
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* INCREMENT BY */
|
/* INCREMENT BY */
|
||||||
if (increment_by != NULL)
|
if (increment_by != NULL)
|
||||||
{
|
{
|
||||||
seqform->seqincrement = defGetInt64(increment_by);
|
seqform->seqincrement = defGetInt64(increment_by);
|
||||||
*changed_seqform = true;
|
|
||||||
if (seqform->seqincrement == 0)
|
if (seqform->seqincrement == 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
@ -1442,28 +1394,24 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
else if (isInit)
|
else if (isInit)
|
||||||
{
|
{
|
||||||
seqform->seqincrement = 1;
|
seqform->seqincrement = 1;
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CYCLE */
|
/* CYCLE */
|
||||||
if (is_cycled != NULL)
|
if (is_cycled != NULL)
|
||||||
{
|
{
|
||||||
seqform->seqcycle = intVal(is_cycled->arg);
|
seqform->seqcycle = intVal(is_cycled->arg);
|
||||||
*changed_seqform = true;
|
|
||||||
Assert(BoolIsValid(seqform->seqcycle));
|
Assert(BoolIsValid(seqform->seqcycle));
|
||||||
seqdataform->log_cnt = 0;
|
seqdataform->log_cnt = 0;
|
||||||
}
|
}
|
||||||
else if (isInit)
|
else if (isInit)
|
||||||
{
|
{
|
||||||
seqform->seqcycle = false;
|
seqform->seqcycle = false;
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MAXVALUE (null arg means NO MAXVALUE) */
|
/* MAXVALUE (null arg means NO MAXVALUE) */
|
||||||
if (max_value != NULL && max_value->arg)
|
if (max_value != NULL && max_value->arg)
|
||||||
{
|
{
|
||||||
seqform->seqmax = defGetInt64(max_value);
|
seqform->seqmax = defGetInt64(max_value);
|
||||||
*changed_seqform = true;
|
|
||||||
seqdataform->log_cnt = 0;
|
seqdataform->log_cnt = 0;
|
||||||
}
|
}
|
||||||
else if (isInit || max_value != NULL || reset_max_value)
|
else if (isInit || max_value != NULL || reset_max_value)
|
||||||
@ -1480,7 +1428,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
seqform->seqmax = -1; /* descending seq */
|
seqform->seqmax = -1; /* descending seq */
|
||||||
*changed_seqform = true;
|
|
||||||
seqdataform->log_cnt = 0;
|
seqdataform->log_cnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1502,7 +1449,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
if (min_value != NULL && min_value->arg)
|
if (min_value != NULL && min_value->arg)
|
||||||
{
|
{
|
||||||
seqform->seqmin = defGetInt64(min_value);
|
seqform->seqmin = defGetInt64(min_value);
|
||||||
*changed_seqform = true;
|
|
||||||
seqdataform->log_cnt = 0;
|
seqdataform->log_cnt = 0;
|
||||||
}
|
}
|
||||||
else if (isInit || min_value != NULL || reset_min_value)
|
else if (isInit || min_value != NULL || reset_min_value)
|
||||||
@ -1519,7 +1465,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
seqform->seqmin = 1; /* ascending seq */
|
seqform->seqmin = 1; /* ascending seq */
|
||||||
*changed_seqform = true;
|
|
||||||
seqdataform->log_cnt = 0;
|
seqdataform->log_cnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1555,7 +1500,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
if (start_value != NULL)
|
if (start_value != NULL)
|
||||||
{
|
{
|
||||||
seqform->seqstart = defGetInt64(start_value);
|
seqform->seqstart = defGetInt64(start_value);
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
else if (isInit)
|
else if (isInit)
|
||||||
{
|
{
|
||||||
@ -1563,7 +1507,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
seqform->seqstart = seqform->seqmin; /* ascending seq */
|
seqform->seqstart = seqform->seqmin; /* ascending seq */
|
||||||
else
|
else
|
||||||
seqform->seqstart = seqform->seqmax; /* descending seq */
|
seqform->seqstart = seqform->seqmax; /* descending seq */
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* crosscheck START */
|
/* crosscheck START */
|
||||||
@ -1638,7 +1581,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
if (cache_value != NULL)
|
if (cache_value != NULL)
|
||||||
{
|
{
|
||||||
seqform->seqcache = defGetInt64(cache_value);
|
seqform->seqcache = defGetInt64(cache_value);
|
||||||
*changed_seqform = true;
|
|
||||||
if (seqform->seqcache <= 0)
|
if (seqform->seqcache <= 0)
|
||||||
{
|
{
|
||||||
char buf[100];
|
char buf[100];
|
||||||
@ -1654,7 +1596,6 @@ init_params(ParseState *pstate, List *options, bool for_identity,
|
|||||||
else if (isInit)
|
else if (isInit)
|
||||||
{
|
{
|
||||||
seqform->seqcache = 1;
|
seqform->seqcache = 1;
|
||||||
*changed_seqform = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,52 @@ step s1commit: COMMIT;
|
|||||||
step s2nv: <... completed>
|
step s2nv: <... completed>
|
||||||
error in steps s1commit s2nv: ERROR: nextval: reached maximum value of sequence "seq1" (10)
|
error in steps s1commit s2nv: ERROR: nextval: reached maximum value of sequence "seq1" (10)
|
||||||
|
|
||||||
|
starting permutation: s1restart s2nv s1commit
|
||||||
|
step s1restart: ALTER SEQUENCE seq1 RESTART WITH 5;
|
||||||
|
step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15); <waiting ...>
|
||||||
|
step s1commit: COMMIT;
|
||||||
|
step s2nv: <... completed>
|
||||||
|
nextval
|
||||||
|
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
10
|
||||||
|
11
|
||||||
|
12
|
||||||
|
13
|
||||||
|
14
|
||||||
|
15
|
||||||
|
16
|
||||||
|
17
|
||||||
|
18
|
||||||
|
19
|
||||||
|
|
||||||
|
starting permutation: s1restart s2nv s1commit
|
||||||
|
step s1restart: ALTER SEQUENCE seq1 RESTART WITH 5;
|
||||||
|
step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15); <waiting ...>
|
||||||
|
step s1commit: COMMIT;
|
||||||
|
step s2nv: <... completed>
|
||||||
|
nextval
|
||||||
|
|
||||||
|
5
|
||||||
|
6
|
||||||
|
7
|
||||||
|
8
|
||||||
|
9
|
||||||
|
10
|
||||||
|
11
|
||||||
|
12
|
||||||
|
13
|
||||||
|
14
|
||||||
|
15
|
||||||
|
16
|
||||||
|
17
|
||||||
|
18
|
||||||
|
19
|
||||||
|
|
||||||
starting permutation: s2begin s2nv s1alter2 s2commit s1commit
|
starting permutation: s2begin s2nv s1alter2 s2commit s1commit
|
||||||
step s2begin: BEGIN;
|
step s2begin: BEGIN;
|
||||||
step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15);
|
step s2nv: SELECT nextval('seq1') FROM generate_series(1, 15);
|
||||||
@ -37,49 +83,3 @@ step s1alter2: ALTER SEQUENCE seq1 MAXVALUE 20; <waiting ...>
|
|||||||
step s2commit: COMMIT;
|
step s2commit: COMMIT;
|
||||||
step s1alter2: <... completed>
|
step s1alter2: <... completed>
|
||||||
step s1commit: COMMIT;
|
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;
|
|
||||||
|
@ -15,6 +15,7 @@ setup { BEGIN; }
|
|||||||
step "s1alter" { ALTER SEQUENCE seq1 MAXVALUE 10; }
|
step "s1alter" { ALTER SEQUENCE seq1 MAXVALUE 10; }
|
||||||
step "s1alter2" { ALTER SEQUENCE seq1 MAXVALUE 20; }
|
step "s1alter2" { ALTER SEQUENCE seq1 MAXVALUE 20; }
|
||||||
step "s1restart" { ALTER SEQUENCE seq1 RESTART WITH 5; }
|
step "s1restart" { ALTER SEQUENCE seq1 RESTART WITH 5; }
|
||||||
|
step "s1setval" { SELECT setval('seq1', 5); }
|
||||||
step "s1commit" { COMMIT; }
|
step "s1commit" { COMMIT; }
|
||||||
|
|
||||||
session "s2"
|
session "s2"
|
||||||
@ -24,16 +25,18 @@ step "s2commit" { COMMIT; }
|
|||||||
|
|
||||||
permutation "s1alter" "s1commit" "s2nv"
|
permutation "s1alter" "s1commit" "s2nv"
|
||||||
|
|
||||||
# Prior to PG10, the s2nv would see the uncommitted s1alter change,
|
# Prior to PG10, the s2nv step would see the uncommitted s1alter
|
||||||
# but now it waits.
|
# change, but now it waits.
|
||||||
permutation "s1alter" "s2nv" "s1commit"
|
permutation "s1alter" "s2nv" "s1commit"
|
||||||
|
|
||||||
|
# Prior to PG10, the s2nv step would see the uncommitted s1reset
|
||||||
|
# change, but now it waits.
|
||||||
|
permutation "s1restart" "s2nv" "s1commit"
|
||||||
|
|
||||||
|
# In contrast to ALTER setval() is non-transactional, so it doesn't
|
||||||
|
# have to wait.
|
||||||
|
permutation "s1restart" "s2nv" "s1commit"
|
||||||
|
|
||||||
# nextval doesn't release lock until transaction end, so s1alter2 has
|
# nextval doesn't release lock until transaction end, so s1alter2 has
|
||||||
# to wait for s2commit.
|
# to wait for s2commit.
|
||||||
permutation "s2begin" "s2nv" "s1alter2" "s2commit" "s1commit"
|
permutation "s2begin" "s2nv" "s1alter2" "s2commit" "s1commit"
|
||||||
|
|
||||||
# RESTART is nontransactional, so s2nv sees it right away
|
|
||||||
permutation "s1restart" "s2nv" "s1commit"
|
|
||||||
|
|
||||||
# RESTART does not wait
|
|
||||||
permutation "s2begin" "s2nv" "s1restart" "s2commit" "s1commit"
|
|
||||||
|
Reference in New Issue
Block a user