Skip to content

Commit

Permalink
sql: move type name resolution into the name resolution pass
Browse files Browse the repository at this point in the history
  • Loading branch information
benesch authored and Andi Wang committed Apr 11, 2022
1 parent e02a0e9 commit bff5350
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 158 deletions.
14 changes: 7 additions & 7 deletions src/coord/src/catalog/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ use mz_sql::ast::{
AvroSchema, CreateIndexStatement, CreateSinkStatement, CreateSourceConnector,
CreateSourceFormat, CreateSourceStatement, CreateTableStatement, CreateTypeStatement,
CreateViewStatement, CsrConnectorAvro, CsrConnectorProto, CsrSeed, CsrSeedCompiled,
CsrSeedCompiledEncoding, CsrSeedCompiledOrLegacy, CsvColumns, DataType, Format, Function,
Ident, ProtobufSchema, Raw, RawName, SqlOption, Statement, TableFunction, UnresolvedObjectName,
Value, ViewDefinition, WithOption, WithOptionValue,
CsrSeedCompiledEncoding, CsrSeedCompiledOrLegacy, CsvColumns, Format, Function, Ident,
ProtobufSchema, Raw, RawName, SqlOption, Statement, TableFunction, UnresolvedDataType,
UnresolvedObjectName, Value, ViewDefinition, WithOption, WithOptionValue,
};
use mz_sql::plan::resolve_names_stmt;

Expand Down Expand Up @@ -292,8 +292,8 @@ fn ast_rewrite_pg_catalog_char_to_text_0_9_1(
}

impl<'ast> VisitMut<'ast, Raw> for TypeNormalizer {
fn visit_data_type_mut(&mut self, data_type: &'ast mut DataType<Raw>) {
if let DataType::Other { name, typ_mod } = data_type {
fn visit_data_type_mut(&mut self, data_type: &'ast mut UnresolvedDataType) {
if let UnresolvedDataType::Other { name, typ_mod } = data_type {
if name.name() == &*CHAR_REFERENCE {
let t = TEXT_REFERENCE.clone();
*name = match name {
Expand Down Expand Up @@ -555,8 +555,8 @@ fn ast_rewrite_type_references_0_6_1(
struct TypeNormalizer;

impl<'ast> VisitMut<'ast, Raw> for TypeNormalizer {
fn visit_data_type_mut(&mut self, data_type: &'ast mut DataType<Raw>) {
if let DataType::Other { name, .. } = data_type {
fn visit_data_type_mut(&mut self, data_type: &'ast mut UnresolvedDataType) {
if let UnresolvedDataType::Other { name, .. } = data_type {
let mut unresolved_name = name.name().clone();
if unresolved_name.0.len() == 1 {
unresolved_name = UnresolvedObjectName(vec![
Expand Down
4 changes: 2 additions & 2 deletions src/sql-parser/src/ast/defs/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use std::path::PathBuf;
use enum_kinds::EnumKind;

use crate::ast::display::{self, AstDisplay, AstFormatter};
use crate::ast::{AstInfo, DataType, Expr, Ident, SqlOption, UnresolvedObjectName, WithOption};
use crate::ast::{AstInfo, Expr, Ident, SqlOption, UnresolvedObjectName, WithOption};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Schema {
Expand Down Expand Up @@ -815,7 +815,7 @@ impl_display!(KeyConstraint);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ColumnDef<T: AstInfo> {
pub name: Ident,
pub data_type: DataType<T>,
pub data_type: T::DataType,
pub collation: Option<UnresolvedObjectName>,
pub options: Vec<ColumnOptionDef<T>>,
}
Expand Down
4 changes: 2 additions & 2 deletions src/sql-parser/src/ast/defs/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use std::fmt;
use std::mem;

use crate::ast::display::{self, AstDisplay, AstFormatter};
use crate::ast::{AstInfo, DataType, Ident, OrderByExpr, Query, UnresolvedObjectName, Value};
use crate::ast::{AstInfo, Ident, OrderByExpr, Query, UnresolvedObjectName, Value};

/// An SQL expression of any type.
///
Expand Down Expand Up @@ -97,7 +97,7 @@ pub enum Expr<T: AstInfo> {
/// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
Cast {
expr: Box<Expr<T>>,
data_type: DataType<T>,
data_type: T::DataType,
},
/// `expr COLLATE collation`
Collate {
Expand Down
5 changes: 4 additions & 1 deletion src/sql-parser/src/ast/defs/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use std::hash::Hash;
use std::mem;

use crate::ast::display::{self, AstDisplay, AstFormatter};
use crate::ast::{Expr, FunctionArgs, Ident, SqlOption, UnresolvedObjectName};
use crate::ast::{Expr, FunctionArgs, Ident, SqlOption, UnresolvedDataType, UnresolvedObjectName};

// This represents the metadata that lives next to an AST, as we take it through
// various stages in the planning process.
Expand All @@ -43,6 +43,8 @@ use crate::ast::{Expr, FunctionArgs, Ident, SqlOption, UnresolvedObjectName};
pub trait AstInfo: Clone {
// The type used for table references.
type ObjectName: AstDisplay + Clone + Hash + Debug + Eq;
// The type used for data types.
type DataType: AstDisplay + Clone + Hash + Debug + Eq;
// The type stored next to CTEs for their assigned ID.
type Id: Clone + Hash + Debug + Eq;
}
Expand Down Expand Up @@ -81,6 +83,7 @@ impl_display!(RawName);

impl AstInfo for Raw {
type ObjectName = RawName;
type DataType = UnresolvedDataType;
type Id = ();
}

Expand Down
8 changes: 4 additions & 4 deletions src/sql-parser/src/ast/defs/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ use std::fmt;

use crate::ast::display::{self, AstDisplay, AstFormatter};
use crate::ast::{
AstInfo, ColumnDef, CreateSinkConnector, CreateSourceConnector, CreateSourceFormat, DataType,
Envelope, Expr, Format, Ident, KeyConstraint, Query, SourceIncludeMetadata, TableAlias,
TableConstraint, TableWithJoins, UnresolvedObjectName, Value,
AstInfo, ColumnDef, CreateSinkConnector, CreateSourceConnector, CreateSourceFormat, Envelope,
Expr, Format, Ident, KeyConstraint, Query, SourceIncludeMetadata, TableAlias, TableConstraint,
TableWithJoins, UnresolvedObjectName, Value,
};

/// A top-level statement (SELECT, INSERT, CREATE, etc.)
Expand Down Expand Up @@ -1412,7 +1412,7 @@ pub enum SqlOption<T: AstInfo> {
},
DataType {
name: Ident,
data_type: DataType<T>,
data_type: T::DataType,
},
}

Expand Down
26 changes: 13 additions & 13 deletions src/sql-parser/src/ast/defs/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
use std::fmt;
use std::str::FromStr;

use crate::ast::defs::AstInfo;
use crate::ast::display::{self, AstDisplay, AstFormatter};
use crate::ast::RawName;

#[derive(Debug)]
pub struct ValueError(pub(crate) String);
Expand Down Expand Up @@ -224,36 +224,36 @@ impl Default for IntervalValue {

/// SQL data types
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DataType<T: AstInfo> {
pub enum UnresolvedDataType {
/// Array
Array(Box<DataType<T>>),
Array(Box<UnresolvedDataType>),
/// List
List(Box<DataType<T>>),
List(Box<UnresolvedDataType>),
/// Map
Map {
key_type: Box<DataType<T>>,
value_type: Box<DataType<T>>,
key_type: Box<UnresolvedDataType>,
value_type: Box<UnresolvedDataType>,
},
/// Types who don't embed other types, e.g. INT
Other {
name: T::ObjectName,
name: RawName,
/// Typ modifiers appended to the type name, e.g. `numeric(38,0)`.
typ_mod: Vec<u64>,
},
}

impl<T: AstInfo> AstDisplay for DataType<T> {
impl AstDisplay for UnresolvedDataType {
fn fmt<W: fmt::Write>(&self, f: &mut AstFormatter<W>) {
match self {
DataType::Array(ty) => {
UnresolvedDataType::Array(ty) => {
f.write_node(&ty);
f.write_str("[]");
}
DataType::List(ty) => {
UnresolvedDataType::List(ty) => {
f.write_node(&ty);
f.write_str(" list");
}
DataType::Map {
UnresolvedDataType::Map {
key_type,
value_type,
} => {
Expand All @@ -263,7 +263,7 @@ impl<T: AstInfo> AstDisplay for DataType<T> {
f.write_node(&value_type);
f.write_str("]");
}
DataType::Other { name, typ_mod } => {
UnresolvedDataType::Other { name, typ_mod } => {
f.write_node(name);
if typ_mod.len() > 0 {
f.write_str("(");
Expand All @@ -274,4 +274,4 @@ impl<T: AstInfo> AstDisplay for DataType<T> {
}
}
}
impl_display_t!(DataType);
impl_display!(UnresolvedDataType);
103 changes: 53 additions & 50 deletions src/sql-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2971,8 +2971,8 @@ impl<'a> Parser<'a> {
}

/// Parse a SQL datatype (in the context of a CREATE TABLE statement for example)
fn parse_data_type(&mut self) -> Result<DataType<Raw>, ParserError> {
let other = |name: &str| DataType::Other {
fn parse_data_type(&mut self) -> Result<UnresolvedDataType, ParserError> {
let other = |name: &str| UnresolvedDataType::Other {
name: RawName::Name(UnresolvedObjectName::unqualified(name)),
typ_mod: vec![],
};
Expand All @@ -2986,22 +2986,22 @@ impl<'a> Parser<'a> {
} else {
"bpchar"
};
DataType::Other {
UnresolvedDataType::Other {
name: RawName::Name(UnresolvedObjectName::unqualified(name)),
typ_mod: match self.parse_optional_precision()? {
Some(u) => vec![u],
None => vec![],
},
}
}
BPCHAR => DataType::Other {
BPCHAR => UnresolvedDataType::Other {
name: RawName::Name(UnresolvedObjectName::unqualified("bpchar")),
typ_mod: match self.parse_optional_precision()? {
Some(u) => vec![u],
None => vec![],
},
},
VARCHAR => DataType::Other {
VARCHAR => UnresolvedDataType::Other {
name: RawName::Name(UnresolvedObjectName::unqualified("varchar")),
typ_mod: match self.parse_optional_precision()? {
Some(u) => vec![u],
Expand All @@ -3013,7 +3013,7 @@ impl<'a> Parser<'a> {
// Number-like types
BIGINT => other("int8"),
SMALLINT => other("int2"),
DEC | DECIMAL => DataType::Other {
DEC | DECIMAL => UnresolvedDataType::Other {
name: RawName::Name(UnresolvedObjectName::unqualified("numeric")),
typ_mod: self.parse_typ_mod()?,
},
Expand Down Expand Up @@ -3069,16 +3069,16 @@ impl<'a> Parser<'a> {
JSON => other("jsonb"),
_ => {
self.prev_token();
DataType::Other {
UnresolvedDataType::Other {
name: RawName::Name(self.parse_object_name()?),
typ_mod: self.parse_typ_mod()?,
}
}
},
Some(Token::Ident(_)) => {
Some(Token::Ident(_) | Token::LBracket) => {
self.prev_token();
DataType::Other {
name: RawName::Name(self.parse_object_name()?),
UnresolvedDataType::Other {
name: self.parse_raw_name()?,
typ_mod: self.parse_typ_mod()?,
}
}
Expand All @@ -3089,16 +3089,16 @@ impl<'a> Parser<'a> {
match self.peek_token() {
Some(Token::Keyword(LIST)) => {
self.next_token();
data_type = DataType::List(Box::new(data_type));
data_type = UnresolvedDataType::List(Box::new(data_type));
}
Some(Token::LBracket) => {
// Handle array suffixes. Note that `int[]`, `int[][][]`,
// and `int[2][2]` all parse to the same "int array" type.
self.next_token();
let _ = self.maybe_parse(|parser| parser.parse_number_value());
self.expect_token(&Token::RBracket)?;
if !matches!(data_type, DataType::Array(_)) {
data_type = DataType::Array(Box::new(data_type));
if !matches!(data_type, UnresolvedDataType::Array(_)) {
data_type = UnresolvedDataType::Array(Box::new(data_type));
}
}
_ => break,
Expand Down Expand Up @@ -3172,6 +3172,29 @@ impl<'a> Parser<'a> {
}
}

fn parse_raw_name(&mut self) -> Result<RawName, ParserError> {
if self.consume_token(&Token::LBracket) {
let id = match self.next_token() {
Some(Token::Ident(id)) => id,
_ => return parser_err!(self, self.peek_prev_pos(), "expected id"),
};
self.expect_keyword(AS)?;
let name = self.parse_object_name()?;
// TODO(justin): is there a more idiomatic way to detect a fully-qualified name?
if name.0.len() < 2 {
return parser_err!(
self,
self.peek_prev_pos(),
"table name in square brackets must be fully qualified"
);
}
self.expect_token(&Token::RBracket)?;
Ok(RawName::Id(id, name))
} else {
Ok(RawName::Name(self.parse_object_name()?))
}
}

/// Parse a possibly qualified, possibly quoted identifier, e.g.
/// `foo` or `myschema."table"`
fn parse_object_name(&mut self) -> Result<UnresolvedObjectName, ParserError> {
Expand Down Expand Up @@ -3272,13 +3295,13 @@ impl<'a> Parser<'a> {
}
}

fn parse_map(&mut self) -> Result<DataType<Raw>, ParserError> {
fn parse_map(&mut self) -> Result<UnresolvedDataType, ParserError> {
self.expect_token(&Token::LBracket)?;
let key_type = Box::new(self.parse_data_type()?);
self.expect_token(&Token::Op("=>".to_owned()))?;
let value_type = Box::new(self.parse_data_type()?);
self.expect_token(&Token::RBracket)?;
Ok(DataType::Map {
Ok(UnresolvedDataType::Map {
key_type,
value_type,
})
Expand Down Expand Up @@ -3965,45 +3988,25 @@ impl<'a> Parser<'a> {
join: Box::new(table_and_joins),
alias: self.parse_optional_table_alias()?,
})
} else if self.consume_token(&Token::LBracket) {
let id = match self.next_token() {
Some(Token::Ident(id)) => id,
_ => return parser_err!(self, self.peek_prev_pos(), "expected id"),
};
self.expect_keyword(AS)?;
let name = self.parse_object_name()?;
// TODO(justin): is there a more idiomatic way to detect a fully-qualified name?
if name.0.len() < 2 {
return parser_err!(
self,
self.peek_prev_pos(),
"table name in square brackets must be fully qualified"
);
}
self.expect_token(&Token::RBracket)?;

Ok(TableFactor::Table {
name: RawName::Id(id, name),
alias: self.parse_optional_table_alias()?,
})
} else if self.parse_keywords(&[ROWS, FROM]) {
Ok(self.parse_rows_from()?)
} else {
let name = self.parse_object_name()?;
if self.consume_token(&Token::LParen) {
let args = self.parse_optional_args(false)?;
let alias = self.parse_optional_table_alias()?;
let with_ordinality = self.parse_keywords(&[WITH, ORDINALITY]);
Ok(TableFactor::Function {
function: TableFunction { name, args },
alias,
with_ordinality,
})
} else {
Ok(TableFactor::Table {
name: RawName::Name(name),
let name = self.parse_raw_name()?;
match name {
RawName::Name(name) if self.consume_token(&Token::LParen) => {
let args = self.parse_optional_args(false)?;
let alias = self.parse_optional_table_alias()?;
let with_ordinality = self.parse_keywords(&[WITH, ORDINALITY]);
Ok(TableFactor::Function {
function: TableFunction { name, args },
alias,
with_ordinality,
})
}
_ => Ok(TableFactor::Table {
name,
alias: self.parse_optional_table_alias()?,
})
}),
}
}
}
Expand Down
Loading

0 comments on commit bff5350

Please sign in to comment.