@remix-run/data-table-mysql 0.3.0 → 0.4.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 +17 -2
- package/dist/lib/adapter.d.ts +12 -8
- package/dist/lib/adapter.d.ts.map +1 -1
- package/dist/lib/adapter.js +14 -394
- package/package.json +8 -5
- package/src/lib/adapter.ts +15 -475
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Use this package when you want `data-table` APIs backed by `mysql2`.
|
|
|
8
8
|
- **Native `mysql2` Integration**: Works with `mysql2/promise` `Pool` and `PoolConnection` instances
|
|
9
9
|
- **Full `data-table` API Support**: Queries, relations, writes, and transactions
|
|
10
10
|
- **Adapter-Owned Compiler**: SQL compilation lives in this adapter, with optional shared pure helpers from `data-table`
|
|
11
|
-
- **
|
|
11
|
+
- **Multi-Statement Migrations**: `executeScript()` runs `up.sql` / `down.sql` files via `mysql2` (requires `multipleStatements: true`)
|
|
12
12
|
- **MySQL Capabilities Enabled By Default**:
|
|
13
13
|
- `returning: false`
|
|
14
14
|
- `savepoints: true`
|
|
@@ -27,7 +27,7 @@ npm i remix mysql2
|
|
|
27
27
|
```ts
|
|
28
28
|
import { createPool } from 'mysql2/promise'
|
|
29
29
|
import { createDatabase } from 'remix/data-table'
|
|
30
|
-
import { createMysqlDatabaseAdapter } from 'remix/data-table
|
|
30
|
+
import { createMysqlDatabaseAdapter } from 'remix/data-table/mysql'
|
|
31
31
|
|
|
32
32
|
let pool = createPool(process.env.DATABASE_URL as string)
|
|
33
33
|
let db = createDatabase(createMysqlDatabaseAdapter(pool))
|
|
@@ -48,6 +48,21 @@ Import any driver-specific types you need directly from `mysql2/promise`.
|
|
|
48
48
|
|
|
49
49
|
## Advanced Usage
|
|
50
50
|
|
|
51
|
+
### Multi-Statement Migrations
|
|
52
|
+
|
|
53
|
+
`remix/data-table/migrations` sends each migration to the adapter as a single multi-statement SQL
|
|
54
|
+
script. mysql2 only accepts multi-statement scripts when the connection is created with
|
|
55
|
+
`multipleStatements: true`:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { createPool } from 'mysql2/promise'
|
|
59
|
+
|
|
60
|
+
let pool = createPool({
|
|
61
|
+
uri: process.env.DATABASE_URL,
|
|
62
|
+
multipleStatements: true,
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
51
66
|
### `returning` On MySQL
|
|
52
67
|
|
|
53
68
|
MySQL does not natively support SQL `RETURNING`. In this adapter, using `returning` on write
|
package/dist/lib/adapter.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DataManipulationRequest,
|
|
1
|
+
import type { DataManipulationRequest, DataManipulationResult, DataManipulationOperation, DatabaseAdapter, SqlStatement, TableRef, TransactionOptions, TransactionToken } from '@remix-run/data-table';
|
|
2
2
|
import type { Connection as MysqlConnection, Pool as MysqlPool, PoolConnection as MysqlPoolConnection } from 'mysql2/promise';
|
|
3
3
|
type MysqlTransactionConnection = MysqlConnection | MysqlPoolConnection;
|
|
4
4
|
type MysqlQueryable = MysqlPool | MysqlTransactionConnection;
|
|
@@ -23,11 +23,11 @@ export declare class MysqlDatabaseAdapter implements DatabaseAdapter {
|
|
|
23
23
|
};
|
|
24
24
|
constructor(client: MysqlQueryable);
|
|
25
25
|
/**
|
|
26
|
-
* Compiles a data
|
|
26
|
+
* Compiles a data-manipulation operation to mysql SQL statements.
|
|
27
27
|
* @param operation Operation to compile.
|
|
28
28
|
* @returns Compiled SQL statements.
|
|
29
29
|
*/
|
|
30
|
-
compileSql(operation: DataManipulationOperation
|
|
30
|
+
compileSql(operation: DataManipulationOperation): SqlStatement[];
|
|
31
31
|
/**
|
|
32
32
|
* Executes a mysql data-manipulation request.
|
|
33
33
|
* @param request Request to execute.
|
|
@@ -35,11 +35,15 @@ export declare class MysqlDatabaseAdapter implements DatabaseAdapter {
|
|
|
35
35
|
*/
|
|
36
36
|
execute(request: DataManipulationRequest): Promise<DataManipulationResult>;
|
|
37
37
|
/**
|
|
38
|
-
* Executes mysql
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* Executes a multi-statement mysql SQL script.
|
|
39
|
+
*
|
|
40
|
+
* mysql2 only accepts multi-statement scripts when the underlying connection
|
|
41
|
+
* was created with `multipleStatements: true`.
|
|
42
|
+
* @param sql SQL script to execute.
|
|
43
|
+
* @param transaction Optional transaction token.
|
|
44
|
+
* @returns A promise that resolves once execution completes.
|
|
41
45
|
*/
|
|
42
|
-
|
|
46
|
+
executeScript(sql: string, transaction?: TransactionToken): Promise<void>;
|
|
43
47
|
/**
|
|
44
48
|
* Checks whether a table exists in mysql.
|
|
45
49
|
* @param table Table reference to inspect.
|
|
@@ -114,7 +118,7 @@ export declare class MysqlDatabaseAdapter implements DatabaseAdapter {
|
|
|
114
118
|
* ```ts
|
|
115
119
|
* import { createPool } from 'mysql2/promise'
|
|
116
120
|
* import { createDatabase } from 'remix/data-table'
|
|
117
|
-
* import { createMysqlDatabaseAdapter } from 'remix/data-table
|
|
121
|
+
* import { createMysqlDatabaseAdapter } from 'remix/data-table/mysql'
|
|
118
122
|
*
|
|
119
123
|
* let pool = createPool({ uri: process.env.DATABASE_URL })
|
|
120
124
|
* let adapter = createMysqlDatabaseAdapter(pool)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/lib/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/lib/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EACV,UAAU,IAAI,eAAe,EAC7B,IAAI,IAAI,SAAS,EACjB,cAAc,IAAI,mBAAmB,EAGtC,MAAM,gBAAgB,CAAA;AAcvB,KAAK,0BAA0B,GAAG,eAAe,GAAG,mBAAmB,CAAA;AACvE,KAAK,cAAc,GAAG,SAAS,GAAG,0BAA0B,CAAA;AAE5D;;GAEG;AACH,qBAAa,oBAAqB,YAAW,eAAe;;IAC1D;;OAEG;IACH,OAAO,SAAU;IAEjB;;OAEG;IACH,YAAY;;;;;;MAAA;IAMZ,YAAY,MAAM,EAAE,cAAc,EASjC;IAED;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,yBAAyB,GAAG,YAAY,EAAE,CAG/D;IAED;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA8B/E;IAED;;;;;;;;OAQG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9E;IAED;;;;;OAKG;IACG,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAchF;IAED;;;;;;OAMG;IACG,SAAS,CACb,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAclB;IAED;;;;OAIG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgC9E;IAED;;;;OAIG;IACG,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB9D;IAED;;;;OAIG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhE;IAED;;;;;OAKG;IACG,eAAe,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1E;IAED;;;;;OAKG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9E;IAED;;;;;OAKG;IACG,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3E;IAED;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;IAED;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1C;CAmBF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,cAAc,GAAG,oBAAoB,CAEvF"}
|
package/dist/lib/adapter.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getTablePrimaryKey } from '@remix-run/data-table';
|
|
2
|
-
import { isDataManipulationOperation as isDataManipulationOperationHelper, quoteLiteral as quoteLiteralHelper, quoteTableRef as quoteTableRefHelper, } from '@remix-run/data-table/sql-helpers';
|
|
3
2
|
import { compileMysqlOperation } from "./sql-compiler.js";
|
|
4
3
|
/**
|
|
5
4
|
* `DatabaseAdapter` implementation for mysql-compatible clients.
|
|
@@ -27,16 +26,13 @@ export class MysqlDatabaseAdapter {
|
|
|
27
26
|
};
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
30
|
-
* Compiles a data
|
|
29
|
+
* Compiles a data-manipulation operation to mysql SQL statements.
|
|
31
30
|
* @param operation Operation to compile.
|
|
32
31
|
* @returns Compiled SQL statements.
|
|
33
32
|
*/
|
|
34
33
|
compileSql(operation) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return [{ text: compiled.text, values: compiled.values }];
|
|
38
|
-
}
|
|
39
|
-
return compileMysqlMigrationOperations(operation);
|
|
34
|
+
let compiled = compileMysqlOperation(operation);
|
|
35
|
+
return [{ text: compiled.text, values: compiled.values }];
|
|
40
36
|
}
|
|
41
37
|
/**
|
|
42
38
|
* Executes a mysql data-manipulation request.
|
|
@@ -69,19 +65,17 @@ export class MysqlDatabaseAdapter {
|
|
|
69
65
|
};
|
|
70
66
|
}
|
|
71
67
|
/**
|
|
72
|
-
* Executes mysql
|
|
73
|
-
*
|
|
74
|
-
*
|
|
68
|
+
* Executes a multi-statement mysql SQL script.
|
|
69
|
+
*
|
|
70
|
+
* mysql2 only accepts multi-statement scripts when the underlying connection
|
|
71
|
+
* was created with `multipleStatements: true`.
|
|
72
|
+
* @param sql SQL script to execute.
|
|
73
|
+
* @param transaction Optional transaction token.
|
|
74
|
+
* @returns A promise that resolves once execution completes.
|
|
75
75
|
*/
|
|
76
|
-
async
|
|
77
|
-
let
|
|
78
|
-
|
|
79
|
-
for (let statement of statements) {
|
|
80
|
-
await client.query(statement.text, statement.values);
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
affectedOperations: statements.length,
|
|
84
|
-
};
|
|
76
|
+
async executeScript(sql, transaction) {
|
|
77
|
+
let client = this.#resolveClient(transaction);
|
|
78
|
+
await client.query(sql);
|
|
85
79
|
}
|
|
86
80
|
/**
|
|
87
81
|
* Checks whether a table exists in mysql.
|
|
@@ -259,7 +253,7 @@ export class MysqlDatabaseAdapter {
|
|
|
259
253
|
* ```ts
|
|
260
254
|
* import { createPool } from 'mysql2/promise'
|
|
261
255
|
* import { createDatabase } from 'remix/data-table'
|
|
262
|
-
* import { createMysqlDatabaseAdapter } from 'remix/data-table
|
|
256
|
+
* import { createMysqlDatabaseAdapter } from 'remix/data-table/mysql'
|
|
263
257
|
*
|
|
264
258
|
* let pool = createPool({ uri: process.env.DATABASE_URL })
|
|
265
259
|
* let adapter = createMysqlDatabaseAdapter(pool)
|
|
@@ -342,383 +336,9 @@ function normalizeInsertId(kind, operation, header) {
|
|
|
342
336
|
function quoteIdentifier(value) {
|
|
343
337
|
return '`' + value.replace(/`/g, '``') + '`';
|
|
344
338
|
}
|
|
345
|
-
function quoteTableRef(table) {
|
|
346
|
-
return quoteTableRefHelper(table, quoteIdentifier);
|
|
347
|
-
}
|
|
348
|
-
function quoteLiteral(value) {
|
|
349
|
-
return quoteLiteralHelper(value);
|
|
350
|
-
}
|
|
351
339
|
function isInsertOperationKind(kind) {
|
|
352
340
|
return kind === 'insert' || kind === 'insertMany' || kind === 'upsert';
|
|
353
341
|
}
|
|
354
342
|
function isInsertOperation(operation) {
|
|
355
343
|
return (operation.kind === 'insert' || operation.kind === 'insertMany' || operation.kind === 'upsert');
|
|
356
344
|
}
|
|
357
|
-
function isDataManipulationOperation(operation) {
|
|
358
|
-
return isDataManipulationOperationHelper(operation);
|
|
359
|
-
}
|
|
360
|
-
function compileMysqlMigrationOperations(operation) {
|
|
361
|
-
if (operation.kind === 'raw') {
|
|
362
|
-
return [{ text: operation.sql.text, values: [...operation.sql.values] }];
|
|
363
|
-
}
|
|
364
|
-
if (operation.kind === 'createTable') {
|
|
365
|
-
let columns = Object.keys(operation.columns).map((columnName) => quoteIdentifier(columnName) + ' ' + compileMysqlColumn(operation.columns[columnName]));
|
|
366
|
-
let constraints = [];
|
|
367
|
-
if (operation.primaryKey) {
|
|
368
|
-
constraints.push('constraint ' +
|
|
369
|
-
quoteIdentifier(operation.primaryKey.name) +
|
|
370
|
-
' primary key (' +
|
|
371
|
-
operation.primaryKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
372
|
-
')');
|
|
373
|
-
}
|
|
374
|
-
for (let unique of operation.uniques ?? []) {
|
|
375
|
-
constraints.push('constraint ' +
|
|
376
|
-
quoteIdentifier(unique.name) +
|
|
377
|
-
' ' +
|
|
378
|
-
'unique (' +
|
|
379
|
-
unique.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
380
|
-
')');
|
|
381
|
-
}
|
|
382
|
-
for (let check of operation.checks ?? []) {
|
|
383
|
-
constraints.push('constraint ' + quoteIdentifier(check.name) + ' ' + 'check (' + check.expression + ')');
|
|
384
|
-
}
|
|
385
|
-
for (let foreignKey of operation.foreignKeys ?? []) {
|
|
386
|
-
let clause = 'constraint ' +
|
|
387
|
-
quoteIdentifier(foreignKey.name) +
|
|
388
|
-
' ' +
|
|
389
|
-
'foreign key (' +
|
|
390
|
-
foreignKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
391
|
-
') references ' +
|
|
392
|
-
quoteTableRef(foreignKey.references.table) +
|
|
393
|
-
' (' +
|
|
394
|
-
foreignKey.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
395
|
-
')';
|
|
396
|
-
if (foreignKey.onDelete) {
|
|
397
|
-
clause += ' on delete ' + foreignKey.onDelete;
|
|
398
|
-
}
|
|
399
|
-
if (foreignKey.onUpdate) {
|
|
400
|
-
clause += ' on update ' + foreignKey.onUpdate;
|
|
401
|
-
}
|
|
402
|
-
constraints.push(clause);
|
|
403
|
-
}
|
|
404
|
-
let sql = 'create table ' +
|
|
405
|
-
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
406
|
-
quoteTableRef(operation.table) +
|
|
407
|
-
' (' +
|
|
408
|
-
[...columns, ...constraints].join(', ') +
|
|
409
|
-
')';
|
|
410
|
-
let statements = [{ text: sql, values: [] }];
|
|
411
|
-
if (operation.comment) {
|
|
412
|
-
statements.push({
|
|
413
|
-
text: 'alter table ' +
|
|
414
|
-
quoteTableRef(operation.table) +
|
|
415
|
-
' comment = ' +
|
|
416
|
-
quoteLiteral(operation.comment),
|
|
417
|
-
values: [],
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
return statements;
|
|
421
|
-
}
|
|
422
|
-
if (operation.kind === 'alterTable') {
|
|
423
|
-
let statements = [];
|
|
424
|
-
for (let change of operation.changes) {
|
|
425
|
-
let sql = 'alter table ' + quoteTableRef(operation.table) + ' ';
|
|
426
|
-
if (change.kind === 'addColumn') {
|
|
427
|
-
sql +=
|
|
428
|
-
'add column ' +
|
|
429
|
-
quoteIdentifier(change.column) +
|
|
430
|
-
' ' +
|
|
431
|
-
compileMysqlColumn(change.definition);
|
|
432
|
-
}
|
|
433
|
-
else if (change.kind === 'changeColumn') {
|
|
434
|
-
sql +=
|
|
435
|
-
'modify column ' +
|
|
436
|
-
quoteIdentifier(change.column) +
|
|
437
|
-
' ' +
|
|
438
|
-
compileMysqlColumn(change.definition);
|
|
439
|
-
}
|
|
440
|
-
else if (change.kind === 'renameColumn') {
|
|
441
|
-
sql += 'rename column ' + quoteIdentifier(change.from) + ' to ' + quoteIdentifier(change.to);
|
|
442
|
-
}
|
|
443
|
-
else if (change.kind === 'dropColumn') {
|
|
444
|
-
sql += 'drop column ' + quoteIdentifier(change.column);
|
|
445
|
-
}
|
|
446
|
-
else if (change.kind === 'addPrimaryKey') {
|
|
447
|
-
sql +=
|
|
448
|
-
'add primary key (' +
|
|
449
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
450
|
-
')';
|
|
451
|
-
}
|
|
452
|
-
else if (change.kind === 'dropPrimaryKey') {
|
|
453
|
-
sql += 'drop primary key';
|
|
454
|
-
}
|
|
455
|
-
else if (change.kind === 'addUnique') {
|
|
456
|
-
sql +=
|
|
457
|
-
'add ' +
|
|
458
|
-
'constraint ' +
|
|
459
|
-
quoteIdentifier(change.constraint.name) +
|
|
460
|
-
' ' +
|
|
461
|
-
'unique (' +
|
|
462
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
463
|
-
')';
|
|
464
|
-
}
|
|
465
|
-
else if (change.kind === 'dropUnique') {
|
|
466
|
-
sql += 'drop index ' + quoteIdentifier(change.name);
|
|
467
|
-
}
|
|
468
|
-
else if (change.kind === 'addForeignKey') {
|
|
469
|
-
sql +=
|
|
470
|
-
'add ' +
|
|
471
|
-
'constraint ' +
|
|
472
|
-
quoteIdentifier(change.constraint.name) +
|
|
473
|
-
' ' +
|
|
474
|
-
'foreign key (' +
|
|
475
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
476
|
-
') references ' +
|
|
477
|
-
quoteTableRef(change.constraint.references.table) +
|
|
478
|
-
' (' +
|
|
479
|
-
change.constraint.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
480
|
-
')';
|
|
481
|
-
}
|
|
482
|
-
else if (change.kind === 'dropForeignKey') {
|
|
483
|
-
sql += 'drop foreign key ' + quoteIdentifier(change.name);
|
|
484
|
-
}
|
|
485
|
-
else if (change.kind === 'addCheck') {
|
|
486
|
-
sql +=
|
|
487
|
-
'add ' +
|
|
488
|
-
'constraint ' +
|
|
489
|
-
quoteIdentifier(change.constraint.name) +
|
|
490
|
-
' ' +
|
|
491
|
-
'check (' +
|
|
492
|
-
change.constraint.expression +
|
|
493
|
-
')';
|
|
494
|
-
}
|
|
495
|
-
else if (change.kind === 'dropCheck') {
|
|
496
|
-
sql += 'drop check ' + quoteIdentifier(change.name);
|
|
497
|
-
}
|
|
498
|
-
else if (change.kind === 'setTableComment') {
|
|
499
|
-
sql += 'comment = ' + quoteLiteral(change.comment);
|
|
500
|
-
}
|
|
501
|
-
else {
|
|
502
|
-
continue;
|
|
503
|
-
}
|
|
504
|
-
statements.push({ text: sql, values: [] });
|
|
505
|
-
}
|
|
506
|
-
return statements;
|
|
507
|
-
}
|
|
508
|
-
if (operation.kind === 'renameTable') {
|
|
509
|
-
return [
|
|
510
|
-
{
|
|
511
|
-
text: 'rename table ' + quoteTableRef(operation.from) + ' to ' + quoteTableRef(operation.to),
|
|
512
|
-
values: [],
|
|
513
|
-
},
|
|
514
|
-
];
|
|
515
|
-
}
|
|
516
|
-
if (operation.kind === 'dropTable') {
|
|
517
|
-
return [
|
|
518
|
-
{
|
|
519
|
-
text: 'drop table ' + (operation.ifExists ? 'if exists ' : '') + quoteTableRef(operation.table),
|
|
520
|
-
values: [],
|
|
521
|
-
},
|
|
522
|
-
];
|
|
523
|
-
}
|
|
524
|
-
if (operation.kind === 'createIndex') {
|
|
525
|
-
return [
|
|
526
|
-
{
|
|
527
|
-
text: 'create ' +
|
|
528
|
-
(operation.index.unique ? 'unique ' : '') +
|
|
529
|
-
'index ' +
|
|
530
|
-
quoteIdentifier(operation.index.name) +
|
|
531
|
-
' on ' +
|
|
532
|
-
quoteTableRef(operation.index.table) +
|
|
533
|
-
(operation.index.using ? ' using ' + operation.index.using : '') +
|
|
534
|
-
' (' +
|
|
535
|
-
operation.index.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
536
|
-
')' +
|
|
537
|
-
(operation.index.where ? ' where ' + operation.index.where : ''),
|
|
538
|
-
values: [],
|
|
539
|
-
},
|
|
540
|
-
];
|
|
541
|
-
}
|
|
542
|
-
if (operation.kind === 'dropIndex') {
|
|
543
|
-
return [
|
|
544
|
-
{
|
|
545
|
-
text: 'drop index ' + quoteIdentifier(operation.name) + ' on ' + quoteTableRef(operation.table),
|
|
546
|
-
values: [],
|
|
547
|
-
},
|
|
548
|
-
];
|
|
549
|
-
}
|
|
550
|
-
if (operation.kind === 'renameIndex') {
|
|
551
|
-
return [
|
|
552
|
-
{
|
|
553
|
-
text: 'alter table ' +
|
|
554
|
-
quoteTableRef(operation.table) +
|
|
555
|
-
' rename index ' +
|
|
556
|
-
quoteIdentifier(operation.from) +
|
|
557
|
-
' to ' +
|
|
558
|
-
quoteIdentifier(operation.to),
|
|
559
|
-
values: [],
|
|
560
|
-
},
|
|
561
|
-
];
|
|
562
|
-
}
|
|
563
|
-
if (operation.kind === 'addForeignKey') {
|
|
564
|
-
return [
|
|
565
|
-
{
|
|
566
|
-
text: 'alter table ' +
|
|
567
|
-
quoteTableRef(operation.table) +
|
|
568
|
-
' add ' +
|
|
569
|
-
'constraint ' +
|
|
570
|
-
quoteIdentifier(operation.constraint.name) +
|
|
571
|
-
' ' +
|
|
572
|
-
'foreign key (' +
|
|
573
|
-
operation.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
574
|
-
') references ' +
|
|
575
|
-
quoteTableRef(operation.constraint.references.table) +
|
|
576
|
-
' (' +
|
|
577
|
-
operation.constraint.references.columns
|
|
578
|
-
.map((column) => quoteIdentifier(column))
|
|
579
|
-
.join(', ') +
|
|
580
|
-
')' +
|
|
581
|
-
(operation.constraint.onDelete ? ' on delete ' + operation.constraint.onDelete : '') +
|
|
582
|
-
(operation.constraint.onUpdate ? ' on update ' + operation.constraint.onUpdate : ''),
|
|
583
|
-
values: [],
|
|
584
|
-
},
|
|
585
|
-
];
|
|
586
|
-
}
|
|
587
|
-
if (operation.kind === 'dropForeignKey') {
|
|
588
|
-
return [
|
|
589
|
-
{
|
|
590
|
-
text: 'alter table ' +
|
|
591
|
-
quoteTableRef(operation.table) +
|
|
592
|
-
' drop foreign key ' +
|
|
593
|
-
quoteIdentifier(operation.name),
|
|
594
|
-
values: [],
|
|
595
|
-
},
|
|
596
|
-
];
|
|
597
|
-
}
|
|
598
|
-
if (operation.kind === 'addCheck') {
|
|
599
|
-
return [
|
|
600
|
-
{
|
|
601
|
-
text: 'alter table ' +
|
|
602
|
-
quoteTableRef(operation.table) +
|
|
603
|
-
' add ' +
|
|
604
|
-
'constraint ' +
|
|
605
|
-
quoteIdentifier(operation.constraint.name) +
|
|
606
|
-
' ' +
|
|
607
|
-
'check (' +
|
|
608
|
-
operation.constraint.expression +
|
|
609
|
-
')',
|
|
610
|
-
values: [],
|
|
611
|
-
},
|
|
612
|
-
];
|
|
613
|
-
}
|
|
614
|
-
if (operation.kind === 'dropCheck') {
|
|
615
|
-
return [
|
|
616
|
-
{
|
|
617
|
-
text: 'alter table ' +
|
|
618
|
-
quoteTableRef(operation.table) +
|
|
619
|
-
' drop check ' +
|
|
620
|
-
quoteIdentifier(operation.name),
|
|
621
|
-
values: [],
|
|
622
|
-
},
|
|
623
|
-
];
|
|
624
|
-
}
|
|
625
|
-
throw new Error('Unsupported data migration operation kind');
|
|
626
|
-
}
|
|
627
|
-
function compileMysqlColumn(definition) {
|
|
628
|
-
let parts = [compileMysqlColumnType(definition)];
|
|
629
|
-
if (definition.nullable === false) {
|
|
630
|
-
parts.push('not null');
|
|
631
|
-
}
|
|
632
|
-
if (definition.default) {
|
|
633
|
-
if (definition.default.kind === 'now') {
|
|
634
|
-
parts.push('default current_timestamp');
|
|
635
|
-
}
|
|
636
|
-
else if (definition.default.kind === 'sql') {
|
|
637
|
-
parts.push('default ' + definition.default.expression);
|
|
638
|
-
}
|
|
639
|
-
else {
|
|
640
|
-
parts.push('default ' + quoteLiteral(definition.default.value));
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
if (definition.autoIncrement) {
|
|
644
|
-
parts.push('auto_increment');
|
|
645
|
-
}
|
|
646
|
-
if (definition.primaryKey) {
|
|
647
|
-
parts.push('primary key');
|
|
648
|
-
}
|
|
649
|
-
if (definition.unique) {
|
|
650
|
-
parts.push('unique');
|
|
651
|
-
}
|
|
652
|
-
if (definition.computed) {
|
|
653
|
-
parts.push('generated always as (' + definition.computed.expression + ')');
|
|
654
|
-
parts.push(definition.computed.stored ? 'stored' : 'virtual');
|
|
655
|
-
}
|
|
656
|
-
if (definition.references) {
|
|
657
|
-
let clause = 'references ' +
|
|
658
|
-
quoteTableRef(definition.references.table) +
|
|
659
|
-
' (' +
|
|
660
|
-
definition.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
661
|
-
')';
|
|
662
|
-
if (definition.references.onDelete) {
|
|
663
|
-
clause += ' on delete ' + definition.references.onDelete;
|
|
664
|
-
}
|
|
665
|
-
if (definition.references.onUpdate) {
|
|
666
|
-
clause += ' on update ' + definition.references.onUpdate;
|
|
667
|
-
}
|
|
668
|
-
parts.push(clause);
|
|
669
|
-
}
|
|
670
|
-
if (definition.checks && definition.checks.length > 0) {
|
|
671
|
-
for (let check of definition.checks) {
|
|
672
|
-
parts.push('check (' + check.expression + ')');
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
return parts.join(' ');
|
|
676
|
-
}
|
|
677
|
-
function compileMysqlColumnType(definition) {
|
|
678
|
-
if (definition.type === 'varchar') {
|
|
679
|
-
return 'varchar(' + String(definition.length ?? 255) + ')';
|
|
680
|
-
}
|
|
681
|
-
if (definition.type === 'text') {
|
|
682
|
-
return 'text';
|
|
683
|
-
}
|
|
684
|
-
if (definition.type === 'integer') {
|
|
685
|
-
return definition.unsigned ? 'int unsigned' : 'int';
|
|
686
|
-
}
|
|
687
|
-
if (definition.type === 'bigint') {
|
|
688
|
-
return definition.unsigned ? 'bigint unsigned' : 'bigint';
|
|
689
|
-
}
|
|
690
|
-
if (definition.type === 'decimal') {
|
|
691
|
-
if (definition.precision !== undefined && definition.scale !== undefined) {
|
|
692
|
-
return 'decimal(' + String(definition.precision) + ', ' + String(definition.scale) + ')';
|
|
693
|
-
}
|
|
694
|
-
return 'decimal';
|
|
695
|
-
}
|
|
696
|
-
if (definition.type === 'boolean') {
|
|
697
|
-
return 'boolean';
|
|
698
|
-
}
|
|
699
|
-
if (definition.type === 'uuid') {
|
|
700
|
-
return 'char(36)';
|
|
701
|
-
}
|
|
702
|
-
if (definition.type === 'date') {
|
|
703
|
-
return 'date';
|
|
704
|
-
}
|
|
705
|
-
if (definition.type === 'time') {
|
|
706
|
-
return 'time';
|
|
707
|
-
}
|
|
708
|
-
if (definition.type === 'timestamp') {
|
|
709
|
-
return 'timestamp';
|
|
710
|
-
}
|
|
711
|
-
if (definition.type === 'json') {
|
|
712
|
-
return 'json';
|
|
713
|
-
}
|
|
714
|
-
if (definition.type === 'binary') {
|
|
715
|
-
return 'blob';
|
|
716
|
-
}
|
|
717
|
-
if (definition.type === 'enum') {
|
|
718
|
-
if (definition.enumValues && definition.enumValues.length > 0) {
|
|
719
|
-
return 'enum(' + definition.enumValues.map((value) => quoteLiteral(value)).join(', ') + ')';
|
|
720
|
-
}
|
|
721
|
-
return 'text';
|
|
722
|
-
}
|
|
723
|
-
return 'text';
|
|
724
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/data-table-mysql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "MySQL adapter for remix/data-table",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,10 +30,12 @@
|
|
|
30
30
|
"@types/node": "^24.6.0",
|
|
31
31
|
"@typescript/native-preview": "7.0.0-dev.20251125.1",
|
|
32
32
|
"mysql2": "^3.15.3",
|
|
33
|
-
"@remix-run/
|
|
33
|
+
"@remix-run/assert": "0.2.0",
|
|
34
|
+
"@remix-run/data-table": "0.3.0",
|
|
35
|
+
"@remix-run/test": "0.4.0"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@remix-run/data-table": "^0.
|
|
38
|
+
"@remix-run/data-table": "^0.3.0"
|
|
37
39
|
},
|
|
38
40
|
"peerDependencies": {
|
|
39
41
|
"mysql2": "^3.15.3"
|
|
@@ -53,8 +55,9 @@
|
|
|
53
55
|
"scripts": {
|
|
54
56
|
"build": "tsgo -p tsconfig.build.json",
|
|
55
57
|
"clean": "git clean -fdX",
|
|
56
|
-
"test": "
|
|
57
|
-
"test:
|
|
58
|
+
"test": "remix-test",
|
|
59
|
+
"test:bun": "bun x --bun remix-test",
|
|
60
|
+
"test:coverage": "remix-test --coverage",
|
|
58
61
|
"typecheck": "tsgo --noEmit"
|
|
59
62
|
}
|
|
60
63
|
}
|
package/src/lib/adapter.ts
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DataManipulationRequest,
|
|
3
|
-
DataMigrationRequest,
|
|
4
|
-
DataMigrationResult,
|
|
5
|
-
DataMigrationOperation,
|
|
6
3
|
DataManipulationResult,
|
|
7
4
|
DataManipulationOperation,
|
|
8
5
|
DatabaseAdapter,
|
|
9
|
-
ColumnDefinition,
|
|
10
6
|
SqlStatement,
|
|
11
7
|
TableRef,
|
|
12
8
|
TransactionOptions,
|
|
13
9
|
TransactionToken,
|
|
14
10
|
} from '@remix-run/data-table'
|
|
15
11
|
import { getTablePrimaryKey } from '@remix-run/data-table'
|
|
16
|
-
import {
|
|
17
|
-
isDataManipulationOperation as isDataManipulationOperationHelper,
|
|
18
|
-
quoteLiteral as quoteLiteralHelper,
|
|
19
|
-
quoteTableRef as quoteTableRefHelper,
|
|
20
|
-
} from '@remix-run/data-table/sql-helpers'
|
|
21
12
|
import type {
|
|
22
13
|
Connection as MysqlConnection,
|
|
23
14
|
Pool as MysqlPool,
|
|
@@ -71,17 +62,13 @@ export class MysqlDatabaseAdapter implements DatabaseAdapter {
|
|
|
71
62
|
}
|
|
72
63
|
|
|
73
64
|
/**
|
|
74
|
-
* Compiles a data
|
|
65
|
+
* Compiles a data-manipulation operation to mysql SQL statements.
|
|
75
66
|
* @param operation Operation to compile.
|
|
76
67
|
* @returns Compiled SQL statements.
|
|
77
68
|
*/
|
|
78
|
-
compileSql(operation: DataManipulationOperation
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return [{ text: compiled.text, values: compiled.values }]
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return compileMysqlMigrationOperations(operation)
|
|
69
|
+
compileSql(operation: DataManipulationOperation): SqlStatement[] {
|
|
70
|
+
let compiled = compileMysqlOperation(operation)
|
|
71
|
+
return [{ text: compiled.text, values: compiled.values }]
|
|
85
72
|
}
|
|
86
73
|
|
|
87
74
|
/**
|
|
@@ -122,21 +109,17 @@ export class MysqlDatabaseAdapter implements DatabaseAdapter {
|
|
|
122
109
|
}
|
|
123
110
|
|
|
124
111
|
/**
|
|
125
|
-
* Executes mysql
|
|
126
|
-
*
|
|
127
|
-
*
|
|
112
|
+
* Executes a multi-statement mysql SQL script.
|
|
113
|
+
*
|
|
114
|
+
* mysql2 only accepts multi-statement scripts when the underlying connection
|
|
115
|
+
* was created with `multipleStatements: true`.
|
|
116
|
+
* @param sql SQL script to execute.
|
|
117
|
+
* @param transaction Optional transaction token.
|
|
118
|
+
* @returns A promise that resolves once execution completes.
|
|
128
119
|
*/
|
|
129
|
-
async
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
for (let statement of statements) {
|
|
134
|
-
await client.query(statement.text, statement.values)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
affectedOperations: statements.length,
|
|
139
|
-
}
|
|
120
|
+
async executeScript(sql: string, transaction?: TransactionToken): Promise<void> {
|
|
121
|
+
let client = this.#resolveClient(transaction)
|
|
122
|
+
await client.query(sql)
|
|
140
123
|
}
|
|
141
124
|
|
|
142
125
|
/**
|
|
@@ -350,7 +333,7 @@ export class MysqlDatabaseAdapter implements DatabaseAdapter {
|
|
|
350
333
|
* ```ts
|
|
351
334
|
* import { createPool } from 'mysql2/promise'
|
|
352
335
|
* import { createDatabase } from 'remix/data-table'
|
|
353
|
-
* import { createMysqlDatabaseAdapter } from 'remix/data-table
|
|
336
|
+
* import { createMysqlDatabaseAdapter } from 'remix/data-table/mysql'
|
|
354
337
|
*
|
|
355
338
|
* let pool = createPool({ uri: process.env.DATABASE_URL })
|
|
356
339
|
* let adapter = createMysqlDatabaseAdapter(pool)
|
|
@@ -461,14 +444,6 @@ function quoteIdentifier(value: string): string {
|
|
|
461
444
|
return '`' + value.replace(/`/g, '``') + '`'
|
|
462
445
|
}
|
|
463
446
|
|
|
464
|
-
function quoteTableRef(table: TableRef): string {
|
|
465
|
-
return quoteTableRefHelper(table, quoteIdentifier)
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
function quoteLiteral(value: unknown): string {
|
|
469
|
-
return quoteLiteralHelper(value)
|
|
470
|
-
}
|
|
471
|
-
|
|
472
447
|
function isInsertOperationKind(kind: DataManipulationRequest['operation']['kind']): boolean {
|
|
473
448
|
return kind === 'insert' || kind === 'insertMany' || kind === 'upsert'
|
|
474
449
|
}
|
|
@@ -483,438 +458,3 @@ function isInsertOperation(
|
|
|
483
458
|
operation.kind === 'insert' || operation.kind === 'insertMany' || operation.kind === 'upsert'
|
|
484
459
|
)
|
|
485
460
|
}
|
|
486
|
-
|
|
487
|
-
function isDataManipulationOperation(
|
|
488
|
-
operation: DataManipulationOperation | DataMigrationOperation,
|
|
489
|
-
): operation is DataManipulationOperation {
|
|
490
|
-
return isDataManipulationOperationHelper(operation)
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function compileMysqlMigrationOperations(operation: DataMigrationOperation): SqlStatement[] {
|
|
494
|
-
if (operation.kind === 'raw') {
|
|
495
|
-
return [{ text: operation.sql.text, values: [...operation.sql.values] }]
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (operation.kind === 'createTable') {
|
|
499
|
-
let columns = Object.keys(operation.columns).map(
|
|
500
|
-
(columnName) =>
|
|
501
|
-
quoteIdentifier(columnName) + ' ' + compileMysqlColumn(operation.columns[columnName]),
|
|
502
|
-
)
|
|
503
|
-
let constraints: string[] = []
|
|
504
|
-
|
|
505
|
-
if (operation.primaryKey) {
|
|
506
|
-
constraints.push(
|
|
507
|
-
'constraint ' +
|
|
508
|
-
quoteIdentifier(operation.primaryKey.name) +
|
|
509
|
-
' primary key (' +
|
|
510
|
-
operation.primaryKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
511
|
-
')',
|
|
512
|
-
)
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
for (let unique of operation.uniques ?? []) {
|
|
516
|
-
constraints.push(
|
|
517
|
-
'constraint ' +
|
|
518
|
-
quoteIdentifier(unique.name) +
|
|
519
|
-
' ' +
|
|
520
|
-
'unique (' +
|
|
521
|
-
unique.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
522
|
-
')',
|
|
523
|
-
)
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
for (let check of operation.checks ?? []) {
|
|
527
|
-
constraints.push(
|
|
528
|
-
'constraint ' + quoteIdentifier(check.name) + ' ' + 'check (' + check.expression + ')',
|
|
529
|
-
)
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
for (let foreignKey of operation.foreignKeys ?? []) {
|
|
533
|
-
let clause =
|
|
534
|
-
'constraint ' +
|
|
535
|
-
quoteIdentifier(foreignKey.name) +
|
|
536
|
-
' ' +
|
|
537
|
-
'foreign key (' +
|
|
538
|
-
foreignKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
539
|
-
') references ' +
|
|
540
|
-
quoteTableRef(foreignKey.references.table) +
|
|
541
|
-
' (' +
|
|
542
|
-
foreignKey.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
543
|
-
')'
|
|
544
|
-
|
|
545
|
-
if (foreignKey.onDelete) {
|
|
546
|
-
clause += ' on delete ' + foreignKey.onDelete
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
if (foreignKey.onUpdate) {
|
|
550
|
-
clause += ' on update ' + foreignKey.onUpdate
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
constraints.push(clause)
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
let sql =
|
|
557
|
-
'create table ' +
|
|
558
|
-
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
559
|
-
quoteTableRef(operation.table) +
|
|
560
|
-
' (' +
|
|
561
|
-
[...columns, ...constraints].join(', ') +
|
|
562
|
-
')'
|
|
563
|
-
|
|
564
|
-
let statements: SqlStatement[] = [{ text: sql, values: [] }]
|
|
565
|
-
|
|
566
|
-
if (operation.comment) {
|
|
567
|
-
statements.push({
|
|
568
|
-
text:
|
|
569
|
-
'alter table ' +
|
|
570
|
-
quoteTableRef(operation.table) +
|
|
571
|
-
' comment = ' +
|
|
572
|
-
quoteLiteral(operation.comment),
|
|
573
|
-
values: [],
|
|
574
|
-
})
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
return statements
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
if (operation.kind === 'alterTable') {
|
|
581
|
-
let statements: SqlStatement[] = []
|
|
582
|
-
|
|
583
|
-
for (let change of operation.changes) {
|
|
584
|
-
let sql = 'alter table ' + quoteTableRef(operation.table) + ' '
|
|
585
|
-
|
|
586
|
-
if (change.kind === 'addColumn') {
|
|
587
|
-
sql +=
|
|
588
|
-
'add column ' +
|
|
589
|
-
quoteIdentifier(change.column) +
|
|
590
|
-
' ' +
|
|
591
|
-
compileMysqlColumn(change.definition)
|
|
592
|
-
} else if (change.kind === 'changeColumn') {
|
|
593
|
-
sql +=
|
|
594
|
-
'modify column ' +
|
|
595
|
-
quoteIdentifier(change.column) +
|
|
596
|
-
' ' +
|
|
597
|
-
compileMysqlColumn(change.definition)
|
|
598
|
-
} else if (change.kind === 'renameColumn') {
|
|
599
|
-
sql += 'rename column ' + quoteIdentifier(change.from) + ' to ' + quoteIdentifier(change.to)
|
|
600
|
-
} else if (change.kind === 'dropColumn') {
|
|
601
|
-
sql += 'drop column ' + quoteIdentifier(change.column)
|
|
602
|
-
} else if (change.kind === 'addPrimaryKey') {
|
|
603
|
-
sql +=
|
|
604
|
-
'add primary key (' +
|
|
605
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
606
|
-
')'
|
|
607
|
-
} else if (change.kind === 'dropPrimaryKey') {
|
|
608
|
-
sql += 'drop primary key'
|
|
609
|
-
} else if (change.kind === 'addUnique') {
|
|
610
|
-
sql +=
|
|
611
|
-
'add ' +
|
|
612
|
-
'constraint ' +
|
|
613
|
-
quoteIdentifier(change.constraint.name) +
|
|
614
|
-
' ' +
|
|
615
|
-
'unique (' +
|
|
616
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
617
|
-
')'
|
|
618
|
-
} else if (change.kind === 'dropUnique') {
|
|
619
|
-
sql += 'drop index ' + quoteIdentifier(change.name)
|
|
620
|
-
} else if (change.kind === 'addForeignKey') {
|
|
621
|
-
sql +=
|
|
622
|
-
'add ' +
|
|
623
|
-
'constraint ' +
|
|
624
|
-
quoteIdentifier(change.constraint.name) +
|
|
625
|
-
' ' +
|
|
626
|
-
'foreign key (' +
|
|
627
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
628
|
-
') references ' +
|
|
629
|
-
quoteTableRef(change.constraint.references.table) +
|
|
630
|
-
' (' +
|
|
631
|
-
change.constraint.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
632
|
-
')'
|
|
633
|
-
} else if (change.kind === 'dropForeignKey') {
|
|
634
|
-
sql += 'drop foreign key ' + quoteIdentifier(change.name)
|
|
635
|
-
} else if (change.kind === 'addCheck') {
|
|
636
|
-
sql +=
|
|
637
|
-
'add ' +
|
|
638
|
-
'constraint ' +
|
|
639
|
-
quoteIdentifier(change.constraint.name) +
|
|
640
|
-
' ' +
|
|
641
|
-
'check (' +
|
|
642
|
-
change.constraint.expression +
|
|
643
|
-
')'
|
|
644
|
-
} else if (change.kind === 'dropCheck') {
|
|
645
|
-
sql += 'drop check ' + quoteIdentifier(change.name)
|
|
646
|
-
} else if (change.kind === 'setTableComment') {
|
|
647
|
-
sql += 'comment = ' + quoteLiteral(change.comment)
|
|
648
|
-
} else {
|
|
649
|
-
continue
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
statements.push({ text: sql, values: [] })
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
return statements
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
if (operation.kind === 'renameTable') {
|
|
659
|
-
return [
|
|
660
|
-
{
|
|
661
|
-
text:
|
|
662
|
-
'rename table ' + quoteTableRef(operation.from) + ' to ' + quoteTableRef(operation.to),
|
|
663
|
-
values: [],
|
|
664
|
-
},
|
|
665
|
-
]
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
if (operation.kind === 'dropTable') {
|
|
669
|
-
return [
|
|
670
|
-
{
|
|
671
|
-
text:
|
|
672
|
-
'drop table ' + (operation.ifExists ? 'if exists ' : '') + quoteTableRef(operation.table),
|
|
673
|
-
values: [],
|
|
674
|
-
},
|
|
675
|
-
]
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
if (operation.kind === 'createIndex') {
|
|
679
|
-
return [
|
|
680
|
-
{
|
|
681
|
-
text:
|
|
682
|
-
'create ' +
|
|
683
|
-
(operation.index.unique ? 'unique ' : '') +
|
|
684
|
-
'index ' +
|
|
685
|
-
quoteIdentifier(operation.index.name) +
|
|
686
|
-
' on ' +
|
|
687
|
-
quoteTableRef(operation.index.table) +
|
|
688
|
-
(operation.index.using ? ' using ' + operation.index.using : '') +
|
|
689
|
-
' (' +
|
|
690
|
-
operation.index.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
691
|
-
')' +
|
|
692
|
-
(operation.index.where ? ' where ' + operation.index.where : ''),
|
|
693
|
-
values: [],
|
|
694
|
-
},
|
|
695
|
-
]
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
if (operation.kind === 'dropIndex') {
|
|
699
|
-
return [
|
|
700
|
-
{
|
|
701
|
-
text:
|
|
702
|
-
'drop index ' + quoteIdentifier(operation.name) + ' on ' + quoteTableRef(operation.table),
|
|
703
|
-
values: [],
|
|
704
|
-
},
|
|
705
|
-
]
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if (operation.kind === 'renameIndex') {
|
|
709
|
-
return [
|
|
710
|
-
{
|
|
711
|
-
text:
|
|
712
|
-
'alter table ' +
|
|
713
|
-
quoteTableRef(operation.table) +
|
|
714
|
-
' rename index ' +
|
|
715
|
-
quoteIdentifier(operation.from) +
|
|
716
|
-
' to ' +
|
|
717
|
-
quoteIdentifier(operation.to),
|
|
718
|
-
values: [],
|
|
719
|
-
},
|
|
720
|
-
]
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (operation.kind === 'addForeignKey') {
|
|
724
|
-
return [
|
|
725
|
-
{
|
|
726
|
-
text:
|
|
727
|
-
'alter table ' +
|
|
728
|
-
quoteTableRef(operation.table) +
|
|
729
|
-
' add ' +
|
|
730
|
-
'constraint ' +
|
|
731
|
-
quoteIdentifier(operation.constraint.name) +
|
|
732
|
-
' ' +
|
|
733
|
-
'foreign key (' +
|
|
734
|
-
operation.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
735
|
-
') references ' +
|
|
736
|
-
quoteTableRef(operation.constraint.references.table) +
|
|
737
|
-
' (' +
|
|
738
|
-
operation.constraint.references.columns
|
|
739
|
-
.map((column) => quoteIdentifier(column))
|
|
740
|
-
.join(', ') +
|
|
741
|
-
')' +
|
|
742
|
-
(operation.constraint.onDelete ? ' on delete ' + operation.constraint.onDelete : '') +
|
|
743
|
-
(operation.constraint.onUpdate ? ' on update ' + operation.constraint.onUpdate : ''),
|
|
744
|
-
values: [],
|
|
745
|
-
},
|
|
746
|
-
]
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
if (operation.kind === 'dropForeignKey') {
|
|
750
|
-
return [
|
|
751
|
-
{
|
|
752
|
-
text:
|
|
753
|
-
'alter table ' +
|
|
754
|
-
quoteTableRef(operation.table) +
|
|
755
|
-
' drop foreign key ' +
|
|
756
|
-
quoteIdentifier(operation.name),
|
|
757
|
-
values: [],
|
|
758
|
-
},
|
|
759
|
-
]
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
if (operation.kind === 'addCheck') {
|
|
763
|
-
return [
|
|
764
|
-
{
|
|
765
|
-
text:
|
|
766
|
-
'alter table ' +
|
|
767
|
-
quoteTableRef(operation.table) +
|
|
768
|
-
' add ' +
|
|
769
|
-
'constraint ' +
|
|
770
|
-
quoteIdentifier(operation.constraint.name) +
|
|
771
|
-
' ' +
|
|
772
|
-
'check (' +
|
|
773
|
-
operation.constraint.expression +
|
|
774
|
-
')',
|
|
775
|
-
values: [],
|
|
776
|
-
},
|
|
777
|
-
]
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
if (operation.kind === 'dropCheck') {
|
|
781
|
-
return [
|
|
782
|
-
{
|
|
783
|
-
text:
|
|
784
|
-
'alter table ' +
|
|
785
|
-
quoteTableRef(operation.table) +
|
|
786
|
-
' drop check ' +
|
|
787
|
-
quoteIdentifier(operation.name),
|
|
788
|
-
values: [],
|
|
789
|
-
},
|
|
790
|
-
]
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
throw new Error('Unsupported data migration operation kind')
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
function compileMysqlColumn(definition: ColumnDefinition): string {
|
|
797
|
-
let parts = [compileMysqlColumnType(definition)]
|
|
798
|
-
|
|
799
|
-
if (definition.nullable === false) {
|
|
800
|
-
parts.push('not null')
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
if (definition.default) {
|
|
804
|
-
if (definition.default.kind === 'now') {
|
|
805
|
-
parts.push('default current_timestamp')
|
|
806
|
-
} else if (definition.default.kind === 'sql') {
|
|
807
|
-
parts.push('default ' + definition.default.expression)
|
|
808
|
-
} else {
|
|
809
|
-
parts.push('default ' + quoteLiteral(definition.default.value))
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
if (definition.autoIncrement) {
|
|
814
|
-
parts.push('auto_increment')
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
if (definition.primaryKey) {
|
|
818
|
-
parts.push('primary key')
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
if (definition.unique) {
|
|
822
|
-
parts.push('unique')
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
if (definition.computed) {
|
|
826
|
-
parts.push('generated always as (' + definition.computed.expression + ')')
|
|
827
|
-
parts.push(definition.computed.stored ? 'stored' : 'virtual')
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
if (definition.references) {
|
|
831
|
-
let clause =
|
|
832
|
-
'references ' +
|
|
833
|
-
quoteTableRef(definition.references.table) +
|
|
834
|
-
' (' +
|
|
835
|
-
definition.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
836
|
-
')'
|
|
837
|
-
|
|
838
|
-
if (definition.references.onDelete) {
|
|
839
|
-
clause += ' on delete ' + definition.references.onDelete
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (definition.references.onUpdate) {
|
|
843
|
-
clause += ' on update ' + definition.references.onUpdate
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
parts.push(clause)
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
if (definition.checks && definition.checks.length > 0) {
|
|
850
|
-
for (let check of definition.checks) {
|
|
851
|
-
parts.push('check (' + check.expression + ')')
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
return parts.join(' ')
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
function compileMysqlColumnType(definition: ColumnDefinition): string {
|
|
859
|
-
if (definition.type === 'varchar') {
|
|
860
|
-
return 'varchar(' + String(definition.length ?? 255) + ')'
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
if (definition.type === 'text') {
|
|
864
|
-
return 'text'
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
if (definition.type === 'integer') {
|
|
868
|
-
return definition.unsigned ? 'int unsigned' : 'int'
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
if (definition.type === 'bigint') {
|
|
872
|
-
return definition.unsigned ? 'bigint unsigned' : 'bigint'
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
if (definition.type === 'decimal') {
|
|
876
|
-
if (definition.precision !== undefined && definition.scale !== undefined) {
|
|
877
|
-
return 'decimal(' + String(definition.precision) + ', ' + String(definition.scale) + ')'
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
return 'decimal'
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
if (definition.type === 'boolean') {
|
|
884
|
-
return 'boolean'
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
if (definition.type === 'uuid') {
|
|
888
|
-
return 'char(36)'
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
if (definition.type === 'date') {
|
|
892
|
-
return 'date'
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
if (definition.type === 'time') {
|
|
896
|
-
return 'time'
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
if (definition.type === 'timestamp') {
|
|
900
|
-
return 'timestamp'
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
if (definition.type === 'json') {
|
|
904
|
-
return 'json'
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
if (definition.type === 'binary') {
|
|
908
|
-
return 'blob'
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
if (definition.type === 'enum') {
|
|
912
|
-
if (definition.enumValues && definition.enumValues.length > 0) {
|
|
913
|
-
return 'enum(' + definition.enumValues.map((value) => quoteLiteral(value)).join(', ') + ')'
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
return 'text'
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
return 'text'
|
|
920
|
-
}
|