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/client.js
CHANGED
|
@@ -104,15 +104,27 @@ export class TransactionClient {
|
|
|
104
104
|
schema;
|
|
105
105
|
middlewares;
|
|
106
106
|
queryOptions;
|
|
107
|
+
sourcePool;
|
|
107
108
|
tableCache = new Map();
|
|
108
109
|
savepointCounter = 0;
|
|
109
110
|
/** Active SQL dialect — owns savepoint keywords and raw-SQL placeholders. */
|
|
110
111
|
dialect;
|
|
111
|
-
constructor(client, schema, middlewares, queryOptions
|
|
112
|
+
constructor(client, schema, middlewares, queryOptions,
|
|
113
|
+
/**
|
|
114
|
+
* The parent pool this transaction runs on. Only its `readonly` and
|
|
115
|
+
* `capabilities` are read (both PowDB-only flags), so the transaction-scoped
|
|
116
|
+
* proxy pool built by {@link createTxPool} carries them through: without this
|
|
117
|
+
* a read-only client's `$transaction` writes bypass the E018 guard, and an
|
|
118
|
+
* older-engine client falls back to ALL_POWDB_CAPABILITIES inside the tx
|
|
119
|
+
* (emitting join PowQL a pre-0.13 engine rejects). Undefined / absent flags
|
|
120
|
+
* for a plain pg pool leave the proxy unchanged.
|
|
121
|
+
*/
|
|
122
|
+
sourcePool) {
|
|
112
123
|
this.client = client;
|
|
113
124
|
this.schema = schema;
|
|
114
125
|
this.middlewares = middlewares;
|
|
115
126
|
this.queryOptions = queryOptions;
|
|
127
|
+
this.sourcePool = sourcePool;
|
|
116
128
|
this.dialect = queryOptions?.dialect ?? postgresDialect;
|
|
117
129
|
// Auto-create typed table accessors for all tables in the schema
|
|
118
130
|
for (const tableName of Object.keys(schema.tables)) {
|
|
@@ -192,7 +204,7 @@ export class TransactionClient {
|
|
|
192
204
|
const client = this.client;
|
|
193
205
|
// Return a minimal pool-compatible object that routes queries
|
|
194
206
|
// through the transaction client
|
|
195
|
-
|
|
207
|
+
const txPool = {
|
|
196
208
|
query: async (textOrConfig, values) => {
|
|
197
209
|
try {
|
|
198
210
|
if (typeof textOrConfig === 'string') {
|
|
@@ -209,6 +221,14 @@ export class TransactionClient {
|
|
|
209
221
|
},
|
|
210
222
|
connect: () => Promise.resolve(client),
|
|
211
223
|
};
|
|
224
|
+
// Carry the parent pool's PowDB-only flags through so a transaction-scoped
|
|
225
|
+
// PowqlInterface reads the same read-only guard and capabilities it would
|
|
226
|
+
// outside the transaction (a plain pg pool has neither, so nothing changes).
|
|
227
|
+
if (this.sourcePool?.readonly !== undefined)
|
|
228
|
+
txPool.readonly = this.sourcePool.readonly;
|
|
229
|
+
if (this.sourcePool?.capabilities !== undefined)
|
|
230
|
+
txPool.capabilities = this.sourcePool.capabilities;
|
|
231
|
+
return txPool;
|
|
212
232
|
}
|
|
213
233
|
}
|
|
214
234
|
// ---------------------------------------------------------------------------
|
|
@@ -876,8 +896,10 @@ export class TurbineClient {
|
|
|
876
896
|
await client.query(cfg.sql, cfg.params);
|
|
877
897
|
}
|
|
878
898
|
}
|
|
879
|
-
// Create the transaction client with typed table accessors
|
|
880
|
-
|
|
899
|
+
// Create the transaction client with typed table accessors. Pass the
|
|
900
|
+
// parent pool so its read-only guard + PowDB capabilities flow into the
|
|
901
|
+
// transaction-scoped proxy pool (see TransactionClient.createTxPool).
|
|
902
|
+
const tx = new TransactionClient(client, this.schema, this.middlewares, this.queryOptions, this.pool);
|
|
881
903
|
// Dynamically attach table accessors to tx
|
|
882
904
|
for (const tableName of Object.keys(this.schema.tables)) {
|
|
883
905
|
const camelName = tableName.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
package/dist/dialect.d.ts
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
import type { WithOptions } from './query/types.js';
|
|
9
9
|
import { type RelationDef, type SchemaMetadata, type TableMetadata } from './schema.js';
|
|
10
10
|
export type DialectName = 'postgresql' | 'mysql' | 'sqlite' | (string & {});
|
|
11
|
+
/**
|
|
12
|
+
* A write statement's returning/output selection. `'*'` returns every column
|
|
13
|
+
* (the historical default, byte-identical SQL). A `string[]` is an explicit,
|
|
14
|
+
* SQL-ready quoted column list used to exclude PII-tagged columns from a
|
|
15
|
+
* write's returned row at the SQL level, so PII never crosses the wire
|
|
16
|
+
* unrequested. Each dialect renders it into its own returning surface
|
|
17
|
+
* (`RETURNING …`, `OUTPUT INSERTED.…`).
|
|
18
|
+
*/
|
|
19
|
+
export type ReturningSelection = '*' | readonly string[];
|
|
11
20
|
export interface InsertStatementInput {
|
|
12
21
|
/** SQL-ready quoted table name. */
|
|
13
22
|
table: string;
|
|
@@ -16,7 +25,7 @@ export interface InsertStatementInput {
|
|
|
16
25
|
/** SQL-ready parameter placeholders/expressions for VALUES. */
|
|
17
26
|
valuePlaceholders: string[];
|
|
18
27
|
/** Optional SQL-ready RETURNING selection. */
|
|
19
|
-
returning?:
|
|
28
|
+
returning?: ReturningSelection;
|
|
20
29
|
}
|
|
21
30
|
export interface BulkInsertStatementInput {
|
|
22
31
|
/** SQL-ready quoted table name. */
|
|
@@ -30,7 +39,7 @@ export interface BulkInsertStatementInput {
|
|
|
30
39
|
/** Skip duplicate rows when supported by the dialect. */
|
|
31
40
|
skipDuplicates?: boolean;
|
|
32
41
|
/** Optional SQL-ready RETURNING selection. */
|
|
33
|
-
returning?:
|
|
42
|
+
returning?: ReturningSelection;
|
|
34
43
|
}
|
|
35
44
|
export interface BuiltStatement {
|
|
36
45
|
sql: string;
|
|
@@ -56,7 +65,7 @@ export interface UpsertStatementInput {
|
|
|
56
65
|
*/
|
|
57
66
|
updateWhere?: string;
|
|
58
67
|
/** Optional SQL-ready RETURNING selection. */
|
|
59
|
-
returning?:
|
|
68
|
+
returning?: ReturningSelection;
|
|
60
69
|
}
|
|
61
70
|
export interface ColumnTypeInput {
|
|
62
71
|
/** Schema-builder column type name (PostgreSQL-native in the root package). */
|
|
@@ -136,7 +145,7 @@ export interface UpdateStatementInput {
|
|
|
136
145
|
/** SQL-ready WHERE fragment INCLUDING the leading ` WHERE ` (or '' for none). */
|
|
137
146
|
whereSql: string;
|
|
138
147
|
/** SQL-ready returning selection (default `*`). */
|
|
139
|
-
returning?:
|
|
148
|
+
returning?: ReturningSelection;
|
|
140
149
|
}
|
|
141
150
|
/**
|
|
142
151
|
* Inputs for {@link Dialect.buildDeleteStatement} — full DELETE assembly. SQL Server
|
|
@@ -148,7 +157,7 @@ export interface DeleteStatementInput {
|
|
|
148
157
|
/** SQL-ready WHERE fragment INCLUDING the leading ` WHERE ` (or '' for none). */
|
|
149
158
|
whereSql: string;
|
|
150
159
|
/** SQL-ready returning selection (default `*`). */
|
|
151
|
-
returning?:
|
|
160
|
+
returning?: ReturningSelection;
|
|
152
161
|
}
|
|
153
162
|
/**
|
|
154
163
|
* Inputs for {@link Dialect.buildLimitOffset} — the trailing pagination clause of an
|
|
@@ -302,8 +311,21 @@ export interface Dialect {
|
|
|
302
311
|
* lateral plan (else E017). PostgreSQL only in this release.
|
|
303
312
|
*/
|
|
304
313
|
readonly supportsLateralJoin?: boolean;
|
|
314
|
+
/**
|
|
315
|
+
* How this dialect surfaces a query plan for a compiled SELECT. When present,
|
|
316
|
+
* `QueryInterface.explain()` prepends `prefix` (plus a single space) to the
|
|
317
|
+
* compiled findMany SQL and runs it as a read, returning the plan text lines.
|
|
318
|
+
* PostgreSQL / CockroachDB / YugabyteDB and MySQL use `EXPLAIN`, SQLite
|
|
319
|
+
* `EXPLAIN QUERY PLAN`. Absent means the engine
|
|
320
|
+
* cannot explain a compiled query in-band (SQL Server, whose SHOWPLAN needs a
|
|
321
|
+
* separate session toggle), so `QueryInterface.explain()` throws E017.
|
|
322
|
+
* Optional: dialects that predate this hook keep throwing E017.
|
|
323
|
+
*/
|
|
324
|
+
readonly explainQuery?: {
|
|
325
|
+
prefix: string;
|
|
326
|
+
};
|
|
305
327
|
/** Build a dialect-specific RETURNING clause. Return an empty string when unsupported. */
|
|
306
|
-
buildReturningClause(selection?:
|
|
328
|
+
buildReturningClause(selection?: ReturningSelection): string;
|
|
307
329
|
/** Build a single-row INSERT statement. Inputs are SQL-ready quoted fragments. */
|
|
308
330
|
buildInsertStatement(input: InsertStatementInput): string;
|
|
309
331
|
/** Build a multi-row bulk INSERT statement and its dialect-shaped params. */
|
package/dist/dialect.js
CHANGED
|
@@ -23,6 +23,7 @@ export const postgresDialect = {
|
|
|
23
23
|
supportsRLS: true,
|
|
24
24
|
supportsAdvisoryLock: true,
|
|
25
25
|
supportsLateralJoin: true,
|
|
26
|
+
explainQuery: { prefix: 'EXPLAIN' },
|
|
26
27
|
paramPlaceholder(index) {
|
|
27
28
|
return `$${index}`;
|
|
28
29
|
},
|
|
@@ -68,7 +69,7 @@ export const postgresDialect = {
|
|
|
68
69
|
return `COALESCE((${subquery}), ${fallback})`;
|
|
69
70
|
},
|
|
70
71
|
buildReturningClause(selection = '*') {
|
|
71
|
-
return ` RETURNING ${selection}`;
|
|
72
|
+
return ` RETURNING ${selection === '*' ? '*' : selection.join(', ')}`;
|
|
72
73
|
},
|
|
73
74
|
buildInsertStatement(input) {
|
|
74
75
|
return `INSERT INTO ${input.table} (${input.columns.join(', ')}) VALUES (${input.valuePlaceholders.join(', ')})${this.buildReturningClause(input.returning)}`;
|
package/dist/errors.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare const TurbineErrorCode: {
|
|
|
23
23
|
readonly OPTIMISTIC_LOCK: "TURBINE_E015";
|
|
24
24
|
readonly EXCLUSION_VIOLATION: "TURBINE_E016";
|
|
25
25
|
readonly UNSUPPORTED_FEATURE: "TURBINE_E017";
|
|
26
|
+
readonly READ_ONLY: "TURBINE_E018";
|
|
26
27
|
};
|
|
27
28
|
export type TurbineErrorCode = (typeof TurbineErrorCode)[keyof typeof TurbineErrorCode];
|
|
28
29
|
/** Base error class for all Turbine errors */
|
|
@@ -303,6 +304,41 @@ export declare class UnsupportedFeatureError extends TurbineError {
|
|
|
303
304
|
readonly dialect: string;
|
|
304
305
|
constructor(feature: string, dialect: string, hint?: string);
|
|
305
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Thrown when a write or DDL statement is refused because the target is
|
|
309
|
+
* read-only. Two shapes reach here, both on PowDB:
|
|
310
|
+
* - an embedded database opened read-only for snapshot serving refuses a write
|
|
311
|
+
* with `readonly mode: statement requires a writer …`;
|
|
312
|
+
* - a networked read-only role refuses a write with `permission denied: role
|
|
313
|
+
* '<role>' cannot execute write statements` (translated by `wrapPowdbError`).
|
|
314
|
+
* It is also raised locally, before the wire, when a write is issued on a pool
|
|
315
|
+
* the caller marked read-only (fail-fast). The message carries the engine text
|
|
316
|
+
* plus a hint to route writes to a writable primary.
|
|
317
|
+
*
|
|
318
|
+
* NOT retryable: the same write against the same read-only target fails
|
|
319
|
+
* identically; route it to a writable primary instead.
|
|
320
|
+
*/
|
|
321
|
+
export declare class ReadOnlyError extends TurbineError {
|
|
322
|
+
/**
|
|
323
|
+
* Why the write was refused. `'snapshot'`: the database itself is read-only
|
|
324
|
+
* (snapshot serving, an embedded `readonly: true` open, or the client-level
|
|
325
|
+
* fail-fast flag), so NOTHING can write here and writes must route to the
|
|
326
|
+
* primary. `'rbac'`: the database is writable but THIS connection's role may
|
|
327
|
+
* not write (per-connection permission), so re-authenticating may suffice.
|
|
328
|
+
*/
|
|
329
|
+
readonly reason: 'snapshot' | 'rbac';
|
|
330
|
+
/**
|
|
331
|
+
* @param detail human-readable description of the refused write (the engine
|
|
332
|
+
* message, or a local fail-fast description). A "route writes to a writable
|
|
333
|
+
* primary" hint is always appended.
|
|
334
|
+
* @param options optional driver `cause` to preserve when wrapping a refusal,
|
|
335
|
+
* and the refusal `reason` (default `'snapshot'`).
|
|
336
|
+
*/
|
|
337
|
+
constructor(detail: string, options?: {
|
|
338
|
+
cause?: unknown;
|
|
339
|
+
reason?: 'snapshot' | 'rbac';
|
|
340
|
+
});
|
|
341
|
+
}
|
|
306
342
|
/**
|
|
307
343
|
* Translate a pg driver error into a typed Turbine error.
|
|
308
344
|
* If the error doesn't match a known constraint code, returns it unchanged.
|
package/dist/errors.js
CHANGED
|
@@ -23,6 +23,7 @@ export const TurbineErrorCode = {
|
|
|
23
23
|
OPTIMISTIC_LOCK: 'TURBINE_E015',
|
|
24
24
|
EXCLUSION_VIOLATION: 'TURBINE_E016',
|
|
25
25
|
UNSUPPORTED_FEATURE: 'TURBINE_E017',
|
|
26
|
+
READ_ONLY: 'TURBINE_E018',
|
|
26
27
|
};
|
|
27
28
|
/**
|
|
28
29
|
* Prefix a human message with its stable error code so logs are greppable
|
|
@@ -479,6 +480,44 @@ export class UnsupportedFeatureError extends TurbineError {
|
|
|
479
480
|
this.dialect = dialect;
|
|
480
481
|
}
|
|
481
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Thrown when a write or DDL statement is refused because the target is
|
|
485
|
+
* read-only. Two shapes reach here, both on PowDB:
|
|
486
|
+
* - an embedded database opened read-only for snapshot serving refuses a write
|
|
487
|
+
* with `readonly mode: statement requires a writer …`;
|
|
488
|
+
* - a networked read-only role refuses a write with `permission denied: role
|
|
489
|
+
* '<role>' cannot execute write statements` (translated by `wrapPowdbError`).
|
|
490
|
+
* It is also raised locally, before the wire, when a write is issued on a pool
|
|
491
|
+
* the caller marked read-only (fail-fast). The message carries the engine text
|
|
492
|
+
* plus a hint to route writes to a writable primary.
|
|
493
|
+
*
|
|
494
|
+
* NOT retryable: the same write against the same read-only target fails
|
|
495
|
+
* identically; route it to a writable primary instead.
|
|
496
|
+
*/
|
|
497
|
+
export class ReadOnlyError extends TurbineError {
|
|
498
|
+
/**
|
|
499
|
+
* Why the write was refused. `'snapshot'`: the database itself is read-only
|
|
500
|
+
* (snapshot serving, an embedded `readonly: true` open, or the client-level
|
|
501
|
+
* fail-fast flag), so NOTHING can write here and writes must route to the
|
|
502
|
+
* primary. `'rbac'`: the database is writable but THIS connection's role may
|
|
503
|
+
* not write (per-connection permission), so re-authenticating may suffice.
|
|
504
|
+
*/
|
|
505
|
+
reason;
|
|
506
|
+
/**
|
|
507
|
+
* @param detail human-readable description of the refused write (the engine
|
|
508
|
+
* message, or a local fail-fast description). A "route writes to a writable
|
|
509
|
+
* primary" hint is always appended.
|
|
510
|
+
* @param options optional driver `cause` to preserve when wrapping a refusal,
|
|
511
|
+
* and the refusal `reason` (default `'snapshot'`).
|
|
512
|
+
*/
|
|
513
|
+
constructor(detail, options) {
|
|
514
|
+
super(TurbineErrorCode.READ_ONLY, `[turbine] ${detail} Route writes to a writable primary.`, {
|
|
515
|
+
cause: options?.cause,
|
|
516
|
+
});
|
|
517
|
+
this.name = 'ReadOnlyError';
|
|
518
|
+
this.reason = options?.reason ?? 'snapshot';
|
|
519
|
+
}
|
|
520
|
+
}
|
|
482
521
|
/**
|
|
483
522
|
* Parse column names out of a pg `detail` string like:
|
|
484
523
|
* "Key (email)=(foo@bar) already exists."
|
package/dist/generate.js
CHANGED
|
@@ -161,8 +161,13 @@ export function generateTypes(schema, options) {
|
|
|
161
161
|
for (const col of table.columns) {
|
|
162
162
|
const pkNote = table.primaryKey.includes(col.name) ? ' (primary key)' : '';
|
|
163
163
|
const nullNote = col.nullable ? ' (nullable)' : '';
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
// PII columns are excluded from default projections, so the field is
|
|
165
|
+
// absent unless the query names it in `select` or passes `includePii`.
|
|
166
|
+
// The emitted type marks it optional so it tells the truth about absence.
|
|
167
|
+
const piiNote = col.pii ? ' (PII: absent unless selected or includePii)' : '';
|
|
168
|
+
const optional = col.pii ? '?' : '';
|
|
169
|
+
lines.push(` /** Column: ${col.name}, ${col.pgType}${pkNote}${nullNote}${piiNote} */`);
|
|
170
|
+
lines.push(` ${col.field}${optional}: ${columnTsType(col, schema.enums)};`);
|
|
166
171
|
}
|
|
167
172
|
lines.push('}');
|
|
168
173
|
lines.push('');
|
|
@@ -524,6 +529,17 @@ export function generateMetadata(schema, options) {
|
|
|
524
529
|
lines.push(` { name: '${escSQ(idx.name)}', columns: [${idx.columns.map((c) => `'${escSQ(c)}'`).join(', ')}], unique: ${idx.unique}, definition: ${JSON.stringify(idx.definition)} },`);
|
|
525
530
|
}
|
|
526
531
|
lines.push(' ],');
|
|
532
|
+
// checks: introspected named CHECK constraints. Emitted only when present
|
|
533
|
+
// (byte-stable for check-less tables) and sorted by name so `--no-timestamp`
|
|
534
|
+
// output is deterministic regardless of catalog row order.
|
|
535
|
+
if (table.checks && table.checks.length > 0) {
|
|
536
|
+
const sortedChecks = [...table.checks].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
537
|
+
lines.push(' checks: [');
|
|
538
|
+
for (const chk of sortedChecks) {
|
|
539
|
+
lines.push(` { name: '${escSQ(chk.name)}', expression: ${JSON.stringify(chk.expression)} },`);
|
|
540
|
+
}
|
|
541
|
+
lines.push(' ],');
|
|
542
|
+
}
|
|
527
543
|
// isView — read-only marker; the runtime write guard reads it.
|
|
528
544
|
if (table.isView)
|
|
529
545
|
lines.push(' isView: true,');
|
|
@@ -727,6 +743,11 @@ function serializeColumn(col) {
|
|
|
727
743
|
if (col.generationExpression !== undefined) {
|
|
728
744
|
parts.push(`generationExpression: '${escSQ(col.generationExpression)}'`);
|
|
729
745
|
}
|
|
746
|
+
// PII marker: emitted only when set, so untagged schemas stay byte-identical.
|
|
747
|
+
// Introspection never sets this (code-first declaration), but a metadata
|
|
748
|
+
// object built from `defineSchema` (pii: true) carries it through codegen.
|
|
749
|
+
if (col.pii)
|
|
750
|
+
parts.push(`pii: true`);
|
|
730
751
|
if (col.maxLength !== undefined)
|
|
731
752
|
parts.push(`maxLength: ${col.maxLength}`);
|
|
732
753
|
return `{ ${parts.join(', ')} }`;
|
package/dist/index.d.ts
CHANGED
|
@@ -37,19 +37,19 @@ export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapt
|
|
|
37
37
|
export { type Middleware, type MiddlewareNext, type MiddlewareParams, type PgCompatPool, type PgCompatPoolClient, type PgCompatQueryResult, type RetryOptions, TransactionClient, type TransactionOptions, TurbineClient, type TurbineConfig, type TurbineDriver, withRetry, } from './client.js';
|
|
38
38
|
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, DialectIntrospector, DialectMigrator, DialectName, InsertStatementInput, IntrospectOptions as DialectIntrospectOptions, ResultStrategy, StreamableConnection, UpsertStatementInput, } from './dialect.js';
|
|
39
39
|
export { postgresDialect } from './dialect.js';
|
|
40
|
-
export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, type ErrorMessageMode, ExclusionConstraintError, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, OptimisticLockError, PipelineError, type PipelineResultSlot, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
|
|
40
|
+
export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, type ErrorMessageMode, ExclusionConstraintError, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, OptimisticLockError, PipelineError, type PipelineResultSlot, ReadOnlyError, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
|
|
41
41
|
export { type GenerateOptions, generate } from './generate.js';
|
|
42
42
|
export { type IntrospectOptions, introspect } from './introspect.js';
|
|
43
43
|
export { executeNestedCreate, executeNestedUpdate, hasRelationFields, type NestedWriteContext, } from './nested-write.js';
|
|
44
44
|
export type { ObserveConfig, ObserveHandle } from './observe.js';
|
|
45
45
|
export { executePipeline, type PipelineOptions, type PipelineResults, pipelineSupported } from './pipeline.js';
|
|
46
|
-
export { type AggregateArgs, type AggregateResult, type ArrayFilter, 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 HavingClause, type JsonFilter, type JsonPathAggregateTarget, type JsonPathGroupKey, type JsonPathOrderBy, type MiddlewareFn, type NestedCreateOp, type NestedUpdateOp, type NestedUpdateOpItem, type NestedUpsertOpItem, type OmitResult, type OrderByClause, type OrderDirection, type QueryEvent, type QueryEventListener, QueryInterface, type QueryResult, type RelationDescriptor, type RelationFilter, 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 WithResult, } from './query/index.js';
|
|
46
|
+
export { type AggregateArgs, type AggregateResult, type ArrayFilter, 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 HavingClause, type JsonFilter, type JsonPathAggregateTarget, type JsonPathGroupKey, type JsonPathOrderBy, type MiddlewareFn, type NestedCreateOp, type NestedUpdateOp, type NestedUpdateOpItem, type NestedUpsertOpItem, type OmitResult, type OrderByClause, 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 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, ReferentialAction, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
|
|
49
49
|
export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
50
50
|
export { applyManyToManyRelations, type CheckDef, ColumnBuilder, type ColumnConfig, type ColumnDef, type ColumnIndexDef, type ColumnType, type ColumnTypeName, column, type DefineSchemaOptions, type DocFieldIndexDef, defineSchema, isDocFieldIndexDef, type ManyToManyDef, type ReferenceDef, type SchemaDef, type SchemaIndexDef, type TableDef, table, } from './schema-builder.js';
|
|
51
51
|
export { schemaDefToMetadata } from './schema-metadata.js';
|
|
52
|
-
export { type AlterColumnDef, type AlterDef, type DiffResult, type PushResult, type SchemaSqlOptions, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
52
|
+
export { type AlterColumnDef, type AlterDef, DestructivePushRefusal, type DiffResult, type PushResult, type SchemaSqlOptions, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
53
53
|
export { type DefinedSeed, defineSeed, type SeedFunction } from './seed.js';
|
|
54
54
|
export { type TurbineHttpOptions, turbineHttp } from './serverless.js';
|
|
55
55
|
export { buildTypedSql, TypedSqlQuery } from './typed-sql.js';
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapt
|
|
|
37
37
|
export { TransactionClient, TurbineClient, withRetry, } from './client.js';
|
|
38
38
|
export { postgresDialect } from './dialect.js';
|
|
39
39
|
// Error types
|
|
40
|
-
export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, ExclusionConstraintError, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, OptimisticLockError, PipelineError, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
|
|
40
|
+
export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, ExclusionConstraintError, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, OptimisticLockError, PipelineError, ReadOnlyError, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, UnsupportedFeatureError, ValidationError, wrapPgError, } from './errors.js';
|
|
41
41
|
// Code generation
|
|
42
42
|
export { generate } from './generate.js';
|
|
43
43
|
// Introspection
|
|
@@ -59,7 +59,7 @@ table, } from './schema-builder.js';
|
|
|
59
59
|
// Schema metadata bridge — defineSchema() → SchemaMetadata without a live DB
|
|
60
60
|
export { schemaDefToMetadata } from './schema-metadata.js';
|
|
61
61
|
// Schema SQL — generate DDL, diff, and push
|
|
62
|
-
export { schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
62
|
+
export { DestructivePushRefusal, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
63
63
|
// Seed helper
|
|
64
64
|
export { defineSeed } from './seed.js';
|
|
65
65
|
// Serverless / edge factory
|
package/dist/mssql.js
CHANGED
|
@@ -448,6 +448,23 @@ function mssqlColumnType(type, maxLength) {
|
|
|
448
448
|
// ---------------------------------------------------------------------------
|
|
449
449
|
// mssqlDialect — the full Dialect contract for SQL Server 2016+
|
|
450
450
|
// ---------------------------------------------------------------------------
|
|
451
|
+
/**
|
|
452
|
+
* Render the SQL Server `OUTPUT` clause for a write's returning selection.
|
|
453
|
+
* `'*'` → ` OUTPUT INSERTED.*` (byte-identical to the historical default); a
|
|
454
|
+
* quoted column list → ` OUTPUT INSERTED.[c1], INSERTED.[c2]`; each column
|
|
455
|
+
* carries its own `INSERTED.`/`DELETED.` prefix (a bare comma list is invalid
|
|
456
|
+
* T-SQL). Used to exclude PII columns from a write's returned row. Empty
|
|
457
|
+
* selection → no clause.
|
|
458
|
+
*/
|
|
459
|
+
function mssqlOutput(returning, alias) {
|
|
460
|
+
if (!returning)
|
|
461
|
+
return '';
|
|
462
|
+
if (returning === '*')
|
|
463
|
+
return ` OUTPUT ${alias}.*`;
|
|
464
|
+
if (returning.length === 0)
|
|
465
|
+
return '';
|
|
466
|
+
return ` OUTPUT ${returning.map((col) => `${alias}.${col}`).join(', ')}`;
|
|
467
|
+
}
|
|
451
468
|
/**
|
|
452
469
|
* SQL Server 2016+ implementation of the {@link Dialect} contract. Bracket
|
|
453
470
|
* identifier quoting (`[…]`), named `@pN` placeholders, the `FOR JSON PATH`
|
|
@@ -472,6 +489,11 @@ export const mssqlDialect = {
|
|
|
472
489
|
supportsLateralJoin: false,
|
|
473
490
|
// sp_getapplock / sp_releaseapplock exist (used by a future migrate adapter).
|
|
474
491
|
supportsAdvisoryLock: true,
|
|
492
|
+
// No in-band EXPLAIN: SQL Server's SHOWPLAN is a session toggle
|
|
493
|
+
// (SET SHOWPLAN_ALL ON), not a statement prefix, so a compiled query cannot
|
|
494
|
+
// be explained in one round-trip. Override the inherited Postgres `EXPLAIN`
|
|
495
|
+
// to absent → QueryInterface.explain() throws E017.
|
|
496
|
+
explainQuery: undefined,
|
|
475
497
|
// FOR JSON over zero rows is NULL → coalesced in the relation override.
|
|
476
498
|
aggSupportsInlineOrderBy: false,
|
|
477
499
|
jsonPathSupport: 'limited',
|
|
@@ -506,7 +528,7 @@ export const mssqlDialect = {
|
|
|
506
528
|
return '';
|
|
507
529
|
},
|
|
508
530
|
buildInsertStatement(input) {
|
|
509
|
-
const out = input.returning
|
|
531
|
+
const out = mssqlOutput(input.returning, 'INSERTED');
|
|
510
532
|
return `INSERT INTO ${input.table} (${input.columns.join(', ')})${out} VALUES (${input.valuePlaceholders.join(', ')})`;
|
|
511
533
|
},
|
|
512
534
|
buildBulkInsertStatement(input) {
|
|
@@ -526,7 +548,7 @@ export const mssqlDialect = {
|
|
|
526
548
|
const placeholders = input.rowValues
|
|
527
549
|
.map((row) => `(${row.map(() => this.paramPlaceholder(++n)).join(', ')})`)
|
|
528
550
|
.join(', ');
|
|
529
|
-
const out = input.returning
|
|
551
|
+
const out = mssqlOutput(input.returning, 'INSERTED');
|
|
530
552
|
// skipDuplicates has no single-statement equivalent here; ignored (documented).
|
|
531
553
|
return {
|
|
532
554
|
sql: `INSERT INTO ${input.table} (${input.columns.join(', ')})${out} VALUES ${placeholders}`,
|
|
@@ -541,7 +563,7 @@ export const mssqlDialect = {
|
|
|
541
563
|
const on = input.conflictColumns.map((c) => `T.${c} = S.${c}`).join(' AND ');
|
|
542
564
|
const insertCols = input.insertColumns.join(', ');
|
|
543
565
|
const sourceVals = input.insertColumns.map((c) => `S.${c}`).join(', ');
|
|
544
|
-
const out = input.returning
|
|
566
|
+
const out = mssqlOutput(input.returning, 'INSERTED');
|
|
545
567
|
return (`MERGE INTO ${input.table} AS T ` +
|
|
546
568
|
`USING (VALUES (${input.valuePlaceholders.join(', ')})) AS S (${insertCols}) ` +
|
|
547
569
|
`ON (${on}) ` +
|
|
@@ -552,11 +574,11 @@ export const mssqlDialect = {
|
|
|
552
574
|
// UPDATE/DELETE inject OUTPUT mid-statement (between SET and WHERE / FROM and
|
|
553
575
|
// WHERE) — a trailing clause would be invalid T-SQL.
|
|
554
576
|
buildUpdateStatement(input) {
|
|
555
|
-
const out = input.returning
|
|
577
|
+
const out = mssqlOutput(input.returning, 'INSERTED');
|
|
556
578
|
return `UPDATE ${input.table} SET ${input.setClauses.join(', ')}${out}${input.whereSql}`;
|
|
557
579
|
},
|
|
558
580
|
buildDeleteStatement(input) {
|
|
559
|
-
const out = input.returning
|
|
581
|
+
const out = mssqlOutput(input.returning, 'DELETED');
|
|
560
582
|
return `DELETE FROM ${input.table}${out}${input.whereSql}`;
|
|
561
583
|
},
|
|
562
584
|
// SQL Server has no LIMIT — emit OFFSET/FETCH, injecting a stable ORDER BY when
|
package/dist/mysql.js
CHANGED
|
@@ -373,6 +373,10 @@ export const mysqlDialect = {
|
|
|
373
373
|
supportsLateralJoin: false,
|
|
374
374
|
// GET_LOCK / RELEASE_LOCK exist (used by a future migrate adapter).
|
|
375
375
|
supportsAdvisoryLock: true,
|
|
376
|
+
// Plain `EXPLAIN` (one row of tabular plan columns) works on every supported
|
|
377
|
+
// MySQL 8.0.x; the readable `FORMAT=TREE` variant only exists from 8.0.16 and
|
|
378
|
+
// the engine floor here is 8.0.0. Plan text is a diagnostic, not a contract.
|
|
379
|
+
explainQuery: { prefix: 'EXPLAIN' },
|
|
376
380
|
// JSON_ARRAYAGG has no inline ORDER BY argument → force the inner-subquery
|
|
377
381
|
// rewrite for every ordered to-many relation.
|
|
378
382
|
aggSupportsInlineOrderBy: false,
|