@mikro-orm/sql 7.0.0-dev.321 → 7.0.0-dev.322
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/AbstractSqlDriver.js +4 -4
- package/PivotCollectionPersister.d.ts +2 -11
- package/PivotCollectionPersister.js +59 -59
- package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
- package/dialects/mysql/MySqlSchemaHelper.js +4 -4
- package/package.json +2 -2
- package/plugin/index.d.ts +1 -3
- package/plugin/index.js +12 -12
- package/plugin/transformer.d.ts +5 -20
- package/plugin/transformer.js +81 -73
- package/query/ObjectCriteriaNode.js +3 -3
- package/query/QueryBuilder.d.ts +57 -51
- package/query/QueryBuilder.js +339 -368
- package/query/QueryBuilderHelper.d.ts +1 -8
- package/query/QueryBuilderHelper.js +97 -97
- package/query/ScalarCriteriaNode.js +3 -1
- package/schema/DatabaseSchema.d.ts +7 -5
- package/schema/DatabaseSchema.js +50 -33
- package/schema/DatabaseTable.d.ts +7 -5
- package/schema/DatabaseTable.js +77 -59
- package/schema/SchemaComparator.d.ts +1 -3
- package/schema/SchemaComparator.js +18 -18
- package/tsconfig.build.tsbuildinfo +1 -1
- package/typings.d.ts +4 -1
package/plugin/transformer.js
CHANGED
|
@@ -1,44 +1,52 @@
|
|
|
1
1
|
import { ReferenceKind, isRaw, } from '@mikro-orm/core';
|
|
2
2
|
import { AliasNode, ColumnNode, ColumnUpdateNode, OperationNodeTransformer, PrimitiveValueListNode, ReferenceNode, SchemableIdentifierNode, TableNode, ValueListNode, ValueNode, ValuesNode, } from 'kysely';
|
|
3
3
|
export class MikroTransformer extends OperationNodeTransformer {
|
|
4
|
-
em;
|
|
5
|
-
options;
|
|
6
4
|
/**
|
|
7
5
|
* Context stack to support nested queries (subqueries, CTEs)
|
|
8
6
|
* Each level of query scope has its own Map of table aliases/names to EntityMetadata
|
|
9
7
|
* Top of stack (highest index) is the current scope
|
|
10
8
|
*/
|
|
11
|
-
contextStack = [];
|
|
9
|
+
#contextStack = [];
|
|
12
10
|
/**
|
|
13
11
|
* Subquery alias map: maps subquery/CTE alias to its source table metadata
|
|
14
12
|
* Used to resolve columns from subqueries/CTEs to their original table definitions
|
|
15
13
|
*/
|
|
16
|
-
subqueryAliasMap = new Map();
|
|
17
|
-
metadata;
|
|
18
|
-
platform;
|
|
14
|
+
#subqueryAliasMap = new Map();
|
|
15
|
+
#metadata;
|
|
16
|
+
#platform;
|
|
19
17
|
/**
|
|
20
18
|
* Global map of all entities involved in the query.
|
|
21
19
|
* Populated during AST transformation and used for result transformation.
|
|
22
20
|
*/
|
|
23
|
-
entityMap = new Map();
|
|
21
|
+
#entityMap = new Map();
|
|
22
|
+
#em;
|
|
23
|
+
#options;
|
|
24
24
|
constructor(em, options = {}) {
|
|
25
25
|
super();
|
|
26
|
-
this
|
|
27
|
-
this
|
|
28
|
-
this
|
|
29
|
-
this
|
|
26
|
+
this.#em = em;
|
|
27
|
+
this.#options = options;
|
|
28
|
+
this.#metadata = em.getMetadata();
|
|
29
|
+
this.#platform = em.getDriver().getPlatform();
|
|
30
30
|
}
|
|
31
31
|
reset() {
|
|
32
|
-
this
|
|
33
|
-
this
|
|
32
|
+
this.#subqueryAliasMap.clear();
|
|
33
|
+
this.#entityMap.clear();
|
|
34
34
|
}
|
|
35
35
|
getOutputEntityMap() {
|
|
36
|
-
return this
|
|
36
|
+
return this.#entityMap;
|
|
37
|
+
}
|
|
38
|
+
/** @internal */
|
|
39
|
+
getContextStack() {
|
|
40
|
+
return this.#contextStack;
|
|
41
|
+
}
|
|
42
|
+
/** @internal */
|
|
43
|
+
getSubqueryAliasMap() {
|
|
44
|
+
return this.#subqueryAliasMap;
|
|
37
45
|
}
|
|
38
46
|
transformSelectQuery(node, queryId) {
|
|
39
47
|
// Push a new context for this query scope (starts with inherited parent context)
|
|
40
48
|
const currentContext = new Map();
|
|
41
|
-
this
|
|
49
|
+
this.#contextStack.push(currentContext);
|
|
42
50
|
try {
|
|
43
51
|
// Process WITH clause (CTEs) first - they define names available in this scope
|
|
44
52
|
if (node.with) {
|
|
@@ -60,12 +68,12 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
60
68
|
}
|
|
61
69
|
finally {
|
|
62
70
|
// Pop the context when exiting this query scope
|
|
63
|
-
this
|
|
71
|
+
this.#contextStack.pop();
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
transformInsertQuery(node, queryId) {
|
|
67
75
|
const currentContext = new Map();
|
|
68
|
-
this
|
|
76
|
+
this.#contextStack.push(currentContext);
|
|
69
77
|
try {
|
|
70
78
|
let entityMeta;
|
|
71
79
|
if (node.into) {
|
|
@@ -75,12 +83,12 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
75
83
|
if (meta) {
|
|
76
84
|
entityMeta = meta;
|
|
77
85
|
currentContext.set(meta.tableName, meta);
|
|
78
|
-
this
|
|
86
|
+
this.#entityMap.set(meta.tableName, meta);
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
|
-
const nodeWithHooks = this
|
|
83
|
-
const nodeWithConvertedValues = this
|
|
90
|
+
const nodeWithHooks = this.#options.processOnCreateHooks && entityMeta ? this.processOnCreateHooks(node, entityMeta) : node;
|
|
91
|
+
const nodeWithConvertedValues = this.#options.convertValues && entityMeta ? this.processInsertValues(nodeWithHooks, entityMeta) : nodeWithHooks;
|
|
84
92
|
// Handle ON CONFLICT clause
|
|
85
93
|
let finalNode = nodeWithConvertedValues;
|
|
86
94
|
if (node.onConflict?.updates && entityMeta) {
|
|
@@ -91,14 +99,14 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
91
99
|
table: node.into, // Dummy table
|
|
92
100
|
updates: node.onConflict.updates,
|
|
93
101
|
};
|
|
94
|
-
const updatesWithHooks = this
|
|
102
|
+
const updatesWithHooks = this.#options.processOnUpdateHooks
|
|
95
103
|
? this.processOnUpdateHooks(tempUpdateNode, entityMeta).updates
|
|
96
104
|
: node.onConflict.updates;
|
|
97
105
|
const tempUpdateNodeWithHooks = {
|
|
98
106
|
...tempUpdateNode,
|
|
99
107
|
updates: updatesWithHooks,
|
|
100
108
|
};
|
|
101
|
-
const updatesWithConvertedValues = this
|
|
109
|
+
const updatesWithConvertedValues = this.#options.convertValues
|
|
102
110
|
? this.processUpdateValues(tempUpdateNodeWithHooks, entityMeta).updates
|
|
103
111
|
: updatesWithHooks;
|
|
104
112
|
if (updatesWithConvertedValues && updatesWithConvertedValues !== node.onConflict.updates) {
|
|
@@ -115,12 +123,12 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
115
123
|
return super.transformInsertQuery(finalNode, queryId);
|
|
116
124
|
}
|
|
117
125
|
finally {
|
|
118
|
-
this
|
|
126
|
+
this.#contextStack.pop();
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
transformUpdateQuery(node, queryId) {
|
|
122
130
|
const currentContext = new Map();
|
|
123
|
-
this
|
|
131
|
+
this.#contextStack.push(currentContext);
|
|
124
132
|
try {
|
|
125
133
|
let entityMeta;
|
|
126
134
|
if (node.table && TableNode.is(node.table)) {
|
|
@@ -130,7 +138,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
130
138
|
if (meta) {
|
|
131
139
|
entityMeta = meta;
|
|
132
140
|
currentContext.set(meta.tableName, meta);
|
|
133
|
-
this
|
|
141
|
+
this.#entityMap.set(meta.tableName, meta);
|
|
134
142
|
}
|
|
135
143
|
}
|
|
136
144
|
}
|
|
@@ -146,17 +154,17 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
146
154
|
this.processJoinNode(join, currentContext);
|
|
147
155
|
}
|
|
148
156
|
}
|
|
149
|
-
const nodeWithHooks = this
|
|
150
|
-
const nodeWithConvertedValues = this
|
|
157
|
+
const nodeWithHooks = this.#options.processOnUpdateHooks && entityMeta ? this.processOnUpdateHooks(node, entityMeta) : node;
|
|
158
|
+
const nodeWithConvertedValues = this.#options.convertValues && entityMeta ? this.processUpdateValues(nodeWithHooks, entityMeta) : nodeWithHooks;
|
|
151
159
|
return super.transformUpdateQuery(nodeWithConvertedValues, queryId);
|
|
152
160
|
}
|
|
153
161
|
finally {
|
|
154
|
-
this
|
|
162
|
+
this.#contextStack.pop();
|
|
155
163
|
}
|
|
156
164
|
}
|
|
157
165
|
transformDeleteQuery(node, queryId) {
|
|
158
166
|
const currentContext = new Map();
|
|
159
|
-
this
|
|
167
|
+
this.#contextStack.push(currentContext);
|
|
160
168
|
try {
|
|
161
169
|
const froms = node.from?.froms;
|
|
162
170
|
if (froms && froms.length > 0) {
|
|
@@ -167,7 +175,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
167
175
|
const meta = this.findEntityMetadata(tableName);
|
|
168
176
|
if (meta) {
|
|
169
177
|
currentContext.set(meta.tableName, meta);
|
|
170
|
-
this
|
|
178
|
+
this.#entityMap.set(meta.tableName, meta);
|
|
171
179
|
}
|
|
172
180
|
}
|
|
173
181
|
}
|
|
@@ -181,24 +189,24 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
181
189
|
return super.transformDeleteQuery(node, queryId);
|
|
182
190
|
}
|
|
183
191
|
finally {
|
|
184
|
-
this
|
|
192
|
+
this.#contextStack.pop();
|
|
185
193
|
}
|
|
186
194
|
}
|
|
187
195
|
transformMergeQuery(node, queryId) {
|
|
188
196
|
const currentContext = new Map();
|
|
189
|
-
this
|
|
197
|
+
this.#contextStack.push(currentContext);
|
|
190
198
|
try {
|
|
191
199
|
return super.transformMergeQuery(node, queryId);
|
|
192
200
|
}
|
|
193
201
|
finally {
|
|
194
|
-
this
|
|
202
|
+
this.#contextStack.pop();
|
|
195
203
|
}
|
|
196
204
|
}
|
|
197
205
|
transformIdentifier(node, queryId) {
|
|
198
206
|
node = super.transformIdentifier(node, queryId);
|
|
199
207
|
const parent = this.nodeStack[this.nodeStack.length - 2];
|
|
200
208
|
// Transform table names when tableNamingStrategy is 'entity'
|
|
201
|
-
if (this
|
|
209
|
+
if (this.#options.tableNamingStrategy === 'entity' && parent && SchemableIdentifierNode.is(parent)) {
|
|
202
210
|
const meta = this.findEntityMetadata(node.name);
|
|
203
211
|
if (meta) {
|
|
204
212
|
return {
|
|
@@ -209,7 +217,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
209
217
|
}
|
|
210
218
|
// Transform column names when columnNamingStrategy is 'property'
|
|
211
219
|
// Support ColumnNode, ColumnUpdateNode, and ReferenceNode (for JOIN conditions)
|
|
212
|
-
if (this
|
|
220
|
+
if (this.#options.columnNamingStrategy === 'property' &&
|
|
213
221
|
parent &&
|
|
214
222
|
(ColumnNode.is(parent) || ColumnUpdateNode.is(parent) || ReferenceNode.is(parent))) {
|
|
215
223
|
const ownerMeta = this.findOwnerEntityInContext();
|
|
@@ -239,8 +247,8 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
239
247
|
const tableName = this.getTableName(reference.table);
|
|
240
248
|
if (tableName) {
|
|
241
249
|
// First, check in subquery alias map (for CTE/subquery columns)
|
|
242
|
-
if (this
|
|
243
|
-
return this
|
|
250
|
+
if (this.#subqueryAliasMap.has(tableName)) {
|
|
251
|
+
return this.#subqueryAliasMap.get(tableName);
|
|
244
252
|
}
|
|
245
253
|
// Find entity metadata to get the actual table name
|
|
246
254
|
// Context uses table names (meta.tableName) as keys, not entity names
|
|
@@ -267,16 +275,16 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
267
275
|
}
|
|
268
276
|
}
|
|
269
277
|
// If no explicit table reference, use the first entity in current context
|
|
270
|
-
if (this
|
|
271
|
-
const currentContext = this
|
|
278
|
+
if (this.#contextStack.length > 0) {
|
|
279
|
+
const currentContext = this.#contextStack[this.#contextStack.length - 1];
|
|
272
280
|
for (const [alias, meta] of currentContext.entries()) {
|
|
273
281
|
if (meta) {
|
|
274
282
|
return meta;
|
|
275
283
|
}
|
|
276
284
|
// If the context value is undefined but the alias is in subqueryAliasMap,
|
|
277
285
|
// use the mapped metadata (for CTE/subquery cases)
|
|
278
|
-
if (!meta && this
|
|
279
|
-
const mappedMeta = this
|
|
286
|
+
if (!meta && this.#subqueryAliasMap.has(alias)) {
|
|
287
|
+
const mappedMeta = this.#subqueryAliasMap.get(alias);
|
|
280
288
|
if (mappedMeta) {
|
|
281
289
|
return mappedMeta;
|
|
282
290
|
}
|
|
@@ -306,7 +314,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
306
314
|
}
|
|
307
315
|
const newRows = node.values.values.map(row => {
|
|
308
316
|
const valuesToAdd = missingProps.map(prop => {
|
|
309
|
-
const val = prop.onCreate(undefined, this
|
|
317
|
+
const val = prop.onCreate(undefined, this.#em);
|
|
310
318
|
return val;
|
|
311
319
|
});
|
|
312
320
|
if (ValueListNode.is(row)) {
|
|
@@ -344,7 +352,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
344
352
|
}
|
|
345
353
|
const newUpdates = [...node.updates];
|
|
346
354
|
for (const prop of missingProps) {
|
|
347
|
-
const val = prop.onUpdate(undefined, this
|
|
355
|
+
const val = prop.onUpdate(undefined, this.#em);
|
|
348
356
|
newUpdates.push(ColumnUpdateNode.create(ColumnNode.create(prop.name), ValueNode.create(val)));
|
|
349
357
|
}
|
|
350
358
|
return {
|
|
@@ -459,7 +467,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
459
467
|
return meta.props.find(prop => prop.fieldNames?.includes(columnName));
|
|
460
468
|
}
|
|
461
469
|
shouldConvertValues() {
|
|
462
|
-
return !!this
|
|
470
|
+
return !!this.#options.convertValues;
|
|
463
471
|
}
|
|
464
472
|
prepareInputValue(prop, value, enabled) {
|
|
465
473
|
if (!enabled || !prop || value == null) {
|
|
@@ -474,14 +482,14 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
474
482
|
}
|
|
475
483
|
}
|
|
476
484
|
if (prop.customType && !isRaw(value)) {
|
|
477
|
-
return prop.customType.convertToDatabaseValue(value, this
|
|
485
|
+
return prop.customType.convertToDatabaseValue(value, this.#platform, {
|
|
478
486
|
fromQuery: true,
|
|
479
487
|
key: prop.name,
|
|
480
488
|
mode: 'query-data',
|
|
481
489
|
});
|
|
482
490
|
}
|
|
483
491
|
if (value instanceof Date) {
|
|
484
|
-
return this
|
|
492
|
+
return this.#platform.processDateProperty(value);
|
|
485
493
|
}
|
|
486
494
|
return value;
|
|
487
495
|
}
|
|
@@ -492,8 +500,8 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
492
500
|
*/
|
|
493
501
|
lookupInContextStack(tableNameOrAlias) {
|
|
494
502
|
// Search from top of stack (current scope) to bottom (parent scopes)
|
|
495
|
-
for (let i = this
|
|
496
|
-
const context = this
|
|
503
|
+
for (let i = this.#contextStack.length - 1; i >= 0; i--) {
|
|
504
|
+
const context = this.#contextStack[i];
|
|
497
505
|
if (context.has(tableNameOrAlias)) {
|
|
498
506
|
return context.get(tableNameOrAlias);
|
|
499
507
|
}
|
|
@@ -515,10 +523,10 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
515
523
|
if (cte.expression?.kind === 'SelectQueryNode') {
|
|
516
524
|
const sourceMeta = this.extractSourceTableFromSelectQuery(cte.expression);
|
|
517
525
|
if (sourceMeta) {
|
|
518
|
-
this
|
|
526
|
+
this.#subqueryAliasMap.set(cteName, sourceMeta);
|
|
519
527
|
// Add CTE to entityMap so it can be used for result transformation if needed
|
|
520
528
|
// (though CTEs usually don't appear in result rows directly, but their columns might)
|
|
521
|
-
this
|
|
529
|
+
this.#entityMap.set(cteName, sourceMeta);
|
|
522
530
|
}
|
|
523
531
|
}
|
|
524
532
|
}
|
|
@@ -548,11 +556,11 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
548
556
|
if (aliasName) {
|
|
549
557
|
context.set(aliasName, meta);
|
|
550
558
|
if (meta) {
|
|
551
|
-
this
|
|
559
|
+
this.#entityMap.set(aliasName, meta);
|
|
552
560
|
}
|
|
553
561
|
// Also map the alias in subqueryAliasMap if the table name is a CTE
|
|
554
|
-
if (this
|
|
555
|
-
this
|
|
562
|
+
if (this.#subqueryAliasMap.has(tableName)) {
|
|
563
|
+
this.#subqueryAliasMap.set(aliasName, this.#subqueryAliasMap.get(tableName));
|
|
556
564
|
}
|
|
557
565
|
}
|
|
558
566
|
}
|
|
@@ -565,7 +573,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
565
573
|
// Try to extract the source table from the subquery
|
|
566
574
|
const sourceMeta = this.extractSourceTableFromSelectQuery(from.node);
|
|
567
575
|
if (sourceMeta) {
|
|
568
|
-
this
|
|
576
|
+
this.#subqueryAliasMap.set(aliasName, sourceMeta);
|
|
569
577
|
}
|
|
570
578
|
}
|
|
571
579
|
}
|
|
@@ -584,7 +592,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
584
592
|
const meta = this.findEntityMetadata(tableName);
|
|
585
593
|
context.set(tableName, meta);
|
|
586
594
|
if (meta) {
|
|
587
|
-
this
|
|
595
|
+
this.#entityMap.set(tableName, meta);
|
|
588
596
|
}
|
|
589
597
|
}
|
|
590
598
|
}
|
|
@@ -604,11 +612,11 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
604
612
|
if (aliasName) {
|
|
605
613
|
context.set(aliasName, meta);
|
|
606
614
|
if (meta) {
|
|
607
|
-
this
|
|
615
|
+
this.#entityMap.set(aliasName, meta);
|
|
608
616
|
}
|
|
609
617
|
// Also map the alias in subqueryAliasMap if the table name is a CTE
|
|
610
|
-
if (this
|
|
611
|
-
this
|
|
618
|
+
if (this.#subqueryAliasMap.has(tableName)) {
|
|
619
|
+
this.#subqueryAliasMap.set(aliasName, this.#subqueryAliasMap.get(tableName));
|
|
612
620
|
}
|
|
613
621
|
}
|
|
614
622
|
}
|
|
@@ -621,7 +629,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
621
629
|
// Try to extract the source table from the subquery
|
|
622
630
|
const sourceMeta = this.extractSourceTableFromSelectQuery(joinTable.node);
|
|
623
631
|
if (sourceMeta) {
|
|
624
|
-
this
|
|
632
|
+
this.#subqueryAliasMap.set(aliasName, sourceMeta);
|
|
625
633
|
}
|
|
626
634
|
}
|
|
627
635
|
}
|
|
@@ -641,7 +649,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
641
649
|
// Use table name (meta.tableName) as key to match transformUpdateQuery behavior
|
|
642
650
|
if (meta) {
|
|
643
651
|
context.set(meta.tableName, meta);
|
|
644
|
-
this
|
|
652
|
+
this.#entityMap.set(meta.tableName, meta);
|
|
645
653
|
// Also set with entity name for backward compatibility
|
|
646
654
|
context.set(tableName, meta);
|
|
647
655
|
}
|
|
@@ -704,11 +712,11 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
704
712
|
* Find entity metadata by table name or entity name
|
|
705
713
|
*/
|
|
706
714
|
findEntityMetadata(name) {
|
|
707
|
-
const byEntity = this
|
|
715
|
+
const byEntity = this.#metadata.getByClassName(name, false);
|
|
708
716
|
if (byEntity) {
|
|
709
717
|
return byEntity;
|
|
710
718
|
}
|
|
711
|
-
const allMetadata = Array.from(this
|
|
719
|
+
const allMetadata = Array.from(this.#metadata);
|
|
712
720
|
const byTable = allMetadata.find(m => m.tableName === name);
|
|
713
721
|
if (byTable) {
|
|
714
722
|
return byTable;
|
|
@@ -721,7 +729,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
721
729
|
*/
|
|
722
730
|
transformResult(rows, entityMap) {
|
|
723
731
|
// Only transform if columnNamingStrategy is 'property' or convertValues is true, and we have data
|
|
724
|
-
if ((this
|
|
732
|
+
if ((this.#options.columnNamingStrategy !== 'property' && !this.#options.convertValues) ||
|
|
725
733
|
!rows ||
|
|
726
734
|
rows.length === 0) {
|
|
727
735
|
return rows;
|
|
@@ -818,7 +826,7 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
818
826
|
continue;
|
|
819
827
|
}
|
|
820
828
|
const converted = this.prepareOutputValue(prop, transformed[fieldName]);
|
|
821
|
-
if (this
|
|
829
|
+
if (this.#options.columnNamingStrategy === 'property' && prop.name !== fieldName) {
|
|
822
830
|
if (!(prop.name in transformed)) {
|
|
823
831
|
transformed[prop.name] = converted;
|
|
824
832
|
}
|
|
@@ -828,13 +836,13 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
828
836
|
delete transformed[fieldName];
|
|
829
837
|
continue;
|
|
830
838
|
}
|
|
831
|
-
if (this
|
|
839
|
+
if (this.#options.convertValues) {
|
|
832
840
|
transformed[fieldName] = converted;
|
|
833
841
|
}
|
|
834
842
|
}
|
|
835
843
|
// Second pass: handle relation fields
|
|
836
844
|
// Only run if columnNamingStrategy is 'property', as we don't want to rename FKs otherwise
|
|
837
|
-
if (this
|
|
845
|
+
if (this.#options.columnNamingStrategy === 'property') {
|
|
838
846
|
for (const [fieldName, relationPropertyName] of Object.entries(relationFieldMap)) {
|
|
839
847
|
if (fieldName in transformed && !(relationPropertyName in transformed)) {
|
|
840
848
|
// Move the foreign key value to the relation property name
|
|
@@ -846,36 +854,36 @@ export class MikroTransformer extends OperationNodeTransformer {
|
|
|
846
854
|
return transformed;
|
|
847
855
|
}
|
|
848
856
|
prepareOutputValue(prop, value) {
|
|
849
|
-
if (!this
|
|
857
|
+
if (!this.#options.convertValues || !prop || value == null) {
|
|
850
858
|
return value;
|
|
851
859
|
}
|
|
852
860
|
if (prop.customType) {
|
|
853
|
-
return prop.customType.convertToJSValue(value, this
|
|
861
|
+
return prop.customType.convertToJSValue(value, this.#platform);
|
|
854
862
|
}
|
|
855
863
|
// Aligned with EntityComparator.getResultMapper logic
|
|
856
864
|
if (prop.runtimeType === 'boolean') {
|
|
857
865
|
// Use !! conversion like EntityComparator: value == null ? value : !!value
|
|
858
866
|
return value == null ? value : !!value;
|
|
859
867
|
}
|
|
860
|
-
if (prop.runtimeType === 'Date' && !this
|
|
868
|
+
if (prop.runtimeType === 'Date' && !this.#platform.isNumericProperty(prop)) {
|
|
861
869
|
// Aligned with EntityComparator: exclude numeric timestamp properties
|
|
862
870
|
// If already Date instance or null, return as is
|
|
863
871
|
if (value == null || value instanceof Date) {
|
|
864
872
|
return value;
|
|
865
873
|
}
|
|
866
874
|
// Handle timezone like EntityComparator.parseDate
|
|
867
|
-
const tz = this
|
|
875
|
+
const tz = this.#platform.getTimezone();
|
|
868
876
|
if (!tz || tz === 'local') {
|
|
869
|
-
return this
|
|
877
|
+
return this.#platform.parseDate(value);
|
|
870
878
|
}
|
|
871
879
|
// For non-local timezone, check if value already has timezone info
|
|
872
880
|
// Number (timestamp) doesn't need timezone handling, string needs check
|
|
873
881
|
if (typeof value === 'number' ||
|
|
874
882
|
(typeof value === 'string' && (value.includes('+') || value.lastIndexOf('-') > 10 || value.endsWith('Z')))) {
|
|
875
|
-
return this
|
|
883
|
+
return this.#platform.parseDate(value);
|
|
876
884
|
}
|
|
877
885
|
// Append timezone if not present (only for string values)
|
|
878
|
-
return this
|
|
886
|
+
return this.#platform.parseDate(value + tz);
|
|
879
887
|
}
|
|
880
888
|
// For all other runtimeTypes (number, string, bigint, Buffer, object, any, etc.)
|
|
881
889
|
// EntityComparator just assigns directly without conversion
|
|
@@ -39,7 +39,7 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
39
39
|
throw new Error('Mixing collection operators with other filters is not allowed.');
|
|
40
40
|
}
|
|
41
41
|
const payload = this.payload[key].unwrap();
|
|
42
|
-
const qb2 = qb.clone(true, ['
|
|
42
|
+
const qb2 = qb.clone(true, ['schema']);
|
|
43
43
|
const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
|
|
44
44
|
const sub = qb2
|
|
45
45
|
.from(parentMeta.class)
|
|
@@ -289,12 +289,12 @@ export class ObjectCriteriaNode extends CriteriaNode {
|
|
|
289
289
|
qb.join(field, nestedAlias, undefined, JoinType.pivotJoin, path);
|
|
290
290
|
}
|
|
291
291
|
else {
|
|
292
|
-
const prev = qb.
|
|
292
|
+
const prev = qb.state.fields?.slice();
|
|
293
293
|
const toOneProperty = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(this.prop.kind);
|
|
294
294
|
const joinType = toOneProperty && !this.prop.nullable ? JoinType.innerJoin : JoinType.leftJoin;
|
|
295
295
|
qb[method](field, nestedAlias, undefined, joinType, path);
|
|
296
296
|
if (!qb.hasFlag(QueryFlag.INFER_POPULATE)) {
|
|
297
|
-
qb.
|
|
297
|
+
qb.state.fields = prev;
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
if (options?.type !== 'orderBy') {
|
package/query/QueryBuilder.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AnyEntity, type AutoPath, type Collection, type ConnectionType, type Dictionary, type EntityData, type EntityDTOFlat, type EntityDTOProp, type EntityKey, type EntityManager, EntityMetadata, type EntityName, type EntityProperty, type ExpandProperty, type FilterObject, type FilterOptions, type FilterValue, type FlushMode, type GroupOperator, type Loaded, LockMode, type LoggingOptions, type MetadataStorage, type PrimaryProperty, type ObjectQuery, PopulateHint, type PopulateOptions, type PopulatePath, QueryFlag, type QueryOrderKeysFlat, type QueryOrderMap, type QueryResult, RawQueryFragment, type Raw, type RequiredEntityData, type Scalar, type SerializeDTO, type Subquery, type Transaction } from '@mikro-orm/core';
|
|
2
2
|
import { JoinType, QueryType } from './enums.js';
|
|
3
3
|
import type { AbstractSqlDriver } from '../AbstractSqlDriver.js';
|
|
4
|
-
import { type Alias, QueryBuilderHelper } from './QueryBuilderHelper.js';
|
|
4
|
+
import { type Alias, type OnConflictClause, QueryBuilderHelper } from './QueryBuilderHelper.js';
|
|
5
5
|
import type { SqlEntityManager } from '../SqlEntityManager.js';
|
|
6
6
|
import type { ICriteriaNodeProcessOptions, InternalField, JoinOptions } from '../typings.js';
|
|
7
7
|
import type { AbstractSqlPlatform } from '../AbstractSqlPlatform.js';
|
|
@@ -139,6 +139,57 @@ type GroupOperators<RootAlias extends string, Context, Entity, RawAliases extend
|
|
|
139
139
|
};
|
|
140
140
|
export type AliasedFilterCondition<RootAlias extends string, Context, Entity, RawAliases extends string = never> = (IsNever<RootAlias> extends true ? {} : string extends RootAlias ? {} : RootAliasFilterKeys<RootAlias, Entity>) & ([Context] extends [never] ? {} : ContextFilterKeys<Context>) & (IsNever<RawAliases> extends true ? {} : string extends RawAliases ? {} : RawFilterKeys<RawAliases>) & GroupOperators<RootAlias, Context, Entity, RawAliases>;
|
|
141
141
|
export type QBFilterQuery<Entity, RootAlias extends string = never, Context = never, RawAliases extends string = never> = FilterObject<Entity> & AliasedFilterCondition<RootAlias, Context, Entity, RawAliases>;
|
|
142
|
+
/** @internal */
|
|
143
|
+
export interface QBState<Entity extends object> {
|
|
144
|
+
type?: QueryType;
|
|
145
|
+
fields?: InternalField<Entity>[];
|
|
146
|
+
populate: PopulateOptions<Entity>[];
|
|
147
|
+
populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
148
|
+
populateFilter?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
149
|
+
resolvedPopulateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
150
|
+
populateMap: Dictionary<string>;
|
|
151
|
+
aliasCounter: number;
|
|
152
|
+
flags: Set<QueryFlag>;
|
|
153
|
+
finalized: boolean;
|
|
154
|
+
populateHintFinalized: boolean;
|
|
155
|
+
joins: Dictionary<JoinOptions>;
|
|
156
|
+
explicitAlias: boolean;
|
|
157
|
+
schema?: string;
|
|
158
|
+
cond: Dictionary;
|
|
159
|
+
data?: Dictionary;
|
|
160
|
+
orderBy: QueryOrderMap<Entity>[];
|
|
161
|
+
groupBy: InternalField<Entity>[];
|
|
162
|
+
having: Dictionary;
|
|
163
|
+
returning?: InternalField<Entity>[];
|
|
164
|
+
onConflict?: OnConflictClause<Entity>[];
|
|
165
|
+
limit?: number;
|
|
166
|
+
offset?: number;
|
|
167
|
+
distinctOn?: string[];
|
|
168
|
+
joinedProps: Map<string, PopulateOptions<any>>;
|
|
169
|
+
cache?: boolean | number | [string, number];
|
|
170
|
+
indexHint?: string;
|
|
171
|
+
collation?: string;
|
|
172
|
+
comments: string[];
|
|
173
|
+
hintComments: string[];
|
|
174
|
+
flushMode?: FlushMode;
|
|
175
|
+
lockMode?: LockMode;
|
|
176
|
+
lockTables?: string[];
|
|
177
|
+
subQueries: Dictionary<string>;
|
|
178
|
+
mainAlias?: Alias<Entity>;
|
|
179
|
+
aliases: Dictionary<Alias<any>>;
|
|
180
|
+
tptAlias: Dictionary<string>;
|
|
181
|
+
unionQuery?: {
|
|
182
|
+
sql: string;
|
|
183
|
+
params: readonly unknown[];
|
|
184
|
+
};
|
|
185
|
+
ctes: (CteOptions & {
|
|
186
|
+
name: string;
|
|
187
|
+
query: NativeQueryBuilder | RawQueryFragment;
|
|
188
|
+
recursive?: boolean;
|
|
189
|
+
})[];
|
|
190
|
+
tptJoinsApplied: boolean;
|
|
191
|
+
autoJoinedPaths: string[];
|
|
192
|
+
}
|
|
142
193
|
/**
|
|
143
194
|
* SQL query builder with fluent interface.
|
|
144
195
|
*
|
|
@@ -159,6 +210,7 @@ export type QBFilterQuery<Entity, RootAlias extends string = never, Context = ne
|
|
|
159
210
|
* ```
|
|
160
211
|
*/
|
|
161
212
|
export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias extends string = never, Hint extends string = never, Context extends object = never, RawAliases extends string = never, Fields extends string = '*', CTEs extends Record<string, object> = {}> implements Subquery {
|
|
213
|
+
#private;
|
|
162
214
|
protected readonly metadata: MetadataStorage;
|
|
163
215
|
protected readonly driver: AbstractSqlDriver;
|
|
164
216
|
protected readonly context?: Transaction | undefined;
|
|
@@ -166,61 +218,15 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
166
218
|
protected em?: SqlEntityManager | undefined;
|
|
167
219
|
protected loggerContext?: (LoggingOptions & Dictionary) | undefined;
|
|
168
220
|
readonly __subquery: true;
|
|
221
|
+
/** @internal */
|
|
222
|
+
static createDefaultState<T extends object>(): QBState<T>;
|
|
169
223
|
get mainAlias(): Alias<Entity>;
|
|
170
224
|
get alias(): string;
|
|
171
225
|
get helper(): QueryBuilderHelper;
|
|
172
226
|
get type(): QueryType;
|
|
173
227
|
/** @internal */
|
|
174
|
-
|
|
175
|
-
/** @internal */
|
|
176
|
-
_fields?: InternalField<Entity>[];
|
|
177
|
-
/** @internal */
|
|
178
|
-
_populate: PopulateOptions<Entity>[];
|
|
179
|
-
/** @internal */
|
|
180
|
-
_populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
181
|
-
/** @internal */
|
|
182
|
-
_populateFilter?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
183
|
-
/** @internal */
|
|
184
|
-
__populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
|
|
185
|
-
/** @internal */
|
|
186
|
-
_populateMap: Dictionary<string>;
|
|
187
|
-
private aliasCounter;
|
|
188
|
-
protected flags: Set<QueryFlag>;
|
|
189
|
-
protected finalized: boolean;
|
|
190
|
-
private populateHintFinalized;
|
|
191
|
-
private _joins;
|
|
192
|
-
private _explicitAlias;
|
|
193
|
-
private _schema?;
|
|
194
|
-
protected _cond: Dictionary;
|
|
195
|
-
private _data;
|
|
196
|
-
protected _orderBy: QueryOrderMap<Entity>[];
|
|
197
|
-
private _groupBy;
|
|
198
|
-
private _having;
|
|
199
|
-
private _returning?;
|
|
200
|
-
private _onConflict?;
|
|
201
|
-
protected _limit?: number;
|
|
202
|
-
protected _offset?: number;
|
|
203
|
-
private _distinctOn?;
|
|
204
|
-
private _joinedProps;
|
|
205
|
-
private _cache?;
|
|
206
|
-
private _indexHint?;
|
|
207
|
-
private _collation?;
|
|
208
|
-
private _comments;
|
|
209
|
-
private _hintComments;
|
|
210
|
-
private flushMode?;
|
|
211
|
-
private lockMode?;
|
|
212
|
-
private lockTables?;
|
|
213
|
-
private subQueries;
|
|
214
|
-
private _mainAlias?;
|
|
215
|
-
protected _aliases: Dictionary<Alias<any>>;
|
|
216
|
-
private _tptAlias;
|
|
217
|
-
private _helper?;
|
|
218
|
-
private _query?;
|
|
219
|
-
private _unionQuery?;
|
|
220
|
-
private _ctes;
|
|
228
|
+
get state(): QBState<Entity>;
|
|
221
229
|
protected readonly platform: AbstractSqlPlatform;
|
|
222
|
-
private tptJoinsApplied;
|
|
223
|
-
private readonly autoJoinedPaths;
|
|
224
230
|
/**
|
|
225
231
|
* @internal
|
|
226
232
|
*/
|
|
@@ -842,7 +848,7 @@ export declare class QueryBuilder<Entity extends object = AnyEntity, RootAlias e
|
|
|
842
848
|
*/
|
|
843
849
|
withRecursive<Name extends string>(name: Name, query: NativeQueryBuilder | RawQueryFragment, options?: CteOptions): QueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs & Record<Name, object>>;
|
|
844
850
|
private addCte;
|
|
845
|
-
clone(reset?: boolean |
|
|
851
|
+
clone(reset?: boolean | (keyof QBState<Entity>)[], preserve?: (keyof QBState<Entity>)[]): QueryBuilder<Entity, RootAlias, Hint, Context, RawAliases, Fields, CTEs>;
|
|
846
852
|
/**
|
|
847
853
|
* Sets logger context for this query builder.
|
|
848
854
|
*/
|