@mikro-orm/sql 7.1.0-dev.9 → 7.1.0
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/AbstractSqlConnection.d.ts +1 -1
- package/AbstractSqlConnection.js +27 -6
- package/AbstractSqlDriver.d.ts +15 -1
- package/AbstractSqlDriver.js +143 -26
- package/AbstractSqlPlatform.d.ts +15 -3
- package/AbstractSqlPlatform.js +25 -7
- package/PivotCollectionPersister.d.ts +2 -2
- package/PivotCollectionPersister.js +6 -1
- package/README.md +2 -1
- package/SqlEntityManager.d.ts +44 -5
- package/SqlEntityManager.js +41 -6
- package/SqlMikroORM.d.ts +23 -0
- package/SqlMikroORM.js +23 -0
- package/dialects/mysql/BaseMySqlPlatform.d.ts +3 -5
- package/dialects/mysql/BaseMySqlPlatform.js +6 -10
- package/dialects/mysql/MySqlSchemaHelper.d.ts +16 -3
- package/dialects/mysql/MySqlSchemaHelper.js +197 -49
- package/dialects/oracledb/OracleDialect.d.ts +1 -1
- package/dialects/oracledb/OracleDialect.js +2 -1
- package/dialects/postgresql/BasePostgreSqlEntityManager.d.ts +19 -0
- package/dialects/postgresql/BasePostgreSqlEntityManager.js +24 -0
- package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +11 -5
- package/dialects/postgresql/BasePostgreSqlPlatform.js +75 -17
- package/dialects/postgresql/PostgreSqlSchemaHelper.d.ts +31 -1
- package/dialects/postgresql/PostgreSqlSchemaHelper.js +269 -28
- package/dialects/postgresql/index.d.ts +2 -0
- package/dialects/postgresql/index.js +2 -0
- package/dialects/postgresql/typeOverrides.d.ts +14 -0
- package/dialects/postgresql/typeOverrides.js +12 -0
- package/dialects/sqlite/SqlitePlatform.d.ts +2 -1
- package/dialects/sqlite/SqlitePlatform.js +4 -0
- package/dialects/sqlite/SqliteSchemaHelper.d.ts +4 -1
- package/dialects/sqlite/SqliteSchemaHelper.js +49 -19
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/package.json +4 -4
- package/plugin/transformer.d.ts +11 -3
- package/plugin/transformer.js +138 -29
- package/query/CriteriaNode.d.ts +1 -1
- package/query/CriteriaNode.js +2 -2
- package/query/ObjectCriteriaNode.js +1 -1
- package/query/QueryBuilder.d.ts +42 -1
- package/query/QueryBuilder.js +78 -7
- package/schema/DatabaseSchema.d.ts +29 -2
- package/schema/DatabaseSchema.js +131 -4
- package/schema/DatabaseTable.d.ts +14 -1
- package/schema/DatabaseTable.js +165 -32
- package/schema/SchemaComparator.d.ts +18 -0
- package/schema/SchemaComparator.js +196 -1
- package/schema/SchemaHelper.d.ts +67 -1
- package/schema/SchemaHelper.js +255 -25
- package/schema/SqlSchemaGenerator.d.ts +2 -2
- package/schema/SqlSchemaGenerator.js +40 -10
- package/schema/partitioning.d.ts +13 -0
- package/schema/partitioning.js +326 -0
- package/typings.d.ts +59 -5
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { splitCommaSeparatedIdentifiers } from '@mikro-orm/core';
|
|
2
|
+
export { splitCommaSeparatedIdentifiers };
|
|
3
|
+
const skipQuotedLiteral = (value, start) => {
|
|
4
|
+
let i = start + 1;
|
|
5
|
+
while (i < value.length) {
|
|
6
|
+
if (value[i] === "'") {
|
|
7
|
+
if (value[i + 1] === "'") {
|
|
8
|
+
i += 2;
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
return i;
|
|
12
|
+
}
|
|
13
|
+
i++;
|
|
14
|
+
}
|
|
15
|
+
// Unterminated literal — point past the end so callers' `slice(start, end + 1)` includes
|
|
16
|
+
// the full remaining tail instead of dropping its last character.
|
|
17
|
+
return value.length;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Apply `transform` only to segments of `value` that lie outside single-quoted
|
|
21
|
+
* SQL literals, leaving literal content (including escaped `''`) untouched.
|
|
22
|
+
*/
|
|
23
|
+
const mapOutsideLiterals = (value, transform) => {
|
|
24
|
+
let ret = '';
|
|
25
|
+
let buffer = '';
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < value.length) {
|
|
28
|
+
if (value[i] === "'") {
|
|
29
|
+
ret += transform(buffer);
|
|
30
|
+
buffer = '';
|
|
31
|
+
const end = skipQuotedLiteral(value, i);
|
|
32
|
+
ret += value.slice(i, end + 1);
|
|
33
|
+
i = end + 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
buffer += value[i];
|
|
37
|
+
i++;
|
|
38
|
+
}
|
|
39
|
+
return ret + transform(buffer);
|
|
40
|
+
};
|
|
41
|
+
const collapseWhitespace = (value) => value.replace(/\s+/g, ' ');
|
|
42
|
+
const normalizeWhitespace = (value) => mapOutsideLiterals(value, collapseWhitespace).trim();
|
|
43
|
+
const stripDoubleQuotes = (value) => mapOutsideLiterals(value, s => s.replaceAll('"', ''));
|
|
44
|
+
const normalizeQuotedIdentifiers = (value) => stripDoubleQuotes(normalizeWhitespace(value));
|
|
45
|
+
const findMatchingParenthesis = (value, start) => {
|
|
46
|
+
let depth = 0;
|
|
47
|
+
for (let i = start; i < value.length; i++) {
|
|
48
|
+
if (value[i] === "'") {
|
|
49
|
+
i = skipQuotedLiteral(value, i);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (value[i] === '(') {
|
|
53
|
+
depth++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (value[i] === ')') {
|
|
57
|
+
depth--;
|
|
58
|
+
if (depth === 0) {
|
|
59
|
+
return i;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return -1;
|
|
64
|
+
};
|
|
65
|
+
const normalizePartitionLiterals = (value) => value
|
|
66
|
+
// PG pg_get_expr output often tacks `::text` onto string literals inside expressions; drop it
|
|
67
|
+
// so the catalog shape matches user-provided bounds. This applies symmetrically to both
|
|
68
|
+
// user metadata and catalog reads, so diffing converges. If a user intentionally writes a
|
|
69
|
+
// `::text` cast in a bound literal it will be stripped on both sides as well.
|
|
70
|
+
.replace(/('(?:[^']|'')*')::text\b/gi, '$1')
|
|
71
|
+
// Strip the `00:00:00` time component so catalog round-trips (timestamp[tz] bounds formatted
|
|
72
|
+
// via the session TimeZone) match user metadata that omitted the time part. Only collapse
|
|
73
|
+
// when we can confidently attribute the literal to a timestamp column: either a numeric
|
|
74
|
+
// offset is present (timestamptz catalog output) or an explicit `::timestamp[tz]` cast
|
|
75
|
+
// follows the literal. Bare `'YYYY-MM-DD 00:00:00'` without offset/cast could just as easily
|
|
76
|
+
// be a text/varchar list-partition value, and collapsing it would produce false-negative
|
|
77
|
+
// diffs.
|
|
78
|
+
.replace(/'(\d{4}-\d{2}-\d{2}) 00:00:00[+-]\d{2}(?::\d{2})?'/g, "'$1'")
|
|
79
|
+
.replace(/'(\d{4}-\d{2}-\d{2}) 00:00:00'(?=\s*::\s*timestamp(?:tz)?(?:\s+(?:with|without)\s+time\s+zone)?\b)/gi, "'$1'");
|
|
80
|
+
const unwrapOuterParentheses = (value) => {
|
|
81
|
+
const trimmed = value.trim();
|
|
82
|
+
if (!trimmed.startsWith('(') || !trimmed.endsWith(')')) {
|
|
83
|
+
return trimmed;
|
|
84
|
+
}
|
|
85
|
+
if (findMatchingParenthesis(trimmed, 0) !== trimmed.length - 1) {
|
|
86
|
+
return trimmed;
|
|
87
|
+
}
|
|
88
|
+
return trimmed.slice(1, -1).trim();
|
|
89
|
+
};
|
|
90
|
+
const unwrapAllOuterParentheses = (value) => {
|
|
91
|
+
let current = value.trim();
|
|
92
|
+
while (current.startsWith('(')) {
|
|
93
|
+
const unwrapped = unwrapOuterParentheses(current);
|
|
94
|
+
if (unwrapped === current) {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
current = unwrapped;
|
|
98
|
+
}
|
|
99
|
+
return current;
|
|
100
|
+
};
|
|
101
|
+
const normalizePartitionSqlFragment = (value) => {
|
|
102
|
+
const normalized = stripDoubleQuotes(normalizeWhitespace(normalizePartitionLiterals(value)));
|
|
103
|
+
let ret = '';
|
|
104
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
105
|
+
if (normalized[i] === "'") {
|
|
106
|
+
const end = skipQuotedLiteral(normalized, i);
|
|
107
|
+
ret += normalized.slice(i, end + 1);
|
|
108
|
+
i = end;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (normalized[i] === '(') {
|
|
112
|
+
const end = findMatchingParenthesis(normalized, i);
|
|
113
|
+
if (end === -1) {
|
|
114
|
+
ret += normalized.slice(i);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
const inner = unwrapAllOuterParentheses(normalizePartitionSqlFragment(normalized.slice(i + 1, end)));
|
|
118
|
+
ret += `(${inner})`;
|
|
119
|
+
i = end;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
ret += normalized[i];
|
|
123
|
+
}
|
|
124
|
+
return normalizeWhitespace(unwrapAllOuterParentheses(ret));
|
|
125
|
+
};
|
|
126
|
+
const unquoteIdentifier = (value) => {
|
|
127
|
+
const trimmed = value.trim();
|
|
128
|
+
if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
129
|
+
return trimmed.slice(1, -1).replaceAll('""', '"');
|
|
130
|
+
}
|
|
131
|
+
return trimmed;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Split a user-supplied partition name into `{ schema, name }`. Supports bare (`child`),
|
|
135
|
+
* schema-qualified (`schema.child`), and quoted (`"my.schema"."child"`) forms. Dots inside
|
|
136
|
+
* double-quoted identifiers are part of the identifier and do not split.
|
|
137
|
+
*/
|
|
138
|
+
const splitPartitionName = (name) => {
|
|
139
|
+
let depth = 0;
|
|
140
|
+
for (let i = 0; i < name.length; i++) {
|
|
141
|
+
const ch = name[i];
|
|
142
|
+
if (ch === '"') {
|
|
143
|
+
if (name[i + 1] === '"') {
|
|
144
|
+
i++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
depth = depth === 0 ? 1 : 0;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (ch === '.' && depth === 0) {
|
|
151
|
+
return {
|
|
152
|
+
schema: unquoteIdentifier(name.slice(0, i)),
|
|
153
|
+
name: unquoteIdentifier(name.slice(i + 1)),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { name: unquoteIdentifier(name) };
|
|
158
|
+
};
|
|
159
|
+
const resolvePartitionKey = (meta, key, quoteIdentifier) => {
|
|
160
|
+
const trimmed = key.trim().replaceAll('"', '');
|
|
161
|
+
if (!trimmed) {
|
|
162
|
+
throw new Error(`Entity ${meta.className} has invalid partitionBy option: empty partition key`);
|
|
163
|
+
}
|
|
164
|
+
const prop = meta.root.properties[trimmed] ??
|
|
165
|
+
Object.values(meta.root.properties).find(candidate => candidate.fieldNames?.length === 1 && candidate.fieldNames[0] === trimmed);
|
|
166
|
+
if (!prop) {
|
|
167
|
+
throw new Error(`Entity ${meta.className} has invalid partitionBy option: unknown partition key '${key.trim()}'`);
|
|
168
|
+
}
|
|
169
|
+
if (prop.fieldNames?.length !== 1) {
|
|
170
|
+
throw new Error(`Entity ${meta.className} has invalid partitionBy option: partition key '${key.trim()}' maps to multiple columns ('${prop.fieldNames?.join("', '")}'); list them explicitly as partition keys`);
|
|
171
|
+
}
|
|
172
|
+
return quoteIdentifier(prop.fieldNames[0]);
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Resolve the partition expression to a SQL fragment. Column-reference forms (array of keys
|
|
176
|
+
* or a clean comma-list of identifiers) are rewritten to the backing `fieldNames` and passed
|
|
177
|
+
* through `quoteIdentifier`. The callback form and the raw-SQL fallback (anything that isn't
|
|
178
|
+
* a clean identifier list, e.g. `date_trunc('day', created_at)`) are emitted verbatim — the
|
|
179
|
+
* user owns identifier quoting inside a raw expression.
|
|
180
|
+
*/
|
|
181
|
+
const resolvePartitionExpression = (meta, expression, quoteIdentifier) => {
|
|
182
|
+
if (typeof expression === 'function') {
|
|
183
|
+
return normalizeWhitespace(expression(meta.createSchemaColumnMappingObject()));
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(expression)) {
|
|
186
|
+
return expression.map(key => resolvePartitionKey(meta, key, quoteIdentifier)).join(', ');
|
|
187
|
+
}
|
|
188
|
+
const trimmed = expression.trim();
|
|
189
|
+
const keys = splitCommaSeparatedIdentifiers(trimmed);
|
|
190
|
+
if (keys) {
|
|
191
|
+
return keys.map(key => resolvePartitionKey(meta, key, quoteIdentifier)).join(', ');
|
|
192
|
+
}
|
|
193
|
+
return trimmed;
|
|
194
|
+
};
|
|
195
|
+
const createPartitionDefinition = (type, expression) => `${type.toLowerCase()} (${normalizeWhitespace(expression)})`;
|
|
196
|
+
/** @internal */
|
|
197
|
+
export function normalizePartitionDefinition(value) {
|
|
198
|
+
const normalized = normalizeWhitespace(value);
|
|
199
|
+
const match = /^(\w+)\s*(.*)$/.exec(normalized);
|
|
200
|
+
const rawType = match ? match[1] : normalized;
|
|
201
|
+
const type = rawType.toLowerCase();
|
|
202
|
+
const expression = match ? match[2].trim() : '';
|
|
203
|
+
if (!expression) {
|
|
204
|
+
return type;
|
|
205
|
+
}
|
|
206
|
+
if (!expression.startsWith('(')) {
|
|
207
|
+
return `${type} ${normalizePartitionSqlFragment(expression)}`;
|
|
208
|
+
}
|
|
209
|
+
return `${type} (${normalizePartitionSqlFragment(unwrapAllOuterParentheses(expression))})`;
|
|
210
|
+
}
|
|
211
|
+
const PARTITION_BOUND_KEYWORDS = /\b(for values|with|in|from|to|minvalue|maxvalue|null)\b/gi;
|
|
212
|
+
/** @internal */
|
|
213
|
+
export function normalizePartitionBound(value) {
|
|
214
|
+
const normalized = normalizeWhitespace(value);
|
|
215
|
+
if (!normalized) {
|
|
216
|
+
return '';
|
|
217
|
+
}
|
|
218
|
+
if (/^default$/i.test(normalized)) {
|
|
219
|
+
return 'default';
|
|
220
|
+
}
|
|
221
|
+
// Prepend `for values` if the caller passed a bare `with/in/from … to …` clause, then lowercase
|
|
222
|
+
// PG bound keywords outside quoted literals (so `FROM (MINVALUE) TO ('hello TO world')` becomes
|
|
223
|
+
// `from (minvalue) to ('hello TO world')` with the inner TO inside the literal preserved).
|
|
224
|
+
// PG's `pg_get_expr` emits `MINVALUE`/`MAXVALUE`/`NULL` in uppercase, so case-folding them here
|
|
225
|
+
// prevents a perpetual diff against user-supplied lowercase bounds.
|
|
226
|
+
const prefixed = /^for values\b/i.test(normalized) ? normalized : `for values ${normalized}`;
|
|
227
|
+
const lowered = mapOutsideLiterals(prefixed, segment => segment.replace(PARTITION_BOUND_KEYWORDS, match => match.toLowerCase()));
|
|
228
|
+
return normalizePartitionSqlFragment(lowered);
|
|
229
|
+
}
|
|
230
|
+
const createPartitionBound = (value) => normalizePartitionBound(value);
|
|
231
|
+
const createHashPartitions = (tableName, tableSchema, partitions) => {
|
|
232
|
+
const count = typeof partitions === 'number' ? partitions : partitions.length;
|
|
233
|
+
return Array.from({ length: count }, (_, remainder) => {
|
|
234
|
+
const bound = normalizePartitionBound(`with (modulus ${count}, remainder ${remainder})`);
|
|
235
|
+
if (typeof partitions === 'number') {
|
|
236
|
+
return { name: `${tableName}_${remainder}`, schema: tableSchema, bound };
|
|
237
|
+
}
|
|
238
|
+
const { name, schema } = splitPartitionName(partitions[remainder]);
|
|
239
|
+
return { name, schema: schema ?? tableSchema, bound };
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
const createExplicitPartitions = (tableName, tableSchema, partitions) => partitions.map((partition, index) => {
|
|
243
|
+
const resolvedName = partition.name ?? `${tableName}_${index}`;
|
|
244
|
+
const { name, schema } = splitPartitionName(resolvedName);
|
|
245
|
+
return {
|
|
246
|
+
name,
|
|
247
|
+
schema: schema ?? tableSchema,
|
|
248
|
+
bound: createPartitionBound(partition.values),
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
/** @internal */
|
|
252
|
+
export const getTablePartitioning = (meta, tableSchema, quoteIdentifier = id => id) => {
|
|
253
|
+
if (!meta.partitionBy) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
const definition = createPartitionDefinition(meta.partitionBy.type, resolvePartitionExpression(meta, meta.partitionBy.expression, quoteIdentifier));
|
|
257
|
+
const partitions = meta.partitionBy.type === 'hash'
|
|
258
|
+
? createHashPartitions(meta.tableName, tableSchema, meta.partitionBy.partitions)
|
|
259
|
+
: createExplicitPartitions(meta.tableName, tableSchema, meta.partitionBy.partitions);
|
|
260
|
+
return { definition, partitions };
|
|
261
|
+
};
|
|
262
|
+
/** @internal */
|
|
263
|
+
export const diffPartitioning = (from, to, defaultSchema) => {
|
|
264
|
+
if (!from && !to) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
if (!from || !to) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
if (normalizeQuotedIdentifiers(normalizePartitionDefinition(from.definition)) !==
|
|
271
|
+
normalizeQuotedIdentifiers(normalizePartitionDefinition(to.definition))) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
if (from.partitions.length !== to.partitions.length) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
const normalizeSchema = (schema) => (schema && schema !== defaultSchema ? schema : '');
|
|
278
|
+
const serializePartition = (partition) => `${normalizeSchema(partition.schema)}.${partition.name}:${normalizeQuotedIdentifiers(normalizePartitionBound(partition.bound))}`;
|
|
279
|
+
const fromPartitions = from.partitions.map(serializePartition).sort();
|
|
280
|
+
const toPartitions = to.partitions.map(serializePartition).sort();
|
|
281
|
+
return fromPartitions.some((partition, index) => partition !== toPartitions[index]);
|
|
282
|
+
};
|
|
283
|
+
const SUPPORTED_PARTITION_TYPES = ['hash', 'list', 'range'];
|
|
284
|
+
const isSupportedPartitionType = (value) => SUPPORTED_PARTITION_TYPES.includes(value);
|
|
285
|
+
/** @internal */
|
|
286
|
+
export const toEntityPartitionBy = (partitioning, parentTableName, parentSchema) => {
|
|
287
|
+
if (!partitioning) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
const normalizedDefinition = normalizePartitionDefinition(partitioning.definition);
|
|
291
|
+
const normalizedPartitions = partitioning.partitions.map(partition => ({
|
|
292
|
+
...partition,
|
|
293
|
+
bound: normalizePartitionBound(partition.bound),
|
|
294
|
+
}));
|
|
295
|
+
// Split the leading type keyword off of the definition without using `split(' ')`, which would
|
|
296
|
+
// shatter quoted literals containing spaces. Match a bareword prefix followed by whitespace.
|
|
297
|
+
const [, rawType = normalizedDefinition, rawExpression = ''] = /^(\S+)(?:\s+([\s\S]*))?$/.exec(normalizeWhitespace(normalizedDefinition)) ?? [];
|
|
298
|
+
const type = rawType.toLowerCase();
|
|
299
|
+
if (!isSupportedPartitionType(type)) {
|
|
300
|
+
throw new Error(`Unsupported partition type '${rawType}' in definition '${partitioning.definition}'`);
|
|
301
|
+
}
|
|
302
|
+
const expression = unwrapOuterParentheses(rawExpression);
|
|
303
|
+
const qualify = (partition) => partition.schema && partition.schema !== parentSchema ? `${partition.schema}.${partition.name}` : partition.name;
|
|
304
|
+
if (type === 'hash') {
|
|
305
|
+
// Collapse to a bare count when catalog names follow the default
|
|
306
|
+
// `${parentTableName}_${remainder}` pattern and live in the parent's schema, or when we have
|
|
307
|
+
// no parent context to compare against (backwards-compatible behavior for callers that pass
|
|
308
|
+
// just the `TablePartitioning`). Otherwise preserve the explicit name array so the next DDL
|
|
309
|
+
// generation reproduces the same children.
|
|
310
|
+
const usesDefaultShape = parentTableName == null ||
|
|
311
|
+
normalizedPartitions.every((p, i) => p.name === `${parentTableName}_${i}` && (!p.schema || p.schema === parentSchema));
|
|
312
|
+
return {
|
|
313
|
+
type,
|
|
314
|
+
expression,
|
|
315
|
+
partitions: usesDefaultShape ? normalizedPartitions.length : normalizedPartitions.map(qualify),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
type,
|
|
320
|
+
expression,
|
|
321
|
+
partitions: normalizedPartitions.map(partition => ({
|
|
322
|
+
name: qualify(partition),
|
|
323
|
+
values: partition.bound === 'default' ? 'default' : partition.bound.replace(/^for values\s+/i, ''),
|
|
324
|
+
})),
|
|
325
|
+
};
|
|
326
|
+
};
|
package/typings.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Generated, Kysely } from 'kysely';
|
|
2
|
-
import type { CheckCallback, DeferMode, Dictionary, EntityName, EntityProperty, EntitySchemaWithMeta, FilterQuery, GroupOperator, IndexColumnOptions, InferEntityName, Opt, Primary, PrimaryProperty, QueryFlag, QueryOrderMap, RawQueryFragment, Scalar, Type } from '@mikro-orm/core';
|
|
2
|
+
import type { CheckCallback, DeferMode, Dictionary, EntityName, EntityProperty, EntitySchemaWithMeta, FilterQuery, GroupOperator, IndexColumnOptions, InferEntityName, Opt, Primary, PrimaryProperty, QueryFlag, QueryOrderMap, RawQueryFragment, RoutineIgnoreField, Scalar, Type } from '@mikro-orm/core';
|
|
3
3
|
import type { JoinType, QueryType } from './query/enums.js';
|
|
4
4
|
import type { DatabaseSchema } from './schema/DatabaseSchema.js';
|
|
5
5
|
import type { DatabaseTable } from './schema/DatabaseTable.js';
|
|
@@ -44,6 +44,7 @@ export interface Column {
|
|
|
44
44
|
default?: string | null;
|
|
45
45
|
defaultConstraint?: string;
|
|
46
46
|
comment?: string;
|
|
47
|
+
collation?: string;
|
|
47
48
|
generated?: string;
|
|
48
49
|
nativeEnumName?: string;
|
|
49
50
|
enumItems?: string[];
|
|
@@ -51,7 +52,7 @@ export interface Column {
|
|
|
51
52
|
unique?: boolean;
|
|
52
53
|
/** mysql only */
|
|
53
54
|
extra?: string;
|
|
54
|
-
ignoreSchemaChanges?: ('type' | 'extra' | 'default')[];
|
|
55
|
+
ignoreSchemaChanges?: ('type' | 'extra' | 'default' | 'collation')[];
|
|
55
56
|
}
|
|
56
57
|
export interface ForeignKey {
|
|
57
58
|
columnNames: string[];
|
|
@@ -71,6 +72,11 @@ export interface IndexDef {
|
|
|
71
72
|
primary: boolean;
|
|
72
73
|
composite?: boolean;
|
|
73
74
|
expression?: string;
|
|
75
|
+
/**
|
|
76
|
+
* WHERE predicate for partial indexes, normalized to a SQL fragment after metadata
|
|
77
|
+
* resolution and introspection. Mutually exclusive with `expression`.
|
|
78
|
+
*/
|
|
79
|
+
where?: string;
|
|
74
80
|
options?: Dictionary;
|
|
75
81
|
type?: string | Readonly<{
|
|
76
82
|
indexType?: string;
|
|
@@ -118,6 +124,44 @@ export interface SqlTriggerDef {
|
|
|
118
124
|
when?: string;
|
|
119
125
|
expression?: string;
|
|
120
126
|
}
|
|
127
|
+
/** Resolved routine definition for schema operations (callback bodies resolved to strings). */
|
|
128
|
+
export interface SqlRoutineDef {
|
|
129
|
+
name: string;
|
|
130
|
+
schema?: string;
|
|
131
|
+
type: 'procedure' | 'function';
|
|
132
|
+
language?: string;
|
|
133
|
+
comment?: string;
|
|
134
|
+
security?: 'invoker' | 'definer';
|
|
135
|
+
definer?: string;
|
|
136
|
+
deterministic?: boolean;
|
|
137
|
+
dataAccess?: 'contains-sql' | 'no-sql' | 'reads-sql-data' | 'modifies-sql-data';
|
|
138
|
+
body?: string;
|
|
139
|
+
expression?: string;
|
|
140
|
+
returns?: {
|
|
141
|
+
type: string;
|
|
142
|
+
runtimeType?: string;
|
|
143
|
+
nullable?: boolean;
|
|
144
|
+
};
|
|
145
|
+
params: SqlRoutineParamDef[];
|
|
146
|
+
ignoreSchemaChanges?: RoutineIgnoreField[];
|
|
147
|
+
}
|
|
148
|
+
/** Resolved parameter of a stored routine for schema operations. */
|
|
149
|
+
export interface SqlRoutineParamDef {
|
|
150
|
+
name: string;
|
|
151
|
+
type: string;
|
|
152
|
+
direction: 'in' | 'out' | 'inout';
|
|
153
|
+
nullable?: boolean;
|
|
154
|
+
defaultRaw?: string;
|
|
155
|
+
}
|
|
156
|
+
export interface TablePartition {
|
|
157
|
+
name: string;
|
|
158
|
+
schema?: string;
|
|
159
|
+
bound: string;
|
|
160
|
+
}
|
|
161
|
+
export interface TablePartitioning {
|
|
162
|
+
definition: string;
|
|
163
|
+
partitions: TablePartition[];
|
|
164
|
+
}
|
|
121
165
|
export interface ColumnDifference {
|
|
122
166
|
oldColumnName: string;
|
|
123
167
|
column: Column;
|
|
@@ -127,6 +171,10 @@ export interface ColumnDifference {
|
|
|
127
171
|
export interface TableDifference {
|
|
128
172
|
name: string;
|
|
129
173
|
changedComment?: string;
|
|
174
|
+
changedPartitioning?: {
|
|
175
|
+
from?: TablePartitioning;
|
|
176
|
+
to?: TablePartitioning;
|
|
177
|
+
};
|
|
130
178
|
fromTable: DatabaseTable;
|
|
131
179
|
toTable: DatabaseTable;
|
|
132
180
|
addedColumns: Dictionary<Column>;
|
|
@@ -174,6 +222,12 @@ export interface SchemaDifference {
|
|
|
174
222
|
to: DatabaseView;
|
|
175
223
|
}>;
|
|
176
224
|
removedViews: Dictionary<DatabaseView>;
|
|
225
|
+
newRoutines: Dictionary<SqlRoutineDef>;
|
|
226
|
+
changedRoutines: Dictionary<{
|
|
227
|
+
from: SqlRoutineDef;
|
|
228
|
+
to: SqlRoutineDef;
|
|
229
|
+
}>;
|
|
230
|
+
removedRoutines: Dictionary<SqlRoutineDef>;
|
|
177
231
|
removedNamespaces: Set<string>;
|
|
178
232
|
removedNativeEnums: {
|
|
179
233
|
name: string;
|
|
@@ -246,7 +300,7 @@ export interface ICriteriaNode<T extends object> {
|
|
|
246
300
|
shouldInline(payload: any): boolean;
|
|
247
301
|
willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
|
|
248
302
|
shouldRename(payload: any): boolean;
|
|
249
|
-
renameFieldToPK<T>(qb: IQueryBuilder<T>, ownerAlias?: string): string;
|
|
303
|
+
renameFieldToPK<T>(qb: IQueryBuilder<T>, ownerAlias?: string, options?: ICriteriaNodeProcessOptions): string;
|
|
250
304
|
getPath(opts?: {
|
|
251
305
|
addIndex?: boolean;
|
|
252
306
|
}): string;
|
|
@@ -343,7 +397,7 @@ type ClassEntityColumns<T, TOptions extends MikroKyselyPluginOptions = {}> = T e
|
|
|
343
397
|
type ClassEntityColumnName<K, V, TOptions extends MikroKyselyPluginOptions = {}> = K extends symbol ? never : NonNullable<V> extends infer NV ? NV extends {
|
|
344
398
|
[k: number]: any;
|
|
345
399
|
readonly owner: object;
|
|
346
|
-
} ? never : TOptions['columnNamingStrategy'] extends 'property' ? K : NV extends Scalar ? K extends string ? SnakeCase<K> : never : K extends string ? ClassEntityJoinColumnName<SnakeCase<K>, NV> : never : never;
|
|
400
|
+
} ? never : TOptions['columnNamingStrategy'] extends 'property' ? K : NV extends Scalar | readonly any[] ? K extends string ? SnakeCase<K> : never : K extends string ? ClassEntityJoinColumnName<SnakeCase<K>, NV> : never : never;
|
|
347
401
|
type ClassEntityJoinColumnName<TName extends string, V> = PrimaryProperty<V> extends string ? `${TName}_${SnakeCase<PrimaryProperty<V>>}` : never;
|
|
348
|
-
type ClassEntityColumnValue<V> = NonNullable<V> extends Scalar ? V : Primary<NonNullable<V>>;
|
|
402
|
+
type ClassEntityColumnValue<V> = NonNullable<V> extends Scalar | readonly any[] ? V : Primary<NonNullable<V>>;
|
|
349
403
|
export {};
|