How can I get emacs sql-mode to use the mysql config file (.my.cnf)?

故事扮演 提交于 2019-12-20 21:02:17

问题


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?


回答1:


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.




回答2:


Sure it's possible. It's quite involved though.

Roughly, the steps are:

  1. Reading and parsing the ini file using ini.el.
  2. Merging the different hosts config with the clients config because the latter is applied globally.
  3. Transforming each host to a suitable format for sql-connection-alist
  4. Populating sql-connection-alist
  5. Using 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."
  (append sql-connection-alist
          (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 map-alist-keys (f alist)
  "Maps on alist's keys and returns new alist with new keys."
  (mapcar
   (lambda (elt) (list (funcall f (car elt)) (cdr elt)))
   alist))

(defun map-alist-values (f alist)
  "Maps on alist's values and returns new alist with new values."
  (mapcar
   (lambda (elt) (list (car elt) (funcall f (cdr elt))))
   alist))

(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 ((key-to-sql-connection
         (lambda (key)
           (cond ((equal key "host") 'sql-server)
                 ((equal key "user") 'sql-user)
                 ((equal key "port") 'sql-port)
                 ((equal key "database") 'sql-database)
                 ((equal key "password") 'sql-password)
                 (t (error (format "Unknown key %s" key)))))))
    (map-alist-values
     (apply-partially 'map-alist-keys key-to-sql-connection)
     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.




回答3:


Just hit return, it's picked up by default.



来源:https://stackoverflow.com/questions/5734965/how-can-i-get-emacs-sql-mode-to-use-the-mysql-config-file-my-cnf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!