Repository: edgedb/edgedb

Issue:
Conflicting data can be inserted into related types in concurrent transactions
If we have
```
CREATE TYPE Foo {
    CREATE PROPERTY name -> str {
        CREATE CONSTRAINT exclusive;
    };
};
CREATE TYPE Bar EXTENDING Foo;
```
and we do, in concurrent transactions `INSERT Foo { name := "foo" }` and `INSERT Bar { name := "foo" }`, both will succeed.

This is real bad.

It appears that the triggers we use for enforcing these cross-table exclusive constraints aren't getting it done here. I haven't dug into the literature yet to see what promises exactly postgres makes and whether it is possible to coax it into behaving. If not, it might be shadow table time.


Discussion:
Looks like we'll have to implement shadow tables. We will discuss on our call today.

Resolving patch:
diff --git a/edb/server/compiler/compiler.py b/edb/server/compiler/compiler.py
--- a/edb/server/compiler/compiler.py
+++ b/edb/server/compiler/compiler.py
@@ -1301,8 +1301,8 @@ def _compile_ql_transaction(
             ctx.state.start_tx()
 
             sql = 'START TRANSACTION'
-            if ql.isolation is not None:
-                sql += f' ISOLATION LEVEL {ql.isolation.value}'
+            # N.B: Currently we ignore the isolation level. We should
+            # remove it if we keep it this way.
             if ql.access is not None:
                 sql += f' {ql.access.value}'
             if ql.deferrable is not None:


Test patch:
diff --git a/tests/test_server_concurrency.py b/tests/test_server_concurrency.py
--- a/tests/test_server_concurrency.py
+++ b/tests/test_server_concurrency.py
@@ -21,7 +21,6 @@
 import edgedb
 
 from edb.testbase import server as tb
-from edb.tools import test
 
 
 class Barrier:
@@ -92,17 +91,9 @@ async def execute_conflict_1(self, name, options=None):
         self.assertEqual(set(results), {1, 2})
         self.assertEqual(iterations, 3)
 
-    @test.xfail("""
-        We generate a ConstraintViolationError instead.
-        Issue #2876.
-    """)
     async def test_server_concurrent_conflict_retry_2(self):
         await self.execute_conflict_2('counter4')
 
-    @test.xfail("""
-        We generate a ConstraintViolationError instead.
-        Issue #2876.
-    """)
     async def test_server_concurrent_conflict_no_retry_2(self):
         with self.assertRaises(edgedb.TransactionSerializationError):
             await self.execute_conflict_2(
@@ -143,10 +134,6 @@ async def test_server_concurrent_inserts_1(self):
             [{'name': 'foo'}],
         )
 
-    @test.xfail("""
-       We *succeed* on both inserts. This is real bad.
-       Issue #2875.
-    """)
     async def test_server_concurrent_inserts_2(self):
         f1 = lambda tx: tx.execute('INSERT Foo { name := "foo" }')
         f2 = lambda tx: tx.execute('INSERT Bar { name := "foo" }')
diff --git a/tests/test_server_proto.py b/tests/test_server_proto.py
--- a/tests/test_server_proto.py
+++ b/tests/test_server_proto.py
@@ -29,6 +29,7 @@
 from edb.common import taskgroup as tg
 from edb.testbase import server as tb
 from edb.server.compiler import enums
+from edb.tools import test
 
 
 SERVER_HEADER_CAPABILITIES = 0x1001
@@ -1710,6 +1711,7 @@ async def test_server_proto_tx_14(self):
             await self.con.query_single('SELECT 1;'),
             1)
 
+    @test.xfail("... we currently always use serializable")
     async def test_server_proto_tx_16(self):
         try:
             for isol, expected in [