Skip to content

Commit 1b1180f

Browse files
Merge pull request #364 from piaste/improve_trace_string
Improve trace string which was failing for several parameter types.
2 parents 37a6ae5 + 42a40e1 commit 1b1180f

File tree

3 files changed

+63
-5
lines changed

3 files changed

+63
-5
lines changed

src/SqlClient/ISqlCommand.fs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,11 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio
134134
member this.ToTraceString parameters =
135135
``ISqlCommand Implementation``.SetParameters(cmd, parameters)
136136
let parameterDefinition (p : SqlParameter) =
137-
if p.Size <> 0 then
137+
// tinyint and Xml have size 1 and -1 respectively, but MSSQL will throw if they are specified
138+
if p.Size <> 0 &&
139+
p.SqlDbType <> SqlDbType.Xml &&
140+
p.SqlDbType <> SqlDbType.TinyInt then
141+
138142
sprintf "%s %A(%d)" p.ParameterName p.SqlDbType p.Size
139143
else
140144
sprintf "%s %A" p.ParameterName p.SqlDbType
@@ -153,7 +157,17 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio
153157
if parameters.Length > 0
154158
then
155159
yield parameters
156-
|> Seq.map(fun (name,value) -> sprintf "%s='%O'" name value)
160+
|> Seq.map(fun (name,value) ->
161+
let printedValue =
162+
match value with
163+
// print dates in roundtrip ISO8601 format "O"
164+
| :? System.DateTime as d -> d.ToString("O")
165+
// print timespans in constant format "c
166+
| :? System.TimeSpan as t -> t.ToString("c")
167+
| v -> sprintf "%O" v
168+
// escapes the resulting value
169+
sprintf "%s='%s'" name (printedValue.Replace("'", "''"))
170+
)
157171
|> String.concat ","
158172
} |> String.concat "," //Using string.concat to handle annoying case with no parameters
159173

tests/SqlClient.Tests/CreateCommand.fs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ let columnsShouldNotBeNull2() =
8585
[<Fact>]
8686
let toTraceString() =
8787
let now = System.DateTime.Now
88+
let universalNow = now.ToString("O")
8889
let num = 42
89-
let expected = sprintf "exec sp_executesql N'SELECT CAST(@Date AS DATE), CAST(@Number AS INT)',N'@Date Date,@Number Int',@Date='%A',@Number='%d'" now num
90+
let expected = sprintf "exec sp_executesql N'SELECT CAST(@Date AS DATE), CAST(@Number AS INT)',N'@Date Date,@Number Int',@Date='%s',@Number='%d'" universalNow num
9091
let cmd = DB.CreateCommand<"SELECT CAST(@Date AS DATE), CAST(@Number AS INT)", ResultType.Tuples>()
9192
Assert.Equal<string>(
9293
expected,
@@ -170,4 +171,4 @@ let ``connection is properly disposed`` () =
170171
use reader = cmd.Execute()
171172
()
172173

173-
Assert.True isDisposed
174+
Assert.True isDisposed

tests/SqlClient.Tests/TypeProviderTest.fs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,47 @@ let singleRowOption() =
8989
[<Fact>]
9090
let ToTraceString() =
9191
let now = DateTime.Now
92+
let universalPrintedNow = now.ToString("O")
9293
let num = 42
93-
let expected = sprintf "exec sp_executesql N'SELECT CAST(@Date AS DATE), CAST(@Number AS INT)',N'@Date Date,@Number Int',@Date='%A',@Number='%d'" now num
94+
let expected = sprintf "exec sp_executesql N'SELECT CAST(@Date AS DATE), CAST(@Number AS INT)',N'@Date Date,@Number Int',@Date='%s',@Number='%d'" universalPrintedNow num
9495
let cmd = new SqlCommandProvider<"SELECT CAST(@Date AS DATE), CAST(@Number AS INT)", ConnectionStrings.AdventureWorksNamed, ResultType.Tuples>()
9596
Assert.Equal<string>(
9697
expected,
9798
actual = cmd.ToTraceString( now, num)
9899
)
100+
101+
let runString query =
102+
use conn = new SqlConnection(ConnectionStrings.AdventureWorks)
103+
conn.Open()
104+
use cmd = new System.Data.SqlClient.SqlCommand()
105+
cmd.Connection <- conn
106+
cmd.CommandText <- query
107+
cmd.ExecuteNonQuery()
108+
109+
[<Fact>]
110+
let ``ToTraceString for dates``() =
111+
let cmd = new SqlCommandProvider<"SELECT CAST(@Date AS DATE)", ConnectionStrings.AdventureWorksNamed>()
112+
runString <| cmd.ToTraceString(System.DateTime.Now)
113+
114+
[<Fact>]
115+
let ``ToTraceString for times``() =
116+
let cmd = new SqlCommandProvider<"SELECT CAST(@Time AS Time)", ConnectionStrings.AdventureWorksNamed>()
117+
runString <| cmd.ToTraceString(System.DateTime.Now.TimeOfDay)
118+
119+
[<Fact>]
120+
let ``ToTraceString for tinyint``() =
121+
let cmd = new SqlCommandProvider<"SELECT CAST(@ti AS TINYINT)", ConnectionStrings.AdventureWorksNamed>()
122+
runString <| cmd.ToTraceString(0uy)
123+
124+
[<Fact>]
125+
let ``ToTraceString for xml``() =
126+
let cmd = new SqlCommandProvider<"SELECT CAST(@x AS XML)", ConnectionStrings.AdventureWorksNamed>()
127+
runString <| cmd.ToTraceString("<foo>bar</foo>")
128+
129+
[<Fact>]
130+
let ``ToTraceString for xml with single quotes``() =
131+
let cmd = new SqlCommandProvider<"SELECT CAST(@x AS XML)", ConnectionStrings.AdventureWorksNamed>()
132+
runString <| cmd.ToTraceString("<foo>b'ar</foo>")
99133

100134
[<Fact>]
101135
let ``ToTraceString for CRUD``() =
@@ -121,6 +155,15 @@ let ``ToTraceString double-quotes``() =
121155
let trace = cmd.ToTraceString()
122156
Assert.Equal<string>("exec sp_executesql N'SELECT OBJECT_ID(''Sales.Currency'')'", trace)
123157

158+
159+
[<Fact>]
160+
let ``ToTraceString double-quotes in parameter``() =
161+
use cmd = new SqlCommandProvider<"SELECT * FROM Sales.Currency WHERE CurrencyCode = @CurrencyCode", ConnectionStrings.AdventureWorksNamed>()
162+
Assert.Equal<string>(
163+
expected = "exec sp_executesql N'SELECT * FROM Sales.Currency WHERE CurrencyCode = @CurrencyCode',N'@CurrencyCode NChar(3)',@CurrencyCode='A''B'",
164+
actual = cmd.ToTraceString("A'B")
165+
)
166+
124167
[<Fact(
125168
Skip = "Don't execute for usual runs. Too slow."
126169
)>]

0 commit comments

Comments
 (0)