turbine-orm 0.50.0 → 0.51.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 +66 -66
- package/dist/adapters/cockroachdb.d.ts +5 -5
- package/dist/adapters/cockroachdb.js +10 -10
- package/dist/adapters/index.d.ts +5 -5
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/yugabytedb.d.ts +7 -7
- package/dist/adapters/yugabytedb.js +10 -10
- package/dist/cjs/adapters/cockroachdb.d.ts +5 -5
- package/dist/cjs/adapters/cockroachdb.js +10 -10
- package/dist/cjs/adapters/index.d.ts +5 -5
- package/dist/cjs/adapters/index.js +7 -7
- package/dist/cjs/adapters/yugabytedb.d.ts +7 -7
- package/dist/cjs/adapters/yugabytedb.js +10 -10
- package/dist/cjs/cli/config.d.ts +13 -2
- package/dist/cjs/cli/config.js +3 -2
- package/dist/cjs/cli/destructive.d.ts +1 -1
- package/dist/cjs/cli/destructive.js +1 -1
- package/dist/cjs/cli/index.d.ts +10 -10
- package/dist/cjs/cli/index.js +49 -45
- package/dist/cjs/cli/loader.d.ts +7 -7
- package/dist/cjs/cli/loader.js +9 -9
- package/dist/cjs/cli/mcp.js +4 -4
- package/dist/cjs/cli/migrate.d.ts +5 -5
- package/dist/cjs/cli/migrate.js +11 -11
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/ui.d.ts +2 -2
- package/dist/cjs/cli/ui.js +2 -2
- package/dist/cjs/client.d.ts +49 -38
- package/dist/cjs/client.js +57 -56
- package/dist/cjs/dialect.d.ts +62 -18
- package/dist/cjs/dialect.js +40 -2
- package/dist/cjs/errors.d.ts +5 -5
- package/dist/cjs/errors.js +11 -11
- package/dist/cjs/generate.d.ts +6 -6
- package/dist/cjs/generate.js +31 -29
- package/dist/cjs/index-advisor.d.ts +5 -5
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +7 -7
- package/dist/cjs/introspect.d.ts +35 -9
- package/dist/cjs/introspect.js +83 -32
- package/dist/cjs/mssql.d.ts +11 -11
- package/dist/cjs/mssql.js +64 -29
- package/dist/cjs/mysql.d.ts +8 -8
- package/dist/cjs/mysql.js +61 -23
- package/dist/cjs/nested-write.d.ts +21 -2
- package/dist/cjs/nested-write.js +51 -14
- package/dist/cjs/optional-peer-import.cjs +7 -7
- package/dist/cjs/optional-peer-import.d.cts +7 -7
- package/dist/cjs/pipeline-submittable.d.ts +2 -2
- package/dist/cjs/pipeline-submittable.js +6 -6
- package/dist/cjs/pipeline.d.ts +1 -1
- package/dist/cjs/pipeline.js +4 -4
- package/dist/cjs/powdb-introspect.d.ts +1 -1
- package/dist/cjs/powdb-introspect.js +1 -1
- package/dist/cjs/powdb.d.ts +28 -28
- package/dist/cjs/powdb.js +66 -66
- package/dist/cjs/powql.d.ts +27 -27
- package/dist/cjs/powql.js +73 -52
- package/dist/cjs/query/aggregates.d.ts +1 -1
- package/dist/cjs/query/aggregates.js +5 -5
- package/dist/cjs/query/batched-loader.d.ts +11 -11
- package/dist/cjs/query/batched-loader.js +24 -24
- package/dist/cjs/query/builder.d.ts +39 -21
- package/dist/cjs/query/builder.js +99 -57
- package/dist/cjs/query/compound-unique.d.ts +1 -1
- package/dist/cjs/query/compound-unique.js +0 -0
- package/dist/cjs/query/deferred.d.ts +12 -6
- package/dist/cjs/query/deferred.js +1 -1
- package/dist/cjs/query/filters.d.ts +31 -11
- package/dist/cjs/query/filters.js +67 -14
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/index.js +1 -1
- package/dist/cjs/query/relations.d.ts +9 -9
- package/dist/cjs/query/relations.js +164 -57
- package/dist/cjs/query/types.d.ts +86 -35
- package/dist/cjs/query/types.js +1 -1
- package/dist/cjs/query/utils.d.ts +27 -10
- package/dist/cjs/query/utils.js +86 -14
- package/dist/cjs/query/where.d.ts +47 -28
- package/dist/cjs/query/where.js +130 -31
- package/dist/cjs/query/writes.d.ts +24 -5
- package/dist/cjs/query/writes.js +102 -13
- package/dist/cjs/realtime.d.ts +7 -7
- package/dist/cjs/realtime.js +9 -9
- package/dist/cjs/schema-builder.d.ts +18 -7
- package/dist/cjs/schema-builder.js +17 -10
- package/dist/cjs/schema-metadata.d.ts +3 -3
- package/dist/cjs/schema-metadata.js +9 -9
- package/dist/cjs/schema-sql.d.ts +9 -9
- package/dist/cjs/schema-sql.js +20 -20
- package/dist/cjs/schema.d.ts +19 -9
- package/dist/cjs/schema.js +6 -6
- package/dist/cjs/serverless.d.ts +15 -15
- package/dist/cjs/serverless.js +16 -16
- package/dist/cjs/sqlite.d.ts +8 -8
- package/dist/cjs/sqlite.js +53 -22
- package/dist/cjs/typed-sql.d.ts +4 -4
- package/dist/cjs/typed-sql.js +5 -5
- package/dist/cli/config.d.ts +13 -2
- package/dist/cli/config.js +3 -2
- package/dist/cli/destructive.d.ts +1 -1
- package/dist/cli/destructive.js +1 -1
- package/dist/cli/index.d.ts +10 -10
- package/dist/cli/index.js +49 -45
- package/dist/cli/loader.d.ts +7 -7
- package/dist/cli/loader.js +9 -9
- package/dist/cli/mcp.js +4 -4
- package/dist/cli/migrate.d.ts +5 -5
- package/dist/cli/migrate.js +11 -11
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/ui.d.ts +2 -2
- package/dist/cli/ui.js +2 -2
- package/dist/client.d.ts +49 -38
- package/dist/client.js +57 -56
- package/dist/dialect.d.ts +62 -18
- package/dist/dialect.js +40 -2
- package/dist/errors.d.ts +5 -5
- package/dist/errors.js +11 -11
- package/dist/generate.d.ts +6 -6
- package/dist/generate.js +31 -29
- package/dist/index-advisor.d.ts +5 -5
- package/dist/index-advisor.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/introspect.d.ts +35 -9
- package/dist/introspect.js +82 -32
- package/dist/mssql.d.ts +11 -11
- package/dist/mssql.js +64 -29
- package/dist/mysql.d.ts +8 -8
- package/dist/mysql.js +61 -23
- package/dist/nested-write.d.ts +21 -2
- package/dist/nested-write.js +51 -14
- package/dist/optional-peer-import.cjs +7 -7
- package/dist/optional-peer-import.d.cts +7 -7
- package/dist/pipeline-submittable.d.ts +2 -2
- package/dist/pipeline-submittable.js +6 -6
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +4 -4
- package/dist/powdb-introspect.d.ts +1 -1
- package/dist/powdb-introspect.js +1 -1
- package/dist/powdb.d.ts +28 -28
- package/dist/powdb.js +66 -66
- package/dist/powql.d.ts +27 -27
- package/dist/powql.js +73 -52
- package/dist/query/aggregates.d.ts +1 -1
- package/dist/query/aggregates.js +5 -5
- package/dist/query/batched-loader.d.ts +11 -11
- package/dist/query/batched-loader.js +24 -24
- package/dist/query/builder.d.ts +39 -21
- package/dist/query/builder.js +100 -58
- package/dist/query/compound-unique.d.ts +1 -1
- package/dist/query/compound-unique.js +0 -0
- package/dist/query/deferred.d.ts +12 -6
- package/dist/query/deferred.js +1 -1
- package/dist/query/filters.d.ts +31 -11
- package/dist/query/filters.js +66 -13
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +9 -9
- package/dist/query/relations.js +165 -58
- package/dist/query/types.d.ts +86 -35
- package/dist/query/types.js +1 -1
- package/dist/query/utils.d.ts +27 -10
- package/dist/query/utils.js +84 -14
- package/dist/query/where.d.ts +47 -28
- package/dist/query/where.js +129 -32
- package/dist/query/writes.d.ts +24 -5
- package/dist/query/writes.js +101 -13
- package/dist/realtime.d.ts +7 -7
- package/dist/realtime.js +9 -9
- package/dist/schema-builder.d.ts +18 -7
- package/dist/schema-builder.js +17 -10
- package/dist/schema-metadata.d.ts +3 -3
- package/dist/schema-metadata.js +9 -9
- package/dist/schema-sql.d.ts +9 -9
- package/dist/schema-sql.js +20 -20
- package/dist/schema.d.ts +19 -9
- package/dist/schema.js +6 -6
- package/dist/serverless.d.ts +15 -15
- package/dist/serverless.js +16 -16
- package/dist/sqlite.d.ts +8 -8
- package/dist/sqlite.js +53 -22
- package/dist/typed-sql.d.ts +4 -4
- package/dist/typed-sql.js +5 -5
- package/package.json +2 -2
package/dist/cjs/powdb.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* turbine-orm/powdb
|
|
3
|
+
* turbine-orm/powdb, Turbine's PowDB / PowQL backend.
|
|
4
4
|
*
|
|
5
5
|
* PowDB is a single-node embedded database with its own query language, **PowQL**
|
|
6
6
|
* (not SQL), reached over `@zvndev/powdb-client`'s binary TCP protocol. PowDB is a
|
|
@@ -11,26 +11,26 @@
|
|
|
11
11
|
*
|
|
12
12
|
* PowDB realities shape the design (all verified firsthand against a live
|
|
13
13
|
* `powdb-server` / the embedded addon, see `docs/internal/strategy/powdb-parity-matrix.md`):
|
|
14
|
-
* - **`RETURNING` (since 0.7.0)
|
|
14
|
+
* - **`RETURNING` (since 0.7.0)**, `create/createMany/update/delete` append the
|
|
15
15
|
* trailing `returning` keyword (`RETURNING *`, all columns) and read the
|
|
16
16
|
* affected rows back in one round-trip. `upsert` is the lone exception (its
|
|
17
17
|
* statement rejects `returning`) and reselects by primary key.
|
|
18
|
-
* - **No generated IDs
|
|
18
|
+
* - **No generated IDs**, the app must supply every value → Turbine generates a
|
|
19
19
|
* client-side UUID for the primary key when it has a default.
|
|
20
20
|
* - **`uuid`/`datetime`/`bytes` columns can't hold client-supplied values** (no
|
|
21
21
|
* literal, no working cast on the wire) → Turbine maps everything onto the four
|
|
22
22
|
* writable types (`str`/`int`/`float`/`bool`); `Date` → `int` epoch micros;
|
|
23
23
|
* `string` PKs hold UUID strings.
|
|
24
|
-
* - **No JSON aggregation / link navigation
|
|
24
|
+
* - **No JSON aggregation / link navigation**, single-query nested `with` is
|
|
25
25
|
* impossible → it degrades to batched N+1 loaders (Phase B).
|
|
26
|
-
* - **Single global write lock; no savepoints/isolation
|
|
26
|
+
* - **Single global write lock; no savepoints/isolation**, nested
|
|
27
27
|
* transactions / isolation / vector / LISTEN-NOTIFY / RLS throw.
|
|
28
28
|
* Independent concurrent `db.$transaction` calls do NOT throw: they queue
|
|
29
29
|
* FIFO on a pool-level gate and run one at a time (see {@link PowdbTxGate}).
|
|
30
|
-
* Only a *re-entrant* transaction
|
|
30
|
+
* Only a *re-entrant* transaction, a `db.$transaction` opened from inside
|
|
31
31
|
* an active transaction callback's async context, which queueing would
|
|
32
|
-
* deadlock
|
|
33
|
-
* - **The wire protocol pipelines
|
|
32
|
+
* deadlock, fails fast with E017.
|
|
33
|
+
* - **The wire protocol pipelines**, `@zvndev/powdb-client` writes each
|
|
34
34
|
* request frame immediately and matches replies FIFO, so multiple queries
|
|
35
35
|
* may be in flight on one connection. {@link PowdbPool}'s checked-out
|
|
36
36
|
* clients advertise `supportsPipelining`, which lets the batch
|
|
@@ -142,9 +142,9 @@ const schema_js_1 = require("./schema.js");
|
|
|
142
142
|
exports.powdbDialect = {
|
|
143
143
|
...dialect_js_1.postgresDialect,
|
|
144
144
|
name: 'powdb',
|
|
145
|
-
// `resultStrategy` is decorative for PowDB
|
|
145
|
+
// `resultStrategy` is decorative for PowDB, PowqlInterface owns its own write
|
|
146
146
|
// path and never reads it. Set to 'returning' for honesty: writes use PowDB
|
|
147
|
-
// 0.7.0's trailing `returning` keyword (upsert excepted
|
|
147
|
+
// 0.7.0's trailing `returning` keyword (upsert excepted, see PowqlInterface).
|
|
148
148
|
resultStrategy: 'returning',
|
|
149
149
|
supportsReturning: true,
|
|
150
150
|
supportsVector: false,
|
|
@@ -161,7 +161,7 @@ exports.powdbDialect = {
|
|
|
161
161
|
beginStatement: () => 'begin',
|
|
162
162
|
commitStatement: () => 'commit',
|
|
163
163
|
rollbackStatement: () => 'rollback',
|
|
164
|
-
// PowDB has no savepoints
|
|
164
|
+
// PowDB has no savepoints, a nested `tx.$transaction` would emit one and PowDB
|
|
165
165
|
// rejects it with a cryptic parse error. Throw a clear typed error instead.
|
|
166
166
|
// These run synchronously in TransactionClient.$transaction before any query,
|
|
167
167
|
// so the nested call fails fast with no partial DB state.
|
|
@@ -169,9 +169,9 @@ exports.powdbDialect = {
|
|
|
169
169
|
releaseSavepointStatement: throwNoNestedTransaction,
|
|
170
170
|
rollbackToSavepointStatement: throwNoNestedTransaction,
|
|
171
171
|
};
|
|
172
|
-
/** Reject any savepoint (nested-transaction) operation
|
|
172
|
+
/** Reject any savepoint (nested-transaction) operation, PowDB is single-writer. */
|
|
173
173
|
function throwNoNestedTransaction() {
|
|
174
|
-
throw new errors_js_1.UnsupportedFeatureError('nested transactions', 'powdb', 'PowDB is single-writer
|
|
174
|
+
throw new errors_js_1.UnsupportedFeatureError('nested transactions', 'powdb', 'PowDB is single-writer, it has one global write lock and no savepoints. ' +
|
|
175
175
|
'Complete the open transaction before starting another; do not nest `$transaction` calls.');
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
@@ -250,7 +250,7 @@ function parsePowdbUrl(connectionString) {
|
|
|
250
250
|
function assertSupportedPowdbVersion(version) {
|
|
251
251
|
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(version ?? '').trim());
|
|
252
252
|
if (!m)
|
|
253
|
-
return; // unknown / non-semver
|
|
253
|
+
return; // unknown / non-semver, don't block
|
|
254
254
|
const [major, minor] = [Number(m[1]), Number(m[2])];
|
|
255
255
|
// 0.7.0 is the floor; >= 0.7 (or any 1.x+) passes.
|
|
256
256
|
if (major > 0 || (major === 0 && minor >= 7))
|
|
@@ -414,7 +414,7 @@ function isJsonColumn(col) {
|
|
|
414
414
|
*/
|
|
415
415
|
function powqlColumnType(col) {
|
|
416
416
|
if (col.isArray) {
|
|
417
|
-
throw new errors_js_1.ValidationError(`[turbine] Column "${col.name}" is an array
|
|
417
|
+
throw new errors_js_1.ValidationError(`[turbine] Column "${col.name}" is an array, PowDB has no array type. Arrays are unsupported on the PowDB backend.`);
|
|
418
418
|
}
|
|
419
419
|
if (isJsonColumn(col))
|
|
420
420
|
return 'json';
|
|
@@ -430,7 +430,7 @@ function powqlColumnType(col) {
|
|
|
430
430
|
if (ts === 'string')
|
|
431
431
|
return 'str';
|
|
432
432
|
if (ts === 'Buffer' || ts === 'Uint8Array') {
|
|
433
|
-
throw new errors_js_1.ValidationError(`[turbine] Column "${col.name}" is binary
|
|
433
|
+
throw new errors_js_1.ValidationError(`[turbine] Column "${col.name}" is binary, PowDB cannot store client-supplied bytes on the wire. Use a string (e.g. base64) instead.`);
|
|
434
434
|
}
|
|
435
435
|
return 'str';
|
|
436
436
|
}
|
|
@@ -453,7 +453,7 @@ function isDateColumn(col) {
|
|
|
453
453
|
* synthesizing a client-side value for it.
|
|
454
454
|
*/
|
|
455
455
|
/**
|
|
456
|
-
* PowQL reserved words
|
|
456
|
+
* PowQL reserved words, the v0.10 lexer keyword table from POWQL.md's
|
|
457
457
|
* "Reserved Words and Quoting" section, including the v0.10 additions
|
|
458
458
|
* `schema` and `describe`. Keyword matching is case-sensitive in the lexer,
|
|
459
459
|
* so only the exact lowercase form collides.
|
|
@@ -557,7 +557,7 @@ const POWQL_BARE_IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
557
557
|
/**
|
|
558
558
|
* Backtick-quote an identifier when PowQL would otherwise lex it as a keyword
|
|
559
559
|
* (or when it contains characters outside the bare-identifier grammar).
|
|
560
|
-
* Applied only in bare-identifier positions
|
|
560
|
+
* Applied only in bare-identifier positions, DDL type/field names, index DDL,
|
|
561
561
|
* and `insert`/`update`/`upsert` assignment targets. Dotted references
|
|
562
562
|
* (`.col` in filters/projections/ordering) bypass keyword lookup on every
|
|
563
563
|
* engine version and deliberately stay bare for ≤0.9 compatibility. Backticks
|
|
@@ -618,7 +618,7 @@ function powqlSchemaDDL(schema, opts = {}) {
|
|
|
618
618
|
const stmts = [];
|
|
619
619
|
for (const meta of Object.values(schema.tables)) {
|
|
620
620
|
const pkSet = new Set(meta.primaryKey);
|
|
621
|
-
// PowDB's `unique` is single-column only
|
|
621
|
+
// PowDB's `unique` is single-column only, there is no composite-unique
|
|
622
622
|
// constraint (`add unique` takes one `.column`). So the per-field `unique`
|
|
623
623
|
// modifier is emitted only for a single-column PK; a composite PK (e.g. a
|
|
624
624
|
// m2m junction's `(source_id, target_id)`) marks its columns `required` but
|
|
@@ -763,7 +763,7 @@ async function applyPowdbLinks(exec, schema, options = {}) {
|
|
|
763
763
|
}
|
|
764
764
|
/**
|
|
765
765
|
* Read the live `schema links` listing into {@link PowdbDesiredLink} rows (owner,
|
|
766
|
-
* name, target, localKey, targetKey; cardinality is dropped
|
|
766
|
+
* name, target, localKey, targetKey; cardinality is dropped, it is derived, not
|
|
767
767
|
* a DDL input). An empty catalog returns `[]`, never an error. Naming edge: a
|
|
768
768
|
* table literally named `links` is described with `describe links`, but the
|
|
769
769
|
* link LISTING is the contextual keyword form `schema links`.
|
|
@@ -950,7 +950,7 @@ function rowToEntity(raw, meta, native = false) {
|
|
|
950
950
|
* So we always run the unique-constraint and message-shape checks first (they
|
|
951
951
|
* fire for both transports and extract detail like constraint / column names),
|
|
952
952
|
* then classify by the typed wire error class (`.wireErrorClass`, networked
|
|
953
|
-
* server >= 0.17
|
|
953
|
+
* server >= 0.17, accurate even when the server sanitized the message text),
|
|
954
954
|
* then fall through to the networked `.code` switch.
|
|
955
955
|
*/
|
|
956
956
|
function wrapPowdbError(err) {
|
|
@@ -958,12 +958,12 @@ function wrapPowdbError(err) {
|
|
|
958
958
|
return new errors_js_1.ConnectionError(`[turbine] PowDB error: ${String(err)}`);
|
|
959
959
|
const e = err;
|
|
960
960
|
const msg = e.message ?? 'unknown PowDB error';
|
|
961
|
-
// Unique-constraint
|
|
961
|
+
// Unique-constraint, message-based on both transports.
|
|
962
962
|
if (/unique (constraint|expression index) violation/i.test(msg)) {
|
|
963
963
|
const m = /on\s+\S+\.(\w+)/i.exec(msg);
|
|
964
964
|
return new errors_js_1.UniqueConstraintError({ constraint: m?.[1], cause: err });
|
|
965
965
|
}
|
|
966
|
-
// NOT NULL
|
|
966
|
+
// NOT NULL, "column 'x' is required but no value was provided". Map on BOTH
|
|
967
967
|
// transports (the networked path used to collapse this into E003).
|
|
968
968
|
if (/required|not[- ]?null|no value/i.test(msg)) {
|
|
969
969
|
const m = /column ['"]?(\w+)['"]?/i.exec(msg);
|
|
@@ -1076,25 +1076,25 @@ function wrapPowdbError(err) {
|
|
|
1076
1076
|
const wireClass = err.wireErrorClass;
|
|
1077
1077
|
if (typeof wireClass === 'number') {
|
|
1078
1078
|
switch (wireClass) {
|
|
1079
|
-
case 3: // timeout (per-query budget, gate wait, idle timeout)
|
|
1079
|
+
case 3: // timeout (per-query budget, gate wait, idle timeout), retryable
|
|
1080
1080
|
return new errors_js_1.TimeoutError(0, 'PowDB query', { message: `[turbine] PowDB ${msg}`, cause: err });
|
|
1081
|
-
case 4: // limit_exceeded (memory / size budget)
|
|
1081
|
+
case 4: // limit_exceeded (memory / size budget), a query-shape defect
|
|
1082
1082
|
return new errors_js_1.ValidationError(`[turbine] PowDB resource limit exceeded: ${msg}`);
|
|
1083
|
-
case 5: // readonly_refused
|
|
1083
|
+
case 5: // readonly_refused, the snapshot-serving routing signal
|
|
1084
1084
|
return new errors_js_1.ReadOnlyError(`PowDB refused a write on a read-only database: ${msg}.`, {
|
|
1085
1085
|
cause: err,
|
|
1086
1086
|
reason: 'snapshot',
|
|
1087
1087
|
});
|
|
1088
1088
|
case 6: // auth_failed at CONNECT
|
|
1089
1089
|
return new errors_js_1.ConnectionError(`[turbine] PowDB authentication failed: ${msg} (check the user / password / dbName for this connection).`, { cause: err });
|
|
1090
|
-
case 7: // rate_limited (repeated bad auth)
|
|
1090
|
+
case 7: // rate_limited (repeated bad auth), connection-establishment class
|
|
1091
1091
|
return new errors_js_1.ConnectionError(`[turbine] PowDB rate-limited this address after repeated failed authentication: ${msg}. Wait before retrying.`, { cause: err });
|
|
1092
1092
|
case 8: {
|
|
1093
|
-
// constraint_violation
|
|
1093
|
+
// constraint_violation, today that is always a unique index
|
|
1094
1094
|
const m = /on\s+\S+\.(\w+)/i.exec(msg);
|
|
1095
1095
|
return new errors_js_1.UniqueConstraintError({ constraint: m?.[1], cause: err });
|
|
1096
1096
|
}
|
|
1097
|
-
case 9: // cancelled (issuing client disconnected)
|
|
1097
|
+
case 9: // cancelled (issuing client disconnected), final, never retry
|
|
1098
1098
|
return new errors_js_1.ConnectionError(`[turbine] PowDB query cancelled by client disconnect: ${msg}`, { cause: err });
|
|
1099
1099
|
case 1: // parse
|
|
1100
1100
|
case 2: // execution
|
|
@@ -1176,15 +1176,15 @@ function txControl(powql) {
|
|
|
1176
1176
|
* the write lock the outer transaction holds), and on the networked transport
|
|
1177
1177
|
* it would block a fresh pooled connection on the lock forever. This guard
|
|
1178
1178
|
* converts that hang into a fast, typed error. Independent concurrent
|
|
1179
|
-
* transactions do NOT hit this
|
|
1179
|
+
* transactions do NOT hit this, they queue FIFO on {@link PowdbTxGate}.
|
|
1180
1180
|
*/
|
|
1181
1181
|
function reentrantTransactionError() {
|
|
1182
|
-
return new errors_js_1.UnsupportedFeatureError('re-entrant transactions', 'powdb', 'PowDB is single-writer
|
|
1182
|
+
return new errors_js_1.UnsupportedFeatureError('re-entrant transactions', 'powdb', 'PowDB is single-writer, a transaction opened from inside an active transaction callback would deadlock ' +
|
|
1183
1183
|
'on the write lock the open transaction holds. Use the `tx` client the callback receives, or start the ' +
|
|
1184
1184
|
'second transaction after the first completes. (Independent concurrent transactions queue automatically.)');
|
|
1185
1185
|
}
|
|
1186
1186
|
// ---------------------------------------------------------------------------
|
|
1187
|
-
// Single-writer transaction gate
|
|
1187
|
+
// Single-writer transaction gate, FIFO queueing + re-entrancy detection
|
|
1188
1188
|
// ---------------------------------------------------------------------------
|
|
1189
1189
|
/**
|
|
1190
1190
|
* Default cap (ms) on how long a `begin` may wait in the FIFO queue for
|
|
@@ -1211,8 +1211,8 @@ const powdbTxStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
|
1211
1211
|
* (and the embedded engine rejects it with a raw parse error).
|
|
1212
1212
|
*
|
|
1213
1213
|
* `acquire()` is called (synchronously, see below) for every `begin`:
|
|
1214
|
-
* - a **re-entrant** `begin
|
|
1215
|
-
* async context, detected via {@link powdbTxStorage}
|
|
1214
|
+
* - a **re-entrant** `begin`, issued from inside an active transaction's
|
|
1215
|
+
* async context, detected via {@link powdbTxStorage}, throws E017
|
|
1216
1216
|
* immediately. Queueing it can never succeed: the open transaction cannot
|
|
1217
1217
|
* commit while its callback awaits the queued one.
|
|
1218
1218
|
* - an **independent** `begin` waits its FIFO turn, bounded by the queue
|
|
@@ -1251,7 +1251,7 @@ const powdbTxStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
|
1251
1251
|
*/
|
|
1252
1252
|
class PowdbTxGate {
|
|
1253
1253
|
queueTimeoutMs;
|
|
1254
|
-
/** Tail of the FIFO queue
|
|
1254
|
+
/** Tail of the FIFO queue, resolves once every earlier transaction has finished. */
|
|
1255
1255
|
tail = Promise.resolve();
|
|
1256
1256
|
constructor(queueTimeoutMs) {
|
|
1257
1257
|
this.queueTimeoutMs = queueTimeoutMs;
|
|
@@ -1267,11 +1267,11 @@ class PowdbTxGate {
|
|
|
1267
1267
|
async acquire() {
|
|
1268
1268
|
// Walk the WHOLE marker chain, not just the innermost marker: with two
|
|
1269
1269
|
// pools, dbA-tx → dbB-tx → dbA-begin leaves dbB's marker innermost, but
|
|
1270
|
-
// the dbA ancestor is still open
|
|
1270
|
+
// the dbA ancestor is still open, queueing the inner dbA begin behind it
|
|
1271
1271
|
// would deadlock. Any live ancestor on this gate ⇒ re-entrant E017.
|
|
1272
1272
|
// Prune completed heads first (`done` never flips back) so sequential
|
|
1273
|
-
// transactions issued from one long-lived context do not chain
|
|
1274
|
-
//
|
|
1273
|
+
// transactions issued from one long-lived context do not chain, and leak
|
|
1274
|
+
// - unboundedly; what remains is bounded by real nesting depth.
|
|
1275
1275
|
let parent = powdbTxStorage.getStore();
|
|
1276
1276
|
while (parent?.done)
|
|
1277
1277
|
parent = parent.parent;
|
|
@@ -1425,7 +1425,7 @@ class PowdbPool {
|
|
|
1425
1425
|
* Pool-level single-writer gate. PowDB holds one global write lock, so at
|
|
1426
1426
|
* most one transaction may be open across the whole pool. Concurrent
|
|
1427
1427
|
* `begin`s queue FIFO on the gate (instead of checking out a second
|
|
1428
|
-
* connection and blocking on the lock forever
|
|
1428
|
+
* connection and blocking on the lock forever, the networked hang);
|
|
1429
1429
|
* re-entrant `begin`s throw E017 (see {@link PowdbTxGate}).
|
|
1430
1430
|
*/
|
|
1431
1431
|
txGate;
|
|
@@ -1485,7 +1485,7 @@ class PowdbPool {
|
|
|
1485
1485
|
if ((ctl === 'commit' || ctl === 'rollback') && this.poolHold === null) {
|
|
1486
1486
|
// No gate hold → our `begin` never ran (gate timeout / re-entrant
|
|
1487
1487
|
// E017 / no begin at all). Never forward a stray commit/rollback to
|
|
1488
|
-
// the engine
|
|
1488
|
+
// the engine, PowDB is single-writer, so it could only ever end a
|
|
1489
1489
|
// DIFFERENT caller's open transaction. Empty success instead.
|
|
1490
1490
|
return { rows: [], rowCount: 0, fields: [] };
|
|
1491
1491
|
}
|
|
@@ -1509,11 +1509,11 @@ class PowdbPool {
|
|
|
1509
1509
|
/**
|
|
1510
1510
|
* Typed guard mirroring {@link PowdbEmbeddedPool}: after `end()` the driver
|
|
1511
1511
|
* pool throws a raw `Error('pool closed')` that {@link wrapPowdbError}
|
|
1512
|
-
* cannot classify
|
|
1512
|
+
* cannot classify, surface the same ConnectionError on both transports.
|
|
1513
1513
|
*/
|
|
1514
1514
|
assertOpen() {
|
|
1515
1515
|
if (this.closed) {
|
|
1516
|
-
throw new errors_js_1.ConnectionError('[turbine] The PowDB pool is closed
|
|
1516
|
+
throw new errors_js_1.ConnectionError('[turbine] The PowDB pool is closed, disconnect() was already called on this client.');
|
|
1517
1517
|
}
|
|
1518
1518
|
}
|
|
1519
1519
|
async connect() {
|
|
@@ -1536,7 +1536,7 @@ class PowdbPool {
|
|
|
1536
1536
|
// lets the batch `$transaction([...])` path dispatch all statements in
|
|
1537
1537
|
// one write burst instead of paying a round trip per statement. Safe
|
|
1538
1538
|
// for the batch's rollback contract because a failed statement leaves
|
|
1539
|
-
// the engine's transaction open (no aborted state, no auto-rollback)
|
|
1539
|
+
// the engine's transaction open (no aborted state, no auto-rollback) -
|
|
1540
1540
|
// later pipelined statements execute inside the same still-open
|
|
1541
1541
|
// transaction and the final `rollback` discards every effect. (The
|
|
1542
1542
|
// batch path awaits `begin` before dispatching the burst, so the gate
|
|
@@ -1554,7 +1554,7 @@ class PowdbPool {
|
|
|
1554
1554
|
hold = await this.txGate.acquire();
|
|
1555
1555
|
}
|
|
1556
1556
|
if ((ctl === 'commit' || ctl === 'rollback') && hold === null) {
|
|
1557
|
-
// This connection never acquired the gate
|
|
1557
|
+
// This connection never acquired the gate, its `begin` never ran
|
|
1558
1558
|
// (gate timeout / re-entrant E017). A stray commit/rollback must
|
|
1559
1559
|
// never reach the single-writer engine, where it could only end a
|
|
1560
1560
|
// DIFFERENT caller's open transaction. Empty success instead.
|
|
@@ -1590,7 +1590,7 @@ class PowdbPool {
|
|
|
1590
1590
|
},
|
|
1591
1591
|
release: (err) => {
|
|
1592
1592
|
// Releasing this connection ends its transaction scope. pg semantics:
|
|
1593
|
-
// a truthy `err` means "destroy, don't re-idle"
|
|
1593
|
+
// a truthy `err` means "destroy, don't re-idle", client.ts's
|
|
1594
1594
|
// $transaction timeout path relies on that to keep an abandoned
|
|
1595
1595
|
// callback's connection out of the pool. Additionally, an OPEN hold
|
|
1596
1596
|
// here means the tx begun on this connection never saw commit/rollback
|
|
@@ -1599,7 +1599,7 @@ class PowdbPool {
|
|
|
1599
1599
|
// server-side transaction ends (destroying the socket alone leaves it
|
|
1600
1600
|
// open until the server's idle timeout), THEN hand the gate to the
|
|
1601
1601
|
// next queued transaction. If the rollback fails or times out the
|
|
1602
|
-
// connection is treated as broken and destroyed. Never throws
|
|
1602
|
+
// connection is treated as broken and destroyed. Never throws, a
|
|
1603
1603
|
// teardown error must not mask the transaction's real outcome.
|
|
1604
1604
|
const openHold = hold;
|
|
1605
1605
|
hold = null;
|
|
@@ -1667,14 +1667,14 @@ function normalizeEmbeddedResult(r) {
|
|
|
1667
1667
|
}
|
|
1668
1668
|
/**
|
|
1669
1669
|
* Encode a JS value as a **PowQL literal** for the embedded driver, which takes
|
|
1670
|
-
* no params array
|
|
1670
|
+
* no params array, `$N` placeholders must be materialized into the query text.
|
|
1671
1671
|
*
|
|
1672
1672
|
* This is the single place Turbine builds PowQL text from a value, so it is the
|
|
1673
1673
|
* security-critical surface. String encoding matches PowDB's lexer
|
|
1674
1674
|
* (`crates/query/src/lexer.rs`) exactly: a string literal is `"…"`, and inside
|
|
1675
1675
|
* it the lexer recognizes only the escapes `\"`, `\\`, `\n`, `\t` (any other
|
|
1676
|
-
* `\x` drops the backslash and keeps `x`; every non-`\`/non-`"` char
|
|
1677
|
-
* newlines, CR, unicode
|
|
1676
|
+
* `\x` drops the backslash and keeps `x`; every non-`\`/non-`"` char, raw
|
|
1677
|
+
* newlines, CR, unicode, is taken literally). So we escape `\` → `\\` and
|
|
1678
1678
|
* `"` → `\"` (the only breakout vectors), render `\n`/`\t` as their recognized
|
|
1679
1679
|
* escapes, and leave everything else raw. Verified against the real engine:
|
|
1680
1680
|
* quotes, backslashes, `$N`, `"); drop … --`, raw CR, and emoji all round-trip
|
|
@@ -1776,7 +1776,7 @@ function powqlNumberText(n) {
|
|
|
1776
1776
|
* `git diff v0.19.0 v0.19.1 -- crates/query/src/lexer.rs` is empty (the bare-dotted-path
|
|
1777
1777
|
* hard error is parser-level, not tokenization), so this ceiling stays `'0.19'`. The
|
|
1778
1778
|
* guard in {@link PowdbEmbeddedPool.exec} compares major.minor only, so `'0.19'`
|
|
1779
|
-
* already covers every 0.19.x patch
|
|
1779
|
+
* already covers every 0.19.x patch, no bump is needed for 0.19.1.
|
|
1780
1780
|
*/
|
|
1781
1781
|
exports.POWQL_LEXER_TESTED_CEILING = '0.19';
|
|
1782
1782
|
/**
|
|
@@ -1805,14 +1805,14 @@ function encodePowqlString(s, position) {
|
|
|
1805
1805
|
else if (ch === '\t')
|
|
1806
1806
|
out += '\\t';
|
|
1807
1807
|
else
|
|
1808
|
-
out += ch; // raw
|
|
1808
|
+
out += ch; // raw, the lexer takes any other char literally (incl. CR, unicode)
|
|
1809
1809
|
}
|
|
1810
1810
|
return `${out}"`;
|
|
1811
1811
|
}
|
|
1812
1812
|
/**
|
|
1813
1813
|
* Substitute every `$N` placeholder in a generator-produced PowQL template with
|
|
1814
1814
|
* the encoded literal of `params[N-1]`. Safe because the template is produced by
|
|
1815
|
-
* {@link PowqlInterface} and contains **no** user string literals
|
|
1815
|
+
* {@link PowqlInterface} and contains **no** user string literals, the only
|
|
1816
1816
|
* `$<digits>` tokens are genuine positional placeholders, so a single scan
|
|
1817
1817
|
* cannot accidentally rewrite a `$N` that is itself part of a value (values are
|
|
1818
1818
|
* params, never inlined into the template by the generator).
|
|
@@ -1843,7 +1843,7 @@ class PowdbEmbeddedPool {
|
|
|
1843
1843
|
closed = false;
|
|
1844
1844
|
/**
|
|
1845
1845
|
* Single-writer gate. The embedded engine is one handle with one global
|
|
1846
|
-
* write lock
|
|
1846
|
+
* write lock, only one transaction may be open at a time. A re-entrant
|
|
1847
1847
|
* `begin` (a fresh top-level `db.$transaction` opened inside an open one's
|
|
1848
1848
|
* callback) would otherwise hit PowDB's raw "already in a transaction"
|
|
1849
1849
|
* parse error; the gate surfaces a typed E017 instead, while INDEPENDENT
|
|
@@ -1944,13 +1944,13 @@ class PowdbEmbeddedPool {
|
|
|
1944
1944
|
}
|
|
1945
1945
|
}
|
|
1946
1946
|
if ((ctl === 'commit' || ctl === 'rollback') && holdRef.hold === null) {
|
|
1947
|
-
// This context never acquired the gate
|
|
1947
|
+
// This context never acquired the gate, its `begin` never ran (the
|
|
1948
1948
|
// gate timed out / threw re-entrant E017, or no begin was issued at
|
|
1949
1949
|
// all). The engine is ONE shared handle: forwarding this stray
|
|
1950
1950
|
// commit/rollback would hit whatever transaction ANOTHER caller has
|
|
1951
1951
|
// open on it (live-reproduced: a best-effort ROLLBACK after a failed
|
|
1952
1952
|
// begin silently discarded a concurrent transaction's writes). Swallow
|
|
1953
|
-
// it as an empty success instead
|
|
1953
|
+
// it as an empty success instead, there is nothing of ours to end.
|
|
1954
1954
|
return { rows: [], rowCount: 0, fields: [] };
|
|
1955
1955
|
}
|
|
1956
1956
|
try {
|
|
@@ -1976,7 +1976,7 @@ class PowdbEmbeddedPool {
|
|
|
1976
1976
|
return this.run(powql, params, this.poolHoldRef);
|
|
1977
1977
|
}
|
|
1978
1978
|
async connect() {
|
|
1979
|
-
// Single in-process handle
|
|
1979
|
+
// Single in-process handle, the "client" shares the one Database; tx
|
|
1980
1980
|
// keywords run serially on it. Each checked-out client scopes its own
|
|
1981
1981
|
// gate hold so release() only ever finishes ITS transaction.
|
|
1982
1982
|
const holdRef = { hold: null };
|
|
@@ -1994,7 +1994,7 @@ class PowdbEmbeddedPool {
|
|
|
1994
1994
|
},
|
|
1995
1995
|
release: () => {
|
|
1996
1996
|
// End-of-scope safety net (see PowdbPool.connect()): a tx torn down
|
|
1997
|
-
// without an explicit commit/rollback must not wedge the queue
|
|
1997
|
+
// without an explicit commit/rollback must not wedge the queue, and
|
|
1998
1998
|
// on the ONE shared embedded handle its open engine transaction must
|
|
1999
1999
|
// actually be rolled back before the gate moves on, or the next
|
|
2000
2000
|
// transaction's work interleaves into it. run() owns the
|
|
@@ -2030,7 +2030,7 @@ class PowdbEmbeddedPool {
|
|
|
2030
2030
|
}
|
|
2031
2031
|
exports.PowdbEmbeddedPool = PowdbEmbeddedPool;
|
|
2032
2032
|
// ---------------------------------------------------------------------------
|
|
2033
|
-
// PowqlInterface
|
|
2033
|
+
// PowqlInterface, the PowQL query generator (Phase A: flat CRUD via returning)
|
|
2034
2034
|
// ---------------------------------------------------------------------------
|
|
2035
2035
|
// `describe`-based introspection (programmatic API; see powdb-introspect.ts).
|
|
2036
2036
|
var powdb_introspect_js_1 = require("./powdb-introspect.js");
|
|
@@ -2044,19 +2044,19 @@ Object.defineProperty(exports, "PowqlInterface", { enumerable: true, get: functi
|
|
|
2044
2044
|
async function loadPowdb() {
|
|
2045
2045
|
try {
|
|
2046
2046
|
// Via the .cts helper so the CJS build keeps a path to a REAL dynamic
|
|
2047
|
-
// import()
|
|
2047
|
+
// import(), @zvndev/powdb-client ≥ 0.9 is ESM-only, and the CommonJS
|
|
2048
2048
|
// pass transpiles a plain `import()` here into an unusable `require()`.
|
|
2049
2049
|
return (await (0, optional_peer_import_cjs_1.default)('@zvndev/powdb-client'));
|
|
2050
2050
|
}
|
|
2051
2051
|
catch (err) {
|
|
2052
|
-
throw new errors_js_1.ConnectionError("[turbine] turbine-orm/powdb requires the optional peer dependency '@zvndev/powdb-client'. Install it: npm i @zvndev/powdb-client
|
|
2052
|
+
throw new errors_js_1.ConnectionError("[turbine] turbine-orm/powdb requires the optional peer dependency '@zvndev/powdb-client'. Install it: npm i @zvndev/powdb-client, " +
|
|
2053
2053
|
'or construct the PowDB pool yourself and inject it: turbinePowDB(pool, schema). ' +
|
|
2054
2054
|
`(${err.message})`);
|
|
2055
2055
|
}
|
|
2056
2056
|
}
|
|
2057
2057
|
/**
|
|
2058
2058
|
* Dynamically load `@zvndev/powdb-embedded` (the in-process napi addon). Kept out
|
|
2059
|
-
* of the static import graph
|
|
2059
|
+
* of the static import graph, `import 'turbine-orm/powdb'` never pulls it. A
|
|
2060
2060
|
* missing package or an unsupported platform (Intel-mac/musl/Windows ship no
|
|
2061
2061
|
* prebuilt binary) throws a clear {@link ConnectionError} pointing at the
|
|
2062
2062
|
* from-source `npm run build` fallback.
|
|
@@ -2064,7 +2064,7 @@ async function loadPowdb() {
|
|
|
2064
2064
|
async function loadPowdbEmbedded() {
|
|
2065
2065
|
let mod;
|
|
2066
2066
|
try {
|
|
2067
|
-
// Via the .cts helper
|
|
2067
|
+
// Via the .cts helper, keeps a real dynamic import() available to the
|
|
2068
2068
|
// CJS build in case a future addon version ships ESM-only (see loadPowdb).
|
|
2069
2069
|
mod = (await (0, optional_peer_import_cjs_1.default)('@zvndev/powdb-embedded'));
|
|
2070
2070
|
}
|
|
@@ -2072,12 +2072,12 @@ async function loadPowdbEmbedded() {
|
|
|
2072
2072
|
throw new errors_js_1.ConnectionError("[turbine] turbine-orm/powdb embedded mode requires the optional peer '@zvndev/powdb-embedded'. " +
|
|
2073
2073
|
'Install it: npm i @zvndev/powdb-embedded. If install succeeded but loading failed, your platform has no ' +
|
|
2074
2074
|
'prebuilt binary (prebuilts ship for macOS arm64/x64 and Linux glibc x64/arm64; Intel-mac/musl/Windows ' +
|
|
2075
|
-
'build from source)
|
|
2075
|
+
'build from source), build it with `npm run build` in the addon, then retry. You can also construct the ' +
|
|
2076
2076
|
'pool yourself and inject it: turbinePowDB(pool, schema). ' +
|
|
2077
2077
|
`(${err.message})`);
|
|
2078
2078
|
}
|
|
2079
2079
|
if (!mod || typeof mod.Database?.open !== 'function') {
|
|
2080
|
-
throw new errors_js_1.ConnectionError("[turbine] '@zvndev/powdb-embedded' loaded but did not export Database.open
|
|
2080
|
+
throw new errors_js_1.ConnectionError("[turbine] '@zvndev/powdb-embedded' loaded but did not export Database.open, the installed version is " +
|
|
2081
2081
|
'likely incompatible (turbine-orm/powdb embedded requires @zvndev/powdb-embedded ^0.7.0).');
|
|
2082
2082
|
}
|
|
2083
2083
|
return mod;
|
|
@@ -2166,7 +2166,7 @@ async function openEmbeddedPool(target, poolOptions = {}, assumeEngineVersion, i
|
|
|
2166
2166
|
* - an `{ embedded: <data-dir> }` object → an in-process
|
|
2167
2167
|
* `@zvndev/powdb-embedded` database (no server);
|
|
2168
2168
|
* - an already-constructed `@zvndev/powdb-client` `Pool` or {@link PowdbPool}
|
|
2169
|
-
* (injection
|
|
2169
|
+
* (injection, you own its lifecycle and `disconnect()` is a no-op).
|
|
2170
2170
|
*
|
|
2171
2171
|
* On the networked transport the server version is probed and a clear
|
|
2172
2172
|
* {@link ConnectionError} is thrown if it is older than {@link MIN_POWDB_VERSION}
|
|
@@ -2243,7 +2243,7 @@ async function turbinePowDB(target, schema, options = {}) {
|
|
|
2243
2243
|
patch.end = close;
|
|
2244
2244
|
}
|
|
2245
2245
|
else {
|
|
2246
|
-
// Injected pool
|
|
2246
|
+
// Injected pool, the caller owns its lifecycle.
|
|
2247
2247
|
client.disconnect = async () => { };
|
|
2248
2248
|
}
|
|
2249
2249
|
return client;
|
package/dist/cjs/powql.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PowqlInterface
|
|
2
|
+
* PowqlInterface, Turbine's PowQL query generator (the PowDB analogue of
|
|
3
3
|
* {@link QueryInterface}). It exposes the same public method surface as the SQL
|
|
4
|
-
* `QueryInterface` (`findMany`, `create`, `update`, …) but emits **PowQL
|
|
5
|
-
* pipeline language, not SQL
|
|
4
|
+
* `QueryInterface` (`findMany`, `create`, `update`, …) but emits **PowQL**, a
|
|
5
|
+
* pipeline language, not SQL, executed through {@link PowdbPool}.
|
|
6
6
|
*
|
|
7
7
|
* It is a *parallel* implementation rather than a `Dialect` of the SQL builder:
|
|
8
8
|
* PowQL's grammar (`T filter <e> order <k> { .col }`) shares no surface with
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
* server):
|
|
15
15
|
* - `create`/`createMany`/`update`/`delete` use PowDB 0.7.0's trailing
|
|
16
16
|
* `returning` keyword (`RETURNING *`, all columns) to surface affected rows
|
|
17
|
-
* in one round-trip. `upsert` is the lone exception
|
|
17
|
+
* in one round-trip. `upsert` is the lone exception, its statement does not
|
|
18
18
|
* accept `returning`, so it reselects the row by PK (a composite-PK upsert
|
|
19
19
|
* reselects-or-writes inside one flat transaction).
|
|
20
20
|
* - The PK is server-assigned when the column is `isGenerated` (PowDB's `auto`
|
|
21
|
-
* int
|
|
21
|
+
* int, read back via `returning`); otherwise a defaulted **string** PK is
|
|
22
22
|
* generated client-side (UUID).
|
|
23
|
-
* - `with` (nested relations) uses **batched N+1 loaders
|
|
24
|
-
* depth D, not one query
|
|
23
|
+
* - `with` (nested relations) uses **batched N+1 loaders**, D round-trips for
|
|
24
|
+
* depth D, not one query, including manyToMany (junction → targets).
|
|
25
25
|
* - **Relation filters** (`some`/`none`/`every`, all cardinalities incl. m2m)
|
|
26
26
|
* are resolved client-side to a literal `in (…)` list, never an IN-subquery:
|
|
27
27
|
* PowDB's executor caches a subquery's result by plan shape and would return
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* shared nested-write engine as one flat top-level transaction (PowDB is
|
|
31
31
|
* single-writer, no savepoints).
|
|
32
32
|
* - pgvector / JSON / array filters and cursor streaming throw
|
|
33
|
-
* {@link UnsupportedFeatureError} (E017)
|
|
33
|
+
* {@link UnsupportedFeatureError} (E017), they have no PowDB equivalent.
|
|
34
34
|
*
|
|
35
35
|
* @module
|
|
36
36
|
*/
|
|
@@ -77,8 +77,8 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
77
77
|
*/
|
|
78
78
|
private param;
|
|
79
79
|
/**
|
|
80
|
-
* Render a value for a write *assignment* (`col := …`). Every value
|
|
81
|
-
* columns included
|
|
80
|
+
* Render a value for a write *assignment* (`col := …`). Every value, float
|
|
81
|
+
* columns included, is sent as a positional `$N` param. PowDB ≥ 0.7.0 fixed
|
|
82
82
|
* the int→float UPDATE coercion bug (`score := $n` with an integer param now
|
|
83
83
|
* reads back the integer value, not the raw i64 bits), so the float-literal
|
|
84
84
|
* inlining workaround Turbine carried for ≤ 0.6.2 is gone. Marks the column as
|
|
@@ -95,7 +95,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
95
95
|
* default so a hand-built test pool never crashes the version gates.
|
|
96
96
|
*/
|
|
97
97
|
private get capabilities();
|
|
98
|
-
/** A predicate that is always false
|
|
98
|
+
/** A predicate that is always false, the empty-`in` / contradiction sentinel. */
|
|
99
99
|
private alwaysFalse;
|
|
100
100
|
/**
|
|
101
101
|
* Compile a {@link WhereClause} into a PowQL filter expression, pushing every
|
|
@@ -153,7 +153,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
153
153
|
private bind;
|
|
154
154
|
/** Bind a LIKE pattern (already escaped), lowercasing for insensitive mode. */
|
|
155
155
|
private bindLike;
|
|
156
|
-
/** `lhs [not] in ($1, $2, …)
|
|
156
|
+
/** `lhs [not] in ($1, $2, …)`, empty list collapses to a constant. */
|
|
157
157
|
private buildInList;
|
|
158
158
|
/**
|
|
159
159
|
* Pre-resolve every relation filter (`some`/`none`/`every`) in a where clause
|
|
@@ -167,7 +167,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
167
167
|
* so a second subquery of the same shape with a different value returns the
|
|
168
168
|
* first one's stale rows (reproduced live on the embedded engine; the
|
|
169
169
|
* single-statement literal `in (list)` form is always correct). Resolving
|
|
170
|
-
* client-side trades extra round-trips for correctness, and recurses
|
|
170
|
+
* client-side trades extra round-trips for correctness, and recurses, nested
|
|
171
171
|
* relation filters in the inner predicate resolve when the target query runs.
|
|
172
172
|
*/
|
|
173
173
|
private resolveRelationFilters;
|
|
@@ -274,8 +274,8 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
274
274
|
*
|
|
275
275
|
* When the engine supports nested projections (>= 0.18) and the strategy
|
|
276
276
|
* does not opt out, eligible `with` relations compile INTO this statement as
|
|
277
|
-
* nested-projection blocks (`nestedPlans`)
|
|
278
|
-
* shape
|
|
277
|
+
* nested-projection blocks (`nestedPlans`), one round-trip for the whole
|
|
278
|
+
* shape, and only the ineligible remainder (`residualWith`) goes to the
|
|
279
279
|
* post-execution loaders. Without nesting the emitted PowQL is byte-identical
|
|
280
280
|
* to the pre-0.18 output (no alias, `.col` refs).
|
|
281
281
|
*/
|
|
@@ -313,7 +313,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
313
313
|
*/
|
|
314
314
|
private loadRelations;
|
|
315
315
|
/**
|
|
316
|
-
* manyToMany nested read
|
|
316
|
+
* manyToMany nested read, a three-hop batched loader (no `json_agg`/join
|
|
317
317
|
* pushdown): (1) read the junction rows for all parents in `sourceKey in (…)`
|
|
318
318
|
* chunks, (2) read the target rows for the collected `targetKey`s, (3) stitch
|
|
319
319
|
* each parent → its junction rows → its targets in memory. Mirrors the
|
|
@@ -445,7 +445,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
445
445
|
/**
|
|
446
446
|
* Shape the nested JSON children back into typed entities on every parent
|
|
447
447
|
* row. The nested field arrives as a decoded JSON array on the native wire
|
|
448
|
-
* (or JSON text on the legacy wire
|
|
448
|
+
* (or JSON text on the legacy wire, parsed here); its values are real JSON
|
|
449
449
|
* types, so each child object goes through the NATIVE coercion policy
|
|
450
450
|
* (`rowToEntity(…, true)`: a date column's micros number becomes a `Date`, a
|
|
451
451
|
* json column's document passes through, a str `"null"` stays a string).
|
|
@@ -477,7 +477,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
477
477
|
* must stay on the loaders (ALWAYS a silent fallback with identical output).
|
|
478
478
|
*
|
|
479
479
|
* SCOPED TIGHT: this fires ONLY for a to-one relation whose child projection
|
|
480
|
-
* includes a bigint/bytes column
|
|
480
|
+
* includes a bigint/bytes column, exactly the case a JSON nested block cannot
|
|
481
481
|
* carry, so nested projections have already fallen back to a per-relation loader
|
|
482
482
|
* (`planNestedRelation` returned `null` for the same shape). Cases nested
|
|
483
483
|
* projections DO serve keep nested projections: link-bearing statements are
|
|
@@ -485,9 +485,9 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
485
485
|
* link path would regress a hot path for no gain. Requires: single-column
|
|
486
486
|
* belongsTo; no relation `with` / `where` / `distinct` / `orderBy` /
|
|
487
487
|
* `limit` / `offset` (a scalar path has no per-hop filter/order and cannot
|
|
488
|
-
* reproduce those
|
|
488
|
+
* reproduce those, such inputs stay on the loader for exact parity); a link
|
|
489
489
|
* name and all projected columns that are bare identifiers (a quoted segment in
|
|
490
|
-
* a dotted link path is outside the verified spelling
|
|
490
|
+
* a dotted link path is outside the verified spelling, fall back); and a
|
|
491
491
|
* DECLARED link that verifiably matches (`findMatchingLink`).
|
|
492
492
|
*/
|
|
493
493
|
private planLinkPathRelation;
|
|
@@ -495,7 +495,7 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
495
495
|
private linkPathFields;
|
|
496
496
|
/**
|
|
497
497
|
* Reconstruct each link-path relation's child entity from its flat hop fields
|
|
498
|
-
* and attach it under the relation name
|
|
498
|
+
* and attach it under the relation name, output indistinguishable from the
|
|
499
499
|
* loader (same keys, same coercions). Presence: the target PK cell arriving
|
|
500
500
|
* Empty (a null/dangling FK at the hop) means no linked row → `null`, matching
|
|
501
501
|
* the loader's `matches[0] ?? null`. Otherwise the gathered snake cells go
|
|
@@ -509,12 +509,12 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
509
509
|
/**
|
|
510
510
|
* Fill in a client-generated UUID for a defaulted **string** PK that wasn't
|
|
511
511
|
* supplied. A server-generated PK ({@link ColumnMetadata.isGenerated}, e.g. an
|
|
512
|
-
* `int` column with PowDB's `auto` modifier) is left untouched
|
|
513
|
-
* it and the trailing `returning` reads it back
|
|
512
|
+
* `int` column with PowDB's `auto` modifier) is left untouched, PowDB assigns
|
|
513
|
+
* it and the trailing `returning` reads it back, as is any non-string PK.
|
|
514
514
|
*/
|
|
515
515
|
private applyPkDefault;
|
|
516
516
|
/**
|
|
517
|
-
* The table name as a PowQL type reference
|
|
517
|
+
* The table name as a PowQL type reference, backtick-quoted when it is a
|
|
518
518
|
* reserved word (e.g. a table named `order`). Used in every emitted
|
|
519
519
|
* statement; plain `this.table` stays in error messages.
|
|
520
520
|
*/
|
|
@@ -580,12 +580,12 @@ export declare class PowqlInterface<T extends object = Record<string, unknown>>
|
|
|
580
580
|
/** Reselect a single row by its single-column primary key value. */
|
|
581
581
|
private reselectByPk;
|
|
582
582
|
/**
|
|
583
|
-
* Empty-where guard
|
|
583
|
+
* Empty-where guard, blocks accidental whole-table writes. Mirrors the SQL
|
|
584
584
|
* path's `assertMutationHasPredicate` (query/builder.ts): it gates on the
|
|
585
585
|
* *compiled* PowQL filter fragment, NOT the shape of the `where` object. A
|
|
586
|
-
* `where` whose conditions all evaporate during compilation
|
|
586
|
+
* `where` whose conditions all evaporate during compilation, `{}`,
|
|
587
587
|
* `{ id: undefined }`, `{ OR: [] }`, `{ AND: [] }`, `{ NOT: {} }`,
|
|
588
|
-
* `{ OR: [{ f: undefined }] }
|
|
588
|
+
* `{ OR: [{ f: undefined }] }`, compiles to the empty string and is refused,
|
|
589
589
|
* because emitting a filter-less write would hit every row.
|
|
590
590
|
*/
|
|
591
591
|
private assertCompiledWhere;
|