Skip to content

Commit

Permalink
Infer set operations
Browse files Browse the repository at this point in the history
  • Loading branch information
emk committed Oct 30, 2023
1 parent 1e0adc8 commit d0d6fdf
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
17 changes: 16 additions & 1 deletion src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,22 @@ impl InferTypes for ast::QueryExpression {
}
query.infer_types(&scope)
}
ast::QueryExpression::SetOperation { .. } => Err(nyi(self, "set operation")),
ast::QueryExpression::SetOperation {
left,
set_operator,
right,
} => {
let left_ty = left.infer_types(scope)?.0;
let right_ty = right.infer_types(scope)?.0;
let result_ty = left_ty.common_supertype(&right_ty).ok_or_else(|| {
Error::annotated(
format!("cannot combine {} and {}", left_ty, right_ty),
set_operator.span(),
"incompatible types",
)
})?;
Ok((result_ty, scope.clone()))
}
}
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,30 @@ impl TableType {
Ok(())
}

/// Compute the least common supertype of two types. Returns `None` if the
/// only common super type would be top (⊤), which isn't part of our type
/// system.
///
/// For some nice theoretical terminology, see [this
/// page](https://orc.csres.utexas.edu/documentation/html/refmanual/ref.types.subtyping.html).
pub fn common_supertype<'a>(&'a self, other: &'a Self) -> Option<Self> {
// Make sure we have the same number of columns.
if self.columns.len() != other.columns.len() {
return None;
}

// For each column, see if we can merge the column definitions.
let mut columns = Vec::new();
for (a, b) in self.columns.iter().zip(&other.columns) {
if let Some(column) = a.common_supertype(b) {
columns.push(column);
} else {
return None;
}
}
Some(TableType { columns })
}

/// Expect this table to be creatable, i.e., that it contains no aggregate
/// columns or uninhabited types.
pub fn expect_creatable(&self, spanned: &dyn Spanned) -> Result<()> {
Expand Down Expand Up @@ -860,6 +884,26 @@ impl ColumnType {
_ => true,
}
}

/// Merge two column types, if possible. Returns `None` if the only common
/// super type would be top (⊤), which isn't part of our type system.
pub fn common_supertype<'a>(&'a self, other: &'a Self) -> Option<Self> {
// If we have two names, they must match. If we have one or zero names,
// do the best we can.
let name = match (&self.name, &other.name) {
(Some(a), Some(b)) if a == b => Some(a.clone()),
(Some(_), Some(_)) => None,
(Some(a), None) => Some(a.clone()),
(None, Some(b)) => Some(b.clone()),
(None, None) => None,
};
let ty = self.ty.common_supertype(&other.ty)?;
Some(ColumnType {
name,
ty,
not_null: self.not_null && other.not_null,
})
}
}

impl fmt::Display for ColumnType {
Expand Down

0 comments on commit d0d6fdf

Please sign in to comment.