aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/progmodes/sql.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/progmodes/sql.el')
-rw-r--r--lisp/progmodes/sql.el608
1 files changed, 421 insertions, 187 deletions
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index a80a555c13f..7148027f487 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -5,10 +5,9 @@
5 5
6;; Author: Alex Schroeder <alex@gnu.org> 6;; Author: Alex Schroeder <alex@gnu.org>
7;; Maintainer: Michael Mauger <mmaug@yahoo.com> 7;; Maintainer: Michael Mauger <mmaug@yahoo.com>
8;; Version: 2.6 8;; Version: 2.8
9;; Keywords: comm languages processes 9;; Keywords: comm languages processes
10;; URL: http://savannah.gnu.org/cgi-bin/viewcvs/emacs/emacs/lisp/progmodes/sql.el 10;; URL: http://savannah.gnu.org/cgi-bin/viewcvs/emacs/emacs/lisp/progmodes/sql.el
11;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?SqlMode
12 11
13;; This file is part of GNU Emacs. 12;; This file is part of GNU Emacs.
14 13
@@ -286,6 +285,9 @@ Customizing your password will store it in your ~/.emacs file."
286 285
287(define-widget 'sql-login-params 'lazy 286(define-widget 'sql-login-params 'lazy
288 "Widget definition of the login parameters list" 287 "Widget definition of the login parameters list"
288 ;; FIXME: does not implement :default property for the user,
289 ;; database and server options. Anybody have some guidance on how to
290 ;; do this.
289 :tag "Login Parameters" 291 :tag "Login Parameters"
290 :type '(repeat (choice 292 :type '(repeat (choice
291 (const user) 293 (const user)
@@ -300,7 +302,7 @@ Customizing your password will store it in your ~/.emacs file."
300 (const :format "" server) 302 (const :format "" server)
301 (const :format "" :completion) 303 (const :format "" :completion)
302 (restricted-sexp 304 (restricted-sexp
303 :match-alternatives (listp symbolp)))) 305 :match-alternatives (listp stringp))))
304 (choice :tag "database" 306 (choice :tag "database"
305 (const database) 307 (const database)
306 (list :tag "file" 308 (list :tag "file"
@@ -311,7 +313,7 @@ Customizing your password will store it in your ~/.emacs file."
311 (const :format "" database) 313 (const :format "" database)
312 (const :format "" :completion) 314 (const :format "" :completion)
313 (restricted-sexp 315 (restricted-sexp
314 :match-alternatives (listp symbolp)))) 316 :match-alternatives (listp stringp))))
315 (const port)))) 317 (const port))))
316 318
317;; SQL Product support 319;; SQL Product support
@@ -401,6 +403,8 @@ Customizing your password will store it in your ~/.emacs file."
401 :sqli-options sql-mysql-options 403 :sqli-options sql-mysql-options
402 :sqli-login sql-mysql-login-params 404 :sqli-login sql-mysql-login-params
403 :sqli-comint-func sql-comint-mysql 405 :sqli-comint-func sql-comint-mysql
406 :list-all "SHOW TABLES;"
407 :list-table "DESCRIBE %s;"
404 :prompt-regexp "^mysql> " 408 :prompt-regexp "^mysql> "
405 :prompt-length 6 409 :prompt-length 6
406 :prompt-cont-regexp "^ -> " 410 :prompt-cont-regexp "^ -> "
@@ -428,6 +432,8 @@ Customizing your password will store it in your ~/.emacs file."
428 :sqli-options sql-postgres-options 432 :sqli-options sql-postgres-options
429 :sqli-login sql-postgres-login-params 433 :sqli-login sql-postgres-login-params
430 :sqli-comint-func sql-comint-postgres 434 :sqli-comint-func sql-comint-postgres
435 :list-all ("\\d+" . "\\dS+")
436 :list-table ("\\d+ %s" . "\\dS+ %s")
431 :prompt-regexp "^.*=[#>] " 437 :prompt-regexp "^.*=[#>] "
432 :prompt-length 5 438 :prompt-length 5
433 :prompt-cont-regexp "^.*[-(][#>] " 439 :prompt-cont-regexp "^.*[-(][#>] "
@@ -452,6 +458,8 @@ Customizing your password will store it in your ~/.emacs file."
452 :sqli-options sql-sqlite-options 458 :sqli-options sql-sqlite-options
453 :sqli-login sql-sqlite-login-params 459 :sqli-login sql-sqlite-login-params
454 :sqli-comint-func sql-comint-sqlite 460 :sqli-comint-func sql-comint-sqlite
461 :list-all ".tables"
462 :list-table ".schema %s"
455 :prompt-regexp "^sqlite> " 463 :prompt-regexp "^sqlite> "
456 :prompt-length 8 464 :prompt-length 8
457 :prompt-cont-regexp "^ ...> " 465 :prompt-cont-regexp "^ ...> "
@@ -510,6 +518,23 @@ may be any one of the following:
510 database. Do product specific 518 database. Do product specific
511 configuration of comint in this function. 519 configuration of comint in this function.
512 520
521 :list-all Command string or function which produces
522 a listing of all objects in the database.
523 If it's a cons cell, then the car
524 produces the standard list of objects and
525 the cdr produces an enhanced list of
526 objects. What \"enhanced\" means is
527 dependent on the SQL product and may not
528 exist. In general though, the
529 \"enhanced\" list should include visible
530 objects from other schemas.
531
532 :list-table Command string or function which produces
533 a detailed listing of a specific database
534 table. If its a cons cell, then the car
535 produces the standard list and the cdr
536 produces an enhanced list.
537
513 :prompt-regexp regular expression string that matches 538 :prompt-regexp regular expression string that matches
514 the prompt issued by the product 539 the prompt issued by the product
515 interpreter. 540 interpreter.
@@ -941,7 +966,9 @@ add your name with a \"-U\" prefix (such as \"-Umark\") to the list."
941 :version "20.8" 966 :version "20.8"
942 :group 'SQL) 967 :group 'SQL)
943 968
944(defcustom sql-postgres-login-params '(user database server) 969(defcustom sql-postgres-login-params `((user :default ,(user-login-name))
970 (database :default ,(user-login-name))
971 server)
945 "List of login parameters needed to connect to Postgres." 972 "List of login parameters needed to connect to Postgres."
946 :type 'sql-login-params 973 :type 'sql-login-params
947 :version "24.1" 974 :version "24.1"
@@ -1025,6 +1052,12 @@ Starts `sql-interactive-mode' after doing some setup."
1025 1052
1026;; Passwords are not kept in a history. 1053;; Passwords are not kept in a history.
1027 1054
1055(defvar sql-product-history nil
1056 "History of products used.")
1057
1058(defvar sql-connection-history nil
1059 "History of connections used.")
1060
1028(defvar sql-buffer nil 1061(defvar sql-buffer nil
1029 "Current SQLi buffer. 1062 "Current SQLi buffer.
1030 1063
@@ -1052,11 +1085,24 @@ You can change `sql-prompt-length' on `sql-interactive-mode-hook'.")
1052 1085
1053Used by `sql-rename-buffer'.") 1086Used by `sql-rename-buffer'.")
1054 1087
1055(defun sql-buffer-live-p (buffer) 1088(defun sql-buffer-live-p (buffer &optional product)
1056 "Returns non-nil if the process associated with buffer is live." 1089 "Returns non-nil if the process associated with buffer is live.
1057 (and buffer 1090
1058 (buffer-live-p (get-buffer buffer)) 1091BUFFER can be a buffer object or a buffer name. The buffer must
1059 (get-buffer-process buffer))) 1092be a live buffer, have an running process attached to it, be in
1093`sql-interactive-mode', and, if PRODUCT is specified, it's
1094`sql-product' must match."
1095
1096 (when buffer
1097 (setq buffer (get-buffer buffer))
1098 (and buffer
1099 (buffer-live-p buffer)
1100 (get-buffer-process buffer)
1101 (comint-check-proc buffer)
1102 (with-current-buffer buffer
1103 (and (derived-mode-p 'sql-interactive-mode)
1104 (or (not product)
1105 (eq product sql-product)))))))
1060 1106
1061;; Keymap for sql-interactive-mode. 1107;; Keymap for sql-interactive-mode.
1062 1108
@@ -1073,6 +1119,8 @@ Used by `sql-rename-buffer'.")
1073 (define-key map (kbd "O") 'sql-magic-go) 1119 (define-key map (kbd "O") 'sql-magic-go)
1074 (define-key map (kbd "o") 'sql-magic-go) 1120 (define-key map (kbd "o") 'sql-magic-go)
1075 (define-key map (kbd ";") 'sql-magic-semicolon) 1121 (define-key map (kbd ";") 'sql-magic-semicolon)
1122 (define-key map (kbd "C-c C-l a") 'sql-list-all)
1123 (define-key map (kbd "C-c C-l t") 'sql-list-table)
1076 map) 1124 map)
1077 "Mode map used for `sql-interactive-mode'. 1125 "Mode map used for `sql-interactive-mode'.
1078Based on `comint-mode-map'.") 1126Based on `comint-mode-map'.")
@@ -1086,6 +1134,8 @@ Based on `comint-mode-map'.")
1086 (define-key map (kbd "C-c C-s") 'sql-send-string) 1134 (define-key map (kbd "C-c C-s") 'sql-send-string)
1087 (define-key map (kbd "C-c C-b") 'sql-send-buffer) 1135 (define-key map (kbd "C-c C-b") 'sql-send-buffer)
1088 (define-key map (kbd "C-c C-i") 'sql-product-interactive) 1136 (define-key map (kbd "C-c C-i") 'sql-product-interactive)
1137 (define-key map (kbd "C-c C-l a") 'sql-list-all)
1138 (define-key map (kbd "C-c C-l t") 'sql-list-table)
1089 map) 1139 map)
1090 "Mode map used for `sql-mode'.") 1140 "Mode map used for `sql-mode'.")
1091 1141
@@ -1101,6 +1151,9 @@ Based on `comint-mode-map'.")
1101 ["Send Buffer" sql-send-buffer (sql-buffer-live-p sql-buffer)] 1151 ["Send Buffer" sql-send-buffer (sql-buffer-live-p sql-buffer)]
1102 ["Send String" sql-send-string (sql-buffer-live-p sql-buffer)] 1152 ["Send String" sql-send-string (sql-buffer-live-p sql-buffer)]
1103 "--" 1153 "--"
1154 ["List all objects" sql-list-all (sql-buffer-live-p sql-buffer)]
1155 ["List table details" sql-list-table (sql-buffer-live-p sql-buffer)]
1156 "--"
1104 ["Start SQLi session" sql-product-interactive 1157 ["Start SQLi session" sql-product-interactive
1105 :visible (not sql-connection-alist) 1158 :visible (not sql-connection-alist)
1106 :enable (sql-get-product-feature sql-product :sqli-comint-func)] 1159 :enable (sql-get-product-feature sql-product :sqli-comint-func)]
@@ -1139,7 +1192,10 @@ Based on `comint-mode-map'.")
1139 "Menu for `sql-interactive-mode'." 1192 "Menu for `sql-interactive-mode'."
1140 '("SQL" 1193 '("SQL"
1141 ["Rename Buffer" sql-rename-buffer t] 1194 ["Rename Buffer" sql-rename-buffer t]
1142 ["Save Connection" sql-save-connection (not sql-connection)])) 1195 ["Save Connection" sql-save-connection (not sql-connection)]
1196 "--"
1197 ["List all objects" sql-list-all t]
1198 ["List table details" sql-list-table t]))
1143 1199
1144;; Abbreviations -- if you want more of them, define them in your 1200;; Abbreviations -- if you want more of them, define them in your
1145;; ~/.emacs file. Abbrevs have to be enabled in your ~/.emacs, too. 1201;; ~/.emacs file. Abbrevs have to be enabled in your ~/.emacs, too.
@@ -2122,6 +2178,16 @@ highlighting rules in SQL mode.")
2122 2178
2123;;; SQL Product support functions 2179;;; SQL Product support functions
2124 2180
2181(defun sql-read-product (prompt &optional initial)
2182 "Read a valid SQL product."
2183 (let ((init (or (and initial (symbol-name initial)) "ansi")))
2184 (intern (completing-read
2185 prompt
2186 (mapcar (lambda (info) (symbol-name (car info)))
2187 sql-product-alist)
2188 nil 'require-match
2189 init 'sql-product-history init))))
2190
2125(defun sql-add-product (product display &rest plist) 2191(defun sql-add-product (product display &rest plist)
2126 "Add support for a database product in `sql-mode'. 2192 "Add support for a database product in `sql-mode'.
2127 2193
@@ -2312,10 +2378,9 @@ adds a fontification pattern to fontify identifiers ending in
2312 (mapcar 2378 (mapcar
2313 (lambda (param) 2379 (lambda (param)
2314 (let ((token (or (and (listp param) (car param)) param)) 2380 (let ((token (or (and (listp param) (car param)) param))
2315 (type (or (and (listp param) (nth 1 param)) nil)) 2381 (plist (or (and (listp param) (cdr param)) nil)))
2316 (arg (or (and (listp param) (nth 2 param)) nil)))
2317 2382
2318 (funcall body token type arg))) 2383 (funcall body token plist)))
2319 login-params))) 2384 login-params)))
2320 2385
2321 2386
@@ -2335,11 +2400,7 @@ adds a fontification pattern to fontify identifiers ending in
2335(defun sql-set-product (product) 2400(defun sql-set-product (product)
2336 "Set `sql-product' to PRODUCT and enable appropriate highlighting." 2401 "Set `sql-product' to PRODUCT and enable appropriate highlighting."
2337 (interactive 2402 (interactive
2338 (list (completing-read "SQL product: " 2403 (list (sql-read-product "SQL product: ")))
2339 (mapcar (lambda (info) (symbol-name (car info)))
2340 sql-product-alist)
2341 nil 'require-match
2342 (or (and sql-product (symbol-name sql-product)) "ansi"))))
2343 (if (stringp product) (setq product (intern product))) 2404 (if (stringp product) (setq product (intern product)))
2344 (when (not (assoc product sql-product-alist)) 2405 (when (not (assoc product sql-product-alist))
2345 (error "SQL product %s is not supported; treated as ANSI" product) 2406 (error "SQL product %s is not supported; treated as ANSI" product)
@@ -2479,37 +2540,53 @@ appended to the SQLi buffer without disturbing your SQL buffer."
2479 "Read a password using PROMPT. Optional DEFAULT is password to start with." 2540 "Read a password using PROMPT. Optional DEFAULT is password to start with."
2480 (read-passwd prompt nil default)) 2541 (read-passwd prompt nil default))
2481 2542
2482(defun sql-get-login-ext (prompt last-value history-var type arg) 2543(defun sql-get-login-ext (prompt last-value history-var plist)
2483 "Prompt user with extended login parameters. 2544 "Prompt user with extended login parameters.
2484 2545
2485If TYPE is nil, then the user is simply prompted for a string 2546If PLIST is nil, then the user is simply prompted for a string
2486value. 2547value.
2487 2548
2488If TYPE is `:file', then the user is prompted for a file 2549The property `:default' specifies the default value. If the
2489name that must match the regexp pattern specified in the ARG 2550`:number' property is non-nil then ask for a number.
2490argument.
2491 2551
2492If TYPE is `:completion', then the user is prompted for a string 2552The `:file' property prompts for a file name that must match the
2493specified by ARG. (ARG is used as the PREDICATE argument to 2553regexp pattern specified in its value.
2494`completing-read'.)"
2495 (cond
2496 ((eq type nil)
2497 (read-from-minibuffer prompt last-value nil nil history-var))
2498 2554
2499 ((eq type :file) 2555The `:completion' property prompts for a string specified by its
2500 (let ((use-dialog-box nil)) 2556value. (The property value is used as the PREDICATE argument to
2557`completing-read'.)"
2558 (let* ((default (plist-get plist :default))
2559 (prompt-def
2560 (if default
2561 (if (string-match "\\(\\):[ \t]*\\'" prompt)
2562 (replace-match (format " (default \"%s\")" default) t t prompt 1)
2563 (replace-regexp-in-string "[ \t]*\\'"
2564 (format " (default \"%s\") " default)
2565 prompt t t))
2566 prompt))
2567 (use-dialog-box nil))
2568 (cond
2569 ((plist-member plist :file)
2501 (expand-file-name 2570 (expand-file-name
2502 (read-file-name prompt 2571 (read-file-name prompt
2503 (file-name-directory last-value) nil t 2572 (file-name-directory last-value) default t
2504 (file-name-nondirectory last-value) 2573 (file-name-nondirectory last-value)
2505 (if arg 2574 (when (plist-get plist :file)
2506 `(lambda (f) 2575 `(lambda (f)
2507 (string-match (concat "\\<" ,arg "\\>") 2576 (string-match
2508 (file-name-nondirectory f))) 2577 (concat "\\<" ,(plist-get plist :file) "\\>")
2509 nil))))) 2578 (file-name-nondirectory f)))))))
2510 2579
2511 ((eq type :completion) 2580 ((plist-member plist :completion)
2512 (completing-read prompt arg nil t last-value history-var)))) 2581 (completing-read prompt-def (plist-get plist :completion) nil t
2582 last-value history-var default))
2583
2584 ((plist-get plist :number)
2585 (read-number prompt (or default last-value 0)))
2586
2587 (t
2588 (let ((r (read-from-minibuffer prompt-def last-value nil nil history-var nil)))
2589 (if (string= "" r) (or default "") r))))))
2513 2590
2514(defun sql-get-login (&rest what) 2591(defun sql-get-login (&rest what)
2515 "Get username, password and database from the user. 2592 "Get username, password and database from the user.
@@ -2528,72 +2605,69 @@ symbol `password', for the server if it contains the symbol
2528`database'. The members of WHAT are processed in the order in 2605`database'. The members of WHAT are processed in the order in
2529which they are provided. 2606which they are provided.
2530 2607
2531The tokens for `database' and `server' may also be lists to 2608Each token may also be a list with the token in the car and a
2532control or limit the values that can be supplied. These can be 2609plist of options as the cdr. The following properties are
2533of the form: 2610supported:
2534
2535 \(database :file \".+\\\\.EXT\")
2536 \(database :completion FUNCTION)
2537 2611
2538The `server' token supports the same forms. 2612 :file <filename-regexp>
2613 :completion <list-of-strings-or-function>
2614 :default <default-value>
2615 :number t
2539 2616
2540In order to ask the user for username, password and database, call the 2617In order to ask the user for username, password and database, call the
2541function like this: (sql-get-login 'user 'password 'database)." 2618function like this: (sql-get-login 'user 'password 'database)."
2542 (interactive) 2619 (interactive)
2543 (mapcar 2620 (mapcar
2544 (lambda (w) 2621 (lambda (w)
2545 (let ((token (or (and (listp w) (car w)) w)) 2622 (let ((token (or (and (consp w) (car w)) w))
2546 (type (or (and (listp w) (nth 1 w)) nil)) 2623 (plist (or (and (consp w) (cdr w)) nil)))
2547 (arg (or (and (listp w) (nth 2 w)) nil))) 2624
2548 2625 (cond
2549 (cond 2626 ((eq token 'user) ; user
2550 ((eq token 'user) ; user 2627 (setq sql-user
2551 (setq sql-user 2628 (sql-get-login-ext "User: " sql-user
2552 (read-from-minibuffer "User: " sql-user nil nil 2629 'sql-user-history plist)))
2553 'sql-user-history))) 2630
2554 2631 ((eq token 'password) ; password
2555 ((eq token 'password) ; password 2632 (setq sql-password
2556 (setq sql-password 2633 (sql-read-passwd "Password: " sql-password)))
2557 (sql-read-passwd "Password: " sql-password))) 2634
2558 2635 ((eq token 'server) ; server
2559 ((eq token 'server) ; server 2636 (setq sql-server
2560 (setq sql-server 2637 (sql-get-login-ext "Server: " sql-server
2561 (sql-get-login-ext "Server: " sql-server 2638 'sql-server-history plist)))
2562 'sql-server-history type arg))) 2639
2563 2640 ((eq token 'database) ; database
2564 ((eq token 'database) ; database 2641 (setq sql-database
2565 (setq sql-database 2642 (sql-get-login-ext "Database: " sql-database
2566 (sql-get-login-ext "Database: " sql-database 2643 'sql-database-history plist)))
2567 'sql-database-history type arg))) 2644
2568 2645 ((eq token 'port) ; port
2569 ((eq token 'port) ; port 2646 (setq sql-port
2570 (setq sql-port 2647 (sql-get-login-ext "Port: " sql-port
2571 (read-number "Port: " (if (numberp sql-port) 2648 nil (append '(:number t) plist)))))))
2572 sql-port 2649 what))
2573 0))))))) 2650
2574 what)) 2651(defun sql-find-sqli-buffer (&optional product)
2575
2576(defun sql-find-sqli-buffer ()
2577 "Returns the name of the current default SQLi buffer or nil. 2652 "Returns the name of the current default SQLi buffer or nil.
2578In order to qualify, the SQLi buffer must be alive, be in 2653In order to qualify, the SQLi buffer must be alive, be in
2579`sql-interactive-mode' and have a process." 2654`sql-interactive-mode' and have a process."
2580 (let ((default-buffer (default-value 'sql-buffer)) 2655 (let ((buf sql-buffer)
2581 (current-product sql-product)) 2656 (prod (or product sql-product)))
2582 (if (sql-buffer-live-p default-buffer) 2657 (or
2583 default-buffer 2658 ;; Current sql-buffer, if there is one.
2584 (save-current-buffer 2659 (and (sql-buffer-live-p buf prod)
2585 (let ((buflist (buffer-list)) 2660 buf)
2586 (found)) 2661 ;; Global sql-buffer
2587 (while (not (or (null buflist) 2662 (and (setq buf (default-value 'sql-buffer))
2588 found)) 2663 (sql-buffer-live-p buf prod)
2589 (let ((candidate (car buflist))) 2664 buf)
2590 (set-buffer candidate) 2665 ;; Look thru each buffer
2591 (if (and (sql-buffer-live-p candidate) 2666 (car (apply 'append
2592 (derived-mode-p 'sql-interactive-mode) 2667 (mapcar (lambda (b)
2593 (eq sql-product current-product)) 2668 (and (sql-buffer-live-p b prod)
2594 (setq found (buffer-name candidate))) 2669 (list (buffer-name b))))
2595 (setq buflist (cdr buflist)))) 2670 (buffer-list)))))))
2596 found)))))
2597 2671
2598(defun sql-set-sqli-buffer-generally () 2672(defun sql-set-sqli-buffer-generally ()
2599 "Set SQLi buffer for all SQL buffers that have none. 2673 "Set SQLi buffer for all SQL buffers that have none.
@@ -2611,10 +2685,11 @@ using `sql-find-sqli-buffer'. If `sql-buffer' is set,
2611 (let ((candidate (car buflist))) 2685 (let ((candidate (car buflist)))
2612 (set-buffer candidate) 2686 (set-buffer candidate)
2613 (if (and (derived-mode-p 'sql-mode) 2687 (if (and (derived-mode-p 'sql-mode)
2614 (not (buffer-live-p sql-buffer))) 2688 (not (sql-buffer-live-p sql-buffer)))
2615 (progn 2689 (progn
2616 (setq sql-buffer default-buffer) 2690 (setq sql-buffer default-buffer)
2617 (run-hooks 'sql-set-sqli-hook)))) 2691 (when default-buffer
2692 (run-hooks 'sql-set-sqli-hook)))))
2618 (setq buflist (cdr buflist)))))) 2693 (setq buflist (cdr buflist))))))
2619 2694
2620(defun sql-set-sqli-buffer () 2695(defun sql-set-sqli-buffer ()
@@ -2632,19 +2707,13 @@ If you call it from anywhere else, it sets the global copy of
2632 (interactive) 2707 (interactive)
2633 (let ((default-buffer (sql-find-sqli-buffer))) 2708 (let ((default-buffer (sql-find-sqli-buffer)))
2634 (if (null default-buffer) 2709 (if (null default-buffer)
2635 (error "There is no suitable SQLi buffer")) 2710 (error "There is no suitable SQLi buffer")
2636 (let ((new-buffer 2711 (let ((new-buffer (read-buffer "New SQLi buffer: " default-buffer t)))
2637 (get-buffer 2712 (if (null (sql-buffer-live-p new-buffer))
2638 (read-buffer "New SQLi buffer: " default-buffer t)))) 2713 (error "Buffer %s is not a working SQLi buffer" new-buffer)
2639 (if (null (get-buffer-process new-buffer)) 2714 (when new-buffer
2640 (error "Buffer %s has no process" (buffer-name new-buffer))) 2715 (setq sql-buffer new-buffer)
2641 (if (null (with-current-buffer new-buffer 2716 (run-hooks 'sql-set-sqli-hook)))))))
2642 (derived-mode-p 'sql-interactive-mode)))
2643 (error "Buffer %s is no SQLi buffer" (buffer-name new-buffer)))
2644 (if new-buffer
2645 (progn
2646 (setq sql-buffer (buffer-name new-buffer))
2647 (run-hooks 'sql-set-sqli-hook))))))
2648 2717
2649(defun sql-show-sqli-buffer () 2718(defun sql-show-sqli-buffer ()
2650 "Show the name of current SQLi buffer. 2719 "Show the name of current SQLi buffer.
@@ -2682,7 +2751,7 @@ server/database name."
2682 (apply 'append nil 2751 (apply 'append nil
2683 (sql-for-each-login 2752 (sql-for-each-login
2684 (sql-get-product-feature sql-product :sqli-login) 2753 (sql-get-product-feature sql-product :sqli-login)
2685 (lambda (token type arg) 2754 (lambda (token plist)
2686 (cond 2755 (cond
2687 ((eq token 'user) 2756 ((eq token 'user)
2688 (unless (string= "" sql-user) 2757 (unless (string= "" sql-user)
@@ -2694,13 +2763,13 @@ server/database name."
2694 ((eq token 'server) 2763 ((eq token 'server)
2695 (unless (string= "" sql-server) 2764 (unless (string= "" sql-server)
2696 (list "." 2765 (list "."
2697 (if (eq type :file) 2766 (if (plist-member plist :file)
2698 (file-name-nondirectory sql-server) 2767 (file-name-nondirectory sql-server)
2699 sql-server)))) 2768 sql-server))))
2700 ((eq token 'database) 2769 ((eq token 'database)
2701 (unless (string= "" sql-database) 2770 (unless (string= "" sql-database)
2702 (list "@" 2771 (list "@"
2703 (if (eq type :file) 2772 (if (plist-member plist :file)
2704 (file-name-nondirectory sql-database) 2773 (file-name-nondirectory sql-database)
2705 sql-database)))) 2774 sql-database))))
2706 2775
@@ -2742,13 +2811,13 @@ NEW-NAME is empty, then the buffer name will be \"*SQL*\"."
2742 (if (not (derived-mode-p 'sql-interactive-mode)) 2811 (if (not (derived-mode-p 'sql-interactive-mode))
2743 (message "Current buffer is not a SQL interactive buffer") 2812 (message "Current buffer is not a SQL interactive buffer")
2744 2813
2745 (cond 2814 (setq sql-alternate-buffer-name
2746 ((stringp new-name) 2815 (cond
2747 (setq sql-alternate-buffer-name new-name)) 2816 ((stringp new-name) new-name)
2748 ((listp new-name) 2817 ((consp new-name)
2749 (setq sql-alternate-buffer-name
2750 (read-string "Buffer name (\"*SQL: XXX*\"; enter `XXX'): " 2818 (read-string "Buffer name (\"*SQL: XXX*\"; enter `XXX'): "
2751 sql-alternate-buffer-name)))) 2819 sql-alternate-buffer-name))
2820 (t sql-alternate-buffer-name)))
2752 2821
2753 (rename-buffer (if (string= "" sql-alternate-buffer-name) 2822 (rename-buffer (if (string= "" sql-alternate-buffer-name)
2754 "*SQL*" 2823 "*SQL*"
@@ -2994,6 +3063,171 @@ If given the optional parameter VALUE, sets
2994 3063
2995 3064
2996 3065
3066;;; Redirect output functions
3067
3068(defun sql-redirect (command combuf &optional outbuf save-prior)
3069 "Execute the SQL command and send output to OUTBUF.
3070
3071COMBUF must be an active SQL interactive buffer. OUTBUF may be
3072an existing buffer, or the name of a non-existing buffer. If
3073omitted the output is sent to a temporary buffer which will be
3074killed after the command completes. COMMAND should be a string
3075of commands accepted by the SQLi program."
3076
3077 (with-current-buffer combuf
3078 (let ((buf (get-buffer-create (or outbuf " *SQL-Redirect*")))
3079 (proc (get-buffer-process (current-buffer)))
3080 (comint-prompt-regexp (sql-get-product-feature sql-product
3081 :prompt-regexp))
3082 (start nil))
3083 (with-current-buffer buf
3084 (toggle-read-only -1)
3085 (unless save-prior
3086 (erase-buffer))
3087 (goto-char (point-max))
3088 (unless (zerop (buffer-size))
3089 (insert "\n"))
3090 (setq start (point)))
3091
3092 ;; Run the command
3093 (message "Executing SQL command...")
3094 (comint-redirect-send-command-to-process command buf proc nil t)
3095 (while (null comint-redirect-completed)
3096 (accept-process-output nil 1))
3097 (message "Executing SQL command...done")
3098
3099 ;; Clean up the output results
3100 (with-current-buffer buf
3101 ;; Remove trailing whitespace
3102 (goto-char (point-max))
3103 (when (looking-back "[ \t\f\n\r]*" start)
3104 (delete-region (match-beginning 0) (match-end 0)))
3105 ;; Remove echo if there was one
3106 (goto-char start)
3107 (when (looking-at (concat "^" (regexp-quote command) "[\\n]"))
3108 (delete-region (match-beginning 0) (match-end 0)))
3109 (goto-char start)))))
3110
3111(defun sql-redirect-value (command combuf regexp &optional regexp-groups)
3112 "Execute the SQL command and return part of result.
3113
3114COMBUF must be an active SQL interactive buffer. COMMAND should
3115be a string of commands accepted by the SQLi program. From the
3116output, the REGEXP is repeatedly matched and the list of
3117REGEXP-GROUPS submatches is returned. This behaves much like
3118\\[comint-redirect-results-list-from-process] but instead of
3119returning a single submatch it returns a list of each submatch
3120for each match."
3121
3122 (let ((outbuf " *SQL-Redirect-values*")
3123 (results nil))
3124 (sql-redirect command combuf outbuf nil)
3125 (with-current-buffer outbuf
3126 (while (re-search-forward regexp nil t)
3127 (push
3128 (cond
3129 ;; no groups-return all of them
3130 ((null regexp-groups)
3131 (let ((i 1)
3132 (r nil))
3133 (while (match-beginning i)
3134 (push (match-string i) r))
3135 (nreverse r)))
3136 ;; one group specified
3137 ((numberp regexp-groups)
3138 (match-string regexp-groups))
3139 ;; list of numbers; return the specified matches only
3140 ((consp regexp-groups)
3141 (mapcar (lambda (c)
3142 (cond
3143 ((numberp c) (match-string c))
3144 ((stringp c) (match-substitute-replacement c))
3145 (t (error "sql-redirect-value: unknown REGEXP-GROUPS value - %s" c))))
3146 regexp-groups))
3147 ;; String is specified; return replacement string
3148 ((stringp regexp-groups)
3149 (match-substitute-replacement regexp-groups))
3150 (t
3151 (error "sql-redirect-value: unknown REGEXP-GROUPS value - %s"
3152 regexp-groups)))
3153 results)))
3154 (nreverse results)))
3155
3156(defun sql-execute (sqlbuf outbuf command arg)
3157 "Executes a command in a SQL interacive buffer and captures the output.
3158
3159The commands are run in SQLBUF and the output saved in OUTBUF.
3160COMMAND must be a string, a function or a list of such elements.
3161Functions are called with SQLBUF, OUTBUF and ARG as parameters;
3162strings are formatted with ARG and executed.
3163
3164If the results are empty the OUTBUF is deleted, otherwise the
3165buffer is popped into a view window. "
3166 (mapc
3167 (lambda (c)
3168 (cond
3169 ((stringp c)
3170 (sql-redirect (if arg (format c arg) c) sqlbuf outbuf) t)
3171 ((functionp c)
3172 (apply c sqlbuf outbuf arg))
3173 (t (error "Unknown sql-execute item %s" c))))
3174 (if (consp command) command (cons command nil)))
3175
3176 (setq outbuf (get-buffer outbuf))
3177 (if (zerop (buffer-size outbuf))
3178 (kill-buffer outbuf)
3179 (let ((one-win (eq (selected-window)
3180 (get-lru-window))))
3181 (with-current-buffer outbuf
3182 (set-buffer-modified-p nil)
3183 (toggle-read-only 1))
3184 (view-buffer-other-window outbuf)
3185 (when one-win
3186 (shrink-window-if-larger-than-buffer)))))
3187
3188(defun sql-execute-feature (sqlbuf outbuf feature enhanced arg)
3189 "List objects or details in a separate display buffer."
3190 (let (command)
3191 (with-current-buffer sqlbuf
3192 (setq command (sql-get-product-feature sql-product feature)))
3193 (unless command
3194 (error "%s does not support %s" sql-product feature))
3195 (when (consp command)
3196 (setq command (if enhanced
3197 (cdr command)
3198 (car command))))
3199 (sql-execute sqlbuf outbuf command arg)))
3200
3201(defun sql-read-table-name (prompt)
3202 "Read the name of a database table."
3203 ;; TODO: Fetch table/view names from database and provide completion.
3204 ;; Also implement thing-at-point if the buffer has valid names in it
3205 ;; (i.e. sql-mode, sql-interactive-mode, or sql-list-all buffers)
3206 (read-from-minibuffer prompt))
3207
3208(defun sql-list-all (&optional enhanced)
3209 "List all database objects."
3210 (interactive "P")
3211 (let ((sqlbuf (sql-find-sqli-buffer)))
3212 (unless sqlbuf
3213 (error "No SQL interactive buffer found"))
3214 (sql-execute-feature sqlbuf "*List All*" :list-all enhanced nil)))
3215
3216(defun sql-list-table (name &optional enhanced)
3217 "List the details of a database table. "
3218 (interactive
3219 (list (sql-read-table-name "Table name: ")
3220 current-prefix-arg))
3221 (let ((sqlbuf (sql-find-sqli-buffer)))
3222 (unless sqlbuf
3223 (error "No SQL interactive buffer found"))
3224 (unless name
3225 (error "No table name specified"))
3226 (sql-execute-feature sqlbuf (format "*List %s*" name)
3227 :list-table enhanced name)))
3228
3229
3230
2997;;; SQL mode -- uses SQL interactive mode 3231;;; SQL mode -- uses SQL interactive mode
2998 3232
2999;;;###autoload 3233;;;###autoload
@@ -3221,6 +3455,14 @@ Sentinels will always get the two parameters PROCESS and EVENT."
3221 3455
3222;;; Connection handling 3456;;; Connection handling
3223 3457
3458(defun sql-read-connection (prompt &optional initial default)
3459 "Read a connection name."
3460 (let ((completion-ignore-case t))
3461 (completing-read prompt
3462 (mapcar (lambda (c) (car c))
3463 sql-connection-alist)
3464 nil t initial 'sql-connection-history default)))
3465
3224;;;###autoload 3466;;;###autoload
3225(defun sql-connect (connection) 3467(defun sql-connect (connection)
3226 "Connect to an interactive session using CONNECTION settings. 3468 "Connect to an interactive session using CONNECTION settings.
@@ -3234,12 +3476,7 @@ is specified in the connection settings."
3234 ;; Prompt for the connection from those defined in the alist 3476 ;; Prompt for the connection from those defined in the alist
3235 (interactive 3477 (interactive
3236 (if sql-connection-alist 3478 (if sql-connection-alist
3237 (list 3479 (list (sql-read-connection "Connection: " nil '(nil)))
3238 (let ((completion-ignore-case t))
3239 (completing-read "Connection: "
3240 (mapcar (lambda (c) (car c))
3241 sql-connection-alist)
3242 nil t nil nil '(()))))
3243 nil)) 3480 nil))
3244 3481
3245 ;; Are there connections defined 3482 ;; Are there connections defined
@@ -3273,10 +3510,10 @@ is specified in the connection settings."
3273 ;; the remaining params (w/o the connection params) 3510 ;; the remaining params (w/o the connection params)
3274 (rem-params (sql-for-each-login 3511 (rem-params (sql-for-each-login
3275 login-params 3512 login-params
3276 (lambda (token type arg) 3513 (lambda (token plist)
3277 (unless (member token set-params) 3514 (unless (member token set-params)
3278 (if (or type arg) 3515 (if plist
3279 (list token type arg) 3516 (cons token plist)
3280 token))))) 3517 token)))))
3281 ;; Remember the connection 3518 ;; Remember the connection
3282 (sql-connection connection)) 3519 (sql-connection connection))
@@ -3317,7 +3554,7 @@ optionally is saved to the user's init file."
3317 (append (list name) 3554 (append (list name)
3318 (sql-for-each-login 3555 (sql-for-each-login
3319 `(product ,@login) 3556 `(product ,@login)
3320 (lambda (token type arg) 3557 (lambda (token plist)
3321 (cond 3558 (cond
3322 ((eq token 'product) `(sql-product ',sql-product)) 3559 ((eq token 'product) `(sql-product ',sql-product))
3323 ((eq token 'user) `(sql-user ,sql-user)) 3560 ((eq token 'user) `(sql-user ,sql-user))
@@ -3365,10 +3602,10 @@ the call to \\[sql-product-interactive] with
3365 3602
3366 ;; Handle universal arguments if specified 3603 ;; Handle universal arguments if specified
3367 (when (not (or executing-kbd-macro noninteractive)) 3604 (when (not (or executing-kbd-macro noninteractive))
3368 (when (and (listp product) 3605 (when (and (consp product)
3369 (not (cdr product)) 3606 (not (cdr product))
3370 (numberp (car product))) 3607 (numberp (car product)))
3371 (when (>= (car product) 16) 3608 (when (>= (prefix-numeric-value product) 16)
3372 (when (not new-name) 3609 (when (not new-name)
3373 (setq new-name '(4))) 3610 (setq new-name '(4)))
3374 (setq product '(4))))) 3611 (setq product '(4)))))
@@ -3376,61 +3613,53 @@ the call to \\[sql-product-interactive] with
3376 ;; Get the value of product that we need 3613 ;; Get the value of product that we need
3377 (setq product 3614 (setq product
3378 (cond 3615 (cond
3379 ((equal product '(4)) ; C-u, prompt for product
3380 (intern (completing-read "SQL product: "
3381 (mapcar (lambda (info) (symbol-name (car info)))
3382 sql-product-alist)
3383 nil 'require-match
3384 (or (and sql-product
3385 (symbol-name sql-product))
3386 "ansi"))))
3387 ((and product ; Product specified 3616 ((and product ; Product specified
3388 (symbolp product)) product) 3617 (symbolp product)) product)
3618 ((= (prefix-numeric-value product) 4) ; C-u, prompt for product
3619 (sql-read-product "SQL product: " sql-product))
3389 (t sql-product))) ; Default to sql-product 3620 (t sql-product))) ; Default to sql-product
3390 3621
3391 ;; If we have a product and it has a interactive mode 3622 ;; If we have a product and it has a interactive mode
3392 (if product 3623 (if product
3393 (when (sql-get-product-feature product :sqli-comint-func) 3624 (when (sql-get-product-feature product :sqli-comint-func)
3394 ;; If no new name specified, fall back on sql-buffer if its for 3625 ;; If no new name specified, try to pop to an active SQL
3395 ;; the same product 3626 ;; interactive for the same product
3396 (if (and (not new-name) 3627 (let ((buf (sql-find-sqli-buffer product)))
3397 sql-buffer 3628 (if (and (not new-name) buf)
3398 (sql-buffer-live-p sql-buffer) 3629 (pop-to-buffer buf)
3399 (comint-check-proc sql-buffer) 3630
3400 (eq product (with-current-buffer sql-buffer sql-product))) 3631 ;; We have a new name or sql-buffer doesn't exist or match
3401 (pop-to-buffer sql-buffer) 3632 ;; Start by remembering where we start
3402 3633 (let ((start-buffer (current-buffer))
3403 ;; We have a new name or sql-buffer doesn't exist or match 3634 new-sqli-buffer)
3404 ;; Start by remembering where we start 3635
3405 (let* ((start-buffer (current-buffer)) 3636 ;; Get credentials.
3406 new-sqli-buffer) 3637 (apply 'sql-get-login (sql-get-product-feature product :sqli-login))
3407 3638
3408 ;; Get credentials. 3639 ;; Connect to database.
3409 (apply 'sql-get-login (sql-get-product-feature product :sqli-login)) 3640 (message "Login...")
3410 3641 (funcall (sql-get-product-feature product :sqli-comint-func)
3411 ;; Connect to database. 3642 product
3412 (message "Login...") 3643 (sql-get-product-feature product :sqli-options))
3413 (funcall (sql-get-product-feature product :sqli-comint-func) 3644
3414 product 3645 ;; Set SQLi mode.
3415 (sql-get-product-feature product :sqli-options)) 3646 (setq new-sqli-buffer (current-buffer))
3416 3647 (let ((sql-interactive-product product))
3417 ;; Set SQLi mode. 3648 (sql-interactive-mode))
3418 (setq new-sqli-buffer (current-buffer)) 3649
3419 (let ((sql-interactive-product product)) 3650 ;; Set the new buffer name
3420 (sql-interactive-mode)) 3651 (when new-name
3421 3652 (sql-rename-buffer new-name))
3422 ;; Set the new buffer name 3653
3423 (when new-name 3654 ;; Set `sql-buffer' in the new buffer and the start buffer
3424 (sql-rename-buffer new-name)) 3655 (setq sql-buffer (buffer-name new-sqli-buffer))
3425 3656 (with-current-buffer start-buffer
3426 ;; Set `sql-buffer' in the start buffer 3657 (setq sql-buffer (buffer-name new-sqli-buffer))
3427 (setq sql-buffer (buffer-name new-sqli-buffer)) 3658 (run-hooks 'sql-set-sqli-hook))
3428 (with-current-buffer start-buffer 3659
3429 (setq sql-buffer (buffer-name new-sqli-buffer))) 3660 ;; All done.
3430 3661 (message "Login...done")
3431 ;; All done. 3662 (pop-to-buffer sql-buffer)))))
3432 (message "Login...done")
3433 (pop-to-buffer sql-buffer))))
3434 (message "No default SQL product defined. Set `sql-product'."))) 3663 (message "No default SQL product defined. Set `sql-product'.")))
3435 3664
3436(defun sql-comint (product params) 3665(defun sql-comint (product params)
@@ -3440,14 +3669,17 @@ PRODUCT is the SQL product. PARAMS is a list of strings which are
3440passed as command line arguments." 3669passed as command line arguments."
3441 (let ((program (sql-get-product-feature product :sqli-program)) 3670 (let ((program (sql-get-product-feature product :sqli-program))
3442 (buf-name "SQL")) 3671 (buf-name "SQL"))
3672 ;; make sure we can find the program
3673 (unless (executable-find program)
3674 (error "Unable to locate SQL program \'%s\'" program))
3443 ;; Make sure buffer name is unique 3675 ;; Make sure buffer name is unique
3444 (when (get-buffer (format "*%s*" buf-name)) 3676 (when (sql-buffer-live-p (format "*%s*" buf-name))
3445 (setq buf-name (format "SQL-%s" product)) 3677 (setq buf-name (format "SQL-%s" product))
3446 (when (get-buffer (format "*%s*" buf-name)) 3678 (when (sql-buffer-live-p (format "*%s*" buf-name))
3447 (let ((i 1)) 3679 (let ((i 1))
3448 (while (get-buffer (format "*%s*" 3680 (while (sql-buffer-live-p
3449 (setq buf-name 3681 (format "*%s*"
3450 (format "SQL-%s%d" product i)))) 3682 (setq buf-name (format "SQL-%s%d" product i))))
3451 (setq i (1+ i)))))) 3683 (setq i (1+ i))))))
3452 (set-buffer 3684 (set-buffer
3453 (apply 'make-comint buf-name program nil params)))) 3685 (apply 'make-comint buf-name program nil params))))
@@ -3890,6 +4122,8 @@ Try to set `comint-output-filter-functions' like this:
3890 (setq params (append (list "-h" sql-server) params))) 4122 (setq params (append (list "-h" sql-server) params)))
3891 (if (not (string= "" sql-user)) 4123 (if (not (string= "" sql-user))
3892 (setq params (append (list "-U" sql-user) params))) 4124 (setq params (append (list "-U" sql-user) params)))
4125 (if (not (= 0 sql-port))
4126 (setq params (append (list "-p" sql-port) params)))
3893 (sql-comint product params))) 4127 (sql-comint product params)))
3894 4128
3895 4129