use crate::{errors::ExecutorError, schema::CombinedSchema};
#[derive(Debug, Clone, Copy)]
pub(crate) enum ColumnPositionResult {
Position(i64),
Negative(i64),
NotAPosition,
}
pub(crate) fn extract_column_position(expr: &vibesql_ast::Expression) -> ColumnPositionResult {
match expr {
vibesql_ast::Expression::Literal(vibesql_types::SqlValue::Integer(pos)) => {
ColumnPositionResult::Position(*pos)
}
vibesql_ast::Expression::UnaryOp { op, expr } => {
if let vibesql_ast::Expression::Literal(vibesql_types::SqlValue::Integer(pos)) =
expr.as_ref()
{
match op {
vibesql_ast::UnaryOperator::Plus => ColumnPositionResult::Position(*pos),
vibesql_ast::UnaryOperator::Minus => ColumnPositionResult::Negative(*pos),
_ => ColumnPositionResult::NotAPosition,
}
} else {
ColumnPositionResult::NotAPosition
}
}
_ => ColumnPositionResult::NotAPosition,
}
}
pub(crate) fn validate_column_position(
pos: i64,
column_count: usize,
term_index: usize,
) -> Result<usize, ExecutorError> {
if pos <= 0 || (pos as usize) > column_count {
return Err(ExecutorError::OrderByOutOfRange {
term_position: term_index + 1, column_number: pos,
select_list_len: column_count,
});
}
Ok((pos as usize) - 1) }
pub(crate) fn resolve_position_to_column_name(
idx: usize,
select_list: &[vibesql_ast::SelectItem],
) -> Option<String> {
if let vibesql_ast::SelectItem::Expression { expr, alias, .. } = &select_list[idx] {
Some(if let Some(alias_name) = alias {
alias_name.clone()
} else if let vibesql_ast::Expression::ColumnRef(col_id) = expr {
col_id.column_canonical().to_string()
} else if let vibesql_ast::Expression::AggregateFunction { name, .. } = expr {
name.to_lowercase()
} else {
format!("col{}", idx + 1)
})
} else {
None
}
}
pub(crate) enum ResolvedPosition<'a> {
Expression(&'a vibesql_ast::Expression),
ColumnName {
table: Option<String>,
column: String,
},
NotFound,
}
pub(crate) fn resolve_position_with_wildcards<'a>(
target_col: usize,
select_list: &'a [vibesql_ast::SelectItem],
schema: Option<&CombinedSchema>,
) -> ResolvedPosition<'a> {
let mut current_col = 0;
for item in select_list {
match item {
vibesql_ast::SelectItem::Wildcard { .. } => {
let wildcard_cols = if let Some(s) = schema {
s.table_schemas.values().map(|(_, ts)| ts.columns.len()).sum()
} else {
1
};
if target_col < current_col + wildcard_cols {
if let Some(s) = schema {
let offset_in_wildcard = target_col - current_col;
let mut all_columns: Vec<(usize, String, String)> = Vec::new();
for (table_id, (start_idx, table_schema)) in &s.table_schemas {
for (i, col) in table_schema.columns.iter().enumerate() {
all_columns.push((
start_idx + i,
table_id.table_canonical().to_string(),
col.name.clone(),
));
}
}
all_columns.sort_by_key(|(idx, _, _)| *idx);
if offset_in_wildcard < all_columns.len() {
let (_, table, column) = all_columns[offset_in_wildcard].clone();
return ResolvedPosition::ColumnName { table: Some(table), column };
}
}
return ResolvedPosition::NotFound;
}
current_col += wildcard_cols;
}
vibesql_ast::SelectItem::QualifiedWildcard { qualifier, .. } => {
let qualified_cols = if let Some(s) = schema {
s.get_table(qualifier).map(|(_, ts)| ts.columns.len()).unwrap_or(1)
} else {
1
};
if target_col < current_col + qualified_cols {
if let Some(s) = schema {
if let Some((_, table_schema)) = s.get_table(qualifier) {
let offset = target_col - current_col;
if offset < table_schema.columns.len() {
return ResolvedPosition::ColumnName {
table: Some(qualifier.to_ascii_lowercase()),
column: table_schema.columns[offset].name.clone(),
};
}
}
}
return ResolvedPosition::NotFound;
}
current_col += qualified_cols;
}
vibesql_ast::SelectItem::Expression { expr, .. } => {
if target_col == current_col {
return ResolvedPosition::Expression(expr);
}
current_col += 1;
}
}
}
ResolvedPosition::NotFound
}
pub(crate) fn count_select_columns(
select_list: &[vibesql_ast::SelectItem],
schema: Option<&CombinedSchema>,
) -> usize {
let mut count = 0;
for item in select_list {
match item {
vibesql_ast::SelectItem::Wildcard { .. } => {
if let Some(s) = schema {
for (_, table_schema) in s.table_schemas.values() {
count += table_schema.columns.len();
}
} else {
count += 1;
}
}
vibesql_ast::SelectItem::QualifiedWildcard { qualifier, .. } => {
if let Some(s) = schema {
if let Some((_, table_schema)) = s.get_table(qualifier) {
count += table_schema.columns.len();
} else {
count += 1;
}
} else {
count += 1;
}
}
vibesql_ast::SelectItem::Expression { .. } => {
count += 1;
}
}
}
count
}