* lisp/progmodes/gdb-mi.el (gdb): Doc fix.
[bpt/emacs.git] / lisp / progmodes / sql.el
index 7f13891..97a1c46 100644 (file)
@@ -1,14 +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 <alex@gnu.org>
 ;; Maintainer: Michael Mauger <mmaug@yahoo.com>
-;; Version: 2.3
+;; Version: 3.0
 ;; Keywords: comm languages processes
-;; URL: http://savannah.gnu.org/cgi-bin/viewcvs/emacs/emacs/lisp/progmodes/sql.el
-;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?SqlMode
+;; URL: http://savannah.gnu.org/projects/emacs/
 
 ;; This file is part of GNU Emacs.
 
@@ -47,7 +45,7 @@
 ;; available in early versions of sql.el.  This support has been
 ;; extended and formalized in later versions.  Part of the impetus for
 ;; the improved support of SQL flavors was borne out of the current
-;; maintainer's consulting experience.  In the past fifteen years, I
+;; maintainers consulting experience.  In the past twenty years, I
 ;; have used Oracle, Sybase, Informix, MySQL, Postgres, and SQLServer.
 ;; On some assignments, I have used two or more of these concurrently.
 
 ;;    identifier characters.
 
 ;;     (sql-set-product-feature 'xyz
-;;                              :syntax-alist ((?# . "w")))
+;;                              :syntax-alist ((?# . "_")))
 
 ;; 4) Define the interactive command interpreter for the database
 ;;    product.
 
 ;;     (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)
-;;                        (const port)))
+;;       :type 'sql-login-params
 ;;       :group 'SQL)
 ;;
 ;;     (sql-set-product-feature 'xyz
 ;;     (sql-set-product-feature 'xyz
 ;;                              :sqli-comint-func 'my-sql-comint-xyz)
 
-;; 6) Define a convienence function to invoke the SQL interpreter.
+;; 6) Define a convenience 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:
 
 (eval-when-compile
   (require 'regexp-opt))
 (require 'custom)
+(require 'thingatpt)
 (eval-when-compile ;; needed in Emacs 19, 20
   (setq max-specpdl-size (max max-specpdl-size 2000)))
 
+(defun sql-signum (n)
+  "Return 1, 0, or -1 to identify the sign of N."
+  (cond
+   ((not (numberp n)) nil)
+   ((< n 0) -1)
+   ((> n 0) 1)
+   (t 0)))
+
 (defvar font-lock-keyword-face)
 (defvar font-lock-set-defaults)
 (defvar font-lock-string-face)
@@ -280,13 +282,48 @@ Customizing your password will store it in your ~/.emacs file."
   :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
   :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
@@ -298,7 +335,8 @@ Customizing your password will store it in your ~/.emacs file."
 (defvar sql-product-alist
   '((ansi
      :name "ANSI"
-     :font-lock sql-mode-ansi-font-lock-keywords)
+     :font-lock sql-mode-ansi-font-lock-keywords
+     :statement sql-ansi-statement-starters)
 
     (db2
      :name "DB2"
@@ -309,6 +347,7 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-comint-func sql-comint-db2
      :prompt-regexp "^db2 => "
      :prompt-length 7
+     :prompt-cont-regexp "^db2 (cont\.) => "
      :input-filter sql-escape-newlines-filter)
 
     (informix
@@ -330,7 +369,8 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-login sql-ingres-login-params
      :sqli-comint-func sql-comint-ingres
      :prompt-regexp "^\* "
-     :prompt-length 2)
+     :prompt-length 2
+     :prompt-cont-regexp "^\* ")
 
     (interbase
      :name "Interbase"
@@ -361,7 +401,7 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-comint-func sql-comint-ms
      :prompt-regexp "^[0-9]*>"
      :prompt-length 5
-     :syntax-alist ((?@ . "w"))
+     :syntax-alist ((?@ . "_"))
      :terminator ("^go" . "go"))
 
     (mysql
@@ -372,8 +412,12 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-options sql-mysql-options
      :sqli-login sql-mysql-login-params
      :sqli-comint-func sql-comint-mysql
+     :list-all "SHOW TABLES;"
+     :list-table "DESCRIBE %s;"
      :prompt-regexp "^mysql> "
      :prompt-length 6
+     :prompt-cont-regexp "^    -> "
+     :syntax-alist ((?# . "< b"))
      :input-filter sql-remove-tabs-filter)
 
     (oracle
@@ -383,10 +427,15 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-options sql-oracle-options
      :sqli-login sql-oracle-login-params
      :sqli-comint-func sql-comint-oracle
+     :list-all sql-oracle-list-all
+     :list-table sql-oracle-list-table
+     :completion-object sql-oracle-completion-object
      :prompt-regexp "^SQL> "
      :prompt-length 5
-     :syntax-alist ((?$ . "w") (?# . "w"))
-     :terminator ("\\(^/\\|;\\)" . "/")
+     :prompt-cont-regexp "^\\s-*[[:digit:]]+  "
+     :statement sql-oracle-statement-starters
+     :syntax-alist ((?$ . "_") (?# . "_"))
+     :terminator ("\\(^/\\|;\\)$" . "/")
      :input-filter sql-placeholders-filter)
 
     (postgres
@@ -397,10 +446,14 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-options sql-postgres-options
      :sqli-login sql-postgres-login-params
      :sqli-comint-func sql-comint-postgres
-     :prompt-regexp "^.*[#>] *"
+     :list-all ("\\d+" . "\\dS+")
+     :list-table ("\\d+ %s" . "\\dS+ %s")
+     :completion-object sql-postgres-completion-object
+     :prompt-regexp "^\\w*=[#>] "
      :prompt-length 5
+     :prompt-cont-regexp "^\\w*[-(][#>] "
      :input-filter sql-remove-tabs-filter
-     :terminator ("\\(^[\\]g\\|;\\)" . ";"))
+     :terminator ("\\(^\\s-*\\\\g$\\|;\\)" . "\\g"))
 
     (solid
      :name "Solid"
@@ -420,8 +473,13 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-options sql-sqlite-options
      :sqli-login sql-sqlite-login-params
      :sqli-comint-func sql-comint-sqlite
+     :list-all ".tables"
+     :list-table ".schema %s"
+     :completion-object sql-sqlite-completion-object
      :prompt-regexp "^sqlite> "
-     :prompt-length 8)
+     :prompt-length 8
+     :prompt-cont-regexp "^   \.\.\.> "
+     :terminator ";")
 
     (sybase
      :name "Sybase"
@@ -432,7 +490,7 @@ Customizing your password will store it in your ~/.emacs file."
      :sqli-comint-func sql-comint-sybase
      :prompt-regexp "^SQL> "
      :prompt-length 5
-     :syntax-alist ((?@ . "w"))
+     :syntax-alist ((?@ . "_"))
      :terminator ("^go" . "go"))
     )
   "An alist of product specific configuration settings.
@@ -471,10 +529,42 @@ may be any one of the following:
  :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
-                        comint buffer and connect to the
-                        database.  Do product specific
-                        configuration of comint in this function.
+                        `sql-database', `sql-server' and
+                        `sql-port' to open a comint buffer and
+                        connect to the 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.
+
+ :completion-object     A function that returns a list of
+                        objects.  Called with a single
+                        parameter--if nil then list objects
+                        accessible in the current schema, if
+                        not-nil it is the name of a schema whose
+                        objects should be listed.
+
+ :completion-column     A function that returns a list of
+                        columns.  Called with a single
+                        parameter--if nil then list objects
+                        accessible in the current schema, if
+                        not-nil it is the name of a schema whose
+                        objects should be listed.
 
  :prompt-regexp         regular expression string that matches
                         the prompt issued by the product
@@ -482,6 +572,10 @@ may be any one of the following:
 
  :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',
@@ -489,7 +583,11 @@ 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.
+
+ :statement             name of a variable containing a regexp that
+                        matches the beginning of SQL statements.
 
  :terminator            the terminator to be sent after a
                         `sql-send-string', `sql-send-region',
@@ -510,9 +608,8 @@ using `sql-get-product-feature' to lookup the product specific
 settings.")
 
 (defvar sql-indirect-features
-  '(:font-lock :sqli-program :sqli-options :sqli-login))
+  '(:font-lock :sqli-program :sqli-options :sqli-login :statement))
 
-;;;###autoload
 (defcustom sql-connection-alist nil
   "An alist of connection parameters for interacting with a SQL
   product.
@@ -561,7 +658,6 @@ prompted for during login."
   :version "24.1"
   :group 'SQL)
 
-;;;###autoload
 (defcustom sql-product 'ansi
   "Select the SQL database product used so that buffers can be
 highlighted properly when you open them."
@@ -574,6 +670,7 @@ highlighted properly when you open them."
                     sql-product-alist))
   :group 'SQL
   :safe 'symbolp)
+(defvaralias 'sql-dialect 'sql-product)
 
 ;; misc customization of sql.el behaviour
 
@@ -620,6 +717,13 @@ it automatically."
   :version "22.2"
   :group 'SQL)
 
+(defvar sql-contains-names nil
+  "When non-nil, the current buffer contains database names.
+
+Globally should be set to nil; it will be non-nil in `sql-mode',
+`sql-interactive-mode' and list all buffers.")
+
+
 (defcustom sql-pop-to-buffer-after-send-region nil
   "When non-nil, pop to the buffer SQL statements are sent to.
 
@@ -707,6 +811,19 @@ is changed."
   :type 'hook
   :group 'SQL)
 
+;; Customization for ANSI
+
+(defcustom sql-ansi-statement-starters (regexp-opt '(
+ "create" "alter" "drop"
+ "select" "insert" "update" "delete" "merge"
+ "grant" "revoke"
+))
+  "Regexp of keywords that start SQL commands
+
+All products share this list; products should define a regexp to
+identify additional keywords in a variable defined by
+the :statement feature.")
+
 ;; Customization for Oracle
 
 (defcustom sql-oracle-program "sqlplus"
@@ -728,33 +845,34 @@ 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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
+(defcustom sql-oracle-statement-starters (regexp-opt '("declare" "begin" "with"))
+  "Additional statement starting keywords in Oracle.")
+
 (defcustom sql-oracle-scan-on t
   "Non-nil if placeholders should be replaced in Oracle SQLi.
 
 When non-nil, Emacs will scan text sent to sqlplus and prompt
 for replacement text for & placeholders as sqlplus does.  This
-is needed on Windows where sqlplus output is buffered and the
+is needed on Windows where SQL*Plus output is buffered and the
 prompts are not shown until after the text is entered.
 
-You will probably want to issue the following command in sqlplus
-to be safe:
+You need to issue the following command in SQL*Plus to be safe:
 
-    SET SCAN OFF"
+    SET DEFINE OFF
+
+In older versions of SQL*Plus, this was the SET SCAN OFF command."
   :type 'boolean
   :group 'SQL)
 
 ;; 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."
@@ -767,18 +885,13 @@ 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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
-;; Customization for MySql
+;; Customization for MySQL
 
 (defcustom sql-mysql-program "mysql"
   "Command to start mysql by TcX.
@@ -796,13 +909,8 @@ on Windows: \"-C\" \"-t\" \"-f\" \"-n\"."
   :group 'SQL)
 
 (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)))
+  "List of login parameters needed to connect to MySQL."
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -817,12 +925,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -844,12 +947,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -864,12 +962,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -884,12 +977,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -911,12 +999,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -941,14 +1024,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -969,12 +1049,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -995,12 +1070,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -1021,12 +1091,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)
-                  (const port)))
+  :type 'sql-login-params
   :version "24.1"
   :group 'SQL)
 
@@ -1043,11 +1108,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.
 
@@ -1067,11 +1135,35 @@ 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 connection)
+  "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 or CONNECTION are
+specified, it's `sql-product' or `sql-connection' 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))
+                (or (not connection)
+                    (eq connection sql-connection)))))))
+
 ;; Keymap for sql-interactive-mode.
 
 (defvar sql-interactive-mode-map
@@ -1087,6 +1179,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'.")
@@ -1100,6 +1194,10 @@ 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)
+    (define-key map [remap beginning-of-defun] 'sql-beginning-of-statement)
+    (define-key map [remap end-of-defun] 'sql-end-of-statement)
     map)
   "Mode map used for `sql-mode'.")
 
@@ -1109,15 +1207,16 @@ 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))]
+                                      (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 (and (sql-buffer-live-p sql-buffer)
+                                         (sql-get-product-feature sql-product :list-all))]
+   ["List table details" sql-list-table (and (sql-buffer-live-p sql-buffer)
+                                             (sql-get-product-feature sql-product :list-table))]
    "--"
    ["Start SQLi session" sql-product-interactive
     :visible (not sql-connection-alist)
@@ -1157,7 +1256,10 @@ Based on `comint-mode-map'.")
  "Menu for `sql-interactive-mode'."
  '("SQL"
    ["Rename Buffer" sql-rename-buffer t]
-   ["Save Connection" sql-save-connection (not sql-connection)]))
+   ["Save Connection" sql-save-connection (not sql-connection)]
+   "--"
+   ["List all objects" sql-list-all (sql-get-product-feature sql-product :list-all)]
+   ["List table details" sql-list-table (sql-get-product-feature sql-product :list-table)]))
 
 ;; Abbreviations -- if you want more of them, define them in your
 ;; ~/.emacs file.  Abbrevs have to be enabled in your ~/.emacs, too.
@@ -1169,13 +1271,13 @@ Based on `comint-mode-map'.")
 
 (mapc
  ;; In Emacs 22+, provide SYSTEM-FLAG to define-abbrev.
'(lambda (abbrev)
-    (let ((name (car abbrev))
-          (expansion (cdr abbrev)))
-      (condition-case nil
-          (define-abbrev sql-mode-abbrev-table name expansion nil 0 t)
-        (error
-         (define-abbrev sql-mode-abbrev-table name expansion)))))
+ (lambda (abbrev)
+   (let ((name (car abbrev))
+         (expansion (cdr abbrev)))
+     (condition-case nil
+         (define-abbrev sql-mode-abbrev-table name expansion nil 0 t)
+       (error
+        (define-abbrev sql-mode-abbrev-table name expansion)))))
  '(("ins"  . "insert")
    ("upd"  . "update")
    ("del"  . "delete")
@@ -1200,8 +1302,9 @@ Based on `comint-mode-map'.")
     (modify-syntax-entry ?' "\"" table)
     ;; double quotes (") don't delimit strings
     (modify-syntax-entry ?\" "." table)
-    ;; backslash is no escape character
-    (modify-syntax-entry ?\\ "." table)
+    ;; Make these all punctuation
+    (mapc (lambda (c) (modify-syntax-entry c "." table))
+          (string-to-list "!#$%&+,.:;<=>?@\\|"))
     table)
   "Syntax table used in `sql-mode' and `sql-interactive-mode'.")
 
@@ -1260,20 +1363,45 @@ statement.  The format of variable should be a valid
 
       ;; Remove keywords that are defined in ANSI
       (setq kwd keywords)
-      (dolist (k keywords)
-       (catch 'next
-         (dolist (a sql-mode-ansi-font-lock-keywords)
-           (when (and (eq face (cdr a))
-                      (eq (string-match (car a) k 0) 0)
-                      (eq (match-end 0) (length k)))
-             (setq kwd (delq k kwd))
-             (throw 'next nil)))))
+      ;; (dolist (k keywords)
+      ;;   (catch 'next
+      ;;     (dolist (a sql-mode-ansi-font-lock-keywords)
+      ;;       (when (and (eq face (cdr a))
+      ;;              (eq (string-match (car a) k 0) 0)
+      ;;              (eq (match-end 0) (length k)))
+      ;;         (setq kwd (delq k kwd))
+      ;;         (throw 'next nil)))))
 
       ;; Create a properly formed font-lock-keywords item
       (cons (concat (car bdy)
                    (regexp-opt kwd t)
                    (cdr bdy))
-           face))))
+           face)))
+
+  (defun sql-regexp-abbrev (keyword)
+    (let ((brk   (string-match "[~]" keyword))
+          (len   (length keyword))
+          (sep   "\\(?:")
+          re i)
+      (if (not brk)
+          keyword
+        (setq re  (substring keyword 0 brk)
+              i   (+ 2 brk)
+              brk (1+ brk))
+        (while (<= i len)
+          (setq re  (concat re sep (substring keyword brk i))
+                sep "\\|"
+                i   (1+ i)))
+        (concat re "\\)?"))))
+
+  (defun sql-regexp-abbrev-list (&rest keyw-list)
+    (let ((re nil)
+          (sep "\\<\\(?:"))
+      (while keyw-list
+        (setq re (concat re sep (sql-regexp-abbrev (car keyw-list)))
+              sep "\\|"
+              keyw-list (cdr keyw-list)))
+      (concat re "\\)\\>"))))
 
 (eval-when-compile
   (setq sql-mode-ansi-font-lock-keywords
@@ -1308,6 +1436,7 @@ statement.  The format of variable should be a valid
 "user_defined_type_catalog" "user_defined_type_name"
 "user_defined_type_schema"
 )
+
         ;; ANSI Reserved keywords
         (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
 "absolute" "action" "add" "admin" "after" "aggregate" "alias" "all"
@@ -1357,6 +1486,7 @@ statement.  The format of variable should be a valid
 "substring" "sum" "system_user" "translate" "treat" "trim" "upper"
 "user"
 )
+
         ;; ANSI Data Types
         (sql-font-lock-keywords-builder 'font-lock-type-face nil
 "array" "binary" "bit" "blob" "boolean" "char" "character" "clob"
@@ -1376,86 +1506,142 @@ function `regexp-opt'.  Therefore, take a look at the source before
 you define your own `sql-mode-ansi-font-lock-keywords'.  You may want
 to add functions and PL/SQL keywords.")
 
+(defun sql-oracle-show-reserved-words ()
+  ;; This function is for use by the maintainer of SQL.EL only.
+  (interactive)
+  (if (or (and (not (derived-mode-p 'sql-mode))
+               (not (derived-mode-p 'sql-interactive-mode)))
+          (not sql-buffer)
+          (not (eq sql-product 'oracle)))
+      (error "Not an Oracle buffer")
+
+    (let ((b "*RESERVED WORDS*"))
+      (sql-execute sql-buffer b
+                   (concat "SELECT "
+                           "  keyword "
+                           ", reserved AS \"Res\" "
+                           ", res_type AS \"Type\" "
+                           ", res_attr AS \"Attr\" "
+                           ", res_semi AS \"Semi\" "
+                           ", duplicate AS \"Dup\" "
+                           "FROM V$RESERVED_WORDS "
+                           "WHERE length > 1 "
+                           "AND SUBSTR(keyword, 1, 1) BETWEEN 'A' AND 'Z' "
+                           "ORDER BY 2 DESC, 3 DESC, 4 DESC, 5 DESC, 6 DESC, 1;")
+                   nil nil)
+      (with-current-buffer b
+        (set (make-local-variable 'sql-product) 'oracle)
+        (sql-product-font-lock t nil)
+        (font-lock-mode +1)))))
+
 (defvar sql-mode-oracle-font-lock-keywords
   (eval-when-compile
     (list
      ;; Oracle SQL*Plus Commands
-     (cons
-      (concat
-       "^\\(?:\\(?:" (regexp-opt '(
-"@" "@@" "accept" "append" "archive" "attribute" "break"
-"btitle" "change" "clear" "column" "connect" "copy" "define"
-"del" "describe" "disconnect" "edit" "execute" "exit" "get" "help"
-"host" "input" "list" "password" "pause" "print" "prompt" "recover"
-"remark" "repfooter" "repheader" "run" "save" "show" "shutdown"
-"spool" "start" "startup" "store" "timing" "ttitle" "undefine"
-"variable" "whenever"
-) t)
+     ;;   Only recognized in they start in column 1 and the
+     ;;   abbreviation is followed by a space or the end of line.
 
-       "\\)\\|"
-       "\\(?:compute\\s-+\\(?:avg\\|cou\\|min\\|max\\|num\\|sum\\|std\\|var\\)\\)\\|"
-       "\\(?:set\\s-+\\("
-
-       (regexp-opt
-       '("appi" "appinfo" "array" "arraysize" "auto" "autocommit"
-         "autop" "autoprint" "autorecovery" "autot" "autotrace" "blo"
-         "blockterminator" "buffer" "closecursor" "cmds" "cmdsep"
-         "colsep" "com" "compatibility" "con" "concat" "constraint"
-         "constraints" "copyc" "copycommit" "copytypecheck" "database"
-         "def" "define" "document" "echo" "editf" "editfile" "emb"
-         "embedded" "esc" "escape" "feed" "feedback" "flagger" "flu"
-         "flush" "hea" "heading" "heads" "headsep" "instance" "lin"
-         "linesize" "lobof" "loboffset" "logsource" "long" "longc"
-         "longchunksize" "maxdata" "newp" "newpage" "null" "num"
-         "numf" "numformat" "numwidth" "pages" "pagesize" "pau"
-         "pause" "recsep" "recsepchar" "role" "scan" "serveroutput"
-         "shift" "shiftinout" "show" "showmode" "space" "sqlbl"
-         "sqlblanklines" "sqlc" "sqlcase" "sqlco" "sqlcontinue" "sqln"
-         "sqlnumber" "sqlp" "sqlpluscompat" "sqlpluscompatibility"
-         "sqlpre" "sqlprefix" "sqlprompt" "sqlt" "sqlterminator"
-         "statement_id" "suf" "suffix" "tab" "term" "termout" "ti"
-         "time" "timi" "timing" "transaction" "trim" "trimout" "trims"
-         "trimspool" "truncate" "und" "underline" "ver" "verify" "wra"
-         "wrap")) "\\)\\)"
-
-       "\\)\\b.*"
-       )
-      'font-lock-doc-face)
-     '("^[ \t]*rem\\(?:ark\\)?.*" . font-lock-comment-face)
+     "\\|"
+     (list (concat "^" (sql-regexp-abbrev "rem~ark") "\\(?:\\s-.*\\)?$")
+           0 'font-lock-comment-face t)
+
+     (list
+      (concat
+       "^\\(?:"
+       (sql-regexp-abbrev-list
+        "[@]\\{1,2\\}" "acc~ept" "a~ppend" "archive" "attribute"
+        "bre~ak" "bti~tle" "c~hange" "cl~ear" "col~umn" "conn~ect"
+        "copy" "def~ine" "del" "desc~ribe" "disc~onnect" "ed~it"
+        "exec~ute" "exit" "get" "help" "ho~st" "[$]" "i~nput" "l~ist"
+        "passw~ord" "pau~se" "pri~nt" "pro~mpt" "quit" "recover"
+        "repf~ooter" "reph~eader" "r~un" "sav~e" "sho~w" "shutdown"
+        "spo~ol" "sta~rt" "startup" "store" "tim~ing" "tti~tle"
+        "undef~ine" "var~iable" "whenever")
+       "\\|"
+       (concat "\\(?:"
+               (sql-regexp-abbrev "comp~ute")
+               "\\s-+"
+               (sql-regexp-abbrev-list
+                "avg" "cou~nt" "min~imum" "max~imum" "num~ber" "sum"
+                "std" "var~iance")
+               "\\)")
+       "\\|"
+       (concat "\\(?:set\\s-+"
+               (sql-regexp-abbrev-list
+                "appi~nfo" "array~size" "auto~commit" "autop~rint"
+                "autorecovery" "autot~race" "blo~ckterminator"
+                "cmds~ep" "colsep" "com~patibility" "con~cat"
+                "copyc~ommit" "copytypecheck" "def~ine" "describe"
+                "echo" "editf~ile" "emb~edded" "esc~ape" "feed~back"
+                "flagger" "flu~sh" "hea~ding" "heads~ep" "instance"
+                "lin~esize" "lobof~fset" "long" "longc~hunksize"
+                "mark~up" "newp~age" "null" "numf~ormat" "num~width"
+                "pages~ize" "pau~se" "recsep" "recsepchar"
+                "scan" "serverout~put" "shift~inout" "show~mode"
+                "sqlbl~anklines" "sqlc~ase" "sqlco~ntinue"
+                "sqln~umber" "sqlpluscompat~ibility" "sqlpre~fix"
+                "sqlp~rompt" "sqlt~erminator" "suf~fix" "tab"
+                "term~out" "ti~me" "timi~ng" "trim~out" "trims~pool"
+                "und~erline" "ver~ify" "wra~p")
+               "\\)")
+
+       "\\)\\(?:\\s-.*\\)?\\(?:[-]\n.*\\)*$")
+      0 'font-lock-doc-face t)
 
      ;; Oracle Functions
      (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
-"abs" "acos" "add_months" "ascii" "asciistr" "asin" "atan" "atan2"
-"avg" "bfilename" "bin_to_num" "bitand" "cast" "ceil" "chartorowid"
-"chr" "coalesce" "compose" "concat" "convert" "corr" "cos" "cosh"
-"count" "covar_pop" "covar_samp" "cume_dist" "current_date"
-"current_timestamp" "current_user" "dbtimezone" "decode" "decompose"
-"dense_rank" "depth" "deref" "dump" "empty_clob" "existsnode" "exp"
-"extract" "extractvalue" "first" "first_value" "floor" "following"
-"from_tz" "greatest" "group_id" "grouping_id" "hextoraw" "initcap"
-"instr" "lag" "last" "last_day" "last_value" "lead" "least" "length"
-"ln" "localtimestamp" "lower" "lpad" "ltrim" "make_ref" "max" "min"
-"mod" "months_between" "new_time" "next_day" "nls_charset_decl_len"
+"abs" "acos" "add_months" "appendchildxml" "ascii" "asciistr" "asin"
+"atan" "atan2" "avg" "bfilename" "bin_to_num" "bitand" "cardinality"
+"cast" "ceil" "chartorowid" "chr" "cluster_id" "cluster_probability"
+"cluster_set" "coalesce" "collect" "compose" "concat" "convert" "corr"
+"corr_k" "corr_s" "cos" "cosh" "count" "covar_pop" "covar_samp"
+"cube_table" "cume_dist" "currrent_date" "currrent_timestamp" "cv"
+"dataobj_to_partition" "dbtimezone" "decode" "decompose" "deletexml"
+"dense_rank" "depth" "deref" "dump" "empty_blob" "empty_clob"
+"existsnode" "exp" "extract" "extractvalue" "feature_id" "feature_set"
+"feature_value" "first" "first_value" "floor" "from_tz" "greatest"
+"grouping" "grouping_id" "group_id" "hextoraw" "initcap"
+"insertchildxml" "insertchildxmlafter" "insertchildxmlbefore"
+"insertxmlafter" "insertxmlbefore" "instr" "instr2" "instr4" "instrb"
+"instrc" "iteration_number" "lag" "last" "last_day" "last_value"
+"lead" "least" "length" "length2" "length4" "lengthb" "lengthc"
+"listagg" "ln" "lnnvl" "localtimestamp" "log" "lower" "lpad" "ltrim"
+"make_ref" "max" "median" "min" "mod" "months_between" "nanvl" "nchr"
+"new_time" "next_day" "nlssort" "nls_charset_decl_len"
 "nls_charset_id" "nls_charset_name" "nls_initcap" "nls_lower"
-"nls_upper" "nlssort" "ntile" "nullif" "numtodsinterval"
-"numtoyminterval" "nvl" "nvl2" "over" "path" "percent_rank"
-"percentile_cont" "percentile_disc" "power" "preceding" "rank"
-"ratio_to_report" "rawtohex" "rawtonhex" "reftohex" "regr_"
-"regr_avgx" "regr_avgy" "regr_count" "regr_intercept" "regr_r2"
-"regr_slope" "regr_sxx" "regr_sxy" "regr_syy" "replace" "round"
-"row_number" "rowidtochar" "rowidtonchar" "rpad" "rtrim"
-"sessiontimezone" "sign" "sin" "sinh" "soundex" "sqrt" "stddev"
-"stddev_pop" "stddev_samp" "substr" "sum" "sys_connect_by_path"
-"sys_context" "sys_dburigen" "sys_extract_utc" "sys_guid" "sys_typeid"
-"sys_xmlagg" "sys_xmlgen" "sysdate" "systimestamp" "tan" "tanh"
+"nls_upper" "nth_value" "ntile" "nullif" "numtodsinterval"
+"numtoyminterval" "nvl" "nvl2" "ora_dst_affected" "ora_dst_convert"
+"ora_dst_error" "ora_hash" "path" "percentile_cont" "percentile_disc"
+"percent_rank" "power" "powermultiset" "powermultiset_by_cardinality"
+"prediction" "prediction_bounds" "prediction_cost"
+"prediction_details" "prediction_probability" "prediction_set"
+"presentnnv" "presentv" "previous" "rank" "ratio_to_report" "rawtohex"
+"rawtonhex" "ref" "reftohex" "regexp_count" "regexp_instr"
+"regexp_replace" "regexp_substr" "regr_avgx" "regr_avgy" "regr_count"
+"regr_intercept" "regr_r2" "regr_slope" "regr_sxx" "regr_sxy"
+"regr_syy" "remainder" "replace" "round" "rowidtochar" "rowidtonchar"
+"row_number" "rpad" "rtrim" "scn_to_timestamp" "sessiontimezone" "set"
+"sign" "sin" "sinh" "soundex" "sqrt" "stats_binomial_test"
+"stats_crosstab" "stats_f_test" "stats_ks_test" "stats_mode"
+"stats_mw_test" "stats_one_way_anova" "stats_t_test_indep"
+"stats_t_test_indepu" "stats_t_test_one" "stats_t_test_paired"
+"stats_wsr_test" "stddev" "stddev_pop" "stddev_samp" "substr"
+"substr2" "substr4" "substrb" "substrc" "sum" "sysdate" "systimestamp"
+"sys_connect_by_path" "sys_context" "sys_dburigen" "sys_extract_utc"
+"sys_guid" "sys_typeid" "sys_xmlagg" "sys_xmlgen" "tan" "tanh"
+"timestamp_to_scn" "to_binary_double" "to_binary_float" "to_blob"
 "to_char" "to_clob" "to_date" "to_dsinterval" "to_lob" "to_multi_byte"
 "to_nchar" "to_nclob" "to_number" "to_single_byte" "to_timestamp"
 "to_timestamp_tz" "to_yminterval" "translate" "treat" "trim" "trunc"
-"tz_offset" "uid" "unbounded" "unistr" "updatexml" "upper" "user"
-"userenv" "var_pop" "var_samp" "variance" "vsize" "width_bucket" "xml"
-"xmlagg" "xmlattribute" "xmlcolattval" "xmlconcat" "xmlelement"
-"xmlforest" "xmlsequence" "xmltransform"
+"tz_offset" "uid" "unistr" "updatexml" "upper" "user" "userenv"
+"value" "variance" "var_pop" "var_samp" "vsize" "width_bucket"
+"xmlagg" "xmlcast" "xmlcdata" "xmlcolattval" "xmlcomment" "xmlconcat"
+"xmldiff" "xmlelement" "xmlexists" "xmlforest" "xmlisvalid" "xmlparse"
+"xmlpatch" "xmlpi" "xmlquery" "xmlroot" "xmlsequence" "xmlserialize"
+"xmltable" "xmltransform"
 )
+
+     ;; See the table V$RESERVED_WORDS
      ;; Oracle Keywords
      (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
 "abort" "access" "accessed" "account" "activate" "add" "admin"
@@ -1544,52 +1730,120 @@ to add functions and PL/SQL keywords.")
 "varray" "version" "view" "wait" "when" "whenever" "where" "with"
 "without" "wnds" "wnps" "work" "write" "xmldata" "xmlschema" "xmltype"
 )
+
      ;; Oracle Data Types
      (sql-font-lock-keywords-builder 'font-lock-type-face nil
-"bfile" "blob" "byte" "char" "character" "clob" "date" "dec" "decimal"
-"double" "float" "int" "integer" "interval" "long" "national" "nchar"
-"nclob" "number" "numeric" "nvarchar2" "precision" "raw" "real"
-"rowid" "second" "smallint" "time" "timestamp" "urowid" "varchar"
-"varchar2" "varying" "year" "zone"
+"bfile" "binary_double" "binary_float" "blob" "byte" "char" "charbyte"
+"clob" "date" "day" "float" "interval" "local" "long" "longraw"
+"minute" "month" "nchar" "nclob" "number" "nvarchar2" "raw" "rowid" "second"
+"time" "timestamp" "urowid" "varchar2" "with" "year" "zone"
 )
 
      ;; Oracle PL/SQL Attributes
-     (sql-font-lock-keywords-builder 'font-lock-builtin-face '("" . "\\b")
-"%bulk_rowcount" "%found" "%isopen" "%notfound" "%rowcount" "%rowtype"
-"%type"
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face '("%" . "\\b")
+"bulk_exceptions" "bulk_rowcount" "found" "isopen" "notfound"
+"rowcount" "rowtype" "type"
 )
 
      ;; Oracle PL/SQL Functions
      (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
-"extend" "prior"
+"delete" "trim" "extend" "exists" "first" "last" "count" "limit"
+"prior" "next"
+)
+
+     ;; Oracle PL/SQL Reserved words
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"all" "alter" "and" "any" "as" "asc" "at" "begin" "between" "by"
+"case" "check" "clusters" "cluster" "colauth" "columns" "compress"
+"connect" "crash" "create" "cursor" "declare" "default" "desc"
+"distinct" "drop" "else" "end" "exception" "exclusive" "fetch" "for"
+"from" "function" "goto" "grant" "group" "having" "identified" "if"
+"in" "index" "indexes" "insert" "intersect" "into" "is" "like" "lock"
+"minus" "mode" "nocompress" "not" "nowait" "null" "of" "on" "option"
+"or" "order" "overlaps" "procedure" "public" "resource" "revoke"
+"select" "share" "size" "sql" "start" "subtype" "tabauth" "table"
+"then" "to" "type" "union" "unique" "update" "values" "view" "views"
+"when" "where" "with"
+
+"true" "false"
+"raise_application_error"
 )
 
      ;; Oracle PL/SQL Keywords
      (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
-"autonomous_transaction" "bulk" "char_base" "collect" "constant"
-"cursor" "declare" "do" "elsif" "exception_init" "execute" "exit"
-"extends" "false" "fetch" "forall" "goto" "hour" "if" "interface"
-"loop" "minute" "number_base" "ocirowid" "opaque" "others" "rowtype"
-"separate" "serially_reusable" "sql" "sqlcode" "sqlerrm" "subtype"
-"the" "timezone_abbr" "timezone_hour" "timezone_minute"
-"timezone_region" "true" "varrying" "while"
+"a" "add" "agent" "aggregate" "array" "attribute" "authid" "avg"
+"bfile_base" "binary" "blob_base" "block" "body" "both" "bound" "bulk"
+"byte" "c" "call" "calling" "cascade" "char" "char_base" "character"
+"charset" "charsetform" "charsetid" "clob_base" "close" "collect"
+"comment" "commit" "committed" "compiled" "constant" "constructor"
+"context" "continue" "convert" "count" "current" "customdatum"
+"dangling" "data" "date" "date_base" "day" "define" "delete"
+"deterministic" "double" "duration" "element" "elsif" "empty" "escape"
+"except" "exceptions" "execute" "exists" "exit" "external" "final"
+"fixed" "float" "forall" "force" "general" "hash" "heap" "hidden"
+"hour" "immediate" "including" "indicator" "indices" "infinite"
+"instantiable" "int" "interface" "interval" "invalidate" "isolation"
+"java" "language" "large" "leading" "length" "level" "library" "like2"
+"like4" "likec" "limit" "limited" "local" "long" "loop" "map" "max"
+"maxlen" "member" "merge" "min" "minute" "mod" "modify" "month"
+"multiset" "name" "nan" "national" "native" "nchar" "new" "nocopy"
+"number_base" "object" "ocicoll" "ocidate" "ocidatetime" "ociduration"
+"ociinterval" "ociloblocator" "ocinumber" "ociraw" "ociref"
+"ocirefcursor" "ocirowid" "ocistring" "ocitype" "old" "only" "opaque"
+"open" "operator" "oracle" "oradata" "organization" "orlany" "orlvary"
+"others" "out" "overriding" "package" "parallel_enable" "parameter"
+"parameters" "parent" "partition" "pascal" "pipe" "pipelined" "pragma"
+"precision" "prior" "private" "raise" "range" "raw" "read" "record"
+"ref" "reference" "relies_on" "rem" "remainder" "rename" "result"
+"result_cache" "return" "returning" "reverse" "rollback" "row"
+"sample" "save" "savepoint" "sb1" "sb2" "sb4" "second" "segment"
+"self" "separate" "sequence" "serializable" "set" "short" "size_t"
+"some" "sparse" "sqlcode" "sqldata" "sqlname" "sqlstate" "standard"
+"static" "stddev" "stored" "string" "struct" "style" "submultiset"
+"subpartition" "substitutable" "sum" "synonym" "tdo" "the" "time"
+"timestamp" "timezone_abbr" "timezone_hour" "timezone_minute"
+"timezone_region" "trailing" "transaction" "transactional" "trusted"
+"ub1" "ub2" "ub4" "under" "unsigned" "untrusted" "use" "using"
+"valist" "value" "variable" "variance" "varray" "varying" "void"
+"while" "work" "wrapped" "write" "year" "zone"
+;; Pragma
+"autonomous_transaction" "exception_init" "inline"
+"restrict_references" "serially_reusable"
 )
 
      ;; Oracle PL/SQL Data Types
      (sql-font-lock-keywords-builder 'font-lock-type-face nil
-"binary_integer" "boolean" "naturaln" "pls_integer" "positive"
-"positiven" "record" "signtype" "string"
+"\"BINARY LARGE OBJECT\"" "\"CHAR LARGE OBJECT\"" "\"CHAR VARYING\""
+"\"CHARACTER LARGE OBJECT\"" "\"CHARACTER VARYING\""
+"\"DOUBLE PRECISION\"" "\"INTERVAL DAY TO SECOND\""
+"\"INTERVAL YEAR TO MONTH\"" "\"LONG RAW\"" "\"NATIONAL CHAR\""
+"\"NATIONAL CHARACTER LARGE OBJECT\"" "\"NATIONAL CHARACTER\""
+"\"NCHAR LARGE OBJECT\"" "\"NCHAR\"" "\"NCLOB\"" "\"NVARCHAR2\""
+"\"TIME WITH TIME ZONE\"" "\"TIMESTAMP WITH LOCAL TIME ZONE\""
+"\"TIMESTAMP WITH TIME ZONE\""
+"bfile" "bfile_base" "binary_double" "binary_float" "binary_integer"
+"blob" "blob_base" "boolean" "char" "character" "char_base" "clob"
+"clob_base" "cursor" "date" "day" "dec" "decimal"
+"dsinterval_unconstrained" "float" "int" "integer" "interval" "local"
+"long" "mlslabel" "month" "natural" "naturaln" "nchar_cs" "number"
+"number_base" "numeric" "pls_integer" "positive" "positiven" "raw"
+"real" "ref" "rowid" "second" "signtype" "simple_double"
+"simple_float" "simple_integer" "smallint" "string" "time" "timestamp"
+"timestamp_ltz_unconstrained" "timestamp_tz_unconstrained"
+"timestamp_unconstrained" "time_tz_unconstrained" "time_unconstrained"
+"to" "urowid" "varchar" "varchar2" "with" "year"
+"yminterval_unconstrained" "zone"
 )
 
      ;; Oracle PL/SQL Exceptions
      (sql-font-lock-keywords-builder 'font-lock-warning-face nil
 "access_into_null" "case_not_found" "collection_is_null"
 "cursor_already_open" "dup_val_on_index" "invalid_cursor"
-"invalid_number" "login_denied" "no_data_found" "not_logged_on"
-"program_error" "rowtype_mismatch" "self_is_null" "storage_error"
-"subscript_beyond_count" "subscript_outside_limit" "sys_invalid_rowid"
-"timeout_on_resource" "too_many_rows" "value_error" "zero_divide"
-"exception" "notfound"
+"invalid_number" "login_denied" "no_data_found" "no_data_needed"
+"not_logged_on" "program_error" "rowtype_mismatch" "self_is_null"
+"storage_error" "subscript_beyond_count" "subscript_outside_limit"
+"sys_invalid_rowid" "timeout_on_resource" "too_many_rows"
+"value_error" "zero_divide"
 )))
 
   "Oracle SQL keywords used by font-lock.
@@ -1603,81 +1857,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.
@@ -1994,7 +2320,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
@@ -2021,6 +2394,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'.
 
@@ -2129,26 +2512,24 @@ also be configured."
 
   (let
       ;; Get the product-specific syntax-alist.
-      ((syntax-alist
-       (append
-        (sql-get-product-feature sql-product :syntax-alist)
-        '((?_ . "w") (?. . "w")))))
+      ((syntax-alist (sql-product-font-lock-syntax-alist)))
 
     ;; 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.
@@ -2204,13 +2585,46 @@ 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)))
+
 \f
 
 ;;; Functions to switch highlighting
 
+(defun sql-product-syntax-table ()
+  (let ((table (copy-syntax-table sql-mode-syntax-table)))
+    (mapc (lambda (entry)
+            (modify-syntax-entry (car entry) (cdr entry) table))
+          (sql-get-product-feature sql-product :syntax-alist))
+    table))
+
+(defun sql-product-font-lock-syntax-alist ()
+  (append
+   ;; Change all symbol character to word characters
+   (mapcar
+    (lambda (entry) (if (string= (substring (cdr entry) 0 1) "_")
+                        (cons (car entry)
+                              (concat "w" (substring (cdr entry) 1)))
+                      entry))
+    (sql-get-product-feature sql-product :syntax-alist))
+   '((?_ . "w"))))
+
 (defun sql-highlight-product ()
   "Turn on the font highlighting for the SQL product selected."
   (when (derived-mode-p 'sql-mode)
+    ;; Enhance the syntax table for the product
+    (set-syntax-table (sql-product-syntax-table))
+
     ;; Setup font-lock
     (sql-product-font-lock nil t)
 
@@ -2221,11 +2635,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)
@@ -2242,11 +2652,77 @@ adds a fontification pattern to fontify identifiers ending in
     ;; comint-line-beginning-position is defined in Emacs 21
     (defun comint-line-beginning-position ()
       "Return the buffer position of the beginning of the line, after any prompt.
-The prompt is assumed to be any text at the beginning of the line matching
-the regular expression `comint-prompt-regexp', a buffer local variable."
+The prompt is assumed to be any text at the beginning of the line
+matching the regular expression `comint-prompt-regexp', a buffer
+local variable."
       (save-excursion (comint-bol nil) (point))))
 
-\f
+;;; Motion Functions
+
+(defun sql-statement-regexp (prod)
+  (let* ((ansi-stmt (sql-get-product-feature 'ansi :statement))
+         (prod-stmt (sql-get-product-feature prod  :statement)))
+    (concat "^\\<"
+            (if prod-stmt
+                ansi-stmt
+              (concat "\\(" ansi-stmt "\\|" prod-stmt "\\)"))
+            "\\>")))
+
+(defun sql-beginning-of-statement (arg)
+  "Moves the cursor to the beginning of the current SQL statement."
+  (interactive "p")
+
+  (let ((here (point))
+        (regexp (sql-statement-regexp sql-product))
+        last next)
+
+    ;; Go to the end of the statement before the start we desire
+    (setq last (or (sql-end-of-statement (- arg))
+                   (point-min)))
+    ;; And find the end after that
+    (setq next (or (sql-end-of-statement 1)
+                   (point-max)))
+
+    ;; Our start must be between them
+    (goto-char last)
+    ;; Find an beginning-of-stmt that's not in a comment
+    (while (and (re-search-forward regexp next t 1)
+                (nth 7 (syntax-ppss)))
+      (goto-char (match-end 0)))
+    (goto-char
+     (if (match-data)
+        (match-beginning 0)
+       last))
+    (beginning-of-line)
+    ;; If we didn't move, try again
+    (when (= here (point))
+      (sql-beginning-of-statement (* 2 (sql-signum arg))))))
+
+(defun sql-end-of-statement (arg)
+  "Moves the cursor to the end of the current SQL statement."
+  (interactive "p")
+  (let ((term (sql-get-product-feature sql-product :terminator))
+        (re-search (if (> 0 arg) 're-search-backward 're-search-forward))
+        (here (point))
+        (n 0))
+    (when (consp term)
+      (setq term (car term)))
+    ;; Iterate until we've moved the desired number of stmt ends
+    (while (not (= (sql-signum arg) 0))
+      ;; if we're looking at the terminator, jump by 2
+      (if (or (and (> 0 arg) (looking-back term))
+              (and (< 0 arg) (looking-at term)))
+          (setq n 2)
+        (setq n 1))
+      ;; If we found another end-of-stmt
+      (if (not (apply re-search term nil t n nil))
+          (setq arg 0)
+        ;; count it if we're not in a comment
+        (unless (nth 7 (syntax-ppss))
+          (setq arg (- arg (sql-signum arg))))))
+    (goto-char (if (match-data)
+                   (match-end 0)
+                 here))))
 
 ;;; Small functions
 
@@ -2280,7 +2756,7 @@ the regular expression `comint-prompt-regexp', a buffer local variable."
 (defun sql-help-list-products (indent freep)
   "Generate listing of products available for use under SQLi.
 
-List products with :free-softare attribute set to FREEP.  Indent
+List products with :free-software attribute set to FREEP.  Indent
 each line with INDENT."
 
   (let (sqli-func doc)
@@ -2365,6 +2841,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.
 
@@ -2382,54 +2906,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 <filename-regexp>
+    :completion <list-of-strings-or-function>
+    :default <default-value>
+    :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 connection)
+  "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 connection)
+          buf)
+     ;; Global sql-buffer
+     (and (setq buf (default-value 'sql-buffer))
+          (sql-buffer-live-p buf prod connection)
+          buf)
+     ;; Look thru each buffer
+     (car (apply 'append
+                 (mapcar (lambda (b)
+                           (and (sql-buffer-live-p b prod connection)
+                                (list (buffer-name b))))
+                         (buffer-list)))))))
 
 (defun sql-set-sqli-buffer-generally ()
   "Set SQLi buffer for all SQL buffers that have none.
@@ -2441,16 +2980,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 ()
@@ -2468,19 +3008,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.
@@ -2488,11 +3022,12 @@ 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 (or (null sql-buffer)
+          (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.
@@ -2511,47 +3046,85 @@ server/database name."
 
   (let ((name ""))
 
-    ;; Try using the :sqli-login setting
-    (when (string= "" (or name ""))
-      (setq name
-            (apply 'concat
-                   (apply 'append nil
-                          (mapcar
-                           (lambda (v)
-                             (cond
-                              ((eq v 'user)     (list "/" sql-user))
-                              ((eq v 'server)   (list "." sql-server))
-                              ((eq v 'database) (list "@" sql-database))
-                              ((eq v 'port)     (list ":" sql-port))
-
-                              ((eq v 'password) nil)
-                              (t                nil)))
-                           (sql-get-product-feature sql-product :sqli-login))))))
-
-    ;; Default: username/server format
-    (when (string= "" (or name ""))
-      (setq 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))))
-
-    ;; Return the final string; prefixed by the connection 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 ""))
-      (substring (or name " ") 1))))
 
-(defun sql-rename-buffer ()
-  "Rename a SQLi buffer."
-  (interactive)
-  (rename-buffer (format "*SQL: %s*" sql-alternate-buffer-name) t))
+      ;; 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.
@@ -2624,14 +3197,94 @@ 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
+
+(defvar sql-preoutput-hold nil)
+
+(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-output-newline-count' is greater than
+zero, then an output line matching the continuation prompt is filtered
+out.  If the count is zero, then a newline is inserted into the output
+to force the output from the query to appear on a new line.
+
+The complication to this filter is that the continuation prompts
+may arrive in multiple chunks.  If they do, then the function
+saves any unfiltered output in a buffer and prepends that buffer
+to the next chunk to properly match the broken-up prompt.
+
+If the filter gets confused, it should reset and stop filtering
+to avoid deleting non-prompt output."
+
+  (let (did-filter)
+    (setq oline (concat (or sql-preoutput-hold "") oline)
+          sql-preoutput-hold nil)
+
+    (if (and comint-prompt-regexp
+             (integerp sql-output-newline-count)
+             (>= sql-output-newline-count 1))
+        (progn
+          (while (and (not (string= oline ""))
+                      (> sql-output-newline-count 0)
+                      (string-match comint-prompt-regexp oline)
+                      (= (match-beginning 0) 0))
+
+            (setq oline (replace-match "" nil nil oline)
+                  sql-output-newline-count (1- sql-output-newline-count)
+                  did-filter t))
+
+          (if (= sql-output-newline-count 0)
+              (setq sql-output-newline-count nil
+                    oline (concat "\n" oline)
+                    sql-output-by-send nil)
+
+            (setq sql-preoutput-hold oline
+                  oline ""))
+
+          (unless did-filter
+            (setq oline (or sql-preoutput-hold "")
+                  sql-preoutput-hold nil
+                  sql-output-newline-count nil)))
+
+      (setq sql-output-newline-count nil))
+
+    oline))
 
 ;;; Sending the region to the SQLi buffer.
 
@@ -2639,28 +3292,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
@@ -2693,7 +3340,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)))
@@ -2714,8 +3361,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."
@@ -2734,10 +3386,285 @@ If given the optional parameter VALUE, sets
 
 \f
 
+;;; Redirect output functions
+
+(defvar sql-debug-redirect nil
+  "If non-nil, display messages related to the use of redirection.")
+
+(defun sql-str-literal (s)
+  (concat "'" (replace-regexp-in-string "[']" "''" s) "'"))
+
+(defun sql-redirect (sqlbuf command &optional outbuf save-prior)
+  "Execute the SQL command and send output to OUTBUF.
+
+SQLBUF 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.  COMMAND may also be a
+list of SQLi command strings."
+
+  (let* ((visible (and outbuf
+                       (not (string= " " (substring outbuf 0 1))))))
+    (when visible
+      (message "Executing SQL command..."))
+    (if (consp command)
+        (mapc (lambda (c) (sql-redirect-one sqlbuf c outbuf save-prior))
+              command)
+      (sql-redirect-one sqlbuf command outbuf save-prior))
+    (when visible
+      (message "Executing SQL command...done"))))
+
+(defun sql-redirect-one (sqlbuf command outbuf save-prior)
+  (with-current-buffer sqlbuf
+    (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)))
+
+      (when sql-debug-redirect
+        (message ">>SQL> %S" command))
+
+      ;; Run the command
+      (comint-redirect-send-command-to-process command buf proc nil t)
+      (while (null comint-redirect-completed)
+       (accept-process-output nil 1))
+
+      ;; 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)))
+        ;; Remove Ctrl-Ms
+        (goto-char start)
+        (while (re-search-forward "\r+$" nil t)
+          (replace-match "" t t))
+        (goto-char start)))))
+
+(defun sql-redirect-value (sqlbuf command regexp &optional regexp-groups)
+  "Execute the SQL command and return part of result.
+
+SQLBUF 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 sqlbuf command 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 (/ (length (match-data)) 2))
+                 (r nil))
+             (while (> i 0)
+               (setq i (1- i))
+               (push (match-string i) r))
+             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)))
+
+    (when sql-debug-redirect
+      (message ">>SQL> = %S" (reverse results)))
+
+    (nreverse results)))
+
+(defun sql-execute (sqlbuf outbuf command enhanced arg)
+  "Executes a command in a SQL interactive 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 sqlbuf (if arg (format c arg) c) outbuf) t)
+      ((functionp c)
+       (apply c sqlbuf outbuf enhanced arg nil))
+      (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 enhanced arg)))
+
+(defvar sql-completion-object nil
+  "A list of database objects used for completion.
+
+The list is maintained in SQL interactive buffers.")
+
+(defvar sql-completion-column nil
+  "A list of column names used for completion.
+
+The list is maintained in SQL interactive buffers.")
+
+(defun sql-build-completions-1 (schema completion-list feature)
+  "Generate a list of objects in the database for use as completions."
+  (let ((f (sql-get-product-feature sql-product feature)))
+    (when f
+      (set completion-list
+            (let (cl)
+              (dolist (e (append (symbol-value completion-list)
+                                 (apply f (current-buffer) (cons schema nil)))
+                         cl)
+                (unless (member e cl) (setq cl (cons e cl))))
+              (sort cl (function string<)))))))
+
+(defun sql-build-completions (schema)
+  "Generate a list of names in the database for use as completions."
+  (sql-build-completions-1 schema 'sql-completion-object :completion-object)
+  (sql-build-completions-1 schema 'sql-completion-column :completion-column))
+
+(defvar sql-completion-sqlbuf nil)
+
+(defun sql-try-completion (string collection &optional predicate)
+  (when sql-completion-sqlbuf
+      (with-current-buffer sql-completion-sqlbuf
+        (let ((schema (and (string-match "\\`\\(\\sw\\(:?\\sw\\|\\s_\\)*\\)[.]" string)
+                           (downcase (match-string 1 string)))))
+
+          ;; If we haven't loaded any object name yet, load local schema
+          (unless sql-completion-object
+            (sql-build-completions nil))
+
+          ;; If they want another schema, load it if we haven't yet
+          (when schema
+            (let ((schema-dot (concat schema "."))
+                  (schema-len (1+ (length schema)))
+                  (names sql-completion-object)
+                  has-schema)
+
+              (while (and (not has-schema) names)
+                (setq has-schema (and
+                                  (>= (length (car names)) schema-len)
+                                  (string= schema-dot
+                                           (downcase (substring (car names) 
+                                                                0 schema-len))))
+                      names (cdr names)))
+              (unless has-schema
+                (sql-build-completions schema)))))
+        
+        ;; Try to find the completion
+        (cond
+         ((not predicate)
+          (try-completion string sql-completion-object))
+         ((eq predicate t)
+          (all-completions string sql-completion-object))
+         ((eq predicate 'lambda)
+          (test-completion string sql-completion-object))
+         ((eq (car predicate) 'boundaries)
+          (completion-boundaries string sql-completion-object nil (cdr predicate)))))))
+
+(defun sql-read-table-name (prompt)
+  "Read the name of a database table."
+  (let* ((tname
+          (and (buffer-local-value 'sql-contains-names (current-buffer))
+               (thing-at-point-looking-at
+                (concat "\\_<\\sw\\(:?\\sw\\|\\s_\\)*"
+                        "\\(?:[.]+\\sw\\(?:\\sw\\|\\s_\\)*\\)*\\_>"))
+               (buffer-substring-no-properties (match-beginning 0)
+                                               (match-end 0))))
+         (sql-completion-sqlbuf (sql-find-sqli-buffer))
+         (product (with-current-buffer sql-completion-sqlbuf sql-product))
+         (completion-ignore-case t))
+
+    (if (sql-get-product-feature product :completion-object)
+        (completing-read prompt (function sql-try-completion)
+                         nil nil tname)
+      (read-from-minibuffer prompt tname))))
+
+(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)
+    (with-current-buffer sqlbuf
+      ;; Contains the name of database objects
+      (set (make-local-variable 'sql-contains-names) t)
+      (set (make-local-variable 'sql-buffer) sqlbuf))))
+
+(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)))
+\f
+
 ;;; 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
@@ -2764,18 +3691,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
@@ -2785,17 +3705,13 @@ 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)
+  ;; Contains the name of database objects
+  (set (make-local-variable 'sql-contains-names) t)
   ;; Catch changes to sql-product and highlight accordingly
-  (sql-highlight-product)
   (add-hook 'hack-local-variables-hook 'sql-highlight-product t t))
 
 \f
@@ -2881,14 +3797,13 @@ you entered, right above the output it created.
 
   ;; 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 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
@@ -2897,31 +3812,49 @@ 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)
-  ;; 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-process-sentinel (get-buffer-process (current-buffer)) 'sql-stop)
+  ;; Save the connection and login params
+  (set (make-local-variable 'sql-user)       sql-user)
+  (set (make-local-variable 'sql-database)   sql-database)
+  (set (make-local-variable 'sql-server)     sql-server)
+  (set (make-local-variable 'sql-port)       sql-port)
+  (set (make-local-variable 'sql-connection) sql-connection)
+  ;; Contains the name of database objects
+  (set (make-local-variable 'sql-contains-names) t)
+  ;; Keep track of existing object names
+  (set (make-local-variable 'sql-completion-object) nil)
+  (set (make-local-variable 'sql-completion-column) nil)
+  ;; Create a useful name for renaming this buffer later.
+  (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-preoutput-hold)
+  (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)
@@ -2950,90 +3883,18 @@ Sentinels will always get the two parameters PROCESS and EVENT."
 
 \f
 
-;;; Entry functions for different SQL interpreters.
-
-;;;###autoload
-(defun sql-product-interactive (&optional product)
-  "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*'.
-
-\(Type \\[describe-mode] in the SQL buffer for a list of commands.)"
-  (interactive "P")
-
-  (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"))))
-         ((and product                  ; Product specified
-               (symbolp product)) product)
-         (t sql-product)))              ; Default to sql-product
-
-  (if product
-      (when (sql-get-product-feature product :sqli-comint-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-comint-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))))
-    (message "No default SQL product defined.  Set `sql-product'.")))
-
-(defun sql-comint (product params)
-  "Set up a comint buffer to run the SQL processor.
+;;; Connection handling
 
-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)))
-    (set-buffer
-     (apply 'make-comint "SQL" program nil params))))
+(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)
+(defun sql-connect (connection &optional new-name)
   "Connect to an interactive session using CONNECTION settings.
 
 See `sql-connection-alist' to see how to define connections and
@@ -3045,12 +3906,8 @@ is specified in the connection settings."
   ;; Prompt for the connection from those defined in the alist
   (interactive
    (if sql-connection-alist
-       (list
-        (let ((completion-ignore-case t))
-          (completing-read "Connection: "
-                           (mapcar (lambda (c) (car c))
-                                   sql-connection-alist)
-                           nil t nil nil '(()))))
+       (list (sql-read-connection "Connection: " nil '(nil))
+             current-prefix-arg)
      nil))
 
   ;; Are there connections defined
@@ -3082,19 +3939,21 @@ is specified in the connection settings."
                                             (t                          (car v))))
                                          (cdr connect-set)))
                           ;; the remaining params (w/o the connection params)
-                          (rem-params   (delq nil
-                                              (mapcar
-                                               (lambda (l)
-                                                 (unless (member l set-params)
-                                                   l))
-                                               login-params)))
-                          ;; Remember the connection
-                          (sql-connection connection))
+                          (rem-params   (sql-for-each-login
+                                         login-params
+                                         (lambda (token plist)
+                                           (unless (member token set-params)
+                                                    (if plist
+                                                        (cons token plist)
+                                                      token))))))
 
                        ;; Set the remaining parameters and start the
                        ;; interactive session
-                       (eval `(let ((,param-var ',rem-params))
-                                (sql-product-interactive sql-product)))))
+                       (eval `(let ((sql-connection ,connection)
+                                     (,param-var ',rem-params))
+                                (sql-product-interactive sql-product 
+                                                         new-name)))))
+
             (message "SQL Connection <%s> does not exist" connection)
             nil)))
     (message "No SQL Connections defined")
@@ -3108,40 +3967,51 @@ 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)
-                      (delq nil
-                            (mapcar
-                             (lambda (param)
-                               (cond
-                                ((eq param 'product)  `(sql-product  (quote ,sql-product)))
-                                ((eq param 'user)     `(sql-user     ,sql-user))
-                                ((eq param 'database) `(sql-database ,sql-database))
-                                ((eq param 'server)   `(sql-server   ,sql-server))
-                                ((eq param 'port)     `(sql-port     ,sql-port))))
-                             (append (list 'product) login)))))
-
-        (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))))))
+  (unless (derived-mode-p 'sql-interactive-mode)
+    (error "Not in a SQL interactive mode!"))
+
+  ;; Capture the buffer local settings
+  (let* ((buf        (current-buffer))
+         (connection (buffer-local-value 'sql-connection buf))
+         (product    (buffer-local-value 'sql-product    buf))
+         (user       (buffer-local-value 'sql-user       buf))
+         (database   (buffer-local-value 'sql-database   buf))
+         (server     (buffer-local-value 'sql-server     buf))
+         (port       (buffer-local-value 'sql-port       buf)))
+
+    (if connection
+        (message "This session was started by a connection; it's already been saved.")
+      
+      (let ((login (sql-get-product-feature 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  ',product))
+                            ((eq token 'user)     `(sql-user     ,user))
+                            ((eq token 'database) `(sql-database ,database))
+                            ((eq token 'server)   `(sql-server   ,server))
+                            ((eq token 'port)     `(sql-port     ,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."
@@ -3149,14 +4019,121 @@ optionally is saved to the user's init file."
    (mapcar
     (lambda (conn)
       (vector
-       (format "Connection <%s>" (car conn))
+       (format "Connection <%s>\t%s" (car conn)
+               (let ((sql-user "") (sql-database "")
+                     (sql-server "") (sql-port 0))
+                 (eval `(let ,(cdr conn) (sql-make-alternate-buffer-name)))))
        (list 'sql-connect (car conn))
        t))
     sql-connection-alist)
    tail))
 
+\f
+
+;;; Entry functions for different SQL interpreters.
+
 ;;;###autoload
-(defun sql-oracle ()
+(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
+         ((= (prefix-numeric-value product) 4) ; C-u, prompt for product
+          (sql-read-product "SQL product: " sql-product))
+         ((and product                  ; Product specified
+               (symbolp product)) product)
+         (t sql-product)))              ; Default to sql-product
+
+  ;; 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 sql-connection)))
+          (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.
+              (let ((sql-interactive-product product))
+                (sql-interactive-mode))
+
+              ;; Set the new buffer name
+              (setq new-sqli-buffer (current-buffer))
+              (when new-name
+                (sql-rename-buffer new-name))
+              (setq sql-buffer (buffer-name new-sqli-buffer))
+
+              ;; Set `sql-buffer' in the start buffer
+              (with-current-buffer start-buffer
+                (when (derived-mode-p 'sql-mode)
+                  (setq sql-buffer (buffer-name new-sqli-buffer))
+                  (run-hooks 'sql-set-sqli-hook)))
+
+              ;; All done.
+              (message "Login...done")
+              (pop-to-buffer new-sqli-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))
+        (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
+     (apply 'make-comint buf-name program nil params))))
+
+;;;###autoload
+(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.
@@ -3171,6 +4148,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]
@@ -3179,8 +4161,8 @@ 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-comint-oracle (product options)
   "Create comint buffer and connect to Oracle."
@@ -3200,10 +4182,161 @@ The default comes from `process-coding-system-alist' and
       (setq parameter options))
     (sql-comint product parameter)))
 
+(defun sql-oracle-save-settings (sqlbuf)
+  "Saves most SQL*Plus settings so they may be reset by \\[sql-redirect]."
+  ;; Note: does not capture the following settings:
+  ;;
+  ;; APPINFO
+  ;; BTITLE
+  ;; COMPATIBILITY
+  ;; COPYTYPECHECK
+  ;; MARKUP
+  ;; RELEASE
+  ;; REPFOOTER
+  ;; REPHEADER
+  ;; SQLPLUSCOMPATIBILITY
+  ;; TTITLE
+  ;; USER
+  ;;
+
+  (append
+  ;; (apply 'concat (append
+  ;;  '("SET")
+
+   ;; option value...
+   (sql-redirect-value
+    sqlbuf
+    (concat "SHOW ARRAYSIZE AUTOCOMMIT AUTOPRINT AUTORECOVERY AUTOTRACE"
+            " CMDSEP COLSEP COPYCOMMIT DESCRIBE ECHO EDITFILE EMBEDDED"
+            " ESCAPE FLAGGER FLUSH HEADING INSTANCE LINESIZE LNO LOBOFFSET"
+            " LOGSOURCE LONG LONGCHUNKSIZE NEWPAGE NULL NUMFORMAT NUMWIDTH"
+            " PAGESIZE PAUSE PNO RECSEP SERVEROUTPUT SHIFTINOUT SHOWMODE"
+            " SPOOL SQLBLANKLINES SQLCASE SQLCODE SQLCONTINUE SQLNUMBER"
+            " SQLPROMPT SUFFIX TAB TERMOUT TIMING TRIMOUT TRIMSPOOL VERIFY")
+    "^.+$"
+    "SET \\&")
+
+   ;; option "c" (hex xx)
+   (sql-redirect-value
+    sqlbuf
+    (concat "SHOW BLOCKTERMINATOR CONCAT DEFINE SQLPREFIX SQLTERMINATOR"
+            " UNDERLINE HEADSEP RECSEPCHAR")
+    "^\\(.+\\) (hex ..)$"
+    "SET \\1")
+
+   ;; FEDDBACK ON for 99 or more rows
+   ;; feedback OFF
+   (sql-redirect-value
+    sqlbuf
+    "SHOW FEEDBACK"
+    "^\\(?:FEEDBACK ON for \\([[:digit:]]+\\) or more rows\\|feedback \\(OFF\\)\\)"
+    "SET FEEDBACK \\1\\2")
+
+   ;; wrap : lines will be wrapped
+   ;; wrap : lines will be truncated
+   (list (concat "SET WRAP "
+                 (if (string=
+                      (car (sql-redirect-value
+                            sqlbuf
+                            "SHOW WRAP"
+                            "^wrap : lines will be \\(wrapped\\|truncated\\)" 1))
+                      "wrapped")
+                     "ON" "OFF")))))
+
+(defun sql-oracle-restore-settings (sqlbuf saved-settings)
+  "Restore the SQL*Plus settings in SAVED-SETTINGS."
+
+  ;; Remove any settings that haven't changed
+  (mapc
+   (lambda (one-cur-setting)
+     (setq saved-settings (delete one-cur-setting saved-settings)))
+   (sql-oracle-save-settings sqlbuf))
+
+  ;; Restore the changed settings
+  (sql-redirect sqlbuf saved-settings))
+
+(defun sql-oracle-list-all (sqlbuf outbuf enhanced table-name)
+  ;; Query from USER_OBJECTS or ALL_OBJECTS
+  (let ((settings (sql-oracle-save-settings sqlbuf))
+        (simple-sql
+         (concat
+          "SELECT INITCAP(x.object_type) AS SQL_EL_TYPE "
+          ", x.object_name AS SQL_EL_NAME "
+          "FROM user_objects                    x "
+          "WHERE x.object_type NOT LIKE '%% BODY' "
+          "ORDER BY 2, 1;"))
+        (enhanced-sql
+         (concat
+          "SELECT INITCAP(x.object_type) AS SQL_EL_TYPE "
+          ", x.owner ||'.'|| x.object_name AS SQL_EL_NAME "
+          "FROM all_objects x "
+          "WHERE x.object_type NOT LIKE '%% BODY' "
+          "AND x.owner <> 'SYS' "
+          "ORDER BY 2, 1;")))
+
+    (sql-redirect sqlbuf
+                  (concat "SET LINESIZE 80 PAGESIZE 50000 TRIMOUT ON"
+                          " TAB OFF TIMING OFF FEEDBACK OFF"))
+
+    (sql-redirect sqlbuf
+                  (list "COLUMN SQL_EL_TYPE  HEADING \"Type\" FORMAT A19"
+                        "COLUMN SQL_EL_NAME  HEADING \"Name\""
+                        (format "COLUMN SQL_EL_NAME  FORMAT A%d"
+                                (if enhanced 60 35))))
+
+    (sql-redirect sqlbuf
+                  (if enhanced enhanced-sql simple-sql)
+                  outbuf)
+
+    (sql-redirect sqlbuf
+                  '("COLUMN SQL_EL_NAME CLEAR"
+                    "COLUMN SQL_EL_TYPE CLEAR"))
+
+    (sql-oracle-restore-settings sqlbuf settings)))
+
+(defun sql-oracle-list-table (sqlbuf outbuf enhanced table-name)
+  "Implements :list-table under Oracle."
+  (let ((settings (sql-oracle-save-settings sqlbuf)))
+
+    (sql-redirect sqlbuf
+                  (format
+                   (concat "SET LINESIZE %d PAGESIZE 50000"
+                           " DESCRIBE DEPTH 1 LINENUM OFF INDENT ON")
+                   (max 65 (min 120 (window-width)))))
+
+    (sql-redirect sqlbuf (format "DESCRIBE %s" table-name)
+                  outbuf)
+
+    (sql-oracle-restore-settings sqlbuf settings)))
+
+(defcustom sql-oracle-completion-types '("FUNCTION" "PACKAGE" "PROCEDURE"
+                                         "SEQUENCE" "SYNONYM" "TABLE" "TRIGGER"
+                                         "TYPE" "VIEW")
+  "List of object types to include for completion under Oracle.
+
+See the distinct values in ALL_OBJECTS.OBJECT_TYPE for possible values."
+  :version "24.1"
+  :type '(repeat string)
+  :group 'SQL)
+
+(defun sql-oracle-completion-object (sqlbuf schema)
+  (sql-redirect-value
+   sqlbuf
+   (concat
+    "SELECT CHR(1)||"
+    (if schema
+        (format "owner||'.'||object_name AS o FROM all_objects WHERE owner = %s AND "
+                (sql-str-literal (upcase schema)))
+      "object_name AS o FROM user_objects WHERE ")
+    "temporary = 'N' AND generated = 'N' AND secondary = 'N' AND "
+    "object_type IN ("
+    (mapconcat (function sql-str-literal) sql-oracle-completion-types ",")
+    ");")
+   "^[\001]\\(.+\\)$" 1))
 \f
 
 ;;;###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.
@@ -3218,6 +4351,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]
@@ -3226,8 +4364,8 @@ 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-comint-sybase (product options)
   "Create comint buffer and connect to Sybase."
@@ -3247,7 +4385,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###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.
@@ -3260,6 +4398,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]
@@ -3268,8 +4411,8 @@ 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-comint-informix (product options)
   "Create comint buffer and connect to Informix."
@@ -3284,7 +4427,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###autoload
-(defun sql-sqlite ()
+(defun sql-sqlite (&optional buffer)
   "Run sqlite as an inferior process.
 
 SQLite is free software.
@@ -3301,6 +4444,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]
@@ -3309,8 +4457,8 @@ 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-comint-sqlite (product options)
   "Create comint buffer and connect to SQLite."
@@ -3318,14 +4466,18 @@ The default comes from `process-coding-system-alist' and
   ;; 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-comint product params)))
 
+(defun sql-sqlite-completion-object (sqlbuf schema)
+  (sql-redirect-value sqlbuf ".tables" "\\sw\\(?:\\sw\\|\\s_\\)*" 0))
+
 \f
 
 ;;;###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.
@@ -3342,6 +4494,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]
@@ -3350,8 +4507,8 @@ 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-comint-mysql (product options)
   "Create comint buffer and connect to MySQL."
@@ -3362,7 +4519,7 @@ 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 (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)))
@@ -3374,7 +4531,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###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.
@@ -3388,6 +4545,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]
@@ -3396,8 +4558,8 @@ 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-comint-solid (product options)
   "Create comint buffer and connect to Solid."
@@ -3415,7 +4577,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###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.
@@ -3428,6 +4590,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]
@@ -3436,8 +4603,8 @@ 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-comint-ingres (product options)
   "Create comint buffer and connect to Ingres."
@@ -3451,7 +4618,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###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.
@@ -3466,6 +4633,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]
@@ -3474,8 +4646,8 @@ 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-comint-ms (product options)
   "Create comint buffer and connect to Microsoft SQL Server."
@@ -3502,7 +4674,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###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.
@@ -3517,6 +4689,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]
@@ -3530,8 +4707,8 @@ 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-comint-postgres (product options)
   "Create comint buffer and connect to Postgres."
@@ -3547,12 +4724,41 @@ 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)))
+    (if (not (= 0 sql-port))
+       (setq params (append (list "-p" (number-to-string sql-port)) params)))
     (sql-comint product params)))
 
+(defun sql-postgres-completion-object (sqlbuf schema)
+  (let (cl re fs a r)
+    (sql-redirect sqlbuf "\\t on")
+    (setq a (car (sql-redirect-value sqlbuf "\\a" "Output format is \\(.*\\)[.]$" 1)))
+    (when (string= a "aligned")
+      (sql-redirect sqlbuf "\\a"))
+    (setq fs (or (car (sql-redirect-value sqlbuf "\\f" "Field separator is \"\\(.\\)[.]$" 1)) "|"))
+
+    (setq re (concat "^\\([^" fs "]*\\)" fs "\\([^" fs "]*\\)" fs "[^" fs "]*" fs  "[^" fs "]*$"))
+    (setq cl (if (not schema)
+                 (sql-redirect-value sqlbuf "\\d" re '(1 2))
+               (append (sql-redirect-value sqlbuf (format "\\dt %s.*" schema) re '(1 2))
+                       (sql-redirect-value sqlbuf (format "\\dv %s.*" schema) re '(1 2))
+                       (sql-redirect-value sqlbuf (format "\\ds %s.*" schema) re '(1 2)))))
+
+    ;; Restore tuples and alignment to what they were
+    (sql-redirect sqlbuf "\\t off")
+    (when (not (string= a "aligned"))
+      (sql-redirect sqlbuf "\\a"))
+    
+    ;; Return the list of table names (public schema name can be omitted) 
+    (mapcar (lambda (tbl)
+              (if (string= (car tbl) "public")
+                  (cadr tbl)
+                (format "%s.%s" (car tbl) (cadr tbl))))
+            cl)))
+
 \f
 
 ;;;###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.
@@ -3566,6 +4772,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]
@@ -3574,8 +4785,8 @@ 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-comint-interbase (product options)
   "Create comint buffer and connect to Interbase."
@@ -3593,7 +4804,7 @@ The default comes from `process-coding-system-alist' and
 \f
 
 ;;;###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.
@@ -3611,6 +4822,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]
@@ -3619,20 +4835,17 @@ 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-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-comint product options)
-)
-;;   ;; Properly escape newlines when DB2 is interactive.
-;;   (setq comint-input-sender 'sql-escape-newlines-and-send))
+  (sql-comint product options))
 
 ;;;###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.
@@ -3654,9 +4867,14 @@ 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-comint-linter (product options)
   "Create comint buffer and connect to Linter."
@@ -3680,6 +4898,7 @@ input.  See `sql-interactive-mode'.
 
 (provide 'sql)
 
-;; arch-tag: 7e1fa1c4-9ca2-402e-87d2-83a5eccb7ac3
 ;;; sql.el ends here
 
+; LocalWords:  sql SQL SQLite sqlite Sybase Informix MySQL
+; LocalWords:  Postgres SQLServer SQLi