import _ from 'lodash'; import { getBackendSrv } from '@grafana/runtime'; import { compactQuery } from '../../../utils'; import mysql from './mysql'; import postgres from './postgres'; import dbConnector, { DBConnector, DEFAULT_QUERY_LIMIT, HISTORY_TO_TABLE_MAP, TREND_TO_TABLE_MAP } from '../dbConnector'; const supportedDatabases = { mysql: 'mysql', postgres: 'postgres' }; export class SQLConnector extends DBConnector { constructor(options) { super(options); this.limit = options.limit || DEFAULT_QUERY_LIMIT; this.sqlDialect = null; super.loadDBDataSource() .then(() => { this.loadSQLDialect(); }); } loadSQLDialect() { if (this.datasourceTypeId === supportedDatabases.postgres) { this.sqlDialect = postgres; } else { this.sqlDialect = mysql; } } /** * Try to invoke test query for one of Zabbix database tables. */ testDataSource() { let testQuery = this.sqlDialect.testQuery(); return this.invokeSQLQuery(testQuery); } getHistory(items, timeFrom, timeTill, options) { let {intervalMs, consolidateBy} = options; let intervalSec = Math.ceil(intervalMs / 1000); // The interval must match the time range exactly n times, otherwise // the resulting first and last data points will yield invalid values in the // calculated average value in downsampleSeries - when using consolidateBy(avg) let numOfIntervals = Math.ceil((timeTill - timeFrom) / intervalSec); intervalSec = (timeTill - timeFrom) / numOfIntervals; consolidateBy = consolidateBy || 'avg'; let aggFunction = dbConnector.consolidateByFunc[consolidateBy]; // Group items by value type and perform request for each value type let grouped_items = _.groupBy(items, 'value_type'); let promises = _.map(grouped_items, (items, value_type) => { let itemids = _.map(items, 'itemid').join(', '); let table = HISTORY_TO_TABLE_MAP[value_type]; let query = this.sqlDialect.historyQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction); query = compactQuery(query); return this.invokeSQLQuery(query); }); return Promise.all(promises).then(results => { return _.flatten(results); }); } getTrends(items, timeFrom, timeTill, options) { let { intervalMs, consolidateBy } = options; let intervalSec = Math.ceil(intervalMs / 1000); // The interval must match the time range exactly n times, otherwise // the resulting first and last data points will yield invalid values in the // calculated average value in downsampleSeries - when using consolidateBy(avg) let numOfIntervals = Math.ceil((timeTill - timeFrom) / intervalSec); intervalSec = (timeTill - timeFrom) / numOfIntervals; consolidateBy = consolidateBy || 'avg'; let aggFunction = dbConnector.consolidateByFunc[consolidateBy]; // Group items by value type and perform request for each value type let grouped_items = _.groupBy(items, 'value_type'); let promises = _.map(grouped_items, (items, value_type) => { let itemids = _.map(items, 'itemid').join(', '); let table = TREND_TO_TABLE_MAP[value_type]; let valueColumn = _.includes(['avg', 'min', 'max', 'sum'], consolidateBy) ? consolidateBy : 'avg'; valueColumn = dbConnector.consolidateByTrendColumns[valueColumn]; let query = this.sqlDialect.trendsQuery(itemids, table, timeFrom, timeTill, intervalSec, aggFunction, valueColumn); query = compactQuery(query); return this.invokeSQLQuery(query); }); return Promise.all(promises).then(results => { return _.flatten(results); }); } invokeSQLQuery(query) { let queryDef = { refId: 'A', format: 'time_series', datasourceId: this.datasourceId, rawSql: query, maxDataPoints: this.limit }; return getBackendSrv().datasourceRequest({ url: '/api/tsdb/query', method: 'POST', data: { queries: [queryDef], } }) .then(response => { let results = response.data.results; if (results['A']) { return results['A'].series; } else { return null; } }); } }