db.sqlite_version property and fix for deterministic=True on SQLite 3.8.3

Closes #408
This commit is contained in:
Simon Willison 2022-03-01 16:24:27 -08:00
commit d25cdd37a3
4 changed files with 47 additions and 3 deletions

View file

@ -1717,6 +1717,16 @@ A more useful example: if you are working with `SpatiaLite <https://www.gaia-gis
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.
.. _python_api_sqlite_version:
Checking the SQLite version
===========================
The ``db.sqlite_version`` property returns a tuple of integers representing the version of SQLite used for that database object::
>>> db.sqlite_version
(3, 36, 0)
.. _python_api_introspection:
Introspecting tables and views

View file

@ -387,7 +387,11 @@ class Database:
if not replace and (name, arity) in self._registered_functions:
return fn
kwargs = {}
if deterministic and sys.version_info >= (3, 8):
if (
deterministic
and sys.version_info >= (3, 8)
and self.sqlite_version >= (3, 8, 3)
):
kwargs["deterministic"] = True
self.conn.create_function(name, arity, fn, **kwargs)
self._registered_functions.add((name, arity))
@ -547,6 +551,12 @@ class Database:
except Exception:
return False
@property
def sqlite_version(self) -> Tuple[int, ...]:
"Version of SQLite, as a tuple of integers e.g. (3, 36, 0)"
row = self.execute("select sqlite_version()").fetchall()[0]
return tuple(map(int, row[0].split(".")))
@property
def journal_mode(self) -> str:
"Current ``journal_mode`` of this database."

View file

@ -16,3 +16,12 @@ def test_memory_name():
db2 = Database(memory_name="shared")
db1["dogs"].insert({"name": "Cleo"})
assert list(db2["dogs"].rows) == [{"name": "Cleo"}]
def test_sqlite_version():
db = Database(memory=True)
version = db.sqlite_version
assert isinstance(version, tuple)
as_string = ".".join(map(str, version))
actual = next(db.query("select sqlite_version() as v"))["v"]
assert actual == as_string

View file

@ -34,16 +34,31 @@ def test_register_function_deterministic(fresh_db):
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="deterministic=True was added in Python 3.8"
)
def test_register_function_deterministic_registered(fresh_db):
@pytest.mark.parametrize(
"fake_sqlite_version,should_use_deterministic",
(
("3.36.0", True),
("3.8.3", True),
("3.8.2", False),
),
)
def test_register_function_deterministic_registered(
fresh_db, fake_sqlite_version, should_use_deterministic
):
fresh_db.conn = MagicMock()
fresh_db.conn.create_function = MagicMock()
fresh_db.conn.execute().fetchall.return_value = [(fake_sqlite_version,)]
@fresh_db.register_function(deterministic=True)
def to_lower_2(s):
return s.lower()
expected_kwargs = {}
if should_use_deterministic:
expected_kwargs = dict(deterministic=True)
fresh_db.conn.create_function.assert_called_with(
"to_lower_2", 1, to_lower_2, deterministic=True
"to_lower_2", 1, to_lower_2, **expected_kwargs
)