Postgres. "Update from source" returns error "Query can't be translated to UPDATE Statement." in some cases
See original GitHub issueAs far as Postgres is concerned, there is some strange case when two queries that are logically exactly the same and processed successfully by Postgres with the same results but in one of the cases, the part of Linq2Db called “LinqToDB.SqlProvider.BasicSqlOptimizer.GetAlternativeUpdatePostgreSqlite” (which I assume by its name somehow specialized in working with Postgres) fails with the error:
LinqToDB.LinqToDBException : Query can't be translated to UPDATE Statement.
at LinqToDB.SqlProvider.BasicSqlOptimizer.GetAlternativeUpdatePostgreSqlite(SqlUpdateStatement statement)
at LinqToDB.DataProvider.PostgreSQL.PostgreSQLSqlOptimizer.TransformStatement(SqlStatement statement)
at LinqToDB.SqlProvider.BasicSqlOptimizer.FinalizeStatement(SqlStatement statement, EvaluationContext context)
at LinqToDB.SqlProvider.BasicSqlOptimizer.Finalize(SqlStatement statement)
at LinqToDB.DataProvider.PostgreSQL.PostgreSQLSqlOptimizer.Finalize(SqlStatement statement)
at LinqToDB.Linq.QueryRunner.FinalizeQuery(Query query)
at LinqToDB.Linq.QueryRunner.SetNonQueryQuery(Query query)
at LinqToDB.Linq.Builder.UpdateBuilder.UpdateContext.BuildQuery[T](Query`1 query, ParameterExpression queryParameter)
at LinqToDB.Linq.Builder.ExpressionBuilder.Build[T]()
at LinqToDB.Linq.Query`1.CreateQuery(ExpressionTreeOptimizationContext optimizationContext, ParametersContext parametersContext, IDataContext dataContext, Expression expr)
at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr, Boolean& dependsOnParameters)
at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache, Boolean& dependsOnParameters)
at LinqToDB.Linq.ExpressionQuery`1.LinqToDB.Async.IQueryProviderAsync.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at LinqToDB.LinqExtensions.UpdateAsync[TSource,TTarget](IQueryable`1 source, ITable`1 target, Expression`1 setter, CancellationToken token)
I prepared the reproducing example (both successful and failed): Database definition:
create table if not exists totals
(
id serial
constraint totals_pk
primary key,
sum integer not null,
label varchar(10) not null
);
create table if not exists entry
(
int serial,
total_id integer not null,
sum integer not null
);
C# tests:
[Test]
public async Task Test_Successful()
{
await
dataConnection.Totals.InnerJoin(
dataConnection.Entries
.GroupBy(e => e.TotalId, (totalId, en) => new { TotalId = totalId, SumAggr = en.Sum(i => i.Sum) }),
(t, eg) => t.Id == eg.TotalId, (t, eg) => new { OldSum = t.Sum, eg.SumAggr, t.Label }
)
.Where(r => r.Label == "spendings")
.UpdateAsync(dataConnection.Totals, g =>
new Total
{
Sum = g.OldSum + g.SumAggr
});
var lastQuery = dataConnection.LastQuery;
TestContext.WriteLine(lastQuery);
}
[Test]
public async Task Test_Failed()
{
await
dataConnection.Entries
.GroupBy(e => e.TotalId, (totalId, en) => new { TotalId = totalId, SumAggr = en.Sum(i => i.Sum) })
.InnerJoin(dataConnection.Totals, (eg, t) =>
t.Id == eg.TotalId, (eg, t) => new { OldSum = t.Sum, eg.SumAggr, t.Label })
.Where(r => r.Label == "spendings")
.UpdateAsync(dataConnection.Totals, g =>
new Total
{
Sum = g.OldSum + g.SumAggr
});
});
}
As you can see above the only thing that changed in Test_Failed is the order of inner join participants. Both queries are run successfully in Postgres. Below I have provided them as well:
Query 1:
UPDATE
"public".totals
SET
"sum" = r."sum" + eg."SumAggr"
FROM
"public".totals r
INNER JOIN (
SELECT
t1.total_id as "TotalId",
Sum(t1."sum") as "SumAggr"
FROM
"public".entry t1
GROUP BY
t1.total_id
) eg ON r.id = eg."TotalId"
WHERE
r.label = 'spendings' AND "public".totals.id = r.id
Query 2:
UPDATE
"public".totals
SET
"sum" = r."sum" + eg."SumAggr"
FROM
(
SELECT
t1.total_id as "TotalId",
Sum(t1."sum") as "SumAggr"
FROM
"public".entry t1
GROUP BY
t1.total_id
) eg
INNER JOIN "public".totals r ON r.id = eg."TotalId"
WHERE
r.label = 'spendings' AND "public".totals.id = r.id
Environment details
linq2db version: 3.7 Database Server: Postgresql 12 Database Provider: Npgsql
Issue Analytics
- State:
- Created a year ago
- Comments:6 (4 by maintainers)
Your version will simplify things a lot, I know. Will check maybe it do not needs too much efforts.
Well, translating UPDATE FROM for PostgreSQL is a kind of magic. Table to update is placed by the Select Query in JOIN operator and we are trying to find a way how to move it to the to the top of the query. In simple words - make it effective and with desired result. Will check, probably I can fix such case.