From: Russ Tyndall Date: Mon, 27 Jun 2011 17:49:56 +0000 (-0400) Subject: ODBC Memory management improvements X-Git-Tag: v5.4.0~3 X-Git-Url: http://git.kpe.io/?p=clsql.git;a=commitdiff_plain;h=d93955e3f6ad71eb27f334c50f997b4d351724c3 ODBC Memory management improvements * More stuff goes through db-close-query; try to make that function more complete * Adding SqlFreeHandle call after disconnect: free resources associated with that connection * Adding with-allocate-foreign-string to wrap allocate-foreign-string ensuring we free correspondingly. * fix bugs in dispose-column-ptrs (set fill pointers to 0) Adding todo comments on apparently dead functions --- diff --git a/ChangeLog b/ChangeLog index 57f93ac..aa4a5c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2011-06-27 Nathan Bird + * db-odbc/: memory management improvements: leak slower + 2011-06-20 Nathan Bird * sql/time.lisp: Handle parsing already parsed objects. diff --git a/db-odbc/odbc-api.lisp b/db-odbc/odbc-api.lisp index b7fd718..5d2acf9 100644 --- a/db-odbc/odbc-api.lisp +++ b/db-odbc/odbc-api.lisp @@ -61,38 +61,48 @@ as possible second argument) to the desired representation of date/time/timestam (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) @@ -209,23 +219,24 @@ as possible second argument) to the desired representation of date/time/timestam (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 @@ -317,14 +328,12 @@ as possible second argument) to the desired representation of date/time/timestam #.$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 @@ -470,27 +479,25 @@ as possible second argument) to the desired representation of date/time/timestam ;; 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. @@ -513,19 +520,17 @@ as possible second argument) to the desired representation of date/time/timestam (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) @@ -549,35 +554,26 @@ as possible second argument) to the desired representation of date/time/timestam (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))))))) @@ -951,6 +947,7 @@ as possible second argument) to the desired representation of date/time/timestam (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))) @@ -964,6 +961,7 @@ as possible second argument) to the desired representation of date/time/timestam 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) @@ -997,6 +995,9 @@ as possible second argument) to the desired representation of date/time/timestam (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))) @@ -1016,34 +1017,28 @@ as possible second argument) to the desired representation of date/time/timestam (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))) - - diff --git a/db-odbc/odbc-constants.lisp b/db-odbc/odbc-constants.lisp index c4265f4..689fc12 100644 --- a/db-odbc/odbc-constants.lisp +++ b/db-odbc/odbc-constants.lisp @@ -966,7 +966,7 @@ (defconstant $SQL_FETCH_RELATIVE 6) (defconstant $SQL_FETCH_BOOKMARK 8) -;;; ODBC v3 constants, added by KMR +;;; ODBC3 constants, added by KMR (defconstant $SQL_ATTR_ODBC_VERSION 200) (defconstant $SQL_OV_ODBC2 2) diff --git a/db-odbc/odbc-dbi.lisp b/db-odbc/odbc-dbi.lisp index a4d973a..5c57c2e 100644 --- a/db-odbc/odbc-dbi.lisp +++ b/db-odbc/odbc-dbi.lisp @@ -65,6 +65,8 @@ parameter-columns)) (defgeneric get-odbc-info (src info-type)) +(defvar *reuse-query-objects* t) + ;;; SQL Interface @@ -145,13 +147,11 @@ the query against." )) 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))) @@ -269,20 +269,22 @@ the query against." )) (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 @@ -365,32 +367,31 @@ the query against." )) "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) @@ -468,29 +469,26 @@ This makes the functions db-execute-command and db-query thread safe." (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) @@ -584,7 +582,8 @@ This makes the functions db-execute-command and db-query thread safe." (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) @@ -607,15 +606,18 @@ This makes the functions db-execute-command and db-query thread safe." (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) @@ -637,9 +639,12 @@ This makes the functions db-execute-command and db-query thread safe." (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)) diff --git a/db-odbc/odbc-ff-interface.lisp b/db-odbc/odbc-ff-interface.lisp index 68270c8..0fa0824 100644 --- a/db-odbc/odbc-ff-interface.lisp +++ b/db-odbc/odbc-ff-interface.lisp @@ -21,7 +21,7 @@ (def-foreign-type string-ptr (* :unsigned-char)) (def-type long-ptr-type (* #.$ODBC-LONG-TYPE)) -;; odbc v3 +;; ODBC3 (def-function "SQLAllocHandle" ((handle-type :short) (input-handle sql-handle) @@ -29,6 +29,14 @@ :module "odbc" :returning :short) +;; ODBC3 version of SQLFreeStmt, SQLFreeConnect, and SSQLFreeStmt +(def-function "SQLFreeHandle" + ((handle-type :short) ; HandleType + (input-handle sql-handle)) ; Handle + :module "odbc" + :returning :short) ; RETCODE_SQL_API + + ;; deprecated (def-function "SQLAllocEnv" ((*phenv sql-handle-ptr) ; HENV FAR *phenv @@ -74,6 +82,13 @@ :module "odbc" :returning :short) ; RETCODE_SQL_API + +;;deprecated +(def-function "SQLFreeConnect" + ((hdbc sql-handle)) ; HDBC hdbc + :module "odbc" + :returning :short) ; RETCODE_SQL_API + ;; deprecated (def-function "SQLAllocStmt" ((hdbc sql-handle) ; HDBC hdbc