r9485: 26 May 2004 Kevin Rosenberg <kevin@rosenberg.net>
[clsql.git] / tests / test-fdml.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; ======================================================================
3 ;;;; File:    test-fdml.lisp
4 ;;;; Author:  Marcus Pearce <m.t.pearce@city.ac.uk>, Kevin Rosenberg
5 ;;;; Created: 30/03/2004
6 ;;;; Updated: $Id$
7 ;;;;
8 ;;;; Tests for the CLSQL Functional Data Manipulation Language
9 ;;;; (FDML).
10 ;;;;
11 ;;;; This file is part of CLSQL.
12 ;;;;
13 ;;;; CLSQL users are granted the rights to distribute and use this software
14 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
15 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
16 ;;;; ======================================================================
17
18 (in-package #:clsql-tests)
19
20 #.(clsql:locally-enable-sql-reader-syntax)
21
22 (setq *rt-fdml*
23       '(
24         
25 ;; inserts a record using all values only and then deletes it 
26 (deftest :fdml/insert/1
27     (let ((now (get-universal-time)))
28       (clsql:insert-records :into [employee] 
29        :values `(11 1 "Yuri" "Gagarin" "gagarin@soviet.org"
30                     1 1 1.85 t ,(clsql:utime->time now) ,now))
31       (values 
32        (clsql:select [first-name] [last-name] [email]
33                     :from [employee] :where [= [emplid] 11])
34        (progn (clsql:delete-records :from [employee] :where [= [emplid] 11])
35               (clsql:select [*] :from [employee] :where [= [emplid] 11]))))
36   (("Yuri" "Gagarin" "gagarin@soviet.org")) nil)
37
38 ;; inserts a record using attributes and values and then deletes it
39 (deftest :fdml/insert/2
40     (progn
41       (clsql:insert-records :into [employee] 
42                            :attributes '(emplid groupid first_name last_name
43                                          email ecompanyid managerid)
44                            :values '(11 1 "Yuri" "Gagarin" "gagarin@soviet.org"
45                                      1 1))
46       (values 
47        (clsql:select [first-name] [last-name] [email] :from [employee]
48                     :where [= [emplid] 11])
49        (progn (clsql:delete-records :from [employee] :where [= [emplid] 11])
50               (clsql:select [*] :from [employee] :where [= [emplid] 11]))))
51   (("Yuri" "Gagarin" "gagarin@soviet.org")) nil)
52
53 ;; inserts a record using av-pairs and then deletes it
54 (deftest :fdml/insert/3
55     (progn
56       (clsql:insert-records :into [employee] 
57                            :av-pairs'((emplid 11) (groupid 1)
58                                       (first_name "Yuri")
59                                       (last_name "Gagarin")
60                                       (email "gagarin@soviet.org")
61                                       (ecompanyid 1) (managerid 1)))
62       (values 
63        (clsql:select [first-name] [last-name] [email] :from [employee]
64                     :where [= [emplid] 11])
65        (progn (clsql:delete-records :from [employee] :where [= [emplid] 11])
66               (clsql:select [first-name] [last-name] [email] :from [employee]
67                            :where [= [emplid] 11]))))
68   (("Yuri" "Gagarin" "gagarin@soviet.org")) nil)
69
70 ;; inserts a records using a query from another table 
71 (deftest :fdml/insert/4
72     (progn
73       (clsql:create-table [employee2] '(([forename] string)
74                                  ([surname] string)
75                                  ([email] string)))
76       (clsql:insert-records :into [employee2] 
77                      :query [select [first-name] [last-name] [email] 
78                                     :from [employee]]
79                      :attributes '(forename surname email))
80       (prog1
81           (equal (clsql:select [*] :from [employee2])
82                  (clsql:select [first-name] [last-name] [email]
83                               :from [employee]))
84         (clsql:drop-table [employee2] :if-does-not-exist :ignore)))
85   t)
86
87 ;; updates a record using attributes and values and then deletes it
88 (deftest :fdml/update/1
89     (progn
90       (clsql:update-records [employee] 
91                            :attributes '(first_name last_name email)
92                            :values '("Yuri" "Gagarin" "gagarin@soviet.org")
93                            :where [= [emplid] 1])
94       (values 
95        (clsql:select [first-name] [last-name] [email] :from [employee]
96                     :where [= [emplid] 1])
97        (progn
98          (clsql:update-records [employee] 
99                               :av-pairs'((first_name "Vladamir")
100                                          (last_name "Lenin")
101                                          (email "lenin@soviet.org"))
102                               :where [= [emplid] 1])
103          (clsql:select [first-name] [last-name] [email] :from [employee]
104                       :where [= [emplid] 1]))))
105   (("Yuri" "Gagarin" "gagarin@soviet.org"))
106   (("Vladamir" "Lenin" "lenin@soviet.org")))
107
108 ;; updates a record using av-pairs and then deletes it
109 (deftest :fdml/update/2
110     (progn
111       (clsql:update-records [employee] 
112                            :av-pairs'((first_name "Yuri")
113                                       (last_name "Gagarin")
114                                       (email "gagarin@soviet.org"))
115                            :where [= [emplid] 1])
116       (values 
117        (clsql:select [first-name] [last-name] [email] :from [employee]
118                     :where [= [emplid] 1])
119        (progn
120          (clsql:update-records [employee]
121                               :av-pairs'((first_name "Vladamir")
122                                          (last_name "Lenin")
123                                          (email "lenin@soviet.org"))
124                               :where [= [emplid] 1])
125          (clsql:select [first-name] [last-name] [email]
126                       :from [employee] :where [= [emplid] 1]))))
127   (("Yuri" "Gagarin" "gagarin@soviet.org"))
128   (("Vladamir" "Lenin" "lenin@soviet.org")))
129
130
131 ;; Computed values are not always classified as numeric by psqlodbc
132 (deftest :fdml/query/1
133     (let ((count (caar (clsql:query "SELECT COUNT(*) FROM EMPLOYEE WHERE (EMAIL LIKE '%org')" :field-names nil))))
134       (if (stringp count)
135           (nth-value 0 (parse-integer count))
136           (nth-value 0 (truncate count))))
137   10)
138
139 (deftest :fdml/query/2
140     (multiple-value-bind (rows field-names)
141         (clsql:query
142          "SELECT FIRST_NAME,LAST_NAME FROM EMPLOYEE WHERE (EMPLID <= 5) ORDER BY LAST_NAME")
143       (values rows (mapcar 'string-upcase field-names)))
144   (("Leonid" "Brezhnev") ("Nikita" "Kruschev") ("Vladamir" "Lenin")
145    ("Josef" "Stalin") ("Leon" "Trotsky"))
146   ("FIRST_NAME" "LAST_NAME"))
147
148 (deftest :fdml/query/3
149     (caar (clsql:query "SELECT EMPLID FROM EMPLOYEE WHERE LAST_NAME = 'Andropov'" :field-names nil))
150   6)
151   
152 (deftest :fdml/query/4
153     (typep (caar (clsql:query "SELECT HEIGHT FROM EMPLOYEE WHERE LAST_NAME = 'Andropov'" :field-names nil))
154      'float)
155   t)
156   
157 (deftest :fdml/query/5
158     (let ((res (clsql:query (clsql:sql [select [first-name] [sum [emplid]] :from [employee]] 
159                                        [group-by [first-name]] [order-by [sum [emplid]]])
160                             :field-names nil :result-types nil)))
161       (mapcar (lambda (p) (list (car p) (truncate (read-from-string (second p)))))
162               res))
163   (("Josef" 2) ("Leon" 3) ("Nikita" 4) ("Leonid" 5) ("Yuri" 6)
164   ("Konstantin" 7) ("Mikhail" 8) ("Boris" 9) ("Vladamir" 11)))
165
166 (deftest :fdml/query/6
167     (let ((res (clsql:query (clsql:sql [union [select [emplid] :from [employee]] 
168                                        [select [groupid] :from [company]]])
169                             :field-names nil :result-types nil :flatp t)))
170       (values (every #'stringp res)
171               (mapcar #'(lambda (f) (truncate (read-from-string f))) res)))
172   t (1 2 3 4 5 6 7 8 9 10))
173
174 (deftest :fdml/query/7
175     (let ((res (car (clsql:query (clsql:sql [intersect [select [emplid] :from [employee]] 
176                                             [select [groupid] :from [company]]])
177                                  :field-names nil :result-types nil :flatp t))))
178       (values (stringp res)
179               (nth-value 0 (truncate (read-from-string res)))))
180   t 1)
181
182 (deftest :fdml/query/8
183     (let ((res (clsql:query (clsql:sql [except [select [emplid] :from [employee]] 
184                                        [select [groupid] :from [company]]])
185                             :field-names nil :result-types nil :flatp t)))
186       (values (every #'stringp res)
187               (mapcar #'(lambda (f) (truncate (read-from-string f))) res)))
188   t (2 3 4 5 6 7 8 9 10))
189
190
191 (deftest :fdml/execute-command/1
192     (values
193      (clsql:table-exists-p [foo] :owner *test-database-user*)
194      (progn
195        (clsql:execute-command "create table foo (bar integer)")
196        (clsql:table-exists-p [foo] :owner *test-database-user*))
197      (progn
198        (clsql:execute-command "drop table foo")
199        (clsql:table-exists-p [foo] :owner *test-database-user*)))
200   nil t nil)
201
202
203 ;; compare min, max and average hieghts in inches (they're quite short
204 ;; these guys!) 
205 (deftest :fdml/select/1
206     (let ((max (clsql:select [function "floor"
207                              [/ [* [max [height]] 100] 2.54]]
208                              :from [employee]
209                              :result-types nil 
210                              :flatp t))
211           (min (clsql:select [function "floor"
212                              [/ [* [min [height]] 100] 2.54]]
213                              :from [employee]
214                              :result-types nil 
215                              :flatp t))
216           (avg (clsql:select [function "floor"
217                              [avg [/ [* [height] 100] 2.54]]]
218                              :from [employee]
219                              :result-types nil 
220                              :flatp t)))
221       (apply #'< (mapcar #'(lambda (s) (parse-integer s :junk-allowed t))
222                          (append min avg max))))
223  t)
224
225 (deftest :fdml/select/2
226  (clsql:select [first-name] :from [employee] :flatp t :distinct t
227                             :field-names nil 
228                             :result-types nil 
229                             :order-by [first-name])
230  ("Boris" "Josef" "Konstantin" "Leon" "Leonid" "Mikhail" "Nikita" "Vladamir"
231   "Yuri"))
232
233 (deftest :fdml/select/3
234     (let ((res (clsql:select [first-name] [count [*]] :from [employee]
235                              :result-types nil 
236                              :group-by [first-name]
237                              :order-by [first-name]
238                              :field-names nil)))
239       (mapcar (lambda (p) (list (car p) (truncate (read-from-string (second p)))))
240               res))
241   (("Boris" 1) ("Josef" 1) ("Konstantin" 1) ("Leon" 1) ("Leonid" 1)
242    ("Mikhail" 1) ("Nikita" 1) ("Vladamir" 2) ("Yuri" 1)))
243
244 (deftest :fdml/select/4
245     (clsql:select [last-name] :from [employee] 
246                           :where [like [email] "%org"]
247                           :order-by [last-name]
248                           :field-names nil 
249                           :result-types nil 
250                           :flatp t)
251  ("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Lenin" "Putin"
252   "Stalin" "Trotsky" "Yeltsin"))
253
254 (deftest :fdml/select/5
255     (clsql:select [email] :from [employee] :flatp t :result-types nil 
256                           :where [in [employee emplid]
257                           [select [managerid] :from [employee]]]
258                           :field-names nil)
259   ("lenin@soviet.org"))
260
261 (deftest :fdml/select/6
262     (if (clsql-sys:db-type-has-fancy-math? *test-database-underlying-type*)
263         (mapcar #'(lambda (s) (parse-integer s :junk-allowed t))
264                 (clsql:select [function "trunc" [height]] :from [employee]
265                               :result-types nil 
266                               :field-names nil
267                               :flatp t))
268         (mapcar #'(lambda (s) (truncate (parse-integer s :junk-allowed t)))
269                 (clsql:select [height] :from [employee] :flatp t 
270                               :field-names nil :result-types nil)))
271  (1 1 1 1 1 1 1 1 1 1))
272
273 (deftest :fdml/select/7
274     (let ((result (car (clsql:select [max [emplid]] :from [employee] :flatp t 
275                                      :field-names nil :result-types nil))))
276       (values 
277        (stringp result)
278        (nth-value 0 (truncate (read-from-string result)))))
279   t 10)
280
281 (deftest :fdml/select/8
282     (let ((result (car (clsql:select [min [emplid]] :from [employee] :flatp t 
283                                      :field-names nil :result-types nil))))
284       (values
285        (stringp result)
286        (nth-value 0 (truncate (read-from-string result)))))
287   t 1)
288
289 (deftest :fdml/select/9
290     (subseq 
291      (car 
292       (clsql:select [avg [emplid]] :from [employee] :flatp t 
293                     :field-names nil :result-types nil)) 
294      0 3)
295   "5.5")
296
297 (deftest :fdml/select/10
298     (clsql:select [last-name] :from [employee]
299                   :where [not [in [emplid]
300                   [select [managerid] :from [company]]]]
301                   :result-types nil 
302                   :field-names nil 
303                   :flatp t
304                   :order-by [last-name])
305  ("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Putin" "Stalin"
306   "Trotsky" "Yeltsin"))
307
308 (deftest :fdml/select/11
309     (clsql:select [last-name] :from [employee] :where [married] :flatp t
310                   :field-names nil :order-by [emplid] :result-types nil)
311   ("Lenin" "Stalin" "Trotsky"))
312
313 (deftest :fdml/select/12
314     (let ((v 1))
315       (clsql:select [last-name] :from [employee] :where [= [emplid] v]
316                     :field-names nil :result-types nil))
317   (("Lenin")))
318
319 (deftest :fdml/select/13
320      (multiple-value-bind (results field-names) 
321          (clsql:select [emplid] [last-name] :from [employee] 
322                        :where [= [emplid] 1])
323        (values results (mapcar #'string-downcase field-names)))
324  ((1 "Lenin"))
325  ("emplid" "last_name"))
326
327 (deftest :fdml/select/14
328      (floatp (car (clsql:select [height] :from [employee] :where [= [emplid] 1] 
329                                 :flatp t)))
330   t)
331
332 (deftest :fdml/select/15
333     (multiple-value-bind (rows field-names)
334         (clsql:select [addressid] [street-number] [street-name] [city_field] [zip] 
335          :from [addr]
336          :where [= 1 [addressid]])
337       (values
338        rows
339        (mapcar #'string-downcase field-names)))
340   ((1 10 "Park Place" "Leningrad" 123))
341   ("addressid" "street_number" "street_name" "city_field" "zip"))
342
343 (deftest :fdml/select/16
344     (clsql:select [emplid] :from [employee] :where [= 1 [emplid]]
345      :field-names nil)
346   ((1)))
347
348 (deftest :fdml/select/17
349     (clsql:select [emplid] [last-name] :from [employee] :where [= 1 [emplid]]
350      :field-names nil)
351   ((1 "Lenin")))
352
353 (deftest :fdml/select/18
354     (clsql:select [emplid :string] [last-name] :from [employee] :where [= 1 [emplid]]
355      :field-names nil)
356   (("1" "Lenin")))
357
358 (deftest :fdml/select/19
359     (clsql:select [emplid] :from [employee] :order-by [emplid] 
360                            :where [between [* [emplid] 10] [* 5 10] [* 10 10]]
361                            :field-names nil :result-types nil :flatp t)
362  ("5" "6" "7" "8" "9" "10"))
363
364 (deftest :fdml/select/20
365     (clsql:select [emplid] :from [employee] :order-by [emplid] 
366                            :where [not [between [* [emplid] 10] [* 5 10] [* 10 10]]]
367                            :field-names nil :result-types nil :flatp t)
368  ("1" "2" "3" "4"))
369
370 (deftest :fdml/select/21 
371   (clsql:select [substr [first-name] 1 4] :from [employee] 
372                 :flatp t :order-by [emplid] :field-names nil)
373  ("Vlad" "Jose" "Leon" "Niki" "Leon" "Yuri" "Kons" "Mikh" "Bori" "Vlad"))
374
375 (deftest :fdml/select/22 
376   (clsql:select [|| [first-name] " " [last-name]] :from [employee]
377                 :flatp t :order-by [emplid] :field-names nil)
378  ("Vladamir Lenin" "Josef Stalin" "Leon Trotsky" "Nikita Kruschev"
379  "Leonid Brezhnev" "Yuri Andropov" "Konstantin Chernenko" "Mikhail Gorbachev"
380  "Boris Yeltsin" "Vladamir Putin"))
381
382 (deftest :fdml/select/23
383  (clsql:select [emplid] :from [employee] :where [in [emplid] '(1 2 3 4)]
384                         :flatp t :order-by [emplid] :field-names nil
385                         :result-types nil)
386  ("1" "2" "3" "4"))
387
388 (deftest :fdml/select/24
389  (clsql:select [distinct [first-name]] :from [employee] :flatp t
390                :order-by [first-name] :field-names nil :result-types nil)
391  ("Boris" "Josef" "Konstantin" "Leon" "Leonid" "Mikhail" "Nikita" "Vladamir"
392   "Yuri"))
393
394 (deftest :fdml/select/25
395  (clsql:select [first-name] :from (clsql-sys:convert-to-db-default-case "employee" *default-database*)
396   :flatp t :distinct t
397   :field-names nil 
398   :result-types nil 
399   :order-by [first-name])
400  ("Boris" "Josef" "Konstantin" "Leon" "Leonid" "Mikhail" "Nikita" "Vladamir"
401   "Yuri"))
402
403 (deftest :fdml/select/26
404  (clsql:select ["table" first-name] ["table" last-name] 
405   :from '([employee "table"] [employee "join"])
406   :where [and [= ["table" first-name] 
407                  ["join" first-name]]
408               [not [= ["table" emplid] 
409                       ["join" emplid]]]]
410   :order-by '(["table" last-name])
411   :result-types nil :field-names nil)
412  (("Vladamir" "Lenin") ("Vladamir" "Putin")))
413
414 (deftest :fdml/select/27 
415     (mapcar
416      (lambda (f) (truncate (read-from-string f)))
417      (clsql:select [coalesce [managerid] 10] :from [employee] :order-by [emplid]
418                    :field-names nil :result-types nil :flatp t))
419   (10 1 1 1 1 1 1 1 1 1))
420   
421 (deftest :fdml/select/28 
422     (mapcar
423      (lambda (f) (truncate (read-from-string (car f))))
424      (loop for column in `([*] [emplid]) collect         
425            (clsql:select [count column] :from [employee] 
426                          :flatp t :result-types nil :field-names nil)))
427  (10 10))
428
429 (deftest :fdml/select/29 
430  (clsql:select [first-name] [last-name] :from [employee] 
431                        :result-types nil :field-names nil 
432                        :order-by '(([first-name] :asc) ([last-name] :desc)))
433  (("Boris" "Yeltsin") ("Josef" "Stalin") ("Konstantin" "Chernenko")
434   ("Leon" "Trotsky") ("Leonid" "Brezhnev") ("Mikhail" "Gorbachev")
435   ("Nikita" "Kruschev") ("Vladamir" "Putin") ("Vladamir" "Lenin")
436   ("Yuri" "Andropov")))
437
438 (deftest :fdml/select/30 
439  (clsql:select [first-name] [last-name] :from [employee] 
440                        :result-types nil :field-names nil 
441                        :order-by '(([first-name] :asc) ([last-name] :asc)))
442  (("Boris" "Yeltsin") ("Josef" "Stalin") ("Konstantin" "Chernenko")
443   ("Leon" "Trotsky") ("Leonid" "Brezhnev") ("Mikhail" "Gorbachev")
444   ("Nikita" "Kruschev") ("Vladamir" "Lenin") ("Vladamir" "Putin")
445   ("Yuri" "Andropov")))
446
447 (deftest :fdml/select/31
448  (clsql:select [last-name] :from [employee]                   
449               :set-operation [union [select [first-name] :from [employee]
450                                             :order-by [last-name]]]
451               :flatp t
452               :result-types nil 
453               :field-names nil)
454  ("Andropov" "Boris" "Brezhnev" "Chernenko" "Gorbachev" "Josef" "Konstantin"
455  "Kruschev" "Lenin" "Leon" "Leonid" "Mikhail" "Nikita" "Putin" "Stalin"
456  "Trotsky" "Vladamir" "Yeltsin" "Yuri"))
457
458 (deftest :fdml/select/32
459     (clsql:select [emplid] :from [employee]
460                 :where [= [emplid] [any [select [companyid] :from [company]]]]
461                 :flatp t :result-types nil :field-names nil)
462   ("1"))
463
464 (deftest :fdml/select/33
465  (clsql:select [last-name] :from [employee] 
466               :where [> [emplid] [all [select [groupid] :from [employee]]]]
467               :order-by [last-name] 
468               :flatp t :result-types nil :field-names nil)
469 ("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Putin" "Stalin"
470  "Trotsky" "Yeltsin"))
471
472 (deftest :fdml/select/34
473   (loop for x from 1 below 5
474    collect
475    (car
476     (clsql:select [last-name] :from [employee] 
477                   :where [= [emplid] x]
478                   :flatp t :result-types nil :field-names nil)))
479   ("Lenin" "Stalin" "Trotsky" "Kruschev"))
480
481 (deftest :fdml/do-query/1
482     (let ((result '()))
483     (clsql:do-query ((name) [select [last-name] :from [employee]
484                                    :order-by [last-name]])
485       (push name result))
486     result)
487  ("Yeltsin" "Trotsky" "Stalin" "Putin" "Lenin" "Kruschev" "Gorbachev"
488             "Chernenko" "Brezhnev" "Andropov")) 
489
490 (deftest :fdml/map-query/1
491     (clsql:map-query 'list #'identity
492                     [select [last-name] :from [employee] :flatp t
493                             :order-by [last-name]])
494   ("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Lenin" "Putin"
495               "Stalin" "Trotsky" "Yeltsin"))
496
497 (deftest :fdml/map-query/2
498     (clsql:map-query 'vector #'identity
499                     [select [last-name] :from [employee] :flatp t
500                             :order-by [last-name]])
501   #("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Lenin" "Putin"
502     "Stalin" "Trotsky" "Yeltsin"))
503
504 (deftest :fdml/map-query/3 
505  (clsql:map-query 'list #'identity
506                   [select [last-name] :from [employee] :order-by [last-name]])
507  (("Andropov") ("Brezhnev") ("Chernenko") ("Gorbachev") ("Kruschev") ("Lenin")
508   ("Putin") ("Stalin") ("Trotsky") ("Yeltsin")))
509
510 (deftest :fdml/map-query/4 
511  (clsql:map-query 'list #'identity
512                   [select [first-name] [last-name] :from [employee] 
513                           :order-by [last-name]])
514  (("Yuri" "Andropov") ("Leonid" "Brezhnev") ("Konstantin" "Chernenko")
515   ("Mikhail" "Gorbachev") ("Nikita" "Kruschev") ("Vladamir" "Lenin")
516   ("Vladamir" "Putin") ("Josef" "Stalin") ("Leon" "Trotsky") 
517   ("Boris" "Yeltsin")))
518   
519 (deftest :fdml/loop/1
520     (loop for (forename surname)
521       being each tuple in
522       [select [first-name] [last-name] :from [employee] :order-by [last-name]]
523       collect (concatenate 'string forename " " surname))
524   ("Yuri Andropov" "Leonid Brezhnev" "Konstantin Chernenko" "Mikhail Gorbachev"
525                    "Nikita Kruschev" "Vladamir Lenin" "Vladamir Putin"
526    "Josef Stalin" "Leon Trotsky" "Boris Yeltsin"))
527
528 (deftest :fdml/loop/2
529     (loop for (addressid)
530       being each tuple in
531       [select [addressid] :from [addr] :order-by [addressid]]
532      collect addressid)
533   (1 2))
534
535 (deftest :fdml/loop/3
536     (loop for addressid
537       being each tuple in
538       [select [addressid] :from [addr] :order-by [addressid]]
539       collect addressid)
540   (1 2))
541
542 ;; starts a transaction deletes a record and then rolls back the deletion 
543 (deftest :fdml/transaction/1
544     (let ((results '()))
545       ;; test if we are in a transaction
546       (push (clsql:in-transaction-p) results)
547       ;;start a transaction 
548       (clsql:start-transaction)
549       ;; test if we are in a transaction
550       (push (clsql:in-transaction-p) results)
551       ;;Putin has got to go
552       (clsql:delete-records :from [employee] :where [= [last-name] "Putin"])
553       ;;Should be nil 
554       (push 
555        (clsql:select [*] :from [employee] :where [= [last-name] "Putin"])
556        results)
557       ;;Oh no, he's still there
558       (clsql:rollback)
559       ;; test that we are out of the transaction
560       (push (clsql:in-transaction-p) results)
561       ;; Check that we got him back alright 
562       (push (clsql:select [email] :from [employee] :where [= [last-name] "Putin"]
563                          :flatp t)
564             results)
565       (apply #'values (nreverse results)))
566   nil t nil nil ("putin@soviet.org"))
567
568 ;; starts a transaction, updates a record and then rolls back the update
569 (deftest :fdml/transaction/2
570     (let ((results '()))
571       ;; test if we are in a transaction
572       (push (clsql:in-transaction-p) results)
573       ;;start a transaction 
574       (clsql:start-transaction)
575       ;; test if we are in a transaction
576       (push (clsql:in-transaction-p) results)
577       ;;Putin has got to go
578       (clsql:update-records [employee]
579        :av-pairs '((email "putin-nospam@soviet.org"))
580        :where [= [last-name] "Putin"])
581       ;;Should be new value  
582       (push (clsql:select [email] :from [employee]
583                          :where [= [last-name] "Putin"]
584                          :flatp t)
585             results)
586       ;;Oh no, he's still there
587       (clsql:rollback)
588       ;; test that we are out of the transaction
589       (push (clsql:in-transaction-p) results)
590       ;; Check that we got him back alright 
591       (push (clsql:select [email] :from [employee] :where [= [last-name] "Putin"]
592                          :flatp t)
593             results)
594       (apply #'values (nreverse results)))
595   nil t ("putin-nospam@soviet.org") nil ("putin@soviet.org")) 
596
597 ;; runs an update within a transaction and checks it is committed
598 (deftest :fdml/transaction/3
599     (let ((results '()))
600       ;; check status 
601       (push (clsql:in-transaction-p) results)
602       ;; update records 
603       (push
604        (clsql:with-transaction () 
605          (clsql:update-records [employee] 
606                               :av-pairs '((email "lenin-nospam@soviet.org"))
607                               :where [= [emplid] 1]))
608        results)
609       ;; check status 
610       (push (clsql:in-transaction-p) results)
611       ;; check that was committed 
612       (push (clsql:select [email] :from [employee] :where [= [emplid] 1]
613                          :flatp t)
614             results)
615       ;; undo the changes 
616       (push
617        (clsql:with-transaction () 
618          (clsql:update-records [employee] 
619                               :av-pairs '((email "lenin@soviet.org"))
620                               :where [= [emplid] 1]))
621        results)
622       ;; and check status 
623       (push (clsql:in-transaction-p) results)
624       ;; check that was committed 
625       (push (clsql:select [email] :from [employee] :where [= [emplid] 1]
626                          :flatp t)
627             results)
628       (apply #'values (nreverse results)))
629   nil :committed nil ("lenin-nospam@soviet.org") :committed
630   nil ("lenin@soviet.org"))
631
632 ;; runs a valid update and an invalid one within a transaction and checks
633 ;; that the valid update is rolled back when the invalid one fails. 
634 (deftest :fdml/transaction/4
635     (let ((results '()))
636       ;; check status
637       (push (clsql:in-transaction-p) results)
638       (handler-case 
639           (clsql:with-transaction () 
640             ;; valid update
641             (clsql:update-records [employee] 
642                                   :av-pairs '((email "lenin-nospam@soviet.org"))
643                                   :where [= [emplid] 1])
644             ;; invalid update which generates an error 
645             (clsql:update-records [employee] 
646                                   :av-pairs
647                                   '((emale "lenin-nospam@soviet.org"))
648                                   :where [= [emplid] 1]))
649         (clsql:sql-database-error ()
650           (progn
651             ;; check status 
652             (push (clsql:in-transaction-p) results)
653             ;; and check nothing done 
654             (push (clsql:select [email] :from [employee] :where [= [emplid] 1]
655                                :flatp t)
656                   results)
657             (apply #'values (nreverse results))))))
658   nil nil ("lenin@soviet.org"))
659
660
661 ))
662
663 #.(clsql:restore-sql-reader-syntax-state)