Skip to content

Commit 39bfbc1

Browse files
committed
Handle backend-specific empty IN list at runtime by sniffing connection type.
This isn't pretty, but definitely simpler than giving the command batch access to the backend that was used to generate the given command fragments.
1 parent de145f7 commit 39bfbc1

1 file changed

Lines changed: 74 additions & 14 deletions

File tree

src/Rezoom.SQL.Mapping/CommandBatch.fs

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,78 @@
11
namespace Rezoom.SQL.Mapping
22
open System
3+
open System.Data
34
open System.Data.Common
45
open System.Collections.Generic
56
open System.Text
67
open System.Threading
78
open FSharp.Control.Tasks.ContextInsensitive
89
open Rezoom.SQL
910

10-
type private CommandBatchBuilder(conn : DbConnection, tran : DbTransaction) =
11-
let maxParameters =
12-
match conn.GetType().Namespace with
13-
| "System.Data.SqlClient" -> 2100 // SQL server
11+
type private CommandBatchRuntimeBackend =
12+
private
13+
| SQLServer
14+
| Oracle
15+
| Postgres
16+
| MySQL
17+
| SQLite
18+
| Other
19+
static member OfNamespace(ns : string) =
20+
match ns with
21+
| "System.Data.SqlClient" -> SQLServer
1422
| "System.Data.OracleClient"
15-
| "Oracle.DataAccess.Client" -> 2000 // Oracle
16-
| "Npgsql" -> 10000 // Postgres -- can support more but it's probably a bad idea
17-
| "MySql.Data.MySqlClient" -> 10000 // MySQL -- can support more but it's probably a bad idea
18-
| _ -> 999 // SQLite default, and probably the lowest of any DB
23+
| "Oracle.DataAccess.Client" -> Oracle
24+
| "Npgsql" -> Postgres
25+
| "MySql.Data.MySqlClient" -> MySQL
26+
| "System.Data.SQLite"
27+
| "Devart.Data.SQLite" -> SQLite
28+
| _ -> Other
29+
member this.MaxParameters() =
30+
match this with
31+
| SQLServer -> 2100
32+
| Oracle -> 2000
33+
| Postgres
34+
| MySQL -> 10_000
35+
| SQLite
36+
| Other -> 999
37+
static member private PgType(ty : DbType) =
38+
match ty with
39+
| DbType.String
40+
| DbType.StringFixedLength
41+
| DbType.AnsiString
42+
| DbType.AnsiStringFixedLength -> "text"
43+
| DbType.Byte
44+
| DbType.SByte
45+
| DbType.UInt16
46+
| DbType.UInt32
47+
| DbType.UInt64
48+
| DbType.Int16
49+
| DbType.Int32
50+
| DbType.Int64
51+
| DbType.VarNumeric
52+
| DbType.Decimal
53+
| DbType.Single
54+
| DbType.Double -> "numeric"
55+
| DbType.Binary -> "bytea"
56+
| DbType.Guid -> "uuid"
57+
| DbType.DateTime
58+
| DbType.DateTime2
59+
| DbType.DateTimeOffset -> "timestamptz"
60+
| _ -> "unknown"
61+
member this.EmptyInList(ty : DbType) =
62+
match this with
63+
| Postgres ->
64+
// PG has to be difficult and demand a type specifier matching the input
65+
"(SELECT NULL::" + CommandBatchRuntimeBackend.PgType(ty) + " WHERE FALSE)"
66+
| SQLite ->
67+
// SQLite is cool and accepts the simple approach. This might be faster than the empty subquery.
68+
"()"
69+
| _ ->
70+
"(SELECT NULL WHERE 1=0)"
71+
72+
type private CommandBatchBuilder(conn : DbConnection, tran : DbTransaction) =
73+
let runtimeBackend = CommandBatchRuntimeBackend.OfNamespace(conn.GetType().Namespace)
74+
let maxParameters = runtimeBackend.MaxParameters()
75+
1976
static let terminatorColumn i = "RZSQL_TERMINATOR_" + string i
2077
static let terminator i = ";--'*/;SELECT NULL AS " + terminatorColumn i
2178
static let parameterName i = "@RZSQL_" + string i
@@ -51,12 +108,15 @@ type private CommandBatchBuilder(conn : DbConnection, tran : DbTransaction) =
51108
| CommandText str -> str
52109
| Parameter i ->
53110
match command.Parameters.[i] with
54-
| ListParameter(_, os) ->
55-
let parNames =
56-
seq {
57-
for j = 0 to os.Length - 1 do yield parameterNameArray (parameterOffset + i) j
58-
}
59-
"(" + String.concat "," parNames + ")"
111+
| ListParameter(dbTy, os) ->
112+
if os.Length = 0 then
113+
runtimeBackend.EmptyInList(dbTy)
114+
else
115+
let parNames =
116+
seq {
117+
for j = 0 to os.Length - 1 do yield parameterNameArray (parameterOffset + i) j
118+
}
119+
"(" + String.concat "," parNames + ")"
60120
| ScalarParameter _ -> parameterName (parameterOffset + i)
61121
| RawSQLParameter frags ->
62122
for frag in frags do

0 commit comments

Comments
 (0)