@mikro-orm/sql 7.2.2-dev.8 → 7.3.0-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.
@@ -419,7 +419,11 @@ export class PostgreSqlSchemaHelper extends SchemaHelper {
419
419
  if ((col.udt_name === 'geometry' || col.udt_name === 'geography') && col.format_type) {
420
420
  type = col.format_type;
421
421
  }
422
- if (type === 'vector' && col.length == null && col.custom_length != null && col.custom_length !== -1) {
422
+ // pgvector stores the dimension in atttypmod for all three of its vector types
423
+ if (['vector', 'halfvec', 'sparsevec'].includes(type) &&
424
+ col.length == null &&
425
+ col.custom_length != null &&
426
+ col.custom_length !== -1) {
423
427
  col.length = col.custom_length;
424
428
  }
425
429
  if (col.length != null && !type.endsWith(`(${col.length})`) && !['text', 'date'].includes(type)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/sql",
3
- "version": "7.2.2-dev.8",
3
+ "version": "7.3.0-dev.0",
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.1"
54
54
  },
55
55
  "peerDependencies": {
56
- "@mikro-orm/core": "7.2.2-dev.8"
56
+ "@mikro-orm/core": "7.3.0-dev.0"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">= 22.17.0"
@@ -127,7 +127,7 @@ export declare class NativeQueryBuilder implements Subquery {
127
127
  as(alias: string): this;
128
128
  toRaw(): RawQueryFragment;
129
129
  protected compileSelect(): void;
130
- /** Whether this COUNT query needs a subquery wrapper for multi-column distinct. */
130
+ /** Whether this COUNT query needs a subquery wrapper: multi-column distinct, or `distinct on` (keeps the null group). */
131
131
  protected needsCountSubquery(): boolean;
132
132
  protected getFields(countSubquery?: boolean): string;
133
133
  protected compileInsert(): void;
@@ -330,12 +330,12 @@ export class NativeQueryBuilder {
330
330
  this.parts.push(`)${asKeyword}${this.quote('dcnt')}`);
331
331
  }
332
332
  }
333
- /** Whether this COUNT query needs a subquery wrapper for multi-column distinct. */
333
+ /** Whether this COUNT query needs a subquery wrapper: multi-column distinct, or `distinct on` (keeps the null group). */
334
334
  needsCountSubquery() {
335
335
  return (this.type === QueryType.COUNT &&
336
336
  !!this.options.distinct &&
337
- this.options.select.length > 1 &&
338
- !this.platform.supportsMultiColumnCountDistinct());
337
+ ((this.options.select.length > 1 && !this.platform.supportsMultiColumnCountDistinct()) ||
338
+ !!this.options.distinctOn));
339
339
  }
340
340
  getFields(countSubquery) {
341
341
  if (!this.options.select || this.options.select.length === 0) {
@@ -257,7 +257,11 @@ export class QueryBuilder {
257
257
  if (field) {
258
258
  this.#state.fields = Utils.asArray(field);
259
259
  }
260
- else if (distinct || this.hasToManyJoins()) {
260
+ else if (this.#state.distinctOn) {
261
+ this.#state.fields = this.#state.distinctOn;
262
+ distinct = true;
263
+ }
264
+ else if (distinct || this.#state.flags.has(QueryFlag.DISTINCT) || this.hasToManyJoins()) {
261
265
  this.#state.fields = this.mainAlias.meta.primaryKeys;
262
266
  }
263
267
  else {
@@ -1566,7 +1570,7 @@ export class QueryBuilder {
1566
1570
  return;
1567
1571
  }
1568
1572
  if (prop?.embedded || (prop?.kind === ReferenceKind.EMBEDDED && prop.object)) {
1569
- const name = prop.embeddedPath?.join('.') ?? prop.fieldNames[0];
1573
+ const name = prop.formula ? prop.name : (prop.embeddedPath?.join('.') ?? prop.fieldNames[0]);
1570
1574
  const aliased = this.#state.aliases[a] ? `${a}.${name}` : name;
1571
1575
  ret.push(getFieldName(aliased, customAlias));
1572
1576
  return;
@@ -1581,11 +1585,11 @@ export class QueryBuilder {
1581
1585
  }
1582
1586
  const nest = (prop) => {
1583
1587
  for (const childProp of Object.values(prop.embeddedProps)) {
1584
- if (childProp.persist === false) {
1588
+ if (childProp.persist === false && !childProp.formula) {
1585
1589
  continue;
1586
1590
  }
1587
1591
  if (childProp.fieldNames && (childProp.kind !== ReferenceKind.EMBEDDED || childProp.object)) {
1588
- const name = childProp.fieldNames[0];
1592
+ const name = childProp.formula ? childProp.name : childProp.fieldNames[0];
1589
1593
  ret.push(getFieldName(this.#state.aliases[a] ? `${a}.${name}` : name));
1590
1594
  }
1591
1595
  else {
@@ -1666,7 +1670,8 @@ export class QueryBuilder {
1666
1670
  return `${currentAlias}.${embeddedProp.fieldNames[0]}`;
1667
1671
  }
1668
1672
  }
1669
- return `${currentAlias}.${embeddedProp?.fieldNames?.[0] ?? propName}`;
1673
+ const name = embeddedProp?.formula ? embeddedProp.name : (embeddedProp?.fieldNames?.[0] ?? propName);
1674
+ return `${currentAlias}.${name}`;
1670
1675
  }
1671
1676
  // Handle relations - auto-join if not the last part
1672
1677
  if (prop.kind === ReferenceKind.MANY_TO_ONE ||
@@ -1759,6 +1764,9 @@ export class QueryBuilder {
1759
1764
  case QueryType.COUNT: {
1760
1765
  const fields = this.#state.fields.map(f => this.helper.mapper(f, this.type, undefined, undefined, schema));
1761
1766
  qb.count(fields, this.#state.flags.has(QueryFlag.DISTINCT));
1767
+ if (this.#state.distinctOn && this.#state.flags.has(QueryFlag.DISTINCT)) {
1768
+ qb.distinctOn(this.prepareFields(this.#state.distinctOn, 'where', schema));
1769
+ }
1762
1770
  this.helper.processJoins(qb, this.#state.joins, joinSchema, schemaOverride);
1763
1771
  break;
1764
1772
  }
@@ -2322,7 +2330,10 @@ export class QueryBuilder {
2322
2330
  const fieldName = this.helper.mapper(field, this.type, undefined, null);
2323
2331
  // virtual fields (e.g. `qb.as(...)`) have no column to reference inside `min()`; inline their
2324
2332
  // expression instead, as a select alias is not resolvable there on some dialects (e.g. PostgreSQL)
2325
- const virtual = !prop?.persist && !prop?.formula && !prop?.hasConvertToJSValueSQL && !pks.includes(fieldName)
2333
+ const virtual = (!prop || prop.persist === false) &&
2334
+ !prop?.formula &&
2335
+ !prop?.hasConvertToJSValueSQL &&
2336
+ !pks.includes(fieldName)
2326
2337
  ? this.resolveVirtualField(f, fieldName)
2327
2338
  : undefined;
2328
2339
  const expr = virtual ? virtual.expr : this.platform.quoteIdentifier(fieldName);
@@ -112,7 +112,7 @@ export class QueryBuilderHelper {
112
112
  const aliasPrefix = isTableNameAliasRequired ? resolvedAlias + '.' : '';
113
113
  const fkIdx2 = prop?.fieldNames.findIndex(name => name === f) ?? -1;
114
114
  const fkIdx = fkIdx2 === -1 ? 0 : fkIdx2;
115
- if (a === prop?.embedded?.[0]) {
115
+ if (a === prop?.embedded?.[0] && !prop.formula) {
116
116
  return aliasPrefix + prop.fieldNames[fkIdx];
117
117
  }
118
118
  const noPrefix = prop?.persist === false;
@@ -120,12 +120,13 @@ export class QueryBuilderHelper {
120
120
  return raw(this.prefix(field, isTableNameAliasRequired));
121
121
  }
122
122
  if (prop?.formula) {
123
- const alias2 = this.#platform.quoteIdentifier(a).toString();
123
+ const formulaAlias = isTableAlias ? a : this.#alias;
124
+ const alias2 = this.#platform.quoteIdentifier(formulaAlias).toString();
124
125
  const aliasName = alias === undefined ? prop.fieldNames[0] : alias;
125
126
  const as = aliasName === null ? '' : ` as ${this.#platform.quoteIdentifier(aliasName)}`;
126
127
  const meta = this.#aliasMap[a]?.meta ?? this.#metadata.get(this.#entityName);
127
128
  const table = this.createFormulaTable(alias2, meta, schema);
128
- const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, a), alias2);
129
+ const columns = meta.createColumnMappingObject(p => this.getTPTAliasForProperty(p.name, formulaAlias), alias2);
129
130
  let value = this.#driver.evaluateFormula(prop.formula, columns, table);
130
131
  if (!this.isTableNameAliasRequired(type)) {
131
132
  value = value.replaceAll(alias2 + '.', '');