From f6699d824e54aac643ddb121d43fee5d44fc80c0 Mon Sep 17 00:00:00 2001 From: Vansh98789 Date: Tue, 18 Aug 2026 01:28:37 +0530 Subject: [PATCH 1/2] fix: apply KV text-index rewrite to where clause (#2927) --- .changeset/fix-sql-where-kv-textindex.md | 5 +++ .../src/__tests__/renderChartConfig.test.ts | 22 ++++++++++ .../src/core/renderChartConfig.ts | 42 +++++++++++-------- 3 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 .changeset/fix-sql-where-kv-textindex.md diff --git a/.changeset/fix-sql-where-kv-textindex.md b/.changeset/fix-sql-where-kv-textindex.md new file mode 100644 index 0000000000..759702e2dc --- /dev/null +++ b/.changeset/fix-sql-where-kv-textindex.md @@ -0,0 +1,5 @@ +--- +'@hyperdx/common-utils': patch +--- + +fix: apply KV text-index rewrite to the SQL `where` search box, not just `sql`-type filters (#2927) \ No newline at end of file diff --git a/packages/common-utils/src/__tests__/renderChartConfig.test.ts b/packages/common-utils/src/__tests__/renderChartConfig.test.ts index 71af1a2aef..a172cd7f17 100644 --- a/packages/common-utils/src/__tests__/renderChartConfig.test.ts +++ b/packages/common-utils/src/__tests__/renderChartConfig.test.ts @@ -1489,6 +1489,28 @@ describe('renderChartConfig', () => { ); expect(sql).toContain("SeverityText = 'error'"); }); + it('rewrites `Map[key] = value` in chartConfig.where when whereLanguage is sql', async () => { + stubKvItemsMetadata(); + const config: ChartConfigWithOptDateRange = { + connection: 'test-connection', + from: { databaseName: 'default', tableName: 'otel_logs' }, + select: [{ aggFn: 'count', valueExpression: '' }], + where: "LogAttributes['service.name'] = 'api'", + whereLanguage: 'sql', + timestampValueExpression: 'Timestamp', + dateRange: [new Date('2025-01-01'), new Date('2025-01-02')], + granularity: '1 minute', + }; + + const sql = parameterizedQueryToSql( + await renderChartConfig(config, mockMetadata, querySettings), + ); + + expect(sql).toContain( + "has(`LogAttributeItems`, concat('service.name', '=', 'api'))", + ); + expect(sql).not.toContain("LogAttributes['service.name'] = 'api'"); + }); }); describe('k8s semantic convention migrations', () => { diff --git a/packages/common-utils/src/core/renderChartConfig.ts b/packages/common-utils/src/core/renderChartConfig.ts index ecb9ea9e72..55eeadd6f2 100644 --- a/packages/common-utils/src/core/renderChartConfig.ts +++ b/packages/common-utils/src/core/renderChartConfig.ts @@ -1142,11 +1142,35 @@ async function renderWhere( chartConfig: BuilderChartConfigWithOptDateRangeEx, metadata: Metadata, ): Promise { + // Build the lookup first — it's needed by both the `where` block and the + // `filters` block below. + const hasSqlFilter = + chartConfig.whereLanguage === 'sql' || + (chartConfig.filters?.some(f => f.type === 'sql') ?? false) || + (typeof chartConfig.select !== 'string' && + chartConfig.select.some(s => s.aggConditionLanguage === 'sql')); + const textIndexInfoLookup: TextIndexInfoLookup = + hasSqlFilter && + chartConfig.from.databaseName && + chartConfig.from.tableName && + !hasSubqueryCte(chartConfig.with) + ? await buildTextIndexInfoLookup({ + metadata, + databaseName: chartConfig.from.databaseName, + tableName: chartConfig.from.tableName, + connectionId: chartConfig.connection, + }) + : new Map(); + let whereSearchCondition: ChSql | [] = []; if (isNonEmptyWhereExpr(chartConfig.where)) { + const rewrittenWhere = + chartConfig.whereLanguage === 'sql' + ? rewriteSqlFilterWithKvItems(chartConfig.where, textIndexInfoLookup) + : chartConfig.where; whereSearchCondition = wrapChSqlIfNotEmpty( await renderWhereExpression({ - condition: chartConfig.where, + condition: rewrittenWhere, from: chartConfig.from, language: chartConfig.whereLanguage ?? 'sql', implicitColumnExpression: chartConfig.implicitColumnExpression, @@ -1192,21 +1216,6 @@ async function renderWhere( ).filter(v => v !== null) as ChSql[]; } - const hasSqlFilter = - chartConfig.filters?.some(f => f.type === 'sql') ?? false; - const textIndexInfoLookup: TextIndexInfoLookup = - hasSqlFilter && - chartConfig.from.databaseName && - chartConfig.from.tableName && - !hasSubqueryCte(chartConfig.with) - ? await buildTextIndexInfoLookup({ - metadata, - databaseName: chartConfig.from.databaseName, - tableName: chartConfig.from.tableName, - connectionId: chartConfig.connection, - }) - : new Map(); - const filterConditions = await Promise.all( (chartConfig.filters ?? []).map(async filter => { if (filter.type === 'sql_ast') { @@ -1237,7 +1246,6 @@ async function renderWhere( ')', ); } - throw new Error(`Unknown filter type: ${filter.type}`); }), ); From e908dea370e17721ffbc4f724e0c394ac051e9b2 Mon Sep 17 00:00:00 2001 From: Vansh98789 Date: Tue, 18 Aug 2026 01:44:48 +0530 Subject: [PATCH 2/2] fix: honor default whereLanguage, drop unused aggCondition trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses review feedback on #: - hasSqlFilter and the rewrite trigger now treat an omitted whereLanguage as SQL (matching renderWhereExpression's own default), instead of only firing on an explicit whereLanguage: 'sql'. - Removed the select[].aggConditionLanguage check — aggCondition is never passed through rewriteSqlFilterWithKvItems, so it was only causing an unnecessary metadata lookup with no benefit. --- packages/common-utils/src/core/renderChartConfig.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/common-utils/src/core/renderChartConfig.ts b/packages/common-utils/src/core/renderChartConfig.ts index c0ba8c70ec..3c166c2f9c 100644 --- a/packages/common-utils/src/core/renderChartConfig.ts +++ b/packages/common-utils/src/core/renderChartConfig.ts @@ -1153,11 +1153,10 @@ async function renderWhere( ): Promise { // Build the lookup first — it's needed by both the `where` block and the // `filters` block below. - const hasSqlFilter = - chartConfig.whereLanguage === 'sql' || - (chartConfig.filters?.some(f => f.type === 'sql') ?? false) || - (typeof chartConfig.select !== 'string' && - chartConfig.select.some(s => s.aggConditionLanguage === 'sql')); + const hasSqlFilter = + (isNonEmptyWhereExpr(chartConfig.where) && + (chartConfig.whereLanguage ?? 'sql') === 'sql') || + (chartConfig.filters?.some(f => f.type === 'sql') ?? false); const textIndexInfoLookup: TextIndexInfoLookup = hasSqlFilter && chartConfig.from.databaseName && @@ -1173,8 +1172,8 @@ async function renderWhere( let whereSearchCondition: ChSql | [] = []; if (isNonEmptyWhereExpr(chartConfig.where)) { - const rewrittenWhere = - chartConfig.whereLanguage === 'sql' + const rewrittenWhere = + (chartConfig.whereLanguage ?? 'sql') === 'sql' ? rewriteSqlFilterWithKvItems(chartConfig.where, textIndexInfoLookup) : chartConfig.where; whereSearchCondition = wrapChSqlIfNotEmpty(