@remix-run/data-table-sqlite 0.4.0 → 0.5.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 +4 -4
- package/dist/lib/adapter.d.ts +11 -10
- package/dist/lib/adapter.d.ts.map +1 -1
- package/dist/lib/adapter.js +19 -386
- package/package.json +5 -5
- package/src/lib/adapter.ts +23 -461
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Use this package when you want `data-table` APIs backed by a synchronous SQLite
|
|
|
8
8
|
- **Native Runtime SQLite Support**: Works with Node's `node:sqlite` `DatabaseSync`, Bun's `bun:sqlite` `Database`, and compatible synchronous SQLite clients
|
|
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 `Database.exec()`
|
|
12
12
|
- **SQLite Capabilities Enabled By Default**:
|
|
13
13
|
- `returning: true`
|
|
14
14
|
- `savepoints: true`
|
|
@@ -29,7 +29,7 @@ npm i remix
|
|
|
29
29
|
```ts
|
|
30
30
|
import { DatabaseSync } from 'node:sqlite'
|
|
31
31
|
import { createDatabase } from 'remix/data-table'
|
|
32
|
-
import { createSqliteDatabaseAdapter } from 'remix/data-table
|
|
32
|
+
import { createSqliteDatabaseAdapter } from 'remix/data-table/sqlite'
|
|
33
33
|
|
|
34
34
|
let sqlite = new DatabaseSync('app.db')
|
|
35
35
|
let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
@@ -40,7 +40,7 @@ let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
|
40
40
|
```ts
|
|
41
41
|
import { Database } from 'bun:sqlite'
|
|
42
42
|
import { createDatabase } from 'remix/data-table'
|
|
43
|
-
import { createSqliteDatabaseAdapter } from 'remix/data-table
|
|
43
|
+
import { createSqliteDatabaseAdapter } from 'remix/data-table/sqlite'
|
|
44
44
|
|
|
45
45
|
let sqlite = new Database('app.db')
|
|
46
46
|
let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
|
@@ -66,7 +66,7 @@ Import any driver-specific types you need directly from your runtime's SQLite mo
|
|
|
66
66
|
```ts
|
|
67
67
|
import { DatabaseSync } from 'node:sqlite'
|
|
68
68
|
import { createDatabase } from 'remix/data-table'
|
|
69
|
-
import { createSqliteDatabaseAdapter } from 'remix/data-table
|
|
69
|
+
import { createSqliteDatabaseAdapter } from 'remix/data-table/sqlite'
|
|
70
70
|
|
|
71
71
|
let sqlite = new DatabaseSync(':memory:')
|
|
72
72
|
let db = createDatabase(createSqliteDatabaseAdapter(sqlite))
|
package/dist/lib/adapter.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { DataManipulationRequest,
|
|
1
|
+
import type { DataManipulationRequest, DataManipulationResult, DataManipulationOperation, DatabaseAdapter, SqlStatement, TableRef, TransactionOptions, TransactionToken } from '@remix-run/data-table';
|
|
2
2
|
/**
|
|
3
3
|
* Synchronous SQLite database client accepted by the sqlite adapter.
|
|
4
4
|
*
|
|
5
5
|
* This matches the shared surface of Node's `node:sqlite` `DatabaseSync`, Bun's `bun:sqlite`
|
|
6
|
-
* `Database`, and
|
|
6
|
+
* `Database`, and compatible synchronous SQLite clients.
|
|
7
7
|
*/
|
|
8
8
|
export interface SqliteDatabase {
|
|
9
9
|
prepare(sql: string): SqliteStatement;
|
|
@@ -24,7 +24,7 @@ export interface SqliteStatement {
|
|
|
24
24
|
* SQLite write execution metadata.
|
|
25
25
|
*/
|
|
26
26
|
export interface SqliteRunResult {
|
|
27
|
-
changes: number;
|
|
27
|
+
changes: number | bigint;
|
|
28
28
|
lastInsertRowid: unknown;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
@@ -48,11 +48,11 @@ export declare class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
48
48
|
};
|
|
49
49
|
constructor(database: SqliteDatabase);
|
|
50
50
|
/**
|
|
51
|
-
* Compiles a data
|
|
51
|
+
* Compiles a data-manipulation operation to sqlite SQL statements.
|
|
52
52
|
* @param operation Operation to compile.
|
|
53
53
|
* @returns Compiled SQL statements.
|
|
54
54
|
*/
|
|
55
|
-
compileSql(operation: DataManipulationOperation
|
|
55
|
+
compileSql(operation: DataManipulationOperation): SqlStatement[];
|
|
56
56
|
/**
|
|
57
57
|
* Executes a sqlite data-manipulation request.
|
|
58
58
|
* @param request Request to execute.
|
|
@@ -60,11 +60,12 @@ export declare class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
60
60
|
*/
|
|
61
61
|
execute(request: DataManipulationRequest): Promise<DataManipulationResult>;
|
|
62
62
|
/**
|
|
63
|
-
* Executes sqlite
|
|
64
|
-
* @param
|
|
65
|
-
* @
|
|
63
|
+
* Executes a multi-statement sqlite SQL script.
|
|
64
|
+
* @param sql SQL script to execute.
|
|
65
|
+
* @param transaction Optional transaction token (asserted when present).
|
|
66
|
+
* @returns A promise that resolves once execution completes.
|
|
66
67
|
*/
|
|
67
|
-
|
|
68
|
+
executeScript(sql: string, transaction?: TransactionToken): Promise<void>;
|
|
68
69
|
/**
|
|
69
70
|
* Checks whether a table exists in sqlite.
|
|
70
71
|
* @param table Table reference to inspect.
|
|
@@ -128,7 +129,7 @@ export declare class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
128
129
|
* ```ts
|
|
129
130
|
* import { DatabaseSync } from 'node:sqlite'
|
|
130
131
|
* import { createDatabase } from 'remix/data-table'
|
|
131
|
-
* import { createSqliteDatabaseAdapter } from 'remix/data-table
|
|
132
|
+
* import { createSqliteDatabaseAdapter } from 'remix/data-table/sqlite'
|
|
132
133
|
*
|
|
133
134
|
* let sqlite = new DatabaseSync('./data/app.db')
|
|
134
135
|
* let adapter = createSqliteDatabaseAdapter(sqlite)
|
|
@@ -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;AAK9B;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAA;IACrC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;IACpC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAA;IAClC,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAA;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,OAAO,EAAE,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;IACxB,eAAe,EAAE,OAAO,CAAA;CACzB;AAED;;GAEG;AACH,qBAAa,qBAAsB,YAAW,eAAe;;IAC3D;;OAEG;IACH,OAAO,SAAW;IAElB;;OAEG;IACH,YAAY;;;;;;MAAA;IAMZ,YAAY,QAAQ,EAAE,cAAc,EASnC;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,CAiC/E;IAED;;;;;OAKG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9E;IAED;;;;;OAKG;IACG,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAahF;IAED;;;;;;OAMG;IACG,SAAS,CACb,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAYlB;IAED;;;;OAIG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAY9E;IAED;;;;OAIG;IACG,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9D;IAED;;;;OAIG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhE;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;CAOF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,qBAAqB,CAE3F"}
|
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 { compileSqliteOperation } from "./sql-compiler.js";
|
|
4
3
|
/**
|
|
5
4
|
* `DatabaseAdapter` implementation for synchronous SQLite clients.
|
|
@@ -27,16 +26,13 @@ export class SqliteDatabaseAdapter {
|
|
|
27
26
|
};
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
30
|
-
* Compiles a data
|
|
29
|
+
* Compiles a data-manipulation operation to sqlite 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 compileSqliteMigrationOperations(operation);
|
|
34
|
+
let compiled = compileSqliteOperation(operation);
|
|
35
|
+
return [{ text: compiled.text, values: compiled.values }];
|
|
40
36
|
}
|
|
41
37
|
/**
|
|
42
38
|
* Executes a sqlite data-manipulation request.
|
|
@@ -53,8 +49,9 @@ export class SqliteDatabaseAdapter {
|
|
|
53
49
|
}
|
|
54
50
|
let statement = this.compileSql(request.operation)[0];
|
|
55
51
|
let prepared = this.#database.prepare(statement.text);
|
|
52
|
+
let values = normalizeStatementValues(statement.values);
|
|
56
53
|
if (shouldReadStatement(request.operation, prepared)) {
|
|
57
|
-
let rows = normalizeRows(prepared.all(...
|
|
54
|
+
let rows = normalizeRows(prepared.all(...values));
|
|
58
55
|
if (request.operation.kind === 'count' || request.operation.kind === 'exists') {
|
|
59
56
|
rows = normalizeCountRows(rows);
|
|
60
57
|
}
|
|
@@ -64,26 +61,23 @@ export class SqliteDatabaseAdapter {
|
|
|
64
61
|
insertId: normalizeInsertIdForReader(request.operation.kind, request.operation, rows),
|
|
65
62
|
};
|
|
66
63
|
}
|
|
67
|
-
let result = prepared.run(...
|
|
64
|
+
let result = prepared.run(...values);
|
|
68
65
|
return {
|
|
69
66
|
affectedRows: normalizeAffectedRowsForRun(request.operation.kind, result),
|
|
70
67
|
insertId: normalizeInsertIdForRun(request.operation.kind, request.operation, result),
|
|
71
68
|
};
|
|
72
69
|
}
|
|
73
70
|
/**
|
|
74
|
-
* Executes sqlite
|
|
75
|
-
* @param
|
|
76
|
-
* @
|
|
71
|
+
* Executes a multi-statement sqlite SQL script.
|
|
72
|
+
* @param sql SQL script to execute.
|
|
73
|
+
* @param transaction Optional transaction token (asserted when present).
|
|
74
|
+
* @returns A promise that resolves once execution completes.
|
|
77
75
|
*/
|
|
78
|
-
async
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
let prepared = this.#database.prepare(statement.text);
|
|
82
|
-
prepared.run(...statement.values);
|
|
76
|
+
async executeScript(sql, transaction) {
|
|
77
|
+
if (transaction) {
|
|
78
|
+
this.#assertTransaction(transaction);
|
|
83
79
|
}
|
|
84
|
-
|
|
85
|
-
affectedOperations: statements.length,
|
|
86
|
-
};
|
|
80
|
+
this.#database.exec(sql);
|
|
87
81
|
}
|
|
88
82
|
/**
|
|
89
83
|
* Checks whether a table exists in sqlite.
|
|
@@ -197,7 +191,7 @@ export class SqliteDatabaseAdapter {
|
|
|
197
191
|
* ```ts
|
|
198
192
|
* import { DatabaseSync } from 'node:sqlite'
|
|
199
193
|
* import { createDatabase } from 'remix/data-table'
|
|
200
|
-
* import { createSqliteDatabaseAdapter } from 'remix/data-table
|
|
194
|
+
* import { createSqliteDatabaseAdapter } from 'remix/data-table/sqlite'
|
|
201
195
|
*
|
|
202
196
|
* let sqlite = new DatabaseSync('./data/app.db')
|
|
203
197
|
* let adapter = createSqliteDatabaseAdapter(sqlite)
|
|
@@ -215,6 +209,9 @@ function normalizeRows(rows) {
|
|
|
215
209
|
return { ...row };
|
|
216
210
|
});
|
|
217
211
|
}
|
|
212
|
+
function normalizeStatementValues(values) {
|
|
213
|
+
return values.map((value) => (value === undefined ? null : value));
|
|
214
|
+
}
|
|
218
215
|
function normalizeCountRows(rows) {
|
|
219
216
|
return rows.map((row) => {
|
|
220
217
|
let count = row.count;
|
|
@@ -258,7 +255,7 @@ function normalizeAffectedRowsForRun(kind, result) {
|
|
|
258
255
|
if (kind === 'select' || kind === 'count' || kind === 'exists') {
|
|
259
256
|
return undefined;
|
|
260
257
|
}
|
|
261
|
-
return result.changes;
|
|
258
|
+
return Number(result.changes);
|
|
262
259
|
}
|
|
263
260
|
function normalizeInsertIdForRun(kind, operation, result) {
|
|
264
261
|
if (!isInsertOperationKind(kind) || !isInsertOperation(operation)) {
|
|
@@ -292,12 +289,6 @@ function shouldReadStatement(operation, statement) {
|
|
|
292
289
|
function quoteIdentifier(value) {
|
|
293
290
|
return '"' + value.replace(/"/g, '""') + '"';
|
|
294
291
|
}
|
|
295
|
-
function quoteTableRef(table) {
|
|
296
|
-
return quoteTableRefHelper(table, quoteIdentifier);
|
|
297
|
-
}
|
|
298
|
-
function quoteLiteral(value) {
|
|
299
|
-
return quoteLiteralHelper(value, { booleansAsIntegers: true });
|
|
300
|
-
}
|
|
301
292
|
function isWriteOperationKind(kind) {
|
|
302
293
|
return (kind === 'insert' ||
|
|
303
294
|
kind === 'insertMany' ||
|
|
@@ -311,361 +302,3 @@ function isInsertOperationKind(kind) {
|
|
|
311
302
|
function isInsertOperation(operation) {
|
|
312
303
|
return (operation.kind === 'insert' || operation.kind === 'insertMany' || operation.kind === 'upsert');
|
|
313
304
|
}
|
|
314
|
-
function isDataManipulationOperation(operation) {
|
|
315
|
-
return isDataManipulationOperationHelper(operation);
|
|
316
|
-
}
|
|
317
|
-
function compileSqliteMigrationOperations(operation) {
|
|
318
|
-
if (operation.kind === 'raw') {
|
|
319
|
-
return [{ text: operation.sql.text, values: [...operation.sql.values] }];
|
|
320
|
-
}
|
|
321
|
-
if (operation.kind === 'createTable') {
|
|
322
|
-
let columns = Object.keys(operation.columns).map((columnName) => quoteIdentifier(columnName) + ' ' + compileSqliteColumn(operation.columns[columnName]));
|
|
323
|
-
let constraints = [];
|
|
324
|
-
if (operation.primaryKey) {
|
|
325
|
-
constraints.push('constraint ' +
|
|
326
|
-
quoteIdentifier(operation.primaryKey.name) +
|
|
327
|
-
' primary key (' +
|
|
328
|
-
operation.primaryKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
329
|
-
')');
|
|
330
|
-
}
|
|
331
|
-
for (let unique of operation.uniques ?? []) {
|
|
332
|
-
constraints.push('constraint ' +
|
|
333
|
-
quoteIdentifier(unique.name) +
|
|
334
|
-
' ' +
|
|
335
|
-
'unique (' +
|
|
336
|
-
unique.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
337
|
-
')');
|
|
338
|
-
}
|
|
339
|
-
for (let check of operation.checks ?? []) {
|
|
340
|
-
constraints.push('constraint ' + quoteIdentifier(check.name) + ' ' + 'check (' + check.expression + ')');
|
|
341
|
-
}
|
|
342
|
-
for (let foreignKey of operation.foreignKeys ?? []) {
|
|
343
|
-
let clause = 'constraint ' +
|
|
344
|
-
quoteIdentifier(foreignKey.name) +
|
|
345
|
-
' ' +
|
|
346
|
-
'foreign key (' +
|
|
347
|
-
foreignKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
348
|
-
') references ' +
|
|
349
|
-
quoteTableRef(foreignKey.references.table) +
|
|
350
|
-
' (' +
|
|
351
|
-
foreignKey.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
352
|
-
')';
|
|
353
|
-
if (foreignKey.onDelete) {
|
|
354
|
-
clause += ' on delete ' + foreignKey.onDelete;
|
|
355
|
-
}
|
|
356
|
-
if (foreignKey.onUpdate) {
|
|
357
|
-
clause += ' on update ' + foreignKey.onUpdate;
|
|
358
|
-
}
|
|
359
|
-
constraints.push(clause);
|
|
360
|
-
}
|
|
361
|
-
return [
|
|
362
|
-
{
|
|
363
|
-
text: 'create table ' +
|
|
364
|
-
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
365
|
-
quoteTableRef(operation.table) +
|
|
366
|
-
' (' +
|
|
367
|
-
[...columns, ...constraints].join(', ') +
|
|
368
|
-
')',
|
|
369
|
-
values: [],
|
|
370
|
-
},
|
|
371
|
-
];
|
|
372
|
-
}
|
|
373
|
-
if (operation.kind === 'alterTable') {
|
|
374
|
-
let statements = [];
|
|
375
|
-
for (let change of operation.changes) {
|
|
376
|
-
let sql = 'alter table ' + quoteTableRef(operation.table) + ' ';
|
|
377
|
-
if (change.kind === 'addColumn') {
|
|
378
|
-
sql +=
|
|
379
|
-
'add column ' +
|
|
380
|
-
quoteIdentifier(change.column) +
|
|
381
|
-
' ' +
|
|
382
|
-
compileSqliteColumn(change.definition);
|
|
383
|
-
}
|
|
384
|
-
else if (change.kind === 'changeColumn') {
|
|
385
|
-
sql +=
|
|
386
|
-
'alter column ' +
|
|
387
|
-
quoteIdentifier(change.column) +
|
|
388
|
-
' type ' +
|
|
389
|
-
compileSqliteColumnType(change.definition);
|
|
390
|
-
}
|
|
391
|
-
else if (change.kind === 'renameColumn') {
|
|
392
|
-
sql += 'rename column ' + quoteIdentifier(change.from) + ' to ' + quoteIdentifier(change.to);
|
|
393
|
-
}
|
|
394
|
-
else if (change.kind === 'dropColumn') {
|
|
395
|
-
sql += 'drop column ' + quoteIdentifier(change.column);
|
|
396
|
-
}
|
|
397
|
-
else if (change.kind === 'addPrimaryKey') {
|
|
398
|
-
sql +=
|
|
399
|
-
'add primary key (' +
|
|
400
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
401
|
-
')';
|
|
402
|
-
}
|
|
403
|
-
else if (change.kind === 'dropPrimaryKey') {
|
|
404
|
-
sql += 'drop primary key';
|
|
405
|
-
}
|
|
406
|
-
else if (change.kind === 'addUnique') {
|
|
407
|
-
sql +=
|
|
408
|
-
'add ' +
|
|
409
|
-
'constraint ' +
|
|
410
|
-
quoteIdentifier(change.constraint.name) +
|
|
411
|
-
' ' +
|
|
412
|
-
'unique (' +
|
|
413
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
414
|
-
')';
|
|
415
|
-
}
|
|
416
|
-
else if (change.kind === 'dropUnique') {
|
|
417
|
-
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
418
|
-
}
|
|
419
|
-
else if (change.kind === 'addForeignKey') {
|
|
420
|
-
sql +=
|
|
421
|
-
'add ' +
|
|
422
|
-
'constraint ' +
|
|
423
|
-
quoteIdentifier(change.constraint.name) +
|
|
424
|
-
' ' +
|
|
425
|
-
'foreign key (' +
|
|
426
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
427
|
-
') references ' +
|
|
428
|
-
quoteTableRef(change.constraint.references.table) +
|
|
429
|
-
' (' +
|
|
430
|
-
change.constraint.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
431
|
-
')';
|
|
432
|
-
}
|
|
433
|
-
else if (change.kind === 'dropForeignKey') {
|
|
434
|
-
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
435
|
-
}
|
|
436
|
-
else if (change.kind === 'addCheck') {
|
|
437
|
-
sql +=
|
|
438
|
-
'add ' +
|
|
439
|
-
'constraint ' +
|
|
440
|
-
quoteIdentifier(change.constraint.name) +
|
|
441
|
-
' ' +
|
|
442
|
-
'check (' +
|
|
443
|
-
change.constraint.expression +
|
|
444
|
-
')';
|
|
445
|
-
}
|
|
446
|
-
else if (change.kind === 'dropCheck') {
|
|
447
|
-
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
448
|
-
}
|
|
449
|
-
else if (change.kind === 'setTableComment') {
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
|
-
statements.push({ text: sql, values: [] });
|
|
456
|
-
}
|
|
457
|
-
return statements;
|
|
458
|
-
}
|
|
459
|
-
if (operation.kind === 'renameTable') {
|
|
460
|
-
return [
|
|
461
|
-
{
|
|
462
|
-
text: 'alter table ' +
|
|
463
|
-
quoteTableRef(operation.from) +
|
|
464
|
-
' rename to ' +
|
|
465
|
-
quoteIdentifier(operation.to.name),
|
|
466
|
-
values: [],
|
|
467
|
-
},
|
|
468
|
-
];
|
|
469
|
-
}
|
|
470
|
-
if (operation.kind === 'dropTable') {
|
|
471
|
-
return [
|
|
472
|
-
{
|
|
473
|
-
text: 'drop table ' + (operation.ifExists ? 'if exists ' : '') + quoteTableRef(operation.table),
|
|
474
|
-
values: [],
|
|
475
|
-
},
|
|
476
|
-
];
|
|
477
|
-
}
|
|
478
|
-
if (operation.kind === 'createIndex') {
|
|
479
|
-
return [
|
|
480
|
-
{
|
|
481
|
-
text: 'create ' +
|
|
482
|
-
(operation.index.unique ? 'unique ' : '') +
|
|
483
|
-
'index ' +
|
|
484
|
-
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
485
|
-
quoteIdentifier(operation.index.name) +
|
|
486
|
-
' on ' +
|
|
487
|
-
quoteTableRef(operation.index.table) +
|
|
488
|
-
' (' +
|
|
489
|
-
operation.index.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
490
|
-
')' +
|
|
491
|
-
(operation.index.where ? ' where ' + operation.index.where : ''),
|
|
492
|
-
values: [],
|
|
493
|
-
},
|
|
494
|
-
];
|
|
495
|
-
}
|
|
496
|
-
if (operation.kind === 'dropIndex') {
|
|
497
|
-
return [
|
|
498
|
-
{
|
|
499
|
-
text: 'drop index ' +
|
|
500
|
-
(operation.ifExists ? 'if exists ' : '') +
|
|
501
|
-
quoteIdentifier(operation.name),
|
|
502
|
-
values: [],
|
|
503
|
-
},
|
|
504
|
-
];
|
|
505
|
-
}
|
|
506
|
-
if (operation.kind === 'renameIndex') {
|
|
507
|
-
return [
|
|
508
|
-
{
|
|
509
|
-
text: 'alter table ' +
|
|
510
|
-
quoteTableRef(operation.table) +
|
|
511
|
-
' rename index ' +
|
|
512
|
-
quoteIdentifier(operation.from) +
|
|
513
|
-
' to ' +
|
|
514
|
-
quoteIdentifier(operation.to),
|
|
515
|
-
values: [],
|
|
516
|
-
},
|
|
517
|
-
];
|
|
518
|
-
}
|
|
519
|
-
if (operation.kind === 'addForeignKey') {
|
|
520
|
-
return [
|
|
521
|
-
{
|
|
522
|
-
text: 'alter table ' +
|
|
523
|
-
quoteTableRef(operation.table) +
|
|
524
|
-
' add ' +
|
|
525
|
-
'constraint ' +
|
|
526
|
-
quoteIdentifier(operation.constraint.name) +
|
|
527
|
-
' ' +
|
|
528
|
-
'foreign key (' +
|
|
529
|
-
operation.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
530
|
-
') references ' +
|
|
531
|
-
quoteTableRef(operation.constraint.references.table) +
|
|
532
|
-
' (' +
|
|
533
|
-
operation.constraint.references.columns
|
|
534
|
-
.map((column) => quoteIdentifier(column))
|
|
535
|
-
.join(', ') +
|
|
536
|
-
')' +
|
|
537
|
-
(operation.constraint.onDelete ? ' on delete ' + operation.constraint.onDelete : '') +
|
|
538
|
-
(operation.constraint.onUpdate ? ' on update ' + operation.constraint.onUpdate : ''),
|
|
539
|
-
values: [],
|
|
540
|
-
},
|
|
541
|
-
];
|
|
542
|
-
}
|
|
543
|
-
if (operation.kind === 'dropForeignKey') {
|
|
544
|
-
return [
|
|
545
|
-
{
|
|
546
|
-
text: 'alter table ' +
|
|
547
|
-
quoteTableRef(operation.table) +
|
|
548
|
-
' drop constraint ' +
|
|
549
|
-
quoteIdentifier(operation.name),
|
|
550
|
-
values: [],
|
|
551
|
-
},
|
|
552
|
-
];
|
|
553
|
-
}
|
|
554
|
-
if (operation.kind === 'addCheck') {
|
|
555
|
-
return [
|
|
556
|
-
{
|
|
557
|
-
text: 'alter table ' +
|
|
558
|
-
quoteTableRef(operation.table) +
|
|
559
|
-
' add ' +
|
|
560
|
-
'constraint ' +
|
|
561
|
-
quoteIdentifier(operation.constraint.name) +
|
|
562
|
-
' ' +
|
|
563
|
-
'check (' +
|
|
564
|
-
operation.constraint.expression +
|
|
565
|
-
')',
|
|
566
|
-
values: [],
|
|
567
|
-
},
|
|
568
|
-
];
|
|
569
|
-
}
|
|
570
|
-
if (operation.kind === 'dropCheck') {
|
|
571
|
-
return [
|
|
572
|
-
{
|
|
573
|
-
text: 'alter table ' +
|
|
574
|
-
quoteTableRef(operation.table) +
|
|
575
|
-
' drop constraint ' +
|
|
576
|
-
quoteIdentifier(operation.name),
|
|
577
|
-
values: [],
|
|
578
|
-
},
|
|
579
|
-
];
|
|
580
|
-
}
|
|
581
|
-
throw new Error('Unsupported data migration operation kind');
|
|
582
|
-
}
|
|
583
|
-
function compileSqliteColumn(definition) {
|
|
584
|
-
let parts = [compileSqliteColumnType(definition)];
|
|
585
|
-
if (definition.nullable === false) {
|
|
586
|
-
parts.push('not null');
|
|
587
|
-
}
|
|
588
|
-
if (definition.default) {
|
|
589
|
-
if (definition.default.kind === 'now') {
|
|
590
|
-
parts.push('default current_timestamp');
|
|
591
|
-
}
|
|
592
|
-
else if (definition.default.kind === 'sql') {
|
|
593
|
-
parts.push('default ' + definition.default.expression);
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
parts.push('default ' + quoteLiteral(definition.default.value));
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
if (definition.primaryKey) {
|
|
600
|
-
parts.push('primary key');
|
|
601
|
-
}
|
|
602
|
-
if (definition.unique) {
|
|
603
|
-
parts.push('unique');
|
|
604
|
-
}
|
|
605
|
-
if (definition.computed) {
|
|
606
|
-
parts.push('generated always as (' + definition.computed.expression + ')');
|
|
607
|
-
parts.push(definition.computed.stored ? 'stored' : 'virtual');
|
|
608
|
-
}
|
|
609
|
-
if (definition.references) {
|
|
610
|
-
let clause = 'references ' +
|
|
611
|
-
quoteTableRef(definition.references.table) +
|
|
612
|
-
' (' +
|
|
613
|
-
definition.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
614
|
-
')';
|
|
615
|
-
if (definition.references.onDelete) {
|
|
616
|
-
clause += ' on delete ' + definition.references.onDelete;
|
|
617
|
-
}
|
|
618
|
-
if (definition.references.onUpdate) {
|
|
619
|
-
clause += ' on update ' + definition.references.onUpdate;
|
|
620
|
-
}
|
|
621
|
-
parts.push(clause);
|
|
622
|
-
}
|
|
623
|
-
if (definition.checks && definition.checks.length > 0) {
|
|
624
|
-
for (let check of definition.checks) {
|
|
625
|
-
parts.push('check (' + check.expression + ')');
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
return parts.join(' ');
|
|
629
|
-
}
|
|
630
|
-
function compileSqliteColumnType(definition) {
|
|
631
|
-
if (definition.type === 'varchar') {
|
|
632
|
-
return 'text';
|
|
633
|
-
}
|
|
634
|
-
if (definition.type === 'text') {
|
|
635
|
-
return 'text';
|
|
636
|
-
}
|
|
637
|
-
if (definition.type === 'integer') {
|
|
638
|
-
return 'integer';
|
|
639
|
-
}
|
|
640
|
-
if (definition.type === 'bigint') {
|
|
641
|
-
return 'integer';
|
|
642
|
-
}
|
|
643
|
-
if (definition.type === 'decimal') {
|
|
644
|
-
return 'numeric';
|
|
645
|
-
}
|
|
646
|
-
if (definition.type === 'boolean') {
|
|
647
|
-
return 'integer';
|
|
648
|
-
}
|
|
649
|
-
if (definition.type === 'uuid') {
|
|
650
|
-
return 'text';
|
|
651
|
-
}
|
|
652
|
-
if (definition.type === 'date') {
|
|
653
|
-
return 'text';
|
|
654
|
-
}
|
|
655
|
-
if (definition.type === 'time') {
|
|
656
|
-
return 'text';
|
|
657
|
-
}
|
|
658
|
-
if (definition.type === 'timestamp') {
|
|
659
|
-
return 'text';
|
|
660
|
-
}
|
|
661
|
-
if (definition.type === 'json') {
|
|
662
|
-
return 'text';
|
|
663
|
-
}
|
|
664
|
-
if (definition.type === 'binary') {
|
|
665
|
-
return 'blob';
|
|
666
|
-
}
|
|
667
|
-
if (definition.type === 'enum') {
|
|
668
|
-
return 'text';
|
|
669
|
-
}
|
|
670
|
-
return 'text';
|
|
671
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/data-table-sqlite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "SQLite adapter for remix/data-table",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^24.6.0",
|
|
31
31
|
"@typescript/native-preview": "7.0.0-dev.20251125.1",
|
|
32
|
-
"@remix-run/assert": "0.
|
|
33
|
-
"@remix-run/
|
|
34
|
-
"@remix-run/
|
|
32
|
+
"@remix-run/assert": "0.2.0",
|
|
33
|
+
"@remix-run/test": "0.4.0",
|
|
34
|
+
"@remix-run/data-table": "0.3.0"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@remix-run/data-table": "^0.
|
|
37
|
+
"@remix-run/data-table": "^0.3.0"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
|
40
40
|
"remix",
|
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
|
|
|
22
13
|
import { compileSqliteOperation } from './sql-compiler.ts'
|
|
23
14
|
|
|
@@ -25,7 +16,7 @@ import { compileSqliteOperation } from './sql-compiler.ts'
|
|
|
25
16
|
* Synchronous SQLite database client accepted by the sqlite adapter.
|
|
26
17
|
*
|
|
27
18
|
* This matches the shared surface of Node's `node:sqlite` `DatabaseSync`, Bun's `bun:sqlite`
|
|
28
|
-
* `Database`, and
|
|
19
|
+
* `Database`, and compatible synchronous SQLite clients.
|
|
29
20
|
*/
|
|
30
21
|
export interface SqliteDatabase {
|
|
31
22
|
prepare(sql: string): SqliteStatement
|
|
@@ -48,7 +39,7 @@ export interface SqliteStatement {
|
|
|
48
39
|
* SQLite write execution metadata.
|
|
49
40
|
*/
|
|
50
41
|
export interface SqliteRunResult {
|
|
51
|
-
changes: number
|
|
42
|
+
changes: number | bigint
|
|
52
43
|
lastInsertRowid: unknown
|
|
53
44
|
}
|
|
54
45
|
|
|
@@ -82,17 +73,13 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
82
73
|
}
|
|
83
74
|
|
|
84
75
|
/**
|
|
85
|
-
* Compiles a data
|
|
76
|
+
* Compiles a data-manipulation operation to sqlite SQL statements.
|
|
86
77
|
* @param operation Operation to compile.
|
|
87
78
|
* @returns Compiled SQL statements.
|
|
88
79
|
*/
|
|
89
|
-
compileSql(operation: DataManipulationOperation
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return [{ text: compiled.text, values: compiled.values }]
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return compileSqliteMigrationOperations(operation)
|
|
80
|
+
compileSql(operation: DataManipulationOperation): SqlStatement[] {
|
|
81
|
+
let compiled = compileSqliteOperation(operation)
|
|
82
|
+
return [{ text: compiled.text, values: compiled.values }]
|
|
96
83
|
}
|
|
97
84
|
|
|
98
85
|
/**
|
|
@@ -111,9 +98,10 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
111
98
|
|
|
112
99
|
let statement = this.compileSql(request.operation)[0]
|
|
113
100
|
let prepared = this.#database.prepare(statement.text)
|
|
101
|
+
let values = normalizeStatementValues(statement.values)
|
|
114
102
|
|
|
115
103
|
if (shouldReadStatement(request.operation, prepared)) {
|
|
116
|
-
let rows = normalizeRows(prepared.all(...
|
|
104
|
+
let rows = normalizeRows(prepared.all(...values))
|
|
117
105
|
|
|
118
106
|
if (request.operation.kind === 'count' || request.operation.kind === 'exists') {
|
|
119
107
|
rows = normalizeCountRows(rows)
|
|
@@ -126,7 +114,7 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
126
114
|
}
|
|
127
115
|
}
|
|
128
116
|
|
|
129
|
-
let result = prepared.run(...
|
|
117
|
+
let result = prepared.run(...values)
|
|
130
118
|
|
|
131
119
|
return {
|
|
132
120
|
affectedRows: normalizeAffectedRowsForRun(request.operation.kind, result),
|
|
@@ -135,21 +123,17 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
135
123
|
}
|
|
136
124
|
|
|
137
125
|
/**
|
|
138
|
-
* Executes sqlite
|
|
139
|
-
* @param
|
|
140
|
-
* @
|
|
126
|
+
* Executes a multi-statement sqlite SQL script.
|
|
127
|
+
* @param sql SQL script to execute.
|
|
128
|
+
* @param transaction Optional transaction token (asserted when present).
|
|
129
|
+
* @returns A promise that resolves once execution completes.
|
|
141
130
|
*/
|
|
142
|
-
async
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
for (let statement of statements) {
|
|
146
|
-
let prepared = this.#database.prepare(statement.text)
|
|
147
|
-
prepared.run(...statement.values)
|
|
131
|
+
async executeScript(sql: string, transaction?: TransactionToken): Promise<void> {
|
|
132
|
+
if (transaction) {
|
|
133
|
+
this.#assertTransaction(transaction)
|
|
148
134
|
}
|
|
149
135
|
|
|
150
|
-
|
|
151
|
-
affectedOperations: statements.length,
|
|
152
|
-
}
|
|
136
|
+
this.#database.exec(sql)
|
|
153
137
|
}
|
|
154
138
|
|
|
155
139
|
/**
|
|
@@ -287,7 +271,7 @@ export class SqliteDatabaseAdapter implements DatabaseAdapter {
|
|
|
287
271
|
* ```ts
|
|
288
272
|
* import { DatabaseSync } from 'node:sqlite'
|
|
289
273
|
* import { createDatabase } from 'remix/data-table'
|
|
290
|
-
* import { createSqliteDatabaseAdapter } from 'remix/data-table
|
|
274
|
+
* import { createSqliteDatabaseAdapter } from 'remix/data-table/sqlite'
|
|
291
275
|
*
|
|
292
276
|
* let sqlite = new DatabaseSync('./data/app.db')
|
|
293
277
|
* let adapter = createSqliteDatabaseAdapter(sqlite)
|
|
@@ -308,6 +292,10 @@ function normalizeRows(rows: unknown[]): Record<string, unknown>[] {
|
|
|
308
292
|
})
|
|
309
293
|
}
|
|
310
294
|
|
|
295
|
+
function normalizeStatementValues(values: unknown[]): unknown[] {
|
|
296
|
+
return values.map((value) => (value === undefined ? null : value))
|
|
297
|
+
}
|
|
298
|
+
|
|
311
299
|
function normalizeCountRows(rows: Record<string, unknown>[]): Record<string, unknown>[] {
|
|
312
300
|
return rows.map((row) => {
|
|
313
301
|
let count = row.count
|
|
@@ -374,7 +362,7 @@ function normalizeAffectedRowsForRun(
|
|
|
374
362
|
return undefined
|
|
375
363
|
}
|
|
376
364
|
|
|
377
|
-
return result.changes
|
|
365
|
+
return Number(result.changes)
|
|
378
366
|
}
|
|
379
367
|
|
|
380
368
|
function normalizeInsertIdForRun(
|
|
@@ -424,14 +412,6 @@ function quoteIdentifier(value: string): string {
|
|
|
424
412
|
return '"' + value.replace(/"/g, '""') + '"'
|
|
425
413
|
}
|
|
426
414
|
|
|
427
|
-
function quoteTableRef(table: TableRef): string {
|
|
428
|
-
return quoteTableRefHelper(table, quoteIdentifier)
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function quoteLiteral(value: unknown): string {
|
|
432
|
-
return quoteLiteralHelper(value, { booleansAsIntegers: true })
|
|
433
|
-
}
|
|
434
|
-
|
|
435
415
|
function isWriteOperationKind(kind: DataManipulationRequest['operation']['kind']): boolean {
|
|
436
416
|
return (
|
|
437
417
|
kind === 'insert' ||
|
|
@@ -456,421 +436,3 @@ function isInsertOperation(
|
|
|
456
436
|
operation.kind === 'insert' || operation.kind === 'insertMany' || operation.kind === 'upsert'
|
|
457
437
|
)
|
|
458
438
|
}
|
|
459
|
-
|
|
460
|
-
function isDataManipulationOperation(
|
|
461
|
-
operation: DataManipulationOperation | DataMigrationOperation,
|
|
462
|
-
): operation is DataManipulationOperation {
|
|
463
|
-
return isDataManipulationOperationHelper(operation)
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function compileSqliteMigrationOperations(operation: DataMigrationOperation): SqlStatement[] {
|
|
467
|
-
if (operation.kind === 'raw') {
|
|
468
|
-
return [{ text: operation.sql.text, values: [...operation.sql.values] }]
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
if (operation.kind === 'createTable') {
|
|
472
|
-
let columns = Object.keys(operation.columns).map(
|
|
473
|
-
(columnName) =>
|
|
474
|
-
quoteIdentifier(columnName) + ' ' + compileSqliteColumn(operation.columns[columnName]),
|
|
475
|
-
)
|
|
476
|
-
let constraints: string[] = []
|
|
477
|
-
|
|
478
|
-
if (operation.primaryKey) {
|
|
479
|
-
constraints.push(
|
|
480
|
-
'constraint ' +
|
|
481
|
-
quoteIdentifier(operation.primaryKey.name) +
|
|
482
|
-
' primary key (' +
|
|
483
|
-
operation.primaryKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
484
|
-
')',
|
|
485
|
-
)
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
for (let unique of operation.uniques ?? []) {
|
|
489
|
-
constraints.push(
|
|
490
|
-
'constraint ' +
|
|
491
|
-
quoteIdentifier(unique.name) +
|
|
492
|
-
' ' +
|
|
493
|
-
'unique (' +
|
|
494
|
-
unique.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
495
|
-
')',
|
|
496
|
-
)
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
for (let check of operation.checks ?? []) {
|
|
500
|
-
constraints.push(
|
|
501
|
-
'constraint ' + quoteIdentifier(check.name) + ' ' + 'check (' + check.expression + ')',
|
|
502
|
-
)
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
for (let foreignKey of operation.foreignKeys ?? []) {
|
|
506
|
-
let clause =
|
|
507
|
-
'constraint ' +
|
|
508
|
-
quoteIdentifier(foreignKey.name) +
|
|
509
|
-
' ' +
|
|
510
|
-
'foreign key (' +
|
|
511
|
-
foreignKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
512
|
-
') references ' +
|
|
513
|
-
quoteTableRef(foreignKey.references.table) +
|
|
514
|
-
' (' +
|
|
515
|
-
foreignKey.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
516
|
-
')'
|
|
517
|
-
|
|
518
|
-
if (foreignKey.onDelete) {
|
|
519
|
-
clause += ' on delete ' + foreignKey.onDelete
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
if (foreignKey.onUpdate) {
|
|
523
|
-
clause += ' on update ' + foreignKey.onUpdate
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
constraints.push(clause)
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
return [
|
|
530
|
-
{
|
|
531
|
-
text:
|
|
532
|
-
'create table ' +
|
|
533
|
-
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
534
|
-
quoteTableRef(operation.table) +
|
|
535
|
-
' (' +
|
|
536
|
-
[...columns, ...constraints].join(', ') +
|
|
537
|
-
')',
|
|
538
|
-
values: [],
|
|
539
|
-
},
|
|
540
|
-
]
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (operation.kind === 'alterTable') {
|
|
544
|
-
let statements: SqlStatement[] = []
|
|
545
|
-
|
|
546
|
-
for (let change of operation.changes) {
|
|
547
|
-
let sql = 'alter table ' + quoteTableRef(operation.table) + ' '
|
|
548
|
-
|
|
549
|
-
if (change.kind === 'addColumn') {
|
|
550
|
-
sql +=
|
|
551
|
-
'add column ' +
|
|
552
|
-
quoteIdentifier(change.column) +
|
|
553
|
-
' ' +
|
|
554
|
-
compileSqliteColumn(change.definition)
|
|
555
|
-
} else if (change.kind === 'changeColumn') {
|
|
556
|
-
sql +=
|
|
557
|
-
'alter column ' +
|
|
558
|
-
quoteIdentifier(change.column) +
|
|
559
|
-
' type ' +
|
|
560
|
-
compileSqliteColumnType(change.definition)
|
|
561
|
-
} else if (change.kind === 'renameColumn') {
|
|
562
|
-
sql += 'rename column ' + quoteIdentifier(change.from) + ' to ' + quoteIdentifier(change.to)
|
|
563
|
-
} else if (change.kind === 'dropColumn') {
|
|
564
|
-
sql += 'drop column ' + quoteIdentifier(change.column)
|
|
565
|
-
} else if (change.kind === 'addPrimaryKey') {
|
|
566
|
-
sql +=
|
|
567
|
-
'add primary key (' +
|
|
568
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
569
|
-
')'
|
|
570
|
-
} else if (change.kind === 'dropPrimaryKey') {
|
|
571
|
-
sql += 'drop primary key'
|
|
572
|
-
} else if (change.kind === 'addUnique') {
|
|
573
|
-
sql +=
|
|
574
|
-
'add ' +
|
|
575
|
-
'constraint ' +
|
|
576
|
-
quoteIdentifier(change.constraint.name) +
|
|
577
|
-
' ' +
|
|
578
|
-
'unique (' +
|
|
579
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
580
|
-
')'
|
|
581
|
-
} else if (change.kind === 'dropUnique') {
|
|
582
|
-
sql += 'drop constraint ' + quoteIdentifier(change.name)
|
|
583
|
-
} else if (change.kind === 'addForeignKey') {
|
|
584
|
-
sql +=
|
|
585
|
-
'add ' +
|
|
586
|
-
'constraint ' +
|
|
587
|
-
quoteIdentifier(change.constraint.name) +
|
|
588
|
-
' ' +
|
|
589
|
-
'foreign key (' +
|
|
590
|
-
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
591
|
-
') references ' +
|
|
592
|
-
quoteTableRef(change.constraint.references.table) +
|
|
593
|
-
' (' +
|
|
594
|
-
change.constraint.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
595
|
-
')'
|
|
596
|
-
} else if (change.kind === 'dropForeignKey') {
|
|
597
|
-
sql += 'drop constraint ' + quoteIdentifier(change.name)
|
|
598
|
-
} else if (change.kind === 'addCheck') {
|
|
599
|
-
sql +=
|
|
600
|
-
'add ' +
|
|
601
|
-
'constraint ' +
|
|
602
|
-
quoteIdentifier(change.constraint.name) +
|
|
603
|
-
' ' +
|
|
604
|
-
'check (' +
|
|
605
|
-
change.constraint.expression +
|
|
606
|
-
')'
|
|
607
|
-
} else if (change.kind === 'dropCheck') {
|
|
608
|
-
sql += 'drop constraint ' + quoteIdentifier(change.name)
|
|
609
|
-
} else if (change.kind === 'setTableComment') {
|
|
610
|
-
continue
|
|
611
|
-
} else {
|
|
612
|
-
continue
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
statements.push({ text: sql, values: [] })
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
return statements
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (operation.kind === 'renameTable') {
|
|
622
|
-
return [
|
|
623
|
-
{
|
|
624
|
-
text:
|
|
625
|
-
'alter table ' +
|
|
626
|
-
quoteTableRef(operation.from) +
|
|
627
|
-
' rename to ' +
|
|
628
|
-
quoteIdentifier(operation.to.name),
|
|
629
|
-
values: [],
|
|
630
|
-
},
|
|
631
|
-
]
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
if (operation.kind === 'dropTable') {
|
|
635
|
-
return [
|
|
636
|
-
{
|
|
637
|
-
text:
|
|
638
|
-
'drop table ' + (operation.ifExists ? 'if exists ' : '') + quoteTableRef(operation.table),
|
|
639
|
-
values: [],
|
|
640
|
-
},
|
|
641
|
-
]
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
if (operation.kind === 'createIndex') {
|
|
645
|
-
return [
|
|
646
|
-
{
|
|
647
|
-
text:
|
|
648
|
-
'create ' +
|
|
649
|
-
(operation.index.unique ? 'unique ' : '') +
|
|
650
|
-
'index ' +
|
|
651
|
-
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
652
|
-
quoteIdentifier(operation.index.name) +
|
|
653
|
-
' on ' +
|
|
654
|
-
quoteTableRef(operation.index.table) +
|
|
655
|
-
' (' +
|
|
656
|
-
operation.index.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
657
|
-
')' +
|
|
658
|
-
(operation.index.where ? ' where ' + operation.index.where : ''),
|
|
659
|
-
values: [],
|
|
660
|
-
},
|
|
661
|
-
]
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
if (operation.kind === 'dropIndex') {
|
|
665
|
-
return [
|
|
666
|
-
{
|
|
667
|
-
text:
|
|
668
|
-
'drop index ' +
|
|
669
|
-
(operation.ifExists ? 'if exists ' : '') +
|
|
670
|
-
quoteIdentifier(operation.name),
|
|
671
|
-
values: [],
|
|
672
|
-
},
|
|
673
|
-
]
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
if (operation.kind === 'renameIndex') {
|
|
677
|
-
return [
|
|
678
|
-
{
|
|
679
|
-
text:
|
|
680
|
-
'alter table ' +
|
|
681
|
-
quoteTableRef(operation.table) +
|
|
682
|
-
' rename index ' +
|
|
683
|
-
quoteIdentifier(operation.from) +
|
|
684
|
-
' to ' +
|
|
685
|
-
quoteIdentifier(operation.to),
|
|
686
|
-
values: [],
|
|
687
|
-
},
|
|
688
|
-
]
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
if (operation.kind === 'addForeignKey') {
|
|
692
|
-
return [
|
|
693
|
-
{
|
|
694
|
-
text:
|
|
695
|
-
'alter table ' +
|
|
696
|
-
quoteTableRef(operation.table) +
|
|
697
|
-
' add ' +
|
|
698
|
-
'constraint ' +
|
|
699
|
-
quoteIdentifier(operation.constraint.name) +
|
|
700
|
-
' ' +
|
|
701
|
-
'foreign key (' +
|
|
702
|
-
operation.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
703
|
-
') references ' +
|
|
704
|
-
quoteTableRef(operation.constraint.references.table) +
|
|
705
|
-
' (' +
|
|
706
|
-
operation.constraint.references.columns
|
|
707
|
-
.map((column) => quoteIdentifier(column))
|
|
708
|
-
.join(', ') +
|
|
709
|
-
')' +
|
|
710
|
-
(operation.constraint.onDelete ? ' on delete ' + operation.constraint.onDelete : '') +
|
|
711
|
-
(operation.constraint.onUpdate ? ' on update ' + operation.constraint.onUpdate : ''),
|
|
712
|
-
values: [],
|
|
713
|
-
},
|
|
714
|
-
]
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (operation.kind === 'dropForeignKey') {
|
|
718
|
-
return [
|
|
719
|
-
{
|
|
720
|
-
text:
|
|
721
|
-
'alter table ' +
|
|
722
|
-
quoteTableRef(operation.table) +
|
|
723
|
-
' drop constraint ' +
|
|
724
|
-
quoteIdentifier(operation.name),
|
|
725
|
-
values: [],
|
|
726
|
-
},
|
|
727
|
-
]
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
if (operation.kind === 'addCheck') {
|
|
731
|
-
return [
|
|
732
|
-
{
|
|
733
|
-
text:
|
|
734
|
-
'alter table ' +
|
|
735
|
-
quoteTableRef(operation.table) +
|
|
736
|
-
' add ' +
|
|
737
|
-
'constraint ' +
|
|
738
|
-
quoteIdentifier(operation.constraint.name) +
|
|
739
|
-
' ' +
|
|
740
|
-
'check (' +
|
|
741
|
-
operation.constraint.expression +
|
|
742
|
-
')',
|
|
743
|
-
values: [],
|
|
744
|
-
},
|
|
745
|
-
]
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
if (operation.kind === 'dropCheck') {
|
|
749
|
-
return [
|
|
750
|
-
{
|
|
751
|
-
text:
|
|
752
|
-
'alter table ' +
|
|
753
|
-
quoteTableRef(operation.table) +
|
|
754
|
-
' drop constraint ' +
|
|
755
|
-
quoteIdentifier(operation.name),
|
|
756
|
-
values: [],
|
|
757
|
-
},
|
|
758
|
-
]
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
throw new Error('Unsupported data migration operation kind')
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
function compileSqliteColumn(definition: ColumnDefinition): string {
|
|
765
|
-
let parts = [compileSqliteColumnType(definition)]
|
|
766
|
-
|
|
767
|
-
if (definition.nullable === false) {
|
|
768
|
-
parts.push('not null')
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
if (definition.default) {
|
|
772
|
-
if (definition.default.kind === 'now') {
|
|
773
|
-
parts.push('default current_timestamp')
|
|
774
|
-
} else if (definition.default.kind === 'sql') {
|
|
775
|
-
parts.push('default ' + definition.default.expression)
|
|
776
|
-
} else {
|
|
777
|
-
parts.push('default ' + quoteLiteral(definition.default.value))
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
if (definition.primaryKey) {
|
|
782
|
-
parts.push('primary key')
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
if (definition.unique) {
|
|
786
|
-
parts.push('unique')
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (definition.computed) {
|
|
790
|
-
parts.push('generated always as (' + definition.computed.expression + ')')
|
|
791
|
-
parts.push(definition.computed.stored ? 'stored' : 'virtual')
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
if (definition.references) {
|
|
795
|
-
let clause =
|
|
796
|
-
'references ' +
|
|
797
|
-
quoteTableRef(definition.references.table) +
|
|
798
|
-
' (' +
|
|
799
|
-
definition.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
800
|
-
')'
|
|
801
|
-
|
|
802
|
-
if (definition.references.onDelete) {
|
|
803
|
-
clause += ' on delete ' + definition.references.onDelete
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (definition.references.onUpdate) {
|
|
807
|
-
clause += ' on update ' + definition.references.onUpdate
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
parts.push(clause)
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
if (definition.checks && definition.checks.length > 0) {
|
|
814
|
-
for (let check of definition.checks) {
|
|
815
|
-
parts.push('check (' + check.expression + ')')
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
return parts.join(' ')
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
function compileSqliteColumnType(definition: ColumnDefinition): string {
|
|
823
|
-
if (definition.type === 'varchar') {
|
|
824
|
-
return 'text'
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
if (definition.type === 'text') {
|
|
828
|
-
return 'text'
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
if (definition.type === 'integer') {
|
|
832
|
-
return 'integer'
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if (definition.type === 'bigint') {
|
|
836
|
-
return 'integer'
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
if (definition.type === 'decimal') {
|
|
840
|
-
return 'numeric'
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
if (definition.type === 'boolean') {
|
|
844
|
-
return 'integer'
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
if (definition.type === 'uuid') {
|
|
848
|
-
return 'text'
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
if (definition.type === 'date') {
|
|
852
|
-
return 'text'
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
if (definition.type === 'time') {
|
|
856
|
-
return 'text'
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
if (definition.type === 'timestamp') {
|
|
860
|
-
return 'text'
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
if (definition.type === 'json') {
|
|
864
|
-
return 'text'
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
if (definition.type === 'binary') {
|
|
868
|
-
return 'blob'
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
if (definition.type === 'enum') {
|
|
872
|
-
return 'text'
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
return 'text'
|
|
876
|
-
}
|