Skip to content

Commit cbd096d

Browse files
committed
Fix bug where MANY(...) on left join didn't work with nulls.
This was caused by the type generator incorrectly putting BlueprintNoKeyAttribute on child types due to them having no sub column maps.
1 parent 2b4b14d commit cbd096d

File tree

5 files changed

+57
-6
lines changed

5 files changed

+57
-6
lines changed

src/Rezoom.SQL.Mapping/DataReader.fs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ let columnMap (reader : IDataReader) =
3636
type DataReaderRow(reader : IDataReader) =
3737
inherit Row()
3838
override __.IsNull(i) = reader.IsDBNull(int i)
39-
override __.GetObject(i) = reader.GetValue(int i)
39+
override __.GetObject(i) =
40+
let o = reader.GetValue(int i)
41+
if obj.ReferenceEquals(o, DBNull.Value) then null else o
4042
override __.GetString(i) = reader.GetString(int i)
4143
override __.GetByte(i) = reader.GetByte(int i)
4244
override __.GetInt16(i) = reader.GetInt16(int i)

src/Rezoom.SQL.Mapping/Row.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Row() =
2424

2525
type ObjectRow([<ParamArray>] row : obj array) =
2626
inherit Row()
27-
override __.IsNull(i) = isNull (row.[int i])
27+
override __.IsNull(i) = isNull (row.[int i]) || obj.ReferenceEquals(DBNull.Value, row.[int i])
2828
override __.GetObject(i) = row.[int i]
2929
override __.GetString(i) = row.[int i] |> Unchecked.unbox
3030
override __.GetByte(i) = row.[int i] |> Unchecked.unbox

src/Rezoom.SQL.Provider/TypeGeneration.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ let private addScalarInterface (ty : ProvidedTypeDefinition) (field : ProvidedFi
102102
ty.DefineMethodOverride(getterMethod, getScalarValue)
103103
ty.AddMember(getterMethod)
104104

105-
let rec private generateRowTypeFromColumns (model : UserModel) name (columnMap : CompileTimeColumnMap) =
105+
let rec private generateRowTypeFromColumns isRoot (model : UserModel) name (columnMap : CompileTimeColumnMap) =
106106
let ty =
107107
ProvidedTypeDefinition
108108
( name
@@ -111,7 +111,7 @@ let rec private generateRowTypeFromColumns (model : UserModel) name (columnMap :
111111
, HideObjectMethods = true
112112
)
113113
ty.AddCustomAttribute(SerializableAttributeData())
114-
if not columnMap.HasSubMaps then
114+
if isRoot && not columnMap.HasSubMaps then
115115
ty.AddCustomAttribute(BlueprintNoKeyAttributeData())
116116
let fields = ResizeArray()
117117
let addField pk (name : string) (fieldTy : Type) =
@@ -139,7 +139,7 @@ let rec private generateRowTypeFromColumns (model : UserModel) name (columnMap :
139139
let info = column.Expr.Info
140140
addField info.PrimaryKey name <| info.Type.CLRType(useOptional = (model.Config.Optionals = Config.FsStyle))
141141
for KeyValue(name, subMap) in columnMap.SubMaps do
142-
let subTy = generateRowTypeFromColumns model (toRowTypeName name) subMap
142+
let subTy = generateRowTypeFromColumns false model (toRowTypeName name) subMap
143143
ty.AddMember(subTy)
144144
addField false name subTy
145145
let ctorParams = [ for camel, field in fields -> ProvidedParameter(camel, field.FieldType) ]
@@ -159,7 +159,7 @@ let rec private generateRowTypeFromColumns (model : UserModel) name (columnMap :
159159

160160
let private generateRowType (model : UserModel) (name : string) (query : ColumnType QueryExprInfo) =
161161
CompileTimeColumnMap.Parse(query.Columns)
162-
|> generateRowTypeFromColumns model name
162+
|> generateRowTypeFromColumns true model name
163163

164164
let private maskOfTables (model : UserModel) (tables : QualifiedObjectName seq) =
165165
let mutable mask = BitMask.Zero

src/Rezoom.SQL.Test/TestCompositeReaders.fs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ open FsUnit
44
open Rezoom.SQL.Mapping
55
open Rezoom.SQL.Mapping.CodeGeneration
66
open System
7+
open System.Globalization
78
open System.ComponentModel.DataAnnotations
89

910
type User =
@@ -18,6 +19,20 @@ type Folder =
1819
Children : Folder array
1920
}
2021

22+
type ManyWithStuffChild =
23+
{
24+
[<BlueprintKey>]
25+
ChildId : int
26+
ChildTime : DateTime
27+
}
28+
type ManyWithStuff =
29+
{
30+
[<BlueprintKey>]
31+
ThingId : int
32+
ThingTime : DateTime
33+
Children : ManyWithStuffChild array
34+
}
35+
2136
type Person =
2237
{
2338
PersonId : int
@@ -145,6 +160,26 @@ let ``read folder 2 levels deep`` () =
145160
Assert.AreEqual(0, folder.Children.[0].Children.Length)
146161
Assert.AreEqual(0, folder.Children.[1].Children.Length)
147162

163+
[<Test>]
164+
let ``read manywithstuff children null`` () =
165+
let colMap =
166+
[|
167+
"ThingId", ColumnType.Int32
168+
"ThingTime", ColumnType.String
169+
"Children.ChildId", ColumnType.Int32
170+
"Children.ChildTime", ColumnType.String
171+
|] |> ColumnMap.Parse
172+
let reader = ReaderTemplate<ManyWithStuff array>.Template().CreateReader()
173+
reader.ProcessColumns(colMap)
174+
reader.Read(ObjectRow(1, DateTime.MinValue.ToString(CultureInfo.InvariantCulture), DBNull.Value, DBNull.Value))
175+
reader.Read(ObjectRow(2, DateTime.MaxValue.ToString(CultureInfo.InvariantCulture), DBNull.Value, DBNull.Value))
176+
reader.Read(ObjectRow(3, DateTime.MaxValue.ToString(CultureInfo.InvariantCulture), DBNull.Value, DBNull.Value))
177+
let things = reader.ToEntity()
178+
Assert.IsNotNull(things)
179+
Assert.AreEqual(3, things.Length)
180+
for thing in things do
181+
Assert.AreEqual(0, thing.Children.Length)
182+
148183
[<Test>]
149184
let ``read person 1 level deep`` () =
150185
let colMap =

src/TypeProviderUsers/TypeProviderUser.SQLite/TestSelects.fs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@ let ``test optional guid parameter`` () =
9494
let results = TestOptionalGuidParameter.Command(Some (Guid.NewGuid())) |> runOnTestData
9595
printfn "%A" results
9696

97+
type TestEmptyMany = SQL<"""
98+
select p.*, many Children(c.*)
99+
from Users p
100+
left join Users c on false
101+
""">
102+
103+
[<Test>]
104+
let ``test empty many`` () =
105+
let results = TestEmptyMany.Command() |> runOnTestData
106+
Assert.AreEqual(2, results.Count)
107+
for result in results do
108+
Assert.AreEqual(0, result.Children.Count)
109+
printfn "%A" results
110+
97111
[<Test>]
98112
let ``replay works`` () =
99113
let plan =

0 commit comments

Comments
 (0)