1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; ======================================================================
4 ;;;; Updated: <04/04/2004 12:05:32 marcusp>
5 ;;;; ======================================================================
7 ;;;; Description ==========================================================
8 ;;;; ======================================================================
10 ;;;; The CLSQL-USQL Functional Data Manipulation Language (FDML).
12 ;;;; ======================================================================
14 (in-package :clsql-usql-sys)
17 ;;; Basic operations on databases
20 (defmethod database-query-result-set ((expr %sql-expression) database
22 (database-query-result-set (sql-output expr database) database
23 :full-set full-set :types types))
25 (defmethod execute-command ((expr %sql-expression)
26 &key (database *default-database*))
27 (execute-command (sql-output expr database) :database database)
32 (defmethod query ((expr %sql-expression) &key (database *default-database*)
33 (result-types nil) (flatp nil))
34 (query (sql-output expr database) :database database :flatp flatp
35 :result-types result-types))
37 (defun print-query (query-exp &key titles (formats t) (sizes t) (stream t)
38 (database *default-database*))
39 "The PRINT-QUERY function takes a symbolic SQL query expression and
40 formatting information and prints onto STREAM a table containing the
41 results of the query. A list of strings to use as column headings is
42 given by TITLES, which has a default value of NIL. The FORMATS
43 argument is a list of format strings used to print each attribute, and
44 has a default value of T, which means that ~A or ~VA are used if sizes
45 are provided or computed. The field sizes are given by SIZES. It has a
46 default value of T, which specifies that minimum sizes are
47 computed. The output stream is given by STREAM, which has a default
48 value of T. This specifies that *STANDARD-OUTPUT* is used."
49 (flet ((compute-sizes (data)
50 (mapcar #'(lambda (x) (apply #'max (mapcar #'length x)))
51 (apply #'mapcar (cons #'list data))))
52 (format-record (record control sizes)
53 (format stream "~&~?" control
54 (if (null sizes) record
55 (mapcan #'(lambda (s f) (list s f)) sizes record)))))
56 (let* ((query-exp (etypecase query-exp
58 (sql-query (sql-output query-exp))))
59 (data (query query-exp :database database))
60 (sizes (if (or (null sizes) (listp sizes)) sizes
61 (compute-sizes (if titles (cons titles data) data))))
62 (formats (if (or (null formats) (not (listp formats)))
63 (make-list (length (car data)) :initial-element
64 (if (null sizes) "~A " "~VA "))
66 (control-string (format nil "~{~A~}" formats)))
67 (when titles (format-record titles control-string sizes))
68 (dolist (d data (values)) (format-record d control-string sizes)))))
70 (defun insert-records (&key (into nil)
75 (database *default-database*))
76 "Inserts a set of values into a table. The records created contain
77 values for attributes (or av-pairs). The argument VALUES is a list of
78 values. If ATTRIBUTES is supplied then VALUES must be a corresponding
79 list of values for each of the listed attribute names. If AV-PAIRS is
80 non-nil, then both ATTRIBUTES and VALUES must be nil. If QUERY is
81 non-nil, then neither VALUES nor AV-PAIRS should be. QUERY should be a
82 query expression, and the attribute names in it must also exist in the
83 table INTO. The default value of DATABASE is *DEFAULT-DATABASE*."
84 (let ((stmt (make-sql-insert :into into :attrs attributes
85 :vals values :av-pairs av-pairs
87 (execute-command stmt :database database)))
89 (defun make-sql-insert (&key (into nil)
95 (error 'clsql-sql-syntax-error :reason ":into keyword not supplied"))
96 (let ((ins (make-instance 'sql-insert :into into)))
97 (with-slots (attributes values query)
99 (cond ((and vals (not attrs) (not query) (not av-pairs))
101 ((and vals attrs (not subquery) (not av-pairs))
102 (setf attributes attrs)
104 ((and av-pairs (not vals) (not attrs) (not subquery))
105 (setf attributes (mapcar #'car av-pairs))
106 (setf values (mapcar #'cadr av-pairs)))
107 ((and subquery (not vals) (not attrs) (not av-pairs))
108 (setf query subquery))
109 ((and subquery attrs (not vals) (not av-pairs))
110 (setf attributes attrs)
111 (setf query subquery))
113 (error 'clsql-sql-syntax-error
114 :reason "bad or ambiguous keyword combination.")))
117 (defun delete-records (&key (from nil)
119 (database *default-database*))
120 "Deletes rows from a database table specified by FROM in which the
121 WHERE condition is true. The argument DATABASE specifies a database
122 from which the records are to be removed, and defaults to
124 (let ((stmt (make-instance 'sql-delete :from from :where where)))
125 (execute-command stmt :database database)))
127 (defun update-records (table &key
132 (database *default-database*))
133 "Changes the values of existing fields in TABLE with columns
134 specified by ATTRIBUTES and VALUES (or AV-PAIRS) where the WHERE
137 (setf attributes (mapcar #'car av-pairs)
138 values (mapcar #'cadr av-pairs)))
139 (let ((stmt (make-instance 'sql-update :table table
140 :attributes attributes
143 (execute-command stmt :database database)))
148 (defun map-query (output-type-spec function query-expression
149 &key (database *default-database*)
151 "Map the function over all tuples that are returned by the query in
152 query-expression. The results of the function are collected as
153 specified in output-type-spec and returned like in MAP."
154 ;; DANGER Will Robinson: Parts of the code for implementing
155 ;; map-query (including the code below and the helper functions
156 ;; called) are highly CMU CL specific.
157 ;; KMR -- these have been replaced with cross-platform instructions above
158 (macrolet ((type-specifier-atom (type)
159 `(if (atom ,type) ,type (car ,type))))
160 (case (type-specifier-atom output-type-spec)
162 (map-query-for-effect function query-expression database types))
164 (map-query-to-list function query-expression database types))
165 ((simple-vector simple-string vector string array simple-array
166 bit-vector simple-bit-vector base-string
168 (map-query-to-simple output-type-spec function query-expression
172 (cmucl-compat:result-type-or-lose output-type-spec t)
173 function query-expression :database database :types types)))))
175 (defun map-query-for-effect (function query-expression database types)
176 (multiple-value-bind (result-set columns)
177 (database-query-result-set query-expression database :full-set nil
181 (do ((row (make-list columns)))
182 ((not (database-store-next-row result-set database row))
184 (apply function row))
185 (database-dump-result-set result-set database)))))
187 (defun map-query-to-list (function query-expression database types)
188 (multiple-value-bind (result-set columns)
189 (database-query-result-set query-expression database :full-set nil
193 (let ((result (list nil)))
194 (do ((row (make-list columns))
195 (current-cons result (cdr current-cons)))
196 ((not (database-store-next-row result-set database row))
198 (rplacd current-cons (list (apply function row)))))
199 (database-dump-result-set result-set database)))))
201 (defun map-query-to-simple (output-type-spec function query-expression
203 (multiple-value-bind (result-set columns rows)
204 (database-query-result-set query-expression database :full-set t
209 ;; We know the row count in advance, so we allocate once
211 (cmucl-compat:make-sequence-of-type output-type-spec rows))
212 (row (make-list columns))
213 (index 0 (1+ index)))
214 ((not (database-store-next-row result-set database row))
216 (declare (fixnum index))
217 (setf (aref result index)
218 (apply function row)))
219 ;; Database can't report row count in advance, so we have
220 ;; to grow and shrink our vector dynamically
222 (cmucl-compat:make-sequence-of-type output-type-spec 100))
223 (allocated-length 100)
224 (row (make-list columns))
225 (index 0 (1+ index)))
226 ((not (database-store-next-row result-set database row))
227 (cmucl-compat:shrink-vector result index))
228 (declare (fixnum allocated-length index))
229 (when (>= index allocated-length)
230 (setf allocated-length (* allocated-length 2)
231 result (adjust-array result allocated-length)))
232 (setf (aref result index)
233 (apply function row))))
234 (database-dump-result-set result-set database)))))
236 (defmacro do-query (((&rest args) query-expression
237 &key (database '*default-database*) (types nil))
239 "Repeatedly executes BODY within a binding of ARGS on the attributes
240 of each record resulting from QUERY. The return value is determined by
241 the result of executing BODY. The default value of DATABASE is
243 (let ((result-set (gensym))
247 `(let ((,db ,database))
248 (multiple-value-bind (,result-set ,columns)
249 (database-query-result-set ,query-expression ,db
250 :full-set nil :types ,types)
253 (do ((,row (make-list ,columns)))
254 ((not (database-store-next-row ,result-set ,db ,row))
256 (destructuring-bind ,args ,row
258 (database-dump-result-set ,result-set ,db)))))))
263 (defmethod database-output-sql ((str string) database)
264 (declare (ignore database)
265 (optimize (speed 3) (safety 1) #+cmu (extensions:inhibit-warnings 3))
266 (type (simple-array * (*)) str))
267 (let ((len (length str)))
268 (declare (type fixnum len))
271 ((and (null (position #\' str))
272 (null (position #\\ str)))
273 (concatenate 'string "'" str "'"))
275 (let ((buf (make-string (+ (* len 2) 2) :initial-element #\')))
278 ((= i len) (subseq buf 0 (1+ j)))
279 (declare (type integer i j))
280 (let ((char (aref str i)))
281 (cond ((eql char #\')
282 (setf (aref buf j) #\\)
284 (setf (aref buf j) #\'))
286 (setf (aref buf j) #\\)
288 (setf (aref buf j) #\\))
290 (setf (aref buf j) char))))))))))
292 (let ((keyword-package (symbol-package :foo)))
293 (defmethod database-output-sql ((sym symbol) database)
294 (declare (ignore database))
295 (if (equal (symbol-package sym) keyword-package)
296 (concatenate 'string "'" (string sym) "'")
299 (defmethod database-output-sql ((tee (eql t)) database)
300 (declare (ignore database))
303 (defmethod database-output-sql ((num number) database)
304 (declare (ignore database))
305 (princ-to-string num))
307 (defmethod database-output-sql ((arg list) database)
310 (format nil "(~{~A~^,~})" (mapcar #'(lambda (val)
311 (sql-output val database))
314 (defmethod database-output-sql ((arg vector) database)
315 (format nil "~{~A~^,~}" (map 'list #'(lambda (val)
316 (sql-output val database))
319 (defmethod database-output-sql ((self wall-time) database)
320 (declare (ignore database))
321 (db-timestring self))
323 (defmethod database-output-sql (thing database)
327 (error 'clsql-simple-error
329 "No type conversion to SQL for ~A is defined for DB ~A."
330 :format-arguments (list (type-of thing) (type-of database)))))
332 (defmethod output-sql-hash-key ((arg vector) &optional database)
333 (list 'vector (map 'list (lambda (arg)
334 (or (output-sql-hash-key arg database)
335 (return-from output-sql-hash-key nil)))
338 (defmethod output-sql (expr &optional (database *default-database*))
339 (write-string (database-output-sql expr database) *sql-stream*)
342 (defmethod output-sql ((expr list) &optional (database *default-database*))
344 (write-string +null-string+ *sql-stream*)
346 (write-char #\( *sql-stream*)
347 (do ((item expr (cdr item)))
349 (output-sql (car item) database))
350 (output-sql (car item) database)
351 (write-char #\, *sql-stream*))
352 (write-char #\) *sql-stream*)))