commit f43845d322f289deb19749161ed01ba2dba70f2b
parent 94bd5f470d032edc611f095d11618732d5f6bfe1
Author: Tomas Hlavaty <tom@logand.com>
Date:   Mon, 23 Sep 2013 00:04:54 +0200
depend on cl-rw, pg improvements e.g. :null handling protocol
Diffstat:
4 files changed, 56 insertions(+), 193 deletions(-)
diff --git a/Makefile b/Makefile
@@ -10,12 +10,12 @@ dbquery-pg: dbquery-pg.c common.c
 	$(CC) $(CFLAGS) -o $@ $< -lpq
 	strip $@
 
-dbquery-mysql: dbquery-mysql.c common.o
-	$(CC) $(CFLAGS) -o $@ $< common.o `mysql_config --cflags --libs`
+dbquery-mysql: dbquery-mysql.c common.c
+	$(CC) $(CFLAGS) -o $@ $< `mysql_config --cflags --libs`
 	strip $@
 
-dbquery-sqlite: dbquery-sqlite.c common.o
-	$(CC) $(CFLAGS) -o $@ $< common.o -lsqlite3
+dbquery-sqlite: dbquery-sqlite.c common.c
+	$(CC) $(CFLAGS) -o $@ $< -lsqlite3
 	strip $@
 
 clean:
diff --git a/common.c b/common.c
@@ -54,7 +54,7 @@ static void pqstr(char *x) {
   putchar('"');
   putchar('\n');
 }
-static void pnil() {pqstr("NIL");}
+static void pnil() {putchar('?'); putchar('\n');}
 
 static int rnum(void) { // TODO limit interval
   int z = 0, y = 0;
@@ -73,7 +73,12 @@ static char *buf;
 
 static char *rstr(void) {
   char *z = buf;
-  if('"' != getchar()) die("expected string");
+  char c = getchar();
+  if('?' == c) { // :null value
+    if('\n' != getchar()) die("expected eol");
+    return NULL;
+  }
+  if('"' != c) die("expected string");
   for(;;) {
     if(heap + HLEN <= buf) die("out of memory");
     int c = getchar();
diff --git a/dbquery.asd b/dbquery.asd
@@ -32,6 +32,6 @@
   :author "Tomas Hlavaty <tom@logand.com>"
   :maintainer "Tomas Hlavaty <tom@logand.com>"
   :licence "MIT"
-  :depends-on (#+sbcl :sb-concurrency)
+  :depends-on (:cl-rw)
   :serial t
   :components ((:file "dbquery")))
diff --git a/dbquery.lisp b/dbquery.lisp
@@ -33,175 +33,6 @@
 
 (in-package :dbquery)
 
-;; (defun call-with-program-output (command fn)
-;;   #+ccl
-;;   (let ((x (ccl:run-program (car command) (cdr command) :output :stream)))
-;;     (funcall fn (ccl:external-process-output-stream x))
-;;     #+nil
-;;     (unwind-protect (funcall fn (ccl:external-process-output-stream x))
-;;       (ccl:external-process-status )))
-;;   #+ecl
-;;   (multiple-value-bind (io status proc)
-;;       (ext:run-program (car command) (cdr command)
-;;                        :input nil
-;;                        :output :stream
-;;                        :error nil ;;t
-;;                        :wait nil)
-;;     (when proc
-;;       (funcall fn io)
-;;       #+nil
-;;       (unwind-protect (funcall fn io)
-;;         (close io)
-;;         (ext:external-process-wait proc))))
-;;   #-(or ccl ecl)
-;;   (error "DBQUERY::CALL-WITH-PROGRAM-OUTPUT not implemented"))
-
-;; (defmacro with-program-output ((stream command) &body body)
-;;   `(call-with-program-output ,command (lambda (,stream) ,@body)))
-
-;; ;;(with-program-output (s '("sha1sum" "/etc/passwd")))
-;; ;;(with-program-output (s '("sha1sum" "/etc/passwd")) (read-line s))
-
-(defmacro with-lock ((lock) &body body)
-  #+ccl `(ccl:with-lock-grabbed (,lock) ,@body)
-  #+ecl `(mp:with-lock (,lock) ,@body)
-  #+sbcl `(sb-concurrency::with-mutex (,lock) ,@body)
-  #-(or ccl ecl sbcl) (error "DBQUERY::WITH-LOCK not implemented"))
-
-(defun make-lock (name)
-  #+ccl (ccl:make-lock name)
-  #+ecl (mp:make-lock :name name)
-  #+sbcl (sb-concurrency::make-mutex :name (string name))
-  #-(or ccl ecl sbcl) (error "DBQUERY::MAKE-LOCK not implemented"))
-
-(defun make-semaphore ()
-  #+ccl (ccl:make-semaphore)
-  #+ecl (mp:make-semaphore)
-  #+sbcl (sb-concurrency::make-semaphore)
-  #-(or ccl ecl sbcl) (error "DBQUERY::MAKE-SEMAPHORE not implemented"))
-
-(defun signal-semaphore (x)
-  #+ccl (ccl:signal-semaphore x)
-  #+ecl (mp:signal-semaphore x)
-  #+sbcl (sb-concurrency::signal-semaphore x)
-  #-(or ccl ecl sbcl) (error "DBQUERY::SIGNAL-SEMAPHORE not implemented"))
-
-(defun wait-on-semaphore (x)
-  #+ccl (ccl:wait-on-semaphore x)
-  #+ecl (mp:wait-on-semaphore x)
-  #+sbcl (sb-concurrency::wait-on-semaphore x)
-  #-(or ccl ecl sbcl) (error "DBQUERY::WAIT-ON-SEMAPHORE not implemented"))
-
-(defun make-concurrent-queue ()
-  (let ((x (cons nil nil))
-        (l (make-lock 'concurrent-queue-lock))
-        (s (make-semaphore)))
-    (setf (car x) x)
-    (lambda (&optional (value nil valuep))
-      (if valuep
-          (let ((y (cons value nil)))
-            (with-lock (l)
-              (setf (cdar x) y
-                    (car x) y)
-              (signal-semaphore s))
-            value)
-          (do (done z)
-              (done z)
-            (wait-on-semaphore s)
-            (with-lock (l)
-              (unless (eq x (car x))
-                (setq done t
-                      z (pop (cdr x)))
-                (unless (cdr x)
-                  (setf (car x) x)))))))))
-
-;; (setq q (make-concurrent-queue))
-;; (funcall q 1)
-;; (funcall q 2)
-;; (funcall q 3)
-;; (funcall q)
-
-(defun make-thread (name fn)
-  #+ccl (ccl:process-run-function name fn)
-  #+ecl (mp:process-run-function name fn)
-  #+sbcl (sb-concurrency::make-thread fn :name (string name))
-  #-(or ccl ecl sbcl) (error "DBQUERY::MAKE-THREAD not implemented"))
-
-(defun make-program (command args)
-  #+ccl
-  (let ((p (ccl:run-program command
-                            args
-                            :input :stream
-                            :output :stream
-                            :error nil
-                            :sharing :external
-                            :wait nil)))
-    (assert (eq :running (ccl:external-process-status p)))
-    (lambda (msg)
-      (ecase msg
-        (status-and-code (ccl:external-process-status p))
-        (input-stream (ccl:external-process-input-stream p))
-        (output-stream (ccl:external-process-output-stream p))
-        (wait (ccl::external-process-wait p)))))
-  #+ecl
-  (multiple-value-bind (io x p)
-      (ext:run-program command
-                       args
-                       :input :stream
-                       :output :stream
-                       :error nil
-                       :wait nil)
-    (declare (ignore x))
-    (assert (eq :running (ext:external-process-status p)))
-    (lambda (msg)
-      (ecase msg
-        (status-and-code (ext:external-process-status p))
-        (input-stream io)
-        (output-stream io)
-        (wait (ext:external-process-wait p)))))
-  #+sbcl
-  (let ((p (sb-ext:run-program command
-                               args
-                               :input :stream
-                               :output :stream
-                               :error nil
-                               :wait nil)))
-    (assert (eq :running (sb-ext:process-status p)))
-    (lambda (msg)
-      (ecase msg
-        (status-and-code (sb-ext:process-status p))
-        (input-stream (sb-ext:process-input p))
-        (output-stream (sb-ext:process-output p))
-        (wait (sb-ext:process-wait p)))))
-  #-(or ccl ecl sbcl)
-  (error "DBQUERY::MAKE-PROGRAM not implemented"))
-
-(defun make-program-server (command args writer reader)
-  (let ((p (make-program command args))
-        (wq (make-concurrent-queue)))
-    (make-thread 'program-server-writer
-                 (let ((s (funcall p 'input-stream)))
-                   (lambda ()
-                     (do (x)
-                         ((not (setq x (funcall wq)))
-                          (close s))
-                       (funcall writer x s)))))
-    (let ((l (make-lock 'program-server-lock))
-          (s (funcall p 'output-stream)))
-      (lambda (query)
-        (with-lock (l)
-          (when wq
-            (cond
-              (query
-                  (funcall wq query)
-                (funcall reader s))
-              (t (funcall wq nil)
-                 (setq wq nil)
-                 (funcall p 'wait)
-                 (multiple-value-bind (status code) (funcall p 'status-and-code)
-                   (assert (eq :exited status))
-                   (assert (zerop code)))))))))))
-
 (defun query (server q &rest args)
   (funcall server `(:query ,q ,@args)))
 
@@ -229,16 +60,20 @@
         (t (error "expected number ~d ~s" z c))))))
 
 (defun rstr ()
-  (assert  (char= #\" (read-char)))
-  (with-output-to-string (*standard-output*)
-    (do (done)
-        (done)
-      (let ((c (read-char)))
-        (case c
-          (#\\ (write-char (read-char)))
-          (#\" (assert (char= #\newline (read-char)))
-               (setq done t))
-          (t (write-char c)))))))
+  (ecase (read-char)
+    (#\?
+     (assert (char= #\newline (read-char)))
+     :null)
+    (#\"
+     (with-output-to-string (*standard-output*)
+       (do (done)
+           (done)
+         (let ((c (read-char)))
+           (case c
+             (#\\ (write-char (read-char)))
+             (#\" (assert (char= #\newline (read-char)))
+                  (setq done t))
+             (t (write-char c)))))))))
 
 (defun dbquery-pg-writer (value *standard-output*)
   (destructuring-bind (cmd &rest rest) value
@@ -262,6 +97,7 @@
            (format t "3~%~d~%~s~%" (length args) stm)
            (dolist (a args)
              (etypecase a
+               (null (format t "-~%"))
                (integer (format t "\"~s\"~%" a))
                (string (format t "~s~%" a))))))
         (:deallocate
@@ -269,6 +105,22 @@
            (%query (format nil "DEALLOCATE ~s" stm)))))))
   (finish-output))
 
+;;http://doxygen.postgresql.org/interfaces_2ecpg_2ecpglib_2pg__type_8h.html
+(defun parse-pg-value (value type)
+  (if (eq :null value)
+      :null
+      (ecase type
+        (16 (cond
+              ((equal "t" value) :true)
+              ((equal "f" value) :false)
+              (t (error "unexpected boolean value ~s" value))))
+        ((#+nil 25 1700) (parse-integer value))
+        (1043 value)
+        (1184 ;; TODO parse "2010-06-21 18:54:11+02"
+         value))))
+
+;;(DBQUERY::PARSE-PG-VALUE "VAF0co3Ymo/mWob7qH1p/zCdSyhgp4+5n+Wp/cuoVy75v9BRKIVlSQtb8Jq4p3AexYExdbmEZO2wKcpkymsqAbfN8F5dB+atrBsIAAAA" 25)
+
 (defun dbquery-pg-reader (*standard-input*)
   (ecase (rnum)
     (0 (error "Database error ~s ~s" (rstr) (rstr)))
@@ -276,21 +128,27 @@
      (let ((n (rnum))
            (m (rnum)))
        (when (and (plusp n) (plusp m))
-         (cons (loop for i from 0 below m collect (cons (rnum) (rstr)))
-               (loop
-                  for i from 0 below n
-                  appending (loop for j from 0 below m collect (rstr)))))))
+         (let ((cols (loop for i from 0 below m collect (cons (rnum) (rstr)))))
+           (progn ;;cons cols
+             (loop
+                for i from 0 below n
+                collect (loop
+                           for j from 0 below m
+                           for (type . name) in cols
+                           collect (parse-pg-value (rstr) type))))))))
     (2)))
 
 (defun make-pg-server (command connection-info)
-  (make-program-server
+  (rw.concurrency:make-program-server
    command (list connection-info) 'dbquery-pg-writer 'dbquery-pg-reader))
 
 (defun make-sqlite-server (command db)
-  (make-program-server command (list db) 'dbquery-writer 'dbquery-reader))
+  (rw.concurrency:make-program-server
+   command (list db) 'dbquery-writer 'dbquery-reader))
 
 (defun make-mysql-server (command host user password db)
-  (make-program-server command (list host user password db) 'dbquery-writer 'dbquery-reader))
+  (rw.concurrency:make-program-server
+   command (list host user password db) 'dbquery-writer 'dbquery-reader))
 
 ;; (setq c (make-pg-server "cat" "-"))
 ;; (funcall c "123")