use inillucent_value::Collation;
use super::{comparison_rules, refused, result_collation, Binder, BoundExpr, SubqueryKind};
use crate::ast::{self, BinaryOp, Expr, ExprId, InRhs, SelectBody};
use crate::diagnostic::{ParseError, ParseErrorKind};
use crate::lexer::Span;
impl Binder<'_> {
pub(super) fn bind_row_in(
&mut self,
parts: &[ExprId],
rhs: &InRhs,
negated: bool,
span: Span,
) -> Result<BoundExpr, ParseError> {
let rows = self.row_value_list(rhs).ok_or_else(|| {
ParseError::new(
ParseErrorKind::Unsupported("a row value IN a query rather than a value list"),
span,
)
})?;
let mut bound_lefts = Vec::with_capacity(parts.len());
for part in parts {
bound_lefts.push(self.bind_expr(*part)?);
}
let mut chain: Option<BoundExpr> = None;
for row in &rows {
if row.len() != parts.len() {
return Err(ParseError::new(
ParseErrorKind::Refused(format!(
"row value misused: {} values on the left and {} on the right",
parts.len(),
row.len()
)),
span,
));
}
let mut bound_rights = Vec::with_capacity(row.len());
for value in row {
bound_rights.push(self.bind_expr(*value)?);
}
let one = equality_chain(&bound_lefts, &bound_rights);
chain = Some(match chain {
None => one,
Some(held) => BoundExpr::Or(Box::new(held), Box::new(one)),
});
}
let bound = chain.unwrap_or(BoundExpr::Integer(0));
Ok(if negated {
BoundExpr::Not(Box::new(bound))
} else {
bound
})
}
fn row_value_list(&self, rhs: &InRhs) -> Option<Vec<Vec<ExprId>>> {
match rhs {
InRhs::Select(select) => {
let held = self.ast.select(*select)?;
if !held.compounds.is_empty() || !held.with.ctes.is_empty() {
return None;
}
let core = self.ast.core(held.first)?;
match &core.body {
SelectBody::Values(rows) => Some(rows.clone()),
_ => None,
}
}
InRhs::List(items) => {
let mut rows = Vec::with_capacity(items.len());
for item in items {
rows.push(self.row_value_parts(*item)?);
}
Some(rows)
}
InRhs::Table { .. } => None,
}
}
pub(super) fn row_value_parts(&self, id: ExprId) -> Option<Vec<ExprId>> {
match self.ast.expr(id)? {
Expr::RowValue(parts) => Some(parts.clone()),
_ => None,
}
}
pub(super) fn bind_row_against_query(
&mut self,
op: BinaryOp,
lefts: &[ExprId],
select: ast::SelectId,
span: Span,
) -> Result<BoundExpr, ParseError> {
let block = self.bind_value_subquery(select, span)?;
if block.columns.len() != lefts.len() {
return Err(refused(
format!(
"row value misused: {} values on the left and {} on the right",
lefts.len(),
block.columns.len()
),
span,
));
}
let mut bound_lefts = Vec::with_capacity(lefts.len());
for part in lefts {
bound_lefts.push(self.bind_expr(*part)?);
}
let bound_rights = self.query_columns(block);
compare_bound_rows(op, &bound_lefts, &bound_rights, span)
}
pub(crate) fn bind_query_columns(
&mut self,
select: ast::SelectId,
span: Span,
) -> Result<Vec<BoundExpr>, ParseError> {
let block = self.bind_value_subquery(select, span)?;
Ok(self.query_columns(block))
}
fn query_columns(&mut self, block: crate::bind::BoundSelect) -> Vec<BoundExpr> {
let mut bound_rights = Vec::with_capacity(block.columns.len());
for at in 0..block.columns.len() {
let mut one = block.clone();
one.columns = block
.columns
.get(at..at.saturating_add(1))
.map_or_else(Vec::new, <[crate::bind::BoundResultColumn]>::to_vec);
let collation = one
.columns
.first()
.map(|column| result_collation(&column.expr))
.unwrap_or(Collation::Binary);
bound_rights.push(BoundExpr::Subquery {
id: self.next_subquery_id(),
kind: SubqueryKind::Scalar,
negated: false,
operand: None,
block: Box::new(one),
affinity: None,
collation,
});
}
bound_rights
}
pub(super) fn bind_row_comparison(
&mut self,
op: BinaryOp,
lefts: &[ExprId],
rights: &[ExprId],
span: Span,
) -> Result<BoundExpr, ParseError> {
if lefts.len() != rights.len() || lefts.is_empty() {
return Err(ParseError::new(
ParseErrorKind::Refused(format!(
"row value misused: {} values on the left and {} on the right",
lefts.len(),
rights.len()
)),
span,
));
}
let mut bound_lefts = Vec::with_capacity(lefts.len());
let mut bound_rights = Vec::with_capacity(rights.len());
for (left, right) in lefts.iter().zip(rights.iter()) {
bound_lefts.push(self.bind_expr(*left)?);
bound_rights.push(self.bind_expr(*right)?);
}
match op {
BinaryOp::Equal => Ok(equality_chain(&bound_lefts, &bound_rights)),
BinaryOp::NotEqual => Ok(BoundExpr::Not(Box::new(equality_chain(
&bound_lefts,
&bound_rights,
)))),
BinaryOp::Less | BinaryOp::LessEqual | BinaryOp::Greater | BinaryOp::GreaterEqual => {
Ok(lexicographic_chain(op, &bound_lefts, &bound_rights, 0))
}
_ => Err(ParseError::new(
ParseErrorKind::Refused("row value misused".to_string()),
span,
)),
}
}
}
impl Binder<'_> {
fn bind_row_versus(
&mut self,
op: BinaryOp,
lefts: &[ExprId],
right: ExprId,
span: Span,
) -> Result<BoundExpr, ParseError> {
if let Some(rights) = self.row_value_parts(right) {
return self.bind_row_comparison(op, lefts, &rights, span);
}
if let Some(Expr::Subquery(select)) = self.ast.expr(right) {
let select = *select;
return self.bind_row_against_query(op, lefts, select, span);
}
Err(misused(span))
}
pub(super) fn bind_row_is(
&mut self,
negated: bool,
lefts: &[ExprId],
rights: &[ExprId],
span: Span,
) -> Result<BoundExpr, ParseError> {
if lefts.len() != rights.len() || lefts.is_empty() {
return Err(misused(span));
}
let mut chain: Option<BoundExpr> = None;
for (left, right) in lefts.iter().zip(rights.iter()) {
let left = self.bind_expr(*left)?;
let right = self.bind_expr(*right)?;
let (affinity, collation) = comparison_rules(&left, &right);
let one = BoundExpr::Is {
negated: false,
left: Box::new(left),
right: Box::new(right),
affinity,
collation,
};
chain = Some(match chain {
None => one,
Some(held) => BoundExpr::And(Box::new(held), Box::new(one)),
});
}
let chain = chain.unwrap_or(BoundExpr::Null);
Ok(match negated {
true => BoundExpr::Not(Box::new(chain)),
false => chain,
})
}
pub(super) fn bind_row_between(
&mut self,
negated: bool,
parts: &[ExprId],
low: ExprId,
high: ExprId,
span: Span,
) -> Result<BoundExpr, ParseError> {
let above = self.bind_row_versus(BinaryOp::GreaterEqual, parts, low, span)?;
let below = self.bind_row_versus(BinaryOp::LessEqual, parts, high, span)?;
let both = BoundExpr::And(Box::new(above), Box::new(below));
Ok(match negated {
true => BoundExpr::Not(Box::new(both)),
false => both,
})
}
pub(super) fn bind_row_case(
&mut self,
parts: &[ExprId],
branches: &[(ExprId, ExprId)],
otherwise: Option<ExprId>,
span: Span,
) -> Result<BoundExpr, ParseError> {
let mut bound_branches = Vec::with_capacity(branches.len());
for (when, then) in branches {
let test = self.bind_row_versus(BinaryOp::Equal, parts, *when, span)?;
bound_branches.push((test, self.bind_expr(*then)?));
}
let otherwise = match otherwise {
Some(expr) => Some(Box::new(self.bind_expr(expr)?)),
None => None,
};
Ok(BoundExpr::Case {
operand: None,
branches: bound_branches,
otherwise,
comparisons: Vec::new(),
})
}
}
pub(super) fn misused(span: Span) -> ParseError {
ParseError::new(
ParseErrorKind::Refused("row value misused".to_string()),
span,
)
}
fn equality_chain(lefts: &[BoundExpr], rights: &[BoundExpr]) -> BoundExpr {
let mut chain: Option<BoundExpr> = None;
for (left, right) in lefts.iter().zip(rights.iter()) {
let (affinity, collation) = comparison_rules(left, right);
let one = BoundExpr::Compare {
op: BinaryOp::Equal,
left: Box::new(left.clone()),
right: Box::new(right.clone()),
affinity,
collation,
};
chain = Some(match chain {
None => one,
Some(held) => BoundExpr::And(Box::new(held), Box::new(one)),
});
}
chain.unwrap_or(BoundExpr::Null)
}
fn lexicographic_chain(
op: BinaryOp,
lefts: &[BoundExpr],
rights: &[BoundExpr],
at: usize,
) -> BoundExpr {
let (Some(left), Some(right)) = (lefts.get(at), rights.get(at)) else {
return BoundExpr::Null;
};
let (affinity, collation) = comparison_rules(left, right);
let last = at.saturating_add(1) >= lefts.len();
let strict = match op {
BinaryOp::LessEqual if !last => BinaryOp::Less,
BinaryOp::GreaterEqual if !last => BinaryOp::Greater,
other => other,
};
let decided = BoundExpr::Compare {
op: strict,
left: Box::new(left.clone()),
right: Box::new(right.clone()),
affinity,
collation,
};
if last {
return decided;
}
let same = BoundExpr::Compare {
op: BinaryOp::Equal,
left: Box::new(left.clone()),
right: Box::new(right.clone()),
affinity,
collation,
};
BoundExpr::Or(
Box::new(decided),
Box::new(BoundExpr::And(
Box::new(same),
Box::new(lexicographic_chain(op, lefts, rights, at.saturating_add(1))),
)),
)
}
fn compare_bound_rows(
op: BinaryOp,
lefts: &[BoundExpr],
rights: &[BoundExpr],
span: Span,
) -> Result<BoundExpr, ParseError> {
match op {
BinaryOp::Equal => Ok(equality_chain(lefts, rights)),
BinaryOp::NotEqual => Ok(BoundExpr::Not(Box::new(equality_chain(lefts, rights)))),
BinaryOp::Less | BinaryOp::LessEqual | BinaryOp::Greater | BinaryOp::GreaterEqual => {
Ok(lexicographic_chain(op, lefts, rights, 0))
}
_ => Err(ParseError::new(
ParseErrorKind::Refused("row value misused".to_string()),
span,
)),
}
}