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,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm: the `lock` read option (`SELECT ... FOR UPDATE OF "t"`).
|
|
4
|
+
*
|
|
5
|
+
* {@link resolveRowLock} is the one place a `lock` value is validated and
|
|
6
|
+
* rendered. The SQL build and the cache key both read its result, so the two
|
|
7
|
+
* cannot disagree about which lock a statement carries.
|
|
8
|
+
*
|
|
9
|
+
* The clause always names the queried table (`OF "t"`). That keeps the lock on
|
|
10
|
+
* the returned rows of that table and nothing else, on every relation
|
|
11
|
+
* strategy: correlated `with` subqueries are never locked anyway, and without
|
|
12
|
+
* `OF` PostgreSQL refuses the `flatten` and lateral-pick plans (their outer
|
|
13
|
+
* joins would put a table on the nullable side of the lock).
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.resolveRowLock = resolveRowLock;
|
|
17
|
+
exports.lockOutsideTransactionMessage = lockOutsideTransactionMessage;
|
|
18
|
+
exports.lockOnStreamMessage = lockOnStreamMessage;
|
|
19
|
+
exports.lockWithDistinctMessage = lockWithDistinctMessage;
|
|
20
|
+
exports.assertNoLockInAutocommitPipeline = assertNoLockInAutocommitPipeline;
|
|
21
|
+
const errors_js_1 = require("../errors.js");
|
|
22
|
+
const global_filters_config_js_1 = require("../global-filters-config.js");
|
|
23
|
+
const utils_js_1 = require("./utils.js");
|
|
24
|
+
const MODES = ['update', 'noKeyUpdate', 'share', 'keyShare'];
|
|
25
|
+
const MODE_SQL = {
|
|
26
|
+
update: 'UPDATE',
|
|
27
|
+
noKeyUpdate: 'NO KEY UPDATE',
|
|
28
|
+
share: 'SHARE',
|
|
29
|
+
keyShare: 'KEY SHARE',
|
|
30
|
+
};
|
|
31
|
+
/** One letter per mode for the SQL-template cache key. */
|
|
32
|
+
const MODE_FP = { update: 'u', noKeyUpdate: 'n', share: 's', keyShare: 'k' };
|
|
33
|
+
const LOCK_KEYS = ['mode', 'noWait', 'skipLocked'];
|
|
34
|
+
/** Why an engine refuses a lock mode, keyed by dialect name. */
|
|
35
|
+
const ENGINE_HINT = {
|
|
36
|
+
sqlite: 'SQLite has no row locks: a write transaction locks the whole database. Remove `lock`; inside ' +
|
|
37
|
+
'$transaction on SQLite, writes are already serialized.',
|
|
38
|
+
mysql: "MySQL supports lock: 'update' and lock: 'share' only. Use 'update' (stronger) or 'share' (weaker).",
|
|
39
|
+
mssql: "Row locks are not available on SQL Server in this release. Use $transaction({ isolationLevel: 'RepeatableRead' }) " +
|
|
40
|
+
'or a raw query with table hints.',
|
|
41
|
+
powdb: 'PowDB has no row locks: a transaction holds the single writer gate.',
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Validate `lock` for a read on `table` and render its clause.
|
|
45
|
+
*
|
|
46
|
+
* `undefined` means no lock. Anything that is not a mode string or a
|
|
47
|
+
* `{ mode, noWait?, skipLocked? }` object is `ValidationError` (E003), and so
|
|
48
|
+
* is asking for both wait policies at once. A mode the dialect does not list in
|
|
49
|
+
* {@link Dialect.rowLockModes} is `UnsupportedFeatureError` (E017).
|
|
50
|
+
*/
|
|
51
|
+
function resolveRowLock(lock, dialect, table) {
|
|
52
|
+
if (lock === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
let mode;
|
|
55
|
+
let noWait;
|
|
56
|
+
let skipLocked;
|
|
57
|
+
if (typeof lock === 'string') {
|
|
58
|
+
mode = lock;
|
|
59
|
+
}
|
|
60
|
+
else if (typeof lock === 'object' && lock !== null && !Array.isArray(lock)) {
|
|
61
|
+
for (const key of Object.keys(lock)) {
|
|
62
|
+
if (!LOCK_KEYS.includes(key)) {
|
|
63
|
+
const hint = (0, utils_js_1.suggestKey)(key, LOCK_KEYS);
|
|
64
|
+
throw new errors_js_1.ValidationError(`lock on "${table}": unknown key "${key}".${hint ? ` Did you mean "${hint}"?` : ''} ` +
|
|
65
|
+
'A lock is a mode, or { mode, noWait: true } or { mode, skipLocked: true }.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
({ mode, noWait, skipLocked } = lock);
|
|
69
|
+
assertFlag(noWait, 'noWait', table);
|
|
70
|
+
assertFlag(skipLocked, 'skipLocked', table);
|
|
71
|
+
if (noWait === true && skipLocked === true) {
|
|
72
|
+
throw new errors_js_1.ValidationError(`lock on "${table}": noWait and skipLocked cannot be combined. noWait fails at once when a row is ` +
|
|
73
|
+
'locked by another transaction; skipLocked leaves that row out of the result. Pick one.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new errors_js_1.ValidationError(`lock on "${table}" must be a mode ('update', 'noKeyUpdate', 'share' or 'keyShare') or ` +
|
|
78
|
+
`{ mode, noWait?, skipLocked? }, received ${describe(lock)}. Leave it out for an unlocked read.`);
|
|
79
|
+
}
|
|
80
|
+
if (typeof mode !== 'string' || !MODES.includes(mode)) {
|
|
81
|
+
const hint = typeof mode === 'string' ? (0, utils_js_1.suggestKey)(mode, MODES) : null;
|
|
82
|
+
throw new errors_js_1.ValidationError(`lock on "${table}": unknown mode ${describe(mode)}.${hint ? ` Did you mean '${hint}'?` : ''} ` +
|
|
83
|
+
"Use 'update', 'noKeyUpdate', 'share' or 'keyShare'.");
|
|
84
|
+
}
|
|
85
|
+
const m = mode;
|
|
86
|
+
if (!(dialect.rowLockModes ?? []).includes(m)) {
|
|
87
|
+
throw new errors_js_1.UnsupportedFeatureError(`lock: '${m}'`, dialect.name, ENGINE_HINT[dialect.name] ?? 'This engine has no row-locking clause Turbine can emit.');
|
|
88
|
+
}
|
|
89
|
+
const wait = noWait === true ? ' NOWAIT' : skipLocked === true ? ' SKIP LOCKED' : '';
|
|
90
|
+
return {
|
|
91
|
+
sql: ` FOR ${MODE_SQL[m]} OF ${dialect.quoteIdentifier(table)}${wait}`,
|
|
92
|
+
fp: `${MODE_FP[m]}${noWait === true ? '.nw' : skipLocked === true ? '.sl' : ''}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The refusal for a locking read outside a transaction. In autocommit the lock
|
|
97
|
+
* is released when the statement ends, before the caller sees the rows, so a
|
|
98
|
+
* `skipLocked` queue claim outside a transaction claims nothing, silently.
|
|
99
|
+
*/
|
|
100
|
+
function lockOutsideTransactionMessage(table, operation) {
|
|
101
|
+
return (`lock on "${table}".${operation}() needs a transaction: outside one the rows are unlocked again before ` +
|
|
102
|
+
`the call returns. Use db.$transaction(async (tx) => tx.${(0, global_filters_config_js_1.tableAccessorName)(table)}.${operation}({ ..., lock })).`);
|
|
103
|
+
}
|
|
104
|
+
/** The refusal for `lock` on a streaming read. */
|
|
105
|
+
function lockOnStreamMessage(table, operation) {
|
|
106
|
+
return (`lock is not supported on "${table}".${operation}(): a stream is not a locking read. Page with ` +
|
|
107
|
+
'findMany({ lock, limit }) inside the transaction instead.');
|
|
108
|
+
}
|
|
109
|
+
/** The refusal for `lock` together with `distinct`. */
|
|
110
|
+
function lockWithDistinctMessage(table) {
|
|
111
|
+
return (`lock on "${table}" cannot be combined with distinct: PostgreSQL does not lock rows of a DISTINCT query. ` +
|
|
112
|
+
'Lock first (findMany with lock), then de-duplicate.');
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Refuse a locking read in a pipeline that runs each statement in autocommit
|
|
116
|
+
* (`pipeline(..., { transactional: false })`). A transactional pipeline, and
|
|
117
|
+
* `$transaction([...])`, keep the lock for the rest of the batch.
|
|
118
|
+
*/
|
|
119
|
+
function assertNoLockInAutocommitPipeline(queries) {
|
|
120
|
+
const at = queries.findIndex((q) => q.rowLock === true);
|
|
121
|
+
if (at === -1)
|
|
122
|
+
return;
|
|
123
|
+
throw new errors_js_1.ValidationError(`pipeline query ${at} (${queries[at]?.tag ?? 'a read'}) is a locking read, and { transactional: false } runs ` +
|
|
124
|
+
'it in autocommit, which releases the lock before the call returns. Drop transactional: false, or run the ' +
|
|
125
|
+
'batch with db.$transaction([...]).');
|
|
126
|
+
}
|
|
127
|
+
function assertFlag(value, name, table) {
|
|
128
|
+
if (value === undefined || typeof value === 'boolean')
|
|
129
|
+
return;
|
|
130
|
+
throw new errors_js_1.ValidationError(`lock on "${table}": ${name} must be true or false, received ${describe(value)}.`);
|
|
131
|
+
}
|
|
132
|
+
function describe(value) {
|
|
133
|
+
if (typeof value === 'string')
|
|
134
|
+
return `'${value}'`;
|
|
135
|
+
if (value === null || typeof value !== 'object')
|
|
136
|
+
return String(value);
|
|
137
|
+
return Array.isArray(value) ? 'an array' : 'an object';
|
|
138
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm: `sqlComment`, sqlcommenter-style key/values on each statement
|
|
3
|
+
* Turbine builds, so `pg_stat_activity`, the server log, `auto_explain` and
|
|
4
|
+
* database APM tools can attribute a statement to a service, a model, a feature
|
|
5
|
+
* or a trace.
|
|
6
|
+
*
|
|
7
|
+
* SELECT ... LIMIT $2 /*action='findMany',application='billing',model='users'*\/
|
|
8
|
+
*
|
|
9
|
+
* FORMAT (the sqlcommenter spec): keys sorted, each value URL-encoded with
|
|
10
|
+
* `encodeURIComponent` and then `'` escaped as `\'`, each pair `key='value'`,
|
|
11
|
+
* pairs joined by commas, the whole comment appended after the statement with
|
|
12
|
+
* one space before it.
|
|
13
|
+
*
|
|
14
|
+
* WHY A VALUE CANNOT ESCAPE THE COMMENT. `encodeURIComponent` encodes `/`, so a
|
|
15
|
+
* value can hold neither `*\/` (the comment's end) nor `/*` (PostgreSQL and SQL
|
|
16
|
+
* Server nest block comments, so an opener would swallow the rest of the
|
|
17
|
+
* text). It also encodes every character a driver-side placeholder rewrite
|
|
18
|
+
* looks for (`$`, `?`, `:`, `@`) and `;`. Keys must match
|
|
19
|
+
* {@link SQL_COMMENT_KEY_PATTERN}, whose first-character rule also rules out
|
|
20
|
+
* MySQL's executable `/*!` and optimizer-hint `/*+` comments.
|
|
21
|
+
*
|
|
22
|
+
* WHERE IT IS APPENDED: at the execute seam, AFTER the SQL-template cache. The
|
|
23
|
+
* cache stores commentless text, so its keys, its hit rate and the lockstep
|
|
24
|
+
* checks are untouched by any comment, and a per-request value can never grow
|
|
25
|
+
* it. `$on('query')` events keep `sql` commentless and carry the comment in
|
|
26
|
+
* their own field.
|
|
27
|
+
*
|
|
28
|
+
* PREPARED-STATEMENT NAMES. node-postgres refuses to reuse a statement name for
|
|
29
|
+
* different text on one connection, and every name a connection prepares stays
|
|
30
|
+
* on the server until the connection closes. So:
|
|
31
|
+
*
|
|
32
|
+
* - no comment: the template's own name, as before;
|
|
33
|
+
* - static, model, action and tag only (constant per call site and label):
|
|
34
|
+
* `c_<16 hex>`, a hash of the template name and the comment, claimed from
|
|
35
|
+
* the same process-wide budget as every other name
|
|
36
|
+
* (`PREPARED_STATEMENT_BUDGET`), so the server-side cost stays bounded
|
|
37
|
+
* however many labels there are;
|
|
38
|
+
* - any dynamic value (a trace id, unique per request): unnamed, parsed on
|
|
39
|
+
* every execution and never kept. A per-request value under a name would
|
|
40
|
+
* prepare a new statement on every pooled connection for every request.
|
|
41
|
+
*
|
|
42
|
+
* Leaf apart from errors.ts, logger.ts, query-events.ts (the `$tag` scope) and
|
|
43
|
+
* utils.ts (the name budget); nothing here is Node-only.
|
|
44
|
+
*/
|
|
45
|
+
import { type TurbineLogger } from '../logger.js';
|
|
46
|
+
import type { DeferredQuery } from './deferred.js';
|
|
47
|
+
/** `TurbineConfig.sqlComment`. */
|
|
48
|
+
export interface SqlCommentOptions {
|
|
49
|
+
/** Constant key/values on every statement this client builds: `{ application: 'billing' }`. */
|
|
50
|
+
static?: Readonly<Record<string, string>> | undefined;
|
|
51
|
+
/** Add `model` (the table) and `action` (`'findMany'`, `'update'`, ...) of the operation. */
|
|
52
|
+
model?: boolean | undefined;
|
|
53
|
+
/** Add the label of the innermost `db.$tag(label, fn)` scope as `tag`. */
|
|
54
|
+
tag?: boolean | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Per-statement values, computed in the caller's async context (for example
|
|
57
|
+
* a W3C `traceparent`). A key whose value is `undefined` is left out. A
|
|
58
|
+
* statement carrying any dynamic value is sent unnamed: parsed on every
|
|
59
|
+
* execution, never kept as a prepared statement. A key or value this
|
|
60
|
+
* function returns that cannot be used is dropped with a warning
|
|
61
|
+
* (`sql-comment-dropped`), and a throw drops all of them: a tracing hook
|
|
62
|
+
* never fails a query.
|
|
63
|
+
*/
|
|
64
|
+
dynamic?: (() => Readonly<Record<string, string | undefined>> | undefined | null) | undefined;
|
|
65
|
+
}
|
|
66
|
+
/** What a comment key must look like. Lower case, so it can never open `/*!` or `/*+`. */
|
|
67
|
+
export declare const SQL_COMMENT_KEY_PATTERN: RegExp;
|
|
68
|
+
/** Longest value accepted, in characters before encoding. */
|
|
69
|
+
export declare const MAX_SQL_COMMENT_VALUE_LENGTH = 256;
|
|
70
|
+
/** One statement's comment. */
|
|
71
|
+
export interface StatementComment {
|
|
72
|
+
/** The comment exactly as appended, without the space before it: `/*key='value',...*\/`. */
|
|
73
|
+
readonly comment: string;
|
|
74
|
+
/** It carries a dynamic value, so the statement goes out unnamed. */
|
|
75
|
+
readonly dynamic: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Builds the comment for each statement and the name it is sent under. One per
|
|
79
|
+
* client, shared by every table accessor and transaction of that client.
|
|
80
|
+
*/
|
|
81
|
+
export declare class SqlCommenter {
|
|
82
|
+
private readonly logger;
|
|
83
|
+
/** Static pairs, encoded and sorted. */
|
|
84
|
+
private readonly staticPairs;
|
|
85
|
+
/** Keys a dynamic value may not take: the static ones and the ones model / tag fill in. */
|
|
86
|
+
private readonly reservedKeys;
|
|
87
|
+
private readonly withModel;
|
|
88
|
+
private readonly withTag;
|
|
89
|
+
private readonly dynamic;
|
|
90
|
+
/** The comment when it depends on nothing per statement (no model, no tag). */
|
|
91
|
+
private readonly constant;
|
|
92
|
+
/** Rendered model / action / tag comments. Bounded: tags may come from data. */
|
|
93
|
+
private readonly rendered;
|
|
94
|
+
/** Claimed `c_` names by template name and comment. Holds only claimed names, so the budget bounds it. */
|
|
95
|
+
private readonly names;
|
|
96
|
+
/**
|
|
97
|
+
* Every statement gets a comment (a static key, or the model and action),
|
|
98
|
+
* so a template's own name is never sent and need not be claimed from the
|
|
99
|
+
* budget; the commented name is claimed instead.
|
|
100
|
+
*/
|
|
101
|
+
readonly alwaysComments: boolean;
|
|
102
|
+
constructor(staticPairs: ReadonlyArray<readonly [string, string]>, withModel: boolean, withTag: boolean, dynamic: SqlCommentOptions['dynamic'], logger: TurbineLogger);
|
|
103
|
+
/**
|
|
104
|
+
* The comment for one statement of operation `action` on `model`, or
|
|
105
|
+
* `undefined` when it carries none. Reads the `$tag` scope and calls
|
|
106
|
+
* `dynamic` in the caller's async context, so call it where the statement
|
|
107
|
+
* is sent.
|
|
108
|
+
*/
|
|
109
|
+
commentFor(model: string, action: string): StatementComment | undefined;
|
|
110
|
+
/**
|
|
111
|
+
* The name a statement with comment `c` is sent under, given its template's
|
|
112
|
+
* name. A template sent unnamed stays unnamed; a dynamic comment is unnamed;
|
|
113
|
+
* anything else gets a stable `c_` name from the process-wide budget, or
|
|
114
|
+
* goes unnamed once that is spent.
|
|
115
|
+
*/
|
|
116
|
+
preparedName(templateName: string | undefined, c: StatementComment): string | undefined;
|
|
117
|
+
/** The usable dynamic pairs, encoded, or `undefined` when there are none. */
|
|
118
|
+
private dynamicPairs;
|
|
119
|
+
/** Why a dynamic key or value cannot be used, as [warning key, sentence], or `undefined`. */
|
|
120
|
+
private refusal;
|
|
121
|
+
/** One warning per reason and key, so a hook that misbehaves on every statement says it once. */
|
|
122
|
+
private dropped;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Validate `TurbineConfig.sqlComment` and build its commenter, or `undefined`
|
|
126
|
+
* when there is nothing to add. Throws `ValidationError` (E003) for a malformed
|
|
127
|
+
* option, and `UnsupportedFeatureError` (E017) where it cannot be honoured:
|
|
128
|
+
*
|
|
129
|
+
* - a backend that builds no SQL (PowDB, which injects its own query
|
|
130
|
+
* interface) has nothing to comment;
|
|
131
|
+
* - `dynamic` on an engine that prepares every statement text
|
|
132
|
+
* (`preparesEveryStatement`), which is MySQL: the MySQL engine sends each
|
|
133
|
+
* parameterized statement through mysql2's `execute()`, which keeps one
|
|
134
|
+
* SERVER-side prepared
|
|
135
|
+
* statement per distinct text per connection (up to 16,000 each), and the
|
|
136
|
+
* server caps the total across every client at `max_prepared_stmt_count`
|
|
137
|
+
* (16,382 by default). A value unique per request makes every text
|
|
138
|
+
* distinct, so a busy pool reaches that cap (error 1461) and every client
|
|
139
|
+
* of the server then fails to prepare.
|
|
140
|
+
*/
|
|
141
|
+
export declare function resolveSqlComment(value: unknown, logger: TurbineLogger, engine: {
|
|
142
|
+
readonly dialect: string;
|
|
143
|
+
readonly preparesEveryStatement: boolean;
|
|
144
|
+
readonly buildsSql: boolean;
|
|
145
|
+
}): SqlCommenter | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* `sql` with a comment Turbine appended removed, for code that reads statement
|
|
148
|
+
* text to decide how to run it (an engine shim telling a row-returning
|
|
149
|
+
* statement from a change count). A value can never contain `/*` (it is
|
|
150
|
+
* URL-encoded), so the last ` /*` of a statement ending in `*\/` is the
|
|
151
|
+
* comment's own opener; text Turbine did not comment is returned unchanged
|
|
152
|
+
* unless it, too, ends in a block comment, which is equally not code.
|
|
153
|
+
*/
|
|
154
|
+
export declare function withoutTrailingComment(sql: string): string;
|
|
155
|
+
/**
|
|
156
|
+
* `q` as sent with `comment`: its statement, and every statement of its
|
|
157
|
+
* reselect plan (MySQL), carry the comment. Everything else on the query is
|
|
158
|
+
* kept. Used by the batch paths (`pipeline`, `$transaction([...])`), which run
|
|
159
|
+
* deferred queries without going through a query interface.
|
|
160
|
+
*/
|
|
161
|
+
export declare function commentDeferred<T>(q: DeferredQuery<T>, comment: string | undefined): DeferredQuery<T>;
|