Skip to content

Commit 807f02a

Browse files
committed
DateTimeOffset function translations, better disposing update commands, avoid double-cast, no reflection on SQLProvider.SQLite if pre-defined library
1 parent 9fb63e6 commit 807f02a

24 files changed

Lines changed: 393 additions & 155 deletions

docs/content/core/sqlite.fsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
(*** hide ***)
2-
#I @"../../../bin/lib/netstandard2.0"
2+
// Dynamic:
3+
//#I @"../../../bin/lib/netstandard2.0"
4+
//#r "FSharp.Data.SqlProvider.dll"
5+
//#r "FSharp.Data.SqlProvider.Common.dll"
6+
7+
(*** hide ***)
8+
// SQLite only:
9+
#I @"../../../bin/sqlite/lib/netstandard2.0"
310
#r "FSharp.Data.SqlProvider.Common.dll"
4-
#r "FSharp.Data.SqlProvider.dll"
11+
#r "FSharp.Data.SqlProvider.SQLite.dll"
512
open System
613
open FSharp.Data.Sql
14+
open FSharp.Data.Sql.SQLite
15+
716
(**
817
918
# SQLite Provider
@@ -99,6 +108,12 @@ When you do insert operation, after .SubmitUpdates call you can get inserted row
99108
let myCustomer = ctx.Main.Customers.``Create(CompanyName)``("MyCompany")
100109
ctx.SubmitUpdates()
101110
let rowid = myCustomer.GetColumn("rowid") : int
111+
// ...or just use myCustomer.CustomerId
102112
```
103113
114+
Also note: SQLite is not very strong on types.
115+
Use float if you try to do any math, don't use decimal.
116+
Decimal is on intention of SQL-drivers handled as text to be non-lossy.
117+
104118
*)
119+

src/SQLProvider.Common/SqlRuntime.Patterns.fs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,8 @@ let rec (|SqlColumnGet|_|) (ex:Expression) =
520520
| "IndexOf", [String search; SqlColumnGet(al2,col2,typ2) as pe] when integerTypes |> Seq.exists(fun t -> t = pe.Type) -> Some(alias, CanonicalOperation(CanonicalOp.IndexOfStart(SqlConstant search, SqlCol(al2,col2)), col), intType typ)
521521
| "IndexOf", [SqlColumnGet(al2,col2,_); SqlColumnGet(al3,col3,typ2) as pe] when integerTypes |> Seq.exists(fun t -> Type.(=)(pe.Type, t)) -> Some(alias, CanonicalOperation(CanonicalOp.IndexOfStart(SqlCol(al2,col2), SqlCol(al3,col3)), col), intType typ)
522522
| _ -> None
523-
| t when Type.(=)(t, typeof<System.DateTime>) || Type.(=)(t, typeof<Option<System.DateTime>>) || Type.(=)(t, typeof<ValueOption<System.DateTime>>) -> // DateTime functions
523+
| t when Type.(=)(t, typeof<System.DateTime>) || Type.(=)(t, typeof<Option<System.DateTime>>) || Type.(=)(t, typeof<ValueOption<System.DateTime>>) || // DateTime functions
524+
Type.(=)(t, typeof<System.DateTimeOffset>) || Type.(=)(t, typeof<Option<System.DateTimeOffset>>) || Type.(=)(t, typeof<ValueOption<System.DateTimeOffset>>) ->
524525
match meth.Name, par with
525526
| "AddYears", [Int x] -> Some(alias, CanonicalOperation(CanonicalOp.AddYears(SqlConstant(box x)), col), typ)
526527
| "AddYears", [SqlColumnGet(al2,col2,typ2) as pe] when integerTypes |> Seq.exists(fun t -> Type.(=)(pe.Type, t)) -> Some(alias, CanonicalOperation(CanonicalOp.AddYears(SqlCol(al2,col2)), col), typ)
@@ -544,7 +545,9 @@ let rec (|SqlColumnGet|_|) (ex:Expression) =
544545
match propInfo.Name with
545546
| "Length" -> Some(alias, CanonicalOperation(CanonicalOp.Length, col), intType typ)
546547
| _ -> None
547-
| t when Type.(=)(t, typeof<System.DateTime>) || Type.(=)(t, typeof<Option<System.DateTime>>) || Type.(=)(t, typeof<ValueOption<System.DateTime>>) -> // DateTime functions
548+
| t when Type.(=)(t, typeof<System.DateTime>) || Type.(=)(t, typeof<Option<System.DateTime>>) || Type.(=)(t, typeof<ValueOption<System.DateTime>>) ||
549+
Type.(=)(t, typeof<System.DateTimeOffset>) || Type.(=)(t, typeof<Option<System.DateTimeOffset>>) || Type.(=)(t, typeof<ValueOption<System.DateTimeOffset>>)
550+
-> // DateTime functions
548551
match propInfo.Name with
549552
| "Date" -> Some(alias, CanonicalOperation(CanonicalOp.Date, col), typ)
550553
| "Year" -> Some(alias, CanonicalOperation(CanonicalOp.Year, col), intType typ)
@@ -557,12 +560,16 @@ let rec (|SqlColumnGet|_|) (ex:Expression) =
557560
| _ -> None
558561
| _, OptionalFSharpOptionValue(PropertyGet(Some(MethodCall(Some(OptionalFSharpOptionValue(SqlColumnGet(alias, col, typ)) as p1), meth, [par])), propInfo))
559562
when (meth.Name = "Subtract" && (Type.(=)(meth.ReturnType, typeof<System.TimeSpan>) || Type.(=)(meth.ReturnType, typeof<Option<System.TimeSpan>>) || Type.(=)(meth.ReturnType, typeof<ValueOption<System.TimeSpan>>)) &&
560-
(Type.(=)(p1.Type, typeof<System.DateTime>) || Type.(=)(p1.Type, typeof<Option<System.DateTime>>) || Type.(=)(p1.Type, typeof<ValueOption<System.DateTime>>))) ->
563+
(Type.(=)(p1.Type, typeof<System.DateTime>) || Type.(=)(p1.Type, typeof<Option<System.DateTime>>) || Type.(=)(p1.Type, typeof<ValueOption<System.DateTime>>) ||
564+
Type.(=)(p1.Type, typeof<System.DateTimeOffset>) || Type.(=)(p1.Type, typeof<Option<System.DateTimeOffset>>) || Type.(=)(p1.Type, typeof<ValueOption<System.DateTimeOffset>>)
565+
)) ->
561566
match propInfo.Name, par with
562-
| "Days", (SqlColumnGet(al2,col2,typ2) as pe) when (Type.(=)(pe.Type, typeof<System.DateTime>) || Type.(=)(pe.Type, typeof<Option<System.DateTime>>) || Type.(=)(pe.Type, typeof<ValueOption<System.DateTime>>)) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffDays(SqlCol(al2,col2)), col), typ)
563-
| "Seconds", (SqlColumnGet(al2,col2,typ2) as pe) when (Type.(=)(pe.Type, typeof<System.DateTime>) || Type.(=)(pe.Type, typeof<Option<System.DateTime>>) || Type.(=)(pe.Type, typeof<ValueOption<System.DateTime>>)) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffSecs(SqlCol(al2,col2)), col), typ)
564-
| "Days", Constant(c,t) when Type.(=)(t, typeof<System.DateTime>) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffDays(SqlConstant(box c)), col), typ)
565-
| "Seconds", Constant(c,t) when Type.(=)(t, typeof<System.DateTime>) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffSecs(SqlConstant(box c)), col), typ)
567+
| "Days", (SqlColumnGet(al2,col2,typ2) as pe) when (Type.(=)(pe.Type, typeof<System.DateTime>) || Type.(=)(pe.Type, typeof<Option<System.DateTime>>) || Type.(=)(pe.Type, typeof<ValueOption<System.DateTime>>))
568+
|| (Type.(=)(pe.Type, typeof<System.DateTimeOffset>) || Type.(=)(pe.Type, typeof<Option<System.DateTimeOffset>>) || Type.(=)(pe.Type, typeof<ValueOption<System.DateTimeOffset>>)) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffDays(SqlCol(al2,col2)), col), typ)
569+
| "Seconds", (SqlColumnGet(al2,col2,typ2) as pe) when (Type.(=)(pe.Type, typeof<System.DateTime>) || Type.(=)(pe.Type, typeof<Option<System.DateTime>>) || Type.(=)(pe.Type, typeof<ValueOption<System.DateTime>>))
570+
|| (Type.(=)(pe.Type, typeof<System.DateTimeOffset>) || Type.(=)(pe.Type, typeof<Option<System.DateTimeOffset>>) || Type.(=)(pe.Type, typeof<ValueOption<System.DateTimeOffset>>)) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffSecs(SqlCol(al2,col2)), col), typ)
571+
| "Days", Constant(c,t) when Type.(=)(t, typeof<System.DateTime>) || Type.(=)(t, typeof<System.DateTimeOffset>) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffDays(SqlConstant(box c)), col), typ)
572+
| "Seconds", Constant(c,t) when Type.(=)(t, typeof<System.DateTime>) || Type.(=)(t, typeof<System.DateTimeOffset>) -> Some(alias, CanonicalOperation(CanonicalOp.DateDiffSecs(SqlConstant(box c)), col), typ)
566573
| _ -> None
567574

568575
// Numerical functions
@@ -622,7 +629,10 @@ let rec (|SqlColumnGet|_|) (ex:Expression) =
622629
| _ -> failwith ("Shouldn't hit " + op.ToString())
623630

624631
if Type.(=)(be.Left.Type, typeof<System.DateTime>) || Type.(=)(be.Right.Type, typeof<System.DateTime>) || Type.(=)(be.Left.Type, typeof<Option<System.DateTime>>) || Type.(=)(be.Right.Type, typeof<Option<System.DateTime>>)
625-
|| Type.(=)(be.Left.Type, typeof<ValueOption<System.DateTime>>) || Type.(=)(be.Right.Type, typeof<ValueOption<System.DateTime>>) then
632+
|| Type.(=)(be.Left.Type, typeof<ValueOption<System.DateTime>>) || Type.(=)(be.Right.Type, typeof<ValueOption<System.DateTime>>) ||
633+
Type.(=)(be.Left.Type, typeof<System.DateTimeOffset>) || Type.(=)(be.Right.Type, typeof<System.DateTimeOffset>) || Type.(=)(be.Left.Type, typeof<Option<System.DateTimeOffset>>) || Type.(=)(be.Right.Type, typeof<Option<System.DateTimeOffset>>)
634+
|| Type.(=)(be.Left.Type, typeof<ValueOption<System.DateTimeOffset>>) || Type.(=)(be.Right.Type, typeof<ValueOption<System.DateTimeOffset>>)
635+
then
626636
// DateTime math operations are not supported directly as they return .NET TimeSpan which is not the clear translation of SQL.
627637
// You can use functions like .AddHours(), .AddDays(), .Subtract().Days and comparison.
628638
None
@@ -705,7 +715,8 @@ let rec (|SqlColumnGet|_|) (ex:Expression) =
705715
| _ -> None
706716

707717
| ExpressionType.Call, (:? MethodCallExpression as e) when e.Method.Name = "Parse" && e.Arguments.Count = 1 &&
708-
(Type.(=)(e.Type, typeof<System.DateTime>) || Type.(=)(e.Type, typeof<Option<System.DateTime>>) || Type.(=)(e.Type, typeof<ValueOption<System.DateTime>>)) ->
718+
(Type.(=)(e.Type, typeof<System.DateTime>) || Type.(=)(e.Type, typeof<Option<System.DateTime>>) || Type.(=)(e.Type, typeof<ValueOption<System.DateTime>>)
719+
|| Type.(=)(e.Type, typeof<System.DateTimeOffset>) || Type.(=)(e.Type, typeof<Option<System.DateTimeOffset>>) || Type.(=)(e.Type, typeof<ValueOption<System.DateTimeOffset>>)) ->
709720
// Don't do any magic, just: DateTime.Parse('2000-01-01') -> '2000-01-01'
710721
match e.Arguments.[0] with
711722
| SqlColumnGet(alias, col, typ) when Type.(=)(typ, typeof<String>) || Type.(=)(typ, typeof<Option<String>>) || Type.(=)(typ, typeof<ValueOption<String>>)

src/SQLProvider.Common/Utils.fs

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ module Utilities =
107107
| :? DateTimeOffset as t -> Option.Some t |> box
108108
| :? TimeSpan as t -> Option.Some t |> box
109109
| :? bigint as t -> Option.Some t |> box
110+
| :? Guid as t -> Option.Some t |> box
110111
| t ->
111112
if isCOpt (t.GetType()) then t |> box
112113
else Option.Some t |> box
@@ -132,6 +133,7 @@ module Utilities =
132133
| :? DateTimeOffset as t -> ValueOption.Some t |> box
133134
| :? TimeSpan as t -> ValueOption.Some t |> box
134135
| :? bigint as t -> ValueOption.Some t |> box
136+
| :? Guid as t -> ValueOption.Some t |> box
135137
| t ->
136138
if isVOpt (t.GetType()) then t|> box
137139
else ValueOption.Some t |> box
@@ -146,26 +148,7 @@ module Utilities =
146148
| :? string as s -> true, s
147149
| _ -> false, ""
148150

149-
match ok, returnType with
150-
| true, t when Type.(=) (t, typeof<String>) -> s |> box
151-
| true, t when Type.(=) (t, typeof<Int32>) && Int32.TryParse s |> fst -> Int32.Parse s |> box
152-
| true, t when Type.(=) (t, typeof<Decimal>) && Decimal.TryParse s |> fst -> Decimal.Parse s |> box
153-
| true, t when Type.(=) (t, typeof<Int64>) && Int64.TryParse s |> fst -> Int64.Parse s |> box
154-
| true, t when Type.(=) (t, typeof<Single>) && Single.TryParse s |> fst -> Single.Parse s |> box
155-
| true, t when Type.(=) (t, typeof<UInt32>) && UInt32.TryParse s |> fst -> UInt32.Parse s |> box
156-
| true, t when Type.(=) (t, typeof<Double>) && Double.TryParse s |> fst -> Double.Parse s |> box
157-
| true, t when Type.(=) (t, typeof<UInt64>) && UInt64.TryParse s |> fst -> UInt64.Parse s |> box
158-
| true, t when Type.(=) (t, typeof<Int16>) && Int16.TryParse s |> fst -> Int16.Parse s |> box
159-
| true, t when Type.(=) (t, typeof<UInt16>) && UInt16.TryParse s |> fst -> UInt16.Parse s |> box
160-
| true, t when Type.(=) (t, typeof<DateTime>) && DateTime.TryParse s |> fst -> DateTime.Parse s |> box
161-
| true, t when Type.(=) (t, typeof<Boolean>) && Boolean.TryParse s |> fst -> Boolean.Parse s |> box
162-
| true, t when Type.(=) (t, typeof<Byte>) && Byte.TryParse s |> fst -> Byte.Parse s |> box
163-
| true, t when Type.(=) (t, typeof<SByte>) && SByte.TryParse s |> fst -> SByte.Parse s |> box
164-
| true, t when Type.(=) (t, typeof<Char>) && Char.TryParse s |> fst -> Char.Parse s |> box
165-
| true, t when Type.(=) (t, typeof<DateTimeOffset>) && DateTimeOffset.TryParse s |> fst -> DateTimeOffset.Parse s |> box
166-
| true, t when Type.(=) (t, typeof<TimeSpan>) && TimeSpan.TryParse s |> fst -> TimeSpan.Parse s |> box
167-
| true, t when Type.(=) (t, typeof<bigint>) && Numerics.BigInteger.TryParse s |> fst -> bigint.Parse s |> box
168-
| _ ->
151+
if not ok then
169152
if Type.(=) (returnType, typeof<String>) then Convert.ToString itm |> box
170153
elif Type.(=) (returnType, typeof<Int32>) then Convert.ToInt32 itm |> box
171154
elif Type.(=) (returnType, typeof<Decimal>) then Convert.ToDecimal itm |> box
@@ -182,6 +165,66 @@ module Utilities =
182165
elif Type.(=) (returnType, typeof<SByte>) then Convert.ToSByte itm |> box
183166
elif Type.(=) (returnType, typeof<Char>) then Convert.ToChar itm |> box
184167
else itm |> box
168+
else
169+
170+
if Type.(=) (returnType, typeof<String>) then s |> box
171+
elif Type.(=) (returnType, typeof<Int32>) then
172+
let ok, x = Int32.TryParse s
173+
if ok then box x else Convert.ToInt32 itm |> box
174+
elif Type.(=) (returnType, typeof<Decimal>) then
175+
let ok, x = Decimal.TryParse s
176+
if ok then box x else Convert.ToDecimal itm |> box
177+
elif Type.(=) (returnType, typeof<Int64>) then
178+
let ok, x = Int64.TryParse s
179+
if ok then box x else Convert.ToInt64 itm |> box
180+
elif Type.(=) (returnType, typeof<Single>) then
181+
let ok, x = Single.TryParse s
182+
if ok then box x else Convert.ToSingle itm |> box
183+
elif Type.(=) (returnType, typeof<UInt32>) then
184+
let ok, x = UInt32.TryParse s
185+
if ok then box x else Convert.ToUInt32 itm |> box
186+
elif Type.(=) (returnType, typeof<Double>) then
187+
let ok, x = Double.TryParse s
188+
if ok then box x else Convert.ToDouble itm |> box
189+
elif Type.(=) (returnType, typeof<UInt64>) then
190+
let ok, x = UInt64.TryParse s
191+
if ok then box x else Convert.ToUInt64 itm |> box
192+
elif Type.(=) (returnType, typeof<Int16>) then
193+
let ok, x = Int16.TryParse s
194+
if ok then box x else Convert.ToInt16 itm |> box
195+
elif Type.(=) (returnType, typeof<UInt16>) then
196+
let ok, x = UInt16.TryParse s
197+
if ok then box x else Convert.ToUInt16 itm |> box
198+
elif Type.(=) (returnType, typeof<DateTime>) then
199+
let ok, x = DateTime.TryParse s
200+
if ok then box x else Convert.ToDateTime itm |> box
201+
elif Type.(=) (returnType, typeof<Boolean>) then
202+
let ok, x = Boolean.TryParse s
203+
if ok then box x else Convert.ToBoolean itm |> box
204+
elif Type.(=) (returnType, typeof<Byte>) then
205+
let ok, x = Byte.TryParse s
206+
if ok then box x else Convert.ToByte itm |> box
207+
elif Type.(=) (returnType, typeof<SByte>) then
208+
let ok, x = SByte.TryParse s
209+
if ok then box x else Convert.ToSByte itm |> box
210+
elif Type.(=) (returnType, typeof<Char>) then
211+
let ok, x = Char.TryParse s
212+
if ok then box x else Convert.ToChar itm |> box
213+
elif Type.(=) (returnType, typeof<DateTimeOffset>) then
214+
let ok, x = DateTimeOffset.TryParse s
215+
if ok then box x else itm |> box
216+
elif Type.(=) (returnType, typeof<TimeSpan>) then
217+
let ok, x = TimeSpan.TryParse s
218+
if ok then box x else itm |> box
219+
elif Type.(=) (returnType, typeof<bigint>) then
220+
let ok, x = Numerics.BigInteger.TryParse s
221+
if ok then box x else itm |> box
222+
elif Type.(=) (returnType, typeof<Guid>) then
223+
let ok, x = Guid.TryParse s
224+
if ok then box x else itm |> box
225+
else
226+
itm |> box
227+
185228

186229
/// Standard SQL. Provider spesific overloads can be done before this.
187230
let genericFieldNotation (recursionBase:SqlColumnType->string) (colSprint:string->string) = function

src/SQLProvider.Runtime/Providers.DuckDb.fs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,22 +1045,22 @@ type internal DuckDbProvider(resolutionPath, contextSchemaPath, owner:string, re
10451045
|> Seq.iter(fun e ->
10461046
match e._State with
10471047
| Created ->
1048-
let cmd = createInsertCommand con sb e
1048+
use cmd = createInsertCommand con sb e
10491049
Common.QueryEvents.PublishSqlQueryICol con.ConnectionString cmd.CommandText cmd.Parameters
10501050
if timeout.IsSome then
10511051
cmd.CommandTimeout <- timeout.Value
10521052
let id = cmd.ExecuteScalar()
10531053
CommonTasks.checkKey schemaCache.PrimaryKeys id e
10541054
e._State <- Unchanged
10551055
| Modified fields ->
1056-
let cmd = createUpdateCommand con sb e fields
1056+
use cmd = createUpdateCommand con sb e fields
10571057
Common.QueryEvents.PublishSqlQueryICol con.ConnectionString cmd.CommandText cmd.Parameters
10581058
if timeout.IsSome then
10591059
cmd.CommandTimeout <- timeout.Value
10601060
cmd.ExecuteNonQuery() |> ignore
10611061
e._State <- Unchanged
10621062
| Delete ->
1063-
let cmd = createDeleteCommand con sb e
1063+
use cmd = createDeleteCommand con sb e
10641064
Common.QueryEvents.PublishSqlQueryICol con.ConnectionString cmd.CommandText cmd.Parameters
10651065
if timeout.IsSome then
10661066
cmd.CommandTimeout <- timeout.Value
@@ -1097,7 +1097,7 @@ type internal DuckDbProvider(resolutionPath, contextSchemaPath, owner:string, re
10971097
match e._State with
10981098
| Created ->
10991099
task {
1100-
let cmd = createInsertCommand con sb e :?> System.Data.Common.DbCommand
1100+
use cmd = createInsertCommand con sb e :?> System.Data.Common.DbCommand
11011101
Common.QueryEvents.PublishSqlQueryICol con.ConnectionString cmd.CommandText cmd.Parameters
11021102
if timeout.IsSome then
11031103
cmd.CommandTimeout <- timeout.Value
@@ -1107,7 +1107,7 @@ type internal DuckDbProvider(resolutionPath, contextSchemaPath, owner:string, re
11071107
}
11081108
| Modified fields ->
11091109
task {
1110-
let cmd = createUpdateCommand con sb e fields :?> System.Data.Common.DbCommand
1110+
use cmd = createUpdateCommand con sb e fields :?> System.Data.Common.DbCommand
11111111
Common.QueryEvents.PublishSqlQueryICol con.ConnectionString cmd.CommandText cmd.Parameters
11121112
if timeout.IsSome then
11131113
cmd.CommandTimeout <- timeout.Value
@@ -1116,7 +1116,7 @@ type internal DuckDbProvider(resolutionPath, contextSchemaPath, owner:string, re
11161116
}
11171117
| Delete ->
11181118
task {
1119-
let cmd = createDeleteCommand con sb e :?> System.Data.Common.DbCommand
1119+
use cmd = createDeleteCommand con sb e :?> System.Data.Common.DbCommand
11201120
Common.QueryEvents.PublishSqlQueryICol con.ConnectionString cmd.CommandText cmd.Parameters
11211121
if timeout.IsSome then
11221122
cmd.CommandTimeout <- timeout.Value

0 commit comments

Comments
 (0)