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