kopia lustrzana https://github.com/simonw/datasette
New run_sanity_checks mechanism, for SpatiLite
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 #466pull/479/head
rodzic
c692cd2911
commit
da0b3ce2b7
|
@ -9,9 +9,8 @@ scratchpad
|
||||||
Pipfile
|
Pipfile
|
||||||
Pipfile.lock
|
Pipfile.lock
|
||||||
|
|
||||||
# SQLite databases
|
fixtures.db
|
||||||
*.db
|
*test.db
|
||||||
*.sqlite
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
|
@ -344,6 +344,28 @@ class Datasette:
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Plugin already registered
|
# Plugin already registered
|
||||||
pass
|
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):
|
def config(self, key):
|
||||||
return self._config.get(key, None)
|
return self._config.get(key, None)
|
||||||
|
@ -530,10 +552,7 @@ class Datasette:
|
||||||
name = path.stem
|
name = path.stem
|
||||||
if name in self._inspect:
|
if name in self._inspect:
|
||||||
raise Exception("Multiple files with same stem %s" % name)
|
raise Exception("Multiple files with same stem %s" % name)
|
||||||
try:
|
with sqlite3.connect("file:{}?mode=ro".format(path), uri=True) as conn:
|
||||||
with sqlite3.connect(
|
|
||||||
"file:{}?mode=ro".format(path), uri=True
|
|
||||||
) as conn:
|
|
||||||
self.prepare_connection(conn)
|
self.prepare_connection(conn)
|
||||||
self._inspect[name] = {
|
self._inspect[name] = {
|
||||||
"hash": inspect_hash(path),
|
"hash": inspect_hash(path),
|
||||||
|
@ -544,15 +563,6 @@ class Datasette:
|
||||||
conn, (self.metadata("databases") or {}).get(name, {})
|
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
|
|
||||||
return self._inspect
|
return self._inspect
|
||||||
|
|
||||||
def register_custom_units(self):
|
def register_custom_units(self):
|
||||||
|
|
|
@ -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")
|
Plik binarny nie jest wyświetlany.
|
@ -1,6 +1,7 @@
|
||||||
from .fixtures import app_client
|
from .fixtures import app_client
|
||||||
from datasette.cli import cli
|
from datasette.cli import cli
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
import pathlib
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,3 +29,12 @@ def test_inspect_cli_writes_to_file(app_client):
|
||||||
assert 0 == result.exit_code, result.output
|
assert 0 == result.exit_code, result.output
|
||||||
data = json.load(open("foo.json"))
|
data = json.load(open("foo.json"))
|
||||||
assert ["fixtures"] == list(data.keys())
|
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
|
Ładowanie…
Reference in New Issue