From e2b17ccc52826416e6b53d48c180ffa71f8fccdf Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Tue, 5 Feb 2013 10:50:50 -0500 Subject: [PATCH] Improvements to Org SQL support * add a header-row delimiter to the tables returned from mysql * add new sql-specific header args for the database connection, and implements them for mysql * add support for :colnames (mysql only) * (minor) add an edebug spec to org-babel-result-cond to allow edebugging through it * add some doc about what header args are used --- lisp/ob-core.el | 3 +- lisp/ob-sql.el | 86 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 69 insertions(+), 20 deletions(-) diff --git a/lisp/ob-core.el b/lisp/ob-core.el index 08946d70b..8ba6edfa8 100644 --- a/lisp/ob-core.el +++ b/lisp/ob-core.el @@ -2572,7 +2572,8 @@ Emacs shutdown.")) (defmacro org-babel-result-cond (result-params scalar-form &rest table-forms) "Call the code to parse raw string results according to RESULT-PARAMS." - (declare (indent 1)) + (declare (indent 1) + (debug (form form &rest form))) `(unless (member "none" ,result-params) (if (or (member "scalar" ,result-params) (member "verbatim" ,result-params) diff --git a/lisp/ob-sql.el b/lisp/ob-sql.el index 8d2ba2419..51cafe616 100644 --- a/lisp/ob-sql.el +++ b/lisp/ob-sql.el @@ -32,12 +32,24 @@ ;; ;; Also SQL evaluation generally takes place inside of a database. ;; -;; For now lets just allow a generic ':cmdline' header argument. +;; Header args used: +;; - engine +;; - cmdline +;; - dbhost +;; - dbuser +;; - dbpassword +;; - database +;; - colnames (default, nil, means "yes") +;; - result-params +;; - out-file +;; The following are used but not really implemented for SQL: +;; - colname-names +;; - rownames +;; - rowname-names ;; ;; 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? ;; @@ -52,21 +64,40 @@ (defvar org-babel-default-header-args:sql '()) -(defvar org-babel-header-args:sql - '((engine . :any) - (out-file . :any))) +(defconst org-babel-header-args:sql + '((engine . :any) + (out-file . :any) + (dbhost . :any) + (dbuser . :any) + (dbpassword . :any) + (database . :any)) + "SQL-specific header arguments.") (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 dbstring-mysql (host user password database) + "Make MySQL cmd line args for database connection. Pass nil to omit that arg." + (combine-and-quote-strings + (remq nil + (list (when host (concat "-h" host)) + (when user (concat "-u" user)) + (when password (concat "-p" password)) + (when database (concat "-D" database)))))) + (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'." (let* ((result-params (cdr (assoc :result-params params))) (cmdline (cdr (assoc :cmdline params))) + (dbhost (cdr (assoc :dbhost params))) + (dbuser (cdr (assoc :dbuser params))) + (dbpassword (cdr (assoc :dbpassword params))) + (database (cdr (assoc :database params))) (engine (cdr (assoc :engine params))) + (colnames-p (not (equal "no" (cdr (assoc :colnames params))))) (in-file (org-babel-temp-file "sql-in-")) (out-file (or (cdr (assoc :out-file params)) (org-babel-temp-file "sql-out-"))) @@ -85,7 +116,9 @@ This function is called by `org-babel-execute-src-block'." (or cmdline "") (org-babel-process-file-name in-file) (org-babel-process-file-name out-file))) - ('mysql (format "mysql %s < %s > %s" + ('mysql (format "mysql %s %s %s < %s > %s" + (dbstring-mysql dbhost dbuser dbpassword database) + (if colnames-p "" "-N") (or cmdline "") (org-babel-process-file-name in-file) (org-babel-process-file-name out-file))) @@ -107,19 +140,34 @@ This function is called by `org-babel-execute-src-block'." (with-temp-buffer (progn (insert-file-contents-literally out-file) (buffer-string))) (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)) + (case (intern engine) + ('mysql + ;; add header row delimiter after column-names header in first line + (cond + (colnames-p + (with-temp-buffer + (insert-file-contents out-file) + (goto-char (point-min)) + (forward-line 1) + (insert "-\n") + (setq header-delim "-") + (write-file out-file) + )))) + (t + ;; 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)