Skip to main content

inillucent_sql/bind/
comparison_rules.rs

1//! The affinity and collation a comparison between two operands uses.
2//!
3//! Invariant: **a comparison applies the left operand's affinity first and an
4//! explicit collation before an implicit one, as `sqlite3BinaryCompareCollSeq`
5//! does, and this is the only place those two answers are combined.** The
6//! functions moved here from `collation.rs` to keep that file under its
7//! recorded size.
8
9use inillucent_value::{Affinity, Collation};
10
11use super::BoundExpr;
12
13/// Returns the affinity and collation a comparison between two operands uses.
14///
15/// SQLite's rule, in order: if either side has a column affinity the comparison
16/// applies it, with the left side winning. The collation is an explicit one on
17/// the left operand, then an explicit one on the right, then the left
18/// operand's implicit one, then the right's, and otherwise BINARY. "On an
19/// operand" includes anywhere inside it: see [`BoundExpr::explicit_collation`].
20///
21/// @param left - the comparison's left operand
22/// @param right - the comparison's right operand
23pub fn comparison_rules(left: &BoundExpr, right: &BoundExpr) -> (Option<Affinity>, Collation) {
24    comparison_rules_over(left, right, left.affinity(), right.affinity())
25}
26
27/// The rules of [`comparison_rules`] for operands whose affinities the caller
28/// has already decided.
29///
30/// **A column of a derived table whose query gave it no affinity has none.** The
31/// binder stores that as BLOB, which is also what a declared column with no
32/// type has, and the two compare differently: a TEXT column meets the first with
33/// TEXT affinity applied to it, and meets the second with nothing converted.
34///
35/// @param left - the comparison's left operand
36/// @param right - the comparison's right operand
37/// @param left_affinity - the left operand's affinity as the comparison sees it
38/// @param right_affinity - the right operand's affinity as the comparison sees it
39pub fn comparison_rules_over(
40    left: &BoundExpr,
41    right: &BoundExpr,
42    left_affinity: Option<Affinity>,
43    right_affinity: Option<Affinity>,
44) -> (Option<Affinity>, Collation) {
45    let affinity = match (left_affinity, right_affinity) {
46        (Some(left), Some(right)) => inillucent_value::compare::comparison_affinity(left, right),
47        (Some(left), None) => Some(left),
48        (None, Some(right)) => Some(right),
49        (None, None) => None,
50    };
51    let collation = left
52        .explicit_collation()
53        .or_else(|| right.explicit_collation())
54        .or_else(|| left.collation())
55        .or_else(|| right.collation())
56        .unwrap_or(Collation::Binary);
57    (affinity, collation)
58}