turbine-orm 0.51.0 → 0.52.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 +33 -5
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/client.d.ts +106 -2
- package/dist/cjs/client.js +111 -5
- package/dist/cjs/dialect.d.ts +33 -0
- package/dist/cjs/dialect.js +14 -0
- package/dist/cjs/engine-config.d.ts +49 -0
- package/dist/cjs/engine-config.js +19 -0
- package/dist/cjs/index-advisor.js +0 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/mssql.d.ts +8 -3
- package/dist/cjs/mssql.js +22 -3
- package/dist/cjs/mysql.d.ts +7 -3
- package/dist/cjs/mysql.js +20 -3
- package/dist/cjs/nested-write.d.ts +31 -0
- package/dist/cjs/nested-write.js +80 -2
- package/dist/cjs/powdb-introspect.d.ts +10 -1
- package/dist/cjs/powdb-introspect.js +10 -1
- package/dist/cjs/powdb.d.ts +116 -6
- package/dist/cjs/powdb.js +169 -10
- package/dist/cjs/powql.d.ts +161 -1
- package/dist/cjs/powql.js +299 -19
- package/dist/cjs/prisma-compat.d.ts +54 -8
- package/dist/cjs/prisma-compat.js +136 -20
- package/dist/cjs/query/batched-loader.d.ts +7 -0
- package/dist/cjs/query/batched-loader.js +97 -15
- package/dist/cjs/query/builder.d.ts +131 -5
- package/dist/cjs/query/builder.js +223 -19
- package/dist/cjs/query/compound-unique.js +0 -0
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/index.js +2 -1
- package/dist/cjs/query/warn-registry.d.ts +10 -0
- package/dist/cjs/query/warn-registry.js +10 -0
- package/dist/cjs/query/writes.js +115 -7
- package/dist/cjs/sqlite.d.ts +10 -4
- package/dist/cjs/sqlite.js +18 -4
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/client.d.ts +106 -2
- package/dist/client.js +111 -5
- package/dist/dialect.d.ts +33 -0
- package/dist/dialect.js +14 -0
- package/dist/engine-config.d.ts +49 -0
- package/dist/engine-config.js +18 -0
- package/dist/index-advisor.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.d.ts +8 -3
- package/dist/mssql.js +22 -3
- package/dist/mysql.d.ts +7 -3
- package/dist/mysql.js +20 -3
- package/dist/nested-write.d.ts +31 -0
- package/dist/nested-write.js +79 -2
- package/dist/powdb-introspect.d.ts +10 -1
- package/dist/powdb-introspect.js +10 -1
- package/dist/powdb.d.ts +116 -6
- package/dist/powdb.js +167 -9
- package/dist/powql.d.ts +161 -1
- package/dist/powql.js +299 -19
- package/dist/prisma-compat.d.ts +54 -8
- package/dist/prisma-compat.js +136 -20
- package/dist/query/batched-loader.d.ts +7 -0
- package/dist/query/batched-loader.js +98 -16
- package/dist/query/builder.d.ts +131 -5
- package/dist/query/builder.js +222 -18
- package/dist/query/compound-unique.js +0 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/warn-registry.d.ts +10 -0
- package/dist/query/warn-registry.js +10 -0
- package/dist/query/writes.js +116 -8
- package/dist/sqlite.d.ts +10 -4
- package/dist/sqlite.js +19 -5
- package/package.json +3 -3
package/dist/cjs/client.d.ts
CHANGED
|
@@ -180,7 +180,19 @@ export interface TurbineConfig {
|
|
|
180
180
|
* owns the pool) and when coercing nested-relation JSON dates. This is the
|
|
181
181
|
* Prisma/Rails/Django convention and makes results independent of the
|
|
182
182
|
* server's local time zone. Default: `true`. Set `false` for the legacy
|
|
183
|
-
* local-time interpretation
|
|
183
|
+
* local-time interpretation, which also turns off the matching WRITE-side
|
|
184
|
+
* rewrite (a bound `Date` on a zone-less `date` / `timestamp` column is then
|
|
185
|
+
* serialized by the driver in the process's zone).
|
|
186
|
+
*
|
|
187
|
+
* PER PROCESS, NOT PER CLIENT. The read half is a pg type parser, and
|
|
188
|
+
* `pg.types.setTypeParser` installs one parser per OID for the whole
|
|
189
|
+
* process. The first Turbine-owned client settles it for every later one, so
|
|
190
|
+
* constructing a second client with the OPPOSITE value throws a
|
|
191
|
+
* `ValidationError` rather than handing back a client whose writes and reads
|
|
192
|
+
* disagree. Give every client in the process the same value, or isolate the
|
|
193
|
+
* odd one in its own process. Clients on an EXTERNAL pool never register a
|
|
194
|
+
* parser and never take part in the check: they inherit whatever parser
|
|
195
|
+
* configuration the caller's driver has.
|
|
184
196
|
*/
|
|
185
197
|
utcTimestamps?: boolean;
|
|
186
198
|
/**
|
|
@@ -297,6 +309,23 @@ export interface TurbineConfig {
|
|
|
297
309
|
* programmatic access regardless of mode.
|
|
298
310
|
*/
|
|
299
311
|
errorMessages?: ErrorMessageMode;
|
|
312
|
+
/**
|
|
313
|
+
* Whether `$on('query')` listeners receive the real bound parameter values.
|
|
314
|
+
*
|
|
315
|
+
* Off by default: every entry of `event.params` is replaced with
|
|
316
|
+
* `'[REDACTED]'` before any listener sees it, so query logs cannot carry
|
|
317
|
+
* user data into a log sink.
|
|
318
|
+
*
|
|
319
|
+
* This is an alias with a discoverable name, NOT a second switch. The
|
|
320
|
+
* redaction has always been governed by {@link errorMessages}, which nobody
|
|
321
|
+
* looks under when they want to see query params. Resolution order, single
|
|
322
|
+
* source of truth: when `logQueryParams` is set it decides; otherwise
|
|
323
|
+
* `errorMessages: 'verbose'` reveals params exactly as it did before. So
|
|
324
|
+
* `logQueryParams: true` with `errorMessages: 'safe'` shows params in query
|
|
325
|
+
* events while keeping error MESSAGES redacted, which is the combination
|
|
326
|
+
* that was previously unreachable.
|
|
327
|
+
*/
|
|
328
|
+
logQueryParams?: boolean;
|
|
300
329
|
/**
|
|
301
330
|
* Enable prepared statements. Queries are submitted with `{ name, text, values }`
|
|
302
331
|
* to the pg driver, which caches the parse+plan on the server per connection.
|
|
@@ -470,6 +499,34 @@ export declare class TransactionClient {
|
|
|
470
499
|
* Execute a raw SQL query within this transaction.
|
|
471
500
|
*/
|
|
472
501
|
raw<T extends Record<string, unknown> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
|
|
502
|
+
/**
|
|
503
|
+
* @internal The `turbine-orm/prisma-compat` adapter's transaction seam. NOT
|
|
504
|
+
* application API: use {@link raw}, whose tagged template makes concatenating
|
|
505
|
+
* a value into the SQL text impossible. This method takes the SQL text as a
|
|
506
|
+
* plain string, so the escaping discipline moves to the caller, which is
|
|
507
|
+
* exactly the property the typed-SQL escape hatch exists to remove. It is
|
|
508
|
+
* documented and kept structurally stable only because the compat adapter
|
|
509
|
+
* detects it by shape (`typeof tx.rawQuery === 'function'`) and refuses to
|
|
510
|
+
* run compat raw SQL on a pool connection when it is missing.
|
|
511
|
+
*
|
|
512
|
+
* Execute an already-parameterized statement on THIS transaction's own
|
|
513
|
+
* connection, returning the driver's `{ rows, rowCount }` pair. It differs
|
|
514
|
+
* from {@link raw} in two ways the adapter needs: it takes a prebuilt
|
|
515
|
+
* `(text, params)` pair rather than a tagged template (the placeholder
|
|
516
|
+
* numbering is the caller's, so nested `Prisma.sql` fragments can be
|
|
517
|
+
* flattened first), and it surfaces `rowCount` so an `$executeRaw`-style call
|
|
518
|
+
* can report affected rows.
|
|
519
|
+
*
|
|
520
|
+
* The statement runs on the transaction's dedicated connection, so it is
|
|
521
|
+
* inside the same BEGIN/COMMIT (and any active SAVEPOINT) as every other
|
|
522
|
+
* statement in the callback. Driver errors are translated by `wrapPgError`,
|
|
523
|
+
* exactly as pool-scoped and table-scoped queries are. Like {@link raw}, it
|
|
524
|
+
* emits no `$on('query')` event and runs no middleware.
|
|
525
|
+
*/
|
|
526
|
+
rawQuery<T extends object = Record<string, unknown>>(text: string, params?: readonly unknown[]): Promise<{
|
|
527
|
+
rows: T[];
|
|
528
|
+
rowCount: number | null;
|
|
529
|
+
}>;
|
|
473
530
|
/**
|
|
474
531
|
* Create a pool-like wrapper around the transaction client.
|
|
475
532
|
* This allows QueryInterface to work with the transaction connection
|
|
@@ -487,7 +544,19 @@ export declare class TurbineClient {
|
|
|
487
544
|
/** The schema metadata this client was built from */
|
|
488
545
|
readonly schema: SchemaMetadata;
|
|
489
546
|
private static int8ParserRegistered;
|
|
490
|
-
|
|
547
|
+
/**
|
|
548
|
+
* The `utcTimestamps` value the FIRST Turbine-owned pool in this process
|
|
549
|
+
* settled the OID 1114 read parser on, or `undefined` while no Turbine-owned
|
|
550
|
+
* client has been constructed yet.
|
|
551
|
+
*
|
|
552
|
+
* `pg.types.setTypeParser` is process-global by nature: there is one parser
|
|
553
|
+
* per OID for the whole pg module, so the READ side of `utcTimestamps` cannot
|
|
554
|
+
* be per client the way the WRITE side is. Recording the settled value (not
|
|
555
|
+
* just "registered yes/no") is what lets the constructor detect a second
|
|
556
|
+
* client asking for the opposite and refuse it, instead of handing back a
|
|
557
|
+
* client whose reads and writes disagree. See {@link assertUtcTimestampsAgree}.
|
|
558
|
+
*/
|
|
559
|
+
private static utcTimestampParserMode;
|
|
491
560
|
private readonly logging;
|
|
492
561
|
/** Active SQL dialect, owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
|
|
493
562
|
private readonly dialect;
|
|
@@ -496,6 +565,8 @@ export declare class TurbineClient {
|
|
|
496
565
|
private readonly queryListeners;
|
|
497
566
|
private queryOptions;
|
|
498
567
|
private readonly errorMessagesSafe;
|
|
568
|
+
/** Whether `$on('query')` events carry real params (see `logQueryParams`). */
|
|
569
|
+
private readonly queryParamsVisible;
|
|
499
570
|
/** True when Turbine created the pool and is responsible for tearing it down */
|
|
500
571
|
private readonly ownsPool;
|
|
501
572
|
/** Active LISTEN subscriptions, torn down on disconnect() so it never hangs */
|
|
@@ -520,6 +591,39 @@ export declare class TurbineClient {
|
|
|
520
591
|
/** Lazily-built, cached primary-only view returned by {@link $primary}. */
|
|
521
592
|
private primaryView?;
|
|
522
593
|
constructor(config: TurbineConfig | undefined, schema: SchemaMetadata);
|
|
594
|
+
/**
|
|
595
|
+
* Refuse a `utcTimestamps` value that contradicts the one the process-global
|
|
596
|
+
* OID 1114 read parser was already settled on.
|
|
597
|
+
*
|
|
598
|
+
* The flag has two halves. The WRITE half is per client: a bound `Date` on a
|
|
599
|
+
* zone-less `date` / `timestamp` column is rewritten to a UTC literal unless
|
|
600
|
+
* the owning client opted out (`coerceWriteValue` in query/writes.ts). The
|
|
601
|
+
* READ half is the pg type parser for OID 1114, and `pg.types.setTypeParser`
|
|
602
|
+
* installs ONE parser per OID for the whole process, shared by every pool,
|
|
603
|
+
* every raw query, and any other library using the same pg module. There is
|
|
604
|
+
* no per-pool parser hook to bind it to, and moving the coercion into
|
|
605
|
+
* `parseRow` instead would leave every non-ORM read (raw SQL, `client.sql`,
|
|
606
|
+
* a caller's own `pool.query`) on the driver's value while changing the
|
|
607
|
+
* default path's output type, so the read half stays process-wide.
|
|
608
|
+
*
|
|
609
|
+
* That makes the mixed shape unserveable rather than merely awkward: the
|
|
610
|
+
* second client would write local calendar fields and read them back as UTC
|
|
611
|
+
* (or the reverse), so its own round trip is off by the process offset.
|
|
612
|
+
* A client that silently does not round-trip is the worst of the three
|
|
613
|
+
* outcomes, so construction fails with the two ways out.
|
|
614
|
+
*
|
|
615
|
+
* Only Turbine-owned pools take part. An external pool (Neon, Vercel
|
|
616
|
+
* Postgres, Hyperdrive) inherits the caller's parser configuration and
|
|
617
|
+
* Turbine never registers on its behalf, so it has no read half to contradict.
|
|
618
|
+
*/
|
|
619
|
+
private static assertUtcTimestampsAgree;
|
|
620
|
+
/**
|
|
621
|
+
* @internal Test-only: forget the process-global `utcTimestamps` decision so
|
|
622
|
+
* one process can exercise both mismatch directions. It does NOT restore the
|
|
623
|
+
* pg parser (parser registration is not reversible), so reads stay on
|
|
624
|
+
* whichever parser was installed first.
|
|
625
|
+
*/
|
|
626
|
+
static resetUtcTimestampsForTests(): void;
|
|
523
627
|
/**
|
|
524
628
|
* Register a middleware function that runs around every query.
|
|
525
629
|
*
|
package/dist/cjs/client.js
CHANGED
|
@@ -198,6 +198,39 @@ class TransactionClient {
|
|
|
198
198
|
throw (0, errors_js_1.wrapPgError)(err);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* @internal The `turbine-orm/prisma-compat` adapter's transaction seam. NOT
|
|
203
|
+
* application API: use {@link raw}, whose tagged template makes concatenating
|
|
204
|
+
* a value into the SQL text impossible. This method takes the SQL text as a
|
|
205
|
+
* plain string, so the escaping discipline moves to the caller, which is
|
|
206
|
+
* exactly the property the typed-SQL escape hatch exists to remove. It is
|
|
207
|
+
* documented and kept structurally stable only because the compat adapter
|
|
208
|
+
* detects it by shape (`typeof tx.rawQuery === 'function'`) and refuses to
|
|
209
|
+
* run compat raw SQL on a pool connection when it is missing.
|
|
210
|
+
*
|
|
211
|
+
* Execute an already-parameterized statement on THIS transaction's own
|
|
212
|
+
* connection, returning the driver's `{ rows, rowCount }` pair. It differs
|
|
213
|
+
* from {@link raw} in two ways the adapter needs: it takes a prebuilt
|
|
214
|
+
* `(text, params)` pair rather than a tagged template (the placeholder
|
|
215
|
+
* numbering is the caller's, so nested `Prisma.sql` fragments can be
|
|
216
|
+
* flattened first), and it surfaces `rowCount` so an `$executeRaw`-style call
|
|
217
|
+
* can report affected rows.
|
|
218
|
+
*
|
|
219
|
+
* The statement runs on the transaction's dedicated connection, so it is
|
|
220
|
+
* inside the same BEGIN/COMMIT (and any active SAVEPOINT) as every other
|
|
221
|
+
* statement in the callback. Driver errors are translated by `wrapPgError`,
|
|
222
|
+
* exactly as pool-scoped and table-scoped queries are. Like {@link raw}, it
|
|
223
|
+
* emits no `$on('query')` event and runs no middleware.
|
|
224
|
+
*/
|
|
225
|
+
async rawQuery(text, params = []) {
|
|
226
|
+
try {
|
|
227
|
+
const result = await this.client.query(text, params);
|
|
228
|
+
return { rows: result.rows, rowCount: result.rowCount };
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
throw (0, errors_js_1.wrapPgError)(err);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
201
234
|
/**
|
|
202
235
|
* Create a pool-like wrapper around the transaction client.
|
|
203
236
|
* This allows QueryInterface to work with the transaction connection
|
|
@@ -248,7 +281,19 @@ class TurbineClient {
|
|
|
248
281
|
/** The schema metadata this client was built from */
|
|
249
282
|
schema;
|
|
250
283
|
static int8ParserRegistered = false;
|
|
251
|
-
|
|
284
|
+
/**
|
|
285
|
+
* The `utcTimestamps` value the FIRST Turbine-owned pool in this process
|
|
286
|
+
* settled the OID 1114 read parser on, or `undefined` while no Turbine-owned
|
|
287
|
+
* client has been constructed yet.
|
|
288
|
+
*
|
|
289
|
+
* `pg.types.setTypeParser` is process-global by nature: there is one parser
|
|
290
|
+
* per OID for the whole pg module, so the READ side of `utcTimestamps` cannot
|
|
291
|
+
* be per client the way the WRITE side is. Recording the settled value (not
|
|
292
|
+
* just "registered yes/no") is what lets the constructor detect a second
|
|
293
|
+
* client asking for the opposite and refuse it, instead of handing back a
|
|
294
|
+
* client whose reads and writes disagree. See {@link assertUtcTimestampsAgree}.
|
|
295
|
+
*/
|
|
296
|
+
static utcTimestampParserMode;
|
|
252
297
|
logging;
|
|
253
298
|
/** Active SQL dialect, owns transaction keywords, set_config, raw-SQL placeholders, capability flags. */
|
|
254
299
|
dialect;
|
|
@@ -257,6 +302,8 @@ class TurbineClient {
|
|
|
257
302
|
queryListeners = new Set();
|
|
258
303
|
queryOptions;
|
|
259
304
|
errorMessagesSafe;
|
|
305
|
+
/** Whether `$on('query')` events carry real params (see `logQueryParams`). */
|
|
306
|
+
queryParamsVisible;
|
|
260
307
|
/** True when Turbine created the pool and is responsible for tearing it down */
|
|
261
308
|
ownsPool = true;
|
|
262
309
|
/** Active LISTEN subscriptions, torn down on disconnect() so it never hangs */
|
|
@@ -292,6 +339,7 @@ class TurbineClient {
|
|
|
292
339
|
this.logging = parent.logging;
|
|
293
340
|
this.dialect = parent.dialect;
|
|
294
341
|
this.errorMessagesSafe = parent.errorMessagesSafe;
|
|
342
|
+
this.queryParamsVisible = parent.queryParamsVisible;
|
|
295
343
|
this.queryOptions = parent.queryOptions;
|
|
296
344
|
this.middlewares = parent.middlewares; // shared reference: $use on parent flows through
|
|
297
345
|
this.pool = parent.pool;
|
|
@@ -375,9 +423,18 @@ class TurbineClient {
|
|
|
375
423
|
// ORM convention (Prisma, Rails, Django), and the only interpretation
|
|
376
424
|
// that round-trips what Postgres stores, is UTC. Same ownership rule as
|
|
377
425
|
// the int8 parser: never mutate parser state on external pools.
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
426
|
+
//
|
|
427
|
+
// Read registration is process-global and one-shot; the WRITE side of the
|
|
428
|
+
// same flag is per client (query/writes.ts). Two clients disagreeing about
|
|
429
|
+
// it therefore cannot both be served, so the disagreement is refused here
|
|
430
|
+
// rather than resolved silently into a client that does not round-trip.
|
|
431
|
+
if (ownsAnyPool) {
|
|
432
|
+
const wantUtcTimestamps = config.utcTimestamps !== false;
|
|
433
|
+
TurbineClient.assertUtcTimestampsAgree(wantUtcTimestamps);
|
|
434
|
+
if (wantUtcTimestamps && TurbineClient.utcTimestampParserMode === undefined) {
|
|
435
|
+
pg_1.default.types.setTypeParser(1114, (val) => new Date(`${val.replace(' ', 'T')}Z`));
|
|
436
|
+
}
|
|
437
|
+
TurbineClient.utcTimestampParserMode = wantUtcTimestamps;
|
|
381
438
|
}
|
|
382
439
|
this.logging = config.logging ?? false;
|
|
383
440
|
this.dialect = config.dialect ?? dialect_js_1.postgresDialect;
|
|
@@ -385,6 +442,10 @@ class TurbineClient {
|
|
|
385
442
|
// Respect env var kill switch
|
|
386
443
|
const envDisablePrepared = typeof process !== 'undefined' && process.env?.TURBINE_DISABLE_PREPARED === '1';
|
|
387
444
|
this.errorMessagesSafe = (config.errorMessages ?? 'safe') === 'safe';
|
|
445
|
+
// Query-event param visibility. One derived boolean, so the two config
|
|
446
|
+
// spellings can never disagree: `logQueryParams` wins when set, otherwise
|
|
447
|
+
// `errorMessages` keeps deciding exactly as it always has.
|
|
448
|
+
this.queryParamsVisible = config.logQueryParams ?? !this.errorMessagesSafe;
|
|
388
449
|
this.queryOptions = {
|
|
389
450
|
defaultLimit: config.defaultLimit,
|
|
390
451
|
warnOnUnlimited: config.warnOnUnlimited,
|
|
@@ -409,7 +470,7 @@ class TurbineClient {
|
|
|
409
470
|
_onQuery: (event) => {
|
|
410
471
|
if (this.queryListeners.size === 0)
|
|
411
472
|
return;
|
|
412
|
-
const emitted = this.
|
|
473
|
+
const emitted = this.queryParamsVisible ? event : { ...event, params: event.params.map(() => '[REDACTED]') };
|
|
413
474
|
for (const listener of this.queryListeners) {
|
|
414
475
|
try {
|
|
415
476
|
listener(emitted);
|
|
@@ -522,6 +583,51 @@ class TurbineClient {
|
|
|
522
583
|
this.$observe({ connectionString: observeUrl }).catch(() => { });
|
|
523
584
|
}
|
|
524
585
|
}
|
|
586
|
+
/**
|
|
587
|
+
* Refuse a `utcTimestamps` value that contradicts the one the process-global
|
|
588
|
+
* OID 1114 read parser was already settled on.
|
|
589
|
+
*
|
|
590
|
+
* The flag has two halves. The WRITE half is per client: a bound `Date` on a
|
|
591
|
+
* zone-less `date` / `timestamp` column is rewritten to a UTC literal unless
|
|
592
|
+
* the owning client opted out (`coerceWriteValue` in query/writes.ts). The
|
|
593
|
+
* READ half is the pg type parser for OID 1114, and `pg.types.setTypeParser`
|
|
594
|
+
* installs ONE parser per OID for the whole process, shared by every pool,
|
|
595
|
+
* every raw query, and any other library using the same pg module. There is
|
|
596
|
+
* no per-pool parser hook to bind it to, and moving the coercion into
|
|
597
|
+
* `parseRow` instead would leave every non-ORM read (raw SQL, `client.sql`,
|
|
598
|
+
* a caller's own `pool.query`) on the driver's value while changing the
|
|
599
|
+
* default path's output type, so the read half stays process-wide.
|
|
600
|
+
*
|
|
601
|
+
* That makes the mixed shape unserveable rather than merely awkward: the
|
|
602
|
+
* second client would write local calendar fields and read them back as UTC
|
|
603
|
+
* (or the reverse), so its own round trip is off by the process offset.
|
|
604
|
+
* A client that silently does not round-trip is the worst of the three
|
|
605
|
+
* outcomes, so construction fails with the two ways out.
|
|
606
|
+
*
|
|
607
|
+
* Only Turbine-owned pools take part. An external pool (Neon, Vercel
|
|
608
|
+
* Postgres, Hyperdrive) inherits the caller's parser configuration and
|
|
609
|
+
* Turbine never registers on its behalf, so it has no read half to contradict.
|
|
610
|
+
*/
|
|
611
|
+
static assertUtcTimestampsAgree(want) {
|
|
612
|
+
const settled = TurbineClient.utcTimestampParserMode;
|
|
613
|
+
if (settled === undefined || settled === want)
|
|
614
|
+
return;
|
|
615
|
+
throw new errors_js_1.ValidationError(`[turbine] utcTimestamps: ${want} conflicts with utcTimestamps: ${settled}, which an earlier TurbineClient ` +
|
|
616
|
+
'in this process already applied. The timestamp READ parser (pg OID 1114) is process-global, so it cannot ' +
|
|
617
|
+
'differ per client, while the WRITE side is per client. Serving both values would give this client a ' +
|
|
618
|
+
`${want ? 'UTC write' : 'local write'} and a ${settled ? 'UTC read' : 'local read'}, so every zone-less ` +
|
|
619
|
+
'`timestamp` it writes would read back shifted by the process offset. Give every TurbineClient in this ' +
|
|
620
|
+
'process the same `utcTimestamps` value, or run the odd one out in its own process.');
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* @internal Test-only: forget the process-global `utcTimestamps` decision so
|
|
624
|
+
* one process can exercise both mismatch directions. It does NOT restore the
|
|
625
|
+
* pg parser (parser registration is not reversible), so reads stay on
|
|
626
|
+
* whichever parser was installed first.
|
|
627
|
+
*/
|
|
628
|
+
static resetUtcTimestampsForTests() {
|
|
629
|
+
TurbineClient.utcTimestampParserMode = undefined;
|
|
630
|
+
}
|
|
525
631
|
// -------------------------------------------------------------------------
|
|
526
632
|
// Middleware, intercept all queries
|
|
527
633
|
// -------------------------------------------------------------------------
|
package/dist/cjs/dialect.d.ts
CHANGED
|
@@ -52,6 +52,20 @@ export interface BulkInsertStatementInput {
|
|
|
52
52
|
*/
|
|
53
53
|
requireRowValues?: boolean;
|
|
54
54
|
}
|
|
55
|
+
export interface DefaultValuesInsertStatementInput {
|
|
56
|
+
/** SQL-ready quoted table name. */
|
|
57
|
+
table: string;
|
|
58
|
+
/**
|
|
59
|
+
* How many all-defaults rows to insert. Always a positive integer (the length
|
|
60
|
+
* of the caller's `data` array), never a user-supplied value, so a dialect may
|
|
61
|
+
* render it as a literal.
|
|
62
|
+
*/
|
|
63
|
+
rowCount: number;
|
|
64
|
+
/** Skip duplicate rows when supported by the dialect. */
|
|
65
|
+
skipDuplicates?: boolean;
|
|
66
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
67
|
+
returning?: ReturningSelection;
|
|
68
|
+
}
|
|
55
69
|
/**
|
|
56
70
|
* The cast/decode pair for one JSON-divergent column type. See
|
|
57
71
|
* {@link Dialect.jsonWireRule}. The two halves MUST agree: a cast with no
|
|
@@ -382,6 +396,25 @@ export interface Dialect {
|
|
|
382
396
|
buildInsertStatement(input: InsertStatementInput): string;
|
|
383
397
|
/** Build a multi-row bulk INSERT statement and its dialect-shaped params. */
|
|
384
398
|
buildBulkInsertStatement(input: BulkInsertStatementInput): BuiltStatement;
|
|
399
|
+
/**
|
|
400
|
+
* Build an INSERT that binds NO column values, so every column takes its
|
|
401
|
+
* declared default: `create({ data: {} })` and `createMany({ data: [{}, …] })`.
|
|
402
|
+
*
|
|
403
|
+
* The naive `INSERT INTO t () VALUES ()` the column-driven builders produce for
|
|
404
|
+
* an empty `data` is a syntax error on every engine but MySQL, and an empty
|
|
405
|
+
* `data` object is easy to reach honestly (a handler that assembles its payload
|
|
406
|
+
* from optional request fields, against a table where every column has a
|
|
407
|
+
* default or is nullable). The correct statement differs per engine, so it
|
|
408
|
+
* lives here rather than as an engine test in the write builders.
|
|
409
|
+
*
|
|
410
|
+
* Implementations MUST honor `rowCount`, and MUST throw an
|
|
411
|
+
* {@link UnsupportedFeatureError} naming the engine when the engine cannot
|
|
412
|
+
* express the requested shape (several engines have a single-row-only
|
|
413
|
+
* `DEFAULT VALUES` and no multi-row equivalent) rather than emitting SQL the
|
|
414
|
+
* database will reject. Optional: a dialect that omits it raises E017 for both
|
|
415
|
+
* shapes.
|
|
416
|
+
*/
|
|
417
|
+
buildDefaultValuesInsertStatement?(input: DefaultValuesInsertStatementInput): string;
|
|
385
418
|
/** Build an upsert statement. Inputs are SQL-ready quoted fragments. */
|
|
386
419
|
buildUpsertStatement(input: UpsertStatementInput): string;
|
|
387
420
|
/** Whether native ILIKE is supported. */
|
package/dist/cjs/dialect.js
CHANGED
|
@@ -146,6 +146,20 @@ exports.postgresDialect = {
|
|
|
146
146
|
sql += ' ON CONFLICT DO NOTHING';
|
|
147
147
|
return { sql: `${sql}${this.buildReturningClause(input.returning)}`, params: columnArrays };
|
|
148
148
|
},
|
|
149
|
+
buildDefaultValuesInsertStatement(input) {
|
|
150
|
+
const conflict = input.skipDuplicates ? ' ON CONFLICT DO NOTHING' : '';
|
|
151
|
+
if (input.rowCount === 1) {
|
|
152
|
+
return `INSERT INTO ${input.table} DEFAULT VALUES${conflict}${this.buildReturningClause(input.returning)}`;
|
|
153
|
+
}
|
|
154
|
+
// N all-defaults rows: `DEFAULT VALUES` is single-row only, so feed the
|
|
155
|
+
// INSERT a zero-column SELECT of N source rows instead. Every column still
|
|
156
|
+
// takes its default (nothing is projected into the INSERT), and unlike
|
|
157
|
+
// `VALUES (DEFAULT), (DEFAULT)` it does not depend on the table's first
|
|
158
|
+
// column accepting the DEFAULT keyword. `rowCount` is the caller's array
|
|
159
|
+
// length, so the literal is an internal integer, not a bound user value.
|
|
160
|
+
return (`INSERT INTO ${input.table} SELECT FROM generate_series(1, ${input.rowCount})` +
|
|
161
|
+
`${conflict}${this.buildReturningClause(input.returning)}`);
|
|
162
|
+
},
|
|
149
163
|
buildUpsertStatement(input) {
|
|
150
164
|
return (`INSERT INTO ${input.table} (${input.insertColumns.join(', ')}) VALUES (${input.valuePlaceholders.join(', ')})` +
|
|
151
165
|
` ON CONFLICT (${input.conflictColumns.join(', ')}) DO UPDATE SET ${input.updateSetClauses.join(', ')}` +
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared client-config seam for the non-Postgres engine factories
|
|
3
|
+
* (`turbine-orm/sqlite`, `turbine-orm/mysql`, `turbine-orm/mssql`).
|
|
4
|
+
*
|
|
5
|
+
* Each factory builds its own driver pool and then hands a {@link TurbineConfig}
|
|
6
|
+
* to `TurbineClient`. Historically each one listed the config keys it forwarded
|
|
7
|
+
* by hand, which made the forwarded set an ALLOWLIST: every option added to
|
|
8
|
+
* `TurbineConfig` afterwards silently became unreachable on three engines until
|
|
9
|
+
* someone remembered to edit three literals (that is exactly how `errorMessages`
|
|
10
|
+
* and later `logQueryParams` ended up Postgres-only).
|
|
11
|
+
*
|
|
12
|
+
* {@link EngineClientConfig} inverts the default. Every `TurbineConfig` field is
|
|
13
|
+
* forwarded EXCEPT the ones listed below, so a new client option is engine-wide
|
|
14
|
+
* the day it lands and only a deliberate `Omit` entry can take it away again.
|
|
15
|
+
* Each engine's options interface extends this type and its factory spreads the
|
|
16
|
+
* config through verbatim.
|
|
17
|
+
*/
|
|
18
|
+
import type { TurbineConfig } from './client.js';
|
|
19
|
+
/**
|
|
20
|
+
* `TurbineConfig` keys an engine factory does NOT forward. Every exclusion is
|
|
21
|
+
* deliberate; nothing else about `TurbineConfig` is engine-specific.
|
|
22
|
+
*
|
|
23
|
+
* - `pool`, `dialect`, `preparedStatements`: owned by the factory. It binds
|
|
24
|
+
* the driver pool and its dialect, and pins prepared statements off (these
|
|
25
|
+
* drivers cache plans themselves). Letting a caller set them would unbind
|
|
26
|
+
* the engine from its own SQL.
|
|
27
|
+
* - `connectionString`, `host`, `port`, `database`, `user`, `password`, `ssl`,
|
|
28
|
+
* `poolSize`, `idleTimeoutMs`, `connectionTimeoutMs`, `max`,
|
|
29
|
+
* `idleTimeoutMillis`, `connectionTimeoutMillis`: pg connection and
|
|
30
|
+
* pool-tuning fields, ignored by `TurbineClient` whenever `pool` is set (it
|
|
31
|
+
* always is here). Each engine takes its connection target as its first
|
|
32
|
+
* argument instead, in that driver's own vocabulary.
|
|
33
|
+
* - `replicas`: entries are Postgres connection strings or pg-compatible
|
|
34
|
+
* pools, which `TurbineClient` opens as `pg.Pool`s. There is no engine
|
|
35
|
+
* equivalent to route reads to.
|
|
36
|
+
*
|
|
37
|
+
* Everything else is forwarded, including options that are Postgres-only by
|
|
38
|
+
* CAPABILITY rather than by plumbing (`jsonEncoding: 'positional'`). Those throw
|
|
39
|
+
* a typed `UnsupportedFeatureError` (E017) when used, which is a better answer
|
|
40
|
+
* than an option the type system says does not exist.
|
|
41
|
+
*/
|
|
42
|
+
type EngineOwnedConfigKey = 'pool' | 'dialect' | 'preparedStatements' | 'connectionString' | 'host' | 'port' | 'database' | 'user' | 'password' | 'ssl' | 'poolSize' | 'idleTimeoutMs' | 'connectionTimeoutMs' | 'max' | 'idleTimeoutMillis' | 'connectionTimeoutMillis' | 'replicas';
|
|
43
|
+
/**
|
|
44
|
+
* The client-level options every engine factory accepts and forwards to
|
|
45
|
+
* `TurbineClient` unchanged: all of {@link TurbineConfig} minus the connection
|
|
46
|
+
* and dialect plumbing the factory owns (see {@link EngineOwnedConfigKey}).
|
|
47
|
+
*/
|
|
48
|
+
export type EngineClientConfig = Omit<TurbineConfig, EngineOwnedConfigKey>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared client-config seam for the non-Postgres engine factories
|
|
4
|
+
* (`turbine-orm/sqlite`, `turbine-orm/mysql`, `turbine-orm/mssql`).
|
|
5
|
+
*
|
|
6
|
+
* Each factory builds its own driver pool and then hands a {@link TurbineConfig}
|
|
7
|
+
* to `TurbineClient`. Historically each one listed the config keys it forwarded
|
|
8
|
+
* by hand, which made the forwarded set an ALLOWLIST: every option added to
|
|
9
|
+
* `TurbineConfig` afterwards silently became unreachable on three engines until
|
|
10
|
+
* someone remembered to edit three literals (that is exactly how `errorMessages`
|
|
11
|
+
* and later `logQueryParams` ended up Postgres-only).
|
|
12
|
+
*
|
|
13
|
+
* {@link EngineClientConfig} inverts the default. Every `TurbineConfig` field is
|
|
14
|
+
* forwarded EXCEPT the ones listed below, so a new client option is engine-wide
|
|
15
|
+
* the day it lands and only a deliberate `Omit` entry can take it away again.
|
|
16
|
+
* Each engine's options interface extends this type and its factory spreads the
|
|
17
|
+
* config through verbatim.
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
Binary file
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export { type IntrospectOptions, introspect } from './introspect.js';
|
|
|
43
43
|
export { executeNestedCreate, executeNestedUpdate, hasRelationFields, type NestedWriteContext, } from './nested-write.js';
|
|
44
44
|
export { HttpJsonSink, type HttpJsonSinkOptions, type MetricsFlushBatch, type MetricsFlushRow, type ObserveConfig, type ObserveHandle, type ObserveSink, PgMetricsSink, type PgMetricsSinkOptions, } from './observe.js';
|
|
45
45
|
export { executePipeline, type PipelineOptions, type PipelineResults, pipelineSupported } from './pipeline.js';
|
|
46
|
-
export { type AggregateArgs, type AggregateResult, type ArrayFilter, AUTO_ASSUMED_ROUND_TRIP_MS, AUTO_JOIN_PENALTY_MS_PER_ROW, AUTO_TO_ONE_JOIN_MAX_ROWS, AUTO_TO_ONE_JOIN_ROWS_MAX, AUTO_TO_ONE_JOIN_ROWS_MIN, type ColumnRef, type ConnectOrCreateOp, type CountArgs, type CreateArgs, type CreateDataInput, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FieldResult, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GlobalFilters, type GroupByAggregateSpec, type GroupByArgs, type GroupByDistinctOn, type GroupByResult, type HavingClause, type JsonFilter, type JsonPathAggregateTarget, type JsonPathGroupKey, type JsonPathOrderBy, type MiddlewareFn, type NestedCreateOp, type NestedUpdateOp, type NestedUpdateOpItem, type NestedUpsertOpItem, type OmitResult, type OrderByClause, type OrderByObject, type OrderDirection, type QueryEvent, type QueryEventListener, QueryInterface, type QueryResult, type RelationDescriptor, type RelationFilter, type RelationLoadStrategy, type RelationPickBy, type RelationPickOrderBy, type SelectResult, type SkipGlobalFilters, type TextSearchFilter, type TypedWithClause, type UpdateArgs, type UpdateDataInput, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type VectorDistanceFilter, type VectorFilter, type VectorMetric, type VectorOrderBy, type VectorOrderByDistance, type WhereClause, type WhereOperator, type WhereValue, type WithClause, type WithOptions, type WithOrderByObject, type WithResult, } from './query/index.js';
|
|
46
|
+
export { type AggregateArgs, type AggregateResult, type ArrayFilter, AUTO_ASSUMED_ROUND_TRIP_MS, AUTO_COUNT_BATCH_MIN_PARENT_ROWS, AUTO_JOIN_PENALTY_MS_PER_ROW, AUTO_TO_ONE_JOIN_MAX_ROWS, AUTO_TO_ONE_JOIN_ROWS_MAX, AUTO_TO_ONE_JOIN_ROWS_MIN, type ColumnRef, type ConnectOrCreateOp, type CountArgs, type CreateArgs, type CreateDataInput, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FieldResult, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GlobalFilters, type GroupByAggregateSpec, type GroupByArgs, type GroupByDistinctOn, type GroupByResult, type HavingClause, type JsonFilter, type JsonPathAggregateTarget, type JsonPathGroupKey, type JsonPathOrderBy, type MiddlewareFn, type NestedCreateOp, type NestedUpdateOp, type NestedUpdateOpItem, type NestedUpsertOpItem, type OmitResult, type OrderByClause, type OrderByObject, type OrderDirection, type QueryEvent, type QueryEventListener, QueryInterface, type QueryResult, type RelationDescriptor, type RelationFilter, type RelationLoadStrategy, type RelationPickBy, type RelationPickOrderBy, type SelectResult, type SkipGlobalFilters, type TextSearchFilter, type TypedWithClause, type UpdateArgs, type UpdateDataInput, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type VectorDistanceFilter, type VectorFilter, type VectorMetric, type VectorOrderBy, type VectorOrderByDistance, type WhereClause, type WhereOperator, type WhereValue, type WithClause, type WithOptions, type WithOrderByObject, type WithResult, } from './query/index.js';
|
|
47
47
|
export { type ActiveSubscription, type NotificationHandler, type Subscription, validateChannel } from './realtime.js';
|
|
48
48
|
export type { CheckMetadata, ColumnMetadata, IndexMetadata, PrismaCompatMap, PrismaModelMap, PrismaRelationMap, ReferentialAction, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
|
|
49
49
|
export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, withDbFieldNames, } from './schema.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
exports.
|
|
38
|
-
exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = exports.withDbFieldNames = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = void 0;
|
|
37
|
+
exports.camelToSnake = exports.validateChannel = exports.QueryInterface = exports.AUTO_TO_ONE_JOIN_ROWS_MIN = exports.AUTO_TO_ONE_JOIN_ROWS_MAX = exports.AUTO_TO_ONE_JOIN_MAX_ROWS = exports.AUTO_JOIN_PENALTY_MS_PER_ROW = exports.AUTO_COUNT_BATCH_MIN_PARENT_ROWS = exports.AUTO_ASSUMED_ROUND_TRIP_MS = exports.pipelineSupported = exports.executePipeline = exports.PgMetricsSink = exports.HttpJsonSink = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UnsupportedFeatureError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.ReadOnlyError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
|
|
38
|
+
exports.TypedSqlQuery = exports.buildTypedSql = exports.turbineHttp = exports.defineSeed = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.DestructivePushRefusal = exports.schemaDefToMetadata = exports.table = exports.isDocFieldIndexDef = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.applyManyToManyRelations = exports.withDbFieldNames = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = void 0;
|
|
39
39
|
var index_js_1 = require("./adapters/index.js");
|
|
40
40
|
Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
|
|
41
41
|
Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
|
|
@@ -96,6 +96,7 @@ Object.defineProperty(exports, "pipelineSupported", { enumerable: true, get: fun
|
|
|
96
96
|
// Query builder
|
|
97
97
|
var index_js_2 = require("./query/index.js");
|
|
98
98
|
Object.defineProperty(exports, "AUTO_ASSUMED_ROUND_TRIP_MS", { enumerable: true, get: function () { return index_js_2.AUTO_ASSUMED_ROUND_TRIP_MS; } });
|
|
99
|
+
Object.defineProperty(exports, "AUTO_COUNT_BATCH_MIN_PARENT_ROWS", { enumerable: true, get: function () { return index_js_2.AUTO_COUNT_BATCH_MIN_PARENT_ROWS; } });
|
|
99
100
|
Object.defineProperty(exports, "AUTO_JOIN_PENALTY_MS_PER_ROW", { enumerable: true, get: function () { return index_js_2.AUTO_JOIN_PENALTY_MS_PER_ROW; } });
|
|
100
101
|
Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_MAX_ROWS", { enumerable: true, get: function () { return index_js_2.AUTO_TO_ONE_JOIN_MAX_ROWS; } });
|
|
101
102
|
Object.defineProperty(exports, "AUTO_TO_ONE_JOIN_ROWS_MAX", { enumerable: true, get: function () { return index_js_2.AUTO_TO_ONE_JOIN_ROWS_MAX; } });
|
package/dist/cjs/mssql.d.ts
CHANGED
|
@@ -89,8 +89,9 @@
|
|
|
89
89
|
* await db.disconnect();
|
|
90
90
|
* ```
|
|
91
91
|
*/
|
|
92
|
-
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient
|
|
92
|
+
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient } from './client.js';
|
|
93
93
|
import { type Dialect, type IntrospectOptions } from './dialect.js';
|
|
94
|
+
import type { EngineClientConfig } from './engine-config.js';
|
|
94
95
|
import { type SchemaMetadata } from './schema.js';
|
|
95
96
|
interface MssqlRequest {
|
|
96
97
|
input(name: string, value: unknown): MssqlRequest;
|
|
@@ -204,8 +205,12 @@ interface MssqlConnectionConfig {
|
|
|
204
205
|
trustServerCertificate?: boolean;
|
|
205
206
|
};
|
|
206
207
|
}
|
|
207
|
-
/**
|
|
208
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Options for {@link turbineMssql}: every client-level `TurbineConfig` field the
|
|
210
|
+
* engine can honour (see {@link EngineClientConfig}) plus the SQL Server schema
|
|
211
|
+
* below.
|
|
212
|
+
*/
|
|
213
|
+
export interface TurbineMssqlOptions extends EngineClientConfig {
|
|
209
214
|
/** SQL Server schema for introspection / DDL (default `dbo`). */
|
|
210
215
|
schema?: string;
|
|
211
216
|
}
|
package/dist/cjs/mssql.js
CHANGED
|
@@ -603,6 +603,22 @@ exports.mssqlDialect = {
|
|
|
603
603
|
params: input.rowValues.flat(),
|
|
604
604
|
};
|
|
605
605
|
},
|
|
606
|
+
buildDefaultValuesInsertStatement(input) {
|
|
607
|
+
// T-SQL puts OUTPUT between the (absent) column list and the value source,
|
|
608
|
+
// so `DEFAULT VALUES` follows it, exactly like the VALUES form above.
|
|
609
|
+
if (input.skipDuplicates) {
|
|
610
|
+
throw new errors_js_1.UnsupportedFeatureError('createMany({ skipDuplicates: true })', 'mssql', 'SQL Server has no ON CONFLICT DO NOTHING equivalent, pre-filter conflicting rows or use a MERGE.');
|
|
611
|
+
}
|
|
612
|
+
// `DEFAULT VALUES` is single-row only in T-SQL, and the multi-row VALUES
|
|
613
|
+
// form needs one DEFAULT keyword per column, which rows of pure defaults
|
|
614
|
+
// do not name.
|
|
615
|
+
if (input.rowCount !== 1) {
|
|
616
|
+
throw new errors_js_1.UnsupportedFeatureError(`createMany with ${input.rowCount} empty data rows`, 'mssql', 'SQL Server INSERT … DEFAULT VALUES inserts a single row and has no multi-row form; ' +
|
|
617
|
+
'issue one create({ data: {} }) per row.');
|
|
618
|
+
}
|
|
619
|
+
const out = mssqlOutput(input.returning, 'INSERTED');
|
|
620
|
+
return `INSERT INTO ${input.table}${out} DEFAULT VALUES`;
|
|
621
|
+
},
|
|
606
622
|
buildUpsertStatement(input) {
|
|
607
623
|
// MERGE is the SQL Server upsert. The MERGE statement MUST end with `;`.
|
|
608
624
|
// CONCURRENCY CAVEAT: MERGE is not a substitute for a UNIQUE/PK constraint -
|
|
@@ -1291,13 +1307,16 @@ async function turbineMssql(target, schema, options = {}) {
|
|
|
1291
1307
|
throw err;
|
|
1292
1308
|
// A non-version probe failure (permissions, etc.) should not block startup.
|
|
1293
1309
|
}
|
|
1310
|
+
// Everything that is not a SQL Server introspection option is client config
|
|
1311
|
+
// and rides through untouched, so a new TurbineConfig option works here the
|
|
1312
|
+
// day it lands. The engine-owned keys come last, so no caller can unbind the
|
|
1313
|
+
// dialect.
|
|
1314
|
+
const { schema: _dbSchema, ...clientConfig } = options;
|
|
1294
1315
|
const client = new client_js_1.TurbineClient({
|
|
1316
|
+
...clientConfig,
|
|
1295
1317
|
pool,
|
|
1296
1318
|
dialect: exports.mssqlDialect,
|
|
1297
1319
|
preparedStatements: false,
|
|
1298
|
-
logging: options.logging,
|
|
1299
|
-
defaultLimit: options.defaultLimit,
|
|
1300
|
-
warnOnUnlimited: options.warnOnUnlimited,
|
|
1301
1320
|
}, schema);
|
|
1302
1321
|
if (owns) {
|
|
1303
1322
|
// Turbine built this pool, so disconnect()/end() must close it. External pools
|
package/dist/cjs/mysql.d.ts
CHANGED
|
@@ -62,8 +62,9 @@
|
|
|
62
62
|
* await db.disconnect();
|
|
63
63
|
* ```
|
|
64
64
|
*/
|
|
65
|
-
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient
|
|
65
|
+
import { type PgCompatPool, type PgCompatPoolClient, TurbineClient } from './client.js';
|
|
66
66
|
import { type Dialect, type IntrospectOptions } from './dialect.js';
|
|
67
|
+
import type { EngineClientConfig } from './engine-config.js';
|
|
67
68
|
import { type SchemaMetadata } from './schema.js';
|
|
68
69
|
/** mysql2's `[result, fields]` tuple. `result` is rows (SELECT) or a header (write). */
|
|
69
70
|
type Mysql2Result = [unknown, unknown];
|
|
@@ -145,8 +146,11 @@ interface MysqlConnectionConfig {
|
|
|
145
146
|
password?: string;
|
|
146
147
|
database?: string;
|
|
147
148
|
}
|
|
148
|
-
/**
|
|
149
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Options for {@link turbineMysql}: every client-level `TurbineConfig` field the
|
|
151
|
+
* engine can honour (see {@link EngineClientConfig}) plus the pool size below.
|
|
152
|
+
*/
|
|
153
|
+
export interface TurbineMysqlOptions extends EngineClientConfig {
|
|
150
154
|
/** Maximum number of pooled connections (when Turbine builds the pool). Default: 10. */
|
|
151
155
|
connectionLimit?: number;
|
|
152
156
|
}
|
package/dist/cjs/mysql.js
CHANGED
|
@@ -524,6 +524,20 @@ exports.mysqlDialect = {
|
|
|
524
524
|
params: input.rowValues.flat(),
|
|
525
525
|
};
|
|
526
526
|
},
|
|
527
|
+
buildDefaultValuesInsertStatement(input) {
|
|
528
|
+
// MySQL is the one engine whose empty `VALUES ()` tuple is legal, and it
|
|
529
|
+
// repeats for any row count, so this is the same statement the column-driven
|
|
530
|
+
// builders already emitted for an empty `data`.
|
|
531
|
+
if (input.skipDuplicates) {
|
|
532
|
+
// `ON DUPLICATE KEY UPDATE` needs a column to assign, and there is no
|
|
533
|
+
// column here. `INSERT IGNORE` would swallow unrelated errors too, so
|
|
534
|
+
// refuse rather than quietly change what the flag means.
|
|
535
|
+
throw new errors_js_1.UnsupportedFeatureError('createMany({ data: [{}, …], skipDuplicates: true })', 'mysql', 'MySQL ON DUPLICATE KEY UPDATE needs a column assignment, and rows of pure defaults name none; ' +
|
|
536
|
+
'supply the conflict column in each row.');
|
|
537
|
+
}
|
|
538
|
+
const tuples = Array.from({ length: input.rowCount }, () => '()').join(', ');
|
|
539
|
+
return `INSERT INTO ${input.table} () VALUES ${tuples}`;
|
|
540
|
+
},
|
|
527
541
|
buildUpsertStatement(input) {
|
|
528
542
|
// MySQL ignores the explicit conflict target, ON DUPLICATE KEY UPDATE keys
|
|
529
543
|
// off the table's PK/unique indexes. The `where`-derived conflictColumns
|
|
@@ -974,13 +988,16 @@ async function turbineMysql(target, schema, options = {}) {
|
|
|
974
988
|
// Probe the server version (fail fast on MySQL < 8.0 / MariaDB).
|
|
975
989
|
const versionRows = (await pool.query('SELECT VERSION() AS v')).rows;
|
|
976
990
|
assertSupportedVersion(String(versionRows[0]?.v ?? ''));
|
|
991
|
+
// Everything that is not a pool-construction option is client config and
|
|
992
|
+
// rides through untouched, so a new TurbineConfig option works here the day
|
|
993
|
+
// it lands. The engine-owned keys come last, so no caller can unbind the
|
|
994
|
+
// dialect.
|
|
995
|
+
const { connectionLimit: _connectionLimit, ...clientConfig } = options;
|
|
977
996
|
const client = new client_js_1.TurbineClient({
|
|
997
|
+
...clientConfig,
|
|
978
998
|
pool,
|
|
979
999
|
dialect: exports.mysqlDialect,
|
|
980
1000
|
preparedStatements: false,
|
|
981
|
-
logging: options.logging,
|
|
982
|
-
defaultLimit: options.defaultLimit,
|
|
983
|
-
warnOnUnlimited: options.warnOnUnlimited,
|
|
984
1001
|
}, schema);
|
|
985
1002
|
if (owns) {
|
|
986
1003
|
// Turbine built this pool, so disconnect()/end() must close it. External
|