From 54bf035477c9d58f62bb14fedabbdbaab97b6706 Mon Sep 17 00:00:00 2001 From: Jordi Enric Date: Wed, 24 Jun 2026 11:00:38 +0200 Subject: [PATCH] feat(studio): deterministic BigQuery->ClickHouse rewrite for Logs Explorer Add rewriteBqLogsSqlToClickhouse: parses the user's BigQuery logs query with the polyglot SDK, rewrites it onto the OTEL logs schema on the AST (FROM logs + source filter, drop unnest joins, nested fields -> log_attributes[...] with numeric casts), and regenerates ClickHouse SQL. The banner's button now runs this directly on the editor and only falls back to the AI Assistant when it can't. Lazy-loaded so the WASM stays out of the main bundle. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Settings/Logs/LogsExplorerOtelBanner.tsx | 43 ++++- .../Settings/Logs/logs-sql-rewrite.test.ts | 49 +++++ .../Settings/Logs/logs-sql-rewrite.ts | 177 ++++++++++++++++++ apps/studio/package.json | 1 + .../project/[ref]/logs/explorer/index.tsx | 24 ++- pnpm-lock.yaml | 19 +- 6 files changed, 303 insertions(+), 10 deletions(-) create mode 100644 apps/studio/components/interfaces/Settings/Logs/logs-sql-rewrite.test.ts create mode 100644 apps/studio/components/interfaces/Settings/Logs/logs-sql-rewrite.ts diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsExplorerOtelBanner.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsExplorerOtelBanner.tsx index 105e9575dfd..4c20d0b0695 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsExplorerOtelBanner.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsExplorerOtelBanner.tsx @@ -1,7 +1,10 @@ import { useLocalStorage } from '@uidotdev/usehooks' +import { useState } from 'react' +import { toast } from 'sonner' import { Button } from 'ui' import { Admonition } from 'ui-patterns' +import { rewriteBqLogsSqlToClickhouse } from './logs-sql-rewrite' import { buildClickhouseRewritePrompt } from './Logs.utils' import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state' @@ -10,16 +13,23 @@ import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' interface LogsExplorerOtelBannerProps { projectRef: string sql: string + onApplyRewrite: (sql: string) => void } /** * Shown in the Logs Explorer when it runs against the ClickHouse-backed OTEL - * endpoint, warning that the SQL dialect changed from BigQuery and offering the - * AI Assistant to rewrite existing queries. Dismissal is persisted per project. + * endpoint, warning that the SQL dialect changed from BigQuery. The primary + * action rewrites the current query to ClickHouse deterministically and falls + * back to the AI Assistant when it can't. Dismissal is persisted per project. */ -export const LogsExplorerOtelBanner = ({ projectRef, sql }: LogsExplorerOtelBannerProps) => { +export const LogsExplorerOtelBanner = ({ + projectRef, + sql, + onApplyRewrite, +}: LogsExplorerOtelBannerProps) => { const { openSidebar } = useSidebarManagerSnapshot() const aiSnap = useAiAssistantStateSnapshot() + const [isRewriting, setIsRewriting] = useState(false) const [dismissed, setDismissed] = useLocalStorage( `logs-explorer-clickhouse-banner-dismissed-${projectRef}`, false @@ -35,6 +45,29 @@ export const LogsExplorerOtelBanner = ({ projectRef, sql }: LogsExplorerOtelBann }) } + const handleRewrite = async () => { + if (!sql.trim()) { + openRewriteAssistant() + return + } + setIsRewriting(true) + try { + const { sql: rewritten, changed } = await rewriteBqLogsSqlToClickhouse(sql) + if (changed) { + onApplyRewrite(rewritten) + toast.success('Rewrote the query for ClickHouse') + } else { + // Nothing to rewrite deterministically, let the assistant help. + openRewriteAssistant() + } + } catch { + toast.info("Couldn't rewrite automatically, opening the AI Assistant") + openRewriteAssistant() + } finally { + setIsRewriting(false) + } + } + return ( -