rake-db 2.3.30 → 2.3.31
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/package.json +11 -21
- package/.env +0 -1
- package/.env.local +0 -2
- package/.turbo/turbo-check.log +0 -23
- package/.turbo/turbo-test.log +0 -22
- package/.turbo/turbo-test:ci.log +0 -22
- package/CHANGELOG.md +0 -395
- package/app/dbScript.ts +0 -33
- package/app/migrations/20221017181504_createUser.ts +0 -14
- package/app/migrations/20221017200111_createProfile.ts +0 -10
- package/app/migrations/20221017200252_createChat.ts +0 -9
- package/app/migrations/20221017200326_createChatUser.ts +0 -10
- package/app/migrations/20221017200900_createMessage.ts +0 -12
- package/app/migrations/20221017201235_createGeoSchema.ts +0 -5
- package/app/migrations/20221017210011_createCountry.ts +0 -8
- package/app/migrations/20221017210133_createCity.ts +0 -9
- package/app/migrations/20221105202843_createUniqueTable.ts +0 -12
- package/jest-setup.ts +0 -3
- package/rollup.config.js +0 -3
- package/src/ast.ts +0 -130
- package/src/commands/createOrDrop.test.ts +0 -214
- package/src/commands/createOrDrop.ts +0 -151
- package/src/commands/generate.test.ts +0 -136
- package/src/commands/generate.ts +0 -93
- package/src/commands/migrateOrRollback.test.ts +0 -267
- package/src/commands/migrateOrRollback.ts +0 -190
- package/src/common.test.ts +0 -295
- package/src/common.ts +0 -353
- package/src/errors.ts +0 -3
- package/src/index.ts +0 -8
- package/src/migration/change.test.ts +0 -16
- package/src/migration/change.ts +0 -15
- package/src/migration/changeTable.test.ts +0 -897
- package/src/migration/changeTable.ts +0 -566
- package/src/migration/createTable.test.ts +0 -384
- package/src/migration/createTable.ts +0 -193
- package/src/migration/migration.test.ts +0 -430
- package/src/migration/migration.ts +0 -518
- package/src/migration/migrationUtils.ts +0 -307
- package/src/migration/tableMethods.ts +0 -8
- package/src/pull/astToMigration.test.ts +0 -275
- package/src/pull/astToMigration.ts +0 -173
- package/src/pull/dbStructure.test.ts +0 -180
- package/src/pull/dbStructure.ts +0 -413
- package/src/pull/pull.test.ts +0 -115
- package/src/pull/pull.ts +0 -22
- package/src/pull/structureToAst.test.ts +0 -841
- package/src/pull/structureToAst.ts +0 -372
- package/src/rakeDb.test.ts +0 -131
- package/src/rakeDb.ts +0 -84
- package/src/test-utils.ts +0 -64
- package/tsconfig.json +0 -12
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
asMock,
|
|
3
|
-
expectSql,
|
|
4
|
-
getDb,
|
|
5
|
-
queryMock,
|
|
6
|
-
resetDb,
|
|
7
|
-
toLine,
|
|
8
|
-
} from '../test-utils';
|
|
9
|
-
|
|
10
|
-
const db = getDb();
|
|
11
|
-
|
|
12
|
-
(['createTable', 'dropTable'] as const).forEach((action) => {
|
|
13
|
-
describe(action, () => {
|
|
14
|
-
beforeEach(resetDb);
|
|
15
|
-
|
|
16
|
-
it('should call appCodeUpdater', async () => {
|
|
17
|
-
await db[action]('name', (t) => ({
|
|
18
|
-
id: t.serial().primaryKey(),
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
expect(db.options.appCodeUpdater).toHaveBeenCalled();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it(`should ${action} with schema`, async () => {
|
|
25
|
-
await db[action]('schema.name', (t) => ({ id: t.serial().primaryKey() }));
|
|
26
|
-
|
|
27
|
-
if (action === 'createTable') {
|
|
28
|
-
expectSql(`
|
|
29
|
-
CREATE TABLE "schema"."name" (
|
|
30
|
-
"id" serial PRIMARY KEY
|
|
31
|
-
)
|
|
32
|
-
`);
|
|
33
|
-
} else {
|
|
34
|
-
expectSql(`
|
|
35
|
-
DROP TABLE "schema"."name"
|
|
36
|
-
`);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it(`should ${action} with comment`, async () => {
|
|
41
|
-
await db[action]('name', { comment: 'this is a table comment' }, (t) => ({
|
|
42
|
-
id: t.serial().primaryKey(),
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
|
-
if (action === 'createTable') {
|
|
46
|
-
expectSql([
|
|
47
|
-
`
|
|
48
|
-
CREATE TABLE "name" (
|
|
49
|
-
"id" serial PRIMARY KEY
|
|
50
|
-
)
|
|
51
|
-
`,
|
|
52
|
-
`COMMENT ON TABLE "name" IS 'this is a table comment'`,
|
|
53
|
-
]);
|
|
54
|
-
} else {
|
|
55
|
-
expectSql(`
|
|
56
|
-
DROP TABLE "name"
|
|
57
|
-
`);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it(`should ${action} and revert on rollback`, async () => {
|
|
62
|
-
const fn = () => {
|
|
63
|
-
return db[action]('table', { dropMode: 'CASCADE' }, (t) => ({
|
|
64
|
-
id: t.serial().primaryKey(),
|
|
65
|
-
nullable: t.text().nullable(),
|
|
66
|
-
nonNullable: t.text(),
|
|
67
|
-
enum: t.enum('mood'),
|
|
68
|
-
withDefault: t.boolean().default(false),
|
|
69
|
-
withDefaultRaw: t.date().default(t.raw(`now()`)),
|
|
70
|
-
withIndex: t.text().index({
|
|
71
|
-
name: 'indexName',
|
|
72
|
-
unique: true,
|
|
73
|
-
using: 'gin',
|
|
74
|
-
collate: 'utf-8',
|
|
75
|
-
opclass: 'opclass',
|
|
76
|
-
order: 'ASC',
|
|
77
|
-
include: 'id',
|
|
78
|
-
with: 'fillfactor = 70',
|
|
79
|
-
tablespace: 'tablespace',
|
|
80
|
-
where: 'column = 123',
|
|
81
|
-
}),
|
|
82
|
-
uniqueColumn: t.text().unique(),
|
|
83
|
-
columnWithComment: t.text().comment('this is a column comment'),
|
|
84
|
-
varcharWithLength: t.varchar(20),
|
|
85
|
-
decimalWithPrecisionAndScale: t.decimal(10, 5),
|
|
86
|
-
columnWithCompression: t.text().compression('compression'),
|
|
87
|
-
columnWithCollate: t.text().collate('utf-8'),
|
|
88
|
-
columnWithForeignKey: t.integer().foreignKey('table', 'column', {
|
|
89
|
-
name: 'fkeyConstraint',
|
|
90
|
-
match: 'FULL',
|
|
91
|
-
onUpdate: 'CASCADE',
|
|
92
|
-
onDelete: 'CASCADE',
|
|
93
|
-
}),
|
|
94
|
-
...t.timestamps(),
|
|
95
|
-
}));
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const expectCreateTable = () => {
|
|
99
|
-
expectSql([
|
|
100
|
-
'SELECT unnest(enum_range(NULL::"mood"))::text',
|
|
101
|
-
`
|
|
102
|
-
CREATE TABLE "table" (
|
|
103
|
-
"id" serial PRIMARY KEY,
|
|
104
|
-
"nullable" text,
|
|
105
|
-
"nonNullable" text NOT NULL,
|
|
106
|
-
"enum" "mood" NOT NULL,
|
|
107
|
-
"withDefault" boolean NOT NULL DEFAULT false,
|
|
108
|
-
"withDefaultRaw" date NOT NULL DEFAULT now(),
|
|
109
|
-
"withIndex" text NOT NULL,
|
|
110
|
-
"uniqueColumn" text NOT NULL,
|
|
111
|
-
"columnWithComment" text NOT NULL,
|
|
112
|
-
"varcharWithLength" varchar(20) NOT NULL,
|
|
113
|
-
"decimalWithPrecisionAndScale" decimal(10, 5) NOT NULL,
|
|
114
|
-
"columnWithCompression" text COMPRESSION compression NOT NULL,
|
|
115
|
-
"columnWithCollate" text COLLATE 'utf-8' NOT NULL,
|
|
116
|
-
"columnWithForeignKey" integer NOT NULL CONSTRAINT "fkeyConstraint" REFERENCES "table"("column") MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
|
|
117
|
-
"createdAt" timestamp NOT NULL DEFAULT now(),
|
|
118
|
-
"updatedAt" timestamp NOT NULL DEFAULT now()
|
|
119
|
-
)
|
|
120
|
-
`,
|
|
121
|
-
toLine(`
|
|
122
|
-
CREATE UNIQUE INDEX "indexName"
|
|
123
|
-
ON "table"
|
|
124
|
-
USING gin
|
|
125
|
-
("withIndex" COLLATE 'utf-8' opclass ASC)
|
|
126
|
-
INCLUDE ("id")
|
|
127
|
-
WITH (fillfactor = 70)
|
|
128
|
-
TABLESPACE tablespace
|
|
129
|
-
WHERE column = 123
|
|
130
|
-
`),
|
|
131
|
-
toLine(`
|
|
132
|
-
CREATE UNIQUE INDEX "table_uniqueColumn_idx"
|
|
133
|
-
ON "table"
|
|
134
|
-
("uniqueColumn")
|
|
135
|
-
`),
|
|
136
|
-
`COMMENT ON COLUMN "table"."columnWithComment" IS 'this is a column comment'`,
|
|
137
|
-
]);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const expectDropTable = () => {
|
|
141
|
-
expectSql([
|
|
142
|
-
'SELECT unnest(enum_range(NULL::"mood"))::text',
|
|
143
|
-
`
|
|
144
|
-
DROP TABLE "table" CASCADE
|
|
145
|
-
`,
|
|
146
|
-
]);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const enumRows = [['one'], ['two']];
|
|
150
|
-
asMock(db.adapter.arrays).mockResolvedValueOnce({ rows: enumRows });
|
|
151
|
-
|
|
152
|
-
await fn();
|
|
153
|
-
(action === 'createTable' ? expectCreateTable : expectDropTable)();
|
|
154
|
-
|
|
155
|
-
const [{ ast: ast1 }] = asMock(db.options.appCodeUpdater).mock.calls[0];
|
|
156
|
-
expect(ast1.shape.enum.options).toEqual(['one', 'two']);
|
|
157
|
-
|
|
158
|
-
db.up = false;
|
|
159
|
-
queryMock.mockClear();
|
|
160
|
-
asMock(db.options.appCodeUpdater).mockClear();
|
|
161
|
-
asMock(db.adapter.arrays).mockResolvedValueOnce({ rows: enumRows });
|
|
162
|
-
await fn();
|
|
163
|
-
(action === 'createTable' ? expectDropTable : expectCreateTable)();
|
|
164
|
-
|
|
165
|
-
const [{ ast: ast2 }] = asMock(db.options.appCodeUpdater).mock.calls[0];
|
|
166
|
-
expect(ast2.shape.enum.options).toEqual(['one', 'two']);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('should support composite primary key defined on multiple columns', async () => {
|
|
170
|
-
await db[action]('table', (t) => ({
|
|
171
|
-
id: t.integer().primaryKey(),
|
|
172
|
-
name: t.text().primaryKey(),
|
|
173
|
-
active: t.boolean().primaryKey(),
|
|
174
|
-
}));
|
|
175
|
-
|
|
176
|
-
if (action === 'createTable') {
|
|
177
|
-
expectSql(`
|
|
178
|
-
CREATE TABLE "table" (
|
|
179
|
-
"id" integer NOT NULL,
|
|
180
|
-
"name" text NOT NULL,
|
|
181
|
-
"active" boolean NOT NULL,
|
|
182
|
-
PRIMARY KEY ("id", "name", "active")
|
|
183
|
-
)
|
|
184
|
-
`);
|
|
185
|
-
} else {
|
|
186
|
-
expectSql(`
|
|
187
|
-
DROP TABLE "table"
|
|
188
|
-
`);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should support composite primary key', async () => {
|
|
193
|
-
await db[action]('table', (t) => ({
|
|
194
|
-
id: t.integer(),
|
|
195
|
-
name: t.text(),
|
|
196
|
-
active: t.boolean(),
|
|
197
|
-
...t.primaryKey(['id', 'name', 'active']),
|
|
198
|
-
}));
|
|
199
|
-
|
|
200
|
-
if (action === 'createTable') {
|
|
201
|
-
expectSql(`
|
|
202
|
-
CREATE TABLE "table" (
|
|
203
|
-
"id" integer NOT NULL,
|
|
204
|
-
"name" text NOT NULL,
|
|
205
|
-
"active" boolean NOT NULL,
|
|
206
|
-
PRIMARY KEY ("id", "name", "active")
|
|
207
|
-
)
|
|
208
|
-
`);
|
|
209
|
-
} else {
|
|
210
|
-
expectSql(`
|
|
211
|
-
DROP TABLE "table"
|
|
212
|
-
`);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should support composite primary key with constraint name', async () => {
|
|
217
|
-
await db[action]('table', (t) => ({
|
|
218
|
-
id: t.integer(),
|
|
219
|
-
name: t.text(),
|
|
220
|
-
active: t.boolean(),
|
|
221
|
-
...t.primaryKey(['id', 'name', 'active'], { name: 'primaryKeyName' }),
|
|
222
|
-
}));
|
|
223
|
-
|
|
224
|
-
if (action === 'createTable') {
|
|
225
|
-
expectSql(`
|
|
226
|
-
CREATE TABLE "table" (
|
|
227
|
-
"id" integer NOT NULL,
|
|
228
|
-
"name" text NOT NULL,
|
|
229
|
-
"active" boolean NOT NULL,
|
|
230
|
-
CONSTRAINT "primaryKeyName" PRIMARY KEY ("id", "name", "active")
|
|
231
|
-
)
|
|
232
|
-
`);
|
|
233
|
-
} else {
|
|
234
|
-
expectSql(`
|
|
235
|
-
DROP TABLE "table"
|
|
236
|
-
`);
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('should support composite index', async () => {
|
|
241
|
-
await db[action]('table', (t) => ({
|
|
242
|
-
id: t.serial().primaryKey(),
|
|
243
|
-
name: t.text(),
|
|
244
|
-
...t.index(['id', { column: 'name', order: 'DESC' }], {
|
|
245
|
-
name: 'compositeIndexOnTable',
|
|
246
|
-
}),
|
|
247
|
-
}));
|
|
248
|
-
|
|
249
|
-
if (action === 'createTable') {
|
|
250
|
-
expectSql([
|
|
251
|
-
`
|
|
252
|
-
CREATE TABLE "table" (
|
|
253
|
-
"id" serial PRIMARY KEY,
|
|
254
|
-
"name" text NOT NULL
|
|
255
|
-
)
|
|
256
|
-
`,
|
|
257
|
-
`
|
|
258
|
-
CREATE INDEX "compositeIndexOnTable" ON "table" ("id", "name" DESC)
|
|
259
|
-
`,
|
|
260
|
-
]);
|
|
261
|
-
} else {
|
|
262
|
-
expectSql(`
|
|
263
|
-
DROP TABLE "table"
|
|
264
|
-
`);
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('should support composite unique index', async () => {
|
|
269
|
-
await db[action]('table', (t) => ({
|
|
270
|
-
id: t.serial().primaryKey(),
|
|
271
|
-
name: t.text(),
|
|
272
|
-
...t.unique(['id', { column: 'name', order: 'DESC' }], {
|
|
273
|
-
name: 'compositeIndexOnTable',
|
|
274
|
-
}),
|
|
275
|
-
}));
|
|
276
|
-
|
|
277
|
-
if (action === 'createTable') {
|
|
278
|
-
expectSql([
|
|
279
|
-
`
|
|
280
|
-
CREATE TABLE "table" (
|
|
281
|
-
"id" serial PRIMARY KEY,
|
|
282
|
-
"name" text NOT NULL
|
|
283
|
-
)
|
|
284
|
-
`,
|
|
285
|
-
`
|
|
286
|
-
CREATE UNIQUE INDEX "compositeIndexOnTable" ON "table" ("id", "name" DESC)
|
|
287
|
-
`,
|
|
288
|
-
]);
|
|
289
|
-
} else {
|
|
290
|
-
expectSql(`
|
|
291
|
-
DROP TABLE "table"
|
|
292
|
-
`);
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('should support composite foreign key', async () => {
|
|
297
|
-
await db[action]('table', (t) => ({
|
|
298
|
-
id: t.serial().primaryKey(),
|
|
299
|
-
name: t.text(),
|
|
300
|
-
...t.foreignKey(
|
|
301
|
-
['id', 'name'],
|
|
302
|
-
'otherTable',
|
|
303
|
-
['foreignId', 'foreignName'],
|
|
304
|
-
{
|
|
305
|
-
name: 'constraintName',
|
|
306
|
-
match: 'FULL',
|
|
307
|
-
onUpdate: 'CASCADE',
|
|
308
|
-
onDelete: 'CASCADE',
|
|
309
|
-
},
|
|
310
|
-
),
|
|
311
|
-
}));
|
|
312
|
-
|
|
313
|
-
if (action === 'createTable') {
|
|
314
|
-
const expectedConstraint = toLine(`
|
|
315
|
-
CONSTRAINT "constraintName"
|
|
316
|
-
FOREIGN KEY ("id", "name")
|
|
317
|
-
REFERENCES "otherTable"("foreignId", "foreignName")
|
|
318
|
-
MATCH FULL
|
|
319
|
-
ON DELETE CASCADE
|
|
320
|
-
ON UPDATE CASCADE
|
|
321
|
-
`);
|
|
322
|
-
|
|
323
|
-
expectSql(`
|
|
324
|
-
CREATE TABLE "table" (
|
|
325
|
-
"id" serial PRIMARY KEY,
|
|
326
|
-
"name" text NOT NULL,
|
|
327
|
-
${expectedConstraint}
|
|
328
|
-
)
|
|
329
|
-
`);
|
|
330
|
-
} else {
|
|
331
|
-
expectSql(`
|
|
332
|
-
DROP TABLE "table"
|
|
333
|
-
`);
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
describe('noPrimaryKey', () => {
|
|
338
|
-
const { warn } = console;
|
|
339
|
-
afterAll(() => {
|
|
340
|
-
db.options.noPrimaryKey = undefined;
|
|
341
|
-
console.warn = warn;
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it('should throw by default when no primary key', async () => {
|
|
345
|
-
await expect(() => db[action]('table', () => ({}))).rejects.toThrow(
|
|
346
|
-
'Table table has no primary key.\nYou can suppress this error by setting { noPrimaryKey: true } after a table name.',
|
|
347
|
-
);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it('should throw when no primary key and noPrimaryKey is set to `error`', async () => {
|
|
351
|
-
db.options.noPrimaryKey = 'error';
|
|
352
|
-
|
|
353
|
-
await expect(() => db[action]('table', () => ({}))).rejects.toThrow(
|
|
354
|
-
'Table table has no primary key.\nYou can suppress this error by setting { noPrimaryKey: true } after a table name.',
|
|
355
|
-
);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it('should warn when no primary key and noPrimaryKey is set to `warning`', async () => {
|
|
359
|
-
console.warn = jest.fn();
|
|
360
|
-
db.options.noPrimaryKey = 'warning';
|
|
361
|
-
|
|
362
|
-
db[action]('table', () => ({}));
|
|
363
|
-
|
|
364
|
-
expect(console.warn).toBeCalledWith(
|
|
365
|
-
'Table table has no primary key.\nYou can suppress this error by setting { noPrimaryKey: true } after a table name.',
|
|
366
|
-
);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('should not throw when no primary key and noPrimaryKey is set to `ignore`', async () => {
|
|
370
|
-
db.options.noPrimaryKey = 'ignore';
|
|
371
|
-
|
|
372
|
-
expect(() => db[action]('table', () => ({}))).not.toThrow();
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
it(`should not throw if option is set to \`true\` as a ${action} option`, async () => {
|
|
376
|
-
db.options.noPrimaryKey = 'error';
|
|
377
|
-
|
|
378
|
-
expect(() =>
|
|
379
|
-
db[action]('table', { noPrimaryKey: true }, () => ({})),
|
|
380
|
-
).not.toThrow();
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
});
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ColumnsShape,
|
|
3
|
-
columnTypes,
|
|
4
|
-
EnumColumn,
|
|
5
|
-
getColumnTypes,
|
|
6
|
-
getTableData,
|
|
7
|
-
NoPrimaryKeyOption,
|
|
8
|
-
QueryArraysResult,
|
|
9
|
-
quote,
|
|
10
|
-
TableData,
|
|
11
|
-
} from 'pqb';
|
|
12
|
-
import {
|
|
13
|
-
ColumnComment,
|
|
14
|
-
ColumnsShapeCallback,
|
|
15
|
-
MigrationBase,
|
|
16
|
-
runCodeUpdater,
|
|
17
|
-
TableOptions,
|
|
18
|
-
} from './migration';
|
|
19
|
-
import {
|
|
20
|
-
addColumnComment,
|
|
21
|
-
addColumnIndex,
|
|
22
|
-
columnToSql,
|
|
23
|
-
commentsToQuery,
|
|
24
|
-
constraintToSql,
|
|
25
|
-
indexesToQuery,
|
|
26
|
-
primaryKeyToSql,
|
|
27
|
-
} from './migrationUtils';
|
|
28
|
-
import {
|
|
29
|
-
getSchemaAndTableFromName,
|
|
30
|
-
makePopulateEnumQuery,
|
|
31
|
-
quoteWithSchema,
|
|
32
|
-
} from '../common';
|
|
33
|
-
import { RakeDbAst } from '../ast';
|
|
34
|
-
import { tableMethods } from './tableMethods';
|
|
35
|
-
import { NoPrimaryKey } from '../errors';
|
|
36
|
-
|
|
37
|
-
const types = Object.assign(Object.create(columnTypes), tableMethods);
|
|
38
|
-
|
|
39
|
-
export type TableQuery = {
|
|
40
|
-
text: string;
|
|
41
|
-
values?: unknown[];
|
|
42
|
-
then?(result: QueryArraysResult): void;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const createTable = async (
|
|
46
|
-
migration: MigrationBase,
|
|
47
|
-
up: boolean,
|
|
48
|
-
tableName: string,
|
|
49
|
-
options: TableOptions,
|
|
50
|
-
fn: ColumnsShapeCallback,
|
|
51
|
-
) => {
|
|
52
|
-
const shape = getColumnTypes(types, fn);
|
|
53
|
-
const tableData = getTableData();
|
|
54
|
-
const ast = makeAst(
|
|
55
|
-
up,
|
|
56
|
-
tableName,
|
|
57
|
-
shape,
|
|
58
|
-
tableData,
|
|
59
|
-
options,
|
|
60
|
-
migration.options.noPrimaryKey,
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
validatePrimaryKey(ast);
|
|
64
|
-
|
|
65
|
-
const queries = astToQueries(ast);
|
|
66
|
-
for (const { then, ...query } of queries) {
|
|
67
|
-
const result = await migration.adapter.arrays(query);
|
|
68
|
-
then?.(result);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
await runCodeUpdater(migration, ast);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const makeAst = (
|
|
75
|
-
up: boolean,
|
|
76
|
-
tableName: string,
|
|
77
|
-
shape: ColumnsShape,
|
|
78
|
-
tableData: TableData,
|
|
79
|
-
options: TableOptions,
|
|
80
|
-
noPrimaryKey?: NoPrimaryKeyOption,
|
|
81
|
-
): RakeDbAst.Table => {
|
|
82
|
-
const shapePKeys: string[] = [];
|
|
83
|
-
for (const key in shape) {
|
|
84
|
-
if (shape[key].isPrimaryKey) {
|
|
85
|
-
shapePKeys.push(key);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const primaryKey = tableData.primaryKey;
|
|
90
|
-
|
|
91
|
-
const [schema, table] = getSchemaAndTableFromName(tableName);
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
type: 'table',
|
|
95
|
-
action: up ? 'create' : 'drop',
|
|
96
|
-
schema,
|
|
97
|
-
name: table,
|
|
98
|
-
shape,
|
|
99
|
-
...tableData,
|
|
100
|
-
primaryKey:
|
|
101
|
-
shapePKeys.length <= 1
|
|
102
|
-
? primaryKey
|
|
103
|
-
: primaryKey
|
|
104
|
-
? { ...primaryKey, columns: [...shapePKeys, ...primaryKey.columns] }
|
|
105
|
-
: { columns: shapePKeys },
|
|
106
|
-
...options,
|
|
107
|
-
noPrimaryKey: options.noPrimaryKey ? 'ignore' : noPrimaryKey || 'error',
|
|
108
|
-
};
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const validatePrimaryKey = (ast: RakeDbAst.Table) => {
|
|
112
|
-
if (ast.noPrimaryKey !== 'ignore') {
|
|
113
|
-
let hasPrimaryKey = !!ast.primaryKey?.columns?.length;
|
|
114
|
-
if (!hasPrimaryKey) {
|
|
115
|
-
for (const key in ast.shape) {
|
|
116
|
-
if (ast.shape[key].isPrimaryKey) {
|
|
117
|
-
hasPrimaryKey = true;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (!hasPrimaryKey) {
|
|
124
|
-
const error = new NoPrimaryKey(
|
|
125
|
-
`Table ${ast.name} has no primary key.\nYou can suppress this error by setting { noPrimaryKey: true } after a table name.`,
|
|
126
|
-
);
|
|
127
|
-
if (ast.noPrimaryKey === 'error') {
|
|
128
|
-
throw error;
|
|
129
|
-
} else {
|
|
130
|
-
console.warn(error.message);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const astToQueries = (ast: RakeDbAst.Table): TableQuery[] => {
|
|
137
|
-
const queries: TableQuery[] = [];
|
|
138
|
-
|
|
139
|
-
for (const key in ast.shape) {
|
|
140
|
-
const item = ast.shape[key];
|
|
141
|
-
if (!(item instanceof EnumColumn)) continue;
|
|
142
|
-
|
|
143
|
-
queries.push(makePopulateEnumQuery(item));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (ast.action === 'drop') {
|
|
147
|
-
queries.push({
|
|
148
|
-
text: `DROP TABLE ${quoteWithSchema(ast)}${
|
|
149
|
-
ast.dropMode ? ` ${ast.dropMode}` : ''
|
|
150
|
-
}`,
|
|
151
|
-
});
|
|
152
|
-
return queries;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const lines: string[] = [];
|
|
156
|
-
const values: unknown[] = [];
|
|
157
|
-
const indexes: TableData.Index[] = [];
|
|
158
|
-
const comments: ColumnComment[] = [];
|
|
159
|
-
|
|
160
|
-
for (const key in ast.shape) {
|
|
161
|
-
const item = ast.shape[key];
|
|
162
|
-
addColumnIndex(indexes, key, item);
|
|
163
|
-
addColumnComment(comments, key, item);
|
|
164
|
-
lines.push(`\n ${columnToSql(key, item, values, !!ast.primaryKey)}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (ast.primaryKey) {
|
|
168
|
-
lines.push(`\n ${primaryKeyToSql(ast.primaryKey)}`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
ast.foreignKeys.forEach((foreignKey) => {
|
|
172
|
-
lines.push(`\n ${constraintToSql(ast, true, foreignKey)}`);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
indexes.push(...ast.indexes);
|
|
176
|
-
|
|
177
|
-
queries.push(
|
|
178
|
-
{
|
|
179
|
-
text: `CREATE TABLE ${quoteWithSchema(ast)} (${lines.join(',')}\n)`,
|
|
180
|
-
values,
|
|
181
|
-
},
|
|
182
|
-
...indexesToQuery(true, ast, indexes),
|
|
183
|
-
...commentsToQuery(ast, comments),
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
if (ast.comment) {
|
|
187
|
-
queries.push({
|
|
188
|
-
text: `COMMENT ON TABLE ${quoteWithSchema(ast)} IS ${quote(ast.comment)}`,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return queries;
|
|
193
|
-
};
|