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
//! A result column alias used in a `WHERE` clause.
//!
//! Invariant: **an alias is looked at only after every column of every visible
//! FROM term has failed to match, and it stands for the expression it names.**
//! `SELECT a*2 AS d FROM t WHERE d > 2` is accepted by SQLite, which resolves
//! `d` to `a*2`. A real column of the same name wins, the first of two equal
//! aliases wins, and an alias that names an aggregate is the usual refusal
//! because a `WHERE` cannot hold one.
use super::{Binder, BoundExpr};
use crate::ast;
use crate::diagnostic::ParseError;
impl Binder<'_> {
/// Makes a block's result column aliases visible to its `WHERE`.
///
/// Returns whether a block was recorded, which the caller pops with
/// [`Binder::forget_where_aliases`] once the `WHERE` is bound.
///
/// @param columns - the block's result columns as written
pub(super) fn offer_where_aliases(&mut self, columns: &[ast::ResultColumn]) -> bool {
// Nearly no statement names an alias in its `WHERE`, and a compile has
// an allocation budget, so a block with no alias at all is not recorded.
if columns.iter().all(|column| column.alias.is_none()) {
return false;
}
let block = columns
.iter()
.filter_map(|column| {
column
.alias
.map(|alias| (self.ast.folded(alias).to_vec(), column.expr))
})
.collect();
self.where_aliases.push(block);
true
}
/// Takes the innermost block's aliases away again.
pub(super) fn forget_where_aliases(&mut self) {
self.where_aliases.pop();
}
/// Binds the expression a name stands for when it is a result column alias
/// of the block being bound or of one around it.
///
/// The expression is bound here, in the scope of the `WHERE` that named it,
/// with no alias visible, so one alias cannot name another, and with
/// aggregates refused.
///
/// @param folded - the lowercase name that matched no column
pub(super) fn bind_where_alias(
&mut self,
folded: &[u8],
) -> Result<Option<BoundExpr>, ParseError> {
let named = self.where_aliases.iter().rev().find_map(|block| {
block
.iter()
.find(|(name, _)| name.as_slice() == folded)
.map(|(_, expr)| *expr)
});
let Some(expr) = named else {
return Ok(None);
};
let aliases = core::mem::take(&mut self.where_aliases);
let allowed = core::mem::replace(&mut self.allow_aggregates, false);
let bound = self.bind_expr(expr);
self.allow_aggregates = allowed;
self.where_aliases = aliases;
bound.map(Some)
}
}