r1683: *** empty log message ***
[clsql.git] / interfaces / postgresql-socket / postgresql-socket-api.cl
index c97eda18be744cf8a4e8a5455f120bdf83bd5ed8..0ee7d8a8fe0ee2911714260dee5288631cd67b4c 100644 (file)
@@ -9,7 +9,7 @@
 ;;;;                
 ;;;; Date Started:  Feb 2002
 ;;;;
-;;;; $Id: postgresql-socket-api.cl,v 1.1 2002/03/23 17:10:48 kevin Exp $
+;;;; $Id: postgresql-socket-api.cl,v 1.12 2002/03/27 12:09:39 kevin Exp $
 ;;;;
 ;;;; This file, part of CLSQL, is Copyright (c) 2002 by Kevin M. Rosenberg
 ;;;; and Copyright (c) 1999-2001 by Pierre R. Mai
 ;;;;  - Changed CMUCL FFI to UFFI
 ;;;;  - Added necessary (force-output) for socket streams on 
 ;;;;     Allegro and Lispworks
+;;;;  - Added initialization variable
+;;;;  - Added field type processing
 
 (declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))
 (in-package :postgresql-socket)
 
+(uffi:def-enum pgsql-ftype
+    ((:bytea 17)
+     (:int2 21)
+     (:int4 23)
+     (:int8 20)
+     (:float4 700)
+     (:float8 701)))
+
+(defmethod database-type-library-loaded ((database-type
+                                         (eql :postgresql-socket)))
+  "T if foreign library was able to be loaded successfully. Always true for
+socket interface"
+  t)
+                                     
 
 ;;; Message I/O stuff
 
 
 (defun read-socket-sequence (string stream)
 "KMR -- Added to support reading from binary stream into a string"
-  (declare (optimize (speed 3) (safety 0)))
+  (declare (optimize (speed 3) (safety 0))
+          (string string))
   (dotimes (i (length string))
     (declare (fixnum i))
     (setf (char string i) (code-char (read-byte stream))))
@@ -547,7 +565,124 @@ connection, if it is still open."
            do (setf (aref result index) (ldb (byte 1 weight) byte))))
     result))
 
-(defun read-cursor-row (cursor)
+
+(defun read-field (socket type)
+  (let ((length (- (read-socket-value 'int32 socket) 4)))
+    (case type
+      ((:int32 :int64)
+       (read-integer-from-socket socket length))
+      (:double
+       (read-double-from-socket socket length))
+      (t
+       (let ((result (make-string length)))
+        (read-socket-sequence result socket)
+        result)))))
+
+(uffi:def-constant +char-code-zero+ (char-code #\0))
+(uffi:def-constant +char-code-minus+ (char-code #\-))
+(uffi:def-constant +char-code-plus+ (char-code #\+))
+(uffi:def-constant +char-code-period+ (char-code #\.))
+(uffi:def-constant +char-code-lower-e+ (char-code #\e))
+(uffi:def-constant +char-code-upper-e+ (char-code #\E))
+
+(defun read-integer-from-socket (socket length)
+  (declare (fixnum length))
+  (if (zerop length)
+      nil
+    (let ((val 0)
+         (first-char (read-byte socket))
+         (minusp nil))
+      (declare (fixnum first-char))
+      (decf length) ;; read first char
+      (cond
+       ((= first-char +char-code-minus+)
+       (setq minusp t))
+       ((= first-char +char-code-plus+)
+       )               ;; nothing to do
+       (t
+       (setq val (- first-char +char-code-zero+))))
+      
+      (dotimes (i length)
+       (declare (fixnum i))
+       (setq val (+
+                  (* 10 val)
+                  (- (read-byte socket) +char-code-zero+))))
+      (if minusp
+         (- val)
+       val))))
+
+(defmacro ascii-digit (int)
+  (let ((offset (gensym)))
+    `(let ((,offset (- ,int +char-code-zero+)))
+      (declare (fixnum ,int ,offset))
+      (if (and (>= ,offset 0)
+              (< ,offset 10))
+         ,offset
+         nil))))
+      
+(defun read-double-from-socket (socket length)
+  (declare (fixnum length))
+  (let ((before-decimal 0)
+       (after-decimal 0)
+       (decimal-count 0)
+       (exponent 0)
+       (decimalp nil)
+       (minusp nil)
+       (result nil)
+       (char (read-byte socket)))
+    (declare (fixnum char exponent decimal-count))
+    (decf length) ;; already read first character
+    (cond
+      ((= char +char-code-minus+)
+       (setq minusp t))
+      ((= char +char-code-plus+)
+       )
+      ((= char +char-code-period+)
+       (setq decimalp t))
+      (t
+       (setq before-decimal (ascii-digit char))
+       (unless before-decimal
+        (error "Unexpected value"))))
+    
+    (block loop
+      (dotimes (i length)
+       (setq char (read-byte socket))
+       ;;      (format t "~&len:~D, i:~D, char:~D, minusp:~A, decimalp:~A" length i char minusp decimalp)
+       (let ((weight (ascii-digit char)))
+         (cond 
+          ((and weight (not decimalp)) ;; before decimal point
+           (setq before-decimal (+ weight (* 10 before-decimal))))
+          ((and weight decimalp) ;; after decimal point
+           (setq after-decimal (+ weight (* 10 after-decimal)))
+           (incf decimal-count))
+          ((and (= char +char-code-period+))
+           (setq decimalp t))
+          ((or (= char +char-code-lower-e+)          ;; E is for exponent
+               (= char +char-code-upper-e+))
+           (setq exponent (read-integer-from-socket socket (- length i 1)))
+           (setq exponent (or exponent 0))
+           (return-from loop))
+         (t 
+          (break "Unexpected value"))
+         )
+       )))
+    (setq result (* (+ (coerce before-decimal 'double-float)
+                      (* after-decimal 
+                         (expt 10 (- decimal-count))))
+                   (expt 10 exponent)))
+    (if minusp
+       (- result)
+       result)))
+       
+      
+#+ignore
+(defun read-double-from-socket (socket length)
+  (let ((result (make-string length)))
+    (read-socket-sequence result socket)
+    (let ((*read-default-float-format* 'double-float))
+      (read-from-string result))))
+
+(defun read-cursor-row (cursor types)
   (let* ((connection (postgresql-cursor-connection cursor))
         (socket (postgresql-connection-socket connection))
         (fields (postgresql-cursor-fields cursor)))
@@ -561,15 +696,13 @@ connection, if it is still open."
                     with null-vector = (read-null-bit-vector socket count)
                     repeat count
                     for null-bit across null-vector
+                    for i from 0
                     for null-p = (zerop null-bit)
                     if null-p
                     collect nil
                     else
                     collect
-                    (let* ((length (read-socket-value 'int32 socket))
-                           (result (make-string (- length 4))))
-                      (read-socket-sequence result socket)
-                      result))))
+                    (read-field socket (nth i types)))))
            (#.+binary-row-message+
             (error "NYI"))
            (#.+completed-response-message+
@@ -593,7 +726,14 @@ connection, if it is still open."
             (error 'postgresql-fatal-error :connection connection
                    :message "Received garbled message from backend")))))))
 
-(defun copy-cursor-row (cursor sequence)
+(defun map-into-indexed (result-seq func seq)
+  (dotimes (i (length seq))
+    (declare (fixnum i))
+    (setf (elt result-seq i)
+         (funcall func (elt seq i) i)))
+  result-seq)
+
+(defun copy-cursor-row (cursor sequence types)
   (let* ((connection (postgresql-cursor-connection cursor))
         (socket (postgresql-connection-socket connection))
         (fields (postgresql-cursor-fields cursor)))
@@ -603,15 +743,21 @@ connection, if it is still open."
          (case code
            (#.+ascii-row-message+
             (return
-              (map-into
+              #+ignore
+              (let* ((count (length sequence))
+                     (null-vector (read-null-bit-vector socket count)))
+                (dotimes (i count)
+                  (declare (fixnum i))
+                  (if (zerop (elt null-vector i))
+                      (setf (elt sequence i) nil)
+                      (let ((value (read-field socket (nth i types))))
+                        (setf (elt sequence i) value)))))
+              (map-into-indexed
                sequence
-               #'(lambda (null-bit)
+               #'(lambda (null-bit i)
                    (if (zerop null-bit)
                        nil
-                       (let* ((length (read-socket-value 'int32 socket))
-                              (result (make-string (- length 4))))
-                         (read-socket-sequence result socket)
-                         result)))
+                       (read-field socket (nth i types))))
                (read-null-bit-vector socket (length sequence)))))
            (#.+binary-row-message+
             (error "NYI"))
@@ -674,12 +820,12 @@ connection, if it is still open."
             (error 'postgresql-fatal-error :connection connection
                    :message "Received garbled message from backend")))))))
 
-(defun run-query (connection query)
+(defun run-query (connection query &optional (types nil))
   (start-query-execution connection query)
   (multiple-value-bind (status cursor)
       (wait-for-query-results connection)
     (assert (eq status :cursor))
-    (loop for row = (read-cursor-row cursor)
+    (loop for row = (read-cursor-row cursor types)
          while row
          collect row
          finally