use super::*;
pub(super) enum NestedHits {
Some(Vec<u16>),
NoneButNamed,
Unrelated,
}
pub(super) fn nested_hits(
names: &[NestedName],
column: &[u8],
qualifier: Option<&[u8]>,
) -> NestedHits {
let named_table = qualifier.is_none_or(|wanted| names.iter().any(|held| held.table == wanted));
if !named_table {
return NestedHits::Unrelated;
}
let hits: Vec<u16> = names
.iter()
.filter(|held| held.column == column && qualifier.is_none_or(|wanted| held.table == wanted))
.map(|held| held.index)
.collect();
if hits.is_empty() {
NestedHits::NoneButNamed
} else {
NestedHits::Some(hits)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct NestedName {
pub table: Vec<u8>,
pub column: Vec<u8>,
pub index: u16,
}
#[derive(Clone, Copy)]
pub(super) struct Origin {
pub source: usize,
pub column: u16,
pub hidden: bool,
pub merged: bool,
}
pub(super) fn prefer_shown(source: &BoundSource, hits: Vec<u16>, unqualified: bool) -> Vec<u16> {
if !unqualified {
return hits;
}
let shown: Vec<u16> = hits
.iter()
.copied()
.filter(|index| source.table.column(*index).is_some_and(|held| !held.hidden))
.collect();
if shown.is_empty() {
hits
} else {
shown
}
}
impl Binder<'_> {
pub(super) fn expose_using_columns(
&mut self,
bound: &mut BoundSelect,
) -> Result<Vec<Origin>, ParseError> {
let mut plan: Vec<Origin> = Vec::new();
for source in &bound.sources {
let derived = source.table.kind == TableKind::Subquery;
for (index, column) in source.table.columns.iter().enumerate() {
let position = index as u16;
let hides = source.suppressed.contains(&position) || (derived && column.hidden);
if hides || !column.hidden {
plan.push(Origin {
source: source.id,
column: position,
hidden: hides,
merged: false,
});
}
}
}
let shown = plan.iter().filter(|origin| !origin.hidden).count();
if shown != bound.columns.len() {
return Ok(Vec::new());
}
let mut old = std::mem::take(&mut bound.columns).into_iter();
let mut origins = Vec::with_capacity(plan.len());
for origin in plan {
if origin.hidden {
let result = self.hidden_result_column(origin.source, origin.column)?;
bound.columns.push(result);
origins.push(origin);
continue;
}
let Some(result) = old.next() else {
continue;
};
let own = self.hidden_result_column(origin.source, origin.column)?;
let merged = result.expr != own.expr;
bound.columns.push(result);
origins.push(Origin { merged, ..origin });
if merged {
bound.columns.push(own);
origins.push(Origin {
hidden: true,
..origin
});
}
}
Ok(origins)
}
fn hidden_result_column(
&mut self,
source: usize,
column: u16,
) -> Result<BoundResultColumn, ParseError> {
let expr = self.column_expr(source, column)?;
let info = self
.sources
.get(source)
.and_then(|held| held.table.column(column));
Ok(BoundResultColumn {
expr,
name: info.map(|held| held.name.clone()).unwrap_or_default(),
origin: None,
declared_type: info
.map(|held| held.declared_type.clone())
.unwrap_or_default(),
written: None,
})
}
pub(super) fn hide_derived_columns(&mut self, id: usize, hidden: &[bool]) {
if !hidden.iter().any(|flag| *flag) {
return;
}
let Some(source) = self.sources.get_mut(id) else {
return;
};
let table = std::rc::Rc::make_mut(&mut source.table);
for (column, flag) in table.columns.iter_mut().zip(hidden) {
column.hidden = *flag;
}
}
pub(super) fn nested_names_of(
&self,
bound: &BoundSelect,
origins: &[Origin],
) -> Vec<NestedName> {
let mut names = Vec::new();
for (index, column) in bound.columns.iter().enumerate() {
let origin = match origins.get(index) {
Some(known) if origins.len() == bound.columns.len() => {
Some((known.source, known.column))
}
_ => match &column.expr {
BoundExpr::Column { source, column, .. } => Some((*source, *column)),
BoundExpr::Function { arguments, .. } => match arguments.first() {
Some(BoundExpr::Column { source, column, .. }) => Some((*source, *column)),
_ => None,
},
_ => None,
},
};
let Some((source, inner)) = origin else {
continue;
};
let merged = origins.get(index).is_some_and(|known| known.merged);
let through = self
.nested_names_for(source)
.and_then(|held| held.iter().find(|name| name.index == inner));
if let Some(name) = through {
names.push(NestedName {
table: if merged {
Vec::new()
} else {
name.table.clone()
},
column: name.column.clone(),
index: index as u16,
});
continue;
}
let Some(held) = self.sources.get(source) else {
continue;
};
let Some(info) = held.table.column(inner) else {
continue;
};
names.push(NestedName {
table: if merged {
Vec::new()
} else {
held.alias.to_ascii_lowercase()
},
column: info.folded.clone(),
index: index as u16,
});
}
names
}
}