rake-db 2.3.26 → 2.3.28
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 +6 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.js +224 -112
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +224 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/commands/migrateOrRollback.test.ts +43 -1
- package/src/commands/migrateOrRollback.ts +13 -15
- package/src/common.ts +16 -0
- package/src/errors.ts +3 -0
- package/src/migration/change.test.ts +16 -0
- package/src/migration/change.ts +5 -16
- package/src/migration/changeTable.test.ts +236 -58
- package/src/migration/changeTable.ts +34 -14
- package/src/migration/createTable.test.ts +31 -7
- package/src/migration/createTable.ts +44 -25
- package/src/migration/migration.test.ts +1 -1
- package/src/migration/migration.ts +6 -8
- package/src/migration/tableMethods.ts +8 -0
- package/src/pull/astToMigration.test.ts +97 -28
- package/src/pull/astToMigration.ts +55 -12
- package/src/pull/dbStructure.test.ts +14 -0
- package/src/pull/dbStructure.ts +21 -0
- package/src/pull/pull.test.ts +4 -0
- package/src/pull/structureToAst.test.ts +37 -0
- package/src/pull/structureToAst.ts +24 -10
- package/src/rakeDb.test.ts +20 -0
- package/src/rakeDb.ts +27 -18
- package/src/test-utils.ts +1 -0
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { migrate, rollback } from './migrateOrRollback';
|
|
1
|
+
import { changeCache, migrate, rollback } from './migrateOrRollback';
|
|
2
2
|
import { createSchemaMigrations, migrationConfigDefaults } from '../common';
|
|
3
3
|
import { getMigrationFiles } from '../common';
|
|
4
4
|
import { Adapter, noop, TransactionAdapter } from 'pqb';
|
|
@@ -33,6 +33,7 @@ Adapter.prototype.transaction = (cb) => {
|
|
|
33
33
|
|
|
34
34
|
const transactionQueryMock = jest.fn();
|
|
35
35
|
TransactionAdapter.prototype.query = transactionQueryMock;
|
|
36
|
+
TransactionAdapter.prototype.arrays = transactionQueryMock;
|
|
36
37
|
|
|
37
38
|
const importMock = jest.fn();
|
|
38
39
|
const config = {
|
|
@@ -67,6 +68,9 @@ describe('migrateOrRollback', () => {
|
|
|
67
68
|
beforeEach(() => {
|
|
68
69
|
jest.clearAllMocks();
|
|
69
70
|
importMock.mockImplementation(() => undefined);
|
|
71
|
+
for (const key in changeCache) {
|
|
72
|
+
delete changeCache[key];
|
|
73
|
+
}
|
|
70
74
|
});
|
|
71
75
|
|
|
72
76
|
describe('migrate', () => {
|
|
@@ -176,6 +180,25 @@ describe('migrateOrRollback', () => {
|
|
|
176
180
|
|
|
177
181
|
expect(appCodeUpdater).toBeCalled();
|
|
178
182
|
});
|
|
183
|
+
|
|
184
|
+
it('should call multiple change callbacks from top to bottom', async () => {
|
|
185
|
+
migrationFiles = [files[0]];
|
|
186
|
+
migratedVersions = [];
|
|
187
|
+
|
|
188
|
+
const called: string[] = [];
|
|
189
|
+
importMock.mockImplementation(() => {
|
|
190
|
+
change(async () => {
|
|
191
|
+
called.push('one');
|
|
192
|
+
});
|
|
193
|
+
change(async () => {
|
|
194
|
+
called.push('two');
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await migrate(options, config, []);
|
|
199
|
+
|
|
200
|
+
expect(called).toEqual(['one', 'two']);
|
|
201
|
+
});
|
|
179
202
|
});
|
|
180
203
|
|
|
181
204
|
describe('rollback', () => {
|
|
@@ -221,5 +244,24 @@ describe('migrateOrRollback', () => {
|
|
|
221
244
|
expect(transactionQueryMock).not.toBeCalled();
|
|
222
245
|
expect(config.logger.log).not.toBeCalled();
|
|
223
246
|
});
|
|
247
|
+
|
|
248
|
+
it('should call multiple change callbacks from top to bottom', async () => {
|
|
249
|
+
migrationFiles = [files[0]];
|
|
250
|
+
migratedVersions = [files[0].version];
|
|
251
|
+
|
|
252
|
+
const called: string[] = [];
|
|
253
|
+
importMock.mockImplementation(() => {
|
|
254
|
+
change(async () => {
|
|
255
|
+
called.push('one');
|
|
256
|
+
});
|
|
257
|
+
change(async () => {
|
|
258
|
+
called.push('two');
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await rollback(options, config, []);
|
|
263
|
+
|
|
264
|
+
expect(called).toEqual(['two', 'one']);
|
|
265
|
+
});
|
|
224
266
|
});
|
|
225
267
|
});
|
|
@@ -15,19 +15,16 @@ import {
|
|
|
15
15
|
quoteWithSchema,
|
|
16
16
|
} from '../common';
|
|
17
17
|
import {
|
|
18
|
-
|
|
19
|
-
setCurrentMigrationUp,
|
|
20
|
-
setCurrentMigration,
|
|
18
|
+
clearChanges,
|
|
21
19
|
ChangeCallback,
|
|
22
|
-
|
|
23
|
-
getCurrentChangeCallback,
|
|
20
|
+
getCurrentChanges,
|
|
24
21
|
} from '../migration/change';
|
|
25
22
|
import { createMigrationInterface } from '../migration/migration';
|
|
26
23
|
import { pathToFileURL } from 'url';
|
|
27
24
|
|
|
28
25
|
const getDb = (adapter: Adapter) => createDb({ adapter });
|
|
29
26
|
|
|
30
|
-
const migrateOrRollback = async (
|
|
27
|
+
export const migrateOrRollback = async (
|
|
31
28
|
options: MaybeArray<AdapterOptions>,
|
|
32
29
|
config: RakeDbConfig,
|
|
33
30
|
args: string[],
|
|
@@ -99,7 +96,7 @@ const migrateOrRollback = async (
|
|
|
99
96
|
}
|
|
100
97
|
};
|
|
101
98
|
|
|
102
|
-
const changeCache: Record<string, ChangeCallback | undefined> = {};
|
|
99
|
+
export const changeCache: Record<string, ChangeCallback[] | undefined> = {};
|
|
103
100
|
|
|
104
101
|
const processMigration = async (
|
|
105
102
|
db: Adapter,
|
|
@@ -117,18 +114,19 @@ const processMigration = async (
|
|
|
117
114
|
options,
|
|
118
115
|
appCodeUpdaterCache,
|
|
119
116
|
);
|
|
120
|
-
|
|
121
|
-
setCurrentMigrationUp(up);
|
|
117
|
+
clearChanges();
|
|
122
118
|
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
change(callback);
|
|
126
|
-
} else {
|
|
119
|
+
let changes = changeCache[file.path];
|
|
120
|
+
if (!changes) {
|
|
127
121
|
await config.import(pathToFileURL(file.path).pathname);
|
|
128
|
-
|
|
122
|
+
changes = getCurrentChanges();
|
|
123
|
+
changeCache[file.path] = changes;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const fn of up ? changes : changes.reverse()) {
|
|
127
|
+
await fn(db, up);
|
|
129
128
|
}
|
|
130
129
|
|
|
131
|
-
await getCurrentPromise();
|
|
132
130
|
await (up ? saveMigratedVersion : removeMigratedVersion)(
|
|
133
131
|
db.adapter,
|
|
134
132
|
file.version,
|
package/src/common.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
AdapterOptions,
|
|
4
4
|
DbResult,
|
|
5
5
|
DefaultColumnTypes,
|
|
6
|
+
EnumColumn,
|
|
6
7
|
NoPrimaryKeyOption,
|
|
7
8
|
QueryLogOptions,
|
|
8
9
|
singleQuote,
|
|
@@ -11,6 +12,7 @@ import path from 'path';
|
|
|
11
12
|
import { readdir } from 'fs/promises';
|
|
12
13
|
import { RakeDbAst } from './ast';
|
|
13
14
|
import prompts from 'prompts';
|
|
15
|
+
import { TableQuery } from './migration/createTable';
|
|
14
16
|
|
|
15
17
|
type Db = DbResult<DefaultColumnTypes>;
|
|
16
18
|
|
|
@@ -335,3 +337,17 @@ export const quoteSchemaTable = ({
|
|
|
335
337
|
}) => {
|
|
336
338
|
return singleQuote(schema ? `${schema}.${name}` : name);
|
|
337
339
|
};
|
|
340
|
+
|
|
341
|
+
export const makePopulateEnumQuery = (item: EnumColumn): TableQuery => {
|
|
342
|
+
const [schema, name] = getSchemaAndTableFromName(item.enumName);
|
|
343
|
+
return {
|
|
344
|
+
text: `SELECT unnest(enum_range(NULL::${quoteWithSchema({
|
|
345
|
+
schema,
|
|
346
|
+
name,
|
|
347
|
+
})}))::text`,
|
|
348
|
+
then(result) {
|
|
349
|
+
// populate empty options array with values from db
|
|
350
|
+
item.options.push(...result.rows.map(([value]) => value));
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
};
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { change, clearChanges, getCurrentChanges } from './change';
|
|
2
|
+
|
|
3
|
+
describe('change', () => {
|
|
4
|
+
const fn = async () => {};
|
|
5
|
+
|
|
6
|
+
it('should push callback to currentChanges', () => {
|
|
7
|
+
change(fn);
|
|
8
|
+
expect(getCurrentChanges()).toEqual([fn]);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should clear changes', () => {
|
|
12
|
+
getCurrentChanges().push(fn);
|
|
13
|
+
clearChanges();
|
|
14
|
+
expect(getCurrentChanges()).toEqual([]);
|
|
15
|
+
});
|
|
16
|
+
});
|
package/src/migration/change.ts
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
import { Migration } from './migration';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
let currentPromise: Promise<void> | undefined;
|
|
5
|
-
let currentUp = true;
|
|
6
|
-
let currentChangeCallback: ChangeCallback | undefined;
|
|
3
|
+
const currentChanges: ChangeCallback[] = [];
|
|
7
4
|
|
|
8
5
|
export type ChangeCallback = (db: Migration, up: boolean) => Promise<void>;
|
|
9
6
|
|
|
10
7
|
export const change = (fn: ChangeCallback) => {
|
|
11
|
-
|
|
12
|
-
currentPromise = fn(currentMigration, currentUp);
|
|
13
|
-
currentChangeCallback = fn;
|
|
8
|
+
currentChanges.push(fn);
|
|
14
9
|
};
|
|
15
10
|
|
|
16
|
-
export const
|
|
17
|
-
|
|
11
|
+
export const clearChanges = () => {
|
|
12
|
+
currentChanges.length = 0;
|
|
18
13
|
};
|
|
19
14
|
|
|
20
|
-
export const
|
|
21
|
-
currentUp = up;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const getCurrentPromise = () => currentPromise;
|
|
25
|
-
|
|
26
|
-
export const getCurrentChangeCallback = () => currentChangeCallback;
|
|
15
|
+
export const getCurrentChanges = () => currentChanges;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
asMock,
|
|
2
3
|
expectSql,
|
|
3
4
|
getDb,
|
|
4
5
|
queryMock,
|
|
@@ -71,27 +72,8 @@ describe('changeTable', () => {
|
|
|
71
72
|
dropCascade: t[action](t.text(), { dropMode: 'CASCADE' }),
|
|
72
73
|
nullable: t[action](t.text().nullable()),
|
|
73
74
|
nonNullable: t[action](t.text()),
|
|
74
|
-
enum: t[action](t.enum('mood')),
|
|
75
75
|
withDefault: t[action](t.boolean().default(false)),
|
|
76
76
|
withDefaultRaw: t[action](t.date().default(t.raw(`now()`))),
|
|
77
|
-
withIndex: t[action](
|
|
78
|
-
t.text().index({
|
|
79
|
-
name: 'indexName',
|
|
80
|
-
unique: true,
|
|
81
|
-
using: 'gin',
|
|
82
|
-
collate: 'utf-8',
|
|
83
|
-
opclass: 'opclass',
|
|
84
|
-
order: 'ASC',
|
|
85
|
-
include: 'id',
|
|
86
|
-
with: 'fillfactor = 70',
|
|
87
|
-
tablespace: 'tablespace',
|
|
88
|
-
where: 'column = 123',
|
|
89
|
-
}),
|
|
90
|
-
),
|
|
91
|
-
uniqueColumn: t[action](t.text().unique({ dropMode: 'CASCADE' })),
|
|
92
|
-
columnWithComment: t[action](
|
|
93
|
-
t.text().comment('this is a column comment'),
|
|
94
|
-
),
|
|
95
77
|
varcharWithLength: t[action](t.varchar(20)),
|
|
96
78
|
decimalWithPrecisionAndScale: t[action](t.decimal(10, 5)),
|
|
97
79
|
columnWithCompression: t[action](t.text().compression('compression')),
|
|
@@ -116,12 +98,8 @@ describe('changeTable', () => {
|
|
|
116
98
|
ADD COLUMN "dropCascade" text NOT NULL,
|
|
117
99
|
ADD COLUMN "nullable" text,
|
|
118
100
|
ADD COLUMN "nonNullable" text NOT NULL,
|
|
119
|
-
ADD COLUMN "enum" "mood" NOT NULL,
|
|
120
101
|
ADD COLUMN "withDefault" boolean NOT NULL DEFAULT false,
|
|
121
102
|
ADD COLUMN "withDefaultRaw" date NOT NULL DEFAULT now(),
|
|
122
|
-
ADD COLUMN "withIndex" text NOT NULL,
|
|
123
|
-
ADD COLUMN "uniqueColumn" text NOT NULL,
|
|
124
|
-
ADD COLUMN "columnWithComment" text NOT NULL,
|
|
125
103
|
ADD COLUMN "varcharWithLength" varchar(20) NOT NULL,
|
|
126
104
|
ADD COLUMN "decimalWithPrecisionAndScale" decimal(10, 5) NOT NULL,
|
|
127
105
|
ADD COLUMN "columnWithCompression" text COMPRESSION compression NOT NULL,
|
|
@@ -130,22 +108,6 @@ describe('changeTable', () => {
|
|
|
130
108
|
ADD COLUMN "createdAt" timestamp NOT NULL DEFAULT now(),
|
|
131
109
|
ADD COLUMN "updatedAt" timestamp NOT NULL DEFAULT now()
|
|
132
110
|
`,
|
|
133
|
-
toLine(`
|
|
134
|
-
CREATE UNIQUE INDEX "indexName"
|
|
135
|
-
ON "table"
|
|
136
|
-
USING gin
|
|
137
|
-
("withIndex" COLLATE 'utf-8' opclass ASC)
|
|
138
|
-
INCLUDE ("id")
|
|
139
|
-
WITH (fillfactor = 70)
|
|
140
|
-
TABLESPACE tablespace
|
|
141
|
-
WHERE column = 123
|
|
142
|
-
`),
|
|
143
|
-
toLine(`
|
|
144
|
-
CREATE UNIQUE INDEX "table_uniqueColumn_idx"
|
|
145
|
-
ON "table"
|
|
146
|
-
("uniqueColumn")
|
|
147
|
-
`),
|
|
148
|
-
`COMMENT ON COLUMN "table"."columnWithComment" IS 'this is a column comment'`,
|
|
149
111
|
]);
|
|
150
112
|
};
|
|
151
113
|
|
|
@@ -157,12 +119,8 @@ describe('changeTable', () => {
|
|
|
157
119
|
DROP COLUMN "dropCascade" CASCADE,
|
|
158
120
|
DROP COLUMN "nullable",
|
|
159
121
|
DROP COLUMN "nonNullable",
|
|
160
|
-
DROP COLUMN "enum",
|
|
161
122
|
DROP COLUMN "withDefault",
|
|
162
123
|
DROP COLUMN "withDefaultRaw",
|
|
163
|
-
DROP COLUMN "withIndex",
|
|
164
|
-
DROP COLUMN "uniqueColumn",
|
|
165
|
-
DROP COLUMN "columnWithComment",
|
|
166
124
|
DROP COLUMN "varcharWithLength",
|
|
167
125
|
DROP COLUMN "decimalWithPrecisionAndScale",
|
|
168
126
|
DROP COLUMN "columnWithCompression",
|
|
@@ -171,20 +129,205 @@ describe('changeTable', () => {
|
|
|
171
129
|
DROP COLUMN "createdAt",
|
|
172
130
|
DROP COLUMN "updatedAt"
|
|
173
131
|
`,
|
|
174
|
-
toLine(`DROP INDEX "indexName"`),
|
|
175
|
-
toLine(`DROP INDEX "table_uniqueColumn_idx" CASCADE`),
|
|
176
132
|
]);
|
|
177
133
|
};
|
|
178
134
|
|
|
135
|
+
asMock(queryMock).mockResolvedValue({ rows: [['one'], ['two']] });
|
|
136
|
+
|
|
179
137
|
await fn();
|
|
180
138
|
(action === 'add' ? expectAddColumns : expectRemoveColumns)();
|
|
181
139
|
|
|
182
140
|
queryMock.mockClear();
|
|
183
141
|
db.up = false;
|
|
142
|
+
|
|
184
143
|
await fn();
|
|
144
|
+
|
|
185
145
|
(action === 'add' ? expectRemoveColumns : expectAddColumns)();
|
|
186
146
|
});
|
|
187
147
|
|
|
148
|
+
it(`should ${action} index`, async () => {
|
|
149
|
+
const fn = () => {
|
|
150
|
+
return db.changeTable('table', (t) => ({
|
|
151
|
+
withIndex: t[action](
|
|
152
|
+
t.text().index({
|
|
153
|
+
name: 'indexName',
|
|
154
|
+
unique: true,
|
|
155
|
+
using: 'gin',
|
|
156
|
+
collate: 'utf-8',
|
|
157
|
+
opclass: 'opclass',
|
|
158
|
+
order: 'ASC',
|
|
159
|
+
include: 'id',
|
|
160
|
+
with: 'fillfactor = 70',
|
|
161
|
+
tablespace: 'tablespace',
|
|
162
|
+
where: 'column = 123',
|
|
163
|
+
}),
|
|
164
|
+
),
|
|
165
|
+
}));
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const expectAdd = () => {
|
|
169
|
+
expectSql([
|
|
170
|
+
`ALTER TABLE "table"
|
|
171
|
+
ADD COLUMN "withIndex" text NOT NULL`,
|
|
172
|
+
toLine(`
|
|
173
|
+
CREATE UNIQUE INDEX "indexName"
|
|
174
|
+
ON "table"
|
|
175
|
+
USING gin
|
|
176
|
+
("withIndex" COLLATE 'utf-8' opclass ASC)
|
|
177
|
+
INCLUDE ("id")
|
|
178
|
+
WITH (fillfactor = 70)
|
|
179
|
+
TABLESPACE tablespace
|
|
180
|
+
WHERE column = 123
|
|
181
|
+
`),
|
|
182
|
+
]);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const expectRemove = () => {
|
|
186
|
+
expectSql([
|
|
187
|
+
`ALTER TABLE "table"
|
|
188
|
+
DROP COLUMN "withIndex"`,
|
|
189
|
+
toLine(`DROP INDEX "indexName"`),
|
|
190
|
+
]);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
asMock(queryMock).mockResolvedValue({ rows: [['one'], ['two']] });
|
|
194
|
+
|
|
195
|
+
await fn();
|
|
196
|
+
(action === 'add' ? expectAdd : expectRemove)();
|
|
197
|
+
|
|
198
|
+
queryMock.mockClear();
|
|
199
|
+
db.up = false;
|
|
200
|
+
|
|
201
|
+
await fn();
|
|
202
|
+
|
|
203
|
+
(action === 'add' ? expectRemove : expectAdd)();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it(`should ${action} unique index`, async () => {
|
|
207
|
+
const fn = () => {
|
|
208
|
+
return db.changeTable('table', (t) => ({
|
|
209
|
+
uniqueColumn: t[action](t.text().unique({ dropMode: 'CASCADE' })),
|
|
210
|
+
}));
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const expectAdd = () => {
|
|
214
|
+
expectSql([
|
|
215
|
+
`ALTER TABLE "table"
|
|
216
|
+
ADD COLUMN "uniqueColumn" text NOT NULL`,
|
|
217
|
+
toLine(`
|
|
218
|
+
CREATE UNIQUE INDEX "table_uniqueColumn_idx"
|
|
219
|
+
ON "table"
|
|
220
|
+
("uniqueColumn")
|
|
221
|
+
`),
|
|
222
|
+
]);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const expectRemove = () => {
|
|
226
|
+
expectSql([
|
|
227
|
+
`ALTER TABLE "table"
|
|
228
|
+
DROP COLUMN "uniqueColumn"`,
|
|
229
|
+
toLine(`DROP INDEX "table_uniqueColumn_idx" CASCADE`),
|
|
230
|
+
]);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
asMock(queryMock).mockResolvedValue({ rows: [['one'], ['two']] });
|
|
234
|
+
|
|
235
|
+
await fn();
|
|
236
|
+
(action === 'add' ? expectAdd : expectRemove)();
|
|
237
|
+
|
|
238
|
+
queryMock.mockClear();
|
|
239
|
+
db.up = false;
|
|
240
|
+
|
|
241
|
+
await fn();
|
|
242
|
+
|
|
243
|
+
(action === 'add' ? expectRemove : expectAdd)();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it(`should ${action} column comment`, async () => {
|
|
247
|
+
const fn = () => {
|
|
248
|
+
return db.changeTable('table', (t) => ({
|
|
249
|
+
columnWithComment: t[action](
|
|
250
|
+
t.text().comment('this is a column comment'),
|
|
251
|
+
),
|
|
252
|
+
}));
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const expectAdd = () => {
|
|
256
|
+
expectSql([
|
|
257
|
+
`ALTER TABLE "table"
|
|
258
|
+
ADD COLUMN "columnWithComment" text NOT NULL`,
|
|
259
|
+
`COMMENT ON COLUMN "table"."columnWithComment" IS 'this is a column comment'`,
|
|
260
|
+
]);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const expectRemove = () => {
|
|
264
|
+
expectSql(
|
|
265
|
+
`ALTER TABLE "table"
|
|
266
|
+
DROP COLUMN "columnWithComment"`,
|
|
267
|
+
);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
asMock(queryMock).mockResolvedValue({ rows: [['one'], ['two']] });
|
|
271
|
+
|
|
272
|
+
await fn();
|
|
273
|
+
(action === 'add' ? expectAdd : expectRemove)();
|
|
274
|
+
|
|
275
|
+
queryMock.mockClear();
|
|
276
|
+
db.up = false;
|
|
277
|
+
|
|
278
|
+
await fn();
|
|
279
|
+
|
|
280
|
+
(action === 'add' ? expectRemove : expectAdd)();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it(`should ${action} enum`, async () => {
|
|
284
|
+
const fn = () => {
|
|
285
|
+
return db.changeTable('table', (t) => ({
|
|
286
|
+
enum: t[action](t.enum('mood')),
|
|
287
|
+
}));
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const expectAdd = () => {
|
|
291
|
+
expectSql([
|
|
292
|
+
'SELECT unnest(enum_range(NULL::"mood"))::text',
|
|
293
|
+
`
|
|
294
|
+
ALTER TABLE "table"
|
|
295
|
+
ADD COLUMN "enum" "mood" NOT NULL
|
|
296
|
+
`,
|
|
297
|
+
]);
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const expectRemove = () => {
|
|
301
|
+
expectSql([
|
|
302
|
+
'SELECT unnest(enum_range(NULL::"mood"))::text',
|
|
303
|
+
`
|
|
304
|
+
ALTER TABLE "table"
|
|
305
|
+
DROP COLUMN "enum"
|
|
306
|
+
`,
|
|
307
|
+
]);
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
asMock(queryMock).mockResolvedValue({ rows: [['one'], ['two']] });
|
|
311
|
+
|
|
312
|
+
await fn();
|
|
313
|
+
|
|
314
|
+
(action === 'add' ? expectAdd : expectRemove)();
|
|
315
|
+
|
|
316
|
+
const [{ ast: ast1 }] = asMock(db.options.appCodeUpdater).mock.calls[0];
|
|
317
|
+
expect(ast1.shape.enum.item.options).toEqual(['one', 'two']);
|
|
318
|
+
|
|
319
|
+
queryMock.mockClear();
|
|
320
|
+
asMock(db.options.appCodeUpdater).mockClear();
|
|
321
|
+
db.up = false;
|
|
322
|
+
|
|
323
|
+
await fn();
|
|
324
|
+
|
|
325
|
+
(action === 'add' ? expectRemove : expectAdd)();
|
|
326
|
+
|
|
327
|
+
const [{ ast: ast2 }] = asMock(db.options.appCodeUpdater).mock.calls[0];
|
|
328
|
+
expect(ast2.shape.enum.item.options).toEqual(['one', 'two']);
|
|
329
|
+
});
|
|
330
|
+
|
|
188
331
|
it(`should ${action} columns with a primary key`, async () => {
|
|
189
332
|
const fn = () => {
|
|
190
333
|
return db.changeTable('table', (t) => ({
|
|
@@ -406,7 +549,7 @@ describe('changeTable', () => {
|
|
|
406
549
|
});
|
|
407
550
|
});
|
|
408
551
|
|
|
409
|
-
|
|
552
|
+
describe('column change', () => {
|
|
410
553
|
const fn = () => {
|
|
411
554
|
return db.changeTable('table', (t) => ({
|
|
412
555
|
changeType: t.change(t.integer(), t.text()),
|
|
@@ -426,9 +569,23 @@ describe('changeTable', () => {
|
|
|
426
569
|
}));
|
|
427
570
|
};
|
|
428
571
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
572
|
+
const enumOne = ['one', 'two'];
|
|
573
|
+
const enumTwo = ['three', 'four'];
|
|
574
|
+
|
|
575
|
+
it('should change column up', async () => {
|
|
576
|
+
asMock(queryMock).mockResolvedValueOnce({
|
|
577
|
+
rows: enumOne.map((value) => [value]),
|
|
578
|
+
});
|
|
579
|
+
asMock(queryMock).mockResolvedValueOnce({
|
|
580
|
+
rows: enumTwo.map((value) => [value]),
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
await fn();
|
|
584
|
+
|
|
585
|
+
expectSql([
|
|
586
|
+
'SELECT unnest(enum_range(NULL::"one"))::text',
|
|
587
|
+
'SELECT unnest(enum_range(NULL::"two"))::text',
|
|
588
|
+
`
|
|
432
589
|
ALTER TABLE "table"
|
|
433
590
|
ALTER COLUMN "changeType" TYPE text,
|
|
434
591
|
ALTER COLUMN "changeEnum" TYPE "two",
|
|
@@ -438,14 +595,30 @@ describe('changeTable', () => {
|
|
|
438
595
|
ALTER COLUMN "changeNull" DROP NOT NULL,
|
|
439
596
|
ALTER COLUMN "changeCompression" SET COMPRESSION value
|
|
440
597
|
`,
|
|
441
|
-
|
|
442
|
-
|
|
598
|
+
`COMMENT ON COLUMN "table"."changeComment" IS 'comment 2'`,
|
|
599
|
+
]);
|
|
443
600
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
601
|
+
const [{ ast }] = asMock(db.options.appCodeUpdater).mock.calls[0];
|
|
602
|
+
expect(ast.shape.changeEnum.from.column.options).toEqual(enumOne);
|
|
603
|
+
expect(ast.shape.changeEnum.to.column.options).toEqual(enumTwo);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it('should change column down', async () => {
|
|
607
|
+
asMock(queryMock).mockResolvedValueOnce({
|
|
608
|
+
rows: enumTwo.map((value) => [value]),
|
|
609
|
+
});
|
|
610
|
+
asMock(queryMock).mockResolvedValueOnce({
|
|
611
|
+
rows: enumOne.map((value) => [value]),
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
db.up = false;
|
|
615
|
+
|
|
616
|
+
await fn();
|
|
617
|
+
|
|
618
|
+
expectSql([
|
|
619
|
+
'SELECT unnest(enum_range(NULL::"two"))::text',
|
|
620
|
+
'SELECT unnest(enum_range(NULL::"one"))::text',
|
|
621
|
+
`
|
|
449
622
|
ALTER TABLE "table"
|
|
450
623
|
ALTER COLUMN "changeType" TYPE integer,
|
|
451
624
|
ALTER COLUMN "changeEnum" TYPE "one",
|
|
@@ -455,8 +628,13 @@ describe('changeTable', () => {
|
|
|
455
628
|
ALTER COLUMN "changeNull" SET NOT NULL,
|
|
456
629
|
ALTER COLUMN "changeCompression" SET COMPRESSION DEFAULT
|
|
457
630
|
`,
|
|
458
|
-
|
|
459
|
-
|
|
631
|
+
`COMMENT ON COLUMN "table"."changeComment" IS 'comment 1'`,
|
|
632
|
+
]);
|
|
633
|
+
|
|
634
|
+
const [{ ast }] = asMock(db.options.appCodeUpdater).mock.calls[0];
|
|
635
|
+
expect(ast.shape.changeEnum.from.column.options).toEqual(enumTwo);
|
|
636
|
+
expect(ast.shape.changeEnum.to.column.options).toEqual(enumOne);
|
|
637
|
+
});
|
|
460
638
|
});
|
|
461
639
|
|
|
462
640
|
it('should add composite primary key via change', async () => {
|