@mikro-orm/sql 7.0.0-rc.3 → 7.0.1-dev.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.
Files changed (59) hide show
  1. package/AbstractSqlConnection.d.ts +5 -4
  2. package/AbstractSqlConnection.js +18 -5
  3. package/AbstractSqlDriver.d.ts +1 -1
  4. package/AbstractSqlDriver.js +39 -10
  5. package/AbstractSqlPlatform.d.ts +34 -0
  6. package/AbstractSqlPlatform.js +47 -3
  7. package/PivotCollectionPersister.d.ts +2 -11
  8. package/PivotCollectionPersister.js +59 -59
  9. package/README.md +5 -4
  10. package/SqlEntityManager.d.ts +1 -1
  11. package/dialects/index.d.ts +1 -0
  12. package/dialects/index.js +1 -0
  13. package/dialects/mysql/BaseMySqlPlatform.d.ts +6 -0
  14. package/dialects/mysql/BaseMySqlPlatform.js +17 -0
  15. package/dialects/mysql/MySqlSchemaHelper.d.ts +1 -1
  16. package/dialects/mysql/MySqlSchemaHelper.js +6 -6
  17. package/dialects/oracledb/OracleDialect.d.ts +78 -0
  18. package/dialects/oracledb/OracleDialect.js +166 -0
  19. package/dialects/oracledb/OracleNativeQueryBuilder.d.ts +19 -0
  20. package/dialects/oracledb/OracleNativeQueryBuilder.js +249 -0
  21. package/dialects/oracledb/index.d.ts +2 -0
  22. package/dialects/oracledb/index.js +2 -0
  23. package/dialects/postgresql/BasePostgreSqlPlatform.d.ts +6 -0
  24. package/dialects/postgresql/BasePostgreSqlPlatform.js +12 -8
  25. package/dialects/postgresql/PostgreSqlSchemaHelper.js +13 -13
  26. package/dialects/sqlite/SqlitePlatform.d.ts +1 -0
  27. package/dialects/sqlite/SqlitePlatform.js +3 -0
  28. package/dialects/sqlite/SqliteSchemaHelper.js +12 -8
  29. package/index.d.ts +1 -1
  30. package/index.js +0 -1
  31. package/package.json +3 -3
  32. package/plugin/index.d.ts +1 -14
  33. package/plugin/index.js +13 -13
  34. package/plugin/transformer.d.ts +6 -22
  35. package/plugin/transformer.js +81 -73
  36. package/query/ArrayCriteriaNode.d.ts +1 -1
  37. package/query/CriteriaNodeFactory.js +15 -3
  38. package/query/NativeQueryBuilder.d.ts +3 -3
  39. package/query/NativeQueryBuilder.js +4 -2
  40. package/query/ObjectCriteriaNode.js +4 -4
  41. package/query/QueryBuilder.d.ts +58 -62
  42. package/query/QueryBuilder.js +377 -370
  43. package/query/QueryBuilderHelper.d.ts +14 -11
  44. package/query/QueryBuilderHelper.js +324 -137
  45. package/query/ScalarCriteriaNode.js +3 -1
  46. package/query/enums.d.ts +2 -0
  47. package/query/enums.js +2 -0
  48. package/schema/DatabaseSchema.d.ts +7 -5
  49. package/schema/DatabaseSchema.js +50 -33
  50. package/schema/DatabaseTable.d.ts +8 -6
  51. package/schema/DatabaseTable.js +84 -60
  52. package/schema/SchemaComparator.d.ts +1 -3
  53. package/schema/SchemaComparator.js +22 -20
  54. package/schema/SchemaHelper.d.ts +2 -13
  55. package/schema/SchemaHelper.js +2 -1
  56. package/schema/SqlSchemaGenerator.d.ts +4 -14
  57. package/schema/SqlSchemaGenerator.js +15 -7
  58. package/typings.d.ts +4 -1
  59. package/tsconfig.build.tsbuildinfo +0 -1
@@ -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.em = em;
27
- this.options = options;
28
- this.metadata = em.getMetadata();
29
- this.platform = em.getDriver().getPlatform();
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.subqueryAliasMap.clear();
33
- this.entityMap.clear();
32
+ this.#subqueryAliasMap.clear();
33
+ this.#entityMap.clear();
34
34
  }
35
35
  getOutputEntityMap() {
36
- return this.entityMap;
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.contextStack.push(currentContext);
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.contextStack.pop();
71
+ this.#contextStack.pop();
64
72
  }
65
73
  }
66
74
  transformInsertQuery(node, queryId) {
67
75
  const currentContext = new Map();
68
- this.contextStack.push(currentContext);
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.entityMap.set(meta.tableName, meta);
86
+ this.#entityMap.set(meta.tableName, meta);
79
87
  }
80
88
  }
81
89
  }
82
- const nodeWithHooks = this.options.processOnCreateHooks && entityMeta ? this.processOnCreateHooks(node, entityMeta) : node;
83
- const nodeWithConvertedValues = this.options.convertValues && entityMeta ? this.processInsertValues(nodeWithHooks, entityMeta) : nodeWithHooks;
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.options.processOnUpdateHooks
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.options.convertValues
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.contextStack.pop();
126
+ this.#contextStack.pop();
119
127
  }
120
128
  }
121
129
  transformUpdateQuery(node, queryId) {
122
130
  const currentContext = new Map();
123
- this.contextStack.push(currentContext);
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.entityMap.set(meta.tableName, meta);
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.options.processOnUpdateHooks && entityMeta ? this.processOnUpdateHooks(node, entityMeta) : node;
150
- const nodeWithConvertedValues = this.options.convertValues && entityMeta ? this.processUpdateValues(nodeWithHooks, entityMeta) : nodeWithHooks;
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.contextStack.pop();
162
+ this.#contextStack.pop();
155
163
  }
156
164
  }
157
165
  transformDeleteQuery(node, queryId) {
158
166
  const currentContext = new Map();
159
- this.contextStack.push(currentContext);
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.entityMap.set(meta.tableName, meta);
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.contextStack.pop();
192
+ this.#contextStack.pop();
185
193
  }
186
194
  }
187
195
  transformMergeQuery(node, queryId) {
188
196
  const currentContext = new Map();
189
- this.contextStack.push(currentContext);
197
+ this.#contextStack.push(currentContext);
190
198
  try {
191
199
  return super.transformMergeQuery(node, queryId);
192
200
  }
193
201
  finally {
194
- this.contextStack.pop();
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.options.tableNamingStrategy === 'entity' && parent && SchemableIdentifierNode.is(parent)) {
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.options.columnNamingStrategy === 'property' &&
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.subqueryAliasMap.has(tableName)) {
243
- return this.subqueryAliasMap.get(tableName);
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.contextStack.length > 0) {
271
- const currentContext = this.contextStack[this.contextStack.length - 1];
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.subqueryAliasMap.has(alias)) {
279
- const mappedMeta = this.subqueryAliasMap.get(alias);
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.em);
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.em);
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.options.convertValues;
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.platform, {
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.platform.processDateProperty(value);
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.contextStack.length - 1; i >= 0; i--) {
496
- const context = this.contextStack[i];
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.subqueryAliasMap.set(cteName, sourceMeta);
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.entityMap.set(cteName, sourceMeta);
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.entityMap.set(aliasName, meta);
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.subqueryAliasMap.has(tableName)) {
555
- this.subqueryAliasMap.set(aliasName, this.subqueryAliasMap.get(tableName));
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.subqueryAliasMap.set(aliasName, sourceMeta);
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.entityMap.set(tableName, meta);
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.entityMap.set(aliasName, meta);
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.subqueryAliasMap.has(tableName)) {
611
- this.subqueryAliasMap.set(aliasName, this.subqueryAliasMap.get(tableName));
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.subqueryAliasMap.set(aliasName, sourceMeta);
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.entityMap.set(meta.tableName, meta);
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.metadata.getByClassName(name, false);
715
+ const byEntity = this.#metadata.getByClassName(name, false);
708
716
  if (byEntity) {
709
717
  return byEntity;
710
718
  }
711
- const allMetadata = Array.from(this.metadata);
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.options.columnNamingStrategy !== 'property' && !this.options.convertValues) ||
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.options.columnNamingStrategy === 'property' && prop.name !== fieldName) {
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.options.convertValues) {
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.options.columnNamingStrategy === 'property') {
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.options.convertValues || !prop || value == null) {
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.platform);
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.platform.isNumericProperty(prop)) {
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.platform.getTimezone();
875
+ const tz = this.#platform.getTimezone();
868
876
  if (!tz || tz === 'local') {
869
- return this.platform.parseDate(value);
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.platform.parseDate(value);
883
+ return this.#platform.parseDate(value);
876
884
  }
877
885
  // Append timezone if not present (only for string values)
878
- return this.platform.parseDate(value + tz);
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
@@ -6,6 +6,6 @@ import type { IQueryBuilder, ICriteriaNodeProcessOptions } from '../typings.js';
6
6
  export declare class ArrayCriteriaNode<T extends object> extends CriteriaNode<T> {
7
7
  process(qb: IQueryBuilder<T>, options?: ICriteriaNodeProcessOptions): any;
8
8
  unwrap(): any;
9
- willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): any;
9
+ willAutoJoin(qb: IQueryBuilder<T>, alias?: string, options?: ICriteriaNodeProcessOptions): boolean;
10
10
  isStrict(): boolean;
11
11
  }
@@ -2,6 +2,7 @@ import { GroupOperator, isRaw, JsonType, RawQueryFragment, ReferenceKind, Utils,
2
2
  import { ObjectCriteriaNode } from './ObjectCriteriaNode.js';
3
3
  import { ArrayCriteriaNode } from './ArrayCriteriaNode.js';
4
4
  import { ScalarCriteriaNode } from './ScalarCriteriaNode.js';
5
+ import { EMBEDDABLE_ARRAY_OPS } from './enums.js';
5
6
  /**
6
7
  * @internal
7
8
  */
@@ -69,15 +70,26 @@ export class CriteriaNodeFactory {
69
70
  }, {});
70
71
  return this.createNode(metadata, entityName, map, node, key, validate);
71
72
  }
73
+ // For array embeddeds stored as real columns, route property-level queries
74
+ // as scalar nodes so QueryBuilderHelper generates EXISTS subqueries with
75
+ // JSON array iteration. Keys containing `~` indicate the property lives
76
+ // inside a parent's object-mode JSON column (MetadataDiscovery uses `~` as
77
+ // the glue for object embeds), where JSON path access is used instead.
78
+ if (prop.array && !String(key).includes('~')) {
79
+ const keys = Object.keys(val);
80
+ const hasOnlyArrayOps = keys.every(k => EMBEDDABLE_ARRAY_OPS.includes(k));
81
+ if (!hasOnlyArrayOps) {
82
+ return this.createScalarNode(metadata, entityName, val, node, key, validate);
83
+ }
84
+ }
72
85
  // array operators can be used on embedded properties
73
- const allowedOperators = ['$contains', '$contained', '$overlap'];
74
- const operator = Object.keys(val).some(f => Utils.isOperator(f) && !allowedOperators.includes(f));
86
+ const operator = Object.keys(val).some(f => Utils.isOperator(f) && !EMBEDDABLE_ARRAY_OPS.includes(f));
75
87
  if (operator) {
76
88
  throw ValidationError.cannotUseOperatorsInsideEmbeddables(entityName, prop.name, payload);
77
89
  }
78
90
  const map = Object.keys(val).reduce((oo, k) => {
79
91
  const embeddedProp = prop.embeddedProps[k] ?? Object.values(prop.embeddedProps).find(p => p.name === k);
80
- if (!embeddedProp && !allowedOperators.includes(k)) {
92
+ if (!embeddedProp && !EMBEDDABLE_ARRAY_OPS.includes(k)) {
81
93
  throw ValidationError.invalidEmbeddableQuery(entityName, k, prop.type);
82
94
  }
83
95
  if (embeddedProp) {
@@ -38,14 +38,14 @@ interface Options {
38
38
  onConflict?: OnConflictClause;
39
39
  lockMode?: LockMode;
40
40
  lockTables?: string[];
41
- returning?: (string | RawQueryFragment)[];
41
+ returning?: (string | RawQueryFragment | [name: string, type: unknown])[];
42
42
  comment?: string[];
43
43
  hintComment?: string[];
44
44
  flags?: Set<QueryFlag>;
45
45
  wrap?: [prefix: string, suffix: string];
46
46
  ctes?: CteClause[];
47
47
  }
48
- interface TableOptions {
48
+ export interface TableOptions {
49
49
  schema?: string;
50
50
  indexHint?: string;
51
51
  alias?: string;
@@ -108,7 +108,7 @@ export declare class NativeQueryBuilder implements Subquery {
108
108
  distinct(): this;
109
109
  distinctOn(fields: string[]): this;
110
110
  onConflict(options: OnConflictClause): OnConflictClause;
111
- returning(fields: (string | RawQueryFragment)[]): this;
111
+ returning(fields: (string | RawQueryFragment | [name: string, type: unknown])[]): this;
112
112
  lockMode(lockMode: LockMode, lockTables?: string[]): this;
113
113
  comment(comment: string | string[]): this;
114
114
  hintComment(comment: string | string[]): this;
@@ -30,7 +30,8 @@ export class NativeQueryBuilder {
30
30
  tableName = tableName.toRaw();
31
31
  }
32
32
  if (typeof tableName === 'string') {
33
- const alias = options?.alias ? ` as ${this.platform.quoteIdentifier(options.alias)}` : '';
33
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
34
+ const alias = options?.alias ? `${asKeyword}${this.platform.quoteIdentifier(options.alias)}` : '';
34
35
  const schema = options?.schema && options.schema !== this.platform.getDefaultSchemaName() ? `${options.schema}.` : '';
35
36
  tableName = this.quote(schema + tableName) + alias;
36
37
  }
@@ -474,7 +475,8 @@ export class NativeQueryBuilder {
474
475
  const parts = id.split(/ as /i);
475
476
  const a = this.platform.quoteIdentifier(parts[0]);
476
477
  const b = this.platform.quoteIdentifier(parts[1]);
477
- return `${a} as ${b}`;
478
+ const asKeyword = this.platform.usesAsKeyword() ? ' as ' : ' ';
479
+ return `${a}${asKeyword}${b}`;
478
480
  }
479
481
  if (id === '*') {
480
482
  return id;
@@ -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, ['_schema']);
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._fields?.slice();
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._fields = prev;
297
+ qb.state.fields = prev;
298
298
  }
299
299
  }
300
300
  if (options?.type !== 'orderBy') {
@@ -303,6 +303,6 @@ export class ObjectCriteriaNode extends CriteriaNode {
303
303
  return nestedAlias;
304
304
  }
305
305
  isPrefixed(field) {
306
- return !!field.match(/\w+\./);
306
+ return !!/\w+\./.exec(field);
307
307
  }
308
308
  }