
Other major changes: - pg code now uses only binary format - pg timestamptz now always UTC (tz = 0), added doc section - added contracts to most pg "can't-convert" errors
193 lines
5.7 KiB
Racket
193 lines
5.7 KiB
Racket
#lang racket/base
|
|
(require racket/class
|
|
"../generic/interfaces.rkt"
|
|
"../generic/common.rkt"
|
|
"../generic/sql-data.rkt"
|
|
"../generic/sql-convert.rkt")
|
|
(provide dbsystem
|
|
field-dvec->field-info
|
|
field-dvec->typeid
|
|
supported-typeid?
|
|
classify-odbc-sql)
|
|
|
|
(define odbc-dbsystem%
|
|
(class* dbsystem-base% (dbsystem<%>)
|
|
(define/public (get-short-name) 'odbc)
|
|
(define/override (get-type-list) type-list)
|
|
(define/public (has-support? x) #f)
|
|
|
|
(define/public (get-parameter-handlers param-typeids)
|
|
(map get-check param-typeids))
|
|
|
|
(define/public (field-dvecs->typeids dvecs)
|
|
(map (lambda (dvec) (vector-ref dvec 1)) dvecs))
|
|
|
|
(define/public (describe-params typeids)
|
|
(map describe-typeid typeids))
|
|
|
|
(define/public (describe-fields dvecs)
|
|
(for/list ([dvec (in-list dvecs)])
|
|
(describe-typeid (field-dvec->typeid dvec))))
|
|
|
|
(super-new)))
|
|
|
|
(define dbsystem
|
|
(new odbc-dbsystem%))
|
|
|
|
;; ----
|
|
|
|
(define (field-dvec->field-info dvec)
|
|
`((name . ,(vector-ref dvec 0))
|
|
(typeid . ,(vector-ref dvec 1))
|
|
(size . ,(vector-ref dvec 2))
|
|
(digits . ,(vector-ref dvec 3))))
|
|
|
|
(define (field-dvec->typeid dvec)
|
|
(vector-ref dvec 1))
|
|
|
|
;; ----
|
|
|
|
;; SQL "parsing"
|
|
;; We just care about detecting commands that affect transaction status.
|
|
|
|
;; Since we have no idea what the actual database system is, just cover
|
|
;; standard commands and assume DDL is not transactional.
|
|
|
|
;; classify-odbc-sql : string [nat] -> symbol/#f
|
|
(define classify-odbc-sql
|
|
(make-sql-classifier #:hash-comments? #t
|
|
'(;; Explicit transaction commands
|
|
("ROLLBACK TRANSACTION TO" rollback-savepoint)
|
|
("ROLLBACK WORK TO" rollback-savepoint)
|
|
("ROLLBACK TO" rollback-savepoint)
|
|
("RELEASE" release-savepoint)
|
|
("SAVEPOINT" savepoint)
|
|
("START" start)
|
|
("BEGIN" start)
|
|
("COMMIT" commit)
|
|
("END" commit)
|
|
("ROLLBACK" rollback) ;; Note: after ROLLBACK TO, etc
|
|
|
|
;; Implicit commit
|
|
("ALTER" implicit-commit)
|
|
("CREATE" implicit-commit)
|
|
("DROP" implicit-commit)
|
|
("GRANT" implicit-commit)
|
|
("RENAME" implicit-commit)
|
|
("TRUNCATE" implicit-commit))))
|
|
|
|
;; ----
|
|
|
|
(define-syntax-rule
|
|
(defchecks get-check [(typeid name pred ...) ...] [(*typeid *name *fun) ...])
|
|
(define get-check
|
|
(let ([name (mk-check typeid (lambda (z) (or (pred z) ...)) #:contract-parts '(pred ...))] ...
|
|
[*name *fun] ...)
|
|
(lambda (x)
|
|
(case x
|
|
((typeid) name) ...
|
|
((*typeid) *name) ...
|
|
(else
|
|
(lambda (fsym index param)
|
|
(error/unsupported-type fsym x))))))))
|
|
|
|
(define (mk-check typeid pred #:contract-parts [ctc-parts #f])
|
|
(lambda (fsym param)
|
|
(unless (pred param)
|
|
(error/no-convert fsym "ODBC" (typeid->type typeid) param
|
|
#:contract (cond [(= (length ctc-parts) 1)
|
|
(car ctc-parts)]
|
|
[else (cons 'or/c ctc-parts)])))
|
|
param))
|
|
|
|
(define (check-numeric fsym param)
|
|
(define (bad note)
|
|
(error/no-convert fsym "ODBC" "numeric" param note))
|
|
(unless (rational? param) (bad ""))
|
|
(let ([scaled (exact->scaled-integer (inexact->exact param))])
|
|
(unless scaled (bad ""))
|
|
(let ([ma (car scaled)]
|
|
[ex (cdr scaled)])
|
|
;; check (abs ma) fits in 16*8 bits, ex fits in char
|
|
(unless (<= -128 ex 127) (bad "(scale too large)"))
|
|
(unless (< (abs ma) (expt 2 (* 16 8))) (bad "(mantissa too long)"))
|
|
(cons ma ex))))
|
|
|
|
(defchecks get-check
|
|
[(0 unknown string? bytes? rational? boolean? sql-date? sql-time? sql-timestamp?)
|
|
(1 character string?)
|
|
(4 integer int32?)
|
|
(5 smallint int16?)
|
|
(6 float real?)
|
|
(7 real real?)
|
|
(8 double real?)
|
|
(9 datetime sql-timestamp?)
|
|
(12 varchar string?)
|
|
(91 date sql-date?)
|
|
(92 time sql-time?)
|
|
(93 timestamp sql-timestamp?)
|
|
(-1 longvarchar string?)
|
|
(-2 binary bytes?)
|
|
(-3 varbinary bytes?)
|
|
(-4 longvarbinary bytes?)
|
|
(-5 bigint int64?)
|
|
(-6 tinyint int8?)
|
|
(-7 bit1 boolean?)
|
|
(-8 wcharacter string?)
|
|
(-9 wvarchar string?)
|
|
(-10 wlongvarchar string?)]
|
|
[(2 numeric check-numeric)
|
|
(3 decimal check-numeric)])
|
|
|
|
;; ----
|
|
|
|
(define-type-table (type-list
|
|
typeid->type
|
|
describe-typeid)
|
|
|
|
(0 unknown 0)
|
|
(1 character 0)
|
|
(2 numeric 0)
|
|
(3 decimal 0)
|
|
(4 integer 0)
|
|
(5 smallint 0)
|
|
(6 float 0)
|
|
(7 real 0)
|
|
(8 double 0)
|
|
(9 datetime 0)
|
|
(12 varchar 0)
|
|
(91 date 0)
|
|
(92 time 0)
|
|
(93 timestamp 0)
|
|
(-1 longvarchar 0)
|
|
(-2 binary 0)
|
|
(-3 varbinary 0)
|
|
(-4 longvarbinary 0)
|
|
(-5 bigint 0)
|
|
(-6 tinyint 0)
|
|
(-7 bit1 0) ;; not bit(n), always single bit
|
|
(-8 wchar 0)
|
|
(-9 wvarchar 0)
|
|
(-10 wlongvarchar 0)
|
|
|
|
;; Unsupported types
|
|
|
|
(101 interval-year #f)
|
|
(102 interval-month #f)
|
|
(103 interval-day #f)
|
|
(104 interval-hour #f)
|
|
(105 interval-minute #f)
|
|
(106 interval-second #f)
|
|
(107 interval-year-month #f)
|
|
(108 interval-day-hour #f)
|
|
(109 interval-day-minute #f)
|
|
(110 interval-day-second #f)
|
|
(111 interval-hour-minute #f)
|
|
(112 interval-hour-second #f)
|
|
(113 interval-minute-second #f))
|
|
|
|
(define (supported-typeid? x)
|
|
(case x
|
|
((0 1 2 3 4 5 6 7 8 9 12 91 92 93 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10) #t)
|
|
(else #f)))
|