metal-orm 1.1.27 → 1.1.29
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.cjs +312 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +312 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/core/ddl/dialects/render-reference.test.ts +13 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -0
- package/src/core/ddl/introspect/sqlite-foreign-key-ddl.ts +321 -0
- package/src/core/ddl/introspect/sqlite.ts +16 -2
- package/src/core/ddl/schema-diff.ts +26 -2
- package/src/core/ddl/schema-generator.ts +6 -1
- package/src/core/ddl/schema-types.ts +1 -0
package/dist/index.d.cts
CHANGED
|
@@ -6232,6 +6232,7 @@ interface ColumnDiff {
|
|
|
6232
6232
|
nullabilityChanged?: boolean;
|
|
6233
6233
|
defaultChanged?: boolean;
|
|
6234
6234
|
autoIncrementChanged?: boolean;
|
|
6235
|
+
referenceChanged?: boolean;
|
|
6235
6236
|
}
|
|
6236
6237
|
/** Represents a column in the database schema. */
|
|
6237
6238
|
interface DatabaseColumn {
|
package/dist/index.d.ts
CHANGED
|
@@ -6232,6 +6232,7 @@ interface ColumnDiff {
|
|
|
6232
6232
|
nullabilityChanged?: boolean;
|
|
6233
6233
|
defaultChanged?: boolean;
|
|
6234
6234
|
autoIncrementChanged?: boolean;
|
|
6235
|
+
referenceChanged?: boolean;
|
|
6235
6236
|
}
|
|
6236
6237
|
/** Represents a column in the database schema. */
|
|
6237
6238
|
interface DatabaseColumn {
|
package/dist/index.js
CHANGED
|
@@ -11155,7 +11155,10 @@ var renderColumnDefinition = (table, col2, dialect, options = {}) => {
|
|
|
11155
11155
|
parts.push(`CHECK (${col2.check})`);
|
|
11156
11156
|
}
|
|
11157
11157
|
if (col2.references) {
|
|
11158
|
-
|
|
11158
|
+
const referenceSql = dialect.renderReference(col2.references, table);
|
|
11159
|
+
parts.push(
|
|
11160
|
+
col2.references.name ? `CONSTRAINT ${dialect.quoteIdentifier(col2.references.name)} ${referenceSql}` : referenceSql
|
|
11161
|
+
);
|
|
11159
11162
|
}
|
|
11160
11163
|
return { sql: parts.join(" "), inlinePrimary: !!(options.includePrimary && col2.primary) };
|
|
11161
11164
|
};
|
|
@@ -11276,6 +11279,11 @@ var normalizeDefault = (value) => {
|
|
|
11276
11279
|
if (value === void 0 || value === null) return void 0;
|
|
11277
11280
|
return String(value).trim();
|
|
11278
11281
|
};
|
|
11282
|
+
var normalizeReferenceAction = (value) => (value || "NO ACTION").toUpperCase().replace(/\s+/g, " ").trim();
|
|
11283
|
+
var sameReference = (expected, actual) => {
|
|
11284
|
+
if (!expected || !actual) return expected === actual;
|
|
11285
|
+
return expected.table === actual.table && expected.column === actual.column && expected.name === actual.name && normalizeReferenceAction(expected.onDelete) === normalizeReferenceAction(actual.onDelete) && normalizeReferenceAction(expected.onUpdate) === normalizeReferenceAction(actual.onUpdate) && !!expected.deferrable === !!actual.deferrable;
|
|
11286
|
+
};
|
|
11279
11287
|
var diffColumn = (expected, actual, dialect) => {
|
|
11280
11288
|
const expectedType = normalizeType(dialect.renderColumnType(expected));
|
|
11281
11289
|
const actualType = normalizeType(actual.type);
|
|
@@ -11285,7 +11293,8 @@ var diffColumn = (expected, actual, dialect) => {
|
|
|
11285
11293
|
typeChanged: expectedType !== actualType,
|
|
11286
11294
|
nullabilityChanged: !!expected.notNull !== !!actual.notNull,
|
|
11287
11295
|
defaultChanged: expectedDefault !== actualDefault,
|
|
11288
|
-
autoIncrementChanged: !!expected.autoIncrement !== !!actual.autoIncrement
|
|
11296
|
+
autoIncrementChanged: !!expected.autoIncrement !== !!actual.autoIncrement,
|
|
11297
|
+
referenceChanged: !sameReference(expected.references, actual.references)
|
|
11289
11298
|
};
|
|
11290
11299
|
};
|
|
11291
11300
|
var unsupportedMutationWarning = (dialect, operation, target) => `Dialect "${dialect.name}" does not provide the ${operation} capability for ${target}; manual migration is required.`;
|
|
@@ -11322,6 +11331,11 @@ var diffSchema = (expectedTables, actualSchema, dialect, options = {}) => {
|
|
|
11322
11331
|
const expectedColumn = table.columns[columnName];
|
|
11323
11332
|
const actualColumn = actualColumns.get(columnName);
|
|
11324
11333
|
const columnDiff = diffColumn(expectedColumn, actualColumn, dialect);
|
|
11334
|
+
if (columnDiff.referenceChanged) {
|
|
11335
|
+
plan.warnings.push(
|
|
11336
|
+
`Foreign key definition on ${key}.${columnName} differs from the expected schema; manual constraint migration is required.`
|
|
11337
|
+
);
|
|
11338
|
+
}
|
|
11325
11339
|
const shouldAlter = columnDiff.typeChanged || columnDiff.nullabilityChanged || columnDiff.defaultChanged || columnDiff.autoIncrementChanged;
|
|
11326
11340
|
if (shouldAlter) {
|
|
11327
11341
|
const capability = dialect.mutations.alterColumn;
|
|
@@ -12403,6 +12417,288 @@ var mysqlIntrospector = {
|
|
|
12403
12417
|
}
|
|
12404
12418
|
};
|
|
12405
12419
|
|
|
12420
|
+
// src/core/ddl/introspect/sqlite-foreign-key-ddl.ts
|
|
12421
|
+
var isIdentifierChar = (value) => /[A-Za-z0-9_]/.test(value);
|
|
12422
|
+
var keywordAt = (text, position2, keyword) => {
|
|
12423
|
+
if (position2 < 0 || position2 + keyword.length > text.length) return false;
|
|
12424
|
+
if (position2 > 0 && isIdentifierChar(text[position2 - 1])) return false;
|
|
12425
|
+
if (position2 + keyword.length < text.length && isIdentifierChar(text[position2 + keyword.length])) {
|
|
12426
|
+
return false;
|
|
12427
|
+
}
|
|
12428
|
+
return text.slice(position2, position2 + keyword.length).toUpperCase() === keyword.toUpperCase();
|
|
12429
|
+
};
|
|
12430
|
+
var skipWhitespace = (text, position2) => {
|
|
12431
|
+
let pos = position2;
|
|
12432
|
+
while (pos < text.length && /\s/.test(text[pos])) pos += 1;
|
|
12433
|
+
return pos;
|
|
12434
|
+
};
|
|
12435
|
+
var readIdentifier = (text, position2) => {
|
|
12436
|
+
let pos = skipWhitespace(text, position2);
|
|
12437
|
+
if (pos >= text.length) return void 0;
|
|
12438
|
+
const quote = text[pos];
|
|
12439
|
+
if (quote === '"' || quote === "`" || quote === "[") {
|
|
12440
|
+
const close = quote === "[" ? "]" : quote;
|
|
12441
|
+
pos += 1;
|
|
12442
|
+
let value = "";
|
|
12443
|
+
while (pos < text.length) {
|
|
12444
|
+
const current = text[pos++];
|
|
12445
|
+
if (current === close) {
|
|
12446
|
+
if (pos < text.length && text[pos] === close) {
|
|
12447
|
+
value += close;
|
|
12448
|
+
pos += 1;
|
|
12449
|
+
continue;
|
|
12450
|
+
}
|
|
12451
|
+
return { value, next: pos };
|
|
12452
|
+
}
|
|
12453
|
+
value += current;
|
|
12454
|
+
}
|
|
12455
|
+
return void 0;
|
|
12456
|
+
}
|
|
12457
|
+
const start = pos;
|
|
12458
|
+
while (pos < text.length && !/\s/.test(text[pos]) && text[pos] !== "(" && text[pos] !== ")" && text[pos] !== ",") {
|
|
12459
|
+
pos += 1;
|
|
12460
|
+
}
|
|
12461
|
+
if (pos === start) return void 0;
|
|
12462
|
+
return { value: text.slice(start, pos), next: pos };
|
|
12463
|
+
};
|
|
12464
|
+
var splitTableBody = (createSql) => {
|
|
12465
|
+
const open = createSql.indexOf("(");
|
|
12466
|
+
if (open < 0) return [];
|
|
12467
|
+
let depth = 1;
|
|
12468
|
+
let quote = "";
|
|
12469
|
+
let bracket = false;
|
|
12470
|
+
let lineComment = false;
|
|
12471
|
+
let blockComment = false;
|
|
12472
|
+
let close = -1;
|
|
12473
|
+
for (let i = open + 1; i < createSql.length; i += 1) {
|
|
12474
|
+
const current = createSql[i];
|
|
12475
|
+
const next = createSql[i + 1] ?? "";
|
|
12476
|
+
if (lineComment) {
|
|
12477
|
+
if (current === "\n" || current === "\r") lineComment = false;
|
|
12478
|
+
continue;
|
|
12479
|
+
}
|
|
12480
|
+
if (blockComment) {
|
|
12481
|
+
if (current === "*" && next === "/") {
|
|
12482
|
+
blockComment = false;
|
|
12483
|
+
i += 1;
|
|
12484
|
+
}
|
|
12485
|
+
continue;
|
|
12486
|
+
}
|
|
12487
|
+
if (quote) {
|
|
12488
|
+
if (current === quote) {
|
|
12489
|
+
if (next === quote) i += 1;
|
|
12490
|
+
else quote = "";
|
|
12491
|
+
}
|
|
12492
|
+
continue;
|
|
12493
|
+
}
|
|
12494
|
+
if (bracket) {
|
|
12495
|
+
if (current === "]") {
|
|
12496
|
+
if (next === "]") i += 1;
|
|
12497
|
+
else bracket = false;
|
|
12498
|
+
}
|
|
12499
|
+
continue;
|
|
12500
|
+
}
|
|
12501
|
+
if (current === "-" && next === "-") {
|
|
12502
|
+
lineComment = true;
|
|
12503
|
+
i += 1;
|
|
12504
|
+
continue;
|
|
12505
|
+
}
|
|
12506
|
+
if (current === "/" && next === "*") {
|
|
12507
|
+
blockComment = true;
|
|
12508
|
+
i += 1;
|
|
12509
|
+
continue;
|
|
12510
|
+
}
|
|
12511
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
12512
|
+
quote = current;
|
|
12513
|
+
continue;
|
|
12514
|
+
}
|
|
12515
|
+
if (current === "[") {
|
|
12516
|
+
bracket = true;
|
|
12517
|
+
continue;
|
|
12518
|
+
}
|
|
12519
|
+
if (current === "(") depth += 1;
|
|
12520
|
+
else if (current === ")") {
|
|
12521
|
+
depth -= 1;
|
|
12522
|
+
if (depth === 0) {
|
|
12523
|
+
close = i;
|
|
12524
|
+
break;
|
|
12525
|
+
}
|
|
12526
|
+
}
|
|
12527
|
+
}
|
|
12528
|
+
if (close < 0) return [];
|
|
12529
|
+
const body = createSql.slice(open + 1, close);
|
|
12530
|
+
const segments = [];
|
|
12531
|
+
let start = 0;
|
|
12532
|
+
depth = 0;
|
|
12533
|
+
quote = "";
|
|
12534
|
+
bracket = false;
|
|
12535
|
+
lineComment = false;
|
|
12536
|
+
blockComment = false;
|
|
12537
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
12538
|
+
const current = body[i];
|
|
12539
|
+
const next = body[i + 1] ?? "";
|
|
12540
|
+
if (lineComment) {
|
|
12541
|
+
if (current === "\n" || current === "\r") lineComment = false;
|
|
12542
|
+
continue;
|
|
12543
|
+
}
|
|
12544
|
+
if (blockComment) {
|
|
12545
|
+
if (current === "*" && next === "/") {
|
|
12546
|
+
blockComment = false;
|
|
12547
|
+
i += 1;
|
|
12548
|
+
}
|
|
12549
|
+
continue;
|
|
12550
|
+
}
|
|
12551
|
+
if (quote) {
|
|
12552
|
+
if (current === quote) {
|
|
12553
|
+
if (next === quote) i += 1;
|
|
12554
|
+
else quote = "";
|
|
12555
|
+
}
|
|
12556
|
+
continue;
|
|
12557
|
+
}
|
|
12558
|
+
if (bracket) {
|
|
12559
|
+
if (current === "]") {
|
|
12560
|
+
if (next === "]") i += 1;
|
|
12561
|
+
else bracket = false;
|
|
12562
|
+
}
|
|
12563
|
+
continue;
|
|
12564
|
+
}
|
|
12565
|
+
if (current === "-" && next === "-") {
|
|
12566
|
+
lineComment = true;
|
|
12567
|
+
i += 1;
|
|
12568
|
+
continue;
|
|
12569
|
+
}
|
|
12570
|
+
if (current === "/" && next === "*") {
|
|
12571
|
+
blockComment = true;
|
|
12572
|
+
i += 1;
|
|
12573
|
+
continue;
|
|
12574
|
+
}
|
|
12575
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
12576
|
+
quote = current;
|
|
12577
|
+
continue;
|
|
12578
|
+
}
|
|
12579
|
+
if (current === "[") {
|
|
12580
|
+
bracket = true;
|
|
12581
|
+
continue;
|
|
12582
|
+
}
|
|
12583
|
+
if (current === "(") depth += 1;
|
|
12584
|
+
else if (current === ")" && depth > 0) depth -= 1;
|
|
12585
|
+
else if (current === "," && depth === 0) {
|
|
12586
|
+
segments.push(body.slice(start, i).trim());
|
|
12587
|
+
start = i + 1;
|
|
12588
|
+
}
|
|
12589
|
+
}
|
|
12590
|
+
segments.push(body.slice(start).trim());
|
|
12591
|
+
return segments.filter(Boolean);
|
|
12592
|
+
};
|
|
12593
|
+
var findTopLevelKeyword = (text, keyword, start = 0) => {
|
|
12594
|
+
let depth = 0;
|
|
12595
|
+
let quote = "";
|
|
12596
|
+
let bracket = false;
|
|
12597
|
+
for (let i = start; i < text.length; i += 1) {
|
|
12598
|
+
const current = text[i];
|
|
12599
|
+
const next = text[i + 1] ?? "";
|
|
12600
|
+
if (quote) {
|
|
12601
|
+
if (current === quote) {
|
|
12602
|
+
if (next === quote) i += 1;
|
|
12603
|
+
else quote = "";
|
|
12604
|
+
}
|
|
12605
|
+
continue;
|
|
12606
|
+
}
|
|
12607
|
+
if (bracket) {
|
|
12608
|
+
if (current === "]") {
|
|
12609
|
+
if (next === "]") i += 1;
|
|
12610
|
+
else bracket = false;
|
|
12611
|
+
}
|
|
12612
|
+
continue;
|
|
12613
|
+
}
|
|
12614
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
12615
|
+
quote = current;
|
|
12616
|
+
continue;
|
|
12617
|
+
}
|
|
12618
|
+
if (current === "[") {
|
|
12619
|
+
bracket = true;
|
|
12620
|
+
continue;
|
|
12621
|
+
}
|
|
12622
|
+
if (current === "(") {
|
|
12623
|
+
depth += 1;
|
|
12624
|
+
continue;
|
|
12625
|
+
}
|
|
12626
|
+
if (current === ")") {
|
|
12627
|
+
if (depth > 0) depth -= 1;
|
|
12628
|
+
continue;
|
|
12629
|
+
}
|
|
12630
|
+
if (depth === 0 && keywordAt(text, i, keyword)) return i;
|
|
12631
|
+
}
|
|
12632
|
+
return -1;
|
|
12633
|
+
};
|
|
12634
|
+
var isInitiallyDeferred = (segment, start) => {
|
|
12635
|
+
const deferrablePos = findTopLevelKeyword(segment, "DEFERRABLE", start);
|
|
12636
|
+
if (deferrablePos < 0) return false;
|
|
12637
|
+
const before = segment.slice(start, deferrablePos).trimEnd();
|
|
12638
|
+
if (/\bNOT$/i.test(before)) return false;
|
|
12639
|
+
return /^DEFERRABLE\s+INITIALLY\s+DEFERRED\b/i.test(segment.slice(deferrablePos));
|
|
12640
|
+
};
|
|
12641
|
+
var parseTableForeignKey = (segment) => {
|
|
12642
|
+
let pos = skipWhitespace(segment, 0);
|
|
12643
|
+
let name;
|
|
12644
|
+
if (keywordAt(segment, pos, "CONSTRAINT")) {
|
|
12645
|
+
pos += "CONSTRAINT".length;
|
|
12646
|
+
const parsedName = readIdentifier(segment, pos);
|
|
12647
|
+
if (!parsedName) return void 0;
|
|
12648
|
+
name = parsedName.value;
|
|
12649
|
+
pos = skipWhitespace(segment, parsedName.next);
|
|
12650
|
+
}
|
|
12651
|
+
if (!keywordAt(segment, pos, "FOREIGN")) return void 0;
|
|
12652
|
+
pos += "FOREIGN".length;
|
|
12653
|
+
pos = skipWhitespace(segment, pos);
|
|
12654
|
+
if (!keywordAt(segment, pos, "KEY")) return void 0;
|
|
12655
|
+
pos += "KEY".length;
|
|
12656
|
+
pos = skipWhitespace(segment, pos);
|
|
12657
|
+
if (segment[pos] !== "(") return void 0;
|
|
12658
|
+
pos += 1;
|
|
12659
|
+
const source = readIdentifier(segment, pos);
|
|
12660
|
+
if (!source) return void 0;
|
|
12661
|
+
pos = skipWhitespace(segment, source.next);
|
|
12662
|
+
if (segment[pos] === ",") return void 0;
|
|
12663
|
+
const referencesPos = findTopLevelKeyword(segment, "REFERENCES", pos);
|
|
12664
|
+
if (referencesPos < 0) return void 0;
|
|
12665
|
+
return {
|
|
12666
|
+
column: source.value,
|
|
12667
|
+
...name ? { name } : {},
|
|
12668
|
+
deferrable: isInitiallyDeferred(segment, referencesPos)
|
|
12669
|
+
};
|
|
12670
|
+
};
|
|
12671
|
+
var parseInlineForeignKey = (segment) => {
|
|
12672
|
+
const source = readIdentifier(segment, 0);
|
|
12673
|
+
if (!source) return void 0;
|
|
12674
|
+
const referencesPos = findTopLevelKeyword(segment, "REFERENCES", source.next);
|
|
12675
|
+
if (referencesPos < 0) return void 0;
|
|
12676
|
+
let name;
|
|
12677
|
+
const constraintPos = findTopLevelKeyword(segment, "CONSTRAINT", source.next);
|
|
12678
|
+
if (constraintPos >= 0 && constraintPos < referencesPos) {
|
|
12679
|
+
const parsedName = readIdentifier(segment, constraintPos + "CONSTRAINT".length);
|
|
12680
|
+
if (parsedName) name = parsedName.value;
|
|
12681
|
+
}
|
|
12682
|
+
return {
|
|
12683
|
+
column: source.value,
|
|
12684
|
+
...name ? { name } : {},
|
|
12685
|
+
deferrable: isInitiallyDeferred(segment, referencesPos)
|
|
12686
|
+
};
|
|
12687
|
+
};
|
|
12688
|
+
var parseSqliteForeignKeyModifiers = (createSql) => {
|
|
12689
|
+
const result = [];
|
|
12690
|
+
for (const segment of splitTableBody(createSql)) {
|
|
12691
|
+
const tableConstraint = parseTableForeignKey(segment);
|
|
12692
|
+
if (tableConstraint) {
|
|
12693
|
+
result.push(tableConstraint);
|
|
12694
|
+
continue;
|
|
12695
|
+
}
|
|
12696
|
+
const inlineConstraint = parseInlineForeignKey(segment);
|
|
12697
|
+
if (inlineConstraint) result.push(inlineConstraint);
|
|
12698
|
+
}
|
|
12699
|
+
return result;
|
|
12700
|
+
};
|
|
12701
|
+
|
|
12406
12702
|
// src/core/ddl/introspect/sqlite.ts
|
|
12407
12703
|
var toReferentialAction = (value) => {
|
|
12408
12704
|
if (!value) return void 0;
|
|
@@ -12420,7 +12716,7 @@ var columnNode2 = (table, name, alias) => ({
|
|
|
12420
12716
|
});
|
|
12421
12717
|
var buildPragmaQuery = (name, table, alias, columnAliases) => ({
|
|
12422
12718
|
type: "SelectQuery",
|
|
12423
|
-
from: fnTable(name, [valueToOperand(table)], alias
|
|
12719
|
+
from: fnTable(name, [valueToOperand(table)], alias),
|
|
12424
12720
|
columns: columnAliases.map((column) => columnNode2(alias, column)),
|
|
12425
12721
|
joins: []
|
|
12426
12722
|
});
|
|
@@ -12481,7 +12777,10 @@ var sqliteIntrospector = {
|
|
|
12481
12777
|
const tablesQuery = {
|
|
12482
12778
|
type: "SelectQuery",
|
|
12483
12779
|
from: { type: "Table", name: "sqlite_master" },
|
|
12484
|
-
columns: [
|
|
12780
|
+
columns: [
|
|
12781
|
+
columnNode2(alias, "name"),
|
|
12782
|
+
columnNode2(alias, "sql")
|
|
12783
|
+
],
|
|
12485
12784
|
joins: [],
|
|
12486
12785
|
where: and(
|
|
12487
12786
|
eq(columnNode2(alias, "type"), "table"),
|
|
@@ -12551,6 +12850,14 @@ var sqliteIntrospector = {
|
|
|
12551
12850
|
};
|
|
12552
12851
|
}
|
|
12553
12852
|
});
|
|
12853
|
+
if (row.sql) {
|
|
12854
|
+
for (const modifier of parseSqliteForeignKeyModifiers(row.sql)) {
|
|
12855
|
+
const reference = tableEntry.columns.find((column) => column.name === modifier.column)?.references;
|
|
12856
|
+
if (!reference) continue;
|
|
12857
|
+
if (modifier.name) reference.name = modifier.name;
|
|
12858
|
+
reference.deferrable = modifier.deferrable;
|
|
12859
|
+
}
|
|
12860
|
+
}
|
|
12554
12861
|
for (const idx of indexList) {
|
|
12555
12862
|
if (!idx.name) continue;
|
|
12556
12863
|
const indexColumns = await runPragma(
|
|
@@ -13774,6 +14081,7 @@ var createSqliteSchemaDialect = () => composeSchemaDialect({
|
|
|
13774
14081
|
void table;
|
|
13775
14082
|
return !!(column.autoIncrement && primaryKey.length === 1 && primaryKey[0] === column.name);
|
|
13776
14083
|
},
|
|
14084
|
+
renderReferenceSuffix: (ref) => ref.deferrable ? "DEFERRABLE INITIALLY DEFERRED" : void 0,
|
|
13777
14085
|
renderIndex(table, index, services) {
|
|
13778
14086
|
const name = index.name || deriveIndexName(table, index);
|
|
13779
14087
|
const columns = renderIndexColumns(services, index.columns);
|