Existe um back-end da empresa para conclusão no modo sql-interativo?

9

Estou usando sql-interactive-modee preciso de um back-end da empresa que complete palavras-chave SQL e, de preferência, também nomes de colunas / tabelas do banco de dados usado.

Ao procurar por alguma conclusão, para minha surpresa, ainda não havia back-end para SQL. Encontrei esse trecho , mas não funcionou corretamente.

É possível que ainda não exista, e talvez eu crie um back-end para SQL. Mas acho difícil acreditar que não haja back-end da empresa para um dos idiomas mais comuns.

ReneFroger
fonte
A conclusão de palavras-chave deve ser bastante simples com um dos back-end do tipo ditado. Dados específicos do banco de dados é muito mais difícil, especialmente se você quer que ele funcione por mais de um banco de dados ...
wasamasa
iSe o preenchimento de palavras-chave for bastante simples, você tem alguma idéia de por que ainda não existe um back-end SQL para a empresa? E eu concordo com você sobre o último, seria difícil, mas quando você tem o conteúdo da coluna em seu buffer, ele seria concluído com o que company-dabbreveu acho?
ReneFroger
Seria apenas uma nova entrada para company-keywords.el, então fique à vontade para contribuir! E sim, o backend dabbrev iria pegar isso (e tudo o mais no seu buffer) ...
wasamasa

Respostas:

4

Eu tive um problema semelhante e decidi criar meu próprio back-end. Um dos back-ends existentes (C ++?) Foi usado como modelo e eu o modifiquei para criar o novo back-end que se comporta como um dicionário.

Na minha configuração, os buffers do SQLi são nomeados automaticamente para corresponder ao banco de dados ao qual está sendo conectado, por exemplo. *DB:DBASE1DM*. O back-end contém uma lista para cada banco de dados com os esquemas, tabelas e colunas. Quando eu quero concluir algo, o nome do buffer é usado para obter a lista correta de candidatos para esse banco de dados.

(defun ry/company-sql-upper-lower (&rest lst)
  (nconc (sort (mapcar 'upcase lst) 'string<) lst))

(defvar ry/company-sql-alist
  `(("DBASE1"               ;; Database name w/o environment suffix.
     "DBASE1DM" "DBASE1UM"  ;; Database name with environment suffix.
     "SCHEMA1" "SCHEMA2"
     "TABLE1" "TABLE2"
     "COLUMN1" "COLUMN2")
    ("DBASE2"
     "DBASE2DM" "DBASE2UM"
     "SCHEMA1" "SCHEMA2"
     "TABLE1" "TABLE2"
     "COLUMN1" "COLUMN2"))
    "Alist mapping sql-mode to candidates.")

(defun ry/company-sql (command &optional arg &rest ignored)
  "`company-mode' back-end for SQL mode based on database name."
  (interactive (list 'interactive))
  (cl-case command
    (interactive (company-begin-backend 'ry/company-sql))
    (prefix (and (assoc (substring (buffer-name (current-buffer)) 4 -3) ry/company-sql-alist)
                 (not (company-in-string-or-comment))
                 (or (company-grab-symbol) 'stop)))
    (candidates
     (let ((completion-ignore-case t)
           (symbols (cdr (assoc (substring (buffer-name (current-buffer)) 4 -3) ry/company-sql-alist))))       
       (all-completions arg (if (consp symbols)
                                symbols
                              (cdr (assoc symbols company-sql-alist))))))
    (sorted t)))

Isso tem a desvantagem de não ser uma conclusão inteligente e que incluir novos bancos de dados ou fazer modificações nos bancos de dados existentes é um processo manual. Algumas consultas podem ser usadas para coletar os dados e, em seguida, não é muito difícil utilizá-los no formato necessário para o back-end.

A função abaixo trata da conexão com um banco de dados e da alteração dos nomes dos buffers para corresponder ao banco de dados ao qual está conectado.

(defun ry/sql-open-database (database username password)
  "Open a SQLI process and name the SQL statement window with the name provided."
  (interactive (list
                (read-string "Database: ")
                (read-string "Username: ")
                (read-passwd "Password: ")))
  (let ((u-dbname (upcase database)))
    (setq sql-set-product "db2")

    (sql-db2 u-dbname)
    (sql-rename-buffer u-dbname)
    (setq sql-buffer (current-buffer))
    (sql-send-string (concat "CONNECT TO " database " USER " username " USING " password ";"))

    (other-window 1)
    (switch-to-buffer (concat "*DB:" u-dbname "*"))
    (sql-mode)
    (sql-set-product "db2")
    (setq sql-buffer (concat "*SQL: " u-dbname "*"))))
Jonakand
fonte
obrigado pela sua resposta, é realmente apreciado! No entanto, tive algumas dificuldades ao tentar sua função. Depois de adicionar a empresa com (add-to-list 'company-backends 'ry/company-sql) (add-to-list 'company-backends 'ry/company-sql-alist), eu tenho o seguinte erro no M-x sql-mysqldepois de tentar uma palavra: Company: An error occurred in auto-begin Args out of range: "*SQL*", 4, -3. Como eu poderia interpretar essa mensagem de erro?
ReneFroger
Atualizei a resposta para incluir a função que utilizo para conectar-me a um banco de dados. Ele lida com a alteração dos nomes dos buffers para corresponder ao banco de dados relacionado aos buffers. Seu nome do buffer é muito curto, portanto, a substring está falhando. A substring é usada para remover o *DB:sufixo e environment do nome do buffer para obter o nome do banco de dados para que a lista correta de conclusões seja usada. A função supõe que o nome do buffer para conclusões esteja no formulário *DB:ACCOUNTSDM*. A substring seria retirada ACCOUNTSdo nome do buffer.
Jonakand
Obrigado pela sua resposta. Eu claramente preciso aprender o Lisp, pois não consegui descobrir como modificá-lo em mysqlvez de db2. Como sua contribuição é realmente apreciada, validei sua resposta. Obrigado por isso.
ReneFroger
Talvez essa resposta deva contribuir para o Emacs.
Stardiviner