rake-db 2.3.10 → 2.3.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rake-db",
3
- "version": "2.3.10",
3
+ "version": "2.3.13",
4
4
  "description": "Migrations tool for Postgresql DB",
5
5
  "homepage": "https://orchid-orm.netlify.app/guide/migration-setup-and-overview.html",
6
6
  "repository": {
@@ -42,7 +42,7 @@
42
42
  "dependencies": {
43
43
  "enquirer": "^2.3.6",
44
44
  "pluralize": "^8.0.0",
45
- "pqb": "0.9.6"
45
+ "pqb": "0.9.8"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@swc/core": "^1.2.210",
package/src/ast.ts CHANGED
@@ -14,7 +14,8 @@ export type RakeDbAst =
14
14
  | RakeDbAst.ChangeTable
15
15
  | RakeDbAst.RenameTable
16
16
  | RakeDbAst.Schema
17
- | RakeDbAst.Extension;
17
+ | RakeDbAst.Extension
18
+ | RakeDbAst.ForeignKey;
18
19
 
19
20
  export namespace RakeDbAst {
20
21
  export type Table = {
@@ -103,4 +104,11 @@ export namespace RakeDbAst {
103
104
  ifExists?: boolean;
104
105
  ifNotExists?: boolean;
105
106
  };
107
+
108
+ export type ForeignKey = {
109
+ type: 'foreignKey';
110
+ action: 'create';
111
+ tableSchema?: string;
112
+ tableName: string;
113
+ } & TableData.ForeignKey;
106
114
  }
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  Adapter,
3
3
  AdapterOptions,
4
- columnTypes,
5
4
  createDb,
6
5
  DbResult,
6
+ DefaultColumnTypes,
7
7
  MaybeArray,
8
8
  toArray,
9
9
  } from 'pqb';
@@ -24,7 +24,7 @@ import {
24
24
  } from '../migration/change';
25
25
  import { createMigrationInterface } from '../migration/migration';
26
26
 
27
- const getDb = (adapter: Adapter) => createDb({ adapter, columnTypes });
27
+ const getDb = (adapter: Adapter) => createDb({ adapter });
28
28
 
29
29
  const migrateOrRollback = async (
30
30
  options: MaybeArray<AdapterOptions>,
@@ -54,7 +54,7 @@ const migrateOrRollback = async (
54
54
 
55
55
  for (const opts of toArray(options)) {
56
56
  const adapter = new Adapter(opts);
57
- let db: DbResult<typeof columnTypes> | undefined;
57
+ let db: DbResult<DefaultColumnTypes> | undefined;
58
58
 
59
59
  if (up) {
60
60
  await config.beforeMigrate?.((db ??= getDb(adapter)));
package/src/common.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  Adapter,
3
3
  AdapterOptions,
4
- columnTypes,
5
4
  DbResult,
5
+ DefaultColumnTypes,
6
6
  NoPrimaryKeyOption,
7
7
  QueryLogOptions,
8
8
  singleQuote,
@@ -12,7 +12,7 @@ import path from 'path';
12
12
  import { readdir } from 'fs/promises';
13
13
  import { RakeDbAst } from './ast';
14
14
 
15
- type Db = DbResult<typeof columnTypes>;
15
+ type Db = DbResult<DefaultColumnTypes>;
16
16
 
17
17
  export type RakeDbConfig = {
18
18
  migrationsPath: string;
@@ -15,8 +15,8 @@ import {
15
15
  TextColumn,
16
16
  AdapterOptions,
17
17
  createDb,
18
- columnTypes,
19
18
  DbResult,
19
+ DefaultColumnTypes,
20
20
  } from 'pqb';
21
21
  import { createTable } from './createTable';
22
22
  import { changeTable, TableChangeData, TableChanger } from './changeTable';
@@ -63,7 +63,7 @@ export type ExtensionOptions = {
63
63
  cascade?: boolean;
64
64
  };
65
65
 
66
- export type Migration = DbResult<typeof columnTypes> & MigrationBase;
66
+ export type Migration = DbResult<DefaultColumnTypes> & MigrationBase;
67
67
 
68
68
  export const createMigrationInterface = (
69
69
  tx: TransactionAdapter,
@@ -84,7 +84,7 @@ export const createMigrationInterface = (
84
84
  return wrapWithLog(log, q, () => arrays.call(adapter, q, types));
85
85
  }) as typeof adapter.arrays;
86
86
 
87
- const db = createDb({ adapter, columnTypes }) as unknown as Migration;
87
+ const db = createDb({ adapter }) as unknown as Migration;
88
88
 
89
89
  const { prototype: proto } = MigrationBase;
90
90
  for (const key of Object.getOwnPropertyNames(proto)) {
@@ -41,6 +41,25 @@ describe('astToMigration', () => {
41
41
  expect(result).toBe(template(` await db.createSchema('schemaName');`));
42
42
  });
43
43
 
44
+ it('should create extension', () => {
45
+ const result = astToMigration([
46
+ {
47
+ type: 'extension',
48
+ action: 'create',
49
+ name: 'extensionName',
50
+ schema: 'schema',
51
+ version: '123',
52
+ },
53
+ ]);
54
+
55
+ expect(result).toBe(
56
+ template(` await db.createExtension('extensionName', {
57
+ schema: 'schema',
58
+ version: '123',
59
+ })`),
60
+ );
61
+ });
62
+
44
63
  describe('table', () => {
45
64
  it('should create table', () => {
46
65
  const result = astToMigration([
@@ -147,4 +166,41 @@ change(async (db) => {
147
166
  );
148
167
  });
149
168
  });
169
+
170
+ describe('foreignKey', () => {
171
+ it('should add standalone foreignKey', () => {
172
+ const result = astToMigration([
173
+ {
174
+ type: 'foreignKey',
175
+ action: 'create',
176
+ tableSchema: 'custom',
177
+ tableName: 'table',
178
+ columns: ['otherId'],
179
+ fnOrTable: 'otherTable',
180
+ foreignColumns: ['id'],
181
+ options: {
182
+ name: 'fkey',
183
+ match: 'FULL',
184
+ onUpdate: 'CASCADE',
185
+ onDelete: 'CASCADE',
186
+ },
187
+ },
188
+ ]);
189
+
190
+ expect(result).toBe(
191
+ template(` await db.addForeignKey(
192
+ 'custom.table',
193
+ ['otherId'],
194
+ 'otherTable',
195
+ ['id'],
196
+ {
197
+ name: 'fkey',
198
+ match: 'FULL',
199
+ onUpdate: 'CASCADE',
200
+ onDelete: 'CASCADE',
201
+ },
202
+ );`),
203
+ );
204
+ });
205
+ });
150
206
  });
@@ -4,6 +4,7 @@ import {
4
4
  Code,
5
5
  codeToString,
6
6
  ColumnType,
7
+ foreignKeyArgsToCode,
7
8
  foreignKeyToCode,
8
9
  indexToCode,
9
10
  isRaw,
@@ -19,9 +20,15 @@ export const astToMigration = (ast: RakeDbAst[]): string | undefined => {
19
20
  for (const item of ast) {
20
21
  if (item.type === 'schema' && item.action === 'create') {
21
22
  code.push(createSchema(item));
23
+ } else if (item.type === 'extension' && item.action === 'create') {
24
+ if (code.length) code.push([]);
25
+ code.push(...createExtension(item));
22
26
  } else if (item.type === 'table' && item.action === 'create') {
23
27
  if (code.length) code.push([]);
24
28
  code.push(...createTable(item));
29
+ } else if (item.type === 'foreignKey') {
30
+ if (code.length) code.push([]);
31
+ code.push(...createForeignKey(item));
25
32
  }
26
33
  }
27
34
 
@@ -39,6 +46,22 @@ const createSchema = (ast: RakeDbAst.Schema) => {
39
46
  return `await db.createSchema(${singleQuote(ast.name)});`;
40
47
  };
41
48
 
49
+ const createExtension = (ast: RakeDbAst.Extension): Code[] => {
50
+ const code: Code[] = [`await db.createExtension(${singleQuote(ast.name)}`];
51
+ if (ast.schema || ast.version) {
52
+ addCode(code, ', {');
53
+ if (ast.schema) {
54
+ code.push([`schema: ${singleQuote(ast.schema)},`]);
55
+ }
56
+ if (ast.version) {
57
+ code.push([`version: ${singleQuote(ast.version)},`]);
58
+ }
59
+ addCode(code, '}');
60
+ }
61
+ addCode(code, ')');
62
+ return code;
63
+ };
64
+
42
65
  const createTable = (ast: RakeDbAst.Table) => {
43
66
  const code: Code[] = [];
44
67
  addCode(code, `await db.createTable(${quoteSchemaTable(ast)}, (t) => ({`);
@@ -91,3 +114,17 @@ const isTimestamp = (column?: ColumnType) => {
91
114
  def.__raw === 'now()'
92
115
  );
93
116
  };
117
+
118
+ const createForeignKey = (item: RakeDbAst.ForeignKey): Code[] => {
119
+ return [
120
+ `await db.addForeignKey(`,
121
+ [
122
+ `${quoteSchemaTable({
123
+ schema: item.tableSchema,
124
+ name: item.tableName,
125
+ })},`,
126
+ ...foreignKeyArgsToCode(item),
127
+ ],
128
+ ');',
129
+ ];
130
+ };
@@ -523,20 +523,25 @@ describe('structureToAst', () => {
523
523
 
524
524
  it('should add foreign key to the column', async () => {
525
525
  const db = new DbStructure(adapter);
526
- db.getTables = async () => [table];
526
+ db.getTables = async () => [
527
+ { ...table, name: 'table1' },
528
+ { ...table, name: 'table2' },
529
+ ];
527
530
  db.getColumns = async () => [
528
531
  ...columns,
529
- { ...intColumn, name: 'otherId' },
532
+ { ...intColumn, name: 'otherId', tableName: 'table2' },
533
+ ];
534
+ db.getForeignKeys = async () => [
535
+ { ...foreignKey, tableName: 'table2', foreignTableName: 'table1' },
530
536
  ];
531
- db.getForeignKeys = async () => [foreignKey];
532
537
 
533
- const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
538
+ const [, ast] = (await structureToAst(db)) as RakeDbAst.Table[];
534
539
 
535
540
  expect(ast.shape.otherId.data.foreignKeys).toEqual([
536
541
  {
537
542
  columns: ['id'],
538
543
  name: 'fkey',
539
- table: 'otherTable',
544
+ table: 'table1',
540
545
  match: 'FULL',
541
546
  onUpdate: 'CASCADE',
542
547
  onDelete: 'CASCADE',
@@ -547,21 +552,28 @@ describe('structureToAst', () => {
547
552
 
548
553
  it('should ignore standard foreign key name', async () => {
549
554
  const db = new DbStructure(adapter);
550
- db.getTables = async () => [table];
555
+ db.getTables = async () => [
556
+ { ...table, name: 'table1' },
557
+ { ...table, name: 'table2' },
558
+ ];
551
559
  db.getColumns = async () => [
552
- ...columns,
553
- { ...intColumn, name: 'otherId' },
560
+ { ...intColumn, name: 'otherId', tableName: 'table2' },
554
561
  ];
555
562
  db.getForeignKeys = async () => [
556
- { ...foreignKey, name: `${table.name}_otherId_fkey` },
563
+ {
564
+ ...foreignKey,
565
+ name: `table2_otherId_fkey`,
566
+ tableName: 'table2',
567
+ foreignTableName: 'table1',
568
+ },
557
569
  ];
558
570
 
559
- const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
571
+ const [, ast] = (await structureToAst(db)) as RakeDbAst.Table[];
560
572
 
561
573
  expect(ast.shape.otherId.data.foreignKeys).toEqual([
562
574
  {
563
575
  columns: ['id'],
564
- table: 'otherTable',
576
+ table: 'table1',
565
577
  match: 'FULL',
566
578
  onUpdate: 'CASCADE',
567
579
  onDelete: 'CASCADE',
@@ -572,26 +584,30 @@ describe('structureToAst', () => {
572
584
 
573
585
  it('should add composite foreign key', async () => {
574
586
  const db = new DbStructure(adapter);
575
- db.getTables = async () => [table];
587
+ db.getTables = async () => [
588
+ { ...table, name: 'table1' },
589
+ { ...table, name: 'table2' },
590
+ ];
576
591
  db.getColumns = async () => [
577
- ...columns,
578
- { ...intColumn, name: 'otherId' },
592
+ { ...intColumn, name: 'otherId', tableName: 'table2' },
579
593
  ];
580
594
  db.getForeignKeys = async () => [
581
595
  {
582
596
  ...foreignKey,
597
+ tableName: 'table2',
583
598
  columnNames: ['name', 'otherId'],
599
+ foreignTableName: 'table1',
584
600
  foreignColumnNames: ['name', 'id'],
585
601
  },
586
602
  ];
587
603
 
588
- const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
604
+ const [, ast] = (await structureToAst(db)) as RakeDbAst.Table[];
589
605
 
590
606
  expect(ast.shape.otherId.data.foreignKeys).toBe(undefined);
591
607
  expect(ast.foreignKeys).toEqual([
592
608
  {
593
609
  columns: ['name', 'otherId'],
594
- fnOrTable: 'otherTable',
610
+ fnOrTable: 'table1',
595
611
  foreignColumns: ['name', 'id'],
596
612
  options: {
597
613
  name: 'fkey',
@@ -605,27 +621,31 @@ describe('structureToAst', () => {
605
621
 
606
622
  it('should ignore standard foreign key name in a composite foreign key', async () => {
607
623
  const db = new DbStructure(adapter);
608
- db.getTables = async () => [table];
624
+ db.getTables = async () => [
625
+ { ...table, name: 'table1' },
626
+ { ...table, name: 'table2' },
627
+ ];
609
628
  db.getColumns = async () => [
610
- ...columns,
611
- { ...intColumn, name: 'otherId' },
629
+ { ...intColumn, name: 'otherId', tableName: 'table2' },
612
630
  ];
613
631
  db.getForeignKeys = async () => [
614
632
  {
615
633
  ...foreignKey,
634
+ tableName: 'table2',
635
+ foreignTableName: 'table1',
616
636
  columnNames: ['name', 'otherId'],
617
637
  foreignColumnNames: ['name', 'id'],
618
- name: 'table_name_otherId_fkey',
638
+ name: 'table2_name_otherId_fkey',
619
639
  },
620
640
  ];
621
641
 
622
- const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
642
+ const [, ast] = (await structureToAst(db)) as RakeDbAst.Table[];
623
643
 
624
644
  expect(ast.shape.otherId.data.foreignKeys).toBe(undefined);
625
645
  expect(ast.foreignKeys).toEqual([
626
646
  {
627
647
  columns: ['name', 'otherId'],
628
- fnOrTable: 'otherTable',
648
+ fnOrTable: 'table1',
629
649
  foreignColumns: ['name', 'id'],
630
650
  options: {
631
651
  match: 'FULL',
@@ -635,6 +655,120 @@ describe('structureToAst', () => {
635
655
  },
636
656
  ]);
637
657
  });
658
+
659
+ it('should have referenced table before the table with foreign key', async () => {
660
+ const db = new DbStructure(adapter);
661
+ db.getTables = async () => [
662
+ { ...table, name: 'fkTable' },
663
+ { ...table, name: 'table1' },
664
+ { ...table, name: 'table2' },
665
+ { ...table, name: 'otherTable' },
666
+ ];
667
+ db.getColumns = async () => [
668
+ { ...intColumn, name: 'table1Id', tableName: 'fkTable' },
669
+ { ...intColumn, name: 'table2Id', tableName: 'fkTable' },
670
+ ];
671
+ db.getForeignKeys = async () => [
672
+ {
673
+ ...foreignKey,
674
+ tableName: 'fkTable',
675
+ columnNames: ['table1Id'],
676
+ foreignTableName: 'table1',
677
+ },
678
+ {
679
+ ...foreignKey,
680
+ tableName: 'fkTable',
681
+ columnNames: ['table2Id'],
682
+ foreignTableName: 'table2',
683
+ },
684
+ ];
685
+
686
+ const [table1, table2, fkTable, otherTable] = (await structureToAst(
687
+ db,
688
+ )) as RakeDbAst.Table[];
689
+
690
+ expect(table1.name).toBe('table1');
691
+ expect(table2.name).toBe('table2');
692
+ expect(fkTable.name).toBe('fkTable');
693
+ expect(otherTable.name).toBe('otherTable');
694
+ });
695
+
696
+ it('should add foreign key to a same table', async () => {
697
+ const db = new DbStructure(adapter);
698
+ db.getTables = async () => [table];
699
+ db.getColumns = async () => [intColumn];
700
+ db.getForeignKeys = async () => [
701
+ {
702
+ ...foreignKey,
703
+ tableName: table.name,
704
+ columnNames: [intColumn.name],
705
+ foreignTableName: table.name,
706
+ },
707
+ ];
708
+
709
+ const [ast] = (await structureToAst(db)) as RakeDbAst.Table[];
710
+
711
+ expect(ast.name).toBe(table.name);
712
+ });
713
+
714
+ it('should add standalone foreign key when it is recursive', async () => {
715
+ const db = new DbStructure(adapter);
716
+ db.getTables = async () => [
717
+ { ...table, name: 'table1' },
718
+ { ...table, name: 'table2' },
719
+ ];
720
+ db.getColumns = async () => [
721
+ { ...intColumn, tableName: 'table1' },
722
+ { ...intColumn, tableName: 'table2' },
723
+ ];
724
+ db.getForeignKeys = async () => [
725
+ {
726
+ ...foreignKey,
727
+ tableName: 'table1',
728
+ columnNames: [intColumn.name],
729
+ foreignTableName: 'table2',
730
+ },
731
+ {
732
+ ...foreignKey,
733
+ tableName: 'table2',
734
+ columnNames: [intColumn.name],
735
+ foreignTableName: 'table1',
736
+ },
737
+ ];
738
+
739
+ const [table1, table2, fkey] = (await structureToAst(
740
+ db,
741
+ )) as RakeDbAst.Table[];
742
+
743
+ expect(table1.name).toBe('table1');
744
+ expect(table1.shape[intColumn.name].data.foreignKeys).toBe(undefined);
745
+ expect(table2.name).toBe('table2');
746
+ expect(table2.shape[intColumn.name].data.foreignKeys).toEqual([
747
+ {
748
+ table: 'table1',
749
+ columns: ['id'],
750
+ match: 'FULL',
751
+ name: 'fkey',
752
+ onUpdate: 'CASCADE',
753
+ onDelete: 'CASCADE',
754
+ },
755
+ ]);
756
+
757
+ expect(fkey).toEqual({
758
+ type: 'foreignKey',
759
+ action: 'create',
760
+ tableName: 'table1',
761
+ columns: ['column'],
762
+ fnOrTable: 'table2',
763
+ foreignColumns: ['id'],
764
+ options: {
765
+ match: 'FULL',
766
+ name: 'fkey',
767
+ onDelete: 'CASCADE',
768
+ onUpdate: 'CASCADE',
769
+ },
770
+ });
771
+ });
638
772
  });
639
773
 
640
774
  describe('extension', () => {