r9510: * db-sqlite: Remove clisp support since clisp can not run CLSQL
[clsql.git] / db-sqlite / sqlite-api.lisp
diff --git a/db-sqlite/sqlite-api.lisp b/db-sqlite/sqlite-api.lisp
new file mode 100644 (file)
index 0000000..9866749
--- /dev/null
@@ -0,0 +1,317 @@
+;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+;;;; *************************************************************************
+;;;; FILE IDENTIFICATION
+;;;;
+;;;; Name:     sqlite-api-uffi.lisp
+;;;; Purpose:  Low-level SQLite interface using UFFI
+;;;; Authors:  Aurelio Bignoli and Kevin Rosenberg
+;;;; Created:  Nov 2003
+;;;;
+;;;; $Id$
+;;;;
+;;;; This file, part of CLSQL, is Copyright (c) 2003 by Aurelio Bignoli
+;;;; and Copyright (c) 2003-2004 by Kevin Rosenberg
+;;;;
+;;;; CLSQL users are granted the rights to distribute and use this software
+;;;; as governed by the terms of the Lisp Lesser GNU Public License
+;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
+;;;; *************************************************************************
+
+(in-package #:cl-user)
+
+(defpackage #:sqlite
+  (:use #:common-lisp #:uffi)
+    (:export
+           ;;; Conditions
+           #:sqlite-error
+          #:sqlite-error-code
+          #:sqlite-error-message
+          
+          ;;; Core API.
+           #:sqlite-open
+          #:sqlite-close
+
+          ;;; New API.
+          #:sqlite-compile
+          #:sqlite-step
+          #:sqlite-finalize
+          
+          ;;; Extended API.
+          #:sqlite-get-table
+          #:sqlite-free-table
+          #:sqlite-version             ; Defined as constant.
+          #:sqlite-encoding            ; Defined as constant.
+          #:sqlite-last-insert-rowid
+
+          ;;; Utility functions.
+          #:make-null-row
+          #:make-null-vm
+          #:null-row-p
+          #:sqlite-aref
+          #:sqlite-free-row
+          
+          ;;; Types.
+          #:sqlite-row
+          #:sqlite-row-pointer
+          #:sqlite-row-pointer-type
+          #:sqlite-vm-pointer))
+
+(in-package #:sqlite)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Return values for sqlite_exec() and sqlite_step()
+;;;;
+(defconstant SQLITE-OK            0  "Successful result")
+(defconstant SQLITE-ERROR         1  "SQL error or missing database")
+(defconstant SQLITE-ROW         100  "sqlite_step() has another row ready")
+(defconstant SQLITE-DONE        101  "sqlite_step() has finished executing")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Conditions.
+;;;;
+(define-condition sqlite-error ()
+  ((message :initarg :message :reader sqlite-error-message :initform "")
+   (code :initarg :code :reader sqlite-error-code))
+  (:report (lambda (condition stream)
+             (let ((code (sqlite-error-code condition)))
+               (format stream "SQLite error [~A]: ~A"
+                      code (sqlite-error-message condition))))))
+
+(defun signal-sqlite-error (code &optional message)
+  (let ((condition
+        (make-condition 'sqlite-error
+                        :code code
+                        :message (if message
+                                     message
+                                   (uffi:convert-from-cstring
+                                    (sqlite-error-string code))))))
+    (unless (signal condition)
+      (invoke-debugger condition))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Foreign types definitions.
+;;;;
+(def-foreign-type errmsg (* :unsigned-char))
+(def-foreign-type sqlite-db :pointer-void)
+(def-foreign-type sqlite-vm :pointer-void)
+(def-foreign-type string-pointer (* (* :unsigned-char)))
+(def-foreign-type sqlite-row-pointer (* (* :unsigned-char)))
+
+(defvar +null-errmsg-pointer+ (make-null-pointer 'errmsg))
+(defvar +null-string-pointer-pointer+ (make-null-pointer 'string-pointer))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; Lisp types used in declarations.
+;;;;
+(def-type sqlite-db-type sqlite-db)
+(def-type sqlite-row string-pointer)
+(def-type sqlite-row-pointer-type (* string-pointer))
+(def-type sqlite-vm-pointer (* sqlite-vm))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Library functions.
+;;;;
+(defmacro def-sqlite-function (name args &key (returning :void))
+  `(def-function ,name ,args
+    :module "sqlite"
+    :returning ,returning))
+
+(def-sqlite-function
+    "sqlite_error_string"
+    ((error-code :int))
+  :returning :cstring)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Core API.
+;;;;
+(declaim (inline %open))
+(def-sqlite-function
+    ("sqlite_open" %open)
+    ((dbname :cstring)
+     (mode :int)
+     (error-message (* errmsg)))
+  :returning sqlite-db)
+
+(declaim (inline sqlite-close))
+(def-sqlite-function
+    "sqlite_close"
+    ((db sqlite-db)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; New API.
+;;;;
+(declaim (inline %compile))
+(def-sqlite-function
+    ("sqlite_compile" %compile)
+    ((db sqlite-db)
+     (sql :cstring)
+     (sql-tail (* (* :unsigned-char)))
+     (vm (* sqlite-vm))
+     (error-message (* errmsg)))
+  :returning :int)
+
+(declaim (inline %step))
+(def-sqlite-function
+    ("sqlite_step" %step)
+    ((vm sqlite-vm)
+     (cols-n (* :int))
+     (cols (* (* (* :unsigned-char))))
+     (col-names (* (* (* :unsigned-char)))))
+  :returning :int)
+
+(declaim (inline %finalize))
+(def-sqlite-function
+    ("sqlite_finalize" %finalize)
+    ((vm sqlite-vm)
+     (error-message (* errmsg)))
+  :returning :int)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Extended API.
+;;;;
+(declaim (inline sqlite-last-insert-rowid))
+(def-sqlite-function
+    "sqlite_last_insert_rowid"
+    ((db sqlite-db))
+  :returning :int)
+
+(declaim (inline %get-table))
+(def-sqlite-function
+    ("sqlite_get_table" %get-table)
+    ((db sqlite-db)
+     (sql :cstring)
+     (result (* (* (* :unsigned-char))))
+     (rows-n (* :int))
+     (cols-n (* :int))
+     (error-message (* errmsg)))
+  :returning :int)
+
+(declaim (inline %free-table))
+(def-sqlite-function
+    ("sqlite_free_table" %free-table)
+    ((rows :pointer-void)))
+
+(declaim (inline sqlite-libversion))
+(def-sqlite-function
+    "sqlite_libversion"
+    ()
+  :returning :cstring)
+
+(declaim (inline sqlite-libencoding))
+(def-sqlite-function
+    "sqlite_libencoding"
+    ()
+  :returning :cstring)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Wrapper functions.
+;;;;
+(defparameter sqlite-version (sqlite-libversion))
+(defparameter sqlite-encoding (sqlite-libencoding))
+
+(defun sqlite-open (db-name &optional (mode 0))
+  (with-cstring (db-name-native db-name) 
+    (let ((db (%open db-name-native mode +null-errmsg-pointer+)))
+      (if (null-pointer-p db)
+         (signal-sqlite-error SQLITE-ERROR
+                              (format nil "unable to open ~A" db-name))
+         db))))
+
+(defun sqlite-compile (db sql)
+  (with-cstring (sql-native sql)
+    (let ((vm (allocate-foreign-object 'sqlite-vm)))
+      (with-foreign-object (sql-tail '(* :unsigned-char))
+       (let ((result (%compile db sql-native sql-tail vm +null-errmsg-pointer+)))
+         (if (= result SQLITE-OK)
+             vm
+             (progn
+               (free-foreign-object vm)
+               (signal-sqlite-error result))))))))
+
+(defun sqlite-step (vm)
+  (declare (type sqlite-vm-pointer vm))
+  (with-foreign-object (cols-n :int)
+    (let ((cols (allocate-foreign-object '(* (* :unsigned-char))))
+         (col-names (allocate-foreign-object '(* (* :unsigned-char)))))
+      (declare (type sqlite-row-pointer-type cols col-names))
+      (let ((result (%step (deref-pointer vm 'sqlite-vm)
+                          cols-n cols col-names)))
+       (cond
+         ((= result SQLITE-ROW)
+          (let ((n (deref-pointer cols-n :int)))
+            (values n cols col-names)))
+         ((= result SQLITE-DONE)
+          (free-foreign-object cols)
+          (free-foreign-object col-names)
+          (values 0 +null-string-pointer-pointer+ +null-string-pointer-pointer+))
+         (t
+          (free-foreign-object cols)
+          (free-foreign-object col-names)
+          (signal-sqlite-error result)))))))
+
+(defun sqlite-finalize (vm)
+  (declare (type sqlite-vm-pointer vm))
+  (let ((result (%finalize (deref-pointer vm 'sqlite-vm) +null-errmsg-pointer+)))
+    (if (= result SQLITE-OK)
+       (progn
+         (free-foreign-object vm)
+         t)
+       (signal-sqlite-error result))))
+
+(defun sqlite-get-table (db sql)
+  (declare (type sqlite-db-type db))
+  (with-cstring (sql-native sql)
+    (let ((rows (allocate-foreign-object '(* (* :unsigned-char)))))
+      (declare (type sqlite-row-pointer-type rows))
+      (with-foreign-object (rows-n :int)
+       (with-foreign-object (cols-n :int)
+         (let ((result (%get-table db sql-native rows rows-n cols-n +null-errmsg-pointer+)))
+           (if (= result SQLITE-OK)
+               (let ((cn (deref-pointer cols-n :int))
+                     (rn (deref-pointer rows-n :int)))
+                 (values rows rn cn))
+               (progn
+                 (free-foreign-object rows)
+                 (signal-sqlite-error result)))))))))
+
+(declaim (inline sqlite-free-table))
+(defun sqlite-free-table (table)
+  (declare (type sqlite-row-pointer-type table))
+  (%free-table (deref-pointer table 'sqlite-row-pointer)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;
+;;;; Utility functions.
+;;;;
+(declaim (inline make-null-row))
+(defun make-null-row ()
+  +null-string-pointer-pointer+)
+
+(declaim (inline make-null-vm))
+(defun make-null-vm ()
+  (uffi:make-null-pointer 'sqlite-vm))
+
+(declaim (inline null-row-p))
+(defun null-row-p (row)
+  (null-pointer-p row))
+
+(declaim (inline sqlite-aref))
+(defun sqlite-aref (a n)
+  (declare (type sqlite-row-pointer-type a))
+  (convert-from-foreign-string
+   (deref-array (deref-pointer a 'sqlite-row-pointer) '(:array (* :unsigned-char)) n)))
+
+(declaim (inline sqlite-free-row))
+(defun sqlite-free-row (row)
+  (declare (type sqlite-row-pointer-type row))
+  (free-foreign-object row))