r10146: 09 Nov 2004 Kevin Rosenberg <kevin@rosenberg.net>
[clsql.git] / sql / fddl.lisp
1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;;
4 ;;;; $Id$
5 ;;;;
6 ;;;; The CLSQL Functional Data Definition Language (FDDL)
7 ;;;; including functions for schema manipulation. Currently supported
8 ;;;; SQL objects include tables, views, indexes, attributes and
9 ;;;; sequences.
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-sys)
19
20
21 ;; Utilities
22
23 (defun database-identifier (name database)
24   (sql-escape (etypecase name
25                 ;; honor case of strings
26                 (string name
27                         #+nil (convert-to-db-default-case name database))
28                 (sql-ident (sql-output name database))
29                 (symbol (sql-output name database)))))
30
31
32 ;; Truncate database 
33
34 (defun truncate-database (&key (database *default-database*))
35   "Drops all tables, views, indexes and sequences in DATABASE which
36 defaults to *DEFAULT-DATABASE*."
37   (unless (typep database 'database)
38     (signal-no-database-error database))
39   (unless (is-database-open database)
40     (database-reconnect database))
41   (when (eq :oracle (database-type database))
42     (ignore-errors (execute-command "PURGE RECYCLEBIN" :database database)))
43   (when (db-type-has-views? (database-underlying-type database))
44     (dolist (view (list-views :database database))
45       (drop-view view :database database)))
46   (dolist (table (list-tables :database database))
47     (drop-table table :database database))
48   (dolist (index (list-indexes :database database))
49     (drop-index index :database database))
50   (dolist (seq (list-sequences :database database))
51     (drop-sequence seq :database database))
52   (when (eq :oracle (database-type database))
53     (ignore-errors (execute-command "PURGE RECYCLEBIN" :database database)))
54   (values))
55
56
57 ;; Tables 
58
59 (defun create-table (name description &key (database *default-database*)
60                           (constraints nil) (transactions t))
61   "Creates a table called NAME, which may be a string, symbol or
62 SQL table identifier, in DATABASE which defaults to
63 *DEFAULT-DATABASE*. DESCRIPTION is a list whose elements are
64 lists containing the attribute names, types, and other
65 constraints such as not-null or primary-key for each column in
66 the table.  CONSTRAINTS is a string representing an SQL table
67 constraint expression or a list of such strings. With MySQL
68 databases, if TRANSACTIONS is t an InnoDB table is created which
69 supports transactions."
70   (let* ((table-name (etypecase name 
71                        (symbol (sql-expression :attribute name))
72                        (string (sql-expression :attribute name))
73                        (sql-ident name)))
74          (stmt (make-instance 'sql-create-table
75                               :name table-name
76                               :columns description
77                               :modifiers constraints
78                               :transactions transactions)))
79     (execute-command stmt :database database)))
80
81 (defun drop-table (name &key (if-does-not-exist :error)
82                              (database *default-database*))
83   "Drops the table called NAME from DATABASE which defaults to
84 *DEFAULT-DATABASE*. If the table does not exist and
85 IF-DOES-NOT-EXIST is :ignore then DROP-TABLE returns nil whereas
86 an error is signalled if IF-DOES-NOT-EXIST is :error."
87   (let ((table-name (database-identifier name database)))
88     (ecase if-does-not-exist
89       (:ignore
90        (unless (table-exists-p table-name :database database)
91          (return-from drop-table nil)))
92       (:error
93        t))
94     
95     ;; Fixme: move to clsql-oracle
96     (let ((expr (concatenate 'string "DROP TABLE " table-name)))
97       (when (and (find-package 'clsql-oracle)
98                  (eq :oracle (database-type database))
99                  (eql 10 (slot-value database 
100                                      (intern (symbol-name '#:major-server-version)
101                                              (symbol-name '#:clsql-oracle)))))
102         (setq expr (concatenate 'string expr " PURGE")))
103
104       (execute-command expr :database database))))
105
106 (defun list-tables (&key (owner nil) (database *default-database*))
107   "Returns a list of strings representing table names in DATABASE
108 which defaults to *DEFAULT-DATABASE*. OWNER is nil by default
109 which means that only tables owned by users are listed. If OWNER
110 is a string denoting a user name, only tables owned by OWNER are
111 listed. If OWNER is :all then all tables are listed."
112   (database-list-tables database :owner owner))
113
114 (defun table-exists-p (name &key (owner nil) (database *default-database*))
115   "Tests for the existence of an SQL table called NAME in DATABASE
116 which defaults to *DEFAULT-DATABASE*. OWNER is nil by default
117 which means that only tables owned by users are examined. If
118 OWNER is a string denoting a user name, only tables owned by
119 OWNER are examined. If OWNER is :all then all tables are
120 examined."
121   (when (member (database-identifier name database)
122                 (list-tables :owner owner :database database)
123                 :test #'string-equal)
124     t))
125
126
127 ;; Views 
128
129 (defun create-view (name &key as column-list (with-check-option nil)
130                          (database *default-database*))
131   "Creates a view called NAME in DATABASE which defaults to
132 *DEFAULT-DATABASE*. The view is created using the query AS and
133 the columns of the view may be specified using the COLUMN-LIST
134 parameter. The WITH-CHECK-OPTION is nil by default but if it has
135 a non-nil value, then all insert/update commands on the view are
136 checked to ensure that the new data satisfy the query AS."
137   (let* ((view-name (etypecase name 
138                       (symbol (sql-expression :attribute name))
139                       (string (sql-expression :attribute (make-symbol name)))
140                       (sql-ident name)))
141          (stmt (make-instance 'sql-create-view
142                               :name view-name
143                               :column-list column-list
144                               :query as
145                               :with-check-option with-check-option)))
146     (execute-command stmt :database database)))
147
148 (defun drop-view (name &key (if-does-not-exist :error)
149                        (database *default-database*))
150   "Drops the view called NAME from DATABASE which defaults to
151 *DEFAULT-DATABASE*. If the view does not exist and
152 IF-DOES-NOT-EXIST is :ignore then DROP-VIEW returns nil whereas
153 an error is signalled if IF-DOES-NOT-EXIST is :error."
154   (let ((view-name (database-identifier name database)))
155     (ecase if-does-not-exist
156       (:ignore
157        (unless (view-exists-p view-name :database database)
158          (return-from drop-view)))
159       (:error
160        t))
161     (let ((expr (concatenate 'string "DROP VIEW " view-name)))
162       (execute-command expr :database database))))
163
164 (defun list-views (&key (owner nil) (database *default-database*))
165   "Returns a list of strings representing view names in DATABASE
166 which defaults to *DEFAULT-DATABASE*. OWNER is nil by default
167 which means that only views owned by users are listed. If OWNER
168 is a string denoting a user name, only views owned by OWNER are
169 listed. If OWNER is :all then all views are listed."
170   (database-list-views database :owner owner))
171
172 (defun view-exists-p (name &key (owner nil) (database *default-database*))
173   "Tests for the existence of an SQL view called NAME in DATABASE
174 which defaults to *DEFAULT-DATABASE*. OWNER is nil by default
175 which means that only views owned by users are examined. If OWNER
176 is a string denoting a user name, only views owned by OWNER are
177 examined. If OWNER is :all then all views are examined."
178   (when (member (database-identifier name database)
179                 (list-views :owner owner :database database)
180                 :test #'string-equal)
181     t))
182
183
184 ;; Indexes 
185
186 (defun create-index (name &key on (unique nil) attributes
187                           (database *default-database*))
188   "Creates an index called NAME on the table specified by ON in
189 DATABASE which default to *DEFAULT-DATABASE*. The table
190 attributes to use in constructing the index NAME are specified by
191 ATTRIBUTES. The UNIQUE argument is nil by default but if it has a
192 non-nil value then the indexed attributes must have unique
193 values."
194   (let* ((index-name (database-identifier name database))
195          (table-name (database-identifier on database))
196          (attributes (mapcar #'(lambda (a) (database-identifier a database)) (listify attributes)))
197          (stmt (format nil "CREATE ~A INDEX ~A ON ~A (~{~A~^, ~})"
198                        (if unique "UNIQUE" "")
199                        index-name table-name attributes)))
200     (execute-command stmt :database database)))
201
202 (defun drop-index (name &key (if-does-not-exist :error)
203                         (on nil)
204                         (database *default-database*))
205   "Drops the index called NAME in DATABASE which defaults to
206 *DEFAULT-DATABASE*. If the index does not exist and
207 IF-DOES-NOT-EXIST is :ignore then DROP-INDEX returns nil whereas
208 an error is signalled if IF-DOES-NOT-EXIST is :error. The
209 argument ON allows the optional specification of a table to drop
210 the index from."
211   (let ((index-name (database-identifier name database)))
212     (ecase if-does-not-exist
213       (:ignore
214        (unless (index-exists-p index-name :database database)
215          (return-from drop-index)))
216       (:error t))
217     (unless (db-type-use-column-on-drop-index? 
218              (database-underlying-type database))
219       (setq on nil))
220     (execute-command (format nil "DROP INDEX ~A~A" index-name
221                              (if (null on) ""
222                                  (concatenate 'string " ON "
223                                               (database-identifier on database))))
224                      :database database)))
225
226 (defun list-indexes (&key (owner nil) (database *default-database*) (on nil))
227   "Returns a list of strings representing index names in DATABASE
228 which defaults to *DEFAULT-DATABASE*. OWNER is nil by default
229 which means that only indexes owned by users are listed. If OWNER
230 is a string denoting a user name, only indexes owned by OWNER are
231 listed. If OWNER is :all then all indexes are listed. The keyword
232 argument ON limits the results to indexes on the specified
233 tables. Meaningful values for ON are nil (the default) which
234 means that all tables are considered, a string, symbol or SQL
235 expression representing a table name in DATABASE or a list of
236 such table identifiers."
237   (if (null on) 
238       (database-list-indexes database :owner owner)
239       (let ((tables (typecase on (cons on) (t (list on)))))
240         (reduce #'append 
241                 (mapcar #'(lambda (table) (database-list-table-indexes 
242                                            (database-identifier table database)
243                                            database :owner owner))
244                         tables)))))
245   
246 (defun index-exists-p (name &key (owner nil) (database *default-database*))
247   "Tests for the existence of an SQL index called NAME in DATABASE
248 which defaults to *DEFAULT-DATABASE*. OWNER is nil by default
249 which means that only indexes owned by users are examined. If
250 OWNER is a string denoting a user name, only indexes owned by
251 OWNER are examined. If OWNER is :all then all indexes are
252 examined."
253   (when (member (database-identifier name database)
254                 (list-indexes :owner owner :database database)
255                 :test #'string-equal)
256     t))
257
258 ;; Attributes 
259
260 (defvar *cache-table-queries-default* nil 
261   "Specifies the default behaivour for caching of attribute
262   types. Meaningful values are t, nil and :flush as described for
263   the action argument to CACHE-TABLE-QUERIES.")
264
265 (defun cache-table-queries (table &key (action nil) (database *default-database*))
266   "Controls the caching of attribute type information on the
267 table specified by TABLE in DATABASE which defaults to
268 *DEFAULT-DATABASE*. ACTION specifies the caching behaviour to
269 adopt. If its value is t then attribute type information is
270 cached whereas if its value is nil then attribute type
271 information is not cached. If ACTION is :flush then all existing
272 type information in the cache for TABLE is removed, but caching
273 is still enabled. TABLE may be a string representing a table for
274 which the caching action is to be taken while the caching action
275 is applied to all tables if TABLE is t. Alternativly, when TABLE
276 is :default, the default caching action specified by
277 *CACHE-TABLE-QUERIES-DEFAULT* is applied to all table for which a
278 caching action has not been explicitly set."
279   (with-slots (attribute-cache) database
280     (cond
281       ((stringp table)
282        (multiple-value-bind (val found) (gethash table attribute-cache)
283          (cond
284            ((and found (eq action :flush))
285             (setf (gethash table attribute-cache) (list t nil)))
286            ((and found (eq action t))
287             (setf (gethash table attribute-cache) (list t (second val))))
288            ((and found (null action))
289             (setf (gethash table attribute-cache) (list nil nil)))
290            ((not found)
291             (setf (gethash table attribute-cache) (list action nil))))))
292       ((eq table t)
293        (maphash (lambda (k v)
294                   (cond
295                     ((eq action :flush)
296                      (setf (gethash k attribute-cache) (list t nil)))
297                     ((null action)
298                      (setf (gethash k attribute-cache) (list nil nil)))
299                     ((eq t action)
300                      (setf (gethash k attribute-cache) (list t (second v))))))
301                 attribute-cache))
302       ((eq table :default)
303        (maphash (lambda (k v)
304                   (when (eq (first v) :unspecified)
305                     (cond
306                       ((eq action :flush)
307                        (setf (gethash k attribute-cache) (list t nil)))
308                       ((null action)
309                        (setf (gethash k attribute-cache) (list nil nil)))
310                       ((eq t action)
311                        (setf (gethash k attribute-cache) (list t (second v)))))))
312                 attribute-cache))))
313   (values))
314                   
315
316 (defun list-attributes (name &key (owner nil) (database *default-database*))
317   "Returns a list of strings representing the attributes of table
318 NAME in DATABASE which defaults to *DEFAULT-DATABASE*. OWNER is
319 nil by default which means that only attributes owned by users
320 are listed. If OWNER is a string denoting a user name, only
321 attributes owned by OWNER are listed. If OWNER is :all then all
322 attributes are listed."
323   (database-list-attributes (database-identifier name database) database 
324                             :owner owner))
325
326 (defun attribute-type (attribute table &key (owner nil)
327                                  (database *default-database*))
328   "Returns a keyword representing the vendor-specific field type
329 of the supplied attribute ATTRIBUTE in the table specified by
330 TABLE in DATABASE which defaults to *DEFAULT-DATABASE*. OWNER is
331 nil by default which means that the attribute specified by
332 ATTRIBUTE, if it exists, must be user owned else nil is
333 returned. If OWNER is a string denoting a user name, the
334 attribute, if it exists, must be owned by OWNER else nil is
335 returned, whereas if OWNER is :all then the attribute, if it
336 exists, will be returned regardless of its owner."
337   (database-attribute-type (database-identifier attribute database)
338                            (database-identifier table database)
339                            database
340                            :owner owner))
341
342 (defun list-attribute-types (table &key (owner nil)
343                                    (database *default-database*))
344   "Returns a list containing information about the SQL types of
345 each of the attributes in the table specified by TABLE in
346 DATABASE which has a default value of *DEFAULT-DATABASE*. OWNER
347 is nil by default which means that only attributes owned by users
348 are listed. If OWNER is a string denoting a user name, only
349 attributes owned by OWNER are listed. If OWNER is :all then all
350 attributes are listed. The elements of the returned list are
351 lists where the first element is the name of the attribute, the
352 second element is its SQL type, the third is the type precision,
353 the fourth is the scale of the attribute and the fifth is 1 if
354 the attribute accepts null values and otherwise 0."
355   (with-slots (attribute-cache) database
356     (let ((table-ident (database-identifier table database)))
357       (multiple-value-bind (val found) (gethash table-ident attribute-cache)
358         (if (and found (second val))
359             (second val)
360             (let ((types (mapcar #'(lambda (attribute)
361                                      (cons attribute
362                                            (multiple-value-list
363                                             (database-attribute-type
364                                              (database-identifier attribute 
365                                                                   database)
366                                              table-ident
367                                              database
368                                              :owner owner))))
369                                  (list-attributes table :database database 
370                                                   :owner owner))))
371               (cond
372                 ((and (not found) (eq t *cache-table-queries-default*))
373                  (setf (gethash table-ident attribute-cache) 
374                        (list :unspecified types)))
375                 ((and found (eq t (first val)) 
376                       (setf (gethash table-ident attribute-cache) 
377                             (list t types)))))
378               types))))))
379   
380
381 ;; Sequences 
382
383 (defun create-sequence (name &key (database *default-database*))
384   "Creates a sequence called NAME in DATABASE which defaults to
385 *DEFAULT-DATABASE*."
386   (let ((sequence-name (database-identifier name database)))
387     (database-create-sequence sequence-name database))
388   (values))
389
390 (defun drop-sequence (name &key (if-does-not-exist :error)
391                            (database *default-database*))
392   "Drops the sequence called NAME from DATABASE which defaults to
393 *DEFAULT-DATABASE*. If the sequence does not exist and
394 IF-DOES-NOT-EXIST is :ignore then DROP-SEQUENCE returns nil
395 whereas an error is signalled if IF-DOES-NOT-EXIST is :error."
396   (let ((sequence-name (database-identifier name database)))
397     (ecase if-does-not-exist
398       (:ignore
399        (unless (sequence-exists-p sequence-name :database database)
400          (return-from drop-sequence)))
401       (:error t))
402     (database-drop-sequence sequence-name database))
403   (values))
404
405 (defun list-sequences (&key (owner nil) (database *default-database*))
406   "Returns a list of strings representing sequence names in
407 DATABASE which defaults to *DEFAULT-DATABASE*. OWNER is nil by
408 default which means that only sequences owned by users are
409 listed. If OWNER is a string denoting a user name, only sequences
410 owned by OWNER are listed. If OWNER is :all then all sequences
411 are listed."
412   (database-list-sequences database :owner owner))
413
414 (defun sequence-exists-p (name &key (owner nil)
415                                (database *default-database*))
416   "Tests for the existence of an SQL sequence called NAME in
417 DATABASE which defaults to *DEFAULT-DATABASE*. OWNER is nil by
418 default which means that only sequences owned by users are
419 examined. If OWNER is a string denoting a user name, only
420 sequences owned by OWNER are examined. If OWNER is :all then all
421 sequences are examined."
422   (when (member (database-identifier name database)
423                 (list-sequences :owner owner :database database)
424                 :test #'string-equal)
425     t))
426   
427 (defun sequence-next (name &key (database *default-database*))
428   "Increment and return the next value in the sequence called
429   NAME in DATABASE which defaults to *DEFAULT-DATABASE*."
430   (database-sequence-next (database-identifier name database) database))
431
432 (defun set-sequence-position (name position &key (database *default-database*))
433   "Explicitly set the the position of the sequence called NAME in
434 DATABASE, which defaults to *DEFAULT-DATABSE*, to POSITION which
435 is returned."
436   (database-set-sequence-position (database-identifier name database) 
437                                   position database))
438
439 (defun sequence-last (name &key (database *default-database*))
440   "Return the last value allocated in the sequence called NAME in DATABASE
441   which defaults to *DEFAULT-DATABASE*."
442   (database-sequence-last (database-identifier name database) database))
443