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
//! A compound query's `ORDER BY`, which may only name a result column.
//!
//! Invariant: **a compound's `ORDER BY` is resolved against the compound's
//! output, never against an arm's `FROM` clause**, because the arms do not share
//! one. A term is an ordinal or the name some arm gives a result column.
use inillucent_value::Collation;
use super::{
compound_collation, compound_order_unmatched, no_such_collation, order_out_of_range, Binder,
BoundExpr, BoundOrderTerm, BoundResultColumn, BoundSelect,
};
use crate::ast::{self, Expr, ExprId, NullOrder, SortOrder};
use crate::diagnostic::ParseError;
use crate::lexer::Span;
impl Binder<'_> {
/// Binds a compound's `ORDER BY`, which may only name a result column.
///
/// SQLite resolves a compound's `ORDER BY` against the output of the
/// compound rather than against any arm's FROM clause, because the arms do
/// not share one. A term that is neither an ordinal nor the name of a
/// result column is an error there and is an error here.
///
/// **A name may be one an arm gives its column, not only the first
/// arm's.** `SELECT a FROM t UNION SELECT x FROM u ORDER BY x` sorts the
/// first column, because the second arm calls it `x`; the arms are tried in
/// written order after the first.
///
/// @param terms - the `ORDER BY` terms
/// @param columns - the first arm's result columns, which name the compound's
/// @param arms - the other arms, whose column names also count
pub(super) fn bind_compound_order_by(
&mut self,
terms: &[ast::OrderTerm],
columns: &[BoundResultColumn],
arms: &[(ast::CompoundOp, BoundSelect)],
) -> Result<Vec<BoundOrderTerm>, ParseError> {
let mut bound = Vec::with_capacity(terms.len());
for (at, term) in terms.iter().enumerate() {
let position = at.saturating_add(1);
let span = self.ast.expr_span(term.expr);
let (target, named) = self.order_term_collation(term.expr, span)?;
let index = match self.as_ordinal(target) {
Some(ordinal) => match ordinal.checked_sub(1) {
Some(index) if index < columns.len() => index,
_ => return Err(order_out_of_range(position, columns.len(), span)),
},
None => {
let Some(Expr::Column {
database: None,
table: None,
column,
}) = self.ast.expr(target)
else {
return Err(compound_order_unmatched(position, span));
};
let folded = self.ast.folded(*column).to_vec();
let named_in = |result: &[BoundResultColumn]| {
result
.iter()
.position(|candidate| candidate.name.eq_ignore_ascii_case(&folded))
};
let Some(index) = named_in(columns)
.or_else(|| arms.iter().find_map(|(_, arm)| named_in(&arm.columns)))
else {
return Err(compound_order_unmatched(position, span));
};
index
}
};
let Some(column) = columns.get(index) else {
return Err(order_out_of_range(position, columns.len(), span));
};
// With no `COLLATE` on the term, the result column's own collation
// governs, read the same way the compound's duplicate removal reads
// it - an explicit `COLLATE` on the result column beats the implicit
// one - so the sort and the duplicate removal cannot disagree about
// a column.
let collation = named.unwrap_or_else(|| {
let later = arms
.iter()
.filter_map(|(_, arm)| arm.columns.get(index))
.map(|later| &later.expr);
compound_collation(std::iter::once(&column.expr).chain(later))
});
let nulls = term.nulls.unwrap_or(match term.order {
SortOrder::Ascending => NullOrder::First,
SortOrder::Descending => NullOrder::Last,
});
bound.push(BoundOrderTerm {
expr: BoundExpr::SorterColumn {
column: index as u16,
},
order: term.order,
nulls,
collation,
});
}
Ok(bound)
}
/// Splits a compound `ORDER BY` term into the term itself and the
/// collation an explicit `COLLATE` named on it.
///
/// **`UNION ... ORDER BY a COLLATE NOCASE` was a parse error (task-1979,
/// F15).** A compound's `ORDER BY` may only name a result column, and the
/// match was made against the term exactly as written, so `a COLLATE
/// NOCASE` was an `Expr::Collate` rather than an `Expr::Column` and the
/// term matched nothing. SQLite reads through the `COLLATE`, matches the
/// name underneath it, and sorts that column with the collation the term
/// named rather than the one the column carries.
///
/// @param expr - the term as written
/// @param span - where to point a `no such collation` diagnostic
pub(super) fn order_term_collation(
&self,
expr: ExprId,
span: Span,
) -> Result<(ExprId, Option<Collation>), ParseError> {
let Some(Expr::Collate { operand, collation }) = self.ast.expr(expr) else {
return Ok((expr, None));
};
let name = self.ast.text(*collation);
let Some(named) = self.collation_named(name) else {
return Err(no_such_collation(name, span));
};
Ok((*operand, Some(named)))
}
}