3b0a7ee431c8f9c0b9ac2dae38850427af720971
[clsql.git] / db-oracle / oracle-api.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          oracle.lisp
6 ;;;; Purpose:       Package definition for CLSQL Oracle interface
7 ;;;;
8 ;;;; $Id$
9 ;;;;
10 ;;;; This file is part of CLSQL.
11 ;;;;
12 ;;;; CLSQL users are granted the rights to distribute and use this software
13 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
14 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
15 ;;;; *************************************************************************
16
17 (in-package #:clsql-oracle)
18
19 ;;
20 ;; OCI integer types
21 ;;
22
23 (uffi:def-foreign-type ub2 :unsigned-short)
24 (uffi:def-foreign-type sb2 :short)
25 (uffi:def-foreign-type ub4 :unsigned-int)
26 (uffi:def-foreign-type sb4 :int)
27 (uffi:def-foreign-type size_t :unsigned-long)
28
29 ;;
30 ;; Opaque pointer types
31 ;;
32
33 (uffi:def-foreign-type void-pointer :pointer-void)
34 (uffi:def-foreign-type oci-env :pointer-void)
35 (uffi:def-foreign-type oci-server :pointer-void)
36 (uffi:def-foreign-type oci-error :pointer-void)
37 (uffi:def-foreign-type oci-svc-ctx :pointer-void)
38 (uffi:def-foreign-type oci-stmt :pointer-void)
39
40 (uffi:def-pointer-var +null-void-pointer+
41   (uffi:make-null-pointer :void))
42 (uffi:def-pointer-var +null-void-pointer-pointer+
43   (uffi:make-null-pointer :pointer-void))
44
45 ;;; Check an OCI return code for erroricity and signal a reasonably
46 ;;; informative condition if so.
47 ;;;
48 ;;; ERRHP provides an error handle which can be used to find
49 ;;; subconditions; if it's not provided, subcodes won't be checked.
50 ;;;
51 ;;; NULLS-OK says that a NULL-VALUE-RETURNED subcondition condition is
52 ;;; normal and needn't cause any signal. An error handle is required
53 ;;; to detect this subcondition, so it doesn't make sense to set ERRHP
54 ;;; unless NULLS-OK is set.
55
56 (defmacro def-oci-routine ((c-oci-symbol lisp-oci-fn) c-return &rest c-parms)
57   (let ((ll (mapcar (lambda (x) (declare (ignore x)) (gensym)) c-parms))
58         (c-oci-fn (intern (concatenate 'string "%" (symbol-name lisp-oci-fn)))))
59     `(progn
60       (declaim (inline ,c-oci-fn ,lisp-oci-fn))
61       (uffi:def-function (,c-oci-symbol ,c-oci-fn)
62           ,c-parms
63         :returning ,c-return)
64       (defun ,lisp-oci-fn (,@ll &key database nulls-ok)
65         (let ((result (,c-oci-fn ,@ll)))
66           (if (= result #.+oci-success+)
67               +oci-success+
68               (handle-oci-result result database nulls-ok)))))))
69
70
71 (defmacro def-raw-oci-routine
72   ((c-oci-symbol lisp-oci-fn) c-return &rest c-parms)
73   (let ((ll (mapcar (lambda (x) (declare (ignore x)) (gensym)) c-parms)))
74     `(let ((%lisp-oci-fn (uffi:def-function (,c-oci-symbol ,(intern (concatenate 'string "%" (symbol-name lisp-oci-fn))))
75                              ,c-parms
76                            :returning ,c-return)))
77        (defun ,lisp-oci-fn (,@ll &key database nulls-ok)
78          (funcall %lisp-oci-fn ,@ll)))))
79
80
81 (def-oci-routine ("OCIInitialize" oci-initialize)
82     :int
83   (mode ub4)                                    ; ub4
84   (ctxp :pointer-void)                  ; dvoid *
85   (malocfp :pointer-void)                       ; dvoid *(*)
86   (ralocfp :pointer-void)                       ; dvoid *(*)
87   (mfreefp (* :pointer-void)))          ; void *(*)
88
89
90 (def-oci-routine ("OCIEnvInit" oci-env-init)
91     :int
92   (envpp :pointer-void)                         ; OCIEnv **
93   (mode ub4)                                    ; ub4
94   (xtramem-sz size_t)            ; size_t
95   (usermempp (* :pointer-void)))                    ; dvoid **
96
97 #-oci7
98 (def-oci-routine ("OCIEnvCreate" oci-env-create)
99     :int
100   (envhpp (* :pointer-void))
101   (mode ub4)
102   (ctxp :pointer-void)
103   (malocfp :pointer-void)
104   (ralocfp :pointer-void)
105   (mfreefp :pointer-void)
106   (xtramemsz size_t)
107   (usrmempp (* :pointer-void)))
108
109 (def-oci-routine ("OCIHandleAlloc" oci-handle-alloc)
110     :int
111   (parenth      :pointer-void)                  ; const dvoid *
112   (hndlpp       (* :pointer-void))              ; dvoid **
113   (type         ub4)                            ; ub4
114   (xtramem_sz   size_t)                         ; size_t
115   (usrmempp     (* :pointer-void)))             ; dvoid **
116
117 (def-oci-routine ("OCIServerAttach" oci-server-attach)
118     :int
119   (srvhp        :pointer-void)                  ; oci-server
120   (errhp        :pointer-void)                  ; oci-error
121   (dblink       :cstring)                       ; :in
122   (dblink-len   sb4)                            ; sb4
123   (mode         ub4))                           ; ub4
124
125
126 (def-oci-routine ("OCIHandleFree" oci-handle-free)
127     :int
128   (p0 :pointer-void) ;; handle
129   (p1 ub4)) ;;type
130
131 (def-oci-routine ("OCILogon" oci-logon)
132     :int
133   (envhp        :pointer-void)          ; env
134   (errhp        :pointer-void)          ; err
135   (svchpp       (* :pointer-void))      ; svc
136   (username     (* :unsigned-char))     ; username
137   (uname-len    ub4)                    ;
138   (passwd       (* :unsigned-char))     ; passwd
139   (password-len ub4)                    ;
140   (dsn          (* :unsigned-char))     ; datasource
141   (dsn-len      ub4))                   ;
142
143 (def-oci-routine ("OCILogoff" oci-logoff)
144     :int
145   (p0   :pointer-void)        ; svc
146   (p1   :pointer-void))       ; err
147
148 (declaim (inline oci-error-get))
149 (uffi:def-function ("OCIErrorGet" oci-error-get)
150     ((handlp    :pointer-void)
151      (recordno  ub4)
152      (sqlstate  :cstring)
153      (errcodep  (* sb4))
154      (bufp      (* :unsigned-char))
155      (bufsize   ub4)
156      (type      ub4))
157   :returning :void)
158
159 (def-oci-routine ("OCIStmtPrepare" oci-stmt-prepare)
160     :int
161   (stmtp      :pointer-void)
162   (errhp      :pointer-void)
163   (stmt       (* :unsigned-char))
164   (stmt_len   ub4)
165   (language   ub4)
166   (mode       ub4))
167
168 (def-oci-routine ("OCIStmtExecute" oci-stmt-execute)
169     :int
170   (svchp      :pointer-void)
171   (stmtp1     :pointer-void)
172   (errhp      :pointer-void)
173   (iters      ub4)
174   (rowoff     ub4)
175   (snap_in    :pointer-void)
176   (snap_out   :pointer-void)
177   (mode       ub4))
178
179 (def-raw-oci-routine ("OCIParamGet" oci-param-get)
180     :int
181   (hndlp      :pointer-void)
182   (htype      ub4)
183   (errhp      :pointer-void)
184   (parmdpp    (* :pointer-void))
185   (pos        ub4))
186
187 (def-oci-routine ("OCIAttrGet" oci-attr-get)
188     :int
189   (trgthndlp  :pointer-void)
190   (trghndltyp ub4)
191   (attributep :pointer-void)
192   (sizep      (* ub4))
193   (attrtype   ub4)
194   (errhp      :pointer-void))
195
196 (def-oci-routine ("OCIAttrSet" oci-attr-set)
197     :int
198   (trgthndlp   :pointer-void)
199   (trgthndltyp ub4 :in)
200   (attributep  :pointer-void)
201   (size        ub4)
202   (attrtype    ub4)
203   (errhp       oci-error))
204
205 (def-oci-routine ("OCIDefineByPos" oci-define-by-pos)
206     :int
207   (stmtp      :pointer-void)
208   (defnpp     (* :pointer-void))
209   (errhp      :pointer-void)
210   (position   ub4)
211   (valuep     :pointer-void)
212   (value_sz   sb4)
213   (dty        ub2)
214   (indp       (* sb2))
215   (rlenp      (* ub2))
216   (rcodep     (* ub2))
217   (mode       ub4))
218
219 (def-oci-routine ("OCIStmtFetch" oci-stmt-fetch)
220     :int
221   (stmthp       :pointer-void)
222   (errhp        :pointer-void)
223   (p2           ub4)
224   (p3           ub2)
225   (p4           ub4))
226
227
228 (def-oci-routine ("OCITransStart" oci-trans-start)
229   :int
230   (svchp        :pointer-void)
231   (errhp        :pointer-void)
232   (p2           :unsigned-short)
233   (p3           :unsigned-short))
234
235 (def-oci-routine ("OCITransCommit" oci-trans-commit)
236   :int
237   (svchp        :pointer-void)
238   (errhp        :pointer-void)
239   (p2           :unsigned-short))
240
241 (def-oci-routine ("OCITransRollback" oci-trans-rollback)
242     :int
243   (svchp       :pointer-void)
244   (errhp        :pointer-void)
245   (p2           :unsigned-short))
246
247
248 (def-oci-routine ("OCIServerVersion" oci-server-version)
249     :int
250     (handlp    :pointer-void)
251     (errhp     :pointer-void)
252     (bufp      (* :unsigned-char))
253     (bufsz     :int)
254     (hndltype  :short))
255
256
257
258 ;;; Low-level routines that don't do error checking. They are used
259 ;;; for setting up global environment.
260
261 (uffi:def-function "OCIInitialize"
262     ((mode ub4)                                 ; ub4
263      (ctxp :pointer-void)                       ; dvoid *
264      (malocfp :pointer-void)                    ; dvoid *(*)
265      (ralocfp :pointer-void)                    ; dvoid *(*)
266      (mfreefp (* :pointer-void)))
267   :returning :int)
268
269 (uffi:def-function "OCIEnvInit"
270     ((envpp :pointer-void)                      ; OCIEnv **
271      (mode ub4)                                 ; ub4
272      (xtramem-sz size_t)                        ; size_t
273      (usermempp (* :pointer-void)))
274   :returning :int)
275
276
277 (uffi:def-function "OCIHandleAlloc"
278     ((parenth      :pointer-void)               ; const dvoid *
279      (hndlpp       (* :pointer-void))           ; dvoid **
280      (type         ub4)                         ; ub4
281      (xtramem_sz   size_t)                      ; size_t
282      (usrmempp     (* :pointer-void)))
283   :returning :int)
284
285 (defstruct oci-handle
286   (type :unknown)
287   (pointer (uffi:allocate-foreign-object :pointer-void)))
288
289 (defvar *oci-initialized* nil)
290 (defvar *oci-env* nil)
291
292 (defvar *oci-handle-types*
293   '(:error                              ; error report handle (OCIError)
294     :service-context                    ; service context handle (OCISvcCtx)
295     :statement                          ; statement (application request) handle (OCIStmt)
296     :describe                           ; select list description handle (OCIDescribe)
297     :server                             ; server context handle (OCIServer)
298     :session                            ; user session handle (OCISession)
299     :transaction                        ; transaction context handle (OCITrans)
300     :complex-object                     ; complex object retrieval handle (OCIComplexObject)
301     :security))                         ; security handle (OCISecurity)
302
303
304
305 (defun oci-init (&key (mode +oci-default+))
306   (let ((x (OCIInitialize mode +null-void-pointer+ +null-void-pointer+
307                           +null-void-pointer+ +null-void-pointer-pointer+)))
308     (if (= x 0)
309         (let ((env (uffi:allocate-foreign-object :pointer-void)))
310           (setq *oci-initialized* mode)
311           (let ((x (OCIEnvInit env +oci-default+ 0 +null-void-pointer+)))
312             (format t ";; OEI: returned ~d~%" x)
313             (setq *oci-env* env))))))
314
315 (defun oci-check-return (value)
316   (when (= value +oci-invalid-handle+)
317     (error 'sql-database-error :message "Invalid Handle")))
318
319 (defun oci-get-handle (&key type)
320   (if (null *oci-initialized*)
321       (oci-init))
322   (case type
323     (:error
324      (let ((ptr (uffi:allocate-foreign-object :pointer-void)))
325        (let ((x (OCIHandleAlloc
326                  (uffi:deref-pointer *oci-env* void-pointer)
327                  ptr
328                  +oci-default+
329                  0
330                  +null-void-pointer-pointer+)))
331          (oci-check-return x)
332          ptr)))
333     (:service-context
334      "OCISvcCtx")
335     (:statement
336      "OCIStmt")
337     (:describe
338      "OCIDescribe")
339     (:server
340      "OCIServer")
341     (:session
342      "OCISession")
343     (:transaction
344      "OCITrans")
345     (:complex-object
346      "OCIComplexObject")
347     (:security
348      "OCISecurity")
349     (t
350      (error 'sql-database-error
351             :message
352             (format nil "'~s' is not a valid OCI handle type" type)))))
353
354 (defun oci-environment ()
355   (let ((envhp (oci-get-handle :type :env)))
356     (oci-env-init envhp 0 0 +null-void-pointer+)
357     envhp))