@zenstackhq/orm 3.4.4 → 3.4.5

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.
package/dist/index.d.cts CHANGED
@@ -604,8 +604,11 @@ type ClientOptions<Schema extends SchemaDef> = QueryOptions<Schema> & {
604
604
  */
605
605
  validateInput?: boolean;
606
606
  /**
607
- * Whether to use compact alias names (e.g., "$t1", "$t2") when transforming ORM queries to SQL.
607
+ * Whether to use compact alias names (e.g., "$$t1", "$$t2") when transforming ORM queries to SQL.
608
608
  * Defaults to `true`.
609
+ *
610
+ * When set to `false`, original aliases are kept unless temporary aliases become too long for
611
+ * safe SQL identifier handling, in which case compact aliases are used as a fallback.
609
612
  */
610
613
  useCompactAliasNames?: boolean;
611
614
  /**
@@ -740,6 +743,12 @@ declare abstract class BaseCrudDialect<Schema extends SchemaDef> {
740
743
  not(...args: Expression<SqlBool>[]): ExpressionWrapper<any, any, SqlBool>;
741
744
  fieldRef(model: string, field: string, modelAlias?: string, inlineComputedField?: boolean): any;
742
745
  protected canJoinWithoutNestedSelect(modelDef: ModelDef, payload: boolean | FindArgs<Schema, GetModels<Schema>, any, true>): boolean;
746
+ /**
747
+ * Builds an EXISTS expression from an inner SELECT query.
748
+ * Can be overridden by dialects that need special handling (e.g., MySQL wraps
749
+ * in a derived table to avoid "can't specify target table for update in FROM clause").
750
+ */
751
+ protected buildExistsExpression(innerQuery: SelectQueryBuilder<any, any, any>): Expression<SqlBool>;
743
752
  abstract get provider(): DataSourceProviderType;
744
753
  /**
745
754
  * Builds selection for a relation field.
package/dist/index.d.ts CHANGED
@@ -604,8 +604,11 @@ type ClientOptions<Schema extends SchemaDef> = QueryOptions<Schema> & {
604
604
  */
605
605
  validateInput?: boolean;
606
606
  /**
607
- * Whether to use compact alias names (e.g., "$t1", "$t2") when transforming ORM queries to SQL.
607
+ * Whether to use compact alias names (e.g., "$$t1", "$$t2") when transforming ORM queries to SQL.
608
608
  * Defaults to `true`.
609
+ *
610
+ * When set to `false`, original aliases are kept unless temporary aliases become too long for
611
+ * safe SQL identifier handling, in which case compact aliases are used as a fallback.
609
612
  */
610
613
  useCompactAliasNames?: boolean;
611
614
  /**
@@ -740,6 +743,12 @@ declare abstract class BaseCrudDialect<Schema extends SchemaDef> {
740
743
  not(...args: Expression<SqlBool>[]): ExpressionWrapper<any, any, SqlBool>;
741
744
  fieldRef(model: string, field: string, modelAlias?: string, inlineComputedField?: boolean): any;
742
745
  protected canJoinWithoutNestedSelect(modelDef: ModelDef, payload: boolean | FindArgs<Schema, GetModels<Schema>, any, true>): boolean;
746
+ /**
747
+ * Builds an EXISTS expression from an inner SELECT query.
748
+ * Can be overridden by dialects that need special handling (e.g., MySQL wraps
749
+ * in a derived table to avoid "can't specify target table for update in FROM clause").
750
+ */
751
+ protected buildExistsExpression(innerQuery: SelectQueryBuilder<any, any, any>): Expression<SqlBool>;
743
752
  abstract get provider(): DataSourceProviderType;
744
753
  /**
745
754
  * Builds selection for a relation field.
package/dist/index.js CHANGED
@@ -931,21 +931,22 @@ var BaseCrudDialect = class {
931
931
  if (!subPayload) {
932
932
  continue;
933
933
  }
934
- const countSelect = /* @__PURE__ */ __name((negate) => {
934
+ const existsSelect = /* @__PURE__ */ __name((negate) => {
935
935
  const filter = this.buildFilter(relationModel, relationFilterSelectAlias, subPayload);
936
- return this.eb.selectFrom(this.buildSelectModel(relationModel, relationFilterSelectAlias).select(() => this.eb.fn.count(this.eb.lit(1)).as("$count")).where(buildPkFkWhereRefs(this.eb)).where(() => negate ? this.eb.not(filter) : filter).as("$sub")).select("$count");
937
- }, "countSelect");
936
+ const innerQuery = this.buildSelectModel(relationModel, relationFilterSelectAlias).select(this.eb.lit(1).as("_")).where(buildPkFkWhereRefs(this.eb)).where(() => negate ? this.eb.not(filter) : filter);
937
+ return this.buildExistsExpression(innerQuery);
938
+ }, "existsSelect");
938
939
  switch (key) {
939
940
  case "some": {
940
- result = this.and(result, this.eb(countSelect(false), ">", 0));
941
+ result = this.and(result, existsSelect(false));
941
942
  break;
942
943
  }
943
944
  case "every": {
944
- result = this.and(result, this.eb(countSelect(true), "=", 0));
945
+ result = this.and(result, this.eb.not(existsSelect(true)));
945
946
  break;
946
947
  }
947
948
  case "none": {
948
- result = this.and(result, this.eb(countSelect(false), "=", 0));
949
+ result = this.and(result, this.eb.not(existsSelect(false)));
949
950
  break;
950
951
  }
951
952
  }
@@ -1562,6 +1563,15 @@ var BaseCrudDialect = class {
1562
1563
  }
1563
1564
  return true;
1564
1565
  }
1566
+ // #endregion
1567
+ /**
1568
+ * Builds an EXISTS expression from an inner SELECT query.
1569
+ * Can be overridden by dialects that need special handling (e.g., MySQL wraps
1570
+ * in a derived table to avoid "can't specify target table for update in FROM clause").
1571
+ */
1572
+ buildExistsExpression(innerQuery) {
1573
+ return this.eb.exists(innerQuery);
1574
+ }
1565
1575
  };
1566
1576
 
1567
1577
  // src/client/crud/dialects/lateral-join-dialect-base.ts
@@ -1784,6 +1794,9 @@ var MySqlCrudDialect = class extends LateralJoinDialectBase {
1784
1794
  }
1785
1795
  // #endregion
1786
1796
  // #region other overrides
1797
+ buildExistsExpression(innerQuery) {
1798
+ return this.eb.exists(this.eb.selectFrom(innerQuery.as("$exists_sub")).select(this.eb.lit(1).as("_")));
1799
+ }
1787
1800
  buildArrayAgg(arg) {
1788
1801
  return this.eb.fn.coalesce(sql2`JSON_ARRAYAGG(${arg})`, sql2`JSON_ARRAY()`);
1789
1802
  }
@@ -7439,20 +7452,45 @@ var TempAliasTransformer = class extends OperationNodeTransformer2 {
7439
7452
  __name(this, "TempAliasTransformer");
7440
7453
  }
7441
7454
  aliasMap = /* @__PURE__ */ new Map();
7455
+ textEncoder = new TextEncoder();
7456
+ mode;
7457
+ maxIdentifierLength;
7458
+ constructor(options = {}) {
7459
+ super();
7460
+ this.mode = options.mode ?? "alwaysCompact";
7461
+ const maxIdentifierLength = options.maxIdentifierLength ?? 63;
7462
+ if (!Number.isFinite(maxIdentifierLength) || !Number.isInteger(maxIdentifierLength) || maxIdentifierLength <= 0) {
7463
+ throw new RangeError("maxIdentifierLength must be a positive integer");
7464
+ }
7465
+ this.maxIdentifierLength = maxIdentifierLength;
7466
+ }
7442
7467
  run(node) {
7443
7468
  this.aliasMap.clear();
7444
7469
  return this.transformNode(node);
7445
7470
  }
7446
7471
  transformIdentifier(node, queryId) {
7447
- if (node.name.startsWith(TEMP_ALIAS_PREFIX)) {
7472
+ if (!node.name.startsWith(TEMP_ALIAS_PREFIX)) {
7473
+ return super.transformIdentifier(node, queryId);
7474
+ }
7475
+ let shouldCompact = false;
7476
+ if (this.mode === "alwaysCompact") {
7477
+ shouldCompact = true;
7478
+ } else {
7479
+ const aliasByteLength = this.textEncoder.encode(node.name).length;
7480
+ if (aliasByteLength > this.maxIdentifierLength) {
7481
+ shouldCompact = true;
7482
+ }
7483
+ }
7484
+ if (shouldCompact) {
7448
7485
  let mapped = this.aliasMap.get(node.name);
7449
7486
  if (!mapped) {
7450
7487
  mapped = `$$t${this.aliasMap.size + 1}`;
7451
7488
  this.aliasMap.set(node.name, mapped);
7452
7489
  }
7453
7490
  return IdentifierNode2.create(mapped);
7491
+ } else {
7492
+ return super.transformIdentifier(node, queryId);
7454
7493
  }
7455
- return super.transformIdentifier(node, queryId);
7456
7494
  }
7457
7495
  };
7458
7496
 
@@ -7842,10 +7880,9 @@ In such cases, ZenStack cannot reliably determine the IDs of the mutated entitie
7842
7880
  return this.nameMapper?.transformNode(query) ?? query;
7843
7881
  }
7844
7882
  processTempAlias(query) {
7845
- if (this.options.useCompactAliasNames === false) {
7846
- return query;
7847
- }
7848
- return new TempAliasTransformer().run(query);
7883
+ return new TempAliasTransformer({
7884
+ mode: this.options.useCompactAliasNames === false ? "compactLongNames" : "alwaysCompact"
7885
+ }).run(query);
7849
7886
  }
7850
7887
  createClientForConnection(connection, inTx) {
7851
7888
  const innerExecutor = this.withConnectionProvider(new SingleConnectionProvider(connection));