From dc4c539d4626fa96a475401c1bc69d698775b494 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Tue, 5 Mar 2024 11:22:33 +0100 Subject: [PATCH] InfluxDB: Fix sql query generation by adding quotes around the identifiers (#83765) * Quote the identifiers * wrap where filter with quotes * fix query generation --- .../influxdb/fsql/datasource.flightsql.ts | 4 +- .../datasource/influxdb/fsql/sqlUtil.test.ts | 55 ++++++++++++++++++- .../datasource/influxdb/fsql/sqlUtil.ts | 26 ++++++--- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts b/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts index 476df864d0a..18c881e2dbb 100644 --- a/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts +++ b/public/app/plugins/datasource/influxdb/fsql/datasource.flightsql.ts @@ -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 { diff --git a/public/app/plugins/datasource/influxdb/fsql/sqlUtil.test.ts b/public/app/plugins/datasource/influxdb/fsql/sqlUtil.test.ts index 57fe15c6709..4864004b8ec 100644 --- a/public/app/plugins/datasource/influxdb/fsql/sqlUtil.test.ts +++ b/public/app/plugins/datasource/influxdb/fsql/sqlUtil.test.ts @@ -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); + }); }); diff --git a/public/app/plugins/datasource/influxdb/fsql/sqlUtil.ts b/public/app/plugins/datasource/influxdb/fsql/sqlUtil.ts index 81ad2673762..41317454a95 100644 --- a/public/app/plugins/datasource/influxdb/fsql/sqlUtil.ts +++ b/public/app/plugins/datasource/influxdb/fsql/sqlUtil.ts @@ -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) {