ob-sql.el 5.2 KB
Newer Older
1 2
;;; ob-sql.el --- org-babel functions for sql evaluation

3
;; Copyright (C) 2009-2012  Free Software Foundation, Inc.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

;; 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.
;;
;; 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.
;;
Bastien Guerry's avatar
Bastien Guerry committed
34
;; For now lets just allow a generic ':cmdline' header argument.
35 36 37 38 39 40 41
;;
;; 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?
42
;;
43 44 45 46 47 48

;;; Code:
(require 'ob)
(eval-when-compile (require 'cl))

(declare-function org-table-import "org-table" (file arg))
Carsten Dominik's avatar
Carsten Dominik committed
49
(declare-function orgtbl-to-csv "org-table" (TABLE PARAMS))
50 51 52

(defvar org-babel-default-header-args:sql '())

Bastien Guerry's avatar
Bastien Guerry committed
53 54 55
(defvar org-babel-header-arg-names:sql
  '(engine out-file))

Carsten Dominik's avatar
Carsten Dominik committed
56 57 58 59 60
(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))))

61 62 63
(defun org-babel-execute:sql (body params)
  "Execute a block of Sql code with Babel.
This function is called by `org-babel-execute-src-block'."
Carsten Dominik's avatar
Carsten Dominik committed
64
  (let* ((result-params (cdr (assoc :result-params params)))
65 66
         (cmdline (cdr (assoc :cmdline params)))
         (engine (cdr (assoc :engine params)))
Carsten Dominik's avatar
Carsten Dominik committed
67
         (in-file (org-babel-temp-file "sql-in-"))
68
         (out-file (or (cdr (assoc :out-file params))
Carsten Dominik's avatar
Carsten Dominik committed
69
                       (org-babel-temp-file "sql-out-")))
70
	 (header-delim "")
71
         (command (case (intern engine)
72 73 74 75 76
                    ('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"
Carsten Dominik's avatar
Carsten Dominik committed
77
                                    (or cmdline "")
78 79 80 81 82 83 84
				    (org-babel-process-file-name in-file)
				    (org-babel-process-file-name out-file)))
		    ('postgresql (format
				  "psql -A -P footer=off -F \"\t\"  -f %s -o %s %s"
				    (org-babel-process-file-name in-file)
				    (org-babel-process-file-name out-file)
				    (or cmdline "")))
85 86
                    (t (error "no support for the %s sql engine" engine)))))
    (with-temp-file in-file
Carsten Dominik's avatar
Carsten Dominik committed
87
      (insert (org-babel-expand-body:sql body params)))
88 89
    (message command)
    (shell-command command)
Bastien Guerry's avatar
Bastien Guerry committed
90 91 92 93 94 95 96
    (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)))
97
      (with-temp-buffer
Bastien Guerry's avatar
Bastien Guerry committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	;; 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))))))))
122

Carsten Dominik's avatar
Carsten Dominik committed
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
(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)
145 146 147 148 149 150 151

(defun org-babel-prep-session:sql (session params)
  "Raise an error because Sql sessions aren't implemented."
  (error "sql sessions not yet implemented"))

(provide 'ob-sql)

152

153 154

;;; ob-sql.el ends here