use super::{
build_values_physical_rows, qualify_source_operator_with_columns, ColumnPrune, CteScope,
Engine, SQLError, SQLParam, ScopedEngineHook, SourceEvalContext, SourcePlan,
};
use uqa_execution::PhysicalOperator;
pub(super) fn build_values_source_operator<'a>(
engine: &'a Engine,
from: &SourcePlan,
params: &'a [SQLParam],
ctes: &CteScope,
prune: Option<&ColumnPrune>,
) -> Result<Box<dyn PhysicalOperator + 'a>, SQLError> {
match from {
SourcePlan::Values {
rows,
alias,
column_aliases,
internal_relation,
internal_column_types,
} => {
let inferred_types = crate::sql::select::values_types_in_scope(
engine,
rows,
&ctes.scalar_subqueries,
None,
params,
ctes,
)?;
let column_types = if internal_relation.is_some() {
if !alias.is_none() || !column_aliases.is_empty() {
return Err(SQLError::Internal(
"internal VALUES carrier has SQL-visible aliases".into(),
));
}
if !rows.is_empty()
&& rows
.iter()
.any(|row| row.len() != internal_column_types.len())
{
return Err(SQLError::Internal(
"internal VALUES carrier row width does not match its declared attributes"
.into(),
));
}
internal_column_types.clone()
} else {
inferred_types
};
let source_columns = if column_aliases.is_empty() {
(0..rows.first().map_or(0, Vec::len))
.map(|index| format!("column{}", index + 1))
.collect::<Vec<_>>()
} else {
column_aliases.clone()
};
let hook = ScopedEngineHook::new(engine, ctes);
let context =
SourceEvalContext::new(engine, params, &hook, &hook, &ctes.scalar_subqueries);
let rows = build_values_physical_rows(&context, rows, &column_types)?;
if let Some(relation) = internal_relation {
let schema =
uqa_execution::RowSchema::with_internal_relation_types(*relation, column_types);
return Ok(Box::new(uqa_execution::TableScan::from_physical_rows(
schema, rows,
)));
}
let schema = uqa_execution::RowSchema::with_types(source_columns.clone(), column_types);
let operator: Box<dyn uqa_execution::PhysicalOperator + 'a> =
Box::new(uqa_execution::TableScan::from_physical_rows(schema, rows));
Ok(qualify_source_operator_with_columns(
operator,
&source_columns,
alias.as_deref().unwrap_or_default(),
prune,
&[],
ctes.lock_identities.emit,
))
}
_ => unreachable!("VALUES source builder called for a different source kind"),
}
}