1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! v7.38.8 — run the cheap half of a conjunction first, whichever half
//! the query happened to write first.
//!
//! Measured on a customer profile, same data, same box, 200,000 rows:
//!
//! ```text
//! WHERE traits @> '{"plan":"pro"}' AND received_at >= … AND received_at < … 39.7 ms
//! WHERE received_at >= … AND received_at < … AND traits @> '{"plan":"pro"}' 8.4 ms
//! ```
//!
//! The same query, in a different written order, 4.7 times apart. PG
//! answers both in 5.3 and 5.4 ms because `order_qual_clauses` sorts
//! quals by estimated cost before the executor sees them; we evaluated
//! them in the order they were typed, and which one a person types
//! first is habit.
//!
//! ## Where this runs, and why not earlier
//!
//! In the compiled predicate, not in the AST. The first version of
//! this pass rewrote the statement in `preprocess`, next to the
//! constant fold, and it won 4.3x on the shape above while making
//! every INDEXED shape slower — 5.20 ms to 6.65 on one, 5.99 to 8.24
//! on the same query written the other way, against 1.86 for the seek
//! alone. Two controls placed it: the same build with the pass
//! early-returning matched the baseline, so it was not code layout;
//! and on an unindexed copy of the same table the pass was FASTER, so
//! it was not a fixed per-query cost.
//!
//! Reordering the tree before the seek matcher reads it changes which
//! seek it finds. PG has the same pass and does not make that mistake:
//! `order_qual_clauses` runs in the executor, over the quals that are
//! LEFT once index conditions have been extracted. So this runs from
//! `compile_expr`, once per plan, after every planner decision is
//! already made.
//!
//! ## Why a partition and not a sort
//!
//! `AND` is commutative under three-valued logic, so the ANSWER does
//! not depend on the order. What does depend on it is which conjuncts
//! get evaluated at all — the machine short-circuits — and therefore
//! which errors a row can raise. Moving an expression that can raise
//! to the FRONT can make an error appear where the query used to
//! short-circuit past it, and that is a change no perf win pays for.
//!
//! So this pass never moves anything that can raise. It stably
//! partitions the conjuncts into "cannot raise, and is cheap" and
//! "everything else", each half keeping the order it was written in.
//! The safe half runs first. A conjunct the classifier does not
//! recognise stays where it is, which is why the classifier is an
//! allowlist and not a deny-list: an expression nobody has thought
//! about is left alone rather than assumed harmless.
//!
//! The effect on errors is one-directional and matches PG's: an error
//! that used to be raised may now be short-circuited past. It cannot
//! introduce one.
use Box;
use Vec;
use ;
/// The same conjunction with its cheap, total conjuncts first, or
/// `None` when there is nothing to move.
///
/// `None` rather than a copy that happens to be equal: the caller
/// compiles the original in that case, so a predicate this pass has no
/// opinion about is not cloned at all.
/// `#[cold]` and never inlined, and that is measured rather than
/// decorative. Compiled inline, this function's presence cost two
/// shapes it does not even reorder 17 % and 20 % — a tax that scaled
/// with ROW COUNT (nothing at 5,000 rows, +1.36 ms at 200,000) on a
/// function that runs once per plan. That is the shape of a layout
/// tax on the row loop, not of work being done: the same build with
/// the body replaced by `return None` — which lets the compiler
/// delete it — matched the baseline exactly.
pub
/// Is this conjunct both cheap to evaluate and incapable of raising?
///
/// An allowlist. A comparison between a column and a literal reads one
/// cell and compares it; `IS [NOT] NULL` reads one cell; a bare boolean
/// column reads one cell. None of them can fail on a row that the scan
/// produced. Everything else — a function call, an operator that
/// interprets a document, arithmetic that can overflow or divide, a
/// subquery, a cast — is left exactly where it was written.