hayate-sql 0.1.0__py3-none-any.whl

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,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,18 @@
1
+ hayate_sql/__init__.py,sha256=bVn0uZLZpEf-8uQTVHM2QCIMbO7YgHZ7lg4ZIOFS2Xo,725
2
+ hayate_sql/__main__.py,sha256=yiJcEoLXRXDs5XkHZVLUo1ZoP7mV42mf8MhzZssznJE,3674
3
+ hayate_sql/adapters/__init__.py,sha256=YkIAPJQWVv3vOWCjLAQa-pVmlIfQclRuzSbVasW13Q4,207
4
+ hayate_sql/adapters/d1.py,sha256=qSb5BHvTtQhY63fCeMXFT9TnPl1rhOWenEqfkcpiFkY,2595
5
+ hayate_sql/adapters/postgres.py,sha256=SDGflNBOxDy90OWPs2Rcw4oN2hJknd_H82JEJn8e7qw,1383
6
+ hayate_sql/adapters/sqlite.py,sha256=j3FRrSJ6fNUSOiV_b017G3jgMa0tqQtU6PeP8vqlBzY,3643
7
+ hayate_sql/check.py,sha256=1fvi6sVz-bjnswsFO1t7AaKrYoZs1CcWrNB5diLzUYo,5033
8
+ hayate_sql/contract.py,sha256=bEri3547i4-j-RQpI9V5e8sf9x3GKMwKqrVdqXNKXrs,3839
9
+ hayate_sql/database.py,sha256=bL4zoNQMyyBqWTvJ8iooG-gDpsJAEghxvCOrcFoBZbk,4503
10
+ hayate_sql/generate.py,sha256=LQi00GhvOpYIXZ_XInljrMxI66UUGmyQ654t2GrDOm4,5650
11
+ hayate_sql/migrations.py,sha256=LRjSDIs0zalJfK36kq0fz-laN_MFRZRcIDNqQe-iL7M,2020
12
+ hayate_sql/parser.py,sha256=5v8ue_jpQYMGee9k0qCC3XZgvCDR-2-HMl6pc2AWauM,5364
13
+ hayate_sql/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
14
+ hayate_sql-0.1.0.dist-info/licenses/LICENSE,sha256=KTFzH0Ey0y0j9ZWIkI_AhY449BepGTS6gJ1MD2u4LHs,1071
15
+ hayate_sql-0.1.0.dist-info/WHEEL,sha256=l3MmIxu8qaet7ng2J9fFoJnYGj8IREj7jXTbgsuzmy4,81
16
+ hayate_sql-0.1.0.dist-info/entry_points.txt,sha256=oJM-V7NZ8znK1o4A8g3IFuiAxN6GrUfAvrbHxEYvWGs,63
17
+ hayate_sql-0.1.0.dist-info/METADATA,sha256=FSrUUAUM2hctgvfFRvdUYEhKyC9-TyDi-5ae2TR18t8,6721
18
+ hayate_sql-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.32
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ hayate-sql = hayate_sql.__main__:entrypoint
3
+
@@ -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.