rake-db 2.3.25 → 2.3.26
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 +8 -0
- package/dist/index.d.ts +27 -21
- package/dist/index.js +30 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +30 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/ast.ts +18 -2
- package/src/migration/changeTable.test.ts +6 -0
- package/src/migration/createTable.test.ts +2 -0
- package/src/migration/migration.test.ts +48 -2
- package/src/migration/migration.ts +70 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rake-db",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.26",
|
|
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": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"license": "ISC",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"pluralize": "^8.0.0",
|
|
44
|
-
"pqb": "0.9.
|
|
44
|
+
"pqb": "0.9.15",
|
|
45
45
|
"prompts": "^2.4.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
package/src/ast.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type RakeDbAst =
|
|
|
15
15
|
| RakeDbAst.RenameTable
|
|
16
16
|
| RakeDbAst.Schema
|
|
17
17
|
| RakeDbAst.Extension
|
|
18
|
+
| RakeDbAst.Enum
|
|
18
19
|
| RakeDbAst.ForeignKey;
|
|
19
20
|
|
|
20
21
|
export namespace RakeDbAst {
|
|
@@ -101,8 +102,23 @@ export namespace RakeDbAst {
|
|
|
101
102
|
schema?: string;
|
|
102
103
|
version?: string;
|
|
103
104
|
cascade?: boolean;
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
createIfNotExists?: boolean;
|
|
106
|
+
dropIfExists?: boolean;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type Enum = {
|
|
110
|
+
type: 'enum';
|
|
111
|
+
action: 'create' | 'drop';
|
|
112
|
+
schema?: string;
|
|
113
|
+
name: string;
|
|
114
|
+
values: string[];
|
|
115
|
+
cascade?: boolean;
|
|
116
|
+
dropIfExists?: boolean;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export type EnumOptions = {
|
|
120
|
+
createIfNotExists?: boolean;
|
|
121
|
+
dropIfExists?: boolean;
|
|
106
122
|
};
|
|
107
123
|
|
|
108
124
|
export type ForeignKey = {
|
|
@@ -71,6 +71,7 @@ describe('changeTable', () => {
|
|
|
71
71
|
dropCascade: t[action](t.text(), { dropMode: 'CASCADE' }),
|
|
72
72
|
nullable: t[action](t.text().nullable()),
|
|
73
73
|
nonNullable: t[action](t.text()),
|
|
74
|
+
enum: t[action](t.enum('mood')),
|
|
74
75
|
withDefault: t[action](t.boolean().default(false)),
|
|
75
76
|
withDefaultRaw: t[action](t.date().default(t.raw(`now()`))),
|
|
76
77
|
withIndex: t[action](
|
|
@@ -115,6 +116,7 @@ describe('changeTable', () => {
|
|
|
115
116
|
ADD COLUMN "dropCascade" text NOT NULL,
|
|
116
117
|
ADD COLUMN "nullable" text,
|
|
117
118
|
ADD COLUMN "nonNullable" text NOT NULL,
|
|
119
|
+
ADD COLUMN "enum" "mood" NOT NULL,
|
|
118
120
|
ADD COLUMN "withDefault" boolean NOT NULL DEFAULT false,
|
|
119
121
|
ADD COLUMN "withDefaultRaw" date NOT NULL DEFAULT now(),
|
|
120
122
|
ADD COLUMN "withIndex" text NOT NULL,
|
|
@@ -155,6 +157,7 @@ describe('changeTable', () => {
|
|
|
155
157
|
DROP COLUMN "dropCascade" CASCADE,
|
|
156
158
|
DROP COLUMN "nullable",
|
|
157
159
|
DROP COLUMN "nonNullable",
|
|
160
|
+
DROP COLUMN "enum",
|
|
158
161
|
DROP COLUMN "withDefault",
|
|
159
162
|
DROP COLUMN "withDefaultRaw",
|
|
160
163
|
DROP COLUMN "withIndex",
|
|
@@ -407,6 +410,7 @@ describe('changeTable', () => {
|
|
|
407
410
|
const fn = () => {
|
|
408
411
|
return db.changeTable('table', (t) => ({
|
|
409
412
|
changeType: t.change(t.integer(), t.text()),
|
|
413
|
+
changeEnum: t.change(t.enum('one'), t.enum('two')),
|
|
410
414
|
changeTypeUsing: t.change(t.integer(), t.text(), {
|
|
411
415
|
usingUp: t.raw('b::text'),
|
|
412
416
|
usingDown: t.raw('b::int'),
|
|
@@ -427,6 +431,7 @@ describe('changeTable', () => {
|
|
|
427
431
|
`
|
|
428
432
|
ALTER TABLE "table"
|
|
429
433
|
ALTER COLUMN "changeType" TYPE text,
|
|
434
|
+
ALTER COLUMN "changeEnum" TYPE "two",
|
|
430
435
|
ALTER COLUMN "changeTypeUsing" TYPE text USING b::text,
|
|
431
436
|
ALTER COLUMN "changeCollate" TYPE text COLLATE 'fr_FR',
|
|
432
437
|
ALTER COLUMN "changeDefault" SET DEFAULT 'to',
|
|
@@ -443,6 +448,7 @@ describe('changeTable', () => {
|
|
|
443
448
|
`
|
|
444
449
|
ALTER TABLE "table"
|
|
445
450
|
ALTER COLUMN "changeType" TYPE integer,
|
|
451
|
+
ALTER COLUMN "changeEnum" TYPE "one",
|
|
446
452
|
ALTER COLUMN "changeTypeUsing" TYPE integer USING b::int,
|
|
447
453
|
ALTER COLUMN "changeCollate" TYPE text COLLATE 'de_DE',
|
|
448
454
|
ALTER COLUMN "changeDefault" SET DEFAULT 'from',
|
|
@@ -57,6 +57,7 @@ const db = getDb();
|
|
|
57
57
|
id: t.serial().primaryKey(),
|
|
58
58
|
nullable: t.text().nullable(),
|
|
59
59
|
nonNullable: t.text(),
|
|
60
|
+
enum: t.enum('mood'),
|
|
60
61
|
withDefault: t.boolean().default(false),
|
|
61
62
|
withDefaultRaw: t.date().default(t.raw(`now()`)),
|
|
62
63
|
withIndex: t.text().index({
|
|
@@ -94,6 +95,7 @@ const db = getDb();
|
|
|
94
95
|
"id" serial PRIMARY KEY,
|
|
95
96
|
"nullable" text,
|
|
96
97
|
"nonNullable" text NOT NULL,
|
|
98
|
+
"enum" "mood" NOT NULL,
|
|
97
99
|
"withDefault" boolean NOT NULL DEFAULT false,
|
|
98
100
|
"withDefaultRaw" date NOT NULL DEFAULT now(),
|
|
99
101
|
"withIndex" text NOT NULL,
|
|
@@ -317,8 +317,8 @@ describe('migration', () => {
|
|
|
317
317
|
} an extension`, async () => {
|
|
318
318
|
const fn = () => {
|
|
319
319
|
return db[action]('extensionName', {
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
dropIfExists: true,
|
|
321
|
+
createIfNotExists: true,
|
|
322
322
|
schema: 'schemaName',
|
|
323
323
|
version: '123',
|
|
324
324
|
cascade: true,
|
|
@@ -352,6 +352,52 @@ describe('migration', () => {
|
|
|
352
352
|
});
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
+
(['createEnum', 'dropEnum'] as const).forEach((action) => {
|
|
356
|
+
describe(action, () => {
|
|
357
|
+
it('should call appCodeUpdater', async () => {
|
|
358
|
+
await db[action]('enumName', ['one']);
|
|
359
|
+
|
|
360
|
+
expect(db.options.appCodeUpdater).toHaveBeenCalled();
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it(`should ${
|
|
364
|
+
action === 'createEnum' ? 'add' : 'drop'
|
|
365
|
+
} an enum`, async () => {
|
|
366
|
+
const fn = () => {
|
|
367
|
+
return db[action]('enumName', ['one', 'two'], {
|
|
368
|
+
dropIfExists: true,
|
|
369
|
+
schema: 'schemaName',
|
|
370
|
+
cascade: true,
|
|
371
|
+
});
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const expectCreateExtension = () => {
|
|
375
|
+
expectSql(`
|
|
376
|
+
CREATE TYPE "schemaName"."enumName" AS ENUM ($1, $2)
|
|
377
|
+
`);
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const expectDropExtension = () => {
|
|
381
|
+
expectSql(`
|
|
382
|
+
DROP TYPE IF EXISTS "schemaName"."enumName" CASCADE
|
|
383
|
+
`);
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
await fn();
|
|
387
|
+
(action === 'createEnum'
|
|
388
|
+
? expectCreateExtension
|
|
389
|
+
: expectDropExtension)();
|
|
390
|
+
|
|
391
|
+
db.up = false;
|
|
392
|
+
queryMock.mockClear();
|
|
393
|
+
await fn();
|
|
394
|
+
(action === 'createEnum'
|
|
395
|
+
? expectDropExtension
|
|
396
|
+
: expectCreateExtension)();
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
355
401
|
describe('tableExists', () => {
|
|
356
402
|
it('should return boolean', async () => {
|
|
357
403
|
queryMock.mockResolvedValueOnce({ rowCount: 1 });
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
createDb,
|
|
18
18
|
DbResult,
|
|
19
19
|
DefaultColumnTypes,
|
|
20
|
+
EnumColumn,
|
|
20
21
|
} from 'pqb';
|
|
21
22
|
import { createTable } from './createTable';
|
|
22
23
|
import { changeTable, TableChangeData, TableChanger } from './changeTable';
|
|
@@ -37,9 +38,13 @@ export type TableOptions = {
|
|
|
37
38
|
|
|
38
39
|
type TextColumnCreator = () => TextColumn;
|
|
39
40
|
|
|
40
|
-
export type MigrationColumnTypes = Omit<
|
|
41
|
+
export type MigrationColumnTypes = Omit<
|
|
42
|
+
ColumnTypes,
|
|
43
|
+
'text' | 'string' | 'enum'
|
|
44
|
+
> & {
|
|
41
45
|
text: TextColumnCreator;
|
|
42
46
|
string: TextColumnCreator;
|
|
47
|
+
enum: (name: string) => EnumColumn;
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
export type ColumnsShapeCallback = (
|
|
@@ -57,12 +62,6 @@ export type JoinTableOptions = {
|
|
|
57
62
|
dropMode?: DropMode;
|
|
58
63
|
};
|
|
59
64
|
|
|
60
|
-
export type ExtensionOptions = {
|
|
61
|
-
schema?: string;
|
|
62
|
-
version?: string;
|
|
63
|
-
cascade?: boolean;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
65
|
export type Migration = DbResult<DefaultColumnTypes> & MigrationBase;
|
|
67
66
|
|
|
68
67
|
export const createMigrationInterface = (
|
|
@@ -285,18 +284,37 @@ export class MigrationBase {
|
|
|
285
284
|
|
|
286
285
|
createExtension(
|
|
287
286
|
name: string,
|
|
288
|
-
options:
|
|
287
|
+
options: Omit<RakeDbAst.Extension, 'type' | 'action' | 'name'> = {},
|
|
289
288
|
) {
|
|
290
289
|
return createExtension(this, this.up, name, options);
|
|
291
290
|
}
|
|
292
291
|
|
|
293
292
|
dropExtension(
|
|
294
293
|
name: string,
|
|
295
|
-
options:
|
|
294
|
+
options: Omit<
|
|
295
|
+
RakeDbAst.Extension,
|
|
296
|
+
'type' | 'action' | 'name' | 'values'
|
|
297
|
+
> = {},
|
|
296
298
|
) {
|
|
297
299
|
return createExtension(this, !this.up, name, options);
|
|
298
300
|
}
|
|
299
301
|
|
|
302
|
+
createEnum(
|
|
303
|
+
name: string,
|
|
304
|
+
values: string[],
|
|
305
|
+
options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values'>,
|
|
306
|
+
) {
|
|
307
|
+
return createEnum(this, this.up, name, values, options);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
dropEnum(
|
|
311
|
+
name: string,
|
|
312
|
+
values: string[],
|
|
313
|
+
options?: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values'>,
|
|
314
|
+
) {
|
|
315
|
+
return createEnum(this, !this.up, name, values, options);
|
|
316
|
+
}
|
|
317
|
+
|
|
300
318
|
async tableExists(tableName: string) {
|
|
301
319
|
return queryExists(this, {
|
|
302
320
|
text: `SELECT 1 FROM "information_schema"."tables" WHERE "table_name" = $1`,
|
|
@@ -420,9 +438,7 @@ const createExtension = async (
|
|
|
420
438
|
migration: MigrationBase,
|
|
421
439
|
up: boolean,
|
|
422
440
|
name: string,
|
|
423
|
-
options:
|
|
424
|
-
checkExists?: boolean;
|
|
425
|
-
},
|
|
441
|
+
options: Omit<RakeDbAst.Extension, 'type' | 'action' | 'name'>,
|
|
426
442
|
) => {
|
|
427
443
|
const ast: RakeDbAst.Extension = {
|
|
428
444
|
type: 'extension',
|
|
@@ -433,13 +449,13 @@ const createExtension = async (
|
|
|
433
449
|
|
|
434
450
|
let query;
|
|
435
451
|
if (ast.action === 'drop') {
|
|
436
|
-
query = `DROP EXTENSION${ast.
|
|
452
|
+
query = `DROP EXTENSION${ast.dropIfExists ? ' IF EXISTS' : ''} "${
|
|
437
453
|
ast.name
|
|
438
454
|
}"${ast.cascade ? ' CASCADE' : ''}`;
|
|
439
455
|
} else {
|
|
440
|
-
query = `CREATE EXTENSION${
|
|
441
|
-
ast.
|
|
442
|
-
}"${ast.schema ? ` SCHEMA "${ast.schema}"` : ''}${
|
|
456
|
+
query = `CREATE EXTENSION${
|
|
457
|
+
ast.createIfNotExists ? ' IF NOT EXISTS' : ''
|
|
458
|
+
} "${ast.name}"${ast.schema ? ` SCHEMA "${ast.schema}"` : ''}${
|
|
443
459
|
ast.version ? ` VERSION '${ast.version}'` : ''
|
|
444
460
|
}${ast.cascade ? ' CASCADE' : ''}`;
|
|
445
461
|
}
|
|
@@ -449,6 +465,44 @@ const createExtension = async (
|
|
|
449
465
|
await runCodeUpdater(migration, ast);
|
|
450
466
|
};
|
|
451
467
|
|
|
468
|
+
const createEnum = async (
|
|
469
|
+
migration: MigrationBase,
|
|
470
|
+
up: boolean,
|
|
471
|
+
name: string,
|
|
472
|
+
values: string[],
|
|
473
|
+
options: Omit<RakeDbAst.Enum, 'type' | 'action' | 'name' | 'values'> = {},
|
|
474
|
+
) => {
|
|
475
|
+
const [schema, enumName] = getSchemaAndTableFromName(name);
|
|
476
|
+
|
|
477
|
+
const ast: RakeDbAst.Enum = {
|
|
478
|
+
type: 'enum',
|
|
479
|
+
action: up ? 'create' : 'drop',
|
|
480
|
+
schema,
|
|
481
|
+
name: enumName,
|
|
482
|
+
values,
|
|
483
|
+
...options,
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
let text;
|
|
487
|
+
const quotedName = quoteWithSchema(ast);
|
|
488
|
+
if (ast.action === 'create') {
|
|
489
|
+
text = `CREATE TYPE ${quotedName} AS ENUM (${values
|
|
490
|
+
.map((_, i) => `$${i + 1}`)
|
|
491
|
+
.join(', ')})`;
|
|
492
|
+
} else {
|
|
493
|
+
text = `DROP TYPE${ast.dropIfExists ? ' IF EXISTS' : ''} ${quotedName}${
|
|
494
|
+
ast.cascade ? ' CASCADE' : ''
|
|
495
|
+
}`;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
await migration.adapter.query({
|
|
499
|
+
text,
|
|
500
|
+
values,
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
await runCodeUpdater(migration, ast);
|
|
504
|
+
};
|
|
505
|
+
|
|
452
506
|
const queryExists = (
|
|
453
507
|
db: MigrationBase,
|
|
454
508
|
sql: { text: string; values: unknown[] },
|