Phase 2: unified query language spanning ClickHouse and Tantivy

Replaces the separate SQL-only /query and text-only /search endpoints
with one pipe-syntax query language (plus raw SQL escape hatch) that
compiles to a single IR and execution plan across both backends, so a
query like `message:"connection refused" | stats count by host` runs
as one request instead of two disjoint tools.

- api/internal/querylang: lexer -> ast -> parser -> ir -> planner ->
  executor, each layer independently tested.
- Execution generalizes Phase 1's proven Tantivy-prefilter pattern
  into a 4-way routing table (pure ClickHouse / text-only / text +
  aggregation / raw SQL passthrough).
- Unified web query page and `sentryctl query`, both hitting the same
  POST /query endpoint.
- Benchmarked against a real 1,022,000-row dataset
  (hack/benchmark-fixture); caught and fixed a real bug where the
  Tantivy prefilter cap (10,000) produced an IN-clause exceeding
  ClickHouse's default max_query_size -- lowered to 5,000, documented
  in docs/query-language-design.md and docs/phase-2-runbook.md.
- docs/query-language-reference.md: customer-facing syntax reference.
This commit is contained in:
2026-08-13 12:21:42 -07:00
parent cd8aa290ca
commit fb5049a747
36 changed files with 4119 additions and 613 deletions
-29
View File
@@ -1,6 +1,5 @@
<script lang="ts">
import favicon from '$lib/assets/favicon.svg';
import { page } from '$app/state';
let { children } = $props();
</script>
@@ -10,32 +9,4 @@
<link rel="icon" href={favicon} />
</svelte:head>
<nav>
<a href="/" class:active={page.url.pathname === '/'}>SQL Query</a>
<a href="/search" class:active={page.url.pathname === '/search'}>Full-Text Search</a>
</nav>
{@render children()}
<style>
nav {
font-family: system-ui, sans-serif;
max-width: 960px;
margin: 1rem auto 0;
padding: 0 1rem;
display: flex;
gap: 1rem;
border-bottom: 1px solid #ccc;
}
nav a {
padding: 0.5rem 0;
text-decoration: none;
color: #555;
border-bottom: 2px solid transparent;
}
nav a.active {
color: #000;
border-bottom-color: #000;
font-weight: 600;
}
</style>