Skip to content
Closed
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,95 +33,67 @@ public UdfDbFunctionFbTests(Fb fixture)
: base(fixture)
{ }

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_CrossApply_Correlated_Select_Anonymous()
{
base.QF_CrossApply_Correlated_Select_Anonymous();
}
=> RunIfLateralIsSupported(base.QF_CrossApply_Correlated_Select_Anonymous);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_OuterApply_Correlated_Select_QF()
{
base.QF_OuterApply_Correlated_Select_QF();
}
=> RunIfLateralIsSupported(base.QF_OuterApply_Correlated_Select_QF);

[NotSupportedOnFirebirdFact]
[Fact]
public override void Udf_with_argument_being_comparison_of_nullable_columns()
{
base.Udf_with_argument_being_comparison_of_nullable_columns();
}
=> RunIfLateralIsSupported(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]
[Fact]
public override void QF_CrossApply_Correlated_Select_Result()
{
base.QF_CrossApply_Correlated_Select_Result();
}
=> RunIfLateralIsSupported(base.QF_CrossApply_Correlated_Select_Result);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Select_Correlated_Subquery_In_Anonymous()
{
base.QF_Select_Correlated_Subquery_In_Anonymous();
}
=> RunIfLateralIsSupported(base.QF_Select_Correlated_Subquery_In_Anonymous);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Correlated_Func_Call_With_Navigation()
{
base.QF_Correlated_Func_Call_With_Navigation();
}
=> RunIfLateralIsSupported(base.QF_Correlated_Func_Call_With_Navigation);

[NotSupportedOnFirebirdFact]
[Fact]
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();
}
=> RunIfLateralIsSupported(base.QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_OuterApply_Correlated_Select_Entity()
{
base.QF_OuterApply_Correlated_Select_Entity();
}
=> RunIfLateralIsSupported(base.QF_OuterApply_Correlated_Select_Entity);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Correlated_Nested_Func_Call()
{
base.QF_Correlated_Nested_Func_Call();
}
=> RunIfLateralIsSupported(base.QF_Correlated_Nested_Func_Call);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_OuterApply_Correlated_Select_Anonymous()
{
base.QF_OuterApply_Correlated_Select_Anonymous();
}
=> RunIfLateralIsSupported(base.QF_OuterApply_Correlated_Select_Anonymous);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF()
{
base.QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF();
}
=> RunIfLateralIsSupported(base.QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_Correlated_Select_In_Anonymous()
{
base.QF_Correlated_Select_In_Anonymous();
}
=> RunIfLateralIsSupported(base.QF_Correlated_Select_In_Anonymous);

[NotSupportedOnFirebirdFact]
[Fact]
public override void QF_CrossApply_Correlated_Select_QF_Type()
{
base.QF_CrossApply_Correlated_Select_QF_Type();
}
=> RunIfLateralIsSupported(base.QF_CrossApply_Correlated_Select_QF_Type);

[NotSupportedOnFirebirdFact]
[Fact]
public override void Udf_with_argument_being_comparison_to_null_parameter()
{
base.Udf_with_argument_being_comparison_to_null_parameter();
}
=> RunIfLateralIsSupported(base.Udf_with_argument_being_comparison_to_null_parameter);

[DoesNotHaveTheDataFact]
public override void QF_CrossJoin_Not_Correlated()
Expand Down Expand Up @@ -217,6 +189,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
}


// A correlated queryable function needs LATERAL, which is Firebird 4 and later.
void RunIfLateralIsSupported(Action test)
{
var fbTestStore = (FbTestStore)Fixture.TestStore;
if (fbTestStore.ServerLessThan4())
return;
test();
}

public class Fb : UdfFixtureBase
{
protected override string StoreName { get; } = nameof(UdfDbFunctionFbTests);
Expand Down Expand Up @@ -349,6 +330,62 @@ having count(""ProductId"") > 1
end
end");

// Used only by the FB4+ tests, and the last name is 39 characters, which FB3 rejects.
if (!((FbTestStore)TestStore).ServerLessThan4())
{
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 ""GetCustomerOrderCountByYearOnlyFrom2000"" (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 @@ -298,6 +298,12 @@ protected override Expression VisitCrossApply(CrossApplyExpression crossApplyExp
.Append(AliasSeparator)
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table.Alias));
}
else if (crossApplyExpression.Table is TableValuedFunctionExpression function)
{
// Same for a table-valued function. The alias goes on the derived table rather than
// on the call, so the rest of the statement keeps referring to it unchanged.
GenerateLateralFunction(function);
}
else
{
Visit(crossApplyExpression.Table);
Expand Down Expand Up @@ -325,6 +331,12 @@ protected override Expression VisitOuterApply(OuterApplyExpression outerApplyExp
.Append(AliasSeparator)
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(table.Alias));
}
else if (outerApplyExpression.Table is TableValuedFunctionExpression function)
{
// Same for a table-valued function. The alias goes on the derived table rather than
// on the call, so the rest of the statement keeps referring to it unchanged.
GenerateLateralFunction(function);
}
else
{
Visit(outerApplyExpression.Table);
Expand All @@ -334,6 +346,23 @@ protected override Expression VisitOuterApply(OuterApplyExpression outerApplyExp
return outerApplyExpression;
}

// Firebird will not take a bare table-valued function after LATERAL either, so it is
// wrapped the same way a table is: (SELECT * FROM "Func"(args)) AS "alias".
void GenerateLateralFunction(TableValuedFunctionExpression function)
{
Sql
.Append("(SELECT * FROM ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(function.Name, function.Schema))
.Append("(");

GenerateList(function.Arguments, e => Visit(e));

Sql
.Append("))")
.Append(AliasSeparator)
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(function.Alias));
}

protected override void GeneratePseudoFromClause()
{
Sql.Append(" FROM RDB$DATABASE");
Expand Down