(incf offset))
offset)
+(defmacro with-allocate-foreign-string ((var len) &body body)
+ "Safely does uffi:allocate-foreign-string-- making sure we do the uffi:free-foreign-object"
+ `(let ((,var))
+ (unwind-protect
+ (progn
+ (setf ,var (uffi:allocate-foreign-string ,len))
+ ,@body)
+ (when ,var
+ (uffi:free-foreign-object ,var)))))
+
+(defmacro with-allocate-foreign-strings (bindings &rest body)
+ (if bindings
+ `(with-allocate-foreign-string ,(car bindings)
+ (with-allocate-foreign-strings ,(cdr bindings)
+ ,@body))
+ `(progn ,@body)))
+
(defun handle-error (henv hdbc hstmt)
- (let ((sql-state (allocate-foreign-string 256))
- (error-message (allocate-foreign-string #.$SQL_MAX_MESSAGE_LENGTH)))
- (with-foreign-objects ((error-code #.$ODBC-LONG-TYPE)
- (msg-length :short))
- (SQLError henv hdbc hstmt sql-state
- error-code error-message
- #.$SQL_MAX_MESSAGE_LENGTH msg-length)
- (let ((err (convert-from-foreign-string error-message))
- (state (convert-from-foreign-string sql-state)))
- (free-foreign-object error-message)
- (free-foreign-object sql-state)
- (values
- err
- state
- (deref-pointer msg-length :short)
- (deref-pointer error-code #.$ODBC-LONG-TYPE))))))
+ (with-allocate-foreign-strings ((sql-state 256)
+ (error-message #.$SQL_MAX_MESSAGE_LENGTH))
+ (with-foreign-objects ((error-code #.$ODBC-LONG-TYPE)
+ (msg-length :short))
+ (SQLError henv hdbc hstmt sql-state
+ error-code error-message
+ #.$SQL_MAX_MESSAGE_LENGTH msg-length)
+ (values
+ (convert-from-foreign-string error-message)
+ (convert-from-foreign-string sql-state)
+ (deref-pointer msg-length :short)
+ (deref-pointer error-code #.$ODBC-LONG-TYPE)))))
(defun sql-state (henv hdbc hstmt)
- (let ((sql-state (allocate-foreign-string 256))
- (error-message (allocate-foreign-string #.$SQL_MAX_MESSAGE_LENGTH)))
- (with-foreign-objects ((error-code #.$ODBC-LONG-TYPE)
- (msg-length :short))
- (SQLError henv hdbc hstmt sql-state error-code
- error-message #.$SQL_MAX_MESSAGE_LENGTH msg-length)
- (let ((state (convert-from-foreign-string sql-state)))
- (free-foreign-object error-message)
- (free-foreign-object sql-state)
- state
- ;; test this: return a keyword for efficiency
- ;;(%cstring-to-keyword state)
- ))))
+ (with-allocate-foreign-strings ((sql-state 256)
+ (error-message #.$SQL_MAX_MESSAGE_LENGTH))
+ (with-foreign-objects ((error-code #.$ODBC-LONG-TYPE)
+ (msg-length :short))
+ (SQLError henv hdbc hstmt sql-state error-code
+ error-message #.$SQL_MAX_MESSAGE_LENGTH msg-length)
+ (convert-from-foreign-string sql-state)
+ ;; test this: return a keyword for efficiency
+ ;;(%cstring-to-keyword state)
+ )))
(defmacro with-error-handling ((&key henv hdbc hstmt (print-info t))
odbc-call &body body)
(defun %sql-driver-connect (hdbc connection-string completion window-handle)
(with-cstring (connection-ptr connection-string)
- (let ((completed-connection-string (allocate-foreign-string $SQL_MAX_CONN_OUT)))
- (unwind-protect
- (with-foreign-object (completed-connection-length :short)
- (with-error-handling
- (:hdbc hdbc)
- (SQLDriverConnect hdbc
- window-handle
- connection-ptr $SQL_NTS
- completed-connection-string $SQL_MAX_CONN_OUT
- completed-connection-length
- completion)))
- (free-foreign-object completed-connection-string)))))
+ (with-allocate-foreign-string (completed-connection-string-ptr $SQL_MAX_CONN_OUT)
+ (with-foreign-object (completed-connection-length :short)
+ (with-error-handling
+ (:hdbc hdbc)
+ (SQLDriverConnect hdbc
+ window-handle
+ connection-ptr $SQL_NTS
+ completed-connection-string-ptr $SQL_MAX_CONN_OUT
+ completed-connection-length
+ completion))))))
(defun %disconnect (hdbc)
(with-error-handling
(:hdbc hdbc)
- (SQLDisconnect hdbc)))
+ (SQLDisconnect hdbc)
+ (with-error-handling
+ (:hdbc hdbc)
+ (SQLFreeHandle $SQL_HANDLE_DBC hdbc))))
(defun %commit (henv hdbc)
(with-error-handling
#.$SQL_SPECIAL_CHARACTERS
#.$SQL_TABLE_TERM
#.$SQL_USER_NAME)
- (let ((info-ptr (allocate-foreign-string 1024)))
+ (with-allocate-foreign-string (info-ptr 1024)
(with-foreign-object (info-length-ptr :short)
- (with-error-handling
- (:hdbc hdbc)
- (SQLGetInfo hdbc info-type info-ptr 1023 info-length-ptr)
- (let ((info (convert-from-foreign-string info-ptr)))
- (free-foreign-object info-ptr)
- info)))))
+ (with-error-handling
+ (:hdbc hdbc)
+ (SQLGetInfo hdbc info-type info-ptr 1023 info-length-ptr)
+ (convert-from-foreign-string info-ptr)))))
;; those returning a word
((#.$SQL_ACTIVE_CONNECTIONS
#.$SQL_ACTIVE_STATEMENTS
;; column counting is 1-based
(defun %describe-column (hstmt column-nr)
- (let ((column-name-ptr (allocate-foreign-string 256)))
+ (with-allocate-foreign-string (column-name-ptr 256)
(with-foreign-objects ((column-name-length-ptr :short)
(column-sql-type-ptr :short)
(column-precision-ptr #.$ODBC-ULONG-TYPE)
(column-scale-ptr :short)
(column-nullable-p-ptr :short))
- (with-error-handling (:hstmt hstmt)
- (SQLDescribeCol hstmt column-nr column-name-ptr 256
- column-name-length-ptr
- column-sql-type-ptr
- column-precision-ptr
- column-scale-ptr
- column-nullable-p-ptr)
- (let ((column-name (convert-from-foreign-string column-name-ptr)))
- (free-foreign-object column-name-ptr)
- (values
- column-name
- (deref-pointer column-sql-type-ptr :short)
- (deref-pointer column-precision-ptr #.$ODBC-ULONG-TYPE)
- (deref-pointer column-scale-ptr :short)
- (deref-pointer column-nullable-p-ptr :short)))))))
+ (with-error-handling (:hstmt hstmt)
+ (SQLDescribeCol hstmt column-nr column-name-ptr 256
+ column-name-length-ptr
+ column-sql-type-ptr
+ column-precision-ptr
+ column-scale-ptr
+ column-nullable-p-ptr)
+ (values
+ (convert-from-foreign-string column-name-ptr)
+ (deref-pointer column-sql-type-ptr :short)
+ (deref-pointer column-precision-ptr #.$ODBC-ULONG-TYPE)
+ (deref-pointer column-scale-ptr :short)
+ (deref-pointer column-nullable-p-ptr :short))))))
;; parameter counting is 1-based
;; this function isn't used, which is good because FreeTDS dosn't support it.
(deref-pointer column-nullable-p-ptr :short)))))
(defun %column-attributes (hstmt column-nr descriptor-type)
- (let ((descriptor-info-ptr (allocate-foreign-string 256)))
+ (with-allocate-foreign-string (descriptor-info-ptr 256)
(with-foreign-objects ((descriptor-length-ptr :short)
(numeric-descriptor-ptr #.$ODBC-LONG-TYPE))
- (with-error-handling
- (:hstmt hstmt)
- (SQLColAttributes hstmt column-nr descriptor-type descriptor-info-ptr
- 256 descriptor-length-ptr
- numeric-descriptor-ptr)
- (let ((desc (convert-from-foreign-string descriptor-info-ptr)))
- (free-foreign-object descriptor-info-ptr)
- (values
- desc
- (deref-pointer numeric-descriptor-ptr #.$ODBC-LONG-TYPE)))))))
+ (with-error-handling
+ (:hstmt hstmt)
+ (SQLColAttributes hstmt column-nr descriptor-type descriptor-info-ptr
+ 256 descriptor-length-ptr
+ numeric-descriptor-ptr)
+ (values
+ (convert-from-foreign-string descriptor-info-ptr)
+ (deref-pointer numeric-descriptor-ptr #.$ODBC-LONG-TYPE))))))
(defun %prepare-describe-columns (hstmt table-qualifier table-owner
table-name column-name)
(fetch-all-rows hstmt)))
(defun %sql-data-sources (henv &key (direction :first))
- (let ((name-ptr (allocate-foreign-string (1+ $SQL_MAX_DSN_LENGTH)))
- (description-ptr (allocate-foreign-string 1024)))
- (with-foreign-objects ((name-length-ptr :short)
- (description-length-ptr :short))
- (let ((res (with-error-handling
- (:henv henv)
- (SQLDataSources henv
- (ecase direction
- (:first $SQL_FETCH_FIRST)
- (:next $SQL_FETCH_NEXT))
- name-ptr
- (1+ $SQL_MAX_DSN_LENGTH)
- name-length-ptr
- description-ptr
- 1024
- description-length-ptr))))
- (cond
- ((= res $SQL_NO_DATA_FOUND)
- (let ((name (convert-from-foreign-string name-ptr))
- (desc (convert-from-foreign-string description-ptr)))
- (free-foreign-object name-ptr)
- (free-foreign-object description-ptr)
- (values
- name
- desc)))
- (t
- (free-foreign-object name-ptr)
- (free-foreign-object description-ptr)
- nil))))))
+ (with-allocate-foreign-strings ((name-ptr (1+ $SQL_MAX_DSN_LENGTH))
+ (description-ptr 1024))
+ (with-foreign-objects ((name-length-ptr :short)
+ (description-length-ptr :short))
+ (let ((res (with-error-handling
+ (:henv henv)
+ (SQLDataSources henv
+ (ecase direction
+ (:first $SQL_FETCH_FIRST)
+ (:next $SQL_FETCH_NEXT))
+ name-ptr
+ (1+ $SQL_MAX_DSN_LENGTH)
+ name-length-ptr
+ description-ptr
+ 1024
+ description-length-ptr))))
+ (when (= res $SQL_NO_DATA_FOUND)
+ (values
+ (convert-from-foreign-string name-ptr)
+ (convert-from-foreign-string description-ptr)))))))
(get-slot-value ptr 'sql-c-timestamp 'fraction)))
(defun universal-time-to-timestamp (time &optional (fraction 0))
+ "TODO: Dead function?"
(multiple-value-bind (sec min hour day month year)
(decode-universal-time time)
(let ((ptr (allocate-foreign-object 'sql-c-timestamp)))
ptr)))
(defun %put-timestamp (ptr time &optional (fraction 0))
+ "TODO: Dead function?"
(declare (type c-timestamp-ptr-type ptr))
(multiple-value-bind (sec min hour day month year)
(decode-universal-time time)
(defun %set-attr-odbc-version (henv version)
(with-error-handling (:henv henv)
+ ;;note that we are passing version as an integer that happens to be
+ ;;stuffed into a pointer.
+ ;;http://msdn.microsoft.com/en-us/library/ms709285%28v=VS.85%29.aspx
(SQLSetEnvAttr henv $SQL_ATTR_ODBC_VERSION
(make-pointer version :void) 0)))
(if ensure $SQL_ENSURE $SQL_QUICK)))))
(defun %list-data-sources (henv)
- (let ((dsn (allocate-foreign-string (1+ $SQL_MAX_DSN_LENGTH)))
- (desc (allocate-foreign-string 256))
- (results nil))
- (unwind-protect
- (with-foreign-objects ((dsn-len :short)
- (desc-len :short))
- (let ((res (with-error-handling (:henv henv)
- (SQLDataSources henv $SQL_FETCH_FIRST dsn
- (1+ $SQL_MAX_DSN_LENGTH)
- dsn-len desc 256 desc-len))))
- (when (or (eql res $SQL_SUCCESS)
- (eql res $SQL_SUCCESS_WITH_INFO))
- (push (convert-from-foreign-string dsn) results))
-
- (do ((res (with-error-handling (:henv henv)
- (SQLDataSources henv $SQL_FETCH_NEXT dsn
- (1+ $SQL_MAX_DSN_LENGTH)
- dsn-len desc 256 desc-len))
- (with-error-handling (:henv henv)
- (SQLDataSources henv $SQL_FETCH_NEXT dsn
- (1+ $SQL_MAX_DSN_LENGTH)
- dsn-len desc 256 desc-len))))
- ((not (or (eql res $SQL_SUCCESS)
- (eql res $SQL_SUCCESS_WITH_INFO))))
- (push (convert-from-foreign-string dsn) results))))
- (progn
- (free-foreign-object dsn)
- (free-foreign-object desc)))
+ (let ((results nil))
+ (with-foreign-strings ((dsn-ptr (1+ $SQL_MAX_DSN_LENGTH))
+ (desc-ptr 256))
+ (with-foreign-objects ((dsn-len :short)
+ (desc-len :short))
+ (let ((res (with-error-handling (:henv henv)
+ (SQLDataSources henv $SQL_FETCH_FIRST dsn-ptr
+ (1+ $SQL_MAX_DSN_LENGTH)
+ dsn-len desc-ptr 256 desc-len))))
+ (when (or (eql res $SQL_SUCCESS)
+ (eql res $SQL_SUCCESS_WITH_INFO))
+ (push (convert-from-foreign-string dsn-ptr) results))
+
+ (do ((res (with-error-handling (:henv henv)
+ (SQLDataSources henv $SQL_FETCH_NEXT dsn-ptr
+ (1+ $SQL_MAX_DSN_LENGTH)
+ dsn-len desc-ptr 256 desc-len))
+ (with-error-handling (:henv henv)
+ (SQLDataSources henv $SQL_FETCH_NEXT dsn-ptr
+ (1+ $SQL_MAX_DSN_LENGTH)
+ dsn-len desc-ptr 256 desc-len))))
+ ((not (or (eql res $SQL_SUCCESS)
+ (eql res $SQL_SUCCESS_WITH_INFO))))
+ (push (convert-from-foreign-string dsn-ptr) results)))))
(nreverse results)))
-
-
parameter-columns))
(defgeneric get-odbc-info (src info-type))
+(defvar *reuse-query-objects* t)
+
;;; SQL Interface
db))
(defun disconnect (database)
+ "This is set in the generic-odbc-database disconnect-fn slot so xref fails
+ but this does get called on generic ODBC connections "
(with-slots (hdbc queries) database
(dolist (query queries)
- (if (query-active-p query)
- (with-slots (hstmt) query
- (when hstmt
- (%free-statement hstmt :drop)
- (setf hstmt nil)))))
+ (db-close-query query :drop-p T))
(when (db-hstmt database)
(%free-statement (db-hstmt database) :drop))
(%disconnect hdbc)))
(setf active-p t)))))
;; one for odbc-db is missing
+;; TODO: Seems to be uncalled
(defmethod terminate ((query odbc-query))
;;(format tb::*local-output* "~%*** terminated: ~s" query)
- (with-slots (hstmt) query
- (when hstmt
- ;(%free-statement hstmt :drop)
- (uffi:free-foreign-object hstmt)) ;; ??
- (%dispose-column-ptrs query)))
+ (db-close-query query))
(defun %dispose-column-ptrs (query)
+ "frees memory allocated for query object column-data and column-data-length"
(with-slots (column-data-ptrs column-out-len-ptrs hstmt) query
(loop for data-ptr across column-data-ptrs
- when data-ptr do (uffi:free-foreign-object data-ptr))
- (loop for out-len-ptr across column-out-len-ptrs
- when out-len-ptr do (uffi:free-foreign-object out-len-ptr))))
+ for out-len-ptr across column-out-len-ptrs
+ when data-ptr
+ do (uffi:free-foreign-object data-ptr)
+ when out-len-ptr
+ do (uffi:free-foreign-object out-len-ptr))
+ (setf (fill-pointer column-data-ptrs) 0
+ (fill-pointer column-out-len-ptrs) 0)))
(defmethod db-open-query ((database odbc-db) query-expression
&key arglen col-positions result-types width
"get-free-query finds or makes a nonactive query object, and then sets it to active.
This makes the functions db-execute-command and db-query thread safe."
(with-slots (queries hdbc) database
- (or (clsql-sys:without-interrupts
- (let ((inactive-query (find-if (lambda (query)
- (not (query-active-p query)))
- queries)))
- (when inactive-query
- (with-slots (column-count column-names column-c-types
- width hstmt
- column-sql-types column-data-ptrs
- column-out-len-ptrs column-precisions
- column-scales column-nullables-p)
- inactive-query
- ;;(print column-data-ptrs tb::*local-output*)
- ;;(%dispose-column-ptrs inactive-query)
- (setf column-count 0
- width +max-precision+
- ;; KMR hstmt (%new-statement-handle hdbc)
- (fill-pointer column-names) 0
- (fill-pointer column-c-types) 0
- (fill-pointer column-sql-types) 0
- (fill-pointer column-data-ptrs) 0
- (fill-pointer column-out-len-ptrs) 0
- (fill-pointer column-precisions) 0
- (fill-pointer column-scales) 0
- (fill-pointer column-nullables-p) 0))
- (setf (query-active-p inactive-query) t))
- inactive-query))
+ (or (and *reuse-query-objects*
+ (clsql-sys:without-interrupts
+ (let ((inactive-query (find-if (lambda (query)
+ (not (query-active-p query)))
+ queries)))
+ (when inactive-query
+ (with-slots (column-count column-names column-c-types
+ width hstmt
+ column-sql-types column-data-ptrs
+ column-out-len-ptrs column-precisions
+ column-scales column-nullables-p)
+ inactive-query
+ (setf column-count 0
+ width +max-precision+
+ ;; KMR hstmt (%new-statement-handle hdbc)
+ (fill-pointer column-names) 0
+ (fill-pointer column-c-types) 0
+ (fill-pointer column-sql-types) 0
+ (fill-pointer column-data-ptrs) 0
+ (fill-pointer column-out-len-ptrs) 0
+ (fill-pointer column-precisions) 0
+ (fill-pointer column-scales) 0
+ (fill-pointer column-nullables-p) 0))
+ (setf (query-active-p inactive-query) t))
+ inactive-query)))
(let ((new-query (make-instance 'odbc-query
:database database
;;(clone-database database)
(t t)))))
query)
-(defun db-close-query (query &key drop-p)
+(defun db-close-query (query &key (drop-p (not *reuse-query-objects*)))
(with-slots (hstmt column-count column-names column-c-types column-sql-types
- column-data-ptrs column-out-len-ptrs column-precisions
- column-scales column-nullables-p) query
- (let ((count (fill-pointer column-data-ptrs)))
- (when (not (zerop count))
- (dotimes (col-nr count)
- (let ((data-ptr (aref column-data-ptrs col-nr))
- (out-len-ptr (aref column-out-len-ptrs col-nr)))
- (declare (ignorable data-ptr out-len-ptr))
- ;; free-statment :unbind frees these
- #+ignore (when data-ptr (uffi:free-foreign-object data-ptr))
- #+ignore (when out-len-ptr (uffi:free-foreign-object out-len-ptr)))))
- (cond ((null hstmt)
- nil)
- (drop-p
- (%free-statement hstmt :drop)
- (setf hstmt nil))
- (t
- (%free-statement hstmt :unbind)
- (%free-statement hstmt :reset)
- (%free-statement hstmt :close)))
- (setf (query-active-p query) nil)))
+ column-data-ptrs column-out-len-ptrs column-precisions
+ column-scales column-nullables-p database) query
+ (%dispose-column-ptrs query)
+ (cond ((null hstmt) nil)
+ (drop-p
+ (%free-statement hstmt :drop)
+ ;; dont free with uffi/ this is a double free and crashes everything
+ ;; (uffi:free-foreign-object hstmt)
+ (setf hstmt nil))
+ (t
+ (%free-statement hstmt :unbind)
+ (%free-statement hstmt :reset)
+ (%free-statement hstmt :close)))
+ (setf (query-active-p query) nil)
+ (when drop-p
+ (clsql-sys:without-interrupts
+ (with-slots (queries) database
+ (setf queries (remove query queries))))))
query)
(defmethod %read-query-data ((database odbc-db) ignore-columns)
(let ((query (get-free-query database)))
(with-slots (hstmt) query
(unless hstmt (setf hstmt (%new-statement-handle hdbc))))
- (db-prepare-statement query sql parameter-table parameter-columns))))
+ (db-prepare-statement
+ query sql :parameter-table parameter-table :parameter-columns parameter-columns))))
(defmethod db-prepare-statement ((query odbc-query) (sql string)
&key parameter-table parameter-columns)
(defun %db-bind-execute (query &rest parameters)
+ "Only used from db-map-bind-query
+ parameters are released in %reset-query
+ "
(with-slots (hstmt parameter-data-ptrs) query
(loop for parameter in parameters
with data-ptr and size and parameter-string
do
(setf parameter-string
(if (stringp parameter)
- parameter
- (write-to-string parameter))
- size (length parameter-string)
+ parameter
+ (write-to-string parameter))
+ size (length parameter-string)
data-ptr
(uffi:allocate-foreign-string (1+ size)))
(vector-push-extend data-ptr parameter-data-ptrs)
(defun %db-reset-query (query)
+ "Only used from db-map-bind-query
+ parameters are allocated in %db-bind-execute
+ "
(with-slots (hstmt parameter-data-ptrs) query
(prog1
- (db-fetch-query-results query nil)
+ (db-fetch-query-results query nil)
(%free-statement hstmt :reset) ;; but _not_ :unbind !
(%free-statement hstmt :close)
(dotimes (param-nr (fill-pointer parameter-data-ptrs))