Skip to content

Commit e3f108e

Browse files
committed
db.supports_strict and table.strict properties, refs #344
1 parent 1267037 commit e3f108e

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

docs/python-api.rst

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,8 +1609,8 @@ A more useful example: if you are working with `SpatiaLite <https://www.gaia-gis
16091609
16101610
.. _python_api_introspection:
16111611
1612-
Introspection
1613-
=============
1612+
Introspecting tables and views
1613+
==============================
16141614
16151615
If you have loaded an existing table or view, you can use introspection to find out more about it::
16161616
@@ -1741,6 +1741,18 @@ The ``.schema`` property outputs the table's schema as a SQL string::
17411741
FOREIGN KEY ("qCareAssistant") REFERENCES [qCareAssistant](id),
17421742
FOREIGN KEY ("qLegalStatus") REFERENCES [qLegalStatus](id))
17431743
1744+
.. _python_api_introspection_strict:
1745+
1746+
.strict
1747+
-------
1748+
1749+
The ``.strict`` property identifies if the table is a `SQLite STRICT table <https://www.sqlite.org/stricttables.html>`__.
1750+
1751+
::
1752+
1753+
>>> db["ny_times_us_counties"].strict
1754+
False
1755+
17441756
.. _python_api_introspection_indexes:
17451757
17461758
.indexes

sqlite_utils/db.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import os
1919
import pathlib
2020
import re
21+
import secrets
2122
from sqlite_fts4 import rank_bm25 # type: ignore
2223
import sys
2324
import textwrap
@@ -521,6 +522,19 @@ def schema(self) -> str:
521522
sqls.append(sql)
522523
return "\n".join(sqls)
523524

525+
@property
526+
def supports_strict(self):
527+
try:
528+
table_name = "t{}".format(secrets.token_hex(16))
529+
with self.conn:
530+
self.conn.execute(
531+
"create table {} (name text) strict".format(table_name)
532+
)
533+
self.conn.execute("drop table {}".format(table_name))
534+
return True
535+
except Exception as e:
536+
return False
537+
524538
@property
525539
def journal_mode(self) -> str:
526540
"Current ``journal_mode`` of this database."
@@ -1219,6 +1233,13 @@ def triggers_dict(self) -> Dict[str, str]:
12191233
"``{trigger_name: sql}`` dictionary of triggers defined on this table."
12201234
return {trigger.name: trigger.sql for trigger in self.triggers}
12211235

1236+
@property
1237+
def strict(self) -> bool:
1238+
"Is this a STRICT table?"
1239+
table_suffix = self.schema.split(")")[-1].strip().upper()
1240+
table_options = [bit.strip() for bit in table_suffix.split(",")]
1241+
return "STRICT" in table_options
1242+
12221243
def create(
12231244
self,
12241245
columns: Dict[str, Any],

tests/test_introspect.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,33 @@ def test_has_counts_triggers(fresh_db):
252252
),
253253
],
254254
)
255-
def test_virtual_table_using(sql, expected_name, expected_using):
256-
db = Database(memory=True)
257-
db.execute(sql)
258-
assert db[expected_name].virtual_table_using == expected_using
259-
260-
261-
def test_use_rowid():
262-
db = Database(memory=True)
263-
db["rowid_table"].insert({"name": "Cleo"})
264-
db["regular_table"].insert({"id": 1, "name": "Cleo"}, pk="id")
265-
assert db["rowid_table"].use_rowid
266-
assert not db["regular_table"].use_rowid
255+
def test_virtual_table_using(fresh_db, sql, expected_name, expected_using):
256+
fresh_db.execute(sql)
257+
assert fresh_db[expected_name].virtual_table_using == expected_using
258+
259+
260+
def test_use_rowid(fresh_db):
261+
fresh_db["rowid_table"].insert({"name": "Cleo"})
262+
fresh_db["regular_table"].insert({"id": 1, "name": "Cleo"}, pk="id")
263+
assert fresh_db["rowid_table"].use_rowid
264+
assert not fresh_db["regular_table"].use_rowid
265+
266+
267+
@pytest.mark.skipif(
268+
not Database(memory=True).supports_strict,
269+
reason="Needs SQLite version that supports strict",
270+
)
271+
@pytest.mark.parametrize(
272+
"create_table,expected_strict",
273+
(
274+
("create table t (id integer) strict", True),
275+
("create table t (id integer) STRICT", True),
276+
("create table t (id integer primary key) StriCt, WITHOUT ROWID", True),
277+
("create table t (id integer primary key) WITHOUT ROWID", False),
278+
("create table t (id integer)", False),
279+
),
280+
)
281+
def test_table_strict(fresh_db, create_table, expected_strict):
282+
fresh_db.execute(create_table)
283+
table = fresh_db["t"]
284+
assert table.strict == expected_strict

0 commit comments

Comments
 (0)