whalesong/js-assembler/db-cache.rkt

140 lines
4.0 KiB
Racket

#lang racket/base
(require (planet ryanc/db)
(prefix-in whalesong: "../version.rkt")
racket/file
racket/path
file/md5
file/gzip
file/gunzip
racket/contract)
(provide cached? save-in-cache!)
;; Contracts are off because when I dynamic-require, I can't
;; dynamic require the syntaxes exposed by the contract.
#;(provide/contract
[cached? (path? . -> . (or/c false/c bytes?))]
[save-in-cache! (path? bytes? . -> . any)])
(define cache-directory-path
(build-path (find-system-path 'pref-dir)
"whalesong"))
;; create-cache-directory!: -> void
(define (create-cache-directory!)
(unless (directory-exists? cache-directory-path)
(make-directory* cache-directory-path)))
;; clear-cache-files!: -> void
;; Remove all the cache files.
(define (clear-cache-files!)
(for ([file (directory-list cache-directory-path)])
(when (file-exists? (build-path cache-directory-path file))
(with-handlers ([exn:fail? void])
(delete-file (build-path cache-directory-path file))))))
(define (ensure-cache-db-structure!)
(when (not (file-exists? whalesong-cache.sqlite3))
;; Clear existing cache files: they're obsolete.
(clear-cache-files!)
(define conn
(sqlite3-connect #:database whalesong-cache.sqlite3
#:mode 'create))
(query-exec conn
(string-append
"create table cache(path string not null primary key, "
" md5sum string not null, "
"data blob not null);"))
(query-exec conn
"CREATE INDEX cache_md5sum_idx ON cache (md5sum);")
(disconnect conn)))
(define whalesong-cache.sqlite3
(build-path cache-directory-path
(format "whalesong-cache-~a.sqlite"
whalesong:version)))
(create-cache-directory!)
(ensure-cache-db-structure!)
(define conn
(sqlite3-connect #:database whalesong-cache.sqlite3))
(define lookup-cache-stmt
(prepare conn (string-append "select path, md5sum, data "
"from cache "
"where path=? and md5sum=?")))
(define delete-cache-stmt
(prepare conn (string-append "delete from cache "
"where path=?")))
(define insert-cache-stmt
(prepare conn (string-append "insert into cache(path, md5sum, data)"
" values (?, ?, ?);")))
;; cached?: path -> (U false bytes)
;; Returns a true value, (vector path md5-signature data), if we can
;; find an appropriate entry in the cache, and false otherwise.
(define (cached? path)
(cond
[(file-exists? path)
(define maybe-row
(query-maybe-row conn
lookup-cache-stmt
(path->string path)
(call-with-input-file* path md5)))
(cond
[maybe-row
(vector-ref maybe-row 2) #;(gunzip-content (vector-ref maybe-row 2))]
[else
#f])]
[else
#f]))
;; save-in-cache!: path bytes -> void
;; Saves a record.
(define (save-in-cache! path data)
(cond
[(file-exists? path)
(define signature (call-with-input-file* path md5))
;; Make sure there's a unique row/column by deleting
;; any row with the same key.
(query-exec conn delete-cache-stmt (path->string path))
(query-exec conn insert-cache-stmt
(path->string path)
signature
data #;(gzip-content data))]
[else
(error 'save-in-cache! "File ~e does not exist" path)]))
;; gzip-content: bytes -> bytes
(define (gzip-content content)
(define op (open-output-bytes))
(gzip-through-ports (open-input-bytes content)
op
#f
0)
(get-output-bytes op))
;; gunzip-content: bytes -> bytes
(define (gunzip-content content)
(define op (open-output-bytes))
(gunzip-through-ports (open-input-bytes content)
op)
(get-output-bytes op))