diff --git a/collects/db/private/generic/interfaces.rkt b/collects/db/private/generic/interfaces.rkt index f9828cec83..29d0de5824 100644 --- a/collects/db/private/generic/interfaces.rkt +++ b/collects/db/private/generic/interfaces.rkt @@ -1,5 +1,6 @@ #lang racket/base -(require racket/class) +(require racket/class + racket/serialize) (provide connection<%> dbsystem<%> prepared-statement<%> @@ -115,8 +116,8 @@ ;; - (simple-result alist) ;; - (rows-result Header data) ;; for user-visible rows-results: headers present, data is (listof vector) -(struct simple-result (info) #:transparent) -(struct rows-result (headers rows) #:transparent) +(serializable-struct simple-result (info) #:transparent) +(serializable-struct rows-result (headers rows) #:transparent) ;; A cursor-result is ;; - (cursor-result Header prepared-statement ???) diff --git a/collects/db/scribblings/config.rkt b/collects/db/scribblings/config.rkt index e6dec4181a..2fd2f9d6d0 100644 --- a/collects/db/scribblings/config.rkt +++ b/collects/db/scribblings/config.rkt @@ -1,6 +1,8 @@ #lang racket/base (require scribble/manual scribble/eval + unstable/sandbox + racket/runtime-path (for-label racket/base racket/contract)) (provide (all-defined-out) @@ -18,18 +20,26 @@ ;; ---- -(define the-eval (make-base-eval)) -(void - (interaction-eval #:eval the-eval - (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))))) +#| +The log-based-eval should be run in an environment that defines +the DSN 'db-scribble-env as a PostgreSQL data source. +|# -(define-syntax-rule (examples/results [example result] ...) - (examples #:eval the-eval (eval:alts example result) ...)) -(define-syntax-rule (my-interaction [example result] ...) - (interaction #:eval the-eval (eval:alts example result) ...)) +(define-runtime-path example-log "example-log.rktd") +(define the-eval (make-log-based-eval example-log 'replay)) + +(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) ...)) diff --git a/collects/db/scribblings/connect.scrbl b/collects/db/scribblings/connect.scrbl index 72ec2fb9bf..b9e6760cde 100644 --- a/collects/db/scribblings/connect.scrbl +++ b/collects/db/scribblings/connect.scrbl @@ -100,7 +100,7 @@ Base connections are made using the following functions. If the connection cannot be made, an exception is raised. - @(examples/results + @fake-examples[ [(postgresql-connect #:server "db.mysite.com" #:port 5432 #: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)") #:user "me" #:database "me") - (new connection%)]) + (new connection%)]] } @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. - @(examples/results + @fake-examples[ [(mysql-connect #:server "db.mysite.com" #:port 3306 #:database "webappdb" @@ -181,7 +181,7 @@ Base connections are made using the following functions. [(mysql-connect #:socket (mysql-guess-socket-path) #:user "me" #:database "me") - (new connection%)]) + (new connection%)]] } @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. - @(examples/results + @fake-examples[ [(sqlite3-connect #:database "/path/to/my.db") (new connection%)] [(sqlite3-connect #:database "relpath/to/my.db" #:mode 'create) - (new connection%)]) + (new connection%)]] } @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 is called. -@examples/results[ -[(define pool - (connection-pool - (lambda () (displayln "connecting!") (sqlite3-connect ....)) - #:max-idle-connections 1)) - (void)] -[(define c1 (connection-pool-lease pool)) - (displayln "connecting!")] -[(define c2 (connection-pool-lease pool)) - (displayln "connecting!")] -[(disconnect c1) - (void)] -[(code:line (define c3 (connection-pool-lease pool)) (code:comment "reuses actual conn. from c1")) - (void)] +@examples[#:eval the-eval +(eval:alts + (define pool + (connection-pool + (lambda () (displayln "connecting!") (sqlite3-connect ....)) + #:max-idle-connections 1)) + (define pool + (connection-pool + (lambda () (displayln "connecting!") (sqlite3-connect #:database 'memory))))) +(define c1 (connection-pool-lease pool)) +(define c2 (connection-pool-lease pool)) +(disconnect c1) +(code:line (define c3 (connection-pool-lease pool)) (code:comment "reuses actual conn. from c1")) ] 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 with efficiency: -@examples/results[ -[(define the-connection - (virtual-connection (connection-pool (lambda () ....)))) - (void)] +@racketblock[ +(define the-connection + (virtual-connection (connection-pool (lambda () ....)))) ] 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 if a query function is executed. -@examples/results[ -[(define c +@examples[#:eval the-eval +(eval:alts + (define c (virtual-connection (lambda () (printf "connecting!\n") (postgresql-connect ....)))) - (void)] -[(connected? c) - (values #f)] -[(query-value c "select 1") - (begin (printf "connecting!\n") 1)] -[(connected? c) - (values #t)] -[(void (thread (lambda () (displayln (query-value c "select 2"))))) - (begin (printf "connecting!\n") (displayln 2))] -[(disconnect c) - (void)] -[(connected? c) - (values #f)] -[(query-value c "select 3") - (begin (printf "connecting!\n") 3)] + (define c + (virtual-connection + (lambda () + (printf "connecting!\n") + (dsn-connect 'db-scribble-env))))) +(connected? c) +(query-value c "select 1") +(connected? c) +(void (thread (lambda () (displayln (query-value c "select 2"))))) +(disconnect c) +(connected? c) +(query-value c "select 3") ] 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 @racket[virtual-statement]. -@examples/results[ -[(prepare c "select 2 + $1") - (error 'prepare "cannot prepare statement with virtual connection")] -[(query-value c "select 2 + $1" 2) - 4] -[(define pst (virtual-statement "select 2 + $1")) - (void)] -[(query-value c pst 3) - 5] +@examples[#:eval the-eval +(prepare c "select 2 + $1") +(query-value c "select 2 + $1" 2) +(define pst (virtual-statement "select 2 + $1")) +(query-value c pst 3) ] } +@(the-eval '(begin (set! c #f) (set! pst #f))) + @;{========================================} @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 @racket[data-source], then @racket[dsn-file] is ignored. -@examples/results[ +@fake-examples[ [(put-dsn 'pg (postgresql-data-source #:user "me" #:database "mydb" diff --git a/collects/db/scribblings/example-log.rktd b/collects/db/scribblings/example-log.rktd new file mode 100644 index 0000000000..81824e02ba --- /dev/null +++ b/collects/db/scribblings/example-log.rktd @@ -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): #")) + #"" + #"") +((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))) + #"" + #"") diff --git a/collects/db/scribblings/query.scrbl b/collects/db/scribblings/query.scrbl index 0de5852240..95485f5c6e 100644 --- a/collects/db/scribblings/query.scrbl +++ b/collects/db/scribblings/query.scrbl @@ -7,6 +7,12 @@ "tabbing.rkt" (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} @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. -@examples/results[ -[(query-exec c "insert into some_table values (1, 'a')") - (void)] -[(query-exec pgc "delete from some_table where n = $1" 42) - (void)] +@examples[#:eval the-eval +(query-exec pgc "insert into the_numbers values (42, 'the answer')") +(query-exec pgc "delete from the_numbers where n = $1" 42) ] } @@ -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 of rows (as vectors) from the query. -@examples/results[ -[(query-rows pgc "select * from the_numbers where n = $1" 2) - (list (vector 2 "company"))] -[(query-rows c "select 17") - (list (vector 17))] +@examples[#:eval the-eval +(query-rows pgc "select * from the_numbers where n = $1" 2) +(query-rows c "select 17") ] 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 column, and returns the list of values from the query. -@examples/results[ -[(query-list c "select n from the_numbers where n < 2") - (list 0 1)] -[(query-list c "select 'hello'") - (list "hello")] +@examples[#:eval the-eval +(query-list c "select n from the_numbers where n < 2") +(query-list c "select '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 returns its (single) row result as a vector. -@examples/results[ -[(query-row myc "select * from the_numbers where n = ?" 2) - (vector 2 "company")] -[(query-row c "select 17") - (vector 17)] +@examples[#:eval the-eval +(query-row pgc "select * from the_numbers where n = $1" 2) +(query-row pgc "select min(n), max(n) from the_numbers") ] } @@ -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 that case, @racket[#f] is returned. -@examples/results[ -[(query-maybe-row pgc "select * from the_numbers where n = $1" 100) - #f] -[(query-maybe-row c "select 17") - (vector 17)] +@examples[#:eval the-eval +(query-maybe-row pgc "select * from the_numbers where n = $1" 100) +(query-maybe-row c "select 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 one column, and returns its single value result. -@examples/results[ -[(query-value pgc "select timestamp 'epoch'") - (sql-timestamp 1970 1 1 0 0 0 0 #f)] -[(query-value pgc "select s from the_numbers where n = $1" 3) - "a crowd"] +@examples[#:eval the-eval +(query-value pgc "select timestamp 'epoch'") +(query-value pgc "select d from the_numbers where n = $1" 3) ] } @@ -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 that case, @racket[#f] is returned. -@examples/results[ -[(query-value myc "select s from some_table where n = ?" 100) - #f] -[(query-value c "select 17") - 17] +@examples[#:eval the-eval +(query-value pgc "select d from the_numbers where n = $1" 100) +(query-value c "select count(*) from the_numbers") ] } @@ -272,18 +264,15 @@ The types of parameters and returned fields are described in @racket[groupings] is not empty, then @racket[fetch-size] must be @racket[+inf.0]; otherwise, an exception is raised. -@examples/results[ -[(for/list ([n (in-query pgc "select n from the_numbers where n < 2")]) - n) - '(0 1)] -[(call-with-transaction pgc - (lambda () - (for ([(n d) - (in-query pgc "select * from the_numbers where n < $1" 4 - #:fetch 1)]) - (printf "~a is ~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"))] +@examples[#:eval the-eval +(for/list ([n (in-query pgc "select n from the_numbers where n < 2")]) + n) +(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)))) ] 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 based on the number of variables in the clause's left-hand side: -@examples/results[ -[(for ([n (in-query pgc "select * from the_numbers")]) - (displayln n)) - (error 'in-query "query returned 2 columns (expected 1): ~e" - "select * from the_numbers")] +@examples[#:eval the-eval +(for ([n (in-query pgc "select * from the_numbers")]) + (displayln n)) ] } @@ -490,16 +477,16 @@ closed. but it must be used with the same connection that created @racket[pst]. - @(examples/results - [(let* ([get-name-pst + @examples[#:eval the-eval + (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))) - (list "company" "a crowd")]) + [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))) + ] Most query functions perform the binding step implicitly. } @@ -526,19 +513,17 @@ closed. function variant allows the SQL syntax to be dynamically customized for the database system in use. -@examples/results[ -[(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")))))) - (void)] -[(query-list pgc pst 3) - (list 1 2)] -[(query-list slc pst 3) - (list 1 2)] +@examples[#:eval the-eval +(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")))))) +(query-list pgc pst 3) +(eval:alts (query-list slc pst 3) + (query-list pgc pst 3)) ] } @@ -748,13 +733,9 @@ type. @racket['message] key is typically present; its value is a string containing the error message. -@examples/results[ -[(with-handlers ([exn:fail:sql? exn:fail:sql-info]) - (query pgc "select * from nosuchtable")) - '((severity . "ERROR") - (code . "42P01") - (message . "relation \"nosuchtable\" does not exist") - ...)] +@examples[#:eval the-eval +(with-handlers ([exn:fail:sql? exn:fail:sql-info]) + (query pgc "select * from nosuchtable")) ] Errors originating from the @racketmodname[db] library, such as diff --git a/collects/db/scribblings/sql-types.scrbl b/collects/db/scribblings/sql-types.scrbl index c5bd8f1449..06b6ed2a62 100644 --- a/collects/db/scribblings/sql-types.scrbl +++ b/collects/db/scribblings/sql-types.scrbl @@ -16,21 +16,19 @@ Connections automatically convert query results to appropriate Racket types. Likewise, query parameters are accepted as Racket values and converted to the appropriate SQL type. -@examples/results[ -[(query-value pgc "select count(*) from the_numbers") 4] -[(query-value pgc "select false") (values #f)] -[(query-value pgc "select 1 + $1" 2) 3] +@examples[#:eval the-eval +(query-value pgc "select count(*) from the_numbers") +(query-value pgc "select false") +(query-value pgc "select 1 + $1" 2) ] 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 to a supported type: -@examples/results[ -[(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)") - "127.0.0.1/32"] +@examples[#:eval the-eval +(query-value pgc "select inet '127.0.0.1'") +(query-value pgc "select cast(inet '127.0.0.1' as varchar)") ] 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 to the same type. -@examples/results[ -[(query-value pgc "select real '+Infinity'") - +inf.0] -[(query-value pgc "select numeric '12345678901234567890'") - 12345678901234567890] +@examples[#:eval the-eval +(query-value pgc "select real '+Infinity'") +(query-value pgc "select numeric '12345678901234567890'") ] 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 constructing a SQL @tt{IN} expression: -@examples/results[ -[(query-value pgc "select 1 in (1, 2, 3)") #t] -[(query-value pgc "select 1 = any ($1::integer[])" - (list->pg-array (list 1 2 3))) - #t] +@examples[#:eval the-eval +(query-value pgc "select 1 in (1, 2, 3)") +(query-value pgc "select 1 = any ($1::integer[])" + (list->pg-array (list 1 2 3))) ] 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 left-hand side. -@examples/results[ -[(query-value pgc "select 1 = any ($1)" (list 1 2 3)) - #t] -[(query-value pgc "select $1::integer = any ($2)" - 1 (list 1 2 3)) - #t] -[(query-value pgc "select $1 = any ($2)" (code:comment "what type are we using?") - 1 (list 1 2 3)) - (error 'query-value "cannot convert to PostgreSQL string type: 1")] +@examples[#:eval the-eval +(query-value pgc "select 1 = any ($1)" (list 1 2 3)) +(query-value pgc "select $1::integer = any ($2)" + 1 (list 1 2 3)) +(query-value pgc "select $1 = any ($2)" (code:comment "what type are we using?") + 1 (list 1 2 3)) ] 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 is converted as @tt{real}, not @tt{integer}. -@examples/results[ +@fake-examples[ [(expt 2 80) (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 @racket[eq?]. -@(examples/results - [(query-value c "select NULL") - sql-null]) +@examples[#:eval the-eval +(query-value c "select NULL") +] } @defproc[(sql-null? [x any/c]) boolean?]{ @@ -410,25 +402,13 @@ values. support nanosecond precision; PostgreSQL, for example, only supports microsecond precision. -@(examples/results - [(query-value pgc "select date '25-dec-1980'") - (make-sql-date 1980 12 25)] - [(query-value pgc "select time '7:30'") - (make-sql-time 7 30 0 0 #f)] - [(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)] +@examples[#:eval the-eval +(query-value pgc "select date '25-dec-1980'") +(query-value pgc "select time '7:30'") +(query-value pgc "select timestamp 'epoch'") +(query-value pgc "select timestamp with time zone 'epoch'") ] +} @defstruct*[sql-interval ([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 string. -@examples/results[ -[(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 (string->sql-bits "010110111"))] +@examples[#:eval the-eval +(sql-bits->list (string->sql-bits "1011")) +(sql-bits->string (query-value pgc "select B'010110111'")) ] } diff --git a/collects/db/scribblings/using-db.scrbl b/collects/db/scribblings/using-db.scrbl index 863ecd3417..89ed619f1e 100644 --- a/collects/db/scribblings/using-db.scrbl +++ b/collects/db/scribblings/using-db.scrbl @@ -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 (@litchar{$1} rather than @litchar{?}). -@my-interaction[ -[(require db) - (void)] +@interaction[#:eval the-eval +(require db) ] First we create a connection. Replace @racket[_user], @racket[_db], and @racket[_password] below with the appropriate values for your configuration (see @secref{creating-connections} for other connection examples): -@my-interaction[ -[(define pgc +@interaction[#:eval the-eval +(eval:alts + (define pgc (postgresql-connect #:user _user #:database _db #:password _password)) - (void)] -[pgc - (new connection%)] + (define pgc (dsn-connect 'db-scribble-env))) ] Use @racket[query-exec] method to execute a SQL statement for effect. -@my-interaction[ -[(query-exec pgc - "create temporary table the_numbers (n integer, d varchar(20))") - (void)] -[(query-exec pgc - "insert into the_numbers values (0, 'nothing')") - (void)] -[(query-exec pgc - "insert into the_numbers values (1, 'the loneliest number')") - (void)] -[(query-exec pgc - "insert into the_numbers values (2, 'company')") - (void)] +@interaction[#:eval the-eval +(query-exec pgc + "create temporary table the_numbers (n integer, d varchar(20))") +(query-exec pgc + "insert into the_numbers values (0, 'nothing')") +(query-exec pgc + "insert into the_numbers values (1, 'the loneliest number')") +(query-exec pgc + "insert into the_numbers values (2, 'company')") ] 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 system to system and is subject to change.) -@my-interaction[ -[(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") - (rows-result - (list - '((name . "n") (typeid . 23)) - '((name . "d") (typeid . 1043))) - '(#(0 "nothing") #(2 "company")))] +@interaction[#:eval the-eval +(query pgc "insert into the_numbers values (3, 'a crowd')") +(query pgc "select n, d from the_numbers where n % 2 = 0") ] When the query is known to return rows and when the field descriptions are not needed, it is more convenient to use the @racket[query-rows] function. -@my-interaction[ -[(query-rows pgc "select n, d from the_numbers where n % 2 = 0") - '(#(0 "nothing") #(2 "company"))] +@interaction[#:eval the-eval +(query-rows pgc "select n, d from the_numbers where n % 2 = 0") ] Use @racket[query-row] for queries that are known to return exactly one row. -@my-interaction[ -[(query-row pgc "select * from the_numbers where n = 0") - (vector 0 "nothing")] +@interaction[#:eval the-eval +(query-row pgc "select * from the_numbers where n = 0") ] Similarly, use @racket[query-list] for queries that produce rows of exactly one column. -@my-interaction[ -[(query-list pgc "select d from the_numbers order by n") - (list "nothing" "the loneliest number" "company" "a crowd")] +@interaction[#:eval the-eval +(query-list pgc "select d from the_numbers order by n") ] When a query is known to return a single value (one row and one column), use @racket[query-value]. -@my-interaction[ -[(query-value pgc "select count(*) from the_numbers") - 4] -[(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")] +@interaction[#:eval the-eval +(query-value pgc "select count(*) from the_numbers") +(query-value pgc "select d from the_numbers where n = 5") ] When a query may return zero or one rows, as the last example, use @racket[query-maybe-row] or @racket[query-maybe-value] instead. -@my-interaction[ -[(query-maybe-value pgc "select d from the_numbers where n = 5") - (values #f)] +@interaction[#:eval the-eval +(query-maybe-value pgc "select d from the_numbers where n = 5") ] The @racket[in-query] function produces a sequence that can be used with Racket's iteration forms: -@my-interaction[ -[(for ([(n d) (in-query pgc "select * from the_numbers where n < 4")]) - (printf "~a is ~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"))] -[(for/fold ([sum 0]) ([n (in-query pgc "select n from the_numbers")]) - (+ sum n)) - (for/fold ([sum 0]) ([n (in-list '(0 1 2 3))]) - (+ sum n))] +@interaction[#:eval the-eval +(for ([(n d) (in-query pgc "select * from the_numbers where n < 4")]) + (printf "~a: ~a\n" n d)) +(for/fold ([sum 0]) ([n (in-query pgc "select n from the_numbers")]) + (+ sum n)) ] Errors in queries generally do not cause the connection to disconnect. -@my-interaction[ -[(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!'")) - (begin (display "query-value: relation \"nosuchtable\" does not exist (SQLSTATE 42P01)") - "okay to proceed!")] +@interaction[#:eval the-eval +(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!'")) ] Queries may contain parameters. The easiest way to execute a parameterized query is to provide the parameters ``inline'' after the SQL statement in the query function call. -@my-interaction[ -[(query-value pgc - "select d from the_numbers where n = $1" 2) - "company"] -[(query-list pgc - "select n from the_numbers where n > $1 and n < $2" 0 3) - (list 1 2)] +@interaction[#:eval the-eval +(query-value pgc + "select d from the_numbers where n = $1" 2) +(query-list pgc + "select n from the_numbers where n > $1 and n < $2" 0 3) ] Alternatively, a parameterized query may be prepared in advance and executed later. @tech{Prepared statements} can be executed multiple times with different parameter values. -@my-interaction[ -[(define get-less-than-pst - (prepare pgc "select n from the_numbers where n < $1")) - (void)] -[(query-list pgc get-less-than-pst 1) - (list 0)] -[(query-list pgc (bind-prepared-statement get-less-than-pst '(2))) - (list 0 1)] +@interaction[#:eval the-eval +(define get-less-than-pst + (prepare pgc "select n from the_numbers where n < $1")) +(query-list pgc get-less-than-pst 1) +(query-list pgc (bind-prepared-statement get-less-than-pst '(2))) ] When a connection's work is done, it should be disconnected. -@my-interaction[ -[(disconnect pgc) - (void)] +@;{ Don't actually disconnect; use this connection for the rest of the examples. } +@interaction[#:eval the-eval +(eval:alts (disconnect pgc) (void)) ] @@ -480,32 +448,3 @@ web-server By using a virtual connection backed by a connection pool, a servlet can achieve simplicity, isolation, and performance all at the same 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)] -] -} diff --git a/collects/db/scribblings/util.scrbl b/collects/db/scribblings/util.scrbl index 3e42c99f1c..84091adf20 100644 --- a/collects/db/scribblings/util.scrbl +++ b/collects/db/scribblings/util.scrbl @@ -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 fields. -@(examples/results - [(sql-datetime->srfi-date - (query-value pgc "select time '7:30'")) - (sql-datetime->srfi-date (make-sql-time 7 30 0 0 #f))] - [(sql-datetime->srfi-date - (query-value pgc "select date '25-dec-1980'")) - (sql-datetime->srfi-date - (make-sql-date 1980 12 25))] - [(sql-datetime->srfi-date - (query-value pgc "select timestamp 'epoch'")) - (sql-datetime->srfi-date (make-sql-timestamp 1970 1 1 0 0 0 0 #f))]) +@examples[#:eval the-eval +(sql-datetime->srfi-date + (query-value pgc "select time '7:30'")) +(sql-datetime->srfi-date + (query-value pgc "select date '25-dec-1980'")) +(sql-datetime->srfi-date + (query-value pgc "select timestamp 'epoch'")) +] } @defproc[(sql-day-time-interval->seconds [interval sql-day-time-interval?])