linkgress-orm 0.4.55 → 0.4.57

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.
@@ -2491,7 +2491,7 @@ class SelectQueryBuilder {
2491
2491
  // Qualify columns with table name when using USING clause to avoid ambiguity
2492
2492
  const hasJoins = whereJoins.length > 0;
2493
2493
  const returningClause = returning !== 'count'
2494
- ? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins)
2494
+ ? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins, { paramCounter: whereParams.length + 1, params: whereParams })
2495
2495
  : undefined;
2496
2496
  let sql = `DELETE FROM ${qualifiedTableName}`;
2497
2497
  if (usingClause) {
@@ -2511,7 +2511,7 @@ class SelectQueryBuilder {
2511
2511
  if (!returningClause) {
2512
2512
  return undefined;
2513
2513
  }
2514
- return queryBuilder.mapDeleteReturningResults(result.rows, returning);
2514
+ return queryBuilder.mapDeleteReturningResults(result.rows, returning, returningClause.fragmentMappers);
2515
2515
  };
2516
2516
  return {
2517
2517
  then(onfulfilled, onrejected) {
@@ -2650,7 +2650,7 @@ class SelectQueryBuilder {
2650
2650
  // Qualify columns with table name when using FROM clause to avoid ambiguity
2651
2651
  const hasJoins = whereJoins.length > 0;
2652
2652
  const returningClause = returning !== 'count'
2653
- ? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins)
2653
+ ? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins, { paramCounter: values.length + 1, params: values })
2654
2654
  : undefined;
2655
2655
  let sql = `UPDATE ${qualifiedTableName} SET ${setClauses.join(', ')}`;
2656
2656
  if (fromClause) {
@@ -2670,7 +2670,7 @@ class SelectQueryBuilder {
2670
2670
  if (!returningClause) {
2671
2671
  return undefined;
2672
2672
  }
2673
- return queryBuilder.mapDeleteReturningResults(result.rows, returning);
2673
+ return queryBuilder.mapDeleteReturningResults(result.rows, returning, returningClause.fragmentMappers);
2674
2674
  };
2675
2675
  return {
2676
2676
  then(onfulfilled, onrejected) {
@@ -2697,9 +2697,12 @@ class SelectQueryBuilder {
2697
2697
  * Build RETURNING clause for delete/update operations
2698
2698
  * @param returning - The returning configuration
2699
2699
  * @param qualifyWithTable - If true, qualify column names with the main table name (needed for DELETE with USING)
2700
+ * @param paramContext - Statement parameter state (params array + next $n). Required when the
2701
+ * selector contains SqlFragments — their parameters append here, which is
2702
+ * positionally correct because RETURNING is last in the statement text.
2700
2703
  * @internal
2701
2704
  */
2702
- buildUpdateDeleteReturningClause(returning, qualifyWithTable = false) {
2705
+ buildUpdateDeleteReturningClause(returning, qualifyWithTable = false, paramContext) {
2703
2706
  if (returning === undefined) {
2704
2707
  return null;
2705
2708
  }
@@ -2717,22 +2720,39 @@ class SelectQueryBuilder {
2717
2720
  if (typeof selection === 'object' && selection !== null) {
2718
2721
  const columns = [];
2719
2722
  const sqlParts = [];
2723
+ let fragmentMappers;
2720
2724
  for (const [alias, field] of Object.entries(selection)) {
2721
- if (field && typeof field === 'object' && '__dbColumnName' in field) {
2725
+ if (field instanceof conditions_1.SqlFragment) {
2726
+ // Raw fragment under its selector key (the key is the alias — a
2727
+ // fragment-side .as() is ignored here). Params append to the
2728
+ // statement's array; headline use case: PG18 `old."col"` capture.
2729
+ if (!paramContext) {
2730
+ throw new Error(`Returning selector field "${alias}" is a SqlFragment, which this mutation path does not support`);
2731
+ }
2732
+ const fragmentSql = field.buildSql(paramContext);
2733
+ columns.push(alias);
2734
+ sqlParts.push(`${fragmentSql} AS "${alias}"`);
2735
+ fragmentMappers = fragmentMappers ?? new Map();
2736
+ fragmentMappers.set(alias, field.getMapper());
2737
+ }
2738
+ else if (field && typeof field === 'object' && '__dbColumnName' in field) {
2722
2739
  const dbName = field.__dbColumnName;
2723
2740
  columns.push(alias);
2724
2741
  sqlParts.push(`${tablePrefix}"${dbName}" AS "${alias}"`);
2725
2742
  }
2726
2743
  }
2727
- return { sql: sqlParts.join(', '), columns };
2744
+ return { sql: sqlParts.join(', '), columns, fragmentMappers };
2728
2745
  }
2729
2746
  return null;
2730
2747
  }
2731
2748
  /**
2732
2749
  * Map row results for delete/update RETURNING clause
2750
+ * @param fragmentMappers - Per-alias mapWith mappers for SqlFragment selector fields; a
2751
+ * fragment alias bypasses the schema-column mapper scan entirely
2752
+ * (its value is the fragment's, not any column's).
2733
2753
  * @internal
2734
2754
  */
2735
- mapDeleteReturningResults(rows, returning) {
2755
+ mapDeleteReturningResults(rows, returning, fragmentMappers) {
2736
2756
  if (returning === true) {
2737
2757
  // Full entity mapping - apply fromDriver mappers
2738
2758
  return rows.map(row => {
@@ -2750,6 +2770,11 @@ class SelectQueryBuilder {
2750
2770
  return rows.map(row => {
2751
2771
  const mapped = {};
2752
2772
  for (const [key, value] of Object.entries(row)) {
2773
+ if (fragmentMappers?.has(key)) {
2774
+ const fragmentMapper = fragmentMappers.get(key);
2775
+ mapped[key] = fragmentMapper?.fromDriver ? fragmentMapper.fromDriver(value) : value;
2776
+ continue;
2777
+ }
2753
2778
  // Try to find column by alias or name
2754
2779
  const colEntry = Object.entries(this.schema.columns).find(([propName, col]) => {
2755
2780
  const config = col.build();
@@ -2833,6 +2858,13 @@ class SelectQueryBuilder {
2833
2858
  allTableAliases.add(collectionBuilder.sourceTable);
2834
2859
  }
2835
2860
  }
2861
+ else if (field instanceof conditions_1.SqlFragment) {
2862
+ // Raw SQL fragment — rendered verbatim by the plain returning-clause
2863
+ // builder; it never implies navigation. Without this skip it would
2864
+ // fall into the nested-object branch and derail the whole selector
2865
+ // onto the CTE navigation path. (v1: FieldRefs inside returning
2866
+ // fragments are unsupported — reference columns as raw quoted SQL.)
2867
+ }
2836
2868
  else if (!Array.isArray(field)) {
2837
2869
  // Nested plain object - recurse into it
2838
2870
  nestedObjects.set(fieldPath, field);