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