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/powdb.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* turbine-orm/powdb
|
|
2
|
+
* turbine-orm/powdb, Turbine's PowDB / PowQL backend.
|
|
3
3
|
*
|
|
4
4
|
* PowDB is a single-node embedded database with its own query language, **PowQL**
|
|
5
5
|
* (not SQL), reached over `@zvndev/powdb-client`'s binary TCP protocol. PowDB is a
|
|
@@ -10,26 +10,26 @@
|
|
|
10
10
|
*
|
|
11
11
|
* PowDB realities shape the design (all verified firsthand against a live
|
|
12
12
|
* `powdb-server` / the embedded addon, see `docs/internal/strategy/powdb-parity-matrix.md`):
|
|
13
|
-
* - **`RETURNING` (since 0.7.0)
|
|
13
|
+
* - **`RETURNING` (since 0.7.0)**, `create/createMany/update/delete` append the
|
|
14
14
|
* trailing `returning` keyword (`RETURNING *`, all columns) and read the
|
|
15
15
|
* affected rows back in one round-trip. `upsert` is the lone exception (its
|
|
16
16
|
* statement rejects `returning`) and reselects by primary key.
|
|
17
|
-
* - **No generated IDs
|
|
17
|
+
* - **No generated IDs**, the app must supply every value → Turbine generates a
|
|
18
18
|
* client-side UUID for the primary key when it has a default.
|
|
19
19
|
* - **`uuid`/`datetime`/`bytes` columns can't hold client-supplied values** (no
|
|
20
20
|
* literal, no working cast on the wire) → Turbine maps everything onto the four
|
|
21
21
|
* writable types (`str`/`int`/`float`/`bool`); `Date` → `int` epoch micros;
|
|
22
22
|
* `string` PKs hold UUID strings.
|
|
23
|
-
* - **No JSON aggregation / link navigation
|
|
23
|
+
* - **No JSON aggregation / link navigation**, single-query nested `with` is
|
|
24
24
|
* impossible → it degrades to batched N+1 loaders (Phase B).
|
|
25
|
-
* - **Single global write lock; no savepoints/isolation
|
|
25
|
+
* - **Single global write lock; no savepoints/isolation**, nested
|
|
26
26
|
* transactions / isolation / vector / LISTEN-NOTIFY / RLS throw.
|
|
27
27
|
* Independent concurrent `db.$transaction` calls do NOT throw: they queue
|
|
28
28
|
* FIFO on a pool-level gate and run one at a time (see {@link PowdbTxGate}).
|
|
29
|
-
* Only a *re-entrant* transaction
|
|
29
|
+
* Only a *re-entrant* transaction, a `db.$transaction` opened from inside
|
|
30
30
|
* an active transaction callback's async context, which queueing would
|
|
31
|
-
* deadlock
|
|
32
|
-
* - **The wire protocol pipelines
|
|
31
|
+
* deadlock, fails fast with E017.
|
|
32
|
+
* - **The wire protocol pipelines**, `@zvndev/powdb-client` writes each
|
|
33
33
|
* request frame immediately and matches replies FIFO, so multiple queries
|
|
34
34
|
* may be in flight on one connection. {@link PowdbPool}'s checked-out
|
|
35
35
|
* clients advertise `supportsPipelining`, which lets the batch
|
|
@@ -84,9 +84,9 @@ import { normalizeKeyColumns } from './schema.js';
|
|
|
84
84
|
export const powdbDialect = {
|
|
85
85
|
...postgresDialect,
|
|
86
86
|
name: 'powdb',
|
|
87
|
-
// `resultStrategy` is decorative for PowDB
|
|
87
|
+
// `resultStrategy` is decorative for PowDB, PowqlInterface owns its own write
|
|
88
88
|
// path and never reads it. Set to 'returning' for honesty: writes use PowDB
|
|
89
|
-
// 0.7.0's trailing `returning` keyword (upsert excepted
|
|
89
|
+
// 0.7.0's trailing `returning` keyword (upsert excepted, see PowqlInterface).
|
|
90
90
|
resultStrategy: 'returning',
|
|
91
91
|
supportsReturning: true,
|
|
92
92
|
supportsVector: false,
|
|
@@ -103,7 +103,7 @@ export const powdbDialect = {
|
|
|
103
103
|
beginStatement: () => 'begin',
|
|
104
104
|
commitStatement: () => 'commit',
|
|
105
105
|
rollbackStatement: () => 'rollback',
|
|
106
|
-
// PowDB has no savepoints
|
|
106
|
+
// PowDB has no savepoints, a nested `tx.$transaction` would emit one and PowDB
|
|
107
107
|
// rejects it with a cryptic parse error. Throw a clear typed error instead.
|
|
108
108
|
// These run synchronously in TransactionClient.$transaction before any query,
|
|
109
109
|
// so the nested call fails fast with no partial DB state.
|
|
@@ -111,9 +111,9 @@ export const powdbDialect = {
|
|
|
111
111
|
releaseSavepointStatement: throwNoNestedTransaction,
|
|
112
112
|
rollbackToSavepointStatement: throwNoNestedTransaction,
|
|
113
113
|
};
|
|
114
|
-
/** Reject any savepoint (nested-transaction) operation
|
|
114
|
+
/** Reject any savepoint (nested-transaction) operation, PowDB is single-writer. */
|
|
115
115
|
function throwNoNestedTransaction() {
|
|
116
|
-
throw new UnsupportedFeatureError('nested transactions', 'powdb', 'PowDB is single-writer
|
|
116
|
+
throw new UnsupportedFeatureError('nested transactions', 'powdb', 'PowDB is single-writer, it has one global write lock and no savepoints. ' +
|
|
117
117
|
'Complete the open transaction before starting another; do not nest `$transaction` calls.');
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
@@ -190,7 +190,7 @@ export function parsePowdbUrl(connectionString) {
|
|
|
190
190
|
export function assertSupportedPowdbVersion(version) {
|
|
191
191
|
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(version ?? '').trim());
|
|
192
192
|
if (!m)
|
|
193
|
-
return; // unknown / non-semver
|
|
193
|
+
return; // unknown / non-semver, don't block
|
|
194
194
|
const [major, minor] = [Number(m[1]), Number(m[2])];
|
|
195
195
|
// 0.7.0 is the floor; >= 0.7 (or any 1.x+) passes.
|
|
196
196
|
if (major > 0 || (major === 0 && minor >= 7))
|
|
@@ -354,7 +354,7 @@ export function isJsonColumn(col) {
|
|
|
354
354
|
*/
|
|
355
355
|
export function powqlColumnType(col) {
|
|
356
356
|
if (col.isArray) {
|
|
357
|
-
throw new ValidationError(`[turbine] Column "${col.name}" is an array
|
|
357
|
+
throw new ValidationError(`[turbine] Column "${col.name}" is an array, PowDB has no array type. Arrays are unsupported on the PowDB backend.`);
|
|
358
358
|
}
|
|
359
359
|
if (isJsonColumn(col))
|
|
360
360
|
return 'json';
|
|
@@ -370,7 +370,7 @@ export function powqlColumnType(col) {
|
|
|
370
370
|
if (ts === 'string')
|
|
371
371
|
return 'str';
|
|
372
372
|
if (ts === 'Buffer' || ts === 'Uint8Array') {
|
|
373
|
-
throw new ValidationError(`[turbine] Column "${col.name}" is binary
|
|
373
|
+
throw new ValidationError(`[turbine] Column "${col.name}" is binary, PowDB cannot store client-supplied bytes on the wire. Use a string (e.g. base64) instead.`);
|
|
374
374
|
}
|
|
375
375
|
return 'str';
|
|
376
376
|
}
|
|
@@ -393,7 +393,7 @@ function isDateColumn(col) {
|
|
|
393
393
|
* synthesizing a client-side value for it.
|
|
394
394
|
*/
|
|
395
395
|
/**
|
|
396
|
-
* PowQL reserved words
|
|
396
|
+
* PowQL reserved words, the v0.10 lexer keyword table from POWQL.md's
|
|
397
397
|
* "Reserved Words and Quoting" section, including the v0.10 additions
|
|
398
398
|
* `schema` and `describe`. Keyword matching is case-sensitive in the lexer,
|
|
399
399
|
* so only the exact lowercase form collides.
|
|
@@ -497,7 +497,7 @@ const POWQL_BARE_IDENT = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
497
497
|
/**
|
|
498
498
|
* Backtick-quote an identifier when PowQL would otherwise lex it as a keyword
|
|
499
499
|
* (or when it contains characters outside the bare-identifier grammar).
|
|
500
|
-
* Applied only in bare-identifier positions
|
|
500
|
+
* Applied only in bare-identifier positions, DDL type/field names, index DDL,
|
|
501
501
|
* and `insert`/`update`/`upsert` assignment targets. Dotted references
|
|
502
502
|
* (`.col` in filters/projections/ordering) bypass keyword lookup on every
|
|
503
503
|
* engine version and deliberately stay bare for ≤0.9 compatibility. Backticks
|
|
@@ -558,7 +558,7 @@ export function powqlSchemaDDL(schema, opts = {}) {
|
|
|
558
558
|
const stmts = [];
|
|
559
559
|
for (const meta of Object.values(schema.tables)) {
|
|
560
560
|
const pkSet = new Set(meta.primaryKey);
|
|
561
|
-
// PowDB's `unique` is single-column only
|
|
561
|
+
// PowDB's `unique` is single-column only, there is no composite-unique
|
|
562
562
|
// constraint (`add unique` takes one `.column`). So the per-field `unique`
|
|
563
563
|
// modifier is emitted only for a single-column PK; a composite PK (e.g. a
|
|
564
564
|
// m2m junction's `(source_id, target_id)`) marks its columns `required` but
|
|
@@ -703,7 +703,7 @@ export async function applyPowdbLinks(exec, schema, options = {}) {
|
|
|
703
703
|
}
|
|
704
704
|
/**
|
|
705
705
|
* Read the live `schema links` listing into {@link PowdbDesiredLink} rows (owner,
|
|
706
|
-
* name, target, localKey, targetKey; cardinality is dropped
|
|
706
|
+
* name, target, localKey, targetKey; cardinality is dropped, it is derived, not
|
|
707
707
|
* a DDL input). An empty catalog returns `[]`, never an error. Naming edge: a
|
|
708
708
|
* table literally named `links` is described with `describe links`, but the
|
|
709
709
|
* link LISTING is the contextual keyword form `schema links`.
|
|
@@ -890,7 +890,7 @@ export function rowToEntity(raw, meta, native = false) {
|
|
|
890
890
|
* So we always run the unique-constraint and message-shape checks first (they
|
|
891
891
|
* fire for both transports and extract detail like constraint / column names),
|
|
892
892
|
* then classify by the typed wire error class (`.wireErrorClass`, networked
|
|
893
|
-
* server >= 0.17
|
|
893
|
+
* server >= 0.17, accurate even when the server sanitized the message text),
|
|
894
894
|
* then fall through to the networked `.code` switch.
|
|
895
895
|
*/
|
|
896
896
|
export function wrapPowdbError(err) {
|
|
@@ -898,12 +898,12 @@ export function wrapPowdbError(err) {
|
|
|
898
898
|
return new ConnectionError(`[turbine] PowDB error: ${String(err)}`);
|
|
899
899
|
const e = err;
|
|
900
900
|
const msg = e.message ?? 'unknown PowDB error';
|
|
901
|
-
// Unique-constraint
|
|
901
|
+
// Unique-constraint, message-based on both transports.
|
|
902
902
|
if (/unique (constraint|expression index) violation/i.test(msg)) {
|
|
903
903
|
const m = /on\s+\S+\.(\w+)/i.exec(msg);
|
|
904
904
|
return new UniqueConstraintError({ constraint: m?.[1], cause: err });
|
|
905
905
|
}
|
|
906
|
-
// NOT NULL
|
|
906
|
+
// NOT NULL, "column 'x' is required but no value was provided". Map on BOTH
|
|
907
907
|
// transports (the networked path used to collapse this into E003).
|
|
908
908
|
if (/required|not[- ]?null|no value/i.test(msg)) {
|
|
909
909
|
const m = /column ['"]?(\w+)['"]?/i.exec(msg);
|
|
@@ -1016,25 +1016,25 @@ export function wrapPowdbError(err) {
|
|
|
1016
1016
|
const wireClass = err.wireErrorClass;
|
|
1017
1017
|
if (typeof wireClass === 'number') {
|
|
1018
1018
|
switch (wireClass) {
|
|
1019
|
-
case 3: // timeout (per-query budget, gate wait, idle timeout)
|
|
1019
|
+
case 3: // timeout (per-query budget, gate wait, idle timeout), retryable
|
|
1020
1020
|
return new TimeoutError(0, 'PowDB query', { message: `[turbine] PowDB ${msg}`, cause: err });
|
|
1021
|
-
case 4: // limit_exceeded (memory / size budget)
|
|
1021
|
+
case 4: // limit_exceeded (memory / size budget), a query-shape defect
|
|
1022
1022
|
return new ValidationError(`[turbine] PowDB resource limit exceeded: ${msg}`);
|
|
1023
|
-
case 5: // readonly_refused
|
|
1023
|
+
case 5: // readonly_refused, the snapshot-serving routing signal
|
|
1024
1024
|
return new ReadOnlyError(`PowDB refused a write on a read-only database: ${msg}.`, {
|
|
1025
1025
|
cause: err,
|
|
1026
1026
|
reason: 'snapshot',
|
|
1027
1027
|
});
|
|
1028
1028
|
case 6: // auth_failed at CONNECT
|
|
1029
1029
|
return new ConnectionError(`[turbine] PowDB authentication failed: ${msg} (check the user / password / dbName for this connection).`, { cause: err });
|
|
1030
|
-
case 7: // rate_limited (repeated bad auth)
|
|
1030
|
+
case 7: // rate_limited (repeated bad auth), connection-establishment class
|
|
1031
1031
|
return new ConnectionError(`[turbine] PowDB rate-limited this address after repeated failed authentication: ${msg}. Wait before retrying.`, { cause: err });
|
|
1032
1032
|
case 8: {
|
|
1033
|
-
// constraint_violation
|
|
1033
|
+
// constraint_violation, today that is always a unique index
|
|
1034
1034
|
const m = /on\s+\S+\.(\w+)/i.exec(msg);
|
|
1035
1035
|
return new UniqueConstraintError({ constraint: m?.[1], cause: err });
|
|
1036
1036
|
}
|
|
1037
|
-
case 9: // cancelled (issuing client disconnected)
|
|
1037
|
+
case 9: // cancelled (issuing client disconnected), final, never retry
|
|
1038
1038
|
return new ConnectionError(`[turbine] PowDB query cancelled by client disconnect: ${msg}`, { cause: err });
|
|
1039
1039
|
case 1: // parse
|
|
1040
1040
|
case 2: // execution
|
|
@@ -1116,15 +1116,15 @@ function txControl(powql) {
|
|
|
1116
1116
|
* the write lock the outer transaction holds), and on the networked transport
|
|
1117
1117
|
* it would block a fresh pooled connection on the lock forever. This guard
|
|
1118
1118
|
* converts that hang into a fast, typed error. Independent concurrent
|
|
1119
|
-
* transactions do NOT hit this
|
|
1119
|
+
* transactions do NOT hit this, they queue FIFO on {@link PowdbTxGate}.
|
|
1120
1120
|
*/
|
|
1121
1121
|
function reentrantTransactionError() {
|
|
1122
|
-
return new UnsupportedFeatureError('re-entrant transactions', 'powdb', 'PowDB is single-writer
|
|
1122
|
+
return new UnsupportedFeatureError('re-entrant transactions', 'powdb', 'PowDB is single-writer, a transaction opened from inside an active transaction callback would deadlock ' +
|
|
1123
1123
|
'on the write lock the open transaction holds. Use the `tx` client the callback receives, or start the ' +
|
|
1124
1124
|
'second transaction after the first completes. (Independent concurrent transactions queue automatically.)');
|
|
1125
1125
|
}
|
|
1126
1126
|
// ---------------------------------------------------------------------------
|
|
1127
|
-
// Single-writer transaction gate
|
|
1127
|
+
// Single-writer transaction gate, FIFO queueing + re-entrancy detection
|
|
1128
1128
|
// ---------------------------------------------------------------------------
|
|
1129
1129
|
/**
|
|
1130
1130
|
* Default cap (ms) on how long a `begin` may wait in the FIFO queue for
|
|
@@ -1151,8 +1151,8 @@ const powdbTxStorage = new AsyncLocalStorage();
|
|
|
1151
1151
|
* (and the embedded engine rejects it with a raw parse error).
|
|
1152
1152
|
*
|
|
1153
1153
|
* `acquire()` is called (synchronously, see below) for every `begin`:
|
|
1154
|
-
* - a **re-entrant** `begin
|
|
1155
|
-
* async context, detected via {@link powdbTxStorage}
|
|
1154
|
+
* - a **re-entrant** `begin`, issued from inside an active transaction's
|
|
1155
|
+
* async context, detected via {@link powdbTxStorage}, throws E017
|
|
1156
1156
|
* immediately. Queueing it can never succeed: the open transaction cannot
|
|
1157
1157
|
* commit while its callback awaits the queued one.
|
|
1158
1158
|
* - an **independent** `begin` waits its FIFO turn, bounded by the queue
|
|
@@ -1191,7 +1191,7 @@ const powdbTxStorage = new AsyncLocalStorage();
|
|
|
1191
1191
|
*/
|
|
1192
1192
|
class PowdbTxGate {
|
|
1193
1193
|
queueTimeoutMs;
|
|
1194
|
-
/** Tail of the FIFO queue
|
|
1194
|
+
/** Tail of the FIFO queue, resolves once every earlier transaction has finished. */
|
|
1195
1195
|
tail = Promise.resolve();
|
|
1196
1196
|
constructor(queueTimeoutMs) {
|
|
1197
1197
|
this.queueTimeoutMs = queueTimeoutMs;
|
|
@@ -1207,11 +1207,11 @@ class PowdbTxGate {
|
|
|
1207
1207
|
async acquire() {
|
|
1208
1208
|
// Walk the WHOLE marker chain, not just the innermost marker: with two
|
|
1209
1209
|
// pools, dbA-tx → dbB-tx → dbA-begin leaves dbB's marker innermost, but
|
|
1210
|
-
// the dbA ancestor is still open
|
|
1210
|
+
// the dbA ancestor is still open, queueing the inner dbA begin behind it
|
|
1211
1211
|
// would deadlock. Any live ancestor on this gate ⇒ re-entrant E017.
|
|
1212
1212
|
// Prune completed heads first (`done` never flips back) so sequential
|
|
1213
|
-
// transactions issued from one long-lived context do not chain
|
|
1214
|
-
//
|
|
1213
|
+
// transactions issued from one long-lived context do not chain, and leak
|
|
1214
|
+
// - unboundedly; what remains is bounded by real nesting depth.
|
|
1215
1215
|
let parent = powdbTxStorage.getStore();
|
|
1216
1216
|
while (parent?.done)
|
|
1217
1217
|
parent = parent.parent;
|
|
@@ -1365,7 +1365,7 @@ export class PowdbPool {
|
|
|
1365
1365
|
* Pool-level single-writer gate. PowDB holds one global write lock, so at
|
|
1366
1366
|
* most one transaction may be open across the whole pool. Concurrent
|
|
1367
1367
|
* `begin`s queue FIFO on the gate (instead of checking out a second
|
|
1368
|
-
* connection and blocking on the lock forever
|
|
1368
|
+
* connection and blocking on the lock forever, the networked hang);
|
|
1369
1369
|
* re-entrant `begin`s throw E017 (see {@link PowdbTxGate}).
|
|
1370
1370
|
*/
|
|
1371
1371
|
txGate;
|
|
@@ -1425,7 +1425,7 @@ export class PowdbPool {
|
|
|
1425
1425
|
if ((ctl === 'commit' || ctl === 'rollback') && this.poolHold === null) {
|
|
1426
1426
|
// No gate hold → our `begin` never ran (gate timeout / re-entrant
|
|
1427
1427
|
// E017 / no begin at all). Never forward a stray commit/rollback to
|
|
1428
|
-
// the engine
|
|
1428
|
+
// the engine, PowDB is single-writer, so it could only ever end a
|
|
1429
1429
|
// DIFFERENT caller's open transaction. Empty success instead.
|
|
1430
1430
|
return { rows: [], rowCount: 0, fields: [] };
|
|
1431
1431
|
}
|
|
@@ -1449,11 +1449,11 @@ export class PowdbPool {
|
|
|
1449
1449
|
/**
|
|
1450
1450
|
* Typed guard mirroring {@link PowdbEmbeddedPool}: after `end()` the driver
|
|
1451
1451
|
* pool throws a raw `Error('pool closed')` that {@link wrapPowdbError}
|
|
1452
|
-
* cannot classify
|
|
1452
|
+
* cannot classify, surface the same ConnectionError on both transports.
|
|
1453
1453
|
*/
|
|
1454
1454
|
assertOpen() {
|
|
1455
1455
|
if (this.closed) {
|
|
1456
|
-
throw new ConnectionError('[turbine] The PowDB pool is closed
|
|
1456
|
+
throw new ConnectionError('[turbine] The PowDB pool is closed, disconnect() was already called on this client.');
|
|
1457
1457
|
}
|
|
1458
1458
|
}
|
|
1459
1459
|
async connect() {
|
|
@@ -1476,7 +1476,7 @@ export class PowdbPool {
|
|
|
1476
1476
|
// lets the batch `$transaction([...])` path dispatch all statements in
|
|
1477
1477
|
// one write burst instead of paying a round trip per statement. Safe
|
|
1478
1478
|
// for the batch's rollback contract because a failed statement leaves
|
|
1479
|
-
// the engine's transaction open (no aborted state, no auto-rollback)
|
|
1479
|
+
// the engine's transaction open (no aborted state, no auto-rollback) -
|
|
1480
1480
|
// later pipelined statements execute inside the same still-open
|
|
1481
1481
|
// transaction and the final `rollback` discards every effect. (The
|
|
1482
1482
|
// batch path awaits `begin` before dispatching the burst, so the gate
|
|
@@ -1494,7 +1494,7 @@ export class PowdbPool {
|
|
|
1494
1494
|
hold = await this.txGate.acquire();
|
|
1495
1495
|
}
|
|
1496
1496
|
if ((ctl === 'commit' || ctl === 'rollback') && hold === null) {
|
|
1497
|
-
// This connection never acquired the gate
|
|
1497
|
+
// This connection never acquired the gate, its `begin` never ran
|
|
1498
1498
|
// (gate timeout / re-entrant E017). A stray commit/rollback must
|
|
1499
1499
|
// never reach the single-writer engine, where it could only end a
|
|
1500
1500
|
// DIFFERENT caller's open transaction. Empty success instead.
|
|
@@ -1530,7 +1530,7 @@ export class PowdbPool {
|
|
|
1530
1530
|
},
|
|
1531
1531
|
release: (err) => {
|
|
1532
1532
|
// Releasing this connection ends its transaction scope. pg semantics:
|
|
1533
|
-
// a truthy `err` means "destroy, don't re-idle"
|
|
1533
|
+
// a truthy `err` means "destroy, don't re-idle", client.ts's
|
|
1534
1534
|
// $transaction timeout path relies on that to keep an abandoned
|
|
1535
1535
|
// callback's connection out of the pool. Additionally, an OPEN hold
|
|
1536
1536
|
// here means the tx begun on this connection never saw commit/rollback
|
|
@@ -1539,7 +1539,7 @@ export class PowdbPool {
|
|
|
1539
1539
|
// server-side transaction ends (destroying the socket alone leaves it
|
|
1540
1540
|
// open until the server's idle timeout), THEN hand the gate to the
|
|
1541
1541
|
// next queued transaction. If the rollback fails or times out the
|
|
1542
|
-
// connection is treated as broken and destroyed. Never throws
|
|
1542
|
+
// connection is treated as broken and destroyed. Never throws, a
|
|
1543
1543
|
// teardown error must not mask the transaction's real outcome.
|
|
1544
1544
|
const openHold = hold;
|
|
1545
1545
|
hold = null;
|
|
@@ -1606,14 +1606,14 @@ function normalizeEmbeddedResult(r) {
|
|
|
1606
1606
|
}
|
|
1607
1607
|
/**
|
|
1608
1608
|
* Encode a JS value as a **PowQL literal** for the embedded driver, which takes
|
|
1609
|
-
* no params array
|
|
1609
|
+
* no params array, `$N` placeholders must be materialized into the query text.
|
|
1610
1610
|
*
|
|
1611
1611
|
* This is the single place Turbine builds PowQL text from a value, so it is the
|
|
1612
1612
|
* security-critical surface. String encoding matches PowDB's lexer
|
|
1613
1613
|
* (`crates/query/src/lexer.rs`) exactly: a string literal is `"…"`, and inside
|
|
1614
1614
|
* it the lexer recognizes only the escapes `\"`, `\\`, `\n`, `\t` (any other
|
|
1615
|
-
* `\x` drops the backslash and keeps `x`; every non-`\`/non-`"` char
|
|
1616
|
-
* newlines, CR, unicode
|
|
1615
|
+
* `\x` drops the backslash and keeps `x`; every non-`\`/non-`"` char, raw
|
|
1616
|
+
* newlines, CR, unicode, is taken literally). So we escape `\` → `\\` and
|
|
1617
1617
|
* `"` → `\"` (the only breakout vectors), render `\n`/`\t` as their recognized
|
|
1618
1618
|
* escapes, and leave everything else raw. Verified against the real engine:
|
|
1619
1619
|
* quotes, backslashes, `$N`, `"); drop … --`, raw CR, and emoji all round-trip
|
|
@@ -1715,7 +1715,7 @@ function powqlNumberText(n) {
|
|
|
1715
1715
|
* `git diff v0.19.0 v0.19.1 -- crates/query/src/lexer.rs` is empty (the bare-dotted-path
|
|
1716
1716
|
* hard error is parser-level, not tokenization), so this ceiling stays `'0.19'`. The
|
|
1717
1717
|
* guard in {@link PowdbEmbeddedPool.exec} compares major.minor only, so `'0.19'`
|
|
1718
|
-
* already covers every 0.19.x patch
|
|
1718
|
+
* already covers every 0.19.x patch, no bump is needed for 0.19.1.
|
|
1719
1719
|
*/
|
|
1720
1720
|
export const POWQL_LEXER_TESTED_CEILING = '0.19';
|
|
1721
1721
|
/**
|
|
@@ -1744,14 +1744,14 @@ function encodePowqlString(s, position) {
|
|
|
1744
1744
|
else if (ch === '\t')
|
|
1745
1745
|
out += '\\t';
|
|
1746
1746
|
else
|
|
1747
|
-
out += ch; // raw
|
|
1747
|
+
out += ch; // raw, the lexer takes any other char literally (incl. CR, unicode)
|
|
1748
1748
|
}
|
|
1749
1749
|
return `${out}"`;
|
|
1750
1750
|
}
|
|
1751
1751
|
/**
|
|
1752
1752
|
* Substitute every `$N` placeholder in a generator-produced PowQL template with
|
|
1753
1753
|
* the encoded literal of `params[N-1]`. Safe because the template is produced by
|
|
1754
|
-
* {@link PowqlInterface} and contains **no** user string literals
|
|
1754
|
+
* {@link PowqlInterface} and contains **no** user string literals, the only
|
|
1755
1755
|
* `$<digits>` tokens are genuine positional placeholders, so a single scan
|
|
1756
1756
|
* cannot accidentally rewrite a `$N` that is itself part of a value (values are
|
|
1757
1757
|
* params, never inlined into the template by the generator).
|
|
@@ -1782,7 +1782,7 @@ export class PowdbEmbeddedPool {
|
|
|
1782
1782
|
closed = false;
|
|
1783
1783
|
/**
|
|
1784
1784
|
* Single-writer gate. The embedded engine is one handle with one global
|
|
1785
|
-
* write lock
|
|
1785
|
+
* write lock, only one transaction may be open at a time. A re-entrant
|
|
1786
1786
|
* `begin` (a fresh top-level `db.$transaction` opened inside an open one's
|
|
1787
1787
|
* callback) would otherwise hit PowDB's raw "already in a transaction"
|
|
1788
1788
|
* parse error; the gate surfaces a typed E017 instead, while INDEPENDENT
|
|
@@ -1883,13 +1883,13 @@ export class PowdbEmbeddedPool {
|
|
|
1883
1883
|
}
|
|
1884
1884
|
}
|
|
1885
1885
|
if ((ctl === 'commit' || ctl === 'rollback') && holdRef.hold === null) {
|
|
1886
|
-
// This context never acquired the gate
|
|
1886
|
+
// This context never acquired the gate, its `begin` never ran (the
|
|
1887
1887
|
// gate timed out / threw re-entrant E017, or no begin was issued at
|
|
1888
1888
|
// all). The engine is ONE shared handle: forwarding this stray
|
|
1889
1889
|
// commit/rollback would hit whatever transaction ANOTHER caller has
|
|
1890
1890
|
// open on it (live-reproduced: a best-effort ROLLBACK after a failed
|
|
1891
1891
|
// begin silently discarded a concurrent transaction's writes). Swallow
|
|
1892
|
-
// it as an empty success instead
|
|
1892
|
+
// it as an empty success instead, there is nothing of ours to end.
|
|
1893
1893
|
return { rows: [], rowCount: 0, fields: [] };
|
|
1894
1894
|
}
|
|
1895
1895
|
try {
|
|
@@ -1915,7 +1915,7 @@ export class PowdbEmbeddedPool {
|
|
|
1915
1915
|
return this.run(powql, params, this.poolHoldRef);
|
|
1916
1916
|
}
|
|
1917
1917
|
async connect() {
|
|
1918
|
-
// Single in-process handle
|
|
1918
|
+
// Single in-process handle, the "client" shares the one Database; tx
|
|
1919
1919
|
// keywords run serially on it. Each checked-out client scopes its own
|
|
1920
1920
|
// gate hold so release() only ever finishes ITS transaction.
|
|
1921
1921
|
const holdRef = { hold: null };
|
|
@@ -1933,7 +1933,7 @@ export class PowdbEmbeddedPool {
|
|
|
1933
1933
|
},
|
|
1934
1934
|
release: () => {
|
|
1935
1935
|
// End-of-scope safety net (see PowdbPool.connect()): a tx torn down
|
|
1936
|
-
// without an explicit commit/rollback must not wedge the queue
|
|
1936
|
+
// without an explicit commit/rollback must not wedge the queue, and
|
|
1937
1937
|
// on the ONE shared embedded handle its open engine transaction must
|
|
1938
1938
|
// actually be rolled back before the gate moves on, or the next
|
|
1939
1939
|
// transaction's work interleaves into it. run() owns the
|
|
@@ -1968,7 +1968,7 @@ export class PowdbEmbeddedPool {
|
|
|
1968
1968
|
}
|
|
1969
1969
|
}
|
|
1970
1970
|
// ---------------------------------------------------------------------------
|
|
1971
|
-
// PowqlInterface
|
|
1971
|
+
// PowqlInterface, the PowQL query generator (Phase A: flat CRUD via returning)
|
|
1972
1972
|
// ---------------------------------------------------------------------------
|
|
1973
1973
|
// `describe`-based introspection (programmatic API; see powdb-introspect.ts).
|
|
1974
1974
|
export { introspectPowdbDatabase, } from './powdb-introspect.js';
|
|
@@ -1980,19 +1980,19 @@ export { PowqlInterface } from './powql.js';
|
|
|
1980
1980
|
async function loadPowdb() {
|
|
1981
1981
|
try {
|
|
1982
1982
|
// Via the .cts helper so the CJS build keeps a path to a REAL dynamic
|
|
1983
|
-
// import()
|
|
1983
|
+
// import(), @zvndev/powdb-client ≥ 0.9 is ESM-only, and the CommonJS
|
|
1984
1984
|
// pass transpiles a plain `import()` here into an unusable `require()`.
|
|
1985
1985
|
return (await importOptionalPeer('@zvndev/powdb-client'));
|
|
1986
1986
|
}
|
|
1987
1987
|
catch (err) {
|
|
1988
|
-
throw new ConnectionError("[turbine] turbine-orm/powdb requires the optional peer dependency '@zvndev/powdb-client'. Install it: npm i @zvndev/powdb-client
|
|
1988
|
+
throw new ConnectionError("[turbine] turbine-orm/powdb requires the optional peer dependency '@zvndev/powdb-client'. Install it: npm i @zvndev/powdb-client, " +
|
|
1989
1989
|
'or construct the PowDB pool yourself and inject it: turbinePowDB(pool, schema). ' +
|
|
1990
1990
|
`(${err.message})`);
|
|
1991
1991
|
}
|
|
1992
1992
|
}
|
|
1993
1993
|
/**
|
|
1994
1994
|
* Dynamically load `@zvndev/powdb-embedded` (the in-process napi addon). Kept out
|
|
1995
|
-
* of the static import graph
|
|
1995
|
+
* of the static import graph, `import 'turbine-orm/powdb'` never pulls it. A
|
|
1996
1996
|
* missing package or an unsupported platform (Intel-mac/musl/Windows ship no
|
|
1997
1997
|
* prebuilt binary) throws a clear {@link ConnectionError} pointing at the
|
|
1998
1998
|
* from-source `npm run build` fallback.
|
|
@@ -2000,7 +2000,7 @@ async function loadPowdb() {
|
|
|
2000
2000
|
async function loadPowdbEmbedded() {
|
|
2001
2001
|
let mod;
|
|
2002
2002
|
try {
|
|
2003
|
-
// Via the .cts helper
|
|
2003
|
+
// Via the .cts helper, keeps a real dynamic import() available to the
|
|
2004
2004
|
// CJS build in case a future addon version ships ESM-only (see loadPowdb).
|
|
2005
2005
|
mod = (await importOptionalPeer('@zvndev/powdb-embedded'));
|
|
2006
2006
|
}
|
|
@@ -2008,12 +2008,12 @@ async function loadPowdbEmbedded() {
|
|
|
2008
2008
|
throw new ConnectionError("[turbine] turbine-orm/powdb embedded mode requires the optional peer '@zvndev/powdb-embedded'. " +
|
|
2009
2009
|
'Install it: npm i @zvndev/powdb-embedded. If install succeeded but loading failed, your platform has no ' +
|
|
2010
2010
|
'prebuilt binary (prebuilts ship for macOS arm64/x64 and Linux glibc x64/arm64; Intel-mac/musl/Windows ' +
|
|
2011
|
-
'build from source)
|
|
2011
|
+
'build from source), build it with `npm run build` in the addon, then retry. You can also construct the ' +
|
|
2012
2012
|
'pool yourself and inject it: turbinePowDB(pool, schema). ' +
|
|
2013
2013
|
`(${err.message})`);
|
|
2014
2014
|
}
|
|
2015
2015
|
if (!mod || typeof mod.Database?.open !== 'function') {
|
|
2016
|
-
throw new ConnectionError("[turbine] '@zvndev/powdb-embedded' loaded but did not export Database.open
|
|
2016
|
+
throw new ConnectionError("[turbine] '@zvndev/powdb-embedded' loaded but did not export Database.open, the installed version is " +
|
|
2017
2017
|
'likely incompatible (turbine-orm/powdb embedded requires @zvndev/powdb-embedded ^0.7.0).');
|
|
2018
2018
|
}
|
|
2019
2019
|
return mod;
|
|
@@ -2102,7 +2102,7 @@ async function openEmbeddedPool(target, poolOptions = {}, assumeEngineVersion, i
|
|
|
2102
2102
|
* - an `{ embedded: <data-dir> }` object → an in-process
|
|
2103
2103
|
* `@zvndev/powdb-embedded` database (no server);
|
|
2104
2104
|
* - an already-constructed `@zvndev/powdb-client` `Pool` or {@link PowdbPool}
|
|
2105
|
-
* (injection
|
|
2105
|
+
* (injection, you own its lifecycle and `disconnect()` is a no-op).
|
|
2106
2106
|
*
|
|
2107
2107
|
* On the networked transport the server version is probed and a clear
|
|
2108
2108
|
* {@link ConnectionError} is thrown if it is older than {@link MIN_POWDB_VERSION}
|
|
@@ -2179,7 +2179,7 @@ export async function turbinePowDB(target, schema, options = {}) {
|
|
|
2179
2179
|
patch.end = close;
|
|
2180
2180
|
}
|
|
2181
2181
|
else {
|
|
2182
|
-
// Injected pool
|
|
2182
|
+
// Injected pool, the caller owns its lifecycle.
|
|
2183
2183
|
client.disconnect = async () => { };
|
|
2184
2184
|
}
|
|
2185
2185
|
return client;
|
package/dist/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;
|