r9657: Cleanup and document the FDDL.
[clsql.git] / tests / test-fddl.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; ======================================================================
3 ;;;; File:     test-fddl.lisp
4 ;;;; Authors:  Marcus Pearce <m.t.pearce@city.ac.uk> and Kevin Rosenberg
5 ;;;; Created:  30/03/2004
6 ;;;; Updated:  $Id$
7 ;;;;
8 ;;;; Tests for the CLSQL Functional Data Definition Language
9 ;;;; (FDDL).
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-fddl*
23       '(
24         
25 ;; list current tables 
26 (deftest :fddl/table/1
27     (sort (mapcar #'string-downcase
28                   (clsql:list-tables :owner *test-database-user*))
29      #'string<)
30   ("addr" "big" "company" "ea_join" "employee" "type_bigint" "type_table"))
31
32 ;; create a table, test for its existence, drop it and test again 
33 (deftest :fddl/table/2
34     (progn (clsql:create-table  [foo]
35                                '(([id] integer)
36                                  ([height] float)
37                                  ([name] (string 24))
38                                  ([comments] longchar)))
39            (values
40             (clsql:table-exists-p [foo] :owner *test-database-user*)
41             (progn
42               (clsql:drop-table [foo] :if-does-not-exist :ignore)
43               (clsql:table-exists-p [foo] :owner *test-database-user*))))
44   t nil)
45
46 ;; create a table, list its attributes and drop it 
47 (deftest :fddl/table/3
48     (apply #'values 
49            (progn (clsql:create-table  [foo]
50                                       '(([id] integer)
51                                         ([height] float)
52                                         ([name] (char 255))
53                                         ([comments] longchar)))
54                   (prog1
55                       (sort (mapcar #'string-downcase
56                                     (clsql:list-attributes [foo]))
57                             #'string<)
58                     (clsql:drop-table [foo] :if-does-not-exist :ignore))))
59   "comments" "height" "id" "name")
60
61 (deftest :fddl/table/4
62     (values
63      (clsql:table-exists-p "MyMixedCase")
64      (progn
65        (clsql:create-table "MyMixedCase" '(([a] integer)))
66        (clsql:table-exists-p "MyMixedCase"))
67      (progn
68        (clsql:drop-table "MyMixedCase")
69        (clsql:table-exists-p "MyMixedCase")))
70   nil t nil)
71
72 (deftest :fddl/table/5
73     (prog1
74         (progn
75           (clsql:create-table "MyMixedCase" '(([a] integer)))
76           (clsql:execute-command "insert into MyMixedCase values (5)")
77            (clsql:insert-records :into "MyMixedCase" :values '(6))
78            (clsql:select [a] :from "MyMixedCase" :order-by '((a :asc))))
79       (clsql:drop-table "MyMixedCase"))
80   ((5) (6)))
81
82 (deftest :fddl/attributes/1
83     (apply #'values
84            (sort 
85             (mapcar #'string-downcase
86                     (clsql:list-attributes [employee]
87                                            :owner *test-database-user*))
88             #'string<))
89   "bd_utime" "birthday" "ecompanyid" "email" "emplid" "first_name" "groupid" "height"
90   "last_name" "managerid" "married")
91
92 (deftest :fddl/attributes/2
93     (apply #'values 
94            (sort 
95             (mapcar #'(lambda (a) (string-downcase (car a)))
96                     (clsql:list-attribute-types [employee]
97                                                :owner *test-database-user*))
98             #'string<))
99   "bd_utime" "birthday" "ecompanyid" "email" "emplid" "first_name" "groupid" "height"
100   "last_name" "managerid" "married")
101
102 ;; Attribute types are vendor specific so need to test a range
103 (deftest :fddl/attributes/3
104     (and (member (clsql:attribute-type [emplid] [employee]) '(:int :integer :int4 :number)) t)
105   t)
106
107 (deftest :fddl/attributes/4
108     (multiple-value-bind (type length scale nullable)
109         (clsql:attribute-type [first-name] [employee])
110       (values (clsql-sys:in type :varchar :varchar2) length scale nullable))
111   t 30 nil 1)
112
113 (deftest :fddl/attributes/5
114     (and (member (clsql:attribute-type [birthday] [employee]) '(:datetime :timestamp :date)) t)
115   t)
116
117 (deftest :fddl/attributes/6
118     (and (member (clsql:attribute-type [height] [employee]) '(:float :float8 :number)) t)
119   t)
120
121 (deftest :fddl/attributes/7
122     (and (member (clsql:attribute-type [bd_utime] [employee]) '(:bigint :int8 :char)) t)
123   t)
124
125
126 ;; create a view, test for existence, drop it and test again
127 (deftest :fddl/view/1
128     (progn (clsql:create-view [lenins-group]
129                               :as [select [first-name] [last-name] [email]
130                                           :from [employee]
131                                           :where [= [managerid] 1]])
132            (values  
133             (clsql:view-exists-p [lenins-group] :owner *test-database-user*)
134             (progn
135               (clsql:drop-view [lenins-group] :if-does-not-exist :ignore)
136               (clsql:view-exists-p [lenins-group] :owner *test-database-user*))))
137   t nil)
138   
139   ;; create a view, list its attributes and drop it 
140 (when (clsql-sys:db-type-has-views? *test-database-underlying-type*)
141   (deftest :fddl/view/2
142       (progn (clsql:create-view [lenins-group]
143                                 :as [select [first-name] [last-name] [email]
144                                             :from [employee]
145                                             :where [= [managerid] 1]])
146              (prog1
147                  (sort (mapcar #'string-downcase
148                                (clsql:list-attributes [lenins-group]))
149                        #'string<)
150                (clsql:drop-view [lenins-group] :if-does-not-exist :ignore)))
151     ("email" "first_name" "last_name")))
152   
153   ;; create a view, select stuff from it and drop it 
154 (deftest :fddl/view/3
155     (progn (clsql:create-view [lenins-group]
156                               :as [select [first-name] [last-name] [email]
157                                           :from [employee]
158                                           :where [= [managerid] 1]])
159            (let ((result 
160                   (list 
161                    ;; Shouldn't exist 
162                    (clsql:select [first-name] [last-name] [email]
163                                  :from [lenins-group]
164                                  :where [= [last-name] "Lenin"])
165                    ;; Should exist 
166                    (car (clsql:select [first-name] [last-name] [email]
167                                       :from [lenins-group]
168                                       :where [= [last-name] "Stalin"])))))
169              (clsql:drop-view [lenins-group] :if-does-not-exist :ignore)
170              (apply #'values result)))
171   nil ("Josef" "Stalin" "stalin@soviet.org"))
172   
173 (deftest :fddl/view/4
174     (progn (clsql:create-view [lenins-group]
175                               :column-list '([forename] [surname] [email])
176                               :as [select [first-name] [last-name] [email]
177                                           :from [employee]
178                                           :where [= [managerid] 1]])
179            (let ((result 
180                   (list
181                    ;; Shouldn't exist 
182                    (clsql:select [forename] [surname] [email]
183                                  :from [lenins-group]
184                                  :where [= [surname] "Lenin"])
185                    ;; Should exist 
186                    (car (clsql:select [forename] [surname] [email]
187                                       :from [lenins-group]
188                                       :where [= [surname] "Stalin"])))))
189              (clsql:drop-view [lenins-group] :if-does-not-exist :ignore)
190              (apply #'values result)))
191   nil ("Josef" "Stalin" "stalin@soviet.org"))
192
193 ;; create an index, test for existence, drop it and test again 
194 (deftest :fddl/index/1
195     (progn (clsql:create-index [bar] :on [employee] :attributes
196                               '([first-name] [last-name] [email]) :unique t)
197            (values
198             (clsql:index-exists-p [bar] :owner *test-database-user*)
199             (progn
200               (clsql:drop-index [bar] :on [employee]
201                                 :if-does-not-exist :ignore)
202               (clsql:index-exists-p [bar] :owner *test-database-user*))))
203   t nil)
204
205 ;; create indexes with names as strings, symbols and in square brackets 
206 (deftest :fddl/index/2
207     (let ((names '("foo" foo [foo]))
208           (result '()))
209       (dolist (name names)
210         (clsql:create-index name :on [employee] :attributes '([emplid]))
211         (push (clsql:index-exists-p name :owner *test-database-user*) result)
212         (clsql:drop-index name :on [employee] :if-does-not-exist :ignore))
213       (apply #'values result))
214   t t t)
215
216 ;; test list-indexes with keyword :ON 
217 (deftest :fddl/index/3
218     (progn
219       (clsql:create-table [i3test] '(([a] (string 10))
220                                      ([b] integer)))
221       (clsql:create-index [foo] :on [i3test] :attributes
222        '([b]) :unique nil)
223       (clsql:create-index [bar] :on [i3test] :attributes
224        '([a]) :unique t)
225       (values
226        (clsql:table-exists-p [i3test])
227        (clsql:index-exists-p [foo])
228        (clsql:index-exists-p [bar])
229        (sort 
230         (mapcar 
231          #'string-downcase
232          (clsql:list-indexes :on [i3test] :owner *test-database-user*))
233         #'string-lessp)
234        (progn
235          (clsql:drop-index [bar] :on [i3test])
236          (clsql:drop-index [foo] :on [i3test])
237          (clsql:drop-table [i3test])
238          t)))
239   t t t ("bar" "foo") t)
240
241 ;; create an sequence, test for existence, drop it and test again 
242 (deftest :fddl/sequence/1
243     (progn (clsql:create-sequence [foo])
244            (values
245             (clsql:sequence-exists-p [foo] :owner *test-database-user*)
246             (progn
247               (clsql:drop-sequence [foo] :if-does-not-exist :ignore)
248               (clsql:sequence-exists-p [foo] :owner *test-database-user*))))
249   t nil)
250
251 ;; create and increment a sequence
252 (deftest :fddl/sequence/2
253     (let ((val1 nil))
254       (clsql:create-sequence [foo])
255       (setf val1 (clsql:sequence-next [foo]))
256       (prog1
257           (< val1 (clsql:sequence-next [foo]))
258         (clsql:drop-sequence [foo] :if-does-not-exist :ignore)))
259   t)
260
261 ;; explicitly set the value of a sequence
262 (deftest :fddl/sequence/3
263     (progn
264       (clsql:create-sequence [foo])
265       (clsql:set-sequence-position [foo] 5)
266       (prog1
267           (clsql:sequence-next [foo])
268         (clsql:drop-sequence [foo] :if-does-not-exist :ignore)))
269   6)
270
271 (deftest :fddl/big/1 
272     (let ((rows (clsql:select [*] :from [big] :field-names nil)))
273       (values
274        (length rows)
275        (do ((i 0 (1+ i))
276             (max (expt 2 60))
277             (rest rows (cdr rest)))
278            ((= i (length rows)) t)
279          (let ((index (1+ i))
280                (int (first (car rest)))
281                (bigint (second (car rest))))
282            (when (and (or (eq *test-database-type* :oracle)
283                           (and (eq *test-database-type* :odbc)
284                                (eq *test-database-underlying-type* :postgresql)))
285                       (stringp bigint))
286              (setf bigint (parse-integer bigint)))
287            (unless (and (eql int index)
288                         (eql bigint (truncate max index)))
289              (return nil))))))
290   555 t)
291
292            
293 ))
294
295 #.(clsql:restore-sql-reader-syntax-state)