rake-db 2.3.0 → 2.3.2
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 +16 -0
- package/db.ts +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +698 -146
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +697 -144
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/commands/generate.ts +14 -6
- package/src/common.ts +14 -3
- package/src/pull/astToMigration.test.ts +150 -0
- package/src/pull/astToMigration.ts +93 -0
- package/src/pull/pull.test.ts +111 -0
- package/src/pull/pull.ts +22 -0
- package/src/pull/structureToAst.test.ts +101 -7
- package/src/pull/structureToAst.ts +45 -4
- package/src/rakeDb.test.ts +100 -0
- package/src/rakeDb.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rake-db",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
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.
|
|
45
|
+
"pqb": "0.9.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@swc/core": "^1.2.210",
|
package/src/commands/generate.ts
CHANGED
|
@@ -7,20 +7,28 @@ import {
|
|
|
7
7
|
import { mkdir, writeFile } from 'fs/promises';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
export const writeMigrationFile = async (
|
|
11
|
+
config: RakeDbConfig,
|
|
12
|
+
name: string,
|
|
13
|
+
content: string,
|
|
14
|
+
) => {
|
|
15
|
+
await mkdir(path.resolve(config.migrationsPath), { recursive: true });
|
|
15
16
|
|
|
16
17
|
const filePath = path.resolve(
|
|
17
18
|
config.migrationsPath,
|
|
18
19
|
`${makeFileTimeStamp()}_${name}.ts`,
|
|
19
20
|
);
|
|
20
|
-
await writeFile(filePath,
|
|
21
|
+
await writeFile(filePath, content);
|
|
21
22
|
console.log(`Created ${filePath}`);
|
|
22
23
|
};
|
|
23
24
|
|
|
25
|
+
export const generate = async (config: RakeDbConfig, args: string[]) => {
|
|
26
|
+
const name = args[0];
|
|
27
|
+
if (!name) throw new Error('Migration name is missing');
|
|
28
|
+
|
|
29
|
+
await writeMigrationFile(config, name, makeContent(name, args.slice(1)));
|
|
30
|
+
};
|
|
31
|
+
|
|
24
32
|
const makeFileTimeStamp = () => {
|
|
25
33
|
const now = new Date();
|
|
26
34
|
return [
|
package/src/common.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
AdapterOptions,
|
|
4
4
|
NoPrimaryKeyOption,
|
|
5
5
|
QueryLogOptions,
|
|
6
|
+
singleQuote,
|
|
6
7
|
} from 'pqb';
|
|
7
8
|
import Enquirer from 'enquirer';
|
|
8
9
|
import path from 'path';
|
|
@@ -25,7 +26,7 @@ export type AppCodeUpdater = (params: {
|
|
|
25
26
|
}) => Promise<void>;
|
|
26
27
|
|
|
27
28
|
export const migrationConfigDefaults = {
|
|
28
|
-
migrationsPath: path.resolve(
|
|
29
|
+
migrationsPath: path.resolve('src', 'migrations'),
|
|
29
30
|
migrationsTable: 'schemaMigrations',
|
|
30
31
|
requireTs: require,
|
|
31
32
|
log: true,
|
|
@@ -191,7 +192,7 @@ export const getMigrationFiles = async (
|
|
|
191
192
|
|
|
192
193
|
let files: string[];
|
|
193
194
|
try {
|
|
194
|
-
files = await readdir(migrationsPath);
|
|
195
|
+
files = await readdir(path.resolve(migrationsPath));
|
|
195
196
|
} catch (_) {
|
|
196
197
|
return [];
|
|
197
198
|
}
|
|
@@ -212,7 +213,7 @@ export const getMigrationFiles = async (
|
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
return {
|
|
215
|
-
path: path.
|
|
216
|
+
path: path.resolve(migrationsPath, file),
|
|
216
217
|
version: timestampMatch[1],
|
|
217
218
|
};
|
|
218
219
|
});
|
|
@@ -253,3 +254,13 @@ export const getSchemaAndTableFromName = (
|
|
|
253
254
|
? [name.slice(0, index), name.slice(index + 1)]
|
|
254
255
|
: [undefined, name];
|
|
255
256
|
};
|
|
257
|
+
|
|
258
|
+
export const quoteSchemaTable = ({
|
|
259
|
+
schema,
|
|
260
|
+
name,
|
|
261
|
+
}: {
|
|
262
|
+
schema?: string;
|
|
263
|
+
name: string;
|
|
264
|
+
}) => {
|
|
265
|
+
return singleQuote(schema ? `${schema}.${name}` : name);
|
|
266
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { astToMigration } from './astToMigration';
|
|
2
|
+
import { columnTypes } from 'pqb';
|
|
3
|
+
import { RakeDbAst } from '../ast';
|
|
4
|
+
|
|
5
|
+
const template = (content: string) => `import { change } from 'rake-db';
|
|
6
|
+
|
|
7
|
+
change(async (db) => {
|
|
8
|
+
${content}
|
|
9
|
+
});
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const tableAst: RakeDbAst.Table = {
|
|
13
|
+
type: 'table',
|
|
14
|
+
action: 'create',
|
|
15
|
+
schema: 'schema',
|
|
16
|
+
name: 'table',
|
|
17
|
+
shape: {},
|
|
18
|
+
noPrimaryKey: 'ignore',
|
|
19
|
+
indexes: [],
|
|
20
|
+
foreignKeys: [],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('astToMigration', () => {
|
|
24
|
+
beforeEach(jest.clearAllMocks);
|
|
25
|
+
|
|
26
|
+
it('should return undefined when ast is empty', () => {
|
|
27
|
+
const result = astToMigration([]);
|
|
28
|
+
|
|
29
|
+
expect(result).toBe(undefined);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should create schema', () => {
|
|
33
|
+
const result = astToMigration([
|
|
34
|
+
{
|
|
35
|
+
type: 'schema',
|
|
36
|
+
action: 'create',
|
|
37
|
+
name: 'schemaName',
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
expect(result).toBe(template(` await db.createSchema('schemaName');`));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('table', () => {
|
|
45
|
+
it('should create table', () => {
|
|
46
|
+
const result = astToMigration([
|
|
47
|
+
{
|
|
48
|
+
...tableAst,
|
|
49
|
+
shape: {
|
|
50
|
+
id: columnTypes.serial().primaryKey(),
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
expect(result).toBe(
|
|
56
|
+
template(` await db.createTable('schema.table', (t) => ({
|
|
57
|
+
id: t.serial().primaryKey(),
|
|
58
|
+
}));`),
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should add columns with indexes and foreignKeys', () => {
|
|
63
|
+
const result = astToMigration([
|
|
64
|
+
{
|
|
65
|
+
...tableAst,
|
|
66
|
+
shape: {
|
|
67
|
+
someId: columnTypes
|
|
68
|
+
.integer()
|
|
69
|
+
.unique({ name: 'indexName' })
|
|
70
|
+
.foreignKey('otherTable', 'otherId', {
|
|
71
|
+
name: 'fkey',
|
|
72
|
+
match: 'FULL',
|
|
73
|
+
onUpdate: 'CASCADE',
|
|
74
|
+
onDelete: 'CASCADE',
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
expect(result).toBe(`import { change } from 'rake-db';
|
|
81
|
+
|
|
82
|
+
change(async (db) => {
|
|
83
|
+
await db.createTable('schema.table', (t) => ({
|
|
84
|
+
someId: t.integer().foreignKey('otherTable', 'otherId', {
|
|
85
|
+
name: 'fkey',
|
|
86
|
+
match: 'FULL',
|
|
87
|
+
onUpdate: 'CASCADE',
|
|
88
|
+
onDelete: 'CASCADE',
|
|
89
|
+
}).unique({
|
|
90
|
+
name: 'indexName',
|
|
91
|
+
}),
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
94
|
+
`);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should add composite primaryKeys, indexes, foreignKeys', () => {
|
|
98
|
+
const result = astToMigration([
|
|
99
|
+
{
|
|
100
|
+
...tableAst,
|
|
101
|
+
shape: {
|
|
102
|
+
id: columnTypes.serial().primaryKey(),
|
|
103
|
+
},
|
|
104
|
+
primaryKey: { columns: ['id', 'name'], options: { name: 'pkey' } },
|
|
105
|
+
indexes: [
|
|
106
|
+
{
|
|
107
|
+
columns: [{ column: 'id' }, { column: 'name' }],
|
|
108
|
+
options: { name: 'index', unique: true },
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
foreignKeys: [
|
|
112
|
+
{
|
|
113
|
+
columns: ['id', 'name'],
|
|
114
|
+
fnOrTable: 'otherTable',
|
|
115
|
+
foreignColumns: ['otherId', 'otherName'],
|
|
116
|
+
options: {
|
|
117
|
+
name: 'fkey',
|
|
118
|
+
match: 'FULL',
|
|
119
|
+
onUpdate: 'CASCADE',
|
|
120
|
+
onDelete: 'CASCADE',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
expect(result).toBe(
|
|
128
|
+
template(` await db.createTable('schema.table', (t) => ({
|
|
129
|
+
id: t.serial().primaryKey(),
|
|
130
|
+
...t.primaryKey(['id', 'name'], { name: 'pkey' }),
|
|
131
|
+
...t.index(['id', 'name'], {
|
|
132
|
+
name: 'index',
|
|
133
|
+
unique: true,
|
|
134
|
+
}),
|
|
135
|
+
...t.foreignKey(
|
|
136
|
+
['id', 'name'],
|
|
137
|
+
'otherTable',
|
|
138
|
+
['otherId', 'otherName'],
|
|
139
|
+
{
|
|
140
|
+
name: 'fkey',
|
|
141
|
+
match: 'FULL',
|
|
142
|
+
onUpdate: 'CASCADE',
|
|
143
|
+
onDelete: 'CASCADE',
|
|
144
|
+
},
|
|
145
|
+
),
|
|
146
|
+
}));`),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { RakeDbAst } from '../ast';
|
|
2
|
+
import {
|
|
3
|
+
addCode,
|
|
4
|
+
Code,
|
|
5
|
+
codeToString,
|
|
6
|
+
ColumnType,
|
|
7
|
+
foreignKeyToCode,
|
|
8
|
+
indexToCode,
|
|
9
|
+
isRaw,
|
|
10
|
+
primaryKeyToCode,
|
|
11
|
+
quoteObjectKey,
|
|
12
|
+
singleQuote,
|
|
13
|
+
TimestampColumn,
|
|
14
|
+
} from 'pqb';
|
|
15
|
+
import { quoteSchemaTable } from '../common';
|
|
16
|
+
|
|
17
|
+
export const astToMigration = (ast: RakeDbAst[]): string | undefined => {
|
|
18
|
+
const code: Code[] = [];
|
|
19
|
+
for (const item of ast) {
|
|
20
|
+
if (item.type === 'schema' && item.action === 'create') {
|
|
21
|
+
code.push(createSchema(item));
|
|
22
|
+
} else if (item.type === 'table' && item.action === 'create') {
|
|
23
|
+
if (code.length) code.push([]);
|
|
24
|
+
code.push(...createTable(item));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!code.length) return;
|
|
29
|
+
|
|
30
|
+
return `import { change } from 'rake-db';
|
|
31
|
+
|
|
32
|
+
change(async (db) => {
|
|
33
|
+
${codeToString(code, ' ', ' ')}
|
|
34
|
+
});
|
|
35
|
+
`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const createSchema = (ast: RakeDbAst.Schema) => {
|
|
39
|
+
return `await db.createSchema(${singleQuote(ast.name)});`;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const createTable = (ast: RakeDbAst.Table) => {
|
|
43
|
+
const code: Code[] = [];
|
|
44
|
+
addCode(code, `await db.createTable(${quoteSchemaTable(ast)}, (t) => ({`);
|
|
45
|
+
|
|
46
|
+
const hasTimestamps =
|
|
47
|
+
isTimestamp(ast.shape.createdAt) && isTimestamp(ast.shape.updatedAt);
|
|
48
|
+
|
|
49
|
+
for (const key in ast.shape) {
|
|
50
|
+
if (hasTimestamps && (key === 'createdAt' || key === 'updatedAt')) continue;
|
|
51
|
+
|
|
52
|
+
const line: Code[] = [`${quoteObjectKey(key)}: `];
|
|
53
|
+
for (const part of ast.shape[key].toCode('t')) {
|
|
54
|
+
addCode(line, part);
|
|
55
|
+
}
|
|
56
|
+
addCode(line, ',');
|
|
57
|
+
code.push(line);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (hasTimestamps) {
|
|
61
|
+
code.push(['...t.timestamps(),']);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (ast.primaryKey) {
|
|
65
|
+
code.push([primaryKeyToCode(ast.primaryKey, 't')]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const index of ast.indexes) {
|
|
69
|
+
code.push(indexToCode(index, 't'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const foreignKey of ast.foreignKeys) {
|
|
73
|
+
code.push(foreignKeyToCode(foreignKey, 't'));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
addCode(code, '}));');
|
|
77
|
+
|
|
78
|
+
return code;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const isTimestamp = (column?: ColumnType) => {
|
|
82
|
+
if (!column) return false;
|
|
83
|
+
|
|
84
|
+
const { default: def } = column.data;
|
|
85
|
+
return (
|
|
86
|
+
column instanceof TimestampColumn &&
|
|
87
|
+
!column.data.isNullable &&
|
|
88
|
+
def &&
|
|
89
|
+
typeof def === 'object' &&
|
|
90
|
+
isRaw(def) &&
|
|
91
|
+
def.__raw === 'now()'
|
|
92
|
+
);
|
|
93
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { DbStructure } from './dbStructure';
|
|
2
|
+
import { pullDbStructure } from './pull';
|
|
3
|
+
import { getMigrationConfigWithDefaults } from '../common';
|
|
4
|
+
import { writeMigrationFile } from '../commands/generate';
|
|
5
|
+
|
|
6
|
+
jest.mock('./dbStructure', () => {
|
|
7
|
+
const { DbStructure } = jest.requireActual('./dbStructure');
|
|
8
|
+
for (const key of Object.getOwnPropertyNames(DbStructure.prototype)) {
|
|
9
|
+
(DbStructure.prototype as unknown as Record<string, () => unknown[]>)[key] =
|
|
10
|
+
() => [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return { DbStructure };
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
jest.mock('../commands/generate', () => ({
|
|
17
|
+
writeMigrationFile: jest.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const db = DbStructure.prototype;
|
|
21
|
+
|
|
22
|
+
describe('pull', () => {
|
|
23
|
+
it('should get db structure, convert it to ast, generate migrations', async () => {
|
|
24
|
+
db.getSchemas = async () => ['schema1', 'schema2'];
|
|
25
|
+
db.getTables = async () => [
|
|
26
|
+
{
|
|
27
|
+
schemaName: 'schema',
|
|
28
|
+
name: 'table1',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
schemaName: 'public',
|
|
32
|
+
name: 'table2',
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
db.getPrimaryKeys = async () => [
|
|
36
|
+
{
|
|
37
|
+
schemaName: 'schema',
|
|
38
|
+
tableName: 'table1',
|
|
39
|
+
name: 'table1_pkey',
|
|
40
|
+
columnNames: ['id'],
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
db.getColumns = async () => [
|
|
44
|
+
{
|
|
45
|
+
schemaName: 'schema',
|
|
46
|
+
tableName: 'table1',
|
|
47
|
+
name: 'id',
|
|
48
|
+
type: 'int4',
|
|
49
|
+
default: `nextval('table1_id_seq'::regclass)`,
|
|
50
|
+
isNullable: false,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
schemaName: 'public',
|
|
54
|
+
tableName: 'table2',
|
|
55
|
+
name: 'text',
|
|
56
|
+
type: 'text',
|
|
57
|
+
isNullable: false,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
schemaName: 'public',
|
|
61
|
+
tableName: 'table2',
|
|
62
|
+
name: 'createdAt',
|
|
63
|
+
type: 'timestamp',
|
|
64
|
+
dateTimePrecision: 6,
|
|
65
|
+
isNullable: false,
|
|
66
|
+
default: 'now()',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
schemaName: 'public',
|
|
70
|
+
tableName: 'table2',
|
|
71
|
+
name: 'updatedAt',
|
|
72
|
+
type: 'timestamp',
|
|
73
|
+
dateTimePrecision: 6,
|
|
74
|
+
isNullable: false,
|
|
75
|
+
default: 'now()',
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const config = getMigrationConfigWithDefaults({
|
|
80
|
+
migrationsPath: 'migrations',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await pullDbStructure(
|
|
84
|
+
{
|
|
85
|
+
databaseURL: 'file:path',
|
|
86
|
+
},
|
|
87
|
+
config,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(writeMigrationFile).toBeCalledWith(
|
|
91
|
+
config,
|
|
92
|
+
'pull',
|
|
93
|
+
`import { change } from 'rake-db';
|
|
94
|
+
|
|
95
|
+
change(async (db) => {
|
|
96
|
+
await db.createSchema('schema1');
|
|
97
|
+
await db.createSchema('schema2');
|
|
98
|
+
|
|
99
|
+
await db.createTable('schema.table1', (t) => ({
|
|
100
|
+
id: t.serial().primaryKey(),
|
|
101
|
+
}));
|
|
102
|
+
|
|
103
|
+
await db.createTable('table2', (t) => ({
|
|
104
|
+
text: t.text(),
|
|
105
|
+
...t.timestamps(),
|
|
106
|
+
}));
|
|
107
|
+
});
|
|
108
|
+
`,
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
});
|
package/src/pull/pull.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RakeDbConfig } from '../common';
|
|
2
|
+
import { Adapter, AdapterOptions } from 'pqb';
|
|
3
|
+
import { DbStructure } from './dbStructure';
|
|
4
|
+
import { structureToAst } from './structureToAst';
|
|
5
|
+
import { astToMigration } from './astToMigration';
|
|
6
|
+
import { writeMigrationFile } from '../commands/generate';
|
|
7
|
+
|
|
8
|
+
export const pullDbStructure = async (
|
|
9
|
+
options: AdapterOptions,
|
|
10
|
+
config: RakeDbConfig,
|
|
11
|
+
) => {
|
|
12
|
+
const adapter = new Adapter(options);
|
|
13
|
+
const db = new DbStructure(adapter);
|
|
14
|
+
const ast = await structureToAst(db);
|
|
15
|
+
|
|
16
|
+
await adapter.close();
|
|
17
|
+
|
|
18
|
+
const result = astToMigration(ast);
|
|
19
|
+
if (!result) return;
|
|
20
|
+
|
|
21
|
+
await writeMigrationFile(config, 'pull', result);
|
|
22
|
+
};
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { DbStructure } from './dbStructure';
|
|
2
2
|
import {
|
|
3
3
|
Adapter,
|
|
4
|
+
BigSerialColumn,
|
|
4
5
|
DecimalColumn,
|
|
5
6
|
IntegerColumn,
|
|
7
|
+
isRaw,
|
|
8
|
+
RawExpression,
|
|
9
|
+
SerialColumn,
|
|
10
|
+
SmallSerialColumn,
|
|
6
11
|
TextColumn,
|
|
7
12
|
TimestampColumn,
|
|
8
13
|
VarCharColumn,
|
|
@@ -118,7 +123,9 @@ describe('structureToAst', () => {
|
|
|
118
123
|
db.getTables = async () => [
|
|
119
124
|
{ schemaName: 'public', name: 'table', comment: 'comment' },
|
|
120
125
|
];
|
|
126
|
+
|
|
121
127
|
const ast = await structureToAst(db);
|
|
128
|
+
|
|
122
129
|
expect(ast).toEqual([
|
|
123
130
|
{
|
|
124
131
|
type: 'table',
|
|
@@ -136,7 +143,9 @@ describe('structureToAst', () => {
|
|
|
136
143
|
it('should add table with schema', async () => {
|
|
137
144
|
const db = new DbStructure(adapter);
|
|
138
145
|
db.getTables = async () => [{ schemaName: 'custom', name: 'table' }];
|
|
146
|
+
|
|
139
147
|
const ast = await structureToAst(db);
|
|
148
|
+
|
|
140
149
|
expect(ast).toEqual([
|
|
141
150
|
{
|
|
142
151
|
type: 'table',
|
|
@@ -151,9 +160,20 @@ describe('structureToAst', () => {
|
|
|
151
160
|
]);
|
|
152
161
|
});
|
|
153
162
|
|
|
163
|
+
it('should ignore schemaMigrations table', async () => {
|
|
164
|
+
const db = new DbStructure(adapter);
|
|
165
|
+
db.getTables = async () => [
|
|
166
|
+
{ schemaName: 'public', name: 'schemaMigrations' },
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const ast = await structureToAst(db);
|
|
170
|
+
|
|
171
|
+
expect(ast).toEqual([]);
|
|
172
|
+
});
|
|
173
|
+
|
|
154
174
|
it('should add columns', async () => {
|
|
155
175
|
const db = new DbStructure(adapter);
|
|
156
|
-
db.getTables = async () => [
|
|
176
|
+
db.getTables = async () => [table];
|
|
157
177
|
db.getColumns = async () => columns;
|
|
158
178
|
|
|
159
179
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
@@ -164,9 +184,83 @@ describe('structureToAst', () => {
|
|
|
164
184
|
expect(ast.shape.name).toBeInstanceOf(TextColumn);
|
|
165
185
|
});
|
|
166
186
|
|
|
187
|
+
it('should wrap column default into raw', async () => {
|
|
188
|
+
const db = new DbStructure(adapter);
|
|
189
|
+
db.getTables = async () => [table];
|
|
190
|
+
db.getColumns = async () => [{ ...timestampColumn, default: 'now()' }];
|
|
191
|
+
|
|
192
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
193
|
+
|
|
194
|
+
const { default: def } = ast.shape.timestamp.data;
|
|
195
|
+
expect(def && typeof def === 'object' && isRaw(def)).toBe(true);
|
|
196
|
+
expect((def as RawExpression).__raw).toBe('now()');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('serial column', () => {
|
|
200
|
+
it('should add serial column based on various default values', async () => {
|
|
201
|
+
const db = new DbStructure(adapter);
|
|
202
|
+
db.getTables = async () => [{ schemaName: 'schema', name: 'table' }];
|
|
203
|
+
|
|
204
|
+
const defaults = [
|
|
205
|
+
`nextval('table_id_seq'::regclass)`,
|
|
206
|
+
`nextval('"table_id_seq"'::regclass)`,
|
|
207
|
+
`nextval('schema.table_id_seq'::regclass)`,
|
|
208
|
+
`nextval('schema."table_id_seq"'::regclass)`,
|
|
209
|
+
`nextval('"schema".table_id_seq'::regclass)`,
|
|
210
|
+
`nextval('"schema"."table_id_seq"'::regclass)`,
|
|
211
|
+
];
|
|
212
|
+
|
|
213
|
+
for (const def of defaults) {
|
|
214
|
+
db.getColumns = async () => [
|
|
215
|
+
{
|
|
216
|
+
...intColumn,
|
|
217
|
+
name: 'id',
|
|
218
|
+
schemaName: 'schema',
|
|
219
|
+
tableName: 'table',
|
|
220
|
+
default: def,
|
|
221
|
+
},
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
225
|
+
|
|
226
|
+
expect(ast.shape.id).toBeInstanceOf(SerialColumn);
|
|
227
|
+
expect(ast.shape.id.data.default).toBe(undefined);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should support smallserial, serial, and bigserial', async () => {
|
|
232
|
+
const db = new DbStructure(adapter);
|
|
233
|
+
db.getTables = async () => [{ schemaName: 'schema', name: 'table' }];
|
|
234
|
+
|
|
235
|
+
const types = [
|
|
236
|
+
['int2', SmallSerialColumn],
|
|
237
|
+
['int4', SerialColumn],
|
|
238
|
+
['int8', BigSerialColumn],
|
|
239
|
+
] as const;
|
|
240
|
+
|
|
241
|
+
for (const [type, Column] of types) {
|
|
242
|
+
db.getColumns = async () => [
|
|
243
|
+
{
|
|
244
|
+
...intColumn,
|
|
245
|
+
type,
|
|
246
|
+
name: 'id',
|
|
247
|
+
schemaName: 'schema',
|
|
248
|
+
tableName: 'table',
|
|
249
|
+
default: `nextval('table_id_seq'::regclass)`,
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
254
|
+
|
|
255
|
+
expect(ast.shape.id).toBeInstanceOf(Column);
|
|
256
|
+
expect(ast.shape.id.data.default).toBe(undefined);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
167
261
|
it('should set maxChars to char column', async () => {
|
|
168
262
|
const db = new DbStructure(adapter);
|
|
169
|
-
db.getTables = async () => [
|
|
263
|
+
db.getTables = async () => [table];
|
|
170
264
|
db.getColumns = async () => [varCharColumn];
|
|
171
265
|
|
|
172
266
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
@@ -178,7 +272,7 @@ describe('structureToAst', () => {
|
|
|
178
272
|
|
|
179
273
|
it('should set numericPrecision and numericScale to decimal column', async () => {
|
|
180
274
|
const db = new DbStructure(adapter);
|
|
181
|
-
db.getTables = async () => [
|
|
275
|
+
db.getTables = async () => [table];
|
|
182
276
|
db.getColumns = async () => [decimalColumn];
|
|
183
277
|
|
|
184
278
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
@@ -191,7 +285,7 @@ describe('structureToAst', () => {
|
|
|
191
285
|
|
|
192
286
|
it('should set dateTimePrecision to timestamp column', async () => {
|
|
193
287
|
const db = new DbStructure(adapter);
|
|
194
|
-
db.getTables = async () => [
|
|
288
|
+
db.getTables = async () => [table];
|
|
195
289
|
db.getColumns = async () => [timestampColumn];
|
|
196
290
|
|
|
197
291
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
@@ -205,7 +299,7 @@ describe('structureToAst', () => {
|
|
|
205
299
|
|
|
206
300
|
it('should set primaryKey to column', async () => {
|
|
207
301
|
const db = new DbStructure(adapter);
|
|
208
|
-
db.getTables = async () => [
|
|
302
|
+
db.getTables = async () => [table];
|
|
209
303
|
db.getColumns = async () => columns;
|
|
210
304
|
db.getPrimaryKeys = async () => [primaryKey];
|
|
211
305
|
|
|
@@ -218,7 +312,7 @@ describe('structureToAst', () => {
|
|
|
218
312
|
|
|
219
313
|
it('should add composite primary key', async () => {
|
|
220
314
|
const db = new DbStructure(adapter);
|
|
221
|
-
db.getTables = async () => [
|
|
315
|
+
db.getTables = async () => [table];
|
|
222
316
|
db.getColumns = async () => columns;
|
|
223
317
|
db.getPrimaryKeys = async () => [
|
|
224
318
|
{ ...primaryKey, columnNames: ['id', 'name'] },
|
|
@@ -236,7 +330,7 @@ describe('structureToAst', () => {
|
|
|
236
330
|
|
|
237
331
|
it('should ignore primary key name if it is standard', async () => {
|
|
238
332
|
const db = new DbStructure(adapter);
|
|
239
|
-
db.getTables = async () => [
|
|
333
|
+
db.getTables = async () => [table];
|
|
240
334
|
db.getColumns = async () => columns;
|
|
241
335
|
db.getPrimaryKeys = async () => [
|
|
242
336
|
{ ...primaryKey, columnNames: ['id', 'name'], name: 'table_pkey' },
|