dbquery

Query RDBMS and return S-expression
git clone https://logand.com/git/dbquery.git/
Log | Files | Refs | README

commit 870a499c1fbef28d728e8d312a3161c472036c70
parent c4b3eb2b27ea83d774a27dbaceafc12904901190
Author: Tomas Hlavaty <tom@logand.com>
Date:   Tue,  5 Mar 2013 22:05:50 +0100

added dbquery for common lisp

Diffstat:
Adbquery.asd | 36++++++++++++++++++++++++++++++++++++
Adbquery.lisp | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 171 insertions(+), 0 deletions(-)

diff --git a/dbquery.asd b/dbquery.asd @@ -0,0 +1,36 @@ +;;; -*- lisp; -*- + +;;; Copyright (C) 2013 Tomas Hlavaty <tom@logand.com> +;;; +;;; Permission is hereby granted, free of charge, to any person +;;; obtaining a copy of this software and associated documentation +;;; files (the "Software"), to deal in the Software without +;;; restriction, including without limitation the rights to use, copy, +;;; modify, merge, publish, distribute, sublicense, and/or sell copies +;;; of the Software, and to permit persons to whom the Software is +;;; furnished to do so, subject to the following conditions: +;;; +;;; The above copyright notice and this permission notice shall be +;;; included in all copies or substantial portions of the Software. +;;; +;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;;; DEALINGS IN THE SOFTWARE. + +(defpackage :dbquery-system + (:use :asdf :cl)) + +(in-package :dbquery-system) + +(defsystem :dbquery + :description "dbquery for Common Lisp" + :author "Tomas Hlavaty <tom@logand.com>" + :maintainer "Tomas Hlavaty <tom@logand.com>" + :licence "MIT" + :serial t + :components ((:file "dbquery"))) diff --git a/dbquery.lisp b/dbquery.lisp @@ -0,0 +1,135 @@ +;;; Copyright (C) 2013 Tomas Hlavaty <tom@logand.com> +;;; +;;; Permission is hereby granted, free of charge, to any person +;;; obtaining a copy of this software and associated documentation +;;; files (the "Software"), to deal in the Software without +;;; restriction, including without limitation the rights to use, copy, +;;; modify, merge, publish, distribute, sublicense, and/or sell copies +;;; of the Software, and to permit persons to whom the Software is +;;; furnished to do so, subject to the following conditions: +;;; +;;; The above copyright notice and this permission notice shall be +;;; included in all copies or substantial portions of the Software. +;;; +;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;;; DEALINGS IN THE SOFTWARE. + +(defpackage :dbquery + (:use :cl)) + +(in-package :dbquery) + +(defun call-with-program-output (command fn) + (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 )))) + +(defmacro with-program-output ((stream command) &body body) + `(call-with-program-output ,command (lambda (,stream) ,@body))) + +#+nil +(time + (with-program-output (s (list "./dbquery-sqlite" + "sqlite.db" + "select 123, 1, 0, null, 12.34, 'hello'")) + (read s))) + +#+nil +(time + (with-program-output (s (list "./dbquery-pg" + "dbname='pokus' user='tomas'" + "select 123, 1, 0, null, 12.34, 'hello'")) + (read s))) + +(defun make-concurrent-queue () + (let ((x (cons nil nil)) + (l (ccl:make-lock 'concurrent-queue-lock)) + (s (ccl:make-semaphore))) + (setf (car x) x) + (lambda (&optional (value nil valuep)) + (if valuep + (let ((y (cons value nil))) + (ccl:with-lock-grabbed (l) + (setf (cdar x) y + (car x) y) + (ccl:signal-semaphore s)) + value) + (do (done z) + (done z) + (ccl:wait-on-semaphore s) + (ccl:with-lock-grabbed (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-program-server (command args writer reader) + (let ((p (ccl:run-program command + args + :input :stream + :output :stream + :sharing :external + :wait nil))) + (assert (eq :running (ccl:external-process-status p))) + (let ((wq (make-concurrent-queue))) + (ccl:process-run-function + 'program-server-writer + (let ((s (ccl:external-process-input-stream p))) + (lambda () + (do (x) + ((not (setq x (funcall wq))) + (close s)) + (funcall writer x s))))) + (let ((l (ccl:make-lock 'program-server-lock)) + (s (ccl:external-process-output-stream p))) + (lambda (&optional query) + (ccl:with-lock-grabbed (l) + (when wq + (cond + (query + (funcall wq query) + (funcall reader s)) + (t (funcall wq nil) + (setq wq nil) + (ccl::external-process-wait p) + (multiple-value-bind (status code) (ccl:external-process-status p) + (assert (eq :exited status)) + (assert (zerop code)))))))))))) + +(defun dbquery-writer (value stream) + (write-line value stream) + (finish-output stream)) + +(defun dbquery-reader (stream) ;; TODO raise backend errors! + (let (*read-eval*) + (prog1 (read stream nil nil) + (assert (char= #\newline (read-char stream)))))) + +(defun make-pg-server (command connection-info) + (make-program-server command (list connection-info) 'dbquery-writer 'dbquery-reader)) + +;; (setq c (make-pg-server "cat" "-")) +;; (funcall c "123") +;; (funcall c nil) +;; (funcall c) + +;; (setq c (make-pg-server "./dbquery-pg" "dbname='pokus' user='tomas'")) +;; (funcall c "select 1, 2+3") +;; (time (funcall c "select 4, 'hello'")) +;; (funcall c nil) +;; (funcall c)