-;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
-;;;; ======================================================================
-;;;; File: table.lisp
-;;;; Updated: <04/04/2004 12:05:03 marcusp>
-;;;; ======================================================================
-;;;;
-;;;; Description ==========================================================
-;;;; ======================================================================
-;;;;
-;;;; The CLSQL-USQL Functional Data Definition Language (FDDL)
-;;;; including functions for schema manipulation. Currently supported
-;;;; SQL objects include tables, views, indexes, attributes and
-;;;; sequences.
-;;;;
-;;;; ======================================================================
-
-(in-package :clsql-usql-sys)
-
-
-;; Utilities
-
-(defun database-identifier (name)
- (sql-escape (etypecase name
- (string
- (string-upcase name))
- (sql-ident
- (sql-output name))
- (symbol
- (sql-output name)))))
-
-
-;; Tables
-
-(defvar *table-schemas* (make-hash-table :test #'equal)
- "Hash of schema name to table lists.")
-
-(defun create-table (name description &key (database *default-database*)
- (constraints nil))
- "Create a table called NAME, in DATABASE which defaults to
-*DEFAULT-DATABASE*, containing the attributes in DESCRIPTION which is
-a list containing lists of attribute-name and type information pairs."
- (let* ((table-name (etypecase name
- (symbol (sql-expression :attribute name))
- (string (sql-expression :attribute (make-symbol name)))
- (sql-ident name)))
- (stmt (make-instance 'sql-create-table
- :name table-name
- :columns description
- :modifiers constraints)))
- (pushnew table-name (gethash *default-schema* *table-schemas*)
- :test #'equal)
- (execute-command stmt :database database)))
-
-(defun drop-table (name &key (if-does-not-exist :error)
- (database *default-database*))
- "Drops table NAME from DATABASE which defaults to
-*DEFAULT-DATABASE*. If the table does not exist and IF-DOES-NOT-EXIST
-is :ignore then DROP-TABLE returns nil whereas an error is signalled
-if IF-DOES-NOT-EXIST is :error."
- (let ((table-name (database-identifier name)))
- (ecase if-does-not-exist
- (:ignore
- (unless (table-exists-p table-name :database database)
- (return-from drop-table nil)))
- (:error
- t))
- (let ((expr (concatenate 'string "DROP TABLE " table-name)))
- (execute-command expr :database database))))
-
-(defun list-tables (&key (owner nil) (database *default-database*))
- "List all tables in DATABASE which defaults to
-*DEFAULT-DATABASE*. If OWNER is nil, only user-owned tables are
-considered. This is the default. If OWNER is :all , all tables are
-considered. If OWNER is a string, this denotes a username and only
-tables owned by OWNER are considered. Table names are returned as a
-list of strings."
- (database-list-tables database :owner owner))
-
-(defun table-exists-p (name &key (owner nil) (database *default-database*))
- "Test for existence of an SQL table called NAME in DATABASE which
-defaults to *DEFAULT-DATABASE*. If OWNER is nil, only user-owned
-tables are considered. This is the default. If OWNER is :all , all
-tables are considered. If OWNER is a string, this denotes a username
-and only tables owned by OWNER are considered. Table names are
-returned as a list of strings."
- (when (member (database-identifier name)
- (list-tables :owner owner :database database)
- :test #'string-equal)
- t))
-
-
-;; Views
-
-(defvar *view-schemas* (make-hash-table :test #'equal)
- "Hash of schema name to view lists.")
-
-(defun create-view (name &key as column-list (with-check-option nil)
- (database *default-database*))
- "Creates a view called NAME using the AS query and the optional
-COLUMN-LIST and WITH-CHECK-OPTION. The COLUMN-LIST argument is a list
-of columns to add to the view. The WITH-CHECK-OPTION adds 'WITH CHECK
-OPTION' to the resulting SQL. The default value of WITH-CHECK-OPTION
-is NIL. The default value of DATABASE is *DEFAULT-DATABASE*."
- (let* ((view-name (etypecase name
- (symbol (sql-expression :attribute name))
- (string (sql-expression :attribute (make-symbol name)))
- (sql-ident name)))
- (stmt (make-instance 'sql-create-view
- :name view-name
- :column-list column-list
- :query as
- :with-check-option with-check-option)))
- (pushnew view-name (gethash *default-schema* *view-schemas*) :test #'equal)
- (execute-command stmt :database database)))
-
-(defun drop-view (name &key (if-does-not-exist :error)
- (database *default-database*))
- "Deletes view NAME from DATABASE which defaults to
-*DEFAULT-DATABASE*. If the view does not exist and IF-DOES-NOT-EXIST
-is :ignore then DROP-VIEW returns nil whereas an error is signalled if
-IF-DOES-NOT-EXIST is :error."
- (let ((view-name (database-identifier name)))
- (ecase if-does-not-exist
- (:ignore
- (unless (view-exists-p view-name :database database)
- (return-from drop-view)))
- (:error
- t))
- (let ((expr (concatenate 'string "DROP VIEW " view-name)))
- (execute-command expr :database database))))
-
-(defun list-views (&key (owner nil) (database *default-database*))
- "List all views in DATABASE which defaults to *DEFAULT-DATABASE*. If
-OWNER is nil, only user-owned views are considered. This is the
-default. If OWNER is :all , all views are considered. If OWNER is a
-string, this denotes a username and only views owned by OWNER are
-considered. View names are returned as a list of strings."
- (database-list-views database :owner owner))
-
-(defun view-exists-p (name &key (owner nil) (database *default-database*))
- "Test for existence of an SQL view called NAME in DATABASE which
-defaults to *DEFAULT-DATABASE*. If OWNER is nil, only user-owned views
-are considered. This is the default. If OWNER is :all , all views are
-considered. If OWNER is a string, this denotes a username and only
-views owned by OWNER are considered. View names are returned as a list
-of strings."
- (when (member (database-identifier name)
- (list-views :owner owner :database database)
- :test #'string-equal)
- t))
-
-
-;; Indexes
-
-(defvar *index-schemas* (make-hash-table :test #'equal)
- "Hash of schema name to index lists.")
-
-(defun create-index (name &key on (unique nil) attributes
- (database *default-database*))
- "Creates an index called NAME on the table specified by ON. The
-attributes of the table to index are given by ATTRIBUTES. Setting
-UNIQUE to T includes UNIQUE in the SQL index command, specifying that
-the columns indexed must contain unique values. The default value of
-UNIQUE is nil. The default value of DATABASE is *DEFAULT-DATABASE*."
- (let* ((index-name (database-identifier name))
- (table-name (database-identifier on))
- (attributes (mapcar #'database-identifier (listify attributes)))
- (stmt (format nil "CREATE ~A INDEX ~A ON ~A (~{~A~^, ~})"
- (if unique "UNIQUE" "")
- index-name table-name attributes)))
- (pushnew index-name (gethash *default-schema* *index-schemas*))
- (execute-command stmt :database database)))
-
-(defun drop-index (name &key (if-does-not-exist :error)
- (on nil)
- (database *default-database*))
- "Deletes index NAME from table FROM in DATABASE which defaults to
-*DEFAULT-DATABASE*. If the index does not exist and IF-DOES-NOT-EXIST
-is :ignore then DROP-INDEX returns nil whereas an error is signalled
-if IF-DOES-NOT-EXIST is :error. The argument ON allows the optional
-specification of a table to drop the index from."
- (let ((index-name (database-identifier name)))
- (ecase if-does-not-exist
- (:ignore
- (unless (index-exists-p index-name :database database)
- (return-from drop-index)))
- (:error t))
- (execute-command (format nil "DROP INDEX ~A~A" index-name
- (if (null on) ""
- (concatenate 'string " ON "
- (database-identifier on))))
- :database database)))
-
-(defun list-indexes (&key (owner nil) (database *default-database*))
- "List all indexes in DATABASE, which defaults to
-*default-database*. If OWNER is :all , all indexs are considered. If
-OWNER is a string, this denotes a username and only indexs owned by
-OWNER are considered. Index names are returned as a list of strings."
- (database-list-indexes database :owner owner))
-
-(defun index-exists-p (name &key (owner nil) (database *default-database*))
- "Test for existence of an index called NAME in DATABASE which
-defaults to *DEFAULT-DATABASE*. If OWNER is :all , all indexs are
-considered. If OWNER is a string, this denotes a username and only
-indexs owned by OWNER are considered. Index names are returned as a
-list of strings."
- (when (member (database-identifier name)
- (list-indexes :owner owner :database database)
- :test #'string-equal)
- t))
-
-;; Attributes
-
-(defun list-attributes (name &key (owner nil) (database *default-database*))
- "List the attributes of a attribute called NAME in DATABASE which
-defaults to *DEFAULT-DATABASE*. If OWNER is nil, only user-owned
-attributes are considered. This is the default. If OWNER is :all , all
-attributes are considered. If OWNER is a string, this denotes a
-username and only attributes owned by OWNER are considered. Attribute
-names are returned as a list of strings. Attributes are returned as a
-list of strings."
- (database-list-attributes (database-identifier name) database :owner owner))
-
-(defun attribute-type (attribute table &key (owner nil)
- (database *default-database*))
- "Return the field type of the ATTRIBUTE in TABLE. The optional
-keyword argument DATABASE specifies the database to query, defaulting
-to *DEFAULT-DATABASE*. If OWNER is nil, only user-owned attributes are
-considered. This is the default. If OWNER is :all , all attributes are
-considered. If OWNER is a string, this denotes a username and only
-attributes owned by OWNER are considered. Attribute names are returned
-as a list of strings. Attributes are returned as a list of strings."
- (database-attribute-type (database-identifier attribute)
- (database-identifier table)
- database
- :owner owner))
-
-(defun list-attribute-types (table &key (owner nil)
- (database *default-database*))
- "Returns type information for the attributes in TABLE from DATABASE
-which has a default value of *default-database*. If OWNER is nil, only
-user-owned attributes are considered. This is the default. If OWNER is
-:all, all attributes are considered. If OWNER is a string, this
-denotes a username and only attributes owned by OWNER are
-considered. Returns a list in which each element is a list (attribute
-datatype). Attribute is a string denoting the atribute name. Datatype
-is the vendor-specific type returned by ATTRIBUTE-TYPE."
- (mapcar #'(lambda (type)
- (list type (attribute-type type table :database database
- :owner owner)))
- (list-attributes table :database database :owner owner)))
-
-;(defun add-attribute (table attribute &key (database *default-database*))
-; (database-add-attribute table attribute database))
-
-;(defun rename-attribute (table oldatt newname
-; &key (database *default-database*))
-; (error "(rename-attribute ~a ~a ~a ~a) is not implemented"
-; table oldatt newname database))
-
-
-;; Sequences
-
-(defvar *sequence-schemas* (make-hash-table :test #'equal)
- "Hash of schema name to sequence lists.")
-
-(defun create-sequence (name &key (database *default-database*))
- "Create a sequence called NAME in DATABASE which defaults to
-*DEFAULT-DATABASE*."
- (let ((sequence-name (database-identifier name)))
- (database-create-sequence sequence-name database)
- (pushnew sequence-name (gethash *default-schema* *sequence-schemas*)
- :test #'equal))
- (values))
-
-(defun drop-sequence (name &key (if-does-not-exist :error)
- (database *default-database*))
- "Drops sequence NAME from DATABASE which defaults to
-*DEFAULT-DATABASE*. If the sequence does not exist and
-IF-DOES-NOT-EXIST is :ignore then DROP-SEQUENCE returns nil whereas an
-error is signalled if IF-DOES-NOT-EXIST is :error."
- (let ((sequence-name (database-identifier name)))
- (ecase if-does-not-exist
- (:ignore
- (unless (sequence-exists-p sequence-name :database database)
- (return-from drop-sequence)))
- (:error t))
- (database-drop-sequence sequence-name database))
- (values))
-
-(defun list-sequences (&key (owner nil) (database *default-database*))
- "List all sequences in DATABASE, which defaults to
-*default-database*. If OWNER is nil, only user-owned sequences are
-considered. This is the default. If OWNER is :all , all sequences are
-considered. If OWNER is a string, this denotes a username and only
-sequences owned by OWNER are considered. Sequence names are returned
-as a list of strings."
- (database-list-sequences database :owner owner))
-
-(defun sequence-exists-p (name &key (owner nil)
- (database *default-database*))
- "Test for existence of a sequence called NAME in DATABASE which
-defaults to *DEFAULT-DATABASE*."
- (when (member (database-identifier name)
- (list-sequences :owner owner :database database)
- :test #'string-equal)
- t))
-
-(defun sequence-next (name &key (database *default-database*))
- "Return the next value in the sequence NAME in DATABASE."
- (database-sequence-next (database-identifier name) database))
-
-(defun set-sequence-position (name position &key (database *default-database*))
- "Explicitly set the the position of the sequence NAME in DATABASE to
-POSITION."
- (database-set-sequence-position (database-identifier name) position database))
-
-(defun sequence-last (name &key (database *default-database*))
- "Return the last value of the sequence NAME in DATABASE."
- (database-sequence-last (database-identifier name) database))
\ No newline at end of file