]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
custom sql.el version
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Tue, 28 May 2024 15:48:54 +0000 (17:48 +0200)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Tue, 28 May 2024 15:48:54 +0000 (17:48 +0200)
some testing before opening a pull request.

Signed-off-by: Andreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
bba.org
lisp/sql.el [new file with mode: 0644]

diff --git a/bba.org b/bba.org
index e365228eadfc72aa6690bb981c01b11af203942c..fb583623f4c0324cdbf56533f0221e9dd51e9fda 100644 (file)
--- a/bba.org
+++ b/bba.org
@@ -1255,6 +1255,14 @@ thus installed via github.
 * sql
 [2016-11-27 Sun 17:56]
 
+#+begin_src emacs-lisp
+  (load-file (expand-file-name "~/.emacs.d/lisp/sql.el"))
+
+
+  (setq sql-imenu-generic-expression
+        (append sql-imenu-generic-expression '(("Projections" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*\\(?:projection\\)\\s-+?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1))))
+#+end_src
+
 thanks to https://truongtx.me/2014/08/23/setup-emacs-as-an-sql-database-client
 
 have a look in [[file:/Applications/Emacs.app/Contents/Resources/lisp/progmodes/sql.el.gz::(let%20((connect-set%20(assoc-string%20connection%20sql-connection-alist%20t)))][sql.el]] and extract the sql-product and set it before
diff --git a/lisp/sql.el b/lisp/sql.el
new file mode 100644 (file)
index 0000000..8a7207f
--- /dev/null
@@ -0,0 +1,5607 @@
+;;; sql.el --- specialized comint.el for SQL interpreters  -*- lexical-binding: t -*-
+
+;; Copyright (C) 1998-2023 Free Software Foundation, Inc.
+
+;; Author: Alex Schroeder <alexNOSPAMgnu.org>
+;; Maintainer: Michael Mauger <michaelNOSPAMmauger.com>
+;; Version: 3.6
+;; Keywords: comm languages processes
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Please send bug reports and bug fixes to the mailing list at
+;; bug-gnu-emacsNOSPAMgnu.org.
+;; See also the general help list at
+;; https://lists.gnu.org/mailman/listinfo/help-gnu-emacs
+;; I monitor this list actively.  If you send an e-mail
+;; to Alex Schroeder it usually makes it to me when Alex has a chance
+;; to forward them along (Thanks, Alex).
+
+;; This file provides a sql-mode and a sql-interactive-mode.  The
+;; original goals were two simple modes providing syntactic
+;; highlighting.  The interactive mode had to provide a command-line
+;; history; the other mode had to provide "send region/buffer to SQL
+;; interpreter" functions.  "simple" in this context means easy to
+;; use, easy to maintain and little or no bells and whistles.  This
+;; has changed somewhat as experience with the mode has accumulated.
+
+;; Support for different flavors of SQL and command interpreters was
+;; 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
+;; 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.
+
+;; If anybody feels like extending this sql mode, take a look at the
+;; above mentioned modes and write a sqlx-mode on top of this one.  If
+;; this proves to be difficult, please suggest changes that will
+;; facilitate your plans.  Facilities have been provided to add
+;; products and product-specific configuration.
+
+;; sql-interactive-mode is used to interact with a SQL interpreter
+;; process in a SQLi buffer (usually called `*SQL*').  The SQLi buffer
+;; is created by calling a SQL interpreter-specific entry function or
+;; sql-product-interactive.  Do *not* call sql-interactive-mode by
+;; itself.
+
+;; The list of currently supported interpreters and the corresponding
+;; entry function used to create the SQLi buffers is shown with
+;; `sql-help' (M-x sql-help).
+
+;; Since sql-interactive-mode is built on top of the general
+;; command-interpreter-in-a-buffer mode (comint mode), it shares a
+;; common base functionality, and a common set of bindings, with all
+;; modes derived from comint mode.  This makes these modes easier to
+;; use.
+
+;; sql-mode can be used to keep editing SQL statements.  The SQL
+;; statements can be sent to the SQL process in the SQLi buffer.
+
+;; For documentation on the functionality provided by comint mode, and
+;; the hooks available for customizing it, see the file `comint.el'.
+
+;; Hint for newbies: take a look at `dabbrev-expand', `abbrev-mode', and
+;; `imenu-add-menubar-index'.
+
+;;; Bugs:
+
+;; sql-ms now uses osql instead of isql.  Osql flushes its error
+;; stream more frequently than isql so that error messages are
+;; available.  There is no prompt and some output still is buffered.
+;; This improves the interaction under Emacs but it still is somewhat
+;; awkward.
+
+;; Quoted identifiers are not supported for highlighting.  Most
+;; databases support the use of double quoted strings in place of
+;; identifiers; ms (Microsoft SQLServer) also supports identifiers
+;; enclosed within brackets [].
+
+;;; Product Support:
+
+;; To add support for additional SQL products the following steps
+;; must be followed ("xyz" is the name of the product in the examples
+;; below):
+
+;; 1) Add the product to the list of known products.
+
+;;     (sql-add-product 'xyz "XyzDB"
+;;                             '(:free-software t))
+
+;; 2) Define font lock settings.  All ANSI keywords will be
+;;    highlighted automatically, so only product specific keywords
+;;    need to be defined here.
+
+;;     (defvar my-sql-mode-xyz-font-lock-keywords
+;;       '(("\\b\\(red\\|orange\\|yellow\\)\\b"
+;;          . font-lock-keyword-face))
+;;       "XyzDB SQL keywords used by font-lock.")
+
+;;     (sql-set-product-feature 'xyz
+;;                              :font-lock
+;;                              'my-sql-mode-xyz-font-lock-keywords)
+
+;; 3) Define any special syntax characters including comments and
+;;    identifier characters.
+
+;;     (sql-set-product-feature 'xyz
+;;                              :syntax-alist ((?# . "_")))
+
+;; 4) Define the interactive command interpreter for the database
+;;    product.
+
+;;     (defcustom my-sql-xyz-program "ixyz"
+;;       "Command to start ixyz by XyzDB."
+;;       :type 'file
+;;       :group 'SQL)
+;;
+;;     (sql-set-product-feature 'xyz
+;;                              :sqli-program 'my-sql-xyz-program)
+;;     (sql-set-product-feature 'xyz
+;;                              :prompt-regexp "^xyzdb> ")
+;;     (sql-set-product-feature 'xyz
+;;                              :prompt-length 7)
+
+;; 5) Define login parameters and command line formatting.
+
+;;     (defcustom my-sql-xyz-login-params '(user password server database)
+;;       "Login parameters to needed to connect to XyzDB."
+;;       :type 'sql-login-params
+;;       :group 'SQL)
+;;
+;;     (sql-set-product-feature 'xyz
+;;                              :sqli-login 'my-sql-xyz-login-params)
+
+;;     (defcustom my-sql-xyz-options '("-X" "-Y" "-Z")
+;;       "List of additional options for `sql-xyz-program'."
+;;       :type '(repeat string)
+;;       :group 'SQL)
+;;
+;;     (sql-set-product-feature 'xyz
+;;                              :sqli-options 'my-sql-xyz-options))
+
+;;     (defun my-sql-comint-xyz (product options &optional buf-name)
+;;       "Connect ti XyzDB in a comint buffer."
+;;
+;;         ;; Do something with `sql-user', `sql-password',
+;;         ;; `sql-database', and `sql-server'.
+;;         (let ((params
+;;                (append
+;;           (if (not (string= "" sql-user))
+;;                     (list "-U" sql-user))
+;;                 (if (not (string= "" sql-password))
+;;                     (list "-P" sql-password))
+;;                 (if (not (string= "" sql-database))
+;;                     (list "-D" sql-database))
+;;                 (if (not (string= "" sql-server))
+;;                     (list "-S" sql-server))
+;;                 options)))
+;;           (sql-comint product params buf-name)))
+;;
+;;     (sql-set-product-feature 'xyz
+;;                              :sqli-comint-func 'my-sql-comint-xyz)
+
+;; 6) Define a convenience function to invoke the SQL interpreter.
+
+;;     (defun my-sql-xyz (&optional buffer)
+;;       "Run ixyz by XyzDB as an inferior process."
+;;       (interactive "P")
+;;       (sql-product-interactive 'xyz buffer))
+
+;;; To Do:
+
+;; Improve keyword highlighting for individual products.  I have tried
+;; to update those database that I use.  Feel free to send me updates,
+;; or direct me to the reference manuals for your favorite database.
+
+;; When there are no keywords defined, the ANSI keywords are
+;; highlighted.  ANSI keywords are highlighted even if the keyword is
+;; not used for your current product.  This should help identify
+;; portability concerns.
+
+;; Add different highlighting levels.
+
+;; Add support for listing available tables or the columns in a table.
+
+;;; Thanks to all the people who helped me out:
+
+;; Alex Schroeder <alexNOSPAMgnu.org> -- the original author
+;; Kai Blauberg <kai.blaubergNOSPAMmetla.fi>
+;; <ibalabanNOSPAMdalet.com>
+;; Yair Friedman <yfriedmaNOSPAMJohnBryce.Co.Il>
+;; Gregor Zych <zychNOSPAMpool.informatik.rwth-aachen.de>
+;; nino <ninoNOSPAMinform.dk>
+;; Berend de Boer <berendNOSPAMpobox.com>
+;; Adam Jenkins <adamNOSPAMthejenkins.org>
+;; Michael Mauger <michaelNOSPAMmauger.com> -- improved product support
+;; Drew Adams <drew.adamsNOSPAMoracle.com> -- Emacs 20 support
+;; Harald Maier <maierhNOSPAMmyself.com> -- sql-send-string
+;; Stefan Monnier <monnierNOSPAMiro.umontreal.ca> -- font-lock corrections;
+;;      code polish; on-going guidance and mentorship
+;; Paul Sleigh <batNOSPAMflurf.net> -- MySQL keyword enhancement
+;; Andrew Schein <andrewNOSPAMandrewschein.com> -- sql-port bug
+;; Ian Bjorhovde <idbjorhNOSPAMdataproxy.com> -- db2 escape newlines
+;;      incorrectly enabled by default
+;; Roman Scherer <roman.schererNOSPAMnugg.ad> -- Connection documentation
+;; Mark Wilkinson <wilkinsonmrNOSPAMgmail.com> -- file-local variables ignored
+;; Simen Heggestøyl <simenhegNOSPAMgmail.com> -- Postgres database completion
+;; Robert Cochran <robert-emacsNOSPAMcochranmail.com> -- MariaDB support
+;; Alex Harsanyi <alexharsanyiNOSPAMgmail.com> -- sql-indent package and support
+;; Roy Mathew <rmathew8NOSPAMgmail.com> -- bug in `sql-send-string'
+;;
+
+\f
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'comint)
+(require 'thingatpt)
+(require 'view)
+(eval-when-compile (require 'subr-x))   ; string-empty-p
+
+(defvar font-lock-keyword-face)
+(defvar font-lock-set-defaults)
+(defvar font-lock-string-face)
+
+;;; Allow customization
+
+(defgroup SQL nil
+  "Running a SQL interpreter from within Emacs buffers."
+  :version "20.4"
+  :group 'languages
+  :group 'processes)
+
+;; These five variables will be used as defaults, if set.
+
+(defcustom sql-user ""
+  "Default username."
+  :type 'string
+  :safe 'stringp)
+
+(defcustom sql-password ""
+  "Default password.
+If you customize this, the value will be stored in your init
+file.  Since that is a plaintext file, this could be dangerous."
+  :type 'string
+  :risky t)
+
+(defcustom sql-database ""
+  "Default database."
+  :type 'string
+  :safe 'stringp)
+
+(defcustom sql-server ""
+  "Default server or host."
+  :type 'string
+  :safe 'stringp)
+
+(defcustom sql-port 0
+  "Default port for connecting to a MySQL or Postgres server."
+  :version "24.1"
+  :type 'natnum
+  :safe 'natnump)
+
+(defcustom sql-default-directory nil
+  "Default directory for SQL processes."
+  :version "25.1"
+  :type '(choice (const nil) string)
+  :safe 'stringp)
+
+;; Login parameter type
+
+;; This seems too prescriptive.  It probably fails to match some of
+;; the possible combinations.  It would probably be better to just use
+;; plist for most of it.
+(define-widget 'sql-login-params 'lazy
+  "Widget definition of the login parameters list"
+  :tag "Login Parameters"
+  :type '(set :tag "Login Parameters"
+              (choice :tag "user"
+                      :value user
+                      (const user)
+                      (list :tag "Specify a default"
+                            (const user)
+                            (list :tag "Default"
+                                  :inline t (const :default) string)))
+              (const password)
+              (choice :tag "server"
+                      :value server
+                      (const server)
+                      (list :tag "Specify a default"
+                            (const server)
+                            (list :tag "Default"
+                                  :inline t (const :default) string))
+                      (list :tag "file"
+                            (const :format "" server)
+                            (const :format "" :file)
+                            regexp)
+                      (list :tag "completion"
+                            (const :format "" server)
+                            (const :format "" :completion)
+                            (const :format "" :must-match)
+                            (restricted-sexp
+                             :match-alternatives (listp stringp))))
+              (choice :tag "database"
+                      :value database
+                      (const database)
+                      (list :tag "Specify a default"
+                            (const database)
+                            (list :tag "Default"
+                                  :inline t (const :default) string))
+                      (list :tag "file"
+                            (const :format "" database)
+                            (const :format "" :file)
+                            (choice (const nil) regexp)
+                            (const :format "" :must-match)
+                            (symbol :tag ":must-match"))
+                      (list :tag "completion"
+                            (const :format "" database)
+                            (const :format "" :default)
+                            (string :tag ":default")
+                            (const :format "" :completion)
+                            (sexp :tag ":completion")
+                            (const :format "" :must-match)
+                            (symbol :tag ":must-match")))
+              (const port)))
+
+;; SQL Product support
+
+(defvar sql-interactive-product nil
+  "Product under `sql-interactive-mode'.")
+
+(defvar sql-connection nil
+  "Connection name if interactive session started by `sql-connect'.")
+
+(defvar sql-product-alist
+  '((ansi
+     :name "ANSI"
+     :font-lock sql-mode-ansi-font-lock-keywords
+     :statement sql-ansi-statement-starters)
+
+    (db2
+     :name "DB2"
+     :font-lock sql-mode-db2-font-lock-keywords
+     :sqli-program sql-db2-program
+     :sqli-options sql-db2-options
+     :sqli-login sql-db2-login-params
+     :sqli-comint-func sql-comint-db2
+     :prompt-regexp "^db2 => "
+     :prompt-length 7
+     :prompt-cont-regexp "^db2 (cont\\.) => "
+     :input-filter sql-escape-newlines-filter)
+
+    (informix
+     :name "Informix"
+     :font-lock sql-mode-informix-font-lock-keywords
+     :sqli-program sql-informix-program
+     :sqli-options sql-informix-options
+     :sqli-login sql-informix-login-params
+     :sqli-comint-func sql-comint-informix
+     :prompt-regexp "^> "
+     :prompt-length 2
+     :syntax-alist ((?{ . "<") (?} . ">")))
+
+    (ingres
+     :name "Ingres"
+     :font-lock sql-mode-ingres-font-lock-keywords
+     :sqli-program sql-ingres-program
+     :sqli-options sql-ingres-options
+     :sqli-login sql-ingres-login-params
+     :sqli-comint-func sql-comint-ingres
+     :prompt-regexp "^\\* "
+     :prompt-length 2
+     :prompt-cont-regexp "^\\* ")
+
+    (interbase
+     :name "Interbase"
+     :font-lock sql-mode-interbase-font-lock-keywords
+     :sqli-program sql-interbase-program
+     :sqli-options sql-interbase-options
+     :sqli-login sql-interbase-login-params
+     :sqli-comint-func sql-comint-interbase
+     :prompt-regexp "^SQL> "
+     :prompt-length 5)
+
+    (linter
+     :name "Linter"
+     :font-lock sql-mode-linter-font-lock-keywords
+     :sqli-program sql-linter-program
+     :sqli-options sql-linter-options
+     :sqli-login sql-linter-login-params
+     :sqli-comint-func sql-comint-linter
+     :prompt-regexp "^SQL>"
+     :prompt-length 4)
+
+    (mariadb
+     :name "MariaDB"
+     :free-software t
+     :font-lock sql-mode-mariadb-font-lock-keywords
+     :sqli-program sql-mariadb-program
+     :sqli-options sql-mariadb-options
+     :sqli-login sql-mariadb-login-params
+     :sqli-comint-func sql-comint-mariadb
+     :list-all "SHOW TABLES;"
+     :list-table "DESCRIBE %s;"
+     :prompt-regexp "^MariaDB \\[.*]> "
+     :prompt-cont-regexp "^    [\"'`-]> "
+     :syntax-alist ((?# . "< b"))
+     :input-filter sql-remove-tabs-filter)
+
+    (ms
+     :name "Microsoft"
+     :font-lock sql-mode-ms-font-lock-keywords
+     :sqli-program sql-ms-program
+     :sqli-options sql-ms-options
+     :sqli-login sql-ms-login-params
+     :sqli-comint-func sql-comint-ms
+     :prompt-regexp "^[0-9]*>"
+     :prompt-cont-regexp "^[0-9]*>"
+     :prompt-length 5
+     :syntax-alist ((?@ . "_"))
+     :terminator ("^go" . "go"))
+
+    (mysql
+     :name "MySQL"
+     :free-software t
+     :font-lock sql-mode-mysql-font-lock-keywords
+     :sqli-program sql-mysql-program
+     :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
+     :name "Oracle"
+     :font-lock sql-mode-oracle-font-lock-keywords
+     :sqli-program sql-oracle-program
+     :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
+     :prompt-cont-regexp "^\\(?:[ ][ ][1-9]\\|[ ][1-9][0-9]\\|[1-9][0-9]\\{2\\}\\)[ ]\\{2\\}"
+     :statement sql-oracle-statement-starters
+     :syntax-alist ((?$ . "_") (?# . "_"))
+     :terminator ("\\(^/\\|;\\)" . "/")
+     :input-filter sql-placeholders-filter)
+
+    (postgres
+     :name "Postgres"
+     :free-software t
+     :font-lock sql-mode-postgres-font-lock-keywords
+     :sqli-program sql-postgres-program
+     :sqli-options sql-postgres-options
+     :sqli-login sql-postgres-login-params
+     :sqli-comint-func sql-comint-postgres
+     :list-all ("\\d+" . "\\dS+")
+     :list-table ("\\d+ %s" . "\\dS+ %s")
+     :completion-object sql-postgres-completion-object
+     :prompt-regexp "^[-[:alnum:]_]*[-=][#>] "
+     :prompt-length 5
+     :prompt-cont-regexp "^[-[:alnum:]_]*[-'(][#>] "
+     :statement sql-postgres-statement-starters
+     :input-filter sql-remove-tabs-filter
+     :terminator ("\\(^\\s-*\\\\g\\|;\\)" . "\\g"))
+
+    (solid
+     :name "Solid"
+     :font-lock sql-mode-solid-font-lock-keywords
+     :sqli-program sql-solid-program
+     :sqli-options sql-solid-options
+     :sqli-login sql-solid-login-params
+     :sqli-comint-func sql-comint-solid
+     :prompt-regexp "^"
+     :prompt-length 0)
+
+    (sqlite
+     :name "SQLite"
+     :free-software t
+     :font-lock sql-mode-sqlite-font-lock-keywords
+     :sqli-program sql-sqlite-program
+     :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-cont-regexp "^   \\.\\.\\.> ")
+
+    (sybase
+     :name "Sybase"
+     :font-lock sql-mode-sybase-font-lock-keywords
+     :sqli-program sql-sybase-program
+     :sqli-options sql-sybase-options
+     :sqli-login sql-sybase-login-params
+     :sqli-comint-func sql-comint-sybase
+     :prompt-regexp "^SQL> "
+     :prompt-length 5
+     :syntax-alist ((?@ . "_"))
+     :terminator ("^go" . "go"))
+
+    (vertica
+     :name "Vertica"
+     :sqli-program sql-vertica-program
+     :sqli-options sql-vertica-options
+     :sqli-login sql-vertica-login-params
+     :sqli-comint-func sql-comint-vertica
+     :list-all ("\\d" . "\\dS")
+     :list-table "\\d %s"
+     :prompt-regexp "^[[:alnum:]_]*=[#>] "
+     :prompt-length 5
+     :prompt-cont-regexp "^[[:alnum:]_]*[-(][#>] "
+     ;; :syntax-alist (("\_" . "w")("\." . "w"))
+     :syntax-alist ((?_ . "w")(?. . "w"))
+     )
+    )
+  "An alist of product specific configuration settings.
+
+Without an entry in this list a product will not be properly
+highlighted and will not support `sql-interactive-mode'.
+
+Each element in the list is in the following format:
+
+ (PRODUCT FEATURE VALUE ...)
+
+where PRODUCT is the appropriate value of `sql-product'.  The
+product name is then followed by FEATURE-VALUE pairs.  If a
+FEATURE is not specified, its VALUE is treated as nil.  FEATURE
+may be any one of the following:
+
+ :name                  string containing the displayable name of
+                        the product.
+
+ :free-software         is the product Free (as in Freedom) software?
+
+ :font-lock             name of the variable containing the product
+                        specific font lock highlighting patterns.
+
+ :sqli-program          name of the variable containing the product
+                        specific interactive program name.
+
+ :sqli-options          name of the variable containing the list
+                        of product specific options.
+
+ :sqli-login            name of the variable containing the list of
+                        login parameters (i.e., user, password,
+                        database and server) needed to connect to
+                        the database.
+
+ :sqli-comint-func      function of two arguments, PRODUCT
+                        and OPTIONS, that will open a comint buffer
+                        and connect to the database.  PRODUCT is the
+                        first argument to be passed to `sql-comint',
+                        and OPTIONS should be included in its second
+                        argument.  The function should use the values
+                        of `sql-user', `sql-password', `sql-database',
+                        `sql-server' and `sql-port' to .  Do product
+                        specific configuration of comint in this
+                        function.  See `sql-comint-oracle' for an
+                        example of such a 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
+                        interpreter.
+
+ :prompt-length         length of the prompt on the line.
+
+ :prompt-cont-regexp    regular expression string that matches
+                        the continuation prompt issued by the
+                        product interpreter.
+
+ :input-filter          function which can filter strings sent to
+                        the command interpreter.  It is also used
+                        by the `sql-send-string',
+                        `sql-send-region', `sql-send-paragraph'
+                        and `sql-send-buffer' functions.  The
+                        function is passed the string sent to the
+                        command interpreter and must return the
+                        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',
+                        `sql-send-paragraph' and
+                        `sql-send-buffer' command.  May be the
+                        literal string or a cons of a regexp to
+                        match an existing terminator in the
+                        string and the terminator to be used if
+                        its absent.  By default \";\".
+
+ :syntax-alist          alist of syntax table entries to enable
+                        special character treatment by font-lock
+                        and imenu.
+
+Other features can be stored but they will be ignored.  However,
+you can develop new functionality which is product independent by
+using `sql-get-product-feature' to lookup the product specific
+settings.")
+
+(defvar sql-indirect-features
+  '(:font-lock :sqli-program :sqli-options :sqli-login :statement))
+
+(defcustom sql-connection-alist nil
+  "An alist of connection parameters for interacting with a SQL product.
+Each element of the alist is as follows:
+
+  (CONNECTION \(SQL-VARIABLE VALUE) ...)
+
+Where CONNECTION is a case-insensitive string identifying the
+connection, SQL-VARIABLE is the symbol name of a SQL mode
+variable, and VALUE is the value to be assigned to the variable.
+The most common SQL-VARIABLE settings associated with a
+connection are: `sql-product', `sql-user', `sql-password',
+`sql-port', `sql-server', and `sql-database'.
+
+If a SQL-VARIABLE is part of the connection, it will not be
+prompted for during login.  The command `sql-connect' starts a
+predefined SQLi session using the parameters from this list.
+Connections defined here appear in the submenu SQL->Start...  for
+making new SQLi sessions."
+  :type `(alist :key-type (string :tag "Connection")
+                :value-type
+                (set
+                 (group (const :tag "Product"  sql-product)
+                        (choice
+                         ,@(mapcar
+                            (lambda (prod-info)
+                              `(const :tag
+                                      ,(or (plist-get (cdr prod-info) :name)
+                                           (capitalize
+                                            (symbol-name (car prod-info))))
+                                      (quote ,(car prod-info))))
+                            sql-product-alist)))
+                 (group (const :tag "Username" sql-user)     string)
+                 (group (const :tag "Password" sql-password) string)
+                 (group (const :tag "Server"   sql-server)   string)
+                 (group (const :tag "Database" sql-database) string)
+                 (group (const :tag "Port"     sql-port)     integer)
+                 (repeat :inline t
+                         (list :tab "Other"
+                               (symbol :tag " Variable Symbol")
+                               ;; FIXME: Why "Value *Expression*"?
+                               (sexp   :tag "Value Expression")))))
+  :version "24.1")
+
+(defun sql-add-connection (connection params)
+  "Add a new connection to `sql-connection-alist'.
+
+If CONNECTION already exists, it is replaced with PARAMS."
+  (setq sql-connection-alist
+        (assoc-delete-all connection sql-connection-alist))
+  (push
+   (cons connection params)
+   sql-connection-alist))
+
+(defvaralias 'sql-dialect 'sql-product)
+(defcustom sql-product 'ansi
+  "Select the SQL database product used.
+This allows highlighting buffers properly when you open them."
+  :type `(choice
+          ,@(mapcar (lambda (prod-info)
+                      `(const :tag
+                              ,(or (plist-get (cdr prod-info) :name)
+                                   (capitalize (symbol-name (car prod-info))))
+                              ,(car prod-info)))
+                    sql-product-alist))
+  :safe 'symbolp)
+
+;; SQL indent support
+
+(defcustom sql-use-indent-support t
+  "If non-nil then use the SQL indent support features of sql-indent.
+The `sql-indent' package in ELPA provides indentation support for
+SQL statements with easy customizations to support varied layout
+requirements.
+
+The package must be available to be loaded and activated."
+  :link '(url-link "https://elpa.gnu.org/packages/sql-indent.html")
+  :type 'boolean
+  :version "27.1")
+
+(defun sql-indent-enable ()
+  "Enable `sqlind-minor-mode' if available and requested."
+  (when (fboundp 'sqlind-minor-mode)
+    (sqlind-minor-mode (if sql-use-indent-support +1 -1))))
+
+;; Secure Password wallet
+
+(require 'auth-source)
+
+(defun sql-auth-source-search-wallet (wallet product user server database port)
+    "Read auth source WALLET to locate the USER secret.
+Sets `auth-sources' to WALLET and uses `auth-source-search' to locate the entry.
+The DATABASE and SERVER are concatenated with a slash between them as the
+host key."
+    (let* ((auth-sources wallet)
+           host
+           secret h-secret sd-secret)
+
+      ;; product
+      (setq product (symbol-name product))
+
+      ;; user
+      (setq user (unless (string-empty-p user) user))
+
+      ;; port
+      (setq port
+            (when (and port (numberp port) (not (zerop port)))
+              (number-to-string port)))
+
+      ;; server
+      (setq server (unless (string-empty-p server) server))
+
+      ;; database
+      (setq database (unless (string-empty-p database) database))
+
+      ;; host
+      (setq host (if server
+                     (if database
+                         (concat server "/" database)
+                       server)
+                   database))
+
+      ;; Perform search
+      (dolist (s (auth-source-search :max 1000))
+        (when (and
+               ;; Is PRODUCT specified, in the entry, and they are equal
+               (if product
+                   (if (plist-member s :product)
+                       (equal (plist-get s :product) product)
+                     t)
+                 t)
+               ;; Is USER specified, in the entry, and they are equal
+               (if user
+                   (if (plist-member s :user)
+                       (equal (plist-get s :user) user)
+                     t)
+                 t)
+               ;; Is PORT specified, in the entry, and they are equal
+               (if port
+                   (if (plist-member s :port)
+                       (equal (plist-get s :port) port)
+                     t)
+                 t))
+          ;; Is HOST specified, in the entry, and they are equal
+          ;; then the H-SECRET list
+          (if (and host
+                   (plist-member s :host)
+                   (equal (plist-get s :host) host))
+              (push s h-secret)
+            ;; Are SERVER and DATABASE specified, present, and equal
+            ;; then the SD-SECRET list
+            (if (and server
+                     (plist-member s :server)
+                     database
+                     (plist-member s :database)
+                     (equal (plist-get s :server) server)
+                     (equal (plist-get s :database) database))
+                (push s sd-secret)
+              ;; Is SERVER specified, in the entry, and they are equal
+              ;; then the base SECRET list
+              (if (and server
+                       (plist-member s :server)
+                       (equal (plist-get s :server) server))
+                  (push s secret)
+                ;; Is DATABASE specified, in the entry, and they are equal
+                ;; then the base SECRET list
+                (if (and database
+                         (plist-member s :database)
+                         (equal (plist-get s :database) database))
+                    (push s secret)))))))
+      (setq secret (or h-secret sd-secret secret))
+
+      ;; If we found a single secret, return the password
+      (when (= 1 (length secret))
+        (setq secret (car secret))
+        (if (plist-member secret :secret)
+            (plist-get secret :secret)
+          nil))))
+
+(defcustom sql-password-wallet
+  (let (wallet w)
+    (dolist (ext '(".json.gpg" ".gpg" ".json" "") wallet)
+      (unless wallet
+        (setq w (locate-user-emacs-file (concat "sql-wallet" ext)
+                                        (concat ".sql-wallet" ext)))
+        (when (file-exists-p w)
+          (setq wallet (list w))))))
+  "Identification of the password wallet.
+See `sql-password-search-wallet-function' to understand how this value
+is used to locate the password wallet."
+  :type (plist-get (symbol-plist 'auth-sources) 'custom-type)
+  :version "27.1")
+
+(defvar sql-password-search-wallet-function #'sql-auth-source-search-wallet
+  "Function to handle the lookup of the database password.
+The specified function will be called as:
+  (wallet-func WALLET PRODUCT USER SERVER DATABASE PORT)
+
+It is expected to return either a string containing the password,
+a function returning the password, or nil.  If you want to support
+another format of password file, then implement a different
+search wallet function and identify the location of the password
+store with `sql-password-wallet'.")
+
+;; misc customization of sql.el behavior
+
+(defcustom sql-electric-stuff nil
+  "Treat some input as electric.
+If set to the symbol `semicolon', then hitting `;' will send current
+input in the SQLi buffer to the process.
+If set to the symbol `go', then hitting `go' on a line by itself will
+send current input in the SQLi buffer to the process.
+If set to nil, then you must use \\[comint-send-input] in order to send
+current input in the SQLi buffer to the process."
+  :type '(choice (const :tag "Nothing" nil)
+                (const :tag "The semicolon `;'" semicolon)
+                (const :tag "The string `go' by itself" go))
+  :version "20.8")
+
+(defcustom sql-send-terminator nil
+  "When non-nil, add a terminator to text sent to the SQL interpreter.
+
+When text is sent to the SQL interpreter (via `sql-send-string',
+`sql-send-region', `sql-send-paragraph' or `sql-send-buffer'), a
+command terminator can be automatically sent as well.  The
+terminator is not sent, if the string sent already ends with the
+terminator.
+
+If this value is t, then the default command terminator for the
+SQL interpreter is sent.  If this value is a string, then the
+string is sent.
+
+If the value is a cons cell of the form (PAT . TERM), then PAT is
+a regexp used to match the terminator in the string and TERM is
+the terminator to be sent.  This form is useful if the SQL
+interpreter has more than one way of submitting a SQL command.
+The PAT regexp can match any of them, and TERM is the way we do
+it automatically."
+
+  :type '(choice (const  :tag "No Terminator" nil)
+                (const  :tag "Default Terminator" t)
+                (string :tag "Terminator String")
+                (cons   :tag "Terminator Pattern and String"
+                        (regexp :tag "Terminator Pattern")
+                        (string :tag "Terminator String")))
+  :version "22.2")
+
+(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.")
+
+(defvar sql-login-delay 7.5 ;; Secs
+  "Maximum number of seconds you are willing to wait for a login connection.")
+
+(defvaralias 'sql-pop-to-buffer-after-send-region 'sql-display-sqli-buffer-function)
+
+(defcustom sql-display-sqli-buffer-function #'display-buffer
+  "Function to be called to display a SQLi buffer after `sql-send-*'.
+
+When set to a function, it will be called to display the buffer.
+When set to t, the default function `pop-to-buffer' will be
+called.  If not set, no attempt will be made to display the
+buffer."
+
+  :type '(choice (const :tag "Default" t)
+                 (const :tag "No display" nil)
+                (function :tag "Display Buffer function"))
+  :version "27.1")
+
+;; imenu support for sql-mode.
+
+(defvar sql-imenu-generic-expression
+  ;; Items are in reverse order because they are rendered in reverse.
+  '(("Rules/Defaults" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*\\(?:rule\\|default\\)\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\s-+\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1)
+    ("Sequences" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*sequence\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1)
+    ("Triggers" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*trigger\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1)
+    ("Functions" "^\\s-*\\(?:create\\s-+\\(?:\\w+\\s-+\\)*\\)?function\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1)
+    ("Procedures" "^\\s-*\\(?:create\\s-+\\(?:\\w+\\s-+\\)*\\)?proc\\(?:edure\\)?\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1)
+    ("Packages" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*package\\s-+\\(?:body\\s-+\\)?\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[.]\\s-*\\)*\\w+\\)" 1)
+    ("Types" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*type\\s-+\\(?:body\\s-+\\)?\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[.]\\s-*\\)*\\w+\\)" 1)
+    ("Indexes" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*index\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1)
+    ("Tables/Views" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*\\(?:table\\|view\\)\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\(?:\\w+\\s-*[_.]\\s-*\\)*\\w+\\)" 1))
+  "Define interesting points in the SQL buffer for `imenu'.
+
+This is used to set `imenu-generic-expression' when SQL mode is
+entered.  Subsequent changes to `sql-imenu-generic-expression' will
+not affect existing SQL buffers because `imenu-generic-expression' is
+a local variable.")
+
+;; history file
+
+(defcustom sql-input-ring-file-name nil
+  "If non-nil, name of the file to read/write input history.
+
+You have to set this variable if you want the history of your commands
+saved from one Emacs session to the next.  If this variable is set,
+exiting the SQL interpreter in an SQLi buffer will write the input
+history to the specified file.  Starting a new process in a SQLi buffer
+will read the input history from the specified file.
+
+This is used to initialize `comint-input-ring-file-name'.
+
+Note that the size of the input history is determined by the variable
+`comint-input-ring-size'."
+  :type '(choice (const :tag "none" nil)
+                (file)))
+
+(defcustom sql-input-ring-separator "\n--\n"
+  "Separator between commands in the history file.
+
+If set to \"\\n\", each line in the history file will be interpreted as
+one command.  Multi-line commands are split into several commands when
+the input ring is initialized from a history file.
+
+This variable used to initialize `comint-input-ring-separator'."
+  :type 'string)
+
+;; The usual hooks
+
+(defcustom sql-interactive-mode-hook '(sql-indent-enable)
+  "Hook for customizing `sql-interactive-mode'."
+  :type 'hook
+  :version "27.1")
+
+(defcustom sql-mode-hook '(sql-indent-enable)
+  "Hook for customizing `sql-mode'."
+  :type 'hook
+  :version "27.1")
+
+(defcustom sql-set-sqli-hook '()
+  "Hook for reacting to changes of `sql-buffer'.
+
+This is called by `sql-set-sqli-buffer' when the value of `sql-buffer'
+is changed."
+  :type 'hook)
+
+(defcustom sql-login-hook '()
+  "Hook for interacting with a buffer in `sql-interactive-mode'.
+
+This hook is invoked in a buffer once it is ready to accept input
+for the first time."
+  :version "24.1"
+  :type 'hook)
+
+;; Customization for Oracle
+
+(defcustom sql-oracle-program "sqlplus"
+  "Command to start sqlplus by Oracle.
+
+Starts `sql-interactive-mode' after doing some setup.
+
+On Windows, \"sqlplus\" usually starts the sqlplus \"GUI\".  In order
+to start the sqlplus console, use \"plus33\" or something similar.
+You will find the file in your Orant\\bin directory."
+  :type 'file)
+
+(defcustom sql-oracle-options '("-L")
+  "List of additional options for `sql-oracle-program'."
+  :type '(repeat string)
+  :version "24.4")
+
+(defcustom sql-oracle-login-params '(user password database)
+  "List of login parameters needed to connect to Oracle."
+  :type 'sql-login-params
+  :version "24.1")
+
+(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 SQL*Plus output is buffered and the
+prompts are not shown until after the text is entered.
+
+You need to issue the following command in SQL*Plus to be safe:
+
+    SET DEFINE OFF
+
+In older versions of SQL*Plus, this was the SET SCAN OFF command."
+  :version "24.1"
+  :type 'boolean)
+
+(defcustom sql-db2-escape-newlines nil
+  "Non-nil if newlines should be escaped by a backslash in DB2 SQLi.
+
+When non-nil, Emacs will automatically insert a space and
+backslash prior to every newline in multi-line SQL statements as
+they are submitted to an interactive DB2 session."
+  :version "24.3"
+  :type 'boolean)
+
+;; Customization for 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."
+  :type 'file)
+
+(defcustom sql-sqlite-options nil
+  "List of additional options for `sql-sqlite-program'."
+  :type '(repeat string)
+  :version "20.8")
+
+(defcustom sql-sqlite-login-params '((database :file nil
+                                               :must-match confirm))
+  "List of login parameters needed to connect to SQLite."
+  :type 'sql-login-params
+  :version "26.1")
+
+;; Customization for MariaDB
+
+;; MariaDB is a drop-in replacement for MySQL, so just make the
+;; MariaDB variables aliases of the MySQL ones.
+
+(defvaralias 'sql-mariadb-program 'sql-mysql-program)
+(defvaralias 'sql-mariadb-options 'sql-mysql-options)
+(defvaralias 'sql-mariadb-login-params 'sql-mysql-login-params)
+
+;; Customization for MySQL
+
+(defcustom sql-mysql-program "mysql"
+  "Command to start mysql by Oracle.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-mysql-options nil
+  "List of additional options for `sql-mysql-program'.
+The following list of options is reported to make things work
+on Windows: \"-C\" \"-t\" \"-f\" \"-n\"."
+  :type '(repeat string)
+  :version "20.8")
+
+(defcustom sql-mysql-login-params '(user password database server)
+  "List of login parameters needed to connect to MySQL."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Solid
+
+(defcustom sql-solid-program "solsql"
+  "Command to start SOLID SQL Editor.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-solid-login-params '(user password server)
+  "List of login parameters needed to connect to Solid."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Sybase
+
+(defcustom sql-sybase-program "isql"
+  "Command to start isql by Sybase.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-sybase-options nil
+  "List of additional options for `sql-sybase-program'.
+Some versions of isql might require the -n option in order to work."
+  :type '(repeat string)
+  :version "20.8")
+
+(defcustom sql-sybase-login-params '(server user password database)
+  "List of login parameters needed to connect to Sybase."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Informix
+
+(defcustom sql-informix-program "dbaccess"
+  "Command to start dbaccess by Informix.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-informix-login-params '(database)
+  "List of login parameters needed to connect to Informix."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Ingres
+
+(defcustom sql-ingres-program "sql"
+  "Command to start sql by Ingres.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-ingres-login-params '(database)
+  "List of login parameters needed to connect to Ingres."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Microsoft
+
+;; Microsoft documentation seems to indicate that ISQL and OSQL are
+;; going away and being replaced by SQLCMD.  If anyone has experience
+;; using SQLCMD, modified product configuration and feedback on its
+;; use would be greatly appreciated.
+
+(defcustom sql-ms-program "osql"
+  "Command to start osql by Microsoft.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-ms-options '("-w" "300" "-n")
+  ;; -w is the linesize
+  "List of additional options for `sql-ms-program'."
+  :type '(repeat string)
+  :version "22.1")
+
+(defcustom sql-ms-login-params '(user password server database)
+  "List of login parameters needed to connect to Microsoft."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Postgres
+
+(defcustom sql-postgres-program "psql"
+  "Command to start psql by Postgres.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-postgres-options '("-P" "pager=off")
+  "List of additional options for `sql-postgres-program'.
+The default setting includes the -P option which breaks older versions
+of the psql client (such as version 6.5.3).  The -P option is equivalent
+to the --pset option.  If you want the psql to prompt you for a user
+name, add the string \"-u\" to the list of options.  If you want to
+provide a user name on the command line (newer versions such as 7.1),
+add your name with a \"-U\" prefix (such as \"-Umark\") to the list."
+  :type '(repeat string)
+  :version "20.8")
+
+(defcustom sql-postgres-login-params
+  `((user :default ,(user-login-name))
+    (database :default ,(user-login-name)
+              :completion ,(completion-table-dynamic
+                            (lambda (_) (sql-postgres-list-databases)))
+              :must-match confirm)
+    server)
+  "List of login parameters needed to connect to Postgres."
+  :type 'sql-login-params
+  :version "26.1")
+
+(defun sql-postgres-list-databases ()
+  "Return a list of available PostgreSQL databases."
+  (when (executable-find sql-postgres-program)
+    (let ((res '()))
+      (ignore-errors
+        (dolist (row (process-lines sql-postgres-program
+                                    "--list"
+                                    "--no-psqlrc"
+                                    "--tuples-only"))
+          (when (string-match "^ \\([^ |]+\\) +|.*" row)
+            (push (match-string 1 row) res))))
+      (nreverse res))))
+
+;; Customization for Interbase
+
+(defcustom sql-interbase-program "isql"
+  "Command to start isql by Interbase.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-interbase-options nil
+  "List of additional options for `sql-interbase-program'."
+  :type '(repeat string)
+  :version "20.8")
+
+(defcustom sql-interbase-login-params '(user password database)
+  "List of login parameters needed to connect to Interbase."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for DB2
+
+(defcustom sql-db2-program "db2"
+  "Command to start db2 by IBM.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-db2-options nil
+  "List of additional options for `sql-db2-program'."
+  :type '(repeat string)
+  :version "20.8")
+
+(defcustom sql-db2-login-params nil
+  "List of login parameters needed to connect to DB2."
+  :type 'sql-login-params
+  :version "24.1")
+
+;; Customization for Linter
+
+(defcustom sql-linter-program "inl"
+  "Command to start inl by RELEX.
+
+Starts `sql-interactive-mode' after doing some setup."
+  :type 'file)
+
+(defcustom sql-linter-options nil
+  "List of additional options for `sql-linter-program'."
+  :type '(repeat string)
+  :version "21.3")
+
+(defcustom sql-linter-login-params '(user password database server)
+  "Login parameters to needed to connect to Linter."
+  :type 'sql-login-params
+  :version "24.1")
+
+\f
+
+;;; Variables which do not need customization
+
+(defvar sql-user-history nil
+  "History of usernames used.")
+
+(defvar sql-database-history nil
+  "History of databases used.")
+
+(defvar sql-server-history nil
+  "History of servers 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.
+
+The global value of `sql-buffer' is the name of the latest SQLi buffer
+created.  Any SQL buffer created will make a local copy of this value.
+See `sql-interactive-mode' for more on multiple sessions.  If you want
+to change the SQLi buffer a SQL mode sends its SQL strings to, change
+the local value of `sql-buffer' using \\[sql-set-sqli-buffer].")
+
+(defvar sql-prompt-regexp nil
+  "Prompt used to initialize `comint-prompt-regexp'.
+
+You can change `sql-prompt-regexp' on `sql-interactive-mode-hook'.")
+
+(defvar sql-prompt-length 0
+  "Prompt used to set `left-margin' in `sql-interactive-mode'.
+
+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)
+  "Return 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 a 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)
+         (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)
+                    (and (stringp connection)
+                         (string= connection sql-connection))))))))
+
+(defun sql-is-sqli-buffer-p (buffer)
+  "Return non-nil if buffer is a SQLi buffer."
+  (when buffer
+    (setq buffer (get-buffer buffer))
+    (and buffer
+         (buffer-live-p buffer)
+         (with-current-buffer buffer
+           (derived-mode-p 'sql-interactive-mode)))))
+
+;; Keymap for sql-interactive-mode.
+
+(defvar-keymap sql-interactive-mode-map
+  :doc "Mode map used for `sql-interactive-mode'.
+Based on `comint-mode-map'."
+  :parent comint-mode-map
+  "C-j"       #'sql-accumulate-and-indent
+  "C-c C-w"   #'sql-copy-column
+  "O"         #'sql-magic-go
+  "o"         #'sql-magic-go
+  ";"         #'sql-magic-semicolon
+  "C-c C-l a" #'sql-list-all
+  "C-c C-l t" #'sql-list-table)
+
+;; Keymap for sql-mode.
+
+(defvar-keymap sql-mode-map
+  :doc "Mode map used for `sql-mode'."
+  "C-c C-c"   #'sql-send-paragraph
+  "C-c C-r"   #'sql-send-region
+  "C-c C-s"   #'sql-send-string
+  "C-c C-b"   #'sql-send-buffer
+  "C-c C-n"   #'sql-send-line-and-next
+  "C-c C-i"   #'sql-product-interactive
+  "C-c C-z"   #'sql-show-sqli-buffer
+  "C-c C-l a" #'sql-list-all
+  "C-c C-l t" #'sql-list-table
+  "<remap> <beginning-of-defun>" #'sql-beginning-of-statement
+  "<remap> <end-of-defun>"       #'sql-end-of-statement)
+
+;; easy menu for sql-mode.
+
+(easy-menu-define
+ sql-mode-menu sql-mode-map
+ "Menu for `sql-mode'."
+ `("SQL"
+   ["Send Paragraph" sql-send-paragraph (sql-buffer-live-p sql-buffer)]
+   ["Send Region" sql-send-region (and mark-active
+                                      (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)
+    :enable (sql-get-product-feature sql-product :sqli-comint-func)]
+   ("Start..."
+    :visible sql-connection-alist
+    :filter sql-connection-menu-filter
+    "--"
+    ["New SQLi Session" sql-product-interactive (sql-get-product-feature sql-product :sqli-comint-func)])
+   ["--"
+    :visible sql-connection-alist]
+   ["Show SQLi buffer" sql-show-sqli-buffer t]
+   ["Set SQLi buffer" sql-set-sqli-buffer t]
+   ["Pop to SQLi buffer after send"
+    sql-toggle-pop-to-buffer-after-send-region
+    :style toggle
+    :selected sql-pop-to-buffer-after-send-region]
+   ["--" nil nil]
+   ("Product"
+    ,@(mapcar (lambda (prod-info)
+                (let* ((prod (pop prod-info))
+                       (name (or (plist-get prod-info :name)
+                                 (capitalize (symbol-name prod))))
+                       (cmd (intern (format "sql-highlight-%s-keywords" prod))))
+                  (fset cmd `(lambda () ,(format "Highlight %s SQL keywords." name)
+                               (interactive)
+                               (sql-set-product ',prod)))
+                  (vector name cmd
+                          :style 'radio
+                          :selected `(eq sql-product ',prod))))
+              sql-product-alist))))
+
+;; easy menu for sql-interactive-mode.
+
+(easy-menu-define
+ sql-interactive-mode-menu sql-interactive-mode-map
+ "Menu for `sql-interactive-mode'."
+ '("SQL"
+   ["Rename Buffer" sql-rename-buffer t]
+   ["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 init
+;; file.  Abbrevs have to be enabled in your init file, too.
+
+(define-abbrev-table 'sql-mode-abbrev-table
+  '(("ins" "insert" nil nil t)
+    ("upd" "update" nil nil t)
+    ("del" "delete" nil nil t)
+    ("sel" "select" nil nil t)
+    ("proc" "procedure" nil nil t)
+    ("func" "function" nil nil t)
+    ("cr" "create" nil nil t))
+  "Abbrev table used in `sql-mode' and `sql-interactive-mode'.")
+
+;; Syntax Table
+
+(defvar sql-mode-syntax-table
+  (let ((table (make-syntax-table)))
+    ;; C-style comments /**/ (see elisp manual "Syntax Flags"))
+    (modify-syntax-entry ?/ ". 14" table)
+    (modify-syntax-entry ?* ". 23" table)
+    ;; double-dash starts comments
+    (modify-syntax-entry ?- ". 12b" table)
+    ;; newline and formfeed end comments
+    (modify-syntax-entry ?\n "> b" table)
+    (modify-syntax-entry ?\f "> b" table)
+    ;; single quotes (') delimit strings
+    (modify-syntax-entry ?' "\"" table)
+    ;; double quotes (") don't delimit strings
+    (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'.")
+
+;; Motion Function Keywords
+
+(defvar 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.")
+
+(defvar sql-oracle-statement-starters
+  (regexp-opt '("declare" "begin" "with"))
+  "Additional statement-starting keywords in Oracle.")
+
+(defvar sql-postgres-statement-starters
+  (regexp-opt '("with"))
+  "Additional statement-starting keywords in Postgres.")
+
+;; Font lock support
+
+(defvar sql-mode-font-lock-object-name
+  (eval-when-compile
+    (list (concat "^\\s-*\\(?:create\\|drop\\|alter\\)\\s-+" ;; lead off with CREATE, DROP or ALTER
+                 "\\(?:\\w+\\s-+\\)*"  ;; optional intervening keywords
+                 "\\(?:table\\|view\\|\\(?:package\\|type\\)\\(?:\\s-+body\\)?\\|proc\\(?:edure\\)?"
+                 "\\|function\\|trigger\\|sequence\\|rule\\|default\\)\\s-+"
+                  "\\(?:if\\s-+not\\s-+exists\\s-+\\)?" ;; IF NOT EXISTS
+                 "\\(\\w+\\(?:\\s-*[.]\\s-*\\w+\\)*\\)")
+         1 'font-lock-function-name-face))
+
+  "Pattern to match the names of top-level objects.
+
+The pattern matches the name in a CREATE, DROP or ALTER
+statement.  The format of variable should be a valid
+`font-lock-keywords' entry.")
+
+;; While there are international and American standards for SQL, they
+;; are not followed closely, and most vendors offer significant
+;; capabilities beyond those defined in the standard specifications.
+
+;; SQL mode provides support for highlighting based on the product.  In
+;; addition to highlighting the product keywords, any ANSI keywords not
+;; used by the product are also highlighted.  This will help identify
+;; keywords that could be restricted in future versions of the product
+;; or might be a problem if ported to another product.
+
+;; To reduce the complexity and size of the regular expressions
+;; generated to match keywords, ANSI keywords are filtered out of
+;; product keywords if they are equivalent.  To do this, we define a
+;; function `sql-font-lock-keywords-builder' that removes any keywords
+;; that are matched by the ANSI patterns and results in the same face
+;; being applied.  For this to work properly, we must play some games
+;; with the execution and compile time behavior.  This code is a
+;; little tricky but works properly.
+
+;; When defining the keywords for individual products you should
+;; include all of the keywords that you want matched.  The filtering
+;; against the ANSI keywords will be automatic if you use the
+;; `sql-font-lock-keywords-builder' function and follow the
+;; implementation pattern used for the other products in this file.
+
+(defvar sql-mode-ansi-font-lock-keywords)
+
+(eval-and-compile
+  (defun sql-font-lock-keywords-builder (face boundaries &rest keywords)
+    "Generation of regexp matching any one of KEYWORDS."
+
+    (let ((bdy (or boundaries '("\\b" . "\\b")))
+         kwd)
+
+      ;; 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)))))
+
+      ;; Create a properly formed font-lock-keywords item
+      (cons (concat (car bdy)
+                   (regexp-opt kwd t)
+                   (cdr bdy))
+           face)))
+
+  (defun sql-regexp-abbrev (keyword)
+    (let ((brk   (string-search "~" 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
+       (list
+        ;; ANSI Non Reserved keywords
+        (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"ada" "asensitive" "assignment" "asymmetric" "atomic" "between"
+"bitvar" "called" "catalog_name" "chain" "character_set_catalog"
+"character_set_name" "character_set_schema" "checked" "class_origin"
+"cobol" "collation_catalog" "collation_name" "collation_schema"
+"column_name" "command_function" "command_function_code" "committed"
+"condition_number" "connection_name" "constraint_catalog"
+"constraint_name" "constraint_schema" "contains" "cursor_name"
+"datetime_interval_code" "datetime_interval_precision" "defined"
+"definer" "dispatch" "dynamic_function" "dynamic_function_code"
+"existing" "exists" "final" "fortran" "generated" "granted"
+"hierarchy" "hold" "implementation" "infix" "insensitive" "instance"
+"instantiable" "invoker" "key_member" "key_type" "length" "m"
+"message_length" "message_octet_length" "message_text" "method" "more"
+"mumps" "name" "nullable" "number" "options" "overlaps" "overriding"
+"parameter_mode" "parameter_name" "parameter_ordinal_position"
+"parameter_specific_catalog" "parameter_specific_name"
+"parameter_specific_schema" "pascal" "pli" "position" "repeatable"
+"returned_length" "returned_octet_length" "returned_sqlstate"
+"routine_catalog" "routine_name" "routine_schema" "row_count" "scale"
+"schema_name" "security" "self" "sensitive" "serializable"
+"server_name" "similar" "simple" "source" "specific_name" "style"
+"subclass_origin" "sublist" "symmetric" "system" "table_name"
+"transaction_active" "transactions_committed"
+"transactions_rolled_back" "transform" "transforms" "trigger_catalog"
+"trigger_name" "trigger_schema" "type" "uncommitted" "unnamed"
+"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"
+"allocate" "alter" "and" "any" "are" "as" "asc" "assertion" "at"
+"authorization" "before" "begin" "both" "breadth" "by" "call"
+"cascade" "cascaded" "case" "catalog" "check" "class" "close"
+"collate" "collation" "column" "commit" "completion" "connect"
+"connection" "constraint" "constraints" "constructor" "continue"
+"corresponding" "create" "cross" "cube" "current" "cursor" "cycle"
+"data" "day" "deallocate" "declare" "default" "deferrable" "deferred"
+"delete" "depth" "deref" "desc" "describe" "descriptor" "destroy"
+"destructor" "deterministic" "diagnostics" "dictionary" "disconnect"
+"distinct" "domain" "drop" "dynamic" "each" "else" "end" "equals"
+"escape" "every" "except" "exception" "exec" "execute" "external"
+"false" "fetch" "first" "for" "foreign" "found" "free" "from" "full"
+"function" "general" "get" "global" "go" "goto" "grant" "group"
+"grouping" "having" "host" "hour" "identity" "ignore" "immediate" "in"
+"indicator" "initialize" "initially" "inner" "inout" "input" "insert"
+"intersect" "into" "is" "isolation" "iterate" "join" "key" "language"
+"last" "lateral" "leading" "left" "less" "level" "like" "limit"
+"local" "locator" "map" "match" "minute" "modifies" "modify" "module"
+"month" "names" "natural" "new" "next" "no" "none" "not" "null" "of"
+"off" "old" "on" "only" "open" "operation" "option" "or" "order"
+"ordinality" "out" "outer" "output" "pad" "parameter" "parameters"
+"partial" "path" "postfix" "prefix" "preorder" "prepare" "preserve"
+"primary" "prior" "privileges" "procedure" "public" "read" "reads"
+"recursive" "references" "referencing" "relative" "restrict" "result"
+"return" "returns" "revoke" "right" "role" "rollback" "rollup"
+"routine" "rows" "savepoint" "schema" "scroll" "search" "second"
+"section" "select" "sequence" "session" "set" "sets" "size" "some"
+"space" "specific" "specifictype" "sql" "sqlexception" "sqlstate"
+"sqlwarning" "start" "state" "statement" "static" "structure" "table"
+"temporary" "terminate" "than" "then" "timezone_hour"
+"timezone_minute" "to" "trailing" "transaction" "translation"
+"trigger" "true" "under" "union" "unique" "unknown" "unnest" "update"
+"usage" "using" "value" "values" "variable" "view" "when" "whenever"
+"where" "with" "without" "work" "write" "year"
+)
+
+        ;; ANSI Functions
+        (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
+"abs" "avg" "bit_length" "cardinality" "cast" "char_length"
+"character_length" "coalesce" "convert" "count" "current_date"
+"current_path" "current_role" "current_time" "current_timestamp"
+"current_user" "extract" "localtime" "localtimestamp" "lower" "max"
+"min" "mod" "nullif" "octet_length" "overlay" "placing" "session_user"
+"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"
+"date" "dec" "decimal" "double" "float" "int" "integer" "interval"
+"large" "national" "nchar" "nclob" "numeric" "object" "precision"
+"real" "ref" "row" "scope" "smallint" "time" "timestamp" "varchar"
+"varying" "zone"
+))))
+
+(defvar sql-mode-ansi-font-lock-keywords
+  (eval-when-compile sql-mode-ansi-font-lock-keywords)
+  "ANSI SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-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.
+  (if (or (and (not (derived-mode-p 'sql-mode))
+               (not (derived-mode-p 'sql-interactive-mode)))
+          (not sql-buffer)
+          (not (eq sql-product 'oracle)))
+      (user-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
+        (setq-local 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
+     ;;   Only recognized in they start in column 1 and the
+     ;;   abbreviation is followed by a space or the end of line.
+     (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)
+     '("&?&\\(?:\\sw\\|\\s_\\)+[.]?" 0 font-lock-preprocessor-face t)
+
+     ;; Oracle PL/SQL Attributes (Declare these first to match %TYPE correctly)
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face '("%" . "\\b")
+"bulk_exceptions" "bulk_rowcount" "found" "isopen" "notfound"
+"rowcount" "rowtype" "type"
+)
+     ;; Oracle Functions
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
+"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"
+"connect_by_root" "connect_by_iscycle" "connect_by_isleaf"
+"corr_k" "corr_s" "cos" "cosh" "count" "covar_pop" "covar_samp"
+"cube_table" "cume_dist" "current_date" "current_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" "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_like"
+"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" "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"
+"advise" "after" "agent" "aggregate" "all" "allocate" "allow" "alter"
+"always" "analyze" "ancillary" "and" "any" "apply" "archive"
+"archivelog" "array" "as" "asc" "associate" "at" "attribute"
+"attributes" "audit" "authenticated" "authid" "authorization" "auto"
+"autoallocate" "automatic" "availability" "backup" "before" "begin"
+"behalf" "between" "binding" "bitmap" "block" "blocksize" "body"
+"both" "buffer_pool" "build" "by"  "cache" "call" "cancel"
+"cascade" "case" "category" "certificate" "chained" "change" "check"
+"checkpoint" "child" "chunk" "class" "clear" "clone" "close" "cluster"
+"column" "column_value" "columns" "comment" "commit" "committed"
+"compatibility" "compile" "complete" "composite_limit" "compress"
+"compute" "connect" "connect_time" "consider" "consistent"
+"constraint" "constraints" "constructor" "contents" "context"
+"continue" "controlfile" "corruption" "cost" "cpu_per_call"
+"cpu_per_session" "create" "cross" "cube" "current" "currval" "cycle"
+"dangling" "data" "database" "datafile" "datafiles" "day" "ddl"
+"deallocate" "debug" "default" "deferrable" "deferred" "definer"
+"delay" "delete" "demand" "desc" "determines" "deterministic"
+"dictionary" "dimension" "directory" "disable" "disassociate"
+"disconnect" "distinct" "distinguished" "distributed" "dml" "drop"
+"each" "element" "else" "enable" "end" "equals_path" "escape"
+"estimate" "except" "exceptions" "exchange" "excluding" "exists"
+"expire" "explain" "extent" "external" "externally"
+"failed_login_attempts" "fast" "file" "final" "finish" "flush" "for"
+"force" "foreign" "freelist" "freelists" "freepools" "fresh" "from"
+"full" "function" "functions" "generated" "global" "global_name"
+"globally" "grant" "group" "grouping" "groups" "guard" "hash"
+"hashkeys" "having" "heap" "hierarchy" "id" "identified" "identifier"
+"idle_time" "immediate" "in" "including" "increment" "index" "indexed"
+"indexes" "indextype" "indextypes" "indicator" "initial" "initialized"
+"initially" "initrans" "inner" "insert" "instance" "instantiable"
+"instead" "intersect" "into" "invalidate" "is" "isolation" "java"
+"join"  "keep" "key" "kill" "language" "left" "less" "level"
+"levels" "library" "like" "like2" "like4" "likec" "limit" "link"
+"list" "lob" "local" "location" "locator" "lock" "log" "logfile"
+"logging" "logical" "logical_reads_per_call"
+"logical_reads_per_session"  "managed" "management" "manual" "map"
+"mapping" "master" "matched" "materialized" "maxdatafiles"
+"maxextents" "maximize" "maxinstances" "maxlogfiles" "maxloghistory"
+"maxlogmembers" "maxsize" "maxtrans" "maxvalue" "member" "memory"
+"merge" "migrate" "minextents" "minimize" "minimum" "minus" "minvalue"
+"mode" "modify" "monitoring" "month" "mount" "move" "movement" "name"
+"named" "natural" "nested" "never" "new" "next" "nextval" "no"
+"noarchivelog" "noaudit" "nocache" "nocompress" "nocopy" "nocycle"
+"nodelay" "noforce" "nologging" "nomapping" "nomaxvalue" "nominimize"
+"nominvalue" "nomonitoring" "none" "noorder" "noparallel" "norely"
+"noresetlogs" "noreverse" "normal" "norowdependencies" "nosort"
+"noswitch" "not" "nothing" "notimeout" "novalidate" "nowait" "null"
+"nulls" "object" "of" "off" "offline" "oidindex" "old" "on" "online"
+"only" "open" "operator" "optimal" "option" "or" "order"
+"organization" "out" "outer" "outline" "over" "overflow" "overriding"
+"package" "packages" "parallel" "parallel_enable" "parameters"
+"parent" "partition" "partitions" "password" "password_grace_time"
+"password_life_time" "password_lock_time" "password_reuse_max"
+"password_reuse_time" "password_verify_function" "pctfree"
+"pctincrease" "pctthreshold" "pctused" "pctversion" "percent"
+"performance" "permanent" "pfile" "physical" "pipelined" "pivot" "plan"
+"post_transaction" "pragma" "prebuilt" "preserve" "primary" "private"
+"private_sga" "privileges" "procedure" "profile" "protection" "public"
+"purge" "query" "quiesce" "quota" "range" "read" "reads" "rebuild"
+"records_per_block" "recover" "recovery" "recycle" "reduced" "ref"
+"references" "referencing" "refresh" "register" "reject" "relational"
+"rely" "rename" "reset" "resetlogs" "resize" "resolve" "resolver"
+"resource" "restrict" "restrict_references" "restricted" "result"
+"resumable" "resume" "retention" "return" "returning" "reuse"
+"reverse" "revoke" "rewrite" "right" "rnds" "rnps" "role" "roles"
+"rollback" "rollup" "row" "rowdependencies" "rownum" "rows" "sample"
+"savepoint" "scan" "schema" "scn" "scope" "segment" "select"
+"selectivity" "self" "sequence" "serializable" "session"
+"sessions_per_user" "set" "sets" "settings" "shared" "shared_pool"
+"shrink" "shutdown" "siblings" "sid" "single" "size" "skip" "some"
+"sort" "source" "space" "specification" "spfile" "split" "standby"
+"start" "statement_id" "static" "statistics" "stop" "storage" "store"
+"structure" "subpartition" "subpartitions" "substitutable"
+"successful" "supplemental" "suspend" "switch" "switchover" "synonym"
+"sys" "system" "table" "tables" "tablespace" "tempfile" "template"
+"temporary" "test" "than" "then" "thread" "through" "time_zone"
+"timeout" "to" "trace" "transaction" "trigger" "triggers" "truncate"
+"trust" "type" "types" "unarchived" "under" "under_path" "undo"
+"uniform" "union" "unique" "unlimited" "unlock" "unpivot" "unquiesce"
+"unrecoverable" "until" "unusable" "unused" "update" "upgrade" "usage"
+"use" "using" "validate" "validation" "value" "values" "variable"
+"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" "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 Functions
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
+"delete" "trim" "extend" "exists" "first" "last" "count" "limit"
+"prior" "next" "sqlcode" "sqlerrm"
+)
+
+     ;; 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
+"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 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" "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.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-oracle-font-lock-keywords'.  You may want
+to add functions and PL/SQL keywords.")
+
+(defvar sql-mode-postgres-font-lock-keywords
+  (eval-when-compile
+    (list
+     ;; 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
+"abort" "absolute" "access" "action" "add" "admin" "after" "aggregate"
+"also" "alter" "always" "assertion" "assignment" "at" "attribute" "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"
+"extension" "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" "label" "language" "large" "last"
+"lc_collate" "lc_ctype" "leakproof" "least" "level" "listen" "load" "local"
+"location" "lock" "login" "mapping" "match" "maxvalue" "minute"
+"minvalue" "mode" "month" "move" "names" "national" "nchar"
+"next" "no" "nocreatedb" "nocreaterole" "nocreateuser" "noinherit"
+"nologin" "none"  "noreplication" "nosuperuser" "nothing" "notify" "nowait" "nullif"
+"nulls" "object" "of" "off" "oids" "operator" "option" "options" "out"
+"overlay" "owned" "owner" "parser" "partial" "partition" "passing" "password"
+"plans" "position" "preceding" "precision" "prepare" "prepared" "preserve" "prior"
+"privileges" "procedural" "procedure" "quote" "range" "read"
+"reassign" "recheck" "recursive" "ref" "reindex" "relative" "release"
+"rename" "repeatable" "replace" "replica" "replication" "reset" "restart" "restrict"
+"returns" "revoke" "role" "rollback" "row" "rows" "rule" "savepoint"
+"schema" "scroll" "search" "second" "security" "sequence"
+"serializable" "server" "session" "set" "setof" "share" "show"
+"simple" "snapshot" "stable" "standalone" "start" "statement" "statistics"
+"stdin" "stdout" "storage" "strict" "strip" "substring" "superuser"
+"sysid" "system" "tables" "tablespace" "temp" "template" "temporary"
+"transaction" "treat" "trim" "truncate" "trusted" "type" "types"
+"unbounded" "uncommitted" "unencrypted" "unlisten" "unlogged" "until"
+"update" "vacuum" "valid" "validate" "validator" "value" "values" "varying" "version"
+"view" "volatile" "whitespace" "without" "work" "wrapper" "write"
+"xmlattributes" "xmlconcat" "xmlelement" "xmlexists" "xmlforest" "xmlparse"
+"xmlpi" "xmlroot" "xmlserialize" "year" "yes" "zone"
+)
+
+     ;; Postgres Reserved
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"all" "analyse" "analyze" "and" "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" "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 PL/pgSQL
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"assign" "if" "case" "loop" "while" "for" "foreach" "exit" "elsif" "return"
+"raise" "execsql" "dynexecute" "perform" "getdiag" "open" "fetch" "move" "close"
+)
+
+     ;; Postgres Data Types
+     (sql-font-lock-keywords-builder 'font-lock-type-face nil
+ "bigint" "bigserial" "bit" "bool" "boolean" "box" "bytea" "char" "character"
+"cidr" "circle" "date" "daterange" "decimal" "double" "float4" "float8" "inet"
+"int" "int2" "int4" "int4range" "int8" "int8range" "integer" "interval"
+"jsonb" "jsonpath" "line" "lseg" "macaddr" "macaddr8" "money" "name" "numeric"
+"numrange" "oid" "path" "point" "polygon" "precision" "real" "regclass"
+"regcollation" "regconfig" "regdictionary" "regnamespace " "regoper"
+"regoperator" "regproc" "regprocedure" "regrole" "regtype" "sequences"
+"serial" "serial4" "serial8" "smallint" "smallserial" "text" "time"
+"timestamp" "timestamptz" "timetz" "tsquery" "tsrange" "tstzrange" "tsvector"
+"txid_snapshot" "unknown" "uuid" "varbit" "varchar" "varying" "without" "xml"
+"zone"
+)))
+
+  "Postgres SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-postgres-font-lock-keywords'.")
+
+(defvar sql-mode-linter-font-lock-keywords
+  (eval-when-compile
+    (list
+     ;; Linter Keywords
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"autocommit" "autoinc" "autorowid" "cancel" "cascade" "channel"
+"committed" "count" "countblob" "cross" "current" "data" "database"
+"datafile" "datafiles" "datesplit" "dba" "dbname" "default" "deferred"
+"denied" "description" "device" "difference" "directory" "error"
+"escape" "euc" "exclusive" "external" "extfile" "false" "file"
+"filename" "filesize" "filetime" "filter" "findblob" "first" "foreign"
+"full" "fuzzy" "global" "granted" "ignore" "immediate" "increment"
+"indexes" "indexfile" "indexfiles" "indextime" "initial" "integrity"
+"internal" "key" "last_autoinc" "last_rowid" "limit" "linter"
+"linter_file_device" "linter_file_size" "linter_name_length" "ln"
+"local" "login" "maxisn" "maxrow" "maxrowid" "maxvalue" "message"
+"minvalue" "module" "names" "national" "natural" "new" "new_table"
+"no" "node" "noneuc" "nulliferror" "numbers" "off" "old" "old_table"
+"only" "operation" "optimistic" "option" "page" "partially" "password"
+"phrase" "plan" "precision" "primary" "priority" "privileges"
+"proc_info_size" "proc_par_name_len" "protocol" "quant" "range" "raw"
+"read" "record" "records" "references" "remote" "rename" "replication"
+"restart" "rewrite" "root" "row" "rule" "savepoint" "security"
+"sensitive" "sequence" "serializable" "server" "since" "size" "some"
+"startup" "statement" "station" "success" "sys_guid" "tables" "test"
+"timeout" "trace" "transaction" "translation" "trigger"
+"trigger_info_size" "true" "trunc" "uncommitted" "unicode" "unknown"
+"unlimited" "unlisted" "user" "utf8" "value" "varying" "volumes"
+"wait" "windows_code" "workspace" "write" "xml"
+)
+
+     ;; Linter Reserved
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"access" "action" "add" "address" "after" "all" "alter" "always" "and"
+"any" "append" "as" "asc" "ascic" "async" "at_begin" "at_end" "audit"
+"aud_obj_name_len" "backup" "base" "before" "between" "blobfile"
+"blobfiles" "blobpct" "brief" "browse" "by" "case" "cast" "check"
+"clear" "close" "column" "comment" "commit" "connect" "contains"
+"correct" "create" "delete" "desc" "disable" "disconnect" "distinct"
+"drop" "each" "ef" "else" "enable" "end" "event" "except" "exclude"
+"execute" "exists" "extract" "fetch" "finish" "for" "from" "get"
+"grant" "group" "having" "identified" "in" "index" "inner" "insert"
+"instead" "intersect" "into" "is" "isolation" "join" "left" "level"
+"like" "lock" "mode" "modify" "not" "nowait" "null" "of" "on" "open"
+"or" "order" "outer" "owner" "press" "prior" "procedure" "public"
+"purge" "rebuild" "resource" "restrict" "revoke" "right" "role"
+"rollback" "rownum" "select" "session" "set" "share" "shutdown"
+"start" "stop" "sync" "synchronize" "synonym" "sysdate" "table" "then"
+"to" "union" "unique" "unlock" "until" "update" "using" "values"
+"view" "when" "where" "with" "without"
+)
+
+     ;; Linter Functions
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
+"abs" "acos" "asin" "atan" "atan2" "avg" "ceil" "cos" "cosh" "divtime"
+"exp" "floor" "getbits" "getblob" "getbyte" "getlong" "getraw"
+"getstr" "gettext" "getword" "hextoraw" "lenblob" "length" "log"
+"lower" "lpad" "ltrim" "max" "min" "mod" "monthname" "nvl"
+"octet_length" "power" "rand" "rawtohex" "repeat_string"
+"right_substr" "round" "rpad" "rtrim" "sign" "sin" "sinh" "soundex"
+"sqrt" "sum" "tan" "tanh" "timeint_to_days" "to_char" "to_date"
+"to_gmtime" "to_localtime" "to_number" "trim" "upper" "decode"
+"substr" "substring" "chr" "dayname" "days" "greatest" "hex" "initcap"
+"instr" "least" "multime" "replace" "width"
+)
+
+     ;; Linter Data Types
+     (sql-font-lock-keywords-builder 'font-lock-type-face nil
+"bigint" "bitmap" "blob" "boolean" "char" "character" "date"
+"datetime" "dec" "decimal" "double" "float" "int" "integer" "nchar"
+"number" "numeric" "real" "smallint" "varbyte" "varchar" "byte"
+"cursor" "long"
+)))
+
+  "Linter SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+regular expressions are created during compilation by calling the
+function `regexp-opt'.")
+
+(defvar sql-mode-ms-font-lock-keywords
+  (eval-when-compile
+    (list
+     ;; MS isql/osql Commands
+     (cons
+      (concat
+       "^\\(?:\\(?:set\\s-+\\(?:"
+       (regexp-opt '(
+"datefirst" "dateformat" "deadlock_priority" "lock_timeout"
+"concat_null_yields_null" "cursor_close_on_commit"
+"disable_def_cnst_chk" "fips_flagger" "identity_insert" "language"
+"offsets" "quoted_identifier" "arithabort" "arithignore" "fmtonly"
+"nocount" "noexec" "numeric_roundabort" "parseonly"
+"query_governor_cost_limit" "rowcount" "textsize" "ansi_defaults"
+"ansi_null_dflt_off" "ansi_null_dflt_on" "ansi_nulls" "ansi_padding"
+"ansi_warnings" "forceplan" "showplan_all" "showplan_text"
+"statistics" "implicit_transactions" "remote_proc_transactions"
+"transaction" "xact_abort"
+)
+                   t)
+       "\\)\\)\\|go\\s-*\\|use\\s-+\\|setuser\\s-+\\|dbcc\\s-+\\).*$")
+      'font-lock-doc-face)
+
+     ;; MS Reserved
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"absolute" "add" "all" "alter" "and" "any" "as" "asc" "authorization"
+"avg" "backup" "begin" "between" "break" "browse" "bulk" "by"
+"cascade" "case" "check" "checkpoint" "close" "clustered" "coalesce"
+"column" "commit" "committed" "compute" "confirm" "constraint"
+"contains" "containstable" "continue" "controlrow" "convert" "count"
+"create" "cross" "current" "current_date" "current_time"
+"current_timestamp" "current_user" "database" "deallocate" "declare"
+"default" "delete" "deny" "desc" "disk" "distinct" "distributed"
+"double" "drop" "dummy" "dump" "else" "end" "errlvl" "errorexit"
+"escape" "except" "exec" "execute" "exists" "exit" "fetch" "file"
+"fillfactor" "first" "floppy" "for" "foreign" "freetext"
+"freetexttable" "from" "full" "goto" "grant" "group" "having"
+"holdlock" "identity" "identity_insert" "identitycol" "if" "in"
+"index" "inner" "insert" "intersect" "into" "is" "isolation" "join"
+"key" "kill" "last" "left" "level" "like" "lineno" "load" "max" "min"
+"mirrorexit" "national" "next" "nocheck" "nolock" "nonclustered" "not"
+"null" "nullif" "of" "off" "offsets" "on" "once" "only" "open"
+"opendatasource" "openquery" "openrowset" "option" "or" "order"
+"outer" "output" "over" "paglock" "percent" "perm" "permanent" "pipe"
+"plan" "precision" "prepare" "primary" "print" "prior" "privileges"
+"proc" "procedure" "processexit" "public" "raiserror" "read"
+"readcommitted" "readpast" "readtext" "readuncommitted" "reconfigure"
+"references" "relative" "repeatable" "repeatableread" "replication"
+"restore" "restrict" "return" "revoke" "right" "rollback" "rowcount"
+"rowguidcol" "rowlock" "rule" "save" "schema" "select" "serializable"
+"session_user" "set" "shutdown" "some" "statistics" "sum"
+"system_user" "table" "tablock" "tablockx" "tape" "temp" "temporary"
+"textsize" "then" "to" "top" "tran" "transaction" "trigger" "truncate"
+"tsequal" "uncommitted" "union" "unique" "update" "updatetext"
+"updlock" "use" "user" "values" "view" "waitfor" "when" "where"
+"while" "with" "work" "writetext" "collate" "function" "openxml"
+"returns"
+)
+
+     ;; MS Functions
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
+"@@connections" "@@cpu_busy" "@@cursor_rows" "@@datefirst" "@@dbts"
+"@@error" "@@fetch_status" "@@identity" "@@idle" "@@io_busy"
+"@@langid" "@@language" "@@lock_timeout" "@@max_connections"
+"@@max_precision" "@@nestlevel" "@@options" "@@pack_received"
+"@@pack_sent" "@@packet_errors" "@@procid" "@@remserver" "@@rowcount"
+"@@servername" "@@servicename" "@@spid" "@@textsize" "@@timeticks"
+"@@total_errors" "@@total_read" "@@total_write" "@@trancount"
+"@@version" "abs" "acos" "and" "app_name" "ascii" "asin" "atan" "atn2"
+"avg" "case" "cast" "ceiling" "char" "charindex" "coalesce"
+"col_length" "col_name" "columnproperty" "containstable" "convert"
+"cos" "cot" "count" "current_timestamp" "current_user" "cursor_status"
+"databaseproperty" "datalength" "dateadd" "datediff" "datename"
+"datepart" "day" "db_id" "db_name" "degrees" "difference" "exp"
+"file_id" "file_name" "filegroup_id" "filegroup_name"
+"filegroupproperty" "fileproperty" "floor" "formatmessage"
+"freetexttable" "fulltextcatalogproperty" "fulltextserviceproperty"
+"getansinull" "getdate" "grouping" "host_id" "host_name" "ident_incr"
+"ident_seed" "identity" "index_col" "indexproperty" "is_member"
+"is_srvrolemember" "isdate" "isnull" "isnumeric" "left" "len" "log"
+"log10" "lower" "ltrim" "max" "min" "month" "nchar" "newid" "nullif"
+"object_id" "object_name" "objectproperty" "openquery" "openrowset"
+"parsename" "patindex" "patindex" "permissions" "pi" "power"
+"quotename" "radians" "rand" "replace" "replicate" "reverse" "right"
+"round" "rtrim" "session_user" "sign" "sin" "soundex" "space" "sqrt"
+"square" "stats_date" "stdev" "stdevp" "str" "stuff" "substring" "sum"
+"suser_id" "suser_name" "suser_sid" "suser_sname" "system_user" "tan"
+"textptr" "textvalid" "typeproperty" "unicode" "upper" "user"
+"user_id" "user_name" "var" "varp" "year"
+)
+
+     ;; MS Variables
+     '("\\b@[a-zA-Z0-9_]*\\b" . font-lock-variable-name-face)
+
+     ;; MS Types
+     (sql-font-lock-keywords-builder 'font-lock-type-face nil
+"binary" "bit" "char" "character" "cursor" "datetime" "dec" "decimal"
+"double" "float" "image" "int" "integer" "money" "national" "nchar"
+"ntext" "numeric" "numeric" "nvarchar" "precision" "real"
+"smalldatetime" "smallint" "smallmoney" "text" "timestamp" "tinyint"
+"uniqueidentifier" "varbinary" "varchar" "varying"
+)))
+
+  "Microsoft SQLServer SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-ms-font-lock-keywords'.")
+
+(defvar sql-mode-sybase-font-lock-keywords nil
+  "Sybase SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-sybase-font-lock-keywords'.")
+
+(defvar sql-mode-informix-font-lock-keywords nil
+  "Informix SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-informix-font-lock-keywords'.")
+
+(defvar sql-mode-interbase-font-lock-keywords nil
+  "Interbase SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-interbase-font-lock-keywords'.")
+
+(defvar sql-mode-ingres-font-lock-keywords nil
+  "Ingres SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-interbase-font-lock-keywords'.")
+
+(defvar sql-mode-solid-font-lock-keywords nil
+  "Solid SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-solid-font-lock-keywords'.")
+
+(defvaralias 'sql-mode-mariadb-font-lock-keywords 'sql-mode-mysql-font-lock-keywords
+  "MariaDB is SQL compatible with MySQL.")
+
+(defvar sql-mode-mysql-font-lock-keywords
+  (eval-when-compile
+    (list
+     ;; MySQL Functions
+     (sql-font-lock-keywords-builder 'font-lock-builtin-face nil
+"acos" "adddate" "addtime" "aes_decrypt" "aes_encrypt" "area"
+"asbinary" "ascii" "asin" "astext" "aswkb" "aswkt" "atan" "atan2"
+"avg" "bdmpolyfromtext" "bdmpolyfromwkb" "bdpolyfromtext"
+"bdpolyfromwkb" "benchmark" "bin" "binlog_gtid_pos" "bit_and"
+"bit_count" "bit_length" "bit_or" "bit_xor" "both" "boundary" "buffer"
+"cast" "ceil" "ceiling" "centroid" "character_length" "char_length"
+"charset" "coalesce" "coercibility" "column_add" "column_check"
+"column_create" "column_delete" "column_exists" "column_get"
+"column_json" "column_list" "compress" "concat" "concat_ws"
+"connection_id" "conv" "convert" "convert_tz" "convexhull" "cos" "cot"
+"count" "crc32" "crosses" "cume_dist" "cume_dist" "curdate"
+"current_date" "current_time" "current_timestamp" "curtime" "date_add"
+"datediff" "date_format" "date_sub" "dayname" "dayofmonth" "dayofweek"
+"dayofyear" "decode" "decode_histogram" "degrees" "dense_rank"
+"dense_rank" "des_decrypt" "des_encrypt" "dimension" "disjoint" "div"
+"elt" "encode" "encrypt" "endpoint" "envelope" "exp" "export_set"
+"exteriorring" "extractvalue" "field" "find_in_set" "floor" "format"
+"found_rows" "from" "from_base64" "from_days" "from_unixtime"
+"geomcollfromtext" "geomcollfromwkb" "geometrycollectionfromtext"
+"geometrycollectionfromwkb" "geometryfromtext" "geometryfromwkb"
+"geometryn" "geometrytype" "geomfromtext" "geomfromwkb" "get_format"
+"get_lock" "glength" "greatest" "group_concat" "hex" "ifnull"
+"inet6_aton" "inet6_ntoa" "inet_aton" "inet_ntoa" "instr"
+"interiorringn" "intersects" "interval" "isclosed" "isempty"
+"is_free_lock" "is_ipv4" "is_ipv4_compat" "is_ipv4_mapped" "is_ipv6"
+"isnull" "isring" "issimple" "is_used_lock" "json_array"
+"json_array_append" "json_array_insert" "json_compact" "json_contains"
+"json_contains_path" "json_depth" "json_detailed" "json_exists"
+"json_extract" "json_insert" "json_keys" "json_length" "json_loose"
+"json_merge" "json_object" "json_query" "json_quote" "json_remove"
+"json_replace" "json_search" "json_set" "json_type" "json_unquote"
+"json_valid" "json_value" "lag" "last_day" "last_insert_id" "lastval"
+"last_value" "last_value" "lcase" "lead" "leading" "least" "length"
+"linefromtext" "linefromwkb" "linestringfromtext" "linestringfromwkb"
+"ln" "load_file" "locate" "log" "log10" "log2" "lower" "lpad" "ltrim"
+"makedate" "make_set" "maketime" "master_gtid_wait" "master_pos_wait"
+"max" "mbrcontains" "mbrdisjoint" "mbrequal" "mbrintersects"
+"mbroverlaps" "mbrtouches" "mbrwithin" "md5" "median"
+"mid" "min" "mlinefromtext" "mlinefromwkb" "monthname"
+"mpointfromtext" "mpointfromwkb" "mpolyfromtext" "mpolyfromwkb"
+"multilinestringfromtext" "multilinestringfromwkb"
+"multipointfromtext" "multipointfromwkb" "multipolygonfromtext"
+"multipolygonfromwkb" "name_const" "nextval" "now" "nth_value" "ntile"
+"ntile" "nullif" "numgeometries" "numinteriorrings" "numpoints" "oct"
+"octet_length" "old_password" "ord" "percentile_cont"
+"percentile_disc" "percent_rank" "percent_rank" "period_add"
+"period_diff" "pi" "pointfromtext" "pointfromwkb" "pointn"
+"pointonsurface" "polyfromtext" "polyfromwkb" "polygonfromtext"
+"polygonfromwkb" "position" "pow" "power" "quote" "radians"
+"rand" "rank" "rank" "regexp" "regexp_instr" "regexp_replace"
+"regexp_substr" "release_lock" "repeat" "replace" "reverse" "rlike"
+"row_number" "row_number" "rpad" "rtrim" "sec_to_time" "setval" "sha"
+"sha1" "sha2" "sign" "sin" "sleep" "soundex" "space"
+"spider_bg_direct_sql" "spider_copy_tables" "spider_direct_sql"
+"spider_flush_table_mon_cache" "sqrt" "srid" "st_area" "startpoint"
+"st_asbinary" "st_astext" "st_aswkb" "st_aswkt" "st_boundary"
+"st_buffer" "st_centroid" "st_contains" "st_convexhull" "st_crosses"
+"std" "stddev" "stddev_pop" "stddev_samp" "st_difference"
+"st_dimension" "st_disjoint" "st_distance" "st_endpoint" "st_envelope"
+"st_equals" "st_exteriorring" "st_geomcollfromtext"
+"st_geomcollfromwkb" "st_geometrycollectionfromtext"
+"st_geometrycollectionfromwkb" "st_geometryfromtext"
+"st_geometryfromwkb" "st_geometryn" "st_geometrytype"
+"st_geomfromtext" "st_geomfromwkb" "st_interiorringn"
+"st_intersection" "st_intersects" "st_isclosed" "st_isempty"
+"st_isring" "st_issimple" "st_length" "st_linefromtext"
+"st_linefromwkb" "st_linestringfromtext" "st_linestringfromwkb"
+"st_numgeometries" "st_numinteriorrings" "st_numpoints" "st_overlaps"
+"st_pointfromtext" "st_pointfromwkb" "st_pointn" "st_pointonsurface"
+"st_polyfromtext" "st_polyfromwkb" "st_polygonfromtext"
+"st_polygonfromwkb" "strcmp" "st_relate" "str_to_date" "st_srid"
+"st_startpoint" "st_symdifference" "st_touches" "st_union" "st_within"
+"st_x" "st_y" "subdate" "substr" "substring" "substring_index"
+"subtime" "sum" "sysdate" "tan" "timediff" "time_format"
+"timestampadd" "timestampdiff" "time_to_sec" "to_base64" "to_days"
+"to_seconds" "touches" "trailing" "trim" "ucase" "uncompress"
+"uncompressed_length" "unhex" "unix_timestamp" "updatexml" "upper"
+"user" "utc_date" "utc_time" "utc_timestamp" "uuid" "uuid_short"
+"variance" "var_pop" "var_samp" "version" "weekday"
+"weekofyear" "weight_string" "within"
+)
+
+     ;; MySQL Keywords
+     (sql-font-lock-keywords-builder 'font-lock-keyword-face nil
+"accessible" "action" "add" "after" "against" "all" "alter" "analyze"
+"and" "as" "asc" "auto_increment" "avg_row_length" "bdb" "between"
+"body" "by" "cascade" "case" "change" "character" "check" "checksum"
+"close" "collate" "collation" "column" "columns" "comment" "committed"
+"concurrent" "condition" "constraint" "create" "cross" "data"
+"database" "databases" "default" "delayed" "delay_key_write" "delete"
+"desc" "directory" "disable" "distinct" "distinctrow" "do" "drop"
+"dual" "dumpfile" "duplicate" "else" "elseif" "elsif" "enable"
+"enclosed" "end" "escaped" "exists" "exit" "explain" "fields" "first"
+"for" "force" "foreign" "from" "full" "fulltext" "global" "group"
+"handler" "having" "heap" "high_priority" "history" "if" "ignore"
+"ignore_server_ids" "in" "index" "infile" "inner" "insert"
+"insert_method" "into" "is" "isam" "isolation" "join" "key" "keys"
+"kill" "last" "leave" "left" "level" "like" "limit" "linear" "lines"
+"load" "local" "lock" "long" "loop" "low_priority"
+"master_heartbeat_period" "master_ssl_verify_server_cert" "match"
+"max_rows" "maxvalue" "merge" "min_rows" "mode" "modify" "mrg_myisam"
+"myisam" "natural" "next" "no" "not" "no_write_to_binlog" "null"
+"offset" "oj" "on" "open" "optimize" "optionally" "or" "order" "outer"
+"outfile" "over" "package" "pack_keys" "partial" "partition"
+"password" "period" "prev" "primary" "procedure" "purge" "quick"
+"raid0" "raid_type" "raise" "range" "read" "read_write" "references"
+"release" "rename" "repeatable" "require" "resignal" "restrict"
+"returning" "right" "rollback" "rollup" "row_format" "rowtype"
+"savepoint" "schemas" "select" "separator" "serializable" "session"
+"set" "share" "show" "signal" "slow" "spatial" "sql_big_result"
+"sql_buffer_result" "sql_cache" "sql_calc_found_rows" "sql_no_cache"
+"sql_small_result" "ssl" "starting" "straight_join" "striped"
+"system_time" "table" "tables" "temporary" "terminated" "then" "to"
+"transaction" "truncate" "type" "uncommitted" "undo" "union" "unique"
+"unlock" "update" "use" "using" "values" "versioning" "when" "where"
+"while" "window" "with" "write" "xor"
+)
+
+     ;; MySQL Data Types
+     (sql-font-lock-keywords-builder 'font-lock-type-face nil
+"bigint" "binary" "bit" "blob" "bool" "boolean" "byte" "char" "curve"
+"date" "datetime" "day" "day_hour" "day_microsecond" "day_minute"
+"day_second" "dec" "decimal" "double" "enum" "fixed" "float" "float4"
+"float8" "geometry" "geometrycollection" "hour" "hour_microsecond"
+"hour_minute" "hour_second" "int" "int1" "int2" "int3" "int4" "int8"
+"integer" "json" "line" "linearring" "linestring" "longblob"
+"longtext" "mediumblob" "mediumint" "mediumtext" "microsecond"
+"middleint" "minute" "minute_microsecond" "minute_second" "month"
+"multicurve" "multilinestring" "multipoint" "multipolygon"
+"multisurface" "national" "numeric" "point" "polygon" "precision"
+"quarter" "real" "second" "second_microsecond" "signed" "smallint"
+"surface" "text" "time" "timestamp" "tinyblob" "tinyint" "tinytext"
+"unsigned" "varbinary" "varchar" "varcharacter" "week" "year" "year2"
+"year4" "year_month" "zerofill"
+)))
+
+  "MySQL SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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
+  (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
+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-sqlite-font-lock-keywords'.")
+
+(defvar sql-mode-db2-font-lock-keywords nil
+  "DB2 SQL keywords used by font-lock.
+
+This variable is used by `sql-mode' and `sql-interactive-mode'.  The
+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-db2-font-lock-keywords'.")
+
+(defvar sql-mode-font-lock-keywords nil
+  "SQL keywords used by font-lock.
+
+Setting this variable directly no longer has any affect.  Use
+`sql-product' and `sql-add-product-keywords' to control the
+highlighting rules in SQL mode.")
+
+\f
+
+;;; 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'.
+
+Add PRODUCT to `sql-product-alist' which enables `sql-mode' to
+properly support syntax highlighting and interactive interaction.
+DISPLAY is the name of the SQL product that will appear in the
+menu bar and in messages.  PLIST initializes the product
+configuration."
+
+  ;; Don't do anything if the product is already supported
+  (if (assoc product sql-product-alist)
+      (user-error "Product `%s' is already defined" product)
+
+    ;; Add product to the alist
+    (add-to-list 'sql-product-alist `(,product :name ,display . ,plist))
+    ;; Add a menu item to the SQL->Product menu
+    (easy-menu-add-item sql-mode-menu '("Product")
+                       ;; Each product is represented by a radio
+                       ;; button with it's display name.
+                       `[,display
+                         (sql-set-product ',product)
+                        :style radio
+                        :selected (eq sql-product ',product)]
+                       ;; Maintain the product list in
+                       ;; (case-insensitive) alphabetic order of the
+                       ;; display names.  Loop thru each keymap item
+                       ;; looking for an item whose display name is
+                       ;; after this product's name.
+                       (let ((next-item)
+                             (down-display (downcase display)))
+                          (map-keymap (lambda (k b)
+                                          (when (and (not next-item)
+                                                     (string-lessp down-display
+                                                                   (downcase (cadr b))))
+                                            (setq next-item k)))
+                                     (easy-menu-get-map sql-mode-menu '("Product")))
+                         next-item))
+    product))
+
+(defun sql-del-product (product)
+  "Remove support for PRODUCT in `sql-mode'."
+
+  ;; Remove the menu item based on the display name
+  (easy-menu-remove-item sql-mode-menu '("Product") (sql-get-product-feature product :name))
+  ;; Remove the product alist item
+  (setq sql-product-alist (assq-delete-all product sql-product-alist))
+  nil)
+
+(defun sql-set-product-feature (product feature newvalue)
+  "Set FEATURE of database PRODUCT to NEWVALUE.
+
+The PRODUCT must be a symbol which identifies the database
+product.  The product must have already exist on the product
+list.  See `sql-add-product' to add new products.  The FEATURE
+argument must be a plist keyword accepted by
+`sql-product-alist'."
+
+  (let* ((p (assoc product sql-product-alist))  ;; (PRODUCT :f v ...)
+         (v (plist-member (cdr p) feature)))    ;; (:FEATURE value ...) or null
+
+    (if p
+        (if (member feature sql-indirect-features) ; is indirect
+            (if v
+                (if (car (cdr v))
+                    (if (symbolp (car (cdr v)))
+                        ;; Indirect reference
+                        (set (car (cdr v)) newvalue)
+                      ;; indirect is not a symbol
+                      (error "The value of `%s' for `%s' is not a symbol" feature product))
+                  ;; keyword present, set the indirect variable name
+                  (if (symbolp newvalue)
+                      (if (cdr v)
+                          (setf (car (cdr v)) newvalue)
+                        (setf (cdr v) (list newvalue)))
+                    (error "The indirect variable of `%s' for `%s' must be a symbol" feature product)))
+              ;; not present; insert list
+              (setq v (list feature newvalue))
+              (setf (cdr (cdr v)) (cdr p))
+              (setf (cdr p) v))
+          ;; Not an indirect feature
+          (if v
+              (if (cdr v)
+                  (setf (car (cdr v)) newvalue)
+                (setf (cdr v) (list newvalue)))
+            ;; no value; insert into the list
+            (setq v (list feature newvalue))
+            (setf (cdr (cdr v)) (cdr p))
+            (setf (cdr p) v)))
+      (error "`%s' is not a known product; use `sql-add-product' to add it first" product))))
+
+(defun sql-get-product-feature (product feature &optional fallback not-indirect)
+  "Lookup FEATURE associated with a SQL PRODUCT.
+
+If the FEATURE is nil for PRODUCT, and FALLBACK is specified,
+then the FEATURE associated with the FALLBACK product is
+returned.
+
+If the FEATURE is in the list `sql-indirect-features', and the
+NOT-INDIRECT parameter is not set, then the value of the symbol
+stored in the connect alist is returned.
+
+See `sql-product-alist' for a list of products and supported features."
+  (let* ((p (assoc product sql-product-alist))
+         (v (plist-get (cdr p) feature)))
+
+    (if p
+        ;; If no value and fallback, lookup feature for fallback
+        (if (and (not v)
+                 fallback
+                 (not (eq product fallback)))
+            (sql-get-product-feature fallback feature)
+
+          (if (and
+               (member feature sql-indirect-features)
+               (not not-indirect)
+               (symbolp v))
+              (symbol-value v)
+            v))
+      (error "`%s' is not a known product; use `sql-add-product' to add it first" product)
+      nil)))
+
+(defun sql-product-font-lock (keywords-only imenu)
+  "Configure font-lock and imenu with product-specific settings.
+
+The KEYWORDS-ONLY flag is passed to font-lock to specify whether
+only keywords should be highlighted and syntactic highlighting
+skipped.  The IMENU flag indicates whether `imenu' should also be
+configured."
+
+  (let
+      ;; Get the product-specific syntax-alist.
+      ((syntax-alist (sql-product-font-lock-syntax-alist)))
+
+    ;; Get the product-specific keywords.
+    (setq-local 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-local 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.
+    (when font-lock-mode
+      (font-lock-mode-internal nil)
+      (font-lock-mode-internal t))
+
+    ;; Setup imenu; it needs the same syntax-alist.
+    (when imenu
+      (setq imenu-syntax-alist syntax-alist))))
+
+;;;###autoload
+(defun sql-add-product-keywords (product keywords &optional append)
+  "Add highlighting KEYWORDS for SQL PRODUCT.
+
+PRODUCT should be a symbol, the name of a SQL product, such as
+`oracle'.  KEYWORDS should be a list; see the variable
+`font-lock-keywords'.  By default they are added at the beginning
+of the current highlighting list.  If optional argument APPEND is
+`set', they are used to replace the current highlighting list.
+If APPEND is any other non-nil value, they are added at the end
+of the current highlighting list.
+
+For example:
+
+ (sql-add-product-keywords \\='ms
+  \\='((\"\\\\b\\\\w+_t\\\\b\" . font-lock-type-face)))
+
+adds a fontification pattern to fontify identifiers ending in
+`_t' as data types."
+
+  (let* ((sql-indirect-features nil)
+         (font-lock-var (sql-get-product-feature product :font-lock))
+         (old-val))
+
+    (setq old-val (symbol-value font-lock-var))
+    (set font-lock-var
+        (if (eq append 'set)
+            keywords
+          (if append
+              (append old-val keywords)
+            (append keywords old-val))))))
+
+(defun sql-for-each-login (login-params body)
+  "Iterate through login parameters and return a list of results."
+  (delq nil
+        (mapcar
+         (lambda (param)
+             (let ((token (or (car-safe param) param))
+                   (plist (cdr-safe param)))
+               (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)
+
+    ;; Set the mode name to include the product.
+    (setq mode-name (concat "SQL[" (or (sql-get-product-feature sql-product :name)
+                                      (symbol-name sql-product)) "]"))))
+
+(defun sql-set-product (product)
+  "Set `sql-product' to PRODUCT and enable appropriate highlighting."
+  (interactive
+   (list (sql-read-product "SQL product: ")))
+  (if (stringp product) (setq product (intern product)))
+  (when (not (assoc product sql-product-alist))
+    (user-error "SQL product %s is not supported; treated as ANSI" product)
+    (setq product 'ansi))
+
+  ;; Save product setting and fontify.
+  (setq sql-product product)
+  (sql-highlight-product))
+(defalias 'sql-set-dialect 'sql-set-product)
+\f
+(defun sql-buffer-hidden-p (buf)
+  "Is the buffer hidden?"
+  (string-prefix-p " "
+                   (cond
+                    ((stringp buf)
+                     (when (get-buffer buf)
+                       buf))
+                    ((bufferp buf)
+                     (buffer-name buf))
+                    (t nil))))
+
+(defun sql-display-buffer (buf)
+  "Display a SQLi buffer based on `sql-display-sqli-buffer-function'.
+
+If BUF is hidden or `sql-display-sqli-buffer-function' is nil,
+then the buffer will not be displayed.  Otherwise the BUF is
+displayed."
+  (unless (sql-buffer-hidden-p buf)
+    (cond
+     ((eq sql-display-sqli-buffer-function t)
+      (pop-to-buffer buf))
+     ((not sql-display-sqli-buffer-function)
+      nil)
+     ((functionp sql-display-sqli-buffer-function)
+      (funcall sql-display-sqli-buffer-function buf))
+     (t
+      (message "Invalid setting of `sql-display-sqli-buffer-function'")
+      (pop-to-buffer buf)))))
+
+(defun sql-make-progress-reporter (buf message &optional min-value max-value current-value min-change min-time)
+  "Make a progress reporter if BUF is not hidden."
+  (unless (or (sql-buffer-hidden-p buf)
+              (not sql-display-sqli-buffer-function))
+    (make-progress-reporter message min-value max-value current-value min-change min-time)))
+
+(defun sql-progress-reporter-update (reporter &optional value)
+  "Report progress of an operation in the echo area."
+  (when reporter
+    (progress-reporter-update reporter value)))
+
+(defun sql-progress-reporter-done (reporter)
+  "Print reporter’s message followed by word \"done\" in echo area."
+  (when reporter
+    (progress-reporter-done reporter)))
+
+;;; SMIE support
+
+;; Needs a lot more love than I can provide.  --Stef
+
+;; (require 'smie)
+
+;; (defconst sql-smie-grammar
+;;   (smie-prec2->grammar
+;;    (smie-bnf->prec2
+;;     ;; Partly based on https://www.h2database.com/html/grammar.html
+;;     '((cmd ("SELECT" select-exp "FROM" select-table-exp)
+;;            )
+;;       (select-exp ("*") (exp) (exp "AS" column-alias))
+;;       (column-alias)
+;;       (select-table-exp (table-exp "WHERE" exp) (table-exp))
+;;       (table-exp)
+;;       (exp ("CASE" exp "WHEN" exp "THEN" exp "ELSE" exp "END")
+;;            ("CASE" exp "WHEN" exp "THEN" exp "END"))
+;;       ;; Random ad-hoc additions.
+;;       (foo (foo "," foo))
+;;       )
+;;     '((assoc ",")))))
+
+;; (defun sql-smie-rules (kind token)
+;;   (pcase (cons kind token)
+;;     (`(:list-intro . ,_) t)
+;;     (`(:before . "(") (smie-rule-parent))))
+
+;;; Motion Functions
+
+(defun sql-statement-regexp (prod)
+  (let* ((ansi-stmt (or (sql-get-product-feature 'ansi :statement) "select"))
+         (prod-stmt (sql-get-product-feature prod  :statement)))
+    (concat "^\\<"
+            (if prod-stmt
+                (concat "\\(" ansi-stmt "\\|" prod-stmt "\\)")
+              ansi-stmt)
+            "\\>")))
+
+(defun sql-beginning-of-statement (arg)
+  "Move 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 a beginning-of-stmt that's not in a string or comment
+    (while (and (re-search-forward regexp next t 1)
+                (or (nth 3 (syntax-ppss))
+                    (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 (cl-signum arg))))))
+
+(defun sql-end-of-statement (arg)
+  "Move to the end of the current SQL statement."
+  (interactive "p")
+  (let ((term (or (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 (= (cl-signum arg) 0))
+      ;; if we're looking at the terminator, jump by 2
+      (if (or (and (> 0 arg) (looking-back term nil))
+              (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 string or comment
+        (unless (or (nth 3 (syntax-ppss))
+                    (nth 7 (syntax-ppss)))
+          (setq arg (- arg (cl-signum arg))))))
+    (goto-char (if (match-data)
+                   (match-end 0)
+                 here))))
+
+;;; Small functions
+
+(defun sql-magic-go (arg)
+  "Insert \"o\" and call `comint-send-input'.
+`sql-electric-stuff' must be the symbol `go'."
+  (interactive "P")
+  (self-insert-command (prefix-numeric-value arg))
+  (if (and (equal sql-electric-stuff 'go)
+          (save-excursion
+            (comint-bol nil)
+            (looking-at "go\\b")))
+      (comint-send-input)))
+(put 'sql-magic-go 'delete-selection t)
+
+(defun sql-magic-semicolon (arg)
+  "Insert semicolon and call `comint-send-input'.
+`sql-electric-stuff' must be the symbol `semicolon'."
+  (interactive "P")
+  (self-insert-command (prefix-numeric-value arg))
+  (if (equal sql-electric-stuff 'semicolon)
+       (comint-send-input)))
+(put 'sql-magic-semicolon 'delete-selection t)
+
+(defun sql-accumulate-and-indent ()
+  "Continue SQL statement on the next line."
+  (interactive)
+  (if (fboundp 'comint-accumulate)
+      (comint-accumulate)
+    (newline))
+  (indent-according-to-mode))
+
+(defun sql-help-list-products (indent freep)
+  "Generate listing of products available for use under SQLi.
+
+List products with :free-software attribute set to FREEP.  Indent
+each line with INDENT."
+
+  (let (sqli-func doc)
+    (setq doc "")
+    (dolist (p sql-product-alist)
+      (setq sqli-func (intern (concat "sql-" (symbol-name (car p)))))
+
+      (if (and (fboundp sqli-func)
+              (eq (sql-get-product-feature (car p) :free-software) freep))
+       (setq doc
+             (concat doc
+                     indent
+                     (or (sql-get-product-feature (car p) :name)
+                         (symbol-name (car p)))
+                     ":\t"
+                     "\\["
+                     (symbol-name sqli-func)
+                     "]\n"))))
+    doc))
+
+(defun sql-help ()
+  "Show short help for the SQL modes."
+  (interactive)
+  (describe-function 'sql-help))
+(put 'sql-help 'function-documentation '(sql--make-help-docstring))
+
+(defvar sql--help-docstring
+  "Show short help for the SQL modes.
+Use an entry function to open an interactive SQL buffer.  This buffer is
+usually named `*SQL*'.  The name of the major mode is SQLi.
+
+Use the following commands to start a specific SQL interpreter:
+
+    \\\\FREE
+
+Other non-free SQL implementations are also supported:
+
+    \\\\NONFREE
+
+But we urge you to choose a free implementation instead of these.
+
+You can also use \\[sql-product-interactive] to invoke the
+interpreter for the current `sql-product'.
+
+Once you have the SQLi buffer, you can enter SQL statements in the
+buffer.  The output generated is appended to the buffer and a new prompt
+is generated.  See the In/Out menu in the SQLi buffer for some functions
+that help you navigate through the buffer, the input history, etc.
+
+If you have a really complex SQL statement or if you are writing a
+procedure, you can do this in a separate buffer.  Put the new buffer in
+`sql-mode' by calling \\[sql-mode].  The name of this buffer can be
+anything.  The name of the major mode is SQL.
+
+In this SQL buffer (SQL mode), you can send the region or the entire
+buffer to the interactive SQL buffer (SQLi mode).  The results are
+appended to the SQLi buffer without disturbing your SQL buffer.")
+
+(defun sql--make-help-docstring ()
+  "Return a docstring for `sql-help' listing loaded SQL products."
+  (let ((doc sql--help-docstring))
+    ;; Insert FREE software list
+    (when (string-match "^\\(\\s-*\\)[\\][\\]FREE\\s-*$" doc 0)
+      (setq doc (replace-match (sql-help-list-products (match-string 1 doc) t)
+                              t t doc 0)))
+    ;; Insert non-FREE software list
+    (when (string-match "^\\(\\s-*\\)[\\][\\]NONFREE\\s-*$" doc 0)
+      (setq doc (replace-match (sql-help-list-products (match-string 1 doc) nil)
+                              t t doc 0)))
+    doc))
+
+(defun sql-default-value (var)
+  "Fetch the value of a variable.
+
+If the current buffer is in `sql-interactive-mode', then fetch
+the global value, otherwise use the buffer local value."
+  (if (derived-mode-p 'sql-interactive-mode)
+      (default-value var)
+    (buffer-local-value var (current-buffer))))
+
+(defun sql-get-login-ext (symbol prompt history-var plist)
+  "Prompt user with extended login parameters.
+
+The global value of SYMBOL is the last value and the global value
+of the SYMBOL is set based on the user's input.
+
+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'.)
+
+For both `:file' and `:completion', there can also be a
+`:must-match' property that controls REQUIRE-MATCH parameter to
+`completing-read'."
+
+  (set-default
+   symbol
+   (let* ((default (plist-get plist :default))
+          (last-value (sql-default-value symbol))
+          (prompt-def (format-prompt prompt default))
+          (use-dialog-box nil))
+     (cond
+      ((plist-member plist :file)
+       (let ((file-name
+              (read-file-name prompt-def
+                              (file-name-directory last-value)
+                              default
+                              (if (plist-member plist :must-match)
+                                  (plist-get plist :must-match)
+                                t)
+                              (file-name-nondirectory last-value)
+                              (when (plist-get plist :file)
+                                `(lambda (f)
+                                   (if (not (file-regular-p f))
+                                       t
+                                     (string-match
+                                      (concat "\\<" ,(plist-get plist :file) "\\>")
+                                      (file-name-nondirectory f))))))))
+         (if (string= file-name "")
+             ""
+           (expand-file-name file-name))))
+
+      ((plist-member plist :completion)
+       (completing-read prompt-def
+                        (plist-get plist :completion)
+                        nil
+                        (if (plist-member plist :must-match)
+                            (plist-get plist :must-match)
+                          t)
+                        last-value
+                        history-var
+                        default))
+
+      ((plist-get plist :number)
+       (read-number (concat prompt ": ") (or default last-value 0)))
+
+      (t
+       (read-string prompt-def last-value history-var default))))))
+
+(defun sql-get-login (&rest what)
+  "Get username, password and database from the user.
+
+The variables `sql-user', `sql-password', `sql-server', and
+`sql-database' can be customized.  They are used as the default values.
+Usernames, servers and databases are stored in `sql-user-history',
+`sql-server-history' and `database-history'.  Passwords are not stored
+in a history.
+
+Parameter WHAT is a list of tokens passed as arguments in the
+function call.  The function asks for the username if WHAT
+contains the symbol `user', for the password if it contains the
+symbol `password', for the server if it contains the symbol
+`server', and for the database if it contains the symbol
+`database'.  The members of WHAT are processed in the order in
+which they are provided.
+
+If the `sql-password-wallet' is non-nil and WHAT contains the
+`password' token, then the `password' token will be pushed to the
+end to be sure that all of the values can be fed to the wallet.
+
+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)."
+
+  ;; Push the password to the end if we have a wallet
+  (when (and sql-password-wallet
+             (fboundp sql-password-search-wallet-function)
+             (member 'password what))
+    (setq what (append (cl-delete 'password what)
+                       '(password))))
+
+  ;; Prompt for each parameter
+  (dolist (w what)
+    (let ((plist (cdr-safe w)))
+      (pcase (or (car-safe w) w)
+        ('user
+         (sql-get-login-ext 'sql-user "User" 'sql-user-history plist))
+
+        ('password
+         (setq-default sql-password
+                       (if (and sql-password-wallet
+                                (fboundp sql-password-search-wallet-function))
+                           (let ((password (funcall sql-password-search-wallet-function
+                                                    sql-password-wallet
+                                                    sql-product
+                                                    sql-user
+                                                    sql-server
+                                                    sql-database
+                                                    sql-port)))
+                             (if password
+                                 password
+                               (read-passwd "Password: " nil (sql-default-value 'sql-password))))
+                         (read-passwd "Password: " nil (sql-default-value 'sql-password)))))
+
+        ('server
+         (sql-get-login-ext 'sql-server "Server" 'sql-server-history plist))
+
+        ('database
+         (sql-get-login-ext 'sql-database "Database"
+                            'sql-database-history plist))
+
+        ('port
+         (sql-get-login-ext 'sql-port "Port"
+                            nil (append '(:number t) plist)))))))
+
+(defun sql-find-sqli-buffer (&optional product connection)
+  "Return 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.
+This function checks all SQL buffers for their SQLi buffer.  If their
+SQLi buffer is nonexistent or has no process, it is set to the current
+default SQLi buffer.  The current default SQLi buffer is determined
+using `sql-find-sqli-buffer'.  If `sql-buffer' is set,
+`sql-set-sqli-hook' is run."
+  (interactive)
+  (save-excursion
+    (let ((buflist (buffer-list))
+         (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 (sql-buffer-live-p sql-buffer)))
+             (progn
+               (setq sql-buffer default-buffer)
+               (when default-buffer
+                  (run-hooks 'sql-set-sqli-hook)))))
+       (setq buflist (cdr buflist))))))
+
+(defun sql-set-sqli-buffer ()
+  "Set the SQLi buffer SQL strings are sent to.
+
+Call this function in a SQL buffer in order to set the SQLi buffer SQL
+strings are sent to.  Calling this function sets `sql-buffer' and runs
+`sql-set-sqli-hook'.
+
+If you call it from a SQL buffer, this sets the local copy of
+`sql-buffer'.
+
+If you call it from anywhere else, it sets the global copy of
+`sql-buffer'."
+  (interactive)
+  (let ((default-buffer (sql-find-sqli-buffer)))
+    (if (null default-buffer)
+        (sql-product-interactive)
+      (let ((new-buffer (read-buffer "New SQLi buffer: " default-buffer t)))
+        (if (null (sql-buffer-live-p new-buffer))
+            (user-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 ()
+  "Display the current SQLi buffer.
+
+This is the buffer SQL strings are sent to.
+It is stored in the variable `sql-buffer'.
+I
+See also `sql-help' on how to create such a buffer."
+  (interactive)
+  (unless (and sql-buffer (buffer-live-p (get-buffer sql-buffer))
+               (get-buffer-process sql-buffer))
+    (sql-set-sqli-buffer))
+  (display-buffer sql-buffer))
+
+(defun sql-make-alternate-buffer-name (&optional product)
+  "Return a string that can be used to rename a SQLi buffer.
+This is used to set `sql-alternate-buffer-name' within
+`sql-interactive-mode'.
+
+If the session was started with `sql-connect' then the alternate
+name would be the name of the connection.
+
+Otherwise, it uses the parameters identified by the :sqlilogin
+parameter.
+
+If all else fails, the alternate name would be the user and
+server/database name."
+
+  (let ((name ""))
+
+    ;; Build a name using the :sqli-login setting
+    (setq name
+          (apply #'concat
+                 (cdr
+                  (apply #'append nil
+                         (sql-for-each-login
+                          (sql-get-product-feature (or product sql-product) :sqli-login)
+                          (lambda (token plist)
+                              (pcase token
+                                ('user
+                                 (unless (string= "" sql-user)
+                                   (list "/" sql-user)))
+                                ('port
+                                 (unless (or (not (numberp sql-port))
+                                             (= 0 sql-port))
+                                   (list ":" (number-to-string sql-port))))
+                                ('server
+                                 (unless (string= "" sql-server)
+                                   (list "."
+                                         (if (plist-member plist :file)
+                                             (file-name-nondirectory sql-server)
+                                           sql-server))))
+                                ('database
+                                 (unless (string= "" sql-database)
+                                   (list "@"
+                                         (if (plist-member plist :file)
+                                             (file-name-nondirectory sql-database)
+                                           sql-database))))
+
+                                ;; (`password nil)
+                                (_         nil))))))))
+
+    ;; If there's a connection, use it and the name thus far
+    (if sql-connection
+        (format "<%s>%s" sql-connection (or name ""))
+
+      ;; If there is no name, try to create something meaningful
+      (if (string= "" (or name ""))
+          (concat
+           (if (string= "" sql-user)
+               (if (string= "" (user-login-name))
+                   ()
+                 (concat (user-login-name) "/"))
+             (concat sql-user "/"))
+           (if (string= "" sql-database)
+               (if (string= "" sql-server)
+               (system-name)
+               sql-server)
+             sql-database))
+
+        ;; Use the name we've got
+        name))))
+
+(defun sql-generate-unique-sqli-buffer-name (product base)
+  "Generate a new, unique buffer name for a SQLi buffer.
+
+Append a sequence number until a unique name is found."
+  (let ((base-name (substring-no-properties
+                    (if base
+                        (if (stringp base)
+                            base
+                          (format "%S" base))
+                      (or (sql-get-product-feature product :name)
+                          (symbol-name product)))))
+        buf-fmt-1st
+        buf-fmt-rest)
+
+    ;; Calculate buffer format
+    (if (string-blank-p base-name)
+        (setq buf-fmt-1st  "*SQL*"
+              buf-fmt-rest "*SQL-%d*")
+      (setq buf-fmt-1st  (format "*SQL: %s*" base-name)
+            buf-fmt-rest (format "*SQL: %s-%%d*" base-name)))
+
+    ;; See if we can find an unused buffer
+    (let ((buf-name buf-fmt-1st)
+          (i 1))
+      (while (if (sql-is-sqli-buffer-p buf-name)
+                 (comint-check-proc buf-name)
+               (buffer-live-p (get-buffer buf-name)))
+        ;; Check a sequence number on the BASE
+        (setq buf-name (format buf-fmt-rest i)
+              i (1+ i)))
+
+      buf-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))
+      (user-error "Current buffer is not a SQL interactive buffer")
+
+    (setq sql-alternate-buffer-name
+          (substring-no-properties
+           (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
+     (sql-generate-unique-sqli-buffer-name sql-product
+                                           sql-alternate-buffer-name)
+     t)))
+
+(defun sql-copy-column ()
+  "Copy current column to the end of buffer.
+Inserts SELECT or commas if appropriate."
+  (interactive)
+  (let ((column))
+    (save-excursion
+      (setq column (buffer-substring-no-properties
+                 (progn (forward-char 1) (backward-sexp 1) (point))
+                 (progn (forward-sexp 1) (point))))
+      (goto-char (point-max))
+      (let ((bol (comint-line-beginning-position)))
+       (cond
+        ;; if empty command line, insert SELECT
+        ((= bol (point))
+         (insert "SELECT "))
+        ;; else if appending to INTO .* (, SELECT or ORDER BY, insert a comma
+        ((save-excursion
+           (re-search-backward "\\b\\(\\(into\\s-+\\S-+\\s-+(\\)\\|select\\|order by\\) .+"
+                               bol t))
+         (insert ", "))
+        ;; else insert a space
+        (t
+         (if (eq (preceding-char) ?\s)
+             nil
+           (insert " ")))))
+      ;; in any case, insert the column
+      (insert column)
+      (message "%s" column))))
+
+;; On Windows, SQL*Plus for Oracle turns on full buffering for stdout
+;; if it is not attached to a character device; therefore placeholder
+;; replacement by SQL*Plus is fully buffered.  The workaround lets
+;; Emacs query for the placeholders.
+
+(defvar sql-placeholder-history nil
+  "History of placeholder values used.")
+
+(defun sql-placeholders-filter (string)
+  "Replace placeholders in STRING.
+Placeholders are words starting with an ampersand like &this."
+
+  (when sql-oracle-scan-on
+    (let ((start 0)
+          (replacement ""))
+      (while (string-match "&?&\\(\\(?:\\sw\\|\\s_\\)+\\)[.]?" string start)
+        (setq replacement (read-from-minibuffer
+                          (format "Enter value for %s: "
+                                   (propertize (match-string 1 string)
+                                               'face 'font-lock-variable-name-face))
+                          nil nil nil 'sql-placeholder-history)
+              string (replace-match replacement t t string)
+              start (+ (match-beginning 1) (length replacement))))))
+  string)
+
+;; Using DB2 interactively, newlines must be escaped with " \".
+;; The space before the backslash is relevant.
+
+(defun sql-escape-newlines-filter (string)
+  "Escape newlines in STRING.
+Every newline in STRING will be preceded with a space and a backslash."
+  (if (not sql-db2-escape-newlines)
+      string
+    (let ((result "") (start 0) mb me)
+      (while (string-match "\n" string start)
+        (setq mb (match-beginning 0)
+              me (match-end 0)
+              result (concat result
+                             (substring string start mb)
+                             (if (and (> mb 1)
+                                      (string-equal " \\" (substring string (- mb 2) mb)))
+                                 "" " \\\n"))
+              start me))
+      (concat result (substring string start)))))
+
+\f
+
+;;; Input sender for SQLi buffers
+
+(defvar sql-output-newline-count 0
+  "Number of newlines in the input string.
+
+Allows the suppression of continuation prompts.")
+
+(defun sql-input-sender (proc string)
+  "Send STRING to PROC after applying filters."
+
+  (let* ((product (buffer-local-value 'sql-product (process-buffer proc)))
+        (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
+          (apply #'+ (mapcar (lambda (ch) (if (eq ch ?\n) 1 0))
+                             string)))
+
+    ;; Send the 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.  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.
+
+The filter goes into play only if something is already
+accumulated, or we're waiting for continuation
+prompts (`sql-output-newline-count' is positive).  In this case:
+- Accumulate process output into `sql-preoutput-hold'.
+- Remove any complete prompts / continuation prompts that we're waiting
+  for.
+- In case we're expecting more prompts - return all currently
+  accumulated _complete_ lines, leaving the rest for the next
+  invocation.  They will appear in the output immediately.  This way we
+  don't accumulate large chunks of data for no reason.
+- If we found all expected prompts - just return all current accumulated
+  data."
+  (when (and comint-prompt-regexp
+             ;; We either already have something held, or expect
+             ;; prompts
+             (or sql-preoutput-hold
+                 (and sql-output-newline-count
+                      (> sql-output-newline-count 0))))
+    (save-match-data
+      ;; Add this text to what's left from the last pass
+      (setq oline (concat sql-preoutput-hold oline)
+            sql-preoutput-hold nil)
+
+      ;; If we are looking for prompts
+      (when (and sql-output-newline-count
+                 (> sql-output-newline-count 0))
+        ;; Loop thru each starting prompt and remove it
+        (while (and (not (string-empty-p oline))
+                    (> sql-output-newline-count 0)
+                    (string-match comint-prompt-regexp oline))
+          (setq oline (replace-match "" nil nil oline)
+                sql-output-newline-count (1- sql-output-newline-count)))
+
+        ;; If we've found all the expected prompts, stop looking
+        (if (= sql-output-newline-count 0)
+            (setq sql-output-newline-count nil)
+          ;; Still more possible prompts, leave them for the next pass
+          (setq sql-preoutput-hold oline
+                oline "")))
+
+      ;; Lines that are now complete may be passed further
+      (when sql-preoutput-hold
+        (let ((last-nl 0))
+          (while (string-match "\n" sql-preoutput-hold last-nl)
+            (setq last-nl (match-end 0)))
+          ;; Return up to last nl, hold after the last nl
+          (setq oline (substring sql-preoutput-hold 0 last-nl)
+                sql-preoutput-hold (substring sql-preoutput-hold last-nl))
+          (when (string-empty-p sql-preoutput-hold)
+            (setq sql-preoutput-hold nil))))))
+  oline)
+
+
+;;; Sending the region to the SQLi buffer.
+(defvar sql-debug-send nil
+  "Display text sent to SQL process pragmatically.")
+
+(defun sql-send-string (str)
+  "Send the string STR to the SQL process."
+  (interactive "sSQL Text: ")
+
+  (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
+           ;; Set product context
+           (with-current-buffer sql-buffer
+              (when sql-debug-send
+                (message ">>SQL> %S" s))
+              (insert "\n")
+              (comint-set-process-mark)
+
+             ;; Send the string (trim the trailing whitespace)
+             (sql-input-sender (get-buffer-process (current-buffer)) s)
+
+             ;; Send a command terminator if we must
+             (sql-send-magic-terminator sql-buffer s sql-send-terminator)
+
+              (when sql-pop-to-buffer-after-send-region
+               (message "Sent string to buffer %s" sql-buffer))))
+
+         ;; Display the sql buffer
+         (sql-display-buffer sql-buffer))
+
+    ;; We don't have no stinkin' sql
+    (user-error "No SQL process started"))))
+
+(defun sql-send-region (start end)
+  "Send a region to the SQL process."
+  (interactive "r")
+  (sql-send-string (buffer-substring-no-properties start end)))
+
+(defun sql-send-paragraph ()
+  "Send the current paragraph to the SQL process."
+  (interactive)
+  (let ((start (save-excursion
+                (backward-paragraph)
+                (point)))
+       (end (save-excursion
+              (forward-paragraph)
+              (point))))
+    (sql-send-region start end)))
+
+(defun sql-send-buffer ()
+  "Send the buffer contents to the SQL process."
+  (interactive)
+  (sql-send-region (point-min) (point-max)))
+
+(defun sql-send-line-and-next ()
+  "Send the current line to the SQL process and go to the next line."
+  (interactive)
+  (sql-send-region (line-beginning-position 1) (line-beginning-position 2))
+  (beginning-of-line 2)
+  (while (forward-comment 1)))  ; skip all comments and whitespace
+
+(defun sql-send-magic-terminator (buf str terminator)
+  "Send TERMINATOR to buffer BUF if its not present in STR."
+  (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)))
+
+    ;; If there is no terminator specified, use default ";"
+    (unless terminator
+      (setq terminator ";"))
+
+    ;; Parse the setting into the pattern and the terminator string
+    (cond ((stringp terminator)
+          (setq pat (regexp-quote terminator)
+                term terminator))
+         ((consp terminator)
+          (setq pat (car terminator)
+                term (cdr terminator)))
+         (t
+          nil))
+
+    ;; Check to see if the pattern is present in the str already sent
+    (unless (and pat term
+                (string-match-p (concat pat "\\'") str))
+      (sql-input-sender (get-buffer-process buf) term))))
+
+(defun sql-remove-tabs-filter (str)
+  "Replace tab characters with spaces."
+  (string-replace "\t" " " str))
+
+(defun sql-toggle-pop-to-buffer-after-send-region (&optional value)
+  "Toggle `sql-pop-to-buffer-after-send-region'.
+
+If given the optional parameter VALUE, sets
+`sql-toggle-pop-to-buffer-after-send-region' to VALUE."
+  (interactive "P")
+  (if value
+      (setq sql-pop-to-buffer-after-send-region value)
+    (setq sql-pop-to-buffer-after-send-region
+         (null sql-pop-to-buffer-after-send-region))))
+
+\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 "'" (string-replace "[']" "''" 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 (sql-buffer-hidden-p outbuf))))
+         (this-save  save-prior)
+         (next-save  t))
+
+    (when visible
+      (message "Executing SQL command..."))
+
+    (if (consp command)
+        (dolist (onecmd command)
+          (sql-redirect-one sqlbuf onecmd outbuf this-save)
+          (setq this-save next-save))
+      (sql-redirect-one sqlbuf command outbuf save-prior))
+
+    (when visible
+      (message "Executing SQL command...done"))
+    nil))
+
+(defun sql-redirect-one (sqlbuf command outbuf save-prior)
+  (when command
+    (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
+          (setq-local view-no-disable-on-exit t)
+          (read-only-mode -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
+        (let ((inhibit-quit t)
+              comint-preoutput-filter-functions)
+          (with-local-quit
+            (comint-redirect-send-command-to-process command buf proc nil t)
+            (while (or quit-flag (null comint-redirect-completed))
+              (accept-process-output nil 1)))
+
+          (if quit-flag
+              (comint-redirect-cleanup)
+            ;; 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 &optional regexp 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 (or 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)
+  "Execute a command in a SQL interactive buffer and capture 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)
+        (setq-local revert-buffer-function
+                    (lambda (_ignore-auto _noconfirm)
+                      (sql-execute sqlbuf (buffer-name outbuf)
+                                   command enhanced arg)))
+        (special-mode))
+      (pop-to-buffer 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
+        (product (buffer-local-value 'sql-product (get-buffer sqlbuf))))
+    (setq command (sql-get-product-feature product feature))
+    (unless command
+      (error "%s does not support %s" 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 #'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--completion-table (string pred action)
+  (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
+      (complete-with-action action sql-completion-object string pred))))
+
+(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 (when sql-completion-sqlbuf
+                    (with-current-buffer sql-completion-sqlbuf sql-product)))
+         (completion-ignore-case t))
+
+    (if product
+        (if (sql-get-product-feature product :completion-object)
+            (completing-read prompt #'sql--completion-table
+                             nil nil tname)
+          (read-from-minibuffer prompt tname))
+      (user-error "There is no active SQLi buffer"))))
+
+(defun sql-list-all (&optional enhanced)
+  "List all database objects.
+With optional prefix argument ENHANCED, displays additional
+details or extends the listing to include other schemas objects."
+  (interactive "P")
+  (let ((sqlbuf (sql-find-sqli-buffer)))
+    (unless sqlbuf
+      (user-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
+      (setq-local sql-contains-names t)
+      (setq-local sql-buffer sqlbuf))))
+
+(defun sql-list-table (name &optional enhanced)
+  "List the details of a database table named NAME.
+Displays the columns in the relation.  With optional prefix argument
+ENHANCED, displays additional details about each column."
+  (interactive
+   (list (sql-read-table-name "Table name: ")
+         current-prefix-arg))
+  (let ((sqlbuf (sql-find-sqli-buffer)))
+    (unless sqlbuf
+      (user-error "No SQL interactive buffer found"))
+    (unless name
+      (user-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
+(define-derived-mode sql-mode prog-mode "SQL"
+  "Major mode to edit SQL.
+
+You can send SQL statements to the SQLi buffer using
+\\[sql-send-region].  Such a buffer must exist before you can do this.
+See `sql-help' on how to create SQLi buffers.
+
+\\{sql-mode-map}
+Customization: Entry to this mode runs the `sql-mode-hook'.
+
+When you put a buffer in SQL mode, the buffer stores the last SQLi
+buffer created as its destination in the variable `sql-buffer'.  This
+will be the buffer \\[sql-send-region] sends the region to.  If this
+SQLi buffer is killed, \\[sql-send-region] is no longer able to
+determine where the strings should be sent to.  You can set the
+value of `sql-buffer' using \\[sql-set-sqli-buffer].
+
+For information on how to create multiple SQLi buffers, see
+`sql-interactive-mode'.
+
+Note that SQL doesn't have an escape character unless you specify
+one.  If you specify backslash as escape character in SQL, you
+must tell Emacs.  Here's how to do that in your init file:
+
+\(add-hook \\='sql-mode-hook
+          (lambda ()
+           (modify-syntax-entry ?\\\\ \"\\\\\" sql-mode-syntax-table)))"
+  :abbrev-table sql-mode-abbrev-table
+
+  ;; (smie-setup sql-smie-grammar #'sql-smie-rules)
+  (setq-local 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
+  ;; is buffer-local, so we don't need a local-variable for it.  SQL is
+  ;; case-insensitive, that's why we have to set imenu-case-fold-search.
+  (setq imenu-generic-expression sql-imenu-generic-expression
+       imenu-case-fold-search t)
+  ;; Make `sql-send-paragraph' work on paragraphs that contain indented
+  ;; lines.
+  (setq-local paragraph-separate "[\f]*$")
+  (setq-local paragraph-start "[\n\f]")
+  ;; Abbrevs
+  (setq-local abbrev-all-caps 1)
+  ;; Contains the name of database objects
+  (setq-local sql-contains-names t)
+  (setq-local escaped-string-quote "'")
+  (setq-local syntax-propertize-function
+              (eval
+               '(syntax-propertize-rules
+                 ;; Handle escaped apostrophes within strings.
+                 ((if (eq sql-product 'mysql)
+                      "\\\\'"
+                    "''")
+                  (0
+                   (if (save-excursion
+                         (nth 3 (syntax-ppss (match-beginning 0))))
+                      (string-to-syntax ".")
+                     (forward-char -1)
+                     nil)))
+                 ;; Propertize rules to not have /- and -* start comments.
+                 ("\\(/-\\)" (1 "."))
+                 ("\\(-\\*\\)"
+                  (1
+                   (if (save-excursion
+                         (not (ppss-comment-depth
+                               (syntax-ppss (match-beginning 1)))))
+                       ;; If we're outside a comment, we don't let -*
+                       ;; start a comment.
+                      (string-to-syntax ".")
+                     ;; Inside a comment, ignore it to avoid -*/ not
+                     ;; being interpreted as a comment end.
+                     (forward-char -1)
+                     nil))))
+               t))
+  ;; Set syntax and font-face highlighting
+  ;; Catch changes to sql-product and highlight accordingly
+  (sql-set-product (or sql-product 'ansi)) ; Fixes bug#13591
+  (add-hook 'hack-local-variables-hook #'sql-highlight-product t t))
+
+\f
+
+;;; SQL interactive mode
+
+(put 'sql-interactive-mode 'mode-class 'special)
+(put 'sql-interactive-mode 'custom-mode-group 'SQL)
+
+(define-derived-mode sql-interactive-mode comint-mode "SQLi[?]"
+  "Major mode to use a SQL interpreter interactively.
+
+Do not call this function by yourself.  The environment must be
+initialized by an entry function specific for the SQL interpreter.
+See `sql-help' for a list of available entry functions.
+
+\\[comint-send-input] after the end of the process' output sends the
+text from the end of process to the end of the current line.
+\\[comint-send-input] before end of process output copies the current
+line minus the prompt to the end of the buffer and sends it.
+\\[comint-copy-old-input] just copies the current line.
+Use \\[sql-accumulate-and-indent] to enter multi-line statements.
+
+If you want to make multiple SQL buffers, rename the `*SQL*' buffer
+using \\[rename-buffer] or \\[rename-uniquely] and start a new process.
+See `sql-help' for a list of available entry functions.  The last buffer
+created by such an entry function is the current SQLi buffer.  SQL
+buffers will send strings to the SQLi buffer current at the time of
+their creation.  See `sql-mode' for details.
+
+Sample session using two connections:
+
+1. Create first SQLi buffer by calling an entry function.
+2. Rename buffer \"*SQL*\" to \"*Connection 1*\".
+3. Create a SQL buffer \"test1.sql\".
+4. Create second SQLi buffer by calling an entry function.
+5. Rename buffer \"*SQL*\" to \"*Connection 2*\".
+6. Create a SQL buffer \"test2.sql\".
+
+Now \\[sql-send-region] in buffer \"test1.sql\" will send the region to
+buffer \"*Connection 1*\", \\[sql-send-region] in buffer \"test2.sql\"
+will send the region to buffer \"*Connection 2*\".
+
+If you accidentally suspend your process, use \\[comint-continue-subjob]
+to continue it.  On some operating systems, this will not work because
+the signals are not supported.
+
+\\{sql-interactive-mode-map}
+Customization: Entry to this mode runs the hooks on `comint-mode-hook'
+and `sql-interactive-mode-hook' (in that order).  Before each input, the
+hooks on `comint-input-filter-functions' are run.  After each SQL
+interpreter output, the hooks on `comint-output-filter-functions' are
+run.
+
+Variable `sql-input-ring-file-name' controls the initialization of the
+input ring history.
+
+Variables `comint-output-filter-functions', a hook, and
+`comint-scroll-to-bottom-on-input' and
+`comint-scroll-to-bottom-on-output' control whether input and output
+cause the window to scroll to the end of the buffer.
+
+If you want to make SQL buffers limited in length, add the function
+`comint-truncate-buffer' to `comint-output-filter-functions'.
+
+Here is an example for your init file.  It keeps the SQLi buffer a
+certain length.
+
+\(add-hook \\='sql-interactive-mode-hook
+    (lambda ()
+        (setq comint-output-filter-functions #\\='comint-truncate-buffer)))
+
+Here is another example.  It will always put point back to the statement
+you entered, right above the output it created.
+
+\(setq comint-output-filter-functions
+       (lambda (STR) (comint-show-output)))"
+  :syntax-table sql-mode-syntax-table
+  ;; FIXME: The doc above uses `setq' on `comint-output-filter-functions',
+  ;; whereas hooks should be manipulated with things like `add/remove-hook'.
+  :after-hook (sql--adjust-interactive-setup)
+
+  ;; Get the `sql-product' for this interactive session.
+  (setq-local sql-product (or sql-interactive-product
+                           sql-product))
+
+  ;; Setup the mode.
+  (setq mode-name
+        (concat "SQLi[" (or (sql-get-product-feature sql-product :name)
+                            (symbol-name sql-product)) "]"))
+
+  ;; 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
+  ;; will have just one quote.  Therefore syntactic highlighting is
+  ;; disabled for interactive buffers.  No imenu support.
+  (sql-product-font-lock t nil)
+
+  ;; Enable commenting and uncommenting of the region.
+  (setq-local 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.
+  (let ((proc (get-buffer-process (current-buffer))))
+    (when proc (set-process-sentinel proc #'sql-stop)))
+  ;; Save the connection and login params
+  (setq-local sql-user       sql-user)
+  (setq-local sql-database   sql-database)
+  (setq-local sql-server     sql-server)
+  (setq-local sql-port       sql-port)
+  (setq-local sql-connection sql-connection)
+  (setq-default sql-connection nil)
+  ;; Contains the name of database objects
+  (setq-local sql-contains-names t)
+  ;; Keep track of existing object names
+  (setq-local sql-completion-object nil)
+  (setq-local sql-completion-column nil)
+  ;; Create a useful name for renaming this buffer later.
+  (setq-local sql-alternate-buffer-name
+              (sql-make-alternate-buffer-name))
+  ;; User stuff.  Initialize before the hook.
+  (setq-local sql-prompt-regexp
+              (or (sql-get-product-feature sql-product :prompt-regexp) "^"))
+  (setq-local sql-prompt-length
+              (sql-get-product-feature sql-product :prompt-length))
+  (setq-local 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)
+  (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))
+
+(defun sql--adjust-interactive-setup ()
+  "Finish the mode's setup after running the mode hook."
+  ;; Set comint based on user overrides.
+  (setq comint-prompt-regexp
+        (if sql-prompt-cont-regexp
+            (concat "\\(?:\\(?:" sql-prompt-regexp "\\)"
+                    "\\|\\(?:" sql-prompt-cont-regexp "\\)\\)")
+          sql-prompt-regexp))
+  (setq left-margin (or sql-prompt-length 0))
+  ;; Install input sender
+  (setq-local comint-input-sender #'sql-input-sender)
+  ;; People wanting a different history file for each
+  ;; buffer/process/client/whatever can change separator and file-name
+  ;; on the sql-interactive-mode-hook.
+  (let
+      ((comint-input-ring-separator sql-input-ring-separator)
+       (comint-input-ring-file-name sql-input-ring-file-name))
+    (comint-read-input-ring t)))
+
+(defun sql-stop (process event)
+  "Called when the SQL process is stopped.
+
+Writes the input history to a history file using
+`comint-write-input-ring' and inserts a short message in the SQL buffer.
+
+This function is a sentinel watching the SQL interpreter process.
+Sentinels will always get the two parameters PROCESS and EVENT."
+  (when (buffer-live-p (process-buffer process))
+    (with-current-buffer (process-buffer process)
+      (let
+          ((comint-input-ring-separator sql-input-ring-separator)
+           (comint-input-ring-file-name sql-input-ring-file-name))
+        (comint-write-input-ring))
+
+      (if (not buffer-read-only)
+          (insert (format "\nProcess %s %s\n" process event))
+        (message "Process %s %s" process event)))))
+
+\f
+
+;;; Connection handling
+
+(defun sql-read-connection (prompt &optional initial default)
+  "Read a connection name."
+  (let ((completion-ignore-case t))
+    (completing-read prompt
+                     (mapcar #'car sql-connection-alist)
+                     nil t initial 'sql-connection-history default)))
+
+;;;###autoload
+(defun sql-connect (connection &optional buf-name)
+  "Connect to an interactive session using CONNECTION settings.
+
+See `sql-connection-alist' to see how to define connections and
+their settings.
+
+The user will not be prompted for any login parameters if a value
+is specified in the connection settings."
+
+  ;; Prompt for the connection from those defined in the alist
+  (interactive
+   (if sql-connection-alist
+       (list (sql-read-connection "Connection: ")
+             current-prefix-arg)
+     (user-error "No SQL Connections defined")))
+
+  ;; Are there connections defined
+  (if sql-connection-alist
+      ;; Was one selected
+      (when connection
+        ;; Get connection settings
+        (let ((connect-set (cdr (assoc-string connection sql-connection-alist t))))
+          ;; Settings are defined
+          (if connect-set
+              ;; Set the desired parameters
+              (let (param-var login-params set-vars rem-vars)
+                ;; Set the parameters and start the interactive session
+                (dolist (vv connect-set)
+                  (let ((var (car vv))
+                        (val (cadr vv)))
+                    (set-default var (eval val)))) ;FIXME: Why `eval'?
+                (setq-default sql-connection connection)
+
+                ;; :sqli-login params variable
+                (setq param-var
+                      (sql-get-product-feature sql-product :sqli-login nil t))
+
+                ;; :sqli-login params value
+                (setq login-params (symbol-value param-var))
+
+                ;; Params set in the connection
+                (setq set-vars
+                      (mapcar
+                       (lambda (v)
+                         (pcase (car v)
+                           ('sql-user     'user)
+                           ('sql-password 'password)
+                           ('sql-server   'server)
+                           ('sql-database 'database)
+                           ('sql-port     'port)
+                           (s             s)))
+                       connect-set))
+
+                ;; the remaining params (w/o the connection params)
+                (setq rem-vars
+                      (sql-for-each-login login-params
+                                          (lambda (var vals)
+                                            (unless (member var set-vars)
+                                              (if vals (cons var vals) var)))))
+
+                ;; Start the SQLi session with revised list of login parameters
+                (cl-progv (list param-var) (list rem-vars)
+                  (sql-product-interactive
+                   sql-product
+                   (or buf-name (format "<%s>" connection)))))
+
+            (user-error "SQL Connection <%s> does not exist" connection)
+            nil)))
+
+    (user-error "No SQL Connections defined")
+    nil))
+
+(defun sql-save-connection (name)
+  "Captures the connection information of the current SQLi session.
+
+The information is appended to `sql-connection-alist' and
+optionally is saved to the user's init file."
+
+  (interactive "sNew connection name: ")
+
+  (unless (derived-mode-p 'sql-interactive-mode)
+    (user-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)
+            (user-error "Connection <%s> already exists" name)
+          (setq connect
+                (cons name
+                      (sql-for-each-login
+                       `(product ,@login)
+                       (lambda (token _plist)
+                           (pcase token
+                             ('product  `(sql-product  ',product))
+                             ('user     `(sql-user     ,user))
+                             ('database `(sql-database ,database))
+                             ('server   `(sql-server   ,server))
+                             ('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)
+  "Generate menu entries for using each connection."
+  (append
+   (mapcar
+    (lambda (conn)
+        (vector
+         (format "Connection <%s>\t%s" (car conn)
+                 (let ((sql-user "") (sql-database "")
+                       (sql-server "") (sql-port 0))
+                   (cl-progv
+                       (mapcar #'car (cdr conn))
+                       (mapcar #'cadr (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-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 make sure buffer `*SQL*'
+is displayed.
+
+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 (>= (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))
+         ((assoc product sql-product-alist) ; Product specified
+          product)
+         (t sql-product)))              ; Default to sql-product
+
+  ;; If we have a product and it has an interactive mode
+  (if product
+      (when (sql-get-product-feature product :sqli-comint-func)
+        ;; If no new name specified or new name in buffer name,
+        ;; try to pop to an active SQL interactive for the same product
+        (let ((buf (sql-find-sqli-buffer product sql-connection)))
+          (if (and buf (or (not new-name)
+                           (and (stringp new-name)
+                                (string-match-p (regexp-quote new-name) buf))))
+              (sql-display-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 rpt)
+
+              ;; Get credentials.
+              (apply #'sql-get-login
+                     (sql-get-product-feature product :sqli-login))
+
+              ;; Connect to database.
+              (setq rpt (sql-make-progress-reporter nil "Login"))
+
+              (let ((sql-user       (default-value 'sql-user))
+                    (sql-password   (default-value 'sql-password))
+                    (sql-server     (default-value 'sql-server))
+                    (sql-database   (default-value 'sql-database))
+                    (sql-port       (default-value 'sql-port))
+                    (default-directory
+                                    (or sql-default-directory
+                                        default-directory)))
+
+                ;; The password wallet returns a function which supplies the password.
+                (when (functionp sql-password)
+                  (setq sql-password (funcall sql-password)))
+
+                ;; Call the COMINT service
+                (funcall (sql-get-product-feature product :sqli-comint-func)
+                         product
+                         (sql-get-product-feature product :sqli-options)
+                         ;; generate a buffer name
+                         (cond
+                          ((not new-name)
+                           (sql-generate-unique-sqli-buffer-name product nil))
+                          ((consp new-name)
+                           (sql-generate-unique-sqli-buffer-name product
+                            (read-string
+                             "Buffer name (\"*SQL: XXX*\"; enter `XXX'): "
+                             (sql-make-alternate-buffer-name product))))
+                          ((stringp new-name)
+                           (if (or (string-prefix-p " " new-name)
+                                   (string-match-p "\\`[*].*[*]\\'" new-name))
+                               new-name
+                             (sql-generate-unique-sqli-buffer-name product new-name)))
+                          (t
+                           (sql-generate-unique-sqli-buffer-name product new-name)))))
+
+              ;; Set SQLi mode.
+              (let ((sql-interactive-product product))
+                (sql-interactive-mode))
+
+              ;; Set the new buffer name
+              (setq new-sqli-buffer (current-buffer))
+              (setq-local 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)))
+
+              ;; Also set the global value.
+              (setq-default sql-buffer (buffer-name new-sqli-buffer))
+
+              ;; Make sure the connection is complete
+              ;; (Sometimes start up can be slow)
+              ;;  and call the login hook
+              (let ((proc (get-buffer-process new-sqli-buffer))
+                    (secs sql-login-delay)
+                    (step 0.3))
+                (while (and proc
+                            (memq (process-status proc) '(open run))
+                            (or (accept-process-output proc step)
+                                (<= 0.0 (setq secs (- secs step))))
+                            (progn (goto-char (point-max))
+                                   (not (re-search-backward sql-prompt-regexp 0 t))))
+                  (sql-progress-reporter-update rpt)))
+
+              (goto-char (point-max))
+              (when (re-search-backward sql-prompt-regexp nil t)
+                (run-hooks 'sql-login-hook))
+
+              ;; All done.
+              (sql-progress-reporter-done rpt)
+              (goto-char (point-max))
+              (let ((sql-display-sqli-buffer-function t))
+                (sql-display-buffer new-sqli-buffer))
+              (get-buffer new-sqli-buffer)))))
+    (user-error "No default SQL product defined: set `sql-product'")))
+
+(defun sql-comint-automatic-password (_)
+  "Intercept password prompts when we know the password.
+This must also do the job of detecting password prompts."
+  (when (and
+         sql-password
+         (not (string= "" sql-password)))
+    sql-password))
+
+(defun sql-comint (product params &optional buf-name)
+  "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.  BUF-NAME is the name of the new
+buffer.  If nil, a name is chosen for it."
+
+  (let ((program (sql-get-product-feature product :sqli-program)))
+    ;; Make sure we can find the program.  `executable-find' does not
+    ;; work for remote hosts; we suppress the check there.
+    (unless (or (file-remote-p default-directory)
+               (executable-find program))
+      (error "Unable to locate SQL program `%s'" program))
+
+    ;; Make sure buffer name is unique.
+    ;;   if not specified, try *SQL* then *SQL-product*, then *SQL-product1*, ...
+    ;;   otherwise, use *buf-name*
+    (if buf-name
+        (unless (or (string-prefix-p " " buf-name)
+                    (string-match-p "\\`[*].*[*]\\'" buf-name))
+          (setq buf-name (concat "*" buf-name "*")))
+      (setq buf-name (sql-generate-unique-sqli-buffer-name product nil)))
+    (set-text-properties 0 (length buf-name) nil buf-name)
+
+    ;; Create the buffer first, because we want to set it up before
+    ;; comint starts to run.
+    (set-buffer (get-buffer-create buf-name))
+    ;; Set up the automatic population of passwords, if supported.
+    (when (sql-get-product-feature product :password-in-comint)
+      (setq comint-password-function #'sql-comint-automatic-password))
+
+    ;; Start the command interpreter in the buffer
+    ;;   PROC-NAME is BUF-NAME without enclosing asterisks
+    (let ((proc-name (replace-regexp-in-string "\\`[*]\\(.*\\)[*]\\'" "\\1" buf-name)))
+      (set-buffer
+       (apply #'make-comint-in-buffer
+              proc-name 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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-oracle-program'.  Login uses
+the variables `sql-user', `sql-password', and `sql-database' as
+defaults, if set.  Additional command line parameters can be stored in
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'oracle buffer))
+
+(defun sql-comint-oracle (product options &optional buf-name)
+  "Create comint buffer and connect to Oracle."
+  ;; Produce user/password@database construct.  Password without user
+  ;; is meaningless; database without user/password is meaningless,
+  ;; because "@param" will ask sqlplus to interpret the script
+  ;; "param".
+  (let (parameter nlslang coding)
+    (if (not (string= "" sql-user))
+       (if (not (string= "" sql-password))
+           (setq parameter (concat sql-user "/" sql-password))
+         (setq parameter sql-user)))
+    (if (and parameter (not (string= "" sql-database)))
+       (setq parameter (concat parameter "@" sql-database)))
+    ;; options must appear before the logon parameters
+    (if parameter
+       (setq parameter (append options (list parameter)))
+      (setq parameter options))
+    (sql-comint product parameter buf-name)
+    ;; Set process coding system to agree with the interpreter
+    (setq nlslang (or (getenv "NLS_LANG") "")
+          coding  (dolist (cs
+                           ;; Are we missing any common NLS character sets
+                           '(("US8PC437"  . cp437)
+                             ("EL8PC737"  . cp737)
+                             ("WE8PC850"  . cp850)
+                             ("EE8PC852"  . cp852)
+                             ("TR8PC857"  . cp857)
+                             ("WE8PC858"  . cp858)
+                             ("IS8PC861"  . cp861)
+                             ("IW8PC1507" . cp862)
+                             ("N8PC865"   . cp865)
+                             ("RU8PC866"  . cp866)
+                             ("US7ASCII"  . us-ascii)
+                             ("UTF8"      . utf-8)
+                             ("AL32UTF8"  . utf-8)
+                             ("AL16UTF16" . utf-16))
+                           (or coding 'utf-8))
+                    (when (string-match (format "\\.%s\\'" (car cs)) nlslang)
+                      (setq coding (cdr cs)))))
+    (set-process-coding-system (get-buffer-process (current-buffer))
+                               coding coding)))
+
+(defun sql-oracle-save-settings (sqlbuf)
+  "Save 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")
+
+   ;; FEEDBACK 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-object-name (obj-name)
+  (format "CASE WHEN REGEXP_LIKE (%s, q'/^[A-Z0-9_#$]+$/','c') THEN %s ELSE '\"'|| %s ||'\"' END "
+          obj-name obj-name obj-name))
+
+(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 "
+          ", " (sql-oracle--list-object-name "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 "
+          ", "  (sql-oracle--list-object-name "x.owner")
+          " ||'.'|| "  (sql-oracle--list-object-name "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))
+
+(defun sql-oracle-completion-object (sqlbuf schema)
+  (sql-redirect-value
+   sqlbuf
+   (concat
+    "SELECT CHR(1)||"
+    (if schema
+        (concat "CASE WHEN REGEXP_LIKE (owner, q'/^[A-Z0-9_#$]+$/','c') THEN owner ELSE '\"'|| owner ||'\"' END "
+                "||'.'||"
+                "CASE WHEN REGEXP_LIKE (object_name, q'/^[A-Z0-9_#$]+$/','c') THEN object_name ELSE '\"'|| object_name ||'\"' END "
+                " AS o FROM all_objects "
+                (format "WHERE owner = %s AND "
+                        (sql-str-literal (if (string-match "^[\"]\\(.+\\)[\"]$" schema)
+                                             (match-string 1 schema) (upcase schema)))))
+      (concat "CASE WHEN REGEXP_LIKE (object_name, q'/^[A-Z0-9_#$]+$/','c') THEN object_name ELSE '\"'|| object_name ||'\"' END "
+              " 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 (&optional buffer)
+  "Run isql by Sybase 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*'.
+
+Interpreter used comes from variable `sql-sybase-program'.  Login uses
+the variables `sql-server', `sql-user', `sql-password', and
+`sql-database' as defaults, if set.  Additional command line parameters
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'sybase buffer))
+
+(defun sql-comint-sybase (product options &optional buf-name)
+  "Create comint buffer and connect to Sybase."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let ((params
+         (append
+          (if (not (string= "" sql-user))
+              (list "-U" sql-user))
+          (if (not (string= "" sql-password))
+              (list "-P" sql-password))
+          (if (not (string= "" sql-database))
+              (list "-D" sql-database))
+          (if (not (string= "" sql-server))
+              (list "-S" sql-server))
+          options)))
+    (sql-comint product params buf-name)))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-informix-program'.  Login uses
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'informix buffer))
+
+(defun sql-comint-informix (product options &optional buf-name)
+  "Create comint buffer and connect to Informix."
+  ;; username and password are ignored.
+  (let ((db (if (string= "" sql-database)
+               "-"
+             (if (string= "" sql-server)
+                 sql-database
+               (concat sql-database "@" sql-server)))))
+    (sql-comint product (append `(,db "-") options) buf-name)))
+
+\f
+
+;;;###autoload
+(defun sql-sqlite (&optional buffer)
+  "Run sqlite as an inferior process.
+
+SQLite is free software.
+
+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*'.
+
+Interpreter used comes from variable `sql-sqlite-program'.  Login uses
+the variables `sql-user', `sql-password', `sql-database', and
+`sql-server' as defaults, if set.  Additional command line parameters
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'sqlite buffer))
+
+(defun sql-comint-sqlite (product options &optional buf-name)
+  "Create comint buffer and connect to SQLite."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let ((params
+         (append options
+                 (if (not (string= "" sql-database))
+                     `(,(expand-file-name sql-database))))))
+    (sql-comint product params buf-name)))
+
+(defun sql-sqlite-completion-object (sqlbuf _schema)
+  (sql-redirect-value sqlbuf ".tables" "\\sw\\(?:\\sw\\|\\s_\\)*" 0))
+
+\f
+
+;;;###autoload
+(defun sql-mysql (&optional buffer)
+  "Run mysql by TcX as an inferior process.
+
+Mysql versions 3.23 and up are free software.
+
+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*'.
+
+Interpreter used comes from variable `sql-mysql-program'.  Login uses
+the variables `sql-user', `sql-password', `sql-database', and
+`sql-server' as defaults, if set.  Additional command line parameters
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'mysql buffer))
+
+(defun sql-comint-mysql (product options &optional buf-name)
+  "Create comint buffer and connect to MySQL."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let ((params
+         (append
+          options
+          (if (not (string= "" sql-user))
+              (list (concat "--user=" sql-user)))
+          (if (not (string= "" sql-password))
+              (list (concat "--password=" sql-password)))
+          (if (not (= 0 sql-port))
+              (list (concat "--port=" (number-to-string sql-port))))
+          (if (not (string= "" sql-server))
+              (list (concat "--host=" sql-server)))
+          (if (not (string= "" sql-database))
+              (list sql-database)))))
+    (sql-comint product params buf-name)))
+
+;;;###autoload
+(defun sql-mariadb (&optional buffer)
+    "Run mysql by MariaDB as an inferior process.
+
+MariaDB is free software.
+
+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*'.
+
+Interpreter used comes from variable `sql-mariadb-program'.  Login uses
+the variables `sql-user', `sql-password', `sql-database', and
+`sql-server' as defaults, if set.  Additional command line parameters
+can be stored in the list `sql-mariadb-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-mariadb].  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-mariadb].  You can also specify this with \\[set-buffer-process-coding-system]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'mariadb buffer))
+
+(defun sql-comint-mariadb (product options &optional buf-name)
+  "Create comint buffer and connect to MariaDB.
+
+Use the MySQL comint driver since the two are compatible."
+  (sql-comint-mysql product options buf-name))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-solid-program'.  Login uses
+the variables `sql-user', `sql-password', and `sql-server' as
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'solid buffer))
+
+(defun sql-comint-solid (product options &optional buf-name)
+  "Create comint buffer and connect to Solid."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let ((params
+         (append
+          (if (not (string= "" sql-server))
+              (list sql-server))
+          ;; It only makes sense if both username and password are there.
+          (if (not (or (string= "" sql-user)
+                       (string= "" sql-password)))
+              (list sql-user sql-password))
+          options)))
+    (sql-comint product params buf-name)))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-ingres-program'.  Login uses
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'ingres buffer))
+
+(defun sql-comint-ingres (product options &optional buf-name)
+  "Create comint buffer and connect to Ingres."
+  ;; username and password are ignored.
+  (sql-comint product
+              (append (if (string= "" sql-database)
+                          nil
+                        (list sql-database))
+                      options)
+              buf-name))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-ms-program'.  Login uses the
+variables `sql-user', `sql-password', `sql-database', and `sql-server'
+as defaults, if set.  Additional command line parameters can be stored
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'ms buffer))
+
+(defun sql-comint-ms (product options &optional buf-name)
+  "Create comint buffer and connect to Microsoft SQL Server."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let ((params
+         (append
+          (if (not (string= "" sql-user))
+              (list "-U" sql-user))
+          (if (not (string= "" sql-database))
+              (list "-d" sql-database))
+          (if (not (string= "" sql-server))
+              (list "-S" sql-server))
+          options)))
+    (setq params
+          (if (not (string= "" sql-password))
+              `("-P" ,sql-password ,@params)
+            (if (string= "" sql-user)
+                ;; If neither user nor password is provided, use system
+                ;; credentials.
+                `("-E" ,@params)
+              ;; If -P is passed to ISQL as the last argument without a
+              ;; password, it's considered null.
+              `(,@params "-P"))))
+    (sql-comint product params buf-name)))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-postgres-program'.  Login uses
+the variables `sql-database' and `sql-server' as default, if set.
+Additional command line parameters can be stored in the list
+`sql-postgres-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-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]
+in the SQL buffer, after you start the process.
+The default comes from `process-coding-system-alist' and
+`default-process-coding-system'.  If your output lines end with ^M,
+your might try undecided-dos as a coding system.  If this doesn't help,
+Try to set `comint-output-filter-functions' like this:
+
+\(add-hook \\='comint-output-filter-functions #\\='comint-strip-ctrl-m \\='append)
+
+\(Type \\[describe-mode] in the SQL buffer for a list of commands.)"
+  (interactive "P")
+  (sql-product-interactive 'postgres buffer))
+
+(defun sql-comint-postgres (product options &optional buf-name)
+  "Create comint buffer and connect to Postgres."
+  ;; username and password are ignored.  Mark Stosberg suggests to add
+  ;; the database at the end.  Jason Beegan suggests using --pset and
+  ;; pager=off instead of \\o|cat.  The later was the solution by
+  ;; Gregor Zych.  Jason's suggestion is the default value for
+  ;; sql-postgres-options.
+  (let ((params
+         (append
+          (if (not (= 0 sql-port))
+              (list "-p" (number-to-string sql-port)))
+          (if (not (string= "" sql-user))
+              (list "-U" sql-user))
+          (if (not (string= "" sql-server))
+              (list "-h" sql-server))
+          options
+          (if (not (string= "" sql-database))
+              (list sql-database)))))
+    (sql-comint product params buf-name)))
+
+(defun sql-postgres-completion-object (sqlbuf schema)
+  (sql-redirect sqlbuf "\\t on")
+  (let ((aligned
+         (string= "aligned"
+                  (car (sql-redirect-value
+                        sqlbuf "\\a"
+                        "Output format is \\(.*\\)[.]$" 1)))))
+    (when aligned
+      (sql-redirect sqlbuf "\\a"))
+    (let* ((fs (or (car (sql-redirect-value
+                         sqlbuf "\\f" "Field separator is \"\\(.\\)[.]$" 1))
+                   "|"))
+           (re (concat "^\\([^" fs "]*\\)" fs "\\([^" fs "]*\\)"
+                       fs "[^" fs "]*" fs  "[^" fs "]*$"))
+           (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 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")
+                      (format "\"%s\"" (cadr tbl))
+                    (format "\"%s\".\"%s\"" (car tbl) (cadr tbl))))
+              cl))))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-interbase-program'.  Login
+uses the variables `sql-user', `sql-password', and `sql-database' as
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'interbase buffer))
+
+(defun sql-comint-interbase (product options &optional buf-name)
+  "Create comint buffer and connect to Interbase."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let ((params
+         (append
+          (if (not (string= "" sql-database))
+              (list sql-database))      ; Add to the front!
+          (if (not (string= "" sql-password))
+              (list "-p" sql-password))
+          (if (not (string= "" sql-user))
+              (list "-u" sql-user))
+          options)))
+    (sql-comint product params buf-name)))
+
+\f
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-db2-program'.  There is not
+automatic login.
+
+The buffer is put in SQL interactive mode, giving commands for sending
+input.  See `sql-interactive-mode'.
+
+If you use \\[sql-accumulate-and-indent] to send multiline commands to
+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]
+in the SQL buffer, after you start the process.
+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 "P")
+  (sql-product-interactive 'db2 buffer))
+
+(defun sql-comint-db2 (product options &optional buf-name)
+  "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 buf-name))
+
+;;;###autoload
+(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.
+If buffer exists and a process is running, just switch to buffer
+`*SQL*'.
+
+Interpreter used comes from variable `sql-linter-program' - usually `inl'.
+Login uses the variables `sql-user', `sql-password', `sql-database' and
+`sql-server' as defaults, if set.  Additional command line parameters
+can be stored in the list `sql-linter-options'.  Run inl -h to get help on
+parameters.
+
+`sql-database' is used to set the LINTER_MBX environment variable for
+local connections, `sql-server' refers to the server name from the
+`nodetab' file for the network connection (dbc_tcp or friends must run
+for this to work).  If `sql-password' is an empty string, inl will use
+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 "P")
+  (sql-product-interactive 'linter buffer))
+
+(defun sql-comint-linter (product options &optional buf-name)
+  "Create comint buffer and connect to Linter."
+  ;; Put all parameters to the program (if defined) in a list and call
+  ;; make-comint.
+  (let* ((login
+          (if (not (string= "" sql-user))
+              (concat sql-user "/" sql-password)))
+         (params
+          (append
+           (if (not (string= "" sql-server))
+               (list "-n" sql-server))
+           (list "-u" login)
+           options)))
+    (cl-letf (((getenv "LINTER_MBX")
+               (unless (string= "" sql-database) sql-database)))
+      (sql-comint product params buf-name))))
+
+\f
+
+(defcustom sql-vertica-program "vsql"
+  "Command to start the Vertica client."
+  :version "25.1"
+  :type 'file)
+
+(defcustom sql-vertica-options '("-P" "pager=off")
+  "List of additional options for `sql-vertica-program'.
+The default value disables the internal pager."
+  :version "25.1"
+  :type '(repeat string))
+
+(defcustom sql-vertica-login-params '(user password database server)
+  "List of login parameters needed to connect to Vertica."
+  :version "25.1"
+  :type 'sql-login-params)
+
+(defun sql-comint-vertica (product options &optional buf-name)
+  "Create comint buffer and connect to Vertica."
+  (sql-comint product
+              (nconc
+               (and (not (string= "" sql-server))
+                    (list "-h" sql-server))
+               (and (not (string= "" sql-database))
+                    (list "-d" sql-database))
+               (and (not (string= "" sql-password))
+                    (list "-w" sql-password))
+               (and (not (string= "" sql-user))
+                    (list "-U" sql-user))
+               options)
+              buf-name))
+
+;;;###autoload
+(defun sql-vertica (&optional buffer)
+  "Run vsql as an inferior process."
+  (interactive "P")
+  (sql-product-interactive 'vertica buffer))
+
+\f
+(provide 'sql)
+
+; LocalWords:  sql SQL SQLite sqlite Sybase Informix MySQL
+; LocalWords:  Postgres SQLServer SQLi
+
+;;; sql.el ends here