Database logging and error handling
This commit is contained in:
+38
-5
@@ -5,6 +5,8 @@ same code can run on CPython (`sqlite3`) and MicroPython (`sqlite3` or
|
||||
`usqlite`, depending on the port).
|
||||
"""
|
||||
|
||||
from shared.logging import log
|
||||
|
||||
try:
|
||||
import sqlite3 as _sqlite
|
||||
DRIVER_NAME = "sqlite3"
|
||||
@@ -13,20 +15,25 @@ except ImportError:
|
||||
import usqlite as _sqlite
|
||||
DRIVER_NAME = "usqlite"
|
||||
except ImportError as exc:
|
||||
log("[DB Error] No sqlite driver found. Expected sqlite3 or usqlite.")
|
||||
raise ImportError("No sqlite driver found. Expected sqlite3 or usqlite.") from exc
|
||||
|
||||
|
||||
def _connect(database_path, **connect_kwargs):
|
||||
try:
|
||||
if connect_kwargs:
|
||||
try:
|
||||
return _sqlite.connect(database_path, **connect_kwargs)
|
||||
except TypeError:
|
||||
pass
|
||||
return _sqlite.connect(database_path)
|
||||
except Exception as e:
|
||||
log(f"[DB Error] Driver connect failed for '{database_path}': {e}")
|
||||
raise
|
||||
|
||||
|
||||
class Database:
|
||||
"""Lightweight connection wrapper with a consistent API."""
|
||||
"""Lightweight connection wrapper with logging and consistent API."""
|
||||
|
||||
def __init__(self, database_path, **connect_kwargs):
|
||||
self._database_path = database_path
|
||||
@@ -40,29 +47,45 @@ class Database:
|
||||
|
||||
def close(self):
|
||||
if self._connection is not None:
|
||||
try:
|
||||
self._connection.close()
|
||||
except Exception as e:
|
||||
log(f"[DB Error] Failed to close database '{self._database_path}': {e}")
|
||||
finally:
|
||||
self._connection = None
|
||||
|
||||
def commit(self):
|
||||
connection = self.open()
|
||||
if hasattr(connection, "commit"):
|
||||
try:
|
||||
connection.commit()
|
||||
except Exception as e:
|
||||
log(f"[DB Error] Commit failed on '{self._database_path}': {e}")
|
||||
raise
|
||||
|
||||
def cursor(self):
|
||||
return self.open().cursor()
|
||||
|
||||
def execute(self, sql, params=None):
|
||||
cursor = self.cursor()
|
||||
try:
|
||||
if params is None:
|
||||
cursor.execute(sql)
|
||||
else:
|
||||
cursor.execute(sql, params)
|
||||
return cursor
|
||||
except Exception as e:
|
||||
log(f"[DB Error] Query failed on '{self._database_path}' | SQL: {sql} | Params: {params} | Error: {e}")
|
||||
raise
|
||||
|
||||
def executemany(self, sql, params_list):
|
||||
cursor = self.cursor()
|
||||
try:
|
||||
cursor.executemany(sql, params_list)
|
||||
return cursor
|
||||
except Exception as e:
|
||||
log(f"[DB Error] Executemany failed on '{self._database_path}' | SQL: {sql} | Error: {e}")
|
||||
raise
|
||||
|
||||
def fetchone(self, sql, params=None):
|
||||
return self.execute(sql, params).fetchone()
|
||||
@@ -73,16 +96,23 @@ class Database:
|
||||
def executescript(self, script):
|
||||
connection = self.open()
|
||||
if hasattr(connection, "executescript"):
|
||||
try:
|
||||
return connection.executescript(script)
|
||||
except Exception as e:
|
||||
log(f"[DB Error] Executescript failed on '{self._database_path}': {e}")
|
||||
raise
|
||||
log("[DB Error] executescript is not available on this sqlite backend")
|
||||
raise NotImplementedError("executescript is not available on this sqlite backend")
|
||||
|
||||
def __enter__(self):
|
||||
self.open()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, traceback):
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is None:
|
||||
self.commit()
|
||||
else:
|
||||
log(f"[DB Error] Context exited with exception on '{self._database_path}': {exc_val}")
|
||||
self.close()
|
||||
|
||||
|
||||
@@ -91,12 +121,15 @@ def connect(database_path, **connect_kwargs):
|
||||
|
||||
|
||||
def execute(database_path, sql, params=None, **connect_kwargs):
|
||||
return connect(database_path, **connect_kwargs).execute(sql, params)
|
||||
with connect(database_path, **connect_kwargs) as db_inst:
|
||||
return db_inst.execute(sql, params)
|
||||
|
||||
|
||||
def fetchone(database_path, sql, params=None, **connect_kwargs):
|
||||
return connect(database_path, **connect_kwargs).fetchone(sql, params)
|
||||
with connect(database_path, **connect_kwargs) as db_inst:
|
||||
return db_inst.fetchone(sql, params)
|
||||
|
||||
|
||||
def fetchall(database_path, sql, params=None, **connect_kwargs):
|
||||
return connect(database_path, **connect_kwargs).fetchall(sql, params)
|
||||
with connect(database_path, **connect_kwargs) as db_inst:
|
||||
return db_inst.fetchall(sql, params)
|
||||
Reference in New Issue
Block a user