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