@mikro-orm/sql 7.2.6-dev.40 → 7.2.6-dev.42

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.
@@ -96,7 +96,7 @@ export class AbstractSqlPlatform extends Platform {
96
96
  return this.formatQuery(value.sql, value.params);
97
97
  }
98
98
  if (Utils.isPlainObject(value) || value?.[JsonProperty]) {
99
- return this.escape(JSON.stringify(value));
99
+ return this.escape(this.convertJsonToDatabaseValue(value));
100
100
  }
101
101
  return this.escape(value);
102
102
  }
@@ -16,6 +16,8 @@ export declare class BaseMySqlPlatform extends AbstractSqlPlatform {
16
16
  };
17
17
  /** mysql and mariadb treat null as the lowest value when no placement is requested. */
18
18
  sortsNullsLowest(): boolean;
19
+ /** mysql reads backslashes in string literals as escapes, so defaults need the same escaping as values (minus the needless `\"`). */
20
+ quoteDefaultValue(value: string): string;
19
21
  supportsMultiColumnCountDistinct(): boolean;
20
22
  /** @internal */
21
23
  createNativeQueryBuilder(): MySqlNativeQueryBuilder;
@@ -22,6 +22,10 @@ export class BaseMySqlPlatform extends AbstractSqlPlatform {
22
22
  sortsNullsLowest() {
23
23
  return true;
24
24
  }
25
+ /** mysql reads backslashes in string literals as escapes, so defaults need the same escaping as values (minus the needless `\"`). */
26
+ quoteDefaultValue(value) {
27
+ return this.quoteValue(value).replaceAll('\\"', '"');
28
+ }
25
29
  supportsMultiColumnCountDistinct() {
26
30
  return true;
27
31
  }
@@ -63,7 +63,5 @@ export declare class MySqlSchemaHelper extends SchemaHelper {
63
63
  normalizeDefaultValue(defaultValue: string, length: number): string | number;
64
64
  protected wrap(val: string | null | undefined, type: Type<unknown>): string | null | undefined;
65
65
  /** Decodes the backslash escapes of an introspected string literal. */
66
- protected unescapeDefault(val: string): string;
67
- /** Metadata doubles embedded quotes and leaves `"` alone, so undo those two escapes of `quoteValue` while keeping the backslash form of control characters. */
68
- protected quoteDefault(val: string): string;
66
+ unescapeDefault(val: string): string;
69
67
  }
@@ -607,18 +607,14 @@ export class MySqlSchemaHelper extends SchemaHelper {
607
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
608
  const expression = type instanceof JsonType ? null : /^_\w+\\'(.*)\\'$/s.exec(val);
609
609
  if (expression) {
610
- return this.quoteDefault(this.unescapeDefault(expression[1].replace(/\\(.)/gs, '$1')));
610
+ return this.platform.quoteDefaultValue(this.unescapeDefault(expression[1].replace(/\\(.)/gs, '$1')));
611
611
  }
612
612
  const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
613
- return stringType ? this.quoteDefault(val) : val;
613
+ return stringType ? this.platform.quoteDefaultValue(val) : val;
614
614
  }
615
615
  /** Decodes the backslash escapes of an introspected string literal. */
616
616
  unescapeDefault(val) {
617
617
  const sqlEscapes = { 0: '\0', b: '\b', n: '\n', r: '\r', t: '\t', Z: '\x1a' };
618
618
  return val.replace(/\\(.)/gs, (_, char) => sqlEscapes[char] ?? char);
619
619
  }
620
- /** Metadata doubles embedded quotes and leaves `"` alone, so undo those two escapes of `quoteValue` while keeping the backslash form of control characters. */
621
- quoteDefault(val) {
622
- return this.platform.quoteValue(val).replaceAll("\\'", "''").replaceAll('\\"', '"');
623
- }
624
620
  }
@@ -1226,7 +1226,7 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
1226
1226
  if (!defaultValue || typeof defaultValue !== 'string') {
1227
1227
  return super.normalizeDefaultValue(defaultValue, length, PostgreSqlSchemaHelper.DEFAULT_VALUES);
1228
1228
  }
1229
- const match = /^'(.*)'::(.*)$/.exec(defaultValue);
1229
+ const match = /^'(.*)'::(.*)$/s.exec(defaultValue);
1230
1230
  if (match) {
1231
1231
  if (match[2] === 'integer') {
1232
1232
  return +match[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.6-dev.40",
3
+ "version": "7.2.6-dev.42",
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.40"
56
+ "@mikro-orm/core": "7.2.6-dev.42"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -1569,6 +1569,19 @@ export class QueryBuilder {
1569
1569
  if (prop && [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
1570
1570
  return;
1571
1571
  }
1572
+ // the joined strategy selects by column name, which can collide with the name of another property
1573
+ const shadowed = customAlias &&
1574
+ prop &&
1575
+ (prop.kind === ReferenceKind.EMBEDDED ||
1576
+ (customAlias.endsWith(`__${f}`) && (prop.persist === false || !prop.fieldNames.includes(f)))) &&
1577
+ (this.#state.aliases[a]?.meta ?? this.mainAlias.meta).props.some(p => p !== prop &&
1578
+ p.persist !== false &&
1579
+ (p.kind !== ReferenceKind.EMBEDDED || p.object) &&
1580
+ p.fieldNames?.includes(f));
1581
+ if (shadowed) {
1582
+ ret.push(raw('?? as ??', [`${a}.${f}`, customAlias]));
1583
+ return;
1584
+ }
1572
1585
  if (prop?.persist === false && !prop.embedded && !prop.formula && type === 'where') {
1573
1586
  return;
1574
1587
  }
@@ -1578,11 +1591,7 @@ export class QueryBuilder {
1578
1591
  ret.push(getFieldName(aliased, customAlias));
1579
1592
  return;
1580
1593
  }
1581
- // the joined strategy selects by column name, which can collide with an inline embedded property name
1582
- const shadowed = customAlias &&
1583
- prop?.kind === ReferenceKind.EMBEDDED &&
1584
- (this.#state.aliases[a]?.meta ?? this.mainAlias.meta).props.some(p => (p.kind !== ReferenceKind.EMBEDDED || p.object) && p.fieldNames?.includes(f));
1585
- if (prop?.kind === ReferenceKind.EMBEDDED && !shadowed) {
1594
+ if (prop?.kind === ReferenceKind.EMBEDDED) {
1586
1595
  if (customAlias) {
1587
1596
  throw new Error(`Cannot use 'as ${customAlias}' alias on embedded property '${field}' because it expands to multiple columns. Alias individual fields instead (e.g. '${field}.propertyName as ${customAlias}').`);
1588
1597
  }
@@ -876,9 +876,9 @@ export class DatabaseTable {
876
876
  return +defaultValue;
877
877
  }
878
878
  // unquote string defaults if `raw = false`
879
- const match = /^'(.*)'$/.exec('' + val);
879
+ const match = /^'(.*)'$/s.exec('' + val);
880
880
  if (!raw && match) {
881
- return match[1].replaceAll("''", "'");
881
+ return schemaHelper.unescapeDefault(match[1]);
882
882
  }
883
883
  return '' + val;
884
884
  }
@@ -163,6 +163,8 @@ export declare abstract class SchemaHelper {
163
163
  getNamespaces(connection: AbstractSqlConnection, ctx?: Transaction): Promise<string[]>;
164
164
  protected mapIndexes(indexes: IndexDef[]): Promise<IndexDef[]>;
165
165
  mapForeignKeys(fks: any[], tableName: string, schemaName?: string): Dictionary;
166
+ /** Decodes the content of an introspected string literal, the inverse of `Platform.quoteDefaultValue()`. */
167
+ unescapeDefault(val: string): string;
166
168
  normalizeDefaultValue(defaultValue: string | RawQueryFragment, length?: number, defaultValues?: Dictionary<string[]>): string | number;
167
169
  getCreateDatabaseSQL(name: string): string;
168
170
  getDropDatabaseSQL(name: string): string;
@@ -718,6 +718,10 @@ export class SchemaHelper {
718
718
  return ret;
719
719
  }, {});
720
720
  }
721
+ /** Decodes the content of an introspected string literal, the inverse of `Platform.quoteDefaultValue()`. */
722
+ unescapeDefault(val) {
723
+ return val.replaceAll("''", "'");
724
+ }
721
725
  normalizeDefaultValue(defaultValue, length, defaultValues = {}) {
722
726
  if (defaultValue == null) {
723
727
  return defaultValue;