@mikro-orm/sql 7.2.6-dev.2 → 7.2.6-dev.21

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.
@@ -130,6 +130,10 @@ export class AbstractSqlConnection extends Connection {
130
130
  }
131
131
  catch (rollbackError) {
132
132
  this.logger.warn('query', `Failed to roll back transaction: ${rollbackError.message}`);
133
+ // the inner writes are still part of the outer transaction, so it must not commit them
134
+ if (options.ctx) {
135
+ options.ctx.failedSavepointRollback ??= new Error(`Cannot commit the transaction, rolling back to savepoint "${trx.savepointName}" failed: ${rollbackError.message}`, { cause: rollbackError });
136
+ }
133
137
  }
134
138
  throw error;
135
139
  }
@@ -203,6 +207,9 @@ export class AbstractSqlConnection extends Connection {
203
207
  if (ctx.isRolledBack) {
204
208
  return;
205
209
  }
210
+ if ('failedSavepointRollback' in ctx) {
211
+ throw ctx.failedSavepointRollback;
212
+ }
206
213
  await eventBroadcaster?.dispatchEvent(EventType.beforeTransactionCommit, ctx);
207
214
  if ('savepointName' in ctx) {
208
215
  await ctx.releaseSavepoint(ctx.savepointName).execute();
@@ -825,11 +825,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
825
825
  value = this.platform.cloneEmbeddable(value);
826
826
  for (let i = 0; i < value.length; i++) {
827
827
  const item = value[i];
828
- value[i] = this.mapDataToFieldNames(item, false, prop.embeddedProps, options.convertCustomTypes);
828
+ value[i] = this.mapDataToFieldNames(item, false, this.variantProps(prop, item), options.convertCustomTypes);
829
829
  }
830
830
  }
831
831
  else {
832
- value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
832
+ value = this.mapDataToFieldNames(value, false, this.variantProps(prop, value), options.convertCustomTypes);
833
833
  }
834
834
  }
835
835
  if (typeof value === 'undefined' && this.platform.usesDefaultKeyword()) {
@@ -1095,11 +1095,11 @@ export class AbstractSqlDriver extends DatabaseDriver {
1095
1095
  if (prop.array && value) {
1096
1096
  for (let i = 0; i < value.length; i++) {
1097
1097
  const item = value[i];
1098
- value[i] = this.mapDataToFieldNames(item, false, prop.embeddedProps, options.convertCustomTypes);
1098
+ value[i] = this.mapDataToFieldNames(item, false, this.variantProps(prop, item), options.convertCustomTypes);
1099
1099
  }
1100
1100
  }
1101
1101
  else {
1102
- value = this.mapDataToFieldNames(value, false, prop.embeddedProps, options.convertCustomTypes);
1102
+ value = this.mapDataToFieldNames(value, false, this.variantProps(prop, value), options.convertCustomTypes);
1103
1103
  }
1104
1104
  }
1105
1105
  params.push(value ?? null);
@@ -2115,7 +2115,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
2115
2115
  if (prop.formula) {
2116
2116
  const quotedAlias = this.platform.quoteIdentifier(tableAlias).toString();
2117
2117
  const table = this.createFormulaTable(quotedAlias, meta, schema);
2118
- const columns = meta.createColumnMappingObject(p => qb.helper.getTPTAliasForProperty(p.name, tableAlias), tableAlias);
2118
+ const columns = meta.createColumnMappingObject(p => qb.helper.getTPTAliasForProperty(p.name, tableAlias), quotedAlias);
2119
2119
  return [raw(`${this.evaluateFormula(prop.formula, columns, table)} as ${aliased}`)];
2120
2120
  }
2121
2121
  if (prop.customTypes?.some(type => !!type?.convertToJSValueSQL)) {
@@ -2539,9 +2539,9 @@ export class AbstractSqlDriver extends DatabaseDriver {
2539
2539
  ret.push('*');
2540
2540
  }
2541
2541
  if (ret.length > 0 && !hasExplicitFields && addFormulas) {
2542
- // Create formula column mapping with unquoted aliases - quoting should be handled by the user via `quote` helper
2542
+ // `toString()` must yield the quoted alias like `QueryBuilder.finalize()`, which dedupes formulas by rendered SQL
2543
2543
  const quotedAlias = this.platform.quoteIdentifier(alias);
2544
- const columns = meta.createColumnMappingObject(alias);
2544
+ const columns = meta.createColumnMappingObject(alias, quotedAlias.toString());
2545
2545
  const effectiveSchema = schema ?? (meta.schema !== '*' ? meta.schema : undefined);
2546
2546
  for (const prop of meta.props) {
2547
2547
  if (lazyProps.includes(prop)) {
@@ -330,10 +330,9 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
330
330
  return columnDefs.map(colDef => {
331
331
  const name = unquote(colDef);
332
332
  const result = { name };
333
- // Find this column in the expression and extract modifiers
334
- // Create a pattern that matches the column name (quoted or unquoted) followed by modifiers
333
+ // Match the column (quoted or unquoted) as a whole list item, so `id` never matches inside `author_id`
335
334
  const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
336
- const colPattern = new RegExp(`"?${escapedName}"?\\s*([^,)]*?)(?:,|$)`, 'i');
335
+ const colPattern = new RegExp(`(?:^|,)\\s*"?${escapedName}"?(?=\\s|,|$)\\s*([^,)]*?)(?:,|$)`, 'i');
337
336
  const colMatch = columnsStr.match(colPattern);
338
337
  if (colMatch) {
339
338
  const modifiers = colMatch[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.6-dev.2",
3
+ "version": "7.2.6-dev.21",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "keywords": [
6
6
  "data-mapper",
@@ -47,13 +47,13 @@
47
47
  "copy": "node ../../scripts/copy.mjs"
48
48
  },
49
49
  "dependencies": {
50
- "kysely": "0.29.6"
50
+ "kysely": "0.29.7"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@mikro-orm/core": "^7.2.5"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.2.6-dev.2"
56
+ "@mikro-orm/core": "7.2.6-dev.21"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -924,6 +924,17 @@ export class DatabaseTable {
924
924
  };
925
925
  return [expand(rootProp, 1)];
926
926
  }
927
+ // object embedded property index, the JSON keys are the field names of the embedded properties
928
+ if (rootProp?.embeddable && rootProp.object && rootProp.embeddedProps[parts[1]]) {
929
+ let p = rootProp.embeddedProps[parts[1]];
930
+ let i = 2;
931
+ while (i < parts.length && p.embeddedProps?.[parts[i]]) {
932
+ p = p.embeddedProps[parts[i]];
933
+ i++;
934
+ }
935
+ // a path below the last embedded property targets plain JSON keys, kept as they are
936
+ return [[...p.embeddedPath, ...parts.slice(i)].join('.')];
937
+ }
927
938
  // json index, we need to rename the column only
928
939
  if (rootProp) {
929
940
  return [prop.replace(root, rootProp.fieldNames[0])];
@@ -866,16 +866,10 @@ export class SchemaComparator {
866
866
  if (cols1.length === 0 && cols2.length === 0) {
867
867
  return true;
868
868
  }
869
- // If only one has column options, they don't match
870
- if (cols1.length !== cols2.length) {
871
- return false;
872
- }
873
- // Compare each column's options
874
- // Note: We don't check c1.name !== c2.name because the indexes already have matching columnNames
875
- // and the columns array is derived from those same column names
876
- for (let i = 0; i < cols1.length; i++) {
877
- const c1 = cols1[i];
878
- const c2 = cols2[i];
869
+ // introspection may omit entries for columns with default options, so match them by name
870
+ for (const name of index1.columnNames) {
871
+ const c1 = cols1.find(c => c.name === name) ?? { name };
872
+ const c2 = cols2.find(c => c.name === name) ?? { name };
879
873
  const sort1 = c1.sort?.toUpperCase() ?? 'ASC';
880
874
  const sort2 = c2.sort?.toUpperCase() ?? 'ASC';
881
875
  if (sort1 !== sort2) {