db: use log-based-eval for most examples

This commit is contained in:
Ryan Culpepper 2012-08-20 23:01:52 -04:00
parent 062a8ef5e7
commit 9bd5a9189b
8 changed files with 899 additions and 329 deletions

View File

@ -1,5 +1,6 @@
#lang racket/base #lang racket/base
(require racket/class) (require racket/class
racket/serialize)
(provide connection<%> (provide connection<%>
dbsystem<%> dbsystem<%>
prepared-statement<%> prepared-statement<%>
@ -115,8 +116,8 @@
;; - (simple-result alist) ;; - (simple-result alist)
;; - (rows-result Header data) ;; - (rows-result Header data)
;; for user-visible rows-results: headers present, data is (listof vector) ;; for user-visible rows-results: headers present, data is (listof vector)
(struct simple-result (info) #:transparent) (serializable-struct simple-result (info) #:transparent)
(struct rows-result (headers rows) #:transparent) (serializable-struct rows-result (headers rows) #:transparent)
;; A cursor-result is ;; A cursor-result is
;; - (cursor-result Header prepared-statement ???) ;; - (cursor-result Header prepared-statement ???)

View File

@ -1,6 +1,8 @@
#lang racket/base #lang racket/base
(require scribble/manual (require scribble/manual
scribble/eval scribble/eval
unstable/sandbox
racket/runtime-path
(for-label racket/base (for-label racket/base
racket/contract)) racket/contract))
(provide (all-defined-out) (provide (all-defined-out)
@ -18,18 +20,26 @@
;; ---- ;; ----
(define the-eval (make-base-eval)) #|
(void The log-based-eval should be run in an environment that defines
(interaction-eval #:eval the-eval the DSN 'db-scribble-env as a PostgreSQL data source.
(require racket/class |#
db/base
db/util/datetime))
(interaction-eval #:eval the-eval
(define connection% (class object% (super-new))))
(interaction-eval #:eval the-eval
(define connection-pool% (class object% (super-new)))))
(define-syntax-rule (examples/results [example result] ...) (define-runtime-path example-log "example-log.rktd")
(examples #:eval the-eval (eval:alts example result) ...)) (define the-eval (make-log-based-eval example-log 'replay))
(define-syntax-rule (my-interaction [example result] ...)
(interaction #:eval the-eval (eval:alts example result) ...)) (the-eval '(require racket/class
db
db/util/postgresql
db/util/datetime))
#|
The fake eval is for eg connection examples
|#
(define fake-eval (make-base-eval))
(fake-eval '(begin (require racket/class)
(define connection% (class object% (super-new)))))
(define-syntax-rule (fake-examples [example result] ...)
(examples #:eval fake-eval (eval:alts example result) ...))

View File

@ -100,7 +100,7 @@ Base connections are made using the following functions.
If the connection cannot be made, an exception is raised. If the connection cannot be made, an exception is raised.
@(examples/results @fake-examples[
[(postgresql-connect #:server "db.mysite.com" [(postgresql-connect #:server "db.mysite.com"
#:port 5432 #:port 5432
#:database "webappdb" #:database "webappdb"
@ -119,7 +119,7 @@ Base connections are made using the following functions.
[(postgresql-connect #:socket 'guess (code:comment "or (postgresql-guess-socket-path)") [(postgresql-connect #:socket 'guess (code:comment "or (postgresql-guess-socket-path)")
#:user "me" #:user "me"
#:database "me") #:database "me")
(new connection%)]) (new connection%)]]
} }
@defproc[(postgresql-guess-socket-path) @defproc[(postgresql-guess-socket-path)
@ -162,7 +162,7 @@ Base connections are made using the following functions.
If the connection cannot be made, an exception is raised. If the connection cannot be made, an exception is raised.
@(examples/results @fake-examples[
[(mysql-connect #:server "db.mysite.com" [(mysql-connect #:server "db.mysite.com"
#:port 3306 #:port 3306
#:database "webappdb" #:database "webappdb"
@ -181,7 +181,7 @@ Base connections are made using the following functions.
[(mysql-connect #:socket (mysql-guess-socket-path) [(mysql-connect #:socket (mysql-guess-socket-path)
#:user "me" #:user "me"
#:database "me") #:database "me")
(new connection%)]) (new connection%)]]
} }
@defproc[(mysql-guess-socket-path) @defproc[(mysql-guess-socket-path)
@ -234,12 +234,12 @@ Base connections are made using the following functions.
If the connection cannot be made, an exception is raised. If the connection cannot be made, an exception is raised.
@(examples/results @fake-examples[
[(sqlite3-connect #:database "/path/to/my.db") [(sqlite3-connect #:database "/path/to/my.db")
(new connection%)] (new connection%)]
[(sqlite3-connect #:database "relpath/to/my.db" [(sqlite3-connect #:database "relpath/to/my.db"
#:mode 'create) #:mode 'create)
(new connection%)]) (new connection%)]]
} }
@defproc[(odbc-connect [#:dsn dsn string?] @defproc[(odbc-connect [#:dsn dsn string?]
@ -348,20 +348,19 @@ Creates a @tech{connection pool}. The pool consists of up to
@racket[connect] function must return a fresh connection each time it @racket[connect] function must return a fresh connection each time it
is called. is called.
@examples/results[ @examples[#:eval the-eval
[(define pool (eval:alts
(define pool
(connection-pool (connection-pool
(lambda () (displayln "connecting!") (sqlite3-connect ....)) (lambda () (displayln "connecting!") (sqlite3-connect ....))
#:max-idle-connections 1)) #:max-idle-connections 1))
(void)] (define pool
[(define c1 (connection-pool-lease pool)) (connection-pool
(displayln "connecting!")] (lambda () (displayln "connecting!") (sqlite3-connect #:database 'memory)))))
[(define c2 (connection-pool-lease pool)) (define c1 (connection-pool-lease pool))
(displayln "connecting!")] (define c2 (connection-pool-lease pool))
[(disconnect c1) (disconnect c1)
(void)] (code:line (define c3 (connection-pool-lease pool)) (code:comment "reuses actual conn. from c1"))
[(code:line (define c3 (connection-pool-lease pool)) (code:comment "reuses actual conn. from c1"))
(void)]
] ]
See also @racket[virtual-connection] for a mechanism that eliminates See also @racket[virtual-connection] for a mechanism that eliminates
@ -434,10 +433,9 @@ closing its own connections. In particular, a @tech{virtual
connection} backed by a @tech{connection pool} combines convenience connection} backed by a @tech{connection pool} combines convenience
with efficiency: with efficiency:
@examples/results[ @racketblock[
[(define the-connection (define the-connection
(virtual-connection (connection-pool (lambda () ....)))) (virtual-connection (connection-pool (lambda () ....))))
(void)]
] ]
The resulting virtual connection leases a connection from the pool on The resulting virtual connection leases a connection from the pool on
@ -451,27 +449,25 @@ causes the current actual connection associated with the thread (if
there is one) to be disconnected, but the connection will be recreated there is one) to be disconnected, but the connection will be recreated
if a query function is executed. if a query function is executed.
@examples/results[ @examples[#:eval the-eval
[(define c (eval:alts
(define c
(virtual-connection (virtual-connection
(lambda () (lambda ()
(printf "connecting!\n") (printf "connecting!\n")
(postgresql-connect ....)))) (postgresql-connect ....))))
(void)] (define c
[(connected? c) (virtual-connection
(values #f)] (lambda ()
[(query-value c "select 1") (printf "connecting!\n")
(begin (printf "connecting!\n") 1)] (dsn-connect 'db-scribble-env)))))
[(connected? c) (connected? c)
(values #t)] (query-value c "select 1")
[(void (thread (lambda () (displayln (query-value c "select 2"))))) (connected? c)
(begin (printf "connecting!\n") (displayln 2))] (void (thread (lambda () (displayln (query-value c "select 2")))))
[(disconnect c) (disconnect c)
(void)] (connected? c)
[(connected? c) (query-value c "select 3")
(values #f)]
[(query-value c "select 3")
(begin (printf "connecting!\n") 3)]
] ]
Connections produced by @racket[virtual-connection] may not be used Connections produced by @racket[virtual-connection] may not be used
@ -479,18 +475,16 @@ with the @racket[prepare] function. However, they may still be used to
execute parameterized queries expressed as strings or encapsulated via execute parameterized queries expressed as strings or encapsulated via
@racket[virtual-statement]. @racket[virtual-statement].
@examples/results[ @examples[#:eval the-eval
[(prepare c "select 2 + $1") (prepare c "select 2 + $1")
(error 'prepare "cannot prepare statement with virtual connection")] (query-value c "select 2 + $1" 2)
[(query-value c "select 2 + $1" 2) (define pst (virtual-statement "select 2 + $1"))
4] (query-value c pst 3)
[(define pst (virtual-statement "select 2 + $1"))
(void)]
[(query-value c pst 3)
5]
] ]
} }
@(the-eval '(begin (set! c #f) (set! pst #f)))
@;{========================================} @;{========================================}
@section[#:tag "kill-safe"]{Kill-safe Connections} @section[#:tag "kill-safe"]{Kill-safe Connections}
@ -555,7 +549,7 @@ ODBC's DSNs.
for @racket[dsn], an exception is raised. If @racket[dsn] is a for @racket[dsn], an exception is raised. If @racket[dsn] is a
@racket[data-source], then @racket[dsn-file] is ignored. @racket[data-source], then @racket[dsn-file] is ignored.
@examples/results[ @fake-examples[
[(put-dsn 'pg [(put-dsn 'pg
(postgresql-data-source #:user "me" (postgresql-data-source #:user "me"
#:database "mydb" #:database "mydb"

View File

@ -0,0 +1,670 @@
;; This file was created by make-log-based-eval
((require racket/class db db/util/postgresql db/util/datetime)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((require db) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define pgc (dsn-connect 'db-scribble-env))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-exec
pgc
"create temporary table the_numbers (n integer, d varchar(20))")
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-exec pgc "insert into the_numbers values (0, 'nothing')")
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-exec pgc "insert into the_numbers values (1, 'the loneliest number')")
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-exec pgc "insert into the_numbers values (2, 'company')")
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query pgc "insert into the_numbers values (3, 'a crowd')")
((3)
1
(((lib "db/private/generic/interfaces.rkt")
.
deserialize-info:simple-result-v0))
0
()
()
(c values c (0 (c (c command u . "INSERT 0 1")))))
#""
#"")
((query pgc "select n, d from the_numbers where n % 2 = 0")
((3)
1
(((lib "db/private/generic/interfaces.rkt")
.
deserialize-info:rows-result-v0))
0
()
()
(c
values
c
(0
(c
(c
(c name u . "n")
c
(c typeid . 23)
c
(c type-size . 4)
c
(c type-mod . -1))
c
(c
(c name u . "d")
c
(c typeid . 1043)
c
(c type-size . -1)
c
(c type-mod . 24)))
(c (v! 0 (u . "nothing")) c (v! 2 (u . "company"))))))
#""
#"")
((query-rows pgc "select n, d from the_numbers where n % 2 = 0")
((3)
0
()
0
()
()
(c values c (c (v! 0 (u . "nothing")) c (v! 2 (u . "company")))))
#""
#"")
((query-row pgc "select * from the_numbers where n = 0")
((3) 0 () 0 () () (c values c (v! 0 (u . "nothing"))))
#""
#"")
((query-list pgc "select d from the_numbers order by n")
((3)
0
()
0
()
()
(c
values
c
(c
(u . "nothing")
c
(u . "the loneliest number")
c
(u . "company")
c
(u . "a crowd"))))
#""
#"")
((query-value pgc "select count(*) from the_numbers")
((3) 0 () 0 () () (c values c 4))
#""
#"")
((query-value pgc "select d from the_numbers where n = 5")
((3)
0
()
0
()
()
(c
exn
c
"query-value: query returned zero rows (expected 1): \"select d from the_numbers where n = 5\""))
#""
#"")
((query-maybe-value pgc "select d from the_numbers where n = 5")
((3) 0 () 0 () () (c values c #f))
#""
#"")
((for
(((n d) (in-query pgc "select * from the_numbers where n < 4")))
(printf "~a: ~a\n" n d))
((3) 0 () 0 () () (c values c (void)))
#"0: nothing\n1: the loneliest number\n2: company\n3: a crowd\n"
#"")
((for/fold
((sum 0))
((n (in-query pgc "select n from the_numbers")))
(+ sum n))
((3) 0 () 0 () () (c values c 6))
#""
#"")
((begin
(with-handlers
((exn:fail? (lambda (e) (printf "~a~n" (exn-message e)))))
(query-value pgc "select NoSuchField from NoSuchTable"))
(query-value pgc "select 'okay to proceed!'"))
((3) 0 () 0 () () (c values c (u . "okay to proceed!")))
#"query-value: relation \"nosuchtable\" does not exist (SQLSTATE 42P01)\n"
#"")
((query-value pgc "select d from the_numbers where n = $1" 2)
((3) 0 () 0 () () (c values c (u . "company")))
#""
#"")
((query-list pgc "select n from the_numbers where n > $1 and n < $2" 0 3)
((3) 0 () 0 () () (c values c (c 1 c 2)))
#""
#"")
((define get-less-than-pst
(prepare pgc "select n from the_numbers where n < $1"))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-list pgc get-less-than-pst 1)
((3) 0 () 0 () () (c values c (c 0)))
#""
#"")
((query-list pgc (bind-prepared-statement get-less-than-pst '(2)))
((3) 0 () 0 () () (c values c (c 0 c 1)))
#""
#"")
((void) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define pool
(connection-pool
(lambda ()
(displayln "connecting!")
(sqlite3-connect #:database 'memory))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define c1 (connection-pool-lease pool))
((3) 0 () 0 () () (c values c (void)))
#"connecting!\n"
#"")
((define c2 (connection-pool-lease pool))
((3) 0 () 0 () () (c values c (void)))
#"connecting!\n"
#"")
((disconnect c1) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((define c3 (connection-pool-lease pool))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define c
(virtual-connection
(lambda () (printf "connecting!\n") (dsn-connect 'db-scribble-env))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((connected? c) ((3) 0 () 0 () () (c values c #f)) #"" #"")
((query-value c "select 1")
((3) 0 () 0 () () (c values c 1))
#"connecting!\n"
#"")
((connected? c) ((3) 0 () 0 () () (c values c #t)) #"" #"")
((void (thread (lambda () (displayln (query-value c "select 2")))))
((3) 0 () 0 () () (c values c (void)))
#"connecting!\n2\n"
#"")
((disconnect c) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((connected? c) ((3) 0 () 0 () () (c values c #f)) #"" #"")
((query-value c "select 3")
((3) 0 () 0 () () (c values c 3))
#"connecting!\n"
#"")
((prepare c "select 2 + $1")
((3)
0
()
0
()
()
(c exn c "prepare: cannot prepare statement with virtual connection"))
#""
#"")
((query-value c "select 2 + $1" 2) ((3) 0 () 0 () () (c values c 4)) #"" #"")
((define pst (virtual-statement "select 2 + $1"))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-value c pst 3) ((3) 0 () 0 () () (c values c 5)) #"" #"")
((begin (set! c #f) (set! pst #f))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((define c pgc) ((3) 0 () 0 () () (c values c (void))) #"" #"")
((query-exec pgc "insert into the_numbers values (42, 'the answer')")
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-exec pgc "delete from the_numbers where n = $1" 42)
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-rows pgc "select * from the_numbers where n = $1" 2)
((3) 0 () 0 () () (c values c (c (v! 2 (u . "company")))))
#""
#"")
((query-rows c "select 17")
((3) 0 () 0 () () (c values c (c (v! 17))))
#""
#"")
((query-list c "select n from the_numbers where n < 2")
((3) 0 () 0 () () (c values c (c 0 c 1)))
#""
#"")
((query-list c "select 'hello'")
((3) 0 () 0 () () (c values c (c (u . "hello"))))
#""
#"")
((query-row pgc "select * from the_numbers where n = $1" 2)
((3) 0 () 0 () () (c values c (v! 2 (u . "company"))))
#""
#"")
((query-row pgc "select min(n), max(n) from the_numbers")
((3) 0 () 0 () () (c values c (v! 0 3)))
#""
#"")
((query-maybe-row pgc "select * from the_numbers where n = $1" 100)
((3) 0 () 0 () () (c values c #f))
#""
#"")
((query-maybe-row c "select 17")
((3) 0 () 0 () () (c values c (v! 17)))
#""
#"")
((query-value pgc "select timestamp 'epoch'")
((3)
1
(((lib "db/private/generic/sql-data.rkt")
.
deserialize-info:sql-timestamp-v0))
0
()
()
(c values c (0 1970 1 1 0 0 0 0 #f)))
#""
#"")
((query-value pgc "select d from the_numbers where n = $1" 3)
((3) 0 () 0 () () (c values c (u . "a crowd")))
#""
#"")
((query-value pgc "select d from the_numbers where n = $1" 100)
((3)
0
()
0
()
()
(c
exn
c
"query-value: query returned zero rows (expected 1): #<statement-binding>"))
#""
#"")
((query-value c "select count(*) from the_numbers")
((3) 0 () 0 () () (c values c 4))
#""
#"")
((for/list ((n (in-query pgc "select n from the_numbers where n < 2"))) n)
((3) 0 () 0 () () (c values c (c 0 c 1)))
#""
#"")
((call-with-transaction
pgc
(lambda ()
(for
(((n d)
(in-query pgc "select * from the_numbers where n < $1" 4 #:fetch 1)))
(printf "~a: ~a\n" n d))))
((3) 0 () 0 () () (c values c (void)))
#"0: nothing\n1: the loneliest number\n2: company\n3: a crowd\n"
#"")
((for ((n (in-query pgc "select * from the_numbers"))) (displayln n))
((3)
0
()
0
()
()
(c
exn
c
"in-query: query returned 2 columns (expected 1): \"select * from the_numbers\""))
#""
#"")
((define vehicles-result
(rows-result
'(((name . "type")) ((name . "maker")) ((name . "model")))
`(#("car" "honda" "civic")
#("car" "ford" "focus")
#("car" "ford" "pinto")
#("bike" "giant" "boulder")
#("bike" "schwinn" ,sql-null))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((group-rows vehicles-result #:group '(#("type")))
((3)
2
(((lib "db/private/generic/interfaces.rkt")
.
deserialize-info:rows-result-v0)
((lib "db/private/generic/sql-data.rkt") . deserialize-info:sql-null-v0))
1
("ford")
()
(c
values
c
(0
(c
(c (c name . "type"))
c
(c
(c name . "grouped")
c
(c grouped c (c (c name . "maker")) c (c (c name . "model")))))
(c
(v!
"car"
(c (v! "honda" "civic") c (v! (? . 0) "focus") c (v! (? . 0) "pinto")))
c
(v! "bike" (c (v! "giant" "boulder") c (v! "schwinn" (1))))))))
#""
#"")
((group-rows
vehicles-result
#:group
'(#("type") #("maker"))
#:group-mode
'(list))
((3)
1
(((lib "db/private/generic/interfaces.rkt")
.
deserialize-info:rows-result-v0))
1
((c name . "grouped"))
()
(c
values
c
(0
(c
(c (c name . "type"))
c
(c
(? . 0)
c
(c
grouped
c
(c (c name . "maker"))
c
(c (? . 0) c (c grouped c (c (c name . "model")))))))
(c
(v!
"car"
(c (v! "honda" (c "civic")) c (v! "ford" (c "focus" c "pinto"))))
c
(v! "bike" (c (v! "giant" (c "boulder")) c (v! "schwinn" ())))))))
#""
#"")
((rows->dict vehicles-result #:key "model" #:value '#("type" "maker"))
((3)
1
(((lib "db/private/generic/sql-data.rkt") . deserialize-info:sql-null-v0))
3
("car" "ford" "bike")
()
(c
values
c
(h
-
(equal)
((0) v! (? . 2) "schwinn")
("civic" v! (? . 0) "honda")
("pinto" v! (? . 0) (? . 1))
("focus" v! (? . 0) (? . 1))
("boulder" v! (? . 2) "giant"))))
#""
#"")
((rows->dict
vehicles-result
#:key
"maker"
#:value
"model"
#:value-mode
'(list))
((3)
0
()
0
()
()
(c
values
c
(h
-
(equal)
("ford" c "focus" c "pinto")
("honda" c "civic")
("giant" c "boulder")
("schwinn"))))
#""
#"")
((let* ((get-name-pst (prepare pgc "select d from the_numbers where n = $1"))
(get-name2 (bind-prepared-statement get-name-pst (list 2)))
(get-name3 (bind-prepared-statement get-name-pst (list 3))))
(list (query-value pgc get-name2) (query-value pgc get-name3)))
((3) 0 () 0 () () (c values c (c (u . "company") c (u . "a crowd"))))
#""
#"")
((define pst
(virtual-statement
(lambda (dbsys)
(case (dbsystem-name dbsys)
((postgresql) "select n from the_numbers where n < $1")
((sqlite3) "select n from the_numbers where n < ?")
(else (error "unknown system"))))))
((3) 0 () 0 () () (c values c (void)))
#""
#"")
((query-list pgc pst 3) ((3) 0 () 0 () () (c values c (c 0 c 1 c 2))) #"" #"")
((query-list pgc pst 3) ((3) 0 () 0 () () (c values c (c 0 c 1 c 2))) #"" #"")
((with-handlers
((exn:fail:sql? exn:fail:sql-info))
(query pgc "select * from nosuchtable"))
((3)
0
()
0
()
()
(c
values
c
(c
(c severity u . "ERROR")
c
(c code u . "42P01")
c
(c message u . "relation \"nosuchtable\" does not exist")
c
(c position u . "15")
c
(c file u . "parse_relation.c")
c
(c line u . "857")
c
(c routine u . "parserOpenTable"))))
#""
#"")
((query-value pgc "select count(*) from the_numbers")
((3) 0 () 0 () () (c values c 4))
#""
#"")
((query-value pgc "select false") ((3) 0 () 0 () () (c values c #f)) #"" #"")
((query-value pgc "select 1 + $1" 2) ((3) 0 () 0 () () (c values c 3)) #"" #"")
((query-value pgc "select inet '127.0.0.1'")
((3)
0
()
0
()
()
(c exn c "query-value: unsupported type: inet (typeid 869)"))
#""
#"")
((query-value pgc "select cast(inet '127.0.0.1' as varchar)")
((3) 0 () 0 () () (c values c (u . "127.0.0.1/32")))
#""
#"")
((query-value pgc "select real '+Infinity'")
((3) 0 () 0 () () (c values c +inf.0))
#""
#"")
((query-value pgc "select numeric '12345678901234567890'")
((3) 0 () 0 () () (c values c 12345678901234567890))
#""
#"")
((query-value pgc "select 1 in (1, 2, 3)")
((3) 0 () 0 () () (c values c #t))
#""
#"")
((query-value
pgc
"select 1 = any ($1::integer[])"
(list->pg-array (list 1 2 3)))
((3) 0 () 0 () () (c values c #t))
#""
#"")
((query-value pgc "select 1 = any ($1)" (list 1 2 3))
((3) 0 () 0 () () (c values c #t))
#""
#"")
((query-value pgc "select $1::integer = any ($2)" 1 (list 1 2 3))
((3) 0 () 0 () () (c values c #t))
#""
#"")
((query-value pgc "select $1 = any ($2)" 1 (list 1 2 3))
((3)
0
()
0
()
()
(c exn c "query-value: cannot convert to PostgreSQL string type: 1"))
#""
#"")
((query-value c "select NULL")
((3)
1
(((lib "db/private/generic/sql-data.rkt") . deserialize-info:sql-null-v0))
0
()
()
(c values c (0)))
#""
#"")
((sql-null->false "apple") ((3) 0 () 0 () () (c values c "apple")) #"" #"")
((sql-null->false sql-null) ((3) 0 () 0 () () (c values c #f)) #"" #"")
((sql-null->false #f) ((3) 0 () 0 () () (c values c #f)) #"" #"")
((false->sql-null "apple") ((3) 0 () 0 () () (c values c "apple")) #"" #"")
((false->sql-null #f)
((3)
1
(((lib "db/private/generic/sql-data.rkt") . deserialize-info:sql-null-v0))
0
()
()
(c values c (0)))
#""
#"")
((query-value pgc "select date '25-dec-1980'")
((3)
1
(((lib "db/private/generic/sql-data.rkt") . deserialize-info:sql-date-v0))
0
()
()
(c values c (0 1980 12 25)))
#""
#"")
((query-value pgc "select time '7:30'")
((3)
1
(((lib "db/private/generic/sql-data.rkt") . deserialize-info:sql-time-v0))
0
()
()
(c values c (0 7 30 0 0 #f)))
#""
#"")
((query-value pgc "select timestamp 'epoch'")
((3)
1
(((lib "db/private/generic/sql-data.rkt")
.
deserialize-info:sql-timestamp-v0))
0
()
()
(c values c (0 1970 1 1 0 0 0 0 #f)))
#""
#"")
((query-value pgc "select timestamp with time zone 'epoch'")
((3)
1
(((lib "db/private/generic/sql-data.rkt")
.
deserialize-info:sql-timestamp-v0))
0
()
()
(c values c (0 1969 12 31 19 0 0 0 -18000)))
#""
#"")
((sql-bits->list (string->sql-bits "1011"))
((3) 0 () 0 () () (c values c (c #t c #f c #t c #t)))
#""
#"")
((sql-bits->string (query-value pgc "select B'010110111'"))
((3) 0 () 0 () () (c values c (u . "010110111")))
#""
#"")
((sql-datetime->srfi-date (query-value pgc "select time '7:30'"))
((3)
1
(((lib "srfi/19/time.rkt") . deserialize-info:tm:date-v0))
0
()
()
(c values c (0 0 0 30 7 0 0 0 0)))
#""
#"")
((sql-datetime->srfi-date (query-value pgc "select date '25-dec-1980'"))
((3)
1
(((lib "srfi/19/time.rkt") . deserialize-info:tm:date-v0))
0
()
()
(c values c (0 0 0 0 0 25 12 1980 0)))
#""
#"")
((sql-datetime->srfi-date (query-value pgc "select timestamp 'epoch'"))
((3)
1
(((lib "srfi/19/time.rkt") . deserialize-info:tm:date-v0))
0
()
()
(c values c (0 0 0 0 0 1 1 1970 0)))
#""
#"")

View File

@ -7,6 +7,12 @@
"tabbing.rkt" "tabbing.rkt"
(for-label db db/util/geometry db/util/postgresql racket/dict)) (for-label db db/util/geometry db/util/postgresql racket/dict))
@;{ c - misc connection (alias to pgc)
myc - MySQL connection (???)
slc - SQLite connection (???)
}
@(the-eval '(define c pgc))
@title[#:tag "query-api"]{Queries} @title[#:tag "query-api"]{Queries}
@declare-exporting[db db/base #:use-sources (db/base)] @declare-exporting[db db/base #:use-sources (db/base)]
@ -126,11 +132,9 @@ The types of parameters and returned fields are described in
Executes a SQL statement for effect. Executes a SQL statement for effect.
@examples/results[ @examples[#:eval the-eval
[(query-exec c "insert into some_table values (1, 'a')") (query-exec pgc "insert into the_numbers values (42, 'the answer')")
(void)] (query-exec pgc "delete from the_numbers where n = $1" 42)
[(query-exec pgc "delete from some_table where n = $1" 42)
(void)]
] ]
} }
@ -150,11 +154,9 @@ The types of parameters and returned fields are described in
Executes a SQL query, which must produce rows, and returns the list Executes a SQL query, which must produce rows, and returns the list
of rows (as vectors) from the query. of rows (as vectors) from the query.
@examples/results[ @examples[#:eval the-eval
[(query-rows pgc "select * from the_numbers where n = $1" 2) (query-rows pgc "select * from the_numbers where n = $1" 2)
(list (vector 2 "company"))] (query-rows c "select 17")
[(query-rows c "select 17")
(list (vector 17))]
] ]
If @racket[groupings] is not empty, the result is the same as if If @racket[groupings] is not empty, the result is the same as if
@ -169,11 +171,9 @@ The types of parameters and returned fields are described in
Executes a SQL query, which must produce rows of exactly one Executes a SQL query, which must produce rows of exactly one
column, and returns the list of values from the query. column, and returns the list of values from the query.
@examples/results[ @examples[#:eval the-eval
[(query-list c "select n from the_numbers where n < 2") (query-list c "select n from the_numbers where n < 2")
(list 0 1)] (query-list c "select 'hello'")
[(query-list c "select 'hello'")
(list "hello")]
] ]
} }
@ -185,11 +185,9 @@ The types of parameters and returned fields are described in
Executes a SQL query, which must produce exactly one row, and Executes a SQL query, which must produce exactly one row, and
returns its (single) row result as a vector. returns its (single) row result as a vector.
@examples/results[ @examples[#:eval the-eval
[(query-row myc "select * from the_numbers where n = ?" 2) (query-row pgc "select * from the_numbers where n = $1" 2)
(vector 2 "company")] (query-row pgc "select min(n), max(n) from the_numbers")
[(query-row c "select 17")
(vector 17)]
] ]
} }
@ -201,11 +199,9 @@ The types of parameters and returned fields are described in
Like @racket[query-row], but the query may produce zero rows; in Like @racket[query-row], but the query may produce zero rows; in
that case, @racket[#f] is returned. that case, @racket[#f] is returned.
@examples/results[ @examples[#:eval the-eval
[(query-maybe-row pgc "select * from the_numbers where n = $1" 100) (query-maybe-row pgc "select * from the_numbers where n = $1" 100)
#f] (query-maybe-row c "select 17")
[(query-maybe-row c "select 17")
(vector 17)]
] ]
} }
@ -217,11 +213,9 @@ The types of parameters and returned fields are described in
Executes a SQL query, which must produce exactly one row of exactly Executes a SQL query, which must produce exactly one row of exactly
one column, and returns its single value result. one column, and returns its single value result.
@examples/results[ @examples[#:eval the-eval
[(query-value pgc "select timestamp 'epoch'") (query-value pgc "select timestamp 'epoch'")
(sql-timestamp 1970 1 1 0 0 0 0 #f)] (query-value pgc "select d from the_numbers where n = $1" 3)
[(query-value pgc "select s from the_numbers where n = $1" 3)
"a crowd"]
] ]
} }
@ -233,11 +227,9 @@ The types of parameters and returned fields are described in
Like @racket[query-value], but the query may produce zero rows; in Like @racket[query-value], but the query may produce zero rows; in
that case, @racket[#f] is returned. that case, @racket[#f] is returned.
@examples/results[ @examples[#:eval the-eval
[(query-value myc "select s from some_table where n = ?" 100) (query-value pgc "select d from the_numbers where n = $1" 100)
#f] (query-value c "select count(*) from the_numbers")
[(query-value c "select 17")
17]
] ]
} }
@ -272,18 +264,15 @@ The types of parameters and returned fields are described in
@racket[groupings] is not empty, then @racket[fetch-size] must @racket[groupings] is not empty, then @racket[fetch-size] must
be @racket[+inf.0]; otherwise, an exception is raised. be @racket[+inf.0]; otherwise, an exception is raised.
@examples/results[ @examples[#:eval the-eval
[(for/list ([n (in-query pgc "select n from the_numbers where n < 2")]) (for/list ([n (in-query pgc "select n from the_numbers where n < 2")])
n) n)
'(0 1)] (call-with-transaction pgc
[(call-with-transaction pgc
(lambda () (lambda ()
(for ([(n d) (for ([(n d)
(in-query pgc "select * from the_numbers where n < $1" 4 (in-query pgc "select * from the_numbers where n < $1" 4
#:fetch 1)]) #:fetch 1)])
(printf "~a is ~a\n" n d)))) (printf "~a: ~a\n" n d))))
(for-each (lambda (n d) (printf "~a: ~a\n" n d))
'(0 1 2 3) '("nothing" "the loneliest number" "company" "a crowd"))]
] ]
An @racket[in-query] application can provide better performance when An @racket[in-query] application can provide better performance when
@ -291,11 +280,9 @@ it appears directly in a @racket[for] clause. In addition, it may
perform stricter checks on the number of columns returned by the query perform stricter checks on the number of columns returned by the query
based on the number of variables in the clause's left-hand side: based on the number of variables in the clause's left-hand side:
@examples/results[ @examples[#:eval the-eval
[(for ([n (in-query pgc "select * from the_numbers")]) (for ([n (in-query pgc "select * from the_numbers")])
(displayln n)) (displayln n))
(error 'in-query "query returned 2 columns (expected 1): ~e"
"select * from the_numbers")]
] ]
} }
@ -490,8 +477,8 @@ closed.
but it must be used with the same connection that created but it must be used with the same connection that created
@racket[pst]. @racket[pst].
@(examples/results @examples[#:eval the-eval
[(let* ([get-name-pst (let* ([get-name-pst
(prepare pgc "select d from the_numbers where n = $1")] (prepare pgc "select d from the_numbers where n = $1")]
[get-name2 [get-name2
(bind-prepared-statement get-name-pst (list 2))] (bind-prepared-statement get-name-pst (list 2))]
@ -499,7 +486,7 @@ closed.
(bind-prepared-statement get-name-pst (list 3))]) (bind-prepared-statement get-name-pst (list 3))])
(list (query-value pgc get-name2) (list (query-value pgc get-name2)
(query-value pgc get-name3))) (query-value pgc get-name3)))
(list "company" "a crowd")]) ]
Most query functions perform the binding step implicitly. Most query functions perform the binding step implicitly.
} }
@ -526,19 +513,17 @@ closed.
function variant allows the SQL syntax to be dynamically customized function variant allows the SQL syntax to be dynamically customized
for the database system in use. for the database system in use.
@examples/results[ @examples[#:eval the-eval
[(define pst (define pst
(virtual-statement (virtual-statement
(lambda (dbsys) (lambda (dbsys)
(case (dbsystem-name dbsys) (case (dbsystem-name dbsys)
((postgresql) "select n from the_numbers where n < $1") ((postgresql) "select n from the_numbers where n < $1")
((sqlite3) "select n from the_numbers where n < ?") ((sqlite3) "select n from the_numbers where n < ?")
(else (error "unknown system")))))) (else (error "unknown system"))))))
(void)] (query-list pgc pst 3)
[(query-list pgc pst 3) (eval:alts (query-list slc pst 3)
(list 1 2)] (query-list pgc pst 3))
[(query-list slc pst 3)
(list 1 2)]
] ]
} }
@ -748,13 +733,9 @@ type.
@racket['message] key is typically present; its value is a string @racket['message] key is typically present; its value is a string
containing the error message. containing the error message.
@examples/results[ @examples[#:eval the-eval
[(with-handlers ([exn:fail:sql? exn:fail:sql-info]) (with-handlers ([exn:fail:sql? exn:fail:sql-info])
(query pgc "select * from nosuchtable")) (query pgc "select * from nosuchtable"))
'((severity . "ERROR")
(code . "42P01")
(message . "relation \"nosuchtable\" does not exist")
...)]
] ]
Errors originating from the @racketmodname[db] library, such as Errors originating from the @racketmodname[db] library, such as

View File

@ -16,21 +16,19 @@ Connections automatically convert query results to appropriate Racket
types. Likewise, query parameters are accepted as Racket values and types. Likewise, query parameters are accepted as Racket values and
converted to the appropriate SQL type. converted to the appropriate SQL type.
@examples/results[ @examples[#:eval the-eval
[(query-value pgc "select count(*) from the_numbers") 4] (query-value pgc "select count(*) from the_numbers")
[(query-value pgc "select false") (values #f)] (query-value pgc "select false")
[(query-value pgc "select 1 + $1" 2) 3] (query-value pgc "select 1 + $1" 2)
] ]
If a query result contains a column with a SQL type not supported by If a query result contains a column with a SQL type not supported by
this library, an exception is raised. As a workaround, cast the column this library, an exception is raised. As a workaround, cast the column
to a supported type: to a supported type:
@examples/results[ @examples[#:eval the-eval
[(query-value pgc "select inet '127.0.0.1'") (query-value pgc "select inet '127.0.0.1'")
(error 'query-value "unsupported type: inet (typeid 869)")] (query-value pgc "select cast(inet '127.0.0.1' as varchar)")
[(query-value pgc "select cast(inet '127.0.0.1' as varchar)")
"127.0.0.1/32"]
] ]
The exception for unsupported types in result columns is raised when The exception for unsupported types in result columns is raised when
@ -100,11 +98,9 @@ lower precision.) Other real values are converted to decimals with a
loss of precision. In PostgreSQL, @tt{numeric} and @tt{decimal} refer loss of precision. In PostgreSQL, @tt{numeric} and @tt{decimal} refer
to the same type. to the same type.
@examples/results[ @examples[#:eval the-eval
[(query-value pgc "select real '+Infinity'") (query-value pgc "select real '+Infinity'")
+inf.0] (query-value pgc "select numeric '12345678901234567890'")
[(query-value pgc "select numeric '12345678901234567890'")
12345678901234567890]
] ]
The geometric types such as @racket['point] are represented by The geometric types such as @racket['point] are represented by
@ -128,11 +124,10 @@ the
@tt{= ANY}} syntax with an array parameter instead of dynamically @tt{= ANY}} syntax with an array parameter instead of dynamically
constructing a SQL @tt{IN} expression: constructing a SQL @tt{IN} expression:
@examples/results[ @examples[#:eval the-eval
[(query-value pgc "select 1 in (1, 2, 3)") #t] (query-value pgc "select 1 in (1, 2, 3)")
[(query-value pgc "select 1 = any ($1::integer[])" (query-value pgc "select 1 = any ($1::integer[])"
(list->pg-array (list 1 2 3))) (list->pg-array (list 1 2 3)))
#t]
] ]
A list may be provided for an array parameter, in which case it is A list may be provided for an array parameter, in which case it is
@ -140,15 +135,12 @@ automatically converted using @racket[list->pg-array]. The type
annotation can be dropped when the array type can be inferred from the annotation can be dropped when the array type can be inferred from the
left-hand side. left-hand side.
@examples/results[ @examples[#:eval the-eval
[(query-value pgc "select 1 = any ($1)" (list 1 2 3)) (query-value pgc "select 1 = any ($1)" (list 1 2 3))
#t] (query-value pgc "select $1::integer = any ($2)"
[(query-value pgc "select $1::integer = any ($2)"
1 (list 1 2 3)) 1 (list 1 2 3))
#t] (query-value pgc "select $1 = any ($2)" (code:comment "what type are we using?")
[(query-value pgc "select $1 = any ($2)" (code:comment "what type are we using?")
1 (list 1 2 3)) 1 (list 1 2 3))
(error 'query-value "cannot convert to PostgreSQL string type: 1")]
] ]
PostgreSQL defines many other types, such as network addresses and row PostgreSQL defines many other types, such as network addresses and row
@ -247,7 +239,7 @@ strings, bytes, and real numbers.
An exact integer that cannot be represented as a 64-bit signed integer An exact integer that cannot be represented as a 64-bit signed integer
is converted as @tt{real}, not @tt{integer}. is converted as @tt{real}, not @tt{integer}.
@examples/results[ @fake-examples[
[(expt 2 80) [(expt 2 80)
(expt 2 80)] (expt 2 80)]
[(query-value slc "select ?" (expt 2 80)) [(query-value slc "select ?" (expt 2 80))
@ -326,9 +318,9 @@ SQL @tt{NULL} is translated into the unique @racket[sql-null] value.
results. The @racket[sql-null] value may be recognized using results. The @racket[sql-null] value may be recognized using
@racket[eq?]. @racket[eq?].
@(examples/results @examples[#:eval the-eval
[(query-value c "select NULL") (query-value c "select NULL")
sql-null]) ]
} }
@defproc[(sql-null? [x any/c]) boolean?]{ @defproc[(sql-null? [x any/c]) boolean?]{
@ -410,25 +402,13 @@ values.
support nanosecond precision; PostgreSQL, for example, only supports support nanosecond precision; PostgreSQL, for example, only supports
microsecond precision. microsecond precision.
@(examples/results @examples[#:eval the-eval
[(query-value pgc "select date '25-dec-1980'") (query-value pgc "select date '25-dec-1980'")
(make-sql-date 1980 12 25)] (query-value pgc "select time '7:30'")
[(query-value pgc "select time '7:30'") (query-value pgc "select timestamp 'epoch'")
(make-sql-time 7 30 0 0 #f)] (query-value pgc "select timestamp with time zone 'epoch'")
[(query-value pgc "select timestamp 'epoch'")
(make-sql-timestamp 1970 1 1 0 0 0 0 #f)]
[(query-value pgc "select timestamp with time zone 'epoch'")
(make-sql-timestamp 1969 12 31 19 0 0 0 -18000)])
}
@examples/results[
[(query-value myc "select date('1980-12-25')")
(make-sql-date 1980 12 25)]
[(query-value myc "select time('7:30')")
(make-sql-time 7 30 0 0 #f)]
[(query-value myc "select from_unixtime(0)")
(make-sql-timestamp 1969 12 31 19 0 0 0 #f)]
] ]
}
@defstruct*[sql-interval @defstruct*[sql-interval
([years exact-integer?] ([years exact-integer?]
@ -550,10 +530,8 @@ represented by sql-bits values.
Converts a sql-bits value to or from its representation as a list or Converts a sql-bits value to or from its representation as a list or
string. string.
@examples/results[ @examples[#:eval the-eval
[(sql-bits->list (string->sql-bits "1011")) (sql-bits->list (string->sql-bits "1011"))
(sql-bits->list (string->sql-bits "1011"))] (sql-bits->string (query-value pgc "select B'010110111'"))
[(sql-bits->string (query-value pgc "select B'010110111'"))
(sql-bits->string (string->sql-bits "010110111"))]
] ]
} }

View File

@ -20,40 +20,34 @@ database and perform simple queries. Some of the SQL syntax used below
is PostgreSQL-specific, such as the syntax of query parameters is PostgreSQL-specific, such as the syntax of query parameters
(@litchar{$1} rather than @litchar{?}). (@litchar{$1} rather than @litchar{?}).
@my-interaction[ @interaction[#:eval the-eval
[(require db) (require db)
(void)]
] ]
First we create a connection. Replace @racket[_user], @racket[_db], First we create a connection. Replace @racket[_user], @racket[_db],
and @racket[_password] below with the appropriate values for your and @racket[_password] below with the appropriate values for your
configuration (see @secref{creating-connections} for other connection examples): configuration (see @secref{creating-connections} for other connection examples):
@my-interaction[ @interaction[#:eval the-eval
[(define pgc (eval:alts
(define pgc
(postgresql-connect #:user _user (postgresql-connect #:user _user
#:database _db #:database _db
#:password _password)) #:password _password))
(void)] (define pgc (dsn-connect 'db-scribble-env)))
[pgc
(new connection%)]
] ]
Use @racket[query-exec] method to execute a SQL statement for effect. Use @racket[query-exec] method to execute a SQL statement for effect.
@my-interaction[ @interaction[#:eval the-eval
[(query-exec pgc (query-exec pgc
"create temporary table the_numbers (n integer, d varchar(20))") "create temporary table the_numbers (n integer, d varchar(20))")
(void)] (query-exec pgc
[(query-exec pgc
"insert into the_numbers values (0, 'nothing')") "insert into the_numbers values (0, 'nothing')")
(void)] (query-exec pgc
[(query-exec pgc
"insert into the_numbers values (1, 'the loneliest number')") "insert into the_numbers values (1, 'the loneliest number')")
(void)] (query-exec pgc
[(query-exec pgc
"insert into the_numbers values (2, 'company')") "insert into the_numbers values (2, 'company')")
(void)]
] ]
The @racket[query] function is a more general way to execute a The @racket[query] function is a more general way to execute a
@ -61,121 +55,95 @@ statement. It returns a structure encapsulating information about the
statement's execution. (But some of that information varies from statement's execution. (But some of that information varies from
system to system and is subject to change.) system to system and is subject to change.)
@my-interaction[ @interaction[#:eval the-eval
[(query pgc "insert into the_numbers values (3, 'a crowd')") (query pgc "insert into the_numbers values (3, 'a crowd')")
(simple-result '((command insert 0 1)))] (query pgc "select n, d from the_numbers where n % 2 = 0")
[(query pgc "select n, d from the_numbers where n % 2 = 0")
(rows-result
(list
'((name . "n") (typeid . 23))
'((name . "d") (typeid . 1043)))
'(#(0 "nothing") #(2 "company")))]
] ]
When the query is known to return rows and when the field When the query is known to return rows and when the field
descriptions are not needed, it is more convenient to use the descriptions are not needed, it is more convenient to use the
@racket[query-rows] function. @racket[query-rows] function.
@my-interaction[ @interaction[#:eval the-eval
[(query-rows pgc "select n, d from the_numbers where n % 2 = 0") (query-rows pgc "select n, d from the_numbers where n % 2 = 0")
'(#(0 "nothing") #(2 "company"))]
] ]
Use @racket[query-row] for queries that are known to return exactly Use @racket[query-row] for queries that are known to return exactly
one row. one row.
@my-interaction[ @interaction[#:eval the-eval
[(query-row pgc "select * from the_numbers where n = 0") (query-row pgc "select * from the_numbers where n = 0")
(vector 0 "nothing")]
] ]
Similarly, use @racket[query-list] for queries that produce rows of Similarly, use @racket[query-list] for queries that produce rows of
exactly one column. exactly one column.
@my-interaction[ @interaction[#:eval the-eval
[(query-list pgc "select d from the_numbers order by n") (query-list pgc "select d from the_numbers order by n")
(list "nothing" "the loneliest number" "company" "a crowd")]
] ]
When a query is known to return a single value (one row and one When a query is known to return a single value (one row and one
column), use @racket[query-value]. column), use @racket[query-value].
@my-interaction[ @interaction[#:eval the-eval
[(query-value pgc "select count(*) from the_numbers") (query-value pgc "select count(*) from the_numbers")
4] (query-value pgc "select d from the_numbers where n = 5")
[(query-value pgc "select d from the_numbers where n = 5")
(error 'query-value
"query returned zero rows: ~s"
"select d from the_numbers where n = 5")]
] ]
When a query may return zero or one rows, as the last example, use When a query may return zero or one rows, as the last example, use
@racket[query-maybe-row] or @racket[query-maybe-value] instead. @racket[query-maybe-row] or @racket[query-maybe-value] instead.
@my-interaction[ @interaction[#:eval the-eval
[(query-maybe-value pgc "select d from the_numbers where n = 5") (query-maybe-value pgc "select d from the_numbers where n = 5")
(values #f)]
] ]
The @racket[in-query] function produces a sequence that can be used The @racket[in-query] function produces a sequence that can be used
with Racket's iteration forms: with Racket's iteration forms:
@my-interaction[ @interaction[#:eval the-eval
[(for ([(n d) (in-query pgc "select * from the_numbers where n < 4")]) (for ([(n d) (in-query pgc "select * from the_numbers where n < 4")])
(printf "~a is ~a\n" n d)) (printf "~a: ~a\n" n d))
(for-each (lambda (n d) (printf "~a: ~a\n" n d)) (for/fold ([sum 0]) ([n (in-query pgc "select n from the_numbers")])
'(0 1 2 3)
'("nothing" "the loneliest number" "company" "a crowd"))]
[(for/fold ([sum 0]) ([n (in-query pgc "select n from the_numbers")])
(+ sum n)) (+ sum n))
(for/fold ([sum 0]) ([n (in-list '(0 1 2 3))])
(+ sum n))]
] ]
Errors in queries generally do not cause the connection to disconnect. Errors in queries generally do not cause the connection to disconnect.
@my-interaction[ @interaction[#:eval the-eval
[(begin (with-handlers [(exn:fail? (begin (with-handlers [(exn:fail?
(lambda (e) (lambda (e)
(printf "~a~n" (exn-message e))))] (printf "~a~n" (exn-message e))))]
(query-value pgc "select NoSuchField from NoSuchTable")) (query-value pgc "select NoSuchField from NoSuchTable"))
(query-value pgc "select 'okay to proceed!'")) (query-value pgc "select 'okay to proceed!'"))
(begin (display "query-value: relation \"nosuchtable\" does not exist (SQLSTATE 42P01)")
"okay to proceed!")]
] ]
Queries may contain parameters. The easiest way to execute a Queries may contain parameters. The easiest way to execute a
parameterized query is to provide the parameters ``inline'' after the parameterized query is to provide the parameters ``inline'' after the
SQL statement in the query function call. SQL statement in the query function call.
@my-interaction[ @interaction[#:eval the-eval
[(query-value pgc (query-value pgc
"select d from the_numbers where n = $1" 2) "select d from the_numbers where n = $1" 2)
"company"] (query-list pgc
[(query-list pgc
"select n from the_numbers where n > $1 and n < $2" 0 3) "select n from the_numbers where n > $1 and n < $2" 0 3)
(list 1 2)]
] ]
Alternatively, a parameterized query may be prepared in advance and Alternatively, a parameterized query may be prepared in advance and
executed later. @tech{Prepared statements} can be executed multiple executed later. @tech{Prepared statements} can be executed multiple
times with different parameter values. times with different parameter values.
@my-interaction[ @interaction[#:eval the-eval
[(define get-less-than-pst (define get-less-than-pst
(prepare pgc "select n from the_numbers where n < $1")) (prepare pgc "select n from the_numbers where n < $1"))
(void)] (query-list pgc get-less-than-pst 1)
[(query-list pgc get-less-than-pst 1) (query-list pgc (bind-prepared-statement get-less-than-pst '(2)))
(list 0)]
[(query-list pgc (bind-prepared-statement get-less-than-pst '(2)))
(list 0 1)]
] ]
When a connection's work is done, it should be disconnected. When a connection's work is done, it should be disconnected.
@my-interaction[ @;{ Don't actually disconnect; use this connection for the rest of the examples. }
[(disconnect pgc) @interaction[#:eval the-eval
(void)] (eval:alts (disconnect pgc) (void))
] ]
@ -480,32 +448,3 @@ web-server
By using a virtual connection backed by a connection pool, a servlet By using a virtual connection backed by a connection pool, a servlet
can achieve simplicity, isolation, and performance all at the same can achieve simplicity, isolation, and performance all at the same
time. time.
@;{
TODO:
- talk about virtual statements, too
- show actual working servlet code
--
A prepared statement is tied to the connection used to create it;
attempting to use it with another connection results in an
error. Unfortunately, in some scenarios such as web servlets, the
lifetimes of connections are short or difficult to track, making
prepared statements inconvenient. In such cases, a better tool is the
@tech{virtual statement}, which prepares statements on demand and
caches them for future use with the same connection.
@my-interaction[
[(define get-less-than-pst
(virtual-statement "select n from the_numbers where n < $1"))
(void)]
[(code:line (query-list pgc1 get-less-than-pst 1) (code:comment "prepares statement for pgc1"))
(list 0)]
[(code:line (query-list pgc2 get-less-than-pst 2) (code:comment "prepares statement for pgc2"))
(list 0 1)]
[(code:line (query-list pgc1 get-less-than-pst 3) (code:comment "uses existing prep. stmt."))
(list 0 1 2)]
]
}

View File

@ -37,17 +37,14 @@ modules below, not by @racketmodname[db] or @racketmodname[db/base].
SRFI date, for example, puts zeroes in the year, month, and day SRFI date, for example, puts zeroes in the year, month, and day
fields. fields.
@(examples/results @examples[#:eval the-eval
[(sql-datetime->srfi-date (sql-datetime->srfi-date
(query-value pgc "select time '7:30'")) (query-value pgc "select time '7:30'"))
(sql-datetime->srfi-date (make-sql-time 7 30 0 0 #f))] (sql-datetime->srfi-date
[(sql-datetime->srfi-date
(query-value pgc "select date '25-dec-1980'")) (query-value pgc "select date '25-dec-1980'"))
(sql-datetime->srfi-date (sql-datetime->srfi-date
(make-sql-date 1980 12 25))]
[(sql-datetime->srfi-date
(query-value pgc "select timestamp 'epoch'")) (query-value pgc "select timestamp 'epoch'"))
(sql-datetime->srfi-date (make-sql-timestamp 1970 1 1 0 0 0 0 #f))]) ]
} }
@defproc[(sql-day-time-interval->seconds [interval sql-day-time-interval?]) @defproc[(sql-day-time-interval->seconds [interval sql-day-time-interval?])