Skip to content

Commit

Permalink
Allow dropping isolation level to REPEATABLE READ in a READ ONLY tx (#…
Browse files Browse the repository at this point in the history
…8237)

This re-enabled REPEATABLE READ isolation in explicit transaction
blocks, but only in those that are READ ONLY.  Anomalies observed in
read statements are normal non-serializability anomalies, so with an
explicit opt-in it is a reasonable tradeoff between lessening the
transaction serialization load and consistency.
  • Loading branch information
elprans authored Jan 21, 2025
1 parent 2122497 commit e6c12f7
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 0 deletions.
1 change: 1 addition & 0 deletions edb/edgeql-parser/src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub const UNRESERVED_KEYWORDS: phf::Set<&str> = phf_set!(
"reject",
"release",
"rename",
"repeatable",
"required",
"reset",
"restrict",
Expand Down
4 changes: 4 additions & 0 deletions edb/edgeql/parser/grammar/statements.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def reduce_ISOLATION_SERIALIZABLE(self, *kids):
self.val = (qltypes.TransactionIsolationLevel.SERIALIZABLE,
kids[0].span)

def reduce_ISOLATION_REPEATABLE_READ(self, *kids):
self.val = (qltypes.TransactionIsolationLevel.REPEATABLE_READ,
kids[0].span)

def reduce_READ_WRITE(self, *kids):
self.val = (qltypes.TransactionAccessMode.READ_WRITE,
kids[0].span)
Expand Down
13 changes: 13 additions & 0 deletions edb/server/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2153,6 +2153,19 @@ def _compile_ql_transaction(
ctx.state.start_tx()

sqls = 'START TRANSACTION'
iso = ql.isolation
if iso is not None:
if (
iso is not qltypes.TransactionIsolationLevel.SERIALIZABLE
and ql.access is not qltypes.TransactionAccessMode.READ_ONLY
):
raise errors.TransactionError(
f"{iso.value} transaction isolation level is only "
"supported in read-only transactions",
span=ql.span,
hint=f"specify READ ONLY access mode",
)
sqls += f' ISOLATION LEVEL {iso.value}'
if ql.access is not None:
sqls += f' {ql.access.value}'
if ql.deferrable is not None:
Expand Down
30 changes: 30 additions & 0 deletions tests/test_server_proto.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,36 @@ async def test_server_proto_tx_07(self):
await self.con.query('SELECT 42'),
[42])

async def test_server_proto_tx_08(self):
try:
await self.con.query('''
START TRANSACTION ISOLATION REPEATABLE READ, READ ONLY;
''')

self.assertEqual(
await self.con.query(
'select <str>sys::get_transaction_isolation();',
),
["RepeatableRead"],
)
finally:
await self.con.query(f'''
ROLLBACK;
''')

async def test_server_proto_tx_09(self):
try:
with self.assertRaisesRegex(
edgedb.TransactionError,
'only supported in read-only transactions'):
await self.con.query('''
START TRANSACTION ISOLATION REPEATABLE READ;
''')
finally:
await self.con.query(f'''
ROLLBACK;
''')

async def test_server_proto_tx_10(self):
# Basic test that ROLLBACK works on SET ALIAS changes.

Expand Down

0 comments on commit e6c12f7

Please sign in to comment.