> For the complete documentation index, see [llms.txt](https://docs.datasentinel.io/manual/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.datasentinel.io/manual/ai-connector/analyze-and-tune-workloads/explain-execution-plans.md).

# Explain execution plans

Get the plan the planner would choose right now for a statement you paste, or for a query already analyzed, without executing anything.

Ask the assistant for the execution plan of a statement on a named instance, as the PostgreSQL planner would choose it right now. Paste the SQL from your code, or start from a query the assistant has already analyzed, and get the plan back with a reading of its shape: the scan and join choices, and the one or two steps worth fixing.

Nothing is executed. The assistant runs a single `EXPLAIN` on the instance, so the plan carries estimates only: no actual row counts, no timings.

{% hint style="warning" %}
**Needs real-time access.** A live plan is obtained through the [Live360](https://app.gitbook.com/o/DaDQkGCM7r2TghFSBFLQ/s/yE16Xb3IemPxJWydtPOj/features/key-features/live360) module, the only part of the platform that connects directly to the PostgreSQL instance. Access to it is a flag set on each user profile, under [Users & Roles](https://app.gitbook.com/o/DaDQkGCM7r2TghFSBFLQ/s/yE16Xb3IemPxJWydtPOj/implementation/platform-usage/configuration/users-and-roles#live360-access). Without that flag, the assistant says so in one line and offers the captured plan instead: the plan recorded while the query ran, read over a time window as in [Optimize your top queries](/manual/ai-connector/analyze-and-tune-workloads/optimize-top-queries.md).
{% endhint %}

## Captured plan, live plan, or generic plan?

The assistant knows three kinds of plans and picks the right one from the way you ask.

| Kind              | Where it comes from                                                                                                                                                                                                         | What it tells you                                                                             | Needs                                                        |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| **Captured plan** | Recorded by the platform while the query ran, through the [pg\_store\_plans](https://app.gitbook.com/o/DaDQkGCM7r2TghFSBFLQ/s/yE16Xb3IemPxJWydtPOj/getting-started/postgresql-clusters/extensions/pg_store_plans) extension | What actually ran during a time window, with actual row counts and timings                    | `pg_store_plans` installed on the instance                   |
| **Live plan**     | One `EXPLAIN` run now on a statement you paste, with its literal values                                                                                                                                                     | What the planner would choose now for those values                                            | Real-time access                                             |
| **Generic plan**  | One `EXPLAIN (GENERIC_PLAN)` run now on a statement that carries `$1`, `$2` bind markers, pasted by you or captured by the platform                                                                                         | What the planner would choose without knowing the values, as it does for a prepared statement | Real-time access, and PostgreSQL 16 or later on the instance |

A question about the plan that ran ("why is this query slow?", "did the plan change between 14:00 and 15:00?") is answered from the captured plan, as part of a query analysis. A pasted statement, or a "what would the planner choose now?" question, gets a live or generic plan. This page covers the last two.

## Example prompts

* **Paste a statement:**
  * "What plan does this get on prod-eu-1, database crm: SELECT c.id, c.email FROM customers c WHERE c.country = 'FR' AND c.created\_at >= '2026-09-01' ORDER BY c.created\_at DESC LIMIT 100"
  * "EXPLAIN this delete on the billing database of prod-eu-2: DELETE FROM order\_details WHERE order\_id = 4812"
* **Paste a statement with bind markers:**
  * "What plan does this get on prod-eu-1: SELECT \* FROM orders WHERE status = $1 AND created\_at >= $2"
* **Check a query already on screen:**
  * "What would the planner choose now for that query?"
  * "I just added an index on orders (status, created\_at) on prod-eu-1. Did it change the plan?"

The named command is `datasentinel explain`, followed by the statement. See the [Skill commands reference](/manual/ai-connector/use-the-assistant/skill-commands.md#look-at-live-activity).

{% @arcade/embed url="<https://app.arcade.software/share/KoTlpJBGaOFYK2HA39GH>" flowId="KoTlpJBGaOFYK2HA39GH" %}

*This walkthrough was recorded with the Chatgpt Desktop app.*

## How the assistant handles your statement

The path depends on where the SQL comes from and whether it carries bind markers.

{% tabs %}
{% tab title="Pasted with literal values" %}
The most direct case: you paste the statement with its actual values. The assistant runs a plain `EXPLAIN` on it, verbatim, on any PostgreSQL version. The plan is specific to those values: a different value in the `WHERE` clause may get a different plan, and the assistant says so if you generalize.

If you paste the statement wrapped in your own `EXPLAIN`, `EXPLAIN ANALYZE`, or `EXPLAIN (ANALYZE, BUFFERS)`, the assistant strips the wrapper and explains the statement inside it. `ANALYZE` is never forwarded, because it would execute the statement.
{% endtab %}

{% tab title="Pasted with bind markers" %}
Application code and prepared statements carry `$1`, `$2` placeholders, and a plain `EXPLAIN` fails on them. On PostgreSQL 16 or later, the assistant offers a **generic plan** and asks you to confirm before running it, or to paste the statement with its literal values instead. On PostgreSQL 15 or earlier, there is no generic plan: the assistant asks for the literal values.

The assistant never fills the placeholders with sample or "representative" values. That would plan a statement that never ran and prove nothing about the one that did.
{% endtab %}

{% tab title="Captured query" %}
After a query analysis (a drill-down from the top queries, for example), the next-step menu offers a generic plan on PostgreSQL 16 or later. The statement text the platform holds is normalized, with every literal replaced by a placeholder, so it always takes the generic path.

The response shows the captured plan first, then the generic plan, with a node-by-node comparison. A difference between them is a lead to explain (an index added since the window, refreshed statistics, selectivity the planner cannot see without the value), not a regression or a fix by itself. On instances below 16, the menu offers the captured plan and a later-window re-profile instead.
{% endtab %}
{% endtabs %}

Whatever the path, the assistant asks which database to run against when neither the conversation nor a previous analysis names one. It never guesses it from a schema name or picks the first one. When a bind marker's type cannot be inferred, the call fails with a data-type error; the assistant proposes a cast such as `$1::integer` and runs again only once you confirm.

<details>

<summary>Which statements can be explained</summary>

The rules are those of PostgreSQL's own `EXPLAIN`, applied to the top-level statement after any leading comments and `WITH` clause.

* **Explainable:** `SELECT` (including its `TABLE` form), `INSERT`, `UPDATE`, `DELETE`, `MERGE` (PostgreSQL 15 or later), `VALUES`, `DECLARE`, `CREATE TABLE AS`, and `CREATE MATERIALIZED VIEW AS`.
* **Generic plan:** only `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `MERGE`, and `VALUES` accept bind markers. A `DECLARE` or `CREATE TABLE AS` carrying `$1` is explained with its literal values instead.
* **Not explainable:** a `CALL`, a `DO`, and any other DDL or utility statement have no live plan in any form. The assistant says so in one sentence and does not ask for values.
* **`EXECUTE` of a prepared statement:** the prepared statement exists only in the session of your application, not in the assistant's connection. Paste the SQL it was prepared from.

</details>

## What you get back

The response follows the shape of every analysis: a few key points first, then the evidence, then a menu.

* **Key points:** the source of the plan (pasted or generic), the top node, the estimated rows and cost at the root, and the one signal that matters.
* **The statement**, verbatim, in a SQL block: exactly as you pasted it, or exactly as the platform captured it, placeholders included.
* **The execution plan**, verbatim, in a code block labelled *live plan* or *generic plan*. When a captured plan exists, it comes first, so the comparison shows both sides.
* **The reading**, phrased as shape ("the planner would now use the index", "the join would become a hash join"), never as runtime. Nothing ran, so no duration can be claimed.
* **Findings**, when the plan carries a signal, each with its evidence, the action to take, and the expected gain.

A pasted statement with its literal values, for example, comes back like this:

```sql
SELECT c.id, c.email
  FROM customers c
 WHERE c.country = 'FR'
   AND c.created_at >= '2026-09-01'
 ORDER BY c.created_at DESC
 LIMIT 100
```

```
Limit  (cost=0.43..812.19 rows=100 width=40)
  ->  Index Scan Backward using customers_created_at_idx on customers c  (cost=0.43..118402.55 rows=14582 width=40)
        Index Cond: (created_at >= '2026-09-01 00:00:00'::timestamp without time zone)
        Filter: ((country)::text = 'FR'::text)
```

> The planner walks the `created_at` index backwards and drops non-FR rows one at a time until 100 survive. It expects to touch about 14,600 rows to return 100. An index on `(country, created_at)` would let it stop after the first 100 matching rows.

Depending on your [communication style](/manual/ai-connector/readme.md#communication-style), the same content is delivered as an evidence-and-action table in the TECHNICAL style, or as plain-language findings in the PLAIN style, with each code block glossed in one line.

## Security

Explaining a statement never opens your database to the assistant. The `EXPLAIN` transits through the platform and runs as a single **read-only** call via the Datasentinel [agent](https://app.gitbook.com/o/DaDQkGCM7r2TghFSBFLQ/s/yE16Xb3IemPxJWydtPOj/getting-started/installation/agent) and the Datasentinel [collection user](https://app.gitbook.com/o/DaDQkGCM7r2TghFSBFLQ/s/yE16Xb3IemPxJWydtPOj/getting-started/postgresql-clusters/monitoring-user). The statement itself is planned, never executed: a `DELETE` you paste deletes nothing, and no PostgreSQL credential is ever known to the AI assistant.

{% hint style="info" %}
**Continue from the menu.** A plan ends with a short menu of next steps: profile the statement over a time window to get its statistics, waits, and captured plan, check the health of the tables the plan touches, re-run the same plan after a fix you are about to apply, or re-profile over a later window, the only verification that carries actual timings. You can also prove a finding, or generate a report whose query detail tab holds the statement and both plans. Pick an option to keep the investigation going without retyping the context.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.datasentinel.io/manual/ai-connector/analyze-and-tune-workloads/explain-execution-plans.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
