qubu 0.6.2 → 0.7.0
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/codegen.d.mts +1 -1
- package/dist/codegen.mjs +1 -1
- package/dist/core.d.mts +1 -1
- package/dist/diff.d.mts +1 -1
- package/dist/index-DB07P2X8.d.mts +2 -0
- package/dist/index.d.mts +1 -1
- package/dist/introspection/mysql.d.mts +1 -1
- package/dist/introspection/postgres.d.mts +1 -1
- package/dist/introspection/sqlite.d.mts +1 -1
- package/dist/introspection.d.mts +3 -2
- package/dist/introspection.mjs +1 -1
- package/dist/mysql.d.mts +2 -2
- package/dist/postgres.d.mts +2 -2
- package/dist/schema.d.mts +1 -1
- package/dist/{serialize-Du2UPZMt.d.mts → serialize-Dg-Q55YQ.d.mts} +1 -1
- package/dist/snapshot/mysql.d.mts +2 -2
- package/dist/snapshot/postgres.d.mts +2 -2
- package/dist/snapshot/sqlite.d.mts +2 -2
- package/dist/{snapshot-mIb-Zzb5.mjs → snapshot-C1x2R-iK.mjs} +25 -9
- package/dist/snapshot.d.mts +3 -3
- package/dist/sqlite.d.mts +2 -2
- package/dist/{types-CiMvKi5V.d.mts → types-CdcYI0lU.d.mts} +1 -1
- package/dist/{types-Dqr4o2I1.d.mts → types-DcIiNePM.d.mts} +7 -0
- package/docs/dialects-and-execution.md +54 -25
- package/docs/getting-started.md +9 -9
- 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 +25 -13
- package/docs/guides/extensions/unsafe-syntax.md +10 -6
- package/docs/guides/json.md +52 -27
- package/docs/guides/mutations.md +15 -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 +24 -17
- package/docs/migrations/adapters.md +58 -22
- package/docs/migrations/adopt.md +331 -0
- package/docs/migrations/artifacts-and-policy.md +49 -20
- package/docs/migrations/index.md +18 -9
- package/docs/migrations/operations.md +60 -70
- package/docs/migrations/recovery.md +34 -17
- package/docs/query-model/fragments.md +13 -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 +26 -19
- 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 -85
- package/docs/schema/catalog-model.md +24 -7
- 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 +12 -4
- package/docs/schema/introspection.md +47 -21
- package/docs/schema/migration-plans.md +18 -10
- package/docs/schema/snapshots.md +57 -29
- package/docs/schema/storage-and-schema-sql.md +10 -4
- 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 +1 -1
- package/dist/index-CaxrMD1A.d.mts +0 -1
- package/docs/migrations/lotta-adoption.md +0 -50
package/docs/getting-started.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Getting started
|
|
2
2
|
|
|
3
|
-
> Define a
|
|
3
|
+
> Define a table and inspect your first query’s SQL and parameters.
|
|
4
4
|
|
|
5
5
|
## Install Qubu
|
|
6
6
|
|
|
@@ -13,10 +13,9 @@ pnpm add qubu
|
|
|
13
13
|
Import query-building functions from the package root. Qubu does not need a
|
|
14
14
|
database connection to construct or render a query.
|
|
15
15
|
|
|
16
|
-
The examples
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
keeping the final call in SQL order makes the query easy to scan and repair.
|
|
16
|
+
The examples write clauses in SQL order so the query is easy to scan.
|
|
17
|
+
`select()` also accepts independent clauses in any order and puts them in SQL
|
|
18
|
+
order when rendering.
|
|
20
19
|
|
|
21
20
|
## Define a table
|
|
22
21
|
|
|
@@ -38,10 +37,9 @@ nullable email column is inferred as `string | null` when selected.
|
|
|
38
37
|
|
|
39
38
|
## Build and render a query
|
|
40
39
|
|
|
41
|
-
Pass
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`users` table from the previous section.
|
|
40
|
+
Pass the fields you want to return as a named object, called the projection.
|
|
41
|
+
Then add the clauses. This example uses the `users` table from the previous
|
|
42
|
+
section.
|
|
45
43
|
|
|
46
44
|
```ts
|
|
47
45
|
import { eq, from, render, select, where } from "qubu"
|
|
@@ -69,6 +67,8 @@ statement.parameters
|
|
|
69
67
|
// [7]
|
|
70
68
|
```
|
|
71
69
|
|
|
70
|
+
### Inspect the result type
|
|
71
|
+
|
|
72
72
|
The selected row type is available on the query value:
|
|
73
73
|
|
|
74
74
|
```ts
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Better Auth
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Define auth tables with Qubu and connect Better Auth to a transactional Qubu client.
|
|
4
|
+
|
|
5
|
+
## Install the integration
|
|
4
6
|
|
|
5
7
|
Install the integration next to Qubu and Better Auth:
|
|
6
8
|
|
|
@@ -8,10 +10,15 @@ Install the integration next to Qubu and Better Auth:
|
|
|
8
10
|
pnpm add qubu @qubu/better-auth better-auth
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
Define the
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
## Define the auth schema
|
|
14
|
+
|
|
15
|
+
Define the Better Auth options once. Qubu derives its schema from Better Auth’s
|
|
16
|
+
resolved public metadata, including:
|
|
17
|
+
|
|
18
|
+
- Core tables and renamed models or fields.
|
|
19
|
+
- Additional fields and plugin tables.
|
|
20
|
+
- References and unique constraints.
|
|
21
|
+
- Compound indexes.
|
|
15
22
|
|
|
16
23
|
```ts
|
|
17
24
|
import { betterAuth } from "better-auth"
|
|
@@ -44,6 +51,8 @@ export const auth = betterAuth({
|
|
|
44
51
|
migration-plan, and DDL workflows. The adapter's Better Auth `createSchema`
|
|
45
52
|
hook emits a TypeScript module that reconstructs the same Qubu-owned metadata.
|
|
46
53
|
|
|
54
|
+
## Database requirements
|
|
55
|
+
|
|
47
56
|
The package never imports PostgreSQL, MySQL, or SQLite drivers. It executes
|
|
48
57
|
through Qubu's query and transaction boundaries. PostgreSQL and SQLite use one
|
|
49
58
|
limited mutation statement for atomic consume and guarded increment operations;
|
|
@@ -51,6 +60,8 @@ MySQL locks one selected row inside the Qubu-owned transaction. A client without
|
|
|
51
60
|
transaction support, or a dialect other than PostgreSQL, MySQL, or SQLite, is
|
|
52
61
|
rejected during adapter construction.
|
|
53
62
|
|
|
63
|
+
## Enum limitation
|
|
64
|
+
|
|
54
65
|
Better Auth enum metadata is currently rejected because Qubu cannot preserve
|
|
55
66
|
the closed value set as a portable column without adding a database constraint.
|
|
56
67
|
The error includes the model and field path instead of silently widening it to
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Compose queries
|
|
2
2
|
|
|
3
|
-
> Reuse a query
|
|
3
|
+
> Reuse a query as a CTE, a derived table, or a subquery, and combine query results.
|
|
4
4
|
|
|
5
5
|
## Turn a query into a CTE
|
|
6
6
|
|
|
@@ -28,6 +28,8 @@ The rendered statement includes the `WITH` clause before `SELECT`. Selected
|
|
|
28
28
|
camelCase keys use snake_case while they belong to the CTE relation; the outer
|
|
29
29
|
result projection aliases them back to camelCase for the returned row.
|
|
30
30
|
|
|
31
|
+
### Use a CTE in a mutation
|
|
32
|
+
|
|
31
33
|
Attach the same clause to an insert, update, or delete when the mutation reads
|
|
32
34
|
through the CTE. For example, an insert can consume a filtered CTE through
|
|
33
35
|
`insertSelect()`:
|
|
@@ -66,11 +68,20 @@ const numbers = recursiveCte("numbers", select({ value: cast(value(1), integer()
|
|
|
66
68
|
const query = select({ value: numbers.value }, withCte(numbers), from(numbers))
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
The anchor
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
The anchor defines the fields the CTE returns:
|
|
72
|
+
|
|
73
|
+
- Field names.
|
|
74
|
+
- Application types.
|
|
75
|
+
- Nullability.
|
|
76
|
+
- SQL domains.
|
|
77
|
+
|
|
78
|
+
The recursive member must select the same fields with compatible types.
|
|
79
|
+
|
|
80
|
+
Give bound anchor values an explicit SQL type with
|
|
72
81
|
`cast()` when the database cannot infer it from surrounding columns; PostgreSQL
|
|
73
|
-
requires this for recursive CTE anchors.
|
|
82
|
+
requires this for recursive CTE anchors.
|
|
83
|
+
|
|
84
|
+
Qubu renders `WITH RECURSIVE`, an
|
|
74
85
|
explicit relation column list, and `anchor UNION ALL member`; ordinary and
|
|
75
86
|
recursive CTEs can share one `withCte()` clause.
|
|
76
87
|
|
|
@@ -117,7 +128,9 @@ const query = select(
|
|
|
117
128
|
`scalar()` throws at runtime when the query selects more than one field. Its
|
|
118
129
|
type is the selected field's value type, widened with `null` when the query may
|
|
119
130
|
return no rows. An ordinary select and `fetchFirst(1)` are both nullable: the
|
|
120
|
-
limit proves at most one row, not that a row exists.
|
|
131
|
+
limit proves at most one row, not that a row exists.
|
|
132
|
+
|
|
133
|
+
A source-free select such
|
|
121
134
|
as `select({ value: value(42) })` is known to produce exactly one row.
|
|
122
135
|
|
|
123
136
|
Qubu does not treat an arbitrary predicate as proof of exactness. Use
|
|
@@ -163,9 +176,8 @@ code.
|
|
|
163
176
|
## Constrain a reusable fragment by required fields
|
|
164
177
|
|
|
165
178
|
Use `TableLike` when a fragment requires a physical table and `SourceLike`
|
|
166
|
-
when aliases, CTEs, derived tables, or custom sources are also valid. Both
|
|
167
|
-
|
|
168
|
-
generic function retains its exact source identity.
|
|
179
|
+
when aliases, CTEs, derived tables, or custom sources are also valid. Both allow the source to contain additional fields. The generic function
|
|
180
|
+
retains the source’s exact identity.
|
|
169
181
|
|
|
170
182
|
For an application-level requirement, describe the required JavaScript row:
|
|
171
183
|
|
package/docs/guides/drizzle.md
CHANGED
|
@@ -123,9 +123,14 @@ override.
|
|
|
123
123
|
## Runtime metadata
|
|
124
124
|
|
|
125
125
|
Each dialect adapter maps Qubu storage descriptors to its own Drizzle builders.
|
|
126
|
-
It also transfers
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
It also transfers:
|
|
127
|
+
|
|
128
|
+
- Concrete defaults and generated expressions.
|
|
129
|
+
- Common primary and unique constraints.
|
|
130
|
+
- Checks and foreign keys.
|
|
131
|
+
- Indexes.
|
|
132
|
+
|
|
133
|
+
Native storage must belong to the selected dialect:
|
|
129
134
|
|
|
130
135
|
```ts
|
|
131
136
|
import { nativeColumn, schema, table } from "qubu"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Add a dialect policy
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Customize how Qubu renders SQL for your driver.
|
|
4
4
|
|
|
5
5
|
Use `createDialect()` when the query is portable but the driver changes
|
|
6
6
|
identifiers, placeholders, or pagination:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Extend Qubu
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Add SQL features that Qubu’s built-in helpers do not cover.
|
|
4
4
|
|
|
5
5
|
Qubu extensions are values that render SQL and carry the metadata later
|
|
6
6
|
composition needs. Choose the page that matches the thing you are adding:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Add sources and clauses
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Add a SQL clause or table-like source that works with Qubu’s query checks.
|
|
4
4
|
|
|
5
5
|
## Add a custom clause
|
|
6
6
|
|
|
@@ -67,9 +67,13 @@ render(query)
|
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
`identity` is the source-scope key; `reference` is the SQL qualifier used by
|
|
70
|
-
the generated columns.
|
|
70
|
+
the generated columns.
|
|
71
|
+
|
|
72
|
+
A nullable column remains nullable, and a
|
|
71
73
|
`leftJoin(rows, ...)` adds outer-join nullability to every selected row
|
|
72
|
-
column.
|
|
74
|
+
column.
|
|
75
|
+
|
|
76
|
+
Render the complete relation in the producer and bind values with
|
|
73
77
|
`context.parameter()`; the normal renderer preserves parameter order.
|
|
74
78
|
|
|
75
79
|
## Read next
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Add typed expressions
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Build custom expressions that preserve result types and query checks.
|
|
4
4
|
|
|
5
5
|
## Build expressions from public helpers
|
|
6
6
|
|
|
@@ -47,13 +47,21 @@ const nameAsCitext = cast(users.name, citext)
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
The first three `column` type arguments are output, insert, and update values;
|
|
50
|
-
the fourth is the SQL domain.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
the fourth is the SQL domain.
|
|
51
|
+
|
|
52
|
+
The `text` equality and ordering groups make the custom domain compatible with `SqlText`. Use a distinct group when cross-type
|
|
53
|
+
comparison is not portable.
|
|
54
|
+
|
|
55
|
+
### Use the definition as a cast target
|
|
56
|
+
|
|
57
|
+
`castType` also makes this definition a cast target. Its SQL text is emitted
|
|
58
|
+
unchanged, so keep it in trusted extension code.
|
|
59
|
+
|
|
54
60
|
Definitions with schema flags are not accepted as cast targets because cast
|
|
55
61
|
nullability comes from the operand and write flags have no cast meaning.
|
|
56
62
|
|
|
63
|
+
### Type individual expressions
|
|
64
|
+
|
|
57
65
|
Declare result domains at other extension boundaries too:
|
|
58
66
|
|
|
59
67
|
```ts
|
|
@@ -66,13 +74,17 @@ const rawNameAsText = typedCast<string, SqlText>()(users.name, "TEXT")
|
|
|
66
74
|
const generated = unsafeExpression<string, SqlText>("custom_text()")
|
|
67
75
|
```
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
Choose the helper for the operation:
|
|
78
|
+
|
|
79
|
+
- `typedCall()` preserves source requirements from its arguments.
|
|
80
|
+
- `typedCast()` supplies a cast target when no reusable definition describes
|
|
81
|
+
it. It preserves operand nullability and source metadata, and emits the
|
|
82
|
+
supplied type name unchanged.
|
|
83
|
+
- `typedValue()` binds a parameter and declares its runtime SQL domain for
|
|
84
|
+
the adapter. It does not choose a JavaScript result decoder; schema columns
|
|
85
|
+
carry decoder metadata separately.
|
|
86
|
+
- `unsafeExpression()` emits its string unchanged. Use it only when the other
|
|
87
|
+
helpers cannot express the syntax.
|
|
76
88
|
|
|
77
89
|
The lower-level forms also expose the SQL domain in their generic lists:
|
|
78
90
|
`call<Output, Name, Arguments, NullableFrom, SqlType>()` and
|
|
@@ -80,7 +92,7 @@ The lower-level forms also expose the SQL domain in their generic lists:
|
|
|
80
92
|
argument or nullability types in its own generic signature.
|
|
81
93
|
|
|
82
94
|
Untyped `column()`, `value()`, `call()`, and custom expressions use
|
|
83
|
-
`SqlUnknown`, which
|
|
95
|
+
`SqlUnknown`, which allows composition without SQL-domain checks. Declaring a
|
|
84
96
|
known domain opts the extension into incompatible-operation errors. See
|
|
85
97
|
[SQL semantic types](../../sql-semantic-types.md) for the capability model and
|
|
86
98
|
its limits.
|
|
@@ -14,12 +14,16 @@ const query = select({
|
|
|
14
14
|
})
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`
|
|
17
|
+
## Choose the right helper
|
|
18
|
+
|
|
19
|
+
Keep raw identifiers and values out of the string:
|
|
20
|
+
|
|
21
|
+
- Use a typed custom fragment for syntax you will reuse.
|
|
22
|
+
- Use the [`sql` template tag](../sql-templates.md) for fixed trusted syntax
|
|
23
|
+
with bound values or existing Qubu fragments.
|
|
24
|
+
- Use `unsafeExpression()` for trusted dynamic SQL text.
|
|
25
|
+
- Use `identifier()` or `qualifiedIdentifier()` from `qubu/core` for runtime
|
|
26
|
+
identifiers.
|
|
23
27
|
|
|
24
28
|
Read [Dialects and execution](../../dialects-and-execution.md) for the boundary
|
|
25
29
|
between rendering and driver behavior. Read [Add typed
|
package/docs/guides/json.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
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
|
|
6
8
|
proven to return at most one row. Both preserve filtering, correlation,
|
|
7
9
|
ordering, and pagination:
|
|
8
10
|
|
|
@@ -57,45 +59,63 @@ const query = select(
|
|
|
57
59
|
// latestPost: { title: string } | null }
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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.
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
FROM/JOIN scope remain checked at every level.
|
|
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.
|
|
69
72
|
|
|
70
|
-
|
|
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.
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
### Preserve ordering
|
|
78
|
+
|
|
79
|
+
Nested arrays retain explicit `ORDER BY` and pagination. Tied sort keys retain
|
|
73
80
|
SQL's unspecified tie order; add a unique tie-breaker when order matters.
|
|
74
|
-
DISTINCT ordering must use the same expressions as the selection. Without an
|
|
75
|
-
ORDER BY
|
|
81
|
+
`DISTINCT` ordering must use the same expressions as the selection. Without an
|
|
82
|
+
`ORDER BY`, array order is unspecified.
|
|
83
|
+
|
|
84
|
+
### Supported databases
|
|
76
85
|
|
|
77
86
|
Nested results support PostgreSQL, MySQL 8.0.21+, and SQLite 3.45+. Other
|
|
78
87
|
dialects fail during rendering. SQLite's minimum includes the JSON aggregate
|
|
79
88
|
ordering fix needed to retain object values.
|
|
80
89
|
|
|
90
|
+
### Decode nested values
|
|
91
|
+
|
|
81
92
|
Built-in column domains decode to their declared types, including bigint,
|
|
82
|
-
Uint8Array
|
|
93
|
+
`Uint8Array`, `Date`, boolean, and nested JSON. Qubu transports precision-sensitive
|
|
83
94
|
values as text and rejects numbers that lose significant decimal digits or
|
|
84
95
|
exceed JavaScript's safe integer range. Use bigint columns for exact large
|
|
85
|
-
integers.
|
|
86
|
-
|
|
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
|
|
87
100
|
not provide runtime decoding information.
|
|
88
101
|
|
|
89
|
-
Custom mapResult() and column decoders receive the JSON transport value as
|
|
90
|
-
unknown
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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;
|
|
94
114
|
unsupported numeric representations fail instead of silently rounding.
|
|
95
115
|
|
|
96
116
|
## Read stored JSON scalars
|
|
97
117
|
|
|
98
|
-
Use a structured jsonPath() when a query needs a scalar or an existence check
|
|
118
|
+
Use a structured `jsonPath()` when a query needs a scalar or an existence check
|
|
99
119
|
inside a JSON document:
|
|
100
120
|
|
|
101
121
|
```ts
|
|
@@ -135,7 +155,7 @@ interpolating caller-provided SQL.
|
|
|
135
155
|
## Understand missing values
|
|
136
156
|
|
|
137
157
|
Scalar reads return SQL NULL when the path is missing, contains JSON null, or
|
|
138
|
-
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
|
|
139
159
|
JSON null, false for a missing path, and false when the document itself is SQL
|
|
140
160
|
NULL.
|
|
141
161
|
|
|
@@ -143,16 +163,21 @@ These rules keep path existence separate from extraction nullability.
|
|
|
143
163
|
|
|
144
164
|
## Check dialect support
|
|
145
165
|
|
|
146
|
-
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.
|
|
147
167
|
PostgreSQL, MySQL, and SQLite use their native JSON policies. The current
|
|
148
168
|
policies require PostgreSQL 12 or newer, MySQL 8.0.21 or newer, and SQLite JSON
|
|
149
169
|
functions. An application-created dialect must provide a JSON renderer.
|
|
150
170
|
|
|
151
171
|
## Know the current limits
|
|
152
172
|
|
|
153
|
-
JSON paths
|
|
154
|
-
|
|
155
|
-
|
|
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.
|
|
156
181
|
|
|
157
182
|
For the SQL domain and nullability rules behind JSON columns, read
|
|
158
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,7 +175,9 @@ 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`.
|
|
@@ -237,11 +243,14 @@ unrestricted operation is intended.
|
|
|
237
243
|
|
|
238
244
|
## Return typed rows
|
|
239
245
|
|
|
240
|
-
`returning()` uses the same named object projection as `SELECT`.
|
|
241
|
-
`{ ...all(table) }`
|
|
242
|
-
|
|
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
|
|
243
250
|
`(await db.execute(query)).rows` has the same shape as a read query when `db`
|
|
244
|
-
comes from `qubu(adapter)`.
|
|
251
|
+
comes from `qubu(adapter)`.
|
|
252
|
+
|
|
253
|
+
The projection’s SQL semantic domains are
|
|
245
254
|
retained too, so a returned query used by typed composition does not collapse
|
|
246
255
|
UUID, text, numeric, or other known fields to their JavaScript types alone.
|
|
247
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
|