Skip to content

Commit 0911d4b

Browse files
gordthompsonrafiss
authored andcommitted
Apply updates for CRDB v24.3
1 parent 32e8f6c commit 0911d4b

File tree

7 files changed

+130
-91
lines changed

7 files changed

+130
-91
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ jobs:
4040
fail-fast: false
4141
matrix:
4242
crdb-version: [
43-
"cockroach:latest-v23.2",
4443
"cockroach:latest-v24.1",
45-
"cockroach:latest-v24.2"
44+
"cockroach:latest-v24.2",
45+
"cockroach:latest-v24.3"
4646
]
4747
db-alias: [
4848
"psycopg2",
@@ -84,4 +84,3 @@ jobs:
8484
run: pip install --user tox==${TOX_VERSION}
8585
- name: Lint
8686
run: ${HOME}/.local/bin/tox -e lint
87-

README.asyncpg.md

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,81 @@ There is a customized version of the FastAPI SQL database tutorial for
99

1010
https://github.com/gordthompson/fastapi-tutorial-cockroachdb-async
1111

12-
### Database support for Alembic
12+
### Default transaction isolation level
1313

14-
CockroachDB version 23.1 or later is required to work with Alembic.
14+
Applications using asyncpg that were developed prior to CockroachDB's inclusion of
15+
READ COMMITTED transaction isolation may operate on the assumption that the default
16+
isolation level will be SERIALIZABLE. For example,
17+
18+
```python
19+
import asyncio
20+
21+
from sqlalchemy.ext.asyncio import create_async_engine
22+
23+
24+
async def async_main():
25+
engine = create_async_engine(
26+
"cockroachdb+asyncpg://root@localhost:26257/defaultdb",
27+
)
28+
async with engine.begin() as conn:
29+
result = await conn.exec_driver_sql("select version()")
30+
print(result.scalar().split("(")[0]) # CockroachDB CCL v23.2.4
31+
32+
result = await conn.exec_driver_sql("show transaction isolation level")
33+
print(result.scalar()) # serializable
34+
35+
36+
asyncio.run(async_main())
37+
```
38+
39+
With current versions of CockroachDB, the default transaction isolation level
40+
**for asyncpg only** is now READ COMMITTED
41+
42+
```python
43+
import asyncio
44+
45+
from sqlalchemy.ext.asyncio import create_async_engine
46+
47+
48+
async def async_main():
49+
engine = create_async_engine(
50+
"cockroachdb+asyncpg://root@localhost:26257/defaultdb",
51+
)
52+
async with engine.begin() as conn:
53+
result = await conn.exec_driver_sql("select version()")
54+
print(result.scalar().split("(")[0]) # CockroachDB CCL v24.3.0
55+
56+
result = await conn.exec_driver_sql("show transaction isolation level")
57+
print(result.scalar()) # read committed
58+
59+
60+
asyncio.run(async_main())
61+
```
62+
63+
Applications that rely on the original behavior will have to add `isolation_level="SERIALIZABLE"`
64+
to their `create_async_engine()` call
65+
66+
```python
67+
import asyncio
68+
69+
from sqlalchemy.ext.asyncio import create_async_engine
70+
71+
72+
async def async_main():
73+
engine = create_async_engine(
74+
"cockroachdb+asyncpg://root@localhost:26257/defaultdb",
75+
isolation_level="SERIALIZABLE",
76+
)
77+
async with engine.begin() as conn:
78+
result = await conn.exec_driver_sql("select version()")
79+
print(result.scalar().split("(")[0]) # CockroachDB CCL v24.3.0
80+
81+
result = await conn.exec_driver_sql("show transaction isolation level")
82+
print(result.scalar()) # serializable
83+
84+
85+
asyncio.run(async_main())
86+
```
1587

1688
### Testing
1789

README.read_committed.md

Lines changed: 0 additions & 37 deletions
This file was deleted.

dev-requirements.txt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
backports-tarfile==1.2.0
22
# via jaraco-context
3-
certifi==2024.8.30
3+
certifi==2024.12.14
44
# via requests
55
cffi==1.17.1
66
# via cryptography
7-
charset-normalizer==3.4.0
7+
charset-normalizer==3.4.1
88
# via requests
9-
cryptography==43.0.1
9+
cryptography==44.0.0
1010
# via secretstorage
1111
distlib==0.3.9
1212
# via virtualenv
@@ -32,7 +32,7 @@ jeepney==0.8.0
3232
# via
3333
# keyring
3434
# secretstorage
35-
keyring==25.4.1
35+
keyring==25.6.0
3636
# via twine
3737
markdown-it-py==3.0.0
3838
# via rich
@@ -42,11 +42,13 @@ more-itertools==10.5.0
4242
# via
4343
# jaraco-classes
4444
# jaraco-functools
45-
nh3==0.2.18
45+
nh3==0.2.20
4646
# via readme-renderer
47-
packaging==24.1
48-
# via tox
49-
pkginfo==1.10.0
47+
packaging==24.2
48+
# via
49+
# tox
50+
# twine
51+
pkginfo==1.12.0
5052
# via twine
5153
platformdirs==4.3.6
5254
# via virtualenv
@@ -56,7 +58,7 @@ py==1.11.0
5658
# via tox
5759
pycparser==2.22
5860
# via cffi
59-
pygments==2.18.0
61+
pygments==2.19.1
6062
# via
6163
# readme-renderer
6264
# rich
@@ -70,25 +72,25 @@ requests-toolbelt==1.0.0
7072
# via twine
7173
rfc3986==2.0.0
7274
# via twine
73-
rich==13.9.2
75+
rich==13.9.4
7476
# via twine
7577
secretstorage==3.3.3
7678
# via keyring
77-
six==1.16.0
79+
six==1.17.0
7880
# via tox
7981
toml==0.10.2
8082
# via tox
8183
tox==3.23.1
8284
# via -r dev-requirements.in
83-
twine==5.1.1
85+
twine==6.0.1
8486
# via -r dev-requirements.in
8587
typing-extensions==4.12.2
8688
# via rich
87-
urllib3==2.2.3
89+
urllib3==2.3.0
8890
# via
8991
# requests
9092
# twine
91-
virtualenv==20.26.6
93+
virtualenv==20.28.1
9294
# via tox
93-
zipp==3.20.2
95+
zipp==3.21.0
9496
# via importlib-metadata
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
from sqlalchemy.testing.provision import temp_table_keyword_args
2+
from sqlalchemy.testing.provision import update_db_opts
23

34

45
@temp_table_keyword_args.for_db("cockroachdb")
56
def _cockroachdb_temp_table_keyword_args(cfg, eng):
67
return {"prefixes": ["TEMPORARY"]}
8+
9+
10+
@update_db_opts.for_db("cockroachdb")
11+
def _update_db_opts(db_url, db_opts, options):
12+
"""Set database options (db_opts) for a test database that we created."""
13+
db_opts["isolation_level"] = "SERIALIZABLE"

test-requirements.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
1-
alembic==1.13.3
1+
alembic==1.14.0
22
# via -r test-requirements.in
3-
async-timeout==4.0.3
3+
async-timeout==5.0.1
44
# via asyncpg
5-
asyncpg==0.29.0
5+
asyncpg==0.30.0
66
# via -r test-requirements.in
7-
attrs==24.2.0
7+
attrs==24.3.0
88
# via pytest
99
futures==3.0.5
1010
# via -r test-requirements.in
1111
greenlet==3.1.1
1212
# via sqlalchemy
1313
iniconfig==2.0.0
1414
# via pytest
15-
mako==1.3.5
15+
mako==1.3.8
1616
# via alembic
17-
markupsafe==3.0.1
17+
markupsafe==3.0.2
1818
# via mako
1919
mock==5.1.0
2020
# via -r test-requirements.in
2121
more-itertools==10.5.0
2222
# via -r test-requirements.in
23-
packaging==24.1
23+
packaging==24.2
2424
# via pytest
2525
pluggy==1.5.0
2626
# via pytest
2727
psycopg==3.2.3
2828
# via -r test-requirements.in
29-
psycopg2==2.9.9
29+
psycopg2==2.9.10
3030
# via -r test-requirements.in
3131
py==1.11.0
3232
# via pytest
3333
pytest==7.1.3
3434
# via -r test-requirements.in
35-
sqlalchemy==2.0.36
35+
sqlalchemy==2.0.37
3636
# via
3737
# -r test-requirements.in
3838
# alembic
39-
tomli==2.0.2
39+
tomli==2.2.1
4040
# via pytest
4141
typing-extensions==4.12.2
4242
# via

test/test_suite_sqlalchemy.py

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
)
1010
from sqlalchemy.testing.suite import HasIndexTest as _HasIndexTest
1111
from sqlalchemy.testing.suite import HasTableTest as _HasTableTest
12+
from sqlalchemy.testing.suite import IntegerTest as _IntegerTest
1213
from sqlalchemy.testing.suite import InsertBehaviorTest as _InsertBehaviorTest
1314
from sqlalchemy.testing.suite import IsolationLevelTest as _IsolationLevelTest
1415
from sqlalchemy.testing.suite import (
@@ -417,24 +418,19 @@ def test_no_results_for_non_returning_insert(self):
417418
pass
418419

419420

420-
class IsolationLevelTest(_IsolationLevelTest):
421-
def test_all_levels(self):
422-
if not config.db.dialect._is_v232plus:
423-
# TODO: enable when READ COMMITTED no longer a preview feature, since
424-
# SET CLUSTER SETTING cannot be used inside a multi-statement transaction
425-
super().test_all_levels()
421+
class IntegerTest(_IntegerTest):
422+
@_IntegerTest._huge_ints()
423+
def test_huge_int(self, integer_round_trip, intvalue):
424+
if config.db.dialect.driver != "asyncpg":
425+
super().test_huge_int(integer_round_trip, intvalue)
426+
426427

428+
class IsolationLevelTest(_IsolationLevelTest):
427429
@skip("cockroachdb")
428430
def test_dialect_user_setting_is_restored(self):
429431
# IndexError: list index out of range
430432
pass
431433

432-
def test_non_default_isolation_level(self):
433-
if not config.db.dialect._is_v232plus:
434-
# TODO: enable when READ COMMITTED no longer a preview feature, since
435-
# SET CLUSTER SETTING cannot be used inside a multi-statement transaction
436-
super().test_non_default_isolation_level()
437-
438434

439435
class LongNameBlowoutTest(_LongNameBlowoutTest):
440436
@testing.combinations(
@@ -450,6 +446,18 @@ def test_long_convention_name(self, type_, metadata, connection):
450446
super().test_long_convention_name(type_, metadata, connection, None)
451447

452448

449+
class NumericTest(_NumericTest):
450+
def test_numeric_as_float(self, do_numeric_test):
451+
# psycopg.errors.InvalidParameterValue: unsupported binary operator: <decimal> + <float>
452+
if config.db.dialect.driver != "psycopg":
453+
super().test_numeric_as_float(do_numeric_test)
454+
455+
def test_numeric_null_as_float(self, do_numeric_test):
456+
# psycopg.errors.InvalidParameterValue: unsupported binary operator: <decimal> + <float>
457+
if config.db.dialect.driver != "psycopg":
458+
super().test_numeric_null_as_float(do_numeric_test)
459+
460+
453461
class QuotedNameArgumentTest(_QuotedNameArgumentTest):
454462
def quote_fixtures(fn):
455463
return testing.combinations(
@@ -464,18 +472,6 @@ def test_get_indexes(self, name):
464472
super().test_get_indexes(name, None)
465473

466474

467-
class NumericTest(_NumericTest):
468-
def test_numeric_as_float(self, do_numeric_test):
469-
# psycopg.errors.InvalidParameterValue: unsupported binary operator: <decimal> + <float>
470-
if config.db.dialect.driver != "psycopg":
471-
super().test_numeric_as_float(do_numeric_test)
472-
473-
def test_numeric_null_as_float(self, do_numeric_test):
474-
# psycopg.errors.InvalidParameterValue: unsupported binary operator: <decimal> + <float>
475-
if config.db.dialect.driver != "psycopg":
476-
super().test_numeric_null_as_float(do_numeric_test)
477-
478-
479475
class TrueDivTest(_TrueDivTest):
480476
@skip("cockroachdb")
481477
def test_floordiv_integer(self):

0 commit comments

Comments
 (0)