X-Git-Url: http://git.kpe.io/?a=blobdiff_plain;f=interfaces%2Fpostgresql-socket%2Fpostgresql-socket-api.cl;h=e30584eac4d5ca08489444cf5b2010c44780d162;hb=42a951e9f7152e7c145958f4dfed41d4e865c9fd;hp=c97eda18be744cf8a4e8a5455f120bdf83bd5ed8;hpb=b06cb6d32e2a334f7dc72e8fb583a5b9609136b7;p=clsql.git diff --git a/interfaces/postgresql-socket/postgresql-socket-api.cl b/interfaces/postgresql-socket/postgresql-socket-api.cl index c97eda1..e30584e 100644 --- a/interfaces/postgresql-socket/postgresql-socket-api.cl +++ b/interfaces/postgresql-socket/postgresql-socket-api.cl @@ -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.14 2002/04/07 15:10:01 kevin Exp $ ;;;; ;;;; This file, part of CLSQL, is Copyright (c) 2002 by Kevin M. Rosenberg ;;;; and Copyright (c) 1999-2001 by Pierre R. Mai @@ -25,10 +25,27 @@ ;;;; - 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 @@ -192,7 +209,8 @@ (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)))) @@ -201,24 +219,24 @@ ;;; Support for encrypted password transmission -(defconstant +crypt-library+ "/usr/lib/libcrypt.so" - "Name of the shared library to load in order to access the crypt -function named by `*crypt-function-name*'.") - (defvar *crypt-library-loaded* nil) (defun crypt-password (password salt) "Encrypt a password for transmission to a PostgreSQL server." (unless *crypt-library-loaded* - (uffi:load-foreign-library +crypt-library+ :supporting-libaries '("c")) - (eval (uffi:def-function "crypt" - ((key :cstring) - (salt :cstring)) + (uffi:load-foreign-library + (uffi:find-foreign-library "libcrypt" + '("/usr/lib/" "/usr/local/lib/" "/lib/")) + :supporting-libaries '("c")) + (eval '(uffi:def-function "crypt" + ((key :cstring) + (salt :cstring)) :returning :cstring)) (setq *crypt-library-loaded* t)) (uffi:with-cstring (password-cstring password) (uffi:with-cstring (salt-cstring salt) - (uffi:convert-from-cstring (crypt password-cstring salt-cstring))))) + (uffi:convert-from-cstring + (funcall (fdefinition 'crypt) password-cstring salt-cstring))))) ;;; Condition hierarchy (define-condition postgresql-condition (condition) @@ -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