r10922: 03 May 2006 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 "Vladimir")
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   (("Vladimir" "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 "Vladimir")
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   (("Vladimir" "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") ("Vladimir" "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) ("Vladimir" 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" "Vladimir"
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) ("Vladimir" 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 [substring [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    (case *test-database-underlying-type*
377      (:mssql (clsql:select [+ [first-name] " " [last-name]] :from [employee]
378                            :flatp t :order-by [emplid] :field-names nil))
379      (t (clsql:select [|| [first-name] " " [last-name]] :from [employee]
380                       :flatp t :order-by [emplid] :field-names nil)))
381  ("Vladimir Lenin" "Josef Stalin" "Leon Trotsky" "Nikita Kruschev"
382  "Leonid Brezhnev" "Yuri Andropov" "Konstantin Chernenko" "Mikhail Gorbachev"
383  "Boris Yeltsin" "Vladimir Putin"))
384
385 (deftest :fdml/select/23
386  (clsql:select [emplid] :from [employee] :where [in [emplid] '(1 2 3 4)]
387                         :flatp t :order-by [emplid] :field-names nil
388                         :result-types nil)
389  ("1" "2" "3" "4"))
390
391 (deftest :fdml/select/24
392  (clsql:select [distinct [first-name]] :from [employee] :flatp t
393                :order-by [first-name] :field-names nil :result-types nil)
394  ("Boris" "Josef" "Konstantin" "Leon" "Leonid" "Mikhail" "Nikita" "Vladimir"
395   "Yuri"))
396
397 (deftest :fdml/select/25
398  (clsql:select [first-name] :from (clsql-sys:convert-to-db-default-case "employee" *default-database*)
399   :flatp t :distinct t
400   :field-names nil 
401   :result-types nil 
402   :order-by [first-name])
403  ("Boris" "Josef" "Konstantin" "Leon" "Leonid" "Mikhail" "Nikita" "Vladimir"
404   "Yuri"))
405
406 (deftest :fdml/select/26
407  (clsql:select ["table" first-name] ["table" last-name] 
408   :from '([employee "table"] [employee "join"])
409   :where [and [= ["table" first-name] 
410                  ["join" first-name]]
411               [not [= ["table" emplid] 
412                       ["join" emplid]]]]
413   :order-by '(["table" last-name])
414   :result-types nil :field-names nil)
415  (("Vladimir" "Lenin") ("Vladimir" "Putin")))
416
417 (deftest :fdml/select/27 
418     (mapcar
419      (lambda (f) (truncate (read-from-string f)))
420      (clsql:select [coalesce [managerid] 10] :from [employee] :order-by [emplid]
421                    :field-names nil :result-types nil :flatp t))
422   (10 1 1 1 1 1 1 1 1 1))
423   
424 (deftest :fdml/select/28 
425     (mapcar
426      (lambda (f) (truncate (read-from-string (car f))))
427      (loop for column in `([*] [emplid]) collect         
428            (clsql:select [count column] :from [employee] 
429                          :flatp t :result-types nil :field-names nil)))
430  (10 10))
431
432 (deftest :fdml/select/29 
433  (clsql:select [first-name] [last-name] :from [employee] 
434                        :result-types nil :field-names nil 
435                        :order-by '(([first-name] :asc) ([last-name] :desc)))
436  (("Boris" "Yeltsin") ("Josef" "Stalin") ("Konstantin" "Chernenko")
437   ("Leon" "Trotsky") ("Leonid" "Brezhnev") ("Mikhail" "Gorbachev")
438   ("Nikita" "Kruschev") ("Vladimir" "Putin") ("Vladimir" "Lenin")
439   ("Yuri" "Andropov")))
440
441 (deftest :fdml/select/30 
442  (clsql:select [first-name] [last-name] :from [employee] 
443                        :result-types nil :field-names nil 
444                        :order-by '(([first-name] :asc) ([last-name] :asc)))
445  (("Boris" "Yeltsin") ("Josef" "Stalin") ("Konstantin" "Chernenko")
446   ("Leon" "Trotsky") ("Leonid" "Brezhnev") ("Mikhail" "Gorbachev")
447   ("Nikita" "Kruschev") ("Vladimir" "Lenin") ("Vladimir" "Putin")
448   ("Yuri" "Andropov")))
449
450 (deftest :fdml/select/31
451  (clsql:select [last-name] :from [employee]                   
452               :set-operation [union [select [first-name] :from [employee]
453                                             :order-by [last-name]]]
454               :flatp t
455               :result-types nil 
456               :field-names nil)
457  ("Andropov" "Boris" "Brezhnev" "Chernenko" "Gorbachev" "Josef" "Konstantin"
458  "Kruschev" "Lenin" "Leon" "Leonid" "Mikhail" "Nikita" "Putin" "Stalin"
459  "Trotsky" "Vladimir" "Yeltsin" "Yuri"))
460
461 (deftest :fdml/select/32
462     (clsql:select [emplid] :from [employee]
463                 :where [= [emplid] [any [select [companyid] :from [company]]]]
464                 :flatp t :result-types nil :field-names nil)
465   ("1"))
466
467 (deftest :fdml/select/33
468  (clsql:select [last-name] :from [employee] 
469               :where [> [emplid] [all [select [groupid] :from [employee]]]]
470               :order-by [last-name] 
471               :flatp t :result-types nil :field-names nil)
472 ("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Putin" "Stalin"
473  "Trotsky" "Yeltsin"))
474
475 (deftest :fdml/select/34
476   (loop for x from 1 below 5
477    collect
478    (car
479     (clsql:select [last-name] :from [employee] 
480                   :where [= [emplid] x]
481                   :flatp t :result-types nil :field-names nil)))
482   ("Lenin" "Stalin" "Trotsky" "Kruschev"))
483
484 ;; test escaping of single quotes 
485 (deftest :fdml/select/35 
486     (clsql:select "What's up doc?" :from [employee] :flatp t :field-names nil)
487   ("What's up doc?" "What's up doc?" "What's up doc?" "What's up doc?"
488    "What's up doc?" "What's up doc?" "What's up doc?" "What's up doc?"
489    "What's up doc?" "What's up doc?"))
490
491 ;; test proper treatment of backslash (depending on backend) 
492 (deftest :fdml/select/36
493     (clsql:select "foo\\bar\\baz" :from [employee] :flatp t :field-names nil)
494  ("foo\\bar\\baz" "foo\\bar\\baz" "foo\\bar\\baz" "foo\\bar\\baz" 
495   "foo\\bar\\baz"  "foo\\bar\\baz" "foo\\bar\\baz" "foo\\bar\\baz" 
496   "foo\\bar\\baz" "foo\\bar\\baz"))
497
498 (deftest :fdml/select/37
499     (clsql:select [emplid] :from [employee] 
500                   :order-by [emplid]
501                   :limit 5 
502                   :field-names nil
503                   :flatp t)
504   (1 2 3 4 5))
505
506 (deftest :fdml/select/38
507     (clsql:select [emplid] :from [employee] 
508                   :order-by [emplid]
509                   :limit 5 
510                   :offset 3
511                   :field-names nil
512                   :flatp t)
513   (4 5 6 7 8))
514
515 (deftest :fdml/do-query/1
516     (let ((result '()))
517     (clsql:do-query ((name) [select [last-name] :from [employee]
518                                    :order-by [last-name]])
519       (push name result))
520     result)
521  ("Yeltsin" "Trotsky" "Stalin" "Putin" "Lenin" "Kruschev" "Gorbachev"
522             "Chernenko" "Brezhnev" "Andropov")) 
523
524 (deftest :fdml/map-query/1
525     (clsql:map-query 'list #'identity
526                     [select [last-name] :from [employee] :flatp t
527                             :order-by [last-name]])
528   ("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Lenin" "Putin"
529               "Stalin" "Trotsky" "Yeltsin"))
530
531 (deftest :fdml/map-query/2
532     (clsql:map-query 'vector #'identity
533                     [select [last-name] :from [employee] :flatp t
534                             :order-by [last-name]])
535   #("Andropov" "Brezhnev" "Chernenko" "Gorbachev" "Kruschev" "Lenin" "Putin"
536     "Stalin" "Trotsky" "Yeltsin"))
537
538 (deftest :fdml/map-query/3 
539  (clsql:map-query 'list #'identity
540                   [select [last-name] :from [employee] :order-by [last-name]])
541  (("Andropov") ("Brezhnev") ("Chernenko") ("Gorbachev") ("Kruschev") ("Lenin")
542   ("Putin") ("Stalin") ("Trotsky") ("Yeltsin")))
543
544 (deftest :fdml/map-query/4 
545  (clsql:map-query 'list #'identity
546                   [select [first-name] [last-name] :from [employee] 
547                           :order-by [last-name]])
548  (("Yuri" "Andropov") ("Leonid" "Brezhnev") ("Konstantin" "Chernenko")
549   ("Mikhail" "Gorbachev") ("Nikita" "Kruschev") ("Vladimir" "Lenin")
550   ("Vladimir" "Putin") ("Josef" "Stalin") ("Leon" "Trotsky") 
551   ("Boris" "Yeltsin")))
552   
553 (deftest :fdml/loop/1
554     (loop for (forename surname)
555       being each tuple in
556       [select [first-name] [last-name] :from [employee] :order-by [last-name]]
557       collect (concatenate 'string forename " " surname))
558   ("Yuri Andropov" "Leonid Brezhnev" "Konstantin Chernenko" "Mikhail Gorbachev"
559                    "Nikita Kruschev" "Vladimir Lenin" "Vladimir Putin"
560    "Josef Stalin" "Leon Trotsky" "Boris Yeltsin"))
561
562 (deftest :fdml/loop/2
563     (loop for (addressid)
564       being each tuple in
565       [select [addressid] :from [addr] :order-by [addressid]]
566      collect addressid)
567   (1 2))
568
569 (deftest :fdml/loop/3
570     (loop for addressid
571       being each tuple in
572       [select [addressid] :from [addr] :order-by [addressid]]
573       collect addressid)
574   (1 2))
575
576 ;; starts a transaction deletes a record and then rolls back the deletion 
577 (deftest :fdml/transaction/1
578     (let ((results '()))
579       ;; test if we are in a transaction
580       (push (clsql:in-transaction-p) results)
581       ;;start a transaction 
582       (clsql:start-transaction)
583       ;; test if we are in a transaction
584       (push (clsql:in-transaction-p) results)
585       ;;Putin has got to go
586       (clsql:delete-records :from [employee] :where [= [last-name] "Putin"])
587       ;;Should be nil 
588       (push 
589        (clsql:select [*] :from [employee] :where [= [last-name] "Putin"])
590        results)
591       ;;Oh no, he's still there
592       (clsql:rollback)
593       ;; test that we are out of the transaction
594       (push (clsql:in-transaction-p) results)
595       ;; Check that we got him back alright 
596       (push (clsql:select [email] :from [employee] :where [= [last-name] "Putin"]
597                          :flatp t)
598             results)
599       (apply #'values (nreverse results)))
600   nil t nil nil ("putin@soviet.org"))
601
602 ;; starts a transaction, updates a record and then rolls back the update
603 (deftest :fdml/transaction/2
604     (let ((results '()))
605       ;; test if we are in a transaction
606       (push (clsql:in-transaction-p) results)
607       ;;start a transaction 
608       (clsql:start-transaction)
609       ;; test if we are in a transaction
610       (push (clsql:in-transaction-p) results)
611       ;;Putin has got to go
612       (clsql:update-records [employee]
613        :av-pairs '((email "putin-nospam@soviet.org"))
614        :where [= [last-name] "Putin"])
615       ;;Should be new value  
616       (push (clsql:select [email] :from [employee]
617                          :where [= [last-name] "Putin"]
618                          :flatp t)
619             results)
620       ;;Oh no, he's still there
621       (clsql:rollback)
622       ;; test that we are out of the transaction
623       (push (clsql:in-transaction-p) results)
624       ;; Check that we got him back alright 
625       (push (clsql:select [email] :from [employee] :where [= [last-name] "Putin"]
626                          :flatp t)
627             results)
628       (apply #'values (nreverse results)))
629   nil t ("putin-nospam@soviet.org") nil ("putin@soviet.org")) 
630
631 ;; runs an update within a transaction and checks it is committed
632 (deftest :fdml/transaction/3
633     (let ((results '()))
634       ;; check status 
635       (push (clsql:in-transaction-p) results)
636       ;; update records 
637       (push
638        (clsql:with-transaction () 
639          (clsql:update-records [employee] 
640                               :av-pairs '((email "lenin-nospam@soviet.org"))
641                               :where [= [emplid] 1]))
642        results)
643       ;; check status 
644       (push (clsql:in-transaction-p) results)
645       ;; check that was committed 
646       (push (clsql:select [email] :from [employee] :where [= [emplid] 1]
647                          :flatp t)
648             results)
649       ;; undo the changes 
650       (push
651        (clsql:with-transaction () 
652          (clsql:update-records [employee] 
653                               :av-pairs '((email "lenin@soviet.org"))
654                               :where [= [emplid] 1]))
655        results)
656       ;; and check status 
657       (push (clsql:in-transaction-p) results)
658       ;; check that was committed 
659       (push (clsql:select [email] :from [employee] :where [= [emplid] 1]
660                          :flatp t)
661             results)
662       (apply #'values (nreverse results)))
663   nil nil nil ("lenin-nospam@soviet.org") nil nil ("lenin@soviet.org"))
664
665 ;; runs a valid update and an invalid one within a transaction and checks
666 ;; that the valid update is rolled back when the invalid one fails. 
667 (deftest :fdml/transaction/4
668     (let ((results '()))
669       ;; check status
670       (push (clsql:in-transaction-p) results)
671       (handler-case 
672           (clsql:with-transaction () 
673             ;; valid update
674             (clsql:update-records [employee] 
675                                   :av-pairs '((email "lenin-nospam@soviet.org"))
676                                   :where [= [emplid] 1])
677             ;; invalid update which generates an error 
678             (clsql:update-records [employee] 
679                                   :av-pairs
680                                   '((emale "lenin-nospam@soviet.org"))
681                                   :where [= [emplid] 1]))
682         (clsql:sql-database-error ()
683           (progn
684             ;; check status 
685             (push (clsql:in-transaction-p) results)
686             ;; and check nothing done 
687             (push (clsql:select [email] :from [employee] :where [= [emplid] 1]
688                                :flatp t)
689                   results)
690             (apply #'values (nreverse results))))))
691   nil nil ("lenin@soviet.org"))
692
693
694 ))
695
696 #.(clsql:restore-sql-reader-syntax-state)