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.
Files changed (52) hide show
  1. package/package.json +11 -21
  2. package/.env +0 -1
  3. package/.env.local +0 -2
  4. package/.turbo/turbo-check.log +0 -23
  5. package/.turbo/turbo-test.log +0 -22
  6. package/.turbo/turbo-test:ci.log +0 -22
  7. package/CHANGELOG.md +0 -395
  8. package/app/dbScript.ts +0 -33
  9. package/app/migrations/20221017181504_createUser.ts +0 -14
  10. package/app/migrations/20221017200111_createProfile.ts +0 -10
  11. package/app/migrations/20221017200252_createChat.ts +0 -9
  12. package/app/migrations/20221017200326_createChatUser.ts +0 -10
  13. package/app/migrations/20221017200900_createMessage.ts +0 -12
  14. package/app/migrations/20221017201235_createGeoSchema.ts +0 -5
  15. package/app/migrations/20221017210011_createCountry.ts +0 -8
  16. package/app/migrations/20221017210133_createCity.ts +0 -9
  17. package/app/migrations/20221105202843_createUniqueTable.ts +0 -12
  18. package/jest-setup.ts +0 -3
  19. package/rollup.config.js +0 -3
  20. package/src/ast.ts +0 -130
  21. package/src/commands/createOrDrop.test.ts +0 -214
  22. package/src/commands/createOrDrop.ts +0 -151
  23. package/src/commands/generate.test.ts +0 -136
  24. package/src/commands/generate.ts +0 -93
  25. package/src/commands/migrateOrRollback.test.ts +0 -267
  26. package/src/commands/migrateOrRollback.ts +0 -190
  27. package/src/common.test.ts +0 -295
  28. package/src/common.ts +0 -353
  29. package/src/errors.ts +0 -3
  30. package/src/index.ts +0 -8
  31. package/src/migration/change.test.ts +0 -16
  32. package/src/migration/change.ts +0 -15
  33. package/src/migration/changeTable.test.ts +0 -897
  34. package/src/migration/changeTable.ts +0 -566
  35. package/src/migration/createTable.test.ts +0 -384
  36. package/src/migration/createTable.ts +0 -193
  37. package/src/migration/migration.test.ts +0 -430
  38. package/src/migration/migration.ts +0 -518
  39. package/src/migration/migrationUtils.ts +0 -307
  40. package/src/migration/tableMethods.ts +0 -8
  41. package/src/pull/astToMigration.test.ts +0 -275
  42. package/src/pull/astToMigration.ts +0 -173
  43. package/src/pull/dbStructure.test.ts +0 -180
  44. package/src/pull/dbStructure.ts +0 -413
  45. package/src/pull/pull.test.ts +0 -115
  46. package/src/pull/pull.ts +0 -22
  47. package/src/pull/structureToAst.test.ts +0 -841
  48. package/src/pull/structureToAst.ts +0 -372
  49. package/src/rakeDb.test.ts +0 -131
  50. package/src/rakeDb.ts +0 -84
  51. package/src/test-utils.ts +0 -64
  52. 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
- };