Skip to content

Commit 36ac1ff

Browse files
committed
my.core: experimental support for apsw, use it for my.bluemaestro
apsw seems to be about 30% faster than python's sqlite adapter
1 parent 8d34c26 commit 36ac1ff

3 files changed

Lines changed: 39 additions & 7 deletions

File tree

src/my/bluemaestro.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
from __future__ import annotations
1111

12+
REQUIRES = [
13+
'apsw', # faster sqlite adapter
14+
]
15+
1216
import calendar
1317

1418
# todo most of it belongs to DAL... but considering so few people use it I didn't bother for now
@@ -31,7 +35,7 @@
3135
)
3236
from my.core.cachew import mcachew
3337
from my.core.pandas import DataFrameT, as_dataframe
34-
from my.core.sqlite import SqliteTool, sqlite_connect_immutable
38+
from my.core.sqlite import SqliteTool, sqlite_connection
3539

3640

3741
class config(Protocol):
@@ -99,7 +103,7 @@ def measurements() -> Iterable[Res[Measurement]]:
99103
tot = 0
100104
new = 0
101105
# todo assert increasing timestamp?
102-
with sqlite_connect_immutable(path) as db:
106+
with sqlite_connection(path, immutable=True, _via_apsw=True) as db:
103107
tool = SqliteTool(db)
104108
old_format = 'data' in tool.get_table_names()
105109

@@ -136,7 +140,7 @@ def measurements() -> Iterable[Res[Measurement]]:
136140
# UPD: fucking hell, so you can set the reference date in the settings (calcReferenceUnix field in meta db)
137141
# but it's not set by default.
138142

139-
log_tables = [c[0] for c in db.execute('SELECT name FROM sqlite_sequence WHERE name LIKE "%_log"')]
143+
log_tables = [row[0] for row in db.execute('SELECT name FROM sqlite_sequence WHERE name LIKE "%_log"')]
140144
# 'omnibus' appears in bmLogger app, but seems that it contains the same data that last export contains?
141145
# not sure what's the point :shrug:
142146
log_tables = [t for t in log_tables if 'omnibus' not in t]

src/my/core/sqlite.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from tempfile import TemporaryDirectory
99
from typing import Any, Callable, Literal, Union, overload
1010

11+
from . import warnings
1112
from .common import PathIsh
1213
from .compat import assert_never
1314

@@ -44,13 +45,19 @@ def dict_factory(cursor, row):
4445

4546

4647
@contextmanager
47-
def sqlite_connection(db: PathIsh, *, immutable: bool = False, row_factory: Factory | None = None) -> Iterator[sqlite3.Connection]:
48-
dbp = f'file:{db}'
48+
def sqlite_connection(
49+
db: PathIsh,
50+
*,
51+
immutable: bool = False,
52+
row_factory: Factory | None = None,
53+
_via_apsw: bool = False, # NOTE: experimental for now
54+
) -> Iterator[sqlite3.Connection]:
55+
uri = f'file:{db}'
4956
# https://www.sqlite.org/draft/uri.html#uriimmutable
5057
if immutable:
5158
# assert results in nicer error than sqlite3.OperationalError
5259
assert Path(db).exists(), db
53-
dbp = f'{dbp}?immutable=1'
60+
uri = f'{uri}?immutable=1'
5461
row_factory_: Any = None
5562
if row_factory is not None:
5663
if callable(row_factory):
@@ -62,7 +69,25 @@ def sqlite_connection(db: PathIsh, *, immutable: bool = False, row_factory: Fact
6269
else:
6370
assert_never(row_factory) # ty: ignore[type-assertion-failure]
6471

65-
conn = sqlite3.connect(dbp, uri=True)
72+
73+
if _via_apsw:
74+
try:
75+
# for now, defensive, will see later how to do it properly
76+
import apsw # type: ignore[import-not-found,unused-ignore] # ty: ignore[unresolved-import]
77+
except ImportError:
78+
warnings.high('apsw is not installed, falling back to sqlite')
79+
else:
80+
assert row_factory is None, "row_factory is not supported with apsw (yet?)"
81+
82+
apsw_conn = apsw.Connection(uri, flags=apsw.SQLITE_OPEN_READONLY | apsw.SQLITE_OPEN_URI)
83+
try:
84+
with apsw_conn:
85+
yield apsw_conn # type: ignore[misc,unused-ignore]
86+
finally:
87+
apsw_conn.close()
88+
return
89+
90+
conn = sqlite3.connect(uri, uri=True)
6691
try:
6792
conn.row_factory = row_factory_
6893
with conn:

tox.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ deps =
6363
ijson # optional dependency for various modules
6464
commands =
6565
{envpython} -m my.core module install \
66+
my.bluemaestro \
6667
## tz/location
6768
my.location.google \
6869
my.time.tz.via_location \
@@ -125,6 +126,8 @@ commands =
125126
{posargs}
126127

127128

129+
# TODO ugh. my.bluemaestro uses apsw, which only supports >=3.11 python syntax?
130+
# so mypy chokes over it... think how to handle it later
128131
[testenv:typecheck-all_base]
129132
hpi_modules =
130133
my.arbtt \

0 commit comments

Comments
 (0)