turbine-orm 0.81.0 → 0.90.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/README.md +26 -25
- package/dist/checkout.d.ts +27 -2
- package/dist/checkout.js +75 -4
- package/dist/cjs/checkout.d.ts +27 -2
- package/dist/cjs/checkout.js +76 -3
- package/dist/cjs/cli/code-first-tags.d.ts +62 -0
- package/dist/cjs/cli/code-first-tags.js +124 -0
- package/dist/cjs/cli/compile-query.js +9 -0
- package/dist/cjs/cli/config.d.ts +35 -6
- package/dist/cjs/cli/config.js +17 -1
- package/dist/cjs/cli/destructive.d.ts +7 -4
- package/dist/cjs/cli/destructive.js +224 -20
- package/dist/cjs/cli/error-catalog.js +36 -0
- package/dist/cjs/cli/error-text.d.ts +15 -0
- package/dist/cjs/cli/error-text.js +40 -0
- package/dist/cjs/cli/index.d.ts +62 -4
- package/dist/cjs/cli/index.js +568 -108
- package/dist/cjs/cli/local-server-guard.d.ts +53 -0
- package/dist/cjs/cli/local-server-guard.js +119 -0
- package/dist/cjs/cli/local-server-output.d.ts +41 -0
- package/dist/cjs/cli/local-server-output.js +103 -0
- package/dist/cjs/cli/mcp.d.ts +8 -0
- package/dist/cjs/cli/mcp.js +121 -32
- package/dist/cjs/cli/migrate.d.ts +113 -5
- package/dist/cjs/cli/migrate.js +270 -21
- package/dist/cjs/cli/observe.d.ts +6 -1
- package/dist/cjs/cli/observe.js +85 -55
- package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cjs/cli/pii-predicate-guard.js +98 -37
- package/dist/cjs/cli/prisma-resolve.js +17 -13
- package/dist/cjs/cli/prisma-schema.js +12 -4
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.d.ts +14 -2
- package/dist/cjs/cli/studio.js +244 -69
- package/dist/cjs/cli/ui.d.ts +1 -1
- package/dist/cjs/client.d.ts +558 -85
- package/dist/cjs/client.js +1270 -395
- package/dist/cjs/connection-guard.d.ts +13 -0
- package/dist/cjs/connection-guard.js +28 -0
- package/dist/cjs/connection-url.d.ts +51 -16
- package/dist/cjs/connection-url.js +67 -34
- package/dist/cjs/diagnostics.d.ts +81 -0
- package/dist/cjs/diagnostics.js +147 -0
- package/dist/cjs/dialect.d.ts +449 -20
- package/dist/cjs/dialect.js +117 -76
- package/dist/cjs/engine-config.d.ts +7 -6
- package/dist/cjs/errors.d.ts +126 -6
- package/dist/cjs/errors.js +256 -13
- package/dist/cjs/generate.d.ts +57 -7
- package/dist/cjs/generate.js +635 -279
- package/dist/cjs/global-filters-config.d.ts +59 -0
- package/dist/cjs/global-filters-config.js +367 -0
- package/dist/cjs/index.d.ts +9 -4
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/introspect.d.ts +31 -6
- package/dist/cjs/introspect.js +108 -50
- package/dist/cjs/log-codes.d.ts +12 -0
- package/dist/cjs/log-codes.js +77 -0
- package/dist/cjs/logger.d.ts +87 -0
- package/dist/cjs/logger.js +98 -0
- package/dist/cjs/mssql.d.ts +8 -5
- package/dist/cjs/mssql.js +352 -65
- package/dist/cjs/mysql.d.ts +3 -5
- package/dist/cjs/mysql.js +420 -70
- package/dist/cjs/nested-write.d.ts +3 -0
- package/dist/cjs/nested-write.js +179 -114
- package/dist/cjs/pipeline-submittable.d.ts +12 -0
- package/dist/cjs/pipeline-submittable.js +118 -44
- package/dist/cjs/pipeline.js +21 -26
- package/dist/cjs/powdb-introspect.d.ts +5 -2
- package/dist/cjs/powdb-introspect.js +20 -15
- package/dist/cjs/powdb-shared.d.ts +36 -5
- package/dist/cjs/powdb-shared.js +67 -8
- package/dist/cjs/powdb.d.ts +4 -0
- package/dist/cjs/powdb.js +183 -51
- package/dist/cjs/powql.d.ts +394 -51
- package/dist/cjs/powql.js +1829 -284
- package/dist/cjs/prisma-compat.d.ts +23 -15
- package/dist/cjs/prisma-compat.js +507 -372
- package/dist/cjs/query/aggregates.d.ts +17 -1
- package/dist/cjs/query/aggregates.js +261 -29
- package/dist/cjs/query/batched-loader.d.ts +25 -6
- package/dist/cjs/query/batched-loader.js +103 -20
- package/dist/cjs/query/builder.d.ts +325 -184
- package/dist/cjs/query/builder.js +1452 -490
- package/dist/cjs/query/column-ref.d.ts +61 -0
- package/dist/cjs/query/column-ref.js +73 -0
- package/dist/cjs/query/compound-unique.d.ts +21 -0
- package/dist/cjs/query/compound-unique.js +98 -18
- package/dist/cjs/query/deferred.d.ts +53 -16
- package/dist/cjs/query/expr.d.ts +217 -0
- package/dist/cjs/query/expr.js +859 -0
- package/dist/cjs/query/filters.d.ts +155 -53
- package/dist/cjs/query/filters.js +613 -76
- package/dist/cjs/query/index.d.ts +2 -0
- package/dist/cjs/query/index.js +7 -1
- package/dist/cjs/query/json-value.d.ts +43 -0
- package/dist/cjs/query/json-value.js +59 -0
- package/dist/cjs/query/normalize-args.d.ts +87 -0
- package/dist/cjs/query/normalize-args.js +527 -0
- package/dist/cjs/query/option-surface.d.ts +16 -4
- package/dist/cjs/query/option-surface.js +57 -14
- package/dist/cjs/query/relation-names.d.ts +14 -0
- package/dist/cjs/query/relation-names.js +77 -18
- package/dist/cjs/query/relations.d.ts +30 -3
- package/dist/cjs/query/relations.js +219 -63
- package/dist/cjs/query/row-lock.d.ts +49 -0
- package/dist/cjs/query/row-lock.js +138 -0
- package/dist/cjs/query/sql-comment.d.ts +161 -0
- package/dist/cjs/query/sql-comment.js +381 -0
- package/dist/cjs/query/stream-idle.d.ts +132 -0
- package/dist/cjs/query/stream-idle.js +212 -0
- package/dist/cjs/query/table-client.d.ts +101 -0
- package/dist/cjs/query/table-client.js +74 -0
- package/dist/cjs/query/types.d.ts +677 -298
- package/dist/cjs/query/utils.d.ts +105 -6
- package/dist/cjs/query/utils.js +426 -19
- package/dist/cjs/query/warn-registry.d.ts +18 -0
- package/dist/cjs/query/warn-registry.js +18 -0
- package/dist/cjs/query/where-compile.d.ts +138 -14
- package/dist/cjs/query/where-compile.js +513 -29
- package/dist/cjs/query/where.d.ts +159 -59
- package/dist/cjs/query/where.js +716 -246
- package/dist/cjs/query/writes.d.ts +112 -50
- package/dist/cjs/query/writes.js +1498 -200
- package/dist/cjs/query-events.d.ts +4 -2
- package/dist/cjs/query-events.js +3 -1
- package/dist/cjs/realtime.d.ts +7 -3
- package/dist/cjs/realtime.js +13 -2
- package/dist/cjs/schema-builder.d.ts +55 -4
- package/dist/cjs/schema-builder.js +382 -18
- package/dist/cjs/schema-metadata.d.ts +21 -1
- package/dist/cjs/schema-metadata.js +29 -7
- package/dist/cjs/schema-sql.d.ts +75 -5
- package/dist/cjs/schema-sql.js +497 -45
- package/dist/cjs/schema.d.ts +26 -5
- package/dist/cjs/schema.js +110 -13
- package/dist/cjs/sql-placeholders.d.ts +56 -0
- package/dist/cjs/sql-placeholders.js +204 -0
- package/dist/cjs/sqlite.d.ts +75 -20
- package/dist/cjs/sqlite.js +593 -95
- package/dist/cjs/supplied-pool-types.d.ts +48 -0
- package/dist/cjs/supplied-pool-types.js +256 -0
- package/dist/cjs/transaction-control.d.ts +51 -0
- package/dist/cjs/transaction-control.js +316 -0
- package/dist/cjs/transaction-state.d.ts +342 -0
- package/dist/cjs/transaction-state.js +707 -0
- package/dist/cjs/typed-sql.d.ts +37 -3
- package/dist/cjs/typed-sql.js +102 -8
- package/dist/cli/code-first-tags.d.ts +62 -0
- package/dist/cli/code-first-tags.js +120 -0
- package/dist/cli/compile-query.js +9 -0
- package/dist/cli/config.d.ts +35 -6
- package/dist/cli/config.js +16 -1
- package/dist/cli/destructive.d.ts +7 -4
- package/dist/cli/destructive.js +224 -20
- package/dist/cli/error-catalog.js +36 -0
- package/dist/cli/error-text.d.ts +15 -0
- package/dist/cli/error-text.js +37 -0
- package/dist/cli/index.d.ts +62 -4
- package/dist/cli/index.js +569 -112
- package/dist/cli/local-server-guard.d.ts +53 -0
- package/dist/cli/local-server-guard.js +113 -0
- package/dist/cli/local-server-output.d.ts +41 -0
- package/dist/cli/local-server-output.js +98 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +121 -34
- package/dist/cli/migrate.d.ts +113 -5
- package/dist/cli/migrate.js +261 -21
- package/dist/cli/observe.d.ts +6 -1
- package/dist/cli/observe.js +84 -54
- package/dist/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cli/pii-predicate-guard.js +100 -35
- package/dist/cli/prisma-resolve.js +15 -11
- package/dist/cli/prisma-schema.js +12 -4
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +14 -2
- package/dist/cli/studio.js +246 -71
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +558 -85
- package/dist/client.js +1275 -400
- package/dist/connection-guard.d.ts +13 -0
- package/dist/connection-guard.js +27 -0
- package/dist/connection-url.d.ts +51 -16
- package/dist/connection-url.js +63 -34
- package/dist/diagnostics.d.ts +81 -0
- package/dist/diagnostics.js +144 -0
- package/dist/dialect.d.ts +449 -20
- package/dist/dialect.js +115 -43
- package/dist/engine-config.d.ts +7 -6
- package/dist/errors.d.ts +126 -6
- package/dist/errors.js +252 -12
- package/dist/generate.d.ts +57 -7
- package/dist/generate.js +635 -281
- package/dist/global-filters-config.d.ts +59 -0
- package/dist/global-filters-config.js +361 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/introspect.d.ts +31 -6
- package/dist/introspect.js +106 -50
- package/dist/log-codes.d.ts +12 -0
- package/dist/log-codes.js +74 -0
- package/dist/logger.d.ts +87 -0
- package/dist/logger.js +93 -0
- package/dist/mssql.d.ts +8 -5
- package/dist/mssql.js +354 -67
- package/dist/mysql.d.ts +3 -5
- package/dist/mysql.js +423 -73
- package/dist/nested-write.d.ts +3 -0
- package/dist/nested-write.js +177 -112
- package/dist/pipeline-submittable.d.ts +12 -0
- package/dist/pipeline-submittable.js +117 -44
- package/dist/pipeline.js +21 -26
- package/dist/powdb-introspect.d.ts +5 -2
- package/dist/powdb-introspect.js +21 -16
- package/dist/powdb-shared.d.ts +36 -5
- package/dist/powdb-shared.js +65 -8
- package/dist/powdb.d.ts +4 -0
- package/dist/powdb.js +183 -51
- package/dist/powql.d.ts +394 -51
- package/dist/powql.js +1836 -291
- package/dist/prisma-compat.d.ts +23 -15
- package/dist/prisma-compat.js +508 -373
- package/dist/query/aggregates.d.ts +17 -1
- package/dist/query/aggregates.js +263 -32
- package/dist/query/batched-loader.d.ts +25 -6
- package/dist/query/batched-loader.js +101 -21
- package/dist/query/builder.d.ts +325 -184
- package/dist/query/builder.js +1458 -495
- package/dist/query/column-ref.d.ts +61 -0
- package/dist/query/column-ref.js +66 -0
- package/dist/query/compound-unique.d.ts +21 -0
- package/dist/query/compound-unique.js +97 -19
- package/dist/query/deferred.d.ts +53 -16
- package/dist/query/expr.d.ts +217 -0
- package/dist/query/expr.js +848 -0
- package/dist/query/filters.d.ts +155 -53
- package/dist/query/filters.js +597 -72
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +4 -0
- package/dist/query/json-value.d.ts +43 -0
- package/dist/query/json-value.js +54 -0
- package/dist/query/normalize-args.d.ts +87 -0
- package/dist/query/normalize-args.js +517 -0
- package/dist/query/option-surface.d.ts +16 -4
- package/dist/query/option-surface.js +56 -13
- package/dist/query/relation-names.d.ts +14 -0
- package/dist/query/relation-names.js +77 -19
- package/dist/query/relations.d.ts +30 -3
- package/dist/query/relations.js +220 -67
- package/dist/query/row-lock.d.ts +49 -0
- package/dist/query/row-lock.js +131 -0
- package/dist/query/sql-comment.d.ts +161 -0
- package/dist/query/sql-comment.js +374 -0
- package/dist/query/stream-idle.d.ts +132 -0
- package/dist/query/stream-idle.js +203 -0
- package/dist/query/table-client.d.ts +101 -0
- package/dist/query/table-client.js +70 -0
- package/dist/query/types.d.ts +677 -298
- package/dist/query/utils.d.ts +105 -6
- package/dist/query/utils.js +414 -18
- package/dist/query/warn-registry.d.ts +18 -0
- package/dist/query/warn-registry.js +18 -0
- package/dist/query/where-compile.d.ts +138 -14
- package/dist/query/where-compile.js +508 -31
- package/dist/query/where.d.ts +159 -59
- package/dist/query/where.js +711 -248
- package/dist/query/writes.d.ts +112 -50
- package/dist/query/writes.js +1496 -202
- package/dist/query-events.d.ts +4 -2
- package/dist/query-events.js +3 -1
- package/dist/realtime.d.ts +7 -3
- package/dist/realtime.js +13 -2
- package/dist/schema-builder.d.ts +55 -4
- package/dist/schema-builder.js +381 -18
- package/dist/schema-metadata.d.ts +21 -1
- package/dist/schema-metadata.js +28 -7
- package/dist/schema-sql.d.ts +75 -5
- package/dist/schema-sql.js +494 -44
- package/dist/schema.d.ts +26 -5
- package/dist/schema.js +109 -13
- package/dist/sql-placeholders.d.ts +56 -0
- package/dist/sql-placeholders.js +200 -0
- package/dist/sqlite.d.ts +75 -20
- package/dist/sqlite.js +597 -99
- package/dist/supplied-pool-types.d.ts +48 -0
- package/dist/supplied-pool-types.js +247 -0
- package/dist/transaction-control.d.ts +51 -0
- package/dist/transaction-control.js +311 -0
- package/dist/transaction-state.d.ts +342 -0
- package/dist/transaction-state.js +696 -0
- package/dist/typed-sql.d.ts +37 -3
- package/dist/typed-sql.js +100 -8
- package/package.json +7 -4
- package/skills/turbine-orm/SKILL.md +56 -6
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm: branded column references
|
|
3
|
+
*
|
|
4
|
+
* `{ equals: col('publishedVersionId') }` compares a column against another
|
|
5
|
+
* column of the same row instead of against a bound value. The reference is
|
|
6
|
+
* written into the SQL TEXT (`"a" = "b"`), so it is the one operand that
|
|
7
|
+
* removes the bound value entirely.
|
|
8
|
+
*
|
|
9
|
+
* That is why it carries a brand. A plain `{ col: 'id' }` object is exactly
|
|
10
|
+
* what `JSON.parse` produces from a request body: were that shape a reference,
|
|
11
|
+
* `delete({ where: { id: body.id } })` with a body of
|
|
12
|
+
* `{"id":{"equals":{"col":"id"}}}` would compile to `WHERE "id" = "id"` and
|
|
13
|
+
* delete every row. A symbol cannot come out of `JSON.parse`, a query string, a
|
|
14
|
+
* form body or a `structuredClone` of parsed input, so only code can build one.
|
|
15
|
+
* Same reasoning as the `UNSAFE` sentinel.
|
|
16
|
+
*
|
|
17
|
+
* `Symbol.for` rather than `Symbol()` because the package ships dual ESM and
|
|
18
|
+
* CJS builds: a reference built by one copy must be recognized by the other.
|
|
19
|
+
*/
|
|
20
|
+
/** The brand every {@link ColumnRef} carries. Registered, see the module note. */
|
|
21
|
+
export declare const COLUMN_REF: unique symbol;
|
|
22
|
+
/**
|
|
23
|
+
* A column-to-column comparison operand, built only by {@link col}.
|
|
24
|
+
*
|
|
25
|
+
* Accepted by `equals`, `not`, `gt`, `gte`, `lt` and `lte` on a non-JSON
|
|
26
|
+
* column. The referenced field resolves through the table's column map
|
|
27
|
+
* (camelCase or snake_case) and compiles to a quoted identifier, so no
|
|
28
|
+
* parameter is bound. An unknown field throws `ValidationError` (E003), and
|
|
29
|
+
* `mode: 'insensitive'` cannot be combined with a reference.
|
|
30
|
+
*
|
|
31
|
+
* `F` narrows the referenced name to the table's field names when the
|
|
32
|
+
* surrounding where clause knows the entity type.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { col } from 'turbine-orm';
|
|
36
|
+
* where: { currentVersionId: { equals: col('publishedVersionId') } }
|
|
37
|
+
* // → WHERE "current_version_id" = "published_version_id"
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export interface ColumnRef<F extends string = string> {
|
|
41
|
+
readonly col: F;
|
|
42
|
+
readonly [COLUMN_REF]: true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build a column reference: `{ gt: col('startsAt') }` compares against the
|
|
46
|
+
* `startsAt` column of the same row.
|
|
47
|
+
*/
|
|
48
|
+
export declare function col<F extends string>(name: F): ColumnRef<F>;
|
|
49
|
+
/** True only for a reference built by {@link col}. */
|
|
50
|
+
export declare function isColumnRef(value: unknown): value is ColumnRef;
|
|
51
|
+
/**
|
|
52
|
+
* True for the UNBRANDED `{ col: '<name>' }` shape, which is what parsed
|
|
53
|
+
* request data produces. Callers refuse it with {@link plainColumnRefMessage}
|
|
54
|
+
* rather than reading it as a value, because whoever wrote it almost certainly
|
|
55
|
+
* meant a reference.
|
|
56
|
+
*/
|
|
57
|
+
export declare function isPlainColumnRefShape(value: unknown): boolean;
|
|
58
|
+
/** The refusal for a plain `{ col }` object at an operand position. */
|
|
59
|
+
export declare function plainColumnRefMessage(value: {
|
|
60
|
+
col: string;
|
|
61
|
+
}, operator: string, column: string, table: string): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm: branded column references
|
|
3
|
+
*
|
|
4
|
+
* `{ equals: col('publishedVersionId') }` compares a column against another
|
|
5
|
+
* column of the same row instead of against a bound value. The reference is
|
|
6
|
+
* written into the SQL TEXT (`"a" = "b"`), so it is the one operand that
|
|
7
|
+
* removes the bound value entirely.
|
|
8
|
+
*
|
|
9
|
+
* That is why it carries a brand. A plain `{ col: 'id' }` object is exactly
|
|
10
|
+
* what `JSON.parse` produces from a request body: were that shape a reference,
|
|
11
|
+
* `delete({ where: { id: body.id } })` with a body of
|
|
12
|
+
* `{"id":{"equals":{"col":"id"}}}` would compile to `WHERE "id" = "id"` and
|
|
13
|
+
* delete every row. A symbol cannot come out of `JSON.parse`, a query string, a
|
|
14
|
+
* form body or a `structuredClone` of parsed input, so only code can build one.
|
|
15
|
+
* Same reasoning as the `UNSAFE` sentinel.
|
|
16
|
+
*
|
|
17
|
+
* `Symbol.for` rather than `Symbol()` because the package ships dual ESM and
|
|
18
|
+
* CJS builds: a reference built by one copy must be recognized by the other.
|
|
19
|
+
*/
|
|
20
|
+
import { ValidationError } from '../errors.js';
|
|
21
|
+
/** The brand every {@link ColumnRef} carries. Registered, see the module note. */
|
|
22
|
+
export const COLUMN_REF = Symbol.for('turbine-orm.columnRef');
|
|
23
|
+
/**
|
|
24
|
+
* Build a column reference: `{ gt: col('startsAt') }` compares against the
|
|
25
|
+
* `startsAt` column of the same row.
|
|
26
|
+
*/
|
|
27
|
+
export function col(name) {
|
|
28
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
29
|
+
throw new ValidationError('col() takes the name of a field on the table, as a non-empty string.');
|
|
30
|
+
}
|
|
31
|
+
const ref = { col: name };
|
|
32
|
+
// Non-enumerable so a reference prints and compares as `{ col }`; a symbol
|
|
33
|
+
// key is never visited by `Object.keys`, so no where walker sees it either.
|
|
34
|
+
Object.defineProperty(ref, COLUMN_REF, { value: true, enumerable: false });
|
|
35
|
+
return Object.freeze(ref);
|
|
36
|
+
}
|
|
37
|
+
/** True only for a reference built by {@link col}. */
|
|
38
|
+
export function isColumnRef(value) {
|
|
39
|
+
return (typeof value === 'object' &&
|
|
40
|
+
value !== null &&
|
|
41
|
+
value[COLUMN_REF] === true &&
|
|
42
|
+
typeof value.col === 'string');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* True for the UNBRANDED `{ col: '<name>' }` shape, which is what parsed
|
|
46
|
+
* request data produces. Callers refuse it with {@link plainColumnRefMessage}
|
|
47
|
+
* rather than reading it as a value, because whoever wrote it almost certainly
|
|
48
|
+
* meant a reference.
|
|
49
|
+
*/
|
|
50
|
+
export function isPlainColumnRefShape(value) {
|
|
51
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value) || isColumnRef(value))
|
|
52
|
+
return false;
|
|
53
|
+
const proto = Object.getPrototypeOf(value);
|
|
54
|
+
if (proto !== Object.prototype && proto !== null)
|
|
55
|
+
return false;
|
|
56
|
+
const keys = Object.keys(value);
|
|
57
|
+
return keys.length === 1 && keys[0] === 'col' && typeof value.col === 'string';
|
|
58
|
+
}
|
|
59
|
+
/** The refusal for a plain `{ col }` object at an operand position. */
|
|
60
|
+
export function plainColumnRefMessage(value, operator, column, table) {
|
|
61
|
+
const name = JSON.stringify(value.col);
|
|
62
|
+
return (`A plain { col: ${name} } object at '${operator}' on "${column}" for table "${table}" is not a column ` +
|
|
63
|
+
`reference. Build one with col(${name}) (import { col } from 'turbine-orm'): references carry a ` +
|
|
64
|
+
'symbol brand so that parsed request data can never name a column. To compare against a value, pass a ' +
|
|
65
|
+
'string, number, boolean, bigint, Date or Buffer.');
|
|
66
|
+
}
|
|
@@ -193,3 +193,24 @@ export declare function whereIdentifiesOneRow(meta: TableMetadata, where: Record
|
|
|
193
193
|
* anything.
|
|
194
194
|
*/
|
|
195
195
|
export declare function upsertWhereMatchesCreate(meta: TableMetadata, where: Record<string, unknown>, create: Record<string, unknown>): boolean;
|
|
196
|
+
/**
|
|
197
|
+
* A unique key, other than the ones in `pinned`, that a new row could collide
|
|
198
|
+
* on, or `undefined` when there is none.
|
|
199
|
+
*
|
|
200
|
+
* The question MySQL's `ON DUPLICATE KEY UPDATE` raises: it has no conflict
|
|
201
|
+
* target and updates the row whichever unique key collides first, so a row
|
|
202
|
+
* that also collides on another key can update a row the caller did not name.
|
|
203
|
+
* A key collides only when every member holds a value, so a key is ruled out
|
|
204
|
+
* when a member is NULL (written as null, or absent with no default), when
|
|
205
|
+
* none of its members is set (defaults are server-generated values in
|
|
206
|
+
* practice), or when it contains a pinned key (it then collides only where the
|
|
207
|
+
* pinned key does, on the same row). `undefined` is the safe answer only for
|
|
208
|
+
* shapes that can never collide; anything uncertain returns the key.
|
|
209
|
+
*/
|
|
210
|
+
export declare function otherCollidingUniqueKey(meta: TableMetadata, pinned: readonly (readonly string[])[], stateOf: (column: string) => 'value' | 'null' | 'absent'): string[] | undefined;
|
|
211
|
+
/**
|
|
212
|
+
* {@link otherCollidingUniqueKey} for a single-row upsert: the keys `where`
|
|
213
|
+
* pins, against the values `create` inserts. Read only after
|
|
214
|
+
* {@link upsertWhereMatchesCreate} holds, so every `where` key is a column.
|
|
215
|
+
*/
|
|
216
|
+
export declare function upsertCreateCollidingKey(meta: TableMetadata, where: Record<string, unknown>, create: Record<string, unknown>): string[] | undefined;
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
* {@link TableMetadata} in a module-level `WeakMap`.
|
|
40
40
|
*/
|
|
41
41
|
import { ValidationError } from '../errors.js';
|
|
42
|
-
import { isArrayFilter, isJsonFilter, isVectorFilter, isWhereOperator } from './filters.js';
|
|
42
|
+
import { isArrayFilter, isJsonFilter, isPlainRecord, isVectorFilter, isWhereOperator } from './filters.js';
|
|
43
43
|
import { isInternalCombinator, ownLookup, resolveColumnName, resolveRelationDef } from './utils.js';
|
|
44
44
|
const syntheticKeyCache = new WeakMap();
|
|
45
45
|
/**
|
|
@@ -466,32 +466,110 @@ export function upsertWhereMatchesCreate(meta, where, create) {
|
|
|
466
466
|
}
|
|
467
467
|
return keys > 0;
|
|
468
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* A unique key, other than the ones in `pinned`, that a new row could collide
|
|
471
|
+
* on, or `undefined` when there is none.
|
|
472
|
+
*
|
|
473
|
+
* The question MySQL's `ON DUPLICATE KEY UPDATE` raises: it has no conflict
|
|
474
|
+
* target and updates the row whichever unique key collides first, so a row
|
|
475
|
+
* that also collides on another key can update a row the caller did not name.
|
|
476
|
+
* A key collides only when every member holds a value, so a key is ruled out
|
|
477
|
+
* when a member is NULL (written as null, or absent with no default), when
|
|
478
|
+
* none of its members is set (defaults are server-generated values in
|
|
479
|
+
* practice), or when it contains a pinned key (it then collides only where the
|
|
480
|
+
* pinned key does, on the same row). `undefined` is the safe answer only for
|
|
481
|
+
* shapes that can never collide; anything uncertain returns the key.
|
|
482
|
+
*/
|
|
483
|
+
export function otherCollidingUniqueKey(meta, pinned, stateOf) {
|
|
484
|
+
for (const key of uniqueKeyNames(meta)) {
|
|
485
|
+
if (pinned.some((p) => p.every((c) => key.includes(c))))
|
|
486
|
+
continue;
|
|
487
|
+
let sets = false;
|
|
488
|
+
let nullMember = false;
|
|
489
|
+
for (const column of key) {
|
|
490
|
+
const state = stateOf(column);
|
|
491
|
+
if (state === 'value')
|
|
492
|
+
sets = true;
|
|
493
|
+
else if (state === 'null' || defaultsToNull(meta, column))
|
|
494
|
+
nullMember = true;
|
|
495
|
+
}
|
|
496
|
+
if (sets && !nullMember)
|
|
497
|
+
return key;
|
|
498
|
+
}
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
/** A nullable column with no default: absent from an INSERT, it is NULL. */
|
|
502
|
+
function defaultsToNull(meta, column) {
|
|
503
|
+
const col = meta.columns.find((c) => c.name === column);
|
|
504
|
+
return col?.nullable === true && !col.hasDefault;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* {@link otherCollidingUniqueKey} for a single-row upsert: the keys `where`
|
|
508
|
+
* pins, against the values `create` inserts. Read only after
|
|
509
|
+
* {@link upsertWhereMatchesCreate} holds, so every `where` key is a column.
|
|
510
|
+
*/
|
|
511
|
+
export function upsertCreateCollidingKey(meta, where, create) {
|
|
512
|
+
const whereColumns = new Set();
|
|
513
|
+
for (const [key, value] of Object.entries(expandCompoundUniqueWhere(meta, where))) {
|
|
514
|
+
if (value === undefined)
|
|
515
|
+
continue;
|
|
516
|
+
const column = resolveColumnName(meta, key);
|
|
517
|
+
if (column !== undefined)
|
|
518
|
+
whereColumns.add(column);
|
|
519
|
+
}
|
|
520
|
+
const pinned = uniqueKeyNames(meta).filter((key) => key.every((c) => whereColumns.has(c)));
|
|
521
|
+
const createState = new Map();
|
|
522
|
+
for (const [key, value] of Object.entries(create)) {
|
|
523
|
+
if (value === undefined)
|
|
524
|
+
continue;
|
|
525
|
+
const column = resolveColumnName(meta, key);
|
|
526
|
+
if (column !== undefined)
|
|
527
|
+
createState.set(column, value === null ? 'null' : 'value');
|
|
528
|
+
}
|
|
529
|
+
return otherCollidingUniqueKey(meta, pinned, (c) => createState.get(c) ?? 'absent');
|
|
530
|
+
}
|
|
469
531
|
/** `{ equals: v }` and nothing else (a `mode` beside it changes the match). */
|
|
470
532
|
function isBareEquals(value) {
|
|
471
|
-
return
|
|
533
|
+
return isPlainRecord(value) && Object.keys(value).length === 1 && 'equals' in value;
|
|
472
534
|
}
|
|
473
535
|
/** Equal non-null key values; two Dates compare by instant, other objects by identity. */
|
|
474
536
|
function sameKeyValue(a, b) {
|
|
475
537
|
return a != null && (a instanceof Date && b instanceof Date ? a.getTime() === b.getTime() : a === b);
|
|
476
538
|
}
|
|
477
|
-
/**
|
|
539
|
+
/**
|
|
540
|
+
* A bare scalar, or `{ equals: <scalar> }` (optionally with `mode: 'default'`).
|
|
541
|
+
*
|
|
542
|
+
* Nothing else pins a column, and the refusals are the point. A column
|
|
543
|
+
* reference (`{ equals: col('id') }`, or the plain `{ col }` a request body
|
|
544
|
+
* carries) compiles to `"id" = "id"`, which every row satisfies, so a
|
|
545
|
+
* `delete({ where: { id: body.id } })` would delete the table. A plain object
|
|
546
|
+
* is a relation filter or a sub-where, `mode: 'insensitive'` matches every
|
|
547
|
+
* casing of the value, and a JSON / array / vector filter narrows rather than
|
|
548
|
+
* identifies.
|
|
549
|
+
*/
|
|
478
550
|
function isPinnedToOneValue(value) {
|
|
479
|
-
if (value
|
|
480
|
-
return
|
|
481
|
-
if (
|
|
482
|
-
const eq = value.equals;
|
|
483
|
-
return eq !== undefined && eq !== null;
|
|
484
|
-
}
|
|
485
|
-
// A JSON / array / vector filter narrows, it does not identify.
|
|
486
|
-
if (isJsonFilter(value) || isArrayFilter(value) || isVectorFilter(value))
|
|
551
|
+
if (isIdentityScalar(value))
|
|
552
|
+
return true;
|
|
553
|
+
if (!isPlainRecord(value))
|
|
487
554
|
return false;
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
return
|
|
555
|
+
const keys = Object.keys(value).filter((k) => value[k] !== undefined);
|
|
556
|
+
const op = value;
|
|
557
|
+
return (keys.every((k) => k === 'equals' || (k === 'mode' && op.mode === 'default')) &&
|
|
558
|
+
keys.includes('equals') &&
|
|
559
|
+
isIdentityScalar(op.equals));
|
|
491
560
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
561
|
+
/** A non-null scalar a unique index compares by value. */
|
|
562
|
+
function isIdentityScalar(value) {
|
|
563
|
+
switch (typeof value) {
|
|
564
|
+
case 'string':
|
|
565
|
+
case 'bigint':
|
|
566
|
+
case 'boolean':
|
|
567
|
+
return true;
|
|
568
|
+
case 'number':
|
|
569
|
+
return !Number.isNaN(value);
|
|
570
|
+
case 'object':
|
|
571
|
+
return value instanceof Date || ArrayBuffer.isView(value);
|
|
572
|
+
default:
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
497
575
|
}
|
package/dist/query/deferred.d.ts
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Split from builder.ts so the class file focuses on SQL assembly / execution.
|
|
5
5
|
*/
|
|
6
|
+
import type { DiagnosticsConfig } from '../diagnostics.js';
|
|
6
7
|
import type { Dialect } from '../dialect.js';
|
|
8
|
+
import type { TurbineLogger } from '../logger.js';
|
|
7
9
|
import type { PgCompatPool, PgCompatQueryResult } from '../pg-types.js';
|
|
8
10
|
import type { SchemaMetadata } from '../schema.js';
|
|
9
|
-
import type {
|
|
11
|
+
import type { SqlCommenter, SqlCommentOptions } from './sql-comment.js';
|
|
12
|
+
import type { TableClient } from './table-client.js';
|
|
10
13
|
import type { GlobalFilters, JsonEncoding, RelationLoadStrategy } from './types.js';
|
|
11
14
|
/**
|
|
12
15
|
* Runs a SQL statement and resolves its raw result. Passed to a
|
|
@@ -34,6 +37,13 @@ export interface DeferredQuery<T> {
|
|
|
34
37
|
* rows), so the PostgreSQL path never allocates or consults it.
|
|
35
38
|
*/
|
|
36
39
|
reselect?: (exec: ReselectExecutor) => Promise<PgCompatQueryResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Set when the statement is a locking read (`lock`). Its locks last until
|
|
42
|
+
* the transaction ends, so a runner that executes it outside a transaction
|
|
43
|
+
* (an autocommit pipeline) must refuse it: the rows would be unlocked again
|
|
44
|
+
* before the caller saw them.
|
|
45
|
+
*/
|
|
46
|
+
rowLock?: true;
|
|
37
47
|
}
|
|
38
48
|
/**
|
|
39
49
|
* How the ORM hands back a Postgres temporal `infinity` / `-infinity`.
|
|
@@ -108,6 +118,14 @@ export interface QueryEvent {
|
|
|
108
118
|
* batch statements are timed individually. Absent for everything else.
|
|
109
119
|
*/
|
|
110
120
|
batch?: 'pipeline' | 'transaction';
|
|
121
|
+
/**
|
|
122
|
+
* The `sqlComment` this statement was sent with, exactly as appended
|
|
123
|
+
* (`/*action='findMany',model='users'*\/`). `sql` stays the commentless
|
|
124
|
+
* text, so events for one statement shape compare equal whatever the
|
|
125
|
+
* comment; the text on the wire was `${sql} ${comment}`. Absent when the
|
|
126
|
+
* statement carried none (no `sqlComment`, raw SQL, transaction control).
|
|
127
|
+
*/
|
|
128
|
+
comment?: string;
|
|
111
129
|
/**
|
|
112
130
|
* Set on a READ that failed because its connection had already been closed
|
|
113
131
|
* by the server, and that Turbine therefore sent once more on a fresh one.
|
|
@@ -167,21 +185,15 @@ export interface QueryInterfaceOptions {
|
|
|
167
185
|
*
|
|
168
186
|
* Default: `true`. Set to `false` as a nuclear kill switch.
|
|
169
187
|
*
|
|
170
|
-
*
|
|
188
|
+
* Safety net: {@link diagnostics}' `cacheCheck` decides whether a cache HIT is
|
|
171
189
|
* cross-checked by rebuilding the SQL + params fresh and comparing them
|
|
172
190
|
* against the cache-hit result, catching any drift between the fingerprint,
|
|
173
191
|
* SQL-build, and param-collect paths (which has silently corrupted results
|
|
174
|
-
* before).
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* Production sampling: the check is off in production by default. Set
|
|
180
|
-
* `TURBINE_CACHE_CHECK_SAMPLE` to a float in `(0,1]` to re-verify that
|
|
181
|
-
* fraction of cache hits under real load (e.g. `0.001` for one in a
|
|
182
|
-
* thousand). A sampled mismatch logs `console.error` once per distinct
|
|
183
|
-
* fingerprint AND throws the same `ValidationError` (E003). `0`, unset, or an
|
|
184
|
-
* unparseable value keeps the check fully off.
|
|
192
|
+
* before). Every hit in development, none in production, or a sampled
|
|
193
|
+
* fraction (`{ sample: 0.001 }`); a sampled mismatch is logged once per
|
|
194
|
+
* distinct fingerprint as well as thrown. Independently of that, every MISS
|
|
195
|
+
* compares the built and collected params and every HIT checks the param
|
|
196
|
+
* count, in every mode; any mismatch is an `InvariantError` (E020).
|
|
185
197
|
*/
|
|
186
198
|
sqlCache?: boolean;
|
|
187
199
|
/**
|
|
@@ -195,6 +207,12 @@ export interface QueryInterfaceOptions {
|
|
|
195
207
|
* per table).
|
|
196
208
|
*/
|
|
197
209
|
sqlCacheSize?: number;
|
|
210
|
+
/**
|
|
211
|
+
* The client's `streamIdleTimeoutMs`: how long a cursor stream may wait for
|
|
212
|
+
* its consumer before Turbine closes it. Default 300000; `0` disables. See
|
|
213
|
+
* `FindManyStreamArgs.idleTimeoutMs`, which overrides it per call.
|
|
214
|
+
*/
|
|
215
|
+
streamIdleTimeoutMs?: number;
|
|
198
216
|
/** SQL dialect implementation. Defaults to PostgreSQL. */
|
|
199
217
|
dialect?: Dialect;
|
|
200
218
|
/**
|
|
@@ -288,6 +306,25 @@ export interface QueryInterfaceOptions {
|
|
|
288
306
|
* {@link GlobalFilters}.
|
|
289
307
|
*/
|
|
290
308
|
globalFilters?: GlobalFilters;
|
|
309
|
+
/**
|
|
310
|
+
* `diagnostics`: per-query advice and the SQL-cache self-check. TurbineClient
|
|
311
|
+
* resolves it once and hands the same resolved object to every interface it
|
|
312
|
+
* builds (resolving that again returns it by reference). An interface
|
|
313
|
+
* constructed directly may pass any `diagnostics` config, or none, and
|
|
314
|
+
* resolves it at construction, never per call.
|
|
315
|
+
*/
|
|
316
|
+
diagnostics?: DiagnosticsConfig;
|
|
317
|
+
/** Where this interface's log lines go. Defaults to the console logger. */
|
|
318
|
+
logger?: TurbineLogger;
|
|
319
|
+
/**
|
|
320
|
+
* `sqlComment`: sqlcommenter key/values appended to every statement this
|
|
321
|
+
* interface sends (see `TurbineConfig.sqlComment`), resolved at
|
|
322
|
+
* construction. TurbineClient resolves it once instead and hands every
|
|
323
|
+
* interface the same commenter as `_sqlCommenter`.
|
|
324
|
+
*/
|
|
325
|
+
sqlComment?: SqlCommentOptions;
|
|
326
|
+
/** @internal The client's resolved `sqlComment`, shared by its accessors; wins over `sqlComment`. */
|
|
327
|
+
_sqlCommenter?: SqlCommenter;
|
|
291
328
|
/** @internal Set by TransactionClient, signals that this QI runs inside an active transaction. */
|
|
292
329
|
_txScoped?: boolean;
|
|
293
330
|
/** @internal Callback from TurbineClient for query event emission. */
|
|
@@ -295,9 +332,9 @@ export interface QueryInterfaceOptions {
|
|
|
295
332
|
/**
|
|
296
333
|
* @internal Factory that builds the per-table query interface. Defaults to
|
|
297
334
|
* `new QueryInterface` (the SQL path). Non-SQL backends (PowDB) supply a
|
|
298
|
-
* factory returning a
|
|
299
|
-
*
|
|
335
|
+
* factory returning a {@link TableClient}, which generates their own query
|
|
336
|
+
* language instead of SQL. The SQL dialects never set this, so their
|
|
300
337
|
* `table()` behavior is byte-identical.
|
|
301
338
|
*/
|
|
302
|
-
queryInterfaceFactory?: (pool: PgCompatPool, table: string, schema: SchemaMetadata, middlewares: MiddlewareFn[], options: QueryInterfaceOptions) =>
|
|
339
|
+
queryInterfaceFactory?: (pool: PgCompatPool, table: string, schema: SchemaMetadata, middlewares: MiddlewareFn[], options: QueryInterfaceOptions) => TableClient<object>;
|
|
303
340
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm: SQL expressions in update data.
|
|
3
|
+
*
|
|
4
|
+
* `expr.greatest(col('viewCount'), 100)` and friends are branded node objects,
|
|
5
|
+
* and `expr.excluded('events')` names the incoming row of an upsert. Both are
|
|
6
|
+
* written into the SQL TEXT, so both carry a symbol brand: `JSON.parse` cannot
|
|
7
|
+
* produce one, so request data can never become an expression (the same
|
|
8
|
+
* reasoning as `col()` and `UNSAFE`). `Symbol.for` so the ESM and CJS copies of
|
|
9
|
+
* the package recognize each other's nodes. A plain look-alike object
|
|
10
|
+
* (`{ kind: 'greatest', args: [...] }`) is a VALUE, and on a non-JSON column
|
|
11
|
+
* the write path refuses it.
|
|
12
|
+
*
|
|
13
|
+
* The public types live in `query/types.ts` (`SqlExpr`, `ExcludedRef`). This
|
|
14
|
+
* module holds the node constructors, their validation, and
|
|
15
|
+
* {@link compileSetExpr}: the ONE traversal that produces an expression's SQL,
|
|
16
|
+
* its bound parameters and its cache fingerprint together, so the SET build,
|
|
17
|
+
* the cache-hit param collector and the fingerprint cannot disagree (the
|
|
18
|
+
* failure class behind the where-clause cache bugs).
|
|
19
|
+
*
|
|
20
|
+
* Where expressions are accepted: `update`, `updateMany`, `upsert`'s `update`,
|
|
21
|
+
* `createMany`'s `onConflict.update`, and nested update data (which runs
|
|
22
|
+
* through `update()`). Not in `create` / `createMany` data, where the write
|
|
23
|
+
* bind seam refuses a node.
|
|
24
|
+
*/
|
|
25
|
+
import type { TableMetadata } from '../schema.js';
|
|
26
|
+
import { type ColumnRef } from './column-ref.js';
|
|
27
|
+
import type { ExcludedRef, SqlExpr } from './types.js';
|
|
28
|
+
import { type BuilderCtx } from './where.js';
|
|
29
|
+
/** The brand every {@link SqlExpr} node carries. */
|
|
30
|
+
export declare const SQL_EXPR: unique symbol;
|
|
31
|
+
/** The brand an {@link ExcludedRef} carries (the incoming row of an upsert). */
|
|
32
|
+
export declare const EXCLUDED_REF: unique symbol;
|
|
33
|
+
/**
|
|
34
|
+
* An operand of an expression: a JS scalar (string, number, bigint, boolean,
|
|
35
|
+
* Date, Buffer or null), `col('field')` for the current row's column, or
|
|
36
|
+
* another expression.
|
|
37
|
+
*/
|
|
38
|
+
export type ExprOperand<V = unknown> = NotInferred<V> | null | ColumnRef | SqlExpr<V>;
|
|
39
|
+
/**
|
|
40
|
+
* `V` as a type the compiler does not infer from (the pre-TypeScript-5.4
|
|
41
|
+
* spelling of `NoInfer`, so the published declarations need no newer
|
|
42
|
+
* compiler). Without it a `col('x')` operand is itself a candidate for `V`,
|
|
43
|
+
* and `expr.greatest(col('a'), col('b'))` would be typed as producing a
|
|
44
|
+
* column reference. `V` comes from the expression operands and from where the
|
|
45
|
+
* result is assigned instead.
|
|
46
|
+
*/
|
|
47
|
+
type NotInferred<V> = [V][V extends unknown ? 0 : never];
|
|
48
|
+
/**
|
|
49
|
+
* A numeric operand: a number or bigint, `col()` of a numeric column, or a
|
|
50
|
+
* numeric expression (`string` covers a numeric/decimal column typed as text).
|
|
51
|
+
* One union-typed `SqlExpr`, not a union of three, so a nested call such as
|
|
52
|
+
* `expr.add(col('a'), expr.mul(col('b'), 2))` infers without a type argument.
|
|
53
|
+
*/
|
|
54
|
+
export type NumericOperand = number | bigint | null | ColumnRef | SqlExpr<number | bigint | string>;
|
|
55
|
+
/** One `expr.case` arm: when the current row matches `when`, the value is `then`. */
|
|
56
|
+
export interface CaseArm<V = unknown> {
|
|
57
|
+
/** A predicate on the current row's columns, written like `where`. No relation filters. */
|
|
58
|
+
readonly when: Record<string, unknown>;
|
|
59
|
+
readonly then: ExprOperand<V>;
|
|
60
|
+
}
|
|
61
|
+
/** The node an `expr.case` call builds. */
|
|
62
|
+
export interface CaseExpr<V = unknown> extends SqlExpr<V> {
|
|
63
|
+
readonly kind: 'case';
|
|
64
|
+
readonly args: readonly CaseArm<V>[];
|
|
65
|
+
/** The value when no arm matches; absent means NULL. */
|
|
66
|
+
readonly else?: ExprOperand<V>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Builders for SQL expressions in update data. Each returns a frozen, branded
|
|
70
|
+
* node; the SQL is generated per engine when the update compiles.
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { col, expr } from 'turbine-orm';
|
|
74
|
+
* await db.posts.update({
|
|
75
|
+
* where: { id },
|
|
76
|
+
* data: {
|
|
77
|
+
* viewCount: expr.greatest(col('viewCount'), 100),
|
|
78
|
+
* score: expr.add(col('score'), expr.mul(col('bonus'), 2)),
|
|
79
|
+
* editedAt: expr.now(),
|
|
80
|
+
* },
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare const expr: Readonly<{
|
|
85
|
+
/** `a + b`. Numbers, bigints, numeric columns and numeric expressions. */
|
|
86
|
+
add<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
|
|
87
|
+
/** `a - b`. */
|
|
88
|
+
sub<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
|
|
89
|
+
/** `a * b`. */
|
|
90
|
+
mul<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
|
|
91
|
+
/**
|
|
92
|
+
* `a / b`. When both operands are integers (an integer column, an integer
|
|
93
|
+
* number or bigint, or an integer expression) the division truncates toward
|
|
94
|
+
* zero on every engine, as the `divide` update operator does on an integer
|
|
95
|
+
* column. With a non-integer operand it is a decimal division.
|
|
96
|
+
*/
|
|
97
|
+
div<V extends number | bigint | string = number>(a: NumericOperand, b: NumericOperand): SqlExpr<V>;
|
|
98
|
+
/** The first non-NULL operand (2 to 8). */
|
|
99
|
+
coalesce<V>(a: ExprOperand<V>, b: ExprOperand<V>, ...rest: ExprOperand<V>[]): SqlExpr<V>;
|
|
100
|
+
/**
|
|
101
|
+
* The largest operand (2 to 8), IGNORING NULLs on every engine (PostgreSQL's
|
|
102
|
+
* meaning; MySQL and SQLite are rewritten to match). NULL only when every
|
|
103
|
+
* operand is NULL.
|
|
104
|
+
*/
|
|
105
|
+
greatest<V>(a: ExprOperand<V>, b: ExprOperand<V>, ...rest: ExprOperand<V>[]): SqlExpr<V>;
|
|
106
|
+
/** The smallest operand (2 to 8), ignoring NULLs on every engine. */
|
|
107
|
+
least<V>(a: ExprOperand<V>, b: ExprOperand<V>, ...rest: ExprOperand<V>[]): SqlExpr<V>;
|
|
108
|
+
/**
|
|
109
|
+
* `CASE WHEN ... THEN ... ELSE ... END`. Each arm's `when` is a predicate on
|
|
110
|
+
* the current row's columns written like `where` (no relation filters); the
|
|
111
|
+
* first matching arm wins. Without `otherwise` an unmatched row gets NULL.
|
|
112
|
+
*/
|
|
113
|
+
case<V>(arms: readonly CaseArm<V>[], otherwise?: ExprOperand<V>): SqlExpr<V>;
|
|
114
|
+
/**
|
|
115
|
+
* The database server's current time, stored in the zone convention Turbine
|
|
116
|
+
* reads with: a zone-less `timestamp` / `date` gets the UTC wall clock (unless
|
|
117
|
+
* the client sets `utcTimestamps: false`). On PostgreSQL it is the
|
|
118
|
+
* TRANSACTION's start time; on MySQL, the statement's. Temporal columns only.
|
|
119
|
+
*/
|
|
120
|
+
now(): SqlExpr<Date>;
|
|
121
|
+
/**
|
|
122
|
+
* The incoming row's value for `field`: inside `upsert`'s `update` (the value
|
|
123
|
+
* `create` supplies) and `createMany`'s `onConflict.update` (each row's own
|
|
124
|
+
* value). Refused anywhere else.
|
|
125
|
+
*/
|
|
126
|
+
excluded<F extends string>(field: F): ExcludedRef<F>;
|
|
127
|
+
}>;
|
|
128
|
+
/** True only for a node built by {@link expr} (any loaded copy of the package). */
|
|
129
|
+
export declare function isSqlExpr(value: unknown): value is SqlExpr;
|
|
130
|
+
/** True only for `expr.excluded(...)`. */
|
|
131
|
+
export declare function isExcludedRef(value: unknown): value is ExcludedRef;
|
|
132
|
+
/**
|
|
133
|
+
* True for an UNBRANDED object shaped like a node (`{ kind: 'greatest', args }`),
|
|
134
|
+
* which is what parsed request data produces. The write path refuses it on a
|
|
135
|
+
* non-JSON column rather than storing it as JSON text.
|
|
136
|
+
*/
|
|
137
|
+
export declare function isPlainExprShape(value: unknown): boolean;
|
|
138
|
+
/** The refusal for {@link isPlainExprShape}. */
|
|
139
|
+
export declare function plainExprShapeMessage(table: string, key: string, kind: string): string;
|
|
140
|
+
/** The refusal for a node at a position that takes values only (create data, `{ set }`). */
|
|
141
|
+
export declare function exprNotAllowedMessage(table: string, key: string, value: SqlExpr | ColumnRef): string;
|
|
142
|
+
/**
|
|
143
|
+
* Refuse an expression assigned to a column it cannot target in this release
|
|
144
|
+
* (json/jsonb and array columns). Shared by the walker and the single-row
|
|
145
|
+
* upsert's `excluded` resolution, so both paths refuse the same columns.
|
|
146
|
+
*/
|
|
147
|
+
export declare function assertExprTarget(qi: BuilderCtx, key: string, value: SqlExpr | ColumnRef, operation: string): string;
|
|
148
|
+
/** What {@link compileSetExpr} needs from its caller (the write module). */
|
|
149
|
+
export interface ExprCompileEnv {
|
|
150
|
+
readonly qi: BuilderCtx;
|
|
151
|
+
/** The `data` key being assigned. */
|
|
152
|
+
readonly targetKey: string;
|
|
153
|
+
/** The operation, for messages: `update`, `updateMany`, `upsert`, `createMany`. */
|
|
154
|
+
readonly operation: string;
|
|
155
|
+
/**
|
|
156
|
+
* Bind one value leaf (coerced for the target column) and return its
|
|
157
|
+
* placeholder, cast included. `arithmetic` is the leaf's class when it is an
|
|
158
|
+
* arithmetic operand, where PostgreSQL types it from its own value rather than
|
|
159
|
+
* from the target column.
|
|
160
|
+
*/
|
|
161
|
+
bind(value: unknown, arithmetic?: 'int' | 'dec'): string;
|
|
162
|
+
/** Render a column of the target row. */
|
|
163
|
+
column(column: string): string;
|
|
164
|
+
/** Render the incoming row's column, or absent where `expr.excluded` has no meaning. */
|
|
165
|
+
readonly excluded?: ((column: string) => string) | undefined;
|
|
166
|
+
/** The columns the incoming rows supply; `expr.excluded` must name one. */
|
|
167
|
+
readonly insertColumns?: ReadonlySet<string> | undefined;
|
|
168
|
+
/** Compile a `case` arm's predicate (already pruned and validated) against the target row. */
|
|
169
|
+
predicate(when: Record<string, unknown>): string;
|
|
170
|
+
}
|
|
171
|
+
/** One compiled expression. */
|
|
172
|
+
export interface CompiledSetExpr {
|
|
173
|
+
/** The right-hand side of the assignment. */
|
|
174
|
+
readonly sql: string;
|
|
175
|
+
/**
|
|
176
|
+
* Value-free shape: node kinds, resolved column names, value-leaf positions,
|
|
177
|
+
* each arm's predicate fingerprint and the `now()` rendering. Never a value.
|
|
178
|
+
*/
|
|
179
|
+
readonly fingerprint: string;
|
|
180
|
+
/** Columns of the target row the expression reads (MySQL assignment order). */
|
|
181
|
+
readonly reads: ReadonlySet<string>;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Compile one expression (or `col()` reference) assigned to `env.targetKey`.
|
|
185
|
+
* The single traversal: SQL, params (through `env.bind`, in emission order)
|
|
186
|
+
* and fingerprint come out of one walk.
|
|
187
|
+
*
|
|
188
|
+
* What an expression stores is the same on every engine, by one rule:
|
|
189
|
+
* arithmetic is integer arithmetic when both operands are integers (integer
|
|
190
|
+
* columns, integer numbers, bigints, integer expressions), and integer
|
|
191
|
+
* division truncates toward zero, as `{ divide }` does on an integer column.
|
|
192
|
+
* Any other numeric operand makes it decimal. A non-integer result is refused
|
|
193
|
+
* on an integer column, because the engines disagree on converting one (round
|
|
194
|
+
* half away from zero, round half to even, truncate, or store it unconverted).
|
|
195
|
+
* The class is decided from the operands' types and the bound values' kinds,
|
|
196
|
+
* both part of the fingerprint, so the SQL text never depends on anything the
|
|
197
|
+
* cache key does not capture.
|
|
198
|
+
*/
|
|
199
|
+
export declare function compileSetExpr(env: ExprCompileEnv, value: SqlExpr | ColumnRef): CompiledSetExpr;
|
|
200
|
+
/**
|
|
201
|
+
* Rewrite every `expr.excluded(f)` in a single-row upsert's `update` to the
|
|
202
|
+
* value `create` supplies for `f`. The incoming row of a one-row upsert IS
|
|
203
|
+
* `create`, so the statement path, the lookup-first path (a plain update) and
|
|
204
|
+
* a nested upsert store the same row. A top-level reference becomes
|
|
205
|
+
* `{ set: value }`, which writes the value exactly as a plain value would be
|
|
206
|
+
* written, on every column type (json and array columns take `set` too). A
|
|
207
|
+
* reference inside an expression becomes a value operand, checked as the
|
|
208
|
+
* node's constructor checks one; the expression is then compiled by the
|
|
209
|
+
* update, with the column-type rules every expression gets. Needs only the
|
|
210
|
+
* table's name and metadata, so the nested-write engine shares it. Returns
|
|
211
|
+
* `update` by reference when it holds no reference.
|
|
212
|
+
*/
|
|
213
|
+
export declare function resolveExcludedFromCreate(target: {
|
|
214
|
+
readonly table: string;
|
|
215
|
+
readonly tableMeta: TableMetadata;
|
|
216
|
+
}, update: Record<string, unknown> | undefined, create: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
217
|
+
export {};
|