简体   繁体   English

如何让 emacs sql-mode 使用 mysql 配置文件 (.my.cnf)?

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

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.当我在 bash 提示符下键入mysql dbname时,我会自动使用.my.cnf文件中包含的usernamepasswordhost信息连接到数据库dbname

When I use Mx sql-mysql in emacs, I am asked for all of this information again.当我在 emacs 中使用Mx sql-mysql时,我再次被要求提供所有这些信息。

Is there a way that I can get emacs sql mode to use the information in my .my.cnf file?有没有办法让 emacs sql 模式使用我的.my.cnf文件中的信息?

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:大致步骤如下:

  1. Reading and parsing the ini file using ini.el .使用ini.el读取和解析 ini 文件。
  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将每个主机转换为适合sql-connection-alist格式
  4. Populating sql-connection-alist sql-connection-alist
  5. Using Mx sql-connect (which auto-completes!)使用Mx sql-connect (自动完成!)

The following code also includes a .pgpass parser, just in case.以下代码还包含一个 .pgpass 解析器,以防万一。 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 :使用以下.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):执行表达式(mycnf-to-sql-connection (parse-mycnf-hosts "~/.my.cnf"))给了我(漂亮的手工打印):

(("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 Mx sql-mysql , use Mx sql-connect and you'll be able to connect using the alias, with auto-completion.最后,不要使用Mx sql-mysql ,而是使用Mx sql-connect ,您将能够使用别名进行连接,并自动完成。

To do this we need to ""trick"" sql-mode into running mysql --login-path=some-connection-name some-db-name ( --login-path being the parameter that sql-mode does not pass by default.)为此,我们需要“欺骗” sql-mode 运行mysql --login-path=some-connection-name some-db-name--login-path是 sql-mode 不通过的参数默认。)

Create your named connections on the sql side via (by default are stored in .mylogin.cnf ):通过(默认情况下存储在.mylogin.cnf )在 sql 端创建您的命名连接:

mysql_config_editor set --login-path=wow --host=127.0.0.1 --port=3306 --user=root --password

Then label the connections on the emacs side as so (in your init.el ):然后在 emacs 端标记连接(在你的init.el ):

(setq sql-connection-alist
  '((wow-local
     (sql-product 'mysql)
     (sql-mysql-options '("--login-path=wow")) ; Note: use the login-path specified earlier
     (sql-server "") ; Note: All of these empty string parameters prevent being prompted for these values and are ignored.
     (sql-user "")
     (sql-password "")
     (sql-database "wowza"))
    (wow-local-test
     (sql-product 'mysql)
     (sql-mysql-options '("--login-path=wow")) ; Note: You can have multiple connections using the same login-path just with different parameters
     (sql-server "")
     (sql-user "")
     (sql-password "")
     (sql-database "wowza-test"))
    (production
     (sql-product 'mysql)
     (sql-mysql-options '("--login-path=production"))
     (sql-server "")
     (sql-port 0) ; Note: 0 ignores, anything else overrides your .cnf
     (sql-user "")
     (sql-password "")
     (sql-database "wowza_prod"))))

Finally with our sql connections created, and our connection labeled on the emacs side, we can run Mx sql-connect which should then prompt you to select one of your lovely connections.最后,我们创建了 sql 连接,并在 emacs 端标记了我们的连接,我们可以运行Mx sql-connect ,然后它会提示您选择一个可爱的连接。

Note:笔记:

  • setting the emacs variable sql-mysql-options allows you to use your connections as defined in the .mylogin.cnf file (YMMV on other .cnf files) (you may also use this to pass in whatever mysql parameters you'd like to)设置 emacs 变量sql-mysql-options允许您使用 .mylogin.cnf 文件中定义的连接(其他 .cnf 文件上的 YMMV)(您也可以使用它来传递您想要的任何 mysql 参数)
  • setting sql-server , sql-user , etc... to empty string prevents a prompt for said valuesql-serversql-user等设置为空字符串可防止提示输入所述值
  • setting sql-server , sql-user , etc... to anything other than empty string will override the values set in the .cnfsql-serversql-user等设置为空字符串以外的任何内容将覆盖 .cnf 中设置的值
  • See Mx (describe-variable sql-mysql-options) , and Mx (describe-function sql-comint-mysql) in the sql.el file for further details regarding logic around ignored parameters.请参阅sql.el文件中的Mx (describe-variable sql-mysql-options)Mx (describe-function sql-comint-mysql)sql.el有关忽略参数周围逻辑的更多详细信息。

TLDR: use sql-mysql-options and --login-path=<your-login-path> magic TLDR:使用sql-mysql-options--login-path=<your-login-path>魔法

只需点击返回,默认情况下它会被选中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM