Skip to content

Commit d25cdd3

Browse files
committed
db.sqlite_version property and fix for deterministic=True on SQLite 3.8.3
Closes #408
1 parent 521921b commit d25cdd3

File tree

4 files changed

+47
-3
lines changed

4 files changed

+47
-3
lines changed

docs/python-api.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,16 @@ A more useful example: if you are working with `SpatiaLite <https://www.gaia-gis
17171717
17181718
This example uses gographical data from [Who's On First](https://whosonfirst.org/) and depends on the [Shapely](https://shapely.readthedocs.io/en/stable/manual.html) and [HTTPX](https://www.python-httpx.org/) Python libraries.
17191719
1720+
.. _python_api_sqlite_version:
1721+
1722+
Checking the SQLite version
1723+
===========================
1724+
1725+
The ``db.sqlite_version`` property returns a tuple of integers representing the version of SQLite used for that database object::
1726+
1727+
>>> db.sqlite_version
1728+
(3, 36, 0)
1729+
17201730
.. _python_api_introspection:
17211731
17221732
Introspecting tables and views

sqlite_utils/db.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,11 @@ def register(fn):
387387
if not replace and (name, arity) in self._registered_functions:
388388
return fn
389389
kwargs = {}
390-
if deterministic and sys.version_info >= (3, 8):
390+
if (
391+
deterministic
392+
and sys.version_info >= (3, 8)
393+
and self.sqlite_version >= (3, 8, 3)
394+
):
391395
kwargs["deterministic"] = True
392396
self.conn.create_function(name, arity, fn, **kwargs)
393397
self._registered_functions.add((name, arity))
@@ -547,6 +551,12 @@ def supports_strict(self) -> bool:
547551
except Exception:
548552
return False
549553

554+
@property
555+
def sqlite_version(self) -> Tuple[int, ...]:
556+
"Version of SQLite, as a tuple of integers e.g. (3, 36, 0)"
557+
row = self.execute("select sqlite_version()").fetchall()[0]
558+
return tuple(map(int, row[0].split(".")))
559+
550560
@property
551561
def journal_mode(self) -> str:
552562
"Current ``journal_mode`` of this database."

tests/test_constructor.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ def test_memory_name():
1616
db2 = Database(memory_name="shared")
1717
db1["dogs"].insert({"name": "Cleo"})
1818
assert list(db2["dogs"].rows) == [{"name": "Cleo"}]
19+
20+
21+
def test_sqlite_version():
22+
db = Database(memory=True)
23+
version = db.sqlite_version
24+
assert isinstance(version, tuple)
25+
as_string = ".".join(map(str, version))
26+
actual = next(db.query("select sqlite_version() as v"))["v"]
27+
assert actual == as_string

tests/test_register_function.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,31 @@ def to_lower(s):
3434
@pytest.mark.skipif(
3535
sys.version_info < (3, 8), reason="deterministic=True was added in Python 3.8"
3636
)
37-
def test_register_function_deterministic_registered(fresh_db):
37+
@pytest.mark.parametrize(
38+
"fake_sqlite_version,should_use_deterministic",
39+
(
40+
("3.36.0", True),
41+
("3.8.3", True),
42+
("3.8.2", False),
43+
),
44+
)
45+
def test_register_function_deterministic_registered(
46+
fresh_db, fake_sqlite_version, should_use_deterministic
47+
):
3848
fresh_db.conn = MagicMock()
3949
fresh_db.conn.create_function = MagicMock()
50+
fresh_db.conn.execute().fetchall.return_value = [(fake_sqlite_version,)]
4051

4152
@fresh_db.register_function(deterministic=True)
4253
def to_lower_2(s):
4354
return s.lower()
4455

56+
expected_kwargs = {}
57+
if should_use_deterministic:
58+
expected_kwargs = dict(deterministic=True)
59+
4560
fresh_db.conn.create_function.assert_called_with(
46-
"to_lower_2", 1, to_lower_2, deterministic=True
61+
"to_lower_2", 1, to_lower_2, **expected_kwargs
4762
)
4863

4964

0 commit comments

Comments
 (0)