Closed haf closed 9 years ago
Is that code open source? Could you point me to it?
No, sorry, not open source...
I'm trying to reproduce in a unit test, but since most calls in this function go through just fine...
Could you see under what circumstances this would be thrown? It would help me narrow it down.
Here's a stack-trace with line numbers:
System.ArgumentNullException: Argument cannot be null.
Parameter name: No connection associated with this transaction
at Mono.Data.Sqlite.SqliteTransaction.IsValid (Boolean throwError) [0x00011] in /private/tmp/source-mono-mac-3.12.0-branch-32/bockbuild-mono-3.12.0-branch/profiles/mono-mac-xamarin/build-root/mono-3.12.0/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteTransaction.cs:148
at Mono.Data.Sqlite.SqliteTransaction.Rollback () [0x00000] in /private/tmp/source-mono-mac-3.12.0-branch-32/bockbuild-mono-3.12.0-branch/profiles/mono-mac-xamarin/build-root/mono-3.12.0/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteTransaction.cs:127
at Tx.subscribe@113[Unit,Unit] (Microsoft.FSharp.Core.FSharpFunc`2 f, IDbTransaction tx, Microsoft.FSharp.Core.FSharpFunc`2 onCommit) [0x00066] in c:\prg\FsSql\FsSql\Transactions.fs:121
at Tx+transactional@125-1[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit].Invoke (IDbConnection conn) [0x0001c] in c:\prg\FsSql\FsSql\Transactions.fs:130
at FsSqlPrelude.withResource[IDbConnection,TxResult`2] (Microsoft.FSharp.Core.FSharpFunc`2 create, Microsoft.FSharp.Core.FSharpFunc`2 dispose, Microsoft.FSharp.Core.FSharpFunc`2 action) [0x0000a] in c:\prg\FsSql\FsSql\prelude.fs:14
at Tx+Run@132[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit].Invoke (ConnectionManager cmgr) [0x00026] in c:\prg\FsSql\FsSql\Transactions.fs:134
member x.Run (f: ConnectionManager -> TxResult<'a,_>) =
let subscribe (tx: IDbTransaction) (onCommit: IDbTransaction -> 'a -> TxResult<'a,_>) =
let r = f (withTransaction tx) // 4, prepares a ConnectionManager that doesn't do anything, invokes f
// what closes the connection opened in withResource (all of this is in its callback `action`)?
match r with
| Commit a -> onCommit tx a
| Rollback a ->
tx.Rollback()
Rollback a
| Failed e ->
tx.Rollback() // last FsSql call
Failed e
let transactional (conn: IDbConnection) =
let il = defaultArg isolation IsolationLevel.Unspecified
let tx = conn.BeginTransaction(il) // 2: this passes
let onCommit (tran: IDbTransaction) result =
tran.Commit()
Commit result
subscribe tx onCommit // 3.
fun cmgr ->
match cmgr.tx with
| None ->
// 1: inlined to withResource cmgr.create cmgr.dispose transactional
doWithConnection cmgr transactional
| Some t -> subscribe t (fun _ -> Commit)
// 5:
12 /// Creates a <see cref="ConnectionManager"/> with an externally-owned transaction¬
11 let withTransaction (tx: IDbTransaction): ConnectionManager =¬
10 let id = Guid.NewGuid().ToString()¬
9 let create() =·¬
8 logf "creating connection from const %s" id¬
7 tx.Connection¬
6 let dispose c = logf "disposing connection (but not really) %s" id¬
5 ConnectionManager.make(create, dispose, tx)¬
withResource
8 let withResource create dispose action =^M¬
9 let x = create()^M¬
10 try^M¬
11 action x^M¬
12 finally^M¬
13 dispose x^M¬
How about a bit more logging infrastructure? I'd be interested in the trace that's currently hard-coded to ()
?
It might have something to do with multiple Tx.execNonQueryi
calls in a tx {}
.
I believe this issue has earned the official Yak-shaving reward!
It also seems that the Tx functionality makes it so that exceptions aren't always raised properly.
Repro, finally:
module logibit.Sqlite.Tests.ExternalBugs
open Fuchu
open NodaTime
open System
open logibit.Sqlite
let prepare () =
let mgr = Sql.withNewConnection (fun () -> Conn.sqlite_open' InMemory)
Sql.execNonQuery mgr "
CREATE TABLE some_table
( id TEXT CONSTRAINT pk_id PRIMARY KEY,
required_field TEXT NOT NULL )"
[]
|> ignore
mgr, Sql.Parameter.make, Tx.TransactionBuilder()
[<Tests>]
let tests =
testList "https://github.com/mausch/FsSql/issues/23" [
testCase "'No connection associated with' ... nested with outer error" <| fun _ ->
let mgr, P, tx = prepare ()
let nested (f_cont : Sql.ConnectionManager -> _) =
tx {
let! ret = f_cont
return ret
}
let inner =
tx {
do! Tx.execNonQueryi "insert into some_table(id) values(@id)"
[ P("@id", "290345632deadbeef") ]
}
match nested inner mgr with
| Tx.Commit _ -> ()
| other -> Tests.failtestf "got %A" other
]
Output:
https://github.com/mausch/FsSql/issues/23/'No connection associated with' ... nested with outer error: Exception: System.ArgumentNullException: Argument cannot be null.
Parameter name: No connection associated with this transaction
at Mono.Data.Sqlite.SqliteTransaction.IsValid (Boolean throwError) [0x00011] in /private/tmp/source-mono-mac-3.12.0-branch-32/bockbuild-mono-3.12.0-branch/profiles/mono-mac-xamarin/build-root/mono-3.12.0/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteTransaction.cs:148
at Mono.Data.Sqlite.SqliteTransaction.Rollback () [0x00000] in /private/tmp/source-mono-mac-3.12.0-branch-32/bockbuild-mono-3.12.0-branch/profiles/mono-mac-xamarin/build-root/mono-3.12.0/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteTransaction.cs:127
at Tx.subscribe@113[Unit,Object] (Microsoft.FSharp.Core.FSharpFunc`2 f, IDbTransaction tx, Microsoft.FSharp.Core.FSharpFunc`2 onCommit) [0x00000] in <filename unknown>:0
at Tx+transactional@125-1[Microsoft.FSharp.Core.Unit,System.Object].Invoke (IDbConnection conn) [0x00000] in <filename unknown>:0
at FsSqlPrelude.withResource[IDbConnection,TxResult`2] (Microsoft.FSharp.Core.FSharpFunc`2 create, Microsoft.FSharp.Core.FSharpFunc`2 dispose, Microsoft.FSharp.Core.FSharpFunc`2 action) [0x00000] in <filename unknown>:0
at Tx+Run@132[Microsoft.FSharp.Core.Unit,System.Object].Invoke (ConnectionManager cmgr) [0x00000] in <filename unknown>:0
at Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.FSharpFunc`2[SqlModule+ConnectionManager,Tx+TxResult`2[Microsoft.FSharp.Core.Unit,System.Object]],SqlModule+ConnectionManager].InvokeFast[TxResult`2] (Microsoft.FSharp.Core.FSharpFunc`2 func, Microsoft.FSharp.Core.FSharpFunc`2 arg1, ConnectionManager arg2) [0x00000] in <filename unknown>:0
at logibit.Sqlite.Tests.ExternalBugs+tests@21.Invoke (Microsoft.FSharp.Core.Unit _arg1) [0x00041] in /src/logibit.sqlite.tests/ExternalBugs.fs:35
at Fuchu.Impl+evalTestList@258-1.Invoke (System.Tuple`2 tupledArg) [0x00000] in <filename unknown>:0 (00:00:00.1288380)
Did you manage to repro from this?
Ping
I just took a look and the problem here is definitely with the TransactionBuilder.Run
method. This is the one that does the actual Rollback/Commit on the real IDbTransaction
object.
The problem is that this particular arrangement in the test calls Run
twice. As the first call to Run
closes the transaction (either by calling Commit or Rollback on IDbTransaction), the second call tries to use the same transaction but fails because it has been already closed.
Fixed in aca1d81fa0786f3898db44e550c123af546206f8
:ice_cream:
Getting a funky exception.
Callee (this crashes, when it gives the SqlConnectionManager as a parameter from the value from the tx workflow builder):