#mysql #emacs #sql-mode
#mysql #emacs #sql-mode
Вопрос:
Когда я набираю mysql dbname
в командной строке bash, я автоматически подключаюсь к базе данных dbname
с username
, password
и host
информацией, включенной в мой .my.cnf
файл.
Когда я использую M-x sql-mysql
в emacs, у меня снова запрашивают всю эту информацию.
Есть ли способ заставить emacs sql mode использовать информацию в моем .my.cnf
файле?
Ответ №1:
Я не думаю, что это возможно, но вы можете установить такие вещи в самой конфигурации режима:
(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 (amp;rest what)))
(sql-product-interactive sql-product)))))
(defun sql-pool-a ()
(interactive)
(sql-connect-preset 'pool-a))
Взгляните на эту статью для получения дополнительной информации.
Комментарии:
1. Поскольку
flet
макрос устарел в Emacs 24.3 , я заменил его наnoflet
, из этого пакета github.com/nicferrier/emacs-noflet , и это работает.2. Ссылка на статью кажется неработающей…
Ответ №2:
Уверен, что это возможно. Хотя это довольно сложно.
Грубо говоря, шаги следующие:
- Чтение и синтаксический анализ ini-файла с помощью ini.el.
- Объединение конфигурации разных хостов с конфигурацией клиентов, поскольку последняя применяется глобально.
- Преобразование каждого хоста в подходящий формат для
sql-connection-alist
- Заполнение
sql-connection-alist
- Используя
M-x sql-connect
(который автоматически завершается!)
Следующий код также включает в себя анализатор .pgpass, на всякий случай. Вы заметите, что реализация проще.
;;; .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 "^\([^st] \)[st]*=[st]*\(. \)$" 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"))
))
С помощью следующего .my.cnf
:
[client]
user=me
[host1]
database=db1
host=db.host1.com
[host2]
database=db2
user=notme
host=db.host2.com
Выполнение выражения (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"))))
Наконец, вместо использования M-x sql-mysql
используйте M-x sql-connect
, и вы сможете подключиться, используя псевдоним, с автоматическим завершением.
Ответ №3:
Для этого нам нужно «обмануть» sql-mode для запуска mysql --login-path=some-connection-name some-db-name
( --login-path
это параметр, который sql-mode не передает по умолчанию.)
Создайте свои именованные соединения на стороне sql через (по умолчанию хранятся в .mylogin.cnf
):
mysql_config_editor set --login-path=wow --host=127.0.0.1 --port=3306 --user=root --password
Затем обозначьте соединения на стороне emacs как so (в вашем 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"))))
Наконец, когда наши sql-соединения созданы и наше соединение помечено на стороне emacs, мы можем запустить M-x sql-connect
, который затем должен предложить вам выбрать одно из ваших любимых подключений.
Примечание:
- установка переменной emacs
sql-mysql-options
позволяет вам использовать ваши подключения, как определено в файле .mylogin.cnf (YMMV в других файлах .cnf) (вы также можете использовать это для передачи любых параметров mysql, которые вы хотите) - установка
sql-server
,sql-user
и т.д. в пустую строку предотвращает запрос указанного значения - настройка
sql-server
,sql-user
и т.д… все, что угодно, кроме пустой строки, переопределит значения, установленные в .cnf - Смотрите
M-x (describe-variable sql-mysql-options)
, иM-x (describe-function sql-comint-mysql)
вsql.el
файле для получения дополнительной информации о логике вокруг игнорируемых параметров.
TLDR: использовать sql-mysql-options
и --login-path=<your-login-path>
магию
Ответ №4:
Просто нажмите return, он будет выбран по умолчанию.