org-mode/lisp/ob-sql.el

170 lines
5.8 KiB
EmacsLisp
Raw Normal View History

;;; ob-sql.el --- org-babel functions for sql evaluation
2012-03-17 11:31:04 -04:00
;; Copyright (C) 2009-2012 Free Software Foundation, Inc.
;; Author: Eric Schulte
;; Keywords: literate programming, reproducible research
;; Homepage: http://orgmode.org
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Org-Babel support for evaluating sql source code.
;; (see also ob-sqlite.el)
;;
;; SQL is somewhat unique in that there are many different engines for
;; the evaluation of sql (Mysql, PostgreSQL, etc...), so much of this
;; file will have to be implemented engine by engine.
;;
;; Also SQL evaluation generally takes place inside of a database.
;;
;; For now lets just allow a generic ':cmdline' header argument.
;;
;; TODO:
;;
;; - support for sessions
;; - add more useful header arguments (user, passwd, database, etc...)
;; - support for more engines (currently only supports mysql)
;; - what's a reasonable way to drop table data into SQL?
;;
;;; Code:
(require 'ob)
(eval-when-compile (require 'cl))
(declare-function org-table-import "org-table" (file arg))
(declare-function orgtbl-to-csv "org-table" (TABLE PARAMS))
(defvar org-babel-default-header-args:sql '())
(defvar org-babel-header-args:sql
'((engine . :any)
(out-file . :any)))
(defun org-babel-expand-body:sql (body params)
"Expand BODY according to the values of PARAMS."
(org-babel-sql-expand-vars
body (mapcar #'cdr (org-babel-get-header params :var))))
(defun org-babel-execute:sql (body params)
2010-07-13 19:20:08 -04:00
"Execute a block of Sql code with Babel.
This function is called by `org-babel-execute-src-block'."
babel:removing calls to ob-process-params, and updating use of results * lisp/ob-C.el (org-babel-C-execute): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-R.el (org-babel-execute:R): removing call to org-babel-process-params which should no longer be called from within a language file (org-babel-R-variable-assignments): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-asymptote.el (org-babel-execute:asymptote): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-clojure.el (org-babel-execute:clojure): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-dot.el (org-babel-execute:dot): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-emacs-lisp.el (org-babel-expand-body:emacs-lisp): removing call to org-babel-process-params which should no longer be called from within a language file (org-babel-execute:emacs-lisp): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-haskell.el (org-babel-execute:haskell): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-js.el (org-babel-execute:js): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-lisp.el (org-babel-execute:lisp): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-ocaml.el (org-babel-execute:ocaml): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-octave.el (org-babel-execute:octave): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-perl.el (org-babel-execute:perl): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-python.el (org-babel-execute:python): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-ruby.el (org-babel-execute:ruby): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-scheme.el (org-babel-execute:scheme): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-screen.el (org-babel-execute:screen): removing call to org-babel-process-params which should no longer be called from within a language file (org-babel-prep-session:screen): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-sh.el (org-babel-execute:sh): removing call to org-babel-process-params which should no longer be called from within a language file * lisp/ob-sql.el (org-babel-execute:sql): removing call to org-babel-process-params which should no longer be called from within a language file * ob-haskell.el (org-babel-execute:haskell): Remove reference to processed params * ob-clojure.el (org-babel-execute:clojure): Remove reference to processed params * ob-R.el (org-babel-execute:R): Remove reference to processed params
2010-10-21 07:52:24 -04:00
(let* ((result-params (cdr (assoc :result-params params)))
(cmdline (cdr (assoc :cmdline params)))
(engine (cdr (assoc :engine params)))
Babel now cleans up any temporary files created using org-babel-temp-file * lisp/ob.el (org-babel-temporary-directory): variable to hold the value of the Babel temporary directory (org-babel-temp-file): replacement for make-temp-file with cleanup on exit of Emacs (org-babel-remove-temporary-directory): cleanup function run on exit of Emacs (kill-emacs-hook): now includes babel cleanup function * lisp/ob-C.el (org-babel-C-execute): using org-babel-temp-file instead of make-temp-file * lisp/ob-R.el (org-babel-R-assign-elisp): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-R-evaluate-external-process): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-R-evaluate-session): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-asymptote.el (org-babel-execute:asymptote): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-clojure.el (org-babel-clojure-evaluate-external-process): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-ditaa.el (org-babel-execute:ditaa): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-dot.el (org-babel-execute:dot): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-gnuplot.el (org-babel-gnuplot-process-vars): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-execute:gnuplot): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-haskell.el (org-babel-load-session:haskell): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-haskell-export-to-lhs): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-latex.el (org-babel-execute:latex): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-ledger.el (org-babel-execute:ledger): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-lisp.el (org-babel-execute:lisp): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-octave.el (org-babel-octave-evaluate-external-process): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-octave-evaluate-session): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-octave-import-elisp-from-file): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-perl.el (org-babel-perl-evaluate): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-python.el (org-babel-python-evaluate): using `org-babel-temp-file' instead of `make-temp-file' using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-ruby.el (org-babel-ruby-evaluate): using `org-babel-temp-file' instead of `make-temp-file' using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sass.el (org-babel-execute:sass): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sh.el (org-babel-sh-evaluate): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sql.el (org-babel-execute:sql): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sqlite.el (org-babel-execute:sqlite): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-sqlite-expand-vars): using `org-babel-temp-file' instead of `make-temp-file'
2010-08-25 16:43:07 -04:00
(in-file (org-babel-temp-file "sql-in-"))
(out-file (or (cdr (assoc :out-file params))
Babel now cleans up any temporary files created using org-babel-temp-file * lisp/ob.el (org-babel-temporary-directory): variable to hold the value of the Babel temporary directory (org-babel-temp-file): replacement for make-temp-file with cleanup on exit of Emacs (org-babel-remove-temporary-directory): cleanup function run on exit of Emacs (kill-emacs-hook): now includes babel cleanup function * lisp/ob-C.el (org-babel-C-execute): using org-babel-temp-file instead of make-temp-file * lisp/ob-R.el (org-babel-R-assign-elisp): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-R-evaluate-external-process): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-R-evaluate-session): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-asymptote.el (org-babel-execute:asymptote): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-clojure.el (org-babel-clojure-evaluate-external-process): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-ditaa.el (org-babel-execute:ditaa): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-dot.el (org-babel-execute:dot): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-gnuplot.el (org-babel-gnuplot-process-vars): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-execute:gnuplot): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-haskell.el (org-babel-load-session:haskell): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-haskell-export-to-lhs): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-latex.el (org-babel-execute:latex): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-ledger.el (org-babel-execute:ledger): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-lisp.el (org-babel-execute:lisp): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-octave.el (org-babel-octave-evaluate-external-process): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-octave-evaluate-session): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-octave-import-elisp-from-file): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-perl.el (org-babel-perl-evaluate): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-python.el (org-babel-python-evaluate): using `org-babel-temp-file' instead of `make-temp-file' using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-ruby.el (org-babel-ruby-evaluate): using `org-babel-temp-file' instead of `make-temp-file' using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sass.el (org-babel-execute:sass): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sh.el (org-babel-sh-evaluate): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sql.el (org-babel-execute:sql): using `org-babel-temp-file' instead of `make-temp-file' * lisp/ob-sqlite.el (org-babel-execute:sqlite): using `org-babel-temp-file' instead of `make-temp-file' (org-babel-sqlite-expand-vars): using `org-babel-temp-file' instead of `make-temp-file'
2010-08-25 16:43:07 -04:00
(org-babel-temp-file "sql-out-")))
2010-12-21 12:33:37 -05:00
(header-delim "")
(command (case (intern engine)
('dbi (format "dbish --batch '%s' < %s | sed '%s' > %s"
(or cmdline "")
(org-babel-process-file-name in-file)
"/^+/d;s/^\|//;$d"
(org-babel-process-file-name out-file)))
('monetdb (format "mclient -f tab %s < %s > %s"
(or cmdline "")
(org-babel-process-file-name in-file)
(org-babel-process-file-name out-file)))
('msosql (format "osql %s -s \"\t\" -i %s -o %s"
(or cmdline "")
(org-babel-process-file-name in-file)
(org-babel-process-file-name out-file)))
('mysql (format "mysql %s < %s > %s"
babel: New function to process file names for use in external processes * ob.el (org-babel-process-file-name): New function (org-babel-maybe-remote-file): Delete function * ob-sql.el (org-babel-execute:sql): Use org-babel-process-file-name * ob-scheme.el (org-babel-execute:scheme): Use org-babel-process-file-name * ob-sass.el (org-babel-execute:sass): Use org-babel-process-file-name * ob-ruby.el (org-babel-ruby-evaluate): Use org-babel-process-file-name * ob-python.el (org-babel-python-evaluate-external-process): Use org-babel-process-file-name (org-babel-python-evaluate-session): Use org-babel-process-file-name * ob-plantuml.el (org-babel-execute:plantuml): Use org-babel-process-file-name * ob-perl.el (org-babel-perl-evaluate): Use org-babel-process-file-name * ob-octave.el (org-babel-octave-evaluate-external-process): Use org-babel-process-file-name (org-babel-octave-evaluate-session): Use org-babel-process-file-name, don't use org-babel-maybe-remote-file * ob-lisp.el (org-babel-execute:lisp): Use org-babel-process-file-name * ob-ledger.el (org-babel-execute:ledger): Use org-babel-process-file-name * ob-js.el (org-babel-execute:js): Use org-babel-process-file-name * ob-haskell.el (org-babel-haskell-export-to-lhs): Use org-babel-process-file-name * ob-gnuplot.el (org-babel-execute:gnuplot): Use org-babel-process-file-name * ob-eval.el (org-babel-eval-read-file): Don't use org-babel-maybe-remote-file * ob-dot.el (org-babel-execute:dot): Use org-babel-process-file-name * ob-ditaa.el (org-babel-execute:ditaa): Use org-babel-process-file-name * ob-clojure.el (org-babel-clojure-evaluate-external-process): Use org-babel-process-file-name * ob-asymptote.el (org-babel-execute:asymptote): Use org-babel-process-file-name * ob-R.el (org-babel-R-assign-elisp): Don't use org-babel-maybe-remote-file, use org-babel-process-file-name (org-babel-R-evaluate-external-process): Use org-babel-process-file-name (org-babel-R-evaluate-session): Use org-babel-process-file-name * ob-C.el (org-babel-C-execute): Use org-babel-process-file-name In addition to passing the file path through `expand-file-name', tramp-style remote file names are converted to conventional (local) file paths. The reason is that, if a tramp file name was in use in emacs, then the shell command will be executing on the remote machine in question. Further, by default the file name is passed through `shell-quote-argument'.
2010-09-22 16:40:14 -04:00
(or cmdline "")
(org-babel-process-file-name in-file)
(org-babel-process-file-name out-file)))
2010-12-21 12:33:37 -05:00
('postgresql (format
"psql -A -P footer=off -F \"\t\" -f %s -o %s %s"
babel: New function to process file names for use in external processes * ob.el (org-babel-process-file-name): New function (org-babel-maybe-remote-file): Delete function * ob-sql.el (org-babel-execute:sql): Use org-babel-process-file-name * ob-scheme.el (org-babel-execute:scheme): Use org-babel-process-file-name * ob-sass.el (org-babel-execute:sass): Use org-babel-process-file-name * ob-ruby.el (org-babel-ruby-evaluate): Use org-babel-process-file-name * ob-python.el (org-babel-python-evaluate-external-process): Use org-babel-process-file-name (org-babel-python-evaluate-session): Use org-babel-process-file-name * ob-plantuml.el (org-babel-execute:plantuml): Use org-babel-process-file-name * ob-perl.el (org-babel-perl-evaluate): Use org-babel-process-file-name * ob-octave.el (org-babel-octave-evaluate-external-process): Use org-babel-process-file-name (org-babel-octave-evaluate-session): Use org-babel-process-file-name, don't use org-babel-maybe-remote-file * ob-lisp.el (org-babel-execute:lisp): Use org-babel-process-file-name * ob-ledger.el (org-babel-execute:ledger): Use org-babel-process-file-name * ob-js.el (org-babel-execute:js): Use org-babel-process-file-name * ob-haskell.el (org-babel-haskell-export-to-lhs): Use org-babel-process-file-name * ob-gnuplot.el (org-babel-execute:gnuplot): Use org-babel-process-file-name * ob-eval.el (org-babel-eval-read-file): Don't use org-babel-maybe-remote-file * ob-dot.el (org-babel-execute:dot): Use org-babel-process-file-name * ob-ditaa.el (org-babel-execute:ditaa): Use org-babel-process-file-name * ob-clojure.el (org-babel-clojure-evaluate-external-process): Use org-babel-process-file-name * ob-asymptote.el (org-babel-execute:asymptote): Use org-babel-process-file-name * ob-R.el (org-babel-R-assign-elisp): Don't use org-babel-maybe-remote-file, use org-babel-process-file-name (org-babel-R-evaluate-external-process): Use org-babel-process-file-name (org-babel-R-evaluate-session): Use org-babel-process-file-name * ob-C.el (org-babel-C-execute): Use org-babel-process-file-name In addition to passing the file path through `expand-file-name', tramp-style remote file names are converted to conventional (local) file paths. The reason is that, if a tramp file name was in use in emacs, then the shell command will be executing on the remote machine in question. Further, by default the file name is passed through `shell-quote-argument'.
2010-09-22 16:40:14 -04:00
(org-babel-process-file-name in-file)
(org-babel-process-file-name out-file)
(or cmdline "")))
(t (error "no support for the %s sql engine" engine)))))
2010-04-16 01:21:56 -04:00
(with-temp-file in-file
(insert
(case (intern engine)
('dbi "/format partbox\n")
(t ""))
(org-babel-expand-body:sql body params)))
(message command)
(shell-command command)
(if (or (member "scalar" result-params)
(member "verbatim" result-params)
(member "html" result-params)
(member "code" result-params)
(equal (point-min) (point-max)))
(with-temp-buffer
(progn (insert-file-contents-literally out-file) (buffer-string)))
2010-12-21 12:33:37 -05:00
(with-temp-buffer
;; need to figure out what the delimiter is for the header row
(with-temp-buffer
(insert-file-contents out-file)
(goto-char (point-min))
(when (re-search-forward "^\\(-+\\)[^-]" nil t)
(setq header-delim (match-string-no-properties 1)))
(goto-char (point-max))
(forward-char -1)
(while (looking-at "\n")
(delete-char 1)
(goto-char (point-max))
(forward-char -1))
(write-file out-file))
(org-table-import out-file '(16))
(org-babel-reassemble-table
(mapcar (lambda (x)
(if (string= (car x) header-delim)
'hline
x))
(org-table-to-lisp))
(org-babel-pick-name (cdr (assoc :colname-names params))
(cdr (assoc :colnames params)))
(org-babel-pick-name (cdr (assoc :rowname-names params))
(cdr (assoc :rownames params))))))))
(defun org-babel-sql-expand-vars (body vars)
"Expand the variables held in VARS in BODY."
(mapc
(lambda (pair)
(setq body
(replace-regexp-in-string
(format "\$%s" (car pair))
((lambda (val)
(if (listp val)
((lambda (data-file)
(with-temp-file data-file
(insert (orgtbl-to-csv
val '(:fmt (lambda (el) (if (stringp el)
el
(format "%S" el)))))))
data-file)
(org-babel-temp-file "sql-data-"))
(if (stringp val) val (format "%S" val))))
(cdr pair))
body)))
vars)
body)
(defun org-babel-prep-session:sql (session params)
2010-07-13 19:20:08 -04:00
"Raise an error because Sql sessions aren't implemented."
(error "sql sessions not yet implemented"))
(provide 'ob-sql)
;;; ob-sql.el ends here