Database — the one SQLite file behind the index, opened once per scratch handle.
The file carries a layout version. An older layout with a migration to the
next one is upgraded in place, step by step, and the cache survives; any other
version is rebuilt empty, and the scratch that opened it discards its objects.
MIGRATIONS = {10: ('DROP TABLE running', steps.CREATE)}
module-attribute
The statements taking a layout to the next version, keyed by the version they start from.
Database
The index connection; every write happens inside one transaction.
A transaction opened while the same thread already has one open joins it as
a savepoint, so an explicit call made mid-transaction never nests a BEGIN.
Source code in capturegraph-lib/capturegraph/recipes/scratch/index/database.py
| class Database:
"""The index connection; every write happens inside one ``transaction``.
A transaction opened while the same thread already has one open joins it as
a savepoint, so an explicit call made mid-transaction never nests a ``BEGIN``.
"""
def __init__(self, path: Path) -> None:
"""Open or create the index at ``path``, migrating or rebuilding an older layout.
``rebuilt`` says whether the index was emptied because its layout could
not be migrated; the objects it indexed are then orphans to discard.
"""
self.path = path
self._lock = threading.RLock()
self._connection = connect(path)
self.rebuilt = self._initialize()
def _initialize(self) -> bool:
with self.transaction() as db:
(version,) = db.execute("PRAGMA user_version").fetchone()
if version == SCHEMA_VERSION:
return False
tables = _tables(db)
if version == 0 and not tables:
_create(db)
return False
while version in MIGRATIONS and version < SCHEMA_VERSION:
for statement in MIGRATIONS[version]:
db.execute(statement)
version += 1
db.execute(f"PRAGMA user_version = {version}")
if version == SCHEMA_VERSION:
logger.info(f"{self.path}: index migrated to layout {SCHEMA_VERSION}")
return False
logger.warning(
f"{self.path}: layout {version} cannot be migrated to {SCHEMA_VERSION}; "
f"rebuilding the index empty"
)
for table in tables:
db.execute(f"DROP TABLE {table}")
_create(db)
return True
@contextmanager
def transaction(self) -> Iterator[sqlite3.Connection]:
"""One immediate transaction, serialized against this process's other writers."""
with self._lock:
if self._connection.in_transaction:
self._connection.execute("SAVEPOINT nested")
try:
yield self._connection
except BaseException:
self._connection.execute("ROLLBACK TO nested")
self._connection.execute("RELEASE nested")
raise
self._connection.execute("RELEASE nested")
return
self._connection.execute("BEGIN IMMEDIATE")
try:
yield self._connection
except BaseException:
self._connection.execute("ROLLBACK")
raise
self._connection.execute("COMMIT")
def close(self) -> None:
"""Close the connection; the index file stays."""
with self._lock:
self._connection.close()
|
__init__(path)
Open or create the index at path, migrating or rebuilding an older layout.
rebuilt says whether the index was emptied because its layout could
not be migrated; the objects it indexed are then orphans to discard.
Source code in capturegraph-lib/capturegraph/recipes/scratch/index/database.py
| def __init__(self, path: Path) -> None:
"""Open or create the index at ``path``, migrating or rebuilding an older layout.
``rebuilt`` says whether the index was emptied because its layout could
not be migrated; the objects it indexed are then orphans to discard.
"""
self.path = path
self._lock = threading.RLock()
self._connection = connect(path)
self.rebuilt = self._initialize()
|
close()
Close the connection; the index file stays.
Source code in capturegraph-lib/capturegraph/recipes/scratch/index/database.py
| def close(self) -> None:
"""Close the connection; the index file stays."""
with self._lock:
self._connection.close()
|
transaction()
One immediate transaction, serialized against this process's other writers.
Source code in capturegraph-lib/capturegraph/recipes/scratch/index/database.py
| @contextmanager
def transaction(self) -> Iterator[sqlite3.Connection]:
"""One immediate transaction, serialized against this process's other writers."""
with self._lock:
if self._connection.in_transaction:
self._connection.execute("SAVEPOINT nested")
try:
yield self._connection
except BaseException:
self._connection.execute("ROLLBACK TO nested")
self._connection.execute("RELEASE nested")
raise
self._connection.execute("RELEASE nested")
return
self._connection.execute("BEGIN IMMEDIATE")
try:
yield self._connection
except BaseException:
self._connection.execute("ROLLBACK")
raise
self._connection.execute("COMMIT")
|
connect(path)
A WAL-mode connection to the index file at path, usable from any thread.
Source code in capturegraph-lib/capturegraph/recipes/scratch/index/database.py
| def connect(path: Path) -> sqlite3.Connection:
"""A WAL-mode connection to the index file at ``path``, usable from any thread."""
connection = sqlite3.connect(
path,
timeout=BUSY_TIMEOUT_SECONDS,
isolation_level=None,
check_same_thread=False,
)
connection.execute("PRAGMA journal_mode=WAL")
connection.execute("PRAGMA synchronous=NORMAL")
return connection
|