Plugin for analyzing PostgreSQL plans in VS Code, and its development
Plan analysis and PostgreSQL query formatting can be done in VS Code using explain.tensor.ru and a plugin, which will be discussed below.
Installation
To perform requests, our plugin uses another popular extension – SQLTools so first you need to configure a connection to PostgreSQL in it and then install Explain PostgreSQL from the application or command line:
code --install-extension TensorCompanyLtd.explain-postgresql
Formatting a request
In the SQL Editor context menu or in the list Code Actions
choose Format SQL
Query analysis
In the SQL Editor context menu or in the list Code Actions
choose Explain Analyze
in this case the request will be executed on the PG server (configured in SQLTools) and the resulting plan will be sent to the service for analysis explain.tensor.ru through public API the result will open in a new window
Settings
In the settings you can specify the site address and analysis options:
Development
To create the plugin framework, install the utilities according to instructions set the parameters and install the recommended extensions:
npx --package yo --package generator-code -- yo code
? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? Explain PostgreSQL
? What's the identifier of your extension? explain-postgresql
? What's the description of your extension? Analyzes EXPLAIN plan from PostgreSQL
? Initialize a git repository? Yes
? Bundle the source code with webpack? Yes
? Which package manager to use? npm
code explain-postgresql
We check – press F5 and enter the command in a new window Ctrl-Shift-P
> Hello World
a message window should appear:
The main file of the plugin is its manifesto – package.json, let’s add in it a dependency on the SQLTools plugin to install it along with our plugin:
"extensionDependencies": [
"mtxr.sqltools"
]
in the parameter activationEvents We define the condition for launching the plugin, in our case editing SQL files:
"activationEvents": [
"onLanguage:sql"
]
in the parameter contributes We are expanding the functionality of VSCode – adding commands, menu items, settings and others. For example, let's add three commands – text formatting, query plan analysis and website opening:
"commands": [
{
"command": "explain-postgresql.formatsql",
"title": "Format SQL",
"icon": "icons/EP.png"
},
{
"command": "explain-postgresql.explainanalyze",
"title": "Explain Analyze",
"icon": "icons/EP.png"
},
{
"command": "explain-postgresql.explainsite",
"title": "Explain PostgreSQL",
"icon": "icons/EP.png"
}
]
Let's add two items to the editor context menu – Format SQL
And Explain Analyze
as well as the button to open the site on the title panel, the display condition will be set to SQL language, and the group to navigation
for placement in the top and 1_modification
to place our commands first in the list:
"menus": {
"editor/title": [
{
"when": "resourceLangId == sql",
"command": "explain-postgresql.explainsite",
"group": "navigation",
"icon": "./icons/EP.png"
}
],
"editor/context": [
{
"when": "resourceLangId == sql",
"command": "explain-postgresql.formatsql",
"group": "1_modification",
"icon": "./icons/EP.png"
},
{
"when": "resourceLangId == sql",
"command": "explain-postgresql.explainanalyze",
"group": "1_modification",
"icon": "./icons/EP.png"
}
]
}
We store the plugin settings as an object, for example the site URL:
"configuration": {
"type": "object",
"title": "Explain PostgreSQL",
"properties": {
"explain-postgresql.url": {
"scope": "resource",
"type": "string",
"default": "https://explain.tensor.ru",
"description": "API site",
"order": 1
}
}
}
To access the settings we use the function:
const url = vscode.workspace.getConfiguration('explain-postgresql').get('url');
In the parameter main
manifest specifies the entry point to the plugin file extension.js which exports the input function activate
. It receives as a parameter context which stores the private parameters of the plugin, for example the path to its directory extensionUri which we use to get the path to the panel icon:
panel.iconPath = vscode.Uri.joinPath(Context.extensionUri, "icons/EP.png");
In this function we register the commands that were specified in the manifest:
vscode.commands.registerCommand('explain-postgresql.formatsql', formatCommand);
vscode.commands.registerCommand('explain-postgresql.explainanalyze', explainCommand);
vscode.commands.registerCommand('explain-postgresql.explainsite', explainsiteCommand);
In the formatting command code, we take the selected area or the entire text and format it using API explain.tensor.ru and replace the text:
export async function formatCommand(): Promise<void> {
let editor = vscode.window.activeTextEditor;
if (editor) {
let validFullRange: vscode.Range;
let selection = editor.selection;
let text = editor.document.getText(selection);
if (!text) {
text = editor.document.getText();
let invalidRange = new vscode.Range(0, 0, editor.document.lineCount, 0);
validFullRange = editor.document.validateRange(invalidRange);
}
try {
let formatted = await beautifier(text); // получаем форматированный текст
editor.edit(editBuilder => {
editBuilder.replace(validFullRange || selection, formatted);
})
} catch (e) {
vscode.window.showErrorMessage(`${e}`, {modal: true});
}
}
}
In the query analysis command, we also take the query text from the current editor and add EXPLAIN to it with a list of options specified in the plugin settings.
Then we run the command executeQuery
from the plugin SQLToolswhich will execute the query on the connected server and return the query plan.
We send the request text along with its execution plan for analysis to API explain.tensor.ru and open the result in a new panel:
export async function explainCommand(): Promise<void> {
let editor = vscode.window.activeTextEditor;
let selection = editor.selection;
let query = editor.document.getText(selection);
if (!query) {
query = editor.document.getText();
}
const explainSettings = vscode.workspace.getConfiguration('explain-postgresql').get('explain');
let options = Object.keys(explainSettings).filter(key => explainSettings[key]).join(',');
let plan = await vscode.commands.executeCommand(`sqltools.executeQuery`, `EXPLAIN (${options}) ${query}`);
try {
let url = await explain(plan, query); // отправляем план на анализ в API explain
const apiUrl = vscode.workspace.getConfiguration('explain-postgresql').get('url');
await createView(`${apiUrl}${url}`);
} catch(e) {
vscode.window.showErrorMessage(`${e}`, {modal: true});
}
}
To create a panel with analysis results, use Webviewwhich is actually an iframe element so we can place our HTML in it.
Let's add our own iframe to the panel, in the src attribute we indicate the URL of the document with the analysis result:
async function getHtml (url: string) {
return `<!DOCTYPE html><html><head></head>
<body style="margin:0px;padding:0px;overflow:hidden">
<div>
<iframe sandbox="allow-scripts allow-forms allow-same-origin allow-downloads" src=${url} style="width:100vw;height:100vh;border: none;display: block;"></iframe>
</div>
</body></html>`;
}
export default async function createView(url: string) {
let panel = vscode.window.createWebviewPanel(
'explainAnalyze',
'Explain Analyze',
vscode.ViewColumn.Active,
{
enableScripts: true,
retainContextWhenHidden: true,
}
);
panel.webview.html = getHtml(url);
panel.iconPath = vscode.Uri.joinPath(Context.extensionUri, "icons/EP.png");
}
For creating Code Actions
we use CodeActionProvider :
export class Actions implements vscode.CodeActionProvider {
public static readonly providedCodeActionKinds = [
vscode.CodeActionKind.Refactor,
];
provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
const commandFormatSQL = this.createCommand('explain-postgresql.formatsql', "Format SQL", "Formatting SQL text");
const commandExplainAnalyze = this.createCommand('explain-postgresql.explainanalyze', "Explain Analyze", "Explain query plan");
return [
commandFormatSQL,
commandExplainAnalyze,
];
}
private createCommand(command: string, title: string, tooltip: string): vscode.CodeAction {
const action = new vscode.CodeAction(title, vscode.CodeActionKind.Refactor);
action.command = {command, title, tooltip};
return action;
}
}
register it in a function activate
along with the commands:
vscode.languages.registerCodeActionsProvider('sql', new Actions(), {
providedCodeActionKinds: Actions.providedCodeActionKinds
})
To create a button on the status panel, create a new one StatusBarItem also in activate
:
const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10);
statusBarItem.command = 'explain-postgresql.explainanalyze';
statusBarItem.text="Explain Analyze";
statusBarItem.show();
Debugging
To debug panels, it is convenient to use the built-in WebView Developer Tools:
Building and publishing the plugin
To build and publish the plugin, install the utility vsce
:
npm install -g @vscode/vsce
Specify the version in the parameter version
V package.json and start the build:
vsce package
Packaged: explain-postgresql/explain-postgresql-1.0.5.vsix (12 files, 319.64KB)
VS Code Extension Marketplace uses Azure DevOps and the utility vsce
publishes only using personal tokens of this service, so to receive it we register on dev.azure.com and create a new organization:
In the organization settings, create a new token:
Copy the created token and go to the site Visual Studio Marketplace publisher management page with the same login that was used to create the token, and create a publication:
When creating, you will need to specify a unique ID, which we will write in package.json in the parameter publisher
:
"publisher": "TensorCompanyLtd"
Checking the publication from the utility vsce
using the generated token:
vsce login TensorCompanyLtd
https://marketplace.visualstudio.com/manage/publishers/
Personal Access Token for publisher 'TensorCompanyLtd': ****************************************************
The Personal Access Token verification succeeded for the publisher 'TensorCompanyLtd'.
And we publish our plugin:
vsce publish
We check – the plugin should appear on the site Visual Studio Marketplace publisher management page and after a few minutes in the list of extensions in VSCode
Plugin code published under the MIT license.