When I type mysql dbname
at the bash prompt, I automatically connect to the database dbname
with the username
, password
, and host
information included in my .my.cnf
file.
When I use M-x sql-mysql
in emacs, I am asked for all of this information again.
Is there a way that I can get emacs sql mode to use the information in my .my.cnf
file?
This page is home of SQL mode for Emacs and XEmacs. SQL mode is part of both Emacs and XEmacs. You can get the latest versions here. The interactive SQL mode is based on ComintMode. The following interpreters are supported: Type M-x sql-help RET to read the online help. The latest version is available from Emacs’s source control:
How can I find the mySql configuration file? You can actually ask MySQL to show you the list of all locations where it searches for my.cnf (or my.ini on Windows). It is not an SQL command though.
MySQL sql_mode option defines supported SQL syntax, and data validation performed by MySQL. SET [ GLOBAL | SESSION] sql_mode=' mode1,mode2, … ' You can set sql_mode in my.cnf (Unix), my.ini (Windows), or -- sql-mode (command line)
The current maintainer of sql.el is MichaelMauger. Starting with version 21.4-a, sql.el is included in the regular sources of Emacs. The CVS version linked above depends on functions that haven’t made it out of CVS, so don’t use the latest version if you’re not running a bleeding edge Emacs from CVS.
I don't think that this is possible but you can set such stuff in the mode configuration itself:
(setq sql-connection-alist
'((pool-a
(sql-product 'mysql)
(sql-server "1.2.3.4")
(sql-user "me")
(sql-password "mypassword")
(sql-database "thedb")
(sql-port 3306))
(pool-b
(sql-product 'mysql)
(sql-server "1.2.3.4")
(sql-user "me")
(sql-password "mypassword")
(sql-database "thedb")
(sql-port 3307))))
(defun sql-connect-preset (name)
"Connect to a predefined SQL connection listed in `sql-connection-alist'"
(eval `(let ,(cdr (assoc name sql-connection-alist))
(flet ((sql-get-login (&rest what)))
(sql-product-interactive sql-product)))))
(defun sql-pool-a ()
(interactive)
(sql-connect-preset 'pool-a))
Have a look at this article for more info.
Sure it's possible. It's quite involved though.
Roughly, the steps are:
sql-connection-alist
sql-connection-alist
M-x sql-connect
(which auto-completes!)The following code also includes a .pgpass parser, just in case. You'll notice the implementation is simpler.
;;; .pgpass parser
(defun read-file (file)
"Returns file as list of lines."
(with-temp-buffer
(insert-file-contents file)
(split-string (buffer-string) "\n" t)))
(defun pgpass-to-sql-connection (config)
"Returns a suitable list for sql-connection-alist from a pgpass file."
(let ((server (lambda (host port db user _pass)
(list
(concat db ":" user ":" port ":" host)
(list 'sql-product ''postgres)
(list 'sql-server host)
(list 'sql-user user)
(list 'sql-port (string-to-number port))
(list 'sql-database db))))
(pgpass-line (lambda (line)
(apply server (split-string line ":" t)))))
(mapcar pgpass-line config)))
;;; .my.cnf parser
;;; Copied verbatim from https://github.com/daniel-ness/ini.el/blob/master/ini.el
(defun ini-decode (ini_text)
;; text -> alist
(interactive)
(if (not (stringp ini_text))
(error "Must be a string"))
(let ((lines (split-string ini_text "\n"))
(section)
(section-list)
(alist))
(dolist (l lines)
;; skip comments
(unless (or (string-match "^;" l)
(string-match "^[ \t]$" l))
;; catch sections
(if (string-match "^\\[\\(.*\\)\\]$" l)
(progn
(if section
;; add as sub-list
(setq alist (cons `(,section . ,section-list) alist))
(setq alist section-list))
(setq section (match-string 1 l))
(setq section-list nil)))
;; catch properties
(if (string-match "^\\([^\s\t]+\\)[\s\t]*=[\s\t]*\\(.+\\)$" l)
(let ((property (match-string 1 l))
(value (match-string 2 l)))
(progn
(setq section-list (cons `(,property . ,value) section-list)))))))
(if section
;; add as sub-list
(setq alist (cons `(,section . ,section-list) alist))
(setq alist section-list))
alist))
(defun read-ini (file)
"Returns ini file as alist."
(with-temp-buffer
(insert-file-contents file)
(ini-decode (buffer-string))))
(defun filter-alist (wanted-members alist)
"Returns a copy of given alist, with only fields from wanted-members."
(let ((result nil)
(add-if-member (lambda (elt)
(when (member (car elt) wanted-members)
(add-to-list 'result elt t)))))
(mapc add-if-member alist)
result))
(defun merge-alist (original override)
"Returns a union of original and override alist. On key conflict, the latter wins."
(let ((result (copy-alist override))
(add (lambda (elt)
(setq result (add-to-list
'result elt t
(lambda (left right) (equal (car left) (car right))))))))
(mapc add original)
result))
(defun parse-mycnf-hosts (file-path)
"Returns list of hosts with clients' section applied to all hosts."
(let ((hosts nil)
(global nil)
(fields '("user" "host" "database" "password" "port"))
(section-parse (lambda(section)
(if (equal (car section) "client")
(setq global (filter-alist fields (cdr section)))
(let ((host (car section))
(config (filter-alist fields (cdr section))))
(when config (add-to-list 'hosts (cons host config) t))))))
(merge-host-with-global (lambda (host)
(cons (car host) (merge-alist global (cdr host))))))
(mapc section-parse (read-ini file-path))
(mapcar merge-host-with-global hosts)))
(defun mycnf-to-sql-connection (config)
(let ((add-sql-product
(lambda (config)
(let ((head (car config))
(tail (cdr config)))
(cons head (append tail (list (list 'sql-product ''mysql)))))))
(parse-keys-and-values
(lambda (config)
(let ((head (car config))
(tail (cdr config)))
(cons
head
(mapcar
(lambda (element)
(let ((key (car element))
(value (cdr element)))
(cond ((equal key "host") (list 'sql-server value))
((equal key "port") (list 'sql-port (string-to-number value)))
((equal key "user") (list 'sql-user value))
((equal key "password") (list 'sql-password value))
((equal key "database") (list 'sql-database value))
(t (error (format "Unknown key %s" key))))))
tail))))))
(mapcar add-sql-product (mapcar parse-keys-and-values config))))
;;; Actually populating sql-connection-alist
(setq sql-connection-alist
(append
(mycnf-to-sql-connection (parse-mycnf-hosts "~/.my.cnf"))
(pgpass-to-sql-connection (read-file "~/.pgpass"))
))
With the following .my.cnf
:
[client]
user=me
[host1]
database=db1
host=db.host1.com
[host2]
database=db2
user=notme
host=db.host2.com
Executing the expression (mycnf-to-sql-connection (parse-mycnf-hosts "~/.my.cnf"))
gives me (pretty printed by hand):
(("host2" ((sql-server "db.host2.com")
(sql-user "notme")
(sql-database "db2")))
("host1" ((sql-server "db.host1.com")
(sql-database "db1")
(sql-user "me"))))
Finally, instead of using M-x sql-mysql
, use M-x sql-connect
and you'll be able to connect using the alias, with auto-completion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With