16 May 2004 Kevin Rosenberg (kevin@rosenberg.net)
+ * db-oracle/oracle-api: Add OCIServerVersion
+ * db-oracle/oracle-sql: Query and store server version on connect
+ * sql/db-interface.lisp: Add new db-type-has-bigint? generic
+ function to handle OCI's lack of bigint support
+ * test/test-basic.lisp: Separate bigint testing
* test/test-utils.lisp: Add oracle to specs and list of backends
* doc/TODO: New file
* test/test-fdml.lisp: Added FDML/SELECT/34 to test
if [ -z "$ORACLE_HOME" ]; then
- ORACLE_HOME=/opt/10g/product/10.1.0/db_1
+ ORACLE_HOME=/10g/app/product/10.1.0/db_1
fi
EMPTY_LIBS=-lclntst10
(p2 :unsigned-short))
+(def-oci-routine ("OCIServerVersion" oci-server-version)
+ :int
+ (handlp (* :void))
+ (errhp (* :void))
+ (bufp (* :unsigned-char))
+ (bufsz :int)
+ (hndltype :short))
+
;;; Functions
(defun oci-init (&key (mode +oci-default+))
;;;; *************************************************************************
;;;; FILE IDENTIFICATION
;;;;
-;;;; Name: oracle-objects.lisp
+;;;; Name: oracle-objects.lisp
;;;;
;;;; $Id$
;;;;
(or (first args) 38) (or (second args) 38))
"NUMBER"))
+(defmethod database-get-type-specifier
+ ((type (eql 'boolean)) args (database oracle-database))
+ (declare (ignore args))
+ "CHAR(1)")
+
(defmethod read-sql-value (val type (database oracle-database))
;;(format t "value is \"~A\" of type ~A~%" val (type-of val))
(declare (ignore type))
(defmethod read-sql-value (val (type (eql 'float)) (database oracle-database))
val)
-;;; LOCAL-TIME stuff that needs to go into hooks
-#+local-time
-(defmethod clsql::database-get-type-specifier
- ((type (eql 'local-time::local-time)) args (database oracle-database))
+(defmethod read-sql-value (val (type (eql 'boolean)) (database oracle-database))
+ (when (char-equal #\t (schar val 0))
+ t))
+
+(defmethod database-get-type-specifier
+ ((type (eql 'wall-time)) args (database oracle-database))
(declare (ignore args))
"DATE")
-#+local-time
-(defmethod clsql::database-get-type-specifier
- ((type (eql 'local-time::duration))
+(defmethod database-get-type-specifier
+ ((type (eql 'duration))
args
(database oracle-database))
(declare (ignore args))
(defpackage #:clsql-oracle
(:use #:common-lisp #:clsql-sys)
(:export #:oracle-database
+ #:*oracle-server-version*
#:*oracle-so-load-path*
#:*oracle-so-libraries*)
(:documentation "This is the CLSQL interface to Oracle."))
(in-package #:clsql-oracle)
+(defvar *oracle-server-version* nil
+ "Version string of Oracle server.")
+
(defmethod database-initialize-database-type
((database-type (eql :oracle)))
t)
; (sql:query "select '',OWNER,TABLE_NAME,TABLE_TYPE,'' from all_catalog"))
-(defmethod list-all-user-database-tables ((db oracle-database))
- (unless db
- (setf db clsql:*default-database*))
+(defmethod database-list-tables ((db oracle-database) &key owner)
(values (database-query "select TABLE_NAME from all_catalog
- where owner <> 'PUBLIC' and owner <> 'SYSTEM' and owner <> 'SYS'"
+ where owner not in ('PUBLIC','SYSTEM','SYS','WMSYS','EXFSYS','CTXSYS','WKSYS','WK_TEST','MDSYS','DMSYS','OLAPSYS','ORDSYS','XDB')"
db nil nil)))
-(defmethod database-list-tables ((database oracle-database)
+(defmethod database-list-views ((database oracle-database)
&key (system-tables nil) owner)
(if system-tables
(database-query "select table_name from all_catalog" database nil nil)
- (database-query "select table_name from all_catalog where owner <> 'PUBLIC' and owner <> 'SYSTEM' and owner <> 'SYS'"
+ (database-query "select table_name from all_catalog where owner != 'PUBLIC' and owner != 'SYSTEM' and owner != 'SYS'"
database nil nil)))
;; Return a list of all columns in TABLE.
(mapcar #'car
(database-query
(format nil
- "select user_tab_columns,column_name from user_tab_columns where user_tab_columns.table_name=~A"
+ "select user_tab_columns,column_name from user_tab_columns where user_tab_columns.table_name='~A'"
relname)
database nil nil))))
database
(unless (eq types :auto)
(error "unsupported TYPES value"))
- (uffi:with-foreign-objects ((dtype :unsigned-short)
+ (uffi:with-foreign-objects ((dtype-foreign :unsigned-short)
(parmdp (* :void))
(precision :byte)
(scale :byte)
;; handle in Lisp.
(oci-attr-get (uffi:deref-pointer parmdp void-pointer)
+oci-dtype-param+
- dtype
+ dtype-foreign
(uffi:make-null-pointer :int) +oci-attr-data-type+
(uffi:deref-pointer errhp void-pointer))
- (case dtype
- (#.SQLT-DATE
- (setf buffer (acquire-foreign-resource :char (* 32 +n-buf-rows+)))
- (setf sizeof 32 dtype #.SQLT-STR))
- (2 ;; number
- ;;(oci-attr-get parmdp +oci-dtype-param+
- ;;(addr precision) nil +oci-attr-precision+
- ;;(uffi:deref-pointer errhp))
- (oci-attr-get (uffi:deref-pointer parmdp void-pointer)
- +oci-dtype-param+
- scale
- (uffi:make-null-pointer :int) +oci-attr-scale+
- (uffi:deref-pointer errhp void-pointer))
- (cond
- ((zerop scale)
- (setf buffer (acquire-foreign-resource :init +n-buf-rows+)
- sizeof 4 ;; sizeof(int)
- dtype #.SQLT-INT))
- (t
- (setf buffer (acquire-foreign-resource :double +n-buf-rows+)
- sizeof 8 ;; sizeof(double)
- dtype #.SQLT-FLT))))
- (t ; Default to SQL-STR
- (setf (uffi:deref-pointer colsize :unsigned-long) 0
- dtype #.SQLT-STR)
- (oci-attr-get (uffi:deref-pointer parmdp void-pointer)
- +oci-dtype-param+
- colsize
- (uffi:make-null-pointer :int) ;; (uffi:pointer-address colsizesize)
- +oci-attr-data-size+
- (uffi:deref-pointer errhp void-pointer))
- (let ((colsize-including-null (1+ (uffi:deref-pointer colsize :unsigned-long))))
- (setf buffer (acquire-foreign-resource
- :char (* +n-buf-rows+ colsize-including-null)))
- (setf sizeof colsize-including-null))))
- (let ((retcodes (acquire-foreign-resource :short +n-buf-rows+))
- (indicators (acquire-foreign-resource :short +n-buf-rows+)))
- (push (make-cd :name "col" ;(subseq colname 0 colnamelen)
- :sizeof sizeof
- :buffer buffer
- :oci-data-type dtype
- :retcodes retcodes
- :indicators indicators)
- cds-as-reversed-list)
- (oci-define-by-pos (uffi:deref-pointer stmthp void-pointer)
- defnp
- (uffi:deref-pointer errhp void-pointer)
- (1+ icolumn) ; OCI 1-based indexing again
- (foreign-resource-buffer buffer)
- sizeof
- dtype
- (foreign-resource-buffer indicators)
- (uffi:make-null-pointer :unsigned-short)
- (foreign-resource-buffer retcodes)
- +oci-default+)))))))
-
+ (let ((dtype (uffi:deref-pointer dtype-foreign :unsigned-short)))
+ (case dtype
+ (#.SQLT-DATE
+ (setf buffer (acquire-foreign-resource :char (* 32 +n-buf-rows+)))
+ (setf sizeof 32 dtype #.SQLT-STR))
+ (2 ;; number
+ ;;(oci-attr-get parmdp +oci-dtype-param+
+ ;;(addr precision) nil +oci-attr-precision+
+ ;;(uffi:deref-pointer errhp))
+ (oci-attr-get (uffi:deref-pointer parmdp void-pointer)
+ +oci-dtype-param+
+ scale
+ (uffi:make-null-pointer :int) +oci-attr-scale+
+ (uffi:deref-pointer errhp void-pointer))
+ (cond
+ ((zerop scale)
+ (setf buffer (acquire-foreign-resource :init +n-buf-rows+)
+ sizeof 4 ;; sizeof(int)
+ dtype #.SQLT-INT))
+ (t
+ (setf buffer (acquire-foreign-resource :double +n-buf-rows+)
+ sizeof 8 ;; sizeof(double)
+ dtype #.SQLT-FLT))))
+ (t ; Default to SQL-STR
+ (setf (uffi:deref-pointer colsize :unsigned-long) 0
+ dtype #.SQLT-STR)
+ (oci-attr-get (uffi:deref-pointer parmdp void-pointer)
+ +oci-dtype-param+
+ colsize
+ (uffi:make-null-pointer :int) ;; (uffi:pointer-address colsizesize)
+ +oci-attr-data-size+
+ (uffi:deref-pointer errhp void-pointer))
+ (let ((colsize-including-null (1+ (uffi:deref-pointer colsize :unsigned-long))))
+ (setf buffer (acquire-foreign-resource
+ :char (* +n-buf-rows+ colsize-including-null)))
+ (setf sizeof colsize-including-null))))
+ (let ((retcodes (acquire-foreign-resource :short +n-buf-rows+))
+ (indicators (acquire-foreign-resource :short +n-buf-rows+)))
+ (push (make-cd :name "col" ;(subseq colname 0 colnamelen)
+ :sizeof sizeof
+ :buffer buffer
+ :oci-data-type dtype
+ :retcodes retcodes
+ :indicators indicators)
+ cds-as-reversed-list)
+ (oci-define-by-pos (uffi:deref-pointer stmthp void-pointer)
+ defnp
+ (uffi:deref-pointer errhp void-pointer)
+ (1+ icolumn) ; OCI 1-based indexing again
+ (foreign-resource-buffer buffer)
+ sizeof
+ dtype
+ (foreign-resource-buffer indicators)
+ (uffi:make-null-pointer :unsigned-short)
+ (foreign-resource-buffer retcodes)
+ +oci-default+))))))))
+
;; Release the resources associated with a QUERY-CURSOR.
(defun close-query (qc)
(uffi:convert-to-cstring data-source-name) (length data-source-name)
:database db)
;; :date-format-length (1+ (length date-format)))))
+ (uffi:with-foreign-object (buf (:array :unsigned-char 512))
+ (oci-server-version (uffi:deref-pointer svchp void-pointer)
+ (uffi:deref-pointer errhp void-pointer)
+ buf
+ 512
+ +oci-htype-svcctx+)
+ (setf *oracle-server-version* (uffi:convert-from-foreign-string buf)))
+
(setf (slot-value db 'clsql-sys::state) :open)
(database-execute-command
(format nil "alter session set NLS_DATE_FORMAT='~A'" (date-format db)) db)
buf)))
+;; Specifications
+
+(defmethod db-type-has-bigint? ((type (eql :oracle)))
+ nil)
t)
(:documentation "T [default] if database-type supports views."))
+(defgeneric db-type-has-bigint? (db-type)
+ (:method (db-type)
+ (declare (ignore db-type))
+ ;; SQL92 has bigint
+ t)
+ (:documentation "T [default] if database-type supports bigint."))
+
(defgeneric db-type-default-case (db-type)
(:method (db-type)
(declare (ignore db-type))
#:db-backend-has-create/destroy-db?
#:db-type-has-views?
+ #:db-type-has-bigint?
#:db-type-has-union?
#:db-type-has-subqueries?
#:db-type-has-boolean-where?
(in-package #:clsql-tests)
-(defun test-basic-initialize ()
- (ignore-errors
- (clsql:execute-command "DROP TABLE TYPE_TABLE"))
- (clsql:execute-command
- "CREATE TABLE TYPE_TABLE (t_int integer, t_float double precision, t_bigint BIGINT, t_str VARCHAR(30))")
- (dotimes (i 11)
- (let* ((test-int (- i 5))
- (test-flt (transform-float-1 test-int)))
- (clsql:execute-command
- (format nil "INSERT INTO TYPE_TABLE VALUES (~a,~a,~a,'~a')"
- test-int
- (clsql-sys:number-to-sql-string test-flt)
- (transform-bigint-1 test-int)
- (clsql-sys:number-to-sql-string test-flt)
- )))))
-
-(defun test-basic-forms ()
- (append
- (test-basic-forms-untyped)
- '(
- (deftest :BASIC/TYPE/1
+(setq *rt-basic*
+ '(
+ (deftest :BASIC/TYPE/1
(let ((results '()))
(dolist (row (query "select * from TYPE_TABLE" :result-types :auto)
- results)
- (destructuring-bind (int float bigint str) row
+ results)
+ (destructuring-bind (int float str) row
(push (list (integerp int)
(typep float 'double-float)
- (if (and (eq :odbc *test-database-type*)
- (eq :postgresql *test-database-underlying-type*))
- ;; ODBC/Postgresql may return returns bigints as strings or integer
- ;; depending upon the platform
- t
- (integerp bigint))
(stringp str))
results))))
- ((t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t)))
+ ((t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t)))
(deftest :BASIC/TYPE/2
(let ((results '()))
(dolist (row (query "select * from TYPE_TABLE" :result-types :auto)
results)
- (destructuring-bind (int float bigint str) row
+ (destructuring-bind (int float str) row
(setq results
(cons (list (double-float-equal
(transform-float-1 int)
results))))
results)
((t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t)))
- )))
-(defun test-basic-forms-untyped ()
- '((deftest :BASIC/SELECT/1
+ (deftest :BASIC/SELECT/1
(let ((rows (query "select * from TYPE_TABLE" :result-types :auto)))
(values
(length rows)
(length (car rows))))
- 11 4)
+ 11 3)
(deftest :BASIC/SELECT/2
(let ((results '()))
(dolist (row (query "select * from TYPE_TABLE" :result-types nil)
results)
- (destructuring-bind (int float bigint str) row
+ (destructuring-bind (int float str) row
(push (list (stringp int)
(stringp float)
- (stringp bigint)
(stringp str))
results))))
- ((t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t) (t t t t)))
+ ((t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t) (t t t)))
(deftest :BASIC/SELECT/3
(let ((results '()))
(dolist (row (query "select * from TYPE_TABLE" :result-types nil)
results)
- (destructuring-bind (int float bigint str) row
- (declare (ignore bigint))
+ (destructuring-bind (int float str) row
(push (list (double-float-equal
(transform-float-1 (parse-integer int))
(parse-double float))
(transform-float-1 (parse-integer (first (aref rows i))))
(parse-double (second (aref rows i)))))
results)))
- ((t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t)))
+ ((t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t)))
(deftest :BASIC/MAP/2
(let ((results '())
(transform-float-1 (parse-integer (first (nth i rows))))
(parse-double (second (nth i rows)))))
results)))
- ((t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t)))
+ ((t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t)))
(deftest :BASIC/MAP/3
(let ((results '())
(transform-float-1 (first (nth i rows)))
(second (nth i rows))))
results)))
- ((t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t) (t 4 t t)))
+ ((t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t) (t 3 t t)))
(deftest :BASIC/DO/1
(let ((results '()))
- (do-query ((int float bigint str) "select * from TYPE_TABLE" :result-types nil)
- (declare (ignore bigint))
+ (do-query ((int float str) "select * from TYPE_TABLE" :result-types nil)
(let ((int-number (parse-integer int)))
(setq results
(cons (list (double-float-equal (transform-float-1
(deftest :BASIC/DO/2
(let ((results '()))
- (do-query ((int float bigint str) "select * from TYPE_TABLE" :result-types :auto)
- (declare (ignore bigint))
+ (do-query ((int float str) "select * from TYPE_TABLE" :result-types :auto)
(setq results
(cons
(list (double-float-equal
results)))
results)
((t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t)))
+
+
+ (deftest :BASIC/BIGINT/1
+ (let ((results '()))
+ (dolist (row (query "select * from TYPE_BIGINT" :result-types :auto)
+ results)
+ (destructuring-bind (int bigint) row
+ (push (list (integerp int)
+ (if (and (eq :odbc *test-database-type*)
+ (eq :postgresql *test-database-underlying-type*))
+ ;; ODBC/Postgresql may return returns bigints as strings or integer
+ ;; depending upon the platform
+ t
+ (integerp bigint)))
+ results))))
+ ((t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t) (t t)))
+
))
+(defun test-basic-initialize ()
+ (ignore-errors
+ (clsql:execute-command "DROP TABLE TYPE_TABLE")
+ (clsql:execute-command "DROP TABLE TYPE_BIGINT"))
+
+ (clsql:execute-command
+ "CREATE TABLE TYPE_TABLE (t_int integer, t_float double precision, t_str VARCHAR(30))")
+
+ (if (clsql-sys:db-type-has-bigint? *test-database-type*)
+ (clsql:execute-command "CREATE TABLE TYPE_BIGINT (t_int integer, t_bigint BIGINT)")
+ (clsql:execute-command "CREATE TABLE TYPE_BIGINT (t_int integer)"))
+
+ (dotimes (i 11)
+ (let* ((test-int (- i 5))
+ (test-flt (transform-float-1 test-int)))
+ (clsql:execute-command
+ (format nil "INSERT INTO TYPE_TABLE VALUES (~a,~a,'~a')"
+ test-int
+ (clsql-sys:number-to-sql-string test-flt)
+ (clsql-sys:number-to-sql-string test-flt)
+ ))
+ (clsql:execute-command
+ (format nil "INSERT INTO TYPE_BIGINT VALUES (~a,~a)"
+ test-int
+ (transform-bigint-1 test-int)
+ )))))
+
;;;; Testing functions
(defun transform-float-1 (i)
(sort (mapcar #'string-downcase
(clsql:list-tables :owner *test-database-user*))
#'string<))
- "addr" "company" "ea_join" "employee" "type_table")
+ "addr" "company" "ea_join" "employee" "type_bigint" "type_table")
;; create a table, test for its existence, drop it and test again
(deftest :fddl/table/2
(defvar *report-stream* *standard-output* "Stream to send text report.")
(defvar *sexp-report-stream* nil "Stream to send sexp report.")
(defvar *rt-connection*)
+(defvar *rt-basic*)
(defvar *rt-fddl*)
(defvar *rt-fdml*)
(defvar *rt-ooddl*)
(defun test-initialise-database ()
(test-basic-initialize)
-
(let ((*backend-warning-behavior*
(if (member *test-database-type* '(:postgresql :postgresql-socket))
:ignore
(defun compute-tests-for-backend (db-type db-underlying-type)
- (declare (ignorable db-type))
(let ((test-forms '())
(skip-tests '()))
- (dolist (test-form (append (test-basic-forms)
- *rt-connection* *rt-fddl* *rt-fdml*
+ (dolist (test-form (append *rt-connection* *rt-basic* *rt-fddl* *rt-fdml*
*rt-ooddl* *rt-oodml* *rt-syntax*))
(let ((test (second test-form)))
(cond
:fdml/select/21 :fdml/select/32
:fdml/select/33))
(push (cons test "not supported by sqlite") skip-tests))
+ ((and (not (clsql-sys:db-type-has-bigint? db-type))
+ (clsql-sys:in test :basic/bigint/1))
+ (push (cons test "bigint not supported") skip-tests))
((and (eql *test-database-underlying-type* :mysql)
(clsql-sys:in test :fdml/select/26))
- (push (cons test "string table aliases not supported") skip-tests))
+ (push (cons test "string table aliases not supported on all mysql versions") skip-tests))
((and (eql *test-database-underlying-type* :mysql)
(clsql-sys:in test :fdml/select/22 :fdml/query/5
:fdml/query/7 :fdml/query/8))
(rapid-load :mysql))
(defun rlo ()
- (rapid-load :odbc))
+ (rapid-load :oracle))