@rdbms-erd/core 0.1.2 → 0.1.4
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/dist/index.d.ts +237 -0
- package/dist/index.js +883 -0
- package/dist/index.js.map +1 -0
- package/package.json +18 -4
- package/src/__tests__/alignment.test.ts +0 -19
- package/src/__tests__/benchmark.test.ts +0 -14
- package/src/__tests__/db-meta-adapter.test.ts +0 -166
- package/src/__tests__/ddl-diagnostics.test.ts +0 -52
- package/src/__tests__/ddl-snapshot.test.ts +0 -91
- package/src/__tests__/designDocument.test.ts +0 -129
- package/src/__tests__/logical-physical.test.ts +0 -94
- package/src/alignment.ts +0 -71
- package/src/index.ts +0 -1272
package/dist/index.js
ADDED
|
@@ -0,0 +1,883 @@
|
|
|
1
|
+
// src/alignment.ts
|
|
2
|
+
function alignNodePositions(selectedIds, positions, command) {
|
|
3
|
+
const entries = selectedIds.map((id) => {
|
|
4
|
+
const pos = positions[id];
|
|
5
|
+
return pos ? { id, pos: { ...pos } } : null;
|
|
6
|
+
}).filter((e) => Boolean(e));
|
|
7
|
+
if (entries.length < 2) {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
const xs = entries.map((e) => e.pos.x);
|
|
11
|
+
const ys = entries.map((e) => e.pos.y);
|
|
12
|
+
const minX = Math.min(...xs);
|
|
13
|
+
const maxX = Math.max(...xs);
|
|
14
|
+
const minY = Math.min(...ys);
|
|
15
|
+
const maxY = Math.max(...ys);
|
|
16
|
+
const centerX = (minX + maxX) / 2;
|
|
17
|
+
const centerY = (minY + maxY) / 2;
|
|
18
|
+
const sortByX = [...entries].sort((a, b) => a.pos.x - b.pos.x);
|
|
19
|
+
const sortByY = [...entries].sort((a, b) => a.pos.y - b.pos.y);
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const e of entries) {
|
|
22
|
+
const next = { ...e.pos };
|
|
23
|
+
if (command === "left") next.x = minX;
|
|
24
|
+
if (command === "h-center") next.x = centerX;
|
|
25
|
+
if (command === "right") next.x = maxX;
|
|
26
|
+
if (command === "top") next.y = minY;
|
|
27
|
+
if (command === "v-center") next.y = centerY;
|
|
28
|
+
if (command === "bottom") next.y = maxY;
|
|
29
|
+
out[e.id] = next;
|
|
30
|
+
}
|
|
31
|
+
if (command === "h-gap") {
|
|
32
|
+
const gap = entries.length > 1 ? (maxX - minX) / (entries.length - 1) : 0;
|
|
33
|
+
sortByX.forEach((e, index) => {
|
|
34
|
+
out[e.id] = { ...e.pos, x: minX + gap * index };
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (command === "v-gap") {
|
|
38
|
+
const gap = entries.length > 1 ? (maxY - minY) / (entries.length - 1) : 0;
|
|
39
|
+
sortByY.forEach((e, index) => {
|
|
40
|
+
out[e.id] = { ...e.pos, y: minY + gap * index };
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/index.ts
|
|
47
|
+
var LOGICAL_DATA_TYPES = [
|
|
48
|
+
"TEXT",
|
|
49
|
+
"DATE",
|
|
50
|
+
"TIME",
|
|
51
|
+
"DATETIME",
|
|
52
|
+
"NUMBER",
|
|
53
|
+
"DECIMAL",
|
|
54
|
+
"FLOAT",
|
|
55
|
+
"BOOLEAN",
|
|
56
|
+
"JSON",
|
|
57
|
+
"UUID",
|
|
58
|
+
"BINARY"
|
|
59
|
+
];
|
|
60
|
+
function isRelationshipLineRenderable(rel, revealHiddenLines) {
|
|
61
|
+
if (rel.canvasLineHidden === true) return revealHiddenLines;
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
function migrateLegacyFkLineVisibility(model) {
|
|
65
|
+
for (const table of model.tables) {
|
|
66
|
+
for (const col of table.columns) {
|
|
67
|
+
const legacy = col.showFkRelationLine;
|
|
68
|
+
if (legacy === false && col.isForeignKey) {
|
|
69
|
+
for (const rel of model.relationships) {
|
|
70
|
+
if (rel.targetTableId === table.id && rel.targetColumnId === col.id) {
|
|
71
|
+
rel.canvasLineHidden = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if ("showFkRelationLine" in col) {
|
|
76
|
+
delete col.showFkRelationLine;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function isObject(value) {
|
|
82
|
+
return typeof value === "object" && value !== null;
|
|
83
|
+
}
|
|
84
|
+
var BUILTIN_DIALECTS = [
|
|
85
|
+
"mssql",
|
|
86
|
+
"oracle",
|
|
87
|
+
"mysql",
|
|
88
|
+
"postgres",
|
|
89
|
+
"sqlite"
|
|
90
|
+
];
|
|
91
|
+
var DIALECT_DEFAULT_TYPE_MAP = {
|
|
92
|
+
mssql: {
|
|
93
|
+
TEXT: "NVARCHAR(255)",
|
|
94
|
+
DATE: "DATE",
|
|
95
|
+
TIME: "TIME",
|
|
96
|
+
DATETIME: "DATETIME2",
|
|
97
|
+
NUMBER: "INT",
|
|
98
|
+
DECIMAL: "DECIMAL(10,2)",
|
|
99
|
+
FLOAT: "FLOAT",
|
|
100
|
+
BOOLEAN: "BIT",
|
|
101
|
+
JSON: "NVARCHAR(MAX)",
|
|
102
|
+
UUID: "UNIQUEIDENTIFIER",
|
|
103
|
+
BINARY: "VARBINARY(255)"
|
|
104
|
+
},
|
|
105
|
+
oracle: {
|
|
106
|
+
TEXT: "VARCHAR2(255)",
|
|
107
|
+
DATE: "DATE",
|
|
108
|
+
TIME: "TIMESTAMP",
|
|
109
|
+
DATETIME: "TIMESTAMP",
|
|
110
|
+
NUMBER: "NUMBER(10)",
|
|
111
|
+
DECIMAL: "NUMBER(10,2)",
|
|
112
|
+
FLOAT: "BINARY_FLOAT",
|
|
113
|
+
BOOLEAN: "NUMBER(1)",
|
|
114
|
+
JSON: "CLOB",
|
|
115
|
+
UUID: "RAW(16)",
|
|
116
|
+
BINARY: "RAW(255)"
|
|
117
|
+
},
|
|
118
|
+
mysql: {
|
|
119
|
+
TEXT: "VARCHAR(255)",
|
|
120
|
+
DATE: "DATE",
|
|
121
|
+
TIME: "TIME",
|
|
122
|
+
DATETIME: "DATETIME",
|
|
123
|
+
NUMBER: "INT",
|
|
124
|
+
DECIMAL: "DECIMAL(10,2)",
|
|
125
|
+
FLOAT: "FLOAT",
|
|
126
|
+
BOOLEAN: "BOOLEAN",
|
|
127
|
+
JSON: "JSON",
|
|
128
|
+
UUID: "CHAR(36)",
|
|
129
|
+
BINARY: "VARBINARY(255)"
|
|
130
|
+
},
|
|
131
|
+
postgres: {
|
|
132
|
+
TEXT: "VARCHAR(255)",
|
|
133
|
+
DATE: "DATE",
|
|
134
|
+
TIME: "TIME",
|
|
135
|
+
DATETIME: "TIMESTAMP",
|
|
136
|
+
NUMBER: "INTEGER",
|
|
137
|
+
DECIMAL: "NUMERIC(10,2)",
|
|
138
|
+
FLOAT: "REAL",
|
|
139
|
+
BOOLEAN: "BOOLEAN",
|
|
140
|
+
JSON: "JSONB",
|
|
141
|
+
UUID: "UUID",
|
|
142
|
+
BINARY: "BYTEA"
|
|
143
|
+
},
|
|
144
|
+
sqlite: {
|
|
145
|
+
TEXT: "TEXT",
|
|
146
|
+
DATE: "TEXT",
|
|
147
|
+
TIME: "TEXT",
|
|
148
|
+
DATETIME: "TEXT",
|
|
149
|
+
NUMBER: "INTEGER",
|
|
150
|
+
DECIMAL: "NUMERIC(10,2)",
|
|
151
|
+
FLOAT: "REAL",
|
|
152
|
+
BOOLEAN: "INTEGER",
|
|
153
|
+
JSON: "TEXT",
|
|
154
|
+
UUID: "TEXT",
|
|
155
|
+
BINARY: "BLOB"
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
var DIALECT_CAPABILITIES = {
|
|
159
|
+
mssql: { supportsSchema: true },
|
|
160
|
+
oracle: { supportsSchema: true },
|
|
161
|
+
mysql: { supportsSchema: true },
|
|
162
|
+
postgres: { supportsSchema: true },
|
|
163
|
+
sqlite: { supportsSchema: false }
|
|
164
|
+
};
|
|
165
|
+
var DIALECT_LABELS = {
|
|
166
|
+
mssql: "MS SQL Server",
|
|
167
|
+
oracle: "Oracle",
|
|
168
|
+
mysql: "MySQL",
|
|
169
|
+
postgres: "PostgreSQL",
|
|
170
|
+
sqlite: "SQLite"
|
|
171
|
+
};
|
|
172
|
+
var DIALECT_DDL_STYLE = {
|
|
173
|
+
mssql: {
|
|
174
|
+
quote: "bracket",
|
|
175
|
+
boolLiteral: "oneZero",
|
|
176
|
+
nowKeyword: "GETDATE()"
|
|
177
|
+
},
|
|
178
|
+
oracle: {
|
|
179
|
+
quote: "double",
|
|
180
|
+
boolLiteral: "oneZero",
|
|
181
|
+
nowKeyword: "CURRENT_TIMESTAMP"
|
|
182
|
+
},
|
|
183
|
+
mysql: {
|
|
184
|
+
quote: "backtick",
|
|
185
|
+
boolLiteral: "oneZero",
|
|
186
|
+
nowKeyword: "CURRENT_TIMESTAMP"
|
|
187
|
+
},
|
|
188
|
+
postgres: {
|
|
189
|
+
quote: "double",
|
|
190
|
+
boolLiteral: "trueFalse",
|
|
191
|
+
nowKeyword: "CURRENT_TIMESTAMP"
|
|
192
|
+
},
|
|
193
|
+
sqlite: {
|
|
194
|
+
quote: "double",
|
|
195
|
+
boolLiteral: "oneZero",
|
|
196
|
+
nowKeyword: "CURRENT_TIMESTAMP"
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
function toLogicalTypeMetas(dialect) {
|
|
200
|
+
return LOGICAL_DATA_TYPES.map((id) => ({
|
|
201
|
+
id,
|
|
202
|
+
defaultPhysicalType: DIALECT_DEFAULT_TYPE_MAP[dialect][id]
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
var BUILTIN_DIALECT_METAS_JSON = BUILTIN_DIALECTS.map((dialect) => ({
|
|
206
|
+
id: dialect,
|
|
207
|
+
label: DIALECT_LABELS[dialect],
|
|
208
|
+
supportsSchema: DIALECT_CAPABILITIES[dialect].supportsSchema,
|
|
209
|
+
logicalTypes: toLogicalTypeMetas(dialect),
|
|
210
|
+
ddlStyle: DIALECT_DDL_STYLE[dialect]
|
|
211
|
+
}));
|
|
212
|
+
function defaultDdlRules() {
|
|
213
|
+
return {
|
|
214
|
+
quoteIdentifier: (dialect, identifier) => {
|
|
215
|
+
if (dialect === "mssql") return `[${identifier}]`;
|
|
216
|
+
if (dialect === "mysql") return `\`${identifier}\``;
|
|
217
|
+
return `"${identifier}"`;
|
|
218
|
+
},
|
|
219
|
+
toDefaultExpression: (dialect, col) => {
|
|
220
|
+
const raw = col.defaultValue?.trim();
|
|
221
|
+
if (!raw) return null;
|
|
222
|
+
const upper = raw.toUpperCase();
|
|
223
|
+
const isFunctionLike = /[()]/.test(raw) || upper === "NULL" || upper === "CURRENT_TIMESTAMP" || upper === "CURRENT_DATE";
|
|
224
|
+
if (isFunctionLike) return raw;
|
|
225
|
+
if (col.logicalType === "NUMBER" || col.logicalType === "DECIMAL" || col.logicalType === "FLOAT")
|
|
226
|
+
return raw;
|
|
227
|
+
if (col.logicalType === "BOOLEAN") {
|
|
228
|
+
if (upper === "TRUE" || upper === "FALSE") {
|
|
229
|
+
if (dialect === "mssql" || dialect === "oracle" || dialect === "sqlite")
|
|
230
|
+
return upper === "TRUE" ? "1" : "0";
|
|
231
|
+
return upper;
|
|
232
|
+
}
|
|
233
|
+
if (raw === "1" || raw === "0") return raw;
|
|
234
|
+
return quoteSqlString(raw);
|
|
235
|
+
}
|
|
236
|
+
if (col.logicalType === "DATE" || col.logicalType === "DATETIME") {
|
|
237
|
+
if (dialect === "mssql" && upper === "NOW") return "GETDATE()";
|
|
238
|
+
if (upper === "NOW") return "CURRENT_TIMESTAMP";
|
|
239
|
+
return quoteSqlString(raw);
|
|
240
|
+
}
|
|
241
|
+
if (raw.startsWith("'") && raw.endsWith("'") || raw.startsWith('"') && raw.endsWith('"'))
|
|
242
|
+
return raw;
|
|
243
|
+
return quoteSqlString(raw);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function toAdapterDialectMeta(meta) {
|
|
248
|
+
const typeMap = {};
|
|
249
|
+
for (const lt of meta.logicalTypes) typeMap[lt.id] = lt.defaultPhysicalType;
|
|
250
|
+
return {
|
|
251
|
+
id: meta.id,
|
|
252
|
+
label: meta.label,
|
|
253
|
+
capabilities: { supportsSchema: meta.supportsSchema },
|
|
254
|
+
logicalTypes: meta.logicalTypes.map((lt) => lt.id),
|
|
255
|
+
defaultPhysicalTypeMap: typeMap
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function mergeDialectMetas(base, host = []) {
|
|
259
|
+
const byId = new Map(
|
|
260
|
+
base.map((meta) => [meta.id, meta])
|
|
261
|
+
);
|
|
262
|
+
for (const meta of host) byId.set(meta.id, meta);
|
|
263
|
+
return Array.from(byId.values());
|
|
264
|
+
}
|
|
265
|
+
function resolveDialectMetas(options) {
|
|
266
|
+
return mergeDialectMetas(
|
|
267
|
+
BUILTIN_DIALECT_METAS_JSON,
|
|
268
|
+
options?.hostMetas ?? []
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
function buildDbMetaAdapterFromMetas(metas) {
|
|
272
|
+
const byId = new Map(
|
|
273
|
+
metas.map((meta) => [meta.id, toAdapterDialectMeta(meta)])
|
|
274
|
+
);
|
|
275
|
+
const defaultRules = defaultDdlRules();
|
|
276
|
+
return {
|
|
277
|
+
listDialects: () => Array.from(byId.values()),
|
|
278
|
+
getDialectMeta: (dialect) => byId.get(dialect),
|
|
279
|
+
getDefaultPhysicalType: (dialect, logicalType) => {
|
|
280
|
+
const mapped = byId.get(dialect)?.defaultPhysicalTypeMap[logicalType];
|
|
281
|
+
return mapped ?? DIALECT_DEFAULT_TYPE_MAP.postgres[logicalType];
|
|
282
|
+
},
|
|
283
|
+
getDdlRules: (dialect) => {
|
|
284
|
+
const json = metas.find((m) => m.id === dialect);
|
|
285
|
+
const style = json?.ddlStyle;
|
|
286
|
+
if (!style) return defaultRules;
|
|
287
|
+
const quoteIdentifier2 = (_d, identifier) => {
|
|
288
|
+
if (style.quote === "bracket") return `[${identifier}]`;
|
|
289
|
+
if (style.quote === "backtick") return `\`${identifier}\``;
|
|
290
|
+
return `"${identifier}"`;
|
|
291
|
+
};
|
|
292
|
+
const toDefaultExpression2 = (_d, col) => {
|
|
293
|
+
const raw = col.defaultValue?.trim();
|
|
294
|
+
if (!raw) return null;
|
|
295
|
+
const upper = raw.toUpperCase();
|
|
296
|
+
const isFunctionLike = /[()]/.test(raw) || upper === "NULL" || upper === "CURRENT_TIMESTAMP" || upper === "CURRENT_DATE";
|
|
297
|
+
if (isFunctionLike) return raw;
|
|
298
|
+
if (col.logicalType === "NUMBER" || col.logicalType === "DECIMAL" || col.logicalType === "FLOAT")
|
|
299
|
+
return raw;
|
|
300
|
+
if (col.logicalType === "BOOLEAN") {
|
|
301
|
+
if (upper === "TRUE" || upper === "FALSE") {
|
|
302
|
+
if ((style.boolLiteral ?? "oneZero") === "oneZero")
|
|
303
|
+
return upper === "TRUE" ? "1" : "0";
|
|
304
|
+
return upper;
|
|
305
|
+
}
|
|
306
|
+
if (raw === "1" || raw === "0") return raw;
|
|
307
|
+
return quoteSqlString(raw);
|
|
308
|
+
}
|
|
309
|
+
if (col.logicalType === "DATE" || col.logicalType === "DATETIME") {
|
|
310
|
+
if (upper === "NOW")
|
|
311
|
+
return style.nowKeyword ?? "CURRENT_TIMESTAMP";
|
|
312
|
+
return quoteSqlString(raw);
|
|
313
|
+
}
|
|
314
|
+
if (raw.startsWith("'") && raw.endsWith("'") || raw.startsWith('"') && raw.endsWith('"'))
|
|
315
|
+
return raw;
|
|
316
|
+
return quoteSqlString(raw);
|
|
317
|
+
};
|
|
318
|
+
return { quoteIdentifier: quoteIdentifier2, toDefaultExpression: toDefaultExpression2 };
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function createDefaultDbMetaAdapter(overrides) {
|
|
323
|
+
const base = buildDbMetaAdapterFromMetas(BUILTIN_DIALECT_METAS_JSON);
|
|
324
|
+
return { ...base, ...overrides };
|
|
325
|
+
}
|
|
326
|
+
var defaultDbMetaAdapter = createDefaultDbMetaAdapter();
|
|
327
|
+
function resolveDbMetaAdapter(options) {
|
|
328
|
+
if (options?.dbMetaAdapter) return options.dbMetaAdapter;
|
|
329
|
+
if (options?.hostMetas && options.hostMetas.length > 0) {
|
|
330
|
+
return buildDbMetaAdapterFromMetas(resolveDialectMetas(options));
|
|
331
|
+
}
|
|
332
|
+
return defaultDbMetaAdapter;
|
|
333
|
+
}
|
|
334
|
+
function defaultPhysicalType(dialect, logicalType, options) {
|
|
335
|
+
return resolveDbMetaAdapter(options).getDefaultPhysicalType(
|
|
336
|
+
dialect,
|
|
337
|
+
logicalType
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
function normalizePhysicalCompare(s) {
|
|
341
|
+
return s.trim().toUpperCase().replace(/\s+/g, " ");
|
|
342
|
+
}
|
|
343
|
+
function physicalTypeBaseName(s) {
|
|
344
|
+
const n = normalizePhysicalCompare(s);
|
|
345
|
+
const p = n.indexOf("(");
|
|
346
|
+
return p >= 0 ? n.slice(0, p).trim() : n;
|
|
347
|
+
}
|
|
348
|
+
function inferLogicalTypeFromPhysical(dialect, physicalType, options) {
|
|
349
|
+
const raw = physicalType?.trim();
|
|
350
|
+
if (!raw) return "TEXT";
|
|
351
|
+
const metas = resolveDialectMetas(options);
|
|
352
|
+
const meta = metas.find((m) => m.id === dialect);
|
|
353
|
+
const normFull = normalizePhysicalCompare(raw);
|
|
354
|
+
const base = physicalTypeBaseName(raw);
|
|
355
|
+
if (meta) {
|
|
356
|
+
for (const lt of meta.logicalTypes) {
|
|
357
|
+
if (normalizePhysicalCompare(lt.defaultPhysicalType) === normFull) {
|
|
358
|
+
return lt.id;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
for (const lt of meta.logicalTypes) {
|
|
362
|
+
if (physicalTypeBaseName(lt.defaultPhysicalType) === base) {
|
|
363
|
+
return lt.id;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const b = base;
|
|
368
|
+
if (b === "INT" || b === "INTEGER" || b === "BIGINT" || b === "SMALLINT" || b === "TINYINT" || b === "NUMBER" || b === "SERIAL" || b === "SERIAL4" || b === "SERIAL8") {
|
|
369
|
+
return "NUMBER";
|
|
370
|
+
}
|
|
371
|
+
if (b === "DECIMAL" || b === "NUMERIC" || b === "MONEY" || b === "SMALLMONEY") {
|
|
372
|
+
return "DECIMAL";
|
|
373
|
+
}
|
|
374
|
+
if (b === "FLOAT" || b === "REAL" || b === "DOUBLE" || b === "BINARY_FLOAT" || b === "BINARY_DOUBLE") {
|
|
375
|
+
return "FLOAT";
|
|
376
|
+
}
|
|
377
|
+
if (b === "BIT" || b === "BOOLEAN" || b === "BOOL") {
|
|
378
|
+
return "BOOLEAN";
|
|
379
|
+
}
|
|
380
|
+
if (b === "DATETIME" || b === "DATETIME2" || b === "SMALLDATETIME" || b === "TIMESTAMP" || b === "TIMESTAMPTZ") {
|
|
381
|
+
return "DATETIME";
|
|
382
|
+
}
|
|
383
|
+
if (b === "DATE") return "DATE";
|
|
384
|
+
if (b === "TIME" || b === "TIMETZ") return "TIME";
|
|
385
|
+
if (b.includes("CHAR") || b === "TEXT" || b === "CLOB" || b === "NCLOB" || b === "NCHAR" || b === "NVARCHAR" || b === "VARCHAR" || b === "VARCHAR2") {
|
|
386
|
+
return "TEXT";
|
|
387
|
+
}
|
|
388
|
+
if (b === "JSON" || b === "JSONB") return "JSON";
|
|
389
|
+
if (b === "UUID" || b === "UNIQUEIDENTIFIER") return "UUID";
|
|
390
|
+
if (b === "BINARY" || b === "VARBINARY" || b === "RAW" || b === "BYTEA" || b === "BLOB" || b === "IMAGE") {
|
|
391
|
+
return "BINARY";
|
|
392
|
+
}
|
|
393
|
+
return "TEXT";
|
|
394
|
+
}
|
|
395
|
+
function getRdbmsDialectCapability(dialect, options) {
|
|
396
|
+
const meta = resolveDbMetaAdapter(options).getDialectMeta(dialect);
|
|
397
|
+
return meta?.capabilities ?? { supportsSchema: false };
|
|
398
|
+
}
|
|
399
|
+
function dialectSupportsSchema(dialect, options) {
|
|
400
|
+
return getRdbmsDialectCapability(dialect, options).supportsSchema;
|
|
401
|
+
}
|
|
402
|
+
function parseTypeArguments(physicalType) {
|
|
403
|
+
const m = physicalType.match(/\(([^)]+)\)/);
|
|
404
|
+
if (!m) return null;
|
|
405
|
+
const values = m[1].split(",").map((v) => Number.parseInt(v.trim(), 10)).filter((v) => Number.isFinite(v));
|
|
406
|
+
return values.length > 0 ? values : null;
|
|
407
|
+
}
|
|
408
|
+
function convertPhysicalTypeByLogicalType(physicalType, logicalType, nextDialect, options) {
|
|
409
|
+
const args = parseTypeArguments(physicalType);
|
|
410
|
+
const precision = args?.[0];
|
|
411
|
+
const scale = args?.[1];
|
|
412
|
+
const length = args?.[0];
|
|
413
|
+
if (logicalType === "NUMBER") {
|
|
414
|
+
if (nextDialect === "oracle") {
|
|
415
|
+
if (precision && scale !== void 0)
|
|
416
|
+
return `NUMBER(${precision},${scale})`;
|
|
417
|
+
if (precision) return `NUMBER(${precision})`;
|
|
418
|
+
return "NUMBER(10)";
|
|
419
|
+
}
|
|
420
|
+
if (nextDialect === "mssql") {
|
|
421
|
+
if (precision && scale !== void 0)
|
|
422
|
+
return `NUMERIC(${precision},${scale})`;
|
|
423
|
+
if (precision) return `NUMERIC(${precision},0)`;
|
|
424
|
+
return "INT";
|
|
425
|
+
}
|
|
426
|
+
if (nextDialect === "mysql") {
|
|
427
|
+
if (precision && scale !== void 0)
|
|
428
|
+
return `DECIMAL(${precision},${scale})`;
|
|
429
|
+
if (precision) return `DECIMAL(${precision},0)`;
|
|
430
|
+
return "INT";
|
|
431
|
+
}
|
|
432
|
+
if (nextDialect === "postgres") {
|
|
433
|
+
if (precision && scale !== void 0)
|
|
434
|
+
return `NUMERIC(${precision},${scale})`;
|
|
435
|
+
if (precision) return `NUMERIC(${precision},0)`;
|
|
436
|
+
return "INTEGER";
|
|
437
|
+
}
|
|
438
|
+
return "INTEGER";
|
|
439
|
+
}
|
|
440
|
+
if (logicalType === "DECIMAL") {
|
|
441
|
+
if (precision && scale !== void 0) {
|
|
442
|
+
if (nextDialect === "oracle")
|
|
443
|
+
return `NUMBER(${precision},${scale})`;
|
|
444
|
+
if (nextDialect === "postgres")
|
|
445
|
+
return `NUMERIC(${precision},${scale})`;
|
|
446
|
+
return `DECIMAL(${precision},${scale})`;
|
|
447
|
+
}
|
|
448
|
+
return defaultPhysicalType(nextDialect, logicalType, options);
|
|
449
|
+
}
|
|
450
|
+
if (logicalType === "FLOAT") {
|
|
451
|
+
if (nextDialect === "oracle") return "BINARY_FLOAT";
|
|
452
|
+
if (nextDialect === "sqlite") return "REAL";
|
|
453
|
+
return "FLOAT";
|
|
454
|
+
}
|
|
455
|
+
if (logicalType === "TEXT") {
|
|
456
|
+
if (nextDialect === "oracle")
|
|
457
|
+
return length ? `VARCHAR2(${length})` : "VARCHAR2(255)";
|
|
458
|
+
if (nextDialect === "mssql")
|
|
459
|
+
return length ? `NVARCHAR(${length})` : "NVARCHAR(255)";
|
|
460
|
+
if (nextDialect === "mysql")
|
|
461
|
+
return length ? `VARCHAR(${length})` : "VARCHAR(255)";
|
|
462
|
+
if (nextDialect === "postgres")
|
|
463
|
+
return length ? `VARCHAR(${length})` : "VARCHAR(255)";
|
|
464
|
+
return "TEXT";
|
|
465
|
+
}
|
|
466
|
+
if (logicalType === "BINARY") {
|
|
467
|
+
if (nextDialect === "mssql")
|
|
468
|
+
return length ? `VARBINARY(${length})` : "VARBINARY(255)";
|
|
469
|
+
if (nextDialect === "oracle")
|
|
470
|
+
return length ? `RAW(${length})` : "RAW(255)";
|
|
471
|
+
if (nextDialect === "mysql")
|
|
472
|
+
return length ? `VARBINARY(${length})` : "VARBINARY(255)";
|
|
473
|
+
if (nextDialect === "postgres") return "BYTEA";
|
|
474
|
+
return "BLOB";
|
|
475
|
+
}
|
|
476
|
+
return defaultPhysicalType(nextDialect, logicalType, options);
|
|
477
|
+
}
|
|
478
|
+
function convertDesignDialect(doc, nextDialect, options) {
|
|
479
|
+
if (doc.model.dialect === nextDialect) return doc;
|
|
480
|
+
const supportsSchema = dialectSupportsSchema(nextDialect, options);
|
|
481
|
+
return {
|
|
482
|
+
...doc,
|
|
483
|
+
model: {
|
|
484
|
+
...doc.model,
|
|
485
|
+
dialect: nextDialect,
|
|
486
|
+
tables: doc.model.tables.map((table) => ({
|
|
487
|
+
...table,
|
|
488
|
+
schemaName: supportsSchema ? table.schemaName : void 0,
|
|
489
|
+
columns: table.columns.map((col) => ({
|
|
490
|
+
...col,
|
|
491
|
+
physicalType: convertPhysicalTypeByLogicalType(
|
|
492
|
+
col.physicalType,
|
|
493
|
+
col.logicalType,
|
|
494
|
+
nextDialect,
|
|
495
|
+
options
|
|
496
|
+
)
|
|
497
|
+
}))
|
|
498
|
+
}))
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function applyLogicalTypeChange(column, nextLogicalType, dialect, options) {
|
|
503
|
+
return {
|
|
504
|
+
...column,
|
|
505
|
+
logicalType: nextLogicalType,
|
|
506
|
+
physicalType: defaultPhysicalType(dialect, nextLogicalType, options)
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function createColumn(dialect, params, options) {
|
|
510
|
+
const physicalName = params.physicalName ?? params.logicalName;
|
|
511
|
+
const isPrimaryKey = params.isPrimaryKey ?? false;
|
|
512
|
+
const isForeignKey = params.isForeignKey ?? false;
|
|
513
|
+
const col = {
|
|
514
|
+
id: params.id,
|
|
515
|
+
logicalName: params.logicalName,
|
|
516
|
+
physicalName,
|
|
517
|
+
description: params.description,
|
|
518
|
+
logicalType: params.logicalType,
|
|
519
|
+
physicalType: defaultPhysicalType(dialect, params.logicalType, options),
|
|
520
|
+
defaultValue: params.defaultValue,
|
|
521
|
+
nullable: isPrimaryKey ? false : params.nullable ?? true,
|
|
522
|
+
isPrimaryKey,
|
|
523
|
+
isForeignKey,
|
|
524
|
+
referencesPrimaryColumnId: params.referencesPrimaryColumnId
|
|
525
|
+
};
|
|
526
|
+
if (params.color !== void 0) col.color = params.color;
|
|
527
|
+
return col;
|
|
528
|
+
}
|
|
529
|
+
function createEmptyDesign(dialect = "mssql") {
|
|
530
|
+
return {
|
|
531
|
+
schemaVersion: 1,
|
|
532
|
+
model: {
|
|
533
|
+
dialect,
|
|
534
|
+
tables: [],
|
|
535
|
+
relationships: [],
|
|
536
|
+
indexes: []
|
|
537
|
+
},
|
|
538
|
+
layout: {
|
|
539
|
+
nodePositions: {}
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function serializeDesign(doc) {
|
|
544
|
+
return JSON.stringify(doc, null, 2);
|
|
545
|
+
}
|
|
546
|
+
function parseDesign(json, options) {
|
|
547
|
+
const parsed = JSON.parse(json);
|
|
548
|
+
return validateDesignDocument(parsed, options);
|
|
549
|
+
}
|
|
550
|
+
function validateDesignDocument(input, options) {
|
|
551
|
+
if (!isObject(input)) {
|
|
552
|
+
throw new Error("Invalid design document: root must be object");
|
|
553
|
+
}
|
|
554
|
+
if (input.schemaVersion !== 1) {
|
|
555
|
+
throw new Error("Unsupported schemaVersion");
|
|
556
|
+
}
|
|
557
|
+
if (!isObject(input.model) || !Array.isArray(input.model.tables) || !Array.isArray(input.model.relationships) || !Array.isArray(input.model.indexes)) {
|
|
558
|
+
throw new Error("Invalid design document: model is malformed");
|
|
559
|
+
}
|
|
560
|
+
const dialects = new Set(
|
|
561
|
+
resolveDbMetaAdapter(options).listDialects().map((d) => d.id)
|
|
562
|
+
);
|
|
563
|
+
const modelDialect = input.model.dialect;
|
|
564
|
+
if (typeof modelDialect !== "string" || !dialects.has(modelDialect)) {
|
|
565
|
+
throw new Error("Invalid design document: model.dialect is invalid");
|
|
566
|
+
}
|
|
567
|
+
if (!isObject(input.layout) || !isObject(input.layout.nodePositions)) {
|
|
568
|
+
throw new Error("Invalid design document: layout is malformed");
|
|
569
|
+
}
|
|
570
|
+
const doc = input;
|
|
571
|
+
migrateLegacyFkLineVisibility(doc.model);
|
|
572
|
+
return doc;
|
|
573
|
+
}
|
|
574
|
+
function roundTripDesign(doc) {
|
|
575
|
+
return parseDesign(serializeDesign(doc));
|
|
576
|
+
}
|
|
577
|
+
function quoteIdentifier(dialect, identifier, options) {
|
|
578
|
+
const quote = resolveDbMetaAdapter(options).getDdlRules(dialect).quoteIdentifier ?? defaultDdlRules().quoteIdentifier;
|
|
579
|
+
return quote(dialect, identifier);
|
|
580
|
+
}
|
|
581
|
+
function qualifiedTableName(table, dialect, options) {
|
|
582
|
+
const schema = table.schemaName?.trim();
|
|
583
|
+
if (dialectSupportsSchema(dialect, options) && schema) {
|
|
584
|
+
return `${quoteIdentifier(dialect, schema, options)}.${quoteIdentifier(dialect, table.physicalName, options)}`;
|
|
585
|
+
}
|
|
586
|
+
return quoteIdentifier(dialect, table.physicalName, options);
|
|
587
|
+
}
|
|
588
|
+
function quoteSqlString(value) {
|
|
589
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
590
|
+
}
|
|
591
|
+
function toDefaultExpression(dialect, col, options) {
|
|
592
|
+
const handler = resolveDbMetaAdapter(options).getDdlRules(dialect).toDefaultExpression ?? defaultDdlRules().toDefaultExpression;
|
|
593
|
+
return handler(dialect, col);
|
|
594
|
+
}
|
|
595
|
+
function joinColumnDefs(table, dialect, options) {
|
|
596
|
+
const defs = table.columns.map((col) => {
|
|
597
|
+
const nullable = col.nullable ? "NULL" : "NOT NULL";
|
|
598
|
+
const defaultExpr = toDefaultExpression(dialect, col, options);
|
|
599
|
+
const defaultSql = defaultExpr ? ` DEFAULT ${defaultExpr}` : "";
|
|
600
|
+
return ` ${quoteIdentifier(dialect, col.physicalName, options)} ${col.physicalType}${defaultSql} ${nullable}`;
|
|
601
|
+
});
|
|
602
|
+
const pkColumns = table.columns.filter((col) => col.isPrimaryKey).map((col) => quoteIdentifier(dialect, col.physicalName, options));
|
|
603
|
+
if (pkColumns.length > 0) {
|
|
604
|
+
defs.push(` PRIMARY KEY (${pkColumns.join(", ")})`);
|
|
605
|
+
}
|
|
606
|
+
return defs;
|
|
607
|
+
}
|
|
608
|
+
function createTableSql(table, dialect, options) {
|
|
609
|
+
const columns = joinColumnDefs(table, dialect, options);
|
|
610
|
+
return `CREATE TABLE ${qualifiedTableName(table, dialect, options)} (
|
|
611
|
+
${columns.join(",\n")}
|
|
612
|
+
);`;
|
|
613
|
+
}
|
|
614
|
+
function createRelationshipSql(rel, model, dialect, index, options) {
|
|
615
|
+
const sourceTable = model.tables.find(
|
|
616
|
+
(table) => table.id === rel.sourceTableId
|
|
617
|
+
);
|
|
618
|
+
const targetTable = model.tables.find(
|
|
619
|
+
(table) => table.id === rel.targetTableId
|
|
620
|
+
);
|
|
621
|
+
if (!sourceTable || !targetTable || !rel.sourceColumnId || !rel.targetColumnId) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
const sourceColumn = sourceTable.columns.find(
|
|
625
|
+
(col) => col.id === rel.sourceColumnId
|
|
626
|
+
);
|
|
627
|
+
const targetColumn = targetTable.columns.find(
|
|
628
|
+
(col) => col.id === rel.targetColumnId
|
|
629
|
+
);
|
|
630
|
+
if (!sourceColumn || !targetColumn) {
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
const fkName = `FK_${targetTable.physicalName}_${sourceTable.physicalName}_${index + 1}`;
|
|
634
|
+
return [
|
|
635
|
+
`ALTER TABLE ${qualifiedTableName(targetTable, dialect, options)}`,
|
|
636
|
+
` ADD CONSTRAINT ${quoteIdentifier(dialect, fkName, options)}`,
|
|
637
|
+
` FOREIGN KEY (${quoteIdentifier(dialect, targetColumn.physicalName, options)})`,
|
|
638
|
+
` REFERENCES ${qualifiedTableName(sourceTable, dialect, options)} (${quoteIdentifier(dialect, sourceColumn.physicalName, options)});`
|
|
639
|
+
].join("\n");
|
|
640
|
+
}
|
|
641
|
+
function createIndexSql(indexModel, model, dialect, options) {
|
|
642
|
+
const table = model.tables.find((item) => item.id === indexModel.tableId);
|
|
643
|
+
if (!table || indexModel.columns.length === 0) {
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
const unique = indexModel.unique ? "UNIQUE " : "";
|
|
647
|
+
const columns = indexModel.columns.map((col) => quoteIdentifier(dialect, col, options)).join(", ");
|
|
648
|
+
return `CREATE ${unique}INDEX ${quoteIdentifier(dialect, indexModel.name, options)} ON ${qualifiedTableName(table, dialect, options)} (${columns});`;
|
|
649
|
+
}
|
|
650
|
+
function buildDdlSql(doc, options) {
|
|
651
|
+
const { model } = doc;
|
|
652
|
+
const tableSql = model.tables.map(
|
|
653
|
+
(table) => createTableSql(table, model.dialect, options)
|
|
654
|
+
);
|
|
655
|
+
const relSql = model.relationships.map(
|
|
656
|
+
(rel, index) => createRelationshipSql(rel, model, model.dialect, index, options)
|
|
657
|
+
).filter((item) => Boolean(item));
|
|
658
|
+
return [...tableSql, ...relSql].join("\n\n");
|
|
659
|
+
}
|
|
660
|
+
function buildIndexDdlSql(doc, options) {
|
|
661
|
+
const { model } = doc;
|
|
662
|
+
const statements = model.indexes.map((index) => createIndexSql(index, model, model.dialect, options)).filter((item) => Boolean(item));
|
|
663
|
+
return statements.join("\n\n");
|
|
664
|
+
}
|
|
665
|
+
function sliceDocByScope(doc, scope) {
|
|
666
|
+
if (scope.kind === "all") return doc;
|
|
667
|
+
const selected = new Set(scope.tableIds);
|
|
668
|
+
return {
|
|
669
|
+
...doc,
|
|
670
|
+
model: {
|
|
671
|
+
...doc.model,
|
|
672
|
+
tables: doc.model.tables.filter((t) => selected.has(t.id)),
|
|
673
|
+
relationships: doc.model.relationships.filter(
|
|
674
|
+
(r) => selected.has(r.sourceTableId) && selected.has(r.targetTableId)
|
|
675
|
+
),
|
|
676
|
+
indexes: doc.model.indexes.filter((i) => selected.has(i.tableId))
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function styleBasedDdlGenerator(input, options) {
|
|
681
|
+
const scoped = sliceDocByScope(input.doc, input.scope);
|
|
682
|
+
return {
|
|
683
|
+
sql: buildDdlSql(scoped, options),
|
|
684
|
+
diagnostics: analyzeDdlDocument(scoped, options)
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function hasBuiltinDialect(dialectId) {
|
|
688
|
+
return BUILTIN_DIALECTS.includes(dialectId);
|
|
689
|
+
}
|
|
690
|
+
function getBuiltinDdlGenerator(dialectId, _options) {
|
|
691
|
+
if (!hasBuiltinDialect(dialectId)) return void 0;
|
|
692
|
+
return (input) => styleBasedDdlGenerator(input, _options);
|
|
693
|
+
}
|
|
694
|
+
function invokeDdlGeneratorSync(generator, input) {
|
|
695
|
+
const result = generator(input);
|
|
696
|
+
if (result && typeof result.then === "function") {
|
|
697
|
+
throw new Error("Async DDL generator is not supported in sync API");
|
|
698
|
+
}
|
|
699
|
+
return result;
|
|
700
|
+
}
|
|
701
|
+
function runDdlGenerator(doc, scope, options) {
|
|
702
|
+
const input = {
|
|
703
|
+
doc,
|
|
704
|
+
dialectId: doc.model.dialect,
|
|
705
|
+
scope
|
|
706
|
+
};
|
|
707
|
+
const hostGenerator = options?.hostDdlGenerators?.[doc.model.dialect];
|
|
708
|
+
const builtinGenerator = getBuiltinDdlGenerator(doc.model.dialect, options);
|
|
709
|
+
const fallback = () => styleBasedDdlGenerator(input, options);
|
|
710
|
+
const fallbackOnError = options?.fallbackOnHookError ?? true;
|
|
711
|
+
const selectedGenerator = hostGenerator ?? builtinGenerator;
|
|
712
|
+
if (!selectedGenerator) return fallback();
|
|
713
|
+
try {
|
|
714
|
+
return invokeDdlGeneratorSync(selectedGenerator, input);
|
|
715
|
+
} catch (error) {
|
|
716
|
+
if (!fallbackOnError) throw error;
|
|
717
|
+
return fallback();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function formatDdlDiagnostic(d) {
|
|
721
|
+
const level = d.severity === "error" ? "ERROR" : "WARN";
|
|
722
|
+
const tail = d.context ? ` | ${d.context}` : "";
|
|
723
|
+
return `[${level}][${d.code}] ${d.message}${tail}`;
|
|
724
|
+
}
|
|
725
|
+
function formatDdlDiagnostics(diagnostics) {
|
|
726
|
+
return diagnostics.map(formatDdlDiagnostic).join("\n");
|
|
727
|
+
}
|
|
728
|
+
function analyzeDdlDocument(doc, options) {
|
|
729
|
+
const out = [];
|
|
730
|
+
const { model } = doc;
|
|
731
|
+
const tableById = new Map(model.tables.map((t) => [t.id, t]));
|
|
732
|
+
const physicalTableNames = /* @__PURE__ */ new Map();
|
|
733
|
+
for (const table of model.tables) {
|
|
734
|
+
const qualifiedName = dialectSupportsSchema(model.dialect, options) && table.schemaName?.trim() ? `${table.schemaName.trim()}.${table.physicalName}` : table.physicalName;
|
|
735
|
+
const list = physicalTableNames.get(qualifiedName) ?? [];
|
|
736
|
+
list.push(table.id);
|
|
737
|
+
physicalTableNames.set(qualifiedName, list);
|
|
738
|
+
if (table.columns.length === 0) {
|
|
739
|
+
out.push({
|
|
740
|
+
severity: "warning",
|
|
741
|
+
code: "DDL_EMPTY_TABLE",
|
|
742
|
+
message: "\uCEEC\uB7FC\uC774 \uC5C6\uB294 \uD14C\uC774\uBE14\uC740 CREATE TABLE \uAD6C\uBB38\uC774 \uBE44\uC5B4 \uC788\uAC70\uB098 \uBB34\uC758\uBBF8\uD560 \uC218 \uC788\uB2E4.",
|
|
743
|
+
context: `table:${table.id}`
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
for (const [name, ids] of physicalTableNames) {
|
|
748
|
+
if (ids.length > 1) {
|
|
749
|
+
out.push({
|
|
750
|
+
severity: "warning",
|
|
751
|
+
code: "DDL_DUPLICATE_TABLE_NAME",
|
|
752
|
+
message: `\uB3D9\uC77C\uD55C \uBB3C\uB9AC \uD14C\uC774\uBE14\uBA85 "${name}"\uC774 ${ids.length}\uAC1C \uD14C\uC774\uBE14\uC5D0 \uC0AC\uC6A9\uB418\uC5C8\uB2E4.`,
|
|
753
|
+
context: `tables:${ids.join(",")}`
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
for (const rel of model.relationships) {
|
|
758
|
+
const ctx = `relationship:${rel.id}`;
|
|
759
|
+
const sourceTable = tableById.get(rel.sourceTableId);
|
|
760
|
+
const targetTable = tableById.get(rel.targetTableId);
|
|
761
|
+
if (!sourceTable || !targetTable) {
|
|
762
|
+
out.push({
|
|
763
|
+
severity: "error",
|
|
764
|
+
code: "DDL_REL_UNKNOWN_TABLE",
|
|
765
|
+
message: "\uAD00\uACC4\uC758 \uC18C\uC2A4 \uB610\uB294 \uD0C0\uAC9F \uD14C\uC774\uBE14\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 FK DDL\uC744 \uC0DD\uC131\uD560 \uC218 \uC5C6\uB2E4.",
|
|
766
|
+
context: ctx
|
|
767
|
+
});
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (!rel.sourceColumnId || !rel.targetColumnId) {
|
|
771
|
+
out.push({
|
|
772
|
+
severity: "warning",
|
|
773
|
+
code: "DDL_REL_MISSING_COLUMNS",
|
|
774
|
+
message: "\uC18C\uC2A4/\uD0C0\uAC9F \uCEEC\uB7FC\uC774 \uC9C0\uC815\uB418\uC9C0 \uC54A\uC544 FK DDL\uC744 \uC0DD\uB7B5\uD55C\uB2E4.",
|
|
775
|
+
context: ctx
|
|
776
|
+
});
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
const sourceColumn = sourceTable.columns.find(
|
|
780
|
+
(c) => c.id === rel.sourceColumnId
|
|
781
|
+
);
|
|
782
|
+
const targetColumn = targetTable.columns.find(
|
|
783
|
+
(c) => c.id === rel.targetColumnId
|
|
784
|
+
);
|
|
785
|
+
if (!sourceColumn || !targetColumn) {
|
|
786
|
+
out.push({
|
|
787
|
+
severity: "warning",
|
|
788
|
+
code: "DDL_REL_UNKNOWN_COLUMN",
|
|
789
|
+
message: "\uAD00\uACC4\uC5D0 \uC9C0\uC815\uB41C \uCEEC\uB7FC id\uB97C \uD14C\uC774\uBE14\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uC5B4 FK DDL\uC744 \uC0DD\uB7B5\uD55C\uB2E4.",
|
|
790
|
+
context: ctx
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
for (const indexModel of model.indexes) {
|
|
795
|
+
const ctx = `index:${indexModel.id}`;
|
|
796
|
+
const table = tableById.get(indexModel.tableId);
|
|
797
|
+
if (!table) {
|
|
798
|
+
out.push({
|
|
799
|
+
severity: "warning",
|
|
800
|
+
code: "IDX_UNKNOWN_TABLE",
|
|
801
|
+
message: "\uC778\uB371\uC2A4\uAC00 \uAC00\uB9AC\uD0A4\uB294 \uD14C\uC774\uBE14\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uC778\uB371\uC2A4 DDL\uC744 \uC0DD\uB7B5\uD55C\uB2E4.",
|
|
802
|
+
context: ctx
|
|
803
|
+
});
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
if (indexModel.columns.length === 0) {
|
|
807
|
+
out.push({
|
|
808
|
+
severity: "warning",
|
|
809
|
+
code: "IDX_EMPTY_COLUMNS",
|
|
810
|
+
message: "\uC778\uB371\uC2A4 \uCEEC\uB7FC \uBAA9\uB85D\uC774 \uBE44\uC5B4 \uC788\uC5B4 \uC778\uB371\uC2A4 DDL\uC744 \uC0DD\uB7B5\uD55C\uB2E4.",
|
|
811
|
+
context: ctx
|
|
812
|
+
});
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
for (const colPhys of indexModel.columns) {
|
|
816
|
+
if (!table.columns.some((c) => c.physicalName === colPhys)) {
|
|
817
|
+
out.push({
|
|
818
|
+
severity: "warning",
|
|
819
|
+
code: "IDX_UNKNOWN_COLUMN",
|
|
820
|
+
message: `\uC778\uB371\uC2A4\uAC00 \uCC38\uC870\uD558\uB294 \uBB3C\uB9AC \uCEEC\uB7FC\uBA85 "${colPhys}"\uC744(\uB97C) \uD14C\uC774\uBE14 "${table.physicalName}"\uC5D0\uC11C \uCC3E\uC744 \uC218 \uC5C6\uB2E4.`,
|
|
821
|
+
context: ctx
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
return out;
|
|
827
|
+
}
|
|
828
|
+
function generateDdlWithDiagnostics(doc, options) {
|
|
829
|
+
const out = runDdlGenerator(doc, { kind: "all" }, options);
|
|
830
|
+
return {
|
|
831
|
+
sql: out.sql,
|
|
832
|
+
diagnostics: out.diagnostics ?? analyzeDdlDocument(doc, options)
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function generateIndexDdlWithDiagnostics(doc, options) {
|
|
836
|
+
return {
|
|
837
|
+
sql: buildIndexDdlSql(doc, options),
|
|
838
|
+
diagnostics: analyzeDdlDocument(doc, options)
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
function generateDdl(doc, options) {
|
|
842
|
+
return runDdlGenerator(doc, { kind: "all" }, options).sql;
|
|
843
|
+
}
|
|
844
|
+
function generateDdlForSelection(doc, tableIds, options) {
|
|
845
|
+
const out = runDdlGenerator(doc, { kind: "selected", tableIds }, options);
|
|
846
|
+
const diagnostics = out.diagnostics ?? analyzeDdlDocument(
|
|
847
|
+
sliceDocByScope(doc, { kind: "selected", tableIds }),
|
|
848
|
+
options
|
|
849
|
+
);
|
|
850
|
+
return { sql: out.sql, diagnostics };
|
|
851
|
+
}
|
|
852
|
+
function generateIndexDdl(doc, options) {
|
|
853
|
+
return buildIndexDdlSql(doc, options);
|
|
854
|
+
}
|
|
855
|
+
function createLargeDesign(tableCount, dialect = "postgres") {
|
|
856
|
+
const doc = createEmptyDesign(dialect);
|
|
857
|
+
const cols = 20;
|
|
858
|
+
for (let i = 0; i < tableCount; i++) {
|
|
859
|
+
const id = `t-${i}`;
|
|
860
|
+
doc.model.tables.push({
|
|
861
|
+
id,
|
|
862
|
+
logicalName: `\uC5D4\uD2F0\uD2F0${i}`,
|
|
863
|
+
physicalName: `TB_T${i}`,
|
|
864
|
+
columns: [
|
|
865
|
+
createColumn(dialect, {
|
|
866
|
+
id: `${id}-pk`,
|
|
867
|
+
logicalName: "\uC2DD\uBCC4\uC790",
|
|
868
|
+
logicalType: "NUMBER",
|
|
869
|
+
nullable: false
|
|
870
|
+
})
|
|
871
|
+
]
|
|
872
|
+
});
|
|
873
|
+
doc.layout.nodePositions[id] = {
|
|
874
|
+
x: i % cols * 220,
|
|
875
|
+
y: Math.floor(i / cols) * 120
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
return doc;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export { BUILTIN_DIALECTS, BUILTIN_DIALECT_METAS_JSON, LOGICAL_DATA_TYPES, alignNodePositions, analyzeDdlDocument, applyLogicalTypeChange, convertDesignDialect, createColumn, createDefaultDbMetaAdapter, createEmptyDesign, createLargeDesign, defaultDbMetaAdapter, defaultPhysicalType, dialectSupportsSchema, formatDdlDiagnostic, formatDdlDiagnostics, generateDdl, generateDdlForSelection, generateDdlWithDiagnostics, generateIndexDdl, generateIndexDdlWithDiagnostics, getRdbmsDialectCapability, inferLogicalTypeFromPhysical, isRelationshipLineRenderable, mergeDialectMetas, migrateLegacyFkLineVisibility, parseDesign, resolveDialectMetas, roundTripDesign, serializeDesign, validateDesignDocument };
|
|
882
|
+
//# sourceMappingURL=index.js.map
|
|
883
|
+
//# sourceMappingURL=index.js.map
|