You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ask the planner why it chose its plan: --why-not [TABLE] in connected
mode, or y on a node in the viewer. The statement is planned again with
the choice taken away (enable_seqscan = off, enable_nestloop = off) or
with enough work_mem for a spill, and the plans are compared. The answer
says whether an index can serve the condition at all and what keeps it
out, how much more expensive the planner estimates the alternative, and,
with --measure, whether the planner is right, or wrong because of a row
misestimate or its cost settings. A cost setting such as random_page_cost = 1.1 is suggested only once the plan it leads to is
measured better too. An existing index the planner did not use gets the
reason found instead of the likely ones.
--measure and --runs N: measure each plan N times, after one run that
only warms the cache, and compare the medians.
Comparisons report pages written to temporary files, and the JSON report
says how two plans compare (change, basis).
explainsql diff BEFORE AFTER compares two plans of the same statement,
node by node: scans that read their relation another way, joins with
another method or order, other strategies, nodes that came or went,
spills, misestimates, and nodes whose work changed beyond the noise, the
most significant first, then the plan after with its changes marked. It
matches nodes by the work they do, so partitions renamed by pruning or by
another PostgreSQL version still match. Text, Markdown or JSON.
Plan shapes: an id for what makes a plan that plan, without its numbers,
literal values or aliases, the same in JSON and text.
Reading every plan of an input: plans pasted one after the other, JSON
arrays and documents, Markdown fences, psql results and log entries
(parse_all). explainsql diff takes both plans from one input this way.
explainsql check, a gate for CI: plan files, or SQL files run against a
database (-d), each checked against its findings (--fail-on) and
against the plan locked for it in explainsql.lock (--update writes
it). A plan fails when it is worse by pages, or by the estimated cost when
not run; time alone, for the same pages, is a note. --strict fails any
change of plan, and --prove tests the suggested fixes of the plans that
failed. Exit codes 0, 1 and 2; text, Markdown, JSON or SARIF.
--fail-on SEVERITY: with a printed report, exit with 1 when a finding is
at least that severe.
explainsql logs FILES: the plans auto_explain logged, and for each
statement, which plans it got, when its plan changed, after how many runs
and in which session, the median duration before and after, how the plan
after compares and what changed, the costliest change first. Reads
stderr logs with any line prefix, csvlog and jsonlog, plans in text or
JSON. Statements are told apart by their query identifier, or by their
text without literal values. A switch to a prepared statement's generic
plan is named, with the values it ran with (PostgreSQL 16+) and the --params --bind command that tests it. sqlcommenter tags say where a
statement comes from. --changed, --query, --trace, --since and --until narrow the report; text, Markdown or JSON.
parse_log: every auto_explain entry of a server log, with its time,
process, user, database, application, duration, query identifier and
parameters.
ES013, planner settings force the plan: the plan was made with an enable_* setting off, as left on in a session or set for a role or a
database, or the planner used a node such a setting disables because it
found no other way (Disabled: true from PostgreSQL 18, the disable cost
before). Applications that plan with the defaults may get another plan.
I/O time, from plans captured with track_io_timing: among the
statement's facts when it takes a tenth of the time or more, in the
verdict when reading pages that were not in shared buffers took half of
it (the cache was cold), and for each node in the viewer's details. It is
compared with the time of every process, so that parallel plans read
right.
--params in connected mode: how the plan of a statement with parameters
($1, or JDBC's ?) depends on their values. explainsql prepares the
statement as an application does, tries values from the columns'
statistics and common LIMIT and OFFSET row counts, and compares the
custom plan each value gets with the generic plan, which PostgreSQL may
switch to after five executions. With --measure, both plans run where
they differ; the report says whether the generic plan does much worse for
some value, whether PostgreSQL would switch to it, and what to do
(plan_cache_mode = force_custom_plan, pgJDBC's prepareThreshold=0),
and shows the generic plan with the values it does worst with. --bind N=VALUE gives a parameter's value.
Changed
ES003 and ES004 name the work_mem a spilled sort or hash needs, from
what the plan shows, to set for the statement alone (SET LOCAL work_mem = '64MB' in its transaction), and say what it may take: each sort, hash
and other operation that uses work_mem may take that much, in each
process that runs it, in every session that runs the statement at once.
In connected mode, a run compared with the previous one says what changed
in the plan, or that it is the same plan.
Before and after comparisons lead with pages, then temporary files, then
time, and ignore differences under 10% (and 0.1 ms). Fewer pages but a
slower run is a mixed result.
The README's demo is a recording of a real session: explainsql run
against PostgreSQL on a slow query, asked why the planner uses no index,
and testing the suggested index with HypoPG. cargo xtask demo --record
records it again; cargo xtask demo draws it from the recording.
Fixed
Measuring a suggested index favored the index: the run without it often
met a colder cache than the run with it, which followed the build that
read the whole table, and any faster run counted as better. Both sides now
run once first to warm the cache, pages decide before time, and a
suggestion that is not better by more than the noise drops to low
confidence.