aeat.adapters.persistence.storage.sql.engine module

SQLAlchemy engine factory for the storage subpackage.

Provides a lazy singleton engine cache used by the rest of adapters.persistence.storage. Bucket-routed engines are cached under their bucket identity (resolved storage root plus bucket id) so that engine lifetime can follow the bucket-session lifecycle; the database URL is an implementation detail of engine construction. Explicit database URLs and the root-fallback database keep their URL-keyed direct path. The factory normalises SQLite URLs against core.paths.PROJECT_ROOT, ensures the parent directory exists, and attaches a connect listener that configures each SQLite connection: foreign_keys=ON (cascade enforcement) and a busy_timeout (so concurrent invocations on one bucket no longer fail immediately with “database is locked”).

Disposal is an internal seam of the engine lifecycle owner: the bucket session (BucketSession.close) disposes via dispose_engine_handle() and dispose_engines_for_bucket(), the bucket-destruction path uses dispose_engines_for_bucket() to release file handles before removing a bucket directory, and test-harness teardown uses dispose_engine(). Production code outside those owners must not dispose engines directly.

create_engine_from_settings(settings)[source]

Create a fresh SQLAlchemy Engine from settings.

When the engine targets SQLite, a connect listener enables PRAGMA foreign_keys=ON on every new connection so that ON DELETE CASCADE / SET NULL constraints declared in the schema are enforced at runtime.

Parameters:

settings (Settings) – Application Settings carrying aeat_database_url.

Return type:

Engine

Returns:

A new SQLAlchemy Engine.

Raises:

StorageError – When the configured URL is empty or cannot be parsed.

get_engine(settings=None)[source]

Return a process-wide singleton engine, keyed by bucket identity.

Bucket-routed settings cache the engine under (storage root, bucket id); explicit-URL and root-fallback settings cache under the database URL. On first access, materialises every ORM table declared on metadata against the new engine. The codebase is forward-only: there is no migration history; the schema is whatever the current ORM defines.

Parameters:

settings (Settings | None) – Optional Settings override. When None, a fresh core.config.load_settings() call is used.

Return type:

Engine

Returns:

The cached Engine for the resolved route, creating it on first access.

dispose_engine(settings=None)[source]

Dispose and forget the cached engine for the given settings.

Internal lifecycle seam: invoked by the session owner (BucketSession.close via the handle/bucket-scoped variants below) and by test-harness teardown. Production code must not call it — engine disposal is owned by the bucket-session lifecycle.

Parameters:

settings (Settings | None) – Optional Settings override. When None, every cached engine is disposed.

Return type:

None

dispose_engines_for_bucket(bucket_id)[source]

Dispose and forget every cached engine bound to bucket_id.

The bucket-scoped disposal seam: the session owner (BucketSession.close) sweeps its bucket’s engines on close/switch, and the bucket-destruction path releases the bucket’s SQLite file handles before removing the bucket directory (an open handle blocks the rename on Windows). Engines cached for other buckets and for explicit database URLs are untouched.

Return type:

None

Parameters:

bucket_id (str)

dispose_engine_handle(engine)[source]

Dispose engine and evict it from the cache by identity.

Session-owner seam for engine handles registered on a BucketSession at first storage access: disposal targets exactly the registered engine, regardless of which route key cached it.

Return type:

None

Parameters:

engine (Engine)