Make code editor work
This commit is contained in:
@@ -8,7 +8,7 @@ import { getSchema, showDatabases, showTables } from './MSSqlMetaQuery';
|
|||||||
import { MSSqlQueryModel } from './MSSqlQueryModel';
|
import { MSSqlQueryModel } from './MSSqlQueryModel';
|
||||||
import { MSSqlResponseParser } from './response_parser';
|
import { MSSqlResponseParser } from './response_parser';
|
||||||
import { fetchColumns, fetchTables, getSqlCompletionProvider } from './sqlCompletionProvider';
|
import { fetchColumns, fetchTables, getSqlCompletionProvider } from './sqlCompletionProvider';
|
||||||
import { getIcon, getRAQBType, toRawSql } from './sqlUtil';
|
import { getIcon, getRAQBType, SCHEMA_NAME, toRawSql } from './sqlUtil';
|
||||||
|
|
||||||
export class MssqlDatasource extends SqlDatasource {
|
export class MssqlDatasource extends SqlDatasource {
|
||||||
completionProvider: LanguageCompletionProvider | undefined = undefined;
|
completionProvider: LanguageCompletionProvider | undefined = undefined;
|
||||||
@@ -72,7 +72,7 @@ export class MssqlDatasource extends SqlDatasource {
|
|||||||
lookup: async (path?: string) => {
|
lookup: async (path?: string) => {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
const datasets = await this.fetchDatasets();
|
const datasets = await this.fetchDatasets();
|
||||||
return datasets.map((d) => ({ name: d, completion: `${d}.` }));
|
return datasets.map((d) => ({ name: d, completion: `${d}.${SCHEMA_NAME}.` }));
|
||||||
} else {
|
} else {
|
||||||
const parts = path.split('.').filter((s: string) => s);
|
const parts = path.split('.').filter((s: string) => s);
|
||||||
if (parts.length > 2) {
|
if (parts.length > 2) {
|
||||||
@@ -80,7 +80,7 @@ export class MssqlDatasource extends SqlDatasource {
|
|||||||
}
|
}
|
||||||
if (parts.length === 1) {
|
if (parts.length === 1) {
|
||||||
const tables = await this.fetchTables(parts[0]);
|
const tables = await this.fetchTables(parts[0]);
|
||||||
return tables.map((t) => ({ name: t, completion: `${t}\`` }));
|
return tables.map((t) => ({ name: t, completion: `${t}` }));
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
LanguageCompletionProvider,
|
LanguageCompletionProvider,
|
||||||
LinkedToken,
|
LinkedToken,
|
||||||
StatementPlacementProvider,
|
StatementPlacementProvider,
|
||||||
// StatementPosition,
|
|
||||||
SuggestionKindProvider,
|
SuggestionKindProvider,
|
||||||
TableDefinition,
|
TableDefinition,
|
||||||
TokenType,
|
TokenType,
|
||||||
@@ -15,10 +14,11 @@ import {
|
|||||||
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
import { AGGREGATE_FNS, OPERATORS } from 'app/features/plugins/sql/constants';
|
||||||
import { DB, SQLQuery } from 'app/features/plugins/sql/types';
|
import { DB, SQLQuery } from 'app/features/plugins/sql/types';
|
||||||
|
|
||||||
|
import { SCHEMA_NAME } from './sqlUtil';
|
||||||
|
|
||||||
interface CompletionProviderGetterArgs {
|
interface CompletionProviderGetterArgs {
|
||||||
getColumns: React.MutableRefObject<(t: SQLQuery) => Promise<ColumnDefinition[]>>;
|
getColumns: React.MutableRefObject<(t: SQLQuery) => Promise<ColumnDefinition[]>>;
|
||||||
getTables: React.MutableRefObject<(d?: string) => Promise<TableDefinition[]>>;
|
getTables: React.MutableRefObject<(d?: string) => Promise<TableDefinition[]>>;
|
||||||
// getTableSchema: React.MutableRefObject<(p: string, d: string, t: string) => Promise<TableSchema | null>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => LanguageCompletionProvider =
|
export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => LanguageCompletionProvider =
|
||||||
@@ -33,20 +33,19 @@ export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => L
|
|||||||
let processedToken = token;
|
let processedToken = token;
|
||||||
let tablePath = processedToken.value;
|
let tablePath = processedToken.value;
|
||||||
|
|
||||||
while (processedToken.next) {
|
while (processedToken.next && processedToken.next.type !== TokenType.Whitespace) {
|
||||||
tablePath += processedToken.next.value;
|
tablePath += processedToken.next.value;
|
||||||
processedToken = processedToken.next;
|
processedToken = processedToken.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tablePath;
|
const tableName = tablePath.split('.').pop();
|
||||||
|
|
||||||
|
return tableName || tablePath;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
columns: {
|
columns: {
|
||||||
resolve: async (t: string) => {
|
resolve: async (t: string) => {
|
||||||
// TODO - seems like a limitation in experimental since we may need database and table to get columns
|
|
||||||
// use . as delimiter?
|
|
||||||
// const cols = await getColumns({ table: t } as SQLQuery);
|
|
||||||
return await getColumns.current({ table: t, refId: 'A' });
|
return await getColumns.current({ table: t, refId: 'A' });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -57,58 +56,36 @@ export const getSqlCompletionProvider: (args: CompletionProviderGetterArgs) => L
|
|||||||
});
|
});
|
||||||
|
|
||||||
export enum CustomStatementPlacement {
|
export enum CustomStatementPlacement {
|
||||||
AfterDataset = 'afterDataset',
|
AfterDatabase = 'afterDatabase',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CustomSuggestionKind {
|
export enum CustomSuggestionKind {
|
||||||
TablesWithinDataset = 'tablesWithinDataset',
|
TablesWithinDatabase = 'tablesWithinDatabase',
|
||||||
Partition = 'partition',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const customStatementPlacement: StatementPlacementProvider = () => [
|
export const customStatementPlacement: StatementPlacementProvider = () => [
|
||||||
{
|
{
|
||||||
id: CustomStatementPlacement.AfterDataset,
|
id: CustomStatementPlacement.AfterDatabase,
|
||||||
resolve: (currentToken, previousKeyword) => {
|
resolve: (currentToken, previousKeyword) => {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
currentToken?.is(TokenType.Delimiter, '.') ||
|
currentToken?.is(TokenType.Delimiter, '.') ||
|
||||||
(currentToken?.is(TokenType.Whitespace) && currentToken?.previous?.is(TokenType.Delimiter, '.')) ||
|
(currentToken?.is(TokenType.Whitespace) && currentToken?.previous?.is(TokenType.Delimiter, '.')) ||
|
||||||
// (currentToken?.value === '`' && currentToken?.previous?.is(TokenType.Delimiter, '.')) ||
|
(currentToken?.isNumber() && currentToken.value.endsWith('.'))
|
||||||
(currentToken?.isNumber() && currentToken.value.endsWith('.')) // number with dot at the end like "projectname-21342."
|
|
||||||
// (currentToken?.value === '`' && isTypingTableIn(currentToken))
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO - remove - frin big query
|
|
||||||
// Overriding default behaviour of AfterFrom resolver
|
|
||||||
// {
|
|
||||||
// id: StatementPosition.AfterFrom,
|
|
||||||
// overrideDefault: true,
|
|
||||||
// resolve: (currentToken) => {
|
|
||||||
// const untilFrom = currentToken?.getPreviousUntil(TokenType.Keyword, [], 'from');
|
|
||||||
// if (!untilFrom) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// let q = '';
|
|
||||||
// for (let i = untilFrom?.length - 1; i >= 0; i--) {
|
|
||||||
// q += untilFrom[i].value;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return q.startsWith('`') && q.endsWith('`');
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const customSuggestionKinds: (
|
export const customSuggestionKinds: (
|
||||||
getTables: CompletionProviderGetterArgs['getTables'],
|
getTables: CompletionProviderGetterArgs['getTables'],
|
||||||
getFields: CompletionProviderGetterArgs['getColumns']
|
getFields: CompletionProviderGetterArgs['getColumns']
|
||||||
// getTableSchema: CompletionProviderGetterArgs['getTableSchema']
|
|
||||||
) => SuggestionKindProvider = (getTables) => () =>
|
) => SuggestionKindProvider = (getTables) => () =>
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
id: CustomSuggestionKind.TablesWithinDataset,
|
id: CustomSuggestionKind.TablesWithinDatabase,
|
||||||
applyTo: [CustomStatementPlacement.AfterDataset],
|
applyTo: [CustomStatementPlacement.AfterDatabase],
|
||||||
suggestionsResolver: async (ctx) => {
|
suggestionsResolver: async (ctx) => {
|
||||||
const tablePath = ctx.currentToken ? getTablePath(ctx.currentToken) : '';
|
const tablePath = ctx.currentToken ? getDatabaseName(ctx.currentToken) : '';
|
||||||
const t = await getTables.current(tablePath);
|
const t = await getTables.current(tablePath);
|
||||||
|
|
||||||
return t.map((table) => ({
|
return t.map((table) => ({
|
||||||
@@ -125,76 +102,25 @@ export const customSuggestionKinds: (
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// id: CustomSuggestionKind.TablesWithinDataset,
|
|
||||||
// applyTo: [StatementPosition.AfterFrom],
|
|
||||||
// suggestionsResolver: async (ctx) => {
|
|
||||||
// console.log('after from');
|
|
||||||
// const tablePath = ctx.currentToken ? getTablePath(ctx.currentToken) : '';
|
|
||||||
// const t = await getTables.current(tablePath);
|
|
||||||
|
|
||||||
// return t.map((table) => ({
|
|
||||||
// label: table.name,
|
|
||||||
// insertText: table.completion ?? table.name,
|
|
||||||
// command: { id: 'editor.action.triggerSuggest', title: '' },
|
|
||||||
// kind: CompletionItemKind.Field,
|
|
||||||
// sortText: CompletionItemPriority.High,
|
|
||||||
// range: {
|
|
||||||
// ...ctx.range,
|
|
||||||
// startColumn: ctx.range.endColumn,
|
|
||||||
// endColumn: ctx.range.endColumn,
|
|
||||||
// },
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getTablePath(token: LinkedToken) {
|
export function getDatabaseName(token: LinkedToken) {
|
||||||
let processedToken = token;
|
let processedToken = token;
|
||||||
let tablePath = '';
|
let database = '';
|
||||||
while (processedToken?.previous && !processedToken.previous.isWhiteSpace()) {
|
while (processedToken?.previous && !processedToken.previous.isWhiteSpace()) {
|
||||||
processedToken = processedToken.previous;
|
processedToken = processedToken.previous;
|
||||||
tablePath = processedToken.value + tablePath;
|
database = processedToken.value + database;
|
||||||
}
|
}
|
||||||
|
|
||||||
tablePath = tablePath.trim();
|
if (database.includes(SCHEMA_NAME)) {
|
||||||
|
database = database.replace(SCHEMA_NAME, '');
|
||||||
|
}
|
||||||
|
|
||||||
// TODO - remove - frin big query
|
database = database.trim();
|
||||||
|
|
||||||
// if (tablePath.startsWith('`')) {
|
return database;
|
||||||
// tablePath = tablePath.slice(1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (tablePath.endsWith('`')) {
|
|
||||||
// tablePath = tablePath.slice(0, -1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return tablePath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - not sure if we need this
|
|
||||||
// function isTypingTableIn(token: LinkedToken | null, l?: boolean) {
|
|
||||||
// if (!token) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// const tokens = token.getPreviousUntil(TokenType.Keyword, [], 'from');
|
|
||||||
// if (!tokens) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let path = '';
|
|
||||||
// for (let i = tokens.length - 1; i >= 0; i--) {
|
|
||||||
// path += tokens[i].value;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO - remove - frin big query
|
|
||||||
// // if (path.startsWith('`')) {
|
|
||||||
// // path = path.slice(1);
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// return path.split('.').length === 2;
|
|
||||||
// }
|
|
||||||
|
|
||||||
export async function fetchColumns(db: DB, q: SQLQuery) {
|
export async function fetchColumns(db: DB, q: SQLQuery) {
|
||||||
const cols = await db.fields(q);
|
const cols = await db.fields(q);
|
||||||
if (cols.length > 0) {
|
if (cols.length > 0) {
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ export function getRAQBType(type: string): RAQBFieldTypes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SCHEMA_NAME = 'dbo';
|
||||||
|
|
||||||
export function toRawSql({ sql, dataset, table }: SQLQuery): string {
|
export function toRawSql({ sql, dataset, table }: SQLQuery): string {
|
||||||
let rawQuery = '';
|
let rawQuery = '';
|
||||||
|
|
||||||
@@ -90,7 +92,7 @@ export function toRawSql({ sql, dataset, table }: SQLQuery): string {
|
|||||||
rawQuery += createSelectClause(sql.columns, sql.limit);
|
rawQuery += createSelectClause(sql.columns, sql.limit);
|
||||||
|
|
||||||
if (dataset && table) {
|
if (dataset && table) {
|
||||||
rawQuery += `FROM ${dataset}.dbo.${table} `;
|
rawQuery += `FROM ${dataset}.${SCHEMA_NAME}.${table} `;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sql.whereString) {
|
if (sql.whereString) {
|
||||||
|
|||||||
Reference in New Issue
Block a user