turbine-orm 0.11.0 → 0.13.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/dist/cjs/cli/migrate.js +25 -28
- package/dist/cjs/dialect.js +74 -0
- package/dist/cjs/query/builder.js +30 -23
- package/dist/cjs/schema-sql.js +64 -61
- package/dist/cli/migrate.d.ts +6 -1
- package/dist/cli/migrate.js +25 -28
- package/dist/dialect.d.ts +107 -0
- package/dist/dialect.js +74 -0
- package/dist/index.d.ts +2 -2
- package/dist/query/builder.js +30 -23
- package/dist/query/index.d.ts +1 -1
- package/dist/schema-sql.d.ts +7 -2
- package/dist/schema-sql.js +64 -61
- package/package.json +3 -3
package/dist/cli/migrate.js
CHANGED
|
@@ -16,27 +16,21 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from
|
|
|
16
16
|
import { join } from 'node:path';
|
|
17
17
|
import pg from 'pg';
|
|
18
18
|
import { postgresql } from '../adapters/index.js';
|
|
19
|
+
import { postgresDialect } from '../dialect.js';
|
|
19
20
|
import { MigrationError } from '../errors.js';
|
|
20
|
-
import { quoteIdent } from '../query/index.js';
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
22
|
// Tracking table management
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
const TRACKING_TABLE = '_turbine_migrations';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
checksum TEXT NOT NULL,
|
|
31
|
-
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
32
|
-
);
|
|
33
|
-
`;
|
|
34
|
-
async function ensureTrackingTable(client) {
|
|
35
|
-
await client.query(CREATE_TRACKING_TABLE);
|
|
25
|
+
function quotedTrackingTable(dialect) {
|
|
26
|
+
return dialect.quoteIdentifier(TRACKING_TABLE);
|
|
27
|
+
}
|
|
28
|
+
async function ensureTrackingTable(client, dialect = postgresDialect) {
|
|
29
|
+
await client.query(dialect.buildMigrationTrackingTable(quotedTrackingTable(dialect)));
|
|
36
30
|
}
|
|
37
|
-
async function getAppliedMigrations(client) {
|
|
38
|
-
await ensureTrackingTable(client);
|
|
39
|
-
const result = await client.query(
|
|
31
|
+
async function getAppliedMigrations(client, dialect = postgresDialect) {
|
|
32
|
+
await ensureTrackingTable(client, dialect);
|
|
33
|
+
const result = await client.query(dialect.buildMigrationSelectApplied(quotedTrackingTable(dialect)));
|
|
40
34
|
return result.rows;
|
|
41
35
|
}
|
|
42
36
|
// ---------------------------------------------------------------------------
|
|
@@ -248,8 +242,8 @@ async function releaseLock(client, lockId, adapter) {
|
|
|
248
242
|
* Validate that applied migration files have not been modified or deleted since they were run.
|
|
249
243
|
* Returns an array of mismatched migrations (empty if all are clean).
|
|
250
244
|
*/
|
|
251
|
-
async function validateChecksums(client, migrationsDir) {
|
|
252
|
-
const applied = await getAppliedMigrations(client);
|
|
245
|
+
async function validateChecksums(client, migrationsDir, dialect = postgresDialect) {
|
|
246
|
+
const applied = await getAppliedMigrations(client, dialect);
|
|
253
247
|
const allFiles = listMigrationFiles(migrationsDir);
|
|
254
248
|
const fileMap = new Map(allFiles.map((f) => [f.name, f]));
|
|
255
249
|
const mismatches = [];
|
|
@@ -269,7 +263,7 @@ async function validateChecksums(client, migrationsDir) {
|
|
|
269
263
|
if (currentHash !== migration.checksum) {
|
|
270
264
|
// Auto-upgrade legacy djb2 checksums to SHA-256 without flagging as modified
|
|
271
265
|
if (isLegacyChecksum(migration.checksum)) {
|
|
272
|
-
await client.query(
|
|
266
|
+
await client.query(dialect.buildMigrationUpdateChecksum(quotedTrackingTable(dialect)), [
|
|
273
267
|
currentHash,
|
|
274
268
|
migration.name,
|
|
275
269
|
]);
|
|
@@ -304,6 +298,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
304
298
|
await client.connect();
|
|
305
299
|
// Treat `force` as an alias for `allowDrift` for backwards compatibility.
|
|
306
300
|
const allowDrift = options?.allowDrift === true || options?.force === true;
|
|
301
|
+
const dialect = options?.dialect ?? postgresDialect;
|
|
307
302
|
try {
|
|
308
303
|
// Derive an advisory lock ID per-database so concurrent migrations in
|
|
309
304
|
// sibling databases on the same Postgres cluster do not contend.
|
|
@@ -317,7 +312,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
317
312
|
throw new MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
|
|
318
313
|
}
|
|
319
314
|
try {
|
|
320
|
-
await ensureTrackingTable(client);
|
|
315
|
+
await ensureTrackingTable(client, dialect);
|
|
321
316
|
// Validate checksums of already-applied migrations.
|
|
322
317
|
// Drift = an APPLIED migration's on-disk file has changed (or been deleted)
|
|
323
318
|
// since it was run. Either situation means the database state and the
|
|
@@ -325,7 +320,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
325
320
|
// Users can pass `allowDrift: true` (CLI: `--allow-drift`) to force past
|
|
326
321
|
// the block when they are intentionally rewriting history.
|
|
327
322
|
if (!allowDrift) {
|
|
328
|
-
const mismatches = await validateChecksums(client, migrationsDir);
|
|
323
|
+
const mismatches = await validateChecksums(client, migrationsDir, dialect);
|
|
329
324
|
if (mismatches.length > 0) {
|
|
330
325
|
const modified = mismatches.filter((m) => m.type === 'modified');
|
|
331
326
|
const missing = mismatches.filter((m) => m.type === 'missing');
|
|
@@ -349,7 +344,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
349
344
|
throw new MigrationError(lines.join('\n'));
|
|
350
345
|
}
|
|
351
346
|
}
|
|
352
|
-
const applied = await getAppliedMigrations(client);
|
|
347
|
+
const applied = await getAppliedMigrations(client, dialect);
|
|
353
348
|
const appliedNames = new Set(applied.map((m) => m.name));
|
|
354
349
|
const allFiles = listMigrationFiles(migrationsDir);
|
|
355
350
|
let pending = allFiles.filter((f) => !appliedNames.has(f.name));
|
|
@@ -369,7 +364,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
369
364
|
try {
|
|
370
365
|
await client.query('BEGIN');
|
|
371
366
|
await client.query(up);
|
|
372
|
-
await client.query(
|
|
367
|
+
await client.query(dialect.buildMigrationInsertApplied(quotedTrackingTable(dialect)), [file.name, hash]);
|
|
373
368
|
await client.query('COMMIT');
|
|
374
369
|
results.push(file);
|
|
375
370
|
}
|
|
@@ -402,6 +397,7 @@ export async function migrateUp(connectionString, migrationsDir, options) {
|
|
|
402
397
|
export async function migrateDown(connectionString, migrationsDir, options) {
|
|
403
398
|
const client = new pg.Client({ connectionString });
|
|
404
399
|
await client.connect();
|
|
400
|
+
const dialect = options?.dialect ?? postgresDialect;
|
|
405
401
|
try {
|
|
406
402
|
// Derive a per-database advisory lock ID so concurrent migrations in
|
|
407
403
|
// sibling databases on the same cluster do not contend.
|
|
@@ -413,8 +409,8 @@ export async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
413
409
|
throw new MigrationError('[turbine] Could not acquire migration lock — another migration is already running');
|
|
414
410
|
}
|
|
415
411
|
try {
|
|
416
|
-
await ensureTrackingTable(client);
|
|
417
|
-
const applied = await getAppliedMigrations(client);
|
|
412
|
+
await ensureTrackingTable(client, dialect);
|
|
413
|
+
const applied = await getAppliedMigrations(client, dialect);
|
|
418
414
|
if (applied.length === 0) {
|
|
419
415
|
return { rolledBack: [], errors: [] };
|
|
420
416
|
}
|
|
@@ -441,7 +437,7 @@ export async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
441
437
|
try {
|
|
442
438
|
await client.query('BEGIN');
|
|
443
439
|
await client.query(down);
|
|
444
|
-
await client.query(
|
|
440
|
+
await client.query(dialect.buildMigrationDeleteApplied(quotedTrackingTable(dialect)), [migration.name]);
|
|
445
441
|
await client.query('COMMIT');
|
|
446
442
|
results.push(file);
|
|
447
443
|
}
|
|
@@ -466,12 +462,13 @@ export async function migrateDown(connectionString, migrationsDir, options) {
|
|
|
466
462
|
* Get the status of all migrations (applied vs pending).
|
|
467
463
|
* Includes checksum validation for applied migrations.
|
|
468
464
|
*/
|
|
469
|
-
export async function migrateStatus(connectionString, migrationsDir) {
|
|
465
|
+
export async function migrateStatus(connectionString, migrationsDir, options) {
|
|
470
466
|
const client = new pg.Client({ connectionString });
|
|
471
467
|
await client.connect();
|
|
468
|
+
const dialect = options?.dialect ?? postgresDialect;
|
|
472
469
|
try {
|
|
473
|
-
await ensureTrackingTable(client);
|
|
474
|
-
const applied = await getAppliedMigrations(client);
|
|
470
|
+
await ensureTrackingTable(client, dialect);
|
|
471
|
+
const applied = await getAppliedMigrations(client, dialect);
|
|
475
472
|
const appliedMap = new Map(applied.map((m) => [m.name, m]));
|
|
476
473
|
const allFiles = listMigrationFiles(migrationsDir);
|
|
477
474
|
return allFiles.map((file) => {
|
package/dist/dialect.d.ts
CHANGED
|
@@ -7,6 +7,85 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { SchemaMetadata } from './schema.js';
|
|
9
9
|
export type DialectName = 'postgresql' | 'mysql' | 'sqlite' | (string & {});
|
|
10
|
+
export interface InsertStatementInput {
|
|
11
|
+
/** SQL-ready quoted table name. */
|
|
12
|
+
table: string;
|
|
13
|
+
/** SQL-ready quoted insert columns. */
|
|
14
|
+
columns: string[];
|
|
15
|
+
/** SQL-ready parameter placeholders/expressions for VALUES. */
|
|
16
|
+
valuePlaceholders: string[];
|
|
17
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
18
|
+
returning?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface BulkInsertStatementInput {
|
|
21
|
+
/** SQL-ready quoted table name. */
|
|
22
|
+
table: string;
|
|
23
|
+
/** SQL-ready quoted insert columns. */
|
|
24
|
+
columns: string[];
|
|
25
|
+
/** Row-major values, one inner array per inserted row. */
|
|
26
|
+
rowValues: unknown[][];
|
|
27
|
+
/** Optional SQL-ready array casts for dialects that batch by column arrays (PostgreSQL UNNEST). */
|
|
28
|
+
columnArrayTypes?: string[];
|
|
29
|
+
/** Skip duplicate rows when supported by the dialect. */
|
|
30
|
+
skipDuplicates?: boolean;
|
|
31
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
32
|
+
returning?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface BuiltStatement {
|
|
35
|
+
sql: string;
|
|
36
|
+
params: unknown[];
|
|
37
|
+
}
|
|
38
|
+
export interface UpsertStatementInput {
|
|
39
|
+
/** SQL-ready quoted table name. */
|
|
40
|
+
table: string;
|
|
41
|
+
/** SQL-ready quoted insert columns. */
|
|
42
|
+
insertColumns: string[];
|
|
43
|
+
/** SQL-ready parameter placeholders/expressions for VALUES. */
|
|
44
|
+
valuePlaceholders: string[];
|
|
45
|
+
/** SQL-ready quoted conflict/unique columns. */
|
|
46
|
+
conflictColumns: string[];
|
|
47
|
+
/** SQL-ready update SET clauses. */
|
|
48
|
+
updateSetClauses: string[];
|
|
49
|
+
/** Optional SQL-ready RETURNING selection. */
|
|
50
|
+
returning?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface ColumnTypeInput {
|
|
53
|
+
/** Schema-builder column type name (PostgreSQL-native in the root package). */
|
|
54
|
+
type: string;
|
|
55
|
+
/** Optional VARCHAR length. */
|
|
56
|
+
maxLength?: number | null;
|
|
57
|
+
}
|
|
58
|
+
export interface ColumnDefinitionInput extends ColumnTypeInput {
|
|
59
|
+
/** SQL-ready quoted column name. */
|
|
60
|
+
name: string;
|
|
61
|
+
/** Whether this column is a single-column primary key. */
|
|
62
|
+
primaryKey?: boolean;
|
|
63
|
+
/** Whether this column is unique. Ignored when primaryKey is true. */
|
|
64
|
+
unique?: boolean;
|
|
65
|
+
/** Whether this column is NOT NULL. */
|
|
66
|
+
notNull?: boolean;
|
|
67
|
+
/** SQL-ready default expression. */
|
|
68
|
+
defaultValue?: string;
|
|
69
|
+
/** SQL-ready REFERENCES clause without the leading REFERENCES keyword. */
|
|
70
|
+
references?: {
|
|
71
|
+
table: string;
|
|
72
|
+
column: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export interface CreateTableStatementInput {
|
|
76
|
+
/** SQL-ready quoted table name. */
|
|
77
|
+
table: string;
|
|
78
|
+
/** SQL-ready column and table constraints. */
|
|
79
|
+
definitions: string[];
|
|
80
|
+
}
|
|
81
|
+
export interface CreateIndexStatementInput {
|
|
82
|
+
/** SQL-ready quoted index name. */
|
|
83
|
+
name: string;
|
|
84
|
+
/** SQL-ready quoted table name. */
|
|
85
|
+
table: string;
|
|
86
|
+
/** SQL-ready quoted index columns. */
|
|
87
|
+
columns: string[];
|
|
88
|
+
}
|
|
10
89
|
export interface Dialect {
|
|
11
90
|
/** Dialect identifier. */
|
|
12
91
|
readonly name: DialectName;
|
|
@@ -26,6 +105,14 @@ export interface Dialect {
|
|
|
26
105
|
buildJsonArrayAgg(jsonObjectExpr: string, orderBy?: string): string;
|
|
27
106
|
/** Whether INSERT/UPDATE/DELETE support RETURNING rows. */
|
|
28
107
|
readonly supportsReturning: boolean;
|
|
108
|
+
/** Build a dialect-specific RETURNING clause. Return an empty string when unsupported. */
|
|
109
|
+
buildReturningClause(selection?: string): string;
|
|
110
|
+
/** Build a single-row INSERT statement. Inputs are SQL-ready quoted fragments. */
|
|
111
|
+
buildInsertStatement(input: InsertStatementInput): string;
|
|
112
|
+
/** Build a multi-row bulk INSERT statement and its dialect-shaped params. */
|
|
113
|
+
buildBulkInsertStatement(input: BulkInsertStatementInput): BuiltStatement;
|
|
114
|
+
/** Build an upsert statement. Inputs are SQL-ready quoted fragments. */
|
|
115
|
+
buildUpsertStatement(input: UpsertStatementInput): string;
|
|
29
116
|
/** Whether native ILIKE is supported. */
|
|
30
117
|
readonly supportsILike: boolean;
|
|
31
118
|
/** Build a case-insensitive LIKE equivalent. */
|
|
@@ -42,6 +129,26 @@ export interface Dialect {
|
|
|
42
129
|
typeToTypeScript(dialectType: string, nullable: boolean): string;
|
|
43
130
|
/** Optional array-cast hook for bulk insert implementations. */
|
|
44
131
|
arrayType?(baseType: string): string;
|
|
132
|
+
/** Map a schema-builder column type to dialect DDL. */
|
|
133
|
+
buildColumnType(input: ColumnTypeInput): string;
|
|
134
|
+
/** Build a column definition line for CREATE/ALTER TABLE. */
|
|
135
|
+
buildColumnDefinition(input: ColumnDefinitionInput): string;
|
|
136
|
+
/** Build a table-level PRIMARY KEY constraint. */
|
|
137
|
+
buildPrimaryKeyConstraint(columns: string[]): string;
|
|
138
|
+
/** Build a CREATE TABLE statement from SQL-ready definitions. */
|
|
139
|
+
buildCreateTableStatement(input: CreateTableStatementInput): string;
|
|
140
|
+
/** Build a CREATE INDEX statement. */
|
|
141
|
+
buildCreateIndexStatement(input: CreateIndexStatementInput): string;
|
|
142
|
+
/** Build the migration tracking table DDL. */
|
|
143
|
+
buildMigrationTrackingTable(table: string): string;
|
|
144
|
+
/** Build the query that reads applied migrations. */
|
|
145
|
+
buildMigrationSelectApplied(table: string): string;
|
|
146
|
+
/** Build the query that updates an applied migration checksum. */
|
|
147
|
+
buildMigrationUpdateChecksum(table: string): string;
|
|
148
|
+
/** Build the query that records an applied migration. */
|
|
149
|
+
buildMigrationInsertApplied(table: string): string;
|
|
150
|
+
/** Build the query that deletes a rolled-back migration record. */
|
|
151
|
+
buildMigrationDeleteApplied(table: string): string;
|
|
45
152
|
}
|
|
46
153
|
export interface DialectIntrospector {
|
|
47
154
|
introspect(options: IntrospectOptions): Promise<SchemaMetadata>;
|
package/dist/dialect.js
CHANGED
|
@@ -30,6 +30,28 @@ export const postgresDialect = {
|
|
|
30
30
|
const suffix = orderBy ? ` ${orderBy}` : '';
|
|
31
31
|
return `COALESCE(json_agg(${jsonObjectExpr}${suffix}), ${this.emptyJsonArrayLiteral})`;
|
|
32
32
|
},
|
|
33
|
+
buildReturningClause(selection = '*') {
|
|
34
|
+
return ` RETURNING ${selection}`;
|
|
35
|
+
},
|
|
36
|
+
buildInsertStatement(input) {
|
|
37
|
+
return `INSERT INTO ${input.table} (${input.columns.join(', ')}) VALUES (${input.valuePlaceholders.join(', ')})${this.buildReturningClause(input.returning)}`;
|
|
38
|
+
},
|
|
39
|
+
buildBulkInsertStatement(input) {
|
|
40
|
+
if (!input.columnArrayTypes || input.columnArrayTypes.length !== input.columns.length) {
|
|
41
|
+
throw new Error('PostgreSQL bulk insert requires one array type per column');
|
|
42
|
+
}
|
|
43
|
+
const columnArrays = input.columns.map((_, columnIndex) => input.rowValues.map((row) => row[columnIndex]));
|
|
44
|
+
const unnestArgs = input.columns.map((_, i) => `${this.paramPlaceholder(i + 1)}::${input.columnArrayTypes[i]}`);
|
|
45
|
+
let sql = `INSERT INTO ${input.table} (${input.columns.join(', ')}) SELECT * FROM UNNEST(${unnestArgs.join(', ')})`;
|
|
46
|
+
if (input.skipDuplicates)
|
|
47
|
+
sql += ' ON CONFLICT DO NOTHING';
|
|
48
|
+
return { sql: `${sql}${this.buildReturningClause(input.returning)}`, params: columnArrays };
|
|
49
|
+
},
|
|
50
|
+
buildUpsertStatement(input) {
|
|
51
|
+
return (`INSERT INTO ${input.table} (${input.insertColumns.join(', ')}) VALUES (${input.valuePlaceholders.join(', ')})` +
|
|
52
|
+
` ON CONFLICT (${input.conflictColumns.join(', ')}) DO UPDATE SET ${input.updateSetClauses.join(', ')}` +
|
|
53
|
+
this.buildReturningClause(input.returning));
|
|
54
|
+
},
|
|
33
55
|
buildInsensitiveLike(column, paramRef) {
|
|
34
56
|
return `${column} ILIKE ${paramRef}`;
|
|
35
57
|
},
|
|
@@ -51,5 +73,57 @@ export const postgresDialect = {
|
|
|
51
73
|
// This hook is the package boundary MySQL/SQLite implementations will fill.
|
|
52
74
|
return 'unknown';
|
|
53
75
|
},
|
|
76
|
+
buildColumnType(input) {
|
|
77
|
+
if (input.type === 'VARCHAR' && input.maxLength != null) {
|
|
78
|
+
return `VARCHAR(${input.maxLength})`;
|
|
79
|
+
}
|
|
80
|
+
return input.type;
|
|
81
|
+
},
|
|
82
|
+
buildColumnDefinition(input) {
|
|
83
|
+
const parts = [input.name, this.buildColumnType(input)];
|
|
84
|
+
if (input.primaryKey)
|
|
85
|
+
parts.push('PRIMARY KEY');
|
|
86
|
+
if (input.unique && !input.primaryKey)
|
|
87
|
+
parts.push('UNIQUE');
|
|
88
|
+
if (input.notNull)
|
|
89
|
+
parts.push('NOT NULL');
|
|
90
|
+
if (input.defaultValue != null)
|
|
91
|
+
parts.push(`DEFAULT ${input.defaultValue}`);
|
|
92
|
+
if (input.references)
|
|
93
|
+
parts.push(`REFERENCES ${input.references.table}(${input.references.column})`);
|
|
94
|
+
return parts.join(' ');
|
|
95
|
+
},
|
|
96
|
+
buildPrimaryKeyConstraint(columns) {
|
|
97
|
+
return `PRIMARY KEY (${columns.join(', ')})`;
|
|
98
|
+
},
|
|
99
|
+
buildCreateTableStatement(input) {
|
|
100
|
+
const body = input.definitions.map((d) => ` ${d}`).join(',\n');
|
|
101
|
+
return `CREATE TABLE ${input.table} (\n${body}\n);`;
|
|
102
|
+
},
|
|
103
|
+
buildCreateIndexStatement(input) {
|
|
104
|
+
return `CREATE INDEX ${input.name} ON ${input.table}(${input.columns.join(', ')});`;
|
|
105
|
+
},
|
|
106
|
+
buildMigrationTrackingTable(table) {
|
|
107
|
+
return `
|
|
108
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
109
|
+
id SERIAL PRIMARY KEY,
|
|
110
|
+
name TEXT NOT NULL UNIQUE,
|
|
111
|
+
checksum TEXT NOT NULL,
|
|
112
|
+
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
113
|
+
);
|
|
114
|
+
`;
|
|
115
|
+
},
|
|
116
|
+
buildMigrationSelectApplied(table) {
|
|
117
|
+
return `SELECT id, name, applied_at, checksum FROM ${table} ORDER BY id ASC`;
|
|
118
|
+
},
|
|
119
|
+
buildMigrationUpdateChecksum(table) {
|
|
120
|
+
return `UPDATE ${table} SET checksum = ${this.paramPlaceholder(1)} WHERE name = ${this.paramPlaceholder(2)}`;
|
|
121
|
+
},
|
|
122
|
+
buildMigrationInsertApplied(table) {
|
|
123
|
+
return `INSERT INTO ${table} (name, checksum) VALUES (${this.paramPlaceholder(1)}, ${this.paramPlaceholder(2)}) ON CONFLICT (name) DO NOTHING`;
|
|
124
|
+
},
|
|
125
|
+
buildMigrationDeleteApplied(table) {
|
|
126
|
+
return `DELETE FROM ${table} WHERE name = ${this.paramPlaceholder(1)}`;
|
|
127
|
+
},
|
|
54
128
|
};
|
|
55
129
|
//# sourceMappingURL=dialect.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
export type { DatabaseAdapter, IntrospectionOverrides } from './adapters/index.js';
|
|
36
36
|
export { alloydb, cockroachdb, postgresql, timescale, yugabytedb } from './adapters/index.js';
|
|
37
37
|
export { type Middleware, type MiddlewareNext, type MiddlewareParams, type PgCompatPool, type PgCompatPoolClient, type PgCompatQueryResult, TransactionClient, type TransactionOptions, TurbineClient, type TurbineConfig, } from './client.js';
|
|
38
|
-
export type { Dialect, DialectIntrospector, DialectMigrator, DialectName, IntrospectOptions as DialectIntrospectOptions, } from './dialect.js';
|
|
38
|
+
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, DialectIntrospector, DialectMigrator, DialectName, InsertStatementInput, IntrospectOptions as DialectIntrospectOptions, UpsertStatementInput, } from './dialect.js';
|
|
39
39
|
export { postgresDialect } from './dialect.js';
|
|
40
40
|
export { CheckConstraintError, CircularRelationError, ConnectionError, DeadlockError, type ErrorMessageMode, ForeignKeyError, getErrorMessageMode, MigrationError, NotFoundError, NotNullViolationError, PipelineError, type PipelineResultSlot, RelationError, SerializationFailureError, setErrorMessageMode, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, ValidationError, wrapPgError, } from './errors.js';
|
|
41
41
|
export { type GenerateOptions, generate } from './generate.js';
|
|
@@ -45,6 +45,6 @@ export { type AggregateArgs, type AggregateResult, type ArrayFilter, type CountA
|
|
|
45
45
|
export type { ColumnMetadata, IndexMetadata, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
|
|
46
46
|
export { camelToSnake, isDateType, normalizeKeyColumns, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
|
|
47
47
|
export { ColumnBuilder, type ColumnConfig, type ColumnDef, type ColumnType, type ColumnTypeName, column, defineSchema, type SchemaDef, type TableDef, table, } from './schema-builder.js';
|
|
48
|
-
export { type AlterColumnDef, type AlterDef, type DiffResult, type PushResult, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
48
|
+
export { type AlterColumnDef, type AlterDef, type DiffResult, type PushResult, type SchemaSqlOptions, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
|
|
49
49
|
export { type TurbineHttpOptions, turbineHttp } from './serverless.js';
|
|
50
50
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/query/builder.js
CHANGED
|
@@ -733,7 +733,12 @@ export class QueryInterface {
|
|
|
733
733
|
const columns = entries.map(([k]) => this.toSqlColumn(k));
|
|
734
734
|
const params = entries.map(([, v]) => v);
|
|
735
735
|
const placeholders = entries.map((_, i) => `${this.p(i + 1)}`);
|
|
736
|
-
const sql =
|
|
736
|
+
const sql = this.dialect.buildInsertStatement({
|
|
737
|
+
table: this.q(this.table),
|
|
738
|
+
columns,
|
|
739
|
+
valuePlaceholders: placeholders,
|
|
740
|
+
returning: '*',
|
|
741
|
+
});
|
|
737
742
|
return {
|
|
738
743
|
sql,
|
|
739
744
|
params,
|
|
@@ -773,27 +778,24 @@ export class QueryInterface {
|
|
|
773
778
|
}
|
|
774
779
|
const keys = Object.keys(args.data[0]).filter((k) => args.data[0][k] !== undefined);
|
|
775
780
|
const columns = keys.map((k) => this.toColumn(k));
|
|
776
|
-
|
|
777
|
-
const columnArrays = keys.map(() => []);
|
|
778
|
-
for (const row of args.data) {
|
|
781
|
+
const rowValues = args.data.map((row) => {
|
|
779
782
|
const record = row;
|
|
780
|
-
keys.
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
// Use actual Postgres types for array casts
|
|
783
|
+
return keys.map((key) => record[key]);
|
|
784
|
+
});
|
|
785
|
+
// Use actual Postgres types for array casts in the default PostgreSQL dialect.
|
|
785
786
|
const typeCasts = columns.map((col) => this.getColumnArrayType(col));
|
|
786
|
-
const unnestArgs = columnArrays.map((_, i) => `${this.p(i + 1)}::${typeCasts[i]}`);
|
|
787
787
|
const quotedColumns = columns.map((c) => this.q(c));
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
788
|
+
const built = this.dialect.buildBulkInsertStatement({
|
|
789
|
+
table: qt,
|
|
790
|
+
columns: quotedColumns,
|
|
791
|
+
rowValues,
|
|
792
|
+
columnArrayTypes: typeCasts,
|
|
793
|
+
skipDuplicates: args.skipDuplicates,
|
|
794
|
+
returning: '*',
|
|
795
|
+
});
|
|
794
796
|
return {
|
|
795
|
-
sql,
|
|
796
|
-
params:
|
|
797
|
+
sql: built.sql,
|
|
798
|
+
params: built.params,
|
|
797
799
|
transform: (result) => result.rows.map((row) => this.parseRow(row, this.table)),
|
|
798
800
|
tag: `${this.table}.createMany`,
|
|
799
801
|
};
|
|
@@ -822,7 +824,7 @@ export class QueryInterface {
|
|
|
822
824
|
const whereClause = this.buildWhereClause(whereObj, freshParams);
|
|
823
825
|
const whereSql = whereClause ? ` WHERE ${whereClause}` : '';
|
|
824
826
|
this.assertMutationHasPredicate('update', whereSql, args.allowFullTableScan);
|
|
825
|
-
return `UPDATE ${this.q(this.table)} SET ${setClauses.join(', ')}${whereSql}
|
|
827
|
+
return `UPDATE ${this.q(this.table)} SET ${setClauses.join(', ')}${whereSql}${this.dialect.buildReturningClause('*')}`;
|
|
826
828
|
});
|
|
827
829
|
// On cache hit, validate predicate
|
|
828
830
|
if (whereFp === '') {
|
|
@@ -871,7 +873,7 @@ export class QueryInterface {
|
|
|
871
873
|
const clause = this.buildWhereClause(whereObj, freshParams);
|
|
872
874
|
const whereSql = clause ? ` WHERE ${clause}` : '';
|
|
873
875
|
this.assertMutationHasPredicate('delete', whereSql, args.allowFullTableScan);
|
|
874
|
-
return `DELETE FROM ${this.q(this.table)}${whereSql}
|
|
876
|
+
return `DELETE FROM ${this.q(this.table)}${whereSql}${this.dialect.buildReturningClause('*')}`;
|
|
875
877
|
});
|
|
876
878
|
// On cache hit, still validate the predicate
|
|
877
879
|
if (whereFp === '') {
|
|
@@ -925,9 +927,14 @@ export class QueryInterface {
|
|
|
925
927
|
});
|
|
926
928
|
const updateParams = updateEntries.map(([, v]) => v);
|
|
927
929
|
const params = [...createParams, ...updateParams];
|
|
928
|
-
const sql =
|
|
929
|
-
|
|
930
|
-
|
|
930
|
+
const sql = this.dialect.buildUpsertStatement({
|
|
931
|
+
table: this.q(this.table),
|
|
932
|
+
insertColumns: columns,
|
|
933
|
+
valuePlaceholders: placeholders,
|
|
934
|
+
conflictColumns,
|
|
935
|
+
updateSetClauses: setClauses,
|
|
936
|
+
returning: '*',
|
|
937
|
+
});
|
|
931
938
|
return {
|
|
932
939
|
sql,
|
|
933
940
|
params,
|
package/dist/query/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* former monolithic `import { … } from './query.js'`.
|
|
7
7
|
*/
|
|
8
8
|
export type { AggregateArgs, AggregateResult, ArrayFilter, CountArgs, CreateArgs, CreateManyArgs, DeleteArgs, DeleteManyArgs, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, JsonFilter, OrderDirection, RelationDescriptor, RelationFilter, TypedWithClause, UpdateArgs, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithResult, } from './types.js';
|
|
9
|
-
export type { Dialect } from '../dialect.js';
|
|
9
|
+
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, InsertStatementInput, UpsertStatementInput, } from '../dialect.js';
|
|
10
10
|
export { postgresDialect } from '../dialect.js';
|
|
11
11
|
export type { SqlCacheEntry } from './utils.js';
|
|
12
12
|
export { buildCorrelation, escapeLike, escSingleQuote, fnv1a64Hex, LRUCache, OPERATOR_KEYS, quoteIdent, sqlToPreparedName, } from './utils.js';
|
package/dist/schema-sql.d.ts
CHANGED
|
@@ -4,14 +4,19 @@
|
|
|
4
4
|
* Converts a SchemaDef (from defineSchema) into executable DDL statements.
|
|
5
5
|
* Also provides diff and push commands for syncing schema to a live database.
|
|
6
6
|
*/
|
|
7
|
+
import { type Dialect } from './dialect.js';
|
|
7
8
|
import type { SchemaDef, TableDef } from './schema-builder.js';
|
|
9
|
+
export interface SchemaSqlOptions {
|
|
10
|
+
/** SQL dialect used for DDL generation. Defaults to PostgreSQL. */
|
|
11
|
+
dialect?: Dialect;
|
|
12
|
+
}
|
|
8
13
|
/**
|
|
9
14
|
* Convert a SchemaDef into an ordered array of SQL DDL statements.
|
|
10
15
|
*
|
|
11
16
|
* Returns CREATE TABLE statements (in dependency order based on references)
|
|
12
17
|
* followed by CREATE INDEX statements for foreign key columns.
|
|
13
18
|
*/
|
|
14
|
-
export declare function schemaToSQL(schema: SchemaDef): string[];
|
|
19
|
+
export declare function schemaToSQL(schema: SchemaDef, options?: SchemaSqlOptions): string[];
|
|
15
20
|
export interface AlterColumnDef {
|
|
16
21
|
/** Column name in snake_case */
|
|
17
22
|
column: string;
|
|
@@ -71,5 +76,5 @@ export declare function schemaPush(schema: SchemaDef, connectionString: string,
|
|
|
71
76
|
* Generate the full DDL as a single formatted string.
|
|
72
77
|
* Useful for printing or saving to a .sql file.
|
|
73
78
|
*/
|
|
74
|
-
export declare function schemaToSQLString(schema: SchemaDef): string;
|
|
79
|
+
export declare function schemaToSQLString(schema: SchemaDef, options?: SchemaSqlOptions): string;
|
|
75
80
|
//# sourceMappingURL=schema-sql.d.ts.map
|