linkgress-orm 0.2.8 → 0.2.9

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.
@@ -2085,6 +2085,20 @@ class SelectQueryBuilder {
2085
2085
  else if (field instanceof CollectionQueryBuilder) {
2086
2086
  // CollectionQueryBuilder (.toList(), .firstOrDefault())
2087
2087
  collectionFields.set(fieldPath, field);
2088
+ // Also extract the navigation path from the collection builder
2089
+ // so we can add the necessary joins to reach the collection's source table
2090
+ const collectionBuilder = field;
2091
+ if (collectionBuilder.navigationPath && Array.isArray(collectionBuilder.navigationPath)) {
2092
+ for (const navJoin of collectionBuilder.navigationPath) {
2093
+ if (navJoin.alias && navJoin.alias !== this.schema.name) {
2094
+ allTableAliases.add(navJoin.alias);
2095
+ }
2096
+ }
2097
+ }
2098
+ // Also add the source table alias
2099
+ if (collectionBuilder.sourceTable && collectionBuilder.sourceTable !== this.schema.name) {
2100
+ allTableAliases.add(collectionBuilder.sourceTable);
2101
+ }
2088
2102
  }
2089
2103
  else if (!Array.isArray(field)) {
2090
2104
  // Nested plain object - recurse into it
@@ -2131,6 +2145,13 @@ class SelectQueryBuilder {
2131
2145
  for (const join of navigationInfo.joins) {
2132
2146
  aliasToSourceTable.set(join.alias, join.targetTable);
2133
2147
  }
2148
+ // Build a map of table name -> alias for collection subquery rewriting
2149
+ // This allows us to rewrite collection subqueries to use the correct joined aliases
2150
+ const tableToAlias = new Map();
2151
+ tableToAlias.set(this.schema.name, '__mutation__'); // Main table uses mutation CTE
2152
+ for (const join of navigationInfo.joins) {
2153
+ tableToAlias.set(join.targetTable, join.alias);
2154
+ }
2134
2155
  // Track collection subqueries for LATERAL JOINs
2135
2156
  const collectionSubqueries = [];
2136
2157
  let lateralCounter = 0;
@@ -2164,7 +2185,7 @@ class SelectQueryBuilder {
2164
2185
  }
2165
2186
  else if (field instanceof CollectionQueryBuilder) {
2166
2187
  // CollectionQueryBuilder (.toList(), .firstOrDefault())
2167
- // Build a correlated subquery that references __mutation__ instead of the source table
2188
+ // Build a correlated subquery that references joined tables from the main query
2168
2189
  const collectionBuilder = field;
2169
2190
  const context = buildCollectionContext();
2170
2191
  // Build the CTE/subquery using lateral strategy
@@ -2175,8 +2196,8 @@ class SelectQueryBuilder {
2175
2196
  // 1. A correlated subquery in selectExpression (no join needed)
2176
2197
  // 2. A LATERAL JOIN with joinClause and selectExpression
2177
2198
  if (cteResult.joinClause && cteResult.joinClause.trim()) {
2178
- // LATERAL JOIN needed - rewrite to reference __mutation__ instead of source table
2179
- const rewrittenJoinClause = this.rewriteCollectionJoinForMutation(cteResult.joinClause, this.schema.name, '__mutation__');
2199
+ // LATERAL JOIN needed - rewrite all table references to use correct aliases
2200
+ const rewrittenJoinClause = this.rewriteCollectionTableReferences(cteResult.joinClause, tableToAlias);
2180
2201
  collectionSubqueries.push({
2181
2202
  fieldPath,
2182
2203
  lateralAlias: cteResult.tableName || `lateral_${lateralCounter - 1}`,
@@ -2186,8 +2207,8 @@ class SelectQueryBuilder {
2186
2207
  selectParts.push(`${cteResult.selectExpression || `"${cteResult.tableName}".data`} AS "${fieldPath}"`);
2187
2208
  }
2188
2209
  else if (cteResult.selectExpression) {
2189
- // Correlated subquery in SELECT - rewrite source table references
2190
- const rewrittenExpr = this.rewriteCollectionExprForMutation(cteResult.selectExpression, this.schema.name, '__mutation__');
2210
+ // Correlated subquery in SELECT - rewrite all table references
2211
+ const rewrittenExpr = this.rewriteCollectionTableReferences(cteResult.selectExpression, tableToAlias);
2191
2212
  selectParts.push(`${rewrittenExpr} AS "${fieldPath}"`);
2192
2213
  }
2193
2214
  }
@@ -2266,20 +2287,24 @@ ${joinClauses.join('\n')}`;
2266
2287
  return { sql, params: allParams, nestedPaths };
2267
2288
  }
2268
2289
  /**
2269
- * Rewrite a LATERAL JOIN clause to reference __mutation__ instead of the source table
2270
- * @internal
2271
- */
2272
- rewriteCollectionJoinForMutation(joinClause, sourceTable, mutationAlias) {
2273
- const sourcePattern = new RegExp(`"${sourceTable}"\\."`, 'g');
2274
- return joinClause.replace(sourcePattern, `"${mutationAlias}"."`);
2275
- }
2276
- /**
2277
- * Rewrite a correlated subquery expression to reference __mutation__ instead of the source table
2290
+ * Rewrite table references in a collection subquery to use the correct aliases
2291
+ * from the main query's JOINs. This handles multi-level navigation where the
2292
+ * collection is accessed through intermediate joined tables.
2293
+ *
2294
+ * @param expression - The SQL expression (join clause or select expression) to rewrite
2295
+ * @param tableToAlias - Map of table names to their aliases in the main query
2296
+ * @returns The rewritten expression with all table references updated
2278
2297
  * @internal
2279
2298
  */
2280
- rewriteCollectionExprForMutation(expression, sourceTable, mutationAlias) {
2281
- const sourcePattern = new RegExp(`"${sourceTable}"\\."`, 'g');
2282
- return expression.replace(sourcePattern, `"${mutationAlias}"."`);
2299
+ rewriteCollectionTableReferences(expression, tableToAlias) {
2300
+ let result = expression;
2301
+ // Rewrite each table reference to use the correct alias
2302
+ // Pattern: "tableName"."columnName" -> "alias"."columnName"
2303
+ for (const [tableName, alias] of tableToAlias) {
2304
+ const pattern = new RegExp(`"${tableName}"\\."`, 'g');
2305
+ result = result.replace(pattern, `"${alias}"."`);
2306
+ }
2307
+ return result;
2283
2308
  }
2284
2309
  /**
2285
2310
  * Map RETURNING results with navigation properties