use crate::schema::{ColumnTypeSchema, ScalarTypeSchema};
use crate::{ColumnType, RowSchema, SQLError};
pub struct ColumnReference<'a> {
pub attribute_number: i64,
pub ty: &'a ColumnType,
pub collation_oid: i64,
}
pub trait ExpressionSchema: ScalarTypeSchema {
fn column_reference(
&self,
name: &str,
qualifier: Option<&str>,
) -> Result<ColumnReference<'_>, SQLError>;
fn whole_row_reference(&self, qualifier: &str) -> Result<ColumnReference<'_>, SQLError> {
self.whole_row_type(qualifier)
.map(|ty| ColumnReference {
attribute_number: 0,
ty,
collation_oid: 0,
})
.ok_or_else(|| SQLError::UnknownTable(qualifier.into()))
}
}
impl ExpressionSchema for RowSchema {
fn column_reference(
&self,
name: &str,
qualifier: Option<&str>,
) -> Result<ColumnReference<'_>, SQLError> {
let position = qualifier
.map_or_else(
|| self.unqualified_position(name),
|qualifier| self.qualified_position(qualifier, name),
)
.ok_or_else(|| SQLError::UnknownColumn(name.into()))?;
let ty = self
.column_type(position)
.ok_or_else(|| SQLError::Internal("catalog column has no declared type".into()))?;
Ok(ColumnReference {
attribute_number: i64::try_from(position + 1)
.map_err(|_| SQLError::Internal("column ordinal overflow".into()))?,
ty,
collation_oid: i64::from(
self.column_collation(position)
.expect("typed catalog column collation"),
),
})
}
}
impl ExpressionSchema for ColumnTypeSchema<'_> {
fn column_reference(
&self,
name: &str,
qualifier: Option<&str>,
) -> Result<ColumnReference<'_>, SQLError> {
if qualifier.is_some_and(|qualifier| !self.has_qualifier(qualifier)) {
return Err(SQLError::UnknownColumn(name.into()));
}
let mut matching = self
.columns()
.iter()
.enumerate()
.filter(|(_, column)| column.name == name);
let Some((position, column)) = matching.next() else {
if let Some(ty) = self.system_column_type(name) {
return Ok(ColumnReference {
attribute_number: -6,
ty,
collation_oid: 0,
});
}
if qualifier.is_none() && self.has_qualifier(name) {
return self.whole_row_reference(name);
}
return Err(SQLError::UnknownColumn(name.into()));
};
if matching.next().is_some() {
return Err(SQLError::UnknownColumn(name.into()));
}
let attribute_number = column.attribute_number.map_or_else(
|| {
i64::try_from(position + 1)
.map_err(|_| SQLError::Internal("column ordinal overflow".into()))
},
|number| Ok(i64::from(number)),
)?;
if attribute_number <= 0 {
return Err(SQLError::Internal(
"invalid catalog attribute number".into(),
));
}
Ok(ColumnReference {
attribute_number,
ty: &column.ty,
collation_oid: crate::catalog::collations::declarations::column_oid(column),
})
}
}