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,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* turbine-orm: `sqlComment`, sqlcommenter-style key/values on each statement
|
|
4
|
+
* Turbine builds, so `pg_stat_activity`, the server log, `auto_explain` and
|
|
5
|
+
* database APM tools can attribute a statement to a service, a model, a feature
|
|
6
|
+
* or a trace.
|
|
7
|
+
*
|
|
8
|
+
* SELECT ... LIMIT $2 /*action='findMany',application='billing',model='users'*\/
|
|
9
|
+
*
|
|
10
|
+
* FORMAT (the sqlcommenter spec): keys sorted, each value URL-encoded with
|
|
11
|
+
* `encodeURIComponent` and then `'` escaped as `\'`, each pair `key='value'`,
|
|
12
|
+
* pairs joined by commas, the whole comment appended after the statement with
|
|
13
|
+
* one space before it.
|
|
14
|
+
*
|
|
15
|
+
* WHY A VALUE CANNOT ESCAPE THE COMMENT. `encodeURIComponent` encodes `/`, so a
|
|
16
|
+
* value can hold neither `*\/` (the comment's end) nor `/*` (PostgreSQL and SQL
|
|
17
|
+
* Server nest block comments, so an opener would swallow the rest of the
|
|
18
|
+
* text). It also encodes every character a driver-side placeholder rewrite
|
|
19
|
+
* looks for (`$`, `?`, `:`, `@`) and `;`. Keys must match
|
|
20
|
+
* {@link SQL_COMMENT_KEY_PATTERN}, whose first-character rule also rules out
|
|
21
|
+
* MySQL's executable `/*!` and optimizer-hint `/*+` comments.
|
|
22
|
+
*
|
|
23
|
+
* WHERE IT IS APPENDED: at the execute seam, AFTER the SQL-template cache. The
|
|
24
|
+
* cache stores commentless text, so its keys, its hit rate and the lockstep
|
|
25
|
+
* checks are untouched by any comment, and a per-request value can never grow
|
|
26
|
+
* it. `$on('query')` events keep `sql` commentless and carry the comment in
|
|
27
|
+
* their own field.
|
|
28
|
+
*
|
|
29
|
+
* PREPARED-STATEMENT NAMES. node-postgres refuses to reuse a statement name for
|
|
30
|
+
* different text on one connection, and every name a connection prepares stays
|
|
31
|
+
* on the server until the connection closes. So:
|
|
32
|
+
*
|
|
33
|
+
* - no comment: the template's own name, as before;
|
|
34
|
+
* - static, model, action and tag only (constant per call site and label):
|
|
35
|
+
* `c_<16 hex>`, a hash of the template name and the comment, claimed from
|
|
36
|
+
* the same process-wide budget as every other name
|
|
37
|
+
* (`PREPARED_STATEMENT_BUDGET`), so the server-side cost stays bounded
|
|
38
|
+
* however many labels there are;
|
|
39
|
+
* - any dynamic value (a trace id, unique per request): unnamed, parsed on
|
|
40
|
+
* every execution and never kept. A per-request value under a name would
|
|
41
|
+
* prepare a new statement on every pooled connection for every request.
|
|
42
|
+
*
|
|
43
|
+
* Leaf apart from errors.ts, logger.ts, query-events.ts (the `$tag` scope) and
|
|
44
|
+
* utils.ts (the name budget); nothing here is Node-only.
|
|
45
|
+
*/
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.SqlCommenter = exports.MAX_SQL_COMMENT_VALUE_LENGTH = exports.SQL_COMMENT_KEY_PATTERN = void 0;
|
|
48
|
+
exports.resolveSqlComment = resolveSqlComment;
|
|
49
|
+
exports.withoutTrailingComment = withoutTrailingComment;
|
|
50
|
+
exports.commentDeferred = commentDeferred;
|
|
51
|
+
const errors_js_1 = require("../errors.js");
|
|
52
|
+
const logger_js_1 = require("../logger.js");
|
|
53
|
+
const query_events_js_1 = require("../query-events.js");
|
|
54
|
+
const utils_js_1 = require("./utils.js");
|
|
55
|
+
const warn_registry_js_1 = require("./warn-registry.js");
|
|
56
|
+
/** What a comment key must look like. Lower case, so it can never open `/*!` or `/*+`. */
|
|
57
|
+
exports.SQL_COMMENT_KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
58
|
+
/** Longest value accepted, in characters before encoding. */
|
|
59
|
+
exports.MAX_SQL_COMMENT_VALUE_LENGTH = 256;
|
|
60
|
+
/** The keys `model: true` and `tag: true` fill in. */
|
|
61
|
+
const MODEL_KEYS = ['action', 'model'];
|
|
62
|
+
const TAG_KEY = 'tag';
|
|
63
|
+
const OPTION_KEYS = new Set(['static', 'model', 'tag', 'dynamic']);
|
|
64
|
+
/** Once-only warning namespace for dropped dynamic values. */
|
|
65
|
+
const DROPPED_WARN_NS = 'sqlCommentDropped';
|
|
66
|
+
/**
|
|
67
|
+
* `value` URL-encoded with `'` escaped, or `undefined` when it cannot be encoded
|
|
68
|
+
* (a lone surrogate). `encodeURIComponent` leaves `'` as is and never emits a
|
|
69
|
+
* backslash; the escape covers both anyway, so it stays complete on its own.
|
|
70
|
+
*/
|
|
71
|
+
function encodeValue(value) {
|
|
72
|
+
try {
|
|
73
|
+
return encodeURIComponent(value).replace(/[\\']/g, '\\$&');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function render(pairs) {
|
|
80
|
+
let out = '/*';
|
|
81
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
82
|
+
const [key, value] = pairs[i];
|
|
83
|
+
out += `${i === 0 ? '' : ','}${key}='${value}'`;
|
|
84
|
+
}
|
|
85
|
+
return `${out}*/`;
|
|
86
|
+
}
|
|
87
|
+
function byKey(a, b) {
|
|
88
|
+
return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
89
|
+
}
|
|
90
|
+
function isPlainObject(value) {
|
|
91
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
92
|
+
return false;
|
|
93
|
+
const proto = Object.getPrototypeOf(value);
|
|
94
|
+
return proto === Object.prototype || proto === null;
|
|
95
|
+
}
|
|
96
|
+
/** A short, printable rendering of a caller's key for a message. */
|
|
97
|
+
function describeKey(key) {
|
|
98
|
+
return JSON.stringify(key.length > 64 ? `${key.slice(0, 64)}...` : key);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Builds the comment for each statement and the name it is sent under. One per
|
|
102
|
+
* client, shared by every table accessor and transaction of that client.
|
|
103
|
+
*/
|
|
104
|
+
class SqlCommenter {
|
|
105
|
+
logger;
|
|
106
|
+
/** Static pairs, encoded and sorted. */
|
|
107
|
+
staticPairs;
|
|
108
|
+
/** Keys a dynamic value may not take: the static ones and the ones model / tag fill in. */
|
|
109
|
+
reservedKeys;
|
|
110
|
+
withModel;
|
|
111
|
+
withTag;
|
|
112
|
+
dynamic;
|
|
113
|
+
/** The comment when it depends on nothing per statement (no model, no tag). */
|
|
114
|
+
constant;
|
|
115
|
+
/** Rendered model / action / tag comments. Bounded: tags may come from data. */
|
|
116
|
+
rendered = new utils_js_1.LRUCache(1000);
|
|
117
|
+
/** Claimed `c_` names by template name and comment. Holds only claimed names, so the budget bounds it. */
|
|
118
|
+
names = new Map();
|
|
119
|
+
/**
|
|
120
|
+
* Every statement gets a comment (a static key, or the model and action),
|
|
121
|
+
* so a template's own name is never sent and need not be claimed from the
|
|
122
|
+
* budget; the commented name is claimed instead.
|
|
123
|
+
*/
|
|
124
|
+
alwaysComments;
|
|
125
|
+
constructor(staticPairs, withModel, withTag, dynamic, logger) {
|
|
126
|
+
this.logger = logger;
|
|
127
|
+
this.staticPairs = [...staticPairs].sort(byKey);
|
|
128
|
+
this.withModel = withModel;
|
|
129
|
+
this.withTag = withTag;
|
|
130
|
+
this.dynamic = dynamic;
|
|
131
|
+
const reserved = new Set(this.staticPairs.map(([k]) => k));
|
|
132
|
+
if (withModel)
|
|
133
|
+
for (const k of MODEL_KEYS)
|
|
134
|
+
reserved.add(k);
|
|
135
|
+
if (withTag)
|
|
136
|
+
reserved.add(TAG_KEY);
|
|
137
|
+
this.reservedKeys = reserved;
|
|
138
|
+
this.alwaysComments = this.staticPairs.length > 0 || withModel;
|
|
139
|
+
this.constant =
|
|
140
|
+
!withModel && !withTag && this.staticPairs.length > 0
|
|
141
|
+
? { comment: render(this.staticPairs), dynamic: false }
|
|
142
|
+
: undefined;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The comment for one statement of operation `action` on `model`, or
|
|
146
|
+
* `undefined` when it carries none. Reads the `$tag` scope and calls
|
|
147
|
+
* `dynamic` in the caller's async context, so call it where the statement
|
|
148
|
+
* is sent.
|
|
149
|
+
*/
|
|
150
|
+
commentFor(model, action) {
|
|
151
|
+
let pairs;
|
|
152
|
+
let base;
|
|
153
|
+
if (this.withModel || this.withTag) {
|
|
154
|
+
const tag = this.withTag ? (0, query_events_js_1.currentQueryTag)() : undefined;
|
|
155
|
+
const key = `${model}\u0000${action}\u0000${tag ?? ''}`;
|
|
156
|
+
let hit = this.rendered.get(key);
|
|
157
|
+
if (hit === undefined) {
|
|
158
|
+
const built = [...this.staticPairs];
|
|
159
|
+
if (this.withModel) {
|
|
160
|
+
built.push(['action', encodeValue(action) ?? ''], ['model', encodeValue(model) ?? '']);
|
|
161
|
+
}
|
|
162
|
+
// A `$tag` label is 1 to 128 characters, checked when the scope opens.
|
|
163
|
+
const encodedTag = tag === undefined ? undefined : encodeValue(tag);
|
|
164
|
+
if (encodedTag !== undefined)
|
|
165
|
+
built.push([TAG_KEY, encodedTag]);
|
|
166
|
+
built.sort(byKey);
|
|
167
|
+
hit = { pairs: built, comment: built.length > 0 ? render(built) : '' };
|
|
168
|
+
this.rendered.set(key, hit);
|
|
169
|
+
}
|
|
170
|
+
pairs = hit.pairs;
|
|
171
|
+
base = hit.comment === '' ? undefined : { comment: hit.comment, dynamic: false };
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
pairs = this.staticPairs;
|
|
175
|
+
base = this.constant;
|
|
176
|
+
}
|
|
177
|
+
if (this.dynamic === undefined)
|
|
178
|
+
return base;
|
|
179
|
+
const extra = this.dynamicPairs();
|
|
180
|
+
if (extra === undefined)
|
|
181
|
+
return base;
|
|
182
|
+
return { comment: render([...pairs, ...extra].sort(byKey)), dynamic: true };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The name a statement with comment `c` is sent under, given its template's
|
|
186
|
+
* name. A template sent unnamed stays unnamed; a dynamic comment is unnamed;
|
|
187
|
+
* anything else gets a stable `c_` name from the process-wide budget, or
|
|
188
|
+
* goes unnamed once that is spent.
|
|
189
|
+
*/
|
|
190
|
+
preparedName(templateName, c) {
|
|
191
|
+
if (!templateName)
|
|
192
|
+
return templateName;
|
|
193
|
+
if (c.dynamic)
|
|
194
|
+
return '';
|
|
195
|
+
const key = `${templateName}\u0000${c.comment}`;
|
|
196
|
+
const known = this.names.get(key);
|
|
197
|
+
if (known !== undefined)
|
|
198
|
+
return known;
|
|
199
|
+
const name = (0, utils_js_1.claimPreparedName)(`c_${(0, utils_js_1.fnv1a64Hex)(key)}`);
|
|
200
|
+
if (name !== '')
|
|
201
|
+
this.names.set(key, name);
|
|
202
|
+
return name;
|
|
203
|
+
}
|
|
204
|
+
/** The usable dynamic pairs, encoded, or `undefined` when there are none. */
|
|
205
|
+
dynamicPairs() {
|
|
206
|
+
let values;
|
|
207
|
+
try {
|
|
208
|
+
values = this.dynamic?.();
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
this.dropped('threw', '', `sqlComment.dynamic threw (${err instanceof Error ? err.message : String(err)}), so ` +
|
|
212
|
+
'this statement carries no dynamic values. The statement itself ran as usual.');
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
if (values === undefined || values === null)
|
|
216
|
+
return undefined;
|
|
217
|
+
if (typeof values !== 'object' || Array.isArray(values)) {
|
|
218
|
+
this.dropped('shape', '', 'sqlComment.dynamic returned something other than an object of string values, so this statement carries ' +
|
|
219
|
+
'no dynamic values. Return an object such as { traceparent: "00-..." }, or undefined for none.');
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
let out;
|
|
223
|
+
for (const key of Object.keys(values)) {
|
|
224
|
+
const value = values[key];
|
|
225
|
+
if (value === undefined)
|
|
226
|
+
continue;
|
|
227
|
+
const reason = this.refusal(key, value);
|
|
228
|
+
if (reason !== undefined) {
|
|
229
|
+
this.dropped(reason[0], key, `sqlComment.dynamic key ${describeKey(key)} was left out of the comment: ${reason[1]}`);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const encoded = encodeValue(value);
|
|
233
|
+
if (encoded === undefined) {
|
|
234
|
+
this.dropped('encoding', key, `sqlComment.dynamic key ${describeKey(key)} was left out of the comment: its value is not well-formed ` +
|
|
235
|
+
'Unicode (a lone surrogate), so it cannot be URL-encoded.');
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (out === undefined)
|
|
239
|
+
out = [];
|
|
240
|
+
out.push([key, encoded]);
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
/** Why a dynamic key or value cannot be used, as [warning key, sentence], or `undefined`. */
|
|
245
|
+
refusal(key, value) {
|
|
246
|
+
if (!exports.SQL_COMMENT_KEY_PATTERN.test(key)) {
|
|
247
|
+
return ['key', 'a key must be lower-case letters, digits and underscores, starting with a letter.'];
|
|
248
|
+
}
|
|
249
|
+
if (this.reservedKeys.has(key)) {
|
|
250
|
+
return ['reserved', 'that key is already set by sqlComment.static, model or tag, which take precedence.'];
|
|
251
|
+
}
|
|
252
|
+
if (typeof value !== 'string')
|
|
253
|
+
return ['type', `its value is ${typeof value}, not a string.`];
|
|
254
|
+
if (value.length > exports.MAX_SQL_COMMENT_VALUE_LENGTH) {
|
|
255
|
+
return ['length', `its value is ${value.length} characters, over the ${exports.MAX_SQL_COMMENT_VALUE_LENGTH} allowed.`];
|
|
256
|
+
}
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
/** One warning per reason and key, so a hook that misbehaves on every statement says it once. */
|
|
260
|
+
dropped(reason, key, message) {
|
|
261
|
+
if (!(0, warn_registry_js_1.shouldWarnOnce)(DROPPED_WARN_NS, `${reason}|${key.slice(0, 64)}`))
|
|
262
|
+
return;
|
|
263
|
+
(0, logger_js_1.logTo)(this.logger, 'warn', message, { code: 'sql-comment-dropped', reason });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
exports.SqlCommenter = SqlCommenter;
|
|
267
|
+
/**
|
|
268
|
+
* Validate `TurbineConfig.sqlComment` and build its commenter, or `undefined`
|
|
269
|
+
* when there is nothing to add. Throws `ValidationError` (E003) for a malformed
|
|
270
|
+
* option, and `UnsupportedFeatureError` (E017) where it cannot be honoured:
|
|
271
|
+
*
|
|
272
|
+
* - a backend that builds no SQL (PowDB, which injects its own query
|
|
273
|
+
* interface) has nothing to comment;
|
|
274
|
+
* - `dynamic` on an engine that prepares every statement text
|
|
275
|
+
* (`preparesEveryStatement`), which is MySQL: the MySQL engine sends each
|
|
276
|
+
* parameterized statement through mysql2's `execute()`, which keeps one
|
|
277
|
+
* SERVER-side prepared
|
|
278
|
+
* statement per distinct text per connection (up to 16,000 each), and the
|
|
279
|
+
* server caps the total across every client at `max_prepared_stmt_count`
|
|
280
|
+
* (16,382 by default). A value unique per request makes every text
|
|
281
|
+
* distinct, so a busy pool reaches that cap (error 1461) and every client
|
|
282
|
+
* of the server then fails to prepare.
|
|
283
|
+
*/
|
|
284
|
+
function resolveSqlComment(value, logger, engine) {
|
|
285
|
+
if (value === undefined)
|
|
286
|
+
return undefined;
|
|
287
|
+
if (!isPlainObject(value)) {
|
|
288
|
+
throw new errors_js_1.ValidationError('Invalid sqlComment: expected an object with any of `static`, `model`, `tag` and `dynamic`, for example ' +
|
|
289
|
+
"{ static: { application: 'billing' }, model: true }.");
|
|
290
|
+
}
|
|
291
|
+
for (const key of Object.keys(value)) {
|
|
292
|
+
if (!OPTION_KEYS.has(key)) {
|
|
293
|
+
throw new errors_js_1.ValidationError(`Invalid sqlComment: unknown key ${describeKey(key)}. The keys are \`static\`, \`model\`, \`tag\` and \`dynamic\`.`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const { static: staticValues, model, tag, dynamic } = value;
|
|
297
|
+
for (const [name, flag] of [
|
|
298
|
+
['model', model],
|
|
299
|
+
['tag', tag],
|
|
300
|
+
]) {
|
|
301
|
+
if (flag !== undefined && typeof flag !== 'boolean') {
|
|
302
|
+
throw new errors_js_1.ValidationError(`Invalid sqlComment.${name}: expected true or false, got ${typeof flag}.`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (dynamic !== undefined && typeof dynamic !== 'function') {
|
|
306
|
+
throw new errors_js_1.ValidationError(`Invalid sqlComment.dynamic: expected a function returning an object of string values, got ${typeof dynamic}.`);
|
|
307
|
+
}
|
|
308
|
+
const pairs = [];
|
|
309
|
+
if (staticValues !== undefined) {
|
|
310
|
+
if (!isPlainObject(staticValues)) {
|
|
311
|
+
throw new errors_js_1.ValidationError("Invalid sqlComment.static: expected an object of string values, for example { application: 'billing' }.");
|
|
312
|
+
}
|
|
313
|
+
for (const key of Object.keys(staticValues)) {
|
|
314
|
+
const v = staticValues[key];
|
|
315
|
+
const where = `sqlComment.static key ${describeKey(key)}`;
|
|
316
|
+
if (!exports.SQL_COMMENT_KEY_PATTERN.test(key)) {
|
|
317
|
+
throw new errors_js_1.ValidationError(`Invalid ${where}: a key must be lower-case letters, digits and underscores, starting with a letter ` +
|
|
318
|
+
'(the comment must never open a MySQL executable or hint comment, `/*!` or `/*+`).');
|
|
319
|
+
}
|
|
320
|
+
if ((model === true && MODEL_KEYS.includes(key)) || (tag === true && key === TAG_KEY)) {
|
|
321
|
+
throw new errors_js_1.ValidationError(`Invalid ${where}: \`${key}\` is filled in by sqlComment.${key === TAG_KEY ? 'tag' : 'model'}. ` +
|
|
322
|
+
'Rename the static key or turn that option off.');
|
|
323
|
+
}
|
|
324
|
+
if (typeof v !== 'string') {
|
|
325
|
+
throw new errors_js_1.ValidationError(`Invalid ${where}: expected a string value, got ${typeof v}.`);
|
|
326
|
+
}
|
|
327
|
+
if (v.length > exports.MAX_SQL_COMMENT_VALUE_LENGTH) {
|
|
328
|
+
throw new errors_js_1.ValidationError(`Invalid ${where}: the value is ${v.length} characters, over the ${exports.MAX_SQL_COMMENT_VALUE_LENGTH} allowed.`);
|
|
329
|
+
}
|
|
330
|
+
const encoded = encodeValue(v);
|
|
331
|
+
if (encoded === undefined) {
|
|
332
|
+
throw new errors_js_1.ValidationError(`Invalid ${where}: the value is not well-formed Unicode (a lone surrogate), so it cannot be URL-encoded.`);
|
|
333
|
+
}
|
|
334
|
+
pairs.push([key, encoded]);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (pairs.length === 0 && model !== true && tag !== true && dynamic === undefined)
|
|
338
|
+
return undefined;
|
|
339
|
+
if (!engine.buildsSql) {
|
|
340
|
+
throw new errors_js_1.UnsupportedFeatureError('sqlComment', engine.dialect, 'This engine does not run SQL, so there is no statement to carry a comment. Remove the option for this client.');
|
|
341
|
+
}
|
|
342
|
+
if (dynamic !== undefined && engine.preparesEveryStatement) {
|
|
343
|
+
throw new errors_js_1.UnsupportedFeatureError('sqlComment.dynamic', engine.dialect, 'The MySQL engine prepares each distinct statement text on the server (mysql2 execute(), up to 16,000 per ' +
|
|
344
|
+
'connection), and the server caps prepared statements across all clients at max_prepared_stmt_count ' +
|
|
345
|
+
'(16,382 by default). A per-statement value makes every text distinct, so a busy pool reaches that cap ' +
|
|
346
|
+
'(error 1461) and prepares start failing server-wide. Use `static`, `model` and `tag`, which are bounded.');
|
|
347
|
+
}
|
|
348
|
+
return new SqlCommenter(pairs, model === true, tag === true, dynamic, logger);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* `sql` with a comment Turbine appended removed, for code that reads statement
|
|
352
|
+
* text to decide how to run it (an engine shim telling a row-returning
|
|
353
|
+
* statement from a change count). A value can never contain `/*` (it is
|
|
354
|
+
* URL-encoded), so the last ` /*` of a statement ending in `*\/` is the
|
|
355
|
+
* comment's own opener; text Turbine did not comment is returned unchanged
|
|
356
|
+
* unless it, too, ends in a block comment, which is equally not code.
|
|
357
|
+
*/
|
|
358
|
+
function withoutTrailingComment(sql) {
|
|
359
|
+
if (!sql.endsWith('*/'))
|
|
360
|
+
return sql;
|
|
361
|
+
const at = sql.lastIndexOf(' /*');
|
|
362
|
+
return at < 0 ? sql : sql.slice(0, at);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* `q` as sent with `comment`: its statement, and every statement of its
|
|
366
|
+
* reselect plan (MySQL), carry the comment. Everything else on the query is
|
|
367
|
+
* kept. Used by the batch paths (`pipeline`, `$transaction([...])`), which run
|
|
368
|
+
* deferred queries without going through a query interface.
|
|
369
|
+
*/
|
|
370
|
+
function commentDeferred(q, comment) {
|
|
371
|
+
if (comment === undefined)
|
|
372
|
+
return q;
|
|
373
|
+
const reselect = q.reselect;
|
|
374
|
+
return {
|
|
375
|
+
...q,
|
|
376
|
+
sql: `${q.sql} ${comment}`,
|
|
377
|
+
...(reselect
|
|
378
|
+
? { reselect: (exec) => reselect((sql, params, name) => exec(`${sql} ${comment}`, params, name)) }
|
|
379
|
+
: {}),
|
|
380
|
+
};
|
|
381
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm: the abandoned-stream idle guard.
|
|
3
|
+
*
|
|
4
|
+
* A cursor stream holds a checked-out connection between pulls (and, on
|
|
5
|
+
* PostgreSQL, an open transaction whose snapshot pins the xmin horizon for the
|
|
6
|
+
* whole database). A consumer that stops pulling without calling `return()`
|
|
7
|
+
* leaves the stream suspended at a `yield` forever, and a suspended generator
|
|
8
|
+
* runs no code, so nothing would ever release either. This guard is the code
|
|
9
|
+
* that runs instead: a timer that notices the consumer has asked for nothing for
|
|
10
|
+
* the configured time and closes the stream from outside.
|
|
11
|
+
*
|
|
12
|
+
* The consuming generator owns the {@link StreamIdleState}: it sets `suspended`
|
|
13
|
+
* around each yield and bumps `pulls` when the consumer resumes it. That is two
|
|
14
|
+
* writes per yield and no timer work, which is what keeps a per-row stream as
|
|
15
|
+
* cheap as before (a timer refreshed per row costs about 15% per row; a
|
|
16
|
+
* `setTimeout` per row more than doubles it).
|
|
17
|
+
*
|
|
18
|
+
* Leaf module: imports errors.ts only, so it stays in the serverless graph
|
|
19
|
+
* without pulling anything node-only.
|
|
20
|
+
*/
|
|
21
|
+
import { TimeoutError } from '../errors.js';
|
|
22
|
+
/** The default for `streamIdleTimeoutMs` (client) and `idleTimeoutMs` (per call). */
|
|
23
|
+
export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
|
|
24
|
+
/**
|
|
25
|
+
* How much later than the idle timeout the PostgreSQL server-side limit fires.
|
|
26
|
+
* The client timer always expires a stream first (it ticks at most every
|
|
27
|
+
* {@link MAX_TICK_MS}, half this grace), so the server limit only acts when the
|
|
28
|
+
* process has stopped running timers: frozen between serverless invocations,
|
|
29
|
+
* or an event loop blocked for longer than the grace.
|
|
30
|
+
*/
|
|
31
|
+
export declare const STREAM_IDLE_BACKSTOP_GRACE_MS = 10000;
|
|
32
|
+
/**
|
|
33
|
+
* The largest idle timeout accepted. PostgreSQL's
|
|
34
|
+
* `idle_in_transaction_session_timeout` is a 32-bit count of milliseconds, and
|
|
35
|
+
* the backstop adds the grace to it.
|
|
36
|
+
*/
|
|
37
|
+
export declare const MAX_STREAM_IDLE_TIMEOUT_MS: number;
|
|
38
|
+
/**
|
|
39
|
+
* Validate an idle timeout: a whole number of milliseconds from 0 (disabled)
|
|
40
|
+
* to {@link MAX_STREAM_IDLE_TIMEOUT_MS}, else E003. `undefined` means "not
|
|
41
|
+
* set here", so the caller falls back to the next level.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveStreamIdleTimeout(value: unknown, name: string): number | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* What the generator that yields to the consumer tells the guard. Written
|
|
46
|
+
* around every yield; read by the guard's timer.
|
|
47
|
+
*/
|
|
48
|
+
export interface StreamIdleState {
|
|
49
|
+
/** How many times the consumer has resumed the stream. */
|
|
50
|
+
pulls: number;
|
|
51
|
+
/** The stream is suspended at a yield, waiting for the consumer to ask for more. */
|
|
52
|
+
suspended: boolean;
|
|
53
|
+
/** Rows handed to the consumer so far, for the log line. */
|
|
54
|
+
rows: number;
|
|
55
|
+
/** Set when the guard closed the stream; the next pull throws it. */
|
|
56
|
+
expired: TimeoutError | undefined;
|
|
57
|
+
}
|
|
58
|
+
export declare function newStreamIdleState(): StreamIdleState;
|
|
59
|
+
/** The E002 the next pull on an expired stream throws. */
|
|
60
|
+
export declare function streamIdleExpiredError(action: string, table: string, timeoutMs: number, unit: string): TimeoutError;
|
|
61
|
+
/**
|
|
62
|
+
* The PostgreSQL server-side limit for a stream's own transaction:
|
|
63
|
+
* `idle_in_transaction_session_timeout` set to the idle timeout plus the grace,
|
|
64
|
+
* TRANSACTION-LOCAL (`set_config(..., true)`), so it ends with the stream's
|
|
65
|
+
* COMMIT and cannot reach another client through a transaction pooler.
|
|
66
|
+
*
|
|
67
|
+
* A stricter limit already in force (a role or server setting below ours) is
|
|
68
|
+
* kept, so a stream never loosens what the operator configured. The value is a
|
|
69
|
+
* validated integer, never caller text: a GUC value cannot be a bind parameter.
|
|
70
|
+
* Reading `pg_settings` rather than calling `set_config` directly also means an
|
|
71
|
+
* engine without the setting gets no row and no error.
|
|
72
|
+
*/
|
|
73
|
+
export declare function streamBackstopStatement(timeoutMs: number): string;
|
|
74
|
+
/**
|
|
75
|
+
* The timer half of the guard. One per open cursor stream, `unref()`ed so it
|
|
76
|
+
* never keeps a process alive.
|
|
77
|
+
*
|
|
78
|
+
* It ticks every `min(timeout / 4, 5 s)`. A tick that finds the stream
|
|
79
|
+
* suspended with no pull since the previous tick counts as idle; enough idle
|
|
80
|
+
* ticks in a row to cover the timeout expire the stream, so expiry lands
|
|
81
|
+
* between 1x the timeout and the timeout plus one tick. Time Turbine spends
|
|
82
|
+
* fetching never counts, because the stream is not suspended then.
|
|
83
|
+
*
|
|
84
|
+
* With `keepalive` set (PostgreSQL, where {@link streamBackstopStatement} is
|
|
85
|
+
* in force), a tick also sends a no-op statement when the connection has been
|
|
86
|
+
* quiet for half the timeout. A consumer that pulls rows of an already-fetched
|
|
87
|
+
* batch slowly is alive while the server sees an idle transaction; without the
|
|
88
|
+
* keepalive the server limit would end a stream the consumer is still reading.
|
|
89
|
+
* The server limit therefore only fires when this timer cannot run.
|
|
90
|
+
*/
|
|
91
|
+
export declare class StreamIdleGuard {
|
|
92
|
+
private readonly state;
|
|
93
|
+
private readonly timeoutMs;
|
|
94
|
+
private readonly expiredError;
|
|
95
|
+
private readonly onExpire;
|
|
96
|
+
private readonly keepalive?;
|
|
97
|
+
private readonly timer;
|
|
98
|
+
private readonly idleTicksToExpire;
|
|
99
|
+
private lastPulls;
|
|
100
|
+
private idleTicks;
|
|
101
|
+
private lastStatementAt;
|
|
102
|
+
private statementsInFlight;
|
|
103
|
+
private keepaliveInFlight;
|
|
104
|
+
/** Settles once an expired stream's cleanup has run (connection released). */
|
|
105
|
+
expiry: Promise<void> | undefined;
|
|
106
|
+
constructor(state: StreamIdleState, timeoutMs: number, expiredError: () => TimeoutError, onExpire: () => Promise<void>, keepalive?: (() => Promise<unknown>) | undefined);
|
|
107
|
+
/** Bracket a statement on the stream's connection, so the keepalive knows it is busy. */
|
|
108
|
+
statementStarted(): void;
|
|
109
|
+
statementSettled(): void;
|
|
110
|
+
stop(): void;
|
|
111
|
+
private tick;
|
|
112
|
+
}
|
|
113
|
+
/** The connection surface a stream drives; structurally `StreamableConnection` in dialect.ts. */
|
|
114
|
+
interface StreamConnection {
|
|
115
|
+
query(text: string, values?: unknown[]): Promise<{
|
|
116
|
+
rows: Record<string, unknown>[];
|
|
117
|
+
}>;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The stream's connection, as the dialect sees it, with two additions: every
|
|
121
|
+
* statement is reported to the guard (for the keepalive), and, when `backstop`
|
|
122
|
+
* is given, the dialect's own BEGIN carries {@link streamBackstopStatement} in
|
|
123
|
+
* the same simple-protocol round trip, so the limit is in force from the
|
|
124
|
+
* transaction's first statement at no extra cost. Matching the exact BEGIN text
|
|
125
|
+
* the dialect emits keeps the dialect itself unchanged; the live backstop test
|
|
126
|
+
* fails if that text ever stops matching.
|
|
127
|
+
*/
|
|
128
|
+
export declare function watchStreamConnection(base: StreamConnection, guard: () => StreamIdleGuard | undefined, backstop?: {
|
|
129
|
+
begin: string;
|
|
130
|
+
statement: string;
|
|
131
|
+
}): StreamConnection;
|
|
132
|
+
export {};
|