Всем привет, на связи команда Amplicode!
Один из частых вопросов, которые мы слышим от наших пользователей, связан с заменой встроенного инструмента для работы с БД, доступного ранее в IntelliJ IDEA Ultimate. В связи с тем, что DataGrip больше не доступен (а именно так называлась эта функциональность), многим разработчикам приходится искать альтернативу инструменту.
Нас эта проблема волнует не только как разработчиков Amplicode, но и как участников проекта OpenIDE. Мы считаем очень важным предоставить российским разработчикам максимально удобное и мощное решение.
В прошлом году вышла подробная статья в Spring АйО, в которой разбирались различные альтернативы Database Client. В статье были рассмотрены несколько вариантов замены. Один из них, использование клиентов вроде pgAdmin, phpMyAdmin и cloudbeaver. Сценарий работы с этими инструментами давно поддерживается Amplicode - через генерацию соответствующих сервисов в Docker Compose.
Но, положим руку на сердце, это далеко не самый удобный DevEx именно с точки зрения последующего взаимодействия с этими сервисами. Из IDE к ним не обратиться, а каждый раз открывать другое приложение – не хочется.
Конечно, в упомянутой статье борьба за пальму первенства происходит между Database Navigator (плагин к IDEA) и отдельным профессиональным инструментом DBeaver. Рассмотрим плюсы и минусы каждого из них более подробно.
Database Navigator: решение на поверхности или поверхностное решение?
Казалось бы, Database Navigator - встроенный в IDE инструмент. К БД обращаться позволяет, что еще нужно? Более того, плагин уже встроен в GigaIDE Desktop. Надо сказать, что и в маркетплейсе OpenIDE он тоже есть. Но, мы бы не стали брать на себя ответственность и рекомендовать это решение.
Плагин имеет ряд недостатков:
Нерегулярно обновляется, а поддержка осуществляется нестабильно.
Наши пользователи сообщали о низкой производительности и периодических зависаниях плагина.
Плохой UX
Нормальная подсветка синтаксиса и автокомплит работают только в SQL файлах. Если вы решите инжектировать SQL язык в внутри Java-кода, то никакого эффекта в виде автодополнений, подсказок и т.д. это не даст. Будет только ругаться на валидный синтаксис :)


Но это еще не все, мы решили провести технический аудит плагина Database Navigator, чтобы принять окончательное решение.
Мы решили посмотреть, как Database Navigator разбирает SQL код. Начали с простого SQL запроса.
select * from order_ where "sum" > 10 and customer_id = :customer_id
IntelliJ IDEA в Internal Mode позволяет изучить структуру PSI произвольного файла, чем мы и занялись. PSI (Program Structure Interface) - абстракция в IntelliJ IDEA для работы с кодом. Очень похожа на AST (абстрактное синтаксическое дерево) и даже работает поверх него.

Сразу в глаза бросается огромное количество узлов дерева для очень маленького выражения. Вообще, абсолютное большинство узлов выглядит избыточным и имеют только один подузел. Точное количество узлов - 133!
PSI дерево
FILE_ELEMENT_TYPE(0,69)
SQL_BLOCK(0,68)
iteration (00000)(0,68)
SQL_STATEMENT SCOPE_DEMARCATION SCOPE_ISOLATION(0,68)
one-of (00001)(0,68)
SELECT_STATEMENT SCOPE_ISOLATION(0,68)
SUBQUERY(0,68)
sequence (00667)(0,68)
token (00668 - KW_SELECT)(0,6)
PsiElement(KW_SELECT)('select')(0,6)
SELECT_LIST(7,8)
iteration (00677)(7,8)
one-of (00678)(7,8)
token (00679 - CHR_STAR)(7,8)
PsiElement(CHR_STAR)('*')(7,8)
FROM_CLAUSE(9,20)
token (00686 - KW_FROM)(9,13)
PsiElement(KW_FROM)('from')(9,13)
iteration (00687)(14,20)
sequence (00688)(14,20)
FROM_ITEM(14,20)
one-of (00803)(14,20)
sequence (00804)(14,20)
identifier sequence (00806)(14,20)
alias-ref dataset (00807)(14,20)
PsiElement(IDENTIFIER)('order_')(14,20)
WHERE_CLAUSE(21,68)
token (00689 - KW_WHERE)(21,26)
PsiElement(KW_WHERE)('where')(21,26)
CONDITION(27,68)
one-of (01342)(27,68)
iteration (01343)(27,68)
CONDITION(27,37)
one-of (01342)(27,37)
sequence (01344)(27,37)
EXPRESSION_LIST(27,32)
iteration (01341)(27,32)
EXPRESSION(27,32)
iteration (00870)(27,32)
sequence (00871)(27,32)
sequence (00872)(27,32)
one-of (00879)(27,32)
EXPRESSION(27,32)
iteration (00870)(27,32)
sequence (00871)(27,32)
sequence (00872)(27,32)
one-of (00879)(27,32)
EXPR(27,32)
one-of (00887)(27,32)
SIMPLE_EXPRESSION(27,32)
one-of (00918)(27,32)
sequence (00919)(27,32)
identifier sequence (00920)(27,32)
object-ref column (00924)(27,32)
PsiElement(QUOTED_IDENTIFIER)('"sum"')(27,32)
one-of (01345)(33,37)
sequence (01346)(33,37)
COMPARISON_OPERATOR(33,34)
one-of (01457)(33,34)
token (01460 - CHR_GREATER)(33,34)
PsiElement(CHR_GREATER)('>')(33,34)
one-of (01347)(35,37)
EXPRESSION_LIST(35,37)
iteration (01341)(35,37)
EXPRESSION(35,37)
iteration (00870)(35,37)
sequence (00871)(35,37)
sequence (00872)(35,37)
one-of (00879)(35,37)
EXPRESSION(35,37)
iteration (00870)(35,37)
sequence (00871)(35,37)
sequence (00872)(35,37)
one-of (00879)(35,37)
EXPR(35,37)
one-of (00887)(35,37)
SIMPLE_EXPRESSION(35,37)
one-of (00918)(35,37)
NUMBER(35,37)
one-of (01478)(35,37)
token (01480 - INTEGER)(35,37)
PsiElement(INTEGER)('10')(35,37)
token (SEPARATOR - KW_AND)(38,41)
PsiElement(KW_AND)('and')(38,41)
CONDITION(42,68)
one-of (01342)(42,68)
iteration (01343)(42,68)
CONDITION(42,68)
one-of (01342)(42,68)
sequence (01344)(42,68)
EXPRESSION_LIST(42,53)
iteration (01341)(42,53)
EXPRESSION(42,53)
iteration (00870)(42,53)
sequence (00871)(42,53)
sequence (00872)(42,53)
one-of (00879)(42,53)
EXPRESSION(42,53)
iteration (00870)(42,53)
sequence (00871)(42,53)
sequence (00872)(42,53)
one-of (00879)(42,53)
EXPR(42,53)
one-of (00887)(42,53)
SIMPLE_EXPRESSION(42,53)
one-of (00918)(42,53)
sequence (00919)(42,53)
identifier sequence (00920)(42,53)
object-ref column (00924)(42,53)
PsiElement(IDENTIFIER)('customer_id')(42,53)
one-of (01345)(54,68)
sequence (01346)(54,68)
COMPARISON_OPERATOR(54,55)
one-of (01457)(54,55)
token (01458 - CHR_EQUAL)(54,55)
PsiElement(CHR_EQUAL)('=')(54,55)
one-of (01347)(56,68)
EXPRESSION_LIST(56,68)
iteration (01341)(56,68)
EXPRESSION(56,68)
iteration (00870)(56,68)
sequence (00871)(56,68)
sequence (00872)(56,68)
one-of (00879)(56,68)
EXPRESSION(56,68)
iteration (00870)(56,68)
sequence (00871)(56,68)
sequence (00872)(56,68)
one-of (00879)(56,68)
EXPR(56,68)
one-of (00887)(56,68)
variable (00889)(56,68)
PsiElement(VARIABLE)(':customer_id')(56,68)
Беглый анализ позволил прийти к выводу, что узлы повторяют некоторые правила грамматики языка. Конкретно в этом плагине они описаны в специальных XML-документах.
Вообще, подход достаточно интересный для разбора кода. Этот xml файл переводится в иерархию маленьких парсеров, каждый из которых отвечает за свое правило. Но поддерживать описание грамматики в таком виде достаточно тяжело. Хотя есть подозрения, что они генерируются автоматически, но не понятно, на основе чего. Однозначно подтвердить или опровергнуть эту теорию не получилось. Если все это было написано руками, то автора очень жаль. Хотя, вопросы, почему языковая поддержка в этом плагине плохая, отпадают.
Еще было интересно проанализировать PSI-структуру для сложных логических выражений и цепочек арифметических операций. Результирующая структура не соответствует ассоциативности и приоритету операций. Хотя, для конечного пользователя это не проблема, лишь бы синтаксис адекватно подсвечивался (а он не).
Вообще, у такого распухшего и местами некорректного дерева PSI есть два неприятных следствия: делать какие-то интеграции с ним очень тяжело; это чревато проблемами с производительностью.
Таким образом, мы пришли к выводу, что достаточно большая часть плагина должна быть переписана с нуля, чтобы работать хорошо. А у автора, судя по всему, времени на это нет.
DBeaver: трейдофф между качеством и удобством
Многие наши пользователи уже перешли на DBeaver. Инструмент обладает рядом преимуществ:
Поддержка широкого спектра баз данных.
Возможность генерации DDL для схем и таблиц.
Поддержка ER-диаграмм и отображения структур БД.
Регулярные обновления.
Подсветка синтаксиса и код комплишн.

Однако DBeaver – отдельное приложение, и необходимость переключения между IDE и внешним инструментом может вызывать дискомфорт.
Amplicode + DBeaver = ❤️
Как обычно, хочется получить лучшие от двух миров. Мощь DBeaver и … про Database Navigator хорошое то и не скажешь ничего. Хочется как DBeaver, но чтобы DevEx получше был. И, кажется, у нас получилось!
Теперь Amplicode поддерживает интеграцию с DBeaver, предоставляя:
Отображение соединений из DBeaver непосредственно в Amplicode с удобной навигацией по структуре базы.

Полноценную подсветку синтаксиса и автокомплит SQL в sql-файлах, native queries в Spring Data JPA, queries в Spring Data JDBC, и в любых других местах, где разработчик не пожелает.
Быстрое переключение в DBeaver для вызова или анализа запроса.
И многое другое)
Обсудим вживую?
Как всегда, мы рады любому вашему фидбеку — пишите здесь, в комментариях, или присоединяйтесь к обсуждению в нашем Telegram-чате. А если хотите увидеть новую функциональность в действии и задать свои вопросы вживую – приходите на трансляцию, которая пройдёт 27 марта. Регистрируйтесь, чтобы не пропустить!