X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/60dd06a08276422871cd3d491a44d10d4bdc690c..58635e4de85621d4f16befe15b1df44a637bd078:/lisp/progmodes/sql.el diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el index e4df102f54..1c1ffc4162 100644 --- a/lisp/progmodes/sql.el +++ b/lisp/progmodes/sql.el @@ -1,13 +1,12 @@ ;;; sql.el --- specialized comint.el for SQL interpreters -;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, -;; 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +;; Copyright (C) 1998-2011 Free Software Foundation, Inc. ;; Author: Alex Schroeder ;; Maintainer: Michael Mauger -;; Version: 2.1 +;; Version: 2.8 ;; Keywords: comm languages processes -;; URL: http://savannah.gnu.org/cgi-bin/viewcvs/emacs/emacs/lisp/progmodes/sql.el +;; URL: http://savannah.gnu.org/projects/emacs/ ;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?SqlMode ;; This file is part of GNU Emacs. @@ -152,11 +151,7 @@ ;; (defcustom my-sql-xyz-login-params '(user password server database) ;; "Login parameters to needed to connect to XyzDB." -;; :type '(repeat (choice -;; (const user) -;; (const password) -;; (const server) -;; (const database))) +;; :type 'sql-login-params ;; :group 'SQL) ;; ;; (sql-set-product-feature 'xyz @@ -170,7 +165,7 @@ ;; (sql-set-product-feature 'xyz ;; :sqli-options 'my-sql-xyz-options)) -;; (defun my-sql-connect-xyz (product options) +;; (defun my-sql-comint-xyz (product options) ;; "Connect ti XyzDB in a comint buffer." ;; ;; ;; Do something with `sql-user', `sql-password', @@ -184,17 +179,17 @@ ;; (setq params (append (list "-P" sql-password) params))) ;; (if (not (string= "" sql-user)) ;; (setq params (append (list "-U" sql-user) params))) -;; (sql-connect product params))) +;; (sql-comint product params))) ;; ;; (sql-set-product-feature 'xyz -;; :sqli-connect-func 'my-sql-connect-xyz) +;; :sqli-comint-func 'my-sql-comint-xyz) ;; 6) Define a convienence function to invoke the SQL interpreter. -;; (defun my-sql-xyz () +;; (defun my-sql-xyz (&optional buffer) ;; "Run ixyz by XyzDB as an inferior process." -;; (interactive) -;; (sql-product-interactive 'xyz)) +;; (interactive "P") +;; (sql-product-interactive 'xyz buffer)) ;;; To Do: @@ -236,7 +231,7 @@ (require 'regexp-opt)) (require 'custom) (eval-when-compile ;; needed in Emacs 19, 20 - (setq max-specpdl-size 2000)) + (setq max-specpdl-size (max max-specpdl-size 2000))) (defvar font-lock-keyword-face) (defvar font-lock-set-defaults) @@ -255,8 +250,8 @@ (defcustom sql-user "" "Default username." :type 'string - :group 'SQL) -(put 'sql-user 'safe-local-variable 'stringp) + :group 'SQL + :safe 'stringp) (defcustom sql-password "" "Default password. @@ -264,32 +259,71 @@ Storing your password in a textfile such as ~/.emacs could be dangerous. Customizing your password will store it in your ~/.emacs file." :type 'string - :group 'SQL) -(put 'sql-password 'risky-local-variable t) + :group 'SQL + :risky t) (defcustom sql-database "" "Default database." :type 'string - :group 'SQL) -(put 'sql-database 'safe-local-variable 'stringp) + :group 'SQL + :safe 'stringp) (defcustom sql-server "" "Default server or host." :type 'string - :group 'SQL) -(put 'sql-server 'safe-local-variable 'stringp) + :group 'SQL + :safe 'stringp) -(defcustom sql-port nil - "Default server or host." +(defcustom sql-port 0 + "Default port." + :version "24.1" :type 'number - :group 'SQL) -(put 'sql-port 'safe-local-variable 'numberp) + :group 'SQL + :safe 'numberp) + +;; Login parameter type + +(define-widget 'sql-login-params 'lazy + "Widget definition of the login parameters list" + ;; FIXME: does not implement :default property for the user, + ;; database and server options. Anybody have some guidance on how to + ;; do this. + :tag "Login Parameters" + :type '(repeat (choice + (const user) + (const password) + (choice :tag "server" + (const server) + (list :tag "file" + (const :format "" server) + (const :format "" :file) + regexp) + (list :tag "completion" + (const :format "" server) + (const :format "" :completion) + (restricted-sexp + :match-alternatives (listp stringp)))) + (choice :tag "database" + (const database) + (list :tag "file" + (const :format "" database) + (const :format "" :file) + regexp) + (list :tag "completion" + (const :format "" database) + (const :format "" :completion) + (restricted-sexp + :match-alternatives (listp stringp)))) + (const port)))) ;; SQL Product support (defvar sql-interactive-product nil "Product under `sql-interactive-mode'.") +(defvar sql-connection nil + "Connection name if interactive session started by `sql-connect'.") + (defvar sql-product-alist '((ansi :name "ANSI" @@ -301,9 +335,10 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-db2-program :sqli-options sql-db2-options :sqli-login sql-db2-login-params - :sqli-connect-func sql-connect-db2 + :sqli-comint-func sql-comint-db2 :prompt-regexp "^db2 => " :prompt-length 7 + :prompt-cont-regexp "^db2 (cont\.) => " :input-filter sql-escape-newlines-filter) (informix @@ -312,7 +347,7 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-informix-program :sqli-options sql-informix-options :sqli-login sql-informix-login-params - :sqli-connect-func sql-connect-informix + :sqli-comint-func sql-comint-informix :prompt-regexp "^> " :prompt-length 2 :syntax-alist ((?{ . "<") (?} . ">"))) @@ -323,9 +358,10 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-ingres-program :sqli-options sql-ingres-options :sqli-login sql-ingres-login-params - :sqli-connect-func sql-connect-ingres + :sqli-comint-func sql-comint-ingres :prompt-regexp "^\* " - :prompt-length 2) + :prompt-length 2 + :prompt-cont-regexp "^\* ") (interbase :name "Interbase" @@ -333,7 +369,7 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-interbase-program :sqli-options sql-interbase-options :sqli-login sql-interbase-login-params - :sqli-connect-func sql-connect-interbase + :sqli-comint-func sql-comint-interbase :prompt-regexp "^SQL> " :prompt-length 5) @@ -343,7 +379,7 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-linter-program :sqli-options sql-linter-options :sqli-login sql-linter-login-params - :sqli-connect-func sql-connect-linter + :sqli-comint-func sql-comint-linter :prompt-regexp "^SQL>" :prompt-length 4) @@ -353,7 +389,7 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-ms-program :sqli-options sql-ms-options :sqli-login sql-ms-login-params - :sqli-connect-func sql-connect-ms + :sqli-comint-func sql-comint-ms :prompt-regexp "^[0-9]*>" :prompt-length 5 :syntax-alist ((?@ . "w")) @@ -366,9 +402,12 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-mysql-program :sqli-options sql-mysql-options :sqli-login sql-mysql-login-params - :sqli-connect-func sql-connect-mysql + :sqli-comint-func sql-comint-mysql + :list-all "SHOW TABLES;" + :list-table "DESCRIBE %s;" :prompt-regexp "^mysql> " :prompt-length 6 + :prompt-cont-regexp "^ -> " :input-filter sql-remove-tabs-filter) (oracle @@ -377,9 +416,10 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-oracle-program :sqli-options sql-oracle-options :sqli-login sql-oracle-login-params - :sqli-connect-func sql-connect-oracle + :sqli-comint-func sql-comint-oracle :prompt-regexp "^SQL> " :prompt-length 5 + :prompt-cont-regexp "^\\s-*\\d+> " :syntax-alist ((?$ . "w") (?# . "w")) :terminator ("\\(^/\\|;\\)" . "/") :input-filter sql-placeholders-filter) @@ -391,11 +431,14 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-postgres-program :sqli-options sql-postgres-options :sqli-login sql-postgres-login-params - :sqli-connect-func sql-connect-postgres - :prompt-regexp "^.*[#>] *" + :sqli-comint-func sql-comint-postgres + :list-all ("\\d+" . "\\dS+") + :list-table ("\\d+ %s" . "\\dS+ %s") + :prompt-regexp "^.*=[#>] " :prompt-length 5 + :prompt-cont-regexp "^.*[-(][#>] " :input-filter sql-remove-tabs-filter - :terminator ("\\(^[\\]g\\|;\\)" . ";")) + :terminator ("\\(^\\s-*\\\\g\\|;\\)" . ";")) (solid :name "Solid" @@ -403,7 +446,7 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-solid-program :sqli-options sql-solid-options :sqli-login sql-solid-login-params - :sqli-connect-func sql-connect-solid + :sqli-comint-func sql-comint-solid :prompt-regexp "^" :prompt-length 0) @@ -414,9 +457,13 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-sqlite-program :sqli-options sql-sqlite-options :sqli-login sql-sqlite-login-params - :sqli-connect-func sql-connect-sqlite + :sqli-comint-func sql-comint-sqlite + :list-all ".tables" + :list-table ".schema %s" :prompt-regexp "^sqlite> " - :prompt-length 8) + :prompt-length 8 + :prompt-cont-regexp "^ ...> " + :terminator ";") (sybase :name "Sybase" @@ -424,7 +471,7 @@ Customizing your password will store it in your ~/.emacs file." :sqli-program sql-sybase-program :sqli-options sql-sybase-options :sqli-login sql-sybase-login-params - :sqli-connect-func sql-connect-sybase + :sqli-comint-func sql-comint-sybase :prompt-regexp "^SQL> " :prompt-length 5 :syntax-alist ((?@ . "w")) @@ -463,7 +510,7 @@ may be any one of the following: database and server) needed to connect to the database. - :sqli-connect-func name of a function which accepts no + :sqli-comint-func name of a function which accepts no parameters that will use the values of `sql-user', `sql-password', `sql-database' and `sql-server' to open a @@ -471,12 +518,33 @@ may be any one of the following: database. Do product specific configuration of comint in this function. + :list-all Command string or function which produces + a listing of all objects in the database. + If it's a cons cell, then the car + produces the standard list of objects and + the cdr produces an enhanced list of + objects. What \"enhanced\" means is + dependent on the SQL product and may not + exist. In general though, the + \"enhanced\" list should include visible + objects from other schemas. + + :list-table Command string or function which produces + a detailed listing of a specific database + table. If its a cons cell, then the car + produces the standard list and the cdr + produces an enhanced list. + :prompt-regexp regular expression string that matches the prompt issued by the product interpreter. :prompt-length length of the prompt on the line. + :prompt-cont-regexp regular expression string that matches + the continuation prompt issued by the + product interpreter. + :input-filter function which can filter strings sent to the command interpreter. It is also used by the `sql-send-string', @@ -484,7 +552,8 @@ may be any one of the following: and `sql-send-buffer' functions. The function is passed the string sent to the command interpreter and must return the - filtered string. + filtered string. May also be a list of + such functions. :terminator the terminator to be sent after a `sql-send-string', `sql-send-region', @@ -507,7 +576,54 @@ settings.") (defvar sql-indirect-features '(:font-lock :sqli-program :sqli-options :sqli-login)) -;;;###autoload +(defcustom sql-connection-alist nil + "An alist of connection parameters for interacting with a SQL + product. + +Each element of the alist is as follows: + + \(CONNECTION \(SQL-VARIABLE VALUE) ...) + +Where CONNECTION is a symbol identifying the connection, SQL-VARIABLE +is the symbol name of a SQL mode variable, and VALUE is the value to +be assigned to the variable. + +The most common SQL-VARIABLE settings associated with a connection +are: + + `sql-product' + `sql-user' + `sql-password' + `sql-port' + `sql-server' + `sql-database' + +If a SQL-VARIABLE is part of the connection, it will not be +prompted for during login." + + :type `(alist :key-type (string :tag "Connection") + :value-type + (set + (group (const :tag "Product" sql-product) + (choice + ,@(mapcar (lambda (prod-info) + `(const :tag + ,(or (plist-get (cdr prod-info) :name) + (capitalize (symbol-name (car prod-info)))) + (quote ,(car prod-info)))) + sql-product-alist))) + (group (const :tag "Username" sql-user) string) + (group (const :tag "Password" sql-password) string) + (group (const :tag "Server" sql-server) string) + (group (const :tag "Database" sql-database) string) + (group (const :tag "Port" sql-port) integer) + (repeat :inline t + (list :tab "Other" + (symbol :tag " Variable Symbol") + (sexp :tag "Value Expression"))))) + :version "24.1" + :group 'SQL) + (defcustom sql-product 'ansi "Select the SQL database product used so that buffers can be highlighted properly when you open them." @@ -518,11 +634,9 @@ highlighted properly when you open them." (capitalize (symbol-name (car prod-info)))) ,(car prod-info))) sql-product-alist)) - :group 'SQL) -(put 'sql-product 'safe-local-variable 'symbolp) - -(defvar sql-interactive-product nil - "Product under `sql-interactive-mode'.") + :group 'SQL + :safe 'symbolp) +(defvaralias 'sql-dialect 'sql-product) ;; misc customization of sql.el behaviour @@ -677,11 +791,7 @@ You will find the file in your Orant\\bin directory." (defcustom sql-oracle-login-params '(user password database) "List of login parameters needed to connect to Oracle." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -702,7 +812,9 @@ to be safe: ;; Customization for SQLite -(defcustom sql-sqlite-program "sqlite" +(defcustom sql-sqlite-program (or (executable-find "sqlite3") + (executable-find "sqlite") + "sqlite") "Command to start SQLite. Starts `sql-interactive-mode' after doing some setup." @@ -715,13 +827,9 @@ Starts `sql-interactive-mode' after doing some setup." :version "20.8" :group 'SQL) -(defcustom sql-sqlite-login-params '(database) +(defcustom sql-sqlite-login-params '((database :file ".*\\.\\(db\\|sqlite[23]?\\)")) "List of login parameters needed to connect to SQLite." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -744,12 +852,7 @@ on Windows: \"-C\" \"-t\" \"-f\" \"-n\"." (defcustom sql-mysql-login-params '(user password database server) "List of login parameters needed to connect to MySql." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database) - (const port))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -764,11 +867,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-solid-login-params '(user password server) "List of login parameters needed to connect to Solid." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -790,11 +889,7 @@ Some versions of isql might require the -n option in order to work." (defcustom sql-sybase-login-params '(server user password database) "List of login parameters needed to connect to Sybase." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -809,11 +904,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-informix-login-params '(database) "List of login parameters needed to connect to Informix." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -828,11 +919,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-ingres-login-params '(database) "List of login parameters needed to connect to Ingres." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -854,11 +941,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-ms-login-params '(user password server database) "List of login parameters needed to connect to Microsoft." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -883,13 +966,11 @@ add your name with a \"-U\" prefix (such as \"-Umark\") to the list." :version "20.8" :group 'SQL) -(defcustom sql-postgres-login-params '(user database server) +(defcustom sql-postgres-login-params `((user :default ,(user-login-name)) + (database :default ,(user-login-name)) + server) "List of login parameters needed to connect to Postgres." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -910,11 +991,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-interbase-login-params '(user password database) "List of login parameters needed to connect to Interbase." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -935,11 +1012,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-db2-login-params nil "List of login parameters needed to connect to DB2." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -960,11 +1033,7 @@ Starts `sql-interactive-mode' after doing some setup." (defcustom sql-linter-login-params '(user password database server) "Login parameters to needed to connect to Linter." - :type '(repeat (choice - (const user) - (const password) - (const server) - (const database))) + :type 'sql-login-params :version "24.1" :group 'SQL) @@ -981,11 +1050,14 @@ Starts `sql-interactive-mode' after doing some setup." (defvar sql-server-history nil "History of servers used.") -(defvar sql-port-history nil - "History of ports used.") - ;; Passwords are not kept in a history. +(defvar sql-product-history nil + "History of products used.") + +(defvar sql-connection-history nil + "History of connections used.") + (defvar sql-buffer nil "Current SQLi buffer. @@ -1005,11 +1077,33 @@ You can change `sql-prompt-regexp' on `sql-interactive-mode-hook'.") You can change `sql-prompt-length' on `sql-interactive-mode-hook'.") +(defvar sql-prompt-cont-regexp nil + "Prompt pattern of statement continuation prompts.") + (defvar sql-alternate-buffer-name nil "Buffer-local string used to possibly rename the SQLi buffer. Used by `sql-rename-buffer'.") +(defun sql-buffer-live-p (buffer &optional product) + "Returns non-nil if the process associated with buffer is live. + +BUFFER can be a buffer object or a buffer name. The buffer must +be a live buffer, have an running process attached to it, be in +`sql-interactive-mode', and, if PRODUCT is specified, it's +`sql-product' must match." + + (when buffer + (setq buffer (get-buffer buffer)) + (and buffer + (buffer-live-p buffer) + (get-buffer-process buffer) + (comint-check-proc buffer) + (with-current-buffer buffer + (and (derived-mode-p 'sql-interactive-mode) + (or (not product) + (eq product sql-product))))))) + ;; Keymap for sql-interactive-mode. (defvar sql-interactive-mode-map @@ -1025,6 +1119,8 @@ Used by `sql-rename-buffer'.") (define-key map (kbd "O") 'sql-magic-go) (define-key map (kbd "o") 'sql-magic-go) (define-key map (kbd ";") 'sql-magic-semicolon) + (define-key map (kbd "C-c C-l a") 'sql-list-all) + (define-key map (kbd "C-c C-l t") 'sql-list-table) map) "Mode map used for `sql-interactive-mode'. Based on `comint-mode-map'.") @@ -1038,6 +1134,8 @@ Based on `comint-mode-map'.") (define-key map (kbd "C-c C-s") 'sql-send-string) (define-key map (kbd "C-c C-b") 'sql-send-buffer) (define-key map (kbd "C-c C-i") 'sql-product-interactive) + (define-key map (kbd "C-c C-l a") 'sql-list-all) + (define-key map (kbd "C-c C-l t") 'sql-list-table) map) "Mode map used for `sql-mode'.") @@ -1047,17 +1145,25 @@ Based on `comint-mode-map'.") sql-mode-menu sql-mode-map "Menu for `sql-mode'." `("SQL" - ["Send Paragraph" sql-send-paragraph (and (buffer-live-p sql-buffer) - (get-buffer-process sql-buffer))] + ["Send Paragraph" sql-send-paragraph (sql-buffer-live-p sql-buffer)] ["Send Region" sql-send-region (and mark-active - (buffer-live-p sql-buffer) - (get-buffer-process sql-buffer))] - ["Send Buffer" sql-send-buffer (and (buffer-live-p sql-buffer) - (get-buffer-process sql-buffer))] - ["Send String" sql-send-string (and (buffer-live-p sql-buffer) - (get-buffer-process sql-buffer))] - ["--" nil nil] - ["Start SQLi session" sql-product-interactive (sql-get-product-feature sql-product :sqli-connect-func)] + (sql-buffer-live-p sql-buffer))] + ["Send Buffer" sql-send-buffer (sql-buffer-live-p sql-buffer)] + ["Send String" sql-send-string (sql-buffer-live-p sql-buffer)] + "--" + ["List all objects" sql-list-all (sql-buffer-live-p sql-buffer)] + ["List table details" sql-list-table (sql-buffer-live-p sql-buffer)] + "--" + ["Start SQLi session" sql-product-interactive + :visible (not sql-connection-alist) + :enable (sql-get-product-feature sql-product :sqli-comint-func)] + ("Start..." + :visible sql-connection-alist + :filter sql-connection-menu-filter + "--" + ["New SQLi Session" sql-product-interactive (sql-get-product-feature sql-product :sqli-comint-func)]) + ["--" + :visible sql-connection-alist] ["Show SQLi buffer" sql-show-sqli-buffer t] ["Set SQLi buffer" sql-set-sqli-buffer t] ["Pop to SQLi buffer after send" @@ -1085,7 +1191,11 @@ Based on `comint-mode-map'.") sql-interactive-mode-menu sql-interactive-mode-map "Menu for `sql-interactive-mode'." '("SQL" - ["Rename Buffer" sql-rename-buffer t])) + ["Rename Buffer" sql-rename-buffer t] + ["Save Connection" sql-save-connection (not sql-connection)] + "--" + ["List all objects" sql-list-all t] + ["List table details" sql-list-table t])) ;; Abbreviations -- if you want more of them, define them in your ;; ~/.emacs file. Abbrevs have to be enabled in your ~/.emacs, too. @@ -1310,7 +1420,7 @@ to add functions and PL/SQL keywords.") ;; Oracle SQL*Plus Commands (cons (concat - "^\\(?:\\(?:" (regexp-opt '( + "^\\s-*\\(?:\\(?:" (regexp-opt '( "@" "@@" "accept" "append" "archive" "attribute" "break" "btitle" "change" "clear" "column" "connect" "copy" "define" "del" "describe" "disconnect" "edit" "execute" "exit" "get" "help" @@ -1349,7 +1459,7 @@ to add functions and PL/SQL keywords.") "\\)\\b.*" ) 'font-lock-doc-face) - '("^[ \t]*rem\\(?:ark\\)?.*" . font-lock-comment-face) + '("^\\s-*rem\\(?:ark\\)?\\>.*" . font-lock-comment-face) ;; Oracle Functions (sql-font-lock-keywords-builder 'font-lock-builtin-face nil @@ -1531,81 +1641,153 @@ to add functions and PL/SQL keywords.") (defvar sql-mode-postgres-font-lock-keywords (eval-when-compile (list - ;; Postgres Functions + ;; Postgres psql commands + '("^\\s-*\\\\.*$" . font-lock-doc-face) + + ;; Postgres unreserved words but may have meaning + (sql-font-lock-keywords-builder 'font-lock-builtin-face nil "a" +"abs" "absent" "according" "ada" "alias" "allocate" "are" "array_agg" +"asensitive" "atomic" "attribute" "attributes" "avg" "base64" +"bernoulli" "bit_length" "bitvar" "blob" "blocked" "bom" "breadth" "c" +"call" "cardinality" "catalog_name" "ceil" "ceiling" "char_length" +"character_length" "character_set_catalog" "character_set_name" +"character_set_schema" "characters" "checked" "class_origin" "clob" +"cobol" "collation" "collation_catalog" "collation_name" +"collation_schema" "collect" "column_name" "columns" +"command_function" "command_function_code" "completion" "condition" +"condition_number" "connect" "connection_name" "constraint_catalog" +"constraint_name" "constraint_schema" "constructor" "contains" +"control" "convert" "corr" "corresponding" "count" "covar_pop" +"covar_samp" "cube" "cume_dist" "current_default_transform_group" +"current_path" "current_transform_group_for_type" "cursor_name" +"datalink" "datetime_interval_code" "datetime_interval_precision" "db" +"defined" "degree" "dense_rank" "depth" "deref" "derived" "describe" +"descriptor" "destroy" "destructor" "deterministic" "diagnostics" +"disconnect" "dispatch" "dlnewcopy" "dlpreviouscopy" "dlurlcomplete" +"dlurlcompleteonly" "dlurlcompletewrite" "dlurlpath" "dlurlpathonly" +"dlurlpathwrite" "dlurlscheme" "dlurlserver" "dlvalue" "dynamic" +"dynamic_function" "dynamic_function_code" "element" "empty" +"end-exec" "equals" "every" "exception" "exec" "existing" "exp" "file" +"filter" "final" "first_value" "flag" "floor" "fortran" "found" "free" +"fs" "fusion" "g" "general" "generated" "get" "go" "goto" "grouping" +"hex" "hierarchy" "host" "id" "ignore" "implementation" "import" +"indent" "indicator" "infix" "initialize" "instance" "instantiable" +"integrity" "intersection" "iterate" "k" "key_member" "key_type" "lag" +"last_value" "lateral" "lead" "length" "less" "library" "like_regex" +"link" "ln" "locator" "lower" "m" "map" "matched" "max" +"max_cardinality" "member" "merge" "message_length" +"message_octet_length" "message_text" "method" "min" "mod" "modifies" +"modify" "module" "more" "multiset" "mumps" "namespace" "nclob" +"nesting" "new" "nfc" "nfd" "nfkc" "nfkd" "nil" "normalize" +"normalized" "nth_value" "ntile" "nullable" "number" +"occurrences_regex" "octet_length" "octets" "old" "open" "operation" +"ordering" "ordinality" "others" "output" "overriding" "p" "pad" +"parameter" "parameter_mode" "parameter_name" +"parameter_ordinal_position" "parameter_specific_catalog" +"parameter_specific_name" "parameter_specific_schema" "parameters" +"pascal" "passing" "passthrough" "percent_rank" "percentile_cont" +"percentile_disc" "permission" "pli" "position_regex" "postfix" +"power" "prefix" "preorder" "public" "rank" "reads" "recovery" "ref" +"referencing" "regr_avgx" "regr_avgy" "regr_count" "regr_intercept" +"regr_r2" "regr_slope" "regr_sxx" "regr_sxy" "regr_syy" "requiring" +"respect" "restore" "result" "return" "returned_cardinality" +"returned_length" "returned_octet_length" "returned_sqlstate" "rollup" +"routine" "routine_catalog" "routine_name" "routine_schema" +"row_count" "row_number" "scale" "schema_name" "scope" "scope_catalog" +"scope_name" "scope_schema" "section" "selective" "self" "sensitive" +"server_name" "sets" "size" "source" "space" "specific" +"specific_name" "specifictype" "sql" "sqlcode" "sqlerror" +"sqlexception" "sqlstate" "sqlwarning" "sqrt" "state" "static" +"stddev_pop" "stddev_samp" "structure" "style" "subclass_origin" +"sublist" "submultiset" "substring_regex" "sum" "system_user" "t" +"table_name" "tablesample" "terminate" "than" "ties" "timezone_hour" +"timezone_minute" "token" "top_level_count" "transaction_active" +"transactions_committed" "transactions_rolled_back" "transform" +"transforms" "translate" "translate_regex" "translation" +"trigger_catalog" "trigger_name" "trigger_schema" "trim_array" +"uescape" "under" "unlink" "unnamed" "unnest" "untyped" "upper" "uri" +"usage" "user_defined_type_catalog" "user_defined_type_code" +"user_defined_type_name" "user_defined_type_schema" "var_pop" +"var_samp" "varbinary" "variable" "whenever" "width_bucket" "within" +"xmlagg" "xmlbinary" "xmlcast" "xmlcomment" "xmldeclaration" +"xmldocument" "xmlexists" "xmliterate" "xmlnamespaces" "xmlquery" +"xmlschema" "xmltable" "xmltext" "xmlvalidate" +) + + ;; Postgres non-reserved words (sql-font-lock-keywords-builder 'font-lock-builtin-face nil -"abbrev" "abs" "acos" "age" "area" "ascii" "asin" "atab2" "atan" -"atan2" "avg" "bit_length" "both" "broadcast" "btrim" "cbrt" "ceil" -"center" "char_length" "chr" "coalesce" "col_description" "convert" -"cos" "cot" "count" "current_database" "current_date" "current_schema" -"current_schemas" "current_setting" "current_time" "current_timestamp" -"current_user" "currval" "date_part" "date_trunc" "decode" "degrees" -"diameter" "encode" "exp" "extract" "floor" "get_bit" "get_byte" -"has_database_privilege" "has_function_privilege" -"has_language_privilege" "has_schema_privilege" "has_table_privilege" -"height" "host" "initcap" "isclosed" "isfinite" "isopen" "leading" -"length" "ln" "localtime" "localtimestamp" "log" "lower" "lpad" -"ltrim" "masklen" "max" "min" "mod" "netmask" "network" "nextval" -"now" "npoints" "nullif" "obj_description" "octet_length" "overlay" -"pclose" "pg_client_encoding" "pg_function_is_visible" -"pg_get_constraintdef" "pg_get_indexdef" "pg_get_ruledef" -"pg_get_userbyid" "pg_get_viewdef" "pg_opclass_is_visible" -"pg_operator_is_visible" "pg_table_is_visible" "pg_type_is_visible" -"pi" "popen" "position" "pow" "quote_ident" "quote_literal" "radians" -"radius" "random" "repeat" "replace" "round" "rpad" "rtrim" -"session_user" "set_bit" "set_byte" "set_config" "set_masklen" -"setval" "sign" "sin" "split_part" "sqrt" "stddev" "strpos" "substr" -"substring" "sum" "tan" "timeofday" "to_ascii" "to_char" "to_date" -"to_hex" "to_number" "to_timestamp" "trailing" "translate" "trim" -"trunc" "upper" "variance" "version" "width" +"abort" "absolute" "access" "action" "add" "admin" "after" "aggregate" +"also" "alter" "always" "assertion" "assignment" "at" "backward" +"before" "begin" "between" "by" "cache" "called" "cascade" "cascaded" +"catalog" "chain" "characteristics" "checkpoint" "class" "close" +"cluster" "coalesce" "comment" "comments" "commit" "committed" +"configuration" "connection" "constraints" "content" "continue" +"conversion" "copy" "cost" "createdb" "createrole" "createuser" "csv" +"current" "cursor" "cycle" "data" "database" "day" "deallocate" "dec" +"declare" "defaults" "deferred" "definer" "delete" "delimiter" +"delimiters" "dictionary" "disable" "discard" "document" "domain" +"drop" "each" "enable" "encoding" "encrypted" "enum" "escape" +"exclude" "excluding" "exclusive" "execute" "exists" "explain" +"external" "extract" "family" "first" "float" "following" "force" +"forward" "function" "functions" "global" "granted" "greatest" +"handler" "header" "hold" "hour" "identity" "if" "immediate" +"immutable" "implicit" "including" "increment" "index" "indexes" +"inherit" "inherits" "inline" "inout" "input" "insensitive" "insert" +"instead" "invoker" "isolation" "key" "language" "large" "last" +"lc_collate" "lc_ctype" "least" "level" "listen" "load" "local" +"location" "lock" "login" "mapping" "match" "maxvalue" "minute" +"minvalue" "mode" "month" "move" "name" "names" "national" "nchar" +"next" "no" "nocreatedb" "nocreaterole" "nocreateuser" "noinherit" +"nologin" "none" "nosuperuser" "nothing" "notify" "nowait" "nullif" +"nulls" "object" "of" "oids" "operator" "option" "options" "out" +"overlay" "owned" "owner" "parser" "partial" "partition" "password" +"plans" "position" "preceding" "prepare" "prepared" "preserve" "prior" +"privileges" "procedural" "procedure" "quote" "range" "read" +"reassign" "recheck" "recursive" "reindex" "relative" "release" +"rename" "repeatable" "replace" "replica" "reset" "restart" "restrict" +"returns" "revoke" "role" "rollback" "row" "rows" "rule" "savepoint" +"schema" "scroll" "search" "second" "security" "sequence" "sequences" +"serializable" "server" "session" "set" "setof" "share" "show" +"simple" "stable" "standalone" "start" "statement" "statistics" +"stdin" "stdout" "storage" "strict" "strip" "substring" "superuser" +"sysid" "system" "tables" "tablespace" "temp" "template" "temporary" +"transaction" "treat" "trigger" "trim" "truncate" "trusted" "type" +"unbounded" "uncommitted" "unencrypted" "unknown" "unlisten" "until" +"update" "vacuum" "valid" "validator" "value" "values" "version" +"view" "volatile" "whitespace" "work" "wrapper" "write" +"xmlattributes" "xmlconcat" "xmlelement" "xmlforest" "xmlparse" +"xmlpi" "xmlroot" "xmlserialize" "year" "yes" ) + ;; Postgres Reserved (sql-font-lock-keywords-builder 'font-lock-keyword-face nil -"abort" "access" "add" "after" "aggregate" "alignment" "all" "alter" -"analyze" "and" "any" "as" "asc" "assignment" "authorization" -"backward" "basetype" "before" "begin" "between" "binary" "by" "cache" -"called" "cascade" "case" "cast" "characteristics" "check" -"checkpoint" "class" "close" "cluster" "column" "comment" "commit" -"committed" "commutator" "constraint" "constraints" "conversion" -"copy" "create" "createdb" "createuser" "cursor" "cycle" "database" -"deallocate" "declare" "default" "deferrable" "deferred" "definer" -"delete" "delimiter" "desc" "distinct" "do" "domain" "drop" "each" -"element" "else" "encoding" "encrypted" "end" "escape" "except" -"exclusive" "execute" "exists" "explain" "extended" "external" "false" -"fetch" "finalfunc" "for" "force" "foreign" "forward" "freeze" "from" -"full" "function" "grant" "group" "gtcmp" "handler" "hashes" "having" -"immediate" "immutable" "implicit" "in" "increment" "index" "inherits" -"initcond" "initially" "input" "insensitive" "insert" "instead" -"internallength" "intersect" "into" "invoker" "is" "isnull" -"isolation" "join" "key" "language" "leftarg" "level" "like" "limit" -"listen" "load" "local" "location" "lock" "ltcmp" "main" "match" -"maxvalue" "merges" "minvalue" "mode" "move" "natural" "negator" -"next" "nocreatedb" "nocreateuser" "none" "not" "nothing" "notify" -"notnull" "null" "of" "offset" "oids" "on" "only" "operator" "or" -"order" "output" "owner" "partial" "passedbyvalue" "password" "plain" -"prepare" "primary" "prior" "privileges" "procedural" "procedure" -"public" "read" "recheck" "references" "reindex" "relative" "rename" -"reset" "restrict" "returns" "revoke" "rightarg" "rollback" "row" -"rule" "schema" "scroll" "security" "select" "sequence" "serializable" -"session" "set" "sfunc" "share" "show" "similar" "some" "sort1" -"sort2" "stable" "start" "statement" "statistics" "storage" "strict" -"stype" "sysid" "table" "temp" "template" "temporary" "then" "to" -"transaction" "trigger" "true" "truncate" "trusted" "type" -"unencrypted" "union" "unique" "unknown" "unlisten" "until" "update" -"usage" "user" "using" "vacuum" "valid" "validator" "values" -"variable" "verbose" "view" "volatile" "when" "where" "with" "without" -"work" +"all" "analyse" "analyze" "and" "any" "array" "asc" "as" "asymmetric" +"authorization" "binary" "both" "case" "cast" "check" "collate" +"column" "concurrently" "constraint" "create" "cross" +"current_catalog" "current_date" "current_role" "current_schema" +"current_time" "current_timestamp" "current_user" "default" +"deferrable" "desc" "distinct" "do" "else" "end" "except" "false" +"fetch" "foreign" "for" "freeze" "from" "full" "grant" "group" +"having" "ilike" "initially" "inner" "in" "intersect" "into" "isnull" +"is" "join" "leading" "left" "like" "limit" "localtime" +"localtimestamp" "natural" "notnull" "not" "null" "off" "offset" +"only" "on" "order" "or" "outer" "overlaps" "over" "placing" "primary" +"references" "returning" "right" "select" "session_user" "similar" +"some" "symmetric" "table" "then" "to" "trailing" "true" "union" +"unique" "user" "using" "variadic" "verbose" "when" "where" "window" +"with" ) ;; Postgres Data Types (sql-font-lock-keywords-builder 'font-lock-type-face nil -"anyarray" "bigint" "bigserial" "bit" "boolean" "box" "bytea" "char" -"character" "cidr" "circle" "cstring" "date" "decimal" "double" -"float4" "float8" "inet" "int2" "int4" "int8" "integer" "internal" -"interval" "language_handler" "line" "lseg" "macaddr" "money" -"numeric" "oid" "opaque" "path" "point" "polygon" "precision" "real" -"record" "regclass" "regoper" "regoperator" "regproc" "regprocedure" -"regtype" "serial" "serial4" "serial8" "smallint" "text" "time" -"timestamp" "varchar" "varying" "void" "zone" +"bigint" "bigserial" "bit" "bool" "boolean" "box" "bytea" "char" +"character" "cidr" "circle" "date" "decimal" "double" "float4" +"float8" "inet" "int" "int2" "int4" "int8" "integer" "interval" "line" +"lseg" "macaddr" "money" "numeric" "path" "point" "polygon" +"precision" "real" "serial" "serial4" "serial8" "smallint" "text" +"time" "timestamp" "timestamptz" "timetz" "tsquery" "tsvector" +"txid_snapshot" "uuid" "varbit" "varchar" "varying" "without" +"xml" "zone" ))) "Postgres SQL keywords used by font-lock. @@ -1922,7 +2104,54 @@ regular expressions are created during compilation by calling the function `regexp-opt'. Therefore, take a look at the source before you define your own `sql-mode-mysql-font-lock-keywords'.") -(defvar sql-mode-sqlite-font-lock-keywords nil +(defvar sql-mode-sqlite-font-lock-keywords + (eval-when-compile + (list + ;; SQLite commands + '("^[.].*$" . font-lock-doc-face) + + ;; SQLite Keyword + (sql-font-lock-keywords-builder 'font-lock-keyword-face nil +"abort" "action" "add" "after" "all" "alter" "analyze" "and" "as" +"asc" "attach" "autoincrement" "before" "begin" "between" "by" +"cascade" "case" "cast" "check" "collate" "column" "commit" "conflict" +"constraint" "create" "cross" "database" "default" "deferrable" +"deferred" "delete" "desc" "detach" "distinct" "drop" "each" "else" +"end" "escape" "except" "exclusive" "exists" "explain" "fail" "for" +"foreign" "from" "full" "glob" "group" "having" "if" "ignore" +"immediate" "in" "index" "indexed" "initially" "inner" "insert" +"instead" "intersect" "into" "is" "isnull" "join" "key" "left" "like" +"limit" "match" "natural" "no" "not" "notnull" "null" "of" "offset" +"on" "or" "order" "outer" "plan" "pragma" "primary" "query" "raise" +"references" "regexp" "reindex" "release" "rename" "replace" +"restrict" "right" "rollback" "row" "savepoint" "select" "set" "table" +"temp" "temporary" "then" "to" "transaction" "trigger" "union" +"unique" "update" "using" "vacuum" "values" "view" "virtual" "when" +"where" +) + ;; SQLite Data types + (sql-font-lock-keywords-builder 'font-lock-type-face nil +"int" "integer" "tinyint" "smallint" "mediumint" "bigint" "unsigned" +"big" "int2" "int8" "character" "varchar" "varying" "nchar" "native" +"nvarchar" "text" "clob" "blob" "real" "double" "precision" "float" +"numeric" "number" "decimal" "boolean" "date" "datetime" +) + ;; SQLite Functions + (sql-font-lock-keywords-builder 'font-lock-builtin-face nil +;; Core functions +"abs" "changes" "coalesce" "glob" "ifnull" "hex" "last_insert_rowid" +"length" "like" "load_extension" "lower" "ltrim" "max" "min" "nullif" +"quote" "random" "randomblob" "replace" "round" "rtrim" "soundex" +"sqlite_compileoption_get" "sqlite_compileoption_used" +"sqlite_source_id" "sqlite_version" "substr" "total_changes" "trim" +"typeof" "upper" "zeroblob" +;; Date/time functions +"time" "julianday" "strftime" +"current_date" "current_time" "current_timestamp" +;; Aggregate functions +"avg" "count" "group_concat" "max" "min" "sum" "total" +))) + "SQLite SQL keywords used by font-lock. This variable is used by `sql-mode' and `sql-interactive-mode'. The @@ -1949,6 +2178,16 @@ highlighting rules in SQL mode.") ;;; SQL Product support functions +(defun sql-read-product (prompt &optional initial) + "Read a valid SQL product." + (let ((init (or (and initial (symbol-name initial)) "ansi"))) + (intern (completing-read + prompt + (mapcar (lambda (info) (symbol-name (car info))) + sql-product-alist) + nil 'require-match + init 'sql-product-history init)))) + (defun sql-add-product (product display &rest plist) "Add support for a database product in `sql-mode'. @@ -1969,7 +2208,7 @@ configuration." ;; Each product is represented by a radio ;; button with it's display name. `[,display - (lambda () (interactive) (sql-set-product ',product)) + (sql-set-product ',product) :style radio :selected (eq sql-product ',product)] ;; Maintain the product list in @@ -2016,13 +2255,17 @@ argument must be a plist keyword accepted by (setcdr p (plist-put (cdr p) feature newvalue))) (message "`%s' is not a known product; use `sql-add-product' to add it first." product)))) -(defun sql-get-product-feature (product feature &optional fallback) +(defun sql-get-product-feature (product feature &optional fallback not-indirect) "Lookup FEATURE associated with a SQL PRODUCT. If the FEATURE is nil for PRODUCT, and FALLBACK is specified, then the FEATURE associated with the FALLBACK product is returned. +If the FEATURE is in the list `sql-indirect-features', and the +NOT-INDIRECT parameter is not set, then the value of the symbol +stored in the connect alist is returned. + See `sql-product-alist' for a list of products and supported features." (let* ((p (assoc product sql-product-alist)) (v (plist-get (cdr p) feature))) @@ -2036,10 +2279,12 @@ See `sql-product-alist' for a list of products and supported features." (if (and (member feature sql-indirect-features) + (not not-indirect) (symbolp v)) (symbol-value v) v)) - (message "`%s' is not a known product; use `sql-add-product' to add it first." product)))) + (message "`%s' is not a known product; use `sql-add-product' to add it first." product) + nil))) (defun sql-product-font-lock (keywords-only imenu) "Configure font-lock and imenu with product-specific settings. @@ -2057,20 +2302,21 @@ also be configured." '((?_ . "w") (?. . "w"))))) ;; Get the product-specific keywords. - (setq sql-mode-font-lock-keywords - (append - (unless (eq sql-product 'ansi) - (sql-get-product-feature sql-product :font-lock)) - ;; Always highlight ANSI keywords - (sql-get-product-feature 'ansi :font-lock) - ;; Fontify object names in CREATE, DROP and ALTER DDL - ;; statements - (list sql-mode-font-lock-object-name))) + (set (make-local-variable 'sql-mode-font-lock-keywords) + (append + (unless (eq sql-product 'ansi) + (sql-get-product-feature sql-product :font-lock)) + ;; Always highlight ANSI keywords + (sql-get-product-feature 'ansi :font-lock) + ;; Fontify object names in CREATE, DROP and ALTER DDL + ;; statements + (list sql-mode-font-lock-object-name))) ;; Setup font-lock. Force re-parsing of `font-lock-defaults'. (kill-local-variable 'font-lock-set-defaults) - (setq font-lock-defaults (list 'sql-mode-font-lock-keywords - keywords-only t syntax-alist)) + (set (make-local-variable 'font-lock-defaults) + (list 'sql-mode-font-lock-keywords + keywords-only t syntax-alist)) ;; Force font lock to reinitialize if it is already on ;; Otherwise, we can wait until it can be started. @@ -2126,6 +2372,18 @@ adds a fontification pattern to fontify identifiers ending in (append old-val keywords) (append keywords old-val)))))) +(defun sql-for-each-login (login-params body) + "Iterates through login parameters and returns a list of results." + + (delq nil + (mapcar + (lambda (param) + (let ((token (or (and (listp param) (car param)) param)) + (plist (or (and (listp param) (cdr param)) nil))) + + (funcall body token plist))) + login-params))) + ;;; Functions to switch highlighting @@ -2143,11 +2401,7 @@ adds a fontification pattern to fontify identifiers ending in (defun sql-set-product (product) "Set `sql-product' to PRODUCT and enable appropriate highlighting." (interactive - (list (completing-read "SQL product: " - (mapcar (lambda (info) (symbol-name (car info))) - sql-product-alist) - nil 'require-match - (or (and sql-product (symbol-name sql-product)) "ansi")))) + (list (sql-read-product "SQL product: "))) (if (stringp product) (setq product (intern product))) (when (not (assoc product sql-product-alist)) (error "SQL product %s is not supported; treated as ANSI" product) @@ -2287,6 +2541,54 @@ appended to the SQLi buffer without disturbing your SQL buffer." "Read a password using PROMPT. Optional DEFAULT is password to start with." (read-passwd prompt nil default)) +(defun sql-get-login-ext (prompt last-value history-var plist) + "Prompt user with extended login parameters. + +If PLIST is nil, then the user is simply prompted for a string +value. + +The property `:default' specifies the default value. If the +`:number' property is non-nil then ask for a number. + +The `:file' property prompts for a file name that must match the +regexp pattern specified in its value. + +The `:completion' property prompts for a string specified by its +value. (The property value is used as the PREDICATE argument to +`completing-read'.)" + (let* ((default (plist-get plist :default)) + (prompt-def + (if default + (if (string-match "\\(\\):[ \t]*\\'" prompt) + (replace-match (format " (default \"%s\")" default) t t prompt 1) + (replace-regexp-in-string "[ \t]*\\'" + (format " (default \"%s\") " default) + prompt t t)) + prompt)) + (use-dialog-box nil)) + (cond + ((plist-member plist :file) + (expand-file-name + (read-file-name prompt + (file-name-directory last-value) default t + (file-name-nondirectory last-value) + (when (plist-get plist :file) + `(lambda (f) + (string-match + (concat "\\<" ,(plist-get plist :file) "\\>") + (file-name-nondirectory f))))))) + + ((plist-member plist :completion) + (completing-read prompt-def (plist-get plist :completion) nil t + last-value history-var default)) + + ((plist-get plist :number) + (read-number prompt (or default last-value 0))) + + (t + (let ((r (read-from-minibuffer prompt-def last-value nil nil history-var nil))) + (if (string= "" r) (or default "") r)))))) + (defun sql-get-login (&rest what) "Get username, password and database from the user. @@ -2304,53 +2606,69 @@ symbol `password', for the server if it contains the symbol `database'. The members of WHAT are processed in the order in which they are provided. +Each token may also be a list with the token in the car and a +plist of options as the cdr. The following properties are +supported: + + :file + :completion + :default + :number t + In order to ask the user for username, password and database, call the function like this: (sql-get-login 'user 'password 'database)." (interactive) - (while what - (cond - ((eq (car what) 'user) ; user - (setq sql-user - (read-from-minibuffer "User: " sql-user nil nil - 'sql-user-history))) - ((eq (car what) 'password) ; password - (setq sql-password - (sql-read-passwd "Password: " sql-password))) - - ((eq (car what) 'server) ; server - (setq sql-server - (read-from-minibuffer "Server: " sql-server nil nil - 'sql-server-history))) - ((eq (car what) 'port) ; port - (setq sql-port - (read-from-minibuffer "Port: " sql-port nil nil - 'sql-port-history))) - ((eq (car what) 'database) ; database - (setq sql-database - (read-from-minibuffer "Database: " sql-database nil nil - 'sql-database-history)))) - (setq what (cdr what)))) - -(defun sql-find-sqli-buffer () - "Returns the current default SQLi buffer or nil. -In order to qualify, the SQLi buffer must be alive, -be in `sql-interactive-mode' and have a process." - (let ((default-buffer (default-value 'sql-buffer))) - (if (and (buffer-live-p default-buffer) - (get-buffer-process default-buffer)) - default-buffer - (save-current-buffer - (let ((buflist (buffer-list)) - (found)) - (while (not (or (null buflist) - found)) - (let ((candidate (car buflist))) - (set-buffer candidate) - (if (and (derived-mode-p 'sql-interactive-mode) - (get-buffer-process candidate)) - (setq found candidate)) - (setq buflist (cdr buflist)))) - found))))) + (mapcar + (lambda (w) + (let ((token (or (and (consp w) (car w)) w)) + (plist (or (and (consp w) (cdr w)) nil))) + + (cond + ((eq token 'user) ; user + (setq sql-user + (sql-get-login-ext "User: " sql-user + 'sql-user-history plist))) + + ((eq token 'password) ; password + (setq sql-password + (sql-read-passwd "Password: " sql-password))) + + ((eq token 'server) ; server + (setq sql-server + (sql-get-login-ext "Server: " sql-server + 'sql-server-history plist))) + + ((eq token 'database) ; database + (setq sql-database + (sql-get-login-ext "Database: " sql-database + 'sql-database-history plist))) + + ((eq token 'port) ; port + (setq sql-port + (sql-get-login-ext "Port: " sql-port + nil (append '(:number t) plist))))))) + what)) + +(defun sql-find-sqli-buffer (&optional product) + "Returns the name of the current default SQLi buffer or nil. +In order to qualify, the SQLi buffer must be alive, be in +`sql-interactive-mode' and have a process." + (let ((buf sql-buffer) + (prod (or product sql-product))) + (or + ;; Current sql-buffer, if there is one. + (and (sql-buffer-live-p buf prod) + buf) + ;; Global sql-buffer + (and (setq buf (default-value 'sql-buffer)) + (sql-buffer-live-p buf prod) + buf) + ;; Look thru each buffer + (car (apply 'append + (mapcar (lambda (b) + (and (sql-buffer-live-p b prod) + (list (buffer-name b)))) + (buffer-list))))))) (defun sql-set-sqli-buffer-generally () "Set SQLi buffer for all SQL buffers that have none. @@ -2362,16 +2680,17 @@ using `sql-find-sqli-buffer'. If `sql-buffer' is set, (interactive) (save-excursion (let ((buflist (buffer-list)) - (default-sqli-buffer (sql-find-sqli-buffer))) - (setq-default sql-buffer default-sqli-buffer) + (default-buffer (sql-find-sqli-buffer))) + (setq-default sql-buffer default-buffer) (while (not (null buflist)) (let ((candidate (car buflist))) (set-buffer candidate) (if (and (derived-mode-p 'sql-mode) - (not (buffer-live-p sql-buffer))) + (not (sql-buffer-live-p sql-buffer))) (progn - (setq sql-buffer default-sqli-buffer) - (run-hooks 'sql-set-sqli-hook)))) + (setq sql-buffer default-buffer) + (when default-buffer + (run-hooks 'sql-set-sqli-hook))))) (setq buflist (cdr buflist)))))) (defun sql-set-sqli-buffer () @@ -2389,19 +2708,13 @@ If you call it from anywhere else, it sets the global copy of (interactive) (let ((default-buffer (sql-find-sqli-buffer))) (if (null default-buffer) - (error "There is no suitable SQLi buffer")) - (let ((new-buffer - (get-buffer - (read-buffer "New SQLi buffer: " default-buffer t)))) - (if (null (get-buffer-process new-buffer)) - (error "Buffer %s has no process" (buffer-name new-buffer))) - (if (null (with-current-buffer new-buffer - (equal major-mode 'sql-interactive-mode))) - (error "Buffer %s is no SQLi buffer" (buffer-name new-buffer))) - (if new-buffer - (progn - (setq sql-buffer new-buffer) - (run-hooks 'sql-set-sqli-hook)))))) + (error "There is no suitable SQLi buffer") + (let ((new-buffer (read-buffer "New SQLi buffer: " default-buffer t))) + (if (null (sql-buffer-live-p new-buffer)) + (error "Buffer %s is not a working SQLi buffer" new-buffer) + (when new-buffer + (setq sql-buffer new-buffer) + (run-hooks 'sql-set-sqli-hook))))))) (defun sql-show-sqli-buffer () "Show the name of current SQLi buffer. @@ -2409,32 +2722,108 @@ If you call it from anywhere else, it sets the global copy of This is the buffer SQL strings are sent to. It is stored in the variable `sql-buffer'. See `sql-help' on how to create such a buffer." (interactive) - (if (null (buffer-live-p sql-buffer)) + (if (null (buffer-live-p (get-buffer sql-buffer))) (message "%s has no SQLi buffer set." (buffer-name (current-buffer))) (if (null (get-buffer-process sql-buffer)) - (message "Buffer %s has no process." (buffer-name sql-buffer)) - (message "Current SQLi buffer is %s." (buffer-name sql-buffer))))) + (message "Buffer %s has no process." sql-buffer) + (message "Current SQLi buffer is %s." sql-buffer)))) (defun sql-make-alternate-buffer-name () "Return a string that can be used to rename a SQLi buffer. This is used to set `sql-alternate-buffer-name' within -`sql-interactive-mode'." - (concat (if (string= "" sql-user) - (if (string= "" (user-login-name)) - () - (concat (user-login-name) "/")) - (concat sql-user "/")) - (if (string= "" sql-database) - (if (string= "" sql-server) - (system-name) - sql-server) - sql-database))) +`sql-interactive-mode'. -(defun sql-rename-buffer () - "Rename a SQLi buffer." - (interactive) - (rename-buffer (format "*SQL: %s*" sql-alternate-buffer-name) t)) +If the session was started with `sql-connect' then the alternate +name would be the name of the connection. + +Otherwise, it uses the parameters identified by the :sqlilogin +parameter. + +If all else fails, the alternate name would be the user and +server/database name." + + (let ((name "")) + + ;; Build a name using the :sqli-login setting + (setq name + (apply 'concat + (cdr + (apply 'append nil + (sql-for-each-login + (sql-get-product-feature sql-product :sqli-login) + (lambda (token plist) + (cond + ((eq token 'user) + (unless (string= "" sql-user) + (list "/" sql-user))) + ((eq token 'port) + (unless (or (not (numberp sql-port)) + (= 0 sql-port)) + (list ":" (number-to-string sql-port)))) + ((eq token 'server) + (unless (string= "" sql-server) + (list "." + (if (plist-member plist :file) + (file-name-nondirectory sql-server) + sql-server)))) + ((eq token 'database) + (unless (string= "" sql-database) + (list "@" + (if (plist-member plist :file) + (file-name-nondirectory sql-database) + sql-database)))) + + ((eq token 'password) nil) + (t nil)))))))) + + ;; If there's a connection, use it and the name thus far + (if sql-connection + (format "<%s>%s" sql-connection (or name "")) + + ;; If there is no name, try to create something meaningful + (if (string= "" (or name "")) + (concat + (if (string= "" sql-user) + (if (string= "" (user-login-name)) + () + (concat (user-login-name) "/")) + (concat sql-user "/")) + (if (string= "" sql-database) + (if (string= "" sql-server) + (system-name) + sql-server) + sql-database)) + + ;; Use the name we've got + name)))) + +(defun sql-rename-buffer (&optional new-name) + "Rename a SQL interactive buffer. + +Prompts for the new name if command is preceded by +\\[universal-argument]. If no buffer name is provided, then the +`sql-alternate-buffer-name' is used. + +The actual buffer name set will be \"*SQL: NEW-NAME*\". If +NEW-NAME is empty, then the buffer name will be \"*SQL*\"." + (interactive "P") + + (if (not (derived-mode-p 'sql-interactive-mode)) + (message "Current buffer is not a SQL interactive buffer") + + (setq sql-alternate-buffer-name + (cond + ((stringp new-name) new-name) + ((consp new-name) + (read-string "Buffer name (\"*SQL: XXX*\"; enter `XXX'): " + sql-alternate-buffer-name)) + (t sql-alternate-buffer-name))) + + (rename-buffer (if (string= "" sql-alternate-buffer-name) + "*SQL*" + (format "*SQL: %s*" sql-alternate-buffer-name)) + t))) (defun sql-copy-column () "Copy current column to the end of buffer. @@ -2507,14 +2896,73 @@ Every newline in STRING will be preceded with a space and a backslash." ;;; Input sender for SQLi buffers +(defvar sql-output-newline-count 0 + "Number of newlines in the input string. + +Allows the suppression of continuation prompts.") + +(defvar sql-output-by-send nil + "Non-nil if the command in the input was generated by `sql-send-string'.") + (defun sql-input-sender (proc string) "Send STRING to PROC after applying filters." (let* ((product (with-current-buffer (process-buffer proc) sql-product)) (filter (sql-get-product-feature product :input-filter))) + ;; Apply filter(s) + (cond + ((not filter) + nil) + ((functionp filter) + (setq string (funcall filter string))) + ((listp filter) + (mapc (lambda (f) (setq string (funcall f string))) filter)) + (t nil)) + + ;; Count how many newlines in the string + (setq sql-output-newline-count 0) + (mapc (lambda (ch) + (when (eq ch ?\n) + (setq sql-output-newline-count (1+ sql-output-newline-count)))) + string) + ;; Send the string - (comint-simple-send proc (if filter (funcall filter string) string)))) + (comint-simple-send proc string))) + +;;; Strip out continuation prompts + +(defun sql-interactive-remove-continuation-prompt (oline) + "Strip out continuation prompts out of the OLINE. + +Added to the `comint-preoutput-filter-functions' hook in a SQL +interactive buffer. If `sql-outut-newline-count' is greater than +zero, then an output line matching the continuation prompt is filtered +out. If the count is one, then the prompt is replaced with a newline +to force the output from the query to appear on a new line." + (if (and sql-prompt-cont-regexp + sql-output-newline-count + (numberp sql-output-newline-count) + (>= sql-output-newline-count 1)) + (progn + (while (and oline + sql-output-newline-count + (> sql-output-newline-count 0) + (string-match sql-prompt-cont-regexp oline)) + + (setq oline + (replace-match (if (and + (= 1 sql-output-newline-count) + sql-output-by-send) + "\n" "") + nil nil oline) + sql-output-newline-count + (1- sql-output-newline-count))) + (if (= sql-output-newline-count 0) + (setq sql-output-newline-count nil)) + (setq sql-output-by-send nil)) + (setq sql-output-newline-count nil)) + oline) ;;; Sending the region to the SQLi buffer. @@ -2522,28 +2970,22 @@ Every newline in STRING will be preceded with a space and a backslash." "Send the string STR to the SQL process." (interactive "sSQL Text: ") - (let (comint-input-sender-no-newline proc) - (if (buffer-live-p sql-buffer) + (let ((comint-input-sender-no-newline nil) + (s (replace-regexp-in-string "[[:space:]\n\r]+\\'" "" str))) + (if (sql-buffer-live-p sql-buffer) (progn ;; Ignore the hoping around... (save-excursion - ;; Get the process - (setq proc (get-buffer-process sql-buffer)) - ;; Set product context (with-current-buffer sql-buffer - ;; Send the string - (sql-input-sender proc str) - - ;; Send a newline if there wasn't one on the end of the string - (unless (string-equal "\n" (substring str (1- (length str)))) - (comint-send-string proc "\n")) + ;; Send the string (trim the trailing whitespace) + (sql-input-sender (get-buffer-process sql-buffer) s) ;; Send a command terminator if we must (if sql-send-terminator - (sql-send-magic-terminator sql-buffer str sql-send-terminator)) + (sql-send-magic-terminator sql-buffer s sql-send-terminator)) - (message "Sent string to buffer %s." (buffer-name sql-buffer)))) + (message "Sent string to buffer %s." sql-buffer))) ;; Display the sql buffer (if sql-pop-to-buffer-after-send-region @@ -2576,7 +3018,7 @@ Every newline in STRING will be preceded with a space and a backslash." (defun sql-send-magic-terminator (buf str terminator) "Send TERMINATOR to buffer BUF if its not present in STR." - (let (pat term) + (let (comint-input-sender-no-newline pat term) ;; If flag is merely on(t), get product-specific terminator (if (eq terminator t) (setq terminator (sql-get-product-feature sql-product :terminator))) @@ -2597,8 +3039,13 @@ Every newline in STRING will be preceded with a space and a backslash." ;; Check to see if the pattern is present in the str already sent (unless (and pat term - (string-match (concat pat "\n?\\'") str)) - (comint-send-string buf (concat term "\n"))))) + (string-match (concat pat "\\'") str)) + (comint-simple-send (get-buffer-process buf) term) + (setq sql-output-newline-count + (if sql-output-newline-count + (1+ sql-output-newline-count) + 1))) + (setq sql-output-by-send t))) (defun sql-remove-tabs-filter (str) "Replace tab characters with spaces." @@ -2617,10 +3064,175 @@ If given the optional parameter VALUE, sets +;;; Redirect output functions + +(defun sql-redirect (command combuf &optional outbuf save-prior) + "Execute the SQL command and send output to OUTBUF. + +COMBUF must be an active SQL interactive buffer. OUTBUF may be +an existing buffer, or the name of a non-existing buffer. If +omitted the output is sent to a temporary buffer which will be +killed after the command completes. COMMAND should be a string +of commands accepted by the SQLi program." + + (with-current-buffer combuf + (let ((buf (get-buffer-create (or outbuf " *SQL-Redirect*"))) + (proc (get-buffer-process (current-buffer))) + (comint-prompt-regexp (sql-get-product-feature sql-product + :prompt-regexp)) + (start nil)) + (with-current-buffer buf + (toggle-read-only -1) + (unless save-prior + (erase-buffer)) + (goto-char (point-max)) + (unless (zerop (buffer-size)) + (insert "\n")) + (setq start (point))) + + ;; Run the command + (message "Executing SQL command...") + (comint-redirect-send-command-to-process command buf proc nil t) + (while (null comint-redirect-completed) + (accept-process-output nil 1)) + (message "Executing SQL command...done") + + ;; Clean up the output results + (with-current-buffer buf + ;; Remove trailing whitespace + (goto-char (point-max)) + (when (looking-back "[ \t\f\n\r]*" start) + (delete-region (match-beginning 0) (match-end 0))) + ;; Remove echo if there was one + (goto-char start) + (when (looking-at (concat "^" (regexp-quote command) "[\\n]")) + (delete-region (match-beginning 0) (match-end 0))) + (goto-char start))))) + +(defun sql-redirect-value (command combuf regexp &optional regexp-groups) + "Execute the SQL command and return part of result. + +COMBUF must be an active SQL interactive buffer. COMMAND should +be a string of commands accepted by the SQLi program. From the +output, the REGEXP is repeatedly matched and the list of +REGEXP-GROUPS submatches is returned. This behaves much like +\\[comint-redirect-results-list-from-process] but instead of +returning a single submatch it returns a list of each submatch +for each match." + + (let ((outbuf " *SQL-Redirect-values*") + (results nil)) + (sql-redirect command combuf outbuf nil) + (with-current-buffer outbuf + (while (re-search-forward regexp nil t) + (push + (cond + ;; no groups-return all of them + ((null regexp-groups) + (let ((i 1) + (r nil)) + (while (match-beginning i) + (push (match-string i) r)) + (nreverse r))) + ;; one group specified + ((numberp regexp-groups) + (match-string regexp-groups)) + ;; list of numbers; return the specified matches only + ((consp regexp-groups) + (mapcar (lambda (c) + (cond + ((numberp c) (match-string c)) + ((stringp c) (match-substitute-replacement c)) + (t (error "sql-redirect-value: unknown REGEXP-GROUPS value - %s" c)))) + regexp-groups)) + ;; String is specified; return replacement string + ((stringp regexp-groups) + (match-substitute-replacement regexp-groups)) + (t + (error "sql-redirect-value: unknown REGEXP-GROUPS value - %s" + regexp-groups))) + results))) + (nreverse results))) + +(defun sql-execute (sqlbuf outbuf command arg) + "Executes a command in a SQL interacive buffer and captures the output. + +The commands are run in SQLBUF and the output saved in OUTBUF. +COMMAND must be a string, a function or a list of such elements. +Functions are called with SQLBUF, OUTBUF and ARG as parameters; +strings are formatted with ARG and executed. + +If the results are empty the OUTBUF is deleted, otherwise the +buffer is popped into a view window. " + (mapc + (lambda (c) + (cond + ((stringp c) + (sql-redirect (if arg (format c arg) c) sqlbuf outbuf) t) + ((functionp c) + (apply c sqlbuf outbuf arg)) + (t (error "Unknown sql-execute item %s" c)))) + (if (consp command) command (cons command nil))) + + (setq outbuf (get-buffer outbuf)) + (if (zerop (buffer-size outbuf)) + (kill-buffer outbuf) + (let ((one-win (eq (selected-window) + (get-lru-window)))) + (with-current-buffer outbuf + (set-buffer-modified-p nil) + (toggle-read-only 1)) + (view-buffer-other-window outbuf) + (when one-win + (shrink-window-if-larger-than-buffer))))) + +(defun sql-execute-feature (sqlbuf outbuf feature enhanced arg) + "List objects or details in a separate display buffer." + (let (command) + (with-current-buffer sqlbuf + (setq command (sql-get-product-feature sql-product feature))) + (unless command + (error "%s does not support %s" sql-product feature)) + (when (consp command) + (setq command (if enhanced + (cdr command) + (car command)))) + (sql-execute sqlbuf outbuf command arg))) + +(defun sql-read-table-name (prompt) + "Read the name of a database table." + ;; TODO: Fetch table/view names from database and provide completion. + ;; Also implement thing-at-point if the buffer has valid names in it + ;; (i.e. sql-mode, sql-interactive-mode, or sql-list-all buffers) + (read-from-minibuffer prompt)) + +(defun sql-list-all (&optional enhanced) + "List all database objects." + (interactive "P") + (let ((sqlbuf (sql-find-sqli-buffer))) + (unless sqlbuf + (error "No SQL interactive buffer found")) + (sql-execute-feature sqlbuf "*List All*" :list-all enhanced nil))) + +(defun sql-list-table (name &optional enhanced) + "List the details of a database table. " + (interactive + (list (sql-read-table-name "Table name: ") + current-prefix-arg)) + (let ((sqlbuf (sql-find-sqli-buffer))) + (unless sqlbuf + (error "No SQL interactive buffer found")) + (unless name + (error "No table name specified")) + (sql-execute-feature sqlbuf (format "*List %s*" name) + :list-table enhanced name))) + + + ;;; SQL mode -- uses SQL interactive mode ;;;###autoload -(defun sql-mode () +(define-derived-mode sql-mode prog-mode "SQL" "Major mode to edit SQL. You can send SQL statements to the SQLi buffer using @@ -2647,18 +3259,11 @@ you must tell Emacs. Here's how to do that in your `~/.emacs' file: \(add-hook 'sql-mode-hook (lambda () (modify-syntax-entry ?\\\\ \".\" sql-mode-syntax-table)))" - (interactive) - (kill-all-local-variables) - (setq major-mode 'sql-mode) - (setq mode-name "SQL") - (use-local-map sql-mode-map) + :abbrev-table sql-mode-abbrev-table (if sql-mode-menu (easy-menu-add sql-mode-menu)); XEmacs - (set-syntax-table sql-mode-syntax-table) - (make-local-variable 'font-lock-defaults) - (make-local-variable 'sql-mode-font-lock-keywords) - (make-local-variable 'comment-start) - (setq comment-start "--") + + (set (make-local-variable 'comment-start) "--") ;; Make each buffer in sql-mode remember the "current" SQLi buffer. (make-local-variable 'sql-buffer) ;; Add imenu support for sql-mode. Note that imenu-generic-expression @@ -2668,17 +3273,11 @@ you must tell Emacs. Here's how to do that in your `~/.emacs' file: imenu-case-fold-search t) ;; Make `sql-send-paragraph' work on paragraphs that contain indented ;; lines. - (make-local-variable 'paragraph-separate) - (make-local-variable 'paragraph-start) - (setq paragraph-separate "[\f]*$" - paragraph-start "[\n\f]") + (set (make-local-variable 'paragraph-separate) "[\f]*$") + (set (make-local-variable 'paragraph-start) "[\n\f]") ;; Abbrevs - (setq local-abbrev-table sql-mode-abbrev-table) (setq abbrev-all-caps 1) - ;; Run hook - (run-mode-hooks 'sql-mode-hook) ;; Catch changes to sql-product and highlight accordingly - (sql-highlight-product) (add-hook 'hack-local-variables-hook 'sql-highlight-product t t)) @@ -2763,15 +3362,14 @@ you entered, right above the output it created. sql-product)) ;; Setup the mode. - (setq major-mode 'sql-interactive-mode) - (setq mode-name (concat "SQLi[" (or (sql-get-product-feature sql-product :name) - (symbol-name sql-product)) "]")) + (setq major-mode 'sql-interactive-mode) ;FIXME: Use define-derived-mode. + (setq mode-name + (concat "SQLi[" (or (sql-get-product-feature sql-product :name) + (symbol-name sql-product)) "]")) (use-local-map sql-interactive-mode-map) (if sql-interactive-mode-menu (easy-menu-add sql-interactive-mode-menu)) ; XEmacs (set-syntax-table sql-mode-syntax-table) - (make-local-variable 'sql-mode-font-lock-keywords) - (make-local-variable 'font-lock-defaults) ;; Note that making KEYWORDS-ONLY nil will cause havoc if you try ;; SELECT 'x' FROM DUAL with SQL*Plus, because the title of the column @@ -2780,29 +3378,39 @@ you entered, right above the output it created. (sql-product-font-lock t nil) ;; Enable commenting and uncommenting of the region. - (make-local-variable 'comment-start) - (setq comment-start "--") + (set (make-local-variable 'comment-start) "--") ;; Abbreviation table init and case-insensitive. It is not activated ;; by default. (setq local-abbrev-table sql-mode-abbrev-table) (setq abbrev-all-caps 1) ;; Exiting the process will call sql-stop. - (set-process-sentinel (get-buffer-process sql-buffer) 'sql-stop) + (set-process-sentinel (get-buffer-process (current-buffer)) 'sql-stop) + ;; Save the connection name + (make-local-variable 'sql-connection) ;; Create a usefull name for renaming this buffer later. - (make-local-variable 'sql-alternate-buffer-name) - (setq sql-alternate-buffer-name (sql-make-alternate-buffer-name)) + (set (make-local-variable 'sql-alternate-buffer-name) + (sql-make-alternate-buffer-name)) ;; User stuff. Initialize before the hook. (set (make-local-variable 'sql-prompt-regexp) (sql-get-product-feature sql-product :prompt-regexp)) (set (make-local-variable 'sql-prompt-length) (sql-get-product-feature sql-product :prompt-length)) + (set (make-local-variable 'sql-prompt-cont-regexp) + (sql-get-product-feature sql-product :prompt-cont-regexp)) + (make-local-variable 'sql-output-newline-count) + (make-local-variable 'sql-output-by-send) + (add-hook 'comint-preoutput-filter-functions + 'sql-interactive-remove-continuation-prompt nil t) (make-local-variable 'sql-input-ring-separator) (make-local-variable 'sql-input-ring-file-name) - (setq comint-process-echoes t) ;; Run the mode hook (along with comint's hooks). (run-mode-hooks 'sql-interactive-mode-hook) ;; Set comint based on user overrides. - (setq comint-prompt-regexp sql-prompt-regexp) + (setq comint-prompt-regexp + (if sql-prompt-cont-regexp + (concat "\\(" sql-prompt-regexp + "\\|" sql-prompt-cont-regexp "\\)") + sql-prompt-regexp)) (setq left-margin sql-prompt-length) ;; Install input sender (set (make-local-variable 'comint-input-sender) 'sql-input-sender) @@ -2831,89 +3439,239 @@ Sentinels will always get the two parameters PROCESS and EVENT." +;;; Connection handling + +(defun sql-read-connection (prompt &optional initial default) + "Read a connection name." + (let ((completion-ignore-case t)) + (completing-read prompt + (mapcar (lambda (c) (car c)) + sql-connection-alist) + nil t initial 'sql-connection-history default))) + +;;;###autoload +(defun sql-connect (connection) + "Connect to an interactive session using CONNECTION settings. + +See `sql-connection-alist' to see how to define connections and +their settings. + +The user will not be prompted for any login parameters if a value +is specified in the connection settings." + + ;; Prompt for the connection from those defined in the alist + (interactive + (if sql-connection-alist + (list (sql-read-connection "Connection: " nil '(nil))) + nil)) + + ;; Are there connections defined + (if sql-connection-alist + ;; Was one selected + (when connection + ;; Get connection settings + (let ((connect-set (assoc connection sql-connection-alist))) + ;; Settings are defined + (if connect-set + ;; Set the desired parameters + (eval `(let* + (,@(cdr connect-set) + ;; :sqli-login params variable + (param-var (sql-get-product-feature sql-product + :sqli-login nil t)) + ;; :sqli-login params value + (login-params (sql-get-product-feature sql-product + :sqli-login)) + ;; which params are in the connection + (set-params (mapcar + (lambda (v) + (cond + ((eq (car v) 'sql-user) 'user) + ((eq (car v) 'sql-password) 'password) + ((eq (car v) 'sql-server) 'server) + ((eq (car v) 'sql-database) 'database) + ((eq (car v) 'sql-port) 'port) + (t (car v)))) + (cdr connect-set))) + ;; the remaining params (w/o the connection params) + (rem-params (sql-for-each-login + login-params + (lambda (token plist) + (unless (member token set-params) + (if plist + (cons token plist) + token))))) + ;; Remember the connection + (sql-connection connection)) + + ;; Set the remaining parameters and start the + ;; interactive session + (eval `(let ((,param-var ',rem-params)) + (sql-product-interactive sql-product))))) + (message "SQL Connection <%s> does not exist" connection) + nil))) + (message "No SQL Connections defined") + nil)) + +(defun sql-save-connection (name) + "Captures the connection information of the current SQLi session. + +The information is appended to `sql-connection-alist' and +optionally is saved to the user's init file." + + (interactive "sNew connection name: ") + + (if sql-connection + (message "This session was started by a connection; it's already been saved.") + + (let ((login (sql-get-product-feature sql-product :sqli-login)) + (alist sql-connection-alist) + connect) + + ;; Remove the existing connection if the user says so + (when (and (assoc name alist) + (yes-or-no-p (format "Replace connection definition <%s>? " name))) + (setq alist (assq-delete-all name alist))) + + ;; Add the new connection if it doesn't exist + (if (assoc name alist) + (message "Connection <%s> already exists" name) + (setq connect + (append (list name) + (sql-for-each-login + `(product ,@login) + (lambda (token plist) + (cond + ((eq token 'product) `(sql-product ',sql-product)) + ((eq token 'user) `(sql-user ,sql-user)) + ((eq token 'database) `(sql-database ,sql-database)) + ((eq token 'server) `(sql-server ,sql-server)) + ((eq token 'port) `(sql-port ,sql-port))))))) + + (setq alist (append alist (list connect))) + + ;; confirm whether we want to save the connections + (if (yes-or-no-p "Save the connections for future sessions? ") + (customize-save-variable 'sql-connection-alist alist) + (customize-set-variable 'sql-connection-alist alist)))))) + +(defun sql-connection-menu-filter (tail) + "Generates menu entries for using each connection." + (append + (mapcar + (lambda (conn) + (vector + (format "Connection <%s>" (car conn)) + (list 'sql-connect (car conn)) + t)) + sql-connection-alist) + tail)) + + + ;;; Entry functions for different SQL interpreters. ;;;###autoload -(defun sql-product-interactive (&optional product) +(defun sql-product-interactive (&optional product new-name) "Run PRODUCT interpreter as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. If buffer exists and a process is running, just switch to buffer `*SQL*'. +To specify the SQL product, prefix the call with +\\[universal-argument]. To set the buffer name as well, prefix +the call to \\[sql-product-interactive] with +\\[universal-argument] \\[universal-argument]. + \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" (interactive "P") + ;; Handle universal arguments if specified + (when (not (or executing-kbd-macro noninteractive)) + (when (and (consp product) + (not (cdr product)) + (numberp (car product))) + (when (>= (prefix-numeric-value product) 16) + (when (not new-name) + (setq new-name '(4))) + (setq product '(4))))) + + ;; Get the value of product that we need (setq product (cond - ((equal product '(4)) ; Universal arg, prompt for product - (intern (completing-read "SQL product: " - (mapcar (lambda (info) (symbol-name (car info))) - sql-product-alist) - nil 'require-match - (or (and sql-product (symbol-name sql-product)) "ansi")))) - ((symbolp product) product) ; Product specified + ((and product ; Product specified + (symbolp product)) product) + ((= (prefix-numeric-value product) 4) ; C-u, prompt for product + (sql-read-product "SQL product: " sql-product)) (t sql-product))) ; Default to sql-product - (when (sql-get-product-feature product :sqli-connect-func) - (if (and sql-buffer - (buffer-live-p sql-buffer) - (comint-check-proc sql-buffer)) - (pop-to-buffer sql-buffer) - - ;; Is the current buffer in sql-mode and - ;; there is a buffer local setting of sql-buffer - (let* ((start-buffer - (and (derived-mode-p 'sql-mode) - (current-buffer))) - (start-sql-buffer - (and start-buffer - (let (found) - (dolist (var (buffer-local-variables)) - (and (consp var) - (eq (car var) 'sql-buffer) - (buffer-live-p (cdr var)) - (get-buffer-process (cdr var)) - (setq found (cdr var)))) - found))) - new-sqli-buffer) - - ;; Get credentials. - (apply 'sql-get-login (sql-get-product-feature product :sqli-login)) - - ;; Connect to database. - (message "Login...") - (funcall (sql-get-product-feature product :sqli-connect-func) - product - (sql-get-product-feature product :sqli-options)) - - ;; Set SQLi mode. - (setq sql-interactive-product product - new-sqli-buffer (current-buffer) - sql-buffer new-sqli-buffer) - (sql-interactive-mode) - - ;; Set `sql-buffer' in the start buffer - (when (and start-buffer (not start-sql-buffer)) - (with-current-buffer start-buffer - (setq sql-buffer new-sqli-buffer))) - - ;; All done. - (message "Login...done") - (pop-to-buffer sql-buffer))))) - -(defun sql-connect (product params) - "Set up a comint buffer to connect to the SQL processor. + ;; If we have a product and it has a interactive mode + (if product + (when (sql-get-product-feature product :sqli-comint-func) + ;; If no new name specified, try to pop to an active SQL + ;; interactive for the same product + (let ((buf (sql-find-sqli-buffer product))) + (if (and (not new-name) buf) + (pop-to-buffer buf) + + ;; We have a new name or sql-buffer doesn't exist or match + ;; Start by remembering where we start + (let ((start-buffer (current-buffer)) + new-sqli-buffer) + + ;; Get credentials. + (apply 'sql-get-login (sql-get-product-feature product :sqli-login)) + + ;; Connect to database. + (message "Login...") + (funcall (sql-get-product-feature product :sqli-comint-func) + product + (sql-get-product-feature product :sqli-options)) + + ;; Set SQLi mode. + (setq new-sqli-buffer (current-buffer)) + (let ((sql-interactive-product product)) + (sql-interactive-mode)) + + ;; Set the new buffer name + (when new-name + (sql-rename-buffer new-name)) + + ;; Set `sql-buffer' in the new buffer and the start buffer + (setq sql-buffer (buffer-name new-sqli-buffer)) + (with-current-buffer start-buffer + (setq sql-buffer (buffer-name new-sqli-buffer)) + (run-hooks 'sql-set-sqli-hook)) + + ;; All done. + (message "Login...done") + (pop-to-buffer sql-buffer))))) + (message "No default SQL product defined. Set `sql-product'."))) + +(defun sql-comint (product params) + "Set up a comint buffer to run the SQL processor. PRODUCT is the SQL product. PARAMS is a list of strings which are passed as command line arguments." - (let ((program (sql-get-product-feature product :sqli-program))) + (let ((program (sql-get-product-feature product :sqli-program)) + (buf-name "SQL")) + ;; make sure we can find the program + (unless (executable-find program) + (error "Unable to locate SQL program \'%s\'" program)) + ;; Make sure buffer name is unique + (when (sql-buffer-live-p (format "*%s*" buf-name)) + (setq buf-name (format "SQL-%s" product)) + (when (sql-buffer-live-p (format "*%s*" buf-name)) + (let ((i 1)) + (while (sql-buffer-live-p + (format "*%s*" + (setq buf-name (format "SQL-%s%d" product i)))) + (setq i (1+ i)))))) (set-buffer - (if params - (apply 'make-comint "SQL" program nil params) - (make-comint "SQL" program nil))))) + (apply 'make-comint buf-name program nil params)))) ;;;###autoload -(defun sql-oracle () +(defun sql-oracle (&optional buffer) "Run sqlplus by Oracle as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -2928,6 +3686,11 @@ the list `sql-oracle-options'. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-oracle]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-oracle]. You can also specify this with \\[set-buffer-process-coding-system] @@ -2936,10 +3699,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'oracle)) + (interactive "P") + (sql-product-interactive 'oracle buffer)) -(defun sql-connect-oracle (product options) +(defun sql-comint-oracle (product options) "Create comint buffer and connect to Oracle." ;; Produce user/password@database construct. Password without user ;; is meaningless; database without user/password is meaningless, @@ -2955,12 +3718,12 @@ The default comes from `process-coding-system-alist' and (if parameter (setq parameter (nconc (list parameter) options)) (setq parameter options)) - (sql-connect product parameter))) + (sql-comint product parameter))) ;;;###autoload -(defun sql-sybase () +(defun sql-sybase (&optional buffer) "Run isql by Sybase as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -2975,6 +3738,11 @@ can be stored in the list `sql-sybase-options'. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-sybase]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-sybase]. You can also specify this with \\[set-buffer-process-coding-system] @@ -2983,10 +3751,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'sybase)) + (interactive "P") + (sql-product-interactive 'sybase buffer)) -(defun sql-connect-sybase (product options) +(defun sql-comint-sybase (product options) "Create comint buffer and connect to Sybase." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. @@ -2999,12 +3767,12 @@ The default comes from `process-coding-system-alist' and (setq params (append (list "-P" sql-password) params))) (if (not (string= "" sql-user)) (setq params (append (list "-U" sql-user) params))) - (sql-connect product params))) + (sql-comint product params))) ;;;###autoload -(defun sql-informix () +(defun sql-informix (&optional buffer) "Run dbaccess by Informix as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3017,6 +3785,11 @@ the variable `sql-database' as default, if set. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-informix]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-informix]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3025,10 +3798,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'informix)) + (interactive "P") + (sql-product-interactive 'informix buffer)) -(defun sql-connect-informix (product options) +(defun sql-comint-informix (product options) "Create comint buffer and connect to Informix." ;; username and password are ignored. (let ((db (if (string= "" sql-database) @@ -3036,12 +3809,12 @@ The default comes from `process-coding-system-alist' and (if (string= "" sql-server) sql-database (concat sql-database "@" sql-server))))) - (sql-connect product (append `(,db "-") options)))) + (sql-comint product (append `(,db "-") options)))) ;;;###autoload -(defun sql-sqlite () +(defun sql-sqlite (&optional buffer) "Run sqlite as an inferior process. SQLite is free software. @@ -3058,6 +3831,11 @@ can be stored in the list `sql-sqlite-options'. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-sqlite]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-sqlite]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3066,23 +3844,24 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'sqlite)) + (interactive "P") + (sql-product-interactive 'sqlite buffer)) -(defun sql-connect-sqlite (product options) +(defun sql-comint-sqlite (product options) "Create comint buffer and connect to SQLite." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. (let ((params)) (if (not (string= "" sql-database)) - (setq params (append (list sql-database) params))) + (setq params (append (list (expand-file-name sql-database)) + params))) (setq params (append options params)) - (sql-connect product params))) + (sql-comint product params))) ;;;###autoload -(defun sql-mysql () +(defun sql-mysql (&optional buffer) "Run mysql by TcX as an inferior process. Mysql versions 3.23 and up are free software. @@ -3099,6 +3878,11 @@ can be stored in the list `sql-mysql-options'. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-mysql]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-mysql]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3107,10 +3891,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'mysql)) + (interactive "P") + (sql-product-interactive 'mysql buffer)) -(defun sql-connect-mysql (product options) +(defun sql-comint-mysql (product options) "Create comint buffer and connect to MySQL." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. @@ -3119,19 +3903,19 @@ The default comes from `process-coding-system-alist' and (setq params (append (list sql-database) params))) (if (not (string= "" sql-server)) (setq params (append (list (concat "--host=" sql-server)) params))) - (if (not (and sql-port (numberp sql-port))) + (if (not (= 0 sql-port)) (setq params (append (list (concat "--port=" (number-to-string sql-port))) params))) (if (not (string= "" sql-password)) (setq params (append (list (concat "--password=" sql-password)) params))) (if (not (string= "" sql-user)) (setq params (append (list (concat "--user=" sql-user)) params))) (setq params (append options params)) - (sql-connect product params))) + (sql-comint product params))) ;;;###autoload -(defun sql-solid () +(defun sql-solid (&optional buffer) "Run solsql by Solid as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3145,6 +3929,11 @@ defaults, if set. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-solid]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-solid]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3153,10 +3942,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'solid)) + (interactive "P") + (sql-product-interactive 'solid buffer)) -(defun sql-connect-solid (product options) +(defun sql-comint-solid (product options) "Create comint buffer and connect to Solid." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. @@ -3167,12 +3956,12 @@ The default comes from `process-coding-system-alist' and (setq params (append (list sql-user sql-password) params))) (if (not (string= "" sql-server)) (setq params (append (list sql-server) params))) - (sql-connect product params))) + (sql-comint product params))) ;;;###autoload -(defun sql-ingres () +(defun sql-ingres (&optional buffer) "Run sql by Ingres as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3185,6 +3974,11 @@ the variable `sql-database' as default, if set. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-ingres]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-ingres]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3193,13 +3987,13 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'ingres)) + (interactive "P") + (sql-product-interactive 'ingres buffer)) -(defun sql-connect-ingres (product options) +(defun sql-comint-ingres (product options) "Create comint buffer and connect to Ingres." ;; username and password are ignored. - (sql-connect product + (sql-comint product (append (if (string= "" sql-database) nil (list sql-database)) @@ -3208,7 +4002,7 @@ The default comes from `process-coding-system-alist' and ;;;###autoload -(defun sql-ms () +(defun sql-ms (&optional buffer) "Run osql by Microsoft as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3223,6 +4017,11 @@ in the list `sql-ms-options'. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-ms]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-ms]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3231,10 +4030,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'ms)) + (interactive "P") + (sql-product-interactive 'ms buffer)) -(defun sql-connect-ms (product options) +(defun sql-comint-ms (product options) "Create comint buffer and connect to Microsoft SQL Server." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. @@ -3254,12 +4053,12 @@ The default comes from `process-coding-system-alist' and ;; If -P is passed to ISQL as the last argument without a ;; password, it's considered null. (setq params (append params (list "-P"))))) - (sql-connect product params))) + (sql-comint product params))) ;;;###autoload -(defun sql-postgres () +(defun sql-postgres (&optional buffer) "Run psql by Postgres as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3274,6 +4073,11 @@ Additional command line parameters can be stored in the list The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-postgres]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-postgres]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3287,10 +4091,10 @@ Try to set `comint-output-filter-functions' like this: '(comint-strip-ctrl-m))) \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'postgres)) + (interactive "P") + (sql-product-interactive 'postgres buffer)) -(defun sql-connect-postgres (product options) +(defun sql-comint-postgres (product options) "Create comint buffer and connect to Postgres." ;; username and password are ignored. Mark Stosberg suggest to add ;; the database at the end. Jason Beegan suggest using --pset and @@ -3304,12 +4108,14 @@ Try to set `comint-output-filter-functions' like this: (setq params (append (list "-h" sql-server) params))) (if (not (string= "" sql-user)) (setq params (append (list "-U" sql-user) params))) - (sql-connect product params))) + (if (not (= 0 sql-port)) + (setq params (append (list "-p" sql-port) params))) + (sql-comint product params))) ;;;###autoload -(defun sql-interbase () +(defun sql-interbase (&optional buffer) "Run isql by Interbase as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3323,6 +4129,11 @@ defaults, if set. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-interbase]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-interbase]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3331,10 +4142,10 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'interbase)) + (interactive "P") + (sql-product-interactive 'interbase buffer)) -(defun sql-connect-interbase (product options) +(defun sql-comint-interbase (product options) "Create comint buffer and connect to Interbase." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. @@ -3345,12 +4156,12 @@ The default comes from `process-coding-system-alist' and (setq params (append (list "-p" sql-password) params))) (if (not (string= "" sql-database)) (setq params (cons sql-database params))) ; add to the front! - (sql-connect product params))) + (sql-comint product params))) ;;;###autoload -(defun sql-db2 () +(defun sql-db2 (&optional buffer) "Run db2 by IBM as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3368,6 +4179,11 @@ db2, newlines will be escaped if necessary. If you don't want that, set `comint-input-sender' back to `comint-simple-send' by writing an after advice. See the elisp manual for more information. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-db2]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + To specify a coding system for converting non-ASCII characters in the input and output to the process, use \\[universal-coding-system-argument] before \\[sql-db2]. You can also specify this with \\[set-buffer-process-coding-system] @@ -3376,20 +4192,18 @@ The default comes from `process-coding-system-alist' and `default-process-coding-system'. \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'db2)) + (interactive "P") + (sql-product-interactive 'db2 buffer)) -(defun sql-connect-db2 (product options) +(defun sql-comint-db2 (product options) "Create comint buffer and connect to DB2." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. - (sql-connect product options) + (sql-comint product options) ) -;; ;; Properly escape newlines when DB2 is interactive. -;; (setq comint-input-sender 'sql-escape-newlines-and-send)) ;;;###autoload -(defun sql-linter () +(defun sql-linter (&optional buffer) "Run inl by RELEX as an inferior process. If buffer `*SQL*' exists but no process is running, make a new process. @@ -3411,11 +4225,16 @@ an empty password. The buffer is put in SQL interactive mode, giving commands for sending input. See `sql-interactive-mode'. +To set the buffer name directly, use \\[universal-argument] +before \\[sql-linter]. Once session has started, +\\[sql-rename-buffer] can be called separately to rename the +buffer. + \(Type \\[describe-mode] in the SQL buffer for a list of commands.)" - (interactive) - (sql-product-interactive 'linter)) + (interactive "P") + (sql-product-interactive 'linter buffer)) -(defun sql-connect-linter (product options) +(defun sql-comint-linter (product options) "Create comint buffer and connect to Linter." ;; Put all parameters to the program (if defined) in a list and call ;; make-comint. @@ -3430,13 +4249,11 @@ input. See `sql-interactive-mode'. (if (string= "" sql-database) (setenv "LINTER_MBX" nil) (setenv "LINTER_MBX" sql-database)) - (sql-connect product params) + (sql-comint product params) (setenv "LINTER_MBX" old-mbx))) (provide 'sql) -;; arch-tag: 7e1fa1c4-9ca2-402e-87d2-83a5eccb7ac3 ;;; sql.el ends here -