Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,96 +33,32 @@ public UdfDbFunctionFbTests(Fb fixture)
: base(fixture)
{ }

[NotSupportedOnFirebirdFact]
public override void QF_CrossApply_Correlated_Select_Anonymous()
{
base.QF_CrossApply_Correlated_Select_Anonymous();
}

[NotSupportedOnFirebirdFact]
public override void QF_OuterApply_Correlated_Select_QF()
{
base.QF_OuterApply_Correlated_Select_QF();
}

[NotSupportedOnFirebirdFact]
public override void Udf_with_argument_being_comparison_of_nullable_columns()
{
base.Udf_with_argument_being_comparison_of_nullable_columns();
}

[Fact]
public override void QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollections()
{
base.QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollections();
}

[NotSupportedOnFirebirdFact]
public override void QF_CrossApply_Correlated_Select_Result()
{
base.QF_CrossApply_Correlated_Select_Result();
}

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Select_Correlated_Subquery_In_Anonymous()
{
// that needs LATERAL which is Firebird 4+
var fbTestStore = (FbTestStore)Fixture.TestStore;
if (fbTestStore.ServerLessThan4())
return;
base.QF_Select_Correlated_Subquery_In_Anonymous();
}

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Correlated_Func_Call_With_Navigation()
{
// that needs LATERAL which is Firebird 4+
var fbTestStore = (FbTestStore)Fixture.TestStore;
if (fbTestStore.ServerLessThan4())
return;
base.QF_Correlated_Func_Call_With_Navigation();
}

[NotSupportedOnFirebirdFact]
public override void QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous()
{
base.QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous();
}

[NotSupportedOnFirebirdFact]
public override void QF_OuterApply_Correlated_Select_Entity()
{
base.QF_OuterApply_Correlated_Select_Entity();
}

[NotSupportedOnFirebirdFact]
public override void QF_Correlated_Nested_Func_Call()
{
base.QF_Correlated_Nested_Func_Call();
}

[NotSupportedOnFirebirdFact]
public override void QF_OuterApply_Correlated_Select_Anonymous()
{
base.QF_OuterApply_Correlated_Select_Anonymous();
}

[NotSupportedOnFirebirdFact]
public override void QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF()
{
base.QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF();
}

[NotSupportedOnFirebirdFact]
public override void QF_Correlated_Select_In_Anonymous()
{
base.QF_Correlated_Select_In_Anonymous();
}

[NotSupportedOnFirebirdFact]
public override void QF_CrossApply_Correlated_Select_QF_Type()
{
base.QF_CrossApply_Correlated_Select_QF_Type();
}

[NotSupportedOnFirebirdFact]
public override void Udf_with_argument_being_comparison_to_null_parameter()
{
base.Udf_with_argument_being_comparison_to_null_parameter();
}

[DoesNotHaveTheDataFact]
public override void QF_CrossJoin_Not_Correlated()
{
Expand Down Expand Up @@ -208,6 +144,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.HasName("GetCustWithMostOrdersAfterDate");
modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(GetCustomerWithMostOrdersAfterDateInstance)))
.HasName("GetCustWithMostOrdersAfterDate");
modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(GetCustomerOrderCountByYearOnlyFrom2000)))
.HasName("GetCustOrderCountByYearFrom2000");

modelBuilder.HasDbFunction(typeof(UDFSqlContext).GetMethod(nameof(IdentityString)))
.HasSchema(null);
Expand Down Expand Up @@ -349,6 +287,58 @@ having count(""ProductId"") > 1
end
end");

await context.Database.ExecuteSqlRawAsync(
@"create function ""AddValues"" (a int, b int)
returns int
as
begin
return :a + :b;
end");

await context.Database.ExecuteSqlRawAsync(
@"create procedure ""GetCustomerOrderCountByYear"" (customerId int)
returns
(
""CustomerId"" int not null,
""Count"" int not null,
""Year"" int not null
)
as
begin
for select :customerId, count(""Id""), extract(year from ""OrderDate"")
from ""Orders""
where ""CustomerId"" = :customerId
group by ""CustomerId"", extract(year from ""OrderDate"")
order by extract(year from ""OrderDate"")
into :""CustomerId"", :""Count"", :""Year"" do
begin
suspend;
end
end");

await context.Database.ExecuteSqlRawAsync(
@"create procedure ""GetCustOrderCountByYearFrom2000"" (customerId int, onlyFrom2000 boolean)
returns
(
""CustomerId"" int not null,
""Count"" int not null,
""Year"" int not null
)
as
begin
for select :customerId, count(""Id""), extract(year from ""OrderDate"")
from ""Orders""
where ""CustomerId"" = 1
and (:onlyFrom2000 = false or :onlyFrom2000 is null
or (:onlyFrom2000 = true and extract(year from ""OrderDate"") = 2000))
group by ""CustomerId"", extract(year from ""OrderDate"")
order by extract(year from ""OrderDate"")
into :""CustomerId"", :""Count"", :""Year"" do
begin
suspend;
end
end");

await context.SaveChangesAsync();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,24 +285,8 @@ protected override void GenerateOrderings(SelectExpression selectExpression)
// Copyright (c) 2002-2021, Npgsql
protected override Expression VisitCrossApply(CrossApplyExpression crossApplyExpression)
{
Sql.Append("JOIN LATERAL ");

if (crossApplyExpression.Table is TableExpression table)
{
// Firebird doesn't support LATERAL JOIN over table, and it doesn't really make sense to do it - but EF Core
// will sometimes generate that.
Sql
.Append("(SELECT * FROM ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table.Name, table.Schema))
.Append(")")
.Append(AliasSeparator)
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table.Alias));
}
else
{
Visit(crossApplyExpression.Table);
}

Sql.Append("JOIN ");
GenerateApplySource(crossApplyExpression.Table);
Sql.Append(" ON TRUE");
return crossApplyExpression;
}
Expand All @@ -312,26 +296,38 @@ protected override Expression VisitCrossApply(CrossApplyExpression crossApplyExp
// Copyright (c) 2002-2021, Npgsql
protected override Expression VisitOuterApply(OuterApplyExpression outerApplyExpression)
{
Sql.Append("LEFT JOIN LATERAL ");
Sql.Append("LEFT JOIN ");
GenerateApplySource(outerApplyExpression.Table);
Sql.Append(" ON TRUE");
return outerApplyExpression;
}

if (outerApplyExpression.Table is TableExpression table)
// LATERAL is only allowed in front of a derived table, so anything that is not one has to
// become one - except a selectable procedure, which is implicitly lateral already.
void GenerateApplySource(TableExpressionBase source)
{
if (source is TableValuedFunctionExpression function)
{
// A selectable procedure takes its arguments from streams earlier in the FROM clause
// without LATERAL, so the call is emitted as it is anywhere else.
Visit(function);
}
else if (source is TableExpression table)
{
// Firebird doesn't support LATERAL JOIN over table, and it doesn't really make sense to do it - but EF Core
// will sometimes generate that.
Sql
.Append("(SELECT * FROM ")
.Append("LATERAL (SELECT * FROM ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table.Name, table.Schema))
.Append(")")
.Append(AliasSeparator)
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table.Alias));
}
else
{
Visit(outerApplyExpression.Table);
Sql.Append("LATERAL ");
Visit(source);
}

Sql.Append(" ON TRUE");
return outerApplyExpression;
}

protected override void GeneratePseudoFromClause()
Expand Down