Reach: the condition under which Postgres evaluates each call.
The scan answers every call before its quals and projection run, but
Postgres short-circuits: an OR arm runs only when the earlier arms
are not true, an AND arm only when they are not false, a CASE
result only when its WHEN is true and the earlier ones are not, a
later qual only when the earlier ones are true, and the projection
only for a row that passed the quals. A call it never reaches must
not be sent, since its answer would be paid for and never read.
This runs in the plan post-pass, after setrefs, over each jev scan's
final qual and targetlist, where a call is an INDEX_VAR column past
the child's. Each call's condition is the OR, over its occurrences,
of the AND of the conditions on the path to it, and it goes in
custom_exprs after the arguments (plan_custom_path leaves a
true there). The scan evaluates it on each child row and leaves an
unreached call NULL, which Postgres then never reads.
A condition Postgres could not evaluate a second time with the same
result is dropped, which only widens the reach: one that reads a call's
answer (it is not known yet), a volatile function, a SubPlan, or a
CASE x WHEN's placeholder. Dropped, the call is judged as before.
24use pgrx::pg_sys;
Sets the reach of every call of every jev scan in plan.
Safety
plan is NULL or a finished plan tree, after setrefs.
33pub(super) unsafe fn plan(plan: *mut pg_sys::Plan, children: unsafe fn(*mut pg_sys::Plan) -> Vec<*mut pg_sys::Plan>) { 34 unsafe { 35 if plan.is_null() { 36 return; 37 } 38 if is_a(plan.cast(), pg_sys::NodeTag::T_CustomScan) { 39 let scan = plan.cast::<pg_sys::CustomScan>(); 40 if std::ptr::eq((*scan).methods, &tables().scan) { 41 set(scan); 42 } 43 } 44 for child in children(plan) { 45 self::plan(child, children); 46 } 47 } 48}
Replaces the trailing true per call in scan's custom_exprs.
51unsafe fn set(scan: *mut pg_sys::CustomScan) { 52 unsafe { 53 let mut private = ints((*scan).custom_private); 54 let prefix = private.next().expect("the child's column count") as i16; 55 let calls = len((*scan).custom_scan_tlist) - prefix as usize; 56 let exprs: Vec<*mut pg_sys::Node> = ptrs((*scan).custom_exprs).collect(); 57 assert!(exprs.len() >= calls, "a reach per call"); 58 59 let mut found = Found { prefix, occurrences: vec![Vec::new(); calls] }; 60 // ExecScan runs the quals in order, stopping at the first that is 61 // not true, then projects the rows that passed. 62 let mut guards = Vec::new(); 63 for qual in ptrs::<pg_sys::Node>((*scan).scan.plan.qual) { 64 found.visit(qual, &guards); 65 guards.push(test(qual, pg_sys::BoolTestType::IS_TRUE)); 66 } 67 found.visit((*scan).scan.plan.targetlist.cast(), &guards); 68 69 let head = exprs.len() - calls; 70 let mut out = std::ptr::null_mut(); 71 out = append(out, exprs[..head].iter().copied()); 72 for occurrences in found.occurrences { 73 out = append(out, [reach(prefix, occurrences)]); 74 } 75 (*scan).custom_exprs = out; 76 } 77}
true when a call is unused or reached unconditionally somewhere,
else the OR of its occurrences' usable conditions.
81unsafe fn reach(prefix: i16, occurrences: Vec<Vec<*mut pg_sys::Node>>) -> *mut pg_sys::Node { 82 unsafe { 83 let mut arms = Vec::new(); 84 for guards in occurrences { 85 let usable: Vec<_> = guards.into_iter().filter(|&g| usable(g, prefix)).collect(); 86 match usable.len() { 87 0 => return always(), 88 1 => arms.push(usable[0]), 89 _ => arms.push(bool_expr(pg_sys::BoolExprType::AND_EXPR, usable)), 90 } 91 } 92 match arms.len() { 93 0 => always(), 94 1 => arms[0], 95 _ => bool_expr(pg_sys::BoolExprType::OR_EXPR, arms), 96 } 97 } 98}
Per call column: each occurrence's conditions, innermost last.
106impl Found { 107 unsafe fn visit(&mut self, node: *mut pg_sys::Node, guards: &[*mut pg_sys::Node]) { 108 use pg_sys::NodeTag::*; 109 unsafe { 110 if node.is_null() { 111 return; 112 } 113 match (*node).type_ { 114 T_Var => { 115 let var = node.cast::<pg_sys::Var>(); 116 if (*var).varno == pg_sys::INDEX_VAR && (*var).varattno > self.prefix { 117 self.occurrences[((*var).varattno - self.prefix - 1) as usize].push(guards.to_vec()); 118 } 119 } 120 T_BoolExpr => { 121 let expr = node.cast::<pg_sys::BoolExpr>(); 122 let before = match (*expr).boolop { 123 pg_sys::BoolExprType::AND_EXPR => Some(pg_sys::BoolTestType::IS_NOT_FALSE), 124 pg_sys::BoolExprType::OR_EXPR => Some(pg_sys::BoolTestType::IS_NOT_TRUE), 125 _ => None, 126 }; 127 let mut inner = guards.to_vec(); 128 for arm in ptrs::<pg_sys::Node>((*expr).args) { 129 self.visit(arm, &inner); 130 if let Some(kind) = before { 131 inner.push(test(arm, kind)); 132 } 133 } 134 } 135 T_CaseExpr => { 136 let case = node.cast::<pg_sys::CaseExpr>(); 137 self.visit((*case).arg.cast(), guards); 138 let mut inner = guards.to_vec(); 139 for when in ptrs::<pg_sys::CaseWhen>((*case).args) { 140 let condition = (*when).expr.cast::<pg_sys::Node>(); 141 self.visit(condition, &inner); 142 let mut chosen = inner.clone(); 143 chosen.push(test(condition, pg_sys::BoolTestType::IS_TRUE)); 144 self.visit((*when).result.cast(), &chosen); 145 inner.push(test(condition, pg_sys::BoolTestType::IS_NOT_TRUE)); 146 } 147 self.visit((*case).defresult.cast(), &inner); 148 } 149 // Every other node evaluates all of its children. 150 _ => { 151 let mut children = Vec::new(); 152 let mut first = true; 153 walk(node, &mut |n| { 154 if first { 155 first = false; 156 return true; 157 } 158 children.push(n); 159 false 160 }); 161 for child in children { 162 self.visit(child, guards); 163 } 164 } 165 } 166 } 167 } 168}
Whether guard can be evaluated on the scan tuple before the calls
are answered, with the result Postgres will get.
172unsafe fn usable(guard: *mut pg_sys::Node, prefix: i16) -> bool { 173 use pg_sys::NodeTag::*; 174 let mut ok = true; 175 unsafe { 176 walk(guard, &mut |n| { 177 match (*n).type_ { 178 T_Var => { 179 let var = n.cast::<pg_sys::Var>(); 180 ok &= (*var).varno == pg_sys::INDEX_VAR && (*var).varattno <= prefix; 181 } 182 T_SubPlan | T_AlternativeSubPlan | T_CaseTestExpr | T_Aggref | T_WindowFunc | T_GroupingFunc 183 | T_PlaceHolderVar => ok = false, 184 _ => {} 185 } 186 ok 187 }); 188 ok && !pg_sys::contain_volatile_functions(guard) 189 } 190}
arg IS [NOT] TRUE|FALSE, over a copy of arg.
193unsafe fn test(arg: *mut pg_sys::Node, kind: pg_sys::BoolTestType::Type) -> *mut pg_sys::Node { 194 unsafe { 195 let node = pg_sys::palloc0(size_of::<pg_sys::BooleanTest>()).cast::<pg_sys::BooleanTest>(); 196 (*node).xpr.type_ = pg_sys::NodeTag::T_BooleanTest; 197 (*node).arg = pg_sys::copyObjectImpl(arg.cast()).cast(); 198 (*node).booltesttype = kind; 199 (*node).location = -1; 200 node.cast() 201 } 202}
The reach of a call with no condition.