SQL tables
POST /v1/projects/{p}/query/sql reads virtual tables only. Each name becomes a subquery over the real ClickHouse table with project_id = {p:UUID} first, then the request time range. GET /v1/projects/{p}/sql/tables lists every table with its columns and types (the allow-list).
Table | Rows | Notes |
|---|---|---|
| as before |
|
| error events | same data as |
| one per browser event |
|
| one per session | vitals ( |
| replay session metadata | no payload: chunk bytes stay in object storage; deleted replays are hidden |
| hourly per function |
|
| per-minute RED rollup |
|
| one per GenAI span | tokens, cost, model, operation, outcome |
The new tables select only their listed columns: project_id, retention_days and aggregate-state columns are never readable.
Join across signals through ids, for example sessions to backend spans:
SELECT r.session_id, count() AS spans
FROM rum_sessions AS r
JOIN rum_events AS e ON r.session_id = e.session_id
JOIN spans AS s ON e.trace_id = s.trace_id
GROUP BY r.session_idLimits: max_rows defaults to 10,000 and may go up to 100,000. Requests above 10,000 count against a per-caller budget of 1,000,000 such rows per hour (429 when spent). A result over 64 MiB fails with "result too large".
Known limits: rum_sessions, replays and rum_events are ReplacingMergeTree tables and are read without FINAL, so a row may appear twice until ClickHouse merges it; use argMax(col, last_seen) grouped by session_id when exact one-row-per-session matters.