Skip to content

Commit

Permalink
Refactor types and inference
Browse files Browse the repository at this point in the history
  • Loading branch information
emk committed Oct 28, 2023
1 parent 82383f3 commit 488df6b
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 44 deletions.
6 changes: 2 additions & 4 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,9 +724,7 @@ impl Emit for Except {
pub enum Expression {
Literal(Literal),
BoolValue(Keyword),
Null {
null_token: Keyword,
},
Null(Keyword),
Interval(IntervalExpression),
ColumnName(Ident),
TableAndColumnName(TableAndColumnName),
Expand Down Expand Up @@ -1975,7 +1973,7 @@ peg::parser! {
paren1:p("(") expression:expression() paren2:p(")") { Expression::Parens { paren1, expression: Box::new(expression), paren2 } }
literal:literal() { Expression::Literal(literal) }
bool_token:(k("TRUE") / k("FALSE")) { Expression::BoolValue(bool_token) }
null_token:k("NULL") { Expression::Null { null_token } }
null_token:k("NULL") { Expression::Null(null_token) }
interval_expression:interval_expression() { Expression::Interval(interval_expression) }
cast:cast() { Expression::Cast(cast) }
array_agg:array_agg() { Expression::ArrayAgg(array_agg) }
Expand Down
76 changes: 45 additions & 31 deletions src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#![allow(dead_code)]

use crate::{
ast::{self, SelectList},
ast,
errors::{Error, Result},
scope::{CaseInsensitiveIdent, Scope, ScopeHandle},
tokenizer::{Ident, Literal, LiteralValue, Spanned},
Expand Down Expand Up @@ -114,7 +114,7 @@ impl InferTypes for ast::CreateTableStatement {
let ty = ValueType::try_from(&column.data_type)?;
let col_ty = ColumnType {
name: Some(column.name.clone()),
ty,
ty: ArgumentType::Value(ty),
// TODO: We don't support this in the main grammar yet.
not_null: false,
};
Expand Down Expand Up @@ -199,7 +199,7 @@ impl InferTypes for ast::Row {
let ty = ty.expect_value_type(expr)?.to_owned();
cols.push(ColumnType {
name: None,
ty,
ty: ArgumentType::Value(ty),
not_null: false,
});
}
Expand Down Expand Up @@ -270,7 +270,7 @@ impl InferTypes for ast::SelectExpression {

let ast::SelectExpression {
//select_options,
select_list: SelectList {
select_list: ast::SelectList {
items: select_list, ..
},
from_clause,
Expand Down Expand Up @@ -299,6 +299,7 @@ impl InferTypes for ast::SelectExpression {
// TODO: Delay assigning anonymous names until we actually
// create a table?
let (ty, _scope) = expression.infer_types(&scope)?;
// Make sure any aggregates have been turned into values.
let ty = ty.expect_value_type(expression)?.to_owned();
let name = alias
.infer_column_name()
Expand All @@ -313,7 +314,7 @@ impl InferTypes for ast::SelectExpression {

cols.push(ColumnType {
name: Some(name),
ty,
ty: ArgumentType::Value(ty),
not_null: false,
});
}
Expand Down Expand Up @@ -360,7 +361,7 @@ impl InferTypes for ast::FromItem {
if let Some(column_name) = &column.name {
scope.add(
column_name.clone().into(),
Type::Argument(ArgumentType::Value(column.ty.clone())),
Type::Argument(column.ty.clone()),
)?;
}
}
Expand All @@ -387,31 +388,9 @@ impl InferTypes for ast::Expression {
let ty = scope.get_or_err(&ident)?.try_as_argument_type(&ident)?;
Ok((ty.to_owned(), scope.clone()))
}
ast::Expression::TableAndColumnName(ast::TableAndColumnName {
table_name,
column_name,
..
}) => {
let table = ident_from_table_name(table_name)?;
let table_type = scope.get_or_err(&table)?.try_as_table_type(&table)?;
let column_type = table_type.column_by_name_or_err(column_name)?;
// TODO: Actually, we ought to be able store `ArgumentType` in a
// column. See the test case `scoped_aggregates.sql`.
Ok((arg(column_type.ty.to_owned()), scope.clone()))
}
ast::Expression::Cast(ast::Cast {
expression,
data_type,
..
}) => {
expression.infer_types(scope)?;
let ty = ValueType::try_from(&*data_type)?;
Ok((arg(ty), scope.clone()))
}
ast::Expression::FunctionCall(fcall) => {
let (ty, _) = fcall.infer_types(scope)?;
Ok((ty, scope.clone()))
}
ast::Expression::TableAndColumnName(name) => name.infer_types(scope),
ast::Expression::Cast(cast) => cast.infer_types(scope),
ast::Expression::FunctionCall(fcall) => fcall.infer_types(scope),
_ => Err(nyi(self, "expression")),
}
}
Expand All @@ -433,6 +412,41 @@ impl InferTypes for LiteralValue {
}
}

impl InferTypes for ast::TableAndColumnName {
type Type = ArgumentType;

fn infer_types(&mut self, scope: &ScopeHandle) -> Result<(Self::Type, ScopeHandle)> {
let ast::TableAndColumnName {
table_name,
column_name,
..
} = self;

let table = ident_from_table_name(table_name)?;
let table_type = scope.get_or_err(&table)?.try_as_table_type(&table)?;
let column_type = table_type.column_by_name_or_err(column_name)?;
// TODO: Actually, we ought to be able store `ArgumentType` in a
// column. See the test case `scoped_aggregates.sql`.
Ok((column_type.ty.to_owned(), scope.clone()))
}
}

impl InferTypes for ast::Cast {
type Type = ArgumentType;

fn infer_types(&mut self, scope: &ScopeHandle) -> Result<(Self::Type, ScopeHandle)> {
let ast::Cast {
expression,
data_type,
..
} = self;
// TODO: Pass through aggregate status.
expression.infer_types(scope)?;
let ty = ValueType::try_from(&*data_type)?;
Ok((ArgumentType::Value(ty), scope.clone()))
}
}

impl InferTypes for ast::FunctionCall {
type Type = ArgumentType;

Expand Down
30 changes: 21 additions & 9 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,22 +702,34 @@ pub struct ColumnType {
/// The name of the column, if it has one. Anonymous columns may be produced
/// using things like `SELECT 1`, but they cannot occur in stored tables.
pub name: Option<Ident>,
pub ty: ValueType,
/// This needs to be an ArgumentType, not a value type, because we may be
/// aggregating over a table, rendering some table entries into aggregate
/// types.
pub ty: ArgumentType,
pub not_null: bool,
}

impl ColumnType {
/// Return an error if this column is not storable.
pub fn expect_creatable(&self, spanned: &dyn Spanned) -> Result<()> {
self.ty.expect_inhabited(spanned)?;
if self.name.is_none() {
return Err(Error::annotated(
"cannot store a table with anonymous columns",
match &self.ty {
ArgumentType::Value(ty) => {
ty.expect_inhabited(spanned)?;
if self.name.is_none() {
return Err(Error::annotated(
"cannot store a table with anonymous columns",
spanned.span(),
"type mismatch",
));
}
Ok(())
}
ArgumentType::Aggregating(_) => Err(Error::annotated(
"Cannot store an aggregate column",
spanned.span(),
"type mismatch",
));
)),
}
Ok(())
}

/// Is this column a subtype of `other`, ignoring nullability?
Expand Down Expand Up @@ -1070,10 +1082,10 @@ peg::parser! {
}

rule column_type() -> ColumnType
= ty:value_type() not_null:not_null() {
= ty:argument_type() not_null:not_null() {
ColumnType { name: None, ty, not_null }
}
/ name:ident() _ ty:value_type() not_null:not_null() {
/ name:ident() _ ty:argument_type() not_null:not_null() {
ColumnType { name: Some(name), ty, not_null }
}

Expand Down

0 comments on commit 488df6b

Please sign in to comment.