Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/src/pages/en/contributing/support-new-drivers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ For an overview on how to create VScode extensions, refer to VSCode guide at [](
Now we will be updating `src/ls/driver.ts` and `src/ls/queries.ts`. Here is where the magic happens.

`src/ls/driver.ts` is where we do the code to query the desired database, for example, where you would be using `node-pg` to
guide SQLTools to get Postgres results. Please take a look in the file, there are further comments there.
guide SQLTools to get Postgres results. In order for your driver to support SQLTools features, you need to implement the methods according to the `IConnectionDriver` interface.
Please take a look in the [file](https://github.com/mtxr/vscode-sqltools/blob/dev/packages/types/index.d.ts#L287), there are further comments there.
Also you can check out the implementation of the standard drivers (`driver.mssql`, `driver.mysql`, `driver.pg`, `driver.sqlite`).

`src/ls/queries.ts` is the file where your base queries are stored. This file is not required, but having those queries split from `driver.ts`
help us to keep the code organized.
Expand Down
5 changes: 5 additions & 0 deletions packages/base-driver/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ export default abstract class AbstractDriver<ConnectionType extends any, DriverO
return Promise.resolve([]);
}

public getDefinitionForItem(_params: { item: NSDatabase.DefinableItem }): Promise<string> {
this.log.error(`###### Attention ######\getDefinitionForItem not implemented for ${this.credentials.driver}\n####################`);
return Promise.resolve("");
}

public async toAbsolutePath(fsPath: string) {
if (!path.isAbsolute(fsPath) && /\$\{workspaceFolder:(.+)}/g.test(fsPath)) {
const workspaceName = fsPath.match(/\$\{workspaceFolder:(.+)}/)[1];
Expand Down
2 changes: 1 addition & 1 deletion packages/driver.mssql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "sqltools-driver-mssql",
"displayName": "SQLTools SQL Server",
"description": "SQLTools SQL Server",
"version": "0.4.10",
"version": "0.4.11-SNAPSHOT",
"engines": {
"vscode": "^1.78.0"
},
Expand Down
113 changes: 104 additions & 9 deletions packages/driver.mssql/src/ls/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import MSSQLLib, { IResult, Binary } from 'mssql';
import * as Queries from './queries';
import AbstractDriver from '@sqltools/base-driver';
import get from 'lodash/get';
import { IConnectionDriver, NSDatabase, ContextValue, Arg0, MConnectionExplorer } from '@sqltools/types';
import { IConnectionDriver, IExpectedResult, NSDatabase, ContextValue, Arg0, MConnectionExplorer } from '@sqltools/types';
import { parse as queryParse } from '@sqltools/util/query';
import generateId from '@sqltools/util/internal-id';
import reservedWordsCompletion from './reserved-words';
Expand All @@ -11,6 +11,7 @@ export default class MSSQL extends AbstractDriver<MSSQLLib.ConnectionPool, any>
queries = Queries;

private retryCount = 0;
private SYSTEM_DATABASES = ['master', 'model', 'msdb', 'tempdb']
public async open(encryptOverride?: boolean) {
if (this.connection) {
return this.connection;
Expand Down Expand Up @@ -154,26 +155,70 @@ export default class MSSQL extends AbstractDriver<MSSQLLib.ConnectionPool, any>
switch (item.type) {
case ContextValue.CONNECTION:
case ContextValue.CONNECTED_CONNECTION:
return this.queryResults(this.queries.fetchDatabases());
case ContextValue.TABLE:
case ContextValue.VIEW:
return this.getColumns(item as NSDatabase.ITable);
const databases = await this.queryResults(this.queries.fetchDatabases());
return <MConnectionExplorer.IChildItem[]>[
{ label: 'System databases', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.DATABASE },
...databases.filter(database => !this.SYSTEM_DATABASES.includes(database.label)),
{ label: 'Instance triggers', type: ContextValue.RESOURCE_GROUP, iconId: 'server-process', childType: ContextValue.TRIGGER },
];
case ContextValue.DATABASE:
return <MConnectionExplorer.IChildItem[]>[
{ label: 'Schemas', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.SCHEMA },
{ label: 'Database triggers', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.TRIGGER },
];
case ContextValue.RESOURCE_GROUP:
return this.getChildrenForGroup({ item, parent });
case ContextValue.SCHEMA:
return <MConnectionExplorer.IChildItem[]>[
{ label: 'Tables', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.TABLE },
{ label: 'Views', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.VIEW },
// { label: 'Functions', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.FUNCTION },
{ label: 'Functions', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.FUNCTION },
{ label: 'Procedures', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.PROCEDURE },
];
case ContextValue.TABLE:
return <MConnectionExplorer.IChildItem[]>[
{ label: 'Columns', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.COLUMN },
// { label: 'Keys', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.KEY },
// { label: 'Constraints', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.CONSTRAINT },
{ label: 'Indexes', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.INDEX },
{ label: 'Triggers', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.TRIGGER },
];
case ContextValue.VIEW:
return <MConnectionExplorer.IChildItem[]>[
{ label: 'Columns', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.COLUMN },
{ label: 'Indexes', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.INDEX },
{ label: 'Triggers', type: ContextValue.RESOURCE_GROUP, iconId: 'folder', childType: ContextValue.TRIGGER },
];
case ContextValue.RESOURCE_GROUP:
return this.getChildrenForGroup({ item, parent });
}
return [];
}

public async getDefinitionForItem({ item }: Arg0<IConnectionDriver['getDefinitionForItem']>) {
let query: IExpectedResult<string>;
switch (item.type) {
case ContextValue.TABLE:
query = this.queries.fetchTableDefinition(item as NSDatabase.ITable);
break;
case ContextValue.VIEW:
query = this.queries.fetchViewDefinition(item as unknown as NSDatabase.ITable);
break;
case ContextValue.FUNCTION:
query = this.queries.fetchFunctionDefinition(item as NSDatabase.IFunction);
break;
case ContextValue.PROCEDURE:
query = this.queries.fetchProcedureDefinition(item as NSDatabase.IProcedure);
break;
case ContextValue.INDEX:
query = this.queries.fetchIndexDefinition(item as NSDatabase.IIndex);
break;
case ContextValue.TRIGGER:
query = this.queries.fetchTriggerDefinition(item as NSDatabase.ITrigger);
break;
}
const result = await this.singleQuery(query, {});
return result.results[0].definition;
}

public showRecords(table, opt) {
return this.searchItems(ContextValue.COLUMN, '', { tables: [table], limit: 1 })
.then(col => {
Expand All @@ -183,6 +228,9 @@ export default class MSSQL extends AbstractDriver<MSSQLLib.ConnectionPool, any>
}
private async getChildrenForGroup({ parent, item }: Arg0<IConnectionDriver['getChildrenForItem']>) {
switch (item.childType) {
case ContextValue.DATABASE:
const databases = await this.queryResults(this.queries.fetchDatabases());
return databases.filter(database => this.SYSTEM_DATABASES.includes(database.label));
case ContextValue.SCHEMA:
try {
const result = await this.queryResults(
Expand All @@ -197,8 +245,23 @@ export default class MSSQL extends AbstractDriver<MSSQLLib.ConnectionPool, any>
return this.queryResults(this.queries.fetchTables(parent as NSDatabase.ISchema));
case ContextValue.VIEW:
return this.queryResults(this.queries.fetchViews(parent as NSDatabase.ISchema));
case ContextValue.COLUMN:
return this.getColumns(parent as NSDatabase.ITable);
case ContextValue.FUNCTION:
return []; //this.queryResults(this.queries.fetchFunctions(parent as NSDatabase.ISchema));
return this.queryResults(this.queries.searchFunctions({search: null, parent: parent as NSDatabase.ParentItem}));
case ContextValue.PROCEDURE:
return this.queryResults(this.queries.searchProcedures({search: null, parent: parent as NSDatabase.ParentItem}));
case ContextValue.INDEX:
return this.getIndexes(parent as NSDatabase.ITable)
case ContextValue.TRIGGER:
if (parent.type === ContextValue.CONNECTION || parent.type === ContextValue.CONNECTED_CONNECTION)
return this.getTriggers();
else
return this.getTriggers(parent as (NSDatabase.IDatabase | NSDatabase.ITable));
// case ContextValue.KEY:
// return [];
// case ContextValue.CONSTRAINT:
// return [];
}
return [];
}
Expand All @@ -213,12 +276,44 @@ export default class MSSQL extends AbstractDriver<MSSQLLib.ConnectionPool, any>
}));
}

private async getIndexes(parent?: NSDatabase.ITable): Promise<NSDatabase.IIndex[]> {
const results = await this.queryResults(this.queries.searchIndexes({search: null, parent: parent as NSDatabase.ITable}));
return results.map(index => ({
...index,
childType: ContextValue.NO_CHILD,
database: parent ? parent.database : '',
schema: parent ? parent.schema : '',
parent: parent,
}));
}

private async getTriggers(parent?: NSDatabase.IDatabase | NSDatabase.ITable): Promise<NSDatabase.ITrigger[]> {
const results = await this.queryResults(this.queries.searchTriggers({search: null, parent: parent}));
return results.map(trigger => ({
...trigger,
iconId: 'symbol-event',
childType: ContextValue.NO_CHILD,
database: parent ? parent.database : '',
schema: parent ? parent.schema : '',
parent: parent ?? null,
}));
}

public searchItems(itemType: ContextValue, search: string, extraParams: any = {}): Promise<NSDatabase.SearchableItem[]> {
switch (itemType) {
case ContextValue.TABLE:
case ContextValue.VIEW:
return this.queryResults(this.queries.searchTables({ search }));
case ContextValue.COLUMN:
return this.queryResults(this.queries.searchColumns({ search, ...extraParams }));
case ContextValue.FUNCTION:
return this.queryResults(this.queries.searchFunctions({ search, ...extraParams }));
case ContextValue.PROCEDURE:
return this.queryResults(this.queries.searchProcedures({ search, ...extraParams }));
case ContextValue.INDEX:
return this.queryResults(this.queries.searchIndexes({ search, ...extraParams }));
case ContextValue.TRIGGER:
return this.queryResults(this.queries.searchTriggers({ search, ...extraParams }));
}
}

Expand Down
Loading