Skip to content

Commit 869554d

Browse files
azabludaclaude
andcommitted
Un-skip the 14 correlated queryable-function tests, and create what they need
Committed red on purpose, so the fix that follows is measured against a failing suite rather than asserted. All 14 carry [NotSupportedOnFirebirdFact]. The store supports every one of them; only the generated SQL is malformed, which is #1277. A correlated queryable function reaches the store as a LATERAL derived table, and LATERAL is Firebird 4 and later, so both the tests and the objects they need are gated on ServerLessThan4(). The gate is the same early return the Associations and ComplexType tests here already use, behind one helper rather than repeated fourteen times. The fixture guard is required rather than tidy. "GetCustomerOrderCountByYearOnlyFrom2000" is 39 characters and Firebird 3 caps identifiers at 31, so creating it there fails the seed with "Name longer than database column size" and takes every test in the class down with it, including ones that pass today. That second gap was hidden behind the first. The fixture never created "AddValues", "GetCustomerOrderCountByYear" or "GetCustomerOrderCountByYearOnlyFrom2000", and nothing noticed, because every test that uses them was skipped. They are ported from the definitions in EF Core's own UdfDbFunctionSqlServerTests fixture, in Firebird form: the two table-valued ones become selectable procedures, as the two already in this fixture are, and year(...) becomes extract(year from ...). UdfDbFunctionFbTests on Firebird 5.0.3: Passed 82 -> 84, Failed 0 -> 12, Skipped 24 -> 10, Total 106. Twelve fail at the LATERAL source with the store's own parse error: FbException: Dynamic SQL Error Token unknown - line 3, column 14 Two pass with no product change at all, so their skips were already stale: QF_Select_Correlated_Subquery_In_Anonymous QF_Correlated_Func_Call_With_Navigation Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 34acfda commit 869554d

1 file changed

Lines changed: 93 additions & 56 deletions

File tree

src/FirebirdSql.EntityFrameworkCore.Firebird.FunctionalTests/Query/UdfDbFunctionFbTests.cs

Lines changed: 93 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -33,95 +33,67 @@ public UdfDbFunctionFbTests(Fb fixture)
3333
: base(fixture)
3434
{ }
3535

36-
[NotSupportedOnFirebirdFact]
36+
[Fact]
3737
public override void QF_CrossApply_Correlated_Select_Anonymous()
38-
{
39-
base.QF_CrossApply_Correlated_Select_Anonymous();
40-
}
38+
=> RunIfLateralIsSupported(base.QF_CrossApply_Correlated_Select_Anonymous);
4139

42-
[NotSupportedOnFirebirdFact]
40+
[Fact]
4341
public override void QF_OuterApply_Correlated_Select_QF()
44-
{
45-
base.QF_OuterApply_Correlated_Select_QF();
46-
}
42+
=> RunIfLateralIsSupported(base.QF_OuterApply_Correlated_Select_QF);
4743

48-
[NotSupportedOnFirebirdFact]
44+
[Fact]
4945
public override void Udf_with_argument_being_comparison_of_nullable_columns()
50-
{
51-
base.Udf_with_argument_being_comparison_of_nullable_columns();
52-
}
46+
=> RunIfLateralIsSupported(base.Udf_with_argument_being_comparison_of_nullable_columns);
5347

5448
[Fact]
5549
public override void QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollections()
5650
{
5751
base.QF_Select_Correlated_Subquery_In_Anonymous_MultipleCollections();
5852
}
5953

60-
[NotSupportedOnFirebirdFact]
54+
[Fact]
6155
public override void QF_CrossApply_Correlated_Select_Result()
62-
{
63-
base.QF_CrossApply_Correlated_Select_Result();
64-
}
56+
=> RunIfLateralIsSupported(base.QF_CrossApply_Correlated_Select_Result);
6557

66-
[NotSupportedOnFirebirdFact]
58+
[Fact]
6759
public override void QF_Select_Correlated_Subquery_In_Anonymous()
68-
{
69-
base.QF_Select_Correlated_Subquery_In_Anonymous();
70-
}
60+
=> RunIfLateralIsSupported(base.QF_Select_Correlated_Subquery_In_Anonymous);
7161

72-
[NotSupportedOnFirebirdFact]
62+
[Fact]
7363
public override void QF_Correlated_Func_Call_With_Navigation()
74-
{
75-
base.QF_Correlated_Func_Call_With_Navigation();
76-
}
64+
=> RunIfLateralIsSupported(base.QF_Correlated_Func_Call_With_Navigation);
7765

78-
[NotSupportedOnFirebirdFact]
66+
[Fact]
7967
public override void QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous()
80-
{
81-
base.QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous();
82-
}
68+
=> RunIfLateralIsSupported(base.QF_Select_Correlated_Direct_With_Function_Query_Parameter_Correlated_In_Anonymous);
8369

84-
[NotSupportedOnFirebirdFact]
70+
[Fact]
8571
public override void QF_OuterApply_Correlated_Select_Entity()
86-
{
87-
base.QF_OuterApply_Correlated_Select_Entity();
88-
}
72+
=> RunIfLateralIsSupported(base.QF_OuterApply_Correlated_Select_Entity);
8973

90-
[NotSupportedOnFirebirdFact]
74+
[Fact]
9175
public override void QF_Correlated_Nested_Func_Call()
92-
{
93-
base.QF_Correlated_Nested_Func_Call();
94-
}
76+
=> RunIfLateralIsSupported(base.QF_Correlated_Nested_Func_Call);
9577

96-
[NotSupportedOnFirebirdFact]
78+
[Fact]
9779
public override void QF_OuterApply_Correlated_Select_Anonymous()
98-
{
99-
base.QF_OuterApply_Correlated_Select_Anonymous();
100-
}
80+
=> RunIfLateralIsSupported(base.QF_OuterApply_Correlated_Select_Anonymous);
10181

102-
[NotSupportedOnFirebirdFact]
82+
[Fact]
10383
public override void QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF()
104-
{
105-
base.QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF();
106-
}
84+
=> RunIfLateralIsSupported(base.QF_Select_Correlated_Subquery_In_Anonymous_Nested_With_QF);
10785

108-
[NotSupportedOnFirebirdFact]
86+
[Fact]
10987
public override void QF_Correlated_Select_In_Anonymous()
110-
{
111-
base.QF_Correlated_Select_In_Anonymous();
112-
}
88+
=> RunIfLateralIsSupported(base.QF_Correlated_Select_In_Anonymous);
11389

114-
[NotSupportedOnFirebirdFact]
90+
[Fact]
11591
public override void QF_CrossApply_Correlated_Select_QF_Type()
116-
{
117-
base.QF_CrossApply_Correlated_Select_QF_Type();
118-
}
92+
=> RunIfLateralIsSupported(base.QF_CrossApply_Correlated_Select_QF_Type);
11993

120-
[NotSupportedOnFirebirdFact]
94+
[Fact]
12195
public override void Udf_with_argument_being_comparison_to_null_parameter()
122-
{
123-
base.Udf_with_argument_being_comparison_to_null_parameter();
124-
}
96+
=> RunIfLateralIsSupported(base.Udf_with_argument_being_comparison_to_null_parameter);
12597

12698
[DoesNotHaveTheDataFact]
12799
public override void QF_CrossJoin_Not_Correlated()
@@ -217,6 +189,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
217189
}
218190

219191

192+
// A correlated queryable function needs LATERAL, which is Firebird 4 and later.
193+
void RunIfLateralIsSupported(Action test)
194+
{
195+
var fbTestStore = (FbTestStore)Fixture.TestStore;
196+
if (fbTestStore.ServerLessThan4())
197+
return;
198+
test();
199+
}
200+
220201
public class Fb : UdfFixtureBase
221202
{
222203
protected override string StoreName { get; } = nameof(UdfDbFunctionFbTests);
@@ -349,6 +330,62 @@ having count(""ProductId"") > 1
349330
end
350331
end");
351332

333+
// Used only by the FB4+ tests, and the last name is 39 characters, which FB3 rejects.
334+
if (!((FbTestStore)TestStore).ServerLessThan4())
335+
{
336+
await context.Database.ExecuteSqlRawAsync(
337+
@"create function ""AddValues"" (a int, b int)
338+
returns int
339+
as
340+
begin
341+
return :a + :b;
342+
end");
343+
344+
await context.Database.ExecuteSqlRawAsync(
345+
@"create procedure ""GetCustomerOrderCountByYear"" (customerId int)
346+
returns
347+
(
348+
""CustomerId"" int not null,
349+
""Count"" int not null,
350+
""Year"" int not null
351+
)
352+
as
353+
begin
354+
for select :customerId, count(""Id""), extract(year from ""OrderDate"")
355+
from ""Orders""
356+
where ""CustomerId"" = :customerId
357+
group by ""CustomerId"", extract(year from ""OrderDate"")
358+
order by extract(year from ""OrderDate"")
359+
into :""CustomerId"", :""Count"", :""Year"" do
360+
begin
361+
suspend;
362+
end
363+
end");
364+
365+
await context.Database.ExecuteSqlRawAsync(
366+
@"create procedure ""GetCustomerOrderCountByYearOnlyFrom2000"" (customerId int, onlyFrom2000 boolean)
367+
returns
368+
(
369+
""CustomerId"" int not null,
370+
""Count"" int not null,
371+
""Year"" int not null
372+
)
373+
as
374+
begin
375+
for select :customerId, count(""Id""), extract(year from ""OrderDate"")
376+
from ""Orders""
377+
where ""CustomerId"" = 1
378+
and (:onlyFrom2000 = false or :onlyFrom2000 is null
379+
or (:onlyFrom2000 = true and extract(year from ""OrderDate"") = 2000))
380+
group by ""CustomerId"", extract(year from ""OrderDate"")
381+
order by extract(year from ""OrderDate"")
382+
into :""CustomerId"", :""Count"", :""Year"" do
383+
begin
384+
suspend;
385+
end
386+
end");
387+
}
388+
352389
await context.SaveChangesAsync();
353390
}
354391
}

0 commit comments

Comments
 (0)