2022-07-16 04:49:24 +08:00
|
|
|
import { DataSourceInstanceSettings, ScopedVars, TimeRange } from '@grafana/data';
|
2022-11-23 17:36:07 +08:00
|
|
|
import { CompletionItemKind, LanguageDefinition, TableIdentifier } from '@grafana/experimental';
|
2022-07-16 04:49:24 +08:00
|
|
|
import { TemplateSrv } from '@grafana/runtime';
|
|
|
|
import { SqlDatasource } from 'app/features/plugins/sql/datasource/SqlDatasource';
|
2022-11-23 17:36:07 +08:00
|
|
|
import { DB, SQLQuery } from 'app/features/plugins/sql/types';
|
|
|
|
import { formatSQL } from 'app/features/plugins/sql/utils/formatSQL';
|
2022-07-16 04:49:24 +08:00
|
|
|
|
|
|
|
import MySQLQueryModel from './MySqlQueryModel';
|
|
|
|
import { mapFieldsToTypes } from './fields';
|
|
|
|
import { buildColumnQuery, buildTableQuery, showDatabases } from './mySqlMetaQuery';
|
2022-11-23 17:36:07 +08:00
|
|
|
import { getSqlCompletionProvider } from './sqlCompletionProvider';
|
2022-07-16 04:49:24 +08:00
|
|
|
import { MySQLOptions } from './types';
|
|
|
|
|
|
|
|
export class MySqlDatasource extends SqlDatasource {
|
2022-11-23 17:36:07 +08:00
|
|
|
sqlLanguageDefinition: LanguageDefinition | undefined;
|
2022-07-16 04:49:24 +08:00
|
|
|
|
|
|
|
constructor(private instanceSettings: DataSourceInstanceSettings<MySQLOptions>) {
|
|
|
|
super(instanceSettings);
|
|
|
|
}
|
|
|
|
|
|
|
|
getQueryModel(target?: Partial<SQLQuery>, templateSrv?: TemplateSrv, scopedVars?: ScopedVars): MySQLQueryModel {
|
|
|
|
return new MySQLQueryModel(target!, templateSrv, scopedVars);
|
|
|
|
}
|
|
|
|
|
2022-11-23 17:36:07 +08:00
|
|
|
getSqlLanguageDefinition(db: DB): LanguageDefinition {
|
|
|
|
if (this.sqlLanguageDefinition !== undefined) {
|
|
|
|
return this.sqlLanguageDefinition;
|
2022-07-16 04:49:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const args = {
|
2022-11-23 17:36:07 +08:00
|
|
|
getMeta: { current: (identifier?: TableIdentifier) => this.fetchMeta(identifier) },
|
2022-07-16 04:49:24 +08:00
|
|
|
};
|
2022-11-23 17:36:07 +08:00
|
|
|
this.sqlLanguageDefinition = {
|
|
|
|
id: 'sql',
|
|
|
|
completionProvider: getSqlCompletionProvider(args),
|
|
|
|
formatter: formatSQL,
|
|
|
|
};
|
|
|
|
return this.sqlLanguageDefinition;
|
2022-07-16 04:49:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async fetchDatasets(): Promise<string[]> {
|
|
|
|
const datasets = await this.runSql<string[]>(showDatabases(), { refId: 'datasets' });
|
|
|
|
return datasets.map((t) => t[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
async fetchTables(dataset?: string): Promise<string[]> {
|
|
|
|
const tables = await this.runSql<string[]>(buildTableQuery(dataset), { refId: 'tables' });
|
|
|
|
return tables.map((t) => t[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
async fetchFields(query: Partial<SQLQuery>) {
|
|
|
|
if (!query.dataset || !query.table) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
const queryString = buildColumnQuery(this.getQueryModel(query), query.table!);
|
|
|
|
const frame = await this.runSql<string[]>(queryString, { refId: 'fields' });
|
|
|
|
const fields = frame.map((f) => ({ name: f[0], text: f[0], value: f[0], type: f[1], label: f[0] }));
|
|
|
|
return mapFieldsToTypes(fields);
|
|
|
|
}
|
|
|
|
|
2022-11-23 17:36:07 +08:00
|
|
|
async fetchMeta(identifier?: TableIdentifier) {
|
2022-07-16 04:49:24 +08:00
|
|
|
const defaultDB = this.instanceSettings.jsonData.database;
|
2022-11-23 17:36:07 +08:00
|
|
|
if (!identifier?.schema && defaultDB) {
|
2022-07-16 04:49:24 +08:00
|
|
|
const tables = await this.fetchTables(defaultDB);
|
2022-11-23 17:36:07 +08:00
|
|
|
return tables.map((t) => ({ name: t, completion: `${defaultDB}.${t}`, kind: CompletionItemKind.Class }));
|
|
|
|
} else if (!identifier?.schema && !defaultDB) {
|
2022-07-16 04:49:24 +08:00
|
|
|
const datasets = await this.fetchDatasets();
|
|
|
|
return datasets.map((d) => ({ name: d, completion: `${d}.`, kind: CompletionItemKind.Module }));
|
|
|
|
} else {
|
2022-11-23 17:36:07 +08:00
|
|
|
if (!identifier?.table && !defaultDB) {
|
|
|
|
const tables = await this.fetchTables(identifier?.schema);
|
2022-07-16 04:49:24 +08:00
|
|
|
return tables.map((t) => ({ name: t, completion: t, kind: CompletionItemKind.Class }));
|
2022-11-23 17:36:07 +08:00
|
|
|
} else if (identifier?.table && identifier.schema) {
|
|
|
|
const fields = await this.fetchFields({ dataset: identifier.schema, table: identifier.table });
|
2022-07-16 04:49:24 +08:00
|
|
|
return fields.map((t) => ({ name: t.value, completion: t.value, kind: CompletionItemKind.Field }));
|
|
|
|
} else {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getDB(): DB {
|
|
|
|
if (this.db !== undefined) {
|
|
|
|
return this.db;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
datasets: () => this.fetchDatasets(),
|
|
|
|
tables: (dataset?: string) => this.fetchTables(dataset),
|
|
|
|
fields: (query: SQLQuery) => this.fetchFields(query),
|
|
|
|
validateQuery: (query: SQLQuery, range?: TimeRange) =>
|
|
|
|
Promise.resolve({ query, error: '', isError: false, isValid: true }),
|
|
|
|
dsID: () => this.id,
|
2022-11-23 17:36:07 +08:00
|
|
|
functions: () => ['VARIANCE', 'STDDEV'],
|
|
|
|
getEditorLanguageDefinition: () => this.getSqlLanguageDefinition(this.db),
|
2022-07-16 04:49:24 +08:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|