From c19eeb3ce982dc3226feb1e2b89d0234db7c0689 Mon Sep 17 00:00:00 2001 From: "stefan.reusch" Date: Thu, 12 Sep 2024 21:11:41 +0200 Subject: [PATCH 1/3] Adds support for IN operator as adhoc filter --- src/data/adHocFilter.test.ts | 22 ++++++++++++++++++++++ src/data/adHocFilter.ts | 12 ++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/data/adHocFilter.test.ts b/src/data/adHocFilter.test.ts index 75e48984..b78de82b 100644 --- a/src/data/adHocFilter.test.ts +++ b/src/data/adHocFilter.test.ts @@ -152,6 +152,28 @@ describe('AdHocManager', () => { ); }); + it('apply ad hoc filter IN operator with string values', () => { + const ahm = new AdHocFilter(); + ahm.setTargetTableFromQuery('SELECT * FROM foo'); + const val = ahm.apply('SELECT stuff FROM foo WHERE col = test', [ + { key: 'key', operator: 'IN', value: '(\'val1\', \'val2\')' }, + ] as AdHocVariableFilter[]); + expect(val).toEqual( + `SELECT stuff FROM foo WHERE col = test settings additional_table_filters={'foo' : ' key IN (\\'val1\\', \\'val2\\') '}` + ); + }); + + it('apply ad hoc filter IN operator with integer values', () => { + const ahm = new AdHocFilter(); + ahm.setTargetTableFromQuery('SELECT * FROM foo'); + const val = ahm.apply('SELECT stuff FROM foo WHERE col = test', [ + { key: 'key', operator: 'IN', value: '(1, 2, 3)' }, + ] as AdHocVariableFilter[]); + expect(val).toEqual( + `SELECT stuff FROM foo WHERE col = test settings additional_table_filters={'foo' : ' key IN (1, 2, 3) '}` + ); + }); + it('does not apply an adhoc filter without "operator"', () => { const ahm = new AdHocFilter(); ahm.setTargetTableFromQuery('SELECT * FROM foo'); diff --git a/src/data/adHocFilter.ts b/src/data/adHocFilter.ts index 2a4dfc94..58457824 100644 --- a/src/data/adHocFilter.ts +++ b/src/data/adHocFilter.ts @@ -38,7 +38,7 @@ export class AdHocFilter { }) .map((f, i) => { const key = f.key.includes('.') ? f.key.split('.')[1] : f.key; - const value = `\\'${f.value}\\'`; + const value = escapeValueBasedOnOperator(f.value, f.operator); const condition = i !== adHocFilters.length - 1 ? (f.condition ? f.condition : 'AND') : ''; const operator = convertOperatorToClickHouseOperator(f.operator); return ` ${key} ${operator} ${value} ${condition}`; @@ -58,6 +58,14 @@ function isValid(filter: AdHocVariableFilter): boolean { return filter.key !== undefined && filter.operator !== undefined && filter.value !== undefined; } +function escapeValueBasedOnOperator(s: string, operator: AdHocVariableFilterOperator): string { + if (operator === 'IN') { + return `${s}`.replace(/'/g, "\\'"); + } + + return `\\'${s}\\'`; +} + function convertOperatorToClickHouseOperator(operator: AdHocVariableFilterOperator): string { if (operator === '=~') { return 'ILIKE'; @@ -68,7 +76,7 @@ function convertOperatorToClickHouseOperator(operator: AdHocVariableFilterOperat return operator; } -type AdHocVariableFilterOperator = '>' | '<' | '=' | '!=' | '=~' | '!~'; +type AdHocVariableFilterOperator = '>' | '<' | '=' | '!=' | '=~' | '!~' | 'IN'; export type AdHocVariableFilter = { key: string; From 0092145c790b87838827edb6acf8c7b2a0d6b9b3 Mon Sep 17 00:00:00 2001 From: "stefan.reusch" Date: Thu, 12 Sep 2024 22:00:18 +0200 Subject: [PATCH 2/3] Changelog entry added --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3777acb..e467226d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Added "Labels" column selector to the log query builder - Datasource OTel configuration will now set default table names for logs and traces. +- Added support for IN operator in adhoc filters ### Fixes From fb1c1e2ea64f04d6d17828e105c1058150bb7b1a Mon Sep 17 00:00:00 2001 From: "stefan.reusch" Date: Sat, 14 Sep 2024 21:29:22 +0200 Subject: [PATCH 3/3] Adds support for values without parentheses --- src/data/adHocFilter.test.ts | 11 +++++++++++ src/data/adHocFilter.ts | 11 ++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/data/adHocFilter.test.ts b/src/data/adHocFilter.test.ts index b78de82b..367804ef 100644 --- a/src/data/adHocFilter.test.ts +++ b/src/data/adHocFilter.test.ts @@ -163,6 +163,17 @@ describe('AdHocManager', () => { ); }); + it('apply ad hoc filter IN operator without parentheses', () => { + const ahm = new AdHocFilter(); + ahm.setTargetTableFromQuery('SELECT * FROM foo'); + const val = ahm.apply('SELECT stuff FROM foo WHERE col = test', [ + { key: 'key', operator: 'IN', value: '\'val1\', \'val2\'' }, + ] as AdHocVariableFilter[]); + expect(val).toEqual( + `SELECT stuff FROM foo WHERE col = test settings additional_table_filters={'foo' : ' key IN (\\'val1\\', \\'val2\\') '}` + ); + }); + it('apply ad hoc filter IN operator with integer values', () => { const ahm = new AdHocFilter(); ahm.setTargetTableFromQuery('SELECT * FROM foo'); diff --git a/src/data/adHocFilter.ts b/src/data/adHocFilter.ts index 58457824..eadb8927 100644 --- a/src/data/adHocFilter.ts +++ b/src/data/adHocFilter.ts @@ -60,10 +60,15 @@ function isValid(filter: AdHocVariableFilter): boolean { function escapeValueBasedOnOperator(s: string, operator: AdHocVariableFilterOperator): string { if (operator === 'IN') { - return `${s}`.replace(/'/g, "\\'"); - } + // Allow list of values without parentheses + if (s.length > 2 && s[0] !== '(' && s[s.length - 1] !== ')') { + s = `(${s})` + } - return `\\'${s}\\'`; + return s.replace(/'/g, "\\'"); + } else { + return `\\'${s}\\'`; + } } function convertOperatorToClickHouseOperator(operator: AdHocVariableFilterOperator): string {