r9525: 30 May 2004 Kevin Rosenberg <kevin@rosenberg.net>
[clsql.git] / db-mysql / mysql-api.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          mysql-api.lisp
6 ;;;; Purpose:       Low-level MySQL interface using UFFI
7 ;;;; Programmers:   Kevin M. Rosenberg based on 
8 ;;;;                Original code by Pierre R. Mai 
9 ;;;; Date Started:  Feb 2002
10 ;;;;
11 ;;;; $Id$
12 ;;;;
13 ;;;; This file, part of CLSQL, is Copyright (c) 2002 by Kevin M. Rosenberg
14 ;;;; and Copyright (c) 1999-2001 by Pierre R. Mai
15 ;;;;
16 ;;;; CLSQL users are granted the rights to distribute and use this software
17 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
18 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
19 ;;;; *************************************************************************
20
21 (in-package #:mysql)
22
23 ;;;; Modifications from original code
24 ;;;;  - Updated C-structures to conform to structures in MySQL 3.23.46
25 ;;;;  - Changed from CMUCL interface to UFFI
26 ;;;;  - Added and call a C-helper file to support 64-bit integers
27 ;;;;    that are used in a few routines.
28 ;;;;  - Removed all references to interiors of C-structions, this will
29 ;;;;    increase robustness when MySQL's internal structures change.
30  
31 ;;;; Type definitions
32
33 ;;; Basic Types
34
35 (uffi:def-foreign-type mysql-socket :int)
36 (uffi:def-foreign-type mysql-bool :char)
37 (uffi:def-foreign-type mysql-byte :unsigned-char)
38
39 (uffi:def-enum mysql-net-type
40     (:tcp-ip
41      :socket
42      :named-pipe))
43
44 (uffi:def-struct mysql-net
45     (vio :pointer-void)
46   (fd mysql-socket)
47   (fcntl :int)
48   (buff (* :unsigned-char))
49   (buff-end (* :unsigned-char))
50   (write-pos (* :unsigned-char))
51   (read-pos (* :unsigned-char))
52   (last-error (:array :char 200))
53   (last-errno :unsigned-int)
54   (max-packet :unsigned-int)
55   (timeout :unsigned-int)
56   (pkt-nr :unsigned-int)
57   (error mysql-bool)
58   (return-errno mysql-bool)
59   (compress mysql-bool)
60   (no-send-ok mysql-bool)
61   (remain-in-buf :unsigned-long)
62   (length :unsigned-long)
63   (buf-length :unsigned-long)
64   (where-b :unsigned-long)
65   (return-status (* :unsigned-int))
66   (reading-or-writing :unsigned-char)
67   (save-char :char))
68
69 ;;; Mem-Root
70 (uffi:def-struct mysql-used-mem
71     (next :pointer-self)
72   (left :unsigned-int)
73   (size :unsigned-int))
74
75 (uffi:def-struct mysql-mem-root
76     (free (:struct-pointer mysql-used-mem))
77   (used (:struct-pointer mysql-used-mem))
78   (pre-alloc (:struct-pointer mysql-used-mem))
79   (min-alloc :unsigned-int)
80   (block-size :unsigned-int)
81   (error-handler :pointer-void))
82
83 ;;; MYSQL-FIELD
84 (uffi:def-enum mysql-field-types
85     (:decimal
86      :tiny
87      :short
88      :long
89      :float
90      :double
91      :null
92      :timestamp
93      :longlong
94      :int24
95      :date
96      :time
97      :datetime
98      :year
99      :newdate
100      (:enum 247)
101      (:set 248)
102      (:tiny-blob 249)
103      (:medium-blob 250)
104      (:long-blob 251)
105      (:blob 252)
106      (:var-string 253)
107      (:string 254)))
108
109 #+mysql-client-v3
110 (uffi:def-struct mysql-field
111     (name (* :char))
112   (table (* :char))
113   (def (* :char))
114   (type mysql-field-types)
115   (length :unsigned-int)
116   (max-length :unsigned-int)
117   (flags :unsigned-int)
118   (decimals :unsigned-int))
119
120 ;; structure changed in mysql 4 client
121 #+(and mysql-client-v4 (not mysql-client-v4.1))
122 (uffi:def-struct mysql-field
123     (name (* :char))
124   (table (* :char))
125   (org_table (* :char))
126   (db (* :char))
127   (def (* :char))
128   (length :unsigned-long)
129   (max-length :unsigned-long)
130   (flags :unsigned-int)
131   (decimals :unsigned-int)
132   (type mysql-field-types))
133
134 #+mysql-client-v4.1
135 (uffi:def-struct mysql-field
136     (name (* :char))
137   (org_table (* :char))
138   (table (* :char))
139   (org_table (* :char))
140   (db (* :char))
141   (catalog_db (* :char))
142   (def (* :char))
143   (length :unsigned-long)
144   (max-length :unsigned-long)
145   (name-length :unsigned-int)
146   (org-name-length :unsigned-int)
147   (table-length :unsigned-int)
148   (org-table-length :unsigned-int)
149   (db-length :unsigned-int)
150   (catalog-length :unsigned-int)
151   (def-length :unsigned-int)
152   (flags :unsigned-int)
153   (decimals :unsigned-int)
154   (charsetnr :unsigned-int)
155   (type mysql-field-types))
156
157 ;;; MYSQL-ROWS
158
159 (uffi:def-array-pointer mysql-row (* :unsigned-char))
160
161 (uffi:def-array-pointer mysql-field-vector (* mysql-field))
162
163 (uffi:def-foreign-type mysql-field-offset :unsigned-int)
164
165 (uffi:def-struct mysql-rows
166     (next :pointer-self)
167   (data mysql-row))
168
169 (uffi:def-foreign-type mysql-row-offset (:struct-pointer mysql-rows))
170
171 (uffi:def-struct mysql-data
172     (rows-high32 :unsigned-long)
173   (rows-low32 :unsigned-long)
174   (fields :unsigned-int)
175   (data (:struct-pointer mysql-rows))
176   (alloc (:struct mysql-mem-root)))
177
178 ;;; MYSQL
179 (uffi:def-struct mysql-options
180     (connect-timeout :unsigned-int)
181   (client-flag :unsigned-int)
182   (compress mysql-bool)
183   (named-pipe mysql-bool)
184   (port :unsigned-int)
185   (host (* :char))
186   (init-command (* :char))
187   (user (* :char))
188   (password (* :char))
189   (unix-socket (* :char))
190   (db (* :char))
191   (my-cnf-file (* :char))
192   (my-cnf-group (* :char))
193   (charset-dir (* :char))
194   (charset-name (* :char))
195   (use-ssl mysql-bool)
196   (ssl-key (* :char))
197   (ssl-cert (* :char))
198   (ssl-ca (* :char))
199   (ssl-capath (* :char)))
200
201 (uffi:def-enum mysql-option
202     (:connect-timeout
203      :compress
204      :named-pipe
205      :init-command
206      :read-default-file
207      :read-default-group))
208
209 (uffi:def-enum mysql-status
210     (:ready 
211      :get-result
212      :use-result))
213
214 (uffi:def-struct mysql-mysql
215     (net (:struct mysql-net))
216   (connected-fd (* :char))
217   (host (* :char))
218   (user (* :char))
219   (passwd (* :char))
220   (unix-socket (* :char))
221   (server-version (* :char))
222   (host-info (* :char))
223   (info (* :char))
224   (db (* :char))
225   (port :unsigned-int)
226   (client-flag :unsigned-int)
227   (server-capabilities :unsigned-int)
228   (protocol-version :unsigned-int)
229   (field-count :unsigned-int)
230   (server-status :unsigned-int)
231   (thread-id :unsigned-long)
232   (affected-rows-high32 :unsigned-long)
233   (affected-rows-low32 :unsigned-long)
234   (insert-id-high32 :unsigned-long)
235   (insert-id-low32 :unsigned-long)
236   (extra-info-high32 :unsigned-long)
237   (extra-info-low32 :unsigned-long)
238   (packet-length :unsigned-long)
239   (status mysql-status)
240   (fields (:struct-pointer mysql-field))
241   (field-alloc (:struct mysql-mem-root))
242   (free-me mysql-bool)
243   (reconnect mysql-bool)
244   (options (:struct mysql-options))
245   (scramble-buff (:array :char 9))
246   (charset :pointer-void)
247   (server-language :unsigned-int))
248
249
250 ;;; MYSQL-RES
251 (uffi:def-struct mysql-mysql-res
252     (row-count-high32 :unsigned-long)
253   (row-count-low32 :unsigned-long)
254   (field-count :unsigned-int)
255   (current-field :unsigned-int)
256   (fields (:struct-pointer mysql-field))
257   (data (:struct-pointer mysql-data))
258   (data-cursor (:struct-pointer mysql-rows))
259   (field-alloc (:struct mysql-mem-root))
260   (row mysql-row)
261   (current-row mysql-row)
262   (lengths (* :unsigned-long))
263   (handle (:struct-pointer mysql-mysql))
264   (eof mysql-bool))
265
266 ;;;; The Foreign C routines
267 (declaim (inline mysql-init))
268 (uffi:def-function "mysql_init"
269   ((mysql (* mysql-mysql)))
270   :module "mysql" 
271   :returning (* mysql-mysql))
272
273 #-mysql-client-v4
274 (declaim (inline mysql-connect))
275 #-mysql-client-v4
276 (uffi:def-function "mysql_connect"
277     ((mysql (* mysql-mysql))
278      (host :cstring)
279      (user :cstring)
280      (passwd :cstring))
281   :module "mysql"
282   :returning (* mysql-mysql))
283
284 ;; Need to comment this out for LW 4.2.6
285 ;; ? bug in LW version
286 #-lispworks (declaim (inline mysql-real-connect))
287 (uffi:def-function "mysql_real_connect"
288     ((mysql (* mysql-mysql))
289      (host :cstring)
290      (user :cstring)
291      (passwd :cstring)
292      (db :cstring)
293      (port :unsigned-int)
294      (unix-socket :cstring)
295      (clientflag :unsigned-long))
296   :module "mysql"
297   :returning (* mysql-mysql))
298
299 (declaim (inline mysql-close))
300 (uffi:def-function "mysql_close"
301     ((sock (* mysql-mysql)))
302   :module "mysql"
303   :returning :void)
304
305 (declaim (inline mysql-select-db))
306 (uffi:def-function "mysql_select_db"
307   ((mysql (* mysql-mysql))
308    (db :cstring))
309   :module "mysql"
310   :returning :int)
311
312 (declaim (inline mysql-query))
313 (uffi:def-function "mysql_query"
314     ((mysql (* mysql-mysql))
315      (query :cstring))
316   :module "mysql"
317   :returning :int)
318
319  ;;; I doubt that this function is really useful for direct Lisp usage,
320 ;;; but it is here for completeness...
321
322 (declaim (inline mysql-real-query))
323 (uffi:def-function "mysql_real_query"
324     ((mysql (* mysql-mysql))
325      (query :cstring)
326      (length :unsigned-int))
327   :module "mysql"
328   :returning :int)
329
330 #-mysql-client-v4
331 (declaim (inline mysql-create-db))
332 #-mysql-client-v4
333 (uffi:def-function "mysql_create_db"
334   ((mysql (* mysql-mysql))
335    (db :cstring))
336   :module "mysql"
337   :returning :int)
338
339 #-mysql-client-v4
340 (declaim (inline mysql-drop-db))
341 #-mysql-client-v4
342 (uffi:def-function "mysql_drop_db"
343     ((mysql (* mysql-mysql))
344      (db :cstring))
345   :module "mysql"
346   :returning :int)
347
348 (declaim (inline mysql-shutdown))
349 (uffi:def-function "mysql_shutdown"
350   ((mysql (* mysql-mysql)))
351   :module "mysql"
352   :returning :int)
353
354 (declaim (inline mysql-dump-debug-info))
355 (uffi:def-function "mysql_dump_debug_info"
356   ((mysql (* mysql-mysql)))
357   :module "mysql"
358   :returning :int)
359
360 (declaim (inline mysql-refresh))
361 (uffi:def-function "mysql_refresh"
362   ((mysql (* mysql-mysql))
363    (refresh-options :unsigned-int))
364   :module "mysql"
365   :returning :int)
366
367 (declaim (inline mysql-kill))
368 (uffi:def-function "mysql_kill"
369     ((mysql (* mysql-mysql))
370      (pid :unsigned-long))
371   :module "mysql"
372   :returning :int)
373
374 (declaim (inline mysql-ping))
375 (uffi:def-function "mysql_ping"
376     ((mysql (* mysql-mysql)))
377   :module "mysql"
378   :returning :int)
379
380 (declaim (inline mysql-stat))
381 (uffi:def-function "mysql_stat"
382   ((mysql (* mysql-mysql)))
383   :module "mysql"
384   :returning :cstring)
385
386 (declaim (inline mysql-get-server-info))
387 (uffi:def-function "mysql_get_server_info"
388     ((mysql (* mysql-mysql)))
389   :module "mysql"
390   :returning :cstring)
391
392 (declaim (inline mysql-get-host-info))
393 (uffi:def-function "mysql_get_host_info"
394     ((mysql (* mysql-mysql)))
395   :module "mysql"
396   :returning :cstring)
397
398 (declaim (inline mysql-get-proto-info))
399 (uffi:def-function "mysql_get_proto_info"
400   ((mysql (* mysql-mysql)))
401   :module "mysql"
402   :returning :unsigned-int)
403
404 (declaim (inline mysql-list-dbs))
405 (uffi:def-function "mysql_list_dbs"
406   ((mysql (* mysql-mysql))
407    (wild :cstring))
408   :module "mysql"
409   :returning (* mysql-mysql-res))
410
411 (declaim (inline mysql-list-tables))
412 (uffi:def-function "mysql_list_tables"
413   ((mysql (* mysql-mysql))
414    (wild :cstring))
415   :module "mysql"
416   :returning (* mysql-mysql-res))
417
418 (declaim (inline mysql-list-fields))
419 (uffi:def-function "mysql_list_fields"
420   ((mysql (* mysql-mysql))
421    (table :cstring)
422    (wild :cstring))
423   :module "mysql"
424   :returning (* mysql-mysql-res))
425
426 (declaim (inline mysql-list-processes))
427 (uffi:def-function "mysql_list_processes"
428   ((mysql (* mysql-mysql)))
429   :module "mysql"
430   :returning (* mysql-mysql-res))
431
432 (declaim (inline mysql-store-result))
433 (uffi:def-function "mysql_store_result"
434   ((mysql (* mysql-mysql)))
435   :module "mysql"
436   :returning (* mysql-mysql-res))
437
438 (declaim (inline mysql-use-result))
439 (uffi:def-function "mysql_use_result"
440   ((mysql (* mysql-mysql)))
441   :module "mysql"
442   :returning (* mysql-mysql-res))
443
444 (declaim (inline mysql-options))
445 (uffi:def-function "mysql_options"
446   ((mysql (* mysql-mysql))
447    (option mysql-option)
448    (arg :cstring))
449   :module "mysql"
450   :returning :int)
451
452 (declaim (inline mysql-free-result))
453 (uffi:def-function "mysql_free_result"
454     ((res (* mysql-mysql-res)))
455   :module "mysql"
456   :returning :void)
457
458 (declaim (inline mysql-row-seek))
459 (uffi:def-function "mysql_row_seek"
460   ((res (* mysql-mysql-res))
461    (offset mysql-row-offset))
462   :module "mysql"
463   :returning mysql-row-offset)
464
465 (declaim (inline mysql-field-seek))
466 (uffi:def-function "mysql_field_seek"
467   ((res (* mysql-mysql-res))
468   (offset mysql-field-offset))
469   :module "mysql"
470   :returning mysql-field-offset)
471
472 (declaim (inline mysql-fetch-row))
473 (uffi:def-function "mysql_fetch_row"
474     ((res (* mysql-mysql-res)))
475   :module "mysql"
476   :returning (* (* :unsigned-char)))
477
478 (declaim (inline mysql-fetch-lengths))
479 (uffi:def-function "mysql_fetch_lengths"
480   ((res (* mysql-mysql-res)))
481   :module "mysql"
482   :returning (* :unsigned-long))
483
484 (declaim (inline mysql-fetch-field))
485 (uffi:def-function "mysql_fetch_field"
486   ((res (* mysql-mysql-res)))
487   :module "mysql"
488   :returning (* mysql-field))
489
490 (declaim (inline mysql-fetch-fields))
491 (uffi:def-function "mysql_fetch_fields"
492   ((res (* mysql-mysql-res)))
493   :module "mysql"
494   :returning (* mysql-field))
495
496 (declaim (inline mysql-fetch-field-direct))
497 (uffi:def-function "mysql_fetch_field_direct"
498   ((res (* mysql-mysql-res))
499    (field-num :unsigned-int))
500   :module "mysql"
501   :returning (* mysql-field))
502
503 (declaim (inline mysql-escape-string))
504 (uffi:def-function "mysql_escape_string"
505     ((to :cstring)
506      (from :cstring)
507      (length :unsigned-int))
508   :module "mysql"
509   :returning :unsigned-int)
510
511 (declaim (inline mysql-debug))
512 (uffi:def-function "mysql_debug"
513     ((debug :cstring))
514   :module "mysql"
515   :returning :void)
516
517 (declaim (inline clsql-mysql-num-rows))
518 (uffi:def-function "clsql_mysql_num_rows"
519     ((res (* mysql-mysql-res))
520      (p-high32 (* :unsigned-int)))
521   :module "clsql-mysql"
522   :returning :unsigned-int)
523
524
525 ;;;; Equivalents of C Macro definitions for accessing various fields
526 ;;;; in the internal MySQL Datastructures
527
528
529 (declaim (inline mysql-num-rows))
530 (defun mysql-num-rows (res)
531   (uffi:with-foreign-object (p-high32 :unsigned-int)
532     (let ((low32 (clsql-mysql-num-rows res p-high32))
533           (high32 (uffi:deref-pointer p-high32 :unsigned-int)))
534       (if (zerop high32)
535           low32
536         (make-64-bit-integer high32 low32)))))
537
538 (uffi:def-function "clsql_mysql_affected_rows"
539     ((mysql (* mysql-mysql))
540      (p-high32 (* :unsigned-int)))
541   :returning :unsigned-int
542   :module "clsql-mysql")
543
544 (defun mysql-affected-rows (mysql)
545   (uffi:with-foreign-object (p-high32 :unsigned-int)
546     (let ((low32 (clsql-mysql-affected-rows mysql p-high32))
547           (high32 (uffi:deref-pointer p-high32 :unsigned-int)))
548       (if (zerop high32)
549           low32
550         (make-64-bit-integer high32 low32)))))
551
552 (uffi:def-function "clsql_mysql_insert_id"
553     ((res (* mysql-mysql))
554      (p-high32 (* :unsigned-int)))
555   :returning :unsigned-int
556   :module "clsql-mysql")
557
558 (defun mysql-insert-id (mysql)
559   (uffi:with-foreign-object (p-high32 :unsigned-int)
560   (let ((low32 (clsql-mysql-insert-id mysql p-high32))
561         (high32 (uffi:deref-pointer p-high32 :unsigned-int)))
562     (if (zerop high32)
563         low32
564       (make-64-bit-integer high32 low32)))))
565
566
567 (declaim (inline mysql-num-fields))
568 (uffi:def-function "mysql_num_fields" 
569   ((res (* mysql-mysql-res)))
570   :returning :unsigned-int
571   :module "mysql")
572                  
573 (declaim (inline clsql-mysql-eof))
574 (uffi:def-function ("mysql_eof" clsql-mysql-eof)
575   ((res (* mysql-mysql-res)))
576   :returning :char
577   :module "mysql")
578
579 (declaim (inline mysql-eof))
580 (defun mysql-eof (res)
581   (if (zerop (clsql-mysql-eof res))
582       nil
583     t))
584
585 (declaim (inline mysql-error))
586 (uffi:def-function ("mysql_error" mysql-error)
587   ((mysql (* mysql-mysql)))
588   :returning :cstring
589   :module "mysql")
590
591 (declaim (inline mysql-error-string))
592 (defun mysql-error-string (mysql)
593   (uffi:convert-from-cstring (mysql-error mysql)))
594
595 (declaim (inline mysql-errno))
596 (uffi:def-function "mysql_errno"
597   ((mysql (* mysql-mysql)))
598   :returning :unsigned-int
599   :module "mysql")
600
601 (declaim (inline mysql-info))
602 (uffi:def-function ("mysql_info" mysql-info)
603   ((mysql (* mysql-mysql)))
604   :returning :cstring
605   :module "mysql")
606
607 (declaim (inline mysql-info-string))
608 (defun mysql-info-string (mysql)
609   (uffi:convert-from-cstring (mysql-info mysql)))
610
611 (declaim (inline clsql-mysql-data-seek))
612 (uffi:def-function "clsql_mysql_data_seek"
613   ((res (* mysql-mysql-res))
614    (offset-high32 :unsigned-int)
615    (offset-low32 :unsigned-int))
616   :module "clsql-mysql"
617   :returning :void)
618
619
620 (defun mysql-data-seek (res offset)
621   (multiple-value-bind (high32 low32) (split-64-bit-integer offset)
622     (clsql-mysql-data-seek res high32 low32)))