V současné době se ponořím do analyzátoru PostgreSQL, přepisovače dotazů a plánovače dotazů v rámci práce na zabezpečení na úrovni řádků pro projekt AXLE. Jak jsem si všiml, že existuje skvělá dokumentace k celkové struktuře a toku, ale málo k některým detailům, řekl jsem si, že začnu psát o některých matoucích zákoutích.
Pokud vás zdrojový kód PostgreSQL a jeho vnitřní fungování nezajímá, můžete nyní přestat číst.
resjunk
Dnešním tématem je termín „resjunk“, který odkazuje na resjunk atribut target-list. Tento termín uvidíte v celém plánovači a přepisovači, obvykle jako předpokládanou znalost. Název není příliš užitečný.
resjunk sloupce jsou popsány v src/backend/executor/execJunk.c , kde je středně podrobný komentář. Ve skutečnosti to však nevysvětluje zastřešující myšlenky.
Koncept je, že někdy PostgreSQL potřebuje sledovat informace pro n-tice, které nejsou součástí výstupu dotazu. Může to být klíč řazení, který není součástí výběrového seznamu, mezivýsledek z dílčího dotazu, který je použit jako filtr a poté zahozen, nebo to může být interní sloupec jako ctid která není uživatelům vystavena.
Uzly plánu mají cílové seznamy – to jsou seznamy sloupců vydaných tímto uzlem plánu. Pro jednoduchý SELECT a, b FROM test sloupce a a b se objeví v cílovém seznamu indexu nebo uzlu plánu seqscan pro dotaz. Můžete to sami pozorovat povolením protokolování plánu podle následujícího oříznutého výstupu:
regress=> CREATE TABLE
regress=> SET enable_print_plan = on;
regress=> SET client_min_messages = debug;
regress=> SELECT a, b FROM test;
LOG: plan:
DETAIL: {PLANNEDSTMT
:commandType 1
:queryId 0
....
:planTree
{SEQSCAN
:startup_cost 0.00
:total_cost 29.40
:plan_rows 1940
:plan_width 12
:targetlist (
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 1
...
:location 7
}
...
:resjunk false
}
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 2
...
:location 10
}
....
:resjunk false
}
)
:qual
:lefttree
:righttree
:initPlan
:extParam (b)
:allParam (b)
:scanrelid 1
}
:rtable (
{RTE
:alias
:eref
{ALIAS
:aliasname test
:colnames ("a" "b")
}
...
:selectedCols (b 9 10)
:modifiedCols (b)
}
)
....
}
Toto je podrobný plán pro:
QUERY PLAN -------------------------------------------------------- Seq Scan on test (cost=0.00..29.40 rows=1940 width=8)
V něm uvidíte, že SELECT má dvě položky v cílovém seznamu, jednu pro každý sloupec. Ani resjunk protože oba jsou výstupem dotazu.
Co když přidáme řazení podle sloupce c , který není v SELECT -list, uvidíme nový sloupec přidaný do cílového seznamu a označený jako resjunk:
regress=> SELECT a, b FROM test ORDER BY c;
LOG: plan:
DETAIL: {PLANNEDSTMT
:commandType 1
....
:planTree
{SORT
....
:targetlist (
{TARGETENTRY
:expr
{VAR
:varno 65001
:varattno 1
...
}
:resno 1
:resname a
...
:resjunk false
}
{TARGETENTRY
:expr
{VAR
:varno 65001
:varattno 2
...
}
:resno 2
:resname b
....
:resjunk false
}
{TARGETENTRY
:expr
{VAR
:varno 65001
:varattno 3
...
}
:resno 3
:resname
....
:resjunk true
}
)
:qual
:lefttree
{SEQSCAN
...
:targetlist (
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 1
...
}
:resno 1
...
:resjunk false
}
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 2
...
}
:resno 2
...
:resjunk false
}
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 3
...
}
:resno 3
...
:resjunk true
}
)
....
}
:rtable (
{RTE
:alias
:eref
{ALIAS
:aliasname test
:colnames ("a" "b" "c")
}
....
:selectedCols (b 9 10 11)
:modifiedCols (b)
}
)
....
}
pro plán dotazů:
QUERY PLAN --------------------------------------------------------------- Sort (cost=135.34..140.19 rows=1940 width=12) Sort Key: c -> Seq Scan on test (cost=0.00..29.40 rows=1940 width=12) (3 rows)
Takže c je označeno resjunk protože je to klíč řazení, který není součástí konečného výstupu plánu.
Uvidíte také ctid označeno resjunk v UPDATE a DELETE plány z podobných důvodů – čtená část plánu načte řádky k úpravě a jejich n-ticová ID; tyto jsou vtaženy do nejvzdálenější MODIFYTABLE uzel plánu, který aktualizuje řádek, aby byl označen jako odstraněný, a pokud se jedná o aktualizaci, vloží novou verzi řádku.
Výzkum vedoucí k těmto výsledkům byl financován ze sedmého rámcového programu Evropské unie (FP7/2007-2013) na základě grantové dohody č. 318633