@zmdb/migrations 1.0.0-beta.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.
- package/LICENSE +674 -0
- package/README.md +36 -0
- package/dist/declarations/emit.d.ts +23 -0
- package/dist/declarations/emit.d.ts.map +1 -0
- package/dist/declarations/emit.js +581 -0
- package/dist/declarations/emit.js.map +1 -0
- package/dist/declarations/index.d.ts +3 -0
- package/dist/declarations/index.d.ts.map +1 -0
- package/dist/declarations/index.js +3 -0
- package/dist/declarations/index.js.map +1 -0
- package/dist/declarations/tagged-property.d.ts +33 -0
- package/dist/declarations/tagged-property.d.ts.map +1 -0
- package/dist/declarations/tagged-property.js +148 -0
- package/dist/declarations/tagged-property.js.map +1 -0
- package/dist/embedded.d.ts +25 -0
- package/dist/embedded.d.ts.map +1 -0
- package/dist/embedded.js +133 -0
- package/dist/embedded.js.map +1 -0
- package/dist/file-io.d.ts +15 -0
- package/dist/file-io.d.ts.map +1 -0
- package/dist/file-io.js +92 -0
- package/dist/file-io.js.map +1 -0
- package/dist/files.d.ts +11 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +10 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +350 -0
- package/dist/index.js.map +1 -0
- package/dist/introspect/common.d.ts +26 -0
- package/dist/introspect/common.d.ts.map +1 -0
- package/dist/introspect/common.js +155 -0
- package/dist/introspect/common.js.map +1 -0
- package/dist/introspect/drift.d.ts +45 -0
- package/dist/introspect/drift.d.ts.map +1 -0
- package/dist/introspect/drift.js +84 -0
- package/dist/introspect/drift.js.map +1 -0
- package/dist/introspect/index.d.ts +8 -0
- package/dist/introspect/index.d.ts.map +1 -0
- package/dist/introspect/index.js +10 -0
- package/dist/introspect/index.js.map +1 -0
- package/dist/operations/check.d.ts +16 -0
- package/dist/operations/check.d.ts.map +1 -0
- package/dist/operations/check.js +236 -0
- package/dist/operations/check.js.map +1 -0
- package/dist/operations/embed.d.ts +23 -0
- package/dist/operations/embed.d.ts.map +1 -0
- package/dist/operations/embed.js +52 -0
- package/dist/operations/embed.js.map +1 -0
- package/dist/operations/export.d.ts +9 -0
- package/dist/operations/export.d.ts.map +1 -0
- package/dist/operations/export.js +15 -0
- package/dist/operations/export.js.map +1 -0
- package/dist/operations/generate.d.ts +17 -0
- package/dist/operations/generate.d.ts.map +1 -0
- package/dist/operations/generate.js +115 -0
- package/dist/operations/generate.js.map +1 -0
- package/dist/operations/migrate.d.ts +23 -0
- package/dist/operations/migrate.d.ts.map +1 -0
- package/dist/operations/migrate.js +67 -0
- package/dist/operations/migrate.js.map +1 -0
- package/dist/operations/pull.d.ts +40 -0
- package/dist/operations/pull.d.ts.map +1 -0
- package/dist/operations/pull.js +100 -0
- package/dist/operations/pull.js.map +1 -0
- package/dist/operations/push.d.ts +20 -0
- package/dist/operations/push.d.ts.map +1 -0
- package/dist/operations/push.js +82 -0
- package/dist/operations/push.js.map +1 -0
- package/dist/operations/upgrade.d.ts +10 -0
- package/dist/operations/upgrade.d.ts.map +1 -0
- package/dist/operations/upgrade.js +38 -0
- package/dist/operations/upgrade.js.map +1 -0
- package/dist/project.d.ts +46 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +42 -0
- package/dist/project.js.map +1 -0
- package/dist/runner.d.ts +59 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +163 -0
- package/dist/runner.js.map +1 -0
- package/dist/testing/official-dialects.fixture.d.ts +16 -0
- package/dist/testing/official-dialects.fixture.d.ts.map +1 -0
- package/dist/testing/official-dialects.fixture.js +21 -0
- package/dist/testing/official-dialects.fixture.js.map +1 -0
- package/dist/testing.d.ts +8 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +27 -0
- package/dist/testing.js.map +1 -0
- package/package.json +81 -0
- package/src/declarations/emit.ts +705 -0
- package/src/declarations/index.ts +13 -0
- package/src/declarations/tagged-property.ts +163 -0
- package/src/embedded.ts +192 -0
- package/src/file-io.ts +114 -0
- package/src/files.ts +34 -0
- package/src/index.ts +502 -0
- package/src/introspect/__fixtures__/mysql-8.4.11.json +371 -0
- package/src/introspect/common.ts +203 -0
- package/src/introspect/drift.ts +157 -0
- package/src/introspect/index.ts +33 -0
- package/src/operations/check.ts +309 -0
- package/src/operations/embed.ts +87 -0
- package/src/operations/export.ts +21 -0
- package/src/operations/generate.ts +144 -0
- package/src/operations/migrate.ts +110 -0
- package/src/operations/pull.ts +160 -0
- package/src/operations/push.ts +98 -0
- package/src/operations/upgrade.ts +56 -0
- package/src/project.ts +93 -0
- package/src/runner.ts +260 -0
- package/src/testing/official-dialects.fixture.ts +24 -0
- package/src/testing.ts +32 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provenance": {
|
|
3
|
+
"capturedOn": "2026-09-04",
|
|
4
|
+
"captureFormat": "mysql --batch --raw; every non-NULL catalog cell is retained as text",
|
|
5
|
+
"image": "docker.io/library/mysql:8.4",
|
|
6
|
+
"imageCreated": "2026-07-27T22:08:26.634911873Z",
|
|
7
|
+
"imageDigest": "sha256:1d6b6a8fcee8ff758ff151d017f5203cd06792a0e698f0a593c9dfcb14609cf0",
|
|
8
|
+
"imageId": "bced325a4ab7aec848f4688371c7433351dcb5dba26fbcc29c67727d898ae5cb",
|
|
9
|
+
"schema": "zmdb430",
|
|
10
|
+
"server": {
|
|
11
|
+
"version": "8.4.11",
|
|
12
|
+
"versionComment": "MySQL Community Server - GPL",
|
|
13
|
+
"versionCompileMachine": "x86_64",
|
|
14
|
+
"versionCompileOs": "Linux"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"ddl": [
|
|
18
|
+
"CREATE TABLE accounts (id BIGINT NOT NULL AUTO_INCREMENT, email VARCHAR(255) NOT NULL, active TINYINT(1) NOT NULL DEFAULT 1, balance DECIMAL(12,2) NULL, status ENUM('active','disabled') NOT NULL DEFAULT 'active', created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), nickname TEXT NULL, PRIMARY KEY (id), UNIQUE KEY accounts_email_uq (email)) ENGINE=InnoDB",
|
|
19
|
+
"CREATE TABLE memberships (tenant_id INT NOT NULL, user_id INT NOT NULL, label VARCHAR(50) NULL, PRIMARY KEY (user_id, tenant_id)) ENGINE=InnoDB",
|
|
20
|
+
"CREATE TABLE posts (id INT NOT NULL AUTO_INCREMENT, account_id BIGINT NOT NULL, slug VARCHAR(120) NOT NULL, body LONGTEXT NULL, PRIMARY KEY (id), KEY posts_account_idx (account_id), CONSTRAINT posts_account_fkey FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE ON UPDATE RESTRICT) ENGINE=InnoDB"
|
|
21
|
+
],
|
|
22
|
+
"queries": {
|
|
23
|
+
"columns": "SELECT TABLE_NAME, ORDINAL_POSITION, COLUMN_NAME, IS_NULLABLE, DATA_TYPE, COLUMN_TYPE, CHARACTER_MAXIMUM_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE, COLUMN_DEFAULT, EXTRA, GENERATION_EXPRESSION FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME, ORDINAL_POSITION",
|
|
24
|
+
"keyColumnUsage": "SELECT TABLE_NAME, CONSTRAINT_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME, CONSTRAINT_NAME, ORDINAL_POSITION",
|
|
25
|
+
"referentialConstraints": "SELECT TABLE_NAME, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE FROM information_schema.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() ORDER BY TABLE_NAME, CONSTRAINT_NAME",
|
|
26
|
+
"statistics": "SELECT TABLE_NAME, INDEX_NAME, NON_UNIQUE, SEQ_IN_INDEX, COLUMN_NAME, EXPRESSION, INDEX_TYPE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX",
|
|
27
|
+
"tables": "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME"
|
|
28
|
+
},
|
|
29
|
+
"rows": {
|
|
30
|
+
"tables": [
|
|
31
|
+
{
|
|
32
|
+
"TABLE_SCHEMA": "zmdb430",
|
|
33
|
+
"TABLE_NAME": "accounts",
|
|
34
|
+
"TABLE_TYPE": "BASE TABLE",
|
|
35
|
+
"ENGINE": "InnoDB"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"TABLE_SCHEMA": "zmdb430",
|
|
39
|
+
"TABLE_NAME": "memberships",
|
|
40
|
+
"TABLE_TYPE": "BASE TABLE",
|
|
41
|
+
"ENGINE": "InnoDB"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"TABLE_SCHEMA": "zmdb430",
|
|
45
|
+
"TABLE_NAME": "posts",
|
|
46
|
+
"TABLE_TYPE": "BASE TABLE",
|
|
47
|
+
"ENGINE": "InnoDB"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"columns": [
|
|
51
|
+
{
|
|
52
|
+
"TABLE_NAME": "accounts",
|
|
53
|
+
"ORDINAL_POSITION": "1",
|
|
54
|
+
"COLUMN_NAME": "id",
|
|
55
|
+
"IS_NULLABLE": "NO",
|
|
56
|
+
"DATA_TYPE": "bigint",
|
|
57
|
+
"COLUMN_TYPE": "bigint",
|
|
58
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
59
|
+
"NUMERIC_PRECISION": "19",
|
|
60
|
+
"NUMERIC_SCALE": "0",
|
|
61
|
+
"COLUMN_DEFAULT": null,
|
|
62
|
+
"EXTRA": "auto_increment",
|
|
63
|
+
"GENERATION_EXPRESSION": ""
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"TABLE_NAME": "accounts",
|
|
67
|
+
"ORDINAL_POSITION": "2",
|
|
68
|
+
"COLUMN_NAME": "email",
|
|
69
|
+
"IS_NULLABLE": "NO",
|
|
70
|
+
"DATA_TYPE": "varchar",
|
|
71
|
+
"COLUMN_TYPE": "varchar(255)",
|
|
72
|
+
"CHARACTER_MAXIMUM_LENGTH": "255",
|
|
73
|
+
"NUMERIC_PRECISION": null,
|
|
74
|
+
"NUMERIC_SCALE": null,
|
|
75
|
+
"COLUMN_DEFAULT": null,
|
|
76
|
+
"EXTRA": "",
|
|
77
|
+
"GENERATION_EXPRESSION": ""
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"TABLE_NAME": "accounts",
|
|
81
|
+
"ORDINAL_POSITION": "3",
|
|
82
|
+
"COLUMN_NAME": "active",
|
|
83
|
+
"IS_NULLABLE": "NO",
|
|
84
|
+
"DATA_TYPE": "tinyint",
|
|
85
|
+
"COLUMN_TYPE": "tinyint(1)",
|
|
86
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
87
|
+
"NUMERIC_PRECISION": "3",
|
|
88
|
+
"NUMERIC_SCALE": "0",
|
|
89
|
+
"COLUMN_DEFAULT": "1",
|
|
90
|
+
"EXTRA": "",
|
|
91
|
+
"GENERATION_EXPRESSION": ""
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"TABLE_NAME": "accounts",
|
|
95
|
+
"ORDINAL_POSITION": "4",
|
|
96
|
+
"COLUMN_NAME": "balance",
|
|
97
|
+
"IS_NULLABLE": "YES",
|
|
98
|
+
"DATA_TYPE": "decimal",
|
|
99
|
+
"COLUMN_TYPE": "decimal(12,2)",
|
|
100
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
101
|
+
"NUMERIC_PRECISION": "12",
|
|
102
|
+
"NUMERIC_SCALE": "2",
|
|
103
|
+
"COLUMN_DEFAULT": null,
|
|
104
|
+
"EXTRA": "",
|
|
105
|
+
"GENERATION_EXPRESSION": ""
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"TABLE_NAME": "accounts",
|
|
109
|
+
"ORDINAL_POSITION": "5",
|
|
110
|
+
"COLUMN_NAME": "status",
|
|
111
|
+
"IS_NULLABLE": "NO",
|
|
112
|
+
"DATA_TYPE": "enum",
|
|
113
|
+
"COLUMN_TYPE": "enum('active','disabled')",
|
|
114
|
+
"CHARACTER_MAXIMUM_LENGTH": "8",
|
|
115
|
+
"NUMERIC_PRECISION": null,
|
|
116
|
+
"NUMERIC_SCALE": null,
|
|
117
|
+
"COLUMN_DEFAULT": "active",
|
|
118
|
+
"EXTRA": "",
|
|
119
|
+
"GENERATION_EXPRESSION": ""
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"TABLE_NAME": "accounts",
|
|
123
|
+
"ORDINAL_POSITION": "6",
|
|
124
|
+
"COLUMN_NAME": "created_at",
|
|
125
|
+
"IS_NULLABLE": "NO",
|
|
126
|
+
"DATA_TYPE": "datetime",
|
|
127
|
+
"COLUMN_TYPE": "datetime(3)",
|
|
128
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
129
|
+
"NUMERIC_PRECISION": null,
|
|
130
|
+
"NUMERIC_SCALE": null,
|
|
131
|
+
"COLUMN_DEFAULT": "CURRENT_TIMESTAMP(3)",
|
|
132
|
+
"EXTRA": "DEFAULT_GENERATED",
|
|
133
|
+
"GENERATION_EXPRESSION": ""
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"TABLE_NAME": "accounts",
|
|
137
|
+
"ORDINAL_POSITION": "7",
|
|
138
|
+
"COLUMN_NAME": "nickname",
|
|
139
|
+
"IS_NULLABLE": "YES",
|
|
140
|
+
"DATA_TYPE": "text",
|
|
141
|
+
"COLUMN_TYPE": "text",
|
|
142
|
+
"CHARACTER_MAXIMUM_LENGTH": "65535",
|
|
143
|
+
"NUMERIC_PRECISION": null,
|
|
144
|
+
"NUMERIC_SCALE": null,
|
|
145
|
+
"COLUMN_DEFAULT": null,
|
|
146
|
+
"EXTRA": "",
|
|
147
|
+
"GENERATION_EXPRESSION": ""
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"TABLE_NAME": "memberships",
|
|
151
|
+
"ORDINAL_POSITION": "1",
|
|
152
|
+
"COLUMN_NAME": "tenant_id",
|
|
153
|
+
"IS_NULLABLE": "NO",
|
|
154
|
+
"DATA_TYPE": "int",
|
|
155
|
+
"COLUMN_TYPE": "int",
|
|
156
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
157
|
+
"NUMERIC_PRECISION": "10",
|
|
158
|
+
"NUMERIC_SCALE": "0",
|
|
159
|
+
"COLUMN_DEFAULT": null,
|
|
160
|
+
"EXTRA": "",
|
|
161
|
+
"GENERATION_EXPRESSION": ""
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"TABLE_NAME": "memberships",
|
|
165
|
+
"ORDINAL_POSITION": "2",
|
|
166
|
+
"COLUMN_NAME": "user_id",
|
|
167
|
+
"IS_NULLABLE": "NO",
|
|
168
|
+
"DATA_TYPE": "int",
|
|
169
|
+
"COLUMN_TYPE": "int",
|
|
170
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
171
|
+
"NUMERIC_PRECISION": "10",
|
|
172
|
+
"NUMERIC_SCALE": "0",
|
|
173
|
+
"COLUMN_DEFAULT": null,
|
|
174
|
+
"EXTRA": "",
|
|
175
|
+
"GENERATION_EXPRESSION": ""
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"TABLE_NAME": "memberships",
|
|
179
|
+
"ORDINAL_POSITION": "3",
|
|
180
|
+
"COLUMN_NAME": "label",
|
|
181
|
+
"IS_NULLABLE": "YES",
|
|
182
|
+
"DATA_TYPE": "varchar",
|
|
183
|
+
"COLUMN_TYPE": "varchar(50)",
|
|
184
|
+
"CHARACTER_MAXIMUM_LENGTH": "50",
|
|
185
|
+
"NUMERIC_PRECISION": null,
|
|
186
|
+
"NUMERIC_SCALE": null,
|
|
187
|
+
"COLUMN_DEFAULT": null,
|
|
188
|
+
"EXTRA": "",
|
|
189
|
+
"GENERATION_EXPRESSION": ""
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"TABLE_NAME": "posts",
|
|
193
|
+
"ORDINAL_POSITION": "1",
|
|
194
|
+
"COLUMN_NAME": "id",
|
|
195
|
+
"IS_NULLABLE": "NO",
|
|
196
|
+
"DATA_TYPE": "int",
|
|
197
|
+
"COLUMN_TYPE": "int",
|
|
198
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
199
|
+
"NUMERIC_PRECISION": "10",
|
|
200
|
+
"NUMERIC_SCALE": "0",
|
|
201
|
+
"COLUMN_DEFAULT": null,
|
|
202
|
+
"EXTRA": "auto_increment",
|
|
203
|
+
"GENERATION_EXPRESSION": ""
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"TABLE_NAME": "posts",
|
|
207
|
+
"ORDINAL_POSITION": "2",
|
|
208
|
+
"COLUMN_NAME": "account_id",
|
|
209
|
+
"IS_NULLABLE": "NO",
|
|
210
|
+
"DATA_TYPE": "bigint",
|
|
211
|
+
"COLUMN_TYPE": "bigint",
|
|
212
|
+
"CHARACTER_MAXIMUM_LENGTH": null,
|
|
213
|
+
"NUMERIC_PRECISION": "19",
|
|
214
|
+
"NUMERIC_SCALE": "0",
|
|
215
|
+
"COLUMN_DEFAULT": null,
|
|
216
|
+
"EXTRA": "",
|
|
217
|
+
"GENERATION_EXPRESSION": ""
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"TABLE_NAME": "posts",
|
|
221
|
+
"ORDINAL_POSITION": "3",
|
|
222
|
+
"COLUMN_NAME": "slug",
|
|
223
|
+
"IS_NULLABLE": "NO",
|
|
224
|
+
"DATA_TYPE": "varchar",
|
|
225
|
+
"COLUMN_TYPE": "varchar(120)",
|
|
226
|
+
"CHARACTER_MAXIMUM_LENGTH": "120",
|
|
227
|
+
"NUMERIC_PRECISION": null,
|
|
228
|
+
"NUMERIC_SCALE": null,
|
|
229
|
+
"COLUMN_DEFAULT": null,
|
|
230
|
+
"EXTRA": "",
|
|
231
|
+
"GENERATION_EXPRESSION": ""
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"TABLE_NAME": "posts",
|
|
235
|
+
"ORDINAL_POSITION": "4",
|
|
236
|
+
"COLUMN_NAME": "body",
|
|
237
|
+
"IS_NULLABLE": "YES",
|
|
238
|
+
"DATA_TYPE": "longtext",
|
|
239
|
+
"COLUMN_TYPE": "longtext",
|
|
240
|
+
"CHARACTER_MAXIMUM_LENGTH": "4294967295",
|
|
241
|
+
"NUMERIC_PRECISION": null,
|
|
242
|
+
"NUMERIC_SCALE": null,
|
|
243
|
+
"COLUMN_DEFAULT": null,
|
|
244
|
+
"EXTRA": "",
|
|
245
|
+
"GENERATION_EXPRESSION": ""
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
"statistics": [
|
|
249
|
+
{
|
|
250
|
+
"TABLE_NAME": "accounts",
|
|
251
|
+
"INDEX_NAME": "accounts_email_uq",
|
|
252
|
+
"NON_UNIQUE": "0",
|
|
253
|
+
"SEQ_IN_INDEX": "1",
|
|
254
|
+
"COLUMN_NAME": "email",
|
|
255
|
+
"EXPRESSION": null,
|
|
256
|
+
"INDEX_TYPE": "BTREE"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"TABLE_NAME": "accounts",
|
|
260
|
+
"INDEX_NAME": "PRIMARY",
|
|
261
|
+
"NON_UNIQUE": "0",
|
|
262
|
+
"SEQ_IN_INDEX": "1",
|
|
263
|
+
"COLUMN_NAME": "id",
|
|
264
|
+
"EXPRESSION": null,
|
|
265
|
+
"INDEX_TYPE": "BTREE"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"TABLE_NAME": "memberships",
|
|
269
|
+
"INDEX_NAME": "PRIMARY",
|
|
270
|
+
"NON_UNIQUE": "0",
|
|
271
|
+
"SEQ_IN_INDEX": "1",
|
|
272
|
+
"COLUMN_NAME": "user_id",
|
|
273
|
+
"EXPRESSION": null,
|
|
274
|
+
"INDEX_TYPE": "BTREE"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"TABLE_NAME": "memberships",
|
|
278
|
+
"INDEX_NAME": "PRIMARY",
|
|
279
|
+
"NON_UNIQUE": "0",
|
|
280
|
+
"SEQ_IN_INDEX": "2",
|
|
281
|
+
"COLUMN_NAME": "tenant_id",
|
|
282
|
+
"EXPRESSION": null,
|
|
283
|
+
"INDEX_TYPE": "BTREE"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"TABLE_NAME": "posts",
|
|
287
|
+
"INDEX_NAME": "posts_account_idx",
|
|
288
|
+
"NON_UNIQUE": "1",
|
|
289
|
+
"SEQ_IN_INDEX": "1",
|
|
290
|
+
"COLUMN_NAME": "account_id",
|
|
291
|
+
"EXPRESSION": null,
|
|
292
|
+
"INDEX_TYPE": "BTREE"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"TABLE_NAME": "posts",
|
|
296
|
+
"INDEX_NAME": "PRIMARY",
|
|
297
|
+
"NON_UNIQUE": "0",
|
|
298
|
+
"SEQ_IN_INDEX": "1",
|
|
299
|
+
"COLUMN_NAME": "id",
|
|
300
|
+
"EXPRESSION": null,
|
|
301
|
+
"INDEX_TYPE": "BTREE"
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
"keyColumnUsage": [
|
|
305
|
+
{
|
|
306
|
+
"TABLE_NAME": "accounts",
|
|
307
|
+
"CONSTRAINT_NAME": "accounts_email_uq",
|
|
308
|
+
"ORDINAL_POSITION": "1",
|
|
309
|
+
"POSITION_IN_UNIQUE_CONSTRAINT": null,
|
|
310
|
+
"COLUMN_NAME": "email",
|
|
311
|
+
"REFERENCED_TABLE_NAME": null,
|
|
312
|
+
"REFERENCED_COLUMN_NAME": null
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"TABLE_NAME": "accounts",
|
|
316
|
+
"CONSTRAINT_NAME": "PRIMARY",
|
|
317
|
+
"ORDINAL_POSITION": "1",
|
|
318
|
+
"POSITION_IN_UNIQUE_CONSTRAINT": null,
|
|
319
|
+
"COLUMN_NAME": "id",
|
|
320
|
+
"REFERENCED_TABLE_NAME": null,
|
|
321
|
+
"REFERENCED_COLUMN_NAME": null
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"TABLE_NAME": "memberships",
|
|
325
|
+
"CONSTRAINT_NAME": "PRIMARY",
|
|
326
|
+
"ORDINAL_POSITION": "1",
|
|
327
|
+
"POSITION_IN_UNIQUE_CONSTRAINT": null,
|
|
328
|
+
"COLUMN_NAME": "user_id",
|
|
329
|
+
"REFERENCED_TABLE_NAME": null,
|
|
330
|
+
"REFERENCED_COLUMN_NAME": null
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"TABLE_NAME": "memberships",
|
|
334
|
+
"CONSTRAINT_NAME": "PRIMARY",
|
|
335
|
+
"ORDINAL_POSITION": "2",
|
|
336
|
+
"POSITION_IN_UNIQUE_CONSTRAINT": null,
|
|
337
|
+
"COLUMN_NAME": "tenant_id",
|
|
338
|
+
"REFERENCED_TABLE_NAME": null,
|
|
339
|
+
"REFERENCED_COLUMN_NAME": null
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"TABLE_NAME": "posts",
|
|
343
|
+
"CONSTRAINT_NAME": "posts_account_fkey",
|
|
344
|
+
"ORDINAL_POSITION": "1",
|
|
345
|
+
"POSITION_IN_UNIQUE_CONSTRAINT": "1",
|
|
346
|
+
"COLUMN_NAME": "account_id",
|
|
347
|
+
"REFERENCED_TABLE_NAME": "accounts",
|
|
348
|
+
"REFERENCED_COLUMN_NAME": "id"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"TABLE_NAME": "posts",
|
|
352
|
+
"CONSTRAINT_NAME": "PRIMARY",
|
|
353
|
+
"ORDINAL_POSITION": "1",
|
|
354
|
+
"POSITION_IN_UNIQUE_CONSTRAINT": null,
|
|
355
|
+
"COLUMN_NAME": "id",
|
|
356
|
+
"REFERENCED_TABLE_NAME": null,
|
|
357
|
+
"REFERENCED_COLUMN_NAME": null
|
|
358
|
+
}
|
|
359
|
+
],
|
|
360
|
+
"referentialConstraints": [
|
|
361
|
+
{
|
|
362
|
+
"TABLE_NAME": "posts",
|
|
363
|
+
"CONSTRAINT_NAME": "posts_account_fkey",
|
|
364
|
+
"UNIQUE_CONSTRAINT_NAME": "PRIMARY",
|
|
365
|
+
"MATCH_OPTION": "NONE",
|
|
366
|
+
"UPDATE_RULE": "RESTRICT",
|
|
367
|
+
"DELETE_RULE": "CASCADE"
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
}
|
|
371
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { type CatalogSelection, type CatalogWarning, type CompiledQuery, type ReferentialAction } from '@zmdb/sql';
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
type CatalogColumnSnapshot,
|
|
5
|
+
type CatalogForeignKeySnapshot,
|
|
6
|
+
type CatalogIndexColumn,
|
|
7
|
+
type CatalogIndexSnapshot,
|
|
8
|
+
type CatalogSchemaSnapshot,
|
|
9
|
+
type CatalogSelection,
|
|
10
|
+
type CatalogTableSnapshot,
|
|
11
|
+
type CatalogWarning,
|
|
12
|
+
type IntrospectionDriver,
|
|
13
|
+
type Introspector,
|
|
14
|
+
type IntrospectOptions,
|
|
15
|
+
type ReferentialAction,
|
|
16
|
+
} from '@zmdb/sql';
|
|
17
|
+
export { normalizeDriftSnapshot } from './drift.js';
|
|
18
|
+
|
|
19
|
+
export class CatalogRowError extends TypeError {
|
|
20
|
+
readonly catalog: string;
|
|
21
|
+
readonly row: number;
|
|
22
|
+
readonly field: string;
|
|
23
|
+
readonly expected: string;
|
|
24
|
+
|
|
25
|
+
constructor(catalog: string, row: number, field: string, expected: string, value: unknown) {
|
|
26
|
+
super(`${catalog} row ${String(row)} field "${field}" must be ${expected}; received ${describeValue(value)}`);
|
|
27
|
+
this.name = 'CatalogRowError';
|
|
28
|
+
this.catalog = catalog;
|
|
29
|
+
this.row = row;
|
|
30
|
+
this.field = field;
|
|
31
|
+
this.expected = expected;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function describeValue(value: unknown): string {
|
|
36
|
+
if (value === null) return 'null';
|
|
37
|
+
if (Array.isArray(value)) return 'an array';
|
|
38
|
+
return typeof value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function valueAt(row: Readonly<Record<string, unknown>>, field: string): unknown {
|
|
42
|
+
return Reflect.get(row, field);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function textField(
|
|
46
|
+
row: Readonly<Record<string, unknown>>,
|
|
47
|
+
field: string,
|
|
48
|
+
catalog: string,
|
|
49
|
+
index: number,
|
|
50
|
+
): string {
|
|
51
|
+
const value = valueAt(row, field);
|
|
52
|
+
if (typeof value !== 'string') throw new CatalogRowError(catalog, index, field, 'a string', value);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function nullableTextField(
|
|
57
|
+
row: Readonly<Record<string, unknown>>,
|
|
58
|
+
field: string,
|
|
59
|
+
catalog: string,
|
|
60
|
+
index: number,
|
|
61
|
+
): string | null {
|
|
62
|
+
const value = valueAt(row, field);
|
|
63
|
+
if (value === null) return null;
|
|
64
|
+
if (typeof value !== 'string') {
|
|
65
|
+
throw new CatalogRowError(catalog, index, field, 'a string or null', value);
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function integerField(
|
|
71
|
+
row: Readonly<Record<string, unknown>>,
|
|
72
|
+
field: string,
|
|
73
|
+
catalog: string,
|
|
74
|
+
index: number,
|
|
75
|
+
): number {
|
|
76
|
+
const value = valueAt(row, field);
|
|
77
|
+
if (typeof value === 'number' && Number.isSafeInteger(value)) return value;
|
|
78
|
+
if (typeof value === 'string' && /^-?\d+$/.test(value)) {
|
|
79
|
+
const parsed = Number(value);
|
|
80
|
+
if (Number.isSafeInteger(parsed)) return parsed;
|
|
81
|
+
}
|
|
82
|
+
throw new CatalogRowError(catalog, index, field, 'a safe integer or decimal integer string', value);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function nullableIntegerField(
|
|
86
|
+
row: Readonly<Record<string, unknown>>,
|
|
87
|
+
field: string,
|
|
88
|
+
catalog: string,
|
|
89
|
+
index: number,
|
|
90
|
+
): number | null {
|
|
91
|
+
const value = valueAt(row, field);
|
|
92
|
+
if (value === null) return null;
|
|
93
|
+
return integerField(row, field, catalog, index);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function flagField(
|
|
97
|
+
row: Readonly<Record<string, unknown>>,
|
|
98
|
+
field: string,
|
|
99
|
+
catalog: string,
|
|
100
|
+
index: number,
|
|
101
|
+
): boolean {
|
|
102
|
+
const value = integerField(row, field, catalog, index);
|
|
103
|
+
if (value === 0) return false;
|
|
104
|
+
if (value === 1) return true;
|
|
105
|
+
throw new CatalogRowError(catalog, index, field, '0 or 1', valueAt(row, field));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function booleanField(
|
|
109
|
+
row: Readonly<Record<string, unknown>>,
|
|
110
|
+
field: string,
|
|
111
|
+
catalog: string,
|
|
112
|
+
index: number,
|
|
113
|
+
): boolean {
|
|
114
|
+
const value = valueAt(row, field);
|
|
115
|
+
if (typeof value === 'boolean') return value;
|
|
116
|
+
throw new CatalogRowError(catalog, index, field, 'a boolean', value);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function query(text: string, parameters: readonly unknown[] = []): CompiledQuery {
|
|
120
|
+
return { text, parameters };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function globExpression(glob: string): RegExp {
|
|
124
|
+
let source = '^';
|
|
125
|
+
for (const character of glob) {
|
|
126
|
+
if (character === '*') source += '.*';
|
|
127
|
+
else if (character === '?') source += '.';
|
|
128
|
+
else source += character.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
|
|
129
|
+
}
|
|
130
|
+
return new RegExp(`${source}$`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function matchesAny(name: string, globs: readonly string[]): boolean {
|
|
134
|
+
return globs.some(glob => globExpression(glob).test(name));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function tableSelected(name: string, selection: CatalogSelection): boolean {
|
|
138
|
+
const include = selection.include;
|
|
139
|
+
if (include !== undefined && include.length > 0 && !matchesAny(name, include)) return false;
|
|
140
|
+
const exclude = selection.exclude ?? ['_zmdb_migrations'];
|
|
141
|
+
return !matchesAny(name, exclude);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function action(value: string, catalog: string, row: number, field: string): ReferentialAction {
|
|
145
|
+
const normalized = value.toLowerCase().replaceAll('_', ' ');
|
|
146
|
+
switch (normalized) {
|
|
147
|
+
case 'no action':
|
|
148
|
+
case 'restrict':
|
|
149
|
+
case 'cascade':
|
|
150
|
+
case 'set null':
|
|
151
|
+
case 'set default':
|
|
152
|
+
return normalized;
|
|
153
|
+
default:
|
|
154
|
+
throw new CatalogRowError(catalog, row, field, 'a referential action', value);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function deterministicForeignKeyName(table: string, columns: readonly string[]): string {
|
|
159
|
+
return `${table}_${columns.join('_')}_fkey`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function sortByName<T extends { readonly name: string }>(values: readonly T[]): readonly T[] {
|
|
163
|
+
return values.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function sortWarnings(values: readonly CatalogWarning[]): readonly CatalogWarning[] {
|
|
167
|
+
return values.toSorted(
|
|
168
|
+
(left, right) =>
|
|
169
|
+
left.table.localeCompare(right.table) ||
|
|
170
|
+
(left.column ?? '').localeCompare(right.column ?? '') ||
|
|
171
|
+
left.reason.localeCompare(right.reason),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function splitSqlList(value: string): readonly string[] {
|
|
176
|
+
const parts: string[] = [];
|
|
177
|
+
let start = 0;
|
|
178
|
+
let depth = 0;
|
|
179
|
+
let quote: "'" | '"' | '`' | undefined;
|
|
180
|
+
|
|
181
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
182
|
+
const character = value[index];
|
|
183
|
+
if (quote !== undefined) {
|
|
184
|
+
if (character === quote) {
|
|
185
|
+
if (value[index + 1] === quote) index += 1;
|
|
186
|
+
else quote = undefined;
|
|
187
|
+
}
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (character === "'" || character === '"' || character === '`') {
|
|
191
|
+
quote = character;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (character === '(') depth += 1;
|
|
195
|
+
else if (character === ')') depth -= 1;
|
|
196
|
+
else if (character === ',' && depth === 0) {
|
|
197
|
+
parts.push(value.slice(start, index).trim());
|
|
198
|
+
start = index + 1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
parts.push(value.slice(start).trim());
|
|
202
|
+
return parts;
|
|
203
|
+
}
|