diff --git a/src/coord/src/catalog/migrate.rs b/src/coord/src/catalog/migrate.rs index 495fea0fe1051..ca03731035bf9 100644 --- a/src/coord/src/catalog/migrate.rs +++ b/src/coord/src/catalog/migrate.rs @@ -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; @@ -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) { - 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 { @@ -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) { - 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![ diff --git a/src/sql-parser/src/ast/defs/ddl.rs b/src/sql-parser/src/ast/defs/ddl.rs index 729f25a491480..e72f9138cb541 100644 --- a/src/sql-parser/src/ast/defs/ddl.rs +++ b/src/sql-parser/src/ast/defs/ddl.rs @@ -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 { @@ -815,7 +815,7 @@ impl_display!(KeyConstraint); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ColumnDef { pub name: Ident, - pub data_type: DataType, + pub data_type: T::DataType, pub collation: Option, pub options: Vec>, } diff --git a/src/sql-parser/src/ast/defs/expr.rs b/src/sql-parser/src/ast/defs/expr.rs index 5e38d117310a8..c81ccecc32425 100644 --- a/src/sql-parser/src/ast/defs/expr.rs +++ b/src/sql-parser/src/ast/defs/expr.rs @@ -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. /// @@ -97,7 +97,7 @@ pub enum Expr { /// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` Cast { expr: Box>, - data_type: DataType, + data_type: T::DataType, }, /// `expr COLLATE collation` Collate { diff --git a/src/sql-parser/src/ast/defs/query.rs b/src/sql-parser/src/ast/defs/query.rs index 85928751d988e..1bb18744586df 100644 --- a/src/sql-parser/src/ast/defs/query.rs +++ b/src/sql-parser/src/ast/defs/query.rs @@ -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. @@ -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; } @@ -81,6 +83,7 @@ impl_display!(RawName); impl AstInfo for Raw { type ObjectName = RawName; + type DataType = UnresolvedDataType; type Id = (); } diff --git a/src/sql-parser/src/ast/defs/statement.rs b/src/sql-parser/src/ast/defs/statement.rs index 9cb8adf1b279f..9e5ee93edf585 100644 --- a/src/sql-parser/src/ast/defs/statement.rs +++ b/src/sql-parser/src/ast/defs/statement.rs @@ -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.) @@ -1412,7 +1412,7 @@ pub enum SqlOption { }, DataType { name: Ident, - data_type: DataType, + data_type: T::DataType, }, } diff --git a/src/sql-parser/src/ast/defs/value.rs b/src/sql-parser/src/ast/defs/value.rs index 55a1eb27d07da..ba94593e85dcf 100644 --- a/src/sql-parser/src/ast/defs/value.rs +++ b/src/sql-parser/src/ast/defs/value.rs @@ -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); @@ -224,36 +224,36 @@ impl Default for IntervalValue { /// SQL data types #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum DataType { +pub enum UnresolvedDataType { /// Array - Array(Box>), + Array(Box), /// List - List(Box>), + List(Box), /// Map Map { - key_type: Box>, - value_type: Box>, + key_type: Box, + value_type: Box, }, /// 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, }, } -impl AstDisplay for DataType { +impl AstDisplay for UnresolvedDataType { fn fmt(&self, f: &mut AstFormatter) { 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, } => { @@ -263,7 +263,7 @@ impl AstDisplay for DataType { 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("("); @@ -274,4 +274,4 @@ impl AstDisplay for DataType { } } } -impl_display_t!(DataType); +impl_display!(UnresolvedDataType); diff --git a/src/sql-parser/src/parser.rs b/src/sql-parser/src/parser.rs index 7bbbd3fcd354d..e4009d017f8ca 100644 --- a/src/sql-parser/src/parser.rs +++ b/src/sql-parser/src/parser.rs @@ -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, ParserError> { - let other = |name: &str| DataType::Other { + fn parse_data_type(&mut self) -> Result { + let other = |name: &str| UnresolvedDataType::Other { name: RawName::Name(UnresolvedObjectName::unqualified(name)), typ_mod: vec![], }; @@ -2986,7 +2986,7 @@ 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], @@ -2994,14 +2994,14 @@ impl<'a> Parser<'a> { }, } } - 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], @@ -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()?, }, @@ -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()?, } } @@ -3089,7 +3089,7 @@ 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[][][]`, @@ -3097,8 +3097,8 @@ impl<'a> Parser<'a> { 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, @@ -3172,6 +3172,29 @@ impl<'a> Parser<'a> { } } + fn parse_raw_name(&mut self) -> Result { + 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 { @@ -3272,13 +3295,13 @@ impl<'a> Parser<'a> { } } - fn parse_map(&mut self) -> Result, ParserError> { + fn parse_map(&mut self) -> Result { 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, }) @@ -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()?, - }) + }), } } } diff --git a/src/sql-parser/tests/sqlparser_common.rs b/src/sql-parser/tests/sqlparser_common.rs index c2b9632e0a3fc..31398865ddfbe 100644 --- a/src/sql-parser/tests/sqlparser_common.rs +++ b/src/sql-parser/tests/sqlparser_common.rs @@ -27,7 +27,7 @@ use mz_ore::fmt::FormatBuffer; use mz_sql_parser::ast::display::AstDisplay; use mz_sql_parser::ast::visit::Visit; use mz_sql_parser::ast::visit_mut::{self, VisitMut}; -use mz_sql_parser::ast::{AstInfo, Expr, Ident, Raw, RawName}; +use mz_sql_parser::ast::{AstInfo, Expr, Ident, Raw, RawName, UnresolvedDataType}; use mz_sql_parser::parser::{self, ParserError}; #[test] @@ -185,6 +185,11 @@ fn test_basic_visitor() -> Result<(), Box> { } } } + fn visit_data_type(&mut self, data_type: &'a ::DataType) { + if let UnresolvedDataType::Other { name, .. } = data_type { + self.visit_object_name(name) + } + } } let stmts = parser::parse_statements( diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index f8932fca097a4..e7880c6d4368c 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -38,10 +38,10 @@ use mz_sql_parser::ast::display::{AstDisplay, AstFormatter}; use mz_sql_parser::ast::fold::Fold; use mz_sql_parser::ast::visit_mut::{self, VisitMut}; use mz_sql_parser::ast::{ - Assignment, AstInfo, Cte, DataType, DeleteStatement, Distinct, Expr, Function, FunctionArgs, + self, Assignment, AstInfo, Cte, DeleteStatement, Distinct, Expr, Function, FunctionArgs, HomogenizingFunction, Ident, InsertSource, IsExprConstruct, Join, JoinConstraint, JoinOperator, Limit, OrderByExpr, Query, Raw, RawName, Select, SelectItem, SetExpr, SetOperator, Statement, - SubscriptPosition, TableAlias, TableFactor, TableFunction, TableWithJoins, + SubscriptPosition, TableAlias, TableFactor, TableFunction, TableWithJoins, UnresolvedDataType, UnresolvedObjectName, UpdateStatement, Value, Values, }; @@ -118,8 +118,86 @@ impl ResolvedObjectName { } } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub enum ResolvedDataType { + // TODO(benesch): `AnonymousArray` should not exist because that concept + // does not exist in PostgreSQL. Array types exist properly in the catalog + // and should be resolved into `Named` types during name resolution. + AnonymousArray(Box), + AnonymousList(Box), + AnonymousMap { + key_type: Box, + value_type: Box, + }, + Named { + id: GlobalId, + name: PartialName, + modifiers: Vec, + print_id: bool, + }, +} + +impl AstDisplay for ResolvedDataType { + fn fmt(&self, f: &mut AstFormatter) { + match self { + ResolvedDataType::AnonymousArray(element_type) => { + element_type.fmt(f); + f.write_str("[]"); + } + ResolvedDataType::AnonymousList(element_type) => { + element_type.fmt(f); + f.write_str(" list"); + } + ResolvedDataType::AnonymousMap { + key_type, + value_type, + } => { + f.write_str("map["); + key_type.fmt(f); + f.write_str("=>"); + value_type.fmt(f); + f.write_str("]"); + } + ResolvedDataType::Named { + id, + name, + modifiers, + print_id, + } => { + if *print_id { + f.write_str(format!("[{} AS ", id)); + } + if let Some(database) = &name.database { + f.write_node(&Ident::new(database)); + f.write_str("."); + } + if let Some(schema) = &name.schema { + f.write_node(&Ident::new(schema)); + f.write_str("."); + } + f.write_node(&Ident::new(&name.item)); + if *print_id { + f.write_str("]"); + } + if modifiers.len() > 0 { + f.write_str("("); + f.write_node(&ast::display::comma_separated(modifiers)); + f.write_str(")"); + } + } + } + } +} + +impl fmt::Display for ResolvedDataType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.to_ast_string().as_str()) + } +} + impl AstInfo for Aug { type ObjectName = ResolvedObjectName; + type DataType = ResolvedDataType; type Id = Id; } @@ -140,6 +218,55 @@ impl<'a> NameResolver<'a> { ids: HashSet::new(), } } + + fn fold_data_type_internal( + &mut self, + data_type: ::DataType, + ) -> Result<::DataType, PlanError> { + match data_type { + UnresolvedDataType::Array(elem_type) => { + let elem_type = self.fold_data_type_internal(*elem_type)?; + Ok(ResolvedDataType::AnonymousArray(Box::new(elem_type))) + } + UnresolvedDataType::List(elem_type) => { + let elem_type = self.fold_data_type_internal(*elem_type)?; + Ok(ResolvedDataType::AnonymousList(Box::new(elem_type))) + } + UnresolvedDataType::Map { + key_type, + value_type, + } => { + let key_type = self.fold_data_type_internal(*key_type)?; + let value_type = self.fold_data_type_internal(*value_type)?; + Ok(ResolvedDataType::AnonymousMap { + key_type: Box::new(key_type), + value_type: Box::new(value_type), + }) + } + UnresolvedDataType::Other { name, typ_mod } => { + let (name, item) = match name { + RawName::Name(name) => { + let name = normalize::unresolved_object_name(name).unwrap(); + let item = self.catalog.resolve_item(&name)?; + (item.name().clone().into(), item) + } + RawName::Id(id, name) => { + let name = normalize::unresolved_object_name(name).unwrap(); + let gid: GlobalId = id.parse()?; + let item = self.catalog.get_item_by_id(&gid); + (name, item) + } + }; + self.ids.insert(item.id()); + Ok(ResolvedDataType::Named { + id: item.id(), + name, + modifiers: typ_mod, + print_id: true, + }) + } + } + } } impl<'a> Fold for NameResolver<'a> { @@ -265,6 +392,30 @@ impl<'a> Fold for NameResolver<'a> { } } } + + fn fold_data_type( + &mut self, + data_type: ::DataType, + ) -> ::DataType { + match self.fold_data_type_internal(data_type) { + Ok(data_type) => data_type, + Err(e) => { + if self.status.is_ok() { + self.status = Err(e); + } + ResolvedDataType::Named { + id: GlobalId::User(0), + name: PartialName { + database: None, + schema: None, + item: "ignored".into(), + }, + modifiers: vec![], + print_id: false, + } + } + } + } } pub fn resolve_names_stmt( @@ -298,8 +449,8 @@ pub fn resolve_names_expr(qcx: &mut QueryContext, expr: Expr) -> Result, -) -> Result<(DataType, HashSet), PlanError> { + data_type: UnresolvedDataType, +) -> Result<(ResolvedDataType, HashSet), PlanError> { let mut n = NameResolver::new(scx.catalog); let result = n.fold_data_type(data_type); n.status?; @@ -3123,7 +3274,7 @@ fn plan_row(ecx: &ExprContext, exprs: &[Expr]) -> Result, - data_type: &DataType, + data_type: &ResolvedDataType, ) -> Result { let to_scalar_type = scalar_type_from_sql(ecx.qcx.scx, data_type)?; let expr = match expr { @@ -4192,10 +4343,10 @@ fn parser_datetimefield_to_adt( pub fn scalar_type_from_sql( scx: &StatementContext, - data_type: &DataType, + data_type: &ResolvedDataType, ) -> Result { Ok(match data_type { - DataType::Array(elem_type) => { + ResolvedDataType::AnonymousArray(elem_type) => { let elem_type = scalar_type_from_sql(scx, &elem_type)?; if matches!( elem_type, @@ -4203,22 +4354,19 @@ pub fn scalar_type_from_sql( ) { bail_unsupported!(format!("{}[]", scx.humanize_scalar_type(&elem_type))); } - ScalarType::Array(Box::new(elem_type)) } - DataType::List(elem_type) => { + ResolvedDataType::AnonymousList(elem_type) => { let elem_type = scalar_type_from_sql(scx, &elem_type)?; - if matches!(elem_type, ScalarType::Char { .. }) { bail_unsupported!("char list"); } - ScalarType::List { element_type: Box::new(elem_type), custom_oid: None, } } - DataType::Map { + ResolvedDataType::AnonymousMap { key_type, value_type, } => { @@ -4235,41 +4383,32 @@ pub fn scalar_type_from_sql( custom_oid: None, } } - DataType::Other { name, typ_mod } => { - let item = match scx.catalog.resolve_item(&name.raw_name()) { - Ok(i) => i, - Err(_) => sql_bail!( - "type {} does not exist", - name.raw_name().to_string().quoted() - ), - }; - match scx.catalog.try_get_lossy_scalar_type_by_id(&item.id()) { - Some(t) => match t { - ScalarType::Numeric { .. } => { - let scale = numeric::extract_typ_mod(typ_mod)?; - ScalarType::Numeric { scale } - } - ScalarType::Char { .. } => { - let length = mz_repr::adt::char::extract_typ_mod(&typ_mod)?; - ScalarType::Char { length } - } - ScalarType::VarChar { .. } => { - let length = mz_repr::adt::varchar::extract_typ_mod(&typ_mod)?; - ScalarType::VarChar { length } - } - t => { - if !typ_mod.is_empty() { - sql_bail!("{} does not support type modifiers", &name.to_string()); - } - t - } - }, - None => sql_bail!( - "type {} does not exist", - name.raw_name().to_string().quoted() - ), + ResolvedDataType::Named { + id, + name, + modifiers, + print_id: _, + } => match scx.catalog.try_get_lossy_scalar_type_by_id(&id) { + Some(ScalarType::Numeric { .. }) => { + let scale = numeric::extract_typ_mod(modifiers)?; + ScalarType::Numeric { scale } } - } + Some(ScalarType::Char { .. }) => { + let length = mz_repr::adt::char::extract_typ_mod(&modifiers)?; + ScalarType::Char { length } + } + Some(ScalarType::VarChar { .. }) => { + let length = mz_repr::adt::varchar::extract_typ_mod(&modifiers)?; + ScalarType::VarChar { length } + } + Some(t) => { + if !modifiers.is_empty() { + sql_bail!("{} does not support type modifiers", &name.to_string()); + } + t + } + None => sql_bail!("type {} does not exist", name.to_string().quoted()), + }, }) } diff --git a/src/sql/src/plan/statement/ddl.rs b/src/sql/src/plan/statement/ddl.rs index 007299c290629..77b90f3291b0c 100644 --- a/src/sql/src/plan/statement/ddl.rs +++ b/src/sql/src/plan/statement/ddl.rs @@ -58,7 +58,7 @@ use crate::ast::{ CreateSourceConnector, CreateSourceFormat, CreateSourceStatement, CreateTableStatement, CreateTypeAs, CreateTypeStatement, CreateViewStatement, CreateViewsDefinitions, CreateViewsStatement, CsrConnectorAvro, CsrConnectorProto, CsrSeedCompiled, CsvColumns, - DataType, DbzMode, DropDatabaseStatement, DropObjectsStatement, Envelope, Expr, Format, Ident, + DbzMode, DropDatabaseStatement, DropObjectsStatement, Envelope, Expr, Format, Ident, IfExistsBehavior, KafkaConsistency, KeyConstraint, ObjectType, ProtobufSchema, Raw, SourceIncludeMetadataType, SqlOption, Statement, TableConstraint, UnresolvedObjectName, Value, ViewDefinition, WithOption, @@ -68,7 +68,7 @@ use crate::kafka_util; use crate::names::{DatabaseSpecifier, FullName, SchemaName}; use crate::normalize; use crate::plan::error::PlanError; -use crate::plan::query::{resolve_names_data_type, QueryLifetime}; +use crate::plan::query::{resolve_names_data_type, QueryLifetime, ResolvedDataType}; use crate::plan::statement::{StatementContext, StatementDesc}; use crate::plan::{ plan_utils, query, AlterIndexEnablePlan, AlterIndexResetOptionsPlan, AlterIndexSetOptionsPlan, @@ -2105,38 +2105,41 @@ pub fn plan_create_type( let mut ids = vec![]; for key in option_keys { - let item_name = match with_options.remove(&key.to_string()) { - Some(SqlOption::DataType { data_type, .. }) => match data_type { - DataType::Other { name, typ_mod } => { - if !typ_mod.is_empty() { - bail!( - "CREATE TYPE ... AS {}option {} cannot accept type modifier on \ - {}, you must use the default type", - as_type.to_string().quoted(), - key, - name - ) + let item = match with_options.remove(&key.to_string()) { + Some(SqlOption::DataType { data_type, .. }) => { + let (data_type, dt_ids) = resolve_names_data_type(scx, data_type)?; + ids.extend(dt_ids); + match data_type { + ResolvedDataType::Named { + name, + id, + modifiers, + print_id: _, + } => { + if !modifiers.is_empty() { + bail!( + "CREATE TYPE ... AS {}option {} cannot accept type modifier on \ + {}, you must use the default type", + as_type.to_string().quoted(), + key, + name + ) + } + scx.catalog.get_item_by_id(&id) } - name + d => bail!( + "CREATE TYPE ... AS {}option {} can only use named data types, but \ + found unnamed data type {}. Use CREATE TYPE to create a named type first", + as_type.to_string().quoted(), + key, + d.to_ast_string(), + ), } - d => bail!( - "CREATE TYPE ... AS {}option {} can only use named data types, but \ - found unnamed data type {}. Use CREATE TYPE to create a named type first", - as_type.to_string().quoted(), - key, - d.to_ast_string(), - ), - }, + } Some(_) => bail!("{} must be a data type", key), None => bail!("{} parameter required", key), }; - let item = scx - .catalog - .resolve_item(&normalize::unresolved_object_name( - item_name.name().clone(), - )?)?; - let item_id = item.id(); - match scx.catalog.try_get_lossy_scalar_type_by_id(&item_id) { + match scx.catalog.try_get_lossy_scalar_type_by_id(&item.id()) { None => bail!( "{} must be of class type, but received {} which is of class {}", key, @@ -2148,7 +2151,6 @@ pub fn plan_create_type( } _ => {} } - ids.push(item_id); } normalize::ensure_empty_options(&with_options, "CREATE TYPE")?; diff --git a/src/sql/src/plan/statement/show.rs b/src/sql/src/plan/statement/show.rs index 63a286182239f..23a8bd6022ce7 100644 --- a/src/sql/src/plan/statement/show.rs +++ b/src/sql/src/plan/statement/show.rs @@ -30,7 +30,7 @@ use crate::ast::{ use crate::catalog::{CatalogItemType, SessionCatalog}; use crate::names::PartialName; use crate::parse; -use crate::plan::query::{resolve_names_stmt, Aug, ResolvedObjectName}; +use crate::plan::query::{resolve_names_stmt, Aug, ResolvedDataType, ResolvedObjectName}; use crate::plan::statement::{dml, StatementContext, StatementDesc}; use crate::plan::{Params, Plan, SendRowsPlan}; @@ -61,6 +61,18 @@ impl<'ast, 'a> VisitMut<'ast, Aug> for NameSimplifier<'a> { } } } + + fn visit_data_type_mut(&mut self, name: &mut ResolvedDataType) { + if let ResolvedDataType::Named { + id, name, print_id, .. + } = name + { + let item = self.catalog.get_item_by_id(id); + if PartialName::from(item.name().clone()) == *name { + *print_id = false; + } + } + } } pub fn plan_show_create_view( @@ -107,10 +119,16 @@ pub fn plan_show_create_table( ) -> Result { let table = scx.resolve_item(table_name)?; if let CatalogItemType::Table = table.item_type() { + let parsed = parse::parse(table.create_sql())?.into_element(); + let mut resolved = resolve_names_stmt(scx.catalog, parsed)?; + let mut s = NameSimplifier { + catalog: scx.catalog, + }; + s.visit_statement_mut(&mut resolved); Ok(Plan::SendRows(SendRowsPlan { rows: vec![Row::pack_slice(&[ Datum::String(&table.name().to_string()), - Datum::String(table.create_sql()), + Datum::String(&resolved.to_ast_string_stable()), ])], })) } else {