@anthonyalayo/drizzle-kit 1.0.1

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 (161) hide show
  1. package/.gitignore +25 -0
  2. package/README.md +79 -0
  3. package/build.dev.ts +51 -0
  4. package/build.ts +112 -0
  5. package/imports-checker/analyze.ts +78 -0
  6. package/imports-checker/checker.ts +296 -0
  7. package/imports-checker/grammar/grammar.ohm +121 -0
  8. package/imports-checker/grammar/grammar.ohm-bundle.d.ts +164 -0
  9. package/imports-checker/grammar/grammar.ohm-bundle.js +753 -0
  10. package/imports-checker/index.ts +48 -0
  11. package/package.json +142 -0
  12. package/patches/difflib@0.2.4.patch +31 -0
  13. package/src/@types/utils.ts +58 -0
  14. package/src/api.ts +504 -0
  15. package/src/cli/commands/_es5.ts +2 -0
  16. package/src/cli/commands/check.ts +52 -0
  17. package/src/cli/commands/drop.ts +60 -0
  18. package/src/cli/commands/introspect.ts +772 -0
  19. package/src/cli/commands/libSqlPushUtils.ts +355 -0
  20. package/src/cli/commands/migrate.ts +1526 -0
  21. package/src/cli/commands/mysqlIntrospect.ts +53 -0
  22. package/src/cli/commands/mysqlPushUtils.ts +352 -0
  23. package/src/cli/commands/mysqlUp.ts +102 -0
  24. package/src/cli/commands/pgIntrospect.ts +55 -0
  25. package/src/cli/commands/pgPushUtils.ts +269 -0
  26. package/src/cli/commands/pgUp.ts +217 -0
  27. package/src/cli/commands/push.ts +637 -0
  28. package/src/cli/commands/singlestoreIntrospect.ts +53 -0
  29. package/src/cli/commands/singlestorePushUtils.ts +456 -0
  30. package/src/cli/commands/singlestoreUp.ts +1 -0
  31. package/src/cli/commands/sqliteIntrospect.ts +96 -0
  32. package/src/cli/commands/sqlitePushUtils.ts +322 -0
  33. package/src/cli/commands/sqliteUp.ts +52 -0
  34. package/src/cli/commands/utils.ts +858 -0
  35. package/src/cli/connections.ts +914 -0
  36. package/src/cli/index.ts +48 -0
  37. package/src/cli/schema.ts +794 -0
  38. package/src/cli/selector-ui.ts +38 -0
  39. package/src/cli/utils.ts +112 -0
  40. package/src/cli/validations/cli.ts +79 -0
  41. package/src/cli/validations/common.ts +193 -0
  42. package/src/cli/validations/libsql.ts +27 -0
  43. package/src/cli/validations/mysql.ts +61 -0
  44. package/src/cli/validations/outputs.ts +91 -0
  45. package/src/cli/validations/postgres.ts +83 -0
  46. package/src/cli/validations/singlestore.ts +61 -0
  47. package/src/cli/validations/sqlite.ts +104 -0
  48. package/src/cli/validations/studio.ts +24 -0
  49. package/src/cli/views.ts +658 -0
  50. package/src/extensions/getTablesFilterByExtensions.ts +16 -0
  51. package/src/extensions/vector.ts +10 -0
  52. package/src/global.ts +61 -0
  53. package/src/index.ts +326 -0
  54. package/src/introspect-mysql.ts +1030 -0
  55. package/src/introspect-pg.ts +1435 -0
  56. package/src/introspect-singlestore.ts +913 -0
  57. package/src/introspect-sqlite.ts +533 -0
  58. package/src/jsonDiffer.js +882 -0
  59. package/src/jsonStatements.ts +3590 -0
  60. package/src/loader.mjs +57 -0
  61. package/src/migrationPreparator.ts +217 -0
  62. package/src/schemaValidator.ts +23 -0
  63. package/src/serializer/index.ts +142 -0
  64. package/src/serializer/mysqlImports.ts +38 -0
  65. package/src/serializer/mysqlSchema.ts +423 -0
  66. package/src/serializer/mysqlSerializer.ts +999 -0
  67. package/src/serializer/pgImports.ts +106 -0
  68. package/src/serializer/pgSchema.ts +937 -0
  69. package/src/serializer/pgSerializer.ts +2223 -0
  70. package/src/serializer/singlestoreImports.ts +38 -0
  71. package/src/serializer/singlestoreSchema.ts +257 -0
  72. package/src/serializer/singlestoreSerializer.ts +767 -0
  73. package/src/serializer/sqliteImports.ts +41 -0
  74. package/src/serializer/sqliteSchema.ts +352 -0
  75. package/src/serializer/sqliteSerializer.ts +931 -0
  76. package/src/serializer/studio.ts +694 -0
  77. package/src/serializer/utils.ts +45 -0
  78. package/src/simulator.ts +157 -0
  79. package/src/snapshotsDiffer.ts +4398 -0
  80. package/src/sqlgenerator.ts +4212 -0
  81. package/src/statementCombiner.ts +597 -0
  82. package/src/utils/certs.ts +44 -0
  83. package/src/utils/words.ts +1333 -0
  84. package/src/utils.ts +370 -0
  85. package/tests/bin.test.ts +58 -0
  86. package/tests/cli/d1http.config.ts +12 -0
  87. package/tests/cli/drizzle.config.ts +9 -0
  88. package/tests/cli/durable-sqlite.config.ts +7 -0
  89. package/tests/cli/expo.config.ts +7 -0
  90. package/tests/cli/postgres.config.ts +13 -0
  91. package/tests/cli/postgres2.config.ts +17 -0
  92. package/tests/cli/schema.ts +1 -0
  93. package/tests/cli/turso.config.ts +10 -0
  94. package/tests/cli-export.test.ts +79 -0
  95. package/tests/cli-generate.test.ts +259 -0
  96. package/tests/cli-migrate.test.ts +103 -0
  97. package/tests/cli-push.test.ts +126 -0
  98. package/tests/common.ts +15 -0
  99. package/tests/indexes/common.ts +21 -0
  100. package/tests/indexes/pg.test.ts +245 -0
  101. package/tests/introspect/libsql.test.ts +35 -0
  102. package/tests/introspect/mysql.test.ts +319 -0
  103. package/tests/introspect/pg.test.ts +894 -0
  104. package/tests/introspect/singlestore.test.ts +275 -0
  105. package/tests/introspect/sqlite.test.ts +122 -0
  106. package/tests/libsql-checks.test.ts +308 -0
  107. package/tests/libsql-statements.test.ts +989 -0
  108. package/tests/libsql-views.test.ts +218 -0
  109. package/tests/migrate/libsq-schema.ts +6 -0
  110. package/tests/migrate/libsql-migrate.test.ts +58 -0
  111. package/tests/migrate/migrations/0000_little_blizzard.sql +4 -0
  112. package/tests/migrate/migrations/0001_nebulous_storm.sql +10 -0
  113. package/tests/migrate/migrations/meta/0000_snapshot.json +40 -0
  114. package/tests/migrate/migrations/meta/0001_snapshot.json +40 -0
  115. package/tests/migrate/migrations/meta/_journal.json +20 -0
  116. package/tests/mysql-checks.test.ts +291 -0
  117. package/tests/mysql-generated.test.ts +1290 -0
  118. package/tests/mysql-schemas.test.ts +156 -0
  119. package/tests/mysql-views.test.ts +553 -0
  120. package/tests/mysql.test.ts +863 -0
  121. package/tests/pg-array.test.ts +368 -0
  122. package/tests/pg-checks.test.ts +329 -0
  123. package/tests/pg-columns.test.ts +484 -0
  124. package/tests/pg-domains.test.ts +306 -0
  125. package/tests/pg-enums.test.ts +751 -0
  126. package/tests/pg-generated.test.ts +529 -0
  127. package/tests/pg-identity.test.ts +447 -0
  128. package/tests/pg-schemas.test.ts +105 -0
  129. package/tests/pg-sequences.test.ts +298 -0
  130. package/tests/pg-tables.test.ts +1021 -0
  131. package/tests/pg-views.test.ts +1929 -0
  132. package/tests/push/common.ts +58 -0
  133. package/tests/push/libsql.test.ts +1400 -0
  134. package/tests/push/mysql-push.test.ts +345 -0
  135. package/tests/push/mysql.test.ts +788 -0
  136. package/tests/push/pg.test.ts +4341 -0
  137. package/tests/push/singlestore-push.test.ts +894 -0
  138. package/tests/push/singlestore.test.ts +444 -0
  139. package/tests/push/sqlite.test.ts +1613 -0
  140. package/tests/rls/pg-policy.test.ts +1656 -0
  141. package/tests/rls/pg-role.test.ts +234 -0
  142. package/tests/schemaDiffer.ts +2852 -0
  143. package/tests/singlestore-generated.test.ts +1290 -0
  144. package/tests/singlestore-schemas.test.ts +155 -0
  145. package/tests/singlestore.test.ts +978 -0
  146. package/tests/sqlite-checks.test.ts +308 -0
  147. package/tests/sqlite-columns.test.ts +1049 -0
  148. package/tests/sqlite-generated.test.ts +1726 -0
  149. package/tests/sqlite-tables.test.ts +637 -0
  150. package/tests/sqlite-views.test.ts +218 -0
  151. package/tests/statements-combiner/libsql-statements-combiner.test.ts +1812 -0
  152. package/tests/statements-combiner/singlestore-statements-combiner.test.ts +882 -0
  153. package/tests/statements-combiner/sqlite-statements-combiner.test.ts +1211 -0
  154. package/tests/test/sqlite.test.ts +39 -0
  155. package/tests/testsinglestore.ts +29 -0
  156. package/tests/validations.test.ts +869 -0
  157. package/tests/wrap-param.test.ts +16 -0
  158. package/tsconfig.build.json +4 -0
  159. package/tsconfig.cli-types.json +9 -0
  160. package/tsconfig.json +28 -0
  161. package/vitest.config.ts +34 -0
@@ -0,0 +1,1613 @@
1
+ import Database from 'better-sqlite3';
2
+ import chalk from 'chalk';
3
+ import { sql } from 'drizzle-orm';
4
+ import {
5
+ blob,
6
+ check,
7
+ foreignKey,
8
+ getTableConfig,
9
+ int,
10
+ integer,
11
+ numeric,
12
+ primaryKey,
13
+ real,
14
+ sqliteTable,
15
+ sqliteView,
16
+ text,
17
+ uniqueIndex,
18
+ } from 'drizzle-orm/sqlite-core';
19
+ import { diffTestSchemasPushSqlite, introspectSQLiteToFile } from 'tests/schemaDiffer';
20
+ import { expect, test } from 'vitest';
21
+
22
+ test('nothing changed in schema', async (t) => {
23
+ const client = new Database(':memory:');
24
+
25
+ const users = sqliteTable('users', {
26
+ id: integer('id').primaryKey().notNull(),
27
+ name: text('name').notNull(),
28
+ email: text('email'),
29
+ textJson: text('text_json', { mode: 'json' }),
30
+ blobJon: blob('blob_json', { mode: 'json' }),
31
+ blobBigInt: blob('blob_bigint', { mode: 'bigint' }),
32
+ numeric: numeric('numeric'),
33
+ createdAt: integer('created_at', { mode: 'timestamp' }),
34
+ createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }),
35
+ real: real('real'),
36
+ text: text('text', { length: 255 }),
37
+ role: text('role', { enum: ['admin', 'user'] }).default('user'),
38
+ isConfirmed: integer('is_confirmed', {
39
+ mode: 'boolean',
40
+ }),
41
+ });
42
+
43
+ const schema1 = {
44
+ users,
45
+
46
+ customers: sqliteTable('customers', {
47
+ id: integer('id').primaryKey(),
48
+ address: text('address').notNull(),
49
+ isConfirmed: integer('is_confirmed', { mode: 'boolean' }),
50
+ registrationDate: integer('registration_date', { mode: 'timestamp_ms' })
51
+ .notNull()
52
+ .$defaultFn(() => new Date()),
53
+ userId: integer('user_id')
54
+ .references(() => users.id)
55
+ .notNull(),
56
+ }),
57
+
58
+ posts: sqliteTable('posts', {
59
+ id: integer('id').primaryKey(),
60
+ content: text('content'),
61
+ authorId: integer('author_id'),
62
+ }),
63
+ };
64
+
65
+ const {
66
+ sqlStatements,
67
+ statements,
68
+ columnsToRemove,
69
+ infoToPrint,
70
+ shouldAskForApprove,
71
+ tablesToRemove,
72
+ tablesToTruncate,
73
+ } = await diffTestSchemasPushSqlite(client, schema1, schema1, [], false);
74
+
75
+ expect(sqlStatements.length).toBe(0);
76
+ expect(statements.length).toBe(0);
77
+ expect(columnsToRemove!.length).toBe(0);
78
+ expect(infoToPrint!.length).toBe(0);
79
+ expect(shouldAskForApprove).toBe(false);
80
+ expect(tablesToRemove!.length).toBe(0);
81
+ expect(tablesToTruncate!.length).toBe(0);
82
+ expect(shouldAskForApprove).toBe(false);
83
+ });
84
+
85
+ test('dropped, added unique index', async (t) => {
86
+ const client = new Database(':memory:');
87
+
88
+ const users = sqliteTable('users', {
89
+ id: integer('id').primaryKey().notNull(),
90
+ name: text('name').notNull(),
91
+ email: text('email'),
92
+ textJson: text('text_json', { mode: 'json' }),
93
+ blobJon: blob('blob_json', { mode: 'json' }),
94
+ blobBigInt: blob('blob_bigint', { mode: 'bigint' }),
95
+ numeric: numeric('numeric'),
96
+ createdAt: integer('created_at', { mode: 'timestamp' }),
97
+ createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }),
98
+ real: real('real'),
99
+ text: text('text', { length: 255 }),
100
+ role: text('role', { enum: ['admin', 'user'] }).default('user'),
101
+ isConfirmed: integer('is_confirmed', {
102
+ mode: 'boolean',
103
+ }),
104
+ });
105
+
106
+ const schema1 = {
107
+ users,
108
+
109
+ customers: sqliteTable(
110
+ 'customers',
111
+ {
112
+ id: integer('id').primaryKey(),
113
+ address: text('address').notNull().unique(),
114
+ isConfirmed: integer('is_confirmed', { mode: 'boolean' }),
115
+ registrationDate: integer('registration_date', { mode: 'timestamp_ms' })
116
+ .notNull()
117
+ .$defaultFn(() => new Date()),
118
+ userId: integer('user_id').notNull(),
119
+ },
120
+ (table) => ({
121
+ uniqueIndex: uniqueIndex('customers_address_unique').on(table.address),
122
+ }),
123
+ ),
124
+
125
+ posts: sqliteTable('posts', {
126
+ id: integer('id').primaryKey(),
127
+ content: text('content'),
128
+ authorId: integer('author_id'),
129
+ }),
130
+ };
131
+
132
+ const schema2 = {
133
+ users,
134
+
135
+ customers: sqliteTable(
136
+ 'customers',
137
+ {
138
+ id: integer('id').primaryKey(),
139
+ address: text('address').notNull(),
140
+ isConfirmed: integer('is_confirmed', { mode: 'boolean' }),
141
+ registrationDate: integer('registration_date', { mode: 'timestamp_ms' })
142
+ .notNull()
143
+ .$defaultFn(() => new Date()),
144
+ userId: integer('user_id').notNull(),
145
+ },
146
+ (table) => ({
147
+ uniqueIndex: uniqueIndex('customers_is_confirmed_unique').on(
148
+ table.isConfirmed,
149
+ ),
150
+ }),
151
+ ),
152
+
153
+ posts: sqliteTable('posts', {
154
+ id: integer('id').primaryKey(),
155
+ content: text('content'),
156
+ authorId: integer('author_id'),
157
+ }),
158
+ };
159
+
160
+ const {
161
+ sqlStatements,
162
+ statements,
163
+ columnsToRemove,
164
+ infoToPrint,
165
+ shouldAskForApprove,
166
+ tablesToRemove,
167
+ tablesToTruncate,
168
+ } = await diffTestSchemasPushSqlite(client, schema1, schema2, [], false);
169
+ expect(statements.length).toBe(2);
170
+ expect(statements[0]).toStrictEqual({
171
+ type: 'drop_index',
172
+ tableName: 'customers',
173
+ data: 'customers_address_unique;address;true;',
174
+ schema: '',
175
+ });
176
+ expect(statements[1]).toStrictEqual({
177
+ type: 'create_index',
178
+ tableName: 'customers',
179
+ data: 'customers_is_confirmed_unique;is_confirmed;true;',
180
+ schema: '',
181
+ internal: {
182
+ indexes: {},
183
+ },
184
+ });
185
+
186
+ expect(sqlStatements.length).toBe(2);
187
+ expect(sqlStatements[0]).toBe(
188
+ `DROP INDEX \`customers_address_unique\`;`,
189
+ );
190
+ expect(sqlStatements[1]).toBe(
191
+ `CREATE UNIQUE INDEX \`customers_is_confirmed_unique\` ON \`customers\` (\`is_confirmed\`);`,
192
+ );
193
+
194
+ expect(columnsToRemove!.length).toBe(0);
195
+ expect(infoToPrint!.length).toBe(0);
196
+ expect(shouldAskForApprove).toBe(false);
197
+ expect(tablesToRemove!.length).toBe(0);
198
+ expect(tablesToTruncate!.length).toBe(0);
199
+ });
200
+
201
+ test('added column not null and without default to table with data', async (t) => {
202
+ const client = new Database(':memory:');
203
+
204
+ const schema1 = {
205
+ companies: sqliteTable('companies', {
206
+ id: integer('id').primaryKey(),
207
+ name: text('name').notNull(),
208
+ }),
209
+ };
210
+
211
+ const schema2 = {
212
+ companies: sqliteTable('companies', {
213
+ id: integer('id').primaryKey(),
214
+ name: text('name').notNull(),
215
+ age: integer('age').notNull(),
216
+ }),
217
+ };
218
+
219
+ const table = getTableConfig(schema1.companies);
220
+ const seedStatements = [
221
+ `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('drizzle');`,
222
+ `INSERT INTO \`${table.name}\` ("${schema1.companies.name.name}") VALUES ('turso');`,
223
+ ];
224
+
225
+ const {
226
+ statements,
227
+ sqlStatements,
228
+ columnsToRemove,
229
+ infoToPrint,
230
+ shouldAskForApprove,
231
+ tablesToRemove,
232
+ tablesToTruncate,
233
+ } = await diffTestSchemasPushSqlite(
234
+ client,
235
+ schema1,
236
+ schema2,
237
+ [],
238
+ false,
239
+ seedStatements,
240
+ );
241
+
242
+ expect(statements.length).toBe(1);
243
+ expect(statements[0]).toStrictEqual({
244
+ type: 'sqlite_alter_table_add_column',
245
+ tableName: 'companies',
246
+ column: {
247
+ name: 'age',
248
+ type: 'integer',
249
+ primaryKey: false,
250
+ notNull: true,
251
+ autoincrement: false,
252
+ },
253
+ referenceData: undefined,
254
+ });
255
+ expect(sqlStatements.length).toBe(2);
256
+ expect(sqlStatements[0]).toBe(`delete from companies;`);
257
+ expect(sqlStatements[1]).toBe(
258
+ `ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`,
259
+ );
260
+
261
+ expect(columnsToRemove!.length).toBe(0);
262
+ expect(infoToPrint!.length).toBe(1);
263
+ expect(infoToPrint![0]).toBe(
264
+ `· You're about to add not-null ${
265
+ chalk.underline(
266
+ 'age',
267
+ )
268
+ } column without default value, which contains 2 items`,
269
+ );
270
+ expect(shouldAskForApprove).toBe(true);
271
+ expect(tablesToRemove!.length).toBe(0);
272
+ expect(tablesToTruncate!.length).toBe(1);
273
+ expect(tablesToTruncate![0]).toBe('companies');
274
+ });
275
+
276
+ test('added column not null and without default to table without data', async (t) => {
277
+ const turso = new Database(':memory:');
278
+
279
+ const schema1 = {
280
+ companies: sqliteTable('companies', {
281
+ id: integer('id').primaryKey(),
282
+ name: text('name').notNull(),
283
+ }),
284
+ };
285
+
286
+ const schema2 = {
287
+ companies: sqliteTable('companies', {
288
+ id: integer('id').primaryKey(),
289
+ name: text('name').notNull(),
290
+ age: integer('age').notNull(),
291
+ }),
292
+ };
293
+
294
+ const {
295
+ sqlStatements,
296
+ statements,
297
+ columnsToRemove,
298
+ infoToPrint,
299
+ shouldAskForApprove,
300
+ tablesToRemove,
301
+ tablesToTruncate,
302
+ } = await diffTestSchemasPushSqlite(turso, schema1, schema2, [], false);
303
+
304
+ expect(statements.length).toBe(1);
305
+ expect(statements[0]).toStrictEqual({
306
+ type: 'sqlite_alter_table_add_column',
307
+ tableName: 'companies',
308
+ column: {
309
+ name: 'age',
310
+ type: 'integer',
311
+ primaryKey: false,
312
+ notNull: true,
313
+ autoincrement: false,
314
+ },
315
+ referenceData: undefined,
316
+ });
317
+
318
+ expect(sqlStatements.length).toBe(1);
319
+ expect(sqlStatements[0]).toBe(
320
+ `ALTER TABLE \`companies\` ADD \`age\` integer NOT NULL;`,
321
+ );
322
+
323
+ expect(infoToPrint!.length).toBe(0);
324
+ expect(columnsToRemove!.length).toBe(0);
325
+ expect(shouldAskForApprove).toBe(false);
326
+ expect(tablesToRemove!.length).toBe(0);
327
+ expect(tablesToTruncate!.length).toBe(0);
328
+ });
329
+
330
+ test('drop autoincrement. drop column with data', async (t) => {
331
+ const turso = new Database(':memory:');
332
+
333
+ const schema1 = {
334
+ companies: sqliteTable('companies', {
335
+ id: integer('id').primaryKey({ autoIncrement: true }),
336
+ name: text('name'),
337
+ }),
338
+ };
339
+
340
+ const schema2 = {
341
+ companies: sqliteTable('companies', {
342
+ id: integer('id').primaryKey({ autoIncrement: false }),
343
+ }),
344
+ };
345
+
346
+ const table = getTableConfig(schema1.companies);
347
+ const seedStatements = [
348
+ `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, 'drizzle');`,
349
+ `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, 'turso');`,
350
+ ];
351
+
352
+ const {
353
+ sqlStatements,
354
+ statements,
355
+ columnsToRemove,
356
+ infoToPrint,
357
+ shouldAskForApprove,
358
+ tablesToRemove,
359
+ tablesToTruncate,
360
+ } = await diffTestSchemasPushSqlite(
361
+ turso,
362
+ schema1,
363
+ schema2,
364
+ [],
365
+ false,
366
+ seedStatements,
367
+ );
368
+
369
+ expect(statements.length).toBe(1);
370
+ expect(statements[0]).toStrictEqual({
371
+ type: 'recreate_table',
372
+ tableName: 'companies',
373
+ columns: [
374
+ {
375
+ name: 'id',
376
+ type: 'integer',
377
+ autoincrement: false,
378
+ notNull: true,
379
+ primaryKey: true,
380
+ generated: undefined,
381
+ },
382
+ ],
383
+ compositePKs: [],
384
+ referenceData: [],
385
+ uniqueConstraints: [],
386
+ checkConstraints: [],
387
+ });
388
+
389
+ expect(sqlStatements.length).toBe(4);
390
+ expect(sqlStatements[0]).toBe(
391
+ `CREATE TABLE \`__new_companies\` (
392
+ \t\`id\` integer PRIMARY KEY NOT NULL
393
+ );\n`,
394
+ );
395
+ expect(sqlStatements[1]).toBe(
396
+ `INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`,
397
+ );
398
+ expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`);
399
+ expect(sqlStatements[3]).toBe(
400
+ `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`,
401
+ );
402
+
403
+ expect(columnsToRemove!.length).toBe(1);
404
+ expect(columnsToRemove![0]).toBe('name');
405
+ expect(infoToPrint!.length).toBe(1);
406
+ expect(infoToPrint![0]).toBe(
407
+ `· You're about to delete ${
408
+ chalk.underline(
409
+ 'name',
410
+ )
411
+ } column in companies table with 2 items`,
412
+ );
413
+ expect(shouldAskForApprove).toBe(true);
414
+ expect(tablesToRemove!.length).toBe(0);
415
+ expect(tablesToTruncate!.length).toBe(0);
416
+ });
417
+
418
+ test('drop autoincrement. drop column with data with pragma off', async (t) => {
419
+ const client = new Database(':memory:');
420
+
421
+ client.exec('PRAGMA foreign_keys=OFF;');
422
+
423
+ const users = sqliteTable('users', {
424
+ id: integer('id').primaryKey({ autoIncrement: true }),
425
+ });
426
+ const schema1 = {
427
+ companies: sqliteTable('companies', {
428
+ id: integer('id').primaryKey({ autoIncrement: true }),
429
+ name: text('name'),
430
+ user_id: integer('user_id').references(() => users.id),
431
+ }),
432
+ };
433
+
434
+ const schema2 = {
435
+ companies: sqliteTable('companies', {
436
+ id: integer('id').primaryKey({ autoIncrement: false }),
437
+ user_id: integer('user_id').references(() => users.id),
438
+ }),
439
+ };
440
+
441
+ const table = getTableConfig(schema1.companies);
442
+ const seedStatements = [
443
+ `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (1, 'drizzle');`,
444
+ `INSERT INTO \`${table.name}\` ("${schema1.companies.id.name}", "${schema1.companies.name.name}") VALUES (2, 'turso');`,
445
+ ];
446
+
447
+ const {
448
+ sqlStatements,
449
+ statements,
450
+ columnsToRemove,
451
+ infoToPrint,
452
+ shouldAskForApprove,
453
+ tablesToRemove,
454
+ tablesToTruncate,
455
+ } = await diffTestSchemasPushSqlite(
456
+ client,
457
+ schema1,
458
+ schema2,
459
+ [],
460
+ false,
461
+ seedStatements,
462
+ );
463
+
464
+ expect(statements.length).toBe(1);
465
+ expect(statements[0]).toStrictEqual({
466
+ type: 'recreate_table',
467
+ tableName: 'companies',
468
+ columns: [
469
+ {
470
+ name: 'id',
471
+ type: 'integer',
472
+ autoincrement: false,
473
+ notNull: true,
474
+ primaryKey: true,
475
+ generated: undefined,
476
+ },
477
+ {
478
+ name: 'user_id',
479
+ type: 'integer',
480
+ autoincrement: false,
481
+ notNull: false,
482
+ primaryKey: false,
483
+ generated: undefined,
484
+ },
485
+ ],
486
+ compositePKs: [],
487
+ referenceData: [
488
+ {
489
+ columnsFrom: [
490
+ 'user_id',
491
+ ],
492
+ columnsTo: [
493
+ 'id',
494
+ ],
495
+ name: '',
496
+ onDelete: 'no action',
497
+ onUpdate: 'no action',
498
+ tableFrom: 'companies',
499
+ tableTo: 'users',
500
+ },
501
+ ],
502
+ uniqueConstraints: [],
503
+ checkConstraints: [],
504
+ });
505
+
506
+ expect(sqlStatements.length).toBe(4);
507
+ expect(sqlStatements[0]).toBe(
508
+ `CREATE TABLE \`__new_companies\` (
509
+ \t\`id\` integer PRIMARY KEY NOT NULL,
510
+ \t\`user_id\` integer,
511
+ \tFOREIGN KEY (\`user_id\`) REFERENCES \`users\`(\`id\`) ON UPDATE no action ON DELETE no action
512
+ );\n`,
513
+ );
514
+ expect(sqlStatements[1]).toBe(
515
+ `INSERT INTO \`__new_companies\`("id", "user_id") SELECT "id", "user_id" FROM \`companies\`;`,
516
+ );
517
+ expect(sqlStatements[2]).toBe(`DROP TABLE \`companies\`;`);
518
+ expect(sqlStatements[3]).toBe(
519
+ `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`,
520
+ );
521
+
522
+ expect(columnsToRemove!.length).toBe(1);
523
+ expect(infoToPrint!.length).toBe(1);
524
+ expect(infoToPrint![0]).toBe(
525
+ `· You're about to delete ${
526
+ chalk.underline(
527
+ 'name',
528
+ )
529
+ } column in companies table with 2 items`,
530
+ );
531
+ expect(shouldAskForApprove).toBe(true);
532
+ expect(tablesToRemove!.length).toBe(0);
533
+ expect(tablesToTruncate!.length).toBe(0);
534
+ });
535
+
536
+ test('change autoincrement. other table references current', async (t) => {
537
+ const client = new Database(':memory:');
538
+
539
+ const companies1 = sqliteTable('companies', {
540
+ id: integer('id').primaryKey({ autoIncrement: true }),
541
+ });
542
+ const users1 = sqliteTable('users', {
543
+ id: integer('id').primaryKey({ autoIncrement: true }),
544
+ name: text('name').unique(),
545
+ companyId: text('company_id').references(() => companies1.id),
546
+ });
547
+ const schema1 = {
548
+ companies: companies1,
549
+ users: users1,
550
+ };
551
+
552
+ const companies2 = sqliteTable('companies', {
553
+ id: integer('id').primaryKey({ autoIncrement: false }),
554
+ });
555
+ const users2 = sqliteTable('users', {
556
+ id: integer('id').primaryKey({ autoIncrement: true }),
557
+ name: text('name').unique(),
558
+ companyId: text('company_id').references(() => companies1.id),
559
+ });
560
+ const schema2 = {
561
+ companies: companies2,
562
+ users: users2,
563
+ };
564
+
565
+ const { name: usersTableName } = getTableConfig(users1);
566
+ const { name: companiesTableName } = getTableConfig(companies1);
567
+ const seedStatements = [
568
+ `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('drizzle');`,
569
+ `INSERT INTO \`${usersTableName}\` ("${schema1.users.name.name}") VALUES ('turso');`,
570
+ `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ('1');`,
571
+ `INSERT INTO \`${companiesTableName}\` ("${schema1.companies.id.name}") VALUES ('2');`,
572
+ ];
573
+
574
+ const {
575
+ statements,
576
+ sqlStatements,
577
+ columnsToRemove,
578
+ infoToPrint,
579
+ shouldAskForApprove,
580
+ tablesToRemove,
581
+ tablesToTruncate,
582
+ } = await diffTestSchemasPushSqlite(
583
+ client,
584
+ schema1,
585
+ schema2,
586
+ [],
587
+ false,
588
+ seedStatements,
589
+ );
590
+
591
+ expect(statements.length).toBe(1);
592
+ expect(statements[0]).toStrictEqual({
593
+ type: 'recreate_table',
594
+ tableName: 'companies',
595
+ columns: [
596
+ {
597
+ name: 'id',
598
+ type: 'integer',
599
+ autoincrement: false,
600
+ notNull: true,
601
+ primaryKey: true,
602
+ generated: undefined,
603
+ },
604
+ ],
605
+ compositePKs: [],
606
+ referenceData: [],
607
+ uniqueConstraints: [],
608
+ checkConstraints: [],
609
+ });
610
+
611
+ expect(sqlStatements.length).toBe(6);
612
+ expect(sqlStatements[0]).toBe(`PRAGMA foreign_keys=OFF;`);
613
+ expect(sqlStatements[1]).toBe(
614
+ `CREATE TABLE \`__new_companies\` (
615
+ \t\`id\` integer PRIMARY KEY NOT NULL
616
+ );\n`,
617
+ );
618
+ expect(sqlStatements[2]).toBe(
619
+ `INSERT INTO \`__new_companies\`("id") SELECT "id" FROM \`companies\`;`,
620
+ );
621
+ expect(sqlStatements[3]).toBe(`DROP TABLE \`companies\`;`);
622
+ expect(sqlStatements[4]).toBe(
623
+ `ALTER TABLE \`__new_companies\` RENAME TO \`companies\`;`,
624
+ );
625
+ expect(sqlStatements[5]).toBe(`PRAGMA foreign_keys=ON;`);
626
+
627
+ expect(columnsToRemove!.length).toBe(0);
628
+ expect(infoToPrint!.length).toBe(0);
629
+ expect(shouldAskForApprove).toBe(false);
630
+ expect(tablesToRemove!.length).toBe(0);
631
+ expect(tablesToTruncate!.length).toBe(0);
632
+ });
633
+
634
+ test('create table with custom name references', async (t) => {
635
+ const client = new Database(':memory:');
636
+
637
+ const users = sqliteTable('users', {
638
+ id: int('id').primaryKey({ autoIncrement: true }),
639
+ name: text('name').notNull(),
640
+ });
641
+
642
+ const schema1 = {
643
+ users,
644
+ posts: sqliteTable(
645
+ 'posts',
646
+ {
647
+ id: int('id').primaryKey({ autoIncrement: true }),
648
+ name: text('name'),
649
+ userId: int('user_id'),
650
+ },
651
+ (t) => ({
652
+ fk: foreignKey({
653
+ columns: [t.id],
654
+ foreignColumns: [users.id],
655
+ name: 'custom_name_fk',
656
+ }),
657
+ }),
658
+ ),
659
+ };
660
+
661
+ const schema2 = {
662
+ users,
663
+ posts: sqliteTable(
664
+ 'posts',
665
+ {
666
+ id: int('id').primaryKey({ autoIncrement: true }),
667
+ name: text('name'),
668
+ userId: int('user_id'),
669
+ },
670
+ (t) => ({
671
+ fk: foreignKey({
672
+ columns: [t.id],
673
+ foreignColumns: [users.id],
674
+ name: 'custom_name_fk',
675
+ }),
676
+ }),
677
+ ),
678
+ };
679
+
680
+ const { sqlStatements } = await diffTestSchemasPushSqlite(
681
+ client,
682
+ schema1,
683
+ schema2,
684
+ [],
685
+ );
686
+
687
+ expect(sqlStatements!.length).toBe(0);
688
+ });
689
+
690
+ test('drop not null, add not null', async (t) => {
691
+ const client = new Database(':memory:');
692
+
693
+ const schema1 = {
694
+ users: sqliteTable('users', {
695
+ id: int('id').primaryKey({ autoIncrement: true }),
696
+ name: text('name').notNull(),
697
+ }),
698
+ posts: sqliteTable('posts', {
699
+ id: int('id').primaryKey({ autoIncrement: true }),
700
+ name: text('name'),
701
+ userId: int('user_id'),
702
+ }),
703
+ };
704
+
705
+ const schema2 = {
706
+ users: sqliteTable('users', {
707
+ id: int('id').primaryKey({ autoIncrement: true }),
708
+ name: text('name'),
709
+ }),
710
+ posts: sqliteTable('posts', {
711
+ id: int('id').primaryKey({ autoIncrement: true }),
712
+ name: text('name').notNull(),
713
+ userId: int('user_id'),
714
+ }),
715
+ };
716
+ const {
717
+ statements,
718
+ sqlStatements,
719
+ columnsToRemove,
720
+ infoToPrint,
721
+ shouldAskForApprove,
722
+ tablesToRemove,
723
+ tablesToTruncate,
724
+ } = await diffTestSchemasPushSqlite(client, schema1, schema2, []);
725
+
726
+ expect(statements!.length).toBe(2);
727
+ expect(statements![0]).toStrictEqual({
728
+ checkConstraints: [],
729
+ columns: [
730
+ {
731
+ autoincrement: true,
732
+ generated: undefined,
733
+ name: 'id',
734
+ notNull: true,
735
+ primaryKey: true,
736
+ type: 'integer',
737
+ },
738
+ {
739
+ autoincrement: false,
740
+ generated: undefined,
741
+ name: 'name',
742
+ notNull: false,
743
+ primaryKey: false,
744
+ type: 'text',
745
+ },
746
+ ],
747
+ compositePKs: [],
748
+ referenceData: [],
749
+ tableName: 'users',
750
+ type: 'recreate_table',
751
+ uniqueConstraints: [],
752
+ });
753
+ expect(statements![1]).toStrictEqual({
754
+ checkConstraints: [],
755
+ columns: [
756
+ {
757
+ autoincrement: true,
758
+ generated: undefined,
759
+ name: 'id',
760
+ notNull: true,
761
+ primaryKey: true,
762
+ type: 'integer',
763
+ },
764
+ {
765
+ autoincrement: false,
766
+ generated: undefined,
767
+ name: 'name',
768
+ notNull: true,
769
+ primaryKey: false,
770
+ type: 'text',
771
+ },
772
+ {
773
+ autoincrement: false,
774
+ generated: undefined,
775
+ name: 'user_id',
776
+ notNull: false,
777
+ primaryKey: false,
778
+ type: 'integer',
779
+ },
780
+ ],
781
+ compositePKs: [],
782
+ referenceData: [],
783
+ tableName: 'posts',
784
+ type: 'recreate_table',
785
+ uniqueConstraints: [],
786
+ });
787
+
788
+ expect(sqlStatements.length).toBe(8);
789
+ expect(sqlStatements[0]).toBe(`CREATE TABLE \`__new_users\` (
790
+ \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
791
+ \t\`name\` text
792
+ );\n`);
793
+ expect(sqlStatements[1]).toBe(
794
+ `INSERT INTO \`__new_users\`("id", "name") SELECT "id", "name" FROM \`users\`;`,
795
+ );
796
+ expect(sqlStatements[2]).toBe(`DROP TABLE \`users\`;`);
797
+ expect(sqlStatements[3]).toBe(
798
+ `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
799
+ );
800
+
801
+ expect(sqlStatements![4]).toBe(`CREATE TABLE \`__new_posts\` (
802
+ \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
803
+ \t\`name\` text NOT NULL,
804
+ \t\`user_id\` integer
805
+ );\n`);
806
+ expect(sqlStatements![5]).toBe(
807
+ `INSERT INTO \`__new_posts\`("id", "name", "user_id") SELECT "id", "name", "user_id" FROM \`posts\`;`,
808
+ );
809
+ expect(sqlStatements![6]).toBe(`DROP TABLE \`posts\`;`);
810
+ expect(sqlStatements![7]).toBe(
811
+ `ALTER TABLE \`__new_posts\` RENAME TO \`posts\`;`,
812
+ );
813
+
814
+ expect(columnsToRemove!.length).toBe(0);
815
+ expect(infoToPrint!.length).toBe(0);
816
+ expect(shouldAskForApprove).toBe(false);
817
+ expect(tablesToRemove!.length).toBe(0);
818
+ expect(tablesToTruncate!.length).toBe(0);
819
+ });
820
+
821
+ test('rename table and change data type', async (t) => {
822
+ const client = new Database(':memory:');
823
+
824
+ const schema1 = {
825
+ users: sqliteTable('old_users', {
826
+ id: int('id').primaryKey({ autoIncrement: true }),
827
+ age: text('age'),
828
+ }),
829
+ };
830
+
831
+ const schema2 = {
832
+ users: sqliteTable('new_users', {
833
+ id: int('id').primaryKey({ autoIncrement: true }),
834
+ age: integer('age'),
835
+ }),
836
+ };
837
+ const {
838
+ statements,
839
+ sqlStatements,
840
+ columnsToRemove,
841
+ infoToPrint,
842
+ shouldAskForApprove,
843
+ tablesToRemove,
844
+ tablesToTruncate,
845
+ } = await diffTestSchemasPushSqlite(client, schema1, schema2, [
846
+ 'public.old_users->public.new_users',
847
+ ]);
848
+
849
+ expect(statements!.length).toBe(2);
850
+ expect(statements![0]).toStrictEqual({
851
+ fromSchema: undefined,
852
+ tableNameFrom: 'old_users',
853
+ tableNameTo: 'new_users',
854
+ toSchema: undefined,
855
+ type: 'rename_table',
856
+ });
857
+ expect(statements![1]).toStrictEqual({
858
+ columns: [
859
+ {
860
+ autoincrement: true,
861
+ name: 'id',
862
+ notNull: true,
863
+ generated: undefined,
864
+ primaryKey: true,
865
+ type: 'integer',
866
+ },
867
+ {
868
+ autoincrement: false,
869
+ name: 'age',
870
+ notNull: false,
871
+ generated: undefined,
872
+ primaryKey: false,
873
+ type: 'integer',
874
+ },
875
+ ],
876
+ compositePKs: [],
877
+ referenceData: [],
878
+ tableName: 'new_users',
879
+ type: 'recreate_table',
880
+ uniqueConstraints: [],
881
+ checkConstraints: [],
882
+ });
883
+
884
+ expect(sqlStatements!.length).toBe(5);
885
+ expect(sqlStatements![0]).toBe(
886
+ `ALTER TABLE \`old_users\` RENAME TO \`new_users\`;`,
887
+ );
888
+ expect(sqlStatements[1]).toBe(`CREATE TABLE \`__new_new_users\` (
889
+ \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
890
+ \t\`age\` integer
891
+ );\n`);
892
+ expect(sqlStatements![2]).toBe(
893
+ `INSERT INTO \`__new_new_users\`("id", "age") SELECT "id", "age" FROM \`new_users\`;`,
894
+ );
895
+ expect(sqlStatements![3]).toBe(`DROP TABLE \`new_users\`;`);
896
+ expect(sqlStatements![4]).toBe(
897
+ `ALTER TABLE \`__new_new_users\` RENAME TO \`new_users\`;`,
898
+ );
899
+
900
+ expect(columnsToRemove!.length).toBe(0);
901
+ expect(infoToPrint!.length).toBe(0);
902
+ expect(shouldAskForApprove).toBe(false);
903
+ expect(tablesToRemove!.length).toBe(0);
904
+ expect(tablesToTruncate!.length).toBe(0);
905
+ });
906
+
907
+ test('rename column and change data type', async (t) => {
908
+ const client = new Database(':memory:');
909
+
910
+ const schema1 = {
911
+ users: sqliteTable('users', {
912
+ id: int('id').primaryKey({ autoIncrement: true }),
913
+ name: text('name'),
914
+ }),
915
+ };
916
+
917
+ const schema2 = {
918
+ users: sqliteTable('users', {
919
+ id: int('id').primaryKey({ autoIncrement: true }),
920
+ age: integer('age'),
921
+ }),
922
+ };
923
+ const {
924
+ statements,
925
+ sqlStatements,
926
+ columnsToRemove,
927
+ infoToPrint,
928
+ shouldAskForApprove,
929
+ tablesToRemove,
930
+ tablesToTruncate,
931
+ } = await diffTestSchemasPushSqlite(client, schema1, schema2, [
932
+ 'public.users.name->public.users.age',
933
+ ]);
934
+
935
+ expect(statements!.length).toBe(1);
936
+ expect(statements![0]).toStrictEqual({
937
+ columns: [
938
+ {
939
+ autoincrement: true,
940
+ name: 'id',
941
+ notNull: true,
942
+ generated: undefined,
943
+ primaryKey: true,
944
+ type: 'integer',
945
+ },
946
+ {
947
+ autoincrement: false,
948
+ name: 'age',
949
+ notNull: false,
950
+ generated: undefined,
951
+ primaryKey: false,
952
+ type: 'integer',
953
+ },
954
+ ],
955
+ compositePKs: [],
956
+ referenceData: [],
957
+ tableName: 'users',
958
+ type: 'recreate_table',
959
+ uniqueConstraints: [],
960
+ checkConstraints: [],
961
+ });
962
+
963
+ expect(sqlStatements!.length).toBe(4);
964
+ expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` (
965
+ \t\`id\` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
966
+ \t\`age\` integer
967
+ );\n`);
968
+ expect(sqlStatements![1]).toBe(
969
+ `INSERT INTO \`__new_users\`("id", "age") SELECT "id", "age" FROM \`users\`;`,
970
+ );
971
+ expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
972
+ expect(sqlStatements![3]).toBe(
973
+ `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
974
+ );
975
+
976
+ expect(columnsToRemove!.length).toBe(0);
977
+ expect(infoToPrint!.length).toBe(0);
978
+ expect(shouldAskForApprove).toBe(false);
979
+ expect(tablesToRemove!.length).toBe(0);
980
+ expect(tablesToTruncate!.length).toBe(0);
981
+ });
982
+
983
+ test('recreate table with nested references', async (t) => {
984
+ const client = new Database(':memory:');
985
+
986
+ let users = sqliteTable('users', {
987
+ id: int('id').primaryKey({ autoIncrement: true }),
988
+ name: text('name'),
989
+ age: integer('age'),
990
+ });
991
+ let subscriptions = sqliteTable('subscriptions', {
992
+ id: int('id').primaryKey({ autoIncrement: true }),
993
+ userId: integer('user_id').references(() => users.id),
994
+ customerId: text('customer_id'),
995
+ });
996
+ const schema1 = {
997
+ users: users,
998
+ subscriptions: subscriptions,
999
+ subscriptionMetadata: sqliteTable('subscriptions_metadata', {
1000
+ id: int('id').primaryKey({ autoIncrement: true }),
1001
+ subscriptionId: text('subscription_id').references(
1002
+ () => subscriptions.id,
1003
+ ),
1004
+ }),
1005
+ };
1006
+
1007
+ users = sqliteTable('users', {
1008
+ id: int('id').primaryKey({ autoIncrement: false }),
1009
+ name: text('name'),
1010
+ age: integer('age'),
1011
+ });
1012
+ const schema2 = {
1013
+ users: users,
1014
+ subscriptions: subscriptions,
1015
+ subscriptionMetadata: sqliteTable('subscriptions_metadata', {
1016
+ id: int('id').primaryKey({ autoIncrement: true }),
1017
+ subscriptionId: text('subscription_id').references(
1018
+ () => subscriptions.id,
1019
+ ),
1020
+ }),
1021
+ };
1022
+
1023
+ const {
1024
+ statements,
1025
+ sqlStatements,
1026
+ columnsToRemove,
1027
+ infoToPrint,
1028
+ shouldAskForApprove,
1029
+ tablesToRemove,
1030
+ tablesToTruncate,
1031
+ } = await diffTestSchemasPushSqlite(client, schema1, schema2, [
1032
+ 'public.users.name->public.users.age',
1033
+ ]);
1034
+
1035
+ expect(statements!.length).toBe(1);
1036
+ expect(statements![0]).toStrictEqual({
1037
+ columns: [
1038
+ {
1039
+ autoincrement: false,
1040
+ name: 'id',
1041
+ notNull: true,
1042
+ generated: undefined,
1043
+ primaryKey: true,
1044
+ type: 'integer',
1045
+ },
1046
+ {
1047
+ autoincrement: false,
1048
+ name: 'name',
1049
+ notNull: false,
1050
+ generated: undefined,
1051
+ primaryKey: false,
1052
+ type: 'text',
1053
+ },
1054
+ {
1055
+ autoincrement: false,
1056
+ name: 'age',
1057
+ notNull: false,
1058
+ generated: undefined,
1059
+ primaryKey: false,
1060
+ type: 'integer',
1061
+ },
1062
+ ],
1063
+ compositePKs: [],
1064
+ referenceData: [],
1065
+ tableName: 'users',
1066
+ type: 'recreate_table',
1067
+ uniqueConstraints: [],
1068
+ checkConstraints: [],
1069
+ });
1070
+
1071
+ expect(sqlStatements!.length).toBe(6);
1072
+ expect(sqlStatements[0]).toBe('PRAGMA foreign_keys=OFF;');
1073
+ expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` (
1074
+ \t\`id\` integer PRIMARY KEY NOT NULL,
1075
+ \t\`name\` text,
1076
+ \t\`age\` integer
1077
+ );\n`);
1078
+ expect(sqlStatements![2]).toBe(
1079
+ `INSERT INTO \`__new_users\`("id", "name", "age") SELECT "id", "name", "age" FROM \`users\`;`,
1080
+ );
1081
+ expect(sqlStatements![3]).toBe(`DROP TABLE \`users\`;`);
1082
+ expect(sqlStatements![4]).toBe(
1083
+ `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
1084
+ );
1085
+ expect(sqlStatements[5]).toBe('PRAGMA foreign_keys=ON;');
1086
+
1087
+ expect(columnsToRemove!.length).toBe(0);
1088
+ expect(infoToPrint!.length).toBe(0);
1089
+ expect(shouldAskForApprove).toBe(false);
1090
+ expect(tablesToRemove!.length).toBe(0);
1091
+ expect(tablesToTruncate!.length).toBe(0);
1092
+ });
1093
+
1094
+ test('recreate table with added column not null and without default with data', async (t) => {
1095
+ const client = new Database(':memory:');
1096
+
1097
+ const schema1 = {
1098
+ users: sqliteTable('users', {
1099
+ id: int('id').primaryKey({ autoIncrement: true }),
1100
+ name: text('name'),
1101
+ age: integer('age'),
1102
+ }),
1103
+ };
1104
+
1105
+ const schema2 = {
1106
+ users: sqliteTable('users', {
1107
+ id: int('id').primaryKey({ autoIncrement: false }),
1108
+ name: text('name'),
1109
+ age: integer('age'),
1110
+ newColumn: text('new_column').notNull(),
1111
+ }),
1112
+ };
1113
+
1114
+ const seedStatements = [
1115
+ `INSERT INTO \`users\` ("name", "age") VALUES ('drizzle', 12)`,
1116
+ `INSERT INTO \`users\` ("name", "age") VALUES ('turso', 12)`,
1117
+ ];
1118
+
1119
+ const {
1120
+ statements,
1121
+ sqlStatements,
1122
+ columnsToRemove,
1123
+ infoToPrint,
1124
+ shouldAskForApprove,
1125
+ tablesToRemove,
1126
+ tablesToTruncate,
1127
+ } = await diffTestSchemasPushSqlite(
1128
+ client,
1129
+ schema1,
1130
+ schema2,
1131
+ [],
1132
+ false,
1133
+ seedStatements,
1134
+ );
1135
+
1136
+ expect(statements!.length).toBe(1);
1137
+ expect(statements![0]).toStrictEqual({
1138
+ columns: [
1139
+ {
1140
+ autoincrement: false,
1141
+ name: 'id',
1142
+ notNull: true,
1143
+ generated: undefined,
1144
+ primaryKey: true,
1145
+ type: 'integer',
1146
+ },
1147
+ {
1148
+ autoincrement: false,
1149
+ name: 'name',
1150
+ notNull: false,
1151
+ generated: undefined,
1152
+ primaryKey: false,
1153
+ type: 'text',
1154
+ },
1155
+ {
1156
+ autoincrement: false,
1157
+ name: 'age',
1158
+ notNull: false,
1159
+ generated: undefined,
1160
+ primaryKey: false,
1161
+ type: 'integer',
1162
+ },
1163
+ {
1164
+ autoincrement: false,
1165
+ name: 'new_column',
1166
+ notNull: true,
1167
+ generated: undefined,
1168
+ primaryKey: false,
1169
+ type: 'text',
1170
+ },
1171
+ ],
1172
+ compositePKs: [],
1173
+ referenceData: [],
1174
+ tableName: 'users',
1175
+ type: 'recreate_table',
1176
+ uniqueConstraints: [],
1177
+ checkConstraints: [],
1178
+ });
1179
+
1180
+ expect(sqlStatements!.length).toBe(4);
1181
+ expect(sqlStatements[0]).toBe('DELETE FROM \`users\`;');
1182
+ expect(sqlStatements![1]).toBe(`CREATE TABLE \`__new_users\` (
1183
+ \t\`id\` integer PRIMARY KEY NOT NULL,
1184
+ \t\`name\` text,
1185
+ \t\`age\` integer,
1186
+ \t\`new_column\` text NOT NULL
1187
+ );\n`);
1188
+ expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
1189
+ expect(sqlStatements![3]).toBe(
1190
+ `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
1191
+ );
1192
+
1193
+ expect(columnsToRemove!.length).toBe(0);
1194
+ expect(infoToPrint!.length).toBe(1);
1195
+ expect(infoToPrint![0]).toBe(
1196
+ `· You're about to add not-null ${
1197
+ chalk.underline('new_column')
1198
+ } column without default value to table, which contains 2 items`,
1199
+ );
1200
+ expect(shouldAskForApprove).toBe(true);
1201
+ expect(tablesToRemove!.length).toBe(0);
1202
+ expect(tablesToTruncate!.length).toBe(1);
1203
+ expect(tablesToTruncate![0]).toBe('users');
1204
+ });
1205
+
1206
+ test('add check constraint to table', async (t) => {
1207
+ const client = new Database(':memory:');
1208
+
1209
+ const schema1 = {
1210
+ users: sqliteTable('users', {
1211
+ id: int('id').primaryKey({ autoIncrement: false }),
1212
+ name: text('name'),
1213
+ age: integer('age'),
1214
+ }),
1215
+ };
1216
+
1217
+ const schema2 = {
1218
+ users: sqliteTable('users', {
1219
+ id: int('id').primaryKey({ autoIncrement: false }),
1220
+ name: text('name'),
1221
+ age: integer('age'),
1222
+ }, (table) => ({
1223
+ someCheck: check('some_check', sql`${table.age} > 21`),
1224
+ })),
1225
+ };
1226
+
1227
+ const {
1228
+ statements,
1229
+ sqlStatements,
1230
+ columnsToRemove,
1231
+ infoToPrint,
1232
+ shouldAskForApprove,
1233
+ tablesToRemove,
1234
+ tablesToTruncate,
1235
+ } = await diffTestSchemasPushSqlite(
1236
+ client,
1237
+ schema1,
1238
+ schema2,
1239
+ [],
1240
+ );
1241
+
1242
+ expect(statements!.length).toBe(1);
1243
+ expect(statements![0]).toStrictEqual({
1244
+ columns: [
1245
+ {
1246
+ autoincrement: false,
1247
+ name: 'id',
1248
+ notNull: true,
1249
+ generated: undefined,
1250
+ primaryKey: true,
1251
+ type: 'integer',
1252
+ },
1253
+ {
1254
+ autoincrement: false,
1255
+ name: 'name',
1256
+ notNull: false,
1257
+ generated: undefined,
1258
+ primaryKey: false,
1259
+ type: 'text',
1260
+ },
1261
+ {
1262
+ autoincrement: false,
1263
+ name: 'age',
1264
+ notNull: false,
1265
+ generated: undefined,
1266
+ primaryKey: false,
1267
+ type: 'integer',
1268
+ },
1269
+ ],
1270
+ compositePKs: [],
1271
+ referenceData: [],
1272
+ tableName: 'users',
1273
+ type: 'recreate_table',
1274
+ uniqueConstraints: [],
1275
+ checkConstraints: ['some_check;"users"."age" > 21'],
1276
+ });
1277
+
1278
+ expect(sqlStatements!.length).toBe(4);
1279
+ expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` (
1280
+ \t\`id\` integer PRIMARY KEY NOT NULL,
1281
+ \t\`name\` text,
1282
+ \t\`age\` integer,
1283
+ \tCONSTRAINT "some_check" CHECK("__new_users"."age" > 21)
1284
+ );\n`);
1285
+ expect(sqlStatements[1]).toBe(
1286
+ 'INSERT INTO `__new_users`("id", "name", "age") SELECT "id", "name", "age" FROM `users`;',
1287
+ );
1288
+ expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
1289
+ expect(sqlStatements![3]).toBe(
1290
+ `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
1291
+ );
1292
+
1293
+ expect(columnsToRemove!.length).toBe(0);
1294
+ expect(infoToPrint!.length).toBe(0);
1295
+ expect(shouldAskForApprove).toBe(false);
1296
+ expect(tablesToRemove!.length).toBe(0);
1297
+ expect(tablesToTruncate!.length).toBe(0);
1298
+ });
1299
+
1300
+ test('drop check constraint', async (t) => {
1301
+ const client = new Database(':memory:');
1302
+
1303
+ const schema1 = {
1304
+ users: sqliteTable('users', {
1305
+ id: int('id').primaryKey({ autoIncrement: false }),
1306
+ name: text('name'),
1307
+ age: integer('age'),
1308
+ }, (table) => ({
1309
+ someCheck: check('some_check', sql`${table.age} > 21`),
1310
+ })),
1311
+ };
1312
+
1313
+ const schema2 = {
1314
+ users: sqliteTable('users', {
1315
+ id: int('id').primaryKey({ autoIncrement: false }),
1316
+ name: text('name'),
1317
+ age: integer('age'),
1318
+ }),
1319
+ };
1320
+
1321
+ const {
1322
+ statements,
1323
+ sqlStatements,
1324
+ columnsToRemove,
1325
+ infoToPrint,
1326
+ shouldAskForApprove,
1327
+ tablesToRemove,
1328
+ tablesToTruncate,
1329
+ } = await diffTestSchemasPushSqlite(
1330
+ client,
1331
+ schema1,
1332
+ schema2,
1333
+ [],
1334
+ );
1335
+
1336
+ expect(statements!.length).toBe(1);
1337
+ expect(statements![0]).toStrictEqual({
1338
+ columns: [
1339
+ {
1340
+ autoincrement: false,
1341
+ name: 'id',
1342
+ notNull: true,
1343
+ generated: undefined,
1344
+ primaryKey: true,
1345
+ type: 'integer',
1346
+ },
1347
+ {
1348
+ autoincrement: false,
1349
+ name: 'name',
1350
+ notNull: false,
1351
+ generated: undefined,
1352
+ primaryKey: false,
1353
+ type: 'text',
1354
+ },
1355
+ {
1356
+ autoincrement: false,
1357
+ name: 'age',
1358
+ notNull: false,
1359
+ generated: undefined,
1360
+ primaryKey: false,
1361
+ type: 'integer',
1362
+ },
1363
+ ],
1364
+ compositePKs: [],
1365
+ referenceData: [],
1366
+ tableName: 'users',
1367
+ type: 'recreate_table',
1368
+ uniqueConstraints: [],
1369
+ checkConstraints: [],
1370
+ });
1371
+
1372
+ expect(sqlStatements!.length).toBe(4);
1373
+ expect(sqlStatements![0]).toBe(`CREATE TABLE \`__new_users\` (
1374
+ \t\`id\` integer PRIMARY KEY NOT NULL,
1375
+ \t\`name\` text,
1376
+ \t\`age\` integer
1377
+ );\n`);
1378
+ expect(sqlStatements[1]).toBe(
1379
+ 'INSERT INTO `__new_users`("id", "name", "age") SELECT "id", "name", "age" FROM `users`;',
1380
+ );
1381
+ expect(sqlStatements![2]).toBe(`DROP TABLE \`users\`;`);
1382
+ expect(sqlStatements![3]).toBe(
1383
+ `ALTER TABLE \`__new_users\` RENAME TO \`users\`;`,
1384
+ );
1385
+
1386
+ expect(columnsToRemove!.length).toBe(0);
1387
+ expect(infoToPrint!.length).toBe(0);
1388
+ expect(shouldAskForApprove).toBe(false);
1389
+ expect(tablesToRemove!.length).toBe(0);
1390
+ expect(tablesToTruncate!.length).toBe(0);
1391
+ });
1392
+
1393
+ test('db has checks. Push with same names', async () => {
1394
+ const client = new Database(':memory:');
1395
+
1396
+ const schema1 = {
1397
+ users: sqliteTable('users', {
1398
+ id: int('id').primaryKey({ autoIncrement: false }),
1399
+ name: text('name'),
1400
+ age: integer('age'),
1401
+ }, (table) => ({
1402
+ someCheck: check('some_check', sql`${table.age} > 21`),
1403
+ })),
1404
+ };
1405
+
1406
+ const schema2 = {
1407
+ users: sqliteTable('users', {
1408
+ id: int('id').primaryKey({ autoIncrement: false }),
1409
+ name: text('name'),
1410
+ age: integer('age'),
1411
+ }, (table) => ({
1412
+ someCheck: check('some_check', sql`some new value`),
1413
+ })),
1414
+ };
1415
+
1416
+ const {
1417
+ statements,
1418
+ sqlStatements,
1419
+ columnsToRemove,
1420
+ infoToPrint,
1421
+ schemasToRemove,
1422
+ shouldAskForApprove,
1423
+ tablesToRemove,
1424
+ tablesToTruncate,
1425
+ } = await diffTestSchemasPushSqlite(
1426
+ client,
1427
+ schema1,
1428
+ schema2,
1429
+ [],
1430
+ false,
1431
+ [],
1432
+ );
1433
+ expect(statements).toStrictEqual([]);
1434
+ expect(sqlStatements).toStrictEqual([]);
1435
+ expect(columnsToRemove!.length).toBe(0);
1436
+ expect(infoToPrint!.length).toBe(0);
1437
+ expect(shouldAskForApprove).toBe(false);
1438
+ expect(tablesToRemove!.length).toBe(0);
1439
+ expect(tablesToTruncate!.length).toBe(0);
1440
+ });
1441
+
1442
+ test('create view', async () => {
1443
+ const client = new Database(':memory:');
1444
+
1445
+ const table = sqliteTable('test', {
1446
+ id: int('id').primaryKey(),
1447
+ });
1448
+
1449
+ const schema1 = {
1450
+ test: table,
1451
+ };
1452
+
1453
+ const schema2 = {
1454
+ test: table,
1455
+ view: sqliteView('view').as((qb) => qb.select().from(table)),
1456
+ };
1457
+
1458
+ const { statements, sqlStatements } = await diffTestSchemasPushSqlite(
1459
+ client,
1460
+ schema1,
1461
+ schema2,
1462
+ [],
1463
+ );
1464
+
1465
+ expect(statements).toStrictEqual([
1466
+ {
1467
+ definition: 'select "id" from "test"',
1468
+ name: 'view',
1469
+ type: 'sqlite_create_view',
1470
+ },
1471
+ ]);
1472
+ expect(sqlStatements).toStrictEqual([
1473
+ `CREATE VIEW \`view\` AS select "id" from "test";`,
1474
+ ]);
1475
+ });
1476
+
1477
+ test('drop view', async () => {
1478
+ const client = new Database(':memory:');
1479
+
1480
+ const table = sqliteTable('test', {
1481
+ id: int('id').primaryKey(),
1482
+ });
1483
+
1484
+ const schema1 = {
1485
+ test: table,
1486
+ view: sqliteView('view').as((qb) => qb.select().from(table)),
1487
+ };
1488
+
1489
+ const schema2 = {
1490
+ test: table,
1491
+ };
1492
+
1493
+ const { statements, sqlStatements } = await diffTestSchemasPushSqlite(
1494
+ client,
1495
+ schema1,
1496
+ schema2,
1497
+ [],
1498
+ );
1499
+
1500
+ expect(statements).toStrictEqual([
1501
+ {
1502
+ name: 'view',
1503
+ type: 'drop_view',
1504
+ },
1505
+ ]);
1506
+ expect(sqlStatements).toStrictEqual([
1507
+ 'DROP VIEW \`view\`;',
1508
+ ]);
1509
+ });
1510
+
1511
+ test('alter view ".as"', async () => {
1512
+ const client = new Database(':memory:');
1513
+
1514
+ const table = sqliteTable('test', {
1515
+ id: int('id').primaryKey(),
1516
+ });
1517
+
1518
+ const schema1 = {
1519
+ test: table,
1520
+ view: sqliteView('view').as((qb) => qb.select().from(table).where(sql`${table.id} = 1`)),
1521
+ };
1522
+
1523
+ const schema2 = {
1524
+ test: table,
1525
+ view: sqliteView('view').as((qb) => qb.select().from(table)),
1526
+ };
1527
+
1528
+ const { statements, sqlStatements } = await diffTestSchemasPushSqlite(
1529
+ client,
1530
+ schema1,
1531
+ schema2,
1532
+ [],
1533
+ );
1534
+
1535
+ expect(statements.length).toBe(0);
1536
+ expect(sqlStatements.length).toBe(0);
1537
+ });
1538
+
1539
+ test('create composite primary key', async (t) => {
1540
+ const client = new Database(':memory:');
1541
+
1542
+ const schema1 = {};
1543
+
1544
+ const schema2 = {
1545
+ table: sqliteTable('table', {
1546
+ col1: integer('col1').notNull(),
1547
+ col2: integer('col2').notNull(),
1548
+ }, (t) => ({
1549
+ pk: primaryKey({
1550
+ columns: [t.col1, t.col2],
1551
+ }),
1552
+ })),
1553
+ };
1554
+
1555
+ const {
1556
+ statements,
1557
+ sqlStatements,
1558
+ } = await diffTestSchemasPushSqlite(
1559
+ client,
1560
+ schema1,
1561
+ schema2,
1562
+ [],
1563
+ );
1564
+
1565
+ expect(statements).toStrictEqual([{
1566
+ type: 'sqlite_create_table',
1567
+ tableName: 'table',
1568
+ compositePKs: [['col1', 'col2']],
1569
+ uniqueConstraints: [],
1570
+ referenceData: [],
1571
+ checkConstraints: [],
1572
+ columns: [
1573
+ { name: 'col1', type: 'integer', primaryKey: false, notNull: true, autoincrement: false },
1574
+ { name: 'col2', type: 'integer', primaryKey: false, notNull: true, autoincrement: false },
1575
+ ],
1576
+ }]);
1577
+ expect(sqlStatements).toStrictEqual([
1578
+ 'CREATE TABLE `table` (\n\t`col1` integer NOT NULL,\n\t`col2` integer NOT NULL,\n\tPRIMARY KEY(`col1`, `col2`)\n);\n',
1579
+ ]);
1580
+ });
1581
+
1582
+ test('rename table with composite primary key', async () => {
1583
+ const client = new Database(':memory:');
1584
+
1585
+ const productsCategoriesTable = (tableName: string) => {
1586
+ return sqliteTable(tableName, {
1587
+ productId: text('product_id').notNull(),
1588
+ categoryId: text('category_id').notNull(),
1589
+ }, (t) => ({
1590
+ pk: primaryKey({
1591
+ columns: [t.productId, t.categoryId],
1592
+ }),
1593
+ }));
1594
+ };
1595
+
1596
+ const schema1 = {
1597
+ table: productsCategoriesTable('products_categories'),
1598
+ };
1599
+ const schema2 = {
1600
+ test: productsCategoriesTable('products_to_categories'),
1601
+ };
1602
+
1603
+ const { sqlStatements } = await diffTestSchemasPushSqlite(
1604
+ client,
1605
+ schema1,
1606
+ schema2,
1607
+ ['public.products_categories->public.products_to_categories'],
1608
+ false,
1609
+ );
1610
+ expect(sqlStatements).toStrictEqual([
1611
+ 'ALTER TABLE `products_categories` RENAME TO `products_to_categories`;',
1612
+ ]);
1613
+ });