Skip to main content

hayate-sql

Hayate ecosystem: Start here · Production golden app · Tested compatibility

Native SQL with a checked, typed execution boundary — not an ORM and not a query builder.

hayate-sql keeps each database's SQL, placeholders, plans, and transaction semantics intact. A small contract above each statement adds named application arguments, result cardinality, result shape, timeout, and parameter-free telemetry.

Status: alpha (0.1.x), typed. SQLite, Cloudflare D1, and asyncpg-compatible PostgreSQL execution are implemented. SQLite/D1 contracts can be compiled against an in-memory schema; PostgreSQL contracts can be prepared against an ephemeral PostgreSQL transaction. The design memo (Japanese, per project convention) is in DESIGN.md.

Install

uv add hayate-sql

# Include asyncpg when PostgreSQL is the target.
uv add "hayate-sql[postgres]"

Write SQL

One .sql file contains one database-native statement:

-- name: get_document :one?
-- param: workspace_id str
-- param: document_id UUID
-- column: id UUID
-- column: title str
-- column: body str
-- timeout: 50ms
SELECT id, title, body
FROM documents
WHERE workspace_id = ?1
  AND id = ?2

The placeholder syntax belongs to the target database. Use ?1 for SQLite/D1 and $1 for PostgreSQL. The declared parameter order maps named Python arguments to those native positions.

Cardinality is explicit:

  • :one — exactly one row
  • :one? — zero or one row
  • :many — zero or more rows
  • :exec — no result rows; returns CommandResult

Supported contract types are str, int, float, bool, bytes, object, Any, datetime, date, Decimal, and UUID. Append ? for a nullable value. Types generate Python annotations; runtime enforcement intentionally checks the result's column shape rather than pretending that SQLite and PostgreSQL use identical value representations. Declarations must therefore describe the target driver's returned Python values (for example, a raw SQLite boolean column is normally declared as int unless the SQL converts it).

Compile and generate

Compile D1/SQLite SQL against the real schema without executing queries:

hayate-sql check queries/ --dialect d1 --schema schema.sql

For PostgreSQL, prepare every query inside a transaction that is always rolled back:

HAYATE_SQL_DATABASE_URL=postgresql://localhost/app_test \
  hayate-sql check queries/postgres/ \
  --dialect postgres \
  --schema schema.sql

Generate a typed facade:

hayate-sql generate queries/ -o app/queries.py

The generated module intentionally uses only Python 3.12+ syntax and does not force postponed annotations. Run the project's formatter after generation when its style policy is stricter than the standard output.

The generated function has named arguments and a TypedDict result:

document = await queries.get_document(
    database,
    workspace_id=workspace_id,
    document_id=document_id,
)

Generated code is a build artifact. SQL remains the source of truth.

Check against migration history

When forward-only SQL migrations are the schema source of truth, replay them in order before compiling the queries:

hayate-sql check queries/ --dialect d1 --migrations migrations/

Migration files use a fixed-width positive number followed by a lowercase description, for example 0001_create_documents.sql. Gaps are allowed, while duplicate versions, mixed widths, malformed names, and empty migrations fail the check.

The migrations run only in hayate-sql's disposable check database. Applying them to development, staging, or production remains the responsibility of the database-native tool, such as wrangler d1 migrations apply or Alembic.

Execute

SQLite

from hayate_sql.adapters import SQLiteDatabase

async with SQLiteDatabase("app.db") as database:
    async with database.transaction(mode="immediate"):
        await queries.create_document(
            database,
            document_id=document_id,
            title=title,
        )

Cloudflare D1

from hayate_sql.adapters import D1Database

database = D1Database(context.env.DB)

# D1 consistency is explicit rather than hidden behind a generic transaction.
session = database.with_session("first-primary")
document = await queries.get_document(
    session,
    workspace_id=workspace_id,
    document_id=document_id,
)
bookmark = session.bookmark()

The repository's reproducible workerd probe builds the 0.1.0 wheel, injects that wheel into the Python Workers bundle, applies a real local D1 migration, and executes :one, :one?, :many, and :exec:

bash scripts/check_workers_d1.sh

The same probe asserts that telemetry contains neither SQL text nor a sentinel bound value.

PostgreSQL

import asyncpg

from hayate_sql.adapters import AsyncpgDatabase

connection = await asyncpg.connect(dsn)
database = AsyncpgDatabase(connection)

# Delegates to asyncpg's native transaction context.
async with database.transaction(isolation="serializable"):
    await queries.update_document(database, document_id=document_id, title=title)

Observe without leaking parameters

An observer receives only the query name, contract fingerprint, duration, success, row count, and error type. SQL text and bound values are deliberately absent.

def observe(event):
    metrics.histogram("db.query.duration", event.duration_ms, query=event.name)


database = SQLiteDatabase("app.db", observer=observe)

Non-goals

  • ORM models, identity maps, lazy relations, or repositories
  • a database-independent SQL DSL or query builder
  • SQL dialect translation
  • a universal transaction abstraction
  • production migration application, implicit schema migration, or write retries

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hayate_sql-0.1.2.tar.gz (14.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hayate_sql-0.1.2-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file hayate_sql-0.1.2.tar.gz.

File metadata

  • Download URL: hayate_sql-0.1.2.tar.gz
  • Upload date:
  • Size: 14.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for hayate_sql-0.1.2.tar.gz
Algorithm Hash digest
SHA256 f6891901d257f5419ccb99d5c5a21740a8f4f7814ca48009e47fea420ac480c8
MD5 91398fce83210e0aae2798a102a6575b
BLAKE2b-256 26dff6bf75d1881889d8cd87048ce97e4acf698ba73335ba38642c57de0e2a78

See more details on using hashes here.

Provenance

The following attestation bundles were made for hayate_sql-0.1.2.tar.gz:

Publisher: release.yml on hayatepy/hayate-sql

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hayate_sql-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: hayate_sql-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for hayate_sql-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 02ff10996779ade9889e373691382d2e9049b56c323c7a80914cffb929006522
MD5 0dc5c63bbae8cfe43d8cbbbec8942179
BLAKE2b-256 cf78e747ca2faed9493cef8723223075b8353eb619751763010bfcc3941fef68

See more details on using hashes here.

Provenance

The following attestation bundles were made for hayate_sql-0.1.2-py3-none-any.whl:

Publisher: release.yml on hayatepy/hayate-sql

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.2 This release

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page