rake-db 2.2.5 → 2.3.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/CHANGELOG.md +21 -0
- package/db.ts +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.esm.js +170 -120
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +170 -120
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/ast.ts +7 -3
- package/src/commands/migrateOrRollback.ts +8 -6
- package/src/common.test.ts +8 -6
- package/src/common.ts +20 -10
- package/src/migration/changeTable.test.ts +27 -15
- package/src/migration/changeTable.ts +103 -87
- package/src/migration/createJoinTable.test.ts +96 -0
- package/src/migration/createJoinTable.ts +17 -6
- package/src/migration/createTable.test.ts +18 -3
- package/src/migration/createTable.ts +11 -8
- package/src/migration/migration.test.ts +18 -86
- package/src/migration/migration.ts +18 -4
- package/src/migration/migrationUtils.ts +58 -40
- package/src/pull/dbStructure.test.ts +166 -0
- package/src/pull/dbStructure.ts +392 -0
- package/src/pull/getColumnByType.ts +15 -0
- package/src/pull/structureToAst.test.ts +476 -0
- package/src/pull/structureToAst.ts +196 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getMigrationFiles,
|
|
5
5
|
RakeDbConfig,
|
|
6
6
|
MigrationFile,
|
|
7
|
-
|
|
7
|
+
quoteWithSchema,
|
|
8
8
|
} from '../common';
|
|
9
9
|
import {
|
|
10
10
|
getCurrentPromise,
|
|
@@ -105,7 +105,9 @@ const saveMigratedVersion = async (
|
|
|
105
105
|
config: RakeDbConfig,
|
|
106
106
|
) => {
|
|
107
107
|
await db.query(
|
|
108
|
-
`INSERT INTO ${
|
|
108
|
+
`INSERT INTO ${quoteWithSchema({
|
|
109
|
+
name: config.migrationsTable,
|
|
110
|
+
})} VALUES ('${version}')`,
|
|
109
111
|
);
|
|
110
112
|
};
|
|
111
113
|
|
|
@@ -115,9 +117,9 @@ const removeMigratedVersion = async (
|
|
|
115
117
|
config: RakeDbConfig,
|
|
116
118
|
) => {
|
|
117
119
|
await db.query(
|
|
118
|
-
`DELETE FROM ${
|
|
119
|
-
config.migrationsTable,
|
|
120
|
-
)} WHERE version = '${version}'`,
|
|
120
|
+
`DELETE FROM ${quoteWithSchema({
|
|
121
|
+
name: config.migrationsTable,
|
|
122
|
+
})} WHERE version = '${version}'`,
|
|
121
123
|
);
|
|
122
124
|
};
|
|
123
125
|
|
|
@@ -127,7 +129,7 @@ const getMigratedVersionsMap = async (
|
|
|
127
129
|
): Promise<Record<string, boolean>> => {
|
|
128
130
|
try {
|
|
129
131
|
const result = await db.arrays<[string]>(
|
|
130
|
-
`SELECT * FROM ${
|
|
132
|
+
`SELECT * FROM ${quoteWithSchema({ name: config.migrationsTable })}`,
|
|
131
133
|
);
|
|
132
134
|
return Object.fromEntries(result.rows.map((row) => [row[0], true]));
|
|
133
135
|
} catch (err) {
|
package/src/common.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
joinColumns,
|
|
9
9
|
joinWords,
|
|
10
10
|
migrationConfigDefaults,
|
|
11
|
-
|
|
11
|
+
quoteWithSchema,
|
|
12
12
|
setAdapterOptions,
|
|
13
13
|
setAdminCredentialsToOptions,
|
|
14
14
|
sortAsc,
|
|
@@ -285,13 +285,15 @@ describe('common', () => {
|
|
|
285
285
|
});
|
|
286
286
|
});
|
|
287
287
|
|
|
288
|
-
describe('
|
|
289
|
-
it('should quote a
|
|
290
|
-
expect(
|
|
288
|
+
describe('quoteWithSchema', () => {
|
|
289
|
+
it('should quote a name', () => {
|
|
290
|
+
expect(quoteWithSchema({ name: 'table' })).toBe('"table"');
|
|
291
291
|
});
|
|
292
292
|
|
|
293
|
-
it('should quote a
|
|
294
|
-
expect(
|
|
293
|
+
it('should quote a name with schema', () => {
|
|
294
|
+
expect(quoteWithSchema({ schema: 'schema', name: 'table' })).toBe(
|
|
295
|
+
'"schema"."table"',
|
|
296
|
+
);
|
|
295
297
|
});
|
|
296
298
|
});
|
|
297
299
|
});
|
package/src/common.ts
CHANGED
|
@@ -126,9 +126,9 @@ export const createSchemaMigrations = async (
|
|
|
126
126
|
) => {
|
|
127
127
|
try {
|
|
128
128
|
await db.query(
|
|
129
|
-
`CREATE TABLE ${
|
|
130
|
-
config.migrationsTable,
|
|
131
|
-
)} ( version TEXT NOT NULL )`,
|
|
129
|
+
`CREATE TABLE ${quoteWithSchema({
|
|
130
|
+
name: config.migrationsTable,
|
|
131
|
+
})} ( version TEXT NOT NULL )`,
|
|
132
132
|
);
|
|
133
133
|
console.log('Created versions table');
|
|
134
134
|
} catch (err) {
|
|
@@ -235,11 +235,21 @@ export const joinColumns = (columns: string[]) => {
|
|
|
235
235
|
return columns.map((column) => `"${column}"`).join(', ');
|
|
236
236
|
};
|
|
237
237
|
|
|
238
|
-
export const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
238
|
+
export const quoteWithSchema = ({
|
|
239
|
+
schema,
|
|
240
|
+
name,
|
|
241
|
+
}: {
|
|
242
|
+
schema?: string;
|
|
243
|
+
name: string;
|
|
244
|
+
}) => {
|
|
245
|
+
return schema ? `"${schema}"."${name}"` : `"${name}"`;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export const getSchemaAndTableFromName = (
|
|
249
|
+
name: string,
|
|
250
|
+
): [string | undefined, string] => {
|
|
251
|
+
const index = name.indexOf('.');
|
|
252
|
+
return index !== -1
|
|
253
|
+
? [name.slice(0, index), name.slice(index + 1)]
|
|
254
|
+
: [undefined, name];
|
|
245
255
|
};
|
|
@@ -18,6 +18,23 @@ describe('changeTable', () => {
|
|
|
18
18
|
expect(db.options.appCodeUpdater).toHaveBeenCalled();
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
it('should work for table with schema', async () => {
|
|
22
|
+
const fn = () => {
|
|
23
|
+
return db.changeTable('schema.table', (t) => ({
|
|
24
|
+
column: t.add(t.text()),
|
|
25
|
+
}));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
await fn();
|
|
29
|
+
expectSql(
|
|
30
|
+
`ALTER TABLE "schema"."table"\nADD COLUMN "column" text NOT NULL`,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
setDbDown();
|
|
34
|
+
await fn();
|
|
35
|
+
expectSql(`ALTER TABLE "schema"."table"\nDROP COLUMN "column"`);
|
|
36
|
+
});
|
|
37
|
+
|
|
21
38
|
it('should set comment', async () => {
|
|
22
39
|
const fn = () => {
|
|
23
40
|
return db.changeTable('table', { comment: 'comment' });
|
|
@@ -61,9 +78,8 @@ describe('changeTable', () => {
|
|
|
61
78
|
name: 'indexName',
|
|
62
79
|
unique: true,
|
|
63
80
|
using: 'gin',
|
|
64
|
-
expression: 10,
|
|
65
81
|
collate: 'utf-8',
|
|
66
|
-
|
|
82
|
+
opclass: 'opclass',
|
|
67
83
|
order: 'ASC',
|
|
68
84
|
include: 'id',
|
|
69
85
|
with: 'fillfactor = 70',
|
|
@@ -116,7 +132,7 @@ describe('changeTable', () => {
|
|
|
116
132
|
CREATE UNIQUE INDEX "indexName"
|
|
117
133
|
ON "table"
|
|
118
134
|
USING gin
|
|
119
|
-
("withIndex"
|
|
135
|
+
("withIndex" COLLATE 'utf-8' opclass ASC)
|
|
120
136
|
INCLUDE ("id")
|
|
121
137
|
WITH (fillfactor = 70)
|
|
122
138
|
TABLESPACE tablespace
|
|
@@ -587,9 +603,8 @@ describe('changeTable', () => {
|
|
|
587
603
|
addIndexWithOptions: t.change(
|
|
588
604
|
t.integer(),
|
|
589
605
|
t.integer().index({
|
|
590
|
-
expression: 'expression',
|
|
591
606
|
collate: 'collate',
|
|
592
|
-
|
|
607
|
+
opclass: 'opclass',
|
|
593
608
|
order: 'order',
|
|
594
609
|
unique: true,
|
|
595
610
|
using: 'using',
|
|
@@ -603,9 +618,8 @@ describe('changeTable', () => {
|
|
|
603
618
|
removeIndex: t.change(t.integer().index(), t.integer()),
|
|
604
619
|
removeIndexWithOptions: t.change(
|
|
605
620
|
t.integer().index({
|
|
606
|
-
expression: 'expression',
|
|
607
621
|
collate: 'collate',
|
|
608
|
-
|
|
622
|
+
opclass: 'opclass',
|
|
609
623
|
order: 'order',
|
|
610
624
|
unique: true,
|
|
611
625
|
using: 'using',
|
|
@@ -620,9 +634,8 @@ describe('changeTable', () => {
|
|
|
620
634
|
changeIndex: t.change(
|
|
621
635
|
t.integer().index({
|
|
622
636
|
name: 'from',
|
|
623
|
-
expression: 'from',
|
|
624
637
|
collate: 'from',
|
|
625
|
-
|
|
638
|
+
opclass: 'from',
|
|
626
639
|
order: 'from',
|
|
627
640
|
unique: false,
|
|
628
641
|
using: 'from',
|
|
@@ -634,9 +647,8 @@ describe('changeTable', () => {
|
|
|
634
647
|
}),
|
|
635
648
|
t.integer().index({
|
|
636
649
|
name: 'to',
|
|
637
|
-
expression: 'to',
|
|
638
650
|
collate: 'to',
|
|
639
|
-
|
|
651
|
+
opclass: 'to',
|
|
640
652
|
order: 'to',
|
|
641
653
|
unique: true,
|
|
642
654
|
using: 'to',
|
|
@@ -656,8 +668,8 @@ describe('changeTable', () => {
|
|
|
656
668
|
`DROP INDEX "tableRemoveIndexWithOptionsIndex" CASCADE`,
|
|
657
669
|
`DROP INDEX "from" CASCADE`,
|
|
658
670
|
`CREATE INDEX "tableAddIndexIndex" ON "table" ("addIndex")`,
|
|
659
|
-
`CREATE UNIQUE INDEX "tableAddIndexWithOptionsIndex" ON "table" USING using ("addIndexWithOptions"
|
|
660
|
-
`CREATE UNIQUE INDEX "to" ON "table" USING to ("changeIndex"
|
|
671
|
+
`CREATE UNIQUE INDEX "tableAddIndexWithOptionsIndex" ON "table" USING using ("addIndexWithOptions" COLLATE 'collate' opclass order) INCLUDE ("a", "b") WITH (with) TABLESPACE tablespace WHERE where`,
|
|
672
|
+
`CREATE UNIQUE INDEX "to" ON "table" USING to ("changeIndex" COLLATE 'to' to to) INCLUDE ("c", "d") WITH (to) TABLESPACE to WHERE to`,
|
|
661
673
|
]);
|
|
662
674
|
|
|
663
675
|
queryMock.mockClear();
|
|
@@ -668,8 +680,8 @@ describe('changeTable', () => {
|
|
|
668
680
|
`DROP INDEX "tableAddIndexWithOptionsIndex" CASCADE`,
|
|
669
681
|
`DROP INDEX "to" RESTRICT`,
|
|
670
682
|
`CREATE INDEX "tableRemoveIndexIndex" ON "table" ("removeIndex")`,
|
|
671
|
-
`CREATE UNIQUE INDEX "tableRemoveIndexWithOptionsIndex" ON "table" USING using ("removeIndexWithOptions"
|
|
672
|
-
`CREATE INDEX "from" ON "table" USING from ("changeIndex"
|
|
683
|
+
`CREATE UNIQUE INDEX "tableRemoveIndexWithOptionsIndex" ON "table" USING using ("removeIndexWithOptions" COLLATE 'collate' opclass order) INCLUDE ("a", "b") WITH (with) TABLESPACE tablespace WHERE where`,
|
|
684
|
+
`CREATE INDEX "from" ON "table" USING from ("changeIndex" COLLATE 'from' from from) INCLUDE ("a", "b") WITH (from) TABLESPACE from WHERE from`,
|
|
673
685
|
]);
|
|
674
686
|
});
|
|
675
687
|
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
runCodeUpdater,
|
|
24
24
|
} from './migration';
|
|
25
25
|
import { RakeDbAst } from '../ast';
|
|
26
|
-
import {
|
|
26
|
+
import { getSchemaAndTableFromName, quoteWithSchema } from '../common';
|
|
27
27
|
import {
|
|
28
28
|
addColumnComment,
|
|
29
29
|
addColumnIndex,
|
|
@@ -127,18 +127,18 @@ const columnTypeToColumnChange = (
|
|
|
127
127
|
item: ColumnType | Change,
|
|
128
128
|
): RakeDbAst.ColumnChange => {
|
|
129
129
|
if (item instanceof ColumnType) {
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
130
|
+
const foreignKeys = item.data.foreignKeys;
|
|
131
|
+
if (foreignKeys?.some((it) => 'fn' in it)) {
|
|
132
132
|
throw new Error('Callback in foreignKey is not allowed in migration');
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
return {
|
|
136
136
|
column: item,
|
|
137
137
|
type: item.toSQL(),
|
|
138
|
-
nullable: item.isNullable,
|
|
138
|
+
nullable: item.data.isNullable,
|
|
139
139
|
primaryKey: item.isPrimaryKey,
|
|
140
140
|
...item.data,
|
|
141
|
-
|
|
141
|
+
foreignKeys: foreignKeys as RakeDbAst.ColumnChange['foreignKeys'],
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -257,9 +257,12 @@ const makeAst = (
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
const [schema, table] = getSchemaAndTableFromName(name);
|
|
261
|
+
|
|
260
262
|
return {
|
|
261
263
|
type: 'changeTable',
|
|
262
|
-
|
|
264
|
+
schema,
|
|
265
|
+
name: table,
|
|
263
266
|
comment: comment
|
|
264
267
|
? up
|
|
265
268
|
? Array.isArray(comment)
|
|
@@ -287,7 +290,7 @@ const astToQueries = (ast: RakeDbAst.ChangeTable): Sql[] => {
|
|
|
287
290
|
|
|
288
291
|
if (ast.comment !== undefined) {
|
|
289
292
|
result.push({
|
|
290
|
-
text: `COMMENT ON TABLE ${
|
|
293
|
+
text: `COMMENT ON TABLE ${quoteWithSchema(ast)} IS ${quote(ast.comment)}`,
|
|
291
294
|
values: [],
|
|
292
295
|
});
|
|
293
296
|
}
|
|
@@ -392,83 +395,96 @@ const astToQueries = (ast: RakeDbAst.ChangeTable): Sql[] => {
|
|
|
392
395
|
);
|
|
393
396
|
}
|
|
394
397
|
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
fromFkey
|
|
405
|
-
fromFkey
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
398
|
+
const foreignKeysLen = Math.max(
|
|
399
|
+
from.foreignKeys?.length || 0,
|
|
400
|
+
to.foreignKeys?.length || 0,
|
|
401
|
+
);
|
|
402
|
+
for (let i = 0; i < foreignKeysLen; i++) {
|
|
403
|
+
const fromFkey = from.foreignKeys?.[i];
|
|
404
|
+
const toFkey = to.foreignKeys?.[i];
|
|
405
|
+
|
|
406
|
+
if (
|
|
407
|
+
(fromFkey || toFkey) &&
|
|
408
|
+
(!fromFkey ||
|
|
409
|
+
!toFkey ||
|
|
410
|
+
fromFkey.name !== toFkey.name ||
|
|
411
|
+
fromFkey.match !== toFkey.match ||
|
|
412
|
+
fromFkey.onUpdate !== toFkey.onUpdate ||
|
|
413
|
+
fromFkey.onDelete !== toFkey.onDelete ||
|
|
414
|
+
fromFkey.dropMode !== toFkey.dropMode ||
|
|
415
|
+
fromFkey.table !== toFkey.table ||
|
|
416
|
+
fromFkey.columns.join(',') !== toFkey.columns.join(','))
|
|
417
|
+
) {
|
|
418
|
+
if (fromFkey) {
|
|
419
|
+
dropForeignKeys.push({
|
|
420
|
+
columns: [key],
|
|
421
|
+
fnOrTable: fromFkey.table,
|
|
422
|
+
foreignColumns: fromFkey.columns,
|
|
423
|
+
options: fromFkey,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (toFkey) {
|
|
428
|
+
addForeignKeys.push({
|
|
429
|
+
columns: [key],
|
|
430
|
+
fnOrTable: toFkey.table,
|
|
431
|
+
foreignColumns: toFkey.columns,
|
|
432
|
+
options: toFkey,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
425
435
|
}
|
|
426
436
|
}
|
|
427
437
|
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
fromIndex
|
|
438
|
-
fromIndex
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
fromIndex.
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
438
|
+
const indexesLen = Math.max(
|
|
439
|
+
from.indexes?.length || 0,
|
|
440
|
+
to.indexes?.length || 0,
|
|
441
|
+
);
|
|
442
|
+
for (let i = 0; i < indexesLen; i++) {
|
|
443
|
+
const fromIndex = from.indexes?.[i];
|
|
444
|
+
const toIndex = to.indexes?.[i];
|
|
445
|
+
|
|
446
|
+
if (
|
|
447
|
+
(fromIndex || toIndex) &&
|
|
448
|
+
(!fromIndex ||
|
|
449
|
+
!toIndex ||
|
|
450
|
+
fromIndex.collate !== toIndex.collate ||
|
|
451
|
+
fromIndex.opclass !== toIndex.opclass ||
|
|
452
|
+
fromIndex.order !== toIndex.order ||
|
|
453
|
+
fromIndex.name !== toIndex.name ||
|
|
454
|
+
fromIndex.unique !== toIndex.unique ||
|
|
455
|
+
fromIndex.using !== toIndex.using ||
|
|
456
|
+
fromIndex.include !== toIndex.include ||
|
|
457
|
+
(Array.isArray(fromIndex.include) &&
|
|
458
|
+
Array.isArray(toIndex.include) &&
|
|
459
|
+
fromIndex.include.join(',') !== toIndex.include.join(',')) ||
|
|
460
|
+
fromIndex.with !== toIndex.with ||
|
|
461
|
+
fromIndex.tablespace !== toIndex.tablespace ||
|
|
462
|
+
fromIndex.where !== toIndex.where ||
|
|
463
|
+
fromIndex.dropMode !== toIndex.dropMode)
|
|
464
|
+
) {
|
|
465
|
+
if (fromIndex) {
|
|
466
|
+
dropIndexes.push({
|
|
467
|
+
columns: [
|
|
468
|
+
{
|
|
469
|
+
column: key,
|
|
470
|
+
...fromIndex,
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
options: fromIndex,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (toIndex) {
|
|
478
|
+
addIndexes.push({
|
|
479
|
+
columns: [
|
|
480
|
+
{
|
|
481
|
+
column: key,
|
|
482
|
+
...toIndex,
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
options: toIndex,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
472
488
|
}
|
|
473
489
|
}
|
|
474
490
|
|
|
@@ -493,7 +509,7 @@ const astToQueries = (ast: RakeDbAst.ChangeTable): Sql[] => {
|
|
|
493
509
|
|
|
494
510
|
prependAlterTable.push(
|
|
495
511
|
...dropForeignKeys.map(
|
|
496
|
-
(foreignKey) => `\n DROP ${constraintToSql(ast
|
|
512
|
+
(foreignKey) => `\n DROP ${constraintToSql(ast, false, foreignKey)}`,
|
|
497
513
|
),
|
|
498
514
|
);
|
|
499
515
|
|
|
@@ -509,22 +525,22 @@ const astToQueries = (ast: RakeDbAst.ChangeTable): Sql[] => {
|
|
|
509
525
|
|
|
510
526
|
alterTable.push(
|
|
511
527
|
...addForeignKeys.map(
|
|
512
|
-
(foreignKey) => `\n ADD ${constraintToSql(ast
|
|
528
|
+
(foreignKey) => `\n ADD ${constraintToSql(ast, true, foreignKey)}`,
|
|
513
529
|
),
|
|
514
530
|
);
|
|
515
531
|
|
|
516
532
|
if (alterTable.length) {
|
|
517
533
|
result.push({
|
|
518
534
|
text:
|
|
519
|
-
`ALTER TABLE ${
|
|
535
|
+
`ALTER TABLE ${quoteWithSchema(ast)}` +
|
|
520
536
|
`\n ${alterTable.join(',\n ')}`,
|
|
521
537
|
values,
|
|
522
538
|
});
|
|
523
539
|
}
|
|
524
540
|
|
|
525
|
-
result.push(...indexesToQuery(false, ast
|
|
526
|
-
result.push(...indexesToQuery(true, ast
|
|
527
|
-
result.push(...commentsToQuery(ast
|
|
541
|
+
result.push(...indexesToQuery(false, ast, dropIndexes));
|
|
542
|
+
result.push(...indexesToQuery(true, ast, addIndexes));
|
|
543
|
+
result.push(...commentsToQuery(ast, comments));
|
|
528
544
|
|
|
529
545
|
return result;
|
|
530
546
|
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getPrimaryKeysOfTable } from './migrationUtils';
|
|
2
|
+
import { expectSql, getDb, queryMock, resetDb } from '../test-utils';
|
|
3
|
+
|
|
4
|
+
const db = getDb();
|
|
5
|
+
|
|
6
|
+
jest.mock('./migrationUtils', () => ({
|
|
7
|
+
...jest.requireActual('./migrationUtils'),
|
|
8
|
+
getPrimaryKeysOfTable: jest.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe('join table', () => {
|
|
12
|
+
beforeEach(resetDb);
|
|
13
|
+
|
|
14
|
+
(['createJoinTable', 'dropJoinTable'] as const).forEach((action) => {
|
|
15
|
+
describe(action, () => {
|
|
16
|
+
it(`should ${
|
|
17
|
+
action === 'createJoinTable' ? 'create' : 'drop'
|
|
18
|
+
} a join table`, async () => {
|
|
19
|
+
const fn = () => {
|
|
20
|
+
return db[action](['posts', 'comments'], (t) => ({
|
|
21
|
+
...t.timestamps(),
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const expectCreateTable = async () => {
|
|
26
|
+
(getPrimaryKeysOfTable as jest.Mock)
|
|
27
|
+
.mockResolvedValueOnce([
|
|
28
|
+
{
|
|
29
|
+
name: 'uuid',
|
|
30
|
+
type: 'uuid',
|
|
31
|
+
},
|
|
32
|
+
])
|
|
33
|
+
.mockResolvedValueOnce([
|
|
34
|
+
{
|
|
35
|
+
name: 'id',
|
|
36
|
+
type: 'integer',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'authorName',
|
|
40
|
+
type: 'text',
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
await fn();
|
|
45
|
+
|
|
46
|
+
expectSql(`
|
|
47
|
+
CREATE TABLE "postsComments" (
|
|
48
|
+
"postUuid" uuid NOT NULL REFERENCES "posts"("uuid"),
|
|
49
|
+
"commentId" integer NOT NULL,
|
|
50
|
+
"commentAuthorName" text NOT NULL,
|
|
51
|
+
"createdAt" timestamp NOT NULL DEFAULT now(),
|
|
52
|
+
"updatedAt" timestamp NOT NULL DEFAULT now(),
|
|
53
|
+
PRIMARY KEY ("postUuid", "commentId", "commentAuthorName"),
|
|
54
|
+
CONSTRAINT "postsComments_commentId_commentAuthorName_fkey" FOREIGN KEY ("commentId", "commentAuthorName") REFERENCES "comments"("id", "authorName")
|
|
55
|
+
)
|
|
56
|
+
`);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const expectDropTable = async () => {
|
|
60
|
+
await fn();
|
|
61
|
+
|
|
62
|
+
expectSql(`
|
|
63
|
+
DROP TABLE "postsComments"
|
|
64
|
+
`);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await (action === 'createJoinTable'
|
|
68
|
+
? expectCreateTable
|
|
69
|
+
: expectDropTable)();
|
|
70
|
+
|
|
71
|
+
db.up = false;
|
|
72
|
+
queryMock.mockClear();
|
|
73
|
+
await (action === 'createJoinTable'
|
|
74
|
+
? expectDropTable
|
|
75
|
+
: expectCreateTable)();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should throw error if table has no primary key', async () => {
|
|
79
|
+
db.up = action !== 'dropJoinTable';
|
|
80
|
+
|
|
81
|
+
(getPrimaryKeysOfTable as jest.Mock)
|
|
82
|
+
.mockResolvedValueOnce([
|
|
83
|
+
{
|
|
84
|
+
name: 'id',
|
|
85
|
+
type: 'integer',
|
|
86
|
+
},
|
|
87
|
+
])
|
|
88
|
+
.mockResolvedValueOnce([]);
|
|
89
|
+
|
|
90
|
+
await expect(db[action](['posts', 'comments'])).rejects.toThrow(
|
|
91
|
+
'Primary key for table "comments" is not defined',
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { ColumnType, Operators } from 'pqb';
|
|
2
2
|
import { ColumnsShapeCallback, JoinTableOptions, Migration } from './migration';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getSchemaAndTableFromName,
|
|
5
|
+
joinWords,
|
|
6
|
+
quoteWithSchema,
|
|
7
|
+
} from '../common';
|
|
4
8
|
import { getPrimaryKeysOfTable } from './migrationUtils';
|
|
5
9
|
import { singular } from 'pluralize';
|
|
6
10
|
import { createTable } from './createTable';
|
|
@@ -46,26 +50,33 @@ export const createJoinTable = async (
|
|
|
46
50
|
})),
|
|
47
51
|
);
|
|
48
52
|
|
|
53
|
+
const [schema, name] = getSchemaAndTableFromName(table);
|
|
49
54
|
if (!primaryKeys.length) {
|
|
50
55
|
throw new Error(
|
|
51
|
-
`Primary key for table ${
|
|
56
|
+
`Primary key for table ${quoteWithSchema({
|
|
57
|
+
schema,
|
|
58
|
+
name,
|
|
59
|
+
})} is not defined`,
|
|
52
60
|
);
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
return [table, primaryKeys] as const;
|
|
63
|
+
return [schema, table, primaryKeys] as const;
|
|
56
64
|
}),
|
|
57
65
|
);
|
|
58
66
|
|
|
59
67
|
return createTable(migration, up, tableName, options, (t) => {
|
|
60
68
|
const result: Record<string, ColumnType> = {};
|
|
61
69
|
|
|
62
|
-
tablesWithPrimaryKeys.forEach(([table, primaryKeys]) => {
|
|
70
|
+
tablesWithPrimaryKeys.forEach(([schema, table, primaryKeys]) => {
|
|
63
71
|
if (primaryKeys.length === 1) {
|
|
64
72
|
const [{ type, joinedName, name }] = primaryKeys;
|
|
65
73
|
|
|
66
74
|
const column = new UnknownColumn(type);
|
|
67
75
|
|
|
68
|
-
result[joinedName] = column.foreignKey(
|
|
76
|
+
result[joinedName] = column.foreignKey(
|
|
77
|
+
schema ? `${schema}.${table}` : table,
|
|
78
|
+
name,
|
|
79
|
+
);
|
|
69
80
|
|
|
70
81
|
return;
|
|
71
82
|
}
|
|
@@ -86,7 +97,7 @@ export const createJoinTable = async (
|
|
|
86
97
|
}
|
|
87
98
|
|
|
88
99
|
t.primaryKey(
|
|
89
|
-
tablesWithPrimaryKeys.flatMap(([, primaryKeys]) =>
|
|
100
|
+
tablesWithPrimaryKeys.flatMap(([, , primaryKeys]) =>
|
|
90
101
|
primaryKeys.map((item) => item.joinedName),
|
|
91
102
|
),
|
|
92
103
|
);
|