@remix-run/data-table-postgres 0.1.0 → 0.2.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 +9 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/adapter.d.ts +100 -33
- package/dist/lib/adapter.d.ts.map +1 -1
- package/dist/lib/adapter.js +553 -17
- package/dist/lib/sql-compiler.d.ts +2 -7
- package/dist/lib/sql-compiler.d.ts.map +1 -1
- package/dist/lib/sql-compiler.js +50 -80
- package/package.json +6 -7
- package/src/index.ts +1 -7
- package/src/lib/adapter.ts +675 -69
- package/src/lib/sql-compiler.ts +66 -106
package/dist/lib/adapter.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { getTablePrimaryKey } from '@remix-run/data-table';
|
|
2
|
-
import {
|
|
2
|
+
import { isDataManipulationOperation as isDataManipulationOperationHelper, quoteLiteral as quoteLiteralHelper, quoteTableRef as quoteTableRefHelper, } from '@remix-run/data-table/sql-helpers';
|
|
3
|
+
import { compilePostgresOperation } from "./sql-compiler.js";
|
|
3
4
|
/**
|
|
4
5
|
* `DatabaseAdapter` implementation for postgres-compatible clients.
|
|
5
6
|
*/
|
|
6
7
|
export class PostgresDatabaseAdapter {
|
|
8
|
+
/**
|
|
9
|
+
* The SQL dialect identifier reported by this adapter.
|
|
10
|
+
*/
|
|
7
11
|
dialect = 'postgres';
|
|
12
|
+
/**
|
|
13
|
+
* Feature flags describing the postgres behaviors supported by this adapter.
|
|
14
|
+
*/
|
|
8
15
|
capabilities;
|
|
9
16
|
#client;
|
|
10
17
|
#transactions = new Map();
|
|
@@ -15,33 +22,99 @@ export class PostgresDatabaseAdapter {
|
|
|
15
22
|
returning: options?.capabilities?.returning ?? true,
|
|
16
23
|
savepoints: options?.capabilities?.savepoints ?? true,
|
|
17
24
|
upsert: options?.capabilities?.upsert ?? true,
|
|
25
|
+
transactionalDdl: options?.capabilities?.transactionalDdl ?? true,
|
|
26
|
+
migrationLock: options?.capabilities?.migrationLock ?? true,
|
|
18
27
|
};
|
|
19
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Compiles a data or migration operation to postgres SQL statements.
|
|
31
|
+
* @param operation Operation to compile.
|
|
32
|
+
* @returns Compiled SQL statements.
|
|
33
|
+
*/
|
|
34
|
+
compileSql(operation) {
|
|
35
|
+
if (isDataManipulationOperation(operation)) {
|
|
36
|
+
let compiled = compilePostgresOperation(operation);
|
|
37
|
+
return [{ text: compiled.text, values: compiled.values }];
|
|
38
|
+
}
|
|
39
|
+
return compilePostgresMigrationOperations(operation);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Executes a postgres data-manipulation request.
|
|
43
|
+
* @param request Request to execute.
|
|
44
|
+
* @returns Execution result.
|
|
45
|
+
*/
|
|
20
46
|
async execute(request) {
|
|
21
|
-
if (request.
|
|
47
|
+
if (request.operation.kind === 'insertMany' && request.operation.values.length === 0) {
|
|
22
48
|
return {
|
|
23
49
|
affectedRows: 0,
|
|
24
50
|
insertId: undefined,
|
|
25
|
-
rows: request.
|
|
51
|
+
rows: request.operation.returning ? [] : undefined,
|
|
26
52
|
};
|
|
27
53
|
}
|
|
28
|
-
let statement =
|
|
54
|
+
let statement = compilePostgresOperation(request.operation);
|
|
29
55
|
let client = this.#resolveClient(request.transaction);
|
|
30
56
|
let result = await client.query(statement.text, statement.values);
|
|
31
57
|
let rows = normalizeRows(result.rows);
|
|
32
|
-
if (request.
|
|
58
|
+
if (request.operation.kind === 'count' || request.operation.kind === 'exists') {
|
|
33
59
|
rows = normalizeCountRows(rows);
|
|
34
60
|
}
|
|
35
61
|
return {
|
|
36
62
|
rows,
|
|
37
|
-
affectedRows: normalizeAffectedRows(request.
|
|
38
|
-
insertId: normalizeInsertId(request.
|
|
63
|
+
affectedRows: normalizeAffectedRows(request.operation.kind, result.rowCount, rows),
|
|
64
|
+
insertId: normalizeInsertId(request.operation.kind, request.operation, rows),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Executes postgres migration operations.
|
|
69
|
+
* @param request Migration request to execute.
|
|
70
|
+
* @returns Migration result.
|
|
71
|
+
*/
|
|
72
|
+
async migrate(request) {
|
|
73
|
+
let statements = this.compileSql(request.operation);
|
|
74
|
+
let client = this.#resolveClient(request.transaction);
|
|
75
|
+
for (let statement of statements) {
|
|
76
|
+
await client.query(statement.text, statement.values);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
affectedOperations: statements.length,
|
|
39
80
|
};
|
|
40
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Checks whether a table exists in postgres.
|
|
84
|
+
* @param table Table reference to inspect.
|
|
85
|
+
* @param transaction Optional transaction token.
|
|
86
|
+
* @returns `true` when the table exists.
|
|
87
|
+
*/
|
|
88
|
+
async hasTable(table, transaction) {
|
|
89
|
+
let relation = toPostgresRelationName(table);
|
|
90
|
+
let client = this.#resolveClient(transaction);
|
|
91
|
+
let result = await client.query('select to_regclass($1) is not null as "exists"', [relation]);
|
|
92
|
+
let row = result.rows[0];
|
|
93
|
+
return toBooleanExists(row?.exists);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Checks whether a column exists in postgres.
|
|
97
|
+
* @param table Table reference to inspect.
|
|
98
|
+
* @param column Column name to look up.
|
|
99
|
+
* @param transaction Optional transaction token.
|
|
100
|
+
* @returns `true` when the column exists.
|
|
101
|
+
*/
|
|
102
|
+
async hasColumn(table, column, transaction) {
|
|
103
|
+
let relation = toPostgresRelationName(table);
|
|
104
|
+
let client = this.#resolveClient(transaction);
|
|
105
|
+
let result = await client.query('select exists (select 1 from pg_attribute where attrelid = to_regclass($1) and attname = $2 and attnum > 0 and not attisdropped) as "exists"', [relation, column]);
|
|
106
|
+
let row = result.rows[0];
|
|
107
|
+
return toBooleanExists(row?.exists);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Starts a postgres transaction.
|
|
111
|
+
* @param options Transaction options.
|
|
112
|
+
* @returns Transaction token.
|
|
113
|
+
*/
|
|
41
114
|
async beginTransaction(options) {
|
|
42
115
|
let releaseOnClose = false;
|
|
43
116
|
let transactionClient;
|
|
44
|
-
if (this.#client
|
|
117
|
+
if (isPostgresPool(this.#client)) {
|
|
45
118
|
transactionClient = await this.#client.connect();
|
|
46
119
|
releaseOnClose = true;
|
|
47
120
|
}
|
|
@@ -60,6 +133,11 @@ export class PostgresDatabaseAdapter {
|
|
|
60
133
|
});
|
|
61
134
|
return token;
|
|
62
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Commits an open postgres transaction.
|
|
138
|
+
* @param token Transaction token to commit.
|
|
139
|
+
* @returns A promise that resolves when the transaction is committed.
|
|
140
|
+
*/
|
|
63
141
|
async commitTransaction(token) {
|
|
64
142
|
let transaction = this.#transactions.get(token.id);
|
|
65
143
|
if (!transaction) {
|
|
@@ -71,10 +149,15 @@ export class PostgresDatabaseAdapter {
|
|
|
71
149
|
finally {
|
|
72
150
|
this.#transactions.delete(token.id);
|
|
73
151
|
if (transaction.releaseOnClose) {
|
|
74
|
-
transaction.client
|
|
152
|
+
releasePostgresClient(transaction.client);
|
|
75
153
|
}
|
|
76
154
|
}
|
|
77
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Rolls back an open postgres transaction.
|
|
158
|
+
* @param token Transaction token to roll back.
|
|
159
|
+
* @returns A promise that resolves when the transaction is rolled back.
|
|
160
|
+
*/
|
|
78
161
|
async rollbackTransaction(token) {
|
|
79
162
|
let transaction = this.#transactions.get(token.id);
|
|
80
163
|
if (!transaction) {
|
|
@@ -86,22 +169,54 @@ export class PostgresDatabaseAdapter {
|
|
|
86
169
|
finally {
|
|
87
170
|
this.#transactions.delete(token.id);
|
|
88
171
|
if (transaction.releaseOnClose) {
|
|
89
|
-
transaction.client
|
|
172
|
+
releasePostgresClient(transaction.client);
|
|
90
173
|
}
|
|
91
174
|
}
|
|
92
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Creates a savepoint in an open postgres transaction.
|
|
178
|
+
* @param token Transaction token to use.
|
|
179
|
+
* @param name Savepoint name.
|
|
180
|
+
* @returns A promise that resolves when the savepoint is created.
|
|
181
|
+
*/
|
|
93
182
|
async createSavepoint(token, name) {
|
|
94
183
|
let client = this.#transactionClient(token);
|
|
95
184
|
await client.query('savepoint ' + quoteIdentifier(name));
|
|
96
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Rolls back to a savepoint in an open postgres transaction.
|
|
188
|
+
* @param token Transaction token to use.
|
|
189
|
+
* @param name Savepoint name.
|
|
190
|
+
* @returns A promise that resolves when the rollback completes.
|
|
191
|
+
*/
|
|
97
192
|
async rollbackToSavepoint(token, name) {
|
|
98
193
|
let client = this.#transactionClient(token);
|
|
99
194
|
await client.query('rollback to savepoint ' + quoteIdentifier(name));
|
|
100
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Releases a savepoint in an open postgres transaction.
|
|
198
|
+
* @param token Transaction token to use.
|
|
199
|
+
* @param name Savepoint name.
|
|
200
|
+
* @returns A promise that resolves when the savepoint is released.
|
|
201
|
+
*/
|
|
101
202
|
async releaseSavepoint(token, name) {
|
|
102
203
|
let client = this.#transactionClient(token);
|
|
103
204
|
await client.query('release savepoint ' + quoteIdentifier(name));
|
|
104
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Acquires the postgres migration lock.
|
|
208
|
+
* @returns A promise that resolves when the lock is acquired.
|
|
209
|
+
*/
|
|
210
|
+
async acquireMigrationLock() {
|
|
211
|
+
await this.#client.query('select pg_advisory_lock(hashtext($1))', ['data_table_migrations']);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Releases the postgres migration lock.
|
|
215
|
+
* @returns A promise that resolves when the lock is released.
|
|
216
|
+
*/
|
|
217
|
+
async releaseMigrationLock() {
|
|
218
|
+
await this.#client.query('select pg_advisory_unlock(hashtext($1))', ['data_table_migrations']);
|
|
219
|
+
}
|
|
105
220
|
#resolveClient(token) {
|
|
106
221
|
if (!token) {
|
|
107
222
|
return this.#client;
|
|
@@ -118,13 +233,30 @@ export class PostgresDatabaseAdapter {
|
|
|
118
233
|
}
|
|
119
234
|
/**
|
|
120
235
|
* Creates a postgres `DatabaseAdapter`.
|
|
121
|
-
* @param client
|
|
236
|
+
* @param client `pg` pool or pool client.
|
|
122
237
|
* @param options Optional adapter capability overrides.
|
|
123
238
|
* @returns A configured postgres adapter.
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* import { Pool } from 'pg'
|
|
242
|
+
* import { createDatabase } from 'remix/data-table'
|
|
243
|
+
* import { createPostgresDatabaseAdapter } from 'remix/data-table-postgres'
|
|
244
|
+
*
|
|
245
|
+
* let pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
|
246
|
+
* let adapter = createPostgresDatabaseAdapter(pool)
|
|
247
|
+
* let db = createDatabase(adapter)
|
|
248
|
+
* ```
|
|
124
249
|
*/
|
|
125
250
|
export function createPostgresDatabaseAdapter(client, options) {
|
|
126
251
|
return new PostgresDatabaseAdapter(client, options);
|
|
127
252
|
}
|
|
253
|
+
function isPostgresPool(client) {
|
|
254
|
+
return 'connect' in client && typeof client.connect === 'function';
|
|
255
|
+
}
|
|
256
|
+
function releasePostgresClient(client) {
|
|
257
|
+
let release = client.release;
|
|
258
|
+
release?.();
|
|
259
|
+
}
|
|
128
260
|
function buildSetTransactionStatement(options) {
|
|
129
261
|
let parts = ['set transaction'];
|
|
130
262
|
if (options.isolationLevel) {
|
|
@@ -176,11 +308,11 @@ function normalizeAffectedRows(kind, rowCount, rows) {
|
|
|
176
308
|
}
|
|
177
309
|
return rows.length;
|
|
178
310
|
}
|
|
179
|
-
function normalizeInsertId(kind,
|
|
180
|
-
if (!
|
|
311
|
+
function normalizeInsertId(kind, operation, rows) {
|
|
312
|
+
if (!isInsertOperationKind(kind) || !isInsertOperation(operation)) {
|
|
181
313
|
return undefined;
|
|
182
314
|
}
|
|
183
|
-
let primaryKey = getTablePrimaryKey(
|
|
315
|
+
let primaryKey = getTablePrimaryKey(operation.table);
|
|
184
316
|
if (primaryKey.length !== 1) {
|
|
185
317
|
return undefined;
|
|
186
318
|
}
|
|
@@ -191,9 +323,413 @@ function normalizeInsertId(kind, statement, rows) {
|
|
|
191
323
|
function quoteIdentifier(value) {
|
|
192
324
|
return '"' + value.replace(/"/g, '""') + '"';
|
|
193
325
|
}
|
|
194
|
-
function
|
|
326
|
+
function toPostgresRelationName(table) {
|
|
327
|
+
if (table.schema) {
|
|
328
|
+
return quoteIdentifier(table.schema) + '.' + quoteIdentifier(table.name);
|
|
329
|
+
}
|
|
330
|
+
return quoteIdentifier(table.name);
|
|
331
|
+
}
|
|
332
|
+
function toBooleanExists(value) {
|
|
333
|
+
if (typeof value === 'boolean') {
|
|
334
|
+
return value;
|
|
335
|
+
}
|
|
336
|
+
if (typeof value === 'number') {
|
|
337
|
+
return value > 0;
|
|
338
|
+
}
|
|
339
|
+
if (typeof value === 'string') {
|
|
340
|
+
return value === 't' || value === 'true' || value === '1';
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
function isInsertOperationKind(kind) {
|
|
195
345
|
return kind === 'insert' || kind === 'insertMany' || kind === 'upsert';
|
|
196
346
|
}
|
|
197
|
-
function
|
|
198
|
-
return (
|
|
347
|
+
function isInsertOperation(operation) {
|
|
348
|
+
return (operation.kind === 'insert' || operation.kind === 'insertMany' || operation.kind === 'upsert');
|
|
349
|
+
}
|
|
350
|
+
function isDataManipulationOperation(operation) {
|
|
351
|
+
return isDataManipulationOperationHelper(operation);
|
|
352
|
+
}
|
|
353
|
+
function compilePostgresMigrationOperations(operation) {
|
|
354
|
+
if (operation.kind === 'raw') {
|
|
355
|
+
return [{ text: operation.sql.text, values: [...operation.sql.values] }];
|
|
356
|
+
}
|
|
357
|
+
if (operation.kind === 'createTable') {
|
|
358
|
+
let columns = Object.keys(operation.columns).map((columnName) => quoteIdentifier(columnName) + ' ' + compilePostgresColumn(operation.columns[columnName]));
|
|
359
|
+
let tableConstraints = [];
|
|
360
|
+
if (operation.primaryKey) {
|
|
361
|
+
tableConstraints.push('constraint ' +
|
|
362
|
+
quoteIdentifier(operation.primaryKey.name) +
|
|
363
|
+
' primary key (' +
|
|
364
|
+
operation.primaryKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
365
|
+
')');
|
|
366
|
+
}
|
|
367
|
+
for (let unique of operation.uniques ?? []) {
|
|
368
|
+
tableConstraints.push('constraint ' +
|
|
369
|
+
quoteIdentifier(unique.name) +
|
|
370
|
+
' ' +
|
|
371
|
+
'unique (' +
|
|
372
|
+
unique.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
373
|
+
')');
|
|
374
|
+
}
|
|
375
|
+
for (let check of operation.checks ?? []) {
|
|
376
|
+
tableConstraints.push('constraint ' + quoteIdentifier(check.name) + ' ' + 'check (' + check.expression + ')');
|
|
377
|
+
}
|
|
378
|
+
for (let foreignKey of operation.foreignKeys ?? []) {
|
|
379
|
+
let clause = 'constraint ' +
|
|
380
|
+
quoteIdentifier(foreignKey.name) +
|
|
381
|
+
' ' +
|
|
382
|
+
'foreign key (' +
|
|
383
|
+
foreignKey.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
384
|
+
') references ' +
|
|
385
|
+
quoteTableRef(foreignKey.references.table) +
|
|
386
|
+
' (' +
|
|
387
|
+
foreignKey.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
388
|
+
')';
|
|
389
|
+
if (foreignKey.onDelete) {
|
|
390
|
+
clause += ' on delete ' + foreignKey.onDelete;
|
|
391
|
+
}
|
|
392
|
+
if (foreignKey.onUpdate) {
|
|
393
|
+
clause += ' on update ' + foreignKey.onUpdate;
|
|
394
|
+
}
|
|
395
|
+
tableConstraints.push(clause);
|
|
396
|
+
}
|
|
397
|
+
let sql = 'create table ' +
|
|
398
|
+
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
399
|
+
quoteTableRef(operation.table) +
|
|
400
|
+
' (' +
|
|
401
|
+
[...columns, ...tableConstraints].join(', ') +
|
|
402
|
+
')';
|
|
403
|
+
let statements = [{ text: sql, values: [] }];
|
|
404
|
+
if (operation.comment) {
|
|
405
|
+
statements.push({
|
|
406
|
+
text: 'comment on table ' +
|
|
407
|
+
quoteTableRef(operation.table) +
|
|
408
|
+
' is ' +
|
|
409
|
+
quoteLiteral(operation.comment),
|
|
410
|
+
values: [],
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return statements;
|
|
414
|
+
}
|
|
415
|
+
if (operation.kind === 'alterTable') {
|
|
416
|
+
let sqlStatements = [];
|
|
417
|
+
for (let change of operation.changes) {
|
|
418
|
+
let sql = 'alter table ' + quoteTableRef(operation.table) + ' ';
|
|
419
|
+
if (change.kind === 'addColumn') {
|
|
420
|
+
sql +=
|
|
421
|
+
'add column ' +
|
|
422
|
+
quoteIdentifier(change.column) +
|
|
423
|
+
' ' +
|
|
424
|
+
compilePostgresColumn(change.definition);
|
|
425
|
+
}
|
|
426
|
+
else if (change.kind === 'changeColumn') {
|
|
427
|
+
let typeSql = compilePostgresColumnType(change.definition);
|
|
428
|
+
sql += 'alter column ' + quoteIdentifier(change.column) + ' type ' + typeSql;
|
|
429
|
+
}
|
|
430
|
+
else if (change.kind === 'renameColumn') {
|
|
431
|
+
sql += 'rename column ' + quoteIdentifier(change.from) + ' to ' + quoteIdentifier(change.to);
|
|
432
|
+
}
|
|
433
|
+
else if (change.kind === 'dropColumn') {
|
|
434
|
+
sql +=
|
|
435
|
+
'drop column ' + (change.ifExists ? 'if exists ' : '') + quoteIdentifier(change.column);
|
|
436
|
+
}
|
|
437
|
+
else if (change.kind === 'addPrimaryKey') {
|
|
438
|
+
sql +=
|
|
439
|
+
'add ' +
|
|
440
|
+
'constraint ' +
|
|
441
|
+
quoteIdentifier(change.constraint.name) +
|
|
442
|
+
' ' +
|
|
443
|
+
'primary key (' +
|
|
444
|
+
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
445
|
+
')';
|
|
446
|
+
}
|
|
447
|
+
else if (change.kind === 'dropPrimaryKey') {
|
|
448
|
+
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
449
|
+
}
|
|
450
|
+
else if (change.kind === 'addUnique') {
|
|
451
|
+
sql +=
|
|
452
|
+
'add ' +
|
|
453
|
+
'constraint ' +
|
|
454
|
+
quoteIdentifier(change.constraint.name) +
|
|
455
|
+
' ' +
|
|
456
|
+
'unique (' +
|
|
457
|
+
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
458
|
+
')';
|
|
459
|
+
}
|
|
460
|
+
else if (change.kind === 'dropUnique') {
|
|
461
|
+
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
462
|
+
}
|
|
463
|
+
else if (change.kind === 'addForeignKey') {
|
|
464
|
+
sql +=
|
|
465
|
+
'add ' +
|
|
466
|
+
'constraint ' +
|
|
467
|
+
quoteIdentifier(change.constraint.name) +
|
|
468
|
+
' ' +
|
|
469
|
+
'foreign key (' +
|
|
470
|
+
change.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
471
|
+
') references ' +
|
|
472
|
+
quoteTableRef(change.constraint.references.table) +
|
|
473
|
+
' (' +
|
|
474
|
+
change.constraint.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
475
|
+
')';
|
|
476
|
+
}
|
|
477
|
+
else if (change.kind === 'dropForeignKey') {
|
|
478
|
+
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
479
|
+
}
|
|
480
|
+
else if (change.kind === 'addCheck') {
|
|
481
|
+
sql +=
|
|
482
|
+
'add ' +
|
|
483
|
+
'constraint ' +
|
|
484
|
+
quoteIdentifier(change.constraint.name) +
|
|
485
|
+
' ' +
|
|
486
|
+
'check (' +
|
|
487
|
+
change.constraint.expression +
|
|
488
|
+
')';
|
|
489
|
+
}
|
|
490
|
+
else if (change.kind === 'dropCheck') {
|
|
491
|
+
sql += 'drop constraint ' + quoteIdentifier(change.name);
|
|
492
|
+
}
|
|
493
|
+
else if (change.kind === 'setTableComment') {
|
|
494
|
+
sqlStatements.push({
|
|
495
|
+
text: 'comment on table ' +
|
|
496
|
+
quoteTableRef(operation.table) +
|
|
497
|
+
' is ' +
|
|
498
|
+
quoteLiteral(change.comment),
|
|
499
|
+
values: [],
|
|
500
|
+
});
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
sqlStatements.push({ text: sql, values: [] });
|
|
507
|
+
}
|
|
508
|
+
return sqlStatements;
|
|
509
|
+
}
|
|
510
|
+
if (operation.kind === 'renameTable') {
|
|
511
|
+
return [
|
|
512
|
+
{
|
|
513
|
+
text: 'alter table ' +
|
|
514
|
+
quoteTableRef(operation.from) +
|
|
515
|
+
' rename to ' +
|
|
516
|
+
quoteIdentifier(operation.to.name),
|
|
517
|
+
values: [],
|
|
518
|
+
},
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
if (operation.kind === 'dropTable') {
|
|
522
|
+
return [
|
|
523
|
+
{
|
|
524
|
+
text: 'drop table ' +
|
|
525
|
+
(operation.ifExists ? 'if exists ' : '') +
|
|
526
|
+
quoteTableRef(operation.table) +
|
|
527
|
+
(operation.cascade ? ' cascade' : ''),
|
|
528
|
+
values: [],
|
|
529
|
+
},
|
|
530
|
+
];
|
|
531
|
+
}
|
|
532
|
+
if (operation.kind === 'createIndex') {
|
|
533
|
+
return [
|
|
534
|
+
{
|
|
535
|
+
text: 'create ' +
|
|
536
|
+
(operation.index.unique ? 'unique ' : '') +
|
|
537
|
+
'index ' +
|
|
538
|
+
(operation.ifNotExists ? 'if not exists ' : '') +
|
|
539
|
+
quoteIdentifier(operation.index.name) +
|
|
540
|
+
' on ' +
|
|
541
|
+
quoteTableRef(operation.index.table) +
|
|
542
|
+
(operation.index.using ? ' using ' + operation.index.using : '') +
|
|
543
|
+
' (' +
|
|
544
|
+
operation.index.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
545
|
+
')' +
|
|
546
|
+
(operation.index.where ? ' where ' + operation.index.where : ''),
|
|
547
|
+
values: [],
|
|
548
|
+
},
|
|
549
|
+
];
|
|
550
|
+
}
|
|
551
|
+
if (operation.kind === 'dropIndex') {
|
|
552
|
+
return [
|
|
553
|
+
{
|
|
554
|
+
text: 'drop index ' +
|
|
555
|
+
(operation.ifExists ? 'if exists ' : '') +
|
|
556
|
+
quoteIdentifier(operation.name),
|
|
557
|
+
values: [],
|
|
558
|
+
},
|
|
559
|
+
];
|
|
560
|
+
}
|
|
561
|
+
if (operation.kind === 'renameIndex') {
|
|
562
|
+
return [
|
|
563
|
+
{
|
|
564
|
+
text: 'alter index ' +
|
|
565
|
+
quoteIdentifier(operation.from) +
|
|
566
|
+
' rename to ' +
|
|
567
|
+
quoteIdentifier(operation.to),
|
|
568
|
+
values: [],
|
|
569
|
+
},
|
|
570
|
+
];
|
|
571
|
+
}
|
|
572
|
+
if (operation.kind === 'addForeignKey') {
|
|
573
|
+
return [
|
|
574
|
+
{
|
|
575
|
+
text: 'alter table ' +
|
|
576
|
+
quoteTableRef(operation.table) +
|
|
577
|
+
' add ' +
|
|
578
|
+
'constraint ' +
|
|
579
|
+
quoteIdentifier(operation.constraint.name) +
|
|
580
|
+
' ' +
|
|
581
|
+
'foreign key (' +
|
|
582
|
+
operation.constraint.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
583
|
+
') references ' +
|
|
584
|
+
quoteTableRef(operation.constraint.references.table) +
|
|
585
|
+
' (' +
|
|
586
|
+
operation.constraint.references.columns
|
|
587
|
+
.map((column) => quoteIdentifier(column))
|
|
588
|
+
.join(', ') +
|
|
589
|
+
')' +
|
|
590
|
+
(operation.constraint.onDelete ? ' on delete ' + operation.constraint.onDelete : '') +
|
|
591
|
+
(operation.constraint.onUpdate ? ' on update ' + operation.constraint.onUpdate : ''),
|
|
592
|
+
values: [],
|
|
593
|
+
},
|
|
594
|
+
];
|
|
595
|
+
}
|
|
596
|
+
if (operation.kind === 'dropForeignKey') {
|
|
597
|
+
return [
|
|
598
|
+
{
|
|
599
|
+
text: 'alter table ' +
|
|
600
|
+
quoteTableRef(operation.table) +
|
|
601
|
+
' drop constraint ' +
|
|
602
|
+
quoteIdentifier(operation.name),
|
|
603
|
+
values: [],
|
|
604
|
+
},
|
|
605
|
+
];
|
|
606
|
+
}
|
|
607
|
+
if (operation.kind === 'addCheck') {
|
|
608
|
+
return [
|
|
609
|
+
{
|
|
610
|
+
text: 'alter table ' +
|
|
611
|
+
quoteTableRef(operation.table) +
|
|
612
|
+
' add ' +
|
|
613
|
+
'constraint ' +
|
|
614
|
+
quoteIdentifier(operation.constraint.name) +
|
|
615
|
+
' ' +
|
|
616
|
+
'check (' +
|
|
617
|
+
operation.constraint.expression +
|
|
618
|
+
')',
|
|
619
|
+
values: [],
|
|
620
|
+
},
|
|
621
|
+
];
|
|
622
|
+
}
|
|
623
|
+
if (operation.kind === 'dropCheck') {
|
|
624
|
+
return [
|
|
625
|
+
{
|
|
626
|
+
text: 'alter table ' +
|
|
627
|
+
quoteTableRef(operation.table) +
|
|
628
|
+
' drop constraint ' +
|
|
629
|
+
quoteIdentifier(operation.name),
|
|
630
|
+
values: [],
|
|
631
|
+
},
|
|
632
|
+
];
|
|
633
|
+
}
|
|
634
|
+
throw new Error('Unsupported data migration operation kind');
|
|
635
|
+
}
|
|
636
|
+
function compilePostgresColumn(definition) {
|
|
637
|
+
let parts = [compilePostgresColumnType(definition)];
|
|
638
|
+
if (definition.nullable === false) {
|
|
639
|
+
parts.push('not null');
|
|
640
|
+
}
|
|
641
|
+
if (definition.default) {
|
|
642
|
+
if (definition.default.kind === 'now') {
|
|
643
|
+
parts.push('default now()');
|
|
644
|
+
}
|
|
645
|
+
else if (definition.default.kind === 'sql') {
|
|
646
|
+
parts.push('default ' + definition.default.expression);
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
parts.push('default ' + quoteLiteral(definition.default.value));
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (definition.primaryKey) {
|
|
653
|
+
parts.push('primary key');
|
|
654
|
+
}
|
|
655
|
+
if (definition.unique) {
|
|
656
|
+
parts.push('unique');
|
|
657
|
+
}
|
|
658
|
+
if (definition.computed) {
|
|
659
|
+
if (!definition.computed.stored) {
|
|
660
|
+
throw new Error('Postgres only supports stored computed/generated columns');
|
|
661
|
+
}
|
|
662
|
+
parts.push('generated always as (' + definition.computed.expression + ') stored');
|
|
663
|
+
}
|
|
664
|
+
if (definition.references) {
|
|
665
|
+
let clause = 'references ' +
|
|
666
|
+
quoteTableRef(definition.references.table) +
|
|
667
|
+
' (' +
|
|
668
|
+
definition.references.columns.map((column) => quoteIdentifier(column)).join(', ') +
|
|
669
|
+
')';
|
|
670
|
+
if (definition.references.onDelete) {
|
|
671
|
+
clause += ' on delete ' + definition.references.onDelete;
|
|
672
|
+
}
|
|
673
|
+
if (definition.references.onUpdate) {
|
|
674
|
+
clause += ' on update ' + definition.references.onUpdate;
|
|
675
|
+
}
|
|
676
|
+
parts.push(clause);
|
|
677
|
+
}
|
|
678
|
+
if (definition.checks && definition.checks.length > 0) {
|
|
679
|
+
for (let check of definition.checks) {
|
|
680
|
+
parts.push('check (' + check.expression + ')');
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return parts.join(' ');
|
|
684
|
+
}
|
|
685
|
+
function compilePostgresColumnType(definition) {
|
|
686
|
+
if (definition.type === 'varchar') {
|
|
687
|
+
return 'varchar(' + String(definition.length ?? 255) + ')';
|
|
688
|
+
}
|
|
689
|
+
if (definition.type === 'text') {
|
|
690
|
+
return 'text';
|
|
691
|
+
}
|
|
692
|
+
if (definition.type === 'integer') {
|
|
693
|
+
return 'integer';
|
|
694
|
+
}
|
|
695
|
+
if (definition.type === 'bigint') {
|
|
696
|
+
return 'bigint';
|
|
697
|
+
}
|
|
698
|
+
if (definition.type === 'decimal') {
|
|
699
|
+
if (definition.precision !== undefined && definition.scale !== undefined) {
|
|
700
|
+
return 'decimal(' + String(definition.precision) + ', ' + String(definition.scale) + ')';
|
|
701
|
+
}
|
|
702
|
+
return 'decimal';
|
|
703
|
+
}
|
|
704
|
+
if (definition.type === 'boolean') {
|
|
705
|
+
return 'boolean';
|
|
706
|
+
}
|
|
707
|
+
if (definition.type === 'uuid') {
|
|
708
|
+
return 'uuid';
|
|
709
|
+
}
|
|
710
|
+
if (definition.type === 'date') {
|
|
711
|
+
return 'date';
|
|
712
|
+
}
|
|
713
|
+
if (definition.type === 'time') {
|
|
714
|
+
return definition.withTimezone ? 'time with time zone' : 'time without time zone';
|
|
715
|
+
}
|
|
716
|
+
if (definition.type === 'timestamp') {
|
|
717
|
+
return definition.withTimezone ? 'timestamp with time zone' : 'timestamp without time zone';
|
|
718
|
+
}
|
|
719
|
+
if (definition.type === 'json') {
|
|
720
|
+
return 'jsonb';
|
|
721
|
+
}
|
|
722
|
+
if (definition.type === 'binary') {
|
|
723
|
+
return 'bytea';
|
|
724
|
+
}
|
|
725
|
+
if (definition.type === 'enum') {
|
|
726
|
+
return 'text';
|
|
727
|
+
}
|
|
728
|
+
return 'text';
|
|
729
|
+
}
|
|
730
|
+
function quoteTableRef(table) {
|
|
731
|
+
return quoteTableRefHelper(table, quoteIdentifier);
|
|
732
|
+
}
|
|
733
|
+
function quoteLiteral(value) {
|
|
734
|
+
return quoteLiteralHelper(value);
|
|
199
735
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
text: string;
|
|
4
|
-
values: unknown[];
|
|
5
|
-
};
|
|
6
|
-
export declare function compilePostgresStatement(statement: AdapterStatement): CompiledSql;
|
|
7
|
-
export {};
|
|
1
|
+
import type { DataManipulationOperation, SqlStatement } from '@remix-run/data-table';
|
|
2
|
+
export declare function compilePostgresOperation(operation: DataManipulationOperation): SqlStatement;
|
|
8
3
|
//# sourceMappingURL=sql-compiler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-compiler.d.ts","sourceRoot":"","sources":["../../src/lib/sql-compiler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"sql-compiler.d.ts","sourceRoot":"","sources":["../../src/lib/sql-compiler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAa,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAe/F,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,yBAAyB,GAAG,YAAY,CAqG3F"}
|