Metadata-Version: 2.4
Name: lexigram-sql
Version: 0.1.3005
Summary: SQL database abstractions for Lexigram Framework — Postgres, MySQL, SQLite with migrations, repositories, and query building
Project-URL: Homepage, https://lexigram.dev
Project-URL: Repository, https://github.com/dbtinoy-/lexigram
Project-URL: Documentation, https://docs.lexigram.dev
Project-URL: Issues, https://github.com/dbtinoy-/lexigram/issues
Project-URL: Changelog, https://github.com/dbtinoy-/lexigram/blob/main/CHANGELOG.md
Author-email: Lexigram Framework Team <team@lexigram.dev>
Maintainer-email: Lexigram Framework Team <team@lexigram.dev>
License: MIT
License-File: LICENSE
Keywords: alembic,async,database,framework,migrations,mysql,orm,postgres,sql,sqlalchemy,sqlite
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: aiosqlite>=0.22.1
Requires-Dist: alembic>=1.17.2
Requires-Dist: jinja2>=3.1.0
Requires-Dist: lexigram-contracts>=0.1.0
Requires-Dist: lexigram>=0.1.1
Requires-Dist: sqlalchemy>=2.0.0
Requires-Dist: typing-extensions>=4.0.0
Provides-Extra: all
Requires-Dist: aiomysql>=0.1.0; extra == 'all'
Requires-Dist: aiosqlite>=0.19.0; extra == 'all'
Requires-Dist: asyncpg>=0.29.0; extra == 'all'
Requires-Dist: lexigram-testing>=0.1.1; extra == 'all'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'all'
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
Requires-Dist: pytest-mock>=3.10.0; extra == 'all'
Requires-Dist: pytest>=8.0.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: black>=23.0.0; extra == 'dev'
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: mysql
Requires-Dist: aiomysql>=0.1.0; extra == 'mysql'
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.29.0; extra == 'postgres'
Provides-Extra: sqlite
Requires-Dist: aiosqlite>=0.22.1; extra == 'sqlite'
Provides-Extra: test
Requires-Dist: lexigram-testing>=0.1.1; extra == 'test'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
Requires-Dist: pytest-mock>=3.10.0; extra == 'test'
Requires-Dist: pytest>=8.0.0; extra == 'test'
Description-Content-Type: text/markdown

# lexigram-sql

SQL database abstractions for Lexigram Framework — Postgres, MySQL, SQLite with migrations, repositories, and query building.

---

## Overview

`lexigram-sql` provides an async SQLAlchemy ORM layer with the repository pattern, unit-of-work, connection pooling, multi-database support, Alembic migrations, and optional HMAC audit checksums. All database operations are wired through `DatabaseProviderProtocol` in the DI container.

---


> Full documentation: [docs.lexigram.dev](https://docs.lexigram.dev)
## Install

```bash
uv add lexigram lexigram-sql

# With async PostgreSQL driver
uv add "lexigram-sql[postgres]"

# With async MySQL driver
uv add "lexigram-sql[mysql]"

# With SQLite async driver
uv add "lexigram-sql[sqlite]"
```

## Quick Start

```python
from lexigram import Application, StandardModule
from lexigram.di.module import Module, module
from lexigram.sql import DatabaseModule
from lexigram.sql.config import DatabaseConfig


@module(
    imports=[
        DatabaseModule.configure(
            DatabaseConfig(url="postgresql+asyncpg://user:pass@localhost/mydb")
        )
    ]
)
class AppModule(Module):
    pass


async def main() -> None:
    async with Application.boot(modules=[AppModule]) as app:
        from lexigram.contracts.data.sql.database import DatabaseProviderProtocol
        db = await app.container.resolve(DatabaseProviderProtocol)
        result = await db.execute_query("SELECT 1")


if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
```

## Configuration

> **Zero-config usage:** Call `DatabaseModule.configure()` with no arguments to use all defaults (SQLite).

### Option 1 — YAML file

```yaml
# application.yaml
sql:
  backend:
    url: "${LEX_SQL__BACKEND__URL}"
  pool:
    min_size: 2
    max_size: 10
    timeout: 30
  operations:
    echo: false
```

### Option 2 — Profiles + Environment Variables *(recommended)*

```bash
export LEX_SQL__BACKEND__URL=postgresql+asyncpg://user:pass@host/db
export LEX_SQL__POOL__MAX_SIZE=20
export LEX_SQL__POOL__TIMEOUT=60
```

### Option 3 — Python

```python
from lexigram.sql import DatabaseModule
from lexigram.sql.config import DatabaseConfig

DatabaseModule.configure(
    DatabaseConfig(
        url="postgresql+asyncpg://user:pass@localhost/mydb",
    )
)
```

### Config reference

| Field | Default | Env var | Description |
|-------|---------|---------|-------------|
| `backend.url` | `"sqlite:///piccolina.db"` | `LEX_SQL__BACKEND__URL` | Database connection URL |
| `pool.min_size` | `1` | `LEX_SQL__POOL__MIN_SIZE` | Minimum pool connections |
| `pool.max_size` | `10` | `LEX_SQL__POOL__MAX_SIZE` | Maximum pool connections |
| `pool.timeout` | `30` | `LEX_SQL__POOL__TIMEOUT` | Pool acquire timeout (seconds) |
| `operations.echo` | `False` | `LEX_SQL__OPERATIONS__ECHO` | Echo SQL statements |
| `audit_hmac_key` | `None` | `LEX_SQL__AUDIT_HMAC_KEY` | HMAC key for audit checksums |

## Module Factory Methods

| Method | Description |
|--------|-------------|
| `DatabaseModule.configure(config, enable_migrations, migration_dir)` | Configure with explicit `DatabaseConfig` |
| `DatabaseModule.scope(*repositories)` | Scope repository classes into a feature module |
| `DatabaseModule.stub(config=None)` | In-memory SQLite for testing |

## Key Features

- **Repository pattern** — `SQLRepository` base class with find, create, update, delete, count
- **Unit of work** — `AbstractUnitOfWork` tracks changes and publishes domain events on commit
- **Multi-database** — `NamedDatabaseConfig` for multiple backends resolved via `Annotated[DatabaseProviderProtocol, Named("analytics")]`
- **Connection pooling** — SQLAlchemy async pool with configurable min/max size
- **Alembic migrations** — auto-run on boot in development; disabled by default in production
- **HMAC audit checksums** — optional signing of write operations for integrity verification
- **Production security** — blocks default passwords (`:password@`, `:postgres@`, etc.) when `LEX_ENV=production`

## Testing

```python
from lexigram import Application
from lexigram.sql import DatabaseModule
from lexigram.sql.config import DatabaseConfig

async def test_repository():
    async with Application.boot(
        modules=[
            DatabaseModule.stub(
                DatabaseConfig(url="sqlite+aiosqlite:///:memory:")
            )
        ]
    ) as app:
        db = await app.container.resolve(DatabaseProviderProtocol)
        # run your test queries
```

## Key Source Files

| File | What it contains |
|------|----------------|
| `src/lexigram/sql/module.py` | `DatabaseModule.configure()`, `.scope()`, `.stub()` |
| `src/lexigram/sql/config.py` | `DatabaseConfig`, `DatabasePoolConfig`, `NamedDatabaseConfig` |
| `src/lexigram/sql/di/provider.py` | `DatabaseProvider` boot and registration |
| `src/lexigram/sql/repositories/base.py` | `SQLRepository` base class |
| `src/lexigram/sql/unit_of_work/base.py` | `AbstractUnitOfWork` |