Плагин для анализа планов PostgreSQL в Eclipse и DBeaver, и его разработка

от автора

Explain PostgreSQL plugin

Explain PostgreSQL plugin

Для пользователей explain.tensor.ru — нашего сервиса визуализации PostgreSQL-планов, в дополнение к плагину Jetbrains мы создали еще один — с возможностью форматировать запросы и анализировать планы в Eclipse IDE и DBeaver.


Установка плагина

В Eclipse установку удобнее выполнить через Marketplace , при этом дополнительно установятся необходимые компоненты из категории Data Tools Platform и Chromium Integration. Если в Eclipse присутствует плагин DBeaver, то к нему также добавятся функции форматирования и анализа запросов.

Установка плагина из marketplace

Установка плагина из marketplace

В отдельном приложении DBeaver надо добавить URL update-сайта с плагином. Для этого необходимо скачать файл:

curl -sLO https://explain.tensor.ru/downloads/plugins/eclipse/bookmarks.xml

импортировать его в меню Window -> Preferences -> General -> Install/Update -> Available Software Sites:

Import software sites

Import software sites

затем в меню Help -> Install New Software в списке сайтов выбрать «Explain PostgreSQL» и установить плагин «Explain PostgreSQL for DBeaver»:

Установка плагина с сайта

Установка плагина с сайта

Установка возможна также из командной строки, текст команд для разных систем можно посмотреть на сайте explain.tensor.ru.

Форматирование запроса

Форматирование доступно в перспективах Database Development и DBeaver в контекстном меню редактора SQL:

Форматирование в Database Development

Форматирование в Database Development
Форматирование в DBeaver

Форматирование в DBeaver

Анализ запроса

В Database Development в контекстном меню выбираем Get Execution Plan, при этом запрос выполнится на подключенном сервере и полученный план отправится на анализ в сервис explain.tensor.ru через публичное API , результат откроется в новом окне:

анализ в Database Development

анализ в Database Development

В DBeaver выбираем Explain Execution Plan на тулбаре или в контекстном меню Execute -> Explain Execution Plan

анализ в DBeaver

анализ в DBeaver

Настройка плагина

При необходимости можно поменять сайт настройках Window -> Preferences -> Explain PostgreSQL , например explain-postgresql.com или локальный для варианта self hosted:

настройка плагина

настройка плагина

Для Mac с процессором Apple опция использования внешнего браузера установится автоматически из-за отсутствия поддержки в Chromium Integration.

Разработка плагина

Устанавливаем Eclipse IDE for RCP and RAP Developers и создаем новый Plug-in Project без использования шаблонов и оставив все значения по умолчанию.

При этом в каталоге проекта будет создан подкаталог META-INF с файлом MANIFEST.MF. В этом файле хранится мета-информация о плагине, а также он может содержать информацию о предоставляемом коде и список необходимых для его работы плагинов.

Вообще, в Eclipse управление плагинами (регистрация, обновление и удаление, управление зависимостями между плагинами и организация их взаимодействия) выполняется по спецификации OSGi и реализуется подсистемой Equinox. В понятиях OSGi плагин является сборкой (bundle), описание которой содержится в MANIFEST.MF . Однако в этом файле содержатся только прямые зависимости — во время выполнения Eclipse активирует плагин только после загрузки всех зависимых модулей.

Другим типом взаимодействия плагинов являются точки расширения. Их описание содержится в файле plugin.xml . Плагин может добавить функционал, описав его в этом файле в декларативном виде. При этом код и данные плагина могут подгружаться только во время использования этого функционала. По сути в этом файле определяется прикладной функционал плагина. Редактировать его можно как обычный текстовый файл, но удобнее это делать с помощью Plug-in Manifest Editor— открыв в нем MANIFEST.MF и перейдя на вкладку Extensions.

Создание панелей

Для создания панелей (view) мы используем плагин org.eclise.ui , а точнее его точку расширения org.eclipse.ui.views — добавляем ее на вкладке Extensions , при этом Eclipse предложит добавить плагин org.eclipse.ui в зависимости, соглашаемся :

добавляем расширение

добавляем расширение

После добавления в контекстном меню расширения выбираем New -> view и создаем новую панель:

создаем панель

создаем панель

Здесь дополнительно укажем категорию, в которой будет отображаться панель, можно выбрать из списка, возьмем org.eclipse.datatools (Data Management)

И создаем класс ru.tensor.explain.eclipse.views.PostgresPlanView — просто пропишем имя в поле class и кликнем по нему:

создаем класс панели

создаем класс панели

Здесь оставляем все по умолчанию и в созданном классе переопределяем методы:

код класса PostgresPlanView
public class PostgresPlanView extends ViewPart { private Browser fBrowser;  @Override public void createPartControl(Composite parent) { fBrowser = new Browser(parent, SWT.NONE); fBrowser.setUrl("https://explain.tensor.ru"); }  @Override public void setFocus() { fBrowser.setFocus(); } }

Проверяем — пробуем запустить плагин:

запуск приложения с плагином

запуск приложения с плагином

выбираем Window -> Show view -> Other и в категории Data Management видим нашу панель:

Проверяем создание панели

Проверяем создание панели

Добавление панели в перспективу

Добавляем расширение org.eclipse.ui.pespectiveExtensions и в нем создаем дополнение к перспективе редактора SQL — добавляем нашу панель рядом с панелью результатов запроса:

добавление панели в перспективу редактора SQL

добавление панели в перспективу редактора SQL

Добавление команды в меню

По аналогии с панелью добавляем новые расширения org.eclipse.ui.menus и org.eclipse.ui.commands

В контекстном меню расширения commands добавляем новую команду Format SQL :

добавляем команду

добавляем команду

в контекстном меню расширения menus добавляем menuContribution с locationURI = popup:org.eclipse.ui.popup.any?before=additions и в его контексте добавляем новую команду:

добавляем команду в меню

добавляем команду в меню

locationURI позволяет выбрать место, куда будет добавлена новая команда, здесь мы добавили команду в контекстное меню перед группой команд additions , подробнее об этом в описании точки расширения:

описание точки расширения

описание точки расширения

В таком виде наша команда будет показываться в любом контекстном меню, а нам нужно показывать ее только в контекстном меню редактора SQL, для этого добавляем условие visibleWhen в котором activeEditorInput будет экземпляром (instanceof) ISQLEditorInput :

добавляем условие для меню

добавляем условие для меню

Для создания обработчика команды добавляем расширение org.eclipse.ui.handlers и в нем, по аналогии с меню, добавляем новый обработчик с условием enabledWhen :

добавляем обработчик команды

добавляем обработчик команды

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

В поле class обработчика пишем его имя и создаем новый класс с параметрами по умолчанию, реализовав метод execute , в котором форматируем текст запроса:

код класса FormatSQLhandler
public class FormatSQLhandler implements IHandler {  @Override public Object execute(ExecutionEvent event) throws ExecutionException { final IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event); final ITextEditor editor = (ITextEditor) window.getActivePage().getActiveEditor(); if (editor == null) { return null; }  String workSql = null; final boolean isSelection; if (editor.getSelectionProvider().getSelection() instanceof TextSelection) { workSql = ((TextSelection) editor.getSelectionProvider().getSelection()).getText(); } if (workSql == null || "".equals(workSql)) { workSql = editor.getDocumentProvider().getDocument(editor.getEditorInput()).get(); isSelection = false; } else { isSelection = true; }  final String sql = workSql; Job job = new Job("Explain PostgreSQL Format Thread") { @Override protected IStatus run(IProgressMonitor monitor) { window.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { try { String fmtText = ""; // get formatted text if (fmtText.startsWith("Error")) { throw new Exception (fmtText); } IDocument doc = editor.getDocumentProvider().getDocument(editor.getEditorInput()); if (isSelection) { TextSelection selection = (TextSelection) editor.getSelectionProvider().getSelection(); int offset = selection.getOffset(); int length = selection.getLength(); doc.replace(offset, length, fmtText); editor.selectAndReveal(offset, fmtText.length()); } else { doc.set(fmtText); } } catch (Exception ex) { MessageDialog.openError(window.getShell(), "Explain PostgreSQL formatter", ex.getMessage()); } } }); return Status.OK_STATUS; } }; job.setPriority(Job.SHORT); job.schedule();  return null; }    @Override public boolean isEnabled() { return true; }  @Override public boolean isHandled() { return true; }  } 

Настройки плагина

Для создания панели настроек добавляем расширение org.eclipse.ui.preferencePages и создаем в нем свою страницу:

создание страницы настроек

создание страницы настроек

При создании класса поменяем Superclass на более удобный FieldEditorPreferencePage (описание) и добавим новый тип поля URLString с валидацией введенного URL :

код класса PreferencePage
public class PreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {  @Override public void init(IWorkbench workbench) { }  @Override protected void createFieldEditors() { addField(new URLStringFieldEditor( "API_URL", "&API URL:", getFieldEditorParent() )); addField(new BooleanFieldEditor( "USE_EXTERNAL", "&Use external browser", getFieldEditorParent() )); }  } 
public class URLStringFieldEditor extends StringFieldEditor {  private int validateStrategy = VALIDATE_ON_FOCUS_LOST;  public URLStringFieldEditor(String name, String labelText, Composite parent) { super(name, labelText, parent); setEmptyStringAllowed(false); setValidateStrategy(validateStrategy); setErrorMessage("Please input valid URL"); }  @Override protected boolean doCheckState() { String text= getTextControl().getText(); if (text != null && text.length() > 0) { try { new URL(text).openStream().close(); } catch (MalformedURLException e) { return false; } catch (IOException e) { return false; } } return true; } } 

Анализ запроса

Для добавления функционала анализа запроса используем точки расширения org.eclipse.datatools.sqltools.plan.planService для Database Management и org.jkiss.dbeaver.sqlPlanView для DBeaver .

В отличие от Database Management, где весь процесс получения и обработки плана запроса отдается на сторону расширения, в DBeaver реализован свой механизм, в котором парсинг плана запроса выполняется строго в формате XML, а расширениям выдается готовый объект с одним полем Plan , остальные корневые поля плана удаляются (Planning, Planning Time, Triggers, Execution Time) :

парсинг плана в DBeaver

парсинг плана в DBeaver

Поэтому для получения полноценного плана мы добавили еще одну команду Explain Analyze вместе с Format SQL:

получение плана в DBeaver

получение плана в DBeaver

Сборка и публикация плагина

Для загрузки и установки плагинов необходимо объединить их в проекте Feature Project под общей лицензией. Для этого в меню File -> New -> Feature Project создаем новый проект и задаем перечень используемых плагинов, зависимости и лицензию. В список зависимостей надо включить все feature, которые должны быть установлены перед установкой нашего плагина.

Для нашего проекта необходимыми являются Chromium Intergration for Eclipse и Eclipse Data Tools Platform , DBeaver — опциональный:

optional dependency

optional dependency

Если создается несколько feature , то их можно объединить в категории, для этого в File -> New -> Other->Plug-in Development->Category Definition создаем category.xml с Explain PostgreSQL, включающей две feature :

создаем категорию

создаем категорию

Экспортируем наши features File -> Export -> Deployable featuresв каталог:

Создаем репозиторий на диске

Создаем репозиторий на диске

Указываем файл с категориями:

используем созданные категории

используем созданные категории

Создаем ключи, сертификаты и хранилище:

# создаем приватный ключ openssl genpkey -aes-256-cbc -algorithm RSA -pkeyopt rsa_keygen_bits:4096 | openssl rsa -out private.pem  # создаем цепочку сертификатов openssl req -key private.pem -new -x509 -days 365 -out chain.crt  # создаем архив PKCS12 openssl pkcs12 -export -in chain.crt -inkey private.pem -out store.p12 -name eclipse-plugin  # создаем java keystore keytool -importkeystore -srckeystore store.p12 -srcstoretype PKCS12 -destkeystore store.jks

Добавляем хранилище на вкладке JAR signing:

JAR Signing

JAR Signing

После завершения экспорта в каталоге /tmp/ru.tensor.explain.update будут созданы файлы content.jar , artifacts.jar и каталоги plugins и features , далее их необходимо опубликовать на своем веб-сайте и при создании страницы плагина на Eclipse Marketplace указать Update Site URL:

публикация на marketplace

публикация на marketplace

Также возможна установка из локального репозитория:

установка из локального репозитория

установка из локального репозитория

При работе с Eclipse в защищенной корпоративной среде может потребоваться установка сертификата в локальное хранилище, для этого запускаем команду, используя стандартный пароль «changeit», и перезапускаем IDE:

# для Eclipse keytool -import -trustcacerts -alias root -file <файл с сертификатом> -keystore <путь к Eclipse>/plugins/org.eclipse.justj.openjdk.hotspot.jre.<версия>/jre/lib/security/cacerts  # для DBeaver на Mac /Applications/DBeaver.app/Contents/Eclipse/jre/Contents/Home/bin/keytool -import -trustcacerts -alias root -file <файл с сертификатом> -keystore /Applications/DBEaver.app/Contents/Eclipse/jre/Contents/Home/lib/security/cacerts

Код плагина опубликован под лицензией MIT.


ссылка на оригинал статьи https://habr.com/ru/articles/789734/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *