qubu 0.6.1 → 0.6.3
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.
- package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
- package/dist/codegen.d.mts +2 -2
- package/dist/codegen.mjs +180 -90
- package/dist/column-Cyc2CMnG.mjs +116 -0
- package/dist/column-DDRvD7SF.mjs +721 -0
- package/dist/{constraints-DM_tarXc.mjs → constraints-CAmi18Uk.mjs} +8 -3
- package/dist/core.d.mts +3 -3
- package/dist/core.mjs +4 -4
- package/dist/diff.d.mts +9 -9
- package/dist/diff.mjs +152 -102
- package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
- package/dist/{expressions-BCjc08zw.mjs → expressions-_6JF_J77.mjs} +2 -1
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +368 -74
- package/dist/introspection/mysql.d.mts +1 -1
- package/dist/introspection/mysql.mjs +156 -23
- package/dist/introspection/postgres.d.mts +6 -3
- package/dist/introspection/postgres.mjs +388 -53
- package/dist/introspection/sqlite.d.mts +1 -1
- package/dist/introspection/sqlite.mjs +198 -12
- package/dist/introspection.d.mts +26 -12
- package/dist/introspection.mjs +2 -672
- package/dist/mysql.d.mts +3 -3
- package/dist/mysql.mjs +6 -5
- package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
- package/dist/{on-conflict-CnaY5qso.mjs → on-conflict-B2rFyHGF.mjs} +6 -8
- package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
- package/dist/{postgres-Dey7QXPL.mjs → postgres-hFhd0I9n.mjs} +4 -5
- package/dist/postgres.d.mts +2 -2
- package/dist/postgres.mjs +2 -2
- package/dist/{registry-oWDiqD7i.mjs → registry-BXE_4M9P.mjs} +1 -1
- package/dist/{relational-DSAJ-l58.mjs → relational-CoPBETjI.mjs} +3 -2
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +8 -8
- package/dist/{serialize-CE-gw5_s.mjs → serialize-CyobNEx-.mjs} +174 -30
- package/dist/serialize-Du2UPZMt.d.mts +92 -0
- package/dist/snapshot/mysql.d.mts +5 -5
- package/dist/snapshot/mysql.mjs +8 -8
- package/dist/snapshot/postgres.d.mts +3 -3
- package/dist/snapshot/postgres.mjs +9 -9
- package/dist/snapshot/sqlite.d.mts +3 -3
- package/dist/snapshot/sqlite.mjs +7 -7
- package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
- package/dist/snapshot.d.mts +4 -5
- package/dist/snapshot.mjs +3 -4
- package/dist/{source-BDuUXmAk.mjs → source-DYSUqzvb.mjs} +2 -2
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +6 -6
- package/dist/{table-C1QGNe4P.mjs → table-B8zEq0az.mjs} +4 -4
- package/dist/{types-BLNRatG_.mjs → types-CYHpSPwj.mjs} +10 -5
- package/dist/{types-BEn0N_al.d.mts → types-CiMvKi5V.d.mts} +14 -4
- package/dist/{types-DUe6eeI0.d.mts → types-Dqr4o2I1.d.mts} +590 -168
- package/dist/value-CpaUFtjw.mjs +45 -0
- package/dist/vite/ambient.d.ts +2 -0
- package/dist/vite.d.mts +1 -1
- package/dist/vite.mjs +2 -0
- package/docs/dialects-and-execution.md +105 -26
- package/docs/getting-started.md +10 -10
- package/docs/guides/better-auth.md +16 -5
- package/docs/guides/compose-queries.md +21 -9
- package/docs/guides/drizzle.md +8 -3
- package/docs/guides/extensions/dialects.md +1 -1
- package/docs/guides/extensions/overview.md +1 -1
- package/docs/guides/extensions/sources-and-clauses.md +7 -3
- package/docs/guides/extensions/typed-expressions.md +26 -12
- package/docs/guides/extensions/unsafe-syntax.md +10 -6
- package/docs/guides/json.md +126 -8
- package/docs/guides/mutations.md +51 -6
- package/docs/guides/select/conditions.md +18 -11
- package/docs/guides/select/grouping-and-windows.md +5 -2
- package/docs/guides/select/ordering-and-pagination.md +5 -3
- package/docs/guides/select/overview.md +6 -3
- package/docs/guides/sql-templates.md +11 -5
- package/docs/guides/valtio-sync.md +11 -5
- package/docs/guides/vite-plugin.md +2 -2
- package/docs/index.md +25 -18
- package/docs/migrations/adapters.md +92 -27
- package/docs/migrations/artifacts-and-policy.md +49 -20
- package/docs/migrations/index.md +15 -8
- package/docs/migrations/lotta-adoption.md +16 -5
- package/docs/migrations/operations.md +29 -15
- package/docs/migrations/recovery.md +40 -17
- package/docs/query-model/fragments.md +33 -5
- package/docs/query-model/result-shapes.md +2 -2
- package/docs/query-model/source-scope.md +5 -3
- package/docs/reference/introspection-support.md +42 -37
- package/docs/reference/mysql-snapshot.md +19 -4
- package/docs/reference/postgres-snapshot.md +17 -4
- package/docs/reference/sqlite-snapshot.md +19 -2
- package/docs/reference/supported-surface.md +221 -84
- package/docs/schema/catalog-model.md +44 -14
- package/docs/schema/code-generation.md +40 -21
- package/docs/schema/columns-and-writes.md +21 -11
- package/docs/schema/constraints-and-indexes.md +12 -5
- package/docs/schema/ddl-emission.md +16 -5
- package/docs/schema/diff.md +13 -5
- package/docs/schema/introspection.md +64 -31
- package/docs/schema/migration-plans.md +18 -10
- package/docs/schema/snapshots.md +68 -30
- package/docs/schema/storage-and-schema-sql.md +16 -5
- package/docs/schema/tables-and-names.md +1 -1
- package/docs/sql-semantic-types.md +11 -8
- package/docs/troubleshooting.md +14 -6
- package/package.json +2 -1
- package/dist/canonical-DMvR9yBe.mjs +0 -972
- package/dist/column-BzN8KFJa.mjs +0 -364
- package/dist/column-CFvSbil0.mjs +0 -309
- package/dist/complete-types-CNMWBWap.d.mts +0 -371
- package/dist/index-CGui70hi.d.mts +0 -32
- package/dist/json-Db7XRD91.mjs +0 -169
- package/dist/omit-OxV58AwX.mjs +0 -5
- package/dist/serialize-OvXCLzjm.d.mts +0 -66
- package/dist/snapshot-DgsOhf_8.mjs +0 -354
package/docs/guides/json.md
CHANGED
|
@@ -1,8 +1,121 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Query nested JSON
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Build inferred nested results, or read scalar values from stored JSON documents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Nest query results
|
|
6
|
+
|
|
7
|
+
Use `jsonArrayFrom()` to nest a query's rows and `jsonObjectFrom()` for a query
|
|
8
|
+
proven to return at most one row. Both preserve filtering, correlation,
|
|
9
|
+
ordering, and pagination:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
correlate,
|
|
14
|
+
desc,
|
|
15
|
+
eq,
|
|
16
|
+
fetchFirst,
|
|
17
|
+
from,
|
|
18
|
+
integer,
|
|
19
|
+
jsonArrayFrom,
|
|
20
|
+
jsonObjectFrom,
|
|
21
|
+
orderBy,
|
|
22
|
+
select,
|
|
23
|
+
table,
|
|
24
|
+
text,
|
|
25
|
+
where,
|
|
26
|
+
} from "qubu"
|
|
27
|
+
|
|
28
|
+
const users = table("users", { id: integer(), name: text() })
|
|
29
|
+
const posts = table("posts", {
|
|
30
|
+
id: integer(),
|
|
31
|
+
authorId: integer(),
|
|
32
|
+
title: text(),
|
|
33
|
+
})
|
|
34
|
+
const latestPosts = select(
|
|
35
|
+
{ id: posts.id, title: posts.title },
|
|
36
|
+
from(posts),
|
|
37
|
+
correlate(users),
|
|
38
|
+
where(eq(posts.authorId, users.id)),
|
|
39
|
+
orderBy(desc(posts.id)),
|
|
40
|
+
fetchFirst(3),
|
|
41
|
+
)
|
|
42
|
+
const latestPost = select(
|
|
43
|
+
{ title: posts.title },
|
|
44
|
+
from(posts),
|
|
45
|
+
correlate(users),
|
|
46
|
+
where(eq(posts.authorId, users.id)),
|
|
47
|
+
orderBy(desc(posts.id)),
|
|
48
|
+
fetchFirst(1),
|
|
49
|
+
)
|
|
50
|
+
const query = select(
|
|
51
|
+
{
|
|
52
|
+
name: users.name,
|
|
53
|
+
posts: jsonArrayFrom(latestPosts),
|
|
54
|
+
latestPost: jsonObjectFrom(latestPost),
|
|
55
|
+
},
|
|
56
|
+
from(users),
|
|
57
|
+
)
|
|
58
|
+
// Row: { name: string; posts: { id: number; title: string }[];
|
|
59
|
+
// latestPost: { title: string } | null }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Empty results and row limits
|
|
63
|
+
|
|
64
|
+
Execute the query through a Qubu adapter to decode nested results:
|
|
65
|
+
|
|
66
|
+
- An empty array query returns `[]`.
|
|
67
|
+
- An empty object query returns `null`.
|
|
68
|
+
- A source-free query proven to return exactly one row produces a non-null object.
|
|
69
|
+
|
|
70
|
+
An object query must be known to return at most one row. An unconditional
|
|
71
|
+
`fetchFirst(1)` or `fetchFirst(0)` proves that limit; a conditional limit does not.
|
|
72
|
+
|
|
73
|
+
The helpers compose inside further `select()` projections, so nesting can
|
|
74
|
+
continue without result-type assertions. `correlate()` and the outer query's
|
|
75
|
+
`FROM/JOIN` scope remain checked at every level.
|
|
76
|
+
|
|
77
|
+
### Preserve ordering
|
|
78
|
+
|
|
79
|
+
Nested arrays retain explicit `ORDER BY` and pagination. Tied sort keys retain
|
|
80
|
+
SQL's unspecified tie order; add a unique tie-breaker when order matters.
|
|
81
|
+
`DISTINCT` ordering must use the same expressions as the selection. Without an
|
|
82
|
+
`ORDER BY`, array order is unspecified.
|
|
83
|
+
|
|
84
|
+
### Supported databases
|
|
85
|
+
|
|
86
|
+
Nested results support PostgreSQL, MySQL 8.0.21+, and SQLite 3.45+. Other
|
|
87
|
+
dialects fail during rendering. SQLite's minimum includes the JSON aggregate
|
|
88
|
+
ordering fix needed to retain object values.
|
|
89
|
+
|
|
90
|
+
### Decode nested values
|
|
91
|
+
|
|
92
|
+
Built-in column domains decode to their declared types, including bigint,
|
|
93
|
+
`Uint8Array`, `Date`, boolean, and nested JSON. Qubu transports precision-sensitive
|
|
94
|
+
values as text and rejects numbers that lose significant decimal digits or
|
|
95
|
+
exceed JavaScript's safe integer range. Use bigint columns for exact large
|
|
96
|
+
integers.
|
|
97
|
+
|
|
98
|
+
Unknown or custom SQL domains need a supported explicit cast, for
|
|
99
|
+
example `cast(value(7), integer())`; declaring a TypeScript result alone does
|
|
100
|
+
not provide runtime decoding information.
|
|
101
|
+
|
|
102
|
+
Custom `mapResult()` and column decoders receive the JSON transport value as
|
|
103
|
+
`unknown`. The value may be:
|
|
104
|
+
|
|
105
|
+
- A bigint or decimal string.
|
|
106
|
+
- A hexadecimal binary string.
|
|
107
|
+
- A serialized JSON string.
|
|
108
|
+
- An ordinary JSON scalar.
|
|
109
|
+
|
|
110
|
+
The custom decoder converts it to the declared application type. Adapter-wide
|
|
111
|
+
decoders do not run inside nested objects.
|
|
112
|
+
|
|
113
|
+
Keep arbitrary stored JSON within JavaScript’s numeric precision;
|
|
114
|
+
unsupported numeric representations fail instead of silently rounding.
|
|
115
|
+
|
|
116
|
+
## Read stored JSON scalars
|
|
117
|
+
|
|
118
|
+
Use a structured `jsonPath()` when a query needs a scalar or an existence check
|
|
6
119
|
inside a JSON document:
|
|
7
120
|
|
|
8
121
|
```ts
|
|
@@ -42,7 +155,7 @@ interpolating caller-provided SQL.
|
|
|
42
155
|
## Understand missing values
|
|
43
156
|
|
|
44
157
|
Scalar reads return SQL NULL when the path is missing, contains JSON null, or
|
|
45
|
-
resolves to another JSON scalar type. jsonExists() returns true for a present
|
|
158
|
+
resolves to another JSON scalar type. `jsonExists()` returns true for a present
|
|
46
159
|
JSON null, false for a missing path, and false when the document itself is SQL
|
|
47
160
|
NULL.
|
|
48
161
|
|
|
@@ -50,16 +163,21 @@ These rules keep path existence separate from extraction nullability.
|
|
|
50
163
|
|
|
51
164
|
## Check dialect support
|
|
52
165
|
|
|
53
|
-
The standard dialect emits SQL/JSON JSON_VALUE and JSON_EXISTS syntax.
|
|
166
|
+
The standard dialect emits SQL/JSON `JSON_VALUE` and `JSON_EXISTS` syntax.
|
|
54
167
|
PostgreSQL, MySQL, and SQLite use their native JSON policies. The current
|
|
55
168
|
policies require PostgreSQL 12 or newer, MySQL 8.0.21 or newer, and SQLite JSON
|
|
56
169
|
functions. An application-created dialect must provide a JSON renderer.
|
|
57
170
|
|
|
58
171
|
## Know the current limits
|
|
59
172
|
|
|
60
|
-
JSON paths
|
|
61
|
-
|
|
62
|
-
|
|
173
|
+
JSON paths follow explicit keys and indexes. These features require
|
|
174
|
+
dialect-specific extensions:
|
|
175
|
+
|
|
176
|
+
- Wildcards and filters.
|
|
177
|
+
- Recursive descent.
|
|
178
|
+
- Extraction that returns JSON.
|
|
179
|
+
- Document mutation.
|
|
180
|
+
- Row expansion.
|
|
63
181
|
|
|
64
182
|
For the SQL domain and nullability rules behind JSON columns, read
|
|
65
183
|
[SQL semantic types](../sql-semantic-types.md).
|
package/docs/guides/mutations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Write mutations
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Insert, update, and delete rows with typed inputs and explicit safeguards.
|
|
4
4
|
|
|
5
5
|
## Define write-time rules
|
|
6
6
|
|
|
@@ -121,6 +121,8 @@ const query = update(
|
|
|
121
121
|
The assignment expression is source-aware, so a column from an unrelated table
|
|
122
122
|
cannot silently enter the update.
|
|
123
123
|
|
|
124
|
+
### Update from another source
|
|
125
|
+
|
|
124
126
|
PostgreSQL updates can introduce one or more typed sources with `updateFrom()`.
|
|
125
127
|
Those sources are available to assignments, the predicate, and `RETURNING`:
|
|
126
128
|
|
|
@@ -153,6 +155,8 @@ with the default, SQLite, or MySQL dialect is rejected. Qubu still requires a
|
|
|
153
155
|
predicate or an explicit `allowAll()` marker; introducing a source does not
|
|
154
156
|
authorize an unrestricted update.
|
|
155
157
|
|
|
158
|
+
### Omit an assignment at runtime
|
|
159
|
+
|
|
156
160
|
Use `omit` for a runtime-conditional assignment. Qubu removes omitted fields
|
|
157
161
|
before validating and rendering the effective assignment set:
|
|
158
162
|
|
|
@@ -171,11 +175,49 @@ const query = update(
|
|
|
171
175
|
|
|
172
176
|
`omit` means that the column is absent from `SET`. It is distinct from `null`
|
|
173
177
|
and explicit `undefined`, which remain bound assignment values, and it does not
|
|
174
|
-
emit SQL `DEFAULT`.
|
|
178
|
+
emit SQL `DEFAULT`.
|
|
179
|
+
|
|
180
|
+
At least one assignment must remain; `update()` throws
|
|
175
181
|
before rendering when every field is omitted. Possible expression branches
|
|
176
182
|
remain source- and capability-aware even when their runtime alternative is
|
|
177
183
|
`omit`.
|
|
178
184
|
|
|
185
|
+
## Update duplicate keys in MySQL
|
|
186
|
+
|
|
187
|
+
Use `onDuplicateKeyUpdate()` from `qubu/mysql` to update a row when an insert
|
|
188
|
+
conflicts with any primary or unique key. MySQL chooses the conflicting key;
|
|
189
|
+
this clause has no conflict-target argument or `RETURNING` support.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { insertInto, values } from "qubu"
|
|
193
|
+
import { incoming, onDuplicateKeyUpdate } from "qubu/mysql"
|
|
194
|
+
|
|
195
|
+
const proposed = incoming(users)
|
|
196
|
+
const query = insertInto(
|
|
197
|
+
users,
|
|
198
|
+
values({ name: "Ada", email: "ada@example.com" }),
|
|
199
|
+
onDuplicateKeyUpdate(users, { name: proposed.name }),
|
|
200
|
+
)
|
|
201
|
+
await db.execute(query)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Declare a unique key on `email` in the database for this example. A duplicate
|
|
205
|
+
email replaces the existing name with the proposed name. Assignments accept
|
|
206
|
+
writable target columns, target-table expressions, `incoming(table)` columns,
|
|
207
|
+
or `omit`. Raw values retain the target column's parameter encoder. At least
|
|
208
|
+
one assignment must remain after omissions.
|
|
209
|
+
|
|
210
|
+
Incoming references are available only inside the matching table's duplicate-key
|
|
211
|
+
assignments. Qubu renders MySQL row aliases for `values()` and `defaultValues()`.
|
|
212
|
+
For `insertSelect()`, it wraps the query in a derived projection and maps its
|
|
213
|
+
fields positionally to the target-column list. Incoming references then cover
|
|
214
|
+
only that list; referencing an omitted target column fails during rendering.
|
|
215
|
+
This syntax requires MySQL 8.0.19 or later.
|
|
216
|
+
|
|
217
|
+
The result retains mysql2's `affectedRows`, `changedRows`, and `insertId` metadata
|
|
218
|
+
when supplied by the driver. These facts do not identify which branch ran for
|
|
219
|
+
every row of a batch. Read rows with a separate query when needed.
|
|
220
|
+
|
|
179
221
|
## Delete with a predicate
|
|
180
222
|
|
|
181
223
|
```ts
|
|
@@ -201,11 +243,14 @@ unrestricted operation is intended.
|
|
|
201
243
|
|
|
202
244
|
## Return typed rows
|
|
203
245
|
|
|
204
|
-
`returning()` uses the same named object projection as `SELECT`.
|
|
205
|
-
`{ ...all(table) }`
|
|
206
|
-
|
|
246
|
+
`returning()` uses the same named object projection as `SELECT`. Use
|
|
247
|
+
`{ ...all(table) }` when you want every table column.
|
|
248
|
+
|
|
249
|
+
The mutation’s `row` type is inferred from that projection, so
|
|
207
250
|
`(await db.execute(query)).rows` has the same shape as a read query when `db`
|
|
208
|
-
comes from `qubu(adapter)`.
|
|
251
|
+
comes from `qubu(adapter)`.
|
|
252
|
+
|
|
253
|
+
The projection’s SQL semantic domains are
|
|
209
254
|
retained too, so a returned query used by typed composition does not collapse
|
|
210
255
|
UUID, text, numeric, or other known fields to their JavaScript types alone.
|
|
211
256
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Add optional conditions
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
> the query keeps the same structural shape.
|
|
3
|
+
> Add or omit conditions at runtime, and handle NULL values and empty lists.
|
|
5
4
|
|
|
6
5
|
The examples use the `users` table from [Build a `SELECT`](overview.md).
|
|
7
6
|
|
|
@@ -65,15 +64,23 @@ Here `omit` affects only whether `email` belongs to the projection. It does
|
|
|
65
64
|
not make the expression nullable: a non-nullable expression would produce
|
|
66
65
|
`email?: string`, while this nullable column produces `email?: string | null`.
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
### Where omission is supported
|
|
68
|
+
|
|
69
|
+
Use `omit` in the clauses and expression lists shown above. Generic
|
|
70
|
+
`sequence()` and `commaSeparated()` collections do not discard it.
|
|
71
|
+
|
|
72
|
+
Pagination also supports `omit`: pair it with `offset()`, `fetchFirst()`, or
|
|
73
|
+
`fetchNext()`. A conditional limit keeps the inferred cardinality at `many`
|
|
74
|
+
because the limit may be absent.
|
|
75
|
+
|
|
76
|
+
Build separate queries when any of these parts differ at runtime. They cannot
|
|
77
|
+
be paired with `omit`:
|
|
78
|
+
|
|
79
|
+
- `from()` or joins.
|
|
80
|
+
- `groupBy()`.
|
|
81
|
+
- Correlation.
|
|
82
|
+
- CTEs.
|
|
83
|
+
- Custom clauses.
|
|
77
84
|
|
|
78
85
|
## Handle `NULL` and empty lists deliberately
|
|
79
86
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Group and rank rows
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Summarize rows with aggregates and rank them with window functions.
|
|
4
4
|
|
|
5
5
|
The examples use the `users` and `posts` tables from [Build a
|
|
6
6
|
`SELECT`](overview.md).
|
|
@@ -28,6 +28,7 @@ const counts = select(
|
|
|
28
28
|
|
|
29
29
|
`users.name` is grouped, while `posts.id` is consumed by `COUNT()`. The
|
|
30
30
|
same dependency rule applies to `HAVING` and grouped `ORDER BY` expressions.
|
|
31
|
+
|
|
31
32
|
A projection such as `{ email: users.email, postCount: count(posts.id) }` is
|
|
32
33
|
rejected unless `users.email` is grouped or is functionally determined by a
|
|
33
34
|
grouped primary or unique key declared in the table schema. Qubu uses only
|
|
@@ -58,7 +59,9 @@ const rankedUsers = select(
|
|
|
58
59
|
Window expressions remain ordinary expressions. They can be projected, aliased,
|
|
59
60
|
and passed to `orderBy()`. Their source requirements and result types are
|
|
60
61
|
retained through `over()`, and values rendered inside the window
|
|
61
|
-
specification remain parameters.
|
|
62
|
+
specification remain parameters.
|
|
63
|
+
|
|
64
|
+
Named windows and frame clauses are outside
|
|
62
65
|
the initial inline scope.
|
|
63
66
|
|
|
64
67
|
## Read next
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Order and paginate
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Sort results and limit the rows a query returns.
|
|
4
4
|
|
|
5
5
|
The examples use the `users` table from [Build a `SELECT`](overview.md).
|
|
6
6
|
|
|
@@ -34,10 +34,12 @@ const page = select(
|
|
|
34
34
|
)
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
The rendered
|
|
38
|
-
pagination. The active dialect decides whether pagination uses standard
|
|
37
|
+
The rendered clauses follow SQL order: `FROM` comes before `WHERE`, followed
|
|
38
|
+
by `ORDER BY` and pagination. The active dialect decides whether pagination uses standard
|
|
39
39
|
`FETCH` syntax or a driver-specific `LIMIT` form.
|
|
40
40
|
|
|
41
|
+
### Make the limit optional
|
|
42
|
+
|
|
41
43
|
Pair a pagination clause with `omit` when the row bound is optional at runtime:
|
|
42
44
|
|
|
43
45
|
```ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Build a `SELECT`
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Choose fields, add a source, and join tables to build a typed SELECT query.
|
|
4
4
|
|
|
5
5
|
## Start with a projection and a source
|
|
6
6
|
|
|
@@ -24,8 +24,7 @@ render(query).text
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
An object projection uses its keys as result names. Name the fields you intend
|
|
27
|
-
to return in the usual case.
|
|
28
|
-
intentionally returns every source column. It returns the source's columns as a
|
|
27
|
+
to return in the usual case. Use `all(source)` when you want every source column. It returns the source's columns as a
|
|
29
28
|
named projection object, so it can still be spread alongside computed
|
|
30
29
|
expressions:
|
|
31
30
|
|
|
@@ -79,6 +78,8 @@ Use `innerJoin`, `leftJoin`, `rightJoin`, or `fullJoin` with an `ON`
|
|
|
79
78
|
condition. `crossJoin` and `naturalJoin` add a source without a condition;
|
|
80
79
|
use them only when that SQL behavior is intentional.
|
|
81
80
|
|
|
81
|
+
### Handle missing joined rows
|
|
82
|
+
|
|
82
83
|
`leftJoin()` also carries nullability into the selected row. A column from the
|
|
83
84
|
joined source is nullable because the row may be missing, while an expression
|
|
84
85
|
with a deliberately non-nullable result such as `count()` remains non-null:
|
|
@@ -98,6 +99,8 @@ const summary = select(
|
|
|
98
99
|
// { userName: string; postTitle: string | null; postCount: number }
|
|
99
100
|
```
|
|
100
101
|
|
|
102
|
+
### Add conditions
|
|
103
|
+
|
|
101
104
|
Compose boolean expressions explicitly:
|
|
102
105
|
|
|
103
106
|
```ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Compose SQL templates
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Write SQL templates that bind values safely and preserve type information from Qubu expressions.
|
|
4
4
|
|
|
5
5
|
## Bind every runtime value
|
|
6
6
|
|
|
@@ -108,10 +108,16 @@ reaches `unsafeExpression()`.
|
|
|
108
108
|
|
|
109
109
|
## Preserve metadata through interpolated fragments
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
Qubu fragment substitutions pass these facts to the template:
|
|
112
|
+
|
|
113
|
+
- Required sources.
|
|
114
|
+
- Possible nulls from outer joins.
|
|
115
|
+
- Grouping dependencies.
|
|
116
|
+
- Aggregate and window state.
|
|
117
|
+
- Subquery state.
|
|
118
|
+
- Required dialect capabilities.
|
|
119
|
+
|
|
120
|
+
Qubu does not infer these facts from template text.
|
|
115
121
|
|
|
116
122
|
Use a built-in expression as the substitution when its semantics matter:
|
|
117
123
|
|
|
@@ -102,11 +102,17 @@ const handlers = applyOpsWithQubu<SyncContext>({
|
|
|
102
102
|
export const sync = valtioSync({ schema: { todos }, handlers })
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
Each mutation runs these steps in one Qubu transaction:
|
|
106
|
+
|
|
107
|
+
1. Check authorization.
|
|
108
|
+
2. Check conflicts.
|
|
109
|
+
3. Apply the application mutation.
|
|
110
|
+
4. Write the sync event with `syncEvents.write()`.
|
|
111
|
+
|
|
112
|
+
If any step fails, the adapter rolls the transaction back.
|
|
113
|
+
|
|
114
|
+
The event sequence becomes `serverVersion` unless the mutation handler returns
|
|
115
|
+
an explicit version. Read handlers pass through unchanged.
|
|
110
116
|
|
|
111
117
|
The integration does not define persistence tables, import Drizzle, or execute
|
|
112
118
|
driver APIs. The application owns table design, authorization, conflict policy,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vite compiler hint
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Use a "use qubu" directive to add the Qubu imports a module needs.
|
|
4
4
|
|
|
5
5
|
The optional Vite plugin recognizes the `"use qubu"` directive and injects only
|
|
6
6
|
the referenced named imports from the configured module.
|
|
@@ -33,7 +33,7 @@ ambient value and type declarations for the TypeScript compiler.
|
|
|
33
33
|
|
|
34
34
|
## Mark a module explicitly
|
|
35
35
|
|
|
36
|
-
Put the directive
|
|
36
|
+
Put the directive at the start of the module, alongside any other directives:
|
|
37
37
|
|
|
38
38
|
```ts
|
|
39
39
|
"use qubu"
|
package/docs/index.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# Qubu
|
|
2
2
|
|
|
3
|
-
> Build
|
|
3
|
+
> Build SQL queries from typed tables and reusable values.
|
|
4
4
|
|
|
5
|
-
Qubu builds SQL from values.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Qubu builds SQL from reusable values. You can combine query parts without
|
|
6
|
+
changing a shared query-builder object.
|
|
7
|
+
|
|
8
|
+
TypeScript checks:
|
|
9
|
+
|
|
10
|
+
- Which fields the query returns and their types.
|
|
11
|
+
- Whether each column belongs to a source in the query.
|
|
12
|
+
- Whether a result can be `null`.
|
|
13
|
+
|
|
14
|
+
Render a query to inspect its SQL text and ordered parameters.
|
|
9
15
|
|
|
10
16
|
The preferred source style names each projected field and writes the final
|
|
11
17
|
`select()` clauses in SQL order. Clause values remain order-independent at
|
|
@@ -17,7 +23,7 @@ and placed in that final call where it reads best.
|
|
|
17
23
|
If this is your first query, follow [Getting started](getting-started.md) to
|
|
18
24
|
define a table, build a `SELECT`, and inspect its SQL and parameters.
|
|
19
25
|
|
|
20
|
-
##
|
|
26
|
+
## Build and run queries
|
|
21
27
|
|
|
22
28
|
- [Build a `SELECT`](guides/select/overview.md) with projections, joins,
|
|
23
29
|
predicates, ordering, and grouping.
|
|
@@ -33,21 +39,21 @@ define a table, build a `SELECT`, and inspect its SQL and parameters.
|
|
|
33
39
|
derivation and a native transactional database adapter.
|
|
34
40
|
- [Extend Qubu](guides/extensions/overview.md) with a custom source, clause,
|
|
35
41
|
dialect policy, or typed expression.
|
|
36
|
-
- [
|
|
42
|
+
- [Query nested JSON](guides/json.md) or read scalars from structured paths.
|
|
37
43
|
- [Enable the Vite compiler hint](guides/vite-plugin.md) when query modules
|
|
38
44
|
should opt into named imports through a directive.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
|
|
46
|
+
## Work with schemas and migrations
|
|
47
|
+
|
|
48
|
+
- [Inspect an existing database](schema/introspection.md) using a connection you provide.
|
|
49
|
+
- [Generate a schema module](schema/code-generation.md) from an introspection result without losing schema facts.
|
|
43
50
|
- [Compare snapshots](schema/diff.md) with explicit rename hints and reviewable
|
|
44
51
|
safety diagnostics.
|
|
45
52
|
- [Build migration plans](schema/migration-plans.md) as reviewed, deterministic
|
|
46
53
|
data before DDL emission.
|
|
47
54
|
- [Emit DDL](schema/ddl-emission.md) from an approved migration plan without
|
|
48
55
|
handing Qubu a database connection.
|
|
49
|
-
- [Operate migrations](migrations/index.md) with
|
|
50
|
-
adapter profiles, baselines, a portable executor, and explicit recovery.
|
|
56
|
+
- [Operate migrations](migrations/index.md) with reviewed migration files and a verified adapter.
|
|
51
57
|
|
|
52
58
|
## The query pipeline
|
|
53
59
|
|
|
@@ -106,8 +112,9 @@ render(query)
|
|
|
106
112
|
The inferred row is `{ id: number; name: string }`. The value `7` stays out
|
|
107
113
|
of the SQL text and appears in the `parameters` array in placeholder order.
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
starts from common errors and
|
|
115
|
+
## Find support details
|
|
116
|
+
|
|
117
|
+
- [Supported features](reference/supported-surface.md) lists package imports and
|
|
118
|
+
explains which responsibilities stay with your application.
|
|
119
|
+
- [Troubleshooting](troubleshooting.md) starts from common errors and explains
|
|
120
|
+
how to fix them.
|