-;;; sql.el --- specialized comint.el for SQL interpreters
+;;; sql.el --- specialized comint.el for SQL interpreters -*- lexical-binding: t -*-
;; Copyright (C) 1998-2013 Free Software Foundation, Inc.
;; Hint for newbies: take a look at `dabbrev-expand', `abbrev-mode', and
;; `imenu-add-menubar-index'.
-;;; Requirements for Emacs 19.34:
-
-;; If you are using Emacs 19.34, you will have to get and install
-;; the file regexp-opt.el
-;; <URL:ftp://ftp.ifi.uio.no/pub/emacs/emacs-20.3/lisp/emacs-lisp/regexp-opt.el>
-;; and the custom package
-;; <URL:http://www.dina.kvl.dk/~abraham/custom/>.
-
;;; Bugs:
;; sql-ms now uses osql instead of isql. Osql flushes its error
;;
;; ;; Do something with `sql-user', `sql-password',
;; ;; `sql-database', and `sql-server'.
-;; (let ((params options))
-;; (if (not (string= "" sql-server))
-;; (setq params (append (list "-S" sql-server) params)))
-;; (if (not (string= "" sql-database))
-;; (setq params (append (list "-D" sql-database) params)))
-;; (if (not (string= "" sql-password))
-;; (setq params (append (list "-P" sql-password) params)))
+;; (let ((params
+;; (append
;; (if (not (string= "" sql-user))
-;; (setq params (append (list "-U" sql-user) params)))
+;; (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)))
;;
;; (sql-set-product-feature 'xyz
;;; Code:
+(require 'cl-lib)
(require 'comint)
;; Need the following to allow GNU Emacs 19 to compile the file.
(eval-when-compile
(require 'regexp-opt))
(require 'custom)
(require 'thingatpt)
-(eval-when-compile ;; needed in Emacs 19, 20
- (setq max-specpdl-size (max max-specpdl-size 2000)))
-
-(defun sql-signum (n)
- "Return 1, 0, or -1 to identify the sign of N."
- (cond
- ((not (numberp n)) nil)
- ((< n 0) -1)
- ((> n 0) 1)
- (t 0)))
(defvar font-lock-keyword-face)
(defvar font-lock-set-defaults)
(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)))
+ ,@(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 'SQL)
(defcustom sql-product 'ansi
- "Select the SQL database product used so that buffers can be
-highlighted properly when you open them."
+ "Select the SQL database product used.
+This allows highlighting buffers properly when you open them."
:type `(choice
,@(mapcar (lambda (prod-info)
`(const :tag
(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\\)\\s-+\\(\\w+\\)" 3)
- ("Sequences" "^\\s-*create\\s-+\\(\\w+\\s-+\\)*sequence\\s-+\\(\\w+\\)" 2)
- ("Triggers" "^\\s-*create\\s-+\\(\\w+\\s-+\\)*trigger\\s-+\\(\\w+\\)" 2)
- ("Functions" "^\\s-*\\(create\\s-+\\(\\w+\\s-+\\)*\\)?function\\s-+\\(\\w+\\)" 3)
- ("Procedures" "^\\s-*\\(create\\s-+\\(\\w+\\s-+\\)*\\)?proc\\(edure\\)?\\s-+\\(\\w+\\)" 4)
- ("Packages" "^\\s-*create\\s-+\\(\\w+\\s-+\\)*package\\s-+\\(body\\s-+\\)?\\(\\w+\\)" 3)
- ("Types" "^\\s-*create\\s-+\\(\\w+\\s-+\\)*type\\s-+\\(body\\s-+\\)?\\(\\w+\\)" 3)
- ("Indexes" "^\\s-*create\\s-+\\(\\w+\\s-+\\)*index\\s-+\\(\\w+\\)" 2)
- ("Tables/Views" "^\\s-*create\\s-+\\(\\w+\\s-+\\)*\\(table\\|view\\)\\s-+\\(\\w+\\)" 3))
+ '(("Rules/Defaults" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*\\(?:rule\\|default\\)\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\s-+\\(\\w+\\)" 1)
+ ("Sequences" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*sequence\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Triggers" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*trigger\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Functions" "^\\s-*\\(?:create\\s-+\\(?:\\w+\\s-+\\)*\\)?function\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Procedures" "^\\s-*\\(?:create\\s-+\\(?:\\w+\\s-+\\)*\\)?proc\\(?:edure\\)?\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Packages" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*package\\s-+\\(?:body\\s-+\\)?\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Types" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*type\\s-+\\(?:body\\s-+\\)?\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Indexes" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*index\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1)
+ ("Tables/Views" "^\\s-*create\\s-+\\(?:\\w+\\s-+\\)*\\(?:table\\|view\\)\\s-+\\(?:if\\s-+not\\s-+exists\\s-+\\)?\\(\\w+\\)" 1))
"Define interesting points in the SQL buffer for `imenu'.
This is used to set `imenu-generic-expression' when SQL mode is
;; Customization for ANSI
-(defcustom sql-ansi-statement-starters (regexp-opt '(
- "create" "alter" "drop"
- "select" "insert" "update" "delete" "merge"
- "grant" "revoke"
-))
- "Regexp of keywords that start SQL commands
+(defcustom sql-ansi-statement-starters
+ (regexp-opt '("create" "alter" "drop"
+ "select" "insert" "update" "delete" "merge"
+ "grant" "revoke"))
+ "Regexp of keywords that start SQL commands.
All products share this list; products should define a regexp to
identify additional keywords in a variable defined by
Used by `sql-rename-buffer'.")
(defun sql-buffer-live-p (buffer &optional product connection)
- "Returns non-nil if the process associated with buffer is live.
+ "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 an running process attached to it, be in
+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."
(setq buffer (get-buffer buffer))
(and buffer
(buffer-live-p buffer)
- (get-buffer-process buffer)
(comint-check-proc buffer)
(with-current-buffer buffer
(and (derived-mode-p 'sql-interactive-mode)
;; Abbreviations -- if you want more of them, define them in your init
;; file. Abbrevs have to be enabled in your init file, too.
-(defvar sql-mode-abbrev-table nil
+(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'.")
-(unless sql-mode-abbrev-table
- (define-abbrev-table 'sql-mode-abbrev-table nil))
-
-(mapc
- ;; In Emacs 22+, provide SYSTEM-FLAG to define-abbrev.
- (lambda (abbrev)
- (let ((name (car abbrev))
- (expansion (cdr abbrev)))
- (condition-case nil
- (define-abbrev sql-mode-abbrev-table name expansion nil 0 t)
- (error
- (define-abbrev sql-mode-abbrev-table name expansion)))))
- '(("ins" . "insert")
- ("upd" . "update")
- ("del" . "delete")
- ("sel" . "select")
- ("proc" . "procedure")
- ("func" . "function")
- ("cr" . "create")))
;; Syntax Table
"\\(?:\\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+\\)")
1 'font-lock-function-name-face))
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 ()
+(defun sql--oracle-show-reserved-words ()
;; This function is for use by the maintainer of SQL.EL only.
- (interactive)
(if (or (and (not (derived-mode-p 'sql-mode))
(not (derived-mode-p 'sql-interactive-mode)))
(not sql-buffer)
;; 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" "backward"
+"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"
"delimiters" "dictionary" "disable" "discard" "document" "domain"
"drop" "each" "enable" "encoding" "encrypted" "enum" "escape"
"exclude" "excluding" "exclusive" "execute" "exists" "explain"
-"external" "extract" "family" "first" "float" "following" "force"
+"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" "language" "large" "last"
-"lc_collate" "lc_ctype" "least" "level" "listen" "load" "local"
+"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" "name" "names" "national" "nchar"
+"minvalue" "mode" "month" "move" "names" "national" "nchar"
"next" "no" "nocreatedb" "nocreaterole" "nocreateuser" "noinherit"
-"nologin" "none" "nosuperuser" "nothing" "notify" "nowait" "nullif"
-"nulls" "object" "of" "oids" "operator" "option" "options" "out"
-"overlay" "owned" "owner" "parser" "partial" "partition" "password"
-"plans" "position" "preceding" "prepare" "prepared" "preserve" "prior"
+"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" "reindex" "relative" "release"
-"rename" "repeatable" "replace" "replica" "reset" "restart" "restrict"
+"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" "sequences"
+"schema" "scroll" "search" "second" "security" "sequence"
"serializable" "server" "session" "set" "setof" "share" "show"
-"simple" "stable" "standalone" "start" "statement" "statistics"
+"simple" "snapshot" "stable" "standalone" "start" "statement" "statistics"
"stdin" "stdout" "storage" "strict" "strip" "substring" "superuser"
"sysid" "system" "tables" "tablespace" "temp" "template" "temporary"
-"transaction" "treat" "trigger" "trim" "truncate" "trusted" "type"
-"unbounded" "uncommitted" "unencrypted" "unknown" "unlisten" "until"
-"update" "vacuum" "valid" "validator" "value" "values" "version"
-"view" "volatile" "whitespace" "work" "wrapper" "write"
-"xmlattributes" "xmlconcat" "xmlelement" "xmlforest" "xmlparse"
-"xmlpi" "xmlroot" "xmlserialize" "year" "yes"
+"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" "any" "array" "asc" "as" "asymmetric"
+"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"
"fetch" "foreign" "for" "freeze" "from" "full" "grant" "group"
"having" "ilike" "initially" "inner" "in" "intersect" "into" "isnull"
"is" "join" "leading" "left" "like" "limit" "localtime"
-"localtimestamp" "natural" "notnull" "not" "null" "off" "offset"
+"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
"bigint" "bigserial" "bit" "bool" "boolean" "box" "bytea" "char"
"character" "cidr" "circle" "date" "decimal" "double" "float4"
"float8" "inet" "int" "int2" "int4" "int8" "integer" "interval" "line"
-"lseg" "macaddr" "money" "numeric" "path" "point" "polygon"
-"precision" "real" "serial" "serial4" "serial8" "smallint" "text"
+"lseg" "macaddr" "money" "name" "numeric" "path" "point" "polygon"
+"precision" "real" "serial" "serial4" "serial8" "sequences" "smallint" "text"
"time" "timestamp" "timestamptz" "timetz" "tsquery" "tsvector"
-"txid_snapshot" "uuid" "varbit" "varchar" "varying" "without"
+"txid_snapshot" "unknown" "uuid" "varbit" "varchar" "varying" "without"
"xml" "zone"
)))
(append keywords old-val))))))
(defun sql-for-each-login (login-params body)
- "Iterates through login parameters and returns a list of results."
-
+ "Iterate through login parameters and return a list of results."
(delq nil
(mapcar
(lambda (param)
- (let ((token (or (and (listp param) (car param)) param))
- (plist (or (and (listp param) (cdr param)) nil)))
-
+ (let ((token (or (car-safe param) param))
+ (plist (cdr-safe param)))
(funcall body token plist)))
login-params)))
local variable."
(save-excursion (comint-bol nil) (point))))
+;;; 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 http://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)
"\\>")))
(defun sql-beginning-of-statement (arg)
- "Moves the cursor to the beginning of the current SQL statement."
+ "Move to the beginning of the current SQL statement."
(interactive "p")
(let ((here (point))
(beginning-of-line)
;; If we didn't move, try again
(when (= here (point))
- (sql-beginning-of-statement (* 2 (sql-signum arg))))))
+ (sql-beginning-of-statement (* 2 (cl-signum arg))))))
(defun sql-end-of-statement (arg)
- "Moves the cursor to the end of the current SQL statement."
+ "Move to the end of the current SQL statement."
(interactive "p")
(let ((term (sql-get-product-feature sql-product :terminator))
(re-search (if (> 0 arg) 're-search-backward 're-search-forward))
(when (consp term)
(setq term (car term)))
;; Iterate until we've moved the desired number of stmt ends
- (while (not (= (sql-signum arg) 0))
+ (while (not (= (cl-signum arg) 0))
;; if we're looking at the terminator, jump by 2
(if (or (and (> 0 arg) (looking-back term))
(and (< 0 arg) (looking-at term)))
(setq arg 0)
;; count it if we're not in a comment
(unless (nth 7 (syntax-ppss))
- (setq arg (- arg (sql-signum arg))))))
+ (setq arg (- arg (cl-signum arg))))))
(goto-char (if (match-data)
(match-end 0)
here))))
doc))
;;;###autoload
-(defun sql-help ()
- "Show short help for the SQL modes.
+(eval
+ ;; FIXME: This dynamic-docstring-function trick doesn't work for byte-compiled
+ ;; functions, because of the lazy-loading of docstrings, which strips away
+ ;; text properties.
+ '(defun sql-help ()
+ #("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.
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."
+ 0 1 (dynamic-docstring-function sql--make-help-docstring))
(interactive)
+ (describe-function 'sql-help)))
- ;; Insert references to loaded products into the help buffer string
- (let ((doc (documentation 'sql-help t))
- changedp)
- (setq changedp nil)
-
- ;; Insert FREE software list
- (when (string-match "^\\(\\s-*\\)[\\\\][\\\\]FREE\\s-*\n" doc 0)
- (setq doc (replace-match (sql-help-list-products (match-string 1 doc) t)
- t t doc 0)
- changedp t))
-
- ;; Insert non-FREE software list
- (when (string-match "^\\(\\s-*\\)[\\\\][\\\\]NONFREE\\s-*\n" doc 0)
- (setq doc (replace-match (sql-help-list-products (match-string 1 doc) nil)
- t t doc 0)
- changedp t))
+(defun sql--make-help-docstring (doc _fun)
+ "Insert references to loaded products into the help buffer string."
- ;; If we changed the help text, save the change so that the help
- ;; sub-system will see it
- (when changedp
- (put 'sql-help 'function-documentation doc)))
+ ;; Insert FREE software list
+ (when (string-match "^\\(\\s-*\\)[\\\\][\\\\]FREE\\s-*\n" doc 0)
+ (setq doc (replace-match (sql-help-list-products (match-string 1 doc) t)
+ t t doc 0)))
- ;; Call help on this function
- (describe-function 'sql-help))
-
-(defun sql-read-passwd (prompt &optional default)
- "Read a password using PROMPT. Optional DEFAULT is password to start with."
- (read-passwd prompt nil default))
+ ;; Insert non-FREE software list
+ (when (string-match "^\\(\\s-*\\)[\\\\][\\\\]NONFREE\\s-*\n" doc 0)
+ (setq doc (replace-match (sql-help-list-products (match-string 1 doc) nil)
+ t t doc 0)))
+ doc)
(defun sql-get-login-ext (symbol prompt history-var plist)
"Prompt user with extended login parameters.
(read-number prompt (or default last-value 0)))
(t
- (let ((r (read-from-minibuffer prompt-def last-value nil nil history-var nil)))
- (if (string= "" r) (or default "") r)))))))
+ (read-string prompt-def last-value history-var default))))))
(defun sql-get-login (&rest what)
"Get username, password and database from the user.
In order to ask the user for username, password and database, call the
function like this: (sql-get-login 'user 'password 'database)."
- (interactive)
- (mapcar
- (lambda (w)
- (let ((token (or (and (consp w) (car w)) w))
- (plist (or (and (consp w) (cdr w)) nil)))
-
- (cond
- ((eq token 'user) ; user
- (sql-get-login-ext 'sql-user "User: " 'sql-user-history plist))
+ (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))
- ((eq token 'password) ; password
- (setq-default sql-password
- (sql-read-passwd "Password: " sql-password)))
+ (`password
+ (setq-default sql-password
+ (read-passwd "Password: " nil sql-password)))
- ((eq token 'server) ; server
- (sql-get-login-ext 'sql-server "Server: " 'sql-server-history plist))
+ (`server
+ (sql-get-login-ext 'sql-server "Server: " 'sql-server-history plist))
- ((eq token 'database) ; database
- (sql-get-login-ext 'sql-database "Database: " 'sql-database-history plist))
+ (`database
+ (sql-get-login-ext 'sql-database "Database: "
+ 'sql-database-history plist))
- ((eq token 'port) ; port
- (sql-get-login-ext 'sql-port "Port: " nil (append '(:number t) plist))))))
- what))
+ (`port
+ (sql-get-login-ext 'sql-port "Port: "
+ nil (append '(:number t) plist)))))))
(defun sql-find-sqli-buffer (&optional product connection)
- "Returns the name of the current default SQLi buffer or nil.
+ "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)
(sql-for-each-login
(sql-get-product-feature sql-product :sqli-login)
(lambda (token plist)
- (cond
- ((eq token 'user)
+ (pcase token
+ (`user
(unless (string= "" sql-user)
(list "/" sql-user)))
- ((eq token 'port)
+ (`port
(unless (or (not (numberp sql-port))
(= 0 sql-port))
(list ":" (number-to-string sql-port))))
- ((eq token 'server)
+ (`server
(unless (string= "" sql-server)
(list "."
(if (plist-member plist :file)
(file-name-nondirectory sql-server)
sql-server))))
- ((eq token 'database)
+ (`database
(unless (string= "" sql-database)
(list "@"
(if (plist-member plist :file)
(file-name-nondirectory sql-database)
sql-database))))
- ((eq token 'password) nil)
- (t nil))))))))
+ ;; (`password nil)
+ (_ nil))))))))
;; If there's a connection, use it and the name thus far
(if sql-connection
(nreverse results)))
(defun sql-execute (sqlbuf outbuf command enhanced arg)
- "Executes a command in a SQL interactive buffer and captures the output.
+ "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.
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. "
+buffer is popped into a view window."
(mapc
(lambda (c)
(cond
(defvar sql-completion-sqlbuf nil)
-(defun sql-try-completion (string collection &optional predicate)
+(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
- (cond
- ((not predicate)
- (try-completion string sql-completion-object))
- ((eq predicate t)
- (all-completions string sql-completion-object))
- ((eq predicate 'lambda)
- (test-completion string sql-completion-object))
- ((eq (car predicate) 'boundaries)
- (completion-boundaries string sql-completion-object nil (cdr predicate)))))))
+ (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."
(completion-ignore-case t))
(if (sql-get-product-feature product :completion-object)
- (completing-read prompt (function sql-try-completion)
+ (completing-read prompt #'sql--completion-table
nil nil tname)
(read-from-minibuffer prompt tname))))
(if sql-mode-menu
(easy-menu-add sql-mode-menu)); XEmacs
+ ;; (smie-setup sql-smie-grammar #'sql-smie-rules)
(set (make-local-variable 'comment-start) "--")
;; Make each buffer in sql-mode remember the "current" SQLi buffer.
(make-local-variable 'sql-buffer)
(set (make-local-variable 'paragraph-separate) "[\f]*$")
(set (make-local-variable 'paragraph-start) "[\n\f]")
;; Abbrevs
- (setq abbrev-all-caps 1)
+ (setq-local abbrev-all-caps 1)
;; Contains the name of database objects
(set (make-local-variable 'sql-contains-names) t)
;; Catch changes to sql-product and highlight accordingly
(setq set-params
(mapcar
(lambda (v)
- (cond
- ((eq (car v) 'sql-user) 'user)
- ((eq (car v) 'sql-password) 'password)
- ((eq (car v) 'sql-server) 'server)
- ((eq (car v) 'sql-database) 'database)
- ((eq (car v) 'sql-port) 'port)
- (t (car v))))
+ (pcase (car v)
+ (`sql-user 'user)
+ (`sql-password 'password)
+ (`sql-server 'server)
+ (`sql-database 'database)
+ (`sql-port 'port)
+ (s s)))
(cdr connect-set)))
;; the remaining params (w/o the connection params)
;; Start the SQLi session with revised list of login parameters
(eval `(let ((,param-var ',rem-params))
- (sql-product-interactive sql-product new-name))))
+ (sql-product-interactive ',sql-product ',new-name))))
(message "SQL Connection <%s> does not exist" connection)
nil)))
(if (assoc name alist)
(message "Connection <%s> already exists" name)
(setq connect
- (append (list name)
- (sql-for-each-login
- `(product ,@login)
- (lambda (token _plist)
- (cond
- ((eq token 'product) `(sql-product ',product))
- ((eq token 'user) `(sql-user ,user))
- ((eq token 'database) `(sql-database ,database))
- ((eq token 'server) `(sql-server ,server))
- ((eq token 'port) `(sql-port ,port)))))))
+ (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)))
(customize-set-variable 'sql-connection-alist alist)))))))
(defun sql-connection-menu-filter (tail)
- "Generates menu entries for using each connection."
+ "Generate menu entries for using each connection."
(append
(mapcar
(lambda (conn)
new-sqli-buffer)
;; Get credentials.
- (apply 'sql-get-login (sql-get-product-feature product :sqli-login))
+ (apply #'sql-get-login
+ (sql-get-product-feature product :sqli-login))
;; Connect to database.
(message "Login...")
(sql-comint product parameter)))
(defun sql-oracle-save-settings (sqlbuf)
- "Saves most SQL*Plus settings so they may be reset by \\[sql-redirect]."
+ "Save most SQL*Plus settings so they may be reset by \\[sql-redirect]."
;; Note: does not capture the following settings:
;;
;; APPINFO
;; Restore the changed settings
(sql-redirect sqlbuf saved-settings))
-(defun sql-oracle-list-all (sqlbuf outbuf enhanced table-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
(sql-oracle-restore-settings sqlbuf settings)))
-(defun sql-oracle-list-table (sqlbuf outbuf enhanced table-name)
+(defun sql-oracle-list-table (sqlbuf outbuf _enhanced table-name)
"Implements :list-table under Oracle."
(let ((settings (sql-oracle-save-settings sqlbuf)))
"Create comint buffer and connect to Sybase."
;; Put all parameters to the program (if defined) in a list and call
;; make-comint.
- (let ((params options))
- (if (not (string= "" sql-server))
- (setq params (append (list "-S" sql-server) params)))
- (if (not (string= "" sql-database))
- (setq params (append (list "-D" sql-database) params)))
- (if (not (string= "" sql-password))
- (setq params (append (list "-P" sql-password) params)))
- (if (not (string= "" sql-user))
- (setq params (append (list "-U" sql-user) params)))
+ (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)))
\f
"Create comint buffer and connect to SQLite."
;; Put all parameters to the program (if defined) in a list and call
;; make-comint.
- (let ((params))
- (if (not (string= "" sql-database))
- (setq params (append (list (expand-file-name sql-database))
- params)))
- (setq params (append options params))
+ (let ((params
+ (append options
+ (if (not (string= "" sql-database))
+ `(,(expand-file-name sql-database))))))
(sql-comint product params)))
-(defun sql-sqlite-completion-object (sqlbuf schema)
+(defun sql-sqlite-completion-object (sqlbuf _schema)
(sql-redirect-value sqlbuf ".tables" "\\sw\\(?:\\sw\\|\\s_\\)*" 0))
\f
"Create comint buffer and connect to MySQL."
;; Put all parameters to the program (if defined) in a list and call
;; make-comint.
- (let ((params))
- (if (not (string= "" sql-database))
- (setq params (append (list sql-database) params)))
- (if (not (string= "" sql-server))
- (setq params (append (list (concat "--host=" sql-server)) params)))
- (if (not (= 0 sql-port))
- (setq params (append (list (concat "--port=" (number-to-string sql-port))) params)))
- (if (not (string= "" sql-password))
- (setq params (append (list (concat "--password=" sql-password)) params)))
- (if (not (string= "" sql-user))
- (setq params (append (list (concat "--user=" sql-user)) params)))
- (setq params (append options params))
+ (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)))
\f
"Create comint buffer and connect to Solid."
;; Put all parameters to the program (if defined) in a list and call
;; make-comint.
- (let ((params options))
- ;; It only makes sense if both username and password are there.
- (if (not (or (string= "" sql-user)
- (string= "" sql-password)))
- (setq params (append (list sql-user sql-password) params)))
- (if (not (string= "" sql-server))
- (setq params (append (list sql-server) params)))
+ (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)))
\f
"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 options))
- (if (not (string= "" sql-server))
- (setq params (append (list "-S" sql-server) params)))
- (if (not (string= "" sql-database))
- (setq params (append (list "-d" sql-database) params)))
- (if (not (string= "" sql-user))
- (setq params (append (list "-U" sql-user) params)))
- (if (not (string= "" sql-password))
- (setq params (append (list "-P" sql-password) params))
- (if (string= "" sql-user)
- ;; if neither user nor password is provided, use system
- ;; credentials.
- (setq params (append (list "-E") params))
- ;; If -P is passed to ISQL as the last argument without a
- ;; password, it's considered null.
- (setq params (append params (list "-P")))))
+ (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)))
\f
(defun sql-comint-postgres (product options)
"Create comint buffer and connect to Postgres."
- ;; username and password are ignored. Mark Stosberg suggest to add
- ;; the database at the end. Jason Beegan suggest using --pset and
+ ;; 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 options))
- (if (not (string= "" sql-database))
- (setq params (append params (list sql-database))))
- (if (not (string= "" sql-server))
- (setq params (append (list "-h" sql-server) params)))
- (if (not (string= "" sql-user))
- (setq params (append (list "-U" sql-user) params)))
- (if (not (= 0 sql-port))
- (setq params (append (list "-p" (number-to-string sql-port)) params)))
+ (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)))
(defun sql-postgres-completion-object (sqlbuf schema)
- (let (cl re fs a r)
- (sql-redirect sqlbuf "\\t on")
- (setq a (car (sql-redirect-value sqlbuf "\\a" "Output format is \\(.*\\)[.]$" 1)))
- (when (string= a "aligned")
- (sql-redirect sqlbuf "\\a"))
- (setq fs (or (car (sql-redirect-value sqlbuf "\\f" "Field separator is \"\\(.\\)[.]$" 1)) "|"))
-
- (setq re (concat "^\\([^" fs "]*\\)" fs "\\([^" fs "]*\\)" fs "[^" fs "]*" fs "[^" fs "]*$"))
- (setq cl (if (not schema)
- (sql-redirect-value sqlbuf "\\d" re '(1 2))
- (append (sql-redirect-value sqlbuf (format "\\dt %s.*" schema) re '(1 2))
- (sql-redirect-value sqlbuf (format "\\dv %s.*" schema) re '(1 2))
- (sql-redirect-value sqlbuf (format "\\ds %s.*" schema) re '(1 2)))))
-
- ;; Restore tuples and alignment to what they were
- (sql-redirect sqlbuf "\\t off")
- (when (not (string= a "aligned"))
+ (sql-redirect sqlbuf "\\t on")
+ (let ((aligned
+ (string= "aligned"
+ (car (sql-redirect-value
+ sqlbuf "\\a"
+ "Output format is \\(.*\\)[.]$" 1)))))
+ (when aligned
(sql-redirect sqlbuf "\\a"))
-
- ;; Return the list of table names (public schema name can be omitted)
- (mapcar (lambda (tbl)
- (if (string= (car tbl) "public")
- (cadr tbl)
- (format "%s.%s" (car tbl) (cadr tbl))))
- cl)))
+ (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")
+ (cadr tbl)
+ (format "%s.%s" (car tbl) (cadr tbl))))
+ cl))))
\f
"Create comint buffer and connect to Interbase."
;; Put all parameters to the program (if defined) in a list and call
;; make-comint.
- (let ((params options))
- (if (not (string= "" sql-user))
- (setq params (append (list "-u" sql-user) params)))
- (if (not (string= "" sql-password))
- (setq params (append (list "-p" sql-password) params)))
- (if (not (string= "" sql-database))
- (setq params (cons sql-database params))) ; add to the front!
+ (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)))
\f
"Create comint buffer and connect to Linter."
;; Put all parameters to the program (if defined) in a list and call
;; make-comint.
- (let ((params options)
- (login nil)
- (old-mbx (getenv "LINTER_MBX")))
- (if (not (string= "" sql-user))
- (setq login (concat sql-user "/" sql-password)))
- (setq params (append (list "-u" login) params))
- (if (not (string= "" sql-server))
- (setq params (append (list "-n" sql-server) params)))
- (if (string= "" sql-database)
- (setenv "LINTER_MBX" nil)
- (setenv "LINTER_MBX" sql-database))
- (sql-comint product params)
- (setenv "LINTER_MBX" old-mbx)))
+ (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))))
\f