r9199: fold clsql-base and clsql-base-sys into clsql-base
[clsql.git] / db-postgresql / postgresql-sql.lisp
index b4eaa821662e0304b0b02c52542b4ef488b7b723..a55683144423b38bcffe04c6b5d0260d2467e35a 100644 (file)
@@ -16,7 +16,7 @@
 (in-package #:cl-user)
 
 (defpackage #:clsql-postgresql
-    (:use #:common-lisp #:clsql-base-sys #:postgresql #:clsql-uffi)
+    (:use #:common-lisp #:clsql-base #:postgresql #:clsql-uffi)
     (:export #:postgresql-database)
     (:documentation "This is the CLSQL interface to PostgreSQL."))
 
   (setf (database-conn-ptr database) nil)
   t)
 
-(defmethod database-query (query-expression (database postgresql-database) result-types)
+(defmethod database-query (query-expression (database postgresql-database) result-types field-names)
   (let ((conn-ptr (database-conn-ptr database)))
     (declare (type pgsql-conn-def conn-ptr))
     (uffi:with-cstring (query-native query-expression)
                 (setq result-types
                   (canonicalize-types result-types num-fields
                                             result))
-                (loop for tuple-index from 0 below (PQntuples result)
-                      collect
-                      (loop for i from 0 below num-fields
-                            collect
-                            (if (zerop (PQgetisnull result tuple-index i))
-                                (convert-raw-field
-                                 (PQgetvalue result tuple-index i)
-                                 result-types i)
-                                nil)))))
+                 (values
+                  (loop for tuple-index from 0 below (PQntuples result)
+                        collect
+                        (loop for i from 0 below num-fields
+                              collect
+                              (if (zerop (PQgetisnull result tuple-index i))
+                                  (convert-raw-field
+                                   (PQgetvalue result tuple-index i)
+                                   result-types i)
+                                nil)))
+                  (when field-names
+                    (result-field-names num-fields result)))))
               (t
                (error 'clsql-sql-error
                       :database database
                               (PQresultErrorMessage result)))))
           (PQclear result))))))
 
+(defun result-field-names (num-fields result)
+  "Return list of result field names."
+  (let ((names '()))
+    (dotimes (i num-fields (nreverse names))
+      (declare (fixnum i))
+      (push (uffi:convert-from-cstring (PQfname result i)) names))))
+
 (defmethod database-execute-command (sql-expression
                                      (database postgresql-database))
   (let ((conn-ptr (database-conn-ptr database)))
 
 ;;; Object listing
 
-(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))))
-    
+(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 "")))
+
+(defun database-list-objects-of-type (database type owner)
+  (mapcar #'car
+         (database-query
+          (format nil
+                  "SELECT relname FROM pg_class WHERE (relkind = '~A')~A"
+                  type
+                  (owner-clause owner))
+          database nil 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 nil))
+       (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 nil))
+       result))))
+
 (defmethod database-list-attributes ((table string)
                                     (database postgresql-database)
                                      &key (owner nil))
                   (format nil "SELECT attname FROM pg_class,pg_attribute WHERE pg_class.oid=attrelid AND relname='~A'~A"
                            (string-downcase table)
                            owner-clause)
-                   database nil))))
+                   database nil nil))))
     (if result
        (reverse
          (remove-if #'(lambda (it) (member it '("cmin"
 (defmethod database-attribute-type (attribute (table string)
                                    (database postgresql-database)
                                     &key (owner nil))
-  (let* ((owner-clause
-          (cond ((stringp owner)
-                 (format nil " AND (relowner=(SELECT usesysid FROM pg_user WHERE usename='~A'))" owner))
-                ((null owner) " AND (not (relowner=1))")
-                (t "")))
-         (result
-         (mapcar #'car
-                 (database-query
-                  (format nil "SELECT pg_type.typname FROM pg_type,pg_class,pg_attribute WHERE pg_class.oid=pg_attribute.attrelid AND pg_class.relname='~A' AND pg_attribute.attname='~A' AND pg_attribute.atttypid=pg_type.oid~A"
+  (let ((row (car (database-query
+                  (format nil "SELECT pg_type.typname,pg_attribute.attlen,pg_attribute.atttypmod,pg_attribute.attnotnull FROM pg_type,pg_class,pg_attribute WHERE pg_class.oid=pg_attribute.attrelid AND pg_class.relname='~A' AND pg_attribute.attname='~A' AND pg_attribute.atttypid=pg_type.oid~A"
                           (string-downcase table)
-                           (string-downcase attribute)
-                           owner-clause)
-                  database nil))))
-    (when result
-      (intern (string-upcase (car result)) :keyword))))
+                          (string-downcase attribute)
+                          (owner-clause owner))
+                  database nil nil))))
+    (when row
+      (values
+       (ensure-keyword (first row))
+       (if (string= "-1" (second row))
+          (- (parse-integer (third row) :junk-allowed t) 4)
+        (parse-integer (second row)))
+       nil
+       (if (string-equal "f" (fourth row))
+          1
+        0)))))
 
 (defmethod database-create-sequence (sequence-name
                                     (database postgresql-database))
     (caar
      (database-query
       (format nil "SELECT SETVAL ('~A', ~A)" name position)
-      database nil)))))
+      database nil nil)))))
 
 (defmethod database-sequence-next (sequence-name 
                                   (database postgresql-database))
     (caar
      (database-query
       (concatenate 'string "SELECT NEXTVAL ('" (sql-escape sequence-name) "')")
-      database nil)))))
+      database nil nil)))))
 
 (defmethod database-sequence-last (sequence-name (database postgresql-database))
   (values
     (caar
      (database-query
       (concatenate 'string "SELECT LAST_VALUE ('" sequence-name "')")
-      database nil)))))
+      database nil nil)))))
   
 (defmethod database-create (connection-spec (type (eql :postgresql)))
   (destructuring-bind (host name user password) connection-spec
     (declare (ignore user password))
     (multiple-value-bind (output status)
-       (clsql-base-sys:command-output "createdb -h~A ~A"
+       (clsql-base:command-output "createdb -h~A ~A"
                                       (if host host "localhost")
                                       name)
       (if (or (not (zerop status))
   (destructuring-bind (host name user password) connection-spec
     (declare (ignore user password))
     (multiple-value-bind (output status)
-       (clsql-base-sys:command-output "dropdb -h~A ~A"
+       (clsql-base:command-output "dropdb -h~A ~A"
                                       (if host host "localhost")
                                       name)
       (if (or (not (zerop status))
                                      type)))
       (unwind-protect
           (progn
-            (setf (slot-value database 'clsql-base-sys::state) :open)
+            (setf (slot-value database 'clsql-base::state) :open)
             (mapcar #'car (database-query "select datname from pg_database" 
-                                          database :auto)))
+                                          database nil nil)))
        (progn
          (database-disconnect database)
-         (setf (slot-value database 'clsql-base-sys::state) :closed))))))
+         (setf (slot-value database 'clsql-base::state) :closed))))))
 
 (defmethod database-describe-table ((database postgresql-database) table)
   (database-query 
                                    and a.attrelid = c.oid
                                    and a.atttypid = t.oid"
            (sql-escape (string-downcase table)))
-   database :auto))
+   database :auto nil))
 
 (defun %pg-database-connection (connection-spec)
   (check-connection-spec connection-spec :postgresql
         connection-spec
       (coerce-string db)
       (coerce-string user)
-      (let ((connection (pqsetdblogin host port options tty db user password)))
+      (let ((connection (PQsetdbLogin host port options tty db user password)))
         (declare (type postgresql::pgsql-conn-ptr connection))
-        (unless (eq (pqstatus connection) :connection-ok)
+        (unless (eq (PQstatus connection) :connection-ok)
           ;; Connect failed
           (error 'clsql-connect-error
                  :database-type :postgresql
                  :connection-spec connection-spec
-                 :errno (pqstatus connection)
-                 :error (pqerrormessage connection)))
+                 :errno (PQstatus connection)
+                 :error (PQerrorMessage connection)))
         connection))))
 
 (defmethod database-reconnect ((database postgresql-database))
        (setf conn-ptr (%pg-database-connection connection-spec))
        database))))
 
-(when (clsql-base-sys:database-type-library-loaded :postgresql)
-  (clsql-base-sys:initialize-database-type :database-type :postgresql))
+;;; Database capabilities
+
+(defmethod db-type-has-fancy-math? ((db-type (eql :postgresql)))
+  t)
+
+(defmethod db-type-default-case ((db-type (eql :postgresql)))
+  :lower)
+
+(when (clsql-base:database-type-library-loaded :postgresql)
+  (clsql-base:initialize-database-type :database-type :postgresql))