rake-db 2.3.2 → 2.3.4

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.
@@ -1,7 +1,7 @@
1
1
  import { change } from '../src';
2
2
 
3
3
  change(async (db) => {
4
- await db.createJoinTable(['chat', 'user'], (t) => ({
4
+ await db.createTable('chatUser', (t) => ({
5
5
  chatId: t.integer().foreignKey('chat', 'id'),
6
6
  userId: t.integer().foreignKey('user', 'id'),
7
7
  ...t.timestamps(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rake-db",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
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": {
@@ -139,7 +139,7 @@ describe('changeTable', () => {
139
139
  WHERE column = 123
140
140
  `),
141
141
  toLine(`
142
- CREATE UNIQUE INDEX "tableUniqueColumnIndex"
142
+ CREATE UNIQUE INDEX "table_uniqueColumn_idx"
143
143
  ON "table"
144
144
  ("uniqueColumn")
145
145
  `),
@@ -169,7 +169,7 @@ describe('changeTable', () => {
169
169
  DROP COLUMN "updatedAt"
170
170
  `,
171
171
  toLine(`DROP INDEX "indexName"`),
172
- toLine(`DROP INDEX "tableUniqueColumnIndex" CASCADE`),
172
+ toLine(`DROP INDEX "table_uniqueColumn_idx" CASCADE`),
173
173
  ]);
174
174
  };
175
175
 
@@ -664,11 +664,11 @@ describe('changeTable', () => {
664
664
 
665
665
  await fn();
666
666
  expectSql([
667
- `DROP INDEX "tableRemoveIndexIndex"`,
668
- `DROP INDEX "tableRemoveIndexWithOptionsIndex" CASCADE`,
667
+ `DROP INDEX "table_removeIndex_idx"`,
668
+ `DROP INDEX "table_removeIndexWithOptions_idx" CASCADE`,
669
669
  `DROP INDEX "from" CASCADE`,
670
- `CREATE INDEX "tableAddIndexIndex" ON "table" ("addIndex")`,
671
- `CREATE UNIQUE INDEX "tableAddIndexWithOptionsIndex" ON "table" USING using ("addIndexWithOptions" COLLATE 'collate' opclass order) INCLUDE ("a", "b") WITH (with) TABLESPACE tablespace WHERE where`,
670
+ `CREATE INDEX "table_addIndex_idx" ON "table" ("addIndex")`,
671
+ `CREATE UNIQUE INDEX "table_addIndexWithOptions_idx" ON "table" USING using ("addIndexWithOptions" COLLATE 'collate' opclass order) INCLUDE ("a", "b") WITH (with) TABLESPACE tablespace WHERE where`,
672
672
  `CREATE UNIQUE INDEX "to" ON "table" USING to ("changeIndex" COLLATE 'to' to to) INCLUDE ("c", "d") WITH (to) TABLESPACE to WHERE to`,
673
673
  ]);
674
674
 
@@ -676,11 +676,11 @@ describe('changeTable', () => {
676
676
  db.up = false;
677
677
  await fn();
678
678
  expectSql([
679
- `DROP INDEX "tableAddIndexIndex"`,
680
- `DROP INDEX "tableAddIndexWithOptionsIndex" CASCADE`,
679
+ `DROP INDEX "table_addIndex_idx"`,
680
+ `DROP INDEX "table_addIndexWithOptions_idx" CASCADE`,
681
681
  `DROP INDEX "to" RESTRICT`,
682
- `CREATE INDEX "tableRemoveIndexIndex" ON "table" ("removeIndex")`,
683
- `CREATE UNIQUE INDEX "tableRemoveIndexWithOptionsIndex" ON "table" USING using ("removeIndexWithOptions" COLLATE 'collate' opclass order) INCLUDE ("a", "b") WITH (with) TABLESPACE tablespace WHERE where`,
682
+ `CREATE INDEX "table_removeIndex_idx" ON "table" ("removeIndex")`,
683
+ `CREATE UNIQUE INDEX "table_removeIndexWithOptions_idx" ON "table" USING using ("removeIndexWithOptions" COLLATE 'collate' opclass order) INCLUDE ("a", "b") WITH (with) TABLESPACE tablespace WHERE where`,
684
684
  `CREATE INDEX "from" ON "table" USING from ("changeIndex" COLLATE 'from' from from) INCLUDE ("a", "b") WITH (from) TABLESPACE from WHERE from`,
685
685
  ]);
686
686
  });
@@ -119,7 +119,7 @@ const db = getDb();
119
119
  WHERE column = 123
120
120
  `),
121
121
  toLine(`
122
- CREATE UNIQUE INDEX "tableUniqueColumnIndex"
122
+ CREATE UNIQUE INDEX "table_uniqueColumn_idx"
123
123
  ON "table"
124
124
  ("uniqueColumn")
125
125
  `),
@@ -26,7 +26,6 @@ import {
26
26
  quoteWithSchema,
27
27
  getSchemaAndTableFromName,
28
28
  } from '../common';
29
- import { createJoinTable } from './createJoinTable';
30
29
  import { RakeDbAst } from '../ast';
31
30
 
32
31
  export type DropMode = 'CASCADE' | 'RESTRICT';
@@ -131,40 +130,6 @@ export class Migration extends TransactionAdapter {
131
130
  return createTable(this, !this.up, tableName, options, fn);
132
131
  }
133
132
 
134
- createJoinTable(
135
- tables: string[],
136
- options?: JoinTableOptions,
137
- fn?: ColumnsShapeCallback,
138
- ): Promise<void>;
139
- createJoinTable(tables: string[], fn?: ColumnsShapeCallback): Promise<void>;
140
- async createJoinTable(
141
- tables: string[],
142
- cbOrOptions?: ColumnsShapeCallback | JoinTableOptions,
143
- cb?: ColumnsShapeCallback,
144
- ): Promise<void> {
145
- const options = typeof cbOrOptions === 'function' ? {} : cbOrOptions || {};
146
- const fn = (cb || cbOrOptions) as ColumnsShapeCallback | undefined;
147
-
148
- return createJoinTable(this, this.up, tables, options, fn);
149
- }
150
-
151
- dropJoinTable(
152
- tables: string[],
153
- options?: JoinTableOptions,
154
- fn?: ColumnsShapeCallback,
155
- ): Promise<void>;
156
- dropJoinTable(tables: string[], fn?: ColumnsShapeCallback): Promise<void>;
157
- async dropJoinTable(
158
- tables: string[],
159
- cbOrOptions?: ColumnsShapeCallback | JoinTableOptions,
160
- cb?: ColumnsShapeCallback,
161
- ): Promise<void> {
162
- const options = typeof cbOrOptions === 'function' ? {} : cbOrOptions || {};
163
- const fn = (cb || cbOrOptions) as ColumnsShapeCallback | undefined;
164
-
165
- return createJoinTable(this, !this.up, tables, options, fn);
166
- }
167
-
168
133
  changeTable(
169
134
  tableName: string,
170
135
  options: ChangeTableOptions,
@@ -13,7 +13,6 @@ import { ColumnComment, Migration } from './migration';
13
13
  import {
14
14
  getSchemaAndTableFromName,
15
15
  joinColumns,
16
- joinWords,
17
16
  quoteWithSchema,
18
17
  } from '../common';
19
18
 
@@ -105,13 +104,17 @@ export const getForeignKeyTable = (
105
104
  return [item.schema, item.table];
106
105
  };
107
106
 
107
+ export const getForeignKeyName = (table: string, columns: string[]) => {
108
+ return `${table}_${columns.join('_')}_fkey`;
109
+ };
110
+
108
111
  export const constraintToSql = (
109
112
  { name }: { schema?: string; name: string },
110
113
  up: boolean,
111
114
  foreignKey: TableData['foreignKeys'][number],
112
115
  ) => {
113
116
  const constraintName =
114
- foreignKey.options.name || `${name}_${foreignKey.columns.join('_')}_fkey`;
117
+ foreignKey.options.name || getForeignKeyName(name, foreignKey.columns);
115
118
 
116
119
  if (!up) {
117
120
  const { dropMode } = foreignKey.options;
@@ -156,21 +159,26 @@ export const referencesToSql = (
156
159
  return sql.join(' ');
157
160
  };
158
161
 
162
+ export const getIndexName = (
163
+ table: string,
164
+ columns: TableData.Index['columns'],
165
+ ) => {
166
+ return `${table}_${columns
167
+ .map((it) =>
168
+ 'column' in it
169
+ ? it.column
170
+ : it.expression.match(/\w+/g)?.join('_') || 'expression',
171
+ )
172
+ .join('_')}_idx`;
173
+ };
174
+
159
175
  export const indexesToQuery = (
160
176
  up: boolean,
161
177
  { schema, name }: { schema?: string; name: string },
162
178
  indexes: TableData.Index[],
163
179
  ): Sql[] => {
164
180
  return indexes.map(({ columns, options }) => {
165
- const indexName =
166
- options.name ||
167
- joinWords(
168
- name,
169
- ...columns
170
- .filter((it): it is { column: string } => 'column' in it)
171
- .map((it) => it.column),
172
- 'index',
173
- );
181
+ const indexName = options.name || getIndexName(name, columns);
174
182
 
175
183
  if (!up) {
176
184
  return {
@@ -14,6 +14,7 @@ import {
14
14
  } from 'pqb';
15
15
  import { structureToAst } from './structureToAst';
16
16
  import { RakeDbAst } from '../ast';
17
+ import { getIndexName } from '../migration/migrationUtils';
17
18
 
18
19
  const adapter = new Adapter({ databaseURL: 'file:path' });
19
20
  const query = jest.fn().mockImplementation(() => ({ rows: [] }));
@@ -361,6 +362,23 @@ describe('structureToAst', () => {
361
362
  expect(ast.indexes).toHaveLength(0);
362
363
  });
363
364
 
365
+ it('should ignore standard index name', async () => {
366
+ const db = new DbStructure(adapter);
367
+ db.getTables = async () => [table];
368
+ db.getColumns = async () => columns;
369
+ db.getIndexes = async () => [
370
+ { ...index, name: getIndexName(table.name, index.columns) },
371
+ ];
372
+
373
+ const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
374
+ expect(ast.shape.name.data.indexes).toEqual([
375
+ {
376
+ unique: false,
377
+ },
378
+ ]);
379
+ expect(ast.indexes).toHaveLength(0);
380
+ });
381
+
364
382
  it('should set index options to column index', async () => {
365
383
  const db = new DbStructure(adapter);
366
384
  db.getTables = async () => [table];
@@ -430,6 +448,30 @@ describe('structureToAst', () => {
430
448
  ]);
431
449
  });
432
450
 
451
+ it('should ignore standard index name in composite index', async () => {
452
+ const db = new DbStructure(adapter);
453
+ db.getTables = async () => [table];
454
+ db.getColumns = async () => columns;
455
+
456
+ const indexColumns = [{ column: 'id' }, { column: 'name' }];
457
+ db.getIndexes = async () => [
458
+ {
459
+ ...index,
460
+ columns: indexColumns,
461
+ name: getIndexName(table.name, indexColumns),
462
+ },
463
+ ];
464
+
465
+ const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
466
+ expect(ast.shape.name.data.indexes).toBe(undefined);
467
+ expect(ast.indexes).toEqual([
468
+ {
469
+ columns: indexColumns,
470
+ options: { unique: false },
471
+ },
472
+ ]);
473
+ });
474
+
433
475
  it('should add index with expression and options to the table', async () => {
434
476
  const db = new DbStructure(adapter);
435
477
  db.getTables = async () => [table];
@@ -503,6 +545,31 @@ describe('structureToAst', () => {
503
545
  expect(ast.foreignKeys).toHaveLength(0);
504
546
  });
505
547
 
548
+ it('should ignore standard foreign key name', async () => {
549
+ const db = new DbStructure(adapter);
550
+ db.getTables = async () => [table];
551
+ db.getColumns = async () => [
552
+ ...columns,
553
+ { ...intColumn, name: 'otherId' },
554
+ ];
555
+ db.getForeignKeys = async () => [
556
+ { ...foreignKey, name: `${table.name}_otherId_fkey` },
557
+ ];
558
+
559
+ const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
560
+
561
+ expect(ast.shape.otherId.data.foreignKeys).toEqual([
562
+ {
563
+ columns: ['id'],
564
+ table: 'otherTable',
565
+ match: 'FULL',
566
+ onUpdate: 'CASCADE',
567
+ onDelete: 'CASCADE',
568
+ },
569
+ ]);
570
+ expect(ast.foreignKeys).toHaveLength(0);
571
+ });
572
+
506
573
  it('should add composite foreign key', async () => {
507
574
  const db = new DbStructure(adapter);
508
575
  db.getTables = async () => [table];
@@ -535,6 +602,39 @@ describe('structureToAst', () => {
535
602
  },
536
603
  ]);
537
604
  });
605
+
606
+ it('should ignore standard foreign key name in a composite foreign key', async () => {
607
+ const db = new DbStructure(adapter);
608
+ db.getTables = async () => [table];
609
+ db.getColumns = async () => [
610
+ ...columns,
611
+ { ...intColumn, name: 'otherId' },
612
+ ];
613
+ db.getForeignKeys = async () => [
614
+ {
615
+ ...foreignKey,
616
+ columnNames: ['name', 'otherId'],
617
+ foreignColumnNames: ['name', 'id'],
618
+ name: 'table_name_otherId_fkey',
619
+ },
620
+ ];
621
+
622
+ const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
623
+
624
+ expect(ast.shape.otherId.data.foreignKeys).toBe(undefined);
625
+ expect(ast.foreignKeys).toEqual([
626
+ {
627
+ columns: ['name', 'otherId'],
628
+ fnOrTable: 'otherTable',
629
+ foreignColumns: ['name', 'id'],
630
+ options: {
631
+ match: 'FULL',
632
+ onUpdate: 'CASCADE',
633
+ onDelete: 'CASCADE',
634
+ },
635
+ },
636
+ ]);
637
+ });
538
638
  });
539
639
 
540
640
  describe('extension', () => {
@@ -7,6 +7,7 @@ import {
7
7
  instantiateColumn,
8
8
  singleQuote,
9
9
  } from 'pqb';
10
+ import { getForeignKeyName, getIndexName } from '../migration/migrationUtils';
10
11
 
11
12
  const matchMap = {
12
13
  s: undefined,
@@ -98,7 +99,10 @@ export const structureToAst = async (db: DbStructure): Promise<RakeDbAst[]> => {
98
99
  collate: options.collate,
99
100
  opclass: options.opclass,
100
101
  order: options.order,
101
- name: index.name,
102
+ name:
103
+ index.name !== getIndexName(name, index.columns)
104
+ ? index.name
105
+ : undefined,
102
106
  using: index.using === 'btree' ? undefined : index.using,
103
107
  unique: index.isUnique,
104
108
  include: index.include,
@@ -116,7 +120,12 @@ export const structureToAst = async (db: DbStructure): Promise<RakeDbAst[]> => {
116
120
  foreignKey.foreignTableName,
117
121
  foreignKey.foreignColumnNames[0],
118
122
  {
119
- name: foreignKey.name,
123
+ name:
124
+ foreignKey.name &&
125
+ foreignKey.name !==
126
+ getForeignKeyName(name, foreignKey.columnNames)
127
+ ? foreignKey.name
128
+ : undefined,
120
129
  match: matchMap[foreignKey.match],
121
130
  onUpdate: fkeyActionMap[foreignKey.onUpdate],
122
131
  onDelete: fkeyActionMap[foreignKey.onDelete],
@@ -161,7 +170,10 @@ export const structureToAst = async (db: DbStructure): Promise<RakeDbAst[]> => {
161
170
  order: it.order,
162
171
  })),
163
172
  options: {
164
- name: index.name,
173
+ name:
174
+ index.name !== getIndexName(name, index.columns)
175
+ ? index.name
176
+ : undefined,
165
177
  using: index.using === 'btree' ? undefined : index.using,
166
178
  unique: index.isUnique,
167
179
  include: index.include,
@@ -177,7 +189,10 @@ export const structureToAst = async (db: DbStructure): Promise<RakeDbAst[]> => {
177
189
  fnOrTable: it.foreignTableName,
178
190
  foreignColumns: it.foreignColumnNames,
179
191
  options: {
180
- name: it.name,
192
+ name:
193
+ it.name && it.name !== getForeignKeyName(name, it.columnNames)
194
+ ? it.name
195
+ : undefined,
181
196
  match: matchMap[it.match],
182
197
  onUpdate: fkeyActionMap[it.onUpdate],
183
198
  onDelete: fkeyActionMap[it.onDelete],
@@ -1,96 +0,0 @@
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,107 +0,0 @@
1
- import { ColumnType, Operators } from 'pqb';
2
- import { ColumnsShapeCallback, JoinTableOptions, Migration } from './migration';
3
- import {
4
- getSchemaAndTableFromName,
5
- joinWords,
6
- quoteWithSchema,
7
- } from '../common';
8
- import { getPrimaryKeysOfTable } from './migrationUtils';
9
- import { singular } from 'pluralize';
10
- import { createTable } from './createTable';
11
-
12
- class UnknownColumn extends ColumnType {
13
- operators = Operators.any;
14
-
15
- constructor(public dataType: string) {
16
- super();
17
- }
18
-
19
- toCode() {
20
- return 'unknown';
21
- }
22
- }
23
-
24
- export const createJoinTable = async (
25
- migration: Migration,
26
- up: boolean,
27
- tables: string[],
28
- options: JoinTableOptions,
29
- fn?: ColumnsShapeCallback,
30
- ) => {
31
- const tableName = options.tableName || joinWords(...tables);
32
-
33
- if (!up) {
34
- return createTable(
35
- migration,
36
- up,
37
- tableName,
38
- { ...options, noPrimaryKey: true },
39
- () => ({}),
40
- );
41
- }
42
-
43
- const tablesWithPrimaryKeys = await Promise.all(
44
- tables.map(async (table) => {
45
- const primaryKeys = await getPrimaryKeysOfTable(migration, table).then(
46
- (items) =>
47
- items.map((item) => ({
48
- ...item,
49
- joinedName: joinWords(singular(table), item.name),
50
- })),
51
- );
52
-
53
- const [schema, name] = getSchemaAndTableFromName(table);
54
- if (!primaryKeys.length) {
55
- throw new Error(
56
- `Primary key for table ${quoteWithSchema({
57
- schema,
58
- name,
59
- })} is not defined`,
60
- );
61
- }
62
-
63
- return [schema, table, primaryKeys] as const;
64
- }),
65
- );
66
-
67
- return createTable(migration, up, tableName, options, (t) => {
68
- const result: Record<string, ColumnType> = {};
69
-
70
- tablesWithPrimaryKeys.forEach(([schema, table, primaryKeys]) => {
71
- if (primaryKeys.length === 1) {
72
- const [{ type, joinedName, name }] = primaryKeys;
73
-
74
- const column = new UnknownColumn(type);
75
-
76
- result[joinedName] = column.foreignKey(
77
- schema ? `${schema}.${table}` : table,
78
- name,
79
- );
80
-
81
- return;
82
- }
83
-
84
- primaryKeys.forEach(({ joinedName, type }) => {
85
- result[joinedName] = new UnknownColumn(type);
86
- });
87
-
88
- t.foreignKey(
89
- primaryKeys.map((key) => key.joinedName) as [string, ...string[]],
90
- table,
91
- primaryKeys.map((key) => key.name) as [string, ...string[]],
92
- );
93
- });
94
-
95
- if (fn) {
96
- Object.assign(result, fn(t));
97
- }
98
-
99
- t.primaryKey(
100
- tablesWithPrimaryKeys.flatMap(([, , primaryKeys]) =>
101
- primaryKeys.map((item) => item.joinedName),
102
- ),
103
- );
104
-
105
- return result;
106
- });
107
- };