InfluxDB: Fix sql query generation by adding quotes around the identifiers (#83765)

* Quote the identifiers

* wrap where filter with quotes

* fix query generation
This commit is contained in:
ismail simsek 2024-03-05 11:22:33 +01:00 committed by GitHub
parent bc7eacfcbd
commit dc4c539d46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 13 deletions

View File

@ -1,12 +1,12 @@
import { DataSourceInstanceSettings, TimeRange } from '@grafana/data';
import { CompletionItemKind, LanguageDefinition, TableIdentifier } from '@grafana/experimental';
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
import { DB, SqlDatasource, SQLQuery, formatSQL } from '@grafana/sql';
import { DB, formatSQL, SqlDatasource, SQLQuery } from '@grafana/sql';
import { mapFieldsToTypes } from './fields';
import { buildColumnQuery, buildTableQuery } from './flightsqlMetaQuery';
import { getSqlCompletionProvider } from './sqlCompletionProvider';
import { quoteLiteral, quoteIdentifierIfNecessary, toRawSql } from './sqlUtil';
import { quoteIdentifierIfNecessary, quoteLiteral, toRawSql } from './sqlUtil';
import { FlightSQLOptions } from './types';
export class FlightSQLDatasource extends SqlDatasource {

View File

@ -1,10 +1,10 @@
import { SQLQuery, QueryEditorExpressionType } from '@grafana/sql';
import { QueryEditorExpressionType, SQLQuery } from '@grafana/sql';
import { toRawSql } from './sqlUtil';
describe('toRawSql', () => {
it('should render sql properly', () => {
const expected = 'SELECT host FROM iox.value1 WHERE time >= $__timeFrom AND time <= $__timeTo LIMIT 50';
const expected = 'SELECT "host" FROM "value1" WHERE "time" >= $__timeFrom AND "time" <= $__timeTo LIMIT 50';
const testQuery: SQLQuery = {
refId: 'A',
sql: {
@ -27,4 +27,55 @@ describe('toRawSql', () => {
const result = toRawSql(testQuery);
expect(result).toEqual(expected);
});
it('should wrap the identifiers with quote', () => {
const expected = 'SELECT "host" FROM "TestValue" WHERE "time" >= $__timeFrom AND "time" <= $__timeTo LIMIT 50';
const testQuery: SQLQuery = {
refId: 'A',
sql: {
limit: 50,
columns: [
{
parameters: [
{
name: 'host',
type: QueryEditorExpressionType.FunctionParameter,
},
],
type: QueryEditorExpressionType.Function,
},
],
},
dataset: 'iox',
table: 'TestValue',
};
const result = toRawSql(testQuery);
expect(result).toEqual(expected);
});
it('should wrap filters in where', () => {
const expected = `SELECT "host" FROM "TestValue" WHERE "time" >= $__timeFrom AND "time" <= $__timeTo AND ("sensor_id" = '12' AND "sensor_id" = '23') LIMIT 50`;
const testQuery: SQLQuery = {
refId: 'A',
sql: {
limit: 50,
columns: [
{
parameters: [
{
name: 'host',
type: QueryEditorExpressionType.FunctionParameter,
},
],
type: QueryEditorExpressionType.Function,
},
],
whereString: `(sensor_id = '12' AND sensor_id = '23')`,
},
dataset: 'iox',
table: 'TestValue',
};
const result = toRawSql(testQuery);
expect(result).toEqual(expected);
});
});

View File

@ -1,6 +1,6 @@
import { isEmpty } from 'lodash';
import { SQLQuery, createSelectClause, haveColumns } from '@grafana/sql';
import { createSelectClause, haveColumns, SQLQuery } from '@grafana/sql';
// remove identifier quoting from identifier to use in metadata queries
export function unquoteIdentifier(value: string) {
@ -17,7 +17,7 @@ export function quoteLiteral(value: string) {
return "'" + value.replace(/'/g, "''") + "'";
}
export function toRawSql({ sql, dataset, table }: SQLQuery): string {
export function toRawSql({ sql, table }: SQLQuery): string {
let rawQuery = '';
// Return early with empty string if there is no sql column
@ -25,25 +25,33 @@ export function toRawSql({ sql, dataset, table }: SQLQuery): string {
return rawQuery;
}
rawQuery += createSelectClause(sql.columns);
// wrapping the column name with quotes
const sc = sql.columns.map((c) => ({ ...c, parameters: c.parameters?.map((p) => ({ ...p, name: `"${p.name}"` })) }));
rawQuery += createSelectClause(sc);
if (dataset && table) {
rawQuery += `FROM ${dataset}.${table} `;
if (table) {
rawQuery += `FROM "${table}" `;
}
// $__timeFrom and $__timeTo will be interpolated on the backend
rawQuery += `WHERE time >= $__timeFrom AND time <= $__timeTo `;
rawQuery += `WHERE "time" >= $__timeFrom AND "time" <= $__timeTo `;
if (sql.whereString) {
rawQuery += `AND ${sql.whereString} `;
// whereString is generated by the react-awesome-query-builder
// we use SQLWhereRow as a common component
// in order to not mess with common component here we just modify the string
const wherePattern = new RegExp('(\\s?)([^\\(]\\S+)(\\s?=)', 'g');
const subst = `$1"$2"$3`;
const whereString = sql.whereString.replace(wherePattern, subst);
rawQuery += `AND ${whereString} `;
}
if (sql.groupBy?.[0]?.property.name) {
const groupBy = sql.groupBy.map((g) => g.property.name).filter((g) => !isEmpty(g));
const groupBy = sql.groupBy.map((g) => `"${g.property.name}"`).filter((g) => !isEmpty(g));
rawQuery += `GROUP BY ${groupBy.join(', ')} `;
}
if (sql.orderBy?.property.name) {
rawQuery += `ORDER BY ${sql.orderBy.property.name} `;
rawQuery += `ORDER BY "${sql.orderBy.property.name}" `;
}
if (sql.orderBy?.property.name && sql.orderByDirection) {