linkgress-orm 0.2.6 → 0.2.7
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/entity/db-context.d.ts +5 -0
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +58 -23
- package/dist/entity/db-context.js.map +1 -1
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +67 -38
- package/dist/query/query-builder.js.map +1 -1
- package/package.json +1 -1
|
@@ -1847,6 +1847,9 @@ class SelectQueryBuilder {
|
|
|
1847
1847
|
if (!queryBuilder.whereCond) {
|
|
1848
1848
|
throw new Error('Update requires a WHERE condition. Use where() before update().');
|
|
1849
1849
|
}
|
|
1850
|
+
// Detect navigation property joins from WHERE condition
|
|
1851
|
+
const whereJoins = [];
|
|
1852
|
+
queryBuilder.detectAndAddJoinsFromCondition(queryBuilder.whereCond, whereJoins);
|
|
1850
1853
|
// Build SET clause
|
|
1851
1854
|
const setClauses = [];
|
|
1852
1855
|
const values = [];
|
|
@@ -1870,13 +1873,40 @@ class SelectQueryBuilder {
|
|
|
1870
1873
|
const { sql: whereSql, params: whereParams } = condBuilder.build(queryBuilder.whereCond, paramIndex);
|
|
1871
1874
|
values.push(...whereParams);
|
|
1872
1875
|
const qualifiedTableName = queryBuilder.getQualifiedTableName(queryBuilder.schema.name, queryBuilder.schema.schema);
|
|
1876
|
+
// Build FROM clause for navigation properties (PostgreSQL syntax for UPDATE with JOINs)
|
|
1877
|
+
let fromClause = '';
|
|
1878
|
+
let joinConditions = [];
|
|
1879
|
+
for (const join of whereJoins) {
|
|
1880
|
+
const sourceTable = join.sourceAlias || queryBuilder.schema.name;
|
|
1881
|
+
const joinTableName = queryBuilder.getQualifiedTableName(join.targetTable, join.targetSchema);
|
|
1882
|
+
if (fromClause) {
|
|
1883
|
+
fromClause += `, ${joinTableName} AS "${join.alias}"`;
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
fromClause = `FROM ${joinTableName} AS "${join.alias}"`;
|
|
1887
|
+
}
|
|
1888
|
+
// Build ON conditions as part of WHERE clause
|
|
1889
|
+
for (let i = 0; i < join.foreignKeys.length; i++) {
|
|
1890
|
+
const fk = join.foreignKeys[i];
|
|
1891
|
+
const match = join.matches[i];
|
|
1892
|
+
joinConditions.push(`"${sourceTable}"."${fk}" = "${join.alias}"."${match}"`);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
// Combine join conditions with the original WHERE clause
|
|
1896
|
+
const fullWhereClause = joinConditions.length > 0
|
|
1897
|
+
? `${joinConditions.join(' AND ')} AND ${whereSql}`
|
|
1898
|
+
: whereSql;
|
|
1873
1899
|
// Check if RETURNING uses navigation properties
|
|
1874
1900
|
const navigationInfo = returning && returning !== 'count' && returning !== true
|
|
1875
1901
|
? queryBuilder.detectNavigationInReturning(returning)
|
|
1876
1902
|
: null;
|
|
1877
1903
|
if (navigationInfo) {
|
|
1878
1904
|
// Use CTE-based approach for navigation properties in RETURNING
|
|
1879
|
-
|
|
1905
|
+
let updateSql = `UPDATE ${qualifiedTableName} SET ${setClauses.join(', ')}`;
|
|
1906
|
+
if (fromClause) {
|
|
1907
|
+
updateSql += ` ${fromClause}`;
|
|
1908
|
+
}
|
|
1909
|
+
updateSql += ` WHERE ${fullWhereClause}`;
|
|
1880
1910
|
const { sql, params } = queryBuilder.buildReturningWithNavigation(updateSql, values, returning, navigationInfo);
|
|
1881
1911
|
const result = queryBuilder.executor
|
|
1882
1912
|
? await queryBuilder.executor.query(sql, params)
|
|
@@ -1884,10 +1914,16 @@ class SelectQueryBuilder {
|
|
|
1884
1914
|
return queryBuilder.mapReturningResultsWithNavigation(result.rows, returning, navigationInfo.navigationFields);
|
|
1885
1915
|
}
|
|
1886
1916
|
// Standard RETURNING (no navigation properties)
|
|
1917
|
+
// Qualify columns with table name when using FROM clause to avoid ambiguity
|
|
1918
|
+
const hasJoins = whereJoins.length > 0;
|
|
1887
1919
|
const returningClause = returning !== 'count'
|
|
1888
|
-
? queryBuilder.buildUpdateDeleteReturningClause(returning)
|
|
1920
|
+
? queryBuilder.buildUpdateDeleteReturningClause(returning, hasJoins)
|
|
1889
1921
|
: undefined;
|
|
1890
|
-
let sql = `UPDATE ${qualifiedTableName} SET ${setClauses.join(', ')}
|
|
1922
|
+
let sql = `UPDATE ${qualifiedTableName} SET ${setClauses.join(', ')}`;
|
|
1923
|
+
if (fromClause) {
|
|
1924
|
+
sql += ` ${fromClause}`;
|
|
1925
|
+
}
|
|
1926
|
+
sql += ` WHERE ${fullWhereClause}`;
|
|
1891
1927
|
if (returningClause) {
|
|
1892
1928
|
sql += ` RETURNING ${returningClause.sql}`;
|
|
1893
1929
|
}
|
|
@@ -2060,6 +2096,21 @@ class SelectQueryBuilder {
|
|
|
2060
2096
|
const mockRow = this._createMockRow();
|
|
2061
2097
|
const selectedMock = this.selector(mockRow);
|
|
2062
2098
|
const selection = returning(selectedMock);
|
|
2099
|
+
// Helper to get FK db column name from a schema
|
|
2100
|
+
const getFkDbColumnName = (sourceSchema, fkPropName) => {
|
|
2101
|
+
const colEntry = Object.entries(sourceSchema.columns).find(([propName, _]) => propName === fkPropName);
|
|
2102
|
+
if (colEntry) {
|
|
2103
|
+
const config = colEntry[1].build();
|
|
2104
|
+
return config.name;
|
|
2105
|
+
}
|
|
2106
|
+
return fkPropName; // Fallback to property name
|
|
2107
|
+
};
|
|
2108
|
+
// Build a map of alias -> source table name for FK lookups
|
|
2109
|
+
const aliasToSourceTable = new Map();
|
|
2110
|
+
aliasToSourceTable.set(this.schema.name, this.schema.name);
|
|
2111
|
+
for (const join of navigationInfo.joins) {
|
|
2112
|
+
aliasToSourceTable.set(join.alias, join.targetTable);
|
|
2113
|
+
}
|
|
2063
2114
|
// For each selected field, determine if it's from main table or navigation
|
|
2064
2115
|
for (const [alias, field] of Object.entries(selection)) {
|
|
2065
2116
|
if (field && typeof field === 'object' && '__dbColumnName' in field) {
|
|
@@ -2076,31 +2127,13 @@ class SelectQueryBuilder {
|
|
|
2076
2127
|
}
|
|
2077
2128
|
}
|
|
2078
2129
|
}
|
|
2079
|
-
//
|
|
2130
|
+
// Include foreign keys needed for joins - only for joins from main table
|
|
2080
2131
|
for (const join of navigationInfo.joins) {
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
const
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
const dbColName = config.name;
|
|
2087
|
-
mainTableColumns.add(dbColName);
|
|
2088
|
-
}
|
|
2089
|
-
else {
|
|
2090
|
-
// FK might be the db column name directly
|
|
2091
|
-
mainTableColumns.add(fk);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
|
-
// Also include foreign keys from intermediate joins (join to another join)
|
|
2096
|
-
for (const join of navigationInfo.joins) {
|
|
2097
|
-
if (join.sourceAlias && join.sourceAlias !== this.schema.name) {
|
|
2098
|
-
// This join comes from another join, we need to include those FK columns too
|
|
2099
|
-
// Find the source join
|
|
2100
|
-
const sourceJoin = navigationInfo.joins.find(j => j.alias === join.sourceAlias);
|
|
2101
|
-
if (sourceJoin) {
|
|
2102
|
-
// We need the source table's FK columns in our RETURNING if the source is the main table
|
|
2103
|
-
// But if source is itself a navigation, we handle it in the outer SELECT
|
|
2132
|
+
// Only add FK to mainTableColumns if the join source is the main table
|
|
2133
|
+
if (join.sourceAlias === this.schema.name || !join.sourceAlias) {
|
|
2134
|
+
for (const fk of join.foreignKeys) {
|
|
2135
|
+
const fkDbCol = getFkDbColumnName(this.schema, fk);
|
|
2136
|
+
mainTableColumns.add(fkDbCol);
|
|
2104
2137
|
}
|
|
2105
2138
|
}
|
|
2106
2139
|
}
|
|
@@ -2111,27 +2144,23 @@ class SelectQueryBuilder {
|
|
|
2111
2144
|
// Build the JOINs for the outer SELECT
|
|
2112
2145
|
const joinClauses = [];
|
|
2113
2146
|
for (const join of navigationInfo.joins) {
|
|
2114
|
-
const sourceAlias = join.sourceAlias === this.schema.name ? '__mutation__' : `"${join.sourceAlias}"`;
|
|
2115
2147
|
const qualifiedJoinTable = this.getQualifiedTableName(join.targetTable, join.targetSchema);
|
|
2116
2148
|
// Find the db column names for the foreign keys
|
|
2117
2149
|
const joinConditions = [];
|
|
2118
2150
|
for (let i = 0; i < join.foreignKeys.length; i++) {
|
|
2119
2151
|
const fk = join.foreignKeys[i];
|
|
2120
2152
|
const match = join.matches[i] || 'id';
|
|
2121
|
-
// Convert property name to db column name for FK
|
|
2122
|
-
let fkDbCol = fk;
|
|
2123
|
-
const colEntry = Object.entries(this.schema.columns).find(([propName, _]) => propName === fk);
|
|
2124
|
-
if (colEntry) {
|
|
2125
|
-
const config = colEntry[1].build();
|
|
2126
|
-
fkDbCol = config.name;
|
|
2127
|
-
}
|
|
2128
|
-
// For source that's the mutation CTE
|
|
2129
2153
|
if (join.sourceAlias === this.schema.name || !join.sourceAlias) {
|
|
2154
|
+
// FK is on main table - look up db column name from main schema
|
|
2155
|
+
const fkDbCol = getFkDbColumnName(this.schema, fk);
|
|
2130
2156
|
joinConditions.push(`"__mutation__"."${fkDbCol}" = "${join.alias}"."${match}"`);
|
|
2131
2157
|
}
|
|
2132
2158
|
else {
|
|
2133
|
-
//
|
|
2134
|
-
|
|
2159
|
+
// FK is on an intermediate joined table - look up from its schema
|
|
2160
|
+
const sourceTableName = aliasToSourceTable.get(join.sourceAlias);
|
|
2161
|
+
const sourceSchema = sourceTableName && this.schemaRegistry ? this.schemaRegistry.get(sourceTableName) : undefined;
|
|
2162
|
+
const fkDbCol = sourceSchema ? getFkDbColumnName(sourceSchema, fk) : fk;
|
|
2163
|
+
joinConditions.push(`"${join.sourceAlias}"."${fkDbCol}" = "${join.alias}"."${match}"`);
|
|
2135
2164
|
}
|
|
2136
2165
|
}
|
|
2137
2166
|
const joinType = join.isMandatory ? 'INNER JOIN' : 'LEFT JOIN';
|