turbine-orm 0.34.0 → 0.36.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 +18 -16
- package/dist/cjs/cli/index.js +109 -16
- package/dist/cjs/cli/migrate.js +78 -3
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.js +333 -22
- package/dist/cjs/cli/ui.js +7 -1
- package/dist/cjs/client.js +26 -4
- package/dist/cjs/dialect.js +2 -1
- package/dist/cjs/errors.js +41 -1
- package/dist/cjs/generate.js +23 -2
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/mssql.js +27 -5
- package/dist/cjs/mysql.js +4 -0
- package/dist/cjs/powdb.js +197 -25
- package/dist/cjs/powql.js +515 -51
- package/dist/cjs/query/aggregates.js +683 -0
- package/dist/cjs/query/batched-loader.js +2 -0
- package/dist/cjs/query/builder.js +361 -4508
- package/dist/cjs/query/filters.js +12 -0
- package/dist/cjs/query/relations.js +1698 -0
- package/dist/cjs/query/where-compile.js +180 -0
- package/dist/cjs/query/where.js +1491 -0
- package/dist/cjs/query/writes.js +680 -0
- package/dist/cjs/schema-builder.js +6 -0
- package/dist/cjs/schema-metadata.js +4 -0
- package/dist/cjs/schema-sql.js +265 -3
- package/dist/cjs/sqlite.js +4 -1
- package/dist/cli/index.d.ts +8 -2
- package/dist/cli/index.js +111 -18
- package/dist/cli/migrate.d.ts +24 -1
- package/dist/cli/migrate.js +77 -3
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +46 -13
- package/dist/cli/studio.js +331 -23
- package/dist/cli/ui.js +7 -1
- package/dist/client.d.ts +32 -5
- package/dist/client.js +26 -4
- package/dist/dialect.d.ts +28 -6
- package/dist/dialect.js +2 -1
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +39 -0
- package/dist/generate.js +23 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/mssql.js +27 -5
- package/dist/mysql.js +4 -0
- package/dist/powdb.d.ts +135 -9
- package/dist/powdb.js +197 -25
- package/dist/powql.d.ts +166 -4
- package/dist/powql.js +516 -52
- package/dist/query/aggregates.d.ts +74 -0
- package/dist/query/aggregates.js +641 -0
- package/dist/query/batched-loader.d.ts +6 -0
- package/dist/query/batched-loader.js +2 -0
- package/dist/query/builder.d.ts +98 -830
- package/dist/query/builder.js +366 -4513
- package/dist/query/deferred.d.ts +13 -2
- package/dist/query/filters.d.ts +7 -0
- package/dist/query/filters.js +11 -0
- package/dist/query/relations.d.ts +441 -0
- package/dist/query/relations.js +1627 -0
- package/dist/query/types.d.ts +25 -6
- package/dist/query/where-compile.d.ts +139 -0
- package/dist/query/where-compile.js +175 -0
- package/dist/query/where.d.ts +494 -0
- package/dist/query/where.js +1431 -0
- package/dist/query/writes.d.ts +131 -0
- package/dist/query/writes.js +626 -0
- package/dist/schema-builder.d.ts +18 -3
- package/dist/schema-builder.js +6 -0
- package/dist/schema-metadata.js +4 -0
- package/dist/schema-sql.d.ts +60 -3
- package/dist/schema-sql.js +261 -4
- package/dist/schema.d.ts +10 -0
- package/dist/sqlite.js +4 -1
- package/package.json +4 -4
package/dist/powdb.d.ts
CHANGED
|
@@ -252,11 +252,13 @@ export interface PowdbCapabilities {
|
|
|
252
252
|
docFieldIndexes: boolean;
|
|
253
253
|
/** ≥ 0.10: `schema` / `describe` introspection statements. */
|
|
254
254
|
introspection: boolean;
|
|
255
|
+
/** ≥ 0.13: server-side joins, hash-accelerated and bounded. */
|
|
256
|
+
serverJoins: boolean;
|
|
255
257
|
/** Networked only: server ≥ 0.13 AND the client exposes `queryNativeRaw`. */
|
|
256
258
|
nativeRaw: boolean;
|
|
257
259
|
}
|
|
258
260
|
/** The feature-gate capability keys (everything except the version/nativeRaw metadata). */
|
|
259
|
-
type PowdbFeatureKey = 'jsonDocs' | 'docFieldIndexes' | 'introspection';
|
|
261
|
+
type PowdbFeatureKey = 'jsonDocs' | 'docFieldIndexes' | 'introspection' | 'serverJoins';
|
|
260
262
|
/**
|
|
261
263
|
* Trusted-caller default: every FEATURE gate on, engine version unknown. Used
|
|
262
264
|
* for a directly-constructed {@link PowdbPool} / {@link PowdbEmbeddedPool} that
|
|
@@ -439,6 +441,15 @@ export interface PowdbPoolOptions {
|
|
|
439
441
|
* Default `false` (typed-error-only).
|
|
440
442
|
*/
|
|
441
443
|
retryStaleReads?: boolean;
|
|
444
|
+
/**
|
|
445
|
+
* Mark this pool read-only: {@link PowqlInterface}'s exec seam then fails a
|
|
446
|
+
* write (or a tx-control `begin`) fast with a {@link ReadOnlyError} (E018)
|
|
447
|
+
* before it reaches the wire. An `{ embedded, readonly: true }` target forces
|
|
448
|
+
* this true; a networked pool bound to a read-only role can also set it so
|
|
449
|
+
* writes are rejected locally instead of round-tripping to the engine's
|
|
450
|
+
* refusal. Default `false`.
|
|
451
|
+
*/
|
|
452
|
+
readonly?: boolean;
|
|
442
453
|
}
|
|
443
454
|
/**
|
|
444
455
|
* A {@link PgCompatPool} backed by a `@zvndev/powdb-client` `Pool`. The query
|
|
@@ -476,6 +487,13 @@ export declare class PowdbPool implements PgCompatPool {
|
|
|
476
487
|
readonly capabilities: PowdbCapabilities;
|
|
477
488
|
/** Opt-in first-statement-read replay on a stale wire frame (read by {@link PowqlInterface}). */
|
|
478
489
|
readonly retryStaleReads: boolean;
|
|
490
|
+
/**
|
|
491
|
+
* True when the caller marked this pool read-only (`readonly: true`). Read by
|
|
492
|
+
* {@link PowqlInterface}'s exec seam to fail writes fast with E018 before the
|
|
493
|
+
* wire; the engine's own read-only-role refusal (mapped by
|
|
494
|
+
* {@link wrapPowdbError}) is the backstop for raw / injected paths.
|
|
495
|
+
*/
|
|
496
|
+
readonly readonly: boolean;
|
|
479
497
|
constructor(pool: PowdbClientPool, toParam?: (v: unknown, i: number) => PowdbParam, options?: PowdbPoolOptions);
|
|
480
498
|
/**
|
|
481
499
|
* Run one statement on `c`, choosing the lossless native typed wire when the
|
|
@@ -503,6 +521,14 @@ interface EmbeddedQueryResult {
|
|
|
503
521
|
affected?: bigint;
|
|
504
522
|
message?: string;
|
|
505
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* The embedded addon's native typed result (`@zvndev/powdb-embedded` ≥ 0.14).
|
|
526
|
+
* Mirrors {@link PowdbRawNativeResult}, the same tagged {@link PowdbWireValue}
|
|
527
|
+
* cells (embedded `bytes` arrive as a `Buffer`, which IS a `Uint8Array`, so the
|
|
528
|
+
* decode path is unchanged), including the `message` kind for DDL / status
|
|
529
|
+
* replies, which {@link adaptNativeResult}'s default branch handles at runtime.
|
|
530
|
+
*/
|
|
531
|
+
type EmbeddedNativeResult = PowdbRawNativeResult;
|
|
506
532
|
/** A single in-process embedded database handle (`@zvndev/powdb-embedded`). */
|
|
507
533
|
interface EmbeddedDatabase {
|
|
508
534
|
query(powql: string): EmbeddedQueryResult;
|
|
@@ -511,6 +537,32 @@ interface EmbeddedDatabase {
|
|
|
511
537
|
isPoisoned(): boolean;
|
|
512
538
|
/** WAL durability selector — `@zvndev/powdb-embedded` ≥ 0.7.1. */
|
|
513
539
|
setSyncMode?(mode: string): void;
|
|
540
|
+
/**
|
|
541
|
+
* Lossless typed native wire (`@zvndev/powdb-embedded` ≥ 0.14). All optional
|
|
542
|
+
* and feature-detected: an older addon omits them, so {@link PowdbEmbeddedPool}
|
|
543
|
+
* falls back to {@link materializePowql} + {@link query}. `queryWithParams`
|
|
544
|
+
* binds positional `$N` params as {@link PowdbParam} values (the NativeParam
|
|
545
|
+
* union) instead of materializing literals.
|
|
546
|
+
*/
|
|
547
|
+
queryNative?(powql: string): EmbeddedNativeResult;
|
|
548
|
+
queryReadonlyNative?(powql: string): EmbeddedNativeResult;
|
|
549
|
+
queryWithParams?(powql: string, params: PowdbParam[]): EmbeddedNativeResult;
|
|
550
|
+
/** Checkpoint-flushing close (`@zvndev/powdb-embedded` ≥ 0.14). Optional (feature-detected). */
|
|
551
|
+
close?(): void;
|
|
552
|
+
}
|
|
553
|
+
interface EmbeddedModule {
|
|
554
|
+
Database: {
|
|
555
|
+
open(dir: string): EmbeddedDatabase;
|
|
556
|
+
/** Open with a per-query memory budget — `@zvndev/powdb-embedded` ≥ 0.7.1. */
|
|
557
|
+
openWithMemoryLimit?(dir: string, limitBytes: number): EmbeddedDatabase;
|
|
558
|
+
/**
|
|
559
|
+
* Open a read-only handle for snapshot serving (`@zvndev/powdb-embedded` ≥
|
|
560
|
+
* 0.14). Optional (feature-detected); a write through such a handle is
|
|
561
|
+
* refused with `readonly mode: statement requires a writer …` (→ E018).
|
|
562
|
+
*/
|
|
563
|
+
openReadOnly?(dir: string): EmbeddedDatabase;
|
|
564
|
+
openReadOnlyWithMemoryLimit?(dir: string, limitBytes: number): EmbeddedDatabase;
|
|
565
|
+
};
|
|
514
566
|
}
|
|
515
567
|
/**
|
|
516
568
|
* Encode a JS value as a **PowQL literal** for the embedded driver, which takes
|
|
@@ -528,6 +580,26 @@ interface EmbeddedDatabase {
|
|
|
528
580
|
* as data and cannot break out of the literal or inject a second statement.
|
|
529
581
|
*/
|
|
530
582
|
export declare function encodePowqlLiteral(value: unknown): string;
|
|
583
|
+
/**
|
|
584
|
+
* The newest PowDB engine LINE (major.minor) whose lexer escape handling
|
|
585
|
+
* {@link encodePowqlString} is VERIFIED against by reading
|
|
586
|
+
* `crates/query/src/lexer.rs`. The legacy materialize path
|
|
587
|
+
* ({@link materializePowql}) inlines encoded string literals directly into query
|
|
588
|
+
* text, so it is only injection-safe while the lexer recognizes exactly the
|
|
589
|
+
* escape set the escaper emits (`\"`, `\\`, `\n`, `\t`, everything else raw). If
|
|
590
|
+
* a future engine line teaches the lexer new escapes (e.g. `\u`, `\x`), a string
|
|
591
|
+
* that the escaper leaves raw could be re-interpreted by the lexer and break out
|
|
592
|
+
* of the literal, turning the fallback into an injection primitive.
|
|
593
|
+
*
|
|
594
|
+
* CONTRACT: bump this ceiling ONLY after re-verifying the escape handling in
|
|
595
|
+
* `crates/query/src/lexer.rs` for the newer line AND confirming
|
|
596
|
+
* {@link encodePowqlString} still escapes every breakout vector the lexer
|
|
597
|
+
* recognizes. The legacy path guards on this value (see
|
|
598
|
+
* {@link PowdbEmbeddedPool.exec}): an embedded addon whose engine line exceeds
|
|
599
|
+
* the ceiling yet still routes through the string wire is refused rather than
|
|
600
|
+
* materialized.
|
|
601
|
+
*/
|
|
602
|
+
export declare const POWQL_LEXER_TESTED_CEILING = "0.15";
|
|
531
603
|
/**
|
|
532
604
|
* Substitute every `$N` placeholder in a generator-produced PowQL template with
|
|
533
605
|
* the encoded literal of `params[N-1]`. Safe because the template is produced by
|
|
@@ -539,10 +611,14 @@ export declare function encodePowqlLiteral(value: unknown): string;
|
|
|
539
611
|
export declare function materializePowql(powql: string, params: unknown[]): string;
|
|
540
612
|
/**
|
|
541
613
|
* A {@link PgCompatPool} backed by an in-process `@zvndev/powdb-embedded`
|
|
542
|
-
* `Database`.
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
614
|
+
* `Database`. On the addon's typed native wire (≥ 0.14, when
|
|
615
|
+
* `capabilities.nativeRaw` is set) this pool binds positional `$N` params via
|
|
616
|
+
* `queryWithParams` and decodes the typed cells, exactly like the networked
|
|
617
|
+
* transport. On an older addon (no `queryWithParams`) it falls back to the
|
|
618
|
+
* legacy string wire, which takes **no params array** (its `query(powql)`
|
|
619
|
+
* accepts only a string), so each positional `$N` is materialized into a PowQL
|
|
620
|
+
* literal via {@link materializePowql} before the text is handed to the engine.
|
|
621
|
+
* One handle, single connection: transaction keywords (`begin`/`commit`/
|
|
546
622
|
* `rollback`) are issued serially as ordinary queries.
|
|
547
623
|
*/
|
|
548
624
|
export declare class PowdbEmbeddedPool implements PgCompatPool {
|
|
@@ -563,14 +639,24 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
|
|
|
563
639
|
private readonly poolHoldRef;
|
|
564
640
|
/**
|
|
565
641
|
* Feature capabilities of the embedded engine (resolved from the addon
|
|
566
|
-
* package version). `nativeRaw` is
|
|
567
|
-
*
|
|
642
|
+
* package version). `nativeRaw` is true when the addon is ≥ 0.14 and the
|
|
643
|
+
* opened handle exposes `queryWithParams` (the typed native wire); an older
|
|
644
|
+
* addon has no such method, so it stays false and the legacy string wire is
|
|
645
|
+
* used.
|
|
568
646
|
*/
|
|
569
647
|
readonly capabilities: PowdbCapabilities;
|
|
570
648
|
/** Carried for surface uniformity with {@link PowdbPool}; inert on embedded (no protocol_error frames). */
|
|
571
649
|
readonly retryStaleReads: boolean;
|
|
650
|
+
/**
|
|
651
|
+
* True when this pool was opened read-only (an `{ embedded, readonly: true }`
|
|
652
|
+
* target, or a directly-constructed pool passed `readonly: true`). Read by
|
|
653
|
+
* {@link PowqlInterface}'s exec seam to fail writes fast with E018 before the
|
|
654
|
+
* wire; the engine's own refusal (mapped by {@link wrapPowdbError}) is the
|
|
655
|
+
* backstop for raw / injected paths.
|
|
656
|
+
*/
|
|
657
|
+
readonly readonly: boolean;
|
|
572
658
|
constructor(db: EmbeddedDatabase, options?: PowdbPoolOptions);
|
|
573
|
-
/**
|
|
659
|
+
/** Run the PowQL on the in-process engine, choosing the native or legacy wire. */
|
|
574
660
|
private exec;
|
|
575
661
|
/**
|
|
576
662
|
* Run one statement, gating transaction control. `holdRef` scopes the gate
|
|
@@ -586,7 +672,17 @@ export declare class PowdbEmbeddedPool implements PgCompatPool {
|
|
|
586
672
|
export { introspectPowdbDatabase, type PowdbExec, type PowdbIntrospectOptions, } from './powdb-introspect.js';
|
|
587
673
|
export { PowqlInterface } from './powql.js';
|
|
588
674
|
/** Options for {@link turbinePowDB}. */
|
|
589
|
-
export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
|
|
675
|
+
export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited' | 'relationLoadStrategy'> {
|
|
676
|
+
/**
|
|
677
|
+
* Client-level default `with`-relation load strategy. On PowDB the default is
|
|
678
|
+
* the batched N+1 loaders; setting `'join'` opts INTO native PowQL server-side
|
|
679
|
+
* joins for eligible top-level relations (ineligible ones, e.g. a paged parent
|
|
680
|
+
* or a nested `with`, fall back to the loaders per-relation and silently). A
|
|
681
|
+
* per-query `relationLoadStrategy` arg still overrides this. Requires an engine
|
|
682
|
+
* that advertises `serverJoins` (PowDB ≥ 0.13); a per-query `'join'` on an
|
|
683
|
+
* older engine throws E017, a client-level default silently falls back.
|
|
684
|
+
*/
|
|
685
|
+
relationLoadStrategy?: TurbineConfig['relationLoadStrategy'];
|
|
590
686
|
/** Max pooled connections (default 10). Networked transport only. */
|
|
591
687
|
connectionLimit?: number;
|
|
592
688
|
/**
|
|
@@ -623,6 +719,18 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
|
|
|
623
719
|
* E017).
|
|
624
720
|
*/
|
|
625
721
|
assumeEngineVersion?: string;
|
|
722
|
+
/**
|
|
723
|
+
* Mark the client read-only: a write (or a transaction `begin`) fails fast
|
|
724
|
+
* locally with a {@link ReadOnlyError} (E018) before it reaches the wire,
|
|
725
|
+
* rather than round-tripping to the engine's refusal. Works on both
|
|
726
|
+
* transports (a networked pool bound to a read-only role, or an embedded
|
|
727
|
+
* handle). An `{ embedded, readonly: true }` target implies this. Default
|
|
728
|
+
* `false`.
|
|
729
|
+
*
|
|
730
|
+
* Ignored when you inject an already-constructed {@link PowdbPool} (it carries
|
|
731
|
+
* its own {@link PowdbPoolOptions}); set it on that pool's constructor instead.
|
|
732
|
+
*/
|
|
733
|
+
readonly?: boolean;
|
|
626
734
|
/**
|
|
627
735
|
* Driver-module injection for the networked target forms (URL / host+port):
|
|
628
736
|
* bypasses the dynamic `import('@zvndev/powdb-client')` and uses this object
|
|
@@ -630,6 +738,14 @@ export interface TurbinePowdbOptions extends Pick<TurbineConfig, 'logging' | 'de
|
|
|
630
738
|
* connections) and advanced embedding; everyday callers never set it.
|
|
631
739
|
*/
|
|
632
740
|
powdbClientModule?: PowdbModule;
|
|
741
|
+
/**
|
|
742
|
+
* Driver-module injection for the **embedded** target form (`{ embedded }`):
|
|
743
|
+
* bypasses the dynamic `import('@zvndev/powdb-embedded')` and uses this object
|
|
744
|
+
* as the addon module instead. Intended for tests (a fake `Database` factory
|
|
745
|
+
* that records how the handle was opened) and advanced embedding; everyday
|
|
746
|
+
* callers never set it. The symmetric counterpart to {@link powdbClientModule}.
|
|
747
|
+
*/
|
|
748
|
+
powdbEmbeddedModule?: EmbeddedModule;
|
|
633
749
|
}
|
|
634
750
|
/**
|
|
635
751
|
* Selects the **embedded** transport — an in-process `@zvndev/powdb-embedded`
|
|
@@ -655,6 +771,16 @@ export interface TurbinePowdbEmbeddedTarget {
|
|
|
655
771
|
syncMode?: 'full' | 'normal' | 'off';
|
|
656
772
|
/** Per-query memory budget in bytes (requires `@zvndev/powdb-embedded` ≥ 0.7.1). */
|
|
657
773
|
memoryLimit?: number;
|
|
774
|
+
/**
|
|
775
|
+
* Open the data directory read-only for snapshot serving (requires
|
|
776
|
+
* `@zvndev/powdb-embedded` ≥ 0.14: `openReadOnly` / `openReadOnlyWithMemoryLimit`).
|
|
777
|
+
* A write through a read-only handle is refused by the engine with
|
|
778
|
+
* `readonly mode: statement requires a writer …` (→ {@link ReadOnlyError}, E018),
|
|
779
|
+
* and Turbine additionally fails writes fast locally (this implies the pool's
|
|
780
|
+
* `readonly` flag). Meaningless together with `syncMode` (a read-only engine
|
|
781
|
+
* never writes), setting both throws a {@link ValidationError}.
|
|
782
|
+
*/
|
|
783
|
+
readonly?: boolean;
|
|
658
784
|
}
|
|
659
785
|
/**
|
|
660
786
|
* Bind Turbine to PowDB. `target` is one of:
|
package/dist/powdb.js
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
56
56
|
import { TurbineClient, } from './client.js';
|
|
57
57
|
import { postgresDialect } from './dialect.js';
|
|
58
|
-
import { ConnectionError, NotNullViolationError, TimeoutError, UniqueConstraintError, UnsupportedFeatureError, ValidationError, } from './errors.js';
|
|
58
|
+
import { ConnectionError, NotNullViolationError, ReadOnlyError, TimeoutError, UniqueConstraintError, UnsupportedFeatureError, ValidationError, } from './errors.js';
|
|
59
59
|
import importOptionalPeer from './optional-peer-import.cjs';
|
|
60
60
|
/**
|
|
61
61
|
* Capability descriptor for PowDB. PowQL generation is owned by
|
|
@@ -195,6 +195,7 @@ const POWDB_FEATURE_MIN_VERSION = {
|
|
|
195
195
|
introspection: '0.10',
|
|
196
196
|
jsonDocs: '0.12',
|
|
197
197
|
docFieldIndexes: '0.13',
|
|
198
|
+
serverJoins: '0.13',
|
|
198
199
|
};
|
|
199
200
|
/**
|
|
200
201
|
* Trusted-caller default: every FEATURE gate on, engine version unknown. Used
|
|
@@ -209,6 +210,7 @@ export const ALL_POWDB_CAPABILITIES = {
|
|
|
209
210
|
jsonDocs: true,
|
|
210
211
|
docFieldIndexes: true,
|
|
211
212
|
introspection: true,
|
|
213
|
+
serverJoins: true,
|
|
212
214
|
nativeRaw: false,
|
|
213
215
|
};
|
|
214
216
|
/** Parse a PowDB semver prefix (`0.13.0`, `0.13`, `1.2.3-rc`) into components, or `null`. */
|
|
@@ -236,6 +238,7 @@ export function capabilitiesFromVersion(version, opts = {}) {
|
|
|
236
238
|
jsonDocs: false,
|
|
237
239
|
docFieldIndexes: false,
|
|
238
240
|
introspection: false,
|
|
241
|
+
serverJoins: false,
|
|
239
242
|
nativeRaw: false,
|
|
240
243
|
};
|
|
241
244
|
}
|
|
@@ -244,6 +247,7 @@ export function capabilitiesFromVersion(version, opts = {}) {
|
|
|
244
247
|
introspection: atLeastVersion(sem, 0, 10),
|
|
245
248
|
jsonDocs: atLeastVersion(sem, 0, 12),
|
|
246
249
|
docFieldIndexes: atLeastVersion(sem, 0, 13),
|
|
250
|
+
serverJoins: atLeastVersion(sem, 0, 13),
|
|
247
251
|
nativeRaw: Boolean(opts.hasNativeRaw) && atLeastVersion(sem, 0, 13),
|
|
248
252
|
};
|
|
249
253
|
}
|
|
@@ -688,9 +692,10 @@ export function wrapPowdbError(err) {
|
|
|
688
692
|
const m = /column ['"]?(\w+)['"]?/i.exec(msg);
|
|
689
693
|
return new NotNullViolationError({ column: m?.[1], cause: err });
|
|
690
694
|
}
|
|
691
|
-
// Driver pool lifecycle errors (acquire after close, acquire timeout
|
|
692
|
-
//
|
|
693
|
-
|
|
695
|
+
// Driver pool lifecycle errors (acquire after close, acquire timeout, or a
|
|
696
|
+
// statement reaching an already-closed embedded handle) carry no .code:
|
|
697
|
+
// classify by message so both transports surface E004.
|
|
698
|
+
if (/pool closed|pool acquire timeout|database is closed/i.test(msg)) {
|
|
694
699
|
return new ConnectionError(`[turbine] PowDB connection unavailable: ${msg}`, { cause: err });
|
|
695
700
|
}
|
|
696
701
|
// Server-side transaction-gate wait bound (PowDB ≥ 0.10, default 5s): another
|
|
@@ -712,6 +717,54 @@ export function wrapPowdbError(err) {
|
|
|
712
717
|
/received unexpected frame|unknown message type|truncated payload|bad framing/i.test(msg)) {
|
|
713
718
|
return new ConnectionError(`[turbine] PowDB connection is in an invalid state: ${msg}`, { cause: err });
|
|
714
719
|
}
|
|
720
|
+
// Read-only refusal → ReadOnlyError (E018). Two engine shapes, both mapped by
|
|
721
|
+
// substring (the networked transport prefixes the message with `query failed:
|
|
722
|
+
// `, so never anchor on the start): an embedded database opened read-only for
|
|
723
|
+
// snapshot serving (`readonly mode: statement requires a writer …`), and a
|
|
724
|
+
// networked read-only role (`permission denied: role '<role>' cannot execute
|
|
725
|
+
// write statements`). These run BEFORE the generic validation regex below so a
|
|
726
|
+
// read-only write is surfaced as the routing signal E018, not a query defect.
|
|
727
|
+
// The driver spec (0.15) distinguishes them via `reason`: snapshot mode
|
|
728
|
+
// means "nothing can write here; route writes to the primary", RBAC means
|
|
729
|
+
// "this connection's role may not write here".
|
|
730
|
+
if (/readonly mode: statement requires a writer/i.test(msg)) {
|
|
731
|
+
return new ReadOnlyError(`PowDB refused a write on a read-only database: ${msg}.`, {
|
|
732
|
+
cause: err,
|
|
733
|
+
reason: 'snapshot',
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
if (/permission denied: role/i.test(msg)) {
|
|
737
|
+
return new ReadOnlyError(`PowDB refused a write for a read-only role: ${msg}.`, { cause: err, reason: 'rbac' });
|
|
738
|
+
}
|
|
739
|
+
// Open-time read-only failure: a read-only handle over a directory whose WAL
|
|
740
|
+
// still has uncommitted frames is refused (`cannot open read-only: the WAL is
|
|
741
|
+
// not empty …`). It is a connection failure (E004), not a query defect, the
|
|
742
|
+
// fix is to recover the directory with a writable open first.
|
|
743
|
+
if (/cannot open read-only: the WAL is not empty/i.test(msg)) {
|
|
744
|
+
return new ConnectionError(`[turbine] PowDB could not open the directory read-only: ${msg}. Open it once with a writable handle to ` +
|
|
745
|
+
'flush the WAL (recover the directory), then reopen it read-only for snapshot serving.', { cause: err });
|
|
746
|
+
}
|
|
747
|
+
// Per-query deadline → TimeoutError (E002). Message-path so it fires on the
|
|
748
|
+
// embedded transport too (code is always 'GenericFailure' there); retryable.
|
|
749
|
+
// Pass the engine prose through the message override (same pattern as the
|
|
750
|
+
// transaction-gate timeout below) so the real "query timeout after <n>ms"
|
|
751
|
+
// survives instead of rendering the placeholder "timed out after 0ms".
|
|
752
|
+
if (/query timeout after/i.test(msg)) {
|
|
753
|
+
return new TimeoutError(0, 'PowDB query', { message: `[turbine] PowDB ${msg}`, cause: err });
|
|
754
|
+
}
|
|
755
|
+
// Client-initiated cancellation → ConnectionError (E004). This is FINAL: the
|
|
756
|
+
// issuing client disconnected, so the query was a clean early return, never
|
|
757
|
+
// auto-retry it (the opt-in stale-read retry only replays stale-FRAME reads).
|
|
758
|
+
if (/query cancelled by client disconnect/i.test(msg)) {
|
|
759
|
+
return new ConnectionError(`[turbine] PowDB query cancelled by client disconnect: ${msg}`, { cause: err });
|
|
760
|
+
}
|
|
761
|
+
// Bounded join rejection → ValidationError (E003). The engine rejects a pure
|
|
762
|
+
// nested-loop join whose candidate-pair count (or result row count) exceeds
|
|
763
|
+
// the safety bound BEFORE executing, and names the fix in the message, keep
|
|
764
|
+
// that fix-hint intact so the caller knows how to make the join eligible.
|
|
765
|
+
if (/nested-loop join would evaluate|join result exceeds row limit/i.test(msg)) {
|
|
766
|
+
return new ValidationError(`[turbine] PowDB join rejected: ${msg}`);
|
|
767
|
+
}
|
|
715
768
|
// Type mismatch / parse / execution / storage / unexpected(token) / row too
|
|
716
769
|
// large → validation (E003). On the embedded transport these are the only
|
|
717
770
|
// signal we get (code is always 'GenericFailure'); on the networked path they
|
|
@@ -1052,12 +1105,20 @@ export class PowdbPool {
|
|
|
1052
1105
|
capabilities;
|
|
1053
1106
|
/** Opt-in first-statement-read replay on a stale wire frame (read by {@link PowqlInterface}). */
|
|
1054
1107
|
retryStaleReads;
|
|
1108
|
+
/**
|
|
1109
|
+
* True when the caller marked this pool read-only (`readonly: true`). Read by
|
|
1110
|
+
* {@link PowqlInterface}'s exec seam to fail writes fast with E018 before the
|
|
1111
|
+
* wire; the engine's own read-only-role refusal (mapped by
|
|
1112
|
+
* {@link wrapPowdbError}) is the backstop for raw / injected paths.
|
|
1113
|
+
*/
|
|
1114
|
+
readonly;
|
|
1055
1115
|
constructor(pool, toParam = (v) => toPowdbParam(v), options = {}) {
|
|
1056
1116
|
this.pool = pool;
|
|
1057
1117
|
this.toParam = toParam;
|
|
1058
1118
|
this.txGate = new PowdbTxGate(options.transactionQueueTimeoutMs ?? DEFAULT_TX_QUEUE_TIMEOUT_MS);
|
|
1059
1119
|
this.capabilities = options.capabilities ?? ALL_POWDB_CAPABILITIES;
|
|
1060
1120
|
this.retryStaleReads = options.retryStaleReads ?? false;
|
|
1121
|
+
this.readonly = options.readonly ?? false;
|
|
1061
1122
|
}
|
|
1062
1123
|
/**
|
|
1063
1124
|
* Run one statement on `c`, choosing the lossless native typed wire when the
|
|
@@ -1311,6 +1372,26 @@ export function encodePowqlLiteral(value) {
|
|
|
1311
1372
|
return encodePowqlString(value);
|
|
1312
1373
|
throw new ValidationError(`[turbine] Value of type ${typeof value} cannot be encoded as a PowDB literal.`);
|
|
1313
1374
|
}
|
|
1375
|
+
/**
|
|
1376
|
+
* The newest PowDB engine LINE (major.minor) whose lexer escape handling
|
|
1377
|
+
* {@link encodePowqlString} is VERIFIED against by reading
|
|
1378
|
+
* `crates/query/src/lexer.rs`. The legacy materialize path
|
|
1379
|
+
* ({@link materializePowql}) inlines encoded string literals directly into query
|
|
1380
|
+
* text, so it is only injection-safe while the lexer recognizes exactly the
|
|
1381
|
+
* escape set the escaper emits (`\"`, `\\`, `\n`, `\t`, everything else raw). If
|
|
1382
|
+
* a future engine line teaches the lexer new escapes (e.g. `\u`, `\x`), a string
|
|
1383
|
+
* that the escaper leaves raw could be re-interpreted by the lexer and break out
|
|
1384
|
+
* of the literal, turning the fallback into an injection primitive.
|
|
1385
|
+
*
|
|
1386
|
+
* CONTRACT: bump this ceiling ONLY after re-verifying the escape handling in
|
|
1387
|
+
* `crates/query/src/lexer.rs` for the newer line AND confirming
|
|
1388
|
+
* {@link encodePowqlString} still escapes every breakout vector the lexer
|
|
1389
|
+
* recognizes. The legacy path guards on this value (see
|
|
1390
|
+
* {@link PowdbEmbeddedPool.exec}): an embedded addon whose engine line exceeds
|
|
1391
|
+
* the ceiling yet still routes through the string wire is refused rather than
|
|
1392
|
+
* materialized.
|
|
1393
|
+
*/
|
|
1394
|
+
export const POWQL_LEXER_TESTED_CEILING = '0.15';
|
|
1314
1395
|
/** Escape a string into a PowQL `"…"` literal, matching the engine lexer's escape rules. */
|
|
1315
1396
|
function encodePowqlString(s) {
|
|
1316
1397
|
let out = '"';
|
|
@@ -1347,10 +1428,14 @@ export function materializePowql(powql, params) {
|
|
|
1347
1428
|
}
|
|
1348
1429
|
/**
|
|
1349
1430
|
* A {@link PgCompatPool} backed by an in-process `@zvndev/powdb-embedded`
|
|
1350
|
-
* `Database`.
|
|
1351
|
-
*
|
|
1352
|
-
*
|
|
1353
|
-
*
|
|
1431
|
+
* `Database`. On the addon's typed native wire (≥ 0.14, when
|
|
1432
|
+
* `capabilities.nativeRaw` is set) this pool binds positional `$N` params via
|
|
1433
|
+
* `queryWithParams` and decodes the typed cells, exactly like the networked
|
|
1434
|
+
* transport. On an older addon (no `queryWithParams`) it falls back to the
|
|
1435
|
+
* legacy string wire, which takes **no params array** (its `query(powql)`
|
|
1436
|
+
* accepts only a string), so each positional `$N` is materialized into a PowQL
|
|
1437
|
+
* literal via {@link materializePowql} before the text is handed to the engine.
|
|
1438
|
+
* One handle, single connection: transaction keywords (`begin`/`commit`/
|
|
1354
1439
|
* `rollback`) are issued serially as ordinary queries.
|
|
1355
1440
|
*/
|
|
1356
1441
|
export class PowdbEmbeddedPool {
|
|
@@ -1371,20 +1456,64 @@ export class PowdbEmbeddedPool {
|
|
|
1371
1456
|
poolHoldRef = { hold: null };
|
|
1372
1457
|
/**
|
|
1373
1458
|
* Feature capabilities of the embedded engine (resolved from the addon
|
|
1374
|
-
* package version). `nativeRaw` is
|
|
1375
|
-
*
|
|
1459
|
+
* package version). `nativeRaw` is true when the addon is ≥ 0.14 and the
|
|
1460
|
+
* opened handle exposes `queryWithParams` (the typed native wire); an older
|
|
1461
|
+
* addon has no such method, so it stays false and the legacy string wire is
|
|
1462
|
+
* used.
|
|
1376
1463
|
*/
|
|
1377
1464
|
capabilities;
|
|
1378
1465
|
/** Carried for surface uniformity with {@link PowdbPool}; inert on embedded (no protocol_error frames). */
|
|
1379
1466
|
retryStaleReads;
|
|
1467
|
+
/**
|
|
1468
|
+
* True when this pool was opened read-only (an `{ embedded, readonly: true }`
|
|
1469
|
+
* target, or a directly-constructed pool passed `readonly: true`). Read by
|
|
1470
|
+
* {@link PowqlInterface}'s exec seam to fail writes fast with E018 before the
|
|
1471
|
+
* wire; the engine's own refusal (mapped by {@link wrapPowdbError}) is the
|
|
1472
|
+
* backstop for raw / injected paths.
|
|
1473
|
+
*/
|
|
1474
|
+
readonly;
|
|
1380
1475
|
constructor(db, options = {}) {
|
|
1381
1476
|
this.db = db;
|
|
1382
1477
|
this.txGate = new PowdbTxGate(options.transactionQueueTimeoutMs ?? DEFAULT_TX_QUEUE_TIMEOUT_MS);
|
|
1383
1478
|
this.capabilities = options.capabilities ?? ALL_POWDB_CAPABILITIES;
|
|
1384
1479
|
this.retryStaleReads = options.retryStaleReads ?? false;
|
|
1480
|
+
this.readonly = options.readonly ?? false;
|
|
1385
1481
|
}
|
|
1386
|
-
/**
|
|
1482
|
+
/** Run the PowQL on the in-process engine, choosing the native or legacy wire. */
|
|
1387
1483
|
exec(powql, params) {
|
|
1484
|
+
// Native typed wire (addon ≥ 0.14): bind positional params with the SAME
|
|
1485
|
+
// binder the networked transport uses ({@link toPowdbParam} yields exactly
|
|
1486
|
+
// the NativeParam union null|bigint|number|boolean|string) and decode the
|
|
1487
|
+
// typed cells: a genuine str "null" survives, a json-null document stays
|
|
1488
|
+
// distinct from an absent value. Gated on the resolved capability AND a
|
|
1489
|
+
// per-call feature-detect so a heterogeneous injected handle cannot crash.
|
|
1490
|
+
if (this.capabilities.nativeRaw && typeof this.db.queryWithParams === 'function') {
|
|
1491
|
+
const bound = params.map((v) => toPowdbParam(v));
|
|
1492
|
+
return adaptNativeResult(this.db.queryWithParams(powql, bound));
|
|
1493
|
+
}
|
|
1494
|
+
// Legacy string wire (addon < 0.14): the engine takes no params array, so
|
|
1495
|
+
// materialize each `$N` into a PowQL literal. Byte-for-byte unchanged, kept
|
|
1496
|
+
// live and tested as the pre-0.14 fallback.
|
|
1497
|
+
//
|
|
1498
|
+
// Safety assertion (see {@link POWQL_LEXER_TESTED_CEILING}): an addon whose
|
|
1499
|
+
// engine line EXCEEDS the escaper's verified lexer ceiling must never be
|
|
1500
|
+
// materialized: a newer lexer may recognize escapes the escaper leaves raw,
|
|
1501
|
+
// making the inline path an injection primitive. Reachability: addons >= 0.14
|
|
1502
|
+
// expose `queryWithParams` and take the native path above, so the ONLY way to
|
|
1503
|
+
// reach this branch with a newer-than-ceiling engine is the anomalous
|
|
1504
|
+
// newer-addon-without-native case (feature-detect failed). Refuse it rather
|
|
1505
|
+
// than inline-encode against an unverified lexer.
|
|
1506
|
+
const engineSem = parsePowdbSemver(this.capabilities.engineVersion);
|
|
1507
|
+
const ceiling = parsePowdbSemver(POWQL_LEXER_TESTED_CEILING);
|
|
1508
|
+
if (engineSem &&
|
|
1509
|
+
ceiling &&
|
|
1510
|
+
(engineSem.major > ceiling.major || (engineSem.major === ceiling.major && engineSem.minor > ceiling.minor))) {
|
|
1511
|
+
throw new ValidationError(`[turbine] Refusing the PowDB legacy string wire: this embedded addon reports engine ` +
|
|
1512
|
+
`${this.capabilities.engineVersion}, which is newer than the escaper's verified lexer range ` +
|
|
1513
|
+
`(<= ${POWQL_LEXER_TESTED_CEILING}) AND such an addon exposes the parameterized native API, so ` +
|
|
1514
|
+
`reaching the legacy materialize path means the queryWithParams feature-detect failed. Upgrade ` +
|
|
1515
|
+
`turbine-orm so its PowQL escaper is verified against this engine line before running on it.`);
|
|
1516
|
+
}
|
|
1388
1517
|
const materialized = materializePowql(powql, params);
|
|
1389
1518
|
return adaptResult(normalizeEmbeddedResult(this.db.query(materialized)));
|
|
1390
1519
|
}
|
|
@@ -1404,6 +1533,15 @@ export class PowdbEmbeddedPool {
|
|
|
1404
1533
|
// transaction callback throws re-entrant E017 fast; independent
|
|
1405
1534
|
// concurrent ones wait their FIFO turn.
|
|
1406
1535
|
holdRef.hold = await this.txGate.acquire();
|
|
1536
|
+
// The gate may have handed us the slot AFTER disconnect() closed the
|
|
1537
|
+
// handle (a transaction queued behind an in-flight one, released as the
|
|
1538
|
+
// pool shut down). Re-check before touching the now-closed engine, and
|
|
1539
|
+
// release the slot we just took so the queue keeps draining.
|
|
1540
|
+
if (this.closed) {
|
|
1541
|
+
holdRef.hold.finish();
|
|
1542
|
+
holdRef.hold = null;
|
|
1543
|
+
throw new ConnectionError('[turbine] The PowDB embedded pool is closed: disconnect() was already called on this client.');
|
|
1544
|
+
}
|
|
1407
1545
|
}
|
|
1408
1546
|
if ((ctl === 'commit' || ctl === 'rollback') && holdRef.hold === null) {
|
|
1409
1547
|
// This context never acquired the gate — its `begin` never ran (the
|
|
@@ -1479,13 +1617,15 @@ export class PowdbEmbeddedPool {
|
|
|
1479
1617
|
async end() {
|
|
1480
1618
|
if (this.closed)
|
|
1481
1619
|
return;
|
|
1482
|
-
// The addon exposes no explicit close — drop the reference and let GC /
|
|
1483
|
-
// the engine's checkpoint flush. Marking the pool closed makes later
|
|
1484
|
-
// queries fail with a typed ConnectionError instead of silently running
|
|
1485
|
-
// against a handle the caller believes is gone. Caveat: durability is
|
|
1486
|
-
// checkpoint-bound, so hold the process open long enough for the final
|
|
1487
|
-
// WAL flush in short scripts.
|
|
1488
1620
|
this.closed = true;
|
|
1621
|
+
// Addon ≥ 0.14 exposes an explicit checkpoint-flushing close(): call it so
|
|
1622
|
+
// the final WAL flush completes deterministically before the handle is
|
|
1623
|
+
// dropped. An older addon has no close, dropping the reference and letting
|
|
1624
|
+
// GC / the engine's checkpoint flush is the fallback (durability is then
|
|
1625
|
+
// checkpoint-bound, so a short script must hold the process open long enough
|
|
1626
|
+
// for the final flush). Marking the pool closed makes later queries fail
|
|
1627
|
+
// with a typed ConnectionError instead of running against a gone handle.
|
|
1628
|
+
this.db.close?.();
|
|
1489
1629
|
}
|
|
1490
1630
|
}
|
|
1491
1631
|
// ---------------------------------------------------------------------------
|
|
@@ -1552,12 +1692,34 @@ function resolveEmbeddedVersion() {
|
|
|
1552
1692
|
return importOptionalPeer.peerPackageVersion('@zvndev/powdb-embedded');
|
|
1553
1693
|
}
|
|
1554
1694
|
/** Open an embedded database handle, wrapping engine open failures (corrupt dir, etc.). */
|
|
1555
|
-
async function openEmbeddedPool(target, poolOptions = {}, assumeEngineVersion) {
|
|
1556
|
-
const mod = await loadPowdbEmbedded();
|
|
1557
|
-
const { embedded: dir, syncMode, memoryLimit } = target;
|
|
1695
|
+
async function openEmbeddedPool(target, poolOptions = {}, assumeEngineVersion, injectedModule) {
|
|
1696
|
+
const mod = injectedModule ?? (await loadPowdbEmbedded());
|
|
1697
|
+
const { embedded: dir, syncMode, memoryLimit, readonly } = target;
|
|
1698
|
+
// A read-only engine never writes, so a durability selector is meaningless
|
|
1699
|
+
// there, reject the combination loudly rather than silently ignoring one.
|
|
1700
|
+
if (readonly && syncMode !== undefined) {
|
|
1701
|
+
throw new ValidationError('[turbine] embedded `syncMode` is meaningless with `readonly: true` (a read-only database never writes). Remove one.');
|
|
1702
|
+
}
|
|
1558
1703
|
let db;
|
|
1559
1704
|
try {
|
|
1560
|
-
if (
|
|
1705
|
+
if (readonly) {
|
|
1706
|
+
// Read-only snapshot serving (addon ≥ 0.14): route to the openReadOnly*
|
|
1707
|
+
// constructors; feature-detect and fail with a clear version hint if the
|
|
1708
|
+
// installed addon predates them.
|
|
1709
|
+
if (memoryLimit !== undefined) {
|
|
1710
|
+
if (typeof mod.Database.openReadOnlyWithMemoryLimit !== 'function') {
|
|
1711
|
+
throw new ConnectionError('[turbine] embedded `readonly` + `memoryLimit` requires @zvndev/powdb-embedded >= 0.14 (openReadOnlyWithMemoryLimit).');
|
|
1712
|
+
}
|
|
1713
|
+
db = mod.Database.openReadOnlyWithMemoryLimit(dir, memoryLimit);
|
|
1714
|
+
}
|
|
1715
|
+
else {
|
|
1716
|
+
if (typeof mod.Database.openReadOnly !== 'function') {
|
|
1717
|
+
throw new ConnectionError('[turbine] embedded `readonly: true` requires @zvndev/powdb-embedded >= 0.14 (the installed addon has no openReadOnly).');
|
|
1718
|
+
}
|
|
1719
|
+
db = mod.Database.openReadOnly(dir);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
else if (memoryLimit !== undefined) {
|
|
1561
1723
|
if (typeof mod.Database.openWithMemoryLimit !== 'function') {
|
|
1562
1724
|
throw new ConnectionError('[turbine] embedded `memoryLimit` requires @zvndev/powdb-embedded ≥ 0.7.1.');
|
|
1563
1725
|
}
|
|
@@ -1578,11 +1740,19 @@ async function openEmbeddedPool(target, poolOptions = {}, assumeEngineVersion) {
|
|
|
1578
1740
|
}
|
|
1579
1741
|
db.setSyncMode(syncMode);
|
|
1580
1742
|
}
|
|
1581
|
-
//
|
|
1743
|
+
// Native typed wire is feature-detected on the OPENED handle: an addon ≥ 0.14
|
|
1744
|
+
// exposes `queryWithParams`, so nativeRaw turns on (server-gate ≥ 0.13 still
|
|
1745
|
+
// applies via the version); an older addon has no such method → false.
|
|
1582
1746
|
const capabilities = capabilitiesFromVersion(assumeEngineVersion ?? resolveEmbeddedVersion(), {
|
|
1583
|
-
hasNativeRaw:
|
|
1747
|
+
hasNativeRaw: typeof db.queryWithParams === 'function',
|
|
1748
|
+
});
|
|
1749
|
+
// A read-only target forces the pool's readonly flag; otherwise honor whatever
|
|
1750
|
+
// `poolOptions` (threaded from `options.readonly`) carried.
|
|
1751
|
+
return new PowdbEmbeddedPool(db, {
|
|
1752
|
+
...poolOptions,
|
|
1753
|
+
capabilities,
|
|
1754
|
+
readonly: Boolean(readonly) || Boolean(poolOptions.readonly),
|
|
1584
1755
|
});
|
|
1585
|
-
return new PowdbEmbeddedPool(db, { ...poolOptions, capabilities });
|
|
1586
1756
|
}
|
|
1587
1757
|
/**
|
|
1588
1758
|
* Bind Turbine to PowDB. `target` is one of:
|
|
@@ -1608,6 +1778,7 @@ export async function turbinePowDB(target, schema, options = {}) {
|
|
|
1608
1778
|
const poolOptions = {
|
|
1609
1779
|
transactionQueueTimeoutMs: options.transactionQueueTimeoutMs,
|
|
1610
1780
|
retryStaleReads: options.retryStaleReads,
|
|
1781
|
+
readonly: options.readonly,
|
|
1611
1782
|
};
|
|
1612
1783
|
const max = options.connectionLimit ?? 10;
|
|
1613
1784
|
if (typeof target === 'string') {
|
|
@@ -1622,7 +1793,7 @@ export async function turbinePowDB(target, schema, options = {}) {
|
|
|
1622
1793
|
pool = target;
|
|
1623
1794
|
}
|
|
1624
1795
|
else if (isEmbeddedTarget(target)) {
|
|
1625
|
-
pool = await openEmbeddedPool(target, poolOptions, options.assumeEngineVersion);
|
|
1796
|
+
pool = await openEmbeddedPool(target, poolOptions, options.assumeEngineVersion, options.powdbEmbeddedModule);
|
|
1626
1797
|
owns = true;
|
|
1627
1798
|
}
|
|
1628
1799
|
else if (isPowdbClientPool(target)) {
|
|
@@ -1649,6 +1820,7 @@ export async function turbinePowDB(target, schema, options = {}) {
|
|
|
1649
1820
|
logging: options.logging,
|
|
1650
1821
|
defaultLimit: options.defaultLimit,
|
|
1651
1822
|
warnOnUnlimited: options.warnOnUnlimited,
|
|
1823
|
+
relationLoadStrategy: options.relationLoadStrategy,
|
|
1652
1824
|
queryInterfaceFactory,
|
|
1653
1825
|
}, schema);
|
|
1654
1826
|
if (owns) {
|