Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

.nornir/sql-guide.md

SQL over the warehouse — guide

Class: guide · audience: a techie querying the warehouse with SQL. The what is in sql-design.md; the why the native provider in warehouse-reasoning.md. The table names + shapes you query are catalogued in warehouse-design.md (f2) and warehouse-schema.md. This file is the CLI + worked SELECTs.

f0 — one command, every table

nornir warehouse query "SELECT * FROM bench_runs LIMIT 5"

f0b — discover the tables + columns first

Don't guess table or column names — ask the warehouse:

# List every queryable table:
nornir warehouse tables

# Describe one table's columns (name · Arrow type · nullability):
nornir warehouse tables call_edges
#   snapshot_id   Utf8  NOT NULL
#   ts_micros     Timestamp(µs, "+00:00")  NOT NULL
#   repo          Utf8  NOT NULL
#   caller_path   Utf8  NOT NULL
#   callee_ident  Utf8  NOT NULL
#   …

# Machine-readable (for tooling / jq):
nornir warehouse tables --json                 # ["agent_model_runs","airgap_events",…]
nornir warehouse tables call_edges --json      # [{"name":"repo","data_type":"Utf8","nullable":false},…]

An unknown table name prints a clear error that lists every known table, so a typo tells you the right spelling. Discovery is cheap — it reads only catalog metadata, never a Parquet file — and is read-only + lock-tolerant just like query. This is the fast path to knowing what a SELECT can reference.

f1 — output formats

# Bordered text table (the default):
nornir warehouse query "SELECT repo, COUNT(*) AS n FROM call_edges GROUP BY repo"

# JSON array of row objects — pipe into jq / a script:
nornir warehouse query --format json \
  "SELECT item_name, file FROM symbol_facts WHERE repo = 'nornir' LIMIT 10" | jq .

An empty result in JSON mode is a valid empty array ([]).

f2 — worked SELECTs

Filter one repo's symbols:

nornir warehouse query \
  "SELECT item_name FROM symbol_facts WHERE repo = 'nornir' ORDER BY item_name"

Count rows per repo (aggregate + group by):

nornir warehouse query \
  "SELECT repo, COUNT(*) AS n FROM call_edges GROUP BY repo ORDER BY n DESC"

Join two tables — every call edge out of a given symbol:

nornir warehouse query "
  SELECT c.callee_ident
  FROM symbol_facts s
  JOIN call_edges c ON s.crate_name = c.crate_name
  WHERE s.item_name = 'query'
  ORDER BY c.callee_ident"

Latest bench runs for a repo/machine:

nornir warehouse query "
  SELECT version, machine, ts_micros
  FROM bench_runs
  WHERE repo = 'nornir'
  ORDER BY ts_micros DESC
  LIMIT 5"

Release provenance:

nornir warehouse query "
  SELECT release_id, repo, git_sha
  FROM release_lineage
  ORDER BY release_id DESC
  LIMIT 10"

Browse a table's columns before writing a query with `nornir warehouse tables

f3 — from an MCP client (embedded only)

The warehouse_query MCP tool runs the same SQL and returns the rows as a JSON array. It runs embedded against the local warehouse — there is no SQL gRPC endpoint yet — so unset NORNIR_SERVER before calling it, or it returns an invalid_params error telling you to.

// MCP tool call
{ "name": "warehouse_query",
  "arguments": { "sql": "SELECT repo, COUNT(*) FROM symbol_facts GROUP BY repo" } }

Discover the schema first with the warehouse_tables MCP tool (the sibling of warehouse_query, also embedded-only): no arguments returns the JSON list of queryable table names, and {"table": "<name>"} returns that table's columns as [{name,data_type,nullable}] — so a small/local agent can look up the exact FROM targets and column names before writing a SELECT, instead of guessing.

{ "name": "warehouse_tables", "arguments": {} }                       // → ["bench_runs","call_edges",…]
{ "name": "warehouse_tables", "arguments": { "table": "bench_runs" } } // → [{"name":"repo",…},…]

Gotchas