Skip to content

Commit

Permalink
New run_sanity_checks mechanism, for SpatiLite
Browse files Browse the repository at this point in the history
Moved VirtualSpatialIndex check into a new mechanism that should allow
us to add further sanity checks in the future.

To test this I've had to commit a binary sample SpatiaLite database to
the repository. I included a build script for creating that database.

Closes #466
  • Loading branch information
simonw committed May 11, 2019
1 parent c692cd2 commit da0b3ce
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 26 deletions.
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ scratchpad
Pipfile
Pipfile.lock

# SQLite databases
*.db
*.sqlite
fixtures.db
*test.db

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
56 changes: 33 additions & 23 deletions datasette/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,28 @@ def __init__(
except ValueError:
# Plugin already registered
pass
# Run the sanity checks
asyncio.get_event_loop().run_until_complete(self.run_sanity_checks())

async def run_sanity_checks(self):
# Only one check right now, for Spatialite
for database_name, database in self.databases.items():
# Run pragma_info on every table
for table in await database.table_names():
try:
await self.execute(
database_name,
"PRAGMA table_info({});".format(escape_sqlite(table)),
)
except sqlite3.OperationalError as e:
if e.args[0] == "no such module: VirtualSpatialIndex":
raise click.UsageError(
"It looks like you're trying to load a SpatiaLite"
" database without first loading the SpatiaLite module."
"\n\nRead more: https://datasette.readthedocs.io/en/latest/spatialite.html"
)
else:
raise

def config(self, key):
return self._config.get(key, None)
Expand Down Expand Up @@ -530,29 +552,17 @@ def inspect(self):
name = path.stem
if name in self._inspect:
raise Exception("Multiple files with same stem %s" % name)
try:
with sqlite3.connect(
"file:{}?mode=ro".format(path), uri=True
) as conn:
self.prepare_connection(conn)
self._inspect[name] = {
"hash": inspect_hash(path),
"file": str(path),
"size": path.stat().st_size,
"views": inspect_views(conn),
"tables": inspect_tables(
conn, (self.metadata("databases") or {}).get(name, {})
),
}
except sqlite3.OperationalError as e:
if e.args[0] == "no such module: VirtualSpatialIndex":
raise click.UsageError(
"It looks like you're trying to load a SpatiaLite"
" database without first loading the SpatiaLite module."
"\n\nRead more: https://datasette.readthedocs.io/en/latest/spatialite.html"
)
else:
raise
with sqlite3.connect("file:{}?mode=ro".format(path), uri=True) as conn:
self.prepare_connection(conn)
self._inspect[name] = {
"hash": inspect_hash(path),
"file": str(path),
"size": path.stat().st_size,
"views": inspect_views(conn),
"tables": inspect_tables(
conn, (self.metadata("databases") or {}).get(name, {})
),
}
return self._inspect

def register_custom_units(self):
Expand Down
23 changes: 23 additions & 0 deletions tests/build_small_spatialite_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import sqlite3

# This script generates the spatialite.db file in our tests directory.


def generate_it(filename):
conn = sqlite3.connect(filename)
# Lead the spatialite extension:
conn.enable_load_extension(True)
conn.load_extension("/usr/local/lib/mod_spatialite.dylib")
conn.execute("select InitSpatialMetadata(1)")
conn.executescript("create table museums (name text)")
conn.execute("SELECT AddGeometryColumn('museums', 'point_geom', 4326, 'POINT', 2);")
# At this point it is around 5MB - we can shrink it dramatically by doing thisO
conn.execute("delete from spatial_ref_sys")
conn.execute("delete from spatial_ref_sys_aux")
conn.commit()
conn.execute("vacuum")
conn.close()


if __name__ == "__main__":
generate_it("spatialite.db")
Binary file added tests/spatialite.db
Binary file not shown.
10 changes: 10 additions & 0 deletions tests/test_inspect.py → tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .fixtures import app_client
from datasette.cli import cli
from click.testing import CliRunner
import pathlib
import json


Expand Down Expand Up @@ -28,3 +29,12 @@ def test_inspect_cli_writes_to_file(app_client):
assert 0 == result.exit_code, result.output
data = json.load(open("foo.json"))
assert ["fixtures"] == list(data.keys())


def test_spatialite_error_if_attempt_to_open_spatialite():
runner = CliRunner()
result = runner.invoke(
cli, ["serve", str(pathlib.Path(__file__).parent / "spatialite.db")]
)
assert result.exit_code != 0
assert "trying to load a SpatiaLite database" in result.output

0 comments on commit da0b3ce

Please sign in to comment.