db: clean up communication, locking

Disconnect on break exn within lock; other break-safety fixes.
This commit is contained in:
Ryan Culpepper 2012-01-15 23:18:03 -07:00
parent f5711c6cc3
commit f142a1c5f2
8 changed files with 271 additions and 200 deletions

View File

@ -111,3 +111,5 @@ Misc
so a statement can be invalidated between cache retrieval and execution so a statement can be invalidated between cache retrieval and execution
- support logging - support logging
- audit: make sure no output within atomic section (in drracket, may thread switch)

View File

@ -118,9 +118,11 @@
(define/public-final (call-with-lock* who proc hopeless require-connected?) (define/public-final (call-with-lock* who proc hopeless require-connected?)
(let ([me (thread-dead-evt (current-thread))] (let ([me (thread-dead-evt (current-thread))]
[eb? (break-enabled)]
[result (sync outer-lock lock-holder)]) [result (sync outer-lock lock-holder)])
(cond [(eq? result outer-lock) (cond [(eq? result outer-lock)
;; Got past outer stage ;; Got past outer stage
(break-enabled #f)
(let ([proceed? (let ([proceed?
(begin (start-atomic) (begin (start-atomic)
(let ([proceed? (semaphore-try-wait? inner-lock)]) (let ([proceed? (semaphore-try-wait? inner-lock)])
@ -133,21 +135,30 @@
;; Acquired lock ;; Acquired lock
;; - lock-holder = me, and outer-lock is closed again ;; - lock-holder = me, and outer-lock is closed again
(when (and require-connected? (not (connected?))) (when (and require-connected? (not (connected?)))
(unlock) (break-enabled eb?)
(unlock #f)
(error/not-connected who)) (error/not-connected who))
(with-handlers ([values (lambda (e) (unlock) (raise e))]) (with-handlers ([(lambda (e) #t)
(begin0 (proc) (unlock)))] (lambda (e)
(when (exn:break? e) (on-break-within-lock))
(unlock #f)
(raise e))])
(break-enabled eb?)
(begin0 (proc) (unlock #t)))]
[else [else
;; Didn't acquire lock; retry ;; Didn't acquire lock; retry
(break-enabled eb?)
(call-with-lock* who proc hopeless require-connected?)]))] (call-with-lock* who proc hopeless require-connected?)]))]
[(eq? result lock-holder) [(eq? result lock-holder)
;; Thread holding lock is dead ;; Thread holding lock is dead
(if hopeless (hopeless) (error/hopeless who))] (if hopeless (hopeless) (error/hopeless who))]
[(eq? me lock-holder)
(error/internal who "attempted to recursively acquire lock")]
[else [else
;; lock-holder was stale; retry ;; lock-holder was stale; retry
(call-with-lock* who proc hopeless require-connected?)]))) (call-with-lock* who proc hopeless require-connected?)])))
(define/private (unlock) (define/private (unlock run-async-calls?)
(let ([async-calls (reverse delayed-async-calls)]) (let ([async-calls (reverse delayed-async-calls)])
(set! delayed-async-calls null) (set! delayed-async-calls null)
(start-atomic) (start-atomic)
@ -155,14 +166,21 @@
(semaphore-post inner-lock) (semaphore-post inner-lock)
(semaphore-post outer-sema) (semaphore-post outer-sema)
(end-atomic) (end-atomic)
(for-each call-with-continuation-barrier async-calls))) (when run-async-calls?
(for-each call-with-continuation-barrier async-calls))))
;; needs overriding ;; needs overriding
(define/public (connected?) #f) (define/public (connected?) #f)
(define/public-final (add-delayed-call! proc) (define/public (add-delayed-call! proc)
(set! delayed-async-calls (cons proc delayed-async-calls))) (set! delayed-async-calls (cons proc delayed-async-calls)))
;; on-break-within-lock : -> void
;; Called before unlock; makes it easy to disconnect on any break
;; within lock.
(define/public (on-break-within-lock)
(void))
(super-new))) (super-new)))
;; ---------------------------------------- ;; ----------------------------------------

View File

@ -119,7 +119,9 @@
;; timeout? = if connection open, then wait longer ;; timeout? = if connection open, then wait longer
(let* ([c (hash-ref key=>conn key #f)] (let* ([c (hash-ref key=>conn key #f)]
[in-trans? (with-handlers ([exn:fail? (lambda (e) #f)]) [in-trans? (with-handlers ([exn:fail? (lambda (e) #f)])
(and c (send c transaction-status 'virtual-connection)))]) (and c
(send c connected?)
(send c transaction-status 'virtual-connection)))])
(cond [(not c) (void)] (cond [(not c) (void)]
[(and timeout? in-trans?) [(and timeout? in-trans?)
(hash-set! alarms c (fresh-alarm-for key))] (hash-set! alarms c (fresh-alarm-for key))]
@ -182,7 +184,7 @@
(#t '_ (fetch/cursor fsym stmt fetch-size)) (#t '_ (fetch/cursor fsym stmt fetch-size))
(#t '_ (start-transaction fsym isolation cwt?)) (#t '_ (start-transaction fsym isolation cwt?))
(#f (void) (end-transaction fsym mode cwt?)) (#f (void) (end-transaction fsym mode cwt?))
(#f #f (transaction-status fsym)) (#t '_ (transaction-status fsym))
(#t '_ (list-tables fsym schema))) (#t '_ (list-tables fsym schema)))
(define/public (get-base) (define/public (get-base)

View File

@ -35,44 +35,61 @@
(super-new) (super-new)
;; with-disconnect-on-error
(define-syntax-rule (with-disconnect-on-error . body)
(with-handlers ([exn:fail? (lambda (e) (disconnect* #f) (raise e))])
. body))
;; ======================================== ;; ========================================
;; == Communication ;; == Communication
;; (Must be called with lock acquired.)
#|
During initial setup, okay to send and recv directly, since reference
to connection does not escape to user. In particular, no danger of trying
to start a new exchange on top of an incomplete failed one.
After initial setup, communication can only happen within lock, and any
error (other than exn:fail:sql) that occurs between sending the message
buffer (flush-message-buffer) and receiving the last message (recv)
must cause the connection to disconnect. Such errors include communication
errors and breaks.
|#
(define msg-buffer null)
(define next-msg-num 0) (define next-msg-num 0)
(define/private (fresh-exchange) (define/private (fresh-exchange)
(set! msg-buffer null)
(set! next-msg-num 0)) (set! next-msg-num 0))
;; buffer-message : message -> void
(define/private (buffer-message msg)
(dprintf " >> ~s\n" msg)
(set! msg-buffer (cons (cons msg next-msg-num) msg-buffer))
(set! next-msg-num (add1 next-msg-num)))
;; flush-message-buffer : -> void
(define/private (flush-message-buffer)
(for ([msg+num (in-list (reverse msg-buffer))])
(write-packet outport (car msg+num) (cdr msg+num)))
(flush-output outport))
;; send-message : message -> void ;; send-message : message -> void
(define/private (send-message msg) (define/private (send-message msg)
(buffer-message msg) (buffer-message msg)
(flush-message-buffer)) (flush-message-buffer))
;; buffer-message : message -> void (define/private (call-with-sync fsym proc)
(define/private (buffer-message msg) (with-handlers ([(lambda (e) #t)
(dprintf " >> ~s\n" msg) (lambda (e)
(with-disconnect-on-error ;; Anything but exn:fail:sql (raised by recv-message) indicates
(write-packet outport msg next-msg-num) ;; a communication error.
(set! next-msg-num (add1 next-msg-num)))) (unless (exn:fail:sql? e)
(disconnect* #f))
;; flush-message-buffer : -> void (raise e))])
(define/private (flush-message-buffer) (flush-message-buffer)
(with-disconnect-on-error (proc)))
(flush-output outport)))
;; recv : symbol/#f [(list-of symbol)] -> message ;; recv : symbol/#f [(list-of symbol)] -> message
;; Automatically handles asynchronous messages ;; Automatically handles asynchronous messages
(define/private (recv fsym expectation [field-dvecs #f]) (define/private (recv fsym expectation [field-dvecs #f])
(define r (define r (recv* fsym expectation field-dvecs))
(with-disconnect-on-error
(recv* fsym expectation field-dvecs)))
(when (error-packet? r) (when (error-packet? r)
(raise-backend-error fsym r)) (raise-backend-error fsym r))
r) r)
@ -126,36 +143,34 @@
(err next)]) (err next)])
next)) next))
(define/override (on-break-within-lock)
(disconnect* #f))
;; ======================================== ;; ========================================
;; Connection management ;; Connection management
;; disconnect : -> (void) ;; disconnect : -> void
(define/public (disconnect) (define/public (disconnect)
(disconnect* #t)) (when (connected?)
(call-with-lock* 'disconnect
(lambda () (disconnect* #t))
(lambda () (disconnect* #f))
#f)))
(define/private (disconnect* lock-not-held?) (define/private (disconnect* politely?)
(define (go politely?) (dprintf " ** Disconnecting\n")
(dprintf " ** Disconnecting\n") (let ([outport* outport]
(let ([outport* outport] [inport* inport])
[inport* inport]) (when outport*
(when outport (when politely?
(when politely? (fresh-exchange)
(fresh-exchange) (send-message (make-command-packet 'quit "")))
(send-message (make-command-packet 'quit ""))) (close-output-port outport*)
(close-output-port outport) (set! outport #f))
(set! outport #f)) (when inport*
(when inport (close-input-port inport*)
(close-input-port inport) (set! inport #f))))
(set! inport #f))))
;; If we don't hold the lock, try to acquire it and disconnect politely.
;; Except, if already disconnected, no need to acquire lock.
(cond [(and lock-not-held? (connected?))
(call-with-lock* 'disconnect
(lambda () (go #t))
(lambda () (go #f))
#f)]
[else (go #f)]))
;; connected? : -> boolean ;; connected? : -> boolean
(define/override (connected?) (define/override (connected?)
@ -176,31 +191,30 @@
;; start-connection-protocol : string/#f string string/#f -> void ;; start-connection-protocol : string/#f string string/#f -> void
(define/public (start-connection-protocol dbname username password ssl ssl-context) (define/public (start-connection-protocol dbname username password ssl ssl-context)
(with-disconnect-on-error (fresh-exchange)
(fresh-exchange) (let ([r (recv 'mysql-connect 'handshake)])
(let ([r (recv 'mysql-connect 'handshake)]) (match r
(match r [(struct handshake-packet (pver sver tid scramble capabilities charset status auth))
[(struct handshake-packet (pver sver tid scramble capabilities charset status auth)) (check-required-flags capabilities)
(check-required-flags capabilities) (unless (member auth '("mysql_native_password" #f))
(unless (member auth '("mysql_native_password" #f)) (uerror 'mysql-connect "unsupported authentication plugin: ~s" auth))
(uerror 'mysql-connect "unsupported authentication plugin: ~s" auth)) (define do-ssl?
(define do-ssl? (and (case ssl ((yes optional) #t) ((no) #f))
(and (case ssl ((yes optional) #t) ((no) #f)) (memq 'ssl capabilities)))
(memq 'ssl capabilities))) (when (and (eq? ssl 'yes) (not do-ssl?))
(when (and (eq? ssl 'yes) (not do-ssl?)) (uerror 'mysql-connect "server refused SSL connection"))
(uerror 'mysql-connect "server refused SSL connection")) (define wanted-capabilities (desired-capabilities capabilities do-ssl? dbname))
(define wanted-capabilities (desired-capabilities capabilities do-ssl? dbname)) (when do-ssl?
(when do-ssl? (send-message (make-abbrev-client-auth-packet wanted-capabilities))
(send-message (make-abbrev-client-auth-packet wanted-capabilities)) (let-values ([(sin sout)
(let-values ([(sin sout) (ports->ssl-ports inport outport
(ports->ssl-ports inport outport #:mode 'connect
#:mode 'connect #:context ssl-context
#:context ssl-context #:close-original? #t)])
#:close-original? #t)]) (attach-to-ports sin sout)))
(attach-to-ports sin sout))) (authenticate wanted-capabilities username password dbname
(authenticate wanted-capabilities username password dbname (or auth "mysql_native_password") scramble)]
(or auth "mysql_native_password") scramble)] [_ (error/comm 'mysql-connect "during authentication")])))
[_ (error/comm 'mysql-connect "during authentication")]))))
(define/private (authenticate capabilities username password dbname auth-plugin scramble) (define/private (authenticate capabilities username password dbname auth-plugin scramble)
(let loop ([auth-plugin auth-plugin] [scramble scramble] [first? #t]) (let loop ([auth-plugin auth-plugin] [scramble scramble] [first? #t])
@ -276,7 +290,8 @@
(let ([wbox (and warnings? (box 0))]) (let ([wbox (and warnings? (box 0))])
(fresh-exchange) (fresh-exchange)
(query1:enqueue stmt cursor?) (query1:enqueue stmt cursor?)
(begin0 (query1:collect fsym stmt (not (string? stmt)) cursor? wbox) (begin0 (call-with-sync fsym
(lambda () (query1:collect fsym stmt (not (string? stmt)) cursor? wbox)))
(when (and warnings? (not (zero? (unbox wbox)))) (when (and warnings? (not (zero? (unbox wbox))))
(fetch-warnings fsym))))) (fetch-warnings fsym)))))
@ -307,11 +322,11 @@
[id (send pst get-handle)] [id (send pst get-handle)]
[params (statement-binding-params stmt)] [params (statement-binding-params stmt)]
[null-map (map sql-null? params)]) [null-map (map sql-null? params)])
(send-message (buffer-message
(let ([flags (if cursor? '(cursor/read-only) '())]) (let ([flags (if cursor? '(cursor/read-only) '())])
(make-execute-packet id flags null-map params))))] (make-execute-packet id flags null-map params))))]
[else ;; string [else ;; string
(send-message (make-command-packet 'query stmt))])) (buffer-message (make-command-packet 'query stmt))]))
;; query1:collect : symbol bool -> QueryResult stream ;; query1:collect : symbol bool -> QueryResult stream
(define/private (query1:collect fsym stmt binary? cursor? wbox) (define/private (query1:collect fsym stmt binary? cursor? wbox)
@ -379,8 +394,9 @@
[else [else
(let ([wbox (box 0)]) (let ([wbox (box 0)])
(fresh-exchange) (fresh-exchange)
(send-message (make-fetch-packet (send pst get-handle) fetch-size)) (buffer-message (make-fetch-packet (send pst get-handle) fetch-size))
(begin0 (query1:get-rows fsym field-dvecs #t wbox end-box) (begin0 (call-with-sync fsym
(lambda () (query1:get-rows fsym field-dvecs #t wbox end-box)))
(when (not (zero? (unbox wbox))) (when (not (zero? (unbox wbox)))
(fetch-warnings fsym))))])))))) (fetch-warnings fsym))))]))))))
@ -390,22 +406,24 @@
(define/override (prepare1* fsym stmt close-on-exec? stmt-type) (define/override (prepare1* fsym stmt close-on-exec? stmt-type)
(fresh-exchange) (fresh-exchange)
(send-message (make-command-packet 'statement-prepare stmt)) (buffer-message (make-command-packet 'statement-prepare stmt))
(let ([r (recv fsym 'prep-ok)]) (call-with-sync fsym
(match r (lambda ()
[(struct ok-prepared-statement-packet (id fields params)) (let ([r (recv fsym 'prep-ok)])
(let ([param-dvecs (match r
(if (zero? params) null (prepare1:get-field-descriptions fsym))] [(struct ok-prepared-statement-packet (id fields params))
[field-dvecs (let ([param-dvecs
(if (zero? fields) null (prepare1:get-field-descriptions fsym))]) (if (zero? params) null (prepare1:get-field-descriptions fsym))]
(new prepared-statement% [field-dvecs
(handle id) (if (zero? fields) null (prepare1:get-field-descriptions fsym))])
(close-on-exec? close-on-exec?) (new prepared-statement%
(param-typeids (map field-dvec->typeid param-dvecs)) (handle id)
(result-dvecs field-dvecs) (close-on-exec? close-on-exec?)
(stmt stmt) (param-typeids (map field-dvec->typeid param-dvecs))
(stmt-type stmt-type) (result-dvecs field-dvecs)
(owner this)))]))) (stmt stmt)
(stmt-type stmt-type)
(owner this)))])))))
(define/private (prepare1:get-field-descriptions fsym) (define/private (prepare1:get-field-descriptions fsym)
(let ([r (recv fsym 'field)]) (let ([r (recv fsym 'field)])

View File

@ -397,12 +397,7 @@ Based on protocol documentation here:
;; [inp (subport in len)] ;; [inp (subport in len)]
[bs (read-bytes len in)] [bs (read-bytes len in)]
[inp (open-input-bytes bs)] [inp (open-input-bytes bs)]
[msg [msg (parse-packet/1 inp expect len field-dvecs)])
(with-handlers ([exn?
(lambda (e)
(eprintf "packet was: ~s\n" (bytes->list bs))
(raise e))])
(parse-packet/1 inp expect len field-dvecs))])
(when (port-has-bytes? inp) (when (port-has-bytes? inp)
(error/internal 'parse-packet "bytes left over after parsing ~s; bytes were: ~s" (error/internal 'parse-packet "bytes left over after parsing ~s; bytes were: ~s"
msg (io:read-bytes-to-eof inp))) msg (io:read-bytes-to-eof inp)))

View File

@ -51,6 +51,9 @@
check-valid-tx-status check-valid-tx-status
check-statement/tx) check-statement/tx)
(define/override (on-break-within-lock)
(disconnect*))
(define/public (get-db fsym) (define/public (get-db fsym)
(unless db (unless db
(error/not-connected fsym)) (error/not-connected fsym))
@ -477,23 +480,25 @@
(vector name type size digits))))) (vector name type size digits)))))
(define/public (disconnect) (define/public (disconnect)
(define (go) (define (go) (disconnect*))
(start-atomic)
(let ([db* db]
[env* env])
(set! db #f)
(set! env #f)
(end-atomic)
(when db*
(let ([statements (hash-map statement-table (lambda (k v) k))])
(for ([pst (in-list statements)])
(free-statement* 'disconnect pst))
(handle-status 'disconnect (SQLDisconnect db*) db*)
(handle-status 'disconnect (SQLFreeHandle SQL_HANDLE_DBC db*))
(handle-status 'disconnect (SQLFreeHandle SQL_HANDLE_ENV env*))
(void)))))
(call-with-lock* 'disconnect go go #f)) (call-with-lock* 'disconnect go go #f))
(define/private (disconnect*)
(start-atomic)
(let ([db* db]
[env* env])
(set! db #f)
(set! env #f)
(end-atomic)
(when db*
(let ([statements (hash-map statement-table (lambda (k v) k))])
(for ([pst (in-list statements)])
(free-statement* 'disconnect pst))
(handle-status 'disconnect (SQLDisconnect db*) db*)
(handle-status 'disconnect (SQLFreeHandle SQL_HANDLE_DBC db*))
(handle-status 'disconnect (SQLFreeHandle SQL_HANDLE_ENV env*))
(void)))))
(define/public (get-base) this) (define/public (get-base) this)
(define/public (free-statement pst need-lock?) (define/public (free-statement pst need-lock?)

View File

@ -45,22 +45,58 @@
(super-new) (super-new)
;; with-disconnect-on-error
(define-syntax-rule (with-disconnect-on-error . body)
(with-handlers ([exn:fail? (lambda (e) (disconnect* #f) (raise e))])
. body))
;; ======================================== ;; ========================================
;; == Communication ;; == Communication
;; (Must be called with lock acquired.) #|
During initial setup, okay to send and recv directly, since reference
to connection does not escape to user. In particular, no danger of trying
to start a new exchange on top of an incomplete failed one.
;; raw-recv : -> message After initial setup, communication can only happen within lock, and any
(define/private (raw-recv) error (other than exn:fail:sql) that occurs between sending the message
(with-disconnect-on-error buffer (flush-message-buffer) and receiving the last message (recv-message)
(let ([r (parse-server-message inport)]) must cause the connection to disconnect. Such errors include communication
(dprintf " << ~s\n" r) errors and breaks.
r))) |#
;; message-buffer : reversed list of messages
(define message-buffer null)
;; fresh-exchange : -> void
(define/private (fresh-exchange)
(set! message-buffer null))
;; buffer-message : message -> void
(define/private (buffer-message msg)
(dprintf " >> ~s\n" msg)
(set! message-buffer (cons msg message-buffer)))
;; flush-message-buffer : -> void
(define/private (flush-message-buffer)
(for ([msg (in-list (reverse message-buffer))])
(write-message msg outport))
(set! message-buffer null)
(flush-output outport))
;; send-message : message -> void
(define/private (send-message msg)
(buffer-message msg)
(flush-message-buffer))
(define/private (call-with-sync fsym proc)
(buffer-message (make-Sync))
(with-handlers ([(lambda (e) #t)
(lambda (e)
;; Anything but exn:fail:sql (raised by recv-message) indicates
;; a communication error.
;; FIXME: alternatively, have check-ready-for-query set an ok flag
(unless (exn:fail:sql? e)
(disconnect* #f))
(raise e))])
(flush-message-buffer)
(begin0 (proc)
(check-ready-for-query fsym #f))))
;; recv-message : symbol -> message ;; recv-message : symbol -> message
(define/private (recv-message fsym) (define/private (recv-message fsym)
@ -75,21 +111,11 @@
(recv-message fsym)] (recv-message fsym)]
[else r]))) [else r])))
;; send-message : message -> void ;; raw-recv : -> message
(define/private (send-message msg) (define/private (raw-recv)
(buffer-message msg) (let ([r (parse-server-message inport)])
(flush-message-buffer)) (dprintf " << ~s\n" r)
r))
;; buffer-message : message -> void
(define/private (buffer-message msg)
(dprintf " >> ~s\n" msg)
(with-disconnect-on-error
(write-message msg outport)))
;; flush-message-buffer : -> void
(define/private (flush-message-buffer)
(with-disconnect-on-error
(flush-output outport)))
;; check-ready-for-query : symbol -> void ;; check-ready-for-query : symbol -> void
(define/private (check-ready-for-query fsym or-eof?) (define/private (check-ready-for-query fsym or-eof?)
@ -104,6 +130,9 @@
[(and or-eof? (eof-object? r)) (void)] [(and or-eof? (eof-object? r)) (void)]
[else (error/comm fsym "expected ready")]))) [else (error/comm fsym "expected ready")])))
(define/override (on-break-within-lock)
(disconnect* #f))
;; == Asynchronous messages ;; == Asynchronous messages
;; handle-async-message : message -> void ;; handle-async-message : message -> void
@ -128,32 +157,27 @@
;; == Connection management ;; == Connection management
;; disconnect : [boolean] -> (void) ;; disconnect : -> void
(define/public (disconnect) (define/public (disconnect)
(disconnect* #t)) (when (connected?)
(call-with-lock* 'disconnect
(lambda () (disconnect* #t))
(lambda () (disconnect* #f))
#f)))
;; disconnect* : boolean -> void ;; disconnect* : boolean -> void
(define/private (disconnect* no-lock-held?) (define/private (disconnect* politely?)
(define (go politely?) (dprintf " ** Disconnecting\n")
(dprintf " ** Disconnecting\n") (let ([outport* outport]
(let ([outport* outport] [inport* inport])
[inport* inport]) (when outport*
(when outport* (when politely?
(when politely? (send-message (make-Terminate)))
(send-message (make-Terminate))) (close-output-port outport*)
(close-output-port outport*) (set! outport #f))
(set! outport #f)) (when inport*
(when inport* (close-input-port inport*)
(close-input-port inport*) (set! inport #f))))
(set! inport #f))))
;; If we don't hold the lock, try to acquire it and disconnect politely.
;; Except, if already disconnected, no need to acquire lock.
(cond [(and no-lock-held? (connected?))
(call-with-lock* 'disconnect
(lambda () (go #t))
(lambda () (go #f))
#f)]
[else (go #f)]))
;; connected? : -> boolean ;; connected? : -> boolean
(define/override (connected?) (define/override (connected?)
@ -176,8 +200,7 @@
;; start-connection-protocol : string string string/#f -> void ;; start-connection-protocol : string string string/#f -> void
(define/public (start-connection-protocol dbname username password) (define/public (start-connection-protocol dbname username password)
(with-disconnect-on-error (call-with-lock 'postgresql-connect
(call-with-lock 'postgresql-connect
(lambda () (lambda ()
(send-message (send-message
(make-StartupMessage (make-StartupMessage
@ -185,7 +208,7 @@
(cons "database" dbname) (cons "database" dbname)
(cons "client_encoding" "UTF8") (cons "client_encoding" "UTF8")
(cons "DateStyle" "ISO, MDY")))) (cons "DateStyle" "ISO, MDY"))))
(connect:expect-auth username password))))) (connect:expect-auth username password))))
;; connect:expect-auth : string/#f -> ConnectionResult ;; connect:expect-auth : string/#f -> ConnectionResult
(define/private (connect:expect-auth username password) (define/private (connect:expect-auth username password)
@ -249,11 +272,12 @@
(define/private (query1 fsym stmt close-on-exec? cursor?) (define/private (query1 fsym stmt close-on-exec? cursor?)
;; if stmt is string, must take no params & results must be binary-readable ;; if stmt is string, must take no params & results must be binary-readable
(fresh-exchange)
(let* ([delenda (check/invalidate-cache stmt)] (let* ([delenda (check/invalidate-cache stmt)]
[portal (query1:enqueue delenda stmt close-on-exec? cursor?)]) [portal (query1:enqueue delenda stmt close-on-exec? cursor?)])
(send-message (make-Sync)) (call-with-sync fsym
(begin0 (query1:collect fsym delenda stmt portal (string? stmt) close-on-exec? cursor?) (lambda ()
(check-ready-for-query fsym #f)))) (query1:collect fsym delenda stmt portal (string? stmt) close-on-exec? cursor?)))))
;; check-statement : symbol statement -> statement-binding ;; check-statement : symbol statement -> statement-binding
;; Convert to statement-binding; need to prepare to get type information, used to ;; Convert to statement-binding; need to prepare to get type information, used to
@ -402,10 +426,11 @@
(lambda () (lambda ()
(cond [(unbox end-box) #f] (cond [(unbox end-box) #f]
[else [else
(fresh-exchange)
(buffer-message (make-Execute portal fetch-size)) (buffer-message (make-Execute portal fetch-size))
(send-message (make-Sync)) (let ([rows
(let ([rows (query1:data-loop fsym end-box)]) (call-with-sync fsym
(check-ready-for-query fsym #f) (lambda () (query1:data-loop fsym end-box)))])
(when (unbox end-box) (when (unbox end-box)
(cursor:close fsym pst portal)) (cursor:close fsym pst portal))
rows)])))]) rows)])))])
@ -413,13 +438,13 @@
(define/private (cursor:close fsym pst portal) (define/private (cursor:close fsym pst portal)
(let ([close-on-exec? (send pst get-close-on-exec?)]) (let ([close-on-exec? (send pst get-close-on-exec?)])
(fresh-exchange)
(buffer-message (make-Close 'portal portal)) (buffer-message (make-Close 'portal portal))
(when close-on-exec? (when close-on-exec?
(buffer-message (make-Close 'statement (send pst get-handle))) (buffer-message (make-Close 'statement (send pst get-handle)))
(send pst set-handle #f)) (send pst set-handle #f))
(send-message (make-Sync)) (call-with-sync fsym
(query1:expect-close-complete fsym close-on-exec?) (lambda () (query1:expect-close-complete fsym close-on-exec?)))))
(check-ready-for-query fsym #f)))
;; == Prepare ;; == Prepare
@ -428,10 +453,10 @@
(define/override (prepare1* fsym stmt close-on-exec? stmt-type) (define/override (prepare1* fsym stmt close-on-exec? stmt-type)
;; name generation within exchange: synchronized ;; name generation within exchange: synchronized
(let ([name (generate-name)]) (let ([name (generate-name)])
(fresh-exchange)
(prepare1:enqueue name stmt) (prepare1:enqueue name stmt)
(send-message (make-Sync)) (call-with-sync fsym
(begin0 (prepare1:collect fsym stmt name close-on-exec? stmt-type) (lambda () (prepare1:collect fsym stmt name close-on-exec? stmt-type)))))
(check-ready-for-query fsym #f))))
(define/private (prepare1:enqueue name stmt) (define/private (prepare1:enqueue name stmt)
(buffer-message (make-Parse name stmt null)) (buffer-message (make-Parse name stmt null))
@ -483,12 +508,13 @@
(let ([name (send pst get-handle)]) (let ([name (send pst get-handle)])
(when (and name outport) ;; outport = connected? (when (and name outport) ;; outport = connected?
(send pst set-handle #f) (send pst set-handle #f)
(fresh-exchange)
(buffer-message (make-Close 'statement name)) (buffer-message (make-Close 'statement name))
(buffer-message (make-Sync)) (call-with-sync 'free-statement
(let ([r (recv-message 'free-statement)]) (lambda ()
(cond [(CloseComplete? r) (void)] (let ([r (recv-message 'free-statement)])
[else (error/comm 'free-statement)]) (cond [(CloseComplete? r) (void)]
(check-ready-for-query 'free-statement #t))))) [else (error/comm 'free-statement)])))))))
(if need-lock? (if need-lock?
(call-with-lock* 'free-statement (call-with-lock* 'free-statement
do-free-statement do-free-statement

View File

@ -37,6 +37,9 @@
(define/override (call-with-lock fsym proc) (define/override (call-with-lock fsym proc)
(call-with-lock* fsym (lambda () (set! saved-tx-status (get-tx-status)) (proc)) #f #t)) (call-with-lock* fsym (lambda () (set! saved-tx-status (get-tx-status)) (proc)) #f #t))
(define/override (on-break-within-lock)
(disconnect*))
(define/private (get-db fsym) (define/private (get-db fsym)
(or -db (error/not-connected fsym))) (or -db (error/not-connected fsym)))
@ -194,19 +197,21 @@
pst))) pst)))
(define/public (disconnect) (define/public (disconnect)
(define (go) (define (go) (disconnect*))
(start-atomic)
(let ([db -db])
(set! -db #f)
(end-atomic)
(when db
(let ([statements (hash-map statement-table (lambda (k v) k))])
(for ([pst (in-list statements)])
(do-free-statement 'disconnect pst))
(HANDLE 'disconnect2 (sqlite3_close db))
(void)))))
(call-with-lock* 'disconnect go go #f)) (call-with-lock* 'disconnect go go #f))
(define/private (disconnect*)
(start-atomic)
(let ([db -db])
(set! -db #f)
(end-atomic)
(when db
(let ([statements (hash-map statement-table (lambda (k v) k))])
(for ([pst (in-list statements)])
(do-free-statement 'disconnect pst))
(HANDLE 'disconnect (sqlite3_close db))
(void)))))
(define/public (get-base) this) (define/public (get-base) this)
(define/public (free-statement pst need-lock?) (define/public (free-statement pst need-lock?)