1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
6 ;;;; Purpose: High-level SQL interface
7 ;;;; Programmers: Kevin M. Rosenberg based on
8 ;;;; Original code by Pierre R. Mai
9 ;;;; Date Started: Feb 2002
11 ;;;; $Id: sql.cl,v 1.19 2002/09/17 17:16:43 kevin Exp $
13 ;;;; This file, part of CLSQL, is Copyright (c) 2002 by Kevin M. Rosenberg
14 ;;;; and Copyright (c) 1999-2001 by Pierre R. Mai
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 ;;;; *************************************************************************
21 (declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))
22 (in-package :clsql-sys)
25 ;;; - to use CMUCL-COMPAT library
26 ;;; - fix format strings in error messages
30 ;;; Simple implementation of SQL along the lines of Harlequin's Common SQL
35 (defvar *connect-if-exists* :error
36 "Default value for the if-exists parameter of connect calls.")
38 (defvar *connected-databases* nil
39 "List of active database objects.")
41 (defun connected-databases ()
42 "Return the list of active database objects."
43 *connected-databases*)
45 (defvar *default-database* nil
46 "Specifies the default database to be used.")
48 (defun find-database (database &optional (errorp t))
51 ;; Return the database object itself
54 (or (find database (connected-databases)
60 :format-control "There exists no database called ~A."
61 :format-arguments (list database)))))))
63 (defun connect (connection-spec
64 &key (if-exists *connect-if-exists*)
65 (database-type *default-database-type*)
67 "Connects to a database of the given database-type, using the type-specific
68 connection-spec. if-exists is currently ignored.
69 If pool is t the the connection will be taken from the general pool,
70 if pool is a conn-pool object the connection will be taken from this pool.
73 (acquire-from-pool connection-spec database-type pool)
74 (let* ((db-name (database-name-from-spec connection-spec database-type))
75 (old-db (unless (eq if-exists :new) (find-database db-name nil)))
81 ; (database-connect connection-spec database-type)))
84 (database-connect connection-spec database-type))
85 (warn 'clsql-exists-warning :old-db old-db :new-db result))
88 (error 'clsql-exists-error :old-db old-db)
90 :report "Create a new connection."
92 (database-connect connection-spec database-type)))
94 :report "Use the existing connection."
95 (setq result old-db))))
98 (warn 'clsql-exists-warning :old-db old-db :new-db old-db))
100 (setq result old-db)))
102 (database-connect connection-spec database-type)))
104 (pushnew result *connected-databases*)
105 (setq *default-database* result)
109 (defun disconnect (&key (database *default-database*))
110 "Closes the connection to database. Resets *default-database* if that
111 database was disconnected and only one other connection exists.
112 if the database is from a pool it will be released to this pool."
113 (if (conn-pool database)
114 (release-to-pool database)
115 (when (database-disconnect database)
116 (setq *connected-databases* (delete database *connected-databases*))
117 (when (eq database *default-database*)
118 (setq *default-database* (car *connected-databases*)))
119 (change-class database 'closed-database)
122 ;;; Basic operations on databases
124 (defmethod query (query-expression &key (database *default-database*)
126 "Execute the SQL query expression query-expression on the given database.
127 Returns a list of lists of values of the result of that expression."
128 (database-query query-expression database types))
132 (defmethod execute-command (sql-expression &key (database *default-database*))
133 "Execute the SQL command expression sql-expression on the given database.
134 Returns true on success or nil on failure."
135 (database-execute-command sql-expression database))
139 (defun map-query (output-type-spec function query-expression
140 &key (database *default-database*)
142 "Map the function over all tuples that are returned by the query in
143 query-expression. The results of the function are collected as
144 specified in output-type-spec and returned like in MAP."
145 ;; DANGER Will Robinson: Parts of the code for implementing
146 ;; map-query (including the code below and the helper functions
147 ;; called) are highly CMU CL specific.
148 ;; KMR -- these have been replaced with cross-platform instructions above
149 (macrolet ((type-specifier-atom (type)
150 `(if (atom ,type) ,type (car ,type))))
151 (case (type-specifier-atom output-type-spec)
153 (map-query-for-effect function query-expression database types))
155 (map-query-to-list function query-expression database types))
156 ((simple-vector simple-string vector string array simple-array
157 bit-vector simple-bit-vector base-string
159 (map-query-to-simple output-type-spec function query-expression database types))
161 (funcall #'map-query (cmucl-compat:result-type-or-lose output-type-spec t)
162 function query-expression :database database :types types)))))
164 (defun map-query-for-effect (function query-expression database types)
165 (multiple-value-bind (result-set columns)
166 (database-query-result-set query-expression database :full-set nil
170 (do ((row (make-list columns)))
171 ((not (database-store-next-row result-set database row))
173 (apply function row))
174 (database-dump-result-set result-set database)))))
176 (defun map-query-to-list (function query-expression database types)
177 (multiple-value-bind (result-set columns)
178 (database-query-result-set query-expression database :full-set nil
182 (let ((result (list nil)))
183 (do ((row (make-list columns))
184 (current-cons result (cdr current-cons)))
185 ((not (database-store-next-row result-set database row))
187 (rplacd current-cons (list (apply function row)))))
188 (database-dump-result-set result-set database)))))
191 (defun map-query-to-simple (output-type-spec function query-expression database types)
192 (multiple-value-bind (result-set columns rows)
193 (database-query-result-set query-expression database :full-set t
198 ;; We know the row count in advance, so we allocate once
200 (cmucl-compat:make-sequence-of-type output-type-spec rows))
201 (row (make-list columns))
202 (index 0 (1+ index)))
203 ((not (database-store-next-row result-set database row))
205 (declare (fixnum index))
206 (setf (aref result index)
207 (apply function row)))
208 ;; Database can't report row count in advance, so we have
209 ;; to grow and shrink our vector dynamically
211 (cmucl-compat:make-sequence-of-type output-type-spec 100))
212 (allocated-length 100)
213 (row (make-list columns))
214 (index 0 (1+ index)))
215 ((not (database-store-next-row result-set database row))
216 (cmucl-compat:shrink-vector result index))
217 (declare (fixnum allocated-length index))
218 (when (>= index allocated-length)
219 (setq allocated-length (* allocated-length 2)
220 result (adjust-array result allocated-length)))
221 (setf (aref result index)
222 (apply function row))))
223 (database-dump-result-set result-set database)))))
225 (defmacro do-query (((&rest args) query-expression
226 &key (database '*default-database*)
229 (let ((result-set (gensym))
233 `(let ((,db ,database))
234 (multiple-value-bind (,result-set ,columns)
235 (database-query-result-set ,query-expression ,db
236 :full-set nil :types ,types)
239 (do ((,row (make-list ,columns)))
240 ((not (database-store-next-row ,result-set ,db ,row))
242 (destructuring-bind ,args ,row
244 (database-dump-result-set ,result-set ,db)))))))
246 ;;; Marc Battyani : Large objects support
248 (defun create-large-object (&key (database *default-database*))
249 "Creates a new large object in the database and returns the object identifier"
250 (database-create-large-object database))
252 (defun write-large-object (object-id data &key (database *default-database*))
253 "Writes data to the large object"
254 (database-write-large-object object-id data database))
256 (defun read-large-object (object-id &key (database *default-database*))
257 "Reads the large object content"
258 (database-read-large-object object-id database))
260 (defun delete-large-object (object-id &key (database *default-database*))
261 "Deletes the large object in the database"
262 (database-delete-large-object object-id database))