r9113: intial changes for list-table-indexes
authorKevin M. Rosenberg <kevin@rosenberg.net>
Tue, 20 Apr 2004 20:35:38 +0000 (20:35 +0000)
committerKevin M. Rosenberg <kevin@rosenberg.net>
Tue, 20 Apr 2004 20:35:38 +0000 (20:35 +0000)
17 files changed:
ChangeLog
base/db-interface.lisp
base/package.lisp
db-aodbc/aodbc-sql.lisp
db-mysql/mysql-sql.lisp
db-odbc/odbc-api.lisp
db-odbc/odbc-constants.lisp
db-odbc/odbc-dbi.lisp
db-odbc/odbc-ff-interface.lisp
db-odbc/odbc-package.lisp
db-odbc/odbc-sql.lisp
db-postgresql-socket/postgresql-socket-sql.lisp
db-postgresql/postgresql-sql.lisp
db-sqlite/sqlite-sql.lisp
sql/package.lisp
sql/table.lisp
tests/test-fddl.lisp

index 5d0c375dfd5374db96f58cc7551c25b3d760d5c8..f3816fe19a8193b332d0312839654537094f2636 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 20 Apr 2004 Kevin Rosenberg (kevin@rosenberg.net)
-       * Version 2.8.3: Fix regression test
+       * Version 2.9.0: New API function: LIST-TABLE-INDEXES,
+       supported by all database backends (except AODBC since
+       AODBC doesn't support index querying)
+       * db-obdc/odbc-sql.lisp: Support DATABASE-LIST-INDEXES
+       * db-odbc/odbc-api.lisp: Add %TABLE-STATISTICS function
+       to support index queries
+       * db-aodbc/aodbc-sql.lisp: Filter driver manager
+       "information_schema" tables from LIST-TABLES
+       * tests/test-basic.lisp: Remove table after testing
 
 19 Apr 2004 Kevin Rosenberg (kevin@rosenberg.net)
        * Version 2.8.2: Build changes for FreeBSD [Slawek Zak]
index 7c75dd0fa95dd7305c64ef4daff373e4763dd73e..d257a5fa3342c70616f117e4eefb23dd33c16c98 100644 (file)
@@ -186,6 +186,9 @@ the given lisp type and parameters."))
 (defgeneric database-list-indexes (database &key owner)
   (:documentation "List all indexes in the DATABASE."))
 
+(defgeneric database-list-table-indexes (table database &key owner)
+  (:documentation "List all indexes for a table in the DATABASE."))
+
 (defgeneric database-list-attributes (table database &key owner)
   (:documentation "List all attributes in TABLE."))
 
index 03d15444f8aa0f26c7a2100e1c95e6e9da41d7f0..daea90f55082e0284549a37f7167d918eb0592c7 100644 (file)
@@ -58,6 +58,7 @@
      #:database-list-attributes
      #:database-list-sequences
      #:database-list-indexes
+     #:database-list-table-indexes
      #:database-list-views
      
      
index 09815914083be6190bab705eeb1ce986027ec372..bbe655e7c83988fbd58a63b2804ddcccbd11bb0a 100644 (file)
   #+aodbc-v2
   (multiple-value-bind (rows col-names)
       (dbi:list-all-database-tables :db (database-aodbc-conn database))
-    (let ((pos (position "TABLE_NAME" col-names :test #'string-equal)))
-      (when pos
-       (loop for row in rows
-           collect (nth pos row))))))
-
+    (declare (ignore col-names))
+      ;; TABLE_SCHEM is hard-coded in second column by ODBC Driver Manager
+      ;; TABLE_NAME in third column, TABLE_TYPE in fourth column
+      (loop for row in rows
+         when (and (not (string-equal "information_schema" (nth 1 row)))
+                   (string-equal "TABLE" (nth 3 row)))
+         collect (nth 2 row))))
 
 (defmethod database-list-attributes ((table string) (database aodbc-database)
                                      &key (owner nil))
index ea66f3de3605dd16de43020a31ee458f5fa74f7a..c047b97a95880e8d2054bc432aed080503bea847 100644 (file)
                                   &key (owner nil))
   (let ((result '()))
     (dolist (table (database-list-tables database :owner owner) result)
-      (mapc #'(lambda (index) (push (nth 2 index) result))
-            (database-query 
-             (format nil "SHOW INDEX FROM ~A" (string-upcase table))
-             database nil)))))
+      (setq result
+       (append (database-list-table-indexes table database :owner owner)
+               result)))))
+
+(defmethod database-list-table-indexes (table (database mysql-database)
+                                       &key (owner nil))
+  (declare (ignore owner))
+  (do ((results nil)
+       (rows (database-query 
+             (format nil "SHOW INDEX FROM ~A" (string-upcase table))
+             database nil)
+            (cdr rows)))
+      ((null rows) (nreverse results))
+    (let ((col (nth 2 (car rows))))
+      (unless (find col results :test #'string-equal)
+       (push col results)))))
   
 (defmethod database-list-attributes ((table string) (database mysql-database)
                                      &key (owner nil))
index 0f27b5767ee3087ec26101ac78bc34e84fdf823c..5ff3d09cb7f4750e32cedaf68fb1e45fb083eae9 100644 (file)
@@ -940,6 +940,20 @@ as possible second argument) to the desired representation of date/time/timestam
   (with-error-handling (:hstmt hstmt)
     (SQLTables hstmt +null-ptr+ 0 +null-ptr+ 0 +null-ptr+ 0 +null-ptr+ 0)))
 
+(defun %table-statistics (table hstmt &key unique (ensure t))
+  (with-cstrings ((table-cs table))
+    (with-error-handling (:hstmt hstmt)
+      (print hstmt)
+      (print table-cs)
+      (print (uffi:convert-from-cstring table-cs))
+      (SQLStatistics 
+       hstmt
+       +null-ptr+ 0
+       +null-ptr+ 0
+       table-cs (length table) ;;$SQL_NTS
+       (if unique $SQL_INDEX_UNIQUE $SQL_INDEX_ALL)
+       (if ensure $SQL_ENSURE $SQL_QUICK)))))
+
 (defun %list-data-sources (henv)
   (let ((dsn (allocate-foreign-string (1+ $SQL_MAX_DSN_LENGTH)))
        (desc (allocate-foreign-string 256))
index 2f52d9de93198f97edc303545209ccfda005087c..cddb4be8288d1377c6a8d375440cabb108185ca5 100644 (file)
 (defconstant $SQL_FETCH_RELATIVE 6)
 (defconstant $SQL_FETCH_BOOKMARK 8)
 
-;;; ODBC v3 constants
+;;; ODBC v3 constants, added by KMR
 
 (defconstant $SQL_ATTR_ODBC_VERSION 200)
 (defconstant $SQL_OV_ODBC2 2)
 (defconstant $SQL_OV_ODBC3 3)
+(defconstant $SQL_INDEX_UNIQUE 0)
+(defconstant $SQL_INDEX_ALL 1)
+(defconstant $SQL_QUICK 0)
+(defconstant $SQL_ENSURE 1)
+
 
index ff96c213b3ba113e635b4befd9bef4099a7037ca..f9e8493f55a7a78ad762344aa543628b93ac220a 100644 (file)
@@ -33,6 +33,7 @@
    #:list-all-data-sources
    #:list-all-database-tables
    #:list-all-table-columns
+   #:list-table-indexes
    #:loop-over-results
    #:prepare-sql
    #:rr-sql
@@ -180,6 +181,20 @@ the query against." ))
             (coerce (column-names query) 'list))))
       (db-close-query query))))
 
+(defun list-table-indexes (table &key db unique hstmt)
+  (declare (ignore hstmt))
+  (let ((query (get-free-query db)))
+    (unwind-protect
+       (progn
+         (with-slots (hstmt) query
+           (unless hstmt (setf hstmt (%new-statement-handle (hdbc db))))
+           (%table-statistics table hstmt :unique unique)
+           (%initialize-query query nil nil)
+           (values
+            (db-fetch-query-results query)
+            (coerce (column-names query) 'list))))
+      (db-close-query query))))
+
 (defun list-all-table-columns (table &key db hstmt)
   (declare (ignore hstmt))
   (db-describe-columns db "" "" table ""))
index fa46667b550bfde891ae1c862803471a6a067c26..4f80888769f96fdde11ec7bf164494ccd1c10124 100644 (file)
   :returning :short)
 
 
+(def-function "SQLStatistics"
+    ((hstmt :pointer-void)
+     (catalog-name :pointer-void)
+     (catalog-name-length :short)
+     (schema-name :pointer-void)
+     (schema-name-length :short)
+     (table-name :cstring)
+     (table-name-length :short)
+     (unique :short)
+     (reserved :short))
+  :returning :short)
+
+
index 3c22c8ba05ff894794d4b1938c3d31ecae8af940..c34147b406d3eecc700fa74c2cec73666942507d 100644 (file)
@@ -63,6 +63,7 @@
      #:result-rows-count
      #:sql-to-c-type
      #:%list-tables
+     #:%table-statistics
      #:%list-data-sources
      )
   (:documentation "This is the low-level interface ODBC."))
index 3bc5368594f287d733391ffd734c9a833b850b6c..2fad84805755c8c3b029d553b899f8d677812978 100644 (file)
                                 &key (owner nil))
   (declare (ignore owner))
     (multiple-value-bind (rows col-names)
-      (odbc-dbi:list-all-database-tables :db (database-odbc-conn database))
-    (let ((pos (position "TABLE_NAME" col-names :test #'string-equal)))
-      (when pos
-       (loop for row in rows
-           collect (nth pos row))))))
+       (odbc-dbi:list-all-database-tables :db (database-odbc-conn database))
+      (declare (ignore col-names))
+      ;; TABLE_SCHEM is hard-coded in second column by ODBC Driver Manager
+      ;; TABLE_NAME in third column, TABLE_TYPE in fourth column
+      (loop for row in rows
+         when (and (not (string-equal "information_schema" (nth 1 row)))
+                   (string-equal "TABLE" (nth 3 row)))
+         collect (nth 2 row))))
 
 (defmethod database-list-attributes ((table string) (database odbc-database)
                                      &key (owner nil))
   (declare (ignore connection-spec))
   (odbc-dbi:list-all-data-sources))
 
-#+ignore                      
+(defmethod database-list-indexes ((database odbc-database)
+                                  &key (owner nil))
+  (let ((result '()))
+    (dolist (table (database-list-tables database :owner owner) result)
+      (append (database-list-table-indexes table database :owner owner)
+             result))))
+
+(defmethod database-list-table-indexes (table (database odbc-database)
+                                       &key (owner nil))
+  (multiple-value-bind (rows col-names)
+      (odbc-dbi:list-table-indexes table :db (database-odbc-conn database))
+    (declare (ignore col-names))
+    ;; INDEX_NAME is hard-coded in sixth position by ODBC driver
+    (loop for row in rows collect (nth 5 row))))
+
+#+ignore
 (when (clsql-base-sys:database-type-library-loaded :odbc)
   (clsql-base-sys:initialize-database-type :database-type :odbc))
index 50c6899afc0f8c6a4a36fefc0597fdb911967663..6307f05f662c7275ad363d5f170d8cea09b35edb 100644 (file)
@@ -314,22 +314,27 @@ doesn't depend on UFFI."
 
 ;;; Object listing
 
+(defun owner-clause (owner)
+  (cond 
+   ((stringp owner)
+    (format
+     nil
+     " AND (relowner=(SELECT usesysid FROM pg_user WHERE (usename='~A')))" 
+     owner))
+   ((null owner)
+    (format nil " AND (NOT (relowner=1))"))
+   (t "")))
+
 (defmethod database-list-objects-of-type ((database postgresql-socket-database)
                                           type owner)
-  (let ((owner-clause
-         (cond ((stringp owner)
-                (format nil " AND (relowner=(SELECT usesysid FROM pg_user WHERE (usename='~A')))" owner))
-               ((null owner)
-                (format nil " AND (NOT (relowner=1))"))
-               (t ""))))
-    (mapcar #'car
-            (database-query
-             (format nil
-                     "SELECT relname FROM pg_class WHERE (relkind = '~A')~A"
-                     type
-                     owner-clause)
-             database nil))))
-    
+  (mapcar #'car
+         (database-query
+          (format nil
+                  "SELECT relname FROM pg_class WHERE (relkind = '~A')~A"
+                  type
+                  (owner-clause owner))
+          database nil)))
+
 (defmethod database-list-tables ((database postgresql-socket-database)
                                  &key (owner nil))
   (database-list-objects-of-type database "r" owner))
@@ -341,7 +346,28 @@ doesn't depend on UFFI."
 (defmethod database-list-indexes ((database postgresql-socket-database)
                                   &key (owner nil))
   (database-list-objects-of-type database "i" owner))
-  
+
+(defmethod database-list-table-indexes (table
+                                       (database postgresql-socket-database)
+                                       &key (owner nil))
+  (let ((indexrelids
+        (database-query
+         (format 
+          nil
+          "select indexrelid from pg_index where indrelid=(select relfilenode from pg_class where relname='~A'~A)"
+          (string-downcase table)
+          (owner-clause owner))
+         database :auto))
+       (result nil))
+    (dolist (indexrelid indexrelids (nreverse result))
+      (push 
+       (caar (database-query
+             (format nil "select relname from pg_class where relfilenode='~A'"
+                     (car indexrelid))
+             database
+             nil))
+       result))))
+
 (defmethod database-list-attributes ((table string)
                                     (database postgresql-socket-database)
                                      &key (owner nil))
index b4eaa821662e0304b0b02c52542b4ef488b7b723..3d1eca3a2b614c4ec2a4d4576d22b72536514899 100644 (file)
 
 ;;; Object listing
 
+(defun owner-clause (owner)
+  (cond 
+   ((stringp owner)
+    (format
+     nil
+     " AND (relowner=(SELECT usesysid FROM pg_user WHERE (usename='~A')))" 
+     owner))
+   ((null owner)
+    (format nil " AND (NOT (relowner=1))"))
+   (t "")))
+
 (defmethod database-list-objects-of-type ((database postgresql-database)
                                           type owner)
-  (let ((owner-clause
-         (cond ((stringp owner)
-                (format nil " AND (relowner=(SELECT usesysid FROM pg_user WHERE (usename='~A')))" owner))
-               ((null owner)
-                (format nil " AND (NOT (relowner=1))"))
-               (t ""))))
-    (mapcar #'car
-            (database-query
-             (format nil
-                     "SELECT relname FROM pg_class WHERE (relkind = '~A')~A"
-                     type
-                     owner-clause)
-             database nil))))
-    
+  (mapcar #'car
+         (database-query
+          (format nil
+                  "SELECT relname FROM pg_class WHERE (relkind = '~A')~A"
+                  type
+                  (owner-clause owner))
+          database nil)))
+
 (defmethod database-list-tables ((database postgresql-database)
                                  &key (owner nil))
   (database-list-objects-of-type database "r" owner))
 (defmethod database-list-indexes ((database postgresql-database)
                                   &key (owner nil))
   (database-list-objects-of-type database "i" owner))
-  
+
+
+(defmethod database-list-table-indexes (table (database postgresql-database)
+                                       &key (owner nil))
+  (let ((indexrelids
+        (database-query
+         (format 
+          nil
+          "select indexrelid from pg_index where indrelid=(select relfilenode from pg_class where relname='~A'~A)"
+          (string-downcase table)
+          (owner-clause owner))
+         database :auto))
+       (result nil))
+    (dolist (indexrelid indexrelids (nreverse result))
+      (push 
+       (caar (database-query
+             (format nil "select relname from pg_class where relfilenode='~A'"
+                     (car indexrelid))
+             database
+             nil))
+       result))))
+
 (defmethod database-list-attributes ((table string)
                                     (database postgresql-database)
                                      &key (owner nil))
           (progn
             (setf (slot-value database 'clsql-base-sys::state) :open)
             (mapcar #'car (database-query "select datname from pg_database" 
-                                          database :auto)))
+                                          database nil)))
        (progn
          (database-disconnect database)
          (setf (slot-value database 'clsql-base-sys::state) :closed))))))
index 1ea5599a468753eb2acaf689c420ab96d31f6785..5a902c97b024c0bb52eba959890a8076548701b2 100644 (file)
                  "SELECT name FROM sqlite_master WHERE type='index' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='index' ORDER BY name"
                  database nil)))
 
+(defmethod database-list-table-indexes (table (database sqlite-database)
+                                       &key (owner nil))
+  (declare (ignore owner))
+  (mapcar #'car 
+         (database-query
+          (format
+           nil
+           "SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='~A' UNION ALL SELECT name FROM sqlite_temp_master WHERE type='index' AND tbl_name='~A' ORDER BY name"
+           table table)
+          database nil)))
+
 (declaim (inline sqlite-table-info))
 (defun sqlite-table-info (table database)
   (database-query (format nil "PRAGMA table_info('~A')" table)
index 63847d33033eb5414acc3e07dc88aaa1654ba769..302bc428c195fa690397d8d318d8ea1b8df87301 100644 (file)
        #:database-list-tables
        #:database-list-views
        #:database-list-indexes
+       #:database-list-table-indexes
        #:database-list-sequences
        #:database-list-attributes
        #:database-attribute-type
     #:database-list-views
     #:database-view-exists-p
     #:database-list-indexes
+    #:database-list-table-indexes
     #:database-index-exists-p
     #:database-list-sequences
     #:database-sequence-exists-p
        #:list-views                        ; table      xx
        #:view-exists-p                     ; table      xx
        #:list-indexes                      ; table      xx
+       #:list-table-indexes                ; table      xx
        #:index-exists-p                    ; table      xx
        #:create-sequence                   ; table      xx
        #:drop-sequence                     ; table      xx
index 4fb91685eff5e690b08ca954e33d3bab4c9d4f7b..52b5c1a155143c3b96a51228b9bfe4d504f2bc31 100644 (file)
@@ -185,6 +185,15 @@ specification of a table to drop the index from."
 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 list-table-indexes (table &key (owner nil)
+                                     (database *default-database*))
+  "List all indexes in DATABASE for a TABLE, 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-table-indexes (database-identifier table)
+                              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
index bafa1c55c79150e17a3214185c7363c4da4c3c9c..e231592c82610d5368c3607984c217b875415de5 100644 (file)
@@ -21,7 +21,7 @@
 
 (setq *rt-fddl*
       '(
-      
+       
 ;; list current tables 
 (deftest :fddl/table/1
     (apply #'values 
            (values
             (clsql:index-exists-p [bar] :owner *test-database-user*)
             (progn
-              (case *test-database-type*
-                (:mysql 
-                 (clsql:drop-index [bar] :on [employee]
-                                  :if-does-not-exist :ignore))
-                (t 
-                 (clsql:drop-index [bar]:if-does-not-exist :ignore)))
+             (clsql:drop-index [bar] :on [employee]
+                               :if-does-not-exist :ignore)
               (clsql:index-exists-p [bar] :owner *test-database-user*))))
   t nil)
 
       (dolist (name names)
         (clsql:create-index name :on [employee] :attributes '([emplid]))
         (push (clsql:index-exists-p name :owner *test-database-user*) result)
-        (case *test-database-type*
-          (:mysql 
-           (clsql:drop-index name :on [employee] :if-does-not-exist :ignore))
-          (t (clsql:drop-index name :if-does-not-exist :ignore))))
+       (clsql:drop-index name :on [employee] :if-does-not-exist :ignore))
       (apply #'values result))
   t t t)
 
+;; test list-table-indexes
+(deftest :fddl/index/3
+    (progn
+      (clsql:create-index [bar] :on [employee] :attributes
+                         '([last-name]) :unique nil)
+      (clsql:create-index [foo] :on [employee] :attributes
+                         '([first-name]) :unique nil)
+      (values
+       
+       (sort 
+       (mapcar 
+        #'string-downcase
+        (clsql:list-table-indexes [employee] :owner *test-database-user*))
+           #'string-lessp)
+       (sort (clsql:list-table-indexes [company] :owner *test-database-user*)
+            #'string-lessp)
+       (progn
+        (clsql:drop-index [bar] :on [employee])
+        (clsql:drop-index [foo] :on [employee])
+        t)))
+
+  ("bar" "foo") nil t)
+
 ;; create an sequence, test for existence, drop it and test again 
 (deftest :fddl/sequence/1
     (progn (clsql:create-sequence [foo])