Getting rid of "tuple concurrently updated" elog()s with concurrent DDLs (at least ALTER TABLE)
Hi all,
Triggering "tuple concurrently updated" from heapam.c, which is an elog(),
is not that difficult for some DDL commands. For example with ALTER ROLE,
just create a role:
psql --no-psqlrc -c 'create role popo'
And then run that in two sessions and the error will show up, triggered
from CatalogTupleUpdate():
while true; do psql --no-psqlrc -c "alter role popo password 'foo'"; done
In this case, upgrading the lock taken on pg_authid from RowExclusiveLock
to ShareRowExclusiveLock prevents concurrent sessions to update each other,
which is what the patch attached does.
I think that we should get rid of all those errors, for many applications
doing concurrent DDLs getting this error is surprising, and could be thought
as a corrupted instance. I am just touching the tip of the iceberg here, as
I have the gut feeling that there are other objects where it is possible to
trigger the failure, and an analysis effort is going to be costly. So I'd
like to get first the temperature about such analysis.
So, opinions?
--
Michael
Attachments:
alter-role-concurrent-v1.patchtext/plain; charset=us-asciiDownload
diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml
index 24613e3c75..a93b31ae6d 100644
--- a/doc/src/sgml/mvcc.sgml
+++ b/doc/src/sgml/mvcc.sgml
@@ -971,6 +971,7 @@ ERROR: could not serialize access due to read/write dependencies among transact
<para>
Acquired by <command>CREATE COLLATION</command>,
+ <command>ALTER ROLE</command>,
<command>CREATE TRIGGER</command>, and many forms of
<command>ALTER TABLE</command> (see <xref linkend="sql-altertable"/>).
</para>
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index f2941352d7..d2e300d949 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -671,9 +671,11 @@ AlterRole(AlterRoleStmt *stmt)
bypassrls = intVal(dbypassRLS->arg);
/*
- * Scan the pg_authid relation to be certain the user exists.
+ * Scan the pg_authid relation to be certain the user exists. Take
+ * ShareRowExclusiveLock to prevent other sessions to update this
+ * tuple in parallel with another ALTER ROLE command.
*/
- pg_authid_rel = heap_open(AuthIdRelationId, RowExclusiveLock);
+ pg_authid_rel = heap_open(AuthIdRelationId, ShareRowExclusiveLock);
pg_authid_dsc = RelationGetDescr(pg_authid_rel);
tuple = get_rolespec_tuple(stmt->role);
On December 26, 2017 5:55:03 AM GMT+01:00, Michael Paquier <michael.paquier@gmail.com> wrote:
Hi all,
Triggering "tuple concurrently updated" from heapam.c, which is an
elog(),
is not that difficult for some DDL commands. For example with ALTER
ROLE,
just create a role:
psql --no-psqlrc -c 'create role popo'And then run that in two sessions and the error will show up, triggered
from CatalogTupleUpdate():
while true; do psql --no-psqlrc -c "alter role popo password 'foo'";
doneIn this case, upgrading the lock taken on pg_authid from
RowExclusiveLock
to ShareRowExclusiveLock prevents concurrent sessions to update each
other,
which is what the patch attached does.I think that we should get rid of all those errors, for many
applications
doing concurrent DDLs getting this error is surprising, and could be
thought
as a corrupted instance. I am just touching the tip of the iceberg
here, as
I have the gut feeling that there are other objects where it is
possible to
trigger the failure, and an analysis effort is going to be costly. So
I'd
like to get first the temperature about such analysis.So, opinions?
You're proposing to lock the entire relation against many forms of concurrent DDL, just to get rid of that error? That seems unacceptable.
Isn't the canonical way to solve this to take object locks?
Andres
Andres
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
On Tue, Dec 26, 2017 at 4:30 PM, Andres Freund <andres@anarazel.de> wrote:
You're proposing to lock the entire relation against many forms of concurrent DDL, just to get rid of that error? That seems unacceptable.
Isn't the canonical way to solve this to take object locks?
Sure. That's where things in lmgr.c come into play, like
LockSharedObject(), and you could hold with an exclusive lock on a
given object until the end of a transaction before opening the catalog
relation with heap_open(), however with those you need to know the
object OID before taking a lock on the parent relation, right? So you
face problems with lock upgrades, or race conditions show up more
easily. I have to admit that I have not dug much into the problem yet,
it is easy enough to have isolation tests by the way, and I just
noticed that ALTER DATABASE SET can equally trigger the error.
--
Michael
On Tue, Dec 26, 2017 at 4:14 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Tue, Dec 26, 2017 at 4:30 PM, Andres Freund <andres@anarazel.de> wrote:
You're proposing to lock the entire relation against many forms of concurrent DDL, just to get rid of that error? That seems unacceptable.
Isn't the canonical way to solve this to take object locks?Sure. That's where things in lmgr.c come into play, like
LockSharedObject(), and you could hold with an exclusive lock on a
given object until the end of a transaction before opening the catalog
relation with heap_open(), however with those you need to know the
object OID before taking a lock on the parent relation, right? So you
face problems with lock upgrades, or race conditions show up more
easily. I have to admit that I have not dug much into the problem yet,
it is easy enough to have isolation tests by the way, and I just
noticed that ALTER DATABASE SET can equally trigger the error.
I don't understand what you mean by "you need to know the object OID
before taking a lock on the parent relation, right?". That seems
wrong.
I think you might need something like what was done in
b3ad5d02c9cd8a4c884cd78480f221afe8ce5590; if, after we look up the
name and before we acquire a lock on the OID, we accept any
invalidation messages, recheck that the object we've locked is still
the one associated with that name.
I think Andres is certainly right that locking all of pg_authid is a nonstarter.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Tue, Dec 26, 2017 at 10:47:59PM -0800, Robert Haas wrote:
On Tue, Dec 26, 2017 at 4:14 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:On Tue, Dec 26, 2017 at 4:30 PM, Andres Freund <andres@anarazel.de> wrote:
You're proposing to lock the entire relation against many forms of concurrent DDL, just to get rid of that error? That seems unacceptable.
Isn't the canonical way to solve this to take object locks?Sure. That's where things in lmgr.c come into play, like
LockSharedObject(), and you could hold with an exclusive lock on a
given object until the end of a transaction before opening the catalog
relation with heap_open(), however with those you need to know the
object OID before taking a lock on the parent relation, right? So you
face problems with lock upgrades, or race conditions show up more
easily. I have to admit that I have not dug much into the problem yet,
it is easy enough to have isolation tests by the way, and I just
noticed that ALTER DATABASE SET can equally trigger the error.I don't understand what you mean by "you need to know the object OID
before taking a lock on the parent relation, right?". That seems
wrong.
Well, the point I am trying to outline here is that it is essential to
take a lock on the object ID before opening pg_authid with heap_open()
with a low-level lock to avoid any concurrency issues when working on
the catalog relation opened.
I think you might need something like what was done in
b3ad5d02c9cd8a4c884cd78480f221afe8ce5590; if, after we look up the
name and before we acquire a lock on the OID, we accept any
invalidation messages, recheck that the object we've locked is still
the one associated with that name.
Indeed, this bit is important, and RangeVarGet does so. Looking at
get_object_address(), it also handles locking of the object. Using that
as interface to address all the concurrency problems could be appealing,
and less invasive for a final result as many DDLs are visibly
impacted (still need to make a list here). What do you think?
--
Michael
On Wed, Dec 27, 2017 at 04:53:42PM +0900, Michael Paquier wrote:
Indeed, this bit is important, and RangeVarGet does so. Looking at
get_object_address(), it also handles locking of the object. Using that
as interface to address all the concurrency problems could be appealing,
and less invasive for a final result as many DDLs are visibly
impacted (still need to make a list here). What do you think?
So, I have looked at all the object types to spot concurrency problems,
and I have found some more. And the winners are:
- database
- role
- domain
- event trigger
- FDW
- server
- user mapping
- type
- tablespace
Most of the failures show "tuple concurrently updated" using a given set
of ALTER commands running concurrently, but I have been able to trigger
as well one "cache lookup failure" for domains, and a duplicate key value
violation for databases.
I have done some extra checks with parallel ALTER commands for the
following objects as well, without spotting issues:
- access method
- cast
- attribute
- extension
- view
- policy
- publication
- subscription
- rule
- transform
- text search stuff
- statistics
- operator [family], etc.
As looking for those failures manually is no fun, I am attaching a patch
which includes a set of isolation regression tests able to trigger
them. You just need to look for unwelcome ERROR messages and you are
good to go. The goal to move forward will be to take care of all those
failures. Please note that isolation tests don't allow dynamic input, so
those have no tests but you can reproduce an error easily with ALTER
TABLESPACE SET SCHEMA between two sessions. Note as well that the domain
test will fail because the cache lookup error on domains exposes the
domain OID, but that's nothing to care about.
Opinions or thoughts?
--
Michael
Attachments:
concurrent-ddl-specs.patchtext/plain; charset=us-asciiDownload
diff --git a/src/test/isolation/expected/concurrent-database.out b/src/test/isolation/expected/concurrent-database.out
new file mode 100644
index 0000000000..59da795117
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-database.out
@@ -0,0 +1,105 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_alterdb_with s2_begin s2_alterdb_with s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_with:
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true;
+step s2_begin: BEGIN;
+step s2_alterdb_with:
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alterdb_with: <... completed>
+error in steps s1_commit s2_alterdb_with: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_with s2_begin s2_alterdb_set s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_with:
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true;
+step s2_begin: BEGIN;
+step s2_alterdb_set:
+ ALTER DATABASE regress_database SET commit_delay = '10';
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_with s2_begin s2_alterdb_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_with:
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true;
+step s2_begin: BEGIN;
+step s2_alterdb_owner:
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_with s2_begin s2_alterdb_tbc s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_with:
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true;
+step s2_begin: BEGIN;
+step s2_alterdb_tbc:
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_set s2_begin s2_alterdb_set s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_set:
+ ALTER DATABASE regress_database SET commit_delay = '10';
+step s2_begin: BEGIN;
+step s2_alterdb_set:
+ ALTER DATABASE regress_database SET commit_delay = '10'; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alterdb_set: <... completed>
+error in steps s1_commit s2_alterdb_set: ERROR: duplicate key value violates unique constraint "pg_db_role_setting_databaseid_rol_index"
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_set s2_begin s2_alterdb_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_set:
+ ALTER DATABASE regress_database SET commit_delay = '10';
+step s2_begin: BEGIN;
+step s2_alterdb_owner:
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_set s2_begin s2_alterdb_tbc s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_set:
+ ALTER DATABASE regress_database SET commit_delay = '10';
+step s2_begin: BEGIN;
+step s2_alterdb_tbc:
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_owner s2_begin s2_alterdb_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_owner:
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_alterdb_owner:
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_owner s2_begin s2_alterdb_tbc s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_owner:
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_alterdb_tbc:
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterdb_tbc s2_begin s2_alterdb_tbc s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterdb_tbc:
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT;
+step s2_begin: BEGIN;
+step s2_alterdb_tbc:
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-domain.out b/src/test/isolation/expected/concurrent-domain.out
new file mode 100644
index 0000000000..caf7902d45
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-domain.out
@@ -0,0 +1,519 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_set_notnull s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_set_notnull: <... completed>
+error in steps s1_commit s2_dom_set_notnull: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_drop_notnull s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_set_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_set_default: <... completed>
+error in steps s1_commit s2_dom_set_default: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_drop_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_drop_default: <... completed>
+error in steps s1_commit s2_dom_drop_default: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_add_con s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_schema_priv: <... completed>
+error in steps s1_commit s2_dom_schema_priv: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_notnull s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_notnull:
+ ALTER DOMAIN regress_domain_conc SET NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_drop_notnull s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_set_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_drop_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_add_con s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_notnull s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_notnull:
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_default s2_begin s2_dom_set_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s2_begin: BEGIN;
+step s2_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_set_default: <... completed>
+error in steps s1_commit s2_dom_set_default: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_default s2_begin s2_dom_drop_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s2_begin: BEGIN;
+step s2_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_drop_default: <... completed>
+error in steps s1_commit s2_dom_drop_default: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_default s2_begin s2_dom_add_con s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s2_begin: BEGIN;
+step s2_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_default s2_begin s2_dom_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_default s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_schema_priv: <... completed>
+error in steps s1_commit s2_dom_schema_priv: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_set_default s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_set_default:
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_default s2_begin s2_dom_drop_default s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT;
+step s2_begin: BEGIN;
+step s2_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_drop_default: <... completed>
+error in steps s1_commit s2_dom_drop_default: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_default s2_begin s2_dom_add_con s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT;
+step s2_begin: BEGIN;
+step s2_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_default s2_begin s2_dom_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_default s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_schema_priv: <... completed>
+error in steps s1_commit s2_dom_schema_priv: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_drop_default s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_drop_default:
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_add_con s2_begin s2_dom_add_con2 s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s2_begin: BEGIN;
+step s2_dom_add_con2:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con2 CHECK (VALUE > 0);
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_add_con s2_begin s2_dom_drop_con s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s2_begin: BEGIN;
+step s2_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con;
+ERROR: constraint "dom_con" of domain "regress_domain_conc" does not exist
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_add_con s2_begin s2_dom_validate_con s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s2_begin: BEGIN;
+step s2_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con;
+ERROR: constraint "dom_con" of domain "regress_domain_conc" does not exist
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_add_con s2_begin s2_dom_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_add_con s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_add_con s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_drop_con s2_begin s2_dom_drop_con s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_drop_con: <... completed>
+error in steps s1_commit s2_dom_drop_con: ERROR: cache lookup failed for constraint 16509
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_drop_con s2_begin s2_dom_validate_con s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_validate_con: <... completed>
+error in steps s1_commit s2_dom_validate_con: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_drop_con s2_begin s2_dom_owner s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_drop_con s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_drop_con s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_drop_con:
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_schema_priv: <... completed>
+error in steps s1_commit s2_dom_schema_priv: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_validate_con s2_begin s2_dom_validate_con s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_validate_con: <... completed>
+error in steps s1_commit s2_dom_validate_con: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_validate_con s2_begin s2_dom_owner s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_validate_con s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_dom_schema_priv: <... completed>
+error in steps s1_commit s2_dom_schema_priv: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_dom_add_con s1_begin s1_dom_validate_con s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_dom_add_con:
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0);
+step s1_begin: BEGIN;
+step s1_dom_validate_con:
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_owner s2_begin s2_dom_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_owner s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_owner s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_owner:
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_schema_priv s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_schema_publ s2_begin s2_dom_schema_priv s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s2_begin: BEGIN;
+step s2_dom_schema_priv:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_dom_schema_publ s2_begin s2_dom_schema_publ s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s2_begin: BEGIN;
+step s2_dom_schema_publ:
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-event-trigger.out b/src/test/isolation/expected/concurrent-event-trigger.out
new file mode 100644
index 0000000000..43dca2d8af
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-event-trigger.out
@@ -0,0 +1,55 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_disable s2_begin s2_disable s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_disable: ALTER EVENT TRIGGER notice_ddl DISABLE;
+step s2_begin: BEGIN;
+step s2_disable: ALTER EVENT TRIGGER notice_ddl DISABLE; <waiting ...>
+step s1_commit: COMMIT;
+step s2_disable: <... completed>
+error in steps s1_commit s2_disable: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_disable s2_begin s2_enable s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_disable: ALTER EVENT TRIGGER notice_ddl DISABLE;
+step s2_begin: BEGIN;
+step s2_enable: ALTER EVENT TRIGGER notice_ddl ENABLE REPLICA; <waiting ...>
+step s1_commit: COMMIT;
+step s2_enable: <... completed>
+error in steps s1_commit s2_enable: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_disable s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_disable: ALTER EVENT TRIGGER notice_ddl DISABLE;
+step s2_begin: BEGIN;
+step s2_owner: ALTER EVENT TRIGGER notice_ddl OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_enable s2_begin s2_enable s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_enable: ALTER EVENT TRIGGER notice_ddl ENABLE REPLICA;
+step s2_begin: BEGIN;
+step s2_enable: ALTER EVENT TRIGGER notice_ddl ENABLE REPLICA; <waiting ...>
+step s1_commit: COMMIT;
+step s2_enable: <... completed>
+error in steps s1_commit s2_enable: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_enable s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_enable: ALTER EVENT TRIGGER notice_ddl ENABLE REPLICA;
+step s2_begin: BEGIN;
+step s2_owner: ALTER EVENT TRIGGER notice_ddl OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_owner s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_owner: ALTER EVENT TRIGGER notice_ddl OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_owner: ALTER EVENT TRIGGER notice_ddl OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-fdw.out b/src/test/isolation/expected/concurrent-fdw.out
new file mode 100644
index 0000000000..9c3724df34
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-fdw.out
@@ -0,0 +1,118 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_options s2_begin s2_options s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1');
+step s2_begin: BEGIN;
+step s2_options:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1'); <waiting ...>
+step s1_commit: COMMIT;
+step s2_options: <... completed>
+error in steps s1_commit s2_options: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_options s2_begin s2_validator s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1');
+step s2_begin: BEGIN;
+step s2_validator:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR; <waiting ...>
+step s1_commit: COMMIT;
+step s2_validator: <... completed>
+error in steps s1_commit s2_validator: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_options s2_begin s2_handler s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1');
+step s2_begin: BEGIN;
+WARNING: changing the foreign-data wrapper handler can change behavior of existing foreign tables
+step s2_handler:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER; <waiting ...>
+step s1_commit: COMMIT;
+step s2_handler: <... completed>
+error in steps s1_commit s2_handler: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_options s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1');
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_validator s2_begin s2_validator s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_validator:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR;
+step s2_begin: BEGIN;
+step s2_validator:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR; <waiting ...>
+step s1_commit: COMMIT;
+step s2_validator: <... completed>
+error in steps s1_commit s2_validator: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_validator s2_begin s2_handler s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_validator:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR;
+step s2_begin: BEGIN;
+WARNING: changing the foreign-data wrapper handler can change behavior of existing foreign tables
+step s2_handler:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER; <waiting ...>
+step s1_commit: COMMIT;
+step s2_handler: <... completed>
+error in steps s1_commit s2_handler: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_validator s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_validator:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_handler s2_begin s2_handler s1_commit s2_commit
+step s1_begin: BEGIN;
+WARNING: changing the foreign-data wrapper handler can change behavior of existing foreign tables
+step s1_handler:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER;
+step s2_begin: BEGIN;
+WARNING: changing the foreign-data wrapper handler can change behavior of existing foreign tables
+step s2_handler:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER; <waiting ...>
+step s1_commit: COMMIT;
+step s2_handler: <... completed>
+error in steps s1_commit s2_handler: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_handler s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+WARNING: changing the foreign-data wrapper handler can change behavior of existing foreign tables
+step s1_handler:
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_owner s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_owner:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-role.out b/src/test/isolation/expected/concurrent-role.out
new file mode 100644
index 0000000000..86bf17365d
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-role.out
@@ -0,0 +1,45 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_alterrole_set s2_begin s2_alterrole_set s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterrole_set:
+ ALTER ROLE regress_role_conc SET commit_delay = '10';
+step s2_begin: BEGIN;
+step s2_alterrole_set:
+ ALTER ROLE regress_role_conc SET commit_delay = '10'; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alterrole_set: <... completed>
+error in steps s1_commit s2_alterrole_set: ERROR: duplicate key value violates unique constraint "pg_db_role_setting_databaseid_rol_index"
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterrole_set s2_begin s2_alterrole_opt s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterrole_set:
+ ALTER ROLE regress_role_conc SET commit_delay = '10';
+step s2_begin: BEGIN;
+step s2_alterrole_opt:
+ ALTER ROLE regress_role_conc PASSWORD 'foo';
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterrole_opt s2_begin s2_alterrole_set s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterrole_opt:
+ ALTER ROLE regress_role_conc PASSWORD 'foo';
+step s2_begin: BEGIN;
+step s2_alterrole_set:
+ ALTER ROLE regress_role_conc SET commit_delay = '10';
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alterrole_opt s2_begin s2_alterrole_opt s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alterrole_opt:
+ ALTER ROLE regress_role_conc PASSWORD 'foo';
+step s2_begin: BEGIN;
+step s2_alterrole_opt:
+ ALTER ROLE regress_role_conc PASSWORD 'foo'; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alterrole_opt: <... completed>
+error in steps s1_commit s2_alterrole_opt: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-server.out b/src/test/isolation/expected/concurrent-server.out
new file mode 100644
index 0000000000..303569f6ce
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-server.out
@@ -0,0 +1,33 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_options s2_begin s2_options s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER SERVER regress_server OPTIONS (a '1');
+step s2_begin: BEGIN;
+step s2_options:
+ ALTER SERVER regress_server OPTIONS (a '1'); <waiting ...>
+step s1_commit: COMMIT;
+step s2_options: <... completed>
+error in steps s1_commit s2_options: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_options s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER SERVER regress_server OPTIONS (a '1');
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER SERVER regress_server OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_owner s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_owner:
+ ALTER SERVER regress_server OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER SERVER regress_server OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-type.out b/src/test/isolation/expected/concurrent-type.out
new file mode 100644
index 0000000000..45dbbcd130
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-type.out
@@ -0,0 +1,229 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_add_attr s2_begin s2_add_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int;
+step s2_begin: BEGIN;
+step s2_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b2 int; <waiting ...>
+step s1_commit: COMMIT;
+step s2_add_attr: <... completed>
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_add_attr s2_begin s2_drop_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int;
+step s2_begin: BEGIN;
+step s2_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_attr: <... completed>
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_add_attr s2_begin s2_alter_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int;
+step s2_begin: BEGIN;
+step s2_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alter_attr: <... completed>
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_add_attr s2_begin s2_rename_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int;
+step s2_begin: BEGIN;
+step s2_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b; <waiting ...>
+step s1_commit: COMMIT;
+step s2_rename_attr: <... completed>
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_add_attr s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_add_attr s2_begin s2_schema s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_add_attr:
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int;
+step s2_begin: BEGIN;
+step s2_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_schema: <... completed>
+error in steps s1_commit s2_schema: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_drop_attr s2_begin s2_drop_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a;
+step s2_begin: BEGIN;
+step s2_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_attr: <... completed>
+error in steps s1_commit s2_drop_attr: ERROR: column "a" of relation "regress_type" does not exist
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_drop_attr s2_begin s2_alter_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a;
+step s2_begin: BEGIN;
+step s2_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alter_attr: <... completed>
+error in steps s1_commit s2_alter_attr: ERROR: column "a" of relation "regress_type" does not exist
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_drop_attr s2_begin s2_rename_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a;
+step s2_begin: BEGIN;
+step s2_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b; <waiting ...>
+step s1_commit: COMMIT;
+step s2_rename_attr: <... completed>
+error in steps s1_commit s2_rename_attr: ERROR: column "a" does not exist
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_drop_attr s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_drop_attr s2_begin s2_schema s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_drop_attr:
+ ALTER TYPE regress_type DROP ATTRIBUTE a;
+step s2_begin: BEGIN;
+step s2_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alter_attr s2_begin s2_alter_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8;
+step s2_begin: BEGIN;
+step s2_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8; <waiting ...>
+step s1_commit: COMMIT;
+step s2_alter_attr: <... completed>
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alter_attr s2_begin s2_rename_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8;
+step s2_begin: BEGIN;
+step s2_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b; <waiting ...>
+step s1_commit: COMMIT;
+step s2_rename_attr: <... completed>
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alter_attr s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_alter_attr s2_begin s2_schema s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_alter_attr:
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8;
+step s2_begin: BEGIN;
+step s2_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_rename_attr s2_begin s2_rename_attr s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b;
+step s2_begin: BEGIN;
+step s2_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b; <waiting ...>
+step s1_commit: COMMIT;
+step s2_rename_attr: <... completed>
+error in steps s1_commit s2_rename_attr: ERROR: column "a" does not exist
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_rename_attr s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_rename_attr s2_begin s2_schema s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_rename_attr:
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b;
+step s2_begin: BEGIN;
+step s2_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_owner s2_begin s2_owner s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_owner s2_begin s2_schema s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_owner:
+ ALTER TYPE regress_type OWNER TO CURRENT_USER;
+step s2_begin: BEGIN;
+step s2_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema;
+step s1_commit: COMMIT;
+step s2_commit: COMMIT;
+
+starting permutation: s1_begin s1_schema s2_begin s2_schema s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema;
+step s2_begin: BEGIN;
+step s2_schema:
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_schema: <... completed>
+error in steps s1_commit s2_schema: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/expected/concurrent-user-mapping.out b/src/test/isolation/expected/concurrent-user-mapping.out
new file mode 100644
index 0000000000..63a89e2e88
--- /dev/null
+++ b/src/test/isolation/expected/concurrent-user-mapping.out
@@ -0,0 +1,15 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_options s2_begin s2_options s1_commit s2_commit
+step s1_begin: BEGIN;
+step s1_options:
+ ALTER USER MAPPING FOR CURRENT_USER server regress_server
+ OPTIONS (SET USER 'popo1');
+step s2_begin: BEGIN;
+step s2_options:
+ ALTER USER MAPPING FOR CURRENT_USER server regress_server
+ OPTIONS (SET USER 'popo1'); <waiting ...>
+step s1_commit: COMMIT;
+step s2_options: <... completed>
+error in steps s1_commit s2_options: ERROR: tuple concurrently updated
+step s2_commit: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index eb566ebb6c..f02a196dcc 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -64,3 +64,11 @@ test: async-notify
test: vacuum-reltuples
test: timeouts
test: vacuum-concurrent-drop
+test: concurrent-database
+test: concurrent-role
+test: concurrent-domain
+test: concurrent-event-trigger
+test: concurrent-fdw
+test: concurrent-server
+test: concurrent-user-mapping
+test: concurrent-type
diff --git a/src/test/isolation/specs/concurrent-database.spec b/src/test/isolation/specs/concurrent-database.spec
new file mode 100644
index 0000000000..a3812abcdc
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-database.spec
@@ -0,0 +1,64 @@
+# Test for interactions with DDL commands manipulating database
+# objects.
+# XXX: This cannot be included in the final patch as installcheck would
+# create a database on existing instances!
+# The following set of commands can interact in concurrency:
+# - ALTER DATABASE SET
+# - ALTER DATABASE OWNER TO
+# - ALTER DATABASE SET TABLESPACE
+# - ALTER DATABASE WITH
+
+setup
+{
+ CREATE DATABASE regress_database TEMPLATE template0;
+}
+
+teardown
+{
+ DROP DATABASE regress_database;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_alterdb_with" {
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true; }
+step "s1_alterdb_set" {
+ ALTER DATABASE regress_database SET commit_delay = '10'; }
+step "s1_alterdb_owner" {
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER; }
+step "s1_alterdb_tbc" {
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_alterdb_with" {
+ ALTER DATABASE regress_database WITH ALLOW_CONNECTIONS true; }
+step "s2_alterdb_set" {
+ ALTER DATABASE regress_database SET commit_delay = '10'; }
+step "s2_alterdb_owner" {
+ ALTER DATABASE regress_database OWNER TO CURRENT_USER; }
+step "s2_alterdb_tbc" {
+ ALTER DATABASE regress_database SET TABLESPACE TO DEFAULT; }
+step "s2_commit" { COMMIT; }
+
+permutation "s1_begin" "s1_alterdb_with" "s2_begin" "s2_alterdb_with"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_with" "s2_begin" "s2_alterdb_set"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_with" "s2_begin" "s2_alterdb_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_with" "s2_begin" "s2_alterdb_tbc"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_set" "s2_begin" "s2_alterdb_set"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_set" "s2_begin" "s2_alterdb_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_set" "s2_begin" "s2_alterdb_tbc"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_owner" "s2_begin" "s2_alterdb_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_owner" "s2_begin" "s2_alterdb_tbc"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterdb_tbc" "s2_begin" "s2_alterdb_tbc"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-domain.spec b/src/test/isolation/specs/concurrent-domain.spec
new file mode 100644
index 0000000000..1525a53c42
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-domain.spec
@@ -0,0 +1,191 @@
+# Test for interactions with DDL commands manipulating domain
+# objects.
+# The following set of commands can interact in concurrency:
+# - ALTER DOMAIN SET NOT NULL
+# - ALTER DOMAIN DROP NOT NULL
+# - ALTER DOMAIN SET DEFAULT
+# - ALTER DOMAIN DROP DEFAULT
+# - ALTER DOMAIN ADD CONSTRAINT
+# - ALTER DOMAIN DROP CONSTRAINT
+# - ALTER DOMAIN VALIDATE CONSTRAINT
+# - ALTER DOMAIN OWNER TO
+# - ALTER DOMAIN SET SCHEMA
+
+setup
+{
+ CREATE SCHEMA regress_domain_schema;
+ CREATE DOMAIN regress_domain_conc AS int;
+}
+
+teardown
+{
+ DROP DOMAIN IF EXISTS regress_domain_conc;
+ DROP SCHEMA regress_domain_schema CASCADE;
+
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_dom_set_notnull" {
+ ALTER DOMAIN regress_domain_conc SET NOT NULL; }
+step "s1_dom_drop_notnull" {
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL; }
+step "s1_dom_set_default" {
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1; }
+step "s1_dom_drop_default" {
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT; }
+step "s1_dom_add_con" {
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0); }
+step "s1_dom_drop_con" {
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con; }
+step "s1_dom_validate_con" {
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con; }
+step "s1_dom_owner" {
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER; }
+step "s1_dom_schema_priv" {
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; }
+step "s1_dom_schema_publ" {
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_dom_set_notnull" {
+ ALTER DOMAIN regress_domain_conc SET NOT NULL; }
+step "s2_dom_drop_notnull" {
+ ALTER DOMAIN regress_domain_conc DROP NOT NULL; }
+step "s2_dom_set_default" {
+ ALTER DOMAIN regress_domain_conc SET DEFAULT 1; }
+step "s2_dom_drop_default" {
+ ALTER DOMAIN regress_domain_conc DROP DEFAULT; }
+step "s2_dom_add_con" {
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con CHECK (VALUE > 0); }
+step "s2_dom_add_con2" {
+ ALTER DOMAIN regress_domain_conc ADD CONSTRAINT dom_con2 CHECK (VALUE > 0); }
+step "s2_dom_drop_con" {
+ ALTER DOMAIN regress_domain_conc DROP CONSTRAINT dom_con; }
+step "s2_dom_validate_con" {
+ ALTER DOMAIN regress_domain_conc VALIDATE CONSTRAINT dom_con; }
+step "s2_dom_owner" {
+ ALTER DOMAIN regress_domain_conc OWNER TO CURRENT_USER; }
+step "s2_dom_schema_priv" {
+ ALTER DOMAIN regress_domain_conc SET SCHEMA regress_domain_schema; }
+step "s2_dom_schema_publ" {
+ ALTER DOMAIN regress_domain_conc SET SCHEMA public; }
+step "s2_commit" { COMMIT; }
+
+# Round 1 of all permutations using SET NOT NULL as base
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_set_notnull"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_drop_notnull"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_set_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_drop_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_add_con"
+ "s1_commit" "s2_commit"
+# No DROP/VALIDATE CONSTRAINT needed as nothing is defined.
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_notnull" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+
+# Round 2 of all permutations using DROP NOT NULL as base
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_drop_notnull"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_set_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_drop_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_add_con"
+ "s1_commit" "s2_commit"
+# No DROP/VALIDATE CONSTRAINT needed as nothing is defined.
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_notnull" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+
+# Round 3 of all permutations using SET DEFAULT as base
+permutation "s1_begin" "s1_dom_set_default" "s2_begin" "s2_dom_set_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_default" "s2_begin" "s2_dom_drop_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_default" "s2_begin" "s2_dom_add_con"
+ "s1_commit" "s2_commit"
+# No DROP/VALIDATE CONSTRAINT needed as nothing is defined.
+permutation "s1_begin" "s1_dom_set_default" "s2_begin" "s2_dom_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_default" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_set_default" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+
+# Round 4 of all permutations using DROP DEFAULT as base
+permutation "s1_begin" "s1_dom_drop_default" "s2_begin" "s2_dom_drop_default"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_default" "s2_begin" "s2_dom_add_con"
+ "s1_commit" "s2_commit"
+# No DROP/VALIDATE CONSTRAINT needed as nothing is defined.
+permutation "s1_begin" "s1_dom_drop_default" "s2_begin" "s2_dom_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_default" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_drop_default" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+
+# Round 5 of all permutations using ADD CONSTRAINT as a base
+permutation "s1_begin" "s1_dom_add_con" "s2_begin" "s2_dom_add_con2"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_add_con" "s2_begin" "s2_dom_drop_con"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_add_con" "s2_begin" "s2_dom_validate_con"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_add_con" "s2_begin" "s2_dom_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_add_con" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_add_con" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+
+# Round 6 with DROP CONSTRAINT
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_drop_con" "s2_begin"
+ "s2_dom_drop_con" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_drop_con" "s2_begin"
+ "s2_dom_validate_con" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_drop_con" "s2_begin"
+ "s2_dom_owner" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_drop_con" "s2_begin"
+ "s2_dom_schema_publ" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_drop_con" "s2_begin"
+ "s2_dom_schema_priv" "s1_commit" "s2_commit"
+
+# Round 7 of all permutations using VALIDATE CONSTRAINT
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_validate_con" "s2_begin"
+ "s2_dom_validate_con" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_validate_con" "s2_begin"
+ "s2_dom_owner" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_validate_con" "s2_begin"
+ "s2_dom_schema_priv" "s1_commit" "s2_commit"
+permutation "s1_dom_add_con" "s1_begin" "s1_dom_validate_con" "s2_begin"
+ "s2_dom_schema_publ" "s1_commit" "s2_commit"
+
+# Round 8 of all permutations using OWNER TO as base
+permutation "s1_begin" "s1_dom_owner" "s2_begin" "s2_dom_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_owner" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_owner" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+
+# Round 9 with schemas
+permutation "s1_begin" "s1_dom_schema_priv" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_schema_publ" "s2_begin" "s2_dom_schema_priv"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_dom_schema_publ" "s2_begin" "s2_dom_schema_publ"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-event-trigger.spec b/src/test/isolation/specs/concurrent-event-trigger.spec
new file mode 100644
index 0000000000..f896f0754c
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-event-trigger.spec
@@ -0,0 +1,53 @@
+# Test for interactions with DDL commands manipulating event trigger
+# objects.
+# The following set of commands can interact in concurrency:
+# - ALTER EVENT TRIGGER ENABLE
+# - ALTER EVENT TRIGGER DISABLE
+# - ALTER EVENT TRIGGER OWNER TO
+
+setup
+{
+ CREATE OR REPLACE FUNCTION notice_any_command()
+ RETURNS event_trigger
+ LANGUAGE plpgsql
+ AS $$
+ BEGIN
+ RAISE NOTICE 'command % is run', tg_tag;
+ END;
+ $$;
+ CREATE EVENT TRIGGER notice_ddl ON ddl_command_start
+ EXECUTE PROCEDURE notice_any_command();
+}
+
+teardown
+{
+ DROP EVENT TRIGGER notice_ddl;
+ DROP FUNCTION notice_any_command();
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_disable" { ALTER EVENT TRIGGER notice_ddl DISABLE; }
+step "s1_enable" { ALTER EVENT TRIGGER notice_ddl ENABLE REPLICA; }
+step "s1_owner" { ALTER EVENT TRIGGER notice_ddl OWNER TO CURRENT_USER; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_disable" { ALTER EVENT TRIGGER notice_ddl DISABLE; }
+step "s2_enable" { ALTER EVENT TRIGGER notice_ddl ENABLE REPLICA; }
+step "s2_owner" { ALTER EVENT TRIGGER notice_ddl OWNER TO CURRENT_USER; }
+step "s2_commit" { COMMIT; }
+
+permutation "s1_begin" "s1_disable" "s2_begin" "s2_disable"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_disable" "s2_begin" "s2_enable"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_disable" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_enable" "s2_begin" "s2_enable"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_enable" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_owner" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-fdw.spec b/src/test/isolation/specs/concurrent-fdw.spec
new file mode 100644
index 0000000000..1e8ce38dad
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-fdw.spec
@@ -0,0 +1,62 @@
+# Test for interactions with DDL commands manipulating fdw
+# objects.
+# The following set of commands can interact in concurrency:
+# - ALTER FDW OPTIONS
+# - ALTER FDW VALIDATOR
+# - ALTER FDW HANDLER
+# - ALTER FDW OWNER TO
+
+setup
+{
+ CREATE FOREIGN DATA WRAPPER regress_fdw;
+}
+
+teardown
+{
+ DROP FOREIGN DATA WRAPPER regress_fdw;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_options" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1'); }
+step "s1_validator" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR; }
+step "s1_handler" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER; }
+step "s1_owner" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_options" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw OPTIONS (a '1'); }
+step "s2_validator" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO VALIDATOR; }
+step "s2_handler" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw NO HANDLER; }
+step "s2_owner" {
+ ALTER FOREIGN DATA WRAPPER regress_fdw OWNER TO CURRENT_USER; }
+step "s2_commit" { COMMIT; }
+
+permutation "s1_begin" "s1_options" "s2_begin" "s2_options"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_options" "s2_begin" "s2_validator"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_options" "s2_begin" "s2_handler"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_options" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_validator" "s2_begin" "s2_validator"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_validator" "s2_begin" "s2_handler"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_validator" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_handler" "s2_begin" "s2_handler"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_handler" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_owner" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-role.spec b/src/test/isolation/specs/concurrent-role.spec
new file mode 100644
index 0000000000..24b96da41a
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-role.spec
@@ -0,0 +1,40 @@
+# Test for interactions with DDL commands manipulating role
+# objects.
+# The following set of commands can interact in concurrency:
+# - ALTER ROLE SET
+# - ALTER ROLE option
+
+setup
+{
+ CREATE ROLE regress_role_conc;
+}
+
+teardown
+{
+ DROP ROLE regress_role_conc;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_alterrole_set" {
+ ALTER ROLE regress_role_conc SET commit_delay = '10'; }
+step "s1_alterrole_opt" {
+ ALTER ROLE regress_role_conc PASSWORD 'foo'; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_alterrole_set" {
+ ALTER ROLE regress_role_conc SET commit_delay = '10'; }
+step "s2_alterrole_opt" {
+ ALTER ROLE regress_role_conc PASSWORD 'foo'; }
+step "s2_commit" { COMMIT; }
+
+permutation "s1_begin" "s1_alterrole_set" "s2_begin" "s2_alterrole_set"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterrole_set" "s2_begin" "s2_alterrole_opt"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterrole_opt" "s2_begin" "s2_alterrole_set"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alterrole_opt" "s2_begin" "s2_alterrole_opt"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-server.spec b/src/test/isolation/specs/concurrent-server.spec
new file mode 100644
index 0000000000..2a41d09397
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-server.spec
@@ -0,0 +1,39 @@
+# Test for interactions with DDL commands manipulating server
+# objects.
+# The following set of commands can interact in concurrency:
+# - ALTER SERVER OPTIONS
+# - ALTER SERVER OWNER TO
+
+setup
+{
+ CREATE FOREIGN DATA WRAPPER regress_fdw;
+ CREATE SERVER regress_server FOREIGN DATA WRAPPER regress_fdw;
+}
+
+teardown
+{
+ DROP FOREIGN DATA WRAPPER regress_fdw CASCADE;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_options" {
+ ALTER SERVER regress_server OPTIONS (a '1'); }
+step "s1_owner" {
+ ALTER SERVER regress_server OWNER TO CURRENT_USER; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_options" {
+ ALTER SERVER regress_server OPTIONS (a '1'); }
+step "s2_owner" {
+ ALTER SERVER regress_server OWNER TO CURRENT_USER; }
+step "s2_commit" { COMMIT; }
+
+permutation "s1_begin" "s1_options" "s2_begin" "s2_options"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_options" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_owner" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-type.spec b/src/test/isolation/specs/concurrent-type.spec
new file mode 100644
index 0000000000..3fe8f2338b
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-type.spec
@@ -0,0 +1,107 @@
+# Test for interactions with DDL commands manipulating type
+# objects.
+# The following set of commands can interact in concurrency:
+# - ALTER TYPE ADD ATTRIBUTE
+# - ALTER TYPE DROP ATTRIBUTE
+# - ALTER TYPE ALTER ATTRIBUTE
+# - ALTER TYPE RENAME ATTRIBUTE
+# - ALTER TYPE OWNER TO
+# - ALTER TYPE SET SCHEMA
+
+setup
+{
+ CREATE SCHEMA regress_type_schema;
+ CREATE TYPE regress_type AS (a int);
+}
+
+teardown
+{
+ DROP TYPE IF EXISTS regress_type;
+ DROP SCHEMA regress_type_schema CASCADE;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_add_attr" {
+ ALTER TYPE regress_type ADD ATTRIBUTE b1 int; }
+step "s1_drop_attr" {
+ ALTER TYPE regress_type DROP ATTRIBUTE a; }
+step "s1_alter_attr" {
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8; }
+step "s1_rename_attr" {
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b; }
+step "s1_owner" {
+ ALTER TYPE regress_type OWNER TO CURRENT_USER; }
+step "s1_schema" {
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_add_attr" {
+ ALTER TYPE regress_type ADD ATTRIBUTE b2 int; }
+step "s2_drop_attr" {
+ ALTER TYPE regress_type DROP ATTRIBUTE a; }
+step "s2_alter_attr" {
+ ALTER TYPE regress_type ALTER ATTRIBUTE a TYPE int8; }
+step "s2_rename_attr" {
+ ALTER TYPE regress_type RENAME ATTRIBUTE a TO b; }
+step "s2_owner" {
+ ALTER TYPE regress_type OWNER TO CURRENT_USER; }
+step "s2_schema" {
+ ALTER TYPE regress_type SET SCHEMA regress_type_schema; }
+step "s2_commit" { COMMIT; }
+
+# Round 1 of permutations based on ADD ATTRIBUTE
+permutation "s1_begin" "s1_add_attr" "s2_begin" "s2_add_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_add_attr" "s2_begin" "s2_drop_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_add_attr" "s2_begin" "s2_alter_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_add_attr" "s2_begin" "s2_rename_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_add_attr" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_add_attr" "s2_begin" "s2_schema"
+ "s1_commit" "s2_commit"
+
+# Round 2 of permutations based on DROP ATTRIBUTE
+permutation "s1_begin" "s1_drop_attr" "s2_begin" "s2_drop_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_drop_attr" "s2_begin" "s2_alter_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_drop_attr" "s2_begin" "s2_rename_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_drop_attr" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_drop_attr" "s2_begin" "s2_schema"
+ "s1_commit" "s2_commit"
+
+# Round 3 of permutations based on ALTER ATTRIBUTE
+permutation "s1_begin" "s1_alter_attr" "s2_begin" "s2_alter_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alter_attr" "s2_begin" "s2_rename_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alter_attr" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_alter_attr" "s2_begin" "s2_schema"
+ "s1_commit" "s2_commit"
+
+# Round 4 of permutations based on RENAME ATTRIBUTE
+permutation "s1_begin" "s1_rename_attr" "s2_begin" "s2_rename_attr"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_rename_attr" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_rename_attr" "s2_begin" "s2_schema"
+ "s1_commit" "s2_commit"
+
+# Round 5 of permutations based on OWNER TO
+permutation "s1_begin" "s1_owner" "s2_begin" "s2_owner"
+ "s1_commit" "s2_commit"
+permutation "s1_begin" "s1_owner" "s2_begin" "s2_schema"
+ "s1_commit" "s2_commit"
+
+# Round 6 of permutations based on SET SCHEMA
+permutation "s1_begin" "s1_schema" "s2_begin" "s2_schema"
+ "s1_commit" "s2_commit"
diff --git a/src/test/isolation/specs/concurrent-user-mapping.spec b/src/test/isolation/specs/concurrent-user-mapping.spec
new file mode 100644
index 0000000000..d8a8669c4f
--- /dev/null
+++ b/src/test/isolation/specs/concurrent-user-mapping.spec
@@ -0,0 +1,34 @@
+# Test for interactions with DDL commands manipulating user
+# mappings
+# The following set of commands can interact in concurrency:
+# - ALTER USER MAPPING OPTIONS
+
+setup
+{
+ CREATE FOREIGN DATA WRAPPER regress_fdw;
+ CREATE SERVER regress_server FOREIGN DATA WRAPPER regress_fdw;
+ CREATE USER MAPPING FOR CURRENT_USER SERVER regress_server
+ OPTIONS (user 'CURRENT_USER');
+}
+
+teardown
+{
+ DROP FOREIGN DATA WRAPPER regress_fdw CASCADE;
+}
+
+session "s1"
+step "s1_begin" { BEGIN; }
+step "s1_options" {
+ ALTER USER MAPPING FOR CURRENT_USER server regress_server
+ OPTIONS (SET USER 'popo1'); }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+step "s2_begin" { BEGIN; }
+step "s2_options" {
+ ALTER USER MAPPING FOR CURRENT_USER server regress_server
+ OPTIONS (SET USER 'popo1'); }
+step "s2_commit" { COMMIT; }
+
+permutation "s1_begin" "s1_options" "s2_begin" "s2_options"
+ "s1_commit" "s2_commit"