qubu 0.6.0 → 0.6.2
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/README.md +112 -0
- package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
- package/dist/codegen.d.mts +2 -2
- package/dist/codegen.mjs +180 -90
- package/dist/column-Cyc2CMnG.mjs +116 -0
- package/dist/column-DDRvD7SF.mjs +721 -0
- package/dist/{constraints-YGyNPQ_z.mjs → constraints-CAmi18Uk.mjs} +8 -3
- package/dist/core.d.mts +3 -3
- package/dist/core.mjs +4 -5
- package/dist/diagnostics-I9vVtXkc.mjs +40 -0
- package/dist/diff.d.mts +9 -9
- package/dist/diff.mjs +152 -102
- package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
- package/dist/expressions-_6JF_J77.mjs +130 -0
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +414 -77
- package/dist/introspection/mysql.d.mts +26 -0
- package/dist/introspection/mysql.mjs +1278 -0
- package/dist/introspection/postgres.d.mts +43 -0
- package/dist/introspection/postgres.mjs +1889 -0
- package/dist/introspection/sqlite.d.mts +15 -0
- package/dist/introspection/sqlite.mjs +1172 -0
- package/dist/introspection.d.mts +26 -87
- package/dist/introspection.mjs +3 -4351
- package/dist/mysql.d.mts +3 -3
- package/dist/mysql.mjs +8 -7
- package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
- package/dist/{on-conflict-DZQ85f1t.mjs → on-conflict-B2rFyHGF.mjs} +78 -8
- package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
- package/dist/postgres-hFhd0I9n.mjs +68 -0
- package/dist/postgres.d.mts +3 -3
- package/dist/postgres.mjs +3 -52
- package/dist/registry-BXE_4M9P.mjs +127 -0
- package/dist/{relational-CxnLCqZQ.mjs → relational-CoPBETjI.mjs} +3 -3
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +8 -7
- package/dist/{serialize-BN07IK0v.mjs → serialize-CyobNEx-.mjs} +174 -30
- package/dist/serialize-Du2UPZMt.d.mts +92 -0
- package/dist/snapshot/mysql.d.mts +5 -5
- package/dist/snapshot/mysql.mjs +32 -32
- package/dist/snapshot/postgres.d.mts +5 -5
- package/dist/snapshot/postgres.mjs +25 -25
- package/dist/snapshot/sqlite.d.mts +5 -5
- package/dist/snapshot/sqlite.mjs +30 -30
- package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
- package/dist/snapshot.d.mts +4 -5
- package/dist/snapshot.mjs +3 -4
- package/dist/{source-SqrKWjFJ.mjs → source-DYSUqzvb.mjs} +2 -2
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +10 -10
- package/dist/{table-BwflqeAj.mjs → table-B8zEq0az.mjs} +4 -4
- package/dist/{types-BIJsj2fJ.mjs → types-CYHpSPwj.mjs} +10 -7
- package/dist/{types-CTCqtFlS.d.mts → types-CiMvKi5V.d.mts} +14 -4
- package/dist/{types-C0VkiwpR.d.mts → types-Dqr4o2I1.d.mts} +640 -189
- package/dist/value-CpaUFtjw.mjs +45 -0
- package/dist/vite/ambient.d.ts +2 -0
- package/dist/vite.d.mts +1 -1
- package/dist/vite.mjs +2 -0
- package/docs/dialects-and-execution.md +61 -11
- package/docs/getting-started.md +1 -1
- package/docs/guides/compose-queries.md +22 -0
- package/docs/guides/drizzle.md +11 -11
- package/docs/guides/extensions/typed-expressions.md +5 -3
- package/docs/guides/json.md +95 -2
- package/docs/guides/mutations.md +125 -0
- package/docs/guides/valtio-sync.md +113 -0
- package/docs/index.md +1 -1
- package/docs/migrations/adapters.md +58 -21
- package/docs/migrations/index.md +50 -12
- package/docs/migrations/operations.md +20 -6
- package/docs/migrations/recovery.md +7 -1
- package/docs/query-model/fragments.md +21 -1
- package/docs/reference/introspection-support.md +16 -18
- package/docs/reference/supported-surface.md +23 -11
- package/docs/schema/catalog-model.md +20 -7
- package/docs/schema/code-generation.md +4 -3
- package/docs/schema/diff.md +2 -2
- package/docs/schema/introspection.md +24 -16
- package/docs/schema/snapshots.md +18 -8
- package/docs/schema/storage-and-schema-sql.md +7 -2
- package/docs/sql-semantic-types.md +8 -0
- package/package.json +14 -1
- package/dist/canonical-DMvR9yBe.mjs +0 -972
- package/dist/column-Da37jYSD.mjs +0 -309
- package/dist/column-r1Y4ivwt.mjs +0 -327
- package/dist/complete-types-CY0KbzNw.d.mts +0 -371
- package/dist/index-B2rZf3-2.d.mts +0 -32
- package/dist/json-Db7XRD91.mjs +0 -169
- package/dist/omit-OxV58AwX.mjs +0 -5
- package/dist/registry-BRcUuazJ.mjs +0 -256
- package/dist/serialize-CEIIlWhC.d.mts +0 -66
- package/dist/snapshot-Xam8-q0j.mjs +0 -392
- package/dist/standard-DfcZEVOj.mjs +0 -12
- package/dist/value-D14I_XgL.mjs +0 -29
|
@@ -0,0 +1,1278 @@
|
|
|
1
|
+
import { n as createIntrospectionDiagnostic } from "../diagnostics-I9vVtXkc.mjs";
|
|
2
|
+
//#region src/introspection/mysql.ts
|
|
3
|
+
const mysqlServerQuery = `SELECT VERSION() AS version, @@version_comment AS version_comment`;
|
|
4
|
+
const mysqlTablesQuery = `
|
|
5
|
+
SELECT TABLE_NAME AS table_name, TABLE_TYPE AS table_type,
|
|
6
|
+
ENGINE AS engine, TABLE_COLLATION AS table_collation,
|
|
7
|
+
CREATE_OPTIONS AS create_options, TABLE_COMMENT AS table_comment
|
|
8
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
9
|
+
WHERE TABLE_SCHEMA = ?
|
|
10
|
+
ORDER BY TABLE_NAME
|
|
11
|
+
`;
|
|
12
|
+
const mysqlColumnsQuery = `
|
|
13
|
+
SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name,
|
|
14
|
+
ORDINAL_POSITION AS ordinal_position, COLUMN_TYPE AS column_type,
|
|
15
|
+
DATA_TYPE AS data_type,
|
|
16
|
+
IS_NULLABLE AS is_nullable, COLUMN_DEFAULT AS column_default,
|
|
17
|
+
EXTRA AS extra, GENERATION_EXPRESSION AS generation_expression,
|
|
18
|
+
CHARACTER_SET_NAME AS character_set_name,
|
|
19
|
+
COLLATION_NAME AS collation_name, COLUMN_COMMENT AS column_comment
|
|
20
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
21
|
+
WHERE TABLE_SCHEMA = ?
|
|
22
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
|
23
|
+
`;
|
|
24
|
+
const mysqlKeyUsageQuery = `
|
|
25
|
+
SELECT kcu.TABLE_NAME AS table_name, kcu.CONSTRAINT_NAME AS constraint_name,
|
|
26
|
+
tc.CONSTRAINT_TYPE AS constraint_type, tc.ENFORCED AS enforced,
|
|
27
|
+
kcu.COLUMN_NAME AS column_name, kcu.ORDINAL_POSITION AS ordinal_position,
|
|
28
|
+
kcu.REFERENCED_TABLE_NAME AS referenced_table_name,
|
|
29
|
+
kcu.REFERENCED_COLUMN_NAME AS referenced_column_name,
|
|
30
|
+
rc.UPDATE_RULE AS update_rule, rc.DELETE_RULE AS delete_rule,
|
|
31
|
+
rc.MATCH_OPTION AS match_option
|
|
32
|
+
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
|
|
33
|
+
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
|
34
|
+
ON tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
|
|
35
|
+
AND tc.TABLE_NAME = kcu.TABLE_NAME
|
|
36
|
+
AND tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
|
37
|
+
LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
|
|
38
|
+
ON rc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA
|
|
39
|
+
AND rc.TABLE_NAME = kcu.TABLE_NAME
|
|
40
|
+
AND rc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
|
|
41
|
+
WHERE kcu.CONSTRAINT_SCHEMA = ?
|
|
42
|
+
ORDER BY kcu.TABLE_NAME, kcu.CONSTRAINT_NAME, kcu.ORDINAL_POSITION
|
|
43
|
+
`;
|
|
44
|
+
const mysqlChecksQuery = `
|
|
45
|
+
SELECT tc.TABLE_NAME AS table_name, tc.CONSTRAINT_NAME AS constraint_name,
|
|
46
|
+
tc.ENFORCED AS enforced, cc.CHECK_CLAUSE AS check_clause
|
|
47
|
+
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
|
48
|
+
JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc
|
|
49
|
+
ON cc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
|
50
|
+
AND cc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
|
|
51
|
+
WHERE tc.CONSTRAINT_SCHEMA = ? AND tc.CONSTRAINT_TYPE = 'CHECK'
|
|
52
|
+
ORDER BY tc.TABLE_NAME, tc.CONSTRAINT_NAME
|
|
53
|
+
`;
|
|
54
|
+
const mysqlStatisticsQuery = `
|
|
55
|
+
SELECT TABLE_NAME AS table_name, INDEX_NAME AS index_name,
|
|
56
|
+
NON_UNIQUE AS non_unique, SEQ_IN_INDEX AS seq_in_index,
|
|
57
|
+
COLUMN_NAME AS column_name, COLLATION AS collation,
|
|
58
|
+
INDEX_TYPE AS index_type, EXPRESSION AS expression,
|
|
59
|
+
SUB_PART AS sub_part, IS_VISIBLE AS is_visible,
|
|
60
|
+
COMMENT AS comment, INDEX_COMMENT AS index_comment
|
|
61
|
+
FROM INFORMATION_SCHEMA.STATISTICS
|
|
62
|
+
WHERE TABLE_SCHEMA = ?
|
|
63
|
+
ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX
|
|
64
|
+
`;
|
|
65
|
+
/** View definitions and execution properties for the selected database. */
|
|
66
|
+
const mysqlViewsQuery = `
|
|
67
|
+
SELECT TABLE_NAME AS table_name, VIEW_DEFINITION AS view_definition,
|
|
68
|
+
CHECK_OPTION AS check_option, IS_UPDATABLE AS is_updatable,
|
|
69
|
+
SECURITY_TYPE AS security_type, DEFINER AS definer
|
|
70
|
+
FROM INFORMATION_SCHEMA.VIEWS
|
|
71
|
+
WHERE TABLE_SCHEMA = ?
|
|
72
|
+
ORDER BY TABLE_NAME
|
|
73
|
+
`;
|
|
74
|
+
/** Stored function and procedure declarations for the selected database. */
|
|
75
|
+
const mysqlRoutinesQuery = `
|
|
76
|
+
SELECT ROUTINE_NAME AS routine_name, ROUTINE_TYPE AS routine_type,
|
|
77
|
+
DATA_TYPE AS data_type, DTD_IDENTIFIER AS dtd_identifier,
|
|
78
|
+
ROUTINE_BODY AS routine_body,
|
|
79
|
+
ROUTINE_DEFINITION AS routine_definition,
|
|
80
|
+
EXTERNAL_LANGUAGE AS external_language,
|
|
81
|
+
SQL_DATA_ACCESS AS sql_data_access,
|
|
82
|
+
IS_DETERMINISTIC AS is_deterministic,
|
|
83
|
+
SECURITY_TYPE AS security_type, SQL_MODE AS sql_mode,
|
|
84
|
+
ROUTINE_COMMENT AS routine_comment
|
|
85
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
86
|
+
WHERE ROUTINE_SCHEMA = ?
|
|
87
|
+
ORDER BY ROUTINE_NAME, ROUTINE_TYPE
|
|
88
|
+
`;
|
|
89
|
+
/** Parameter rows, including the ordinal zero return row for functions. */
|
|
90
|
+
const mysqlRoutineParametersQuery = `
|
|
91
|
+
SELECT SPECIFIC_NAME AS routine_name, ORDINAL_POSITION AS ordinal_position,
|
|
92
|
+
PARAMETER_MODE AS parameter_mode, PARAMETER_NAME AS parameter_name,
|
|
93
|
+
DATA_TYPE AS data_type, DTD_IDENTIFIER AS dtd_identifier
|
|
94
|
+
FROM INFORMATION_SCHEMA.PARAMETERS
|
|
95
|
+
WHERE SPECIFIC_SCHEMA = ?
|
|
96
|
+
ORDER BY SPECIFIC_NAME, ORDINAL_POSITION
|
|
97
|
+
`;
|
|
98
|
+
/** Trigger bodies and their table/event metadata. */
|
|
99
|
+
const mysqlTriggersQuery = `
|
|
100
|
+
SELECT TRIGGER_NAME AS trigger_name, EVENT_MANIPULATION AS event_manipulation,
|
|
101
|
+
EVENT_OBJECT_TABLE AS table_name, ACTION_CONDITION AS action_condition,
|
|
102
|
+
ACTION_STATEMENT AS action_statement,
|
|
103
|
+
ACTION_ORIENTATION AS action_orientation,
|
|
104
|
+
ACTION_TIMING AS action_timing, ACTION_ORDER AS action_order,
|
|
105
|
+
DEFINER AS definer, SQL_MODE AS sql_mode
|
|
106
|
+
FROM INFORMATION_SCHEMA.TRIGGERS
|
|
107
|
+
WHERE TRIGGER_SCHEMA = ?
|
|
108
|
+
ORDER BY TRIGGER_NAME, ACTION_ORDER
|
|
109
|
+
`;
|
|
110
|
+
/** Partition and subpartition declarations for tables in the selected database. */
|
|
111
|
+
const mysqlPartitionsQuery = `
|
|
112
|
+
SELECT TABLE_NAME AS table_name, PARTITION_NAME AS partition_name,
|
|
113
|
+
SUBPARTITION_NAME AS subpartition_name,
|
|
114
|
+
PARTITION_ORDINAL_POSITION AS partition_ordinal_position,
|
|
115
|
+
SUBPARTITION_ORDINAL_POSITION AS subpartition_ordinal_position,
|
|
116
|
+
PARTITION_METHOD AS partition_method,
|
|
117
|
+
SUBPARTITION_METHOD AS subpartition_method,
|
|
118
|
+
PARTITION_EXPRESSION AS partition_expression,
|
|
119
|
+
SUBPARTITION_EXPRESSION AS subpartition_expression,
|
|
120
|
+
PARTITION_DESCRIPTION AS partition_description,
|
|
121
|
+
PARTITION_COMMENT AS partition_comment,
|
|
122
|
+
TABLESPACE_NAME AS tablespace_name
|
|
123
|
+
FROM INFORMATION_SCHEMA.PARTITIONS
|
|
124
|
+
WHERE TABLE_SCHEMA = ? AND PARTITION_NAME IS NOT NULL
|
|
125
|
+
ORDER BY TABLE_NAME, PARTITION_ORDINAL_POSITION, SUBPARTITION_ORDINAL_POSITION
|
|
126
|
+
`;
|
|
127
|
+
/** Collations used by tables or columns in the selected database. */
|
|
128
|
+
const mysqlCollationsQuery = `
|
|
129
|
+
SELECT c.COLLATION_NAME AS collation_name,
|
|
130
|
+
c.CHARACTER_SET_NAME AS character_set_name,
|
|
131
|
+
c.ID AS collation_id, c.IS_DEFAULT AS is_default,
|
|
132
|
+
c.IS_COMPILED AS is_compiled, c.SORTLEN AS sort_length,
|
|
133
|
+
c.PAD_ATTRIBUTE AS pad_attribute
|
|
134
|
+
FROM INFORMATION_SCHEMA.COLLATIONS c
|
|
135
|
+
JOIN (
|
|
136
|
+
SELECT DISTINCT TABLE_COLLATION AS collation_name
|
|
137
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
138
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_COLLATION IS NOT NULL
|
|
139
|
+
UNION
|
|
140
|
+
SELECT DISTINCT COLLATION_NAME AS collation_name
|
|
141
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
142
|
+
WHERE TABLE_SCHEMA = ? AND COLLATION_NAME IS NOT NULL
|
|
143
|
+
) used ON used.collation_name = c.COLLATION_NAME
|
|
144
|
+
ORDER BY c.COLLATION_NAME
|
|
145
|
+
`;
|
|
146
|
+
/** Scheduled events are retained as opaque MySQL objects, not migration input. */
|
|
147
|
+
const mysqlEventsQuery = `
|
|
148
|
+
SELECT EVENT_NAME AS event_name, EVENT_TYPE AS event_type,
|
|
149
|
+
STATUS AS status, EVENT_DEFINITION AS event_definition,
|
|
150
|
+
EVENT_BODY AS event_body, EXECUTE_AT AS execute_at,
|
|
151
|
+
INTERVAL_VALUE AS interval_value, INTERVAL_FIELD AS interval_field,
|
|
152
|
+
EVENT_COMMENT AS event_comment, DEFINER AS definer
|
|
153
|
+
FROM INFORMATION_SCHEMA.EVENTS
|
|
154
|
+
WHERE EVENT_SCHEMA = ?
|
|
155
|
+
ORDER BY EVENT_NAME
|
|
156
|
+
`;
|
|
157
|
+
/** Read one MySQL database into the normalized catalog contract. */
|
|
158
|
+
async function readCatalog(connection, options) {
|
|
159
|
+
const diagnostics = [];
|
|
160
|
+
if (connection.dialect !== "mysql") {
|
|
161
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
162
|
+
severity: "error",
|
|
163
|
+
code: "dialect-mismatch",
|
|
164
|
+
message: "MySQL catalog reading requires a MySQL CatalogConnection",
|
|
165
|
+
path: ["connection", "dialect"]
|
|
166
|
+
}));
|
|
167
|
+
return emptyCatalog(options.namespace, diagnostics);
|
|
168
|
+
}
|
|
169
|
+
const server = serverInfo((await query(connection, mysqlServerQuery, [], options, diagnostics, "server"))[0], diagnostics);
|
|
170
|
+
const tableRows = await query(connection, mysqlTablesQuery, [options.namespace], options, diagnostics, "tables");
|
|
171
|
+
const tables = tableRows.filter((row) => text(row.table_type) === "BASE TABLE").map((row) => table(row, options.namespace));
|
|
172
|
+
const deferredObjects = [];
|
|
173
|
+
const opaqueObjects = [];
|
|
174
|
+
const views = [];
|
|
175
|
+
const comments = [];
|
|
176
|
+
const tableByName = new Map(tables.map((item) => [item.physicalName, item]));
|
|
177
|
+
const viewRows = await query(connection, mysqlViewsQuery, [options.namespace], options, diagnostics, "views");
|
|
178
|
+
const viewRowsByName = new Map(viewRows.map((row) => [text(row.table_name) ?? "", row]));
|
|
179
|
+
for (const row of tableRows.filter((currentRow) => text(currentRow.table_type) !== "BASE TABLE")) {
|
|
180
|
+
const kind = text(row.table_type)?.toUpperCase();
|
|
181
|
+
if (kind === "VIEW" || kind === "SYSTEM VIEW") {
|
|
182
|
+
const mapped = viewObject(row, viewRowsByName.get(text(row.table_name) ?? ""), options.namespace, diagnostics);
|
|
183
|
+
if (mapped.kind === "deferred-object") deferredObjects.push(mapped);
|
|
184
|
+
else views.push(mapped);
|
|
185
|
+
} else {
|
|
186
|
+
const mapped = deferred(row, options.namespace);
|
|
187
|
+
deferredObjects.push(mapped);
|
|
188
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
189
|
+
severity: "warning",
|
|
190
|
+
code: "unmodeled-object",
|
|
191
|
+
message: `MySQL object ${mapped.physicalName} (${kind ?? "unknown"}) is retained as a deferred record`,
|
|
192
|
+
path: ["deferredObjects", mapped.physicalName],
|
|
193
|
+
physicalReference: mapped.reference,
|
|
194
|
+
remediation: "Inspect the deferred record before using it as migration input."
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const relationByName = /* @__PURE__ */ new Map();
|
|
199
|
+
for (const currentTable of tables) relationByName.set(currentTable.physicalName, {
|
|
200
|
+
kind: "table",
|
|
201
|
+
id: currentTable.id
|
|
202
|
+
});
|
|
203
|
+
for (const view of views) relationByName.set(view.physicalName, {
|
|
204
|
+
kind: "view",
|
|
205
|
+
id: view.id
|
|
206
|
+
});
|
|
207
|
+
const columnRows = await query(connection, mysqlColumnsQuery, [options.namespace], options, diagnostics, "columns");
|
|
208
|
+
for (const currentTable of tables) currentTable.columns = columnRows.filter((row) => text(row.table_name) === currentTable.physicalName).map((row) => column(row, currentTable, options.namespace));
|
|
209
|
+
for (const view of views) view.columns = columnRows.filter((row) => text(row.table_name) === view.physicalName).map((row) => column(row, view, options.namespace));
|
|
210
|
+
const keyRows = await query(connection, mysqlKeyUsageQuery, [options.namespace], options, diagnostics, "constraints");
|
|
211
|
+
for (const currentTable of tables) currentTable.constraints = constraints(currentTable, keyRows.filter((row) => text(row.table_name) === currentTable.physicalName), tableByName, options.namespace, diagnostics);
|
|
212
|
+
const checkRows = await query(connection, mysqlChecksQuery, [options.namespace], options, diagnostics, "checks");
|
|
213
|
+
const constraintsByTable = /* @__PURE__ */ new Map();
|
|
214
|
+
for (const currentTable of tables) constraintsByTable.set(currentTable.physicalName, [...currentTable.constraints]);
|
|
215
|
+
for (const row of checkRows) {
|
|
216
|
+
const currentTable = tableByName.get(text(row.table_name) ?? "");
|
|
217
|
+
if (!currentTable) continue;
|
|
218
|
+
const physicalName = text(row.constraint_name) ?? `check_${currentTable.physicalName}`;
|
|
219
|
+
const check = {
|
|
220
|
+
kind: "check",
|
|
221
|
+
id: stableId(physicalName),
|
|
222
|
+
identitySource: "physical-name",
|
|
223
|
+
physicalName,
|
|
224
|
+
expression: sql(text(row.check_clause) ?? "true", options.namespace, currentTable, physicalName),
|
|
225
|
+
dialect: text(row.enforced)?.toUpperCase() === "NO" ? {
|
|
226
|
+
dialect: "mysql",
|
|
227
|
+
version: 1,
|
|
228
|
+
data: { enforced: false }
|
|
229
|
+
} : void 0
|
|
230
|
+
};
|
|
231
|
+
constraintsByTable.get(currentTable.physicalName)?.push(check);
|
|
232
|
+
}
|
|
233
|
+
for (const currentTable of tables) currentTable.constraints = constraintsByTable.get(currentTable.physicalName) ?? [];
|
|
234
|
+
const statRows = await query(connection, mysqlStatisticsQuery, [options.namespace], options, diagnostics, "statistics");
|
|
235
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
236
|
+
for (const row of statRows) {
|
|
237
|
+
const key = `${text(row.table_name) ?? ""}\u0000${text(row.index_name) ?? ""}`;
|
|
238
|
+
const group = grouped.get(key) ?? [];
|
|
239
|
+
group.push(row);
|
|
240
|
+
grouped.set(key, group);
|
|
241
|
+
}
|
|
242
|
+
for (const rows of grouped.values()) {
|
|
243
|
+
const currentTable = tableByName.get(text(rows[0].table_name) ?? "");
|
|
244
|
+
if (!currentTable) continue;
|
|
245
|
+
const mappedIndex = mapIndex(rows, currentTable, options.namespace, diagnostics);
|
|
246
|
+
if (mappedIndex) currentTable.indexes = [...currentTable.indexes, mappedIndex];
|
|
247
|
+
}
|
|
248
|
+
const routineRows = await query(connection, mysqlRoutinesQuery, [options.namespace], options, diagnostics, "routines");
|
|
249
|
+
const routineParameterRows = await query(connection, mysqlRoutineParametersQuery, [options.namespace], options, diagnostics, "routine-parameters");
|
|
250
|
+
const routineParameters = /* @__PURE__ */ new Map();
|
|
251
|
+
for (const row of routineParameterRows) {
|
|
252
|
+
const name = text(row.routine_name) ?? "unknown";
|
|
253
|
+
const group = routineParameters.get(name) ?? [];
|
|
254
|
+
group.push(row);
|
|
255
|
+
routineParameters.set(name, group);
|
|
256
|
+
}
|
|
257
|
+
const routines = routineRows.map((row) => {
|
|
258
|
+
const routine = routineObject(row, routineParameters.get(text(row.routine_name) ?? "") ?? [], options.namespace);
|
|
259
|
+
if (routine.comment) comments.push(routine.comment);
|
|
260
|
+
relationByName.set(routine.physicalName, {
|
|
261
|
+
kind: "routine",
|
|
262
|
+
id: routine.id
|
|
263
|
+
});
|
|
264
|
+
return routine;
|
|
265
|
+
});
|
|
266
|
+
const triggerRows = await query(connection, mysqlTriggersQuery, [options.namespace], options, diagnostics, "triggers");
|
|
267
|
+
const triggers = [];
|
|
268
|
+
for (const rows of groupRows(triggerRows, (row) => text(row.trigger_name) ?? "unknown")) {
|
|
269
|
+
const mapped = triggerObject(rows, relationByName, options.namespace, diagnostics);
|
|
270
|
+
if (mapped.kind === "trigger") triggers.push(mapped);
|
|
271
|
+
else deferredObjects.push(mapped);
|
|
272
|
+
}
|
|
273
|
+
const partitionRows = await query(connection, mysqlPartitionsQuery, [options.namespace], options, diagnostics, "partitions");
|
|
274
|
+
const partitions = [];
|
|
275
|
+
for (const row of partitionRows) {
|
|
276
|
+
const mapped = partitionObject(row, tableByName, options.namespace, diagnostics);
|
|
277
|
+
if (mapped.kind === "partition") {
|
|
278
|
+
partitions.push(mapped);
|
|
279
|
+
if (mapped.comment) comments.push(mapped.comment);
|
|
280
|
+
} else deferredObjects.push(mapped);
|
|
281
|
+
}
|
|
282
|
+
const collationRows = await query(connection, mysqlCollationsQuery, [options.namespace, options.namespace], options, diagnostics, "collations");
|
|
283
|
+
const usedCollations = new Set([...tableRows.map((row) => text(row.table_collation)), ...columnRows.map((row) => text(row.collation_name))].filter((value) => value !== void 0));
|
|
284
|
+
const collations = collationRows.filter((row) => {
|
|
285
|
+
const name = text(row.collation_name);
|
|
286
|
+
return name !== void 0 && usedCollations.has(name);
|
|
287
|
+
}).map((row) => collationObject(row, options.namespace));
|
|
288
|
+
const eventRows = await query(connection, mysqlEventsQuery, [options.namespace], options, diagnostics, "events");
|
|
289
|
+
for (const row of eventRows) {
|
|
290
|
+
const event = opaqueEvent(row, options.namespace);
|
|
291
|
+
opaqueObjects.push(event);
|
|
292
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
293
|
+
severity: "warning",
|
|
294
|
+
code: "unmodeled-object",
|
|
295
|
+
message: `MySQL event ${event.physicalName} is retained as opaque data`,
|
|
296
|
+
path: ["opaqueObjects", event.physicalName],
|
|
297
|
+
physicalReference: event.reference,
|
|
298
|
+
remediation: "Inspect the event definition before treating it as migration input."
|
|
299
|
+
}));
|
|
300
|
+
const eventComment = text(row.event_comment);
|
|
301
|
+
if (eventComment !== void 0 && eventComment !== "") comments.push(objectComment({
|
|
302
|
+
kind: "opaque-object",
|
|
303
|
+
id: event.id
|
|
304
|
+
}, eventComment, event.reference, options.namespace));
|
|
305
|
+
}
|
|
306
|
+
for (const currentTable of tables) {
|
|
307
|
+
const tableComment = text(tableRows.find((row) => text(row.table_name) === currentTable.physicalName)?.table_comment);
|
|
308
|
+
if (tableComment !== void 0 && tableComment !== "") comments.push(objectComment({
|
|
309
|
+
kind: "table",
|
|
310
|
+
id: currentTable.id
|
|
311
|
+
}, tableComment, currentTable.reference, options.namespace));
|
|
312
|
+
for (const currentColumn of currentTable.columns) {
|
|
313
|
+
const columnComment = text(columnRows.find((row) => text(row.table_name) === currentTable.physicalName && text(row.column_name) === currentColumn.physicalName)?.column_comment);
|
|
314
|
+
if (columnComment !== void 0 && columnComment !== "") comments.push(objectComment({
|
|
315
|
+
kind: "column",
|
|
316
|
+
id: currentColumn.id,
|
|
317
|
+
owner: {
|
|
318
|
+
kind: "table",
|
|
319
|
+
id: currentTable.id
|
|
320
|
+
}
|
|
321
|
+
}, columnComment, currentColumn.reference, options.namespace));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const view of views) {
|
|
325
|
+
const viewComment = text(tableRows.find((row) => text(row.table_name) === view.physicalName)?.table_comment);
|
|
326
|
+
if (viewComment !== void 0 && viewComment !== "") comments.push(objectComment({
|
|
327
|
+
kind: view.kind,
|
|
328
|
+
id: view.id
|
|
329
|
+
}, viewComment, view.reference, options.namespace));
|
|
330
|
+
for (const currentColumn of view.columns) {
|
|
331
|
+
const columnComment = text(columnRows.find((row) => text(row.table_name) === view.physicalName && text(row.column_name) === currentColumn.physicalName)?.column_comment);
|
|
332
|
+
if (columnComment !== void 0 && columnComment !== "") comments.push(objectComment({
|
|
333
|
+
kind: "column",
|
|
334
|
+
id: currentColumn.id,
|
|
335
|
+
owner: {
|
|
336
|
+
kind: view.kind,
|
|
337
|
+
id: view.id
|
|
338
|
+
}
|
|
339
|
+
}, columnComment, currentColumn.reference, options.namespace));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const capabilities = {
|
|
343
|
+
generatedColumns: server.capabilities.generatedColumns,
|
|
344
|
+
identityMetadata: server.capabilities.identityMetadata,
|
|
345
|
+
checkConstraints: server.capabilities.checkConstraints,
|
|
346
|
+
checkConstraintEnforcement: server.capabilities.checkConstraintEnforcement,
|
|
347
|
+
expressionDecompilation: server.capabilities.expressionDecompilation,
|
|
348
|
+
indexExpressions: server.capabilities.indexExpressions,
|
|
349
|
+
indexPredicates: server.capabilities.indexPredicates,
|
|
350
|
+
indexIncludedColumns: server.capabilities.indexIncludedColumns,
|
|
351
|
+
namespaces: server.capabilities.namespaces,
|
|
352
|
+
visibility: server.capabilities.visibility
|
|
353
|
+
};
|
|
354
|
+
const serverCapabilities = {
|
|
355
|
+
...server.capabilities,
|
|
356
|
+
views: true,
|
|
357
|
+
materializedViews: false,
|
|
358
|
+
sequences: false,
|
|
359
|
+
enums: false,
|
|
360
|
+
domains: false,
|
|
361
|
+
collations: true,
|
|
362
|
+
routines: true,
|
|
363
|
+
triggers: true,
|
|
364
|
+
partitions: true,
|
|
365
|
+
policies: false,
|
|
366
|
+
extensions: false,
|
|
367
|
+
comments: true,
|
|
368
|
+
ownership: false,
|
|
369
|
+
scheduledEvents: true,
|
|
370
|
+
tableEngines: true,
|
|
371
|
+
generatedColumnModes: true,
|
|
372
|
+
selectedNamespace: options.namespace,
|
|
373
|
+
productFamily: server.product === "mariadb" ? "mariadb" : "mysql8"
|
|
374
|
+
};
|
|
375
|
+
const namespace = {
|
|
376
|
+
kind: "mysql-database",
|
|
377
|
+
name: options.namespace,
|
|
378
|
+
reference: reference("namespace", options.namespace, options.namespace, "INFORMATION_SCHEMA.SCHEMATA", "SCHEMA_NAME"),
|
|
379
|
+
dialect: extension({
|
|
380
|
+
selectedNamespace: options.namespace,
|
|
381
|
+
views: true,
|
|
382
|
+
materializedViews: false,
|
|
383
|
+
sequences: false,
|
|
384
|
+
routines: true,
|
|
385
|
+
triggers: true,
|
|
386
|
+
partitions: true,
|
|
387
|
+
collations: true,
|
|
388
|
+
comments: true,
|
|
389
|
+
ownership: false,
|
|
390
|
+
policies: false,
|
|
391
|
+
extensions: false
|
|
392
|
+
})
|
|
393
|
+
};
|
|
394
|
+
return Object.freeze({
|
|
395
|
+
dialect: "mysql",
|
|
396
|
+
server: {
|
|
397
|
+
...server,
|
|
398
|
+
capabilities: serverCapabilities
|
|
399
|
+
},
|
|
400
|
+
namespace,
|
|
401
|
+
tables: Object.freeze(tables),
|
|
402
|
+
views: Object.freeze(views),
|
|
403
|
+
collations: Object.freeze(collations),
|
|
404
|
+
routines: Object.freeze(routines),
|
|
405
|
+
triggers: Object.freeze(triggers),
|
|
406
|
+
partitions: Object.freeze(partitions),
|
|
407
|
+
deferredObjects: Object.freeze(deferredObjects),
|
|
408
|
+
opaqueObjects: Object.freeze(opaqueObjects),
|
|
409
|
+
comments: Object.freeze(comments),
|
|
410
|
+
ownership: Object.freeze([]),
|
|
411
|
+
capabilities,
|
|
412
|
+
diagnostics: Object.freeze(diagnostics)
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
async function query(connection, textValue, parameters, options, diagnostics, operation) {
|
|
416
|
+
try {
|
|
417
|
+
return await connection.query({
|
|
418
|
+
text: textValue,
|
|
419
|
+
parameters
|
|
420
|
+
}, { signal: options.signal });
|
|
421
|
+
} catch {
|
|
422
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
423
|
+
severity: "error",
|
|
424
|
+
code: "query-failed",
|
|
425
|
+
message: `MySQL catalog query failed while reading ${operation}`,
|
|
426
|
+
path: [operation],
|
|
427
|
+
remediation: "Check Information Schema permissions and the selected database."
|
|
428
|
+
}));
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function serverInfo(row, diagnostics) {
|
|
433
|
+
const rawVersion = text(row?.version) ?? "unknown";
|
|
434
|
+
const comment = text(row?.version_comment) ?? "";
|
|
435
|
+
const mariadb = /mariadb/i.test(rawVersion) || /mariadb/i.test(comment);
|
|
436
|
+
const parts = rawVersion.match(/(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
437
|
+
const major = parts ? Number(parts[1]) : void 0;
|
|
438
|
+
const minor = parts ? Number(parts[2]) : void 0;
|
|
439
|
+
if (mariadb) diagnostics.push(createIntrospectionDiagnostic({
|
|
440
|
+
severity: "error",
|
|
441
|
+
code: "unsupported-product",
|
|
442
|
+
message: "MariaDB requires a dedicated catalog adapter and is not treated as MySQL",
|
|
443
|
+
path: ["server", "product"]
|
|
444
|
+
}));
|
|
445
|
+
const supported = !mariadb && major === 8 && ((minor ?? 0) > 0 || (minor ?? 0) === 0 && Number(parts?.[3] ?? 0) >= 16);
|
|
446
|
+
if (!supported && !mariadb) diagnostics.push(createIntrospectionDiagnostic({
|
|
447
|
+
severity: "error",
|
|
448
|
+
code: "unsupported-server",
|
|
449
|
+
message: "MySQL introspection requires MySQL 8.0 or newer",
|
|
450
|
+
path: ["server", "version"]
|
|
451
|
+
}));
|
|
452
|
+
return {
|
|
453
|
+
product: mariadb ? "mariadb" : "mysql",
|
|
454
|
+
rawVersion,
|
|
455
|
+
parsedVersion: major === void 0 ? void 0 : {
|
|
456
|
+
major,
|
|
457
|
+
minor,
|
|
458
|
+
patch: parts?.[3] ? Number(parts[3]) : void 0
|
|
459
|
+
},
|
|
460
|
+
capabilities: {
|
|
461
|
+
generatedColumns: supported,
|
|
462
|
+
identityMetadata: supported,
|
|
463
|
+
checkConstraints: supported,
|
|
464
|
+
checkConstraintEnforcement: supported ? "enforced" : "unknown",
|
|
465
|
+
expressionDecompilation: false,
|
|
466
|
+
indexExpressions: supported,
|
|
467
|
+
indexPredicates: false,
|
|
468
|
+
indexIncludedColumns: false,
|
|
469
|
+
namespaces: true,
|
|
470
|
+
visibility: "complete",
|
|
471
|
+
mysql8: supported
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function table(row, namespace) {
|
|
476
|
+
const physicalName = text(row.table_name) ?? "unnamed_table";
|
|
477
|
+
return {
|
|
478
|
+
kind: "table",
|
|
479
|
+
id: stableId(physicalName),
|
|
480
|
+
identitySource: "physical-name",
|
|
481
|
+
physicalName,
|
|
482
|
+
reference: reference("table", physicalName, namespace, "INFORMATION_SCHEMA.TABLES", "TABLE_NAME"),
|
|
483
|
+
columns: [],
|
|
484
|
+
constraints: [],
|
|
485
|
+
indexes: [],
|
|
486
|
+
dialect: extension({
|
|
487
|
+
...text(row.engine) === void 0 ? {} : { engine: text(row.engine) },
|
|
488
|
+
...text(row.table_collation) === void 0 ? {} : { collation: text(row.table_collation) },
|
|
489
|
+
...text(row.create_options) === void 0 ? {} : { createOptions: text(row.create_options) }
|
|
490
|
+
})
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function deferred(row, namespace) {
|
|
494
|
+
const physicalName = text(row.table_name) ?? "unnamed_object";
|
|
495
|
+
return {
|
|
496
|
+
kind: "deferred-object",
|
|
497
|
+
id: stableId(`deferred:${text(row.table_type) ?? "other"}:${physicalName}`),
|
|
498
|
+
identitySource: "physical-name",
|
|
499
|
+
objectKind: text(row.table_type)?.toUpperCase() === "VIEW" || text(row.table_type)?.toUpperCase() === "SYSTEM VIEW" ? "view" : "other",
|
|
500
|
+
physicalName,
|
|
501
|
+
reference: reference("deferred-object", physicalName, namespace, "INFORMATION_SCHEMA.TABLES", "TABLE_NAME"),
|
|
502
|
+
dialect: extension({
|
|
503
|
+
...text(row.table_type) === void 0 ? {} : { tableType: text(row.table_type) },
|
|
504
|
+
...text(row.engine) === void 0 ? {} : { engine: text(row.engine) }
|
|
505
|
+
})
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function column(row, table, namespace) {
|
|
509
|
+
const physicalName = text(row.column_name) ?? "unnamed_column";
|
|
510
|
+
const extra = text(row.extra) ?? "";
|
|
511
|
+
const generationExpression = text(row.generation_expression);
|
|
512
|
+
const generated = generationExpression !== void 0 && (/GENERATED/i.test(extra) || generationExpression !== "") ? {
|
|
513
|
+
kind: "generated",
|
|
514
|
+
mode: /VIRTUAL/i.test(extra) ? "virtual" : /STORED/i.test(extra) ? "stored" : "unknown",
|
|
515
|
+
expression: sql(generationExpression, namespace, table, physicalName)
|
|
516
|
+
} : void 0;
|
|
517
|
+
const identity = /auto_increment/i.test(extra) ? {
|
|
518
|
+
kind: "identity",
|
|
519
|
+
generation: "by-default",
|
|
520
|
+
options: {},
|
|
521
|
+
dialect: {
|
|
522
|
+
dialect: "mysql",
|
|
523
|
+
version: 1,
|
|
524
|
+
data: { autoIncrement: true }
|
|
525
|
+
}
|
|
526
|
+
} : void 0;
|
|
527
|
+
const onUpdateMatch = extra.match(/on update\s+(.+)$/i);
|
|
528
|
+
const defaultValue = mysqlDefault(text(row.column_default), text(row.data_type), namespace, table, physicalName);
|
|
529
|
+
return {
|
|
530
|
+
kind: "column",
|
|
531
|
+
id: stableId(physicalName),
|
|
532
|
+
identitySource: "physical-name",
|
|
533
|
+
physicalName,
|
|
534
|
+
ordinalPosition: number(row.ordinal_position) ?? 0,
|
|
535
|
+
nullable: text(row.is_nullable)?.toUpperCase() !== "NO",
|
|
536
|
+
storage: { nativeType: text(row.column_type) ?? "unknown" },
|
|
537
|
+
default: defaultValue,
|
|
538
|
+
generated,
|
|
539
|
+
identity,
|
|
540
|
+
onUpdate: onUpdateMatch?.[1] ? sql(onUpdateMatch[1], namespace, table, physicalName) : void 0,
|
|
541
|
+
dialect: extension({
|
|
542
|
+
...text(row.data_type) === void 0 ? {} : { dataType: text(row.data_type) },
|
|
543
|
+
...text(row.character_set_name) === void 0 ? {} : { characterSet: text(row.character_set_name) },
|
|
544
|
+
...text(row.collation_name) === void 0 ? {} : { collation: text(row.collation_name) },
|
|
545
|
+
...extra === "" ? {} : { extra }
|
|
546
|
+
}),
|
|
547
|
+
reference: reference("column", physicalName, namespace, "INFORMATION_SCHEMA.COLUMNS", "COLUMN_NAME")
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function constraints(table, rows, tableByName, namespace, diagnostics) {
|
|
551
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
552
|
+
for (const row of rows) {
|
|
553
|
+
const key = text(row.constraint_name) ?? "unknown";
|
|
554
|
+
const group = grouped.get(key) ?? [];
|
|
555
|
+
group.push(row);
|
|
556
|
+
grouped.set(key, group);
|
|
557
|
+
}
|
|
558
|
+
const result = [];
|
|
559
|
+
for (const [physicalName, group] of grouped) {
|
|
560
|
+
const first = group[0];
|
|
561
|
+
const columns = group.sort((left, right) => (number(left.ordinal_position) ?? 0) - (number(right.ordinal_position) ?? 0)).map((row) => text(row.column_name) ?? "unknown");
|
|
562
|
+
const common = {
|
|
563
|
+
id: stableId(physicalName),
|
|
564
|
+
identitySource: "physical-name",
|
|
565
|
+
physicalName,
|
|
566
|
+
reference: reference("constraint", physicalName, namespace, "INFORMATION_SCHEMA.TABLE_CONSTRAINTS", "CONSTRAINT_NAME")
|
|
567
|
+
};
|
|
568
|
+
const type = text(first.constraint_type);
|
|
569
|
+
if (type === "PRIMARY KEY") {
|
|
570
|
+
result.push({
|
|
571
|
+
kind: "primary-key",
|
|
572
|
+
...common,
|
|
573
|
+
columns
|
|
574
|
+
});
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (type === "UNIQUE") {
|
|
578
|
+
result.push({
|
|
579
|
+
kind: "unique",
|
|
580
|
+
...common,
|
|
581
|
+
columns,
|
|
582
|
+
nulls: "distinct"
|
|
583
|
+
});
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
if (type !== "FOREIGN KEY") continue;
|
|
587
|
+
const targetTableName = text(first.referenced_table_name) ?? "unknown";
|
|
588
|
+
if (!tableByName.has(targetTableName)) diagnostics.push(createIntrospectionDiagnostic({
|
|
589
|
+
severity: "error",
|
|
590
|
+
code: "unresolved-reference",
|
|
591
|
+
message: `MySQL foreign key target ${targetTableName} was not found`,
|
|
592
|
+
path: [
|
|
593
|
+
table.id,
|
|
594
|
+
"constraints",
|
|
595
|
+
physicalName
|
|
596
|
+
]
|
|
597
|
+
}));
|
|
598
|
+
result.push({
|
|
599
|
+
kind: "foreign-key",
|
|
600
|
+
...common,
|
|
601
|
+
columns,
|
|
602
|
+
target: {
|
|
603
|
+
table: targetTableName,
|
|
604
|
+
columns: group.map((row) => text(row.referenced_column_name) ?? "unknown")
|
|
605
|
+
},
|
|
606
|
+
onUpdate: action(first.update_rule),
|
|
607
|
+
onDelete: action(first.delete_rule),
|
|
608
|
+
match: match(first.match_option),
|
|
609
|
+
dialect: text(first.enforced)?.toUpperCase() === "NO" ? {
|
|
610
|
+
dialect: "mysql",
|
|
611
|
+
version: 1,
|
|
612
|
+
data: { enforced: false }
|
|
613
|
+
} : void 0
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
return result;
|
|
617
|
+
}
|
|
618
|
+
function mapIndex(rows, table, namespace, diagnostics) {
|
|
619
|
+
const first = rows[0];
|
|
620
|
+
const physicalName = text(first.index_name);
|
|
621
|
+
if (!physicalName) return;
|
|
622
|
+
const terms = [];
|
|
623
|
+
for (const row of rows) {
|
|
624
|
+
const expression = text(row.expression);
|
|
625
|
+
const columnName = text(row.column_name);
|
|
626
|
+
if (!expression && !columnName) {
|
|
627
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
628
|
+
severity: "error",
|
|
629
|
+
code: "unsupported-feature",
|
|
630
|
+
message: `MySQL index ${physicalName} has an unrepresented term`,
|
|
631
|
+
path: [
|
|
632
|
+
table.id,
|
|
633
|
+
"indexes",
|
|
634
|
+
physicalName
|
|
635
|
+
]
|
|
636
|
+
}));
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
terms.push(expression ? {
|
|
640
|
+
kind: "expression",
|
|
641
|
+
expression: sql(expression, namespace, table, physicalName),
|
|
642
|
+
position: number(row.seq_in_index) ?? 0
|
|
643
|
+
} : {
|
|
644
|
+
kind: "column",
|
|
645
|
+
column: columnName,
|
|
646
|
+
position: number(row.seq_in_index) ?? 0,
|
|
647
|
+
direction: text(row.collation) === "D" ? "DESC" : "ASC",
|
|
648
|
+
prefixLength: row.sub_part === null || row.sub_part === void 0 ? void 0 : {
|
|
649
|
+
kind: "literal",
|
|
650
|
+
value: number(row.sub_part) ?? text(row.sub_part) ?? ""
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
if (row.sub_part !== null && row.sub_part !== void 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
654
|
+
severity: "warning",
|
|
655
|
+
code: "lossy-mapping",
|
|
656
|
+
message: `MySQL index prefix length for ${physicalName} is retained only in catalog rows`,
|
|
657
|
+
path: [
|
|
658
|
+
table.id,
|
|
659
|
+
"indexes",
|
|
660
|
+
physicalName
|
|
661
|
+
]
|
|
662
|
+
}));
|
|
663
|
+
}
|
|
664
|
+
return {
|
|
665
|
+
kind: "index",
|
|
666
|
+
id: stableId(physicalName),
|
|
667
|
+
identitySource: "physical-name",
|
|
668
|
+
physicalName,
|
|
669
|
+
unique: number(first.non_unique) === 0,
|
|
670
|
+
terms,
|
|
671
|
+
method: text(first.index_type),
|
|
672
|
+
dialect: extension({
|
|
673
|
+
...text(first.index_type) === void 0 ? {} : { indexType: text(first.index_type) },
|
|
674
|
+
...text(first.is_visible) === void 0 ? {} : { visible: text(first.is_visible).toUpperCase() !== "NO" },
|
|
675
|
+
...text(first.comment) === void 0 ? {} : { comment: text(first.comment) },
|
|
676
|
+
...text(first.index_comment) === void 0 ? {} : { indexComment: text(first.index_comment) }
|
|
677
|
+
}),
|
|
678
|
+
reference: reference("index", physicalName, namespace, "INFORMATION_SCHEMA.STATISTICS", "INDEX_NAME")
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function viewObject(tableRow, viewRow, namespace, diagnostics) {
|
|
682
|
+
const physicalName = text(tableRow.table_name) ?? "unnamed_view";
|
|
683
|
+
const physicalReference = reference("view", physicalName, namespace, "INFORMATION_SCHEMA.VIEWS", "TABLE_NAME");
|
|
684
|
+
const definition = text(viewRow?.view_definition);
|
|
685
|
+
if (definition === void 0 || definition.trim() === "") {
|
|
686
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
687
|
+
severity: "error",
|
|
688
|
+
code: "expression-parse-failed",
|
|
689
|
+
message: `MySQL view ${physicalName} has no recoverable definition`,
|
|
690
|
+
path: [
|
|
691
|
+
"views",
|
|
692
|
+
physicalName,
|
|
693
|
+
"definition"
|
|
694
|
+
],
|
|
695
|
+
physicalReference,
|
|
696
|
+
remediation: "Grant the metadata privilege needed to read INFORMATION_SCHEMA.VIEWS.VIEW_DEFINITION."
|
|
697
|
+
}));
|
|
698
|
+
return {
|
|
699
|
+
kind: "deferred-object",
|
|
700
|
+
id: stableId(`view:${physicalName}`),
|
|
701
|
+
identitySource: "physical-name",
|
|
702
|
+
objectKind: "view",
|
|
703
|
+
physicalName,
|
|
704
|
+
reference: physicalReference,
|
|
705
|
+
dialect: extension({
|
|
706
|
+
reason: "definition-unavailable",
|
|
707
|
+
...text(viewRow?.definer) === void 0 ? {} : { definer: text(viewRow?.definer) }
|
|
708
|
+
})
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
const checkOption = normalizeCheckOption(viewRow?.check_option);
|
|
712
|
+
const securityType = text(viewRow?.security_type)?.toUpperCase();
|
|
713
|
+
return {
|
|
714
|
+
kind: "view",
|
|
715
|
+
id: stableId(physicalName),
|
|
716
|
+
identitySource: "physical-name",
|
|
717
|
+
physicalName,
|
|
718
|
+
columns: [],
|
|
719
|
+
definition: sql(definition, namespace, {
|
|
720
|
+
kind: "view",
|
|
721
|
+
physicalName,
|
|
722
|
+
reference: physicalReference
|
|
723
|
+
}, physicalName),
|
|
724
|
+
...checkOption === void 0 ? {} : { checkOption },
|
|
725
|
+
...securityType === "INVOKER" ? { securityInvoker: true } : {},
|
|
726
|
+
reference: physicalReference,
|
|
727
|
+
provenance: {
|
|
728
|
+
kind: "catalog",
|
|
729
|
+
dialect: "mysql",
|
|
730
|
+
reference: physicalReference
|
|
731
|
+
},
|
|
732
|
+
dialect: extension({
|
|
733
|
+
...text(viewRow?.is_updatable) === void 0 ? {} : { isUpdatable: text(viewRow?.is_updatable) },
|
|
734
|
+
...securityType === void 0 ? {} : { securityType },
|
|
735
|
+
...text(viewRow?.definer) === void 0 ? {} : { definer: text(viewRow?.definer) }
|
|
736
|
+
})
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function routineObject(row, parameterRows, namespace) {
|
|
740
|
+
const physicalName = text(row.routine_name) ?? "unnamed_routine";
|
|
741
|
+
const physicalReference = reference("routine", physicalName, namespace, "INFORMATION_SCHEMA.ROUTINES", "ROUTINE_NAME");
|
|
742
|
+
const parameters = parameterRows.filter((row) => (number(row.ordinal_position) ?? 0) > 0).sort((left, right) => (number(left.ordinal_position) ?? 0) - (number(right.ordinal_position) ?? 0)).map(routineParameter);
|
|
743
|
+
const definition = text(row.routine_definition);
|
|
744
|
+
const returnType = text(row.data_type);
|
|
745
|
+
const routine = {
|
|
746
|
+
kind: "routine",
|
|
747
|
+
id: stableId(physicalName),
|
|
748
|
+
identitySource: "physical-name",
|
|
749
|
+
physicalName,
|
|
750
|
+
routineKind: text(row.routine_type)?.toUpperCase() === "FUNCTION" ? "function" : text(row.routine_type)?.toUpperCase() === "PROCEDURE" ? "procedure" : "unknown",
|
|
751
|
+
parameters,
|
|
752
|
+
...returnType === void 0 || returnType.toUpperCase() === "VOID" ? {} : { returnType: storage(row.data_type, row.dtd_identifier) },
|
|
753
|
+
...text(row.external_language) === void 0 && text(row.routine_body) === void 0 ? {} : { language: text(row.external_language) ?? text(row.routine_body) ?? "unknown" },
|
|
754
|
+
...definition === void 0 ? {} : { body: sql(definition, namespace, {
|
|
755
|
+
kind: "routine",
|
|
756
|
+
physicalName,
|
|
757
|
+
reference: physicalReference
|
|
758
|
+
}, physicalName) },
|
|
759
|
+
volatility: "unknown",
|
|
760
|
+
...text(row.security_type) === void 0 ? {} : { security: routineSecurity(row.security_type) },
|
|
761
|
+
reference: physicalReference,
|
|
762
|
+
provenance: {
|
|
763
|
+
kind: "catalog",
|
|
764
|
+
dialect: "mysql",
|
|
765
|
+
reference: physicalReference
|
|
766
|
+
},
|
|
767
|
+
dialect: extension({
|
|
768
|
+
...text(row.is_deterministic) === void 0 ? {} : { deterministic: text(row.is_deterministic).toUpperCase() === "YES" },
|
|
769
|
+
...text(row.sql_data_access) === void 0 ? {} : { sqlDataAccess: text(row.sql_data_access) },
|
|
770
|
+
...text(row.sql_mode) === void 0 ? {} : { sqlMode: text(row.sql_mode) }
|
|
771
|
+
})
|
|
772
|
+
};
|
|
773
|
+
const commentText = text(row.routine_comment);
|
|
774
|
+
if (commentText === void 0 || commentText === "") return routine;
|
|
775
|
+
return {
|
|
776
|
+
...routine,
|
|
777
|
+
comment: objectComment({
|
|
778
|
+
kind: "routine",
|
|
779
|
+
id: routine.id
|
|
780
|
+
}, commentText, physicalReference, namespace)
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function routineParameter(row) {
|
|
784
|
+
const parameterName = text(row.parameter_name);
|
|
785
|
+
return {
|
|
786
|
+
...parameterName === void 0 ? {} : { name: parameterName },
|
|
787
|
+
...routineParameterMode(row.parameter_mode) === void 0 ? {} : { mode: routineParameterMode(row.parameter_mode) },
|
|
788
|
+
storage: storage(row.data_type, row.dtd_identifier),
|
|
789
|
+
ordinalPosition: number(row.ordinal_position) ?? 0
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
function triggerObject(rows, relations, namespace, diagnostics) {
|
|
793
|
+
const first = rows[0];
|
|
794
|
+
const physicalName = text(first.trigger_name) ?? "unnamed_trigger";
|
|
795
|
+
const physicalReference = reference("trigger", physicalName, namespace, "INFORMATION_SCHEMA.TRIGGERS", "TRIGGER_NAME");
|
|
796
|
+
const tableName = text(first.table_name);
|
|
797
|
+
const target = tableName === void 0 ? void 0 : relations.get(tableName);
|
|
798
|
+
const body = text(first.action_statement);
|
|
799
|
+
if (target === void 0 || body === void 0 || body.trim() === "") {
|
|
800
|
+
if (target === void 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
801
|
+
severity: "error",
|
|
802
|
+
code: "unresolved-reference",
|
|
803
|
+
message: `MySQL trigger ${physicalName} target ${tableName ?? "unknown"} was not found`,
|
|
804
|
+
path: [
|
|
805
|
+
"triggers",
|
|
806
|
+
physicalName,
|
|
807
|
+
"table"
|
|
808
|
+
],
|
|
809
|
+
physicalReference
|
|
810
|
+
}));
|
|
811
|
+
if (body === void 0 || body.trim() === "") diagnostics.push(createIntrospectionDiagnostic({
|
|
812
|
+
severity: "error",
|
|
813
|
+
code: "expression-parse-failed",
|
|
814
|
+
message: `MySQL trigger ${physicalName} has no recoverable body`,
|
|
815
|
+
path: [
|
|
816
|
+
"triggers",
|
|
817
|
+
physicalName,
|
|
818
|
+
"body"
|
|
819
|
+
],
|
|
820
|
+
physicalReference
|
|
821
|
+
}));
|
|
822
|
+
return {
|
|
823
|
+
kind: "deferred-object",
|
|
824
|
+
id: stableId(`trigger:${physicalName}`),
|
|
825
|
+
identitySource: "physical-name",
|
|
826
|
+
objectKind: "trigger",
|
|
827
|
+
physicalName,
|
|
828
|
+
reference: physicalReference,
|
|
829
|
+
dialect: extension({
|
|
830
|
+
...tableName === void 0 ? {} : { tableName },
|
|
831
|
+
...body === void 0 ? {} : { actionStatement: body }
|
|
832
|
+
})
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
const timing = triggerTiming(first.action_timing);
|
|
836
|
+
const events = [...new Set(rows.map((row) => triggerEvent(row.event_manipulation)).filter((value) => value !== void 0))];
|
|
837
|
+
return {
|
|
838
|
+
kind: "trigger",
|
|
839
|
+
id: stableId(physicalName),
|
|
840
|
+
identitySource: "physical-name",
|
|
841
|
+
physicalName,
|
|
842
|
+
table: target,
|
|
843
|
+
timing,
|
|
844
|
+
events,
|
|
845
|
+
orientation: triggerOrientation(first.action_orientation),
|
|
846
|
+
...text(first.action_condition) === void 0 ? {} : { condition: sql(text(first.action_condition), namespace, {
|
|
847
|
+
kind: "trigger",
|
|
848
|
+
physicalName,
|
|
849
|
+
reference: physicalReference
|
|
850
|
+
}, physicalName) },
|
|
851
|
+
body: sql(body, namespace, {
|
|
852
|
+
kind: "trigger",
|
|
853
|
+
physicalName,
|
|
854
|
+
reference: physicalReference
|
|
855
|
+
}, physicalName),
|
|
856
|
+
reference: physicalReference,
|
|
857
|
+
provenance: {
|
|
858
|
+
kind: "catalog",
|
|
859
|
+
dialect: "mysql",
|
|
860
|
+
reference: physicalReference
|
|
861
|
+
},
|
|
862
|
+
dialect: extension({
|
|
863
|
+
...text(first.definer) === void 0 ? {} : { definer: text(first.definer) },
|
|
864
|
+
...text(first.sql_mode) === void 0 ? {} : { sqlMode: text(first.sql_mode) },
|
|
865
|
+
...number(first.action_order) === void 0 ? {} : { actionOrder: number(first.action_order) }
|
|
866
|
+
})
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
function partitionObject(row, tables, namespace, diagnostics) {
|
|
870
|
+
const tableName = text(row.table_name) ?? "unknown";
|
|
871
|
+
const partitionName = text(row.partition_name);
|
|
872
|
+
const subpartitionName = text(row.subpartition_name);
|
|
873
|
+
const physicalName = partitionName === void 0 ? tableName : subpartitionName === void 0 ? partitionName : `${partitionName}/${subpartitionName}`;
|
|
874
|
+
const physicalReference = reference("partition", physicalName, namespace, "INFORMATION_SCHEMA.PARTITIONS", "PARTITION_NAME");
|
|
875
|
+
const parent = tables.get(tableName);
|
|
876
|
+
if (parent === void 0) {
|
|
877
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
878
|
+
severity: "error",
|
|
879
|
+
code: "unresolved-reference",
|
|
880
|
+
message: `MySQL partition ${physicalName} parent table ${tableName} was not found`,
|
|
881
|
+
path: [
|
|
882
|
+
"partitions",
|
|
883
|
+
physicalName,
|
|
884
|
+
"parent"
|
|
885
|
+
],
|
|
886
|
+
physicalReference
|
|
887
|
+
}));
|
|
888
|
+
return {
|
|
889
|
+
kind: "deferred-object",
|
|
890
|
+
id: stableId(`partition:${tableName}:${physicalName}`),
|
|
891
|
+
identitySource: "physical-name",
|
|
892
|
+
objectKind: "partition",
|
|
893
|
+
physicalName,
|
|
894
|
+
reference: physicalReference,
|
|
895
|
+
dialect: extension({ tableName })
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
const method = text(row.subpartition_method) ?? text(row.partition_method) ?? "UNKNOWN";
|
|
899
|
+
const strategy = partitionStrategy(method);
|
|
900
|
+
if (strategy === "unknown") diagnostics.push(createIntrospectionDiagnostic({
|
|
901
|
+
severity: "warning",
|
|
902
|
+
code: "unsupported-feature",
|
|
903
|
+
message: `MySQL partition method ${method} is retained with an unknown normalized strategy`,
|
|
904
|
+
path: [
|
|
905
|
+
"partitions",
|
|
906
|
+
physicalName,
|
|
907
|
+
"strategy"
|
|
908
|
+
],
|
|
909
|
+
physicalReference
|
|
910
|
+
}));
|
|
911
|
+
const boundText = text(row.partition_description);
|
|
912
|
+
const keyExpression = text(row.subpartition_expression) ?? text(row.partition_expression);
|
|
913
|
+
const key = keyExpression === void 0 ? void 0 : partitionKeyColumns(keyExpression);
|
|
914
|
+
const unknownFields = [];
|
|
915
|
+
if (keyExpression !== void 0 && key !== void 0 && !key.representable) {
|
|
916
|
+
const keySql = sql(keyExpression, namespace, parent, physicalName);
|
|
917
|
+
unknownFields.push({
|
|
918
|
+
name: "partitionExpression",
|
|
919
|
+
value: keySql
|
|
920
|
+
});
|
|
921
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
922
|
+
severity: "warning",
|
|
923
|
+
code: "unsupported-feature",
|
|
924
|
+
message: `MySQL partition ${physicalName} has a functional key retained as dialect evidence`,
|
|
925
|
+
path: [
|
|
926
|
+
"partitions",
|
|
927
|
+
physicalName,
|
|
928
|
+
"partitionExpression"
|
|
929
|
+
],
|
|
930
|
+
physicalReference,
|
|
931
|
+
remediation: "Inspect the retained partition expression before using it as migration input."
|
|
932
|
+
}));
|
|
933
|
+
}
|
|
934
|
+
const partition = {
|
|
935
|
+
kind: "partition",
|
|
936
|
+
id: stableId(`partition:${tableName}:${physicalName}`),
|
|
937
|
+
identitySource: "physical-name",
|
|
938
|
+
physicalName,
|
|
939
|
+
parent: {
|
|
940
|
+
kind: "table",
|
|
941
|
+
id: parent.id
|
|
942
|
+
},
|
|
943
|
+
strategy,
|
|
944
|
+
...key?.representable && key.columns.length > 0 ? { keyColumns: key.columns } : {},
|
|
945
|
+
...boundText === void 0 ? {} : { bound: sql(boundText, namespace, parent, physicalName) },
|
|
946
|
+
reference: physicalReference,
|
|
947
|
+
...unknownFields.length === 0 ? {} : { unknownFields },
|
|
948
|
+
dialect: extension({
|
|
949
|
+
...text(row.partition_method) === void 0 ? {} : { partitionMethod: text(row.partition_method) },
|
|
950
|
+
...text(row.subpartition_method) === void 0 ? {} : { subpartitionMethod: text(row.subpartition_method) },
|
|
951
|
+
...text(row.subpartition_name) === void 0 ? {} : { subpartitionName: text(row.subpartition_name) },
|
|
952
|
+
...text(row.tablespace_name) === void 0 ? {} : { tablespace: text(row.tablespace_name) }
|
|
953
|
+
})
|
|
954
|
+
};
|
|
955
|
+
const commentText = text(row.partition_comment);
|
|
956
|
+
if (commentText === void 0 || commentText === "") return partition;
|
|
957
|
+
return {
|
|
958
|
+
...partition,
|
|
959
|
+
comment: objectComment({
|
|
960
|
+
kind: "partition",
|
|
961
|
+
id: partition.id
|
|
962
|
+
}, commentText, physicalReference, namespace)
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
function collationObject(row, namespace) {
|
|
966
|
+
const physicalName = text(row.collation_name) ?? "unknown_collation";
|
|
967
|
+
return {
|
|
968
|
+
kind: "collation",
|
|
969
|
+
id: stableId(physicalName),
|
|
970
|
+
identitySource: "physical-name",
|
|
971
|
+
physicalName,
|
|
972
|
+
reference: reference("collation", physicalName, namespace, "INFORMATION_SCHEMA.COLLATIONS", "COLLATION_NAME"),
|
|
973
|
+
dialect: extension({
|
|
974
|
+
...text(row.character_set_name) === void 0 ? {} : { characterSet: text(row.character_set_name) },
|
|
975
|
+
...number(row.collation_id) === void 0 ? {} : { id: number(row.collation_id) },
|
|
976
|
+
...text(row.is_default) === void 0 ? {} : { isDefault: text(row.is_default) === "Yes" },
|
|
977
|
+
...text(row.is_compiled) === void 0 ? {} : { isCompiled: text(row.is_compiled) === "Yes" },
|
|
978
|
+
...number(row.sort_length) === void 0 ? {} : { sortLength: number(row.sort_length) },
|
|
979
|
+
...text(row.pad_attribute) === void 0 ? {} : { padAttribute: text(row.pad_attribute) }
|
|
980
|
+
})
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
function opaqueEvent(row, namespace) {
|
|
984
|
+
const physicalName = text(row.event_name) ?? "unnamed_event";
|
|
985
|
+
const physicalReference = reference("opaque-object", physicalName, namespace, "INFORMATION_SCHEMA.EVENTS", "EVENT_NAME");
|
|
986
|
+
const definition = text(row.event_definition) ?? text(row.event_body);
|
|
987
|
+
return {
|
|
988
|
+
kind: "opaque-object",
|
|
989
|
+
id: stableId(`event:${physicalName}`),
|
|
990
|
+
identitySource: "physical-name",
|
|
991
|
+
objectKind: "event",
|
|
992
|
+
physicalName,
|
|
993
|
+
data: {
|
|
994
|
+
...text(row.event_type) === void 0 ? {} : { eventType: text(row.event_type) },
|
|
995
|
+
...text(row.status) === void 0 ? {} : { status: text(row.status) },
|
|
996
|
+
...text(row.execute_at) === void 0 ? {} : { executeAt: text(row.execute_at) },
|
|
997
|
+
...text(row.interval_value) === void 0 ? {} : { intervalValue: text(row.interval_value) },
|
|
998
|
+
...text(row.interval_field) === void 0 ? {} : { intervalField: text(row.interval_field) },
|
|
999
|
+
...text(row.definer) === void 0 ? {} : { definer: text(row.definer) }
|
|
1000
|
+
},
|
|
1001
|
+
...definition === void 0 ? {} : { sql: sql(definition, namespace, {
|
|
1002
|
+
kind: "opaque-object",
|
|
1003
|
+
physicalName,
|
|
1004
|
+
reference: physicalReference
|
|
1005
|
+
}, physicalName) },
|
|
1006
|
+
reference: physicalReference,
|
|
1007
|
+
provenance: {
|
|
1008
|
+
kind: "catalog",
|
|
1009
|
+
dialect: "mysql",
|
|
1010
|
+
reference: physicalReference
|
|
1011
|
+
},
|
|
1012
|
+
dialect: extension({ objectKind: "event" })
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function objectComment(object, comment, physicalReference, namespace) {
|
|
1016
|
+
return {
|
|
1017
|
+
kind: "comment",
|
|
1018
|
+
id: metadataId(object, "comment"),
|
|
1019
|
+
object,
|
|
1020
|
+
text: comment,
|
|
1021
|
+
reference: physicalReference,
|
|
1022
|
+
provenance: {
|
|
1023
|
+
kind: "catalog",
|
|
1024
|
+
dialect: "mysql",
|
|
1025
|
+
reference: physicalReference,
|
|
1026
|
+
path: ["namespace", namespace]
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
function metadataId(object, suffix) {
|
|
1031
|
+
const owner = object.owner;
|
|
1032
|
+
return stableId(`${owner === void 0 ? "" : `${owner.kind}_${owner.id}_`}${object.kind}_${object.id}_${suffix}`);
|
|
1033
|
+
}
|
|
1034
|
+
function groupRows(rows, keyOf) {
|
|
1035
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1036
|
+
for (const row of rows) {
|
|
1037
|
+
const key = keyOf(row);
|
|
1038
|
+
const group = groups.get(key) ?? [];
|
|
1039
|
+
group.push(row);
|
|
1040
|
+
groups.set(key, group);
|
|
1041
|
+
}
|
|
1042
|
+
return [...groups.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([, group]) => group);
|
|
1043
|
+
}
|
|
1044
|
+
function extension(data) {
|
|
1045
|
+
return {
|
|
1046
|
+
dialect: "mysql",
|
|
1047
|
+
version: 1,
|
|
1048
|
+
data
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
function storage(dataType, declaration) {
|
|
1052
|
+
return { nativeType: text(declaration) ?? text(dataType) ?? "unknown" };
|
|
1053
|
+
}
|
|
1054
|
+
function normalizeCheckOption(value) {
|
|
1055
|
+
const normalized = text(value)?.toLowerCase();
|
|
1056
|
+
return normalized === "none" || normalized === void 0 ? normalized === void 0 ? void 0 : "none" : normalized === "local" ? "local" : normalized === "cascaded" ? "cascaded" : void 0;
|
|
1057
|
+
}
|
|
1058
|
+
function routineSecurity(value) {
|
|
1059
|
+
return text(value)?.toLowerCase() === "invoker" ? "invoker" : text(value)?.toLowerCase() === "definer" ? "definer" : "unknown";
|
|
1060
|
+
}
|
|
1061
|
+
function routineParameterMode(value) {
|
|
1062
|
+
const normalized = text(value)?.toLowerCase();
|
|
1063
|
+
return normalized === "in" ? "in" : normalized === "out" ? "out" : normalized === "inout" ? "inout" : void 0;
|
|
1064
|
+
}
|
|
1065
|
+
function triggerTiming(value) {
|
|
1066
|
+
const normalized = text(value)?.toLowerCase();
|
|
1067
|
+
return normalized === "before" ? "before" : normalized === "after" ? "after" : "unknown";
|
|
1068
|
+
}
|
|
1069
|
+
function triggerEvent(value) {
|
|
1070
|
+
const normalized = text(value)?.toLowerCase();
|
|
1071
|
+
return normalized === "insert" || normalized === "update" || normalized === "delete" ? normalized : void 0;
|
|
1072
|
+
}
|
|
1073
|
+
function triggerOrientation(value) {
|
|
1074
|
+
const normalized = text(value)?.toLowerCase();
|
|
1075
|
+
return normalized === "row" ? "row" : normalized === "statement" ? "statement" : void 0;
|
|
1076
|
+
}
|
|
1077
|
+
function partitionStrategy(value) {
|
|
1078
|
+
const normalized = value.toLowerCase();
|
|
1079
|
+
return normalized === "range" ? "range" : normalized === "list" ? "list" : normalized === "hash" ? "hash" : "unknown";
|
|
1080
|
+
}
|
|
1081
|
+
function partitionKeyColumns(expression) {
|
|
1082
|
+
const terms = splitPartitionKeyExpression(expression);
|
|
1083
|
+
const columns = [];
|
|
1084
|
+
for (const term of terms) {
|
|
1085
|
+
const value = term.trim();
|
|
1086
|
+
if (/^[A-Za-z_][A-Za-z0-9_$]*$/.test(value)) {
|
|
1087
|
+
columns.push(value);
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
const quoted = value.match(/^`((?:``|[^`])*)`$/);
|
|
1091
|
+
if (quoted) {
|
|
1092
|
+
columns.push(quoted[1].replace(/``/g, "`"));
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
columns: [],
|
|
1097
|
+
representable: false
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
columns,
|
|
1102
|
+
representable: true
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function splitPartitionKeyExpression(source) {
|
|
1106
|
+
const values = [];
|
|
1107
|
+
let start = 0;
|
|
1108
|
+
let depth = 0;
|
|
1109
|
+
let quote = false;
|
|
1110
|
+
for (let index = 0; index < source.length; index++) {
|
|
1111
|
+
const character = source[index];
|
|
1112
|
+
if (quote) {
|
|
1113
|
+
if (character === "`") {
|
|
1114
|
+
if (source[index + 1] === "`") index++;
|
|
1115
|
+
else quote = false;
|
|
1116
|
+
}
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (character === "`") quote = true;
|
|
1120
|
+
else if (character === "(") depth++;
|
|
1121
|
+
else if (character === ")") depth--;
|
|
1122
|
+
else if (character === "," && depth === 0) {
|
|
1123
|
+
values.push(source.slice(start, index));
|
|
1124
|
+
start = index + 1;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
values.push(source.slice(start));
|
|
1128
|
+
return values;
|
|
1129
|
+
}
|
|
1130
|
+
function sql(textValue, namespace, table, name) {
|
|
1131
|
+
return {
|
|
1132
|
+
kind: "sql",
|
|
1133
|
+
dialect: "mysql",
|
|
1134
|
+
text: textValue,
|
|
1135
|
+
provenance: {
|
|
1136
|
+
kind: "catalog",
|
|
1137
|
+
dialect: "mysql",
|
|
1138
|
+
reference: table.reference ?? reference(table.kind, name, namespace, "INFORMATION_SCHEMA", "TABLE_NAME", table.physicalName)
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function reference(kind, name, namespace, relation, key, value = name) {
|
|
1143
|
+
return {
|
|
1144
|
+
kind,
|
|
1145
|
+
name,
|
|
1146
|
+
namespace,
|
|
1147
|
+
catalog: {
|
|
1148
|
+
relation,
|
|
1149
|
+
key,
|
|
1150
|
+
value
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function mysqlDefault(value, dataType, namespace, table, columnName) {
|
|
1155
|
+
if (value === void 0) return;
|
|
1156
|
+
const literalValue = literal(value, dataType);
|
|
1157
|
+
return literalValue !== void 0 ? {
|
|
1158
|
+
kind: "literal",
|
|
1159
|
+
value: literalValue
|
|
1160
|
+
} : {
|
|
1161
|
+
kind: "expression",
|
|
1162
|
+
expression: sql(value, namespace, table, columnName)
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
function literal(value, dataType) {
|
|
1166
|
+
const trimmed = value.trim();
|
|
1167
|
+
const normalizedType = dataType?.trim().toLowerCase();
|
|
1168
|
+
if (trimmed.toUpperCase() === "NULL") return null;
|
|
1169
|
+
if (trimmed.toUpperCase() === "TRUE") return true;
|
|
1170
|
+
if (trimmed.toUpperCase() === "FALSE") return false;
|
|
1171
|
+
if (isNumericDataType(normalizedType)) {
|
|
1172
|
+
const numeric = numericLiteral(trimmed);
|
|
1173
|
+
if (numeric !== void 0) return numeric;
|
|
1174
|
+
}
|
|
1175
|
+
if (/^'(?:''|[^'])*'$/.test(trimmed)) return trimmed.slice(1, -1).replace(/''/g, "'");
|
|
1176
|
+
if (trimmed === "" && isTextDataType(normalizedType)) return "";
|
|
1177
|
+
if (isTextDataType(normalizedType)) return value;
|
|
1178
|
+
}
|
|
1179
|
+
function isNumericDataType(value) {
|
|
1180
|
+
return /^(?:tinyint|smallint|mediumint|int|integer|bigint|decimal|numeric|dec|float|double|real)/.test(value ?? "");
|
|
1181
|
+
}
|
|
1182
|
+
function isTextDataType(value) {
|
|
1183
|
+
return /^(?:char|varchar|text|tinytext|mediumtext|longtext|enum|set|binary|varbinary|blob)/.test(value ?? "");
|
|
1184
|
+
}
|
|
1185
|
+
function numericLiteral(value) {
|
|
1186
|
+
if (/^[+-]?\d+$/.test(value)) try {
|
|
1187
|
+
const integer = BigInt(value);
|
|
1188
|
+
return integer <= BigInt(Number.MAX_SAFE_INTEGER) && integer >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer) : integer;
|
|
1189
|
+
} catch {
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(value)) return;
|
|
1193
|
+
const numeric = Number(value);
|
|
1194
|
+
return Number.isFinite(numeric) && canonicalNumericText(value) === canonicalNumericText(String(numeric)) ? numeric : void 0;
|
|
1195
|
+
}
|
|
1196
|
+
function canonicalNumericText(value) {
|
|
1197
|
+
const match = value.match(/^([+-]?)(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:[eE]([+-]?\d+))?$/);
|
|
1198
|
+
if (!match) return;
|
|
1199
|
+
const sign = match[1] === "-" ? "-" : "";
|
|
1200
|
+
const integerPart = match[2] ?? "";
|
|
1201
|
+
const fractionalPart = match[3] ?? match[4] ?? "";
|
|
1202
|
+
const exponent = Number(match[5] ?? 0);
|
|
1203
|
+
if (!Number.isSafeInteger(exponent)) return;
|
|
1204
|
+
const digits = integerPart + fractionalPart;
|
|
1205
|
+
const decimalPosition = integerPart.length + exponent;
|
|
1206
|
+
let integer;
|
|
1207
|
+
let fraction;
|
|
1208
|
+
if (decimalPosition <= 0) {
|
|
1209
|
+
integer = "0";
|
|
1210
|
+
fraction = `${"0".repeat(-decimalPosition)}${digits}`;
|
|
1211
|
+
} else if (decimalPosition >= digits.length) {
|
|
1212
|
+
integer = `${digits}${"0".repeat(decimalPosition - digits.length)}`;
|
|
1213
|
+
fraction = "";
|
|
1214
|
+
} else {
|
|
1215
|
+
integer = digits.slice(0, decimalPosition);
|
|
1216
|
+
fraction = digits.slice(decimalPosition);
|
|
1217
|
+
}
|
|
1218
|
+
integer = integer.replace(/^0+(?=\d)/, "") || "0";
|
|
1219
|
+
fraction = fraction.replace(/0+$/, "");
|
|
1220
|
+
if (integer === "0" && fraction === "") return "0";
|
|
1221
|
+
return `${sign}${integer}${fraction === "" ? "" : `.${fraction}`}`;
|
|
1222
|
+
}
|
|
1223
|
+
function emptyCatalog(namespace, diagnostics) {
|
|
1224
|
+
return {
|
|
1225
|
+
dialect: "mysql",
|
|
1226
|
+
server: {
|
|
1227
|
+
product: "mysql",
|
|
1228
|
+
rawVersion: "unknown",
|
|
1229
|
+
capabilities: {
|
|
1230
|
+
generatedColumns: false,
|
|
1231
|
+
identityMetadata: false,
|
|
1232
|
+
checkConstraints: false,
|
|
1233
|
+
checkConstraintEnforcement: "unknown",
|
|
1234
|
+
expressionDecompilation: false,
|
|
1235
|
+
indexExpressions: false,
|
|
1236
|
+
indexPredicates: false,
|
|
1237
|
+
indexIncludedColumns: false,
|
|
1238
|
+
namespaces: true,
|
|
1239
|
+
visibility: "unknown"
|
|
1240
|
+
}
|
|
1241
|
+
},
|
|
1242
|
+
namespace: {
|
|
1243
|
+
kind: "mysql-database",
|
|
1244
|
+
name: namespace
|
|
1245
|
+
},
|
|
1246
|
+
tables: [],
|
|
1247
|
+
deferredObjects: [],
|
|
1248
|
+
diagnostics
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
function stableId(value) {
|
|
1252
|
+
if (value && !/[.\\\u0000-\u001f\u007f]/.test(value)) return value;
|
|
1253
|
+
let hash = 2166136261;
|
|
1254
|
+
for (const character of value) {
|
|
1255
|
+
hash ^= character.charCodeAt(0);
|
|
1256
|
+
hash = Math.imul(hash, 16777619);
|
|
1257
|
+
}
|
|
1258
|
+
return `introspected_${(hash >>> 0).toString(16)}`;
|
|
1259
|
+
}
|
|
1260
|
+
function text(value) {
|
|
1261
|
+
return value === null || value === void 0 ? void 0 : String(value);
|
|
1262
|
+
}
|
|
1263
|
+
function number(value) {
|
|
1264
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1265
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1266
|
+
const result = Number(value);
|
|
1267
|
+
return Number.isFinite(result) ? result : void 0;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
function action(value) {
|
|
1271
|
+
const normalized = text(value)?.toLowerCase();
|
|
1272
|
+
return normalized === "cascade" ? "cascade" : normalized === "restrict" ? "restrict" : normalized === "set null" ? "set-null" : normalized === "set default" ? "set-default" : "no-action";
|
|
1273
|
+
}
|
|
1274
|
+
function match(value) {
|
|
1275
|
+
return text(value)?.toLowerCase() === "full" ? "full" : "simple";
|
|
1276
|
+
}
|
|
1277
|
+
//#endregion
|
|
1278
|
+
export { mysqlChecksQuery, mysqlCollationsQuery, mysqlColumnsQuery, mysqlEventsQuery, mysqlKeyUsageQuery, mysqlPartitionsQuery, mysqlRoutineParametersQuery, mysqlRoutinesQuery, mysqlServerQuery, mysqlStatisticsQuery, mysqlTablesQuery, mysqlTriggersQuery, mysqlViewsQuery, readCatalog };
|