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

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.
@@ -467,9 +467,14 @@ export class AbstractSqlDriver extends DatabaseDriver {
467
467
  return;
468
468
  }
469
469
  const mapToPk = !hint.dataOnly && !!(ref || prop.mapToPk);
470
- const targetProps = mapToPk
471
- ? meta2.getPrimaryProps()
472
- : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []));
470
+ // all rows of a result share their columns, so resolve the mapped props once per join path
471
+ const joinedTargets = (map.joinedTargets ??= {});
472
+ const { targetProps, virtualProps } = (joinedTargets[path] ??= {
473
+ targetProps: (mapToPk
474
+ ? meta2.getPrimaryProps()
475
+ : meta2.props.filter(prop => this.platform.shouldHaveColumn(prop, hint.children || []))).filter(prop => prop.fieldNames.some(name => typeof root[`${relationAlias}__${name}`] !== 'undefined')),
476
+ virtualProps: meta2.props.filter(prop => !ref && prop.persist === false && prop.fieldNames),
477
+ });
473
478
  // If the primary key value for the relation is null, we know we haven't joined to anything
474
479
  // and therefore we don't return any record (since all values would be null)
475
480
  const hasPK = meta2.getPrimaryProps().every(pk => pk.fieldNames.every(name => {
@@ -490,9 +495,7 @@ export class AbstractSqlDriver extends DatabaseDriver {
490
495
  return;
491
496
  }
492
497
  let relationPojo = {};
493
- meta2.props
494
- .filter(prop => !ref && prop.persist === false && prop.fieldNames)
495
- .forEach(prop => {
498
+ virtualProps.forEach(prop => {
496
499
  /* v8 ignore next */
497
500
  if (prop.fieldNames.length > 1) {
498
501
  // composite keys
@@ -1,4 +1,4 @@
1
- import { EnumType, StringType, TextType } from '@mikro-orm/core';
1
+ import { EnumType, JsonType, StringType, TextType, } from '@mikro-orm/core';
2
2
  import { SchemaHelper } from '../../schema/SchemaHelper.js';
3
3
  export class MySqlSchemaHelper extends SchemaHelper {
4
4
  #cache = {};
@@ -601,7 +601,17 @@ export class MySqlSchemaHelper extends SchemaHelper {
601
601
  return super.normalizeDefaultValue(defaultValue, length, MySqlSchemaHelper.DEFAULT_VALUES);
602
602
  }
603
603
  wrap(val, type) {
604
+ if (typeof val !== 'string' || val.length === 0) {
605
+ return val;
606
+ }
607
+ // text/blob literal defaults come back escaped twice with a charset introducer, e.g. `_utf8mb4\'it\\\'s\'` (JSON is parsed by the comparator)
608
+ const expression = type instanceof JsonType ? null : /^_\w+\\'(.*)\\'$/s.exec(val);
609
+ if (expression) {
610
+ const unescape = (str, escapes = {}) => str.replace(/\\(.)/gs, (_, char) => escapes[char] ?? char);
611
+ const sqlEscapes = { 0: '\0', b: '\b', n: '\n', r: '\r', t: '\t', Z: '\x1a' };
612
+ return this.platform.quoteValue(unescape(unescape(expression[1]), sqlEscapes));
613
+ }
604
614
  const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
605
- return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
615
+ return stringType ? this.platform.quoteValue(val) : val;
606
616
  }
607
617
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.6-dev.21",
3
+ "version": "7.2.6-dev.23",
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",
@@ -53,7 +53,7 @@
53
53
  "@mikro-orm/core": "^7.2.5"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.2.6-dev.21"
56
+ "@mikro-orm/core": "7.2.6-dev.23"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -57,6 +57,8 @@ export class ObjectCriteriaNode extends CriteriaNode {
57
57
  // schema-less entities the schema is resolved dynamically and needs to be carried over
58
58
  const fixedSchema = parentMeta.schema && parentMeta.schema !== '*';
59
59
  const qb2 = qb.clone(true, fixedSchema ? undefined : ['schema']);
60
+ // the payload was already converted when processing the parent query
61
+ qb2.unsetFlag(QueryFlag.CONVERT_CUSTOM_TYPES);
60
62
  const joinAlias = qb2.getNextAlias(this.prop.targetMeta.class);
61
63
  const sub = qb2
62
64
  .from(parentMeta.class)
@@ -1157,8 +1157,9 @@ export class QueryBuilder {
1157
1157
  const hash = (data) => {
1158
1158
  return Utils.getPrimaryKeyHash(meta.primaryKeys.map(pk => data[pk]));
1159
1159
  };
1160
+ const map = {};
1160
1161
  for await (const row of res) {
1161
- const mapped = this.driver.mapResult(row, meta, this.#state.populate, this);
1162
+ const mapped = this.driver.mapResult(row, meta, this.#state.populate, this, map);
1162
1163
  if (!options.mergeResults || joinedProps.length === 0) {
1163
1164
  yield this.mapResult(mapped, options.mapResults);
1164
1165
  continue;
@@ -614,6 +614,8 @@ export class SchemaHelper {
614
614
  }
615
615
  if (useDefault) {
616
616
  // https://dev.mysql.com/doc/refman/9.0/en/data-type-defaults.html
617
+ // `tinytext`, `mediumtext`, `longblob` etc. follow the same rule as `text` and `blob`
618
+ const baseType = column.type.toLowerCase().replace(/^(tiny|medium|long)/, '');
617
619
  const needsExpression = [
618
620
  'blob',
619
621
  'text',
@@ -625,7 +627,7 @@ export class SchemaHelper {
625
627
  'multilinestring',
626
628
  'multipolygon',
627
629
  'geometrycollection',
628
- ].some(type => column.type.toLowerCase().startsWith(type));
630
+ ].some(type => baseType.startsWith(type));
629
631
  const defaultSql = needsExpression && !column.default.startsWith('(') ? `(${column.default})` : column.default;
630
632
  col.push(`default ${defaultSql}`);
631
633
  }