rake-db 2.2.6 → 2.3.0
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 +13 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.esm.js +82 -64
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +82 -64
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/ast.ts +3 -3
- package/src/migration/changeTable.test.ts +10 -15
- package/src/migration/changeTable.ts +90 -77
- package/src/migration/createTable.test.ts +2 -3
- package/src/migration/migrationUtils.ts +28 -22
- package/src/pull/dbStructure.test.ts +14 -6
- package/src/pull/dbStructure.ts +167 -47
- package/src/pull/structureToAst.test.ts +203 -35
- package/src/pull/structureToAst.ts +69 -18
package/src/pull/dbStructure.ts
CHANGED
|
@@ -4,6 +4,7 @@ export namespace DbStructure {
|
|
|
4
4
|
export type Table = {
|
|
5
5
|
schemaName: string;
|
|
6
6
|
name: string;
|
|
7
|
+
comment?: string;
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
export type View = {
|
|
@@ -35,17 +36,31 @@ export namespace DbStructure {
|
|
|
35
36
|
dateTimePrecision?: number;
|
|
36
37
|
default?: string;
|
|
37
38
|
isNullable: boolean;
|
|
39
|
+
collation?: string;
|
|
40
|
+
compression?: 'p' | 'l'; // p for pglz, l for lz4
|
|
41
|
+
comment?: string;
|
|
38
42
|
};
|
|
39
43
|
|
|
40
44
|
export type Index = {
|
|
41
45
|
schemaName: string;
|
|
42
46
|
tableName: string;
|
|
43
|
-
columnNames: string[];
|
|
44
47
|
name: string;
|
|
48
|
+
using: string;
|
|
45
49
|
isUnique: boolean;
|
|
46
|
-
|
|
50
|
+
columns: (({ column: string } | { expression: string }) & {
|
|
51
|
+
collate?: string;
|
|
52
|
+
opclass?: string;
|
|
53
|
+
order?: string;
|
|
54
|
+
})[];
|
|
55
|
+
include?: string[];
|
|
56
|
+
with?: string;
|
|
57
|
+
tablespace?: string;
|
|
58
|
+
where?: string;
|
|
47
59
|
};
|
|
48
60
|
|
|
61
|
+
// a = no action, r = restrict, c = cascade, n = set null, d = set default
|
|
62
|
+
type ForeignKeyAction = 'a' | 'r' | 'c' | 'n' | 'd';
|
|
63
|
+
|
|
49
64
|
export type ForeignKey = {
|
|
50
65
|
schemaName: string;
|
|
51
66
|
tableName: string;
|
|
@@ -54,13 +69,15 @@ export namespace DbStructure {
|
|
|
54
69
|
name: string;
|
|
55
70
|
columnNames: string[];
|
|
56
71
|
foreignColumnNames: string[];
|
|
72
|
+
match: 'f' | 'p' | 's'; // FULL | PARTIAL | SIMPLE
|
|
73
|
+
onUpdate: ForeignKeyAction;
|
|
74
|
+
onDelete: ForeignKeyAction;
|
|
57
75
|
};
|
|
58
76
|
|
|
59
|
-
export type
|
|
77
|
+
export type PrimaryKey = {
|
|
60
78
|
schemaName: string;
|
|
61
79
|
tableName: string;
|
|
62
80
|
name: string;
|
|
63
|
-
type: 'CHECK' | 'FOREIGN KEY' | 'PRIMARY KEY' | 'UNIQUE';
|
|
64
81
|
columnNames: string[];
|
|
65
82
|
};
|
|
66
83
|
|
|
@@ -101,12 +118,14 @@ ORDER BY "name"`,
|
|
|
101
118
|
async getTables() {
|
|
102
119
|
const { rows } = await this.db.query<DbStructure.Table>(
|
|
103
120
|
`SELECT
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
121
|
+
nspname AS "schemaName",
|
|
122
|
+
relname AS "name",
|
|
123
|
+
obj_description(c.oid) AS comment
|
|
124
|
+
FROM pg_class c
|
|
125
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = relnamespace
|
|
126
|
+
WHERE relkind = 'r'
|
|
127
|
+
AND ${filterSchema('nspname')}
|
|
128
|
+
ORDER BY relname`,
|
|
110
129
|
);
|
|
111
130
|
return rows;
|
|
112
131
|
}
|
|
@@ -156,7 +175,8 @@ WHERE ${filterSchema('n.nspname')}`,
|
|
|
156
175
|
|
|
157
176
|
async getColumns() {
|
|
158
177
|
const { rows } = await this.db.query<DbStructure.Column>(
|
|
159
|
-
`SELECT
|
|
178
|
+
`SELECT
|
|
179
|
+
table_schema "schemaName",
|
|
160
180
|
table_name "tableName",
|
|
161
181
|
column_name "name",
|
|
162
182
|
udt_name "type",
|
|
@@ -165,10 +185,22 @@ WHERE ${filterSchema('n.nspname')}`,
|
|
|
165
185
|
numeric_scale AS "numericScale",
|
|
166
186
|
datetime_precision AS "dateTimePrecision",
|
|
167
187
|
column_default "default",
|
|
168
|
-
is_nullable::boolean "isNullable"
|
|
169
|
-
|
|
188
|
+
is_nullable::boolean "isNullable",
|
|
189
|
+
collation_name AS "collation",
|
|
190
|
+
NULLIF(a.attcompression, '') AS compression,
|
|
191
|
+
pgd.description AS "comment"
|
|
192
|
+
FROM information_schema.columns c
|
|
193
|
+
LEFT JOIN pg_catalog.pg_statio_all_tables AS st
|
|
194
|
+
ON c.table_schema = st.schemaname
|
|
195
|
+
AND c.table_name = st.relname
|
|
196
|
+
LEFT JOIN pg_catalog.pg_description pgd
|
|
197
|
+
ON pgd.objoid = st.relid
|
|
198
|
+
AND pgd.objsubid = c.ordinal_position
|
|
199
|
+
LEFT JOIN pg_catalog.pg_attribute a
|
|
200
|
+
ON a.attrelid = st.relid
|
|
201
|
+
AND a.attnum = c.ordinal_position
|
|
170
202
|
WHERE ${filterSchema('table_schema')}
|
|
171
|
-
ORDER BY ordinal_position`,
|
|
203
|
+
ORDER BY c.ordinal_position`,
|
|
172
204
|
);
|
|
173
205
|
return rows;
|
|
174
206
|
}
|
|
@@ -176,64 +208,152 @@ ORDER BY ordinal_position`,
|
|
|
176
208
|
async getIndexes() {
|
|
177
209
|
const { rows } = await this.db.query<DbStructure.Index>(
|
|
178
210
|
`SELECT
|
|
179
|
-
nspname "schemaName",
|
|
211
|
+
n.nspname "schemaName",
|
|
180
212
|
t.relname "tableName",
|
|
181
|
-
json_agg(attname) "columnNames",
|
|
182
213
|
ic.relname "name",
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
214
|
+
am.amname AS "using",
|
|
215
|
+
i.indisunique "isUnique",
|
|
216
|
+
(
|
|
217
|
+
SELECT json_agg(
|
|
218
|
+
(
|
|
219
|
+
CASE WHEN t.e = 0
|
|
220
|
+
THEN jsonb_build_object('expression', pg_get_indexdef(i.indexrelid, t.i::int4, false))
|
|
221
|
+
ELSE jsonb_build_object('column', (
|
|
222
|
+
(
|
|
223
|
+
SELECT attname
|
|
224
|
+
FROM pg_catalog.pg_attribute
|
|
225
|
+
WHERE attrelid = i.indrelid
|
|
226
|
+
AND attnum = t.e
|
|
227
|
+
)
|
|
228
|
+
))
|
|
229
|
+
END
|
|
230
|
+
) || (
|
|
231
|
+
CASE WHEN i.indcollation[t.i - 1] = 0
|
|
232
|
+
THEN '{}'::jsonb
|
|
233
|
+
ELSE (
|
|
234
|
+
SELECT (
|
|
235
|
+
CASE WHEN collname = 'default'
|
|
236
|
+
THEN '{}'::jsonb
|
|
237
|
+
ELSE jsonb_build_object('collate', collname)
|
|
238
|
+
END
|
|
239
|
+
)
|
|
240
|
+
FROM pg_catalog.pg_collation
|
|
241
|
+
WHERE oid = i.indcollation[t.i - 1]
|
|
242
|
+
)
|
|
243
|
+
END
|
|
244
|
+
) || (
|
|
245
|
+
SELECT
|
|
246
|
+
CASE WHEN opcdefault AND attoptions IS NULL
|
|
247
|
+
THEN '{}'::jsonb
|
|
248
|
+
ELSE jsonb_build_object(
|
|
249
|
+
'opclass', opcname || COALESCE('(' || array_to_string(attoptions, ', ') || ')', '')
|
|
250
|
+
)
|
|
251
|
+
END
|
|
252
|
+
FROM pg_opclass
|
|
253
|
+
LEFT JOIN pg_attribute
|
|
254
|
+
ON attrelid = i.indexrelid
|
|
255
|
+
AND attnum = t.i
|
|
256
|
+
WHERE oid = i.indclass[t.i - 1]
|
|
257
|
+
) || (
|
|
258
|
+
CASE WHEN i.indoption[t.i - 1] = 0
|
|
259
|
+
THEN '{}'::jsonb
|
|
260
|
+
ELSE jsonb_build_object(
|
|
261
|
+
'order',
|
|
262
|
+
CASE
|
|
263
|
+
WHEN i.indoption[t.i - 1] = 1 THEN 'DESC NULLS LAST'
|
|
264
|
+
WHEN i.indoption[t.i - 1] = 2 THEN 'ASC NULLS FIRST'
|
|
265
|
+
WHEN i.indoption[t.i - 1] = 3 THEN 'DESC'
|
|
266
|
+
ELSE NULL
|
|
267
|
+
END
|
|
268
|
+
)
|
|
269
|
+
END
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
FROM unnest(i.indkey[:indnkeyatts - 1]) WITH ORDINALITY AS t(e, i)
|
|
273
|
+
) "columns",
|
|
274
|
+
(
|
|
275
|
+
SELECT json_agg(
|
|
276
|
+
(
|
|
277
|
+
SELECT attname
|
|
278
|
+
FROM pg_catalog.pg_attribute
|
|
279
|
+
WHERE attrelid = i.indrelid
|
|
280
|
+
AND attnum = j.e
|
|
281
|
+
)
|
|
282
|
+
)
|
|
283
|
+
FROM unnest(i.indkey[indnkeyatts:]) AS j(e)
|
|
284
|
+
) AS "include",
|
|
285
|
+
NULLIF(pg_catalog.array_to_string(
|
|
286
|
+
ic.reloptions || array(SELECT 'toast.' || x FROM pg_catalog.unnest(tc.reloptions) x),
|
|
287
|
+
', '
|
|
288
|
+
), '') AS "with",
|
|
289
|
+
(
|
|
290
|
+
SELECT tablespace
|
|
291
|
+
FROM pg_indexes
|
|
292
|
+
WHERE schemaname = n.nspname
|
|
293
|
+
AND indexname = ic.relname
|
|
294
|
+
) AS tablespace,
|
|
295
|
+
pg_get_expr(i.indpred, i.indrelid) AS "where"
|
|
296
|
+
FROM pg_index i
|
|
297
|
+
JOIN pg_class t ON t.oid = i.indrelid
|
|
187
298
|
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
188
|
-
JOIN
|
|
189
|
-
JOIN
|
|
299
|
+
JOIN pg_class ic ON ic.oid = i.indexrelid
|
|
300
|
+
JOIN pg_am am ON am.oid = ic.relam
|
|
301
|
+
LEFT JOIN pg_catalog.pg_class tc ON (ic.reltoastrelid = tc.oid)
|
|
190
302
|
WHERE ${filterSchema('n.nspname')}
|
|
191
|
-
|
|
192
|
-
ORDER BY
|
|
303
|
+
AND NOT i.indisprimary
|
|
304
|
+
ORDER BY ic.relname`,
|
|
193
305
|
);
|
|
194
306
|
return rows;
|
|
195
307
|
}
|
|
196
308
|
|
|
197
309
|
async getForeignKeys() {
|
|
198
310
|
const { rows } = await this.db.query<DbStructure.ForeignKey>(
|
|
199
|
-
`SELECT
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
311
|
+
`SELECT
|
|
312
|
+
s.nspname AS "schemaName",
|
|
313
|
+
t.relname AS "tableName",
|
|
314
|
+
fs.nspname AS "foreignTableSchemaName",
|
|
315
|
+
ft.relname AS "foreignTableName",
|
|
316
|
+
c.conname AS "name",
|
|
204
317
|
(
|
|
205
|
-
SELECT json_agg(
|
|
206
|
-
FROM information_schema.key_column_usage
|
|
207
|
-
WHERE
|
|
208
|
-
AND
|
|
318
|
+
SELECT json_agg(ccu.column_name)
|
|
319
|
+
FROM information_schema.key_column_usage ccu
|
|
320
|
+
WHERE ccu.constraint_name = c.conname
|
|
321
|
+
AND ccu.table_schema = cs.nspname
|
|
209
322
|
) AS "columnNames",
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
323
|
+
(
|
|
324
|
+
SELECT json_agg(ccu.column_name)
|
|
325
|
+
FROM information_schema.constraint_column_usage ccu
|
|
326
|
+
WHERE ccu.constraint_name = c.conname
|
|
327
|
+
AND ccu.table_schema = cs.nspname
|
|
328
|
+
) AS "foreignColumnNames",
|
|
329
|
+
c.confmatchtype AS match,
|
|
330
|
+
c.confupdtype AS "onUpdate",
|
|
331
|
+
c.confdeltype AS "onDelete"
|
|
332
|
+
FROM pg_catalog.pg_constraint c
|
|
333
|
+
JOIN pg_class t ON t.oid = conrelid
|
|
334
|
+
JOIN pg_catalog.pg_namespace s ON s.oid = t.relnamespace
|
|
335
|
+
JOIN pg_class ft ON ft.oid = confrelid
|
|
336
|
+
JOIN pg_catalog.pg_namespace fs ON fs.oid = ft.relnamespace
|
|
337
|
+
JOIN pg_catalog.pg_namespace cs ON cs.oid = c.connamespace
|
|
338
|
+
WHERE contype = 'f'
|
|
339
|
+
ORDER BY c.conname`,
|
|
219
340
|
);
|
|
220
341
|
return rows;
|
|
221
342
|
}
|
|
222
343
|
|
|
223
|
-
async
|
|
224
|
-
const { rows } = await this.db.query<DbStructure.
|
|
344
|
+
async getPrimaryKeys() {
|
|
345
|
+
const { rows } = await this.db.query<DbStructure.PrimaryKey>(
|
|
225
346
|
`SELECT tc.table_schema AS "schemaName",
|
|
226
347
|
tc.table_name AS "tableName",
|
|
227
348
|
tc.constraint_name AS "name",
|
|
228
|
-
tc.constraint_type AS "type",
|
|
229
349
|
json_agg(ccu.column_name) "columnNames"
|
|
230
350
|
FROM information_schema.table_constraints tc
|
|
231
351
|
JOIN information_schema.constraint_column_usage ccu
|
|
232
352
|
ON ccu.constraint_name = tc.constraint_name
|
|
233
353
|
AND ccu.table_schema = tc.table_schema
|
|
234
|
-
WHERE tc.constraint_type
|
|
354
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
235
355
|
AND ${filterSchema('tc.table_schema')}
|
|
236
|
-
GROUP BY "schemaName", "tableName", "name"
|
|
356
|
+
GROUP BY "schemaName", "tableName", "name"
|
|
237
357
|
ORDER BY "name"`,
|
|
238
358
|
);
|
|
239
359
|
return rows;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { DbStructure } from './dbStructure';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Adapter,
|
|
4
|
+
DecimalColumn,
|
|
5
|
+
IntegerColumn,
|
|
6
|
+
TextColumn,
|
|
7
|
+
TimestampColumn,
|
|
8
|
+
VarCharColumn,
|
|
9
|
+
} from 'pqb';
|
|
3
10
|
import { structureToAst } from './structureToAst';
|
|
4
11
|
import { RakeDbAst } from '../ast';
|
|
5
12
|
|
|
@@ -8,7 +15,7 @@ const query = jest.fn().mockImplementation(() => ({ rows: [] }));
|
|
|
8
15
|
adapter.query = query;
|
|
9
16
|
adapter.arrays = query;
|
|
10
17
|
|
|
11
|
-
const
|
|
18
|
+
const intColumn: DbStructure.Column = {
|
|
12
19
|
schemaName: 'public',
|
|
13
20
|
tableName: 'table',
|
|
14
21
|
name: 'column',
|
|
@@ -17,32 +24,54 @@ const tableColumn: DbStructure.Column = {
|
|
|
17
24
|
isNullable: false,
|
|
18
25
|
};
|
|
19
26
|
|
|
27
|
+
const varCharColumn: DbStructure.Column = {
|
|
28
|
+
...intColumn,
|
|
29
|
+
name: 'varchar',
|
|
30
|
+
type: 'character varying',
|
|
31
|
+
collation: 'en_US',
|
|
32
|
+
maxChars: 10,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const decimalColumn: DbStructure.Column = {
|
|
36
|
+
...intColumn,
|
|
37
|
+
name: 'decimal',
|
|
38
|
+
type: 'decimal',
|
|
39
|
+
numericPrecision: 10,
|
|
40
|
+
numericScale: 2,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const timestampColumn: DbStructure.Column = {
|
|
44
|
+
...intColumn,
|
|
45
|
+
name: 'timestamp',
|
|
46
|
+
type: 'timestamp',
|
|
47
|
+
dateTimePrecision: 10,
|
|
48
|
+
};
|
|
49
|
+
|
|
20
50
|
const tableColumns = [
|
|
21
|
-
{ ...
|
|
22
|
-
{ ...
|
|
51
|
+
{ ...intColumn, name: 'id' },
|
|
52
|
+
{ ...intColumn, name: 'name', type: 'text' },
|
|
23
53
|
];
|
|
24
54
|
|
|
25
|
-
const otherTableColumn = { ...
|
|
55
|
+
const otherTableColumn = { ...intColumn, tableName: 'otherTable' };
|
|
26
56
|
|
|
27
57
|
const table = { schemaName: 'public', name: 'table' };
|
|
28
58
|
|
|
29
59
|
const columns = [...tableColumns, otherTableColumn];
|
|
30
60
|
|
|
31
|
-
const primaryKey: DbStructure.
|
|
61
|
+
const primaryKey: DbStructure.PrimaryKey = {
|
|
32
62
|
schemaName: 'public',
|
|
33
63
|
tableName: 'table',
|
|
34
64
|
name: 'pkey',
|
|
35
|
-
type: 'PRIMARY KEY',
|
|
36
65
|
columnNames: ['id'],
|
|
37
66
|
};
|
|
38
67
|
|
|
39
68
|
const index: DbStructure.Index = {
|
|
40
69
|
schemaName: 'public',
|
|
41
70
|
tableName: 'table',
|
|
42
|
-
columnNames: ['name'],
|
|
43
71
|
name: 'index',
|
|
72
|
+
using: 'btree',
|
|
44
73
|
isUnique: false,
|
|
45
|
-
|
|
74
|
+
columns: [{ column: 'name' }],
|
|
46
75
|
};
|
|
47
76
|
|
|
48
77
|
const foreignKey: DbStructure.ForeignKey = {
|
|
@@ -53,6 +82,9 @@ const foreignKey: DbStructure.ForeignKey = {
|
|
|
53
82
|
name: 'fkey',
|
|
54
83
|
columnNames: ['otherId'],
|
|
55
84
|
foreignColumnNames: ['id'],
|
|
85
|
+
match: 'f',
|
|
86
|
+
onUpdate: 'c',
|
|
87
|
+
onDelete: 'c',
|
|
56
88
|
};
|
|
57
89
|
|
|
58
90
|
const extension: DbStructure.Extension = {
|
|
@@ -83,13 +115,16 @@ describe('structureToAst', () => {
|
|
|
83
115
|
describe('table', () => {
|
|
84
116
|
it('should add table', async () => {
|
|
85
117
|
const db = new DbStructure(adapter);
|
|
86
|
-
db.getTables = async () => [
|
|
118
|
+
db.getTables = async () => [
|
|
119
|
+
{ schemaName: 'public', name: 'table', comment: 'comment' },
|
|
120
|
+
];
|
|
87
121
|
const ast = await structureToAst(db);
|
|
88
122
|
expect(ast).toEqual([
|
|
89
123
|
{
|
|
90
124
|
type: 'table',
|
|
91
125
|
action: 'create',
|
|
92
126
|
name: 'table',
|
|
127
|
+
comment: 'comment',
|
|
93
128
|
shape: {},
|
|
94
129
|
noPrimaryKey: 'ignore',
|
|
95
130
|
indexes: [],
|
|
@@ -129,11 +164,50 @@ describe('structureToAst', () => {
|
|
|
129
164
|
expect(ast.shape.name).toBeInstanceOf(TextColumn);
|
|
130
165
|
});
|
|
131
166
|
|
|
167
|
+
it('should set maxChars to char column', async () => {
|
|
168
|
+
const db = new DbStructure(adapter);
|
|
169
|
+
db.getTables = async () => [{ schemaName: 'public', name: 'table' }];
|
|
170
|
+
db.getColumns = async () => [varCharColumn];
|
|
171
|
+
|
|
172
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
173
|
+
|
|
174
|
+
const column = ast.shape[varCharColumn.name];
|
|
175
|
+
expect(column).toBeInstanceOf(VarCharColumn);
|
|
176
|
+
expect(column.data.maxChars).toBe(varCharColumn.maxChars);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should set numericPrecision and numericScale to decimal column', async () => {
|
|
180
|
+
const db = new DbStructure(adapter);
|
|
181
|
+
db.getTables = async () => [{ schemaName: 'public', name: 'table' }];
|
|
182
|
+
db.getColumns = async () => [decimalColumn];
|
|
183
|
+
|
|
184
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
185
|
+
|
|
186
|
+
const column = ast.shape[decimalColumn.name];
|
|
187
|
+
expect(column).toBeInstanceOf(DecimalColumn);
|
|
188
|
+
expect(column.data.numericPrecision).toBe(decimalColumn.numericPrecision);
|
|
189
|
+
expect(column.data.numericScale).toBe(decimalColumn.numericScale);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should set dateTimePrecision to timestamp column', async () => {
|
|
193
|
+
const db = new DbStructure(adapter);
|
|
194
|
+
db.getTables = async () => [{ schemaName: 'public', name: 'table' }];
|
|
195
|
+
db.getColumns = async () => [timestampColumn];
|
|
196
|
+
|
|
197
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
198
|
+
|
|
199
|
+
const column = ast.shape[timestampColumn.name];
|
|
200
|
+
expect(column).toBeInstanceOf(TimestampColumn);
|
|
201
|
+
expect(column.data.dateTimePrecision).toBe(
|
|
202
|
+
timestampColumn.dateTimePrecision,
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
132
206
|
it('should set primaryKey to column', async () => {
|
|
133
207
|
const db = new DbStructure(adapter);
|
|
134
208
|
db.getTables = async () => [{ schemaName: 'public', name: 'table' }];
|
|
135
209
|
db.getColumns = async () => columns;
|
|
136
|
-
db.
|
|
210
|
+
db.getPrimaryKeys = async () => [primaryKey];
|
|
137
211
|
|
|
138
212
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
139
213
|
|
|
@@ -146,7 +220,7 @@ describe('structureToAst', () => {
|
|
|
146
220
|
const db = new DbStructure(adapter);
|
|
147
221
|
db.getTables = async () => [{ schemaName: 'public', name: 'table' }];
|
|
148
222
|
db.getColumns = async () => columns;
|
|
149
|
-
db.
|
|
223
|
+
db.getPrimaryKeys = async () => [
|
|
150
224
|
{ ...primaryKey, columnNames: ['id', 'name'] },
|
|
151
225
|
];
|
|
152
226
|
|
|
@@ -164,7 +238,7 @@ describe('structureToAst', () => {
|
|
|
164
238
|
const db = new DbStructure(adapter);
|
|
165
239
|
db.getTables = async () => [{ schemaName: 'public', name: 'table' }];
|
|
166
240
|
db.getColumns = async () => columns;
|
|
167
|
-
db.
|
|
241
|
+
db.getPrimaryKeys = async () => [
|
|
168
242
|
{ ...primaryKey, columnNames: ['id', 'name'], name: 'table_pkey' },
|
|
169
243
|
];
|
|
170
244
|
|
|
@@ -177,42 +251,79 @@ describe('structureToAst', () => {
|
|
|
177
251
|
});
|
|
178
252
|
});
|
|
179
253
|
|
|
180
|
-
it('should
|
|
254
|
+
it('should add index to column', async () => {
|
|
181
255
|
const db = new DbStructure(adapter);
|
|
182
|
-
db.getTables = async () => [
|
|
256
|
+
db.getTables = async () => [table];
|
|
183
257
|
db.getColumns = async () => columns;
|
|
184
|
-
db.getIndexes = async () => [
|
|
258
|
+
db.getIndexes = async () => [index];
|
|
185
259
|
|
|
186
260
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
187
|
-
expect(ast.shape.name.data.
|
|
261
|
+
expect(ast.shape.name.data.indexes).toEqual([
|
|
262
|
+
{
|
|
263
|
+
name: 'index',
|
|
264
|
+
unique: false,
|
|
265
|
+
},
|
|
266
|
+
]);
|
|
188
267
|
expect(ast.indexes).toHaveLength(0);
|
|
189
268
|
});
|
|
190
269
|
|
|
191
|
-
it('should
|
|
270
|
+
it('should set index options to column index', async () => {
|
|
192
271
|
const db = new DbStructure(adapter);
|
|
193
272
|
db.getTables = async () => [table];
|
|
194
273
|
db.getColumns = async () => columns;
|
|
195
|
-
db.getIndexes = async () => [
|
|
274
|
+
db.getIndexes = async () => [
|
|
275
|
+
{
|
|
276
|
+
...index,
|
|
277
|
+
using: 'gist',
|
|
278
|
+
isUnique: true,
|
|
279
|
+
columns: [
|
|
280
|
+
{
|
|
281
|
+
column: 'name',
|
|
282
|
+
collate: 'en_US',
|
|
283
|
+
opclass: 'varchar_ops',
|
|
284
|
+
order: 'DESC',
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
include: ['id'],
|
|
288
|
+
with: 'fillfactor=80',
|
|
289
|
+
tablespace: 'tablespace',
|
|
290
|
+
where: 'condition',
|
|
291
|
+
},
|
|
292
|
+
];
|
|
196
293
|
|
|
197
294
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
198
|
-
expect(ast.shape.name.data.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
295
|
+
expect(ast.shape.name.data.indexes).toEqual([
|
|
296
|
+
{
|
|
297
|
+
name: 'index',
|
|
298
|
+
using: 'gist',
|
|
299
|
+
unique: true,
|
|
300
|
+
collate: 'en_US',
|
|
301
|
+
opclass: 'varchar_ops',
|
|
302
|
+
order: 'DESC',
|
|
303
|
+
include: ['id'],
|
|
304
|
+
with: 'fillfactor=80',
|
|
305
|
+
tablespace: 'tablespace',
|
|
306
|
+
where: 'condition',
|
|
307
|
+
},
|
|
308
|
+
]);
|
|
202
309
|
expect(ast.indexes).toHaveLength(0);
|
|
203
310
|
});
|
|
204
311
|
|
|
205
|
-
it('should add composite indexes to table', async () => {
|
|
312
|
+
it('should add composite indexes to the table', async () => {
|
|
206
313
|
const db = new DbStructure(adapter);
|
|
207
314
|
db.getTables = async () => [table];
|
|
208
315
|
db.getColumns = async () => columns;
|
|
209
316
|
db.getIndexes = async () => [
|
|
210
|
-
{ ...index,
|
|
211
|
-
{
|
|
317
|
+
{ ...index, columns: [{ column: 'id' }, { column: 'name' }] },
|
|
318
|
+
{
|
|
319
|
+
...index,
|
|
320
|
+
columns: [{ column: 'id' }, { column: 'name' }],
|
|
321
|
+
isUnique: true,
|
|
322
|
+
},
|
|
212
323
|
];
|
|
213
324
|
|
|
214
325
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
215
|
-
expect(ast.shape.name.data.
|
|
326
|
+
expect(ast.shape.name.data.indexes).toBe(undefined);
|
|
216
327
|
expect(ast.indexes).toEqual([
|
|
217
328
|
{
|
|
218
329
|
columns: [{ column: 'id' }, { column: 'name' }],
|
|
@@ -225,22 +336,76 @@ describe('structureToAst', () => {
|
|
|
225
336
|
]);
|
|
226
337
|
});
|
|
227
338
|
|
|
339
|
+
it('should add index with expression and options to the table', async () => {
|
|
340
|
+
const db = new DbStructure(adapter);
|
|
341
|
+
db.getTables = async () => [table];
|
|
342
|
+
db.getColumns = async () => columns;
|
|
343
|
+
db.getIndexes = async () => [
|
|
344
|
+
{
|
|
345
|
+
...index,
|
|
346
|
+
using: 'gist',
|
|
347
|
+
isUnique: true,
|
|
348
|
+
columns: [
|
|
349
|
+
{
|
|
350
|
+
expression: 'lower(name)',
|
|
351
|
+
collate: 'en_US',
|
|
352
|
+
opclass: 'varchar_ops',
|
|
353
|
+
order: 'DESC',
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
include: ['id'],
|
|
357
|
+
with: 'fillfactor=80',
|
|
358
|
+
tablespace: 'tablespace',
|
|
359
|
+
where: 'condition',
|
|
360
|
+
},
|
|
361
|
+
];
|
|
362
|
+
|
|
363
|
+
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
364
|
+
expect(ast.shape.name.data.indexes).toBe(undefined);
|
|
365
|
+
expect(ast.indexes).toEqual([
|
|
366
|
+
{
|
|
367
|
+
columns: [
|
|
368
|
+
{
|
|
369
|
+
expression: 'lower(name)',
|
|
370
|
+
collate: 'en_US',
|
|
371
|
+
opclass: 'varchar_ops',
|
|
372
|
+
order: 'DESC',
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
options: {
|
|
376
|
+
name: 'index',
|
|
377
|
+
using: 'gist',
|
|
378
|
+
unique: true,
|
|
379
|
+
include: ['id'],
|
|
380
|
+
with: 'fillfactor=80',
|
|
381
|
+
tablespace: 'tablespace',
|
|
382
|
+
where: 'condition',
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
]);
|
|
386
|
+
});
|
|
387
|
+
|
|
228
388
|
it('should add foreign key to the column', async () => {
|
|
229
389
|
const db = new DbStructure(adapter);
|
|
230
390
|
db.getTables = async () => [table];
|
|
231
391
|
db.getColumns = async () => [
|
|
232
392
|
...columns,
|
|
233
|
-
{ ...
|
|
393
|
+
{ ...intColumn, name: 'otherId' },
|
|
234
394
|
];
|
|
235
395
|
db.getForeignKeys = async () => [foreignKey];
|
|
236
396
|
|
|
237
397
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
238
398
|
|
|
239
|
-
expect(ast.shape.otherId.data.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
399
|
+
expect(ast.shape.otherId.data.foreignKeys).toEqual([
|
|
400
|
+
{
|
|
401
|
+
columns: ['id'],
|
|
402
|
+
name: 'fkey',
|
|
403
|
+
table: 'otherTable',
|
|
404
|
+
match: 'FULL',
|
|
405
|
+
onUpdate: 'CASCADE',
|
|
406
|
+
onDelete: 'CASCADE',
|
|
407
|
+
},
|
|
408
|
+
]);
|
|
244
409
|
expect(ast.foreignKeys).toHaveLength(0);
|
|
245
410
|
});
|
|
246
411
|
|
|
@@ -249,7 +414,7 @@ describe('structureToAst', () => {
|
|
|
249
414
|
db.getTables = async () => [table];
|
|
250
415
|
db.getColumns = async () => [
|
|
251
416
|
...columns,
|
|
252
|
-
{ ...
|
|
417
|
+
{ ...intColumn, name: 'otherId' },
|
|
253
418
|
];
|
|
254
419
|
db.getForeignKeys = async () => [
|
|
255
420
|
{
|
|
@@ -261,7 +426,7 @@ describe('structureToAst', () => {
|
|
|
261
426
|
|
|
262
427
|
const [ast] = (await structureToAst(db)) as [RakeDbAst.Table];
|
|
263
428
|
|
|
264
|
-
expect(ast.shape.otherId.data.
|
|
429
|
+
expect(ast.shape.otherId.data.foreignKeys).toBe(undefined);
|
|
265
430
|
expect(ast.foreignKeys).toEqual([
|
|
266
431
|
{
|
|
267
432
|
columns: ['name', 'otherId'],
|
|
@@ -269,6 +434,9 @@ describe('structureToAst', () => {
|
|
|
269
434
|
foreignColumns: ['name', 'id'],
|
|
270
435
|
options: {
|
|
271
436
|
name: 'fkey',
|
|
437
|
+
match: 'FULL',
|
|
438
|
+
onUpdate: 'CASCADE',
|
|
439
|
+
onDelete: 'CASCADE',
|
|
272
440
|
},
|
|
273
441
|
},
|
|
274
442
|
]);
|