@@ -10,10 +10,16 @@ import (
10
10
// Transaction returns all errors from txFunc or transaction commit errors happening after txFunc.
11
11
// If parentConn is already a transaction, then it is passed through to txFunc unchanged as tx Connection
12
12
// and no parentConn.Begin, Commit, or Rollback calls will occour within this Transaction call.
13
+ // An error is returned, if the requested transaction options passed via opts
14
+ // are stricter than the options of the parent transaction.
13
15
// Errors and panics from txFunc will rollback the transaction if parentConn was not already a transaction.
14
16
// Recovered panics are re-paniced and rollback errors after a panic are logged with ErrLogger.
15
17
func Transaction (parentConn Connection , opts * sql.TxOptions , txFunc func (tx Connection ) error ) (err error ) {
16
- if parentConn .IsTransaction () {
18
+ if parentOpts , parentIsTx := parentConn .TransactionOptions (); parentIsTx {
19
+ err = CheckTxOptionsCompatibility (parentOpts , opts , parentConn .Config ().DefaultIsolationLevel )
20
+ if err != nil {
21
+ return err
22
+ }
17
23
return txFunc (parentConn )
18
24
}
19
25
@@ -52,3 +58,34 @@ func Transaction(parentConn Connection, opts *sql.TxOptions, txFunc func(tx Conn
52
58
53
59
return txFunc (tx )
54
60
}
61
+
62
+ // CheckTxOptionsCompatibility returns an error
63
+ // if the parent transaction options are less strict than the child options.
64
+ func CheckTxOptionsCompatibility (parent , child * sql.TxOptions , defaultIsolation sql.IsolationLevel ) error {
65
+ var (
66
+ parentReadOnly = false
67
+ parentIsolation = defaultIsolation
68
+ childReadOnly = false
69
+ childIsolation = defaultIsolation
70
+ )
71
+ if parent != nil {
72
+ parentReadOnly = parent .ReadOnly
73
+ if parent .Isolation != sql .LevelDefault {
74
+ parentIsolation = parent .Isolation
75
+ }
76
+ }
77
+ if child != nil {
78
+ childReadOnly = child .ReadOnly
79
+ if child .Isolation != sql .LevelDefault {
80
+ childIsolation = child .Isolation
81
+ }
82
+ }
83
+
84
+ if parentReadOnly && ! childReadOnly {
85
+ return errors .New ("parent transaction is read-only but child is not" )
86
+ }
87
+ if parentIsolation < childIsolation {
88
+ return fmt .Errorf ("parent transaction isolation level '%s' is less strict child level '%s'" , parentIsolation , childIsolation )
89
+ }
90
+ return nil
91
+ }
0 commit comments