diff --git a/.betterer.results b/.betterer.results index 83fec9813cb..329a99932a2 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2904,6 +2904,9 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"] ], + "public/app/features/plugins/sql/components/visual-query-builder/SQLWhereRow.tsx:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], "public/app/features/plugins/tests/datasource_srv.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], diff --git a/public/app/features/plugins/sql/components/SqlComponents.test.tsx b/public/app/features/plugins/sql/components/SqlComponents.test.tsx index 26eb3e66d67..696cf6598e7 100644 --- a/public/app/features/plugins/sql/components/SqlComponents.test.tsx +++ b/public/app/features/plugins/sql/components/SqlComponents.test.tsx @@ -2,10 +2,14 @@ import { render, waitFor } from '@testing-library/react'; import React from 'react'; import { config } from '@grafana/runtime'; +import { customBuilder } from 'app/features/variables/shared/testing/builders'; + +import { SQLExpression } from '../types'; import { DatasetSelector } from './DatasetSelector'; import { buildMockDatasetSelectorProps, buildMockTableSelectorProps } from './SqlComponents.testHelpers'; import { TableSelector } from './TableSelector'; +import { removeQuotesForMultiVariables } from './visual-query-builder/SQLWhereRow'; beforeEach(() => { config.featureToggles.sqlDatasourceDatabaseSelection = true; @@ -63,3 +67,41 @@ describe('TableSelector', () => { }); }); }); + +describe('SQLWhereRow', () => { + it('should remove quotes in a where clause including multi-value variable', () => { + const exp: SQLExpression = { + whereString: "hostname IN ('${multiHost}')", + }; + + const multiVar = customBuilder().withId('multiVar').withName('multiHost').build(); + const nonMultiVar = customBuilder().withId('nonMultiVar').withName('host').build(); + + multiVar.multi = true; + nonMultiVar.multi = false; + + const variables = [multiVar, nonMultiVar]; + + removeQuotesForMultiVariables(exp, variables); + + expect(exp.whereString).toBe('hostname IN (${multiHost})'); + }); + + it('should not remove quotes in a where clause not including a multi-value variable', () => { + const exp: SQLExpression = { + whereString: "hostname IN ('${nonMultiHost}')", + }; + + const multiVar = customBuilder().withId('multiVar').withName('multiHost').build(); + const nonMultiVar = customBuilder().withId('nonMultiVar').withName('host').build(); + + multiVar.multi = true; + nonMultiVar.multi = false; + + const variables = [multiVar, nonMultiVar]; + + removeQuotesForMultiVariables(exp, variables); + + expect(exp.whereString).toBe("hostname IN ('${nonMultiHost}')"); + }); +}); diff --git a/public/app/features/plugins/sql/components/visual-query-builder/SQLWhereRow.tsx b/public/app/features/plugins/sql/components/visual-query-builder/SQLWhereRow.tsx index 2602a485776..a4c073153e5 100644 --- a/public/app/features/plugins/sql/components/visual-query-builder/SQLWhereRow.tsx +++ b/public/app/features/plugins/sql/components/visual-query-builder/SQLWhereRow.tsx @@ -1,7 +1,8 @@ import React from 'react'; import useAsync from 'react-use/lib/useAsync'; -import { SelectableValue } from '@grafana/data'; +import { SelectableValue, VariableWithMultiSupport } from '@grafana/data'; +import { getTemplateSrv } from '@grafana/runtime'; import { QueryWithDefaults } from '../../defaults'; import { DB, SQLExpression, SQLQuery, SQLSelectableValue } from '../../types'; @@ -31,6 +32,9 @@ export function SQLWhereRow({ query, fields, onQueryChange, db }: WhereRowProps) config={{ fields: state.value || {} }} sql={query.sql!} onSqlChange={(val: SQLExpression) => { + const templateVars = getTemplateSrv().getVariables() as VariableWithMultiSupport[]; + removeQuotesForMultiVariables(val, templateVars); + onSqlChange(val); }} /> @@ -49,3 +53,13 @@ function mapFieldsToTypes(columns: SQLSelectableValue[]) { } return fields; } + +export function removeQuotesForMultiVariables(val: SQLExpression, templateVars: VariableWithMultiSupport[]) { + const multiVariableInWhereString = (tv: VariableWithMultiSupport) => + tv.multi && (val.whereString?.includes(`\${${tv.name}}`) || val.whereString?.includes(`$${tv.name}`)); + + if (templateVars.some((tv) => multiVariableInWhereString(tv))) { + val.whereString = val.whereString?.replaceAll("')", ')'); + val.whereString = val.whereString?.replaceAll("('", '('); + } +}