(module postgresql8 mzscheme
(require (only (lib "class.ss") send)
(lib "kw.ss")
(lib "pregexp.ss")
(lib "unitsig.ss")
(lib "cut.ss" "srfi" "26"))
(require (prefix postgresql: (planet "spgsql.ss" ("schematics" "spgsql.plt" 2)))
(planet "debug.ss" ("untyped" "unlib.plt" 2))
(planet "gen.ss" ("untyped" "unlib.plt" 2))
(planet "profile.ss" ("untyped" "unlib.plt" 2))
(planet "symbol.ss" ("untyped" "unlib.plt" 2)))
(require (file "../generic/util.ss")
(file "../base.ss")
(file "../db-sig.ss")
(prefix era: (file "../era.ss"))
(file "../query-core.ss")
(file "../transaction.ss")
(file "../type.ss")
(file "extract.ss")
(file "sql.ss"))
(provide config?
config-server
config-port
config-database
config-username
config-password
config-ssl
config-ssl-encrypt
(rename create-config make-config)
db@)
(define-struct config (server port database username password ssl ssl-encrypt))
(define create-config
(lambda/kw (server port database username #:optional [password #f] #:key [ssl 'yes] [ssl-encrypt 'sslv2-or-v3])
(make-config server port database username password ssl ssl-encrypt)))
(define db@
(unit/sig db^
(import)
(define (connect config)
(with-snooze-reraise (exn:fail? "Could not connect to database")
(let* ([server (config-server config)]
[port (config-port config)]
[database (config-database config)]
[username (config-username config)]
[password (config-password config)]
[ssl (config-ssl config)]
[ssl-encrypt (config-ssl-encrypt config)]
[conn (postgresql:connect #:server server
#:port port
#:database database
#:user username
#:password password
#:ssl ssl
#:ssl-encrypt ssl-encrypt)])
(send conn exec "SET DATESTYLE TO ISO;")
conn)))
(define (disconnect conn)
(with-snooze-reraise (exn:fail? "Could not disconnect from database")
(send conn disconnect)))
(define (create-table conn entity)
(with-snooze-reraise (exn:fail? (format "Could not create table for ~a" entity))
(for-each (cut send conn exec <>)
(map (cut string-append <> ";")
(pregexp-split #px";" (create-sql entity))))))
(define (drop-table conn entity)
(with-snooze-reraise (exn:fail? (format "Could not drop table for ~a" entity))
(for-each (cut send conn exec <>)
(map (cut string-append <> ";")
(pregexp-split #px";" (drop-sql entity))))))
(define (insert-record conn struct)
(with-snooze-reraise (exn:fail? (format "Could not insert database record for ~a" struct))
(let ([sequence-name (symbol-append (era:entity-name (era:struct-entity struct)) '-seq)])
(send conn exec (insert-sql struct))
(unquote-data type:id (send conn query-value (string-append "SELECT currval('" (quote-id sequence-name) "');"))))))
(define (update-record conn struct)
(with-snooze-reraise (exn:fail? (format "Could not update database record for ~a" struct))
(send conn exec (update-sql struct)))
(void))
(define (delete-record conn entity id)
(with-snooze-reraise (exn:fail? (format "Could not delete database record for ~a ~a" entity id))
(send conn exec (delete-sql entity id)))
(void))
(define (find-gen conn select)
(define sql (select-sql select))
(with-snooze-reraise (exn:fail? (format "Could not execute SELECT query:~n~a" (select-sql select)))
(g:map (make-struct-extractor (select-what-entities select) (select-single-item? select))
(g:map (make-data-unquoter (select-what-types select))
(list->generator (send conn map sql vector))))))
(define current-savepoints (make-parameter null))
(define (call-with-transaction conn body)
(let* ([frame (make-frame)]
[savepoint (gensym 'save)]
[old-savepoints (current-savepoints)]
[quoted-savepoint (quote-id savepoint)]
[body-complete-cell (make-thread-cell #f)] [transaction-complete-cell (make-thread-cell #f)]) (parameterize ([current-savepoints (cons savepoint old-savepoints)])
(dynamic-wind
(lambda ()
(if (thread-cell-ref transaction-complete-cell)
(raise-exn exn:fail:snooze:transaction
"Transaction block was interrupted and cannot be resumed.")
(begin
(when (null? old-savepoints)
(send conn exec "BEGIN;"))
(send conn exec (string-append "SAVEPOINT " quoted-savepoint ";")))))
(lambda ()
(call-with-frame frame
(lambda ()
(begin0 (body)
(thread-cell-set! body-complete-cell #t)))))
(lambda ()
(if (thread-cell-ref transaction-complete-cell)
(raise-exn exn:fail:snooze:transaction
"Transaction block was interrupted and resumed.")
(begin
(if (thread-cell-ref body-complete-cell)
(send conn exec (string-append "RELEASE SAVEPOINT " quoted-savepoint ";"))
(begin
(send conn exec (string-append "ROLLBACK TO SAVEPOINT " quoted-savepoint ";"))
(roll-back-frame! frame)))
(thread-cell-set! transaction-complete-cell #t)
(when (null? old-savepoints)
(send conn exec "COMMIT;")))))))))
(define (dump-sql select output-port format)
(fprintf output-port format (select-sql select))
select)
))
)