@jskit-ai/database-runtime-mysql 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/database-runtime-mysql",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.32",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
options: {
|
|
7
7
|
"db-host": {
|
|
@@ -91,7 +91,7 @@ export default Object.freeze({
|
|
|
91
91
|
mutations: {
|
|
92
92
|
dependencies: {
|
|
93
93
|
runtime: {
|
|
94
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
94
|
+
"@jskit-ai/database-runtime": "0.1.33",
|
|
95
95
|
"mysql2": "^3.11.2"
|
|
96
96
|
},
|
|
97
97
|
dev: {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/database-runtime-mysql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
"./shared/dialect": "./src/shared/dialect.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
15
|
+
"@jskit-ai/database-runtime": "0.1.33"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -201,6 +201,8 @@ function normalizeColumn(row = {}) {
|
|
|
201
201
|
numericPrecision: toNullableNumber(row.numericPrecision ?? row.numeric_precision),
|
|
202
202
|
numericScale: toNullableNumber(row.numericScale ?? row.numeric_scale),
|
|
203
203
|
datetimePrecision: toNullableNumber(row.datetimePrecision ?? row.datetime_precision),
|
|
204
|
+
characterSetName: normalizeText(row.characterSetName ?? row.character_set_name),
|
|
205
|
+
collationName: normalizeText(row.collationName ?? row.collation_name),
|
|
204
206
|
ordinalPosition: toNullableNumber(row.ordinalPosition ?? row.ordinal_position),
|
|
205
207
|
enumValues
|
|
206
208
|
});
|
|
@@ -228,9 +230,11 @@ function normalizeIndexes(rows = []) {
|
|
|
228
230
|
|
|
229
231
|
const seqInIndex = toNullableNumber(row.seqInIndex ?? row.seq_in_index) || 0;
|
|
230
232
|
const nonUnique = toBoolean(row.nonUnique ?? row.non_unique);
|
|
233
|
+
const indexType = normalizeText(row.indexType || row.index_type).toUpperCase();
|
|
231
234
|
const existing = byName.get(indexName) || {
|
|
232
235
|
name: indexName,
|
|
233
236
|
unique: !nonUnique,
|
|
237
|
+
indexType,
|
|
234
238
|
columns: []
|
|
235
239
|
};
|
|
236
240
|
existing.columns.push({
|
|
@@ -246,6 +250,7 @@ function normalizeIndexes(rows = []) {
|
|
|
246
250
|
Object.freeze({
|
|
247
251
|
name: index.name,
|
|
248
252
|
unique: index.unique,
|
|
253
|
+
indexType: index.indexType,
|
|
249
254
|
columns: Object.freeze(
|
|
250
255
|
index.columns
|
|
251
256
|
.sort((left, right) => left.order - right.order)
|
|
@@ -312,6 +317,26 @@ function normalizeForeignKeys(rows = []) {
|
|
|
312
317
|
);
|
|
313
318
|
}
|
|
314
319
|
|
|
320
|
+
function normalizeCheckConstraints(rows = []) {
|
|
321
|
+
return Object.freeze(
|
|
322
|
+
(Array.isArray(rows) ? rows : [])
|
|
323
|
+
.map((row) => {
|
|
324
|
+
const name = normalizeText(row.constraintName || row.constraint_name);
|
|
325
|
+
const clause = normalizeText(row.checkClause || row.check_clause);
|
|
326
|
+
if (!name || !clause) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return Object.freeze({
|
|
331
|
+
name,
|
|
332
|
+
clause
|
|
333
|
+
});
|
|
334
|
+
})
|
|
335
|
+
.filter(Boolean)
|
|
336
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
315
340
|
function requireIdColumn(columns, idColumn) {
|
|
316
341
|
const normalizedIdColumn = normalizeText(idColumn) || "id";
|
|
317
342
|
const idSpec = columns.find((column) => column.name === normalizedIdColumn) || null;
|
|
@@ -351,6 +376,20 @@ async function introspectCrudTableSnapshot(knex, { tableName = "", idColumn = "i
|
|
|
351
376
|
const schemaRows = normalizeRows(await knex.raw("SELECT DATABASE() AS schemaName"));
|
|
352
377
|
const schemaName = normalizeDbSchemaName(schemaRows);
|
|
353
378
|
|
|
379
|
+
const tableRows = normalizeRows(
|
|
380
|
+
await knex.raw(
|
|
381
|
+
`
|
|
382
|
+
SELECT
|
|
383
|
+
t.table_collation AS tableCollation
|
|
384
|
+
FROM information_schema.tables t
|
|
385
|
+
WHERE t.table_schema = ?
|
|
386
|
+
AND t.table_name = ?
|
|
387
|
+
LIMIT 1
|
|
388
|
+
`,
|
|
389
|
+
[schemaName, resolvedTableName]
|
|
390
|
+
)
|
|
391
|
+
);
|
|
392
|
+
|
|
354
393
|
const columnRows = normalizeRows(
|
|
355
394
|
await knex.raw(
|
|
356
395
|
`
|
|
@@ -362,6 +401,8 @@ async function introspectCrudTableSnapshot(knex, { tableName = "", idColumn = "i
|
|
|
362
401
|
c.column_default AS columnDefault,
|
|
363
402
|
c.extra AS extra,
|
|
364
403
|
c.character_maximum_length AS characterMaximumLength,
|
|
404
|
+
c.character_set_name AS characterSetName,
|
|
405
|
+
c.collation_name AS collationName,
|
|
365
406
|
c.numeric_precision AS numericPrecision,
|
|
366
407
|
c.numeric_scale AS numericScale,
|
|
367
408
|
c.datetime_precision AS datetimePrecision,
|
|
@@ -404,6 +445,7 @@ async function introspectCrudTableSnapshot(knex, { tableName = "", idColumn = "i
|
|
|
404
445
|
SELECT
|
|
405
446
|
s.index_name AS indexName,
|
|
406
447
|
s.non_unique AS nonUnique,
|
|
448
|
+
s.index_type AS indexType,
|
|
407
449
|
s.column_name AS columnName,
|
|
408
450
|
s.seq_in_index AS seqInIndex
|
|
409
451
|
FROM information_schema.statistics s
|
|
@@ -440,22 +482,44 @@ async function introspectCrudTableSnapshot(knex, { tableName = "", idColumn = "i
|
|
|
440
482
|
)
|
|
441
483
|
);
|
|
442
484
|
|
|
485
|
+
const checkConstraintRows = normalizeRows(
|
|
486
|
+
await knex.raw(
|
|
487
|
+
`
|
|
488
|
+
SELECT
|
|
489
|
+
tc.constraint_name AS constraintName,
|
|
490
|
+
cc.check_clause AS checkClause
|
|
491
|
+
FROM information_schema.table_constraints tc
|
|
492
|
+
JOIN information_schema.check_constraints cc
|
|
493
|
+
ON cc.constraint_schema = tc.constraint_schema
|
|
494
|
+
AND cc.constraint_name = tc.constraint_name
|
|
495
|
+
WHERE tc.table_schema = ?
|
|
496
|
+
AND tc.table_name = ?
|
|
497
|
+
AND tc.constraint_type = 'CHECK'
|
|
498
|
+
ORDER BY tc.constraint_name ASC
|
|
499
|
+
`,
|
|
500
|
+
[schemaName, resolvedTableName]
|
|
501
|
+
)
|
|
502
|
+
);
|
|
503
|
+
|
|
443
504
|
const columns = Object.freeze(columnRows.map((row) => normalizeColumn(row)));
|
|
444
505
|
const resolvedIdColumn = requireIdColumn(columns, idColumn);
|
|
445
506
|
const primaryKeyColumns = normalizePrimaryKeyColumns(primaryRows);
|
|
446
507
|
requirePrimaryKeyContainsId(primaryKeyColumns, resolvedIdColumn);
|
|
508
|
+
const firstTableRow = Array.isArray(tableRows) ? tableRows[0] : null;
|
|
447
509
|
|
|
448
510
|
const snapshot = Object.freeze({
|
|
449
511
|
dialect: "mysql2",
|
|
450
512
|
schemaName,
|
|
451
513
|
tableName: resolvedTableName,
|
|
514
|
+
tableCollation: normalizeText(firstTableRow?.tableCollation || firstTableRow?.table_collation),
|
|
452
515
|
idColumn: resolvedIdColumn,
|
|
453
516
|
primaryKeyColumns,
|
|
454
|
-
|
|
455
|
-
|
|
517
|
+
hasWorkspaceIdColumn: columns.some((column) => column.name === "workspace_id"),
|
|
518
|
+
hasUserIdColumn: columns.some((column) => column.name === "user_id"),
|
|
456
519
|
columns,
|
|
457
520
|
indexes: normalizeIndexes(indexRows),
|
|
458
|
-
foreignKeys: normalizeForeignKeys(foreignKeyRows)
|
|
521
|
+
foreignKeys: normalizeForeignKeys(foreignKeyRows),
|
|
522
|
+
checkConstraints: normalizeCheckConstraints(checkConstraintRows)
|
|
459
523
|
});
|
|
460
524
|
|
|
461
525
|
return snapshot;
|
|
@@ -5,10 +5,12 @@ import { introspectCrudTableSnapshot } from "../src/shared/introspectCrudTable.j
|
|
|
5
5
|
|
|
6
6
|
function createKnexRawDouble({
|
|
7
7
|
schemaName = "appdb",
|
|
8
|
+
tableCollation = "utf8mb4_general_ci",
|
|
8
9
|
columns = [],
|
|
9
10
|
primaryKeyColumns = [],
|
|
10
11
|
indexes = [],
|
|
11
|
-
foreignKeys = []
|
|
12
|
+
foreignKeys = [],
|
|
13
|
+
checkConstraints = []
|
|
12
14
|
} = {}) {
|
|
13
15
|
const calls = [];
|
|
14
16
|
|
|
@@ -23,9 +25,15 @@ function createKnexRawDouble({
|
|
|
23
25
|
if (normalizedSql.includes("select database() as schemaname")) {
|
|
24
26
|
return [[{ schemaName }], []];
|
|
25
27
|
}
|
|
28
|
+
if (normalizedSql.includes("from information_schema.tables")) {
|
|
29
|
+
return [[{ tableCollation }], []];
|
|
30
|
+
}
|
|
26
31
|
if (normalizedSql.includes("from information_schema.columns")) {
|
|
27
32
|
return [[...columns], []];
|
|
28
33
|
}
|
|
34
|
+
if (normalizedSql.includes("information_schema.check_constraints")) {
|
|
35
|
+
return [[...checkConstraints], []];
|
|
36
|
+
}
|
|
29
37
|
if (normalizedSql.includes("from information_schema.table_constraints")) {
|
|
30
38
|
return [[...primaryKeyColumns], []];
|
|
31
39
|
}
|
|
@@ -57,32 +65,38 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
57
65
|
columnDefault: null,
|
|
58
66
|
extra: "auto_increment",
|
|
59
67
|
characterMaximumLength: null,
|
|
68
|
+
characterSetName: null,
|
|
69
|
+
collationName: null,
|
|
60
70
|
numericPrecision: 10,
|
|
61
71
|
numericScale: 0,
|
|
62
72
|
datetimePrecision: null,
|
|
63
73
|
ordinalPosition: 1
|
|
64
74
|
},
|
|
65
75
|
{
|
|
66
|
-
columnName: "
|
|
76
|
+
columnName: "workspace_id",
|
|
67
77
|
dataType: "int",
|
|
68
78
|
columnType: "int unsigned",
|
|
69
79
|
isNullable: "YES",
|
|
70
80
|
columnDefault: "NULL",
|
|
71
81
|
extra: "",
|
|
72
82
|
characterMaximumLength: null,
|
|
83
|
+
characterSetName: null,
|
|
84
|
+
collationName: null,
|
|
73
85
|
numericPrecision: 10,
|
|
74
86
|
numericScale: 0,
|
|
75
87
|
datetimePrecision: null,
|
|
76
88
|
ordinalPosition: 2
|
|
77
89
|
},
|
|
78
90
|
{
|
|
79
|
-
columnName: "
|
|
91
|
+
columnName: "user_id",
|
|
80
92
|
dataType: "int",
|
|
81
93
|
columnType: "int unsigned",
|
|
82
94
|
isNullable: "YES",
|
|
83
95
|
columnDefault: "NULL",
|
|
84
96
|
extra: "",
|
|
85
97
|
characterMaximumLength: null,
|
|
98
|
+
characterSetName: null,
|
|
99
|
+
collationName: null,
|
|
86
100
|
numericPrecision: 10,
|
|
87
101
|
numericScale: 0,
|
|
88
102
|
datetimePrecision: null,
|
|
@@ -96,6 +110,8 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
96
110
|
columnDefault: null,
|
|
97
111
|
extra: "",
|
|
98
112
|
characterMaximumLength: 160,
|
|
113
|
+
characterSetName: "utf8mb4",
|
|
114
|
+
collationName: "utf8mb4_general_ci",
|
|
99
115
|
numericPrecision: null,
|
|
100
116
|
numericScale: null,
|
|
101
117
|
datetimePrecision: null,
|
|
@@ -109,6 +125,8 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
109
125
|
columnDefault: "0",
|
|
110
126
|
extra: "",
|
|
111
127
|
characterMaximumLength: null,
|
|
128
|
+
characterSetName: null,
|
|
129
|
+
collationName: null,
|
|
112
130
|
numericPrecision: 3,
|
|
113
131
|
numericScale: 0,
|
|
114
132
|
datetimePrecision: null,
|
|
@@ -122,6 +140,8 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
122
140
|
columnDefault: "VIP",
|
|
123
141
|
extra: "",
|
|
124
142
|
characterMaximumLength: null,
|
|
143
|
+
characterSetName: "utf8mb4",
|
|
144
|
+
collationName: "utf8mb4_general_ci",
|
|
125
145
|
numericPrecision: null,
|
|
126
146
|
numericScale: null,
|
|
127
147
|
datetimePrecision: null,
|
|
@@ -135,10 +155,27 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
135
155
|
columnDefault: "CURRENT_TIMESTAMP",
|
|
136
156
|
extra: "",
|
|
137
157
|
characterMaximumLength: null,
|
|
158
|
+
characterSetName: null,
|
|
159
|
+
collationName: null,
|
|
138
160
|
numericPrecision: null,
|
|
139
161
|
numericScale: null,
|
|
140
162
|
datetimePrecision: 0,
|
|
141
163
|
ordinalPosition: 7
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
columnName: "settings_json",
|
|
167
|
+
dataType: "longtext",
|
|
168
|
+
columnType: "longtext",
|
|
169
|
+
isNullable: "YES",
|
|
170
|
+
columnDefault: null,
|
|
171
|
+
extra: "",
|
|
172
|
+
characterMaximumLength: null,
|
|
173
|
+
characterSetName: "utf8mb4",
|
|
174
|
+
collationName: "utf8mb4_bin",
|
|
175
|
+
numericPrecision: null,
|
|
176
|
+
numericScale: null,
|
|
177
|
+
datetimePrecision: null,
|
|
178
|
+
ordinalPosition: 8
|
|
142
179
|
}
|
|
143
180
|
],
|
|
144
181
|
primaryKeyColumns: [{ columnName: "id" }],
|
|
@@ -158,14 +195,20 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
158
195
|
],
|
|
159
196
|
foreignKeys: [
|
|
160
197
|
{
|
|
161
|
-
constraintName: "
|
|
162
|
-
columnName: "
|
|
198
|
+
constraintName: "contacts_workspace_id_foreign",
|
|
199
|
+
columnName: "workspace_id",
|
|
163
200
|
referencedTableName: "workspaces",
|
|
164
201
|
referencedColumnName: "id",
|
|
165
202
|
ordinalPosition: 1,
|
|
166
203
|
updateRule: "CASCADE",
|
|
167
204
|
deleteRule: "SET NULL"
|
|
168
205
|
}
|
|
206
|
+
],
|
|
207
|
+
checkConstraints: [
|
|
208
|
+
{
|
|
209
|
+
constraintName: "settings_json",
|
|
210
|
+
checkClause: "json_valid(`settings_json`)"
|
|
211
|
+
}
|
|
169
212
|
]
|
|
170
213
|
});
|
|
171
214
|
|
|
@@ -176,10 +219,11 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
176
219
|
|
|
177
220
|
assert.equal(snapshot.dialect, "mysql2");
|
|
178
221
|
assert.equal(snapshot.tableName, "contacts");
|
|
222
|
+
assert.equal(snapshot.tableCollation, "utf8mb4_general_ci");
|
|
179
223
|
assert.equal(snapshot.idColumn, "id");
|
|
180
224
|
assert.deepEqual(snapshot.primaryKeyColumns, ["id"]);
|
|
181
|
-
assert.equal(snapshot.
|
|
182
|
-
assert.equal(snapshot.
|
|
225
|
+
assert.equal(snapshot.hasWorkspaceIdColumn, true);
|
|
226
|
+
assert.equal(snapshot.hasUserIdColumn, true);
|
|
183
227
|
|
|
184
228
|
const firstName = snapshot.columns.find((column) => column.name === "first_name");
|
|
185
229
|
assert.ok(firstName);
|
|
@@ -187,9 +231,9 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
187
231
|
assert.equal(firstName.typeKind, "string");
|
|
188
232
|
assert.equal(firstName.maxLength, 160);
|
|
189
233
|
|
|
190
|
-
const
|
|
191
|
-
assert.ok(
|
|
192
|
-
assert.equal(
|
|
234
|
+
const workspaceId = snapshot.columns.find((column) => column.name === "workspace_id");
|
|
235
|
+
assert.ok(workspaceId);
|
|
236
|
+
assert.equal(workspaceId.hasDefault, false);
|
|
193
237
|
|
|
194
238
|
const vip = snapshot.columns.find((column) => column.name === "vip");
|
|
195
239
|
assert.ok(vip);
|
|
@@ -200,32 +244,45 @@ test("introspectCrudTableSnapshot maps MySQL table metadata to normalized snapsh
|
|
|
200
244
|
assert.ok(contactTier);
|
|
201
245
|
assert.deepEqual(contactTier.enumValues, ["VIP", "New"]);
|
|
202
246
|
|
|
247
|
+
const settingsJson = snapshot.columns.find((column) => column.name === "settings_json");
|
|
248
|
+
assert.ok(settingsJson);
|
|
249
|
+
assert.equal(settingsJson.characterSetName, "utf8mb4");
|
|
250
|
+
assert.equal(settingsJson.collationName, "utf8mb4_bin");
|
|
251
|
+
|
|
203
252
|
assert.deepEqual(snapshot.indexes, [
|
|
204
253
|
{
|
|
205
254
|
name: "idx_contacts_first_name",
|
|
206
255
|
unique: false,
|
|
256
|
+
indexType: "",
|
|
207
257
|
columns: ["first_name"]
|
|
208
258
|
},
|
|
209
259
|
{
|
|
210
260
|
name: "uq_contacts_vip",
|
|
211
261
|
unique: true,
|
|
262
|
+
indexType: "",
|
|
212
263
|
columns: ["vip"]
|
|
213
264
|
}
|
|
214
265
|
]);
|
|
215
266
|
assert.deepEqual(snapshot.foreignKeys, [
|
|
216
267
|
{
|
|
217
|
-
name: "
|
|
268
|
+
name: "contacts_workspace_id_foreign",
|
|
218
269
|
referencedTableName: "workspaces",
|
|
219
270
|
updateRule: "CASCADE",
|
|
220
271
|
deleteRule: "SET NULL",
|
|
221
272
|
columns: [
|
|
222
273
|
{
|
|
223
|
-
name: "
|
|
274
|
+
name: "workspace_id",
|
|
224
275
|
referencedName: "id"
|
|
225
276
|
}
|
|
226
277
|
]
|
|
227
278
|
}
|
|
228
279
|
]);
|
|
280
|
+
assert.deepEqual(snapshot.checkConstraints, [
|
|
281
|
+
{
|
|
282
|
+
name: "settings_json",
|
|
283
|
+
clause: "json_valid(`settings_json`)"
|
|
284
|
+
}
|
|
285
|
+
]);
|
|
229
286
|
});
|
|
230
287
|
|
|
231
288
|
test("introspectCrudTableSnapshot rejects unsupported column types", async () => {
|
|
@@ -239,6 +296,8 @@ test("introspectCrudTableSnapshot rejects unsupported column types", async () =>
|
|
|
239
296
|
columnDefault: null,
|
|
240
297
|
extra: "auto_increment",
|
|
241
298
|
characterMaximumLength: null,
|
|
299
|
+
characterSetName: null,
|
|
300
|
+
collationName: null,
|
|
242
301
|
numericPrecision: 10,
|
|
243
302
|
numericScale: 0,
|
|
244
303
|
datetimePrecision: null,
|
|
@@ -252,6 +311,8 @@ test("introspectCrudTableSnapshot rejects unsupported column types", async () =>
|
|
|
252
311
|
columnDefault: null,
|
|
253
312
|
extra: "",
|
|
254
313
|
characterMaximumLength: null,
|
|
314
|
+
characterSetName: null,
|
|
315
|
+
collationName: null,
|
|
255
316
|
numericPrecision: null,
|
|
256
317
|
numericScale: null,
|
|
257
318
|
datetimePrecision: null,
|
|
@@ -278,6 +339,8 @@ test("introspectCrudTableSnapshot rejects when primary key does not include id c
|
|
|
278
339
|
columnDefault: null,
|
|
279
340
|
extra: "auto_increment",
|
|
280
341
|
characterMaximumLength: null,
|
|
342
|
+
characterSetName: null,
|
|
343
|
+
collationName: null,
|
|
281
344
|
numericPrecision: 10,
|
|
282
345
|
numericScale: 0,
|
|
283
346
|
datetimePrecision: null,
|