metal-orm 1.1.28 → 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 +1 -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.cjs
CHANGED
|
@@ -11642,7 +11642,10 @@ var renderColumnDefinition = (table, col2, dialect, options = {}) => {
|
|
|
11642
11642
|
parts.push(`CHECK (${col2.check})`);
|
|
11643
11643
|
}
|
|
11644
11644
|
if (col2.references) {
|
|
11645
|
-
|
|
11645
|
+
const referenceSql = dialect.renderReference(col2.references, table);
|
|
11646
|
+
parts.push(
|
|
11647
|
+
col2.references.name ? `CONSTRAINT ${dialect.quoteIdentifier(col2.references.name)} ${referenceSql}` : referenceSql
|
|
11648
|
+
);
|
|
11646
11649
|
}
|
|
11647
11650
|
return { sql: parts.join(" "), inlinePrimary: !!(options.includePrimary && col2.primary) };
|
|
11648
11651
|
};
|
|
@@ -11763,6 +11766,11 @@ var normalizeDefault = (value) => {
|
|
|
11763
11766
|
if (value === void 0 || value === null) return void 0;
|
|
11764
11767
|
return String(value).trim();
|
|
11765
11768
|
};
|
|
11769
|
+
var normalizeReferenceAction = (value) => (value || "NO ACTION").toUpperCase().replace(/\s+/g, " ").trim();
|
|
11770
|
+
var sameReference = (expected, actual) => {
|
|
11771
|
+
if (!expected || !actual) return expected === actual;
|
|
11772
|
+
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;
|
|
11773
|
+
};
|
|
11766
11774
|
var diffColumn = (expected, actual, dialect) => {
|
|
11767
11775
|
const expectedType = normalizeType(dialect.renderColumnType(expected));
|
|
11768
11776
|
const actualType = normalizeType(actual.type);
|
|
@@ -11772,7 +11780,8 @@ var diffColumn = (expected, actual, dialect) => {
|
|
|
11772
11780
|
typeChanged: expectedType !== actualType,
|
|
11773
11781
|
nullabilityChanged: !!expected.notNull !== !!actual.notNull,
|
|
11774
11782
|
defaultChanged: expectedDefault !== actualDefault,
|
|
11775
|
-
autoIncrementChanged: !!expected.autoIncrement !== !!actual.autoIncrement
|
|
11783
|
+
autoIncrementChanged: !!expected.autoIncrement !== !!actual.autoIncrement,
|
|
11784
|
+
referenceChanged: !sameReference(expected.references, actual.references)
|
|
11776
11785
|
};
|
|
11777
11786
|
};
|
|
11778
11787
|
var unsupportedMutationWarning = (dialect, operation, target) => `Dialect "${dialect.name}" does not provide the ${operation} capability for ${target}; manual migration is required.`;
|
|
@@ -11809,6 +11818,11 @@ var diffSchema = (expectedTables, actualSchema, dialect, options = {}) => {
|
|
|
11809
11818
|
const expectedColumn = table.columns[columnName];
|
|
11810
11819
|
const actualColumn = actualColumns.get(columnName);
|
|
11811
11820
|
const columnDiff = diffColumn(expectedColumn, actualColumn, dialect);
|
|
11821
|
+
if (columnDiff.referenceChanged) {
|
|
11822
|
+
plan.warnings.push(
|
|
11823
|
+
`Foreign key definition on ${key}.${columnName} differs from the expected schema; manual constraint migration is required.`
|
|
11824
|
+
);
|
|
11825
|
+
}
|
|
11812
11826
|
const shouldAlter = columnDiff.typeChanged || columnDiff.nullabilityChanged || columnDiff.defaultChanged || columnDiff.autoIncrementChanged;
|
|
11813
11827
|
if (shouldAlter) {
|
|
11814
11828
|
const capability = dialect.mutations.alterColumn;
|
|
@@ -12890,6 +12904,288 @@ var mysqlIntrospector = {
|
|
|
12890
12904
|
}
|
|
12891
12905
|
};
|
|
12892
12906
|
|
|
12907
|
+
// src/core/ddl/introspect/sqlite-foreign-key-ddl.ts
|
|
12908
|
+
var isIdentifierChar = (value) => /[A-Za-z0-9_]/.test(value);
|
|
12909
|
+
var keywordAt = (text, position2, keyword) => {
|
|
12910
|
+
if (position2 < 0 || position2 + keyword.length > text.length) return false;
|
|
12911
|
+
if (position2 > 0 && isIdentifierChar(text[position2 - 1])) return false;
|
|
12912
|
+
if (position2 + keyword.length < text.length && isIdentifierChar(text[position2 + keyword.length])) {
|
|
12913
|
+
return false;
|
|
12914
|
+
}
|
|
12915
|
+
return text.slice(position2, position2 + keyword.length).toUpperCase() === keyword.toUpperCase();
|
|
12916
|
+
};
|
|
12917
|
+
var skipWhitespace = (text, position2) => {
|
|
12918
|
+
let pos = position2;
|
|
12919
|
+
while (pos < text.length && /\s/.test(text[pos])) pos += 1;
|
|
12920
|
+
return pos;
|
|
12921
|
+
};
|
|
12922
|
+
var readIdentifier = (text, position2) => {
|
|
12923
|
+
let pos = skipWhitespace(text, position2);
|
|
12924
|
+
if (pos >= text.length) return void 0;
|
|
12925
|
+
const quote = text[pos];
|
|
12926
|
+
if (quote === '"' || quote === "`" || quote === "[") {
|
|
12927
|
+
const close = quote === "[" ? "]" : quote;
|
|
12928
|
+
pos += 1;
|
|
12929
|
+
let value = "";
|
|
12930
|
+
while (pos < text.length) {
|
|
12931
|
+
const current = text[pos++];
|
|
12932
|
+
if (current === close) {
|
|
12933
|
+
if (pos < text.length && text[pos] === close) {
|
|
12934
|
+
value += close;
|
|
12935
|
+
pos += 1;
|
|
12936
|
+
continue;
|
|
12937
|
+
}
|
|
12938
|
+
return { value, next: pos };
|
|
12939
|
+
}
|
|
12940
|
+
value += current;
|
|
12941
|
+
}
|
|
12942
|
+
return void 0;
|
|
12943
|
+
}
|
|
12944
|
+
const start = pos;
|
|
12945
|
+
while (pos < text.length && !/\s/.test(text[pos]) && text[pos] !== "(" && text[pos] !== ")" && text[pos] !== ",") {
|
|
12946
|
+
pos += 1;
|
|
12947
|
+
}
|
|
12948
|
+
if (pos === start) return void 0;
|
|
12949
|
+
return { value: text.slice(start, pos), next: pos };
|
|
12950
|
+
};
|
|
12951
|
+
var splitTableBody = (createSql) => {
|
|
12952
|
+
const open = createSql.indexOf("(");
|
|
12953
|
+
if (open < 0) return [];
|
|
12954
|
+
let depth = 1;
|
|
12955
|
+
let quote = "";
|
|
12956
|
+
let bracket = false;
|
|
12957
|
+
let lineComment = false;
|
|
12958
|
+
let blockComment = false;
|
|
12959
|
+
let close = -1;
|
|
12960
|
+
for (let i = open + 1; i < createSql.length; i += 1) {
|
|
12961
|
+
const current = createSql[i];
|
|
12962
|
+
const next = createSql[i + 1] ?? "";
|
|
12963
|
+
if (lineComment) {
|
|
12964
|
+
if (current === "\n" || current === "\r") lineComment = false;
|
|
12965
|
+
continue;
|
|
12966
|
+
}
|
|
12967
|
+
if (blockComment) {
|
|
12968
|
+
if (current === "*" && next === "/") {
|
|
12969
|
+
blockComment = false;
|
|
12970
|
+
i += 1;
|
|
12971
|
+
}
|
|
12972
|
+
continue;
|
|
12973
|
+
}
|
|
12974
|
+
if (quote) {
|
|
12975
|
+
if (current === quote) {
|
|
12976
|
+
if (next === quote) i += 1;
|
|
12977
|
+
else quote = "";
|
|
12978
|
+
}
|
|
12979
|
+
continue;
|
|
12980
|
+
}
|
|
12981
|
+
if (bracket) {
|
|
12982
|
+
if (current === "]") {
|
|
12983
|
+
if (next === "]") i += 1;
|
|
12984
|
+
else bracket = false;
|
|
12985
|
+
}
|
|
12986
|
+
continue;
|
|
12987
|
+
}
|
|
12988
|
+
if (current === "-" && next === "-") {
|
|
12989
|
+
lineComment = true;
|
|
12990
|
+
i += 1;
|
|
12991
|
+
continue;
|
|
12992
|
+
}
|
|
12993
|
+
if (current === "/" && next === "*") {
|
|
12994
|
+
blockComment = true;
|
|
12995
|
+
i += 1;
|
|
12996
|
+
continue;
|
|
12997
|
+
}
|
|
12998
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
12999
|
+
quote = current;
|
|
13000
|
+
continue;
|
|
13001
|
+
}
|
|
13002
|
+
if (current === "[") {
|
|
13003
|
+
bracket = true;
|
|
13004
|
+
continue;
|
|
13005
|
+
}
|
|
13006
|
+
if (current === "(") depth += 1;
|
|
13007
|
+
else if (current === ")") {
|
|
13008
|
+
depth -= 1;
|
|
13009
|
+
if (depth === 0) {
|
|
13010
|
+
close = i;
|
|
13011
|
+
break;
|
|
13012
|
+
}
|
|
13013
|
+
}
|
|
13014
|
+
}
|
|
13015
|
+
if (close < 0) return [];
|
|
13016
|
+
const body = createSql.slice(open + 1, close);
|
|
13017
|
+
const segments = [];
|
|
13018
|
+
let start = 0;
|
|
13019
|
+
depth = 0;
|
|
13020
|
+
quote = "";
|
|
13021
|
+
bracket = false;
|
|
13022
|
+
lineComment = false;
|
|
13023
|
+
blockComment = false;
|
|
13024
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
13025
|
+
const current = body[i];
|
|
13026
|
+
const next = body[i + 1] ?? "";
|
|
13027
|
+
if (lineComment) {
|
|
13028
|
+
if (current === "\n" || current === "\r") lineComment = false;
|
|
13029
|
+
continue;
|
|
13030
|
+
}
|
|
13031
|
+
if (blockComment) {
|
|
13032
|
+
if (current === "*" && next === "/") {
|
|
13033
|
+
blockComment = false;
|
|
13034
|
+
i += 1;
|
|
13035
|
+
}
|
|
13036
|
+
continue;
|
|
13037
|
+
}
|
|
13038
|
+
if (quote) {
|
|
13039
|
+
if (current === quote) {
|
|
13040
|
+
if (next === quote) i += 1;
|
|
13041
|
+
else quote = "";
|
|
13042
|
+
}
|
|
13043
|
+
continue;
|
|
13044
|
+
}
|
|
13045
|
+
if (bracket) {
|
|
13046
|
+
if (current === "]") {
|
|
13047
|
+
if (next === "]") i += 1;
|
|
13048
|
+
else bracket = false;
|
|
13049
|
+
}
|
|
13050
|
+
continue;
|
|
13051
|
+
}
|
|
13052
|
+
if (current === "-" && next === "-") {
|
|
13053
|
+
lineComment = true;
|
|
13054
|
+
i += 1;
|
|
13055
|
+
continue;
|
|
13056
|
+
}
|
|
13057
|
+
if (current === "/" && next === "*") {
|
|
13058
|
+
blockComment = true;
|
|
13059
|
+
i += 1;
|
|
13060
|
+
continue;
|
|
13061
|
+
}
|
|
13062
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
13063
|
+
quote = current;
|
|
13064
|
+
continue;
|
|
13065
|
+
}
|
|
13066
|
+
if (current === "[") {
|
|
13067
|
+
bracket = true;
|
|
13068
|
+
continue;
|
|
13069
|
+
}
|
|
13070
|
+
if (current === "(") depth += 1;
|
|
13071
|
+
else if (current === ")" && depth > 0) depth -= 1;
|
|
13072
|
+
else if (current === "," && depth === 0) {
|
|
13073
|
+
segments.push(body.slice(start, i).trim());
|
|
13074
|
+
start = i + 1;
|
|
13075
|
+
}
|
|
13076
|
+
}
|
|
13077
|
+
segments.push(body.slice(start).trim());
|
|
13078
|
+
return segments.filter(Boolean);
|
|
13079
|
+
};
|
|
13080
|
+
var findTopLevelKeyword = (text, keyword, start = 0) => {
|
|
13081
|
+
let depth = 0;
|
|
13082
|
+
let quote = "";
|
|
13083
|
+
let bracket = false;
|
|
13084
|
+
for (let i = start; i < text.length; i += 1) {
|
|
13085
|
+
const current = text[i];
|
|
13086
|
+
const next = text[i + 1] ?? "";
|
|
13087
|
+
if (quote) {
|
|
13088
|
+
if (current === quote) {
|
|
13089
|
+
if (next === quote) i += 1;
|
|
13090
|
+
else quote = "";
|
|
13091
|
+
}
|
|
13092
|
+
continue;
|
|
13093
|
+
}
|
|
13094
|
+
if (bracket) {
|
|
13095
|
+
if (current === "]") {
|
|
13096
|
+
if (next === "]") i += 1;
|
|
13097
|
+
else bracket = false;
|
|
13098
|
+
}
|
|
13099
|
+
continue;
|
|
13100
|
+
}
|
|
13101
|
+
if (current === "'" || current === '"' || current === "`") {
|
|
13102
|
+
quote = current;
|
|
13103
|
+
continue;
|
|
13104
|
+
}
|
|
13105
|
+
if (current === "[") {
|
|
13106
|
+
bracket = true;
|
|
13107
|
+
continue;
|
|
13108
|
+
}
|
|
13109
|
+
if (current === "(") {
|
|
13110
|
+
depth += 1;
|
|
13111
|
+
continue;
|
|
13112
|
+
}
|
|
13113
|
+
if (current === ")") {
|
|
13114
|
+
if (depth > 0) depth -= 1;
|
|
13115
|
+
continue;
|
|
13116
|
+
}
|
|
13117
|
+
if (depth === 0 && keywordAt(text, i, keyword)) return i;
|
|
13118
|
+
}
|
|
13119
|
+
return -1;
|
|
13120
|
+
};
|
|
13121
|
+
var isInitiallyDeferred = (segment, start) => {
|
|
13122
|
+
const deferrablePos = findTopLevelKeyword(segment, "DEFERRABLE", start);
|
|
13123
|
+
if (deferrablePos < 0) return false;
|
|
13124
|
+
const before = segment.slice(start, deferrablePos).trimEnd();
|
|
13125
|
+
if (/\bNOT$/i.test(before)) return false;
|
|
13126
|
+
return /^DEFERRABLE\s+INITIALLY\s+DEFERRED\b/i.test(segment.slice(deferrablePos));
|
|
13127
|
+
};
|
|
13128
|
+
var parseTableForeignKey = (segment) => {
|
|
13129
|
+
let pos = skipWhitespace(segment, 0);
|
|
13130
|
+
let name;
|
|
13131
|
+
if (keywordAt(segment, pos, "CONSTRAINT")) {
|
|
13132
|
+
pos += "CONSTRAINT".length;
|
|
13133
|
+
const parsedName = readIdentifier(segment, pos);
|
|
13134
|
+
if (!parsedName) return void 0;
|
|
13135
|
+
name = parsedName.value;
|
|
13136
|
+
pos = skipWhitespace(segment, parsedName.next);
|
|
13137
|
+
}
|
|
13138
|
+
if (!keywordAt(segment, pos, "FOREIGN")) return void 0;
|
|
13139
|
+
pos += "FOREIGN".length;
|
|
13140
|
+
pos = skipWhitespace(segment, pos);
|
|
13141
|
+
if (!keywordAt(segment, pos, "KEY")) return void 0;
|
|
13142
|
+
pos += "KEY".length;
|
|
13143
|
+
pos = skipWhitespace(segment, pos);
|
|
13144
|
+
if (segment[pos] !== "(") return void 0;
|
|
13145
|
+
pos += 1;
|
|
13146
|
+
const source = readIdentifier(segment, pos);
|
|
13147
|
+
if (!source) return void 0;
|
|
13148
|
+
pos = skipWhitespace(segment, source.next);
|
|
13149
|
+
if (segment[pos] === ",") return void 0;
|
|
13150
|
+
const referencesPos = findTopLevelKeyword(segment, "REFERENCES", pos);
|
|
13151
|
+
if (referencesPos < 0) return void 0;
|
|
13152
|
+
return {
|
|
13153
|
+
column: source.value,
|
|
13154
|
+
...name ? { name } : {},
|
|
13155
|
+
deferrable: isInitiallyDeferred(segment, referencesPos)
|
|
13156
|
+
};
|
|
13157
|
+
};
|
|
13158
|
+
var parseInlineForeignKey = (segment) => {
|
|
13159
|
+
const source = readIdentifier(segment, 0);
|
|
13160
|
+
if (!source) return void 0;
|
|
13161
|
+
const referencesPos = findTopLevelKeyword(segment, "REFERENCES", source.next);
|
|
13162
|
+
if (referencesPos < 0) return void 0;
|
|
13163
|
+
let name;
|
|
13164
|
+
const constraintPos = findTopLevelKeyword(segment, "CONSTRAINT", source.next);
|
|
13165
|
+
if (constraintPos >= 0 && constraintPos < referencesPos) {
|
|
13166
|
+
const parsedName = readIdentifier(segment, constraintPos + "CONSTRAINT".length);
|
|
13167
|
+
if (parsedName) name = parsedName.value;
|
|
13168
|
+
}
|
|
13169
|
+
return {
|
|
13170
|
+
column: source.value,
|
|
13171
|
+
...name ? { name } : {},
|
|
13172
|
+
deferrable: isInitiallyDeferred(segment, referencesPos)
|
|
13173
|
+
};
|
|
13174
|
+
};
|
|
13175
|
+
var parseSqliteForeignKeyModifiers = (createSql) => {
|
|
13176
|
+
const result = [];
|
|
13177
|
+
for (const segment of splitTableBody(createSql)) {
|
|
13178
|
+
const tableConstraint = parseTableForeignKey(segment);
|
|
13179
|
+
if (tableConstraint) {
|
|
13180
|
+
result.push(tableConstraint);
|
|
13181
|
+
continue;
|
|
13182
|
+
}
|
|
13183
|
+
const inlineConstraint = parseInlineForeignKey(segment);
|
|
13184
|
+
if (inlineConstraint) result.push(inlineConstraint);
|
|
13185
|
+
}
|
|
13186
|
+
return result;
|
|
13187
|
+
};
|
|
13188
|
+
|
|
12893
13189
|
// src/core/ddl/introspect/sqlite.ts
|
|
12894
13190
|
var toReferentialAction = (value) => {
|
|
12895
13191
|
if (!value) return void 0;
|
|
@@ -12907,7 +13203,7 @@ var columnNode2 = (table, name, alias) => ({
|
|
|
12907
13203
|
});
|
|
12908
13204
|
var buildPragmaQuery = (name, table, alias, columnAliases) => ({
|
|
12909
13205
|
type: "SelectQuery",
|
|
12910
|
-
from: fnTable(name, [valueToOperand(table)], alias
|
|
13206
|
+
from: fnTable(name, [valueToOperand(table)], alias),
|
|
12911
13207
|
columns: columnAliases.map((column) => columnNode2(alias, column)),
|
|
12912
13208
|
joins: []
|
|
12913
13209
|
});
|
|
@@ -12968,7 +13264,10 @@ var sqliteIntrospector = {
|
|
|
12968
13264
|
const tablesQuery = {
|
|
12969
13265
|
type: "SelectQuery",
|
|
12970
13266
|
from: { type: "Table", name: "sqlite_master" },
|
|
12971
|
-
columns: [
|
|
13267
|
+
columns: [
|
|
13268
|
+
columnNode2(alias, "name"),
|
|
13269
|
+
columnNode2(alias, "sql")
|
|
13270
|
+
],
|
|
12972
13271
|
joins: [],
|
|
12973
13272
|
where: and(
|
|
12974
13273
|
eq(columnNode2(alias, "type"), "table"),
|
|
@@ -13038,6 +13337,14 @@ var sqliteIntrospector = {
|
|
|
13038
13337
|
};
|
|
13039
13338
|
}
|
|
13040
13339
|
});
|
|
13340
|
+
if (row.sql) {
|
|
13341
|
+
for (const modifier of parseSqliteForeignKeyModifiers(row.sql)) {
|
|
13342
|
+
const reference = tableEntry.columns.find((column) => column.name === modifier.column)?.references;
|
|
13343
|
+
if (!reference) continue;
|
|
13344
|
+
if (modifier.name) reference.name = modifier.name;
|
|
13345
|
+
reference.deferrable = modifier.deferrable;
|
|
13346
|
+
}
|
|
13347
|
+
}
|
|
13041
13348
|
for (const idx of indexList) {
|
|
13042
13349
|
if (!idx.name) continue;
|
|
13043
13350
|
const indexColumns = await runPragma(
|
|
@@ -14261,6 +14568,7 @@ var createSqliteSchemaDialect = () => composeSchemaDialect({
|
|
|
14261
14568
|
void table;
|
|
14262
14569
|
return !!(column.autoIncrement && primaryKey.length === 1 && primaryKey[0] === column.name);
|
|
14263
14570
|
},
|
|
14571
|
+
renderReferenceSuffix: (ref) => ref.deferrable ? "DEFERRABLE INITIALLY DEFERRED" : void 0,
|
|
14264
14572
|
renderIndex(table, index, services) {
|
|
14265
14573
|
const name = index.name || deriveIndexName(table, index);
|
|
14266
14574
|
const columns = renderIndexColumns(services, index.columns);
|