# `QuackDB.Profile`
[🔗](https://github.com/elixir-vibe/quackdb/blob/main/lib/quack_db/profile.ex#L118)

DuckDB query profiling helpers.

`analyze/4` runs `EXPLAIN (ANALYZE, FORMAT json)` and decodes DuckDB's
profile into typed structs using JSONCodec, grouped the way DuckDB 2.0
reports them: `query`, `parser`, `planner`, `physical_planner`, `optimizer`,
`io`, `system`, and the `operators` tree. `explain/4` returns the plan tree
only. DuckDB's open-ended `extra_info` maps and optimizer metric names
retain string keys.

Use `flatten/1`, `slowest/2`, and `report/2` when you want a profiler-style
operator view.

# `operator_row`

```elixir
@type operator_row() :: %{
  path: [non_neg_integer()],
  name: String.t() | nil,
  type: String.t() | nil,
  timing: number() | nil,
  timing_percent: float() | nil,
  cardinality: non_neg_integer() | nil,
  rows_scanned: non_neg_integer() | nil,
  size_bytes: non_neg_integer() | nil,
  extra_info: map()
}
```

A flattened DuckDB operator row with its tree path and timing share.

# `t`

```elixir
@type t() :: %QuackDB.Profile{
  io: QuackDB.Profile.IO.t() | nil,
  operators: [QuackDB.Profile.Operator.t()],
  optimizer: %{required(String.t()) =&gt; number()},
  parser: QuackDB.Profile.Planner.t() | nil,
  physical_planner: QuackDB.Profile.Planner.t() | nil,
  planner: QuackDB.Profile.Planner.t() | nil,
  query: QuackDB.Profile.Query.t() | nil,
  system: QuackDB.Profile.System.t() | nil
}
```

# `analyze`

```elixir
@spec analyze(DBConnection.conn() | module(), iodata() | term(), [term()], keyword()) ::
  {:ok, t()} | {:error, Exception.t()}
```

Runs `EXPLAIN (ANALYZE, FORMAT json)` and returns a decoded DuckDB query profile.

# `analyze!`

```elixir
@spec analyze!(DBConnection.conn() | module(), iodata() | term(), [term()], keyword()) ::
  t()
```

Runs `EXPLAIN (ANALYZE, FORMAT json)` and returns a decoded DuckDB query profile, raising on errors.

# `decode`

Decodes a JSON string into this struct.

# `decode!`

Decodes a JSON string into this struct, raising on failure.

# `dump`

Dumps this struct into JSON-shaped data, respecting JSON field names.

# `explain`

```elixir
@spec explain(DBConnection.conn() | module(), iodata() | term(), [term()], keyword()) ::
  {:ok, t()} | {:error, Exception.t()}
```

Runs `EXPLAIN (FORMAT json)` and returns a decoded DuckDB plan profile.

# `explain!`

```elixir
@spec explain!(DBConnection.conn() | module(), iodata() | term(), [term()], keyword()) ::
  t()
```

Runs `EXPLAIN (FORMAT json)` and returns a decoded DuckDB plan profile, raising on errors.

# `flatten`

```elixir
@spec flatten(t()) :: [operator_row()]
```

Converts DuckDB's profile tree into profiler-style operator rows.

# `from_map`

Builds this struct from a decoded JSON map.

# `from_map!`

Builds this struct from a decoded JSON map, raising on failure.

# `report`

```elixir
@spec report(
  t(),
  keyword()
) :: String.t()
```

Formats a compact text report for humans.

# `schema`

Returns a JSON Schema-compatible schema map.

# `slowest`

```elixir
@spec slowest(t(), pos_integer()) :: [operator_row()]
```

Returns the slowest operators by `timing`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
