---
search:
  tags:
    - Operations
    - POST
seo:
  description: >-
    Every drill_down_sql that /v1/diagnose returns can be pasted here verbatim.
    Reference for the POST /v1/query endpoint in the sigiro API Reference API.
sidebar:
  label: Run a SQL query
  badge: POST
title: Run a SQL query
type: openapi-operation
---
Every `drill_down_sql` that `/v1/diagnose` returns can be pasted here
verbatim.

Readable tables, optionally qualified `lake.*` or `main.*`:

- `sigiro_spans`
- `sigiro_logs`
- `sigiro_metrics_gauge`
- `sigiro_metrics_sum`
- `sigiro_metrics_histogram`
- `sigiro_metrics_exp_histogram`
- `sigiro_profiles`
- `sigiro_anomalies`

## Bound `timestamp` with a literal, not `now()`

Statistics pruning only happens when the comparison value is a constant the
planner can fold. `now()` is volatile, so any predicate built from it is
evaluated per row and every file in the table is opened.

```sql
-- prunes: the planner folds the constant and skips whole files
WHERE timestamp > TIMESTAMP '2026-08-31 08:00:00'

-- does not prune: opens every file, then filters
WHERE timestamp > (now() - INTERVAL 1 HOUR)::TIMESTAMP
```

Compute the bound in your client and send it as a literal.

## If you must use `now()`, cast it

The `timestamp` columns are `TIMESTAMP` (no timezone) and `now()` returns
`TIMESTAMPTZ`. Comparing them directly makes DuckDB cast the **column**,
which adds a per-row conversion on top of the missing pruning. Measured
against production on 2026-08-31, same table and window:

| predicate | result |
|---|---|
| `timestamp > TIMESTAMP '2026-08-31 08:00:00'` | 200 in 2.48s |
| `timestamp > (now() - INTERVAL 5 MINUTE)::TIMESTAMP` | 200 in 0.65s |
| `timestamp > now() - INTERVAL 5 MINUTE` | 400 `INTERRUPT` at 10.98s |
| `timestamp > TIMESTAMPTZ '2026-08-31 08:00:00+00'` | 400 `INTERRUPT` at 10.65s |

So the cast is worth having, but it is a mitigation and not the fix: the
literal is.

`POST /v1/query`
