diff --git a/collects/db/private/generic/common.rkt b/collects/db/private/generic/common.rkt index 02e575cb63..b1f8d0a583 100644 --- a/collects/db/private/generic/common.rkt +++ b/collects/db/private/generic/common.rkt @@ -167,23 +167,28 @@ (define transactions% (class locking% - (inherit call-with-lock) - #| A transaction created via SQL is "unmanaged". A transaction created via start-tx, call-with-tx is "managed". - FIXME: eliminate distinction, if possible. - - currently: tx-stack != null means tx-status != #f - - would also like: tx-stack = null iff tx-status = #f + tx-status : #f, #t, 'invalid + Indicates whether in a transaction (managed or unmanaged) and if + transaction is valid or invalid. + + tx-stack : (list (cons string boolean) ... (cons #f boolean)) + Represents the "managed" transaction stack. + + If tx-status = #f, then tx-stack = null (except temporarily, + within lock). But it is possible for tx-status != #f and + tx-stack = null; that indicates an unmanaged tx. |# - ;; tx-status : #f, #t, 'invalid - (field [tx-status #f]) + (define tx-status #f) + (define tx-stack null) - ;; tx-stack : (list (cons string boolean) ... (cons #f boolean) - ;; Represents the "managed" transaction stack. - (field [tx-stack null]) + (define/public (get-tx-status) tx-status) + (define/public (set-tx-status! fsym s) + (set! tx-status s)) ;; check-valid-tx-status : symbol -> void (define/public (check-valid-tx-status fsym) @@ -192,16 +197,19 @@ ;; ---- + ;; (inherit call-with-lock) + (define/override (call-with-lock fsym proc) + (super call-with-lock fsym + (lambda () + (begin0 (proc) + (when (and (eq? tx-status #f) (not (null? tx-stack))) + (error/internal fsym "managed transaction unexpectedly closed")))))) + + ;; ---- + (define/public (transaction-status fsym) (call-with-lock fsym (lambda () tx-status))) - ;; transaction-nesting : -> (U #f 'unmanaged 'top-level 'nested) - (define/public (transaction-nesting) - (cond [(eq? tx-status #f) #f] - [(null? tx-stack) 'unmanaged] - [(null? (cdr tx-stack)) 'top-level] - [else 'nested])) - (define/public (tx-state->string) (string-append (case (transaction-nesting) ((#f) "not in transaction") @@ -213,6 +221,12 @@ (string-join savepoints ", ")) "")))) + (define/private (transaction-nesting) + (cond [(eq? tx-status #f) #f] + [(null? tx-stack) 'unmanaged] + [(null? (cdr tx-stack)) 'top-level] + [else 'nested])) + ;; ---- (define/public (start-transaction fsym isolation cwt?) @@ -296,8 +310,8 @@ if in "managed" top-level transaction (no "managed" savepoints): - START not allowed - COMMIT, ROLLBACK not allowed (for now!) - - SAVEPOINT allowed - - RELEASE TO, ROLLBACK TO allowed + - SAVEPOINT not allowed (for consistency, for ease of stmt cache) + - RELEASE TO, ROLLBACK TO not allowed (for consistency, for ease of stmt cache) - implicit-commit not allowed if in nested "managed" transaction (impl as "managed" savepoint): @@ -321,15 +335,7 @@ (void)) ((unmanaged) (void)) - ((top-level) - (case stmt-type - ((start) - (no! " within transaction")) - ((commit rollback - implicit-commit) - (no! " within managed transaction")) - (else (void)))) - ((nested) + ((top-level nested) (case stmt-type ((start) (no! " within transaction")) @@ -337,7 +343,7 @@ savepoint prepare-transaction release-savepoint rollback-savepoint implicit-commit) - (no! " in managed transaction")) + (no! " within managed transaction")) (else (void)))))) (super-new))) diff --git a/collects/db/private/mysql/connection.rkt b/collects/db/private/mysql/connection.rkt index 8f6ff681b0..57eb5b969c 100644 --- a/collects/db/private/mysql/connection.rkt +++ b/collects/db/private/mysql/connection.rkt @@ -26,8 +26,9 @@ call-with-lock* add-delayed-call! check-valid-tx-status + get-tx-status + set-tx-status! check-statement/tx) - (inherit-field tx-status) (super-new) @@ -97,15 +98,13 @@ (eprintf " << ~s\n" next)) ;; Update transaction status (see Transactions below) (when (ok-packet? next) - (set! tx-status - (bitwise-bit-set? (ok-packet-server-status next) 0))) + (set-tx-status! fsym (bitwise-bit-set? (ok-packet-server-status next) 0))) (when (eof-packet? next) - (set! tx-status - (bitwise-bit-set? (eof-packet-server-status next) 0))) + (set-tx-status! fsym (bitwise-bit-set? (eof-packet-server-status next) 0))) (when (error-packet? next) - (when tx-status - (when (member (error-packet-errno next) '(1213 1205)) - (set! tx-status 'invalid)))) + (when (member (error-packet-errno next) '(1213 1205)) + (when (get-tx-status) + (set-tx-status! fsym 'invalid)))) (match next [(? handshake-packet?) (advance 'handshake)] diff --git a/collects/db/private/odbc/connection.rkt b/collects/db/private/odbc/connection.rkt index f32d6eea1e..77b77742ae 100644 --- a/collects/db/private/odbc/connection.rkt +++ b/collects/db/private/odbc/connection.rkt @@ -43,9 +43,10 @@ (inherit call-with-lock call-with-lock* add-delayed-call! + get-tx-status + set-tx-status! check-valid-tx-status check-statement/tx) - (inherit-field tx-status) (define/public (get-db fsym) (unless db @@ -539,7 +540,7 @@ (handle-status fsym status db))) (let ([status (SQLSetConnectAttr db SQL_ATTR_AUTOCOMMIT SQL_AUTOCOMMIT_OFF)]) (handle-status fsym status db) - (set! tx-status #t) + (set-tx-status! fsym #t) (void))) (define/override (end-transaction* fsym mode _savepoint) @@ -553,7 +554,7 @@ (let ([status (SQLSetConnectAttr db SQL_ATTR_AUTOCOMMIT SQL_AUTOCOMMIT_ON)]) (handle-status fsym status db) ;; commit/rollback can fail; don't change status until possible error handled - (set! tx-status #f) + (set-tx-status! fsym #f) (void)))) ;; GetTables @@ -637,8 +638,8 @@ ;; if the driver does one-statement rollback. (let ([db db]) (when db - (when tx-status - (set! tx-status 'invalid)))) + (when (get-tx-status) + (set-tx-status! who 'invalid)))) (raise e)) ;; Be careful: shouldn't do rollback before we call handle-status* ;; just in case rollback destroys statement with diagnostic records. diff --git a/collects/db/private/postgresql/connection.rkt b/collects/db/private/postgresql/connection.rkt index cedd379b82..efe2ccf843 100644 --- a/collects/db/private/postgresql/connection.rkt +++ b/collects/db/private/postgresql/connection.rkt @@ -34,11 +34,11 @@ (inherit call-with-lock call-with-lock* add-delayed-call! + get-tx-status + set-tx-status! check-valid-tx-status check-statement/tx - transaction-nesting tx-state->string) - (inherit-field tx-status) (super-new) @@ -105,10 +105,11 @@ (let ([r (recv-message fsym)]) (cond [(ReadyForQuery? r) ;; Update transaction status - (case (ReadyForQuery-transaction-status r) - ((idle) (set! tx-status #f)) - ((transaction) (set! tx-status #t)) - ((failed) (set! tx-status 'invalid)))] + (set-tx-status! fsym + (case (ReadyForQuery-transaction-status r) + ((idle) #f) + ((transaction) #t) + ((failed) 'invalid)))] [(and or-eof? (eof-object? r)) (void)] [else (error/comm fsym "expected ready")]))) diff --git a/collects/db/private/sqlite3/connection.rkt b/collects/db/private/sqlite3/connection.rkt index a084c29040..edcc0a5c91 100644 --- a/collects/db/private/sqlite3/connection.rkt +++ b/collects/db/private/sqlite3/connection.rkt @@ -25,12 +25,13 @@ (inherit call-with-lock* add-delayed-call! + get-tx-status + set-tx-status! check-valid-tx-status check-statement/tx) - (inherit-field tx-status) (define/override (call-with-lock fsym proc) - (call-with-lock* fsym (lambda () (set! saved-tx-status tx-status) (proc)) #f #t)) + (call-with-lock* fsym (lambda () (set! saved-tx-status (get-tx-status)) (proc)) #f #t)) (define/private (get-db fsym) (or -db (error/not-connected fsym))) @@ -63,8 +64,8 @@ [result (or cursor? (step* fsym db stmt #f +inf.0))]) - (unless (eq? tx-status 'invalid) - (set! tx-status (get-tx-status))) + (unless (eq? (get-tx-status) 'invalid) + (set-tx-status! fsym (read-tx-status))) (unless cursor? (send pst after-exec #f)) (cond [(and (pair? info) (not cursor?)) (rows-result info result)] @@ -223,7 +224,7 @@ ;; http://www.sqlite.org/lang_transaction.html - (define/private (get-tx-status) + (define/private (read-tx-status) (not (sqlite3_get_autocommit -db))) (define/override (start-transaction* fsym isolation) @@ -252,7 +253,7 @@ [else (internal-query1 fsym "ROLLBACK TRANSACTION")]) ;; remove 'invalid status, if necessary - (set! tx-status (get-tx-status)))) + (set-tx-status! fsym (read-tx-status)))) (void)) ;; name-counter : number @@ -295,8 +296,8 @@ ;; Can't figure out how to test... (define/private (handle-status who s) (when (memv s maybe-rollback-status-list) - (when (and saved-tx-status -db (not (get-tx-status))) ;; was in trans, now not - (set! tx-status 'invalid))) + (when (and saved-tx-status -db (not (read-tx-status))) ;; was in trans, now not + (set-tx-status! who 'invalid))) (handle-status* who s -db)) ;; ----