Je me plonge actuellement dans l'analyseur, le réécriture de requêtes et le planificateur de requêtes PostgreSQL, dans le cadre du travail sur la sécurité au niveau des lignes pour le projet AXLE. Comme j'ai remarqué qu'il existe une excellente documentation sur la structure globale et le flux, mais pas beaucoup sur certains détails, j'ai pensé commencer à publier sur certains des coins les plus déroutants.
Si vous n'êtes pas intéressé par le code source de PostgreSQL et son fonctionnement interne, vous pouvez arrêter de lire maintenant.
resjunk
Le sujet d'aujourd'hui est le terme "resjunk", qui fait référence à resjunk attribut de liste cible. Vous verrez ce terme tout au long du planificateur et du réécrivain, généralement comme une connaissance supposée. Le nom n'est pas extrêmement utile.
resjunk les colonnes sont décrites dans src/backend/executor/execJunk.c , où il y a un commentaire modérément détaillé. Cependant, cela n'explique pas vraiment les idées globales.
Le concept est que parfois PostgreSQL doit garder une trace des informations par tuple qui ne font pas partie de la sortie de la requête. Il peut s'agir d'une clé de tri qui ne fait pas partie de la liste de sélection, d'un résultat intermédiaire d'une sous-requête utilisée comme filtre puis ignorée, ou d'une colonne interne telle que ctid qui n'est pas exposé aux utilisateurs.
Les nœuds de plan ont des listes cibles - ce sont des listes des colonnes générées par ce nœud de plan. Pour un simple test SELECT a, b FROM les colonnes a et b apparaîtra dans la liste cible du nœud d'index ou de plan seqscan pour la requête. Vous pouvez observer cela vous-même en activant la journalisation du plan, selon la sortie ajustée suivante :
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) } ) .... }
Voici le plan détaillé pour :
QUERY PLAN -------------------------------------------------------- Seq Scan on test (cost=0.00..29.40 rows=1940 width=8)
Vous y verrez que le SELECT a deux entrées dans la liste cible, une pour chaque colonne. Ni resjunk puisque les deux sont générés par la requête.
Et si on ajoutait un tri par colonne c , qui n'est pas dans SELECT -list, nous verrons une nouvelle colonne ajoutée à la liste cible et marquée comme indésirable :
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) } ) .... }
pour le plan de requête :
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)
Alors c est marqué resjunk car il s'agit d'une clé de tri qui ne fait pas partie de la sortie finale du plan.
Vous verrez également ctid marqué resjunk dans MISE À JOUR et SUPPRIMER plans pour des raisons similaires - la partie lecture du plan récupère les lignes à modifier et leurs ID de tuple ; ceux-ci sont tirés dans un MODIFYTABLE le plus externe nœud de plan qui met à jour la ligne pour la marquer comme supprimée et, s'il s'agit d'une mise à jour, insère une nouvelle version de la ligne.
La recherche menant à ces résultats a reçu un financement du septième programme-cadre de l'Union européenne (FP7/2007-2013) sous la convention de subvention n° 318633