1 ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
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
11 ;;;; This file is part of CLSQL.
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 ;;;; *************************************************************************
18 (in-package #:clsql-sys)
23 (defun database-identifier (name database)
24 (sql-escape (etypecase name
25 ;; honor case of strings
27 #+nil (convert-to-db-default-case name database))
28 (sql-ident (sql-output name database))
29 (symbol (sql-output name database)))))
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)))
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))
74 (stmt (make-instance 'sql-create-table
77 :modifiers constraints
78 :transactions transactions)))
79 (execute-command stmt :database database)))
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
90 (unless (table-exists-p table-name :database database)
91 (return-from drop-table nil)))
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")))
104 (execute-command expr :database database))))
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))
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
121 (when (member (database-identifier name database)
122 (list-tables :owner owner :database database)
123 :test #'string-equal)
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)))
141 (stmt (make-instance 'sql-create-view
143 :column-list column-list
145 :with-check-option with-check-option)))
146 (execute-command stmt :database database)))
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
157 (unless (view-exists-p view-name :database database)
158 (return-from drop-view)))
161 (let ((expr (concatenate 'string "DROP VIEW " view-name)))
162 (execute-command expr :database database))))
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))
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)
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
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)))
202 (defun drop-index (name &key (if-does-not-exist :error)
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
211 (let ((index-name (database-identifier name database)))
212 (ecase if-does-not-exist
214 (unless (index-exists-p index-name :database database)
215 (return-from drop-index)))
217 (unless (db-type-use-column-on-drop-index?
218 (database-underlying-type database))
220 (execute-command (format nil "DROP INDEX ~A~A" index-name
222 (concatenate 'string " ON "
223 (database-identifier on database))))
224 :database database)))
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."
238 (database-list-indexes database :owner owner)
239 (let ((tables (typecase on (cons on) (t (list on)))))
241 (mapcar #'(lambda (table) (database-list-table-indexes
242 (database-identifier table database)
243 database :owner owner))
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
253 (when (member (database-identifier name database)
254 (list-indexes :owner owner :database database)
255 :test #'string-equal)
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.")
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
282 (multiple-value-bind (val found) (gethash table attribute-cache)
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)))
291 (setf (gethash table attribute-cache) (list action nil))))))
293 (maphash (lambda (k v)
296 (setf (gethash k attribute-cache) (list t nil)))
298 (setf (gethash k attribute-cache) (list nil nil)))
300 (setf (gethash k attribute-cache) (list t (second v))))))
303 (maphash (lambda (k v)
304 (when (eq (first v) :unspecified)
307 (setf (gethash k attribute-cache) (list t nil)))
309 (setf (gethash k attribute-cache) (list nil nil)))
311 (setf (gethash k attribute-cache) (list t (second v)))))))
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
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)
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))
360 (let ((types (mapcar #'(lambda (attribute)
363 (database-attribute-type
364 (database-identifier attribute
369 (list-attributes table :database database
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)
383 (defun create-sequence (name &key (database *default-database*))
384 "Creates a sequence called NAME in DATABASE which defaults to
386 (let ((sequence-name (database-identifier name database)))
387 (database-create-sequence sequence-name database))
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
399 (unless (sequence-exists-p sequence-name :database database)
400 (return-from drop-sequence)))
402 (database-drop-sequence sequence-name database))
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
412 (database-list-sequences database :owner owner))
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)
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))
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
436 (database-set-sequence-position (database-identifier name database)
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))