¿Hay un back-end de la compañía para completar en modo interactivo SQL?

9

Estoy usando sql-interactive-modey necesito un back-end de la Compañía que complete las palabras clave de SQL, y preferiblemente, también los nombres de columna / tabla de la base de datos utilizada.

Cuando busqué alguna terminación, para mi sorpresa, todavía no había un back-end para SQL. Encontré este fragmento , pero eso no funcionó correctamente.

Es posible que aún no exista, y podría crear un propio back-end para SQL. Pero me resulta difícil creer que no haya un backend de la Compañía para uno de los idiomas más comunes.

ReneFroger
fuente
Completar palabras clave debe ser lo suficientemente simple con uno de los backends similares a dict. Los datos específicos de la base de datos son mucho más difíciles, especialmente si desea que funcionen para más de una base de datos ...
wasamasa
Si completar palabras clave sería bastante simple, ¿tiene alguna idea de por qué todavía no hay un servidor SQL para la empresa? Y estoy de acuerdo con usted en lo último, sería difícil, pero cuando tenga contenido de columna en su búfer, ¿se completará con company-dabbrev, supongo?
ReneFroger
Sería simplemente una nueva entrada company-keywords.el, ¡así que siéntase libre de contribuir! Y sí, el backend dabbrev que atraparía (y todo lo demás en su memoria intermedia) ...
wasamasa

Respuestas:

4

Tuve un problema similar y decidí crear mi propio backend. Uno de los backends existentes (C ++?) Se usó como plantilla y lo modifiqué para crear el nuevo back-end que se comporta como un diccionario.

En mi configuración, los buffers SQLi se nombran automáticamente para que coincidan con la base de datos a la que se está conectando, por ejemplo. *DB:DBASE1DM*. El backend contiene una lista para cada base de datos con esquemas, tablas y columnas. Cuando quiero completar algo, el nombre del búfer se usa para obtener la lista correcta de candidatos para esa base de datos.

(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)))

Esto tiene el inconveniente de que no es una finalización inteligente y que incluir nuevas bases de datos o realizar modificaciones en las bases de datos existentes es un proceso manual. Se pueden usar un par de consultas para recopilar los datos y luego no es muy difícil darles masajes en el formato necesario para el backend.

La siguiente función maneja la conexión a una base de datos y el cambio de los nombres de los buffers para que coincidan con la base de datos que está conectada.

(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
fuente
gracias por tu respuesta, ¡es realmente apreciado! Sin embargo, tuve algunas dificultades al probar su función. Después de añadir a la empresa con (add-to-list 'company-backends 'ry/company-sql) (add-to-list 'company-backends 'ry/company-sql-alist), tengo el siguiente error en M-x sql-mysqldespués de probar una palabra: Company: An error occurred in auto-begin Args out of range: "*SQL*", 4, -3. ¿Cómo podría interpretar este mensaje de error?
ReneFroger
He actualizado la respuesta para incluir la función que uso para conectarme a una base de datos. Se encarga de cambiar los nombres de los búferes para que coincidan con la base de datos relacionada con los búferes. Su nombre de búfer es demasiado corto, por lo que la subcadena falla. La subcadena se utiliza para eliminar el *DB:sufijo y el entorno del nombre del búfer para obtener el nombre de la base de datos, de modo que se use la lista correcta de finalizaciones. La función supone que el nombre del búfer para las terminaciones estará en el formulario *DB:ACCOUNTSDM*. La subcadena se extraería ACCOUNTSdel nombre del búfer.
Jonakand
Gracias por su respuesta. Claramente necesito aprender Lisp, ya que no pude entender cómo podría modificarlo en mysqllugar de hacerlo db2. Pero su contribución es muy apreciada, por lo que he validado su respuesta. Gracias por eso.
ReneFroger
Quizás esta respuesta debería contribuir a Emacs.
stardiviner el