hayate-sql 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yusuke Hayashi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: hayate-sql
3
+ Version: 0.1.0
4
+ Summary: SQL-first query contracts for Python, PostgreSQL, SQLite, and Cloudflare D1
5
+ Keywords: hayate,sql,postgresql,sqlite,cloudflare-d1
6
+ Author: Yusuke Hayashi
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: Database
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: asyncpg>=0.30 ; extra == 'postgres'
17
+ Requires-Python: >=3.12
18
+ Project-URL: Repository, https://github.com/hayatepy/hayate-sql
19
+ Project-URL: Documentation, https://github.com/hayatepy/hayate-sql#readme
20
+ Project-URL: Changelog, https://github.com/hayatepy/hayate-sql/blob/main/CHANGELOG.md
21
+ Provides-Extra: postgres
22
+ Description-Content-Type: text/markdown
23
+
24
+ # hayate-sql
25
+
26
+ Native SQL with a checked, typed execution boundary — not an ORM and not a
27
+ query builder.
28
+
29
+ `hayate-sql` keeps each database's SQL, placeholders, plans, and transaction
30
+ semantics intact. A small contract above each statement adds named application
31
+ arguments, result cardinality, result shape, timeout, and parameter-free
32
+ telemetry.
33
+
34
+ > **Status: alpha (0.1.x), typed.** SQLite, Cloudflare D1, and
35
+ > asyncpg-compatible PostgreSQL execution are implemented. SQLite/D1 contracts
36
+ > can be compiled against an in-memory schema; PostgreSQL contracts can be
37
+ > prepared against an ephemeral PostgreSQL transaction. The design memo
38
+ > (Japanese, per project convention) is in [DESIGN.md](DESIGN.md).
39
+
40
+ ## Install
41
+
42
+ ```sh
43
+ uv add hayate-sql
44
+
45
+ # Include asyncpg when PostgreSQL is the target.
46
+ uv add "hayate-sql[postgres]"
47
+ ```
48
+
49
+ ## Write SQL
50
+
51
+ One `.sql` file contains one database-native statement:
52
+
53
+ ```sql
54
+ -- name: get_document :one?
55
+ -- param: workspace_id str
56
+ -- param: document_id UUID
57
+ -- column: id UUID
58
+ -- column: title str
59
+ -- column: body str
60
+ -- timeout: 50ms
61
+ SELECT id, title, body
62
+ FROM documents
63
+ WHERE workspace_id = ?1
64
+ AND id = ?2
65
+ ```
66
+
67
+ The placeholder syntax belongs to the target database. Use `?1` for
68
+ SQLite/D1 and `$1` for PostgreSQL. The declared parameter order maps named
69
+ Python arguments to those native positions.
70
+
71
+ Cardinality is explicit:
72
+
73
+ - `:one` — exactly one row
74
+ - `:one?` — zero or one row
75
+ - `:many` — zero or more rows
76
+ - `:exec` — no result rows; returns `CommandResult`
77
+
78
+ Supported contract types are `str`, `int`, `float`, `bool`, `bytes`, `object`,
79
+ `Any`, `datetime`, `date`, `Decimal`, and `UUID`. Append `?` for a nullable
80
+ value. Types generate Python annotations; runtime enforcement intentionally
81
+ checks the result's column shape rather than pretending that SQLite and
82
+ PostgreSQL use identical value representations. Declarations must therefore
83
+ describe the target driver's returned Python values (for example, a raw
84
+ SQLite boolean column is normally declared as `int` unless the SQL converts it).
85
+
86
+ ## Compile and generate
87
+
88
+ Compile D1/SQLite SQL against the real schema without executing queries:
89
+
90
+ ```sh
91
+ hayate-sql check queries/ --dialect d1 --schema schema.sql
92
+ ```
93
+
94
+ For PostgreSQL, `prepare` every query inside a transaction that is always
95
+ rolled back:
96
+
97
+ ```sh
98
+ HAYATE_SQL_DATABASE_URL=postgresql://localhost/app_test \
99
+ hayate-sql check queries/postgres/ \
100
+ --dialect postgres \
101
+ --schema schema.sql
102
+ ```
103
+
104
+ Generate a typed facade:
105
+
106
+ ```sh
107
+ hayate-sql generate queries/ -o app/queries.py
108
+ ```
109
+
110
+ The generated module intentionally uses only Python 3.12+ syntax and does not
111
+ force postponed annotations. Run the project's formatter after generation
112
+ when its style policy is stricter than the standard output.
113
+
114
+ The generated function has named arguments and a `TypedDict` result:
115
+
116
+ ```python
117
+ document = await queries.get_document(
118
+ database,
119
+ workspace_id=workspace_id,
120
+ document_id=document_id,
121
+ )
122
+ ```
123
+
124
+ Generated code is a build artifact. SQL remains the source of truth.
125
+
126
+ ### Check against migration history
127
+
128
+ When forward-only SQL migrations are the schema source of truth, replay them
129
+ in order before compiling the queries:
130
+
131
+ ```sh
132
+ hayate-sql check queries/ --dialect d1 --migrations migrations/
133
+ ```
134
+
135
+ Migration files use a fixed-width positive number followed by a lowercase
136
+ description, for example `0001_create_documents.sql`. Gaps are allowed, while
137
+ duplicate versions, mixed widths, malformed names, and empty migrations fail
138
+ the check.
139
+
140
+ The migrations run only in hayate-sql's disposable check database. Applying
141
+ them to development, staging, or production remains the responsibility of the
142
+ database-native tool, such as `wrangler d1 migrations apply` or Alembic.
143
+
144
+ ## Execute
145
+
146
+ ### SQLite
147
+
148
+ ```python
149
+ from hayate_sql.adapters import SQLiteDatabase
150
+
151
+ async with SQLiteDatabase("app.db") as database:
152
+ async with database.transaction(mode="immediate"):
153
+ await queries.create_document(
154
+ database,
155
+ document_id=document_id,
156
+ title=title,
157
+ )
158
+ ```
159
+
160
+ ### Cloudflare D1
161
+
162
+ ```python
163
+ from hayate_sql.adapters import D1Database
164
+
165
+ database = D1Database(context.env.DB)
166
+
167
+ # D1 consistency is explicit rather than hidden behind a generic transaction.
168
+ session = database.with_session("first-primary")
169
+ document = await queries.get_document(
170
+ session,
171
+ workspace_id=workspace_id,
172
+ document_id=document_id,
173
+ )
174
+ bookmark = session.bookmark()
175
+ ```
176
+
177
+ The repository's reproducible workerd probe builds the 0.1.0 wheel, injects
178
+ that wheel into the Python Workers bundle, applies a real local D1 migration,
179
+ and executes `:one`, `:one?`, `:many`, and `:exec`:
180
+
181
+ ```sh
182
+ bash scripts/check_workers_d1.sh
183
+ ```
184
+
185
+ The same probe asserts that telemetry contains neither SQL text nor a sentinel
186
+ bound value.
187
+
188
+ ### PostgreSQL
189
+
190
+ ```python
191
+ import asyncpg
192
+
193
+ from hayate_sql.adapters import AsyncpgDatabase
194
+
195
+ connection = await asyncpg.connect(dsn)
196
+ database = AsyncpgDatabase(connection)
197
+
198
+ # Delegates to asyncpg's native transaction context.
199
+ async with database.transaction(isolation="serializable"):
200
+ await queries.update_document(database, document_id=document_id, title=title)
201
+ ```
202
+
203
+ ## Observe without leaking parameters
204
+
205
+ An observer receives only the query name, contract fingerprint, duration,
206
+ success, row count, and error type. SQL text and bound values are deliberately
207
+ absent.
208
+
209
+ ```python
210
+ def observe(event):
211
+ metrics.histogram("db.query.duration", event.duration_ms, query=event.name)
212
+
213
+
214
+ database = SQLiteDatabase("app.db", observer=observe)
215
+ ```
216
+
217
+ ## Non-goals
218
+
219
+ - ORM models, identity maps, lazy relations, or repositories
220
+ - a database-independent SQL DSL or query builder
221
+ - SQL dialect translation
222
+ - a universal transaction abstraction
223
+ - production migration application, implicit schema migration, or write retries
224
+
225
+ ## License
226
+
227
+ MIT
@@ -0,0 +1,204 @@
1
+ # hayate-sql
2
+
3
+ Native SQL with a checked, typed execution boundary — not an ORM and not a
4
+ query builder.
5
+
6
+ `hayate-sql` keeps each database's SQL, placeholders, plans, and transaction
7
+ semantics intact. A small contract above each statement adds named application
8
+ arguments, result cardinality, result shape, timeout, and parameter-free
9
+ telemetry.
10
+
11
+ > **Status: alpha (0.1.x), typed.** SQLite, Cloudflare D1, and
12
+ > asyncpg-compatible PostgreSQL execution are implemented. SQLite/D1 contracts
13
+ > can be compiled against an in-memory schema; PostgreSQL contracts can be
14
+ > prepared against an ephemeral PostgreSQL transaction. The design memo
15
+ > (Japanese, per project convention) is in [DESIGN.md](DESIGN.md).
16
+
17
+ ## Install
18
+
19
+ ```sh
20
+ uv add hayate-sql
21
+
22
+ # Include asyncpg when PostgreSQL is the target.
23
+ uv add "hayate-sql[postgres]"
24
+ ```
25
+
26
+ ## Write SQL
27
+
28
+ One `.sql` file contains one database-native statement:
29
+
30
+ ```sql
31
+ -- name: get_document :one?
32
+ -- param: workspace_id str
33
+ -- param: document_id UUID
34
+ -- column: id UUID
35
+ -- column: title str
36
+ -- column: body str
37
+ -- timeout: 50ms
38
+ SELECT id, title, body
39
+ FROM documents
40
+ WHERE workspace_id = ?1
41
+ AND id = ?2
42
+ ```
43
+
44
+ The placeholder syntax belongs to the target database. Use `?1` for
45
+ SQLite/D1 and `$1` for PostgreSQL. The declared parameter order maps named
46
+ Python arguments to those native positions.
47
+
48
+ Cardinality is explicit:
49
+
50
+ - `:one` — exactly one row
51
+ - `:one?` — zero or one row
52
+ - `:many` — zero or more rows
53
+ - `:exec` — no result rows; returns `CommandResult`
54
+
55
+ Supported contract types are `str`, `int`, `float`, `bool`, `bytes`, `object`,
56
+ `Any`, `datetime`, `date`, `Decimal`, and `UUID`. Append `?` for a nullable
57
+ value. Types generate Python annotations; runtime enforcement intentionally
58
+ checks the result's column shape rather than pretending that SQLite and
59
+ PostgreSQL use identical value representations. Declarations must therefore
60
+ describe the target driver's returned Python values (for example, a raw
61
+ SQLite boolean column is normally declared as `int` unless the SQL converts it).
62
+
63
+ ## Compile and generate
64
+
65
+ Compile D1/SQLite SQL against the real schema without executing queries:
66
+
67
+ ```sh
68
+ hayate-sql check queries/ --dialect d1 --schema schema.sql
69
+ ```
70
+
71
+ For PostgreSQL, `prepare` every query inside a transaction that is always
72
+ rolled back:
73
+
74
+ ```sh
75
+ HAYATE_SQL_DATABASE_URL=postgresql://localhost/app_test \
76
+ hayate-sql check queries/postgres/ \
77
+ --dialect postgres \
78
+ --schema schema.sql
79
+ ```
80
+
81
+ Generate a typed facade:
82
+
83
+ ```sh
84
+ hayate-sql generate queries/ -o app/queries.py
85
+ ```
86
+
87
+ The generated module intentionally uses only Python 3.12+ syntax and does not
88
+ force postponed annotations. Run the project's formatter after generation
89
+ when its style policy is stricter than the standard output.
90
+
91
+ The generated function has named arguments and a `TypedDict` result:
92
+
93
+ ```python
94
+ document = await queries.get_document(
95
+ database,
96
+ workspace_id=workspace_id,
97
+ document_id=document_id,
98
+ )
99
+ ```
100
+
101
+ Generated code is a build artifact. SQL remains the source of truth.
102
+
103
+ ### Check against migration history
104
+
105
+ When forward-only SQL migrations are the schema source of truth, replay them
106
+ in order before compiling the queries:
107
+
108
+ ```sh
109
+ hayate-sql check queries/ --dialect d1 --migrations migrations/
110
+ ```
111
+
112
+ Migration files use a fixed-width positive number followed by a lowercase
113
+ description, for example `0001_create_documents.sql`. Gaps are allowed, while
114
+ duplicate versions, mixed widths, malformed names, and empty migrations fail
115
+ the check.
116
+
117
+ The migrations run only in hayate-sql's disposable check database. Applying
118
+ them to development, staging, or production remains the responsibility of the
119
+ database-native tool, such as `wrangler d1 migrations apply` or Alembic.
120
+
121
+ ## Execute
122
+
123
+ ### SQLite
124
+
125
+ ```python
126
+ from hayate_sql.adapters import SQLiteDatabase
127
+
128
+ async with SQLiteDatabase("app.db") as database:
129
+ async with database.transaction(mode="immediate"):
130
+ await queries.create_document(
131
+ database,
132
+ document_id=document_id,
133
+ title=title,
134
+ )
135
+ ```
136
+
137
+ ### Cloudflare D1
138
+
139
+ ```python
140
+ from hayate_sql.adapters import D1Database
141
+
142
+ database = D1Database(context.env.DB)
143
+
144
+ # D1 consistency is explicit rather than hidden behind a generic transaction.
145
+ session = database.with_session("first-primary")
146
+ document = await queries.get_document(
147
+ session,
148
+ workspace_id=workspace_id,
149
+ document_id=document_id,
150
+ )
151
+ bookmark = session.bookmark()
152
+ ```
153
+
154
+ The repository's reproducible workerd probe builds the 0.1.0 wheel, injects
155
+ that wheel into the Python Workers bundle, applies a real local D1 migration,
156
+ and executes `:one`, `:one?`, `:many`, and `:exec`:
157
+
158
+ ```sh
159
+ bash scripts/check_workers_d1.sh
160
+ ```
161
+
162
+ The same probe asserts that telemetry contains neither SQL text nor a sentinel
163
+ bound value.
164
+
165
+ ### PostgreSQL
166
+
167
+ ```python
168
+ import asyncpg
169
+
170
+ from hayate_sql.adapters import AsyncpgDatabase
171
+
172
+ connection = await asyncpg.connect(dsn)
173
+ database = AsyncpgDatabase(connection)
174
+
175
+ # Delegates to asyncpg's native transaction context.
176
+ async with database.transaction(isolation="serializable"):
177
+ await queries.update_document(database, document_id=document_id, title=title)
178
+ ```
179
+
180
+ ## Observe without leaking parameters
181
+
182
+ An observer receives only the query name, contract fingerprint, duration,
183
+ success, row count, and error type. SQL text and bound values are deliberately
184
+ absent.
185
+
186
+ ```python
187
+ def observe(event):
188
+ metrics.histogram("db.query.duration", event.duration_ms, query=event.name)
189
+
190
+
191
+ database = SQLiteDatabase("app.db", observer=observe)
192
+ ```
193
+
194
+ ## Non-goals
195
+
196
+ - ORM models, identity maps, lazy relations, or repositories
197
+ - a database-independent SQL DSL or query builder
198
+ - SQL dialect translation
199
+ - a universal transaction abstraction
200
+ - production migration application, implicit schema migration, or write retries
201
+
202
+ ## License
203
+
204
+ MIT
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.9,<1"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "hayate-sql"
7
+ version = "0.1.0"
8
+ description = "SQL-first query contracts for Python, PostgreSQL, SQLite, and Cloudflare D1"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Yusuke Hayashi" }]
14
+ keywords = ["hayate", "sql", "postgresql", "sqlite", "cloudflare-d1"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ "Topic :: Database",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = []
25
+
26
+ [project.optional-dependencies]
27
+ postgres = ["asyncpg>=0.30"]
28
+
29
+ [project.scripts]
30
+ hayate-sql = "hayate_sql.__main__:entrypoint"
31
+
32
+ [project.urls]
33
+ Repository = "https://github.com/hayatepy/hayate-sql"
34
+ Documentation = "https://github.com/hayatepy/hayate-sql#readme"
35
+ Changelog = "https://github.com/hayatepy/hayate-sql/blob/main/CHANGELOG.md"
36
+
37
+ [dependency-groups]
38
+ dev = [
39
+ "mypy>=1.18",
40
+ "pytest>=8.3",
41
+ "pytest-asyncio>=0.25",
42
+ "ruff>=0.9",
43
+ "workers-py==1.15.0",
44
+ "workers-runtime-sdk==1.6.3",
45
+ ]
46
+
47
+ [tool.mypy]
48
+ python_version = "3.12"
49
+ strict = true
50
+
51
+ [tool.pytest.ini_options]
52
+ asyncio_mode = "auto"
53
+ testpaths = ["tests"]
54
+
55
+ [tool.ruff]
56
+ line-length = 100
57
+ target-version = "py312"
58
+ src = ["src", "tests"]
59
+
60
+ [tool.ruff.lint]
61
+ select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
@@ -0,0 +1,36 @@
1
+ """hayate-sql: SQL-first query contracts, not an ORM."""
2
+
3
+ from .contract import (
4
+ Cardinality,
5
+ CardinalityError,
6
+ Column,
7
+ Parameter,
8
+ Query,
9
+ QueryArgumentError,
10
+ QueryContractError,
11
+ RowShapeError,
12
+ )
13
+ from .database import CommandResult, Database, Observer, QueryEvent, Row
14
+ from .parser import QueryParseError, load_queries, parse_query
15
+
16
+ __version__ = "0.1.0"
17
+
18
+ __all__ = [
19
+ "Cardinality",
20
+ "CardinalityError",
21
+ "Column",
22
+ "CommandResult",
23
+ "Database",
24
+ "Observer",
25
+ "Parameter",
26
+ "Query",
27
+ "QueryArgumentError",
28
+ "QueryContractError",
29
+ "QueryEvent",
30
+ "QueryParseError",
31
+ "Row",
32
+ "RowShapeError",
33
+ "__version__",
34
+ "load_queries",
35
+ "parse_query",
36
+ ]
@@ -0,0 +1,105 @@
1
+ """The ``hayate-sql`` command line."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import os
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import NoReturn
11
+
12
+ from .check import CheckFailure, check_postgres, check_sqlite
13
+ from .generate import generate_module
14
+ from .migrations import load_migrations
15
+ from .parser import load_queries
16
+
17
+
18
+ def main(argv: list[str] | None = None) -> int:
19
+ parser = _parser()
20
+ args = parser.parse_args(argv)
21
+ try:
22
+ queries = load_queries([Path(item) for item in args.paths])
23
+ if args.command == "generate":
24
+ output = generate_module([query for _, query in queries])
25
+ Path(args.output).write_text(output)
26
+ print(f"generated {len(queries)} queries in {args.output}")
27
+ return 0
28
+
29
+ schemas = [Path(path).read_text() for path in args.schema]
30
+ migrations = load_migrations(Path(args.migrations)) if args.migrations else ()
31
+ if args.dialect in ("sqlite", "d1"):
32
+ failures = check_sqlite(
33
+ queries,
34
+ schemas=schemas,
35
+ migrations=migrations,
36
+ d1=args.dialect == "d1",
37
+ )
38
+ else:
39
+ database_url = args.database_url or os.environ.get("HAYATE_SQL_DATABASE_URL")
40
+ if database_url is None:
41
+ parser.error("PostgreSQL checks require --database-url or HAYATE_SQL_DATABASE_URL")
42
+ failures = asyncio.run(
43
+ check_postgres(
44
+ queries,
45
+ database_url=database_url,
46
+ schemas=schemas,
47
+ migrations=migrations,
48
+ )
49
+ )
50
+ return _report(failures, len(queries))
51
+ except (OSError, RuntimeError, ValueError) as error:
52
+ print(f"hayate-sql: {error}", file=sys.stderr)
53
+ return 1
54
+
55
+
56
+ def entrypoint() -> NoReturn:
57
+ raise SystemExit(main())
58
+
59
+
60
+ def _parser() -> argparse.ArgumentParser:
61
+ parser = argparse.ArgumentParser(
62
+ prog="hayate-sql",
63
+ description="Compile SQL contracts and generate typed Python accessors.",
64
+ )
65
+ commands = parser.add_subparsers(dest="command", required=True)
66
+
67
+ generate = commands.add_parser("generate", help="generate a typed Python module")
68
+ generate.add_argument("paths", nargs="+", help=".sql files or directories")
69
+ generate.add_argument("-o", "--output", required=True, help="generated .py path")
70
+
71
+ check = commands.add_parser("check", help="compile queries against a real schema")
72
+ check.add_argument("paths", nargs="+", help=".sql files or directories")
73
+ check.add_argument("--dialect", choices=("sqlite", "d1", "postgres"), default="sqlite")
74
+ schema_source = check.add_mutually_exclusive_group()
75
+ schema_source.add_argument(
76
+ "--schema",
77
+ action="append",
78
+ default=[],
79
+ metavar="FILE",
80
+ help="schema SQL to apply before checking; repeatable",
81
+ )
82
+ schema_source.add_argument(
83
+ "--migrations",
84
+ metavar="DIRECTORY",
85
+ help="ordered SQL migrations to replay in a disposable check database",
86
+ )
87
+ check.add_argument(
88
+ "--database-url",
89
+ help="ephemeral PostgreSQL URL; prefer HAYATE_SQL_DATABASE_URL to avoid shell history",
90
+ )
91
+ return parser
92
+
93
+
94
+ def _report(failures: list[CheckFailure], total: int) -> int:
95
+ if not failures:
96
+ print(f"checked {total} queries")
97
+ return 0
98
+ for failure in failures:
99
+ print(f"{failure.path}: {failure.query_name}: {failure.message}", file=sys.stderr)
100
+ print(f"{len(failures)} of {total} queries failed", file=sys.stderr)
101
+ return 1
102
+
103
+
104
+ if __name__ == "__main__":
105
+ entrypoint()
@@ -0,0 +1,7 @@
1
+ """Database-native hayate-sql adapters."""
2
+
3
+ from .d1 import D1Database
4
+ from .postgres import AsyncpgDatabase
5
+ from .sqlite import SQLiteDatabase
6
+
7
+ __all__ = ["AsyncpgDatabase", "D1Database", "SQLiteDatabase"]