Про Vim " Клиент БД

Mission Impossible (c) Paramount PicturesMission Impossible © Paramount Pictures

Существует три основных способа работы с базами данных. В первую очередь, это конечно же родной графический или веб интерфейс. Который, помимо прочего, разбирается как в устройстве СУБД, так и может на лету подгружать структуру текущей БД. Вряд ли кто-то не согласен с тем, что это наиболее удобный способ составления сложных запросов и процедур на целевом диалекте SQL. Да, и сегодня речь конкретно о реляционных БД.

Второй, не менее распространенный способ, это использование, опять же, графического клиента большой комплексной IDE. Отличие заключается в том, что встроенный клиент IDE, как правило, более универсален, использует либо какую-то внешнюю библиотеку-драйвер, и тем самым может быть несколько ограничен возможностями этой библиотеки или степенью поддержки специфических функций. Надо сказать, современные IDE это отличие максимально пытаются нивелировать. Так что для каких-то не самых сложных задач такой способ тоже достаточно оправдан и популярен.

Однако есть и третий сценарий. Это работа с клиентским консольным приложением и вынесенными в файлы скриптами или исходными кодами запросов и объектов БД. Вариант относительно не частый, но он существует. Более того, с внедрением автоматизированных конвейеров развертывания приложений нынче реализуется всё чаще. Современное приложение должно быть способно не только единожды устанавливаться на статическую БД, но и автоматически разворачиваться в виртуальных средах, обновляться и контролировать собственную целостность.

До какой-то степени работать с отдельными файлами можно и при помощи графических клиентов, но это, как правило, довольно таки избыточный процесс — графические средства предназначены всё-таки больше для редактирования объектов и данных в БД «напрямую». Да, в итоге это всё-равно текстовые запросы, но средства отображения и редактирования заточены именно под визуальное взаимодействие. Скрипты же для развертывания и обновления чаще всего не интерактивные и не нуждаются в каком-то форматировании вывода. Естественным образом возникает необходимость в некоем «оперативном» редакторе, который бы запускался быстро и при этом имел какие-то минимальные средства подсветки синтаксиса, автоматического дополнения ключевых слов, форматирования и даже непосредственного исполнения и отладки.

И всеми перечисленными возможностями обладает стоковый Vim. Как было упомянуто в предыдущей заметке Vim, в числе прочего, может самостоятельно определять тип файла и загружать средства работы с SQL запросами и скриптами. Так же, при желании, можно совместить это с вызовом консольного клиента БД или просто путем вызова команд в терминале.

Тем не менее, сразу возникает закономерное желание всё-таки сделать так, что бы редактор глубже разбирался в специфике конкретного диалекта, и, может быть, даже самостоятельно подключался к конкретной БД и, например, был в курсе наличия тех или иных объектов, сущностей, примерно так же, как это делают более навороченные системы. Неудивительно, что достаточно давно такой функционал появился.

dbext

Это плагин. Заранее предвидя справедливые замечания читателей, следует оговориться, что далее по тексту и в целом по циклу я захожу за рамки какой-то минималистской конфигурации и отсылаю читателя к вводной части цикла, а так же к README проекта, где изложено что я подразумеваю под «стандартным окружением». То есть то окружение, где я обычно привык непосредственно работать с исходным кодом.

Устанавливается плагин элементарно и не требует каких-то третьих программ, дополнительных привязок и настроек.

" Database
Plug 'vim-scripts/dbext.vim'

Несмотря на кажущуюся простоту, плагин достаточно мощный, и в каком-то не самом изысканном сценарии использования может полностью закрыть все потребности в общении с БД из редактора. Более того, плагин ложится в основу других плагинов, далее развивающих идею универсального клиента для взаимодействия с СУБД. Плагин удобен при выполнения каких-то промежуточных запросов и прямого ввода команд, не отвлекаясь на внешний терминал.

Ранее упомянутый плагин dotenv.vim так же позволяет настраивать локальные профили соединения к БД для проекта путем добавления переменных окружения в файл .env. Например для соединения к стандартной Oracle строка подключения будет выглядеть следующим образом:

DATABASE_EXT="type=ORA:srvname=//localhost/orcl:user=scott:passwd=tiger"

Далее необходимо инициализировать специальную переменную g:dbext_default_profile_oracle, которая позволит выбрать подключение сразу после открытия файла.

Инициализацию переменной можно добавить как в глобальную конфигурацию при помощи автокоманды, либо как предпочитаю делать это я, поместив команду в конфигурацию специфичную для SQL файлов ~/.vim/ftplugin/sql.vim.

function! s:env(var) abort
	return exists('*DotenvGet') ? DotenvGet(a:var) : eval('$'.a:var)
endfunction

let g:dbext_default_profile_oracle = s:env('DATABASE_EXT')

После подключения к БД к словарю автодополнения добавляются имена и поля таблиц и представлений из базы данных.

98619ae9444a391fbde6c77146405f2f.png

vim-dadbod

Следующий плагин расширяет возможности dbext путем добавления многофункциональной команды :DB практически на все мыслимые сценарии использования консольного режима работы с СУБД. Он не заменяет предыдущий, назначение фактически дублируется, но в сторону небольшого увеличения функциональности.

Plug 'tpope/vim-dadbod'

В качестве первичной настройки можно прописать подключение по умолчанию. Как и в предыдущем варианте можно прописать БД для конкретного проекта в файле .env, так что бы плагин сразу понимал, откуда брать объекты для словаря.

DATABASE_URL="oracle://scott:tiger@localhost/orcl"

Следует обратить внимание, что в обоих случаях пароль от БД прописан в строке. Это, надо всегда понимать, не безопасно, особенно при работе с базами с реальными данными. С другой стороны соединение достаточно легко выполнять вручную при помощи команды :DBPromptForBufferParameters привязанной к сочетанию sbp.

Так же можно добавить инструкцию, что бы плагин брал одно из значений профиля из настроек предыдущего плагина.

" Dadbod

let g:db = "dbext:profile=oracle"

Плагин может оказаться необходимым и достаточным для повседневной несложной работы связанной с составлением скриптов, служебных запросов, но, с точки зрения разработчика, часто и этого недостаточно.

77f18fbd2ebd4a87987a6923a49edbfe.png

Первая проблема — отсутствие в словаре специфических объектов БД. Вторая — всё-таки не очень складный механизм автодополнения, плохо подстраивающийся к контексту и малопригодный для составления сложных запросов, с учетом структуры конкретной БД, и не предоставляющий доступ к таким объектам как процедуры, функции, пакеты и прочим, отсутствие которых в современных БД скорее исключение, чем правило.

В общем, цепочка такого рода рассуждений, рано или поздно, приводит к местами спорному, но фактически единственному, для Vim решению — установке Conqueror of Completion.

coc.nvim

Конечно coc.nvim не совсем про SQL и уж тем более не про данные в базе. Но ничего не мешает использовать его и в таких целях тоже. Главная особенность coc.nvim состоит в том, что он сам в свою очередь представляет из себя не цельное решение, а так же имеет модульную структуру и расширяется за счет каких-то сторонних провайдеров словарей.

Я в данном цикле не буду распинаться о всей широте функций и возможностей расширения плагина, а так его недостатках и отличиях от аналогичных решений для NeoVim типа nvim-cmp. Меня интересуют достаточно ограниченные и специфические сценарии его использования. Одним из которых является его расширение для вышеупомянутого dadbod и использование coc.nvim в качестве эдакой замены функционала «больших» систем общения с СУБД.

Надо отметить так же, что это первый плагин сильно зависящий от сторонней среды исполнения. Работает он на Node.js как бы к нему не относились что энтузиасты, что и сторонники олдсульных компилируемых платформ. (Кстати, coc.nvim является на удивление хорошим экземпляром применения Node.js).

Итак, ставим ноду (ухитряемся её запустить под вашей системой) и устанавливаем пока голый coc.nvim через менеджер плагинов. Для чего в ~/.vim/plugins добавим:

" Conqueror of completion
Plug 'neoclide/coc.nvim', {'branch': 'release'}

Перезапустим или применим новую конфигурацию (:source %) и установим плагин (:PlugInstall). Далее coc.nvim работает внутри Vim как отдельный менеджер уже собственных расширений. Но тут, в отличии от всего что я описывал ранее, понадобится довольно серьезная дополнительная настройка. Чтобы не отнимать драгоценное время ни у себя ни у читателя я отойду от собою же озвученных принципов размещения файлов конфигурации и просто добавлю еще один файл, который я просто откуда-то нашел, в директорию ~/.vim/, а в ~/.vim/plugins.vim добавлю на него ссылку.

" Coc config
source ~/.vim/coc.vim

В файле разнообразные костыли, переопределения и назначения горячих клавиш специфичных для LSP. Так же там «перегрузка» клавиши TAB делающая её поведение похожими на принятое в распространенных графических редакторах.

Но и это еще не всё. Выше были настройки только Vim относительно использования coc.nvim. А есть еще настройки самого coc.nvim относительно собственного поведения плагина и его расширений. Глобально эти настройки лежат в файле ~/.vim/coc-settings.json или локально для каждого проекта в его корне. Что порой весьма удобно. Создавать их можно как руками, так и изнутри Vim при помощи команд :CocConfig и :CocLocalConfig соответственно. Настройки по умолчанию самого coc.nvim при этом достаточно сбалансированы и наверняка подойдут для того кто установил его впервые. Далее всё придет само. И параметры отображения выпадающего меню и разнообразные более тонкие настройки. Так что можно по началу не трогать его совсем. Я, тем не менее, приложил пример, подходящий для сегодняшнего сценария.

Далее, не делая долгих промежутков, требуется установить языковое расширение, которым, собственно, и планируется пользоваться.

coc-db

Два способа: выполнить команду :ConInstall coc-db либо добавить в переменную g:coc_global_extensions соответствующую запись.

let g:coc_global_extensions = [
			\ 'coc-db',
			\]

При использовании второго способа coc.nvim при каждом запуске будет проверять этот список и автоматически устанавливать расширение, если его нет.

Это дальнейшая эволюция dadbod значительно улучшающая автодополнение путем добавления контекстного режима к той самой omnifunc упомянутой в предыдущей статье. Поэтому так же понадобится установка :ConInstall coc-omni.

Ну и вишенкой на торте — что бы это всё благообразно работало нужно чуть подшаманить ~/.vim/ftplugin/sql.vim.

set nospell

" default was , which is weird
let g:ftplugin_sql_omni_key = ''
" default dialect
let g:sql_type_default = 'plsql'
" prefer exact match
let g:completion_matching_strategy_list = ['exact', 'substring']
" do not need case sensitivity here
let g:completion_matching_ignore_case = 1
" limit suggestion list
set pumheight=20

" dotenv wrapper
function! s:env(var) abort
	return exists('*DotenvGet') ? DotenvGet(a:var) : eval('$'.a:var)
endfunction

" Dbext

" default profiles
let g:dbext_default_profile_oracle = s:env('DATABASE_EXT')

" Dadbod

let b:db = s:env('DATABASE_URL')
" completion menu label
let g:vim_dadbod_completion_mark = ''
" use lower case where possible
let g:vim_dadbod_prefer_lowercase = 1

Где первая строка выключает проверку орфографии — вряд ли она вам нужна в SQL. Вторая строка, как несложно догадаться, отвечает за диалект SQL по умолчанию.

Далее идут более интересные настройки. Во-первых, предпочтение при выборе варианта из выпадающего списка — эта опция позволяет практически всегда (субъективно) попадать по нужному варианту. Так же, думаю, мало кому надо объяснять, что для SQL запросов совершенно не обязательно соблюдать регистр объектов и процедур зачем-то добавленный разработчиком БД в коде. Есть исключения, но о них чуть позже.

Последняя общая настройка — длина (или высота) выпадающего меню. Здесь это не чистая вкусовщина — экспериментально выясняется, что слишком длинный список ощутимо отражается на производительности. Причем именно для баз данных. В реальных базах количество объектов и полей, как правило, исчисляется тысячами. Поэтому, чем меньше Vim пытается каждый раз их отображать, тем задержка меньше. При наборе высокого темпа печати это начинает становиться очевидным. Разумеется, этот показатель сильно зависит от общей производительности железа и от личных предпочтений. Чтобы читатель просто знал, что это регулируется.

В разделе «Dadbod» специфические для плагина настройки. Опция vim_dadbod_completion_mark это просто вспомогательная надпись сбоку от предлагаемого варианта. А вот последняя инструкция let g:vim_dadbod_prefer_lowercase = 1 это моя личная поделка. И о ней чуть подробнее.

Мне категорически непонято зачем до сих пор люди, с упорством, достойным лучшего применения, зажимают SHIFT (CAPS то точно уже ни кто не пользуется?) когда дело заходит об SQL. Практика выделения SQL запросов верхним регистром вымерла вместе языком Clipper еще бог знает когда. Именно в нем надо было как-то отличать ключевые слова языка и запросов. Может быть еще в каких-то допотопных языках. Но нынче то зачем это делать?

Хорошо, поля и объекты в большинстве СУБД по умолчанию переводятся в верхний регистр если специальным образом не обозначен смешанный или нижний регистр (обычно в кавычках). Но это тоже легаси поведение, сама СУБД при синтаксическом разборе не различает регистр пока её об этом, опять же, специально не попросить. В общем, что dbext, что dadbod зачем-то тоже пытаются по умолчанию превратить всё в ИСТЕРИЧЕСКИЙ КРИК. С этим я абсолютно не согласен, и решил немного подшаманить последний.

Доступен исходник пока только у меня на GitHub в отдельной ветке. Я над ним пока еще тружусь, но на сегодня можно говорить о более менее стабильной версии. По крайней мере для Oracle. (Как-раз надо бы проверить на других СУБД, но пока не дошли руки). Таким образом, если есть желание использовать последнюю опцию, и перестать «кричать» на базу, то нужно будет вручную заменить то, что установил плагин менеджер на версию по ссылке.

$ git clone https://github.com/johnrembo/vim-dadbod.git
$ cp -R vim-dadbod-completion/autoload/* ~/.config/coc/extensions/node_modules/coc-db/autoload/

Либо скопировать измененные файлы вручную из репозитория цикла.

208f25bc3d913e42afaf3d6403149914.png

Модифицированная версия пытается понять, использован ли верхний регистр принудительно или это обозначенный атавизм, и при выборе предлагаемого объекта или поля переводит его в нижний регистр. Это не имеет отношения к форматированию, потому что известные мне форматировщики могут привести регистр только по отношению к ключевым словам и стандартным функциям, остальное они оставляют в исходном регистре. Впрочем о форматировании в одной из следующих заметок (здесь так же не обошлось без собственных «запрещенных» приемчиков). Так же, конкретно, для Oracle я попытался значительно расширить перечень объектов БД, которые попадают в словарь. Это специфические для Oracle объекты и даже пакеты и процедуры с их описанием и обязательными параметрами.

При этом, на данный момент, плагин очень сильно путается при применении кавычек и при использовании псевдонимов. Впрочем, этой болячкой страдает и исходный вариант.

Осталась еще пара вещей, которые следовало бы отметить в контексте использования coc.nvim для SQL, но в этот раз статья получилось, наверное, где-то слишком подробной. Тем не менее данный кусок, на мой взгляд, не делим в том смысле, что промежуточный и недостаточно детальный вариант может испортить первое впечатление, которое потом сложно восстановить без известной степени энтузиазма. Вряд ли большинство с места в карьер решат тут же переписать готовый плагин или повозиться с тонкими настройками.

В следующей части предлагаю остановиться недолго на некоторых деталях реализации форматирования для того же Oracle SQL. Потому что готовыми средствами так же не удалось получить желаемый результат. Хотя, как и в данном случае с автодополнением, с автоформатированием я до сих пор вожусь и выглядит всё сыровато, но, по моему мнению, чуть лучше.

:write
:exit

© Habrahabr.ru