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 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,
});
}
compare_bound_rows(op, &bound_lefts, &bound_rights, span)
}
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,
)),
}
}
}
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,
)),
}
}