diff --git a/src/sql-parser/src/ast/defs/ddl.rs b/src/sql-parser/src/ast/defs/ddl.rs index b6eac5bce1bc5..6693d6530e8b6 100644 --- a/src/sql-parser/src/ast/defs/ddl.rs +++ b/src/sql-parser/src/ast/defs/ddl.rs @@ -1420,6 +1420,25 @@ impl AstDisplay for KeyConstraint { } impl_display!(KeyConstraint); +/// The source and external reference for a table-fed source, specified in a +/// `CREATE TABLE .. FROM SOURCE .. (REFERENCE ..)` statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TableFromSource { + pub source: T::ItemName, + pub external_reference: UnresolvedItemName, +} + +impl AstDisplay for TableFromSource { + fn fmt(&self, f: &mut AstFormatter) { + f.write_str(" FROM SOURCE "); + f.write_node(&self.source); + f.write_str(" (REFERENCE = "); + f.write_node(&self.external_reference); + f.write_str(")"); + } +} +impl_display_t!(TableFromSource); + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum CreateSourceOptionName { IgnoreKeys, @@ -1465,6 +1484,36 @@ pub struct CreateSourceOption { impl_display_for_with_option!(CreateSourceOption); impl_display_t!(CreateSourceOption); +/// A specification for a column, either just the name or the full definition. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ColumnSpec { + ColumnDef(ColumnDef), + ColumnName(Ident), +} + +impl ColumnSpec { + pub fn is_column_name(&self) -> bool { + matches!(self, ColumnSpec::ColumnName(_)) + } + + pub fn name(&self) -> &Ident { + match self { + ColumnSpec::ColumnDef(def) => &def.name, + ColumnSpec::ColumnName(name) => name, + } + } +} + +impl AstDisplay for ColumnSpec { + fn fmt(&self, f: &mut AstFormatter) { + match self { + ColumnSpec::ColumnDef(def) => f.write_node(def), + ColumnSpec::ColumnName(name) => f.write_node(name), + } + } +} +impl_display_t!(ColumnSpec); + /// SQL column definition #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ColumnDef { diff --git a/src/sql-parser/src/ast/defs/statement.rs b/src/sql-parser/src/ast/defs/statement.rs index 7ddd45dead910..45364a5b0a7de 100644 --- a/src/sql-parser/src/ast/defs/statement.rs +++ b/src/sql-parser/src/ast/defs/statement.rs @@ -27,13 +27,13 @@ use smallvec::{smallvec, SmallVec}; use crate::ast::display::{self, AstDisplay, AstFormatter, WithOptionName}; use crate::ast::{ - AstInfo, ColumnDef, ConnectionOption, ConnectionOptionName, CreateConnectionOption, + AstInfo, ColumnDef, ColumnSpec, ConnectionOption, ConnectionOptionName, CreateConnectionOption, CreateConnectionType, CreateSinkConnection, CreateSourceConnection, CreateSourceFormat, CreateSourceOption, CreateSourceOptionName, DeferredItemName, Expr, Format, Ident, IntervalValue, KeyConstraint, MaterializedViewOption, Query, SelectItem, SinkEnvelope, SourceEnvelope, SourceIncludeMetadata, SubscribeOutput, TableAlias, TableConstraint, - TableWithJoins, UnresolvedDatabaseName, UnresolvedItemName, UnresolvedObjectName, - UnresolvedSchemaName, Value, + TableFromSource, TableWithJoins, UnresolvedDatabaseName, UnresolvedItemName, + UnresolvedObjectName, UnresolvedSchemaName, Value, }; /// A top-level statement (SELECT, INSERT, CREATE, etc.) @@ -1406,11 +1406,14 @@ pub struct CreateTableStatement { /// Table name pub name: UnresolvedItemName, /// Optional schema - pub columns: Vec>, - pub constraints: Vec>, + pub columns: Option>>, + pub constraints: Option>>, pub if_not_exists: bool, pub temporary: bool, pub with_options: Vec>, + /// Optionally specify that this is a read-only table + /// fed by an upstream source. + pub from_source: Option>, } impl AstDisplay for CreateTableStatement { @@ -1422,6 +1425,7 @@ impl AstDisplay for CreateTableStatement { if_not_exists, temporary, with_options, + from_source, } = self; f.write_str("CREATE "); if *temporary { @@ -1432,13 +1436,23 @@ impl AstDisplay for CreateTableStatement { f.write_str("IF NOT EXISTS "); } f.write_node(name); - f.write_str(" ("); - f.write_node(&display::comma_separated(columns)); - if !self.constraints.is_empty() { - f.write_str(", "); - f.write_node(&display::comma_separated(constraints)); + if columns.is_some() || constraints.is_some() { + f.write_str(" ("); + if let Some(columns) = columns { + f.write_node(&display::comma_separated(columns)); + } + + if let Some(constraints) = constraints { + if !constraints.is_empty() { + f.write_str(", "); + f.write_node(&display::comma_separated(constraints)); + } + } + f.write_str(")"); + } + if let Some(from_source) = from_source { + f.write_node(from_source); } - f.write_str(")"); if !with_options.is_empty() { f.write_str(" WITH ("); f.write_node(&display::comma_separated(&self.with_options)); diff --git a/src/sql-parser/src/parser.rs b/src/sql-parser/src/parser.rs index 00949656b7ee9..42d2a84ff26ce 100644 --- a/src/sql-parser/src/parser.rs +++ b/src/sql-parser/src/parser.rs @@ -2649,7 +2649,19 @@ impl<'a> Parser<'a> { let if_not_exists = self.parse_if_not_exists()?; let name = self.parse_item_name()?; - let (columns, constraints) = self.parse_columns(Mandatory)?; + let (columns, constraints) = self + .parse_columns(Mandatory)? + .expect("columns are mandatory"); + let columns = columns + .into_iter() + .map(|c| match c { + ColumnSpec::ColumnName(_) => Err(ParserError::new( + self.peek_prev_pos(), + "Columns must be fully specified in CREATE SUBSOURCE", + )), + ColumnSpec::ColumnDef(c) => Ok(c), + }) + .collect::, ParserError>>()?; let of_source = if self.parse_keyword(OF) { self.expect_keyword(SOURCE)?; @@ -4207,8 +4219,61 @@ impl<'a> Parser<'a> { self.expect_keyword(TABLE)?; let if_not_exists = self.parse_if_not_exists()?; let table_name = self.parse_item_name()?; + // parse optional column list (schema) - let (columns, constraints) = self.parse_columns(Mandatory)?; + let (columns, constraints) = match self.parse_columns(Optional)? { + Some((c, c1)) => (Some(c), Some(c1)), + None => (None, None), + }; + let column_pos = self.peek_prev_pos(); + let column_next_token = self.peek_token(); + + // If this is a read-only table from a source, parse the source and external reference + // and ensure that any columns specified do not include full definitions + let from_source = if self.parse_keywords(&[FROM, SOURCE]) { + let source = self.parse_raw_name()?; + self.expect_token(&Token::LParen)?; + self.expect_keyword(REFERENCE)?; + let _ = self.consume_token(&Token::Eq); + let external_reference = self.parse_item_name()?; + self.expect_token(&Token::RParen)?; + + if columns + .as_ref() + .map_or(false, |cols| cols.iter().any(|c| !c.is_column_name())) + { + return Err(ParserError::new( + column_pos, + "full column definitions are not allowed in CREATE TABLE ... FROM SOURCE", + )); + } + + Some(TableFromSource { + source, + external_reference, + }) + } else { + // If there is no from_source, we should assert that columns were specified (not None) + // and include full definitions + match &columns { + None => { + return self.expected( + column_pos, + "a list of columns in parentheses", + column_next_token, + ) + } + Some(cols) if cols.iter().any(|c| c.is_column_name()) => { + return self.expected( + column_pos, + "a list of columns with types in parentheses", + column_next_token, + ) + } + Some(_) => {} + }; + None + }; let with_options = if self.parse_keyword(WITH) { self.expect_token(&Token::LParen)?; @@ -4230,19 +4295,17 @@ impl<'a> Parser<'a> { if_not_exists, temporary, with_options, + from_source, })) } fn parse_columns( &mut self, optional: IsOptional, - ) -> Result<(Vec>, Vec>), ParserError> { - let mut columns = vec![]; - let mut constraints = vec![]; - + ) -> Result>, Vec>)>, ParserError> { if !self.consume_token(&Token::LParen) { if optional == Optional { - return Ok((columns, constraints)); + return Ok(None); } else { return self.expected( self.peek_pos(), @@ -4251,35 +4314,46 @@ impl<'a> Parser<'a> { ); } } + let mut columns = vec![]; + let mut constraints = vec![]; + if self.consume_token(&Token::RParen) { // Tables with zero columns are a PostgreSQL extension. - return Ok((columns, constraints)); + return Ok(Some((columns, constraints))); } loop { if let Some(constraint) = self.parse_optional_table_constraint()? { constraints.push(constraint); } else if let Some(column_name) = self.consume_identifier()? { - let data_type = self.parse_data_type()?; - let collation = if self.parse_keyword(COLLATE) { - Some(self.parse_item_name()?) - } else { - None - }; - let mut options = vec![]; - loop { - match self.peek_token() { - None | Some(Token::Comma) | Some(Token::RParen) => break, - _ => options.push(self.parse_column_option_def()?), + // Check if this is just a column name or a full column definition. + let col = match self.peek_token() { + Some(Token::Comma) | Some(Token::RParen) => ColumnSpec::ColumnName(column_name), + _ => { + let data_type = self.parse_data_type()?; + let collation = if self.parse_keyword(COLLATE) { + Some(self.parse_item_name()?) + } else { + None + }; + let mut options = vec![]; + loop { + match self.peek_token() { + None | Some(Token::Comma) | Some(Token::RParen) => break, + _ => options.push(self.parse_column_option_def()?), + } + } + + ColumnSpec::ColumnDef(ColumnDef { + name: column_name, + data_type, + collation, + options, + }) } - } + }; - columns.push(ColumnDef { - name: column_name, - data_type, - collation, - options, - }); + columns.push(col); } else { return self.expected( self.peek_pos(), @@ -4300,7 +4374,7 @@ impl<'a> Parser<'a> { } } - Ok((columns, constraints)) + Ok(Some((columns, constraints))) } fn parse_column_option_def(&mut self) -> Result, ParserError> { diff --git a/src/sql-parser/tests/testdata/create b/src/sql-parser/tests/testdata/create index 8ac986d062755..293c06b79403d 100644 --- a/src/sql-parser/tests/testdata/create +++ b/src/sql-parser/tests/testdata/create @@ -193,21 +193,21 @@ CREATE TABLE "table_name" (col_name int) ---- CREATE TABLE table_name (col_name int4) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("table_name")]), columns: [ColumnDef { name: Ident("col_name"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("table_name")]), columns: Some([ColumnDef(ColumnDef { name: Ident("col_name"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE schema_name.table_name (col_name int) ---- CREATE TABLE schema_name.table_name (col_name int4) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("schema_name"), Ident("table_name")]), columns: [ColumnDef { name: Ident("col_name"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("schema_name"), Ident("table_name")]), columns: Some([ColumnDef(ColumnDef { name: Ident("col_name"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE schema_name.table_name (col_name text COLLATE en) ---- CREATE TABLE schema_name.table_name (col_name text COLLATE en) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("schema_name"), Ident("table_name")]), columns: [ColumnDef { name: Ident("col_name"), data_type: Other { name: Name(UnresolvedItemName([Ident("text")])), typ_mod: [] }, collation: Some(UnresolvedItemName([Ident("en")])), options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("schema_name"), Ident("table_name")]), columns: Some([ColumnDef(ColumnDef { name: Ident("col_name"), data_type: Other { name: Name(UnresolvedItemName([Ident("text")])), typ_mod: [] }, collation: Some(UnresolvedItemName([Ident("en")])), options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE "" (col_name int) @@ -239,14 +239,14 @@ CREATE TABLE row (row int) ---- CREATE TABLE row (row int4) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("row")]), columns: [ColumnDef { name: Ident("row"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("row")]), columns: Some([ColumnDef(ColumnDef { name: Ident("row"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE t (x int) WITH (RETAIN HISTORY = FOR '1 day') ---- CREATE TABLE t (x int4) WITH (RETAIN HISTORY = FOR '1 day') => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("x"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [TableOption { name: RetainHistory, value: Some(RetainHistoryFor(String("1 day"))) }] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("x"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [TableOption { name: RetainHistory, value: Some(RetainHistoryFor(String("1 day"))) }], from_source: None }) parse-statement CREATE SOURCE webhook_json IN CLUSTER webhook_cluster FROM WEBHOOK BODY FORMAT JSON INCLUDE HEADERS diff --git a/src/sql-parser/tests/testdata/ddl b/src/sql-parser/tests/testdata/ddl index 0d9abb42ca25f..59f1eda8c3d11 100644 --- a/src/sql-parser/tests/testdata/ddl +++ b/src/sql-parser/tests/testdata/ddl @@ -29,7 +29,7 @@ CREATE TABLE uk_cities ( ---- CREATE TABLE uk_cities (name varchar(100) NOT NULL, lat float8 NULL, lng float8, constrained int4 NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), ref int4 REFERENCES othertable (a, b)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("uk_cities")]), columns: [ColumnDef { name: Ident("name"), data_type: Other { name: Name(UnresolvedItemName([Ident("varchar")])), typ_mod: [100] }, collation: None, options: [ColumnOptionDef { name: None, option: NotNull }] }, ColumnDef { name: Ident("lat"), data_type: Other { name: Name(UnresolvedItemName([Ident("float8")])), typ_mod: [] }, collation: None, options: [ColumnOptionDef { name: None, option: Null }] }, ColumnDef { name: Ident("lng"), data_type: Other { name: Name(UnresolvedItemName([Ident("float8")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("constrained"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [ColumnOptionDef { name: None, option: Null }, ColumnOptionDef { name: Some(Ident("pkey")), option: Unique { is_primary: true } }, ColumnOptionDef { name: None, option: NotNull }, ColumnOptionDef { name: None, option: Unique { is_primary: false } }, ColumnOptionDef { name: None, option: Check(Op { op: Op { namespace: None, op: ">" }, expr1: Identifier([Ident("constrained")]), expr2: Some(Value(Number("0"))) }) }] }, ColumnDef { name: Ident("ref"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [ColumnOptionDef { name: None, option: ForeignKey { foreign_table: UnresolvedItemName([Ident("othertable")]), referred_columns: [Ident("a"), Ident("b")] } }] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("uk_cities")]), columns: Some([ColumnDef(ColumnDef { name: Ident("name"), data_type: Other { name: Name(UnresolvedItemName([Ident("varchar")])), typ_mod: [100] }, collation: None, options: [ColumnOptionDef { name: None, option: NotNull }] }), ColumnDef(ColumnDef { name: Ident("lat"), data_type: Other { name: Name(UnresolvedItemName([Ident("float8")])), typ_mod: [] }, collation: None, options: [ColumnOptionDef { name: None, option: Null }] }), ColumnDef(ColumnDef { name: Ident("lng"), data_type: Other { name: Name(UnresolvedItemName([Ident("float8")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("constrained"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [ColumnOptionDef { name: None, option: Null }, ColumnOptionDef { name: Some(Ident("pkey")), option: Unique { is_primary: true } }, ColumnOptionDef { name: None, option: NotNull }, ColumnOptionDef { name: None, option: Unique { is_primary: false } }, ColumnOptionDef { name: None, option: Check(Op { op: Op { namespace: None, op: ">" }, expr1: Identifier([Ident("constrained")]), expr2: Some(Value(Number("0"))) }) }] }), ColumnDef(ColumnDef { name: Ident("ref"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [ColumnOptionDef { name: None, option: ForeignKey { foreign_table: UnresolvedItemName([Ident("othertable")]), referred_columns: [Ident("a"), Ident("b")] } }] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE t (a int NOT NULL GARBAGE) @@ -50,28 +50,28 @@ CREATE TABLE types_table (char_col char, bpchar_col bpchar, text_col text, bool_ ---- CREATE TABLE types_table (char_col bpchar, bpchar_col bpchar, text_col text, bool_col bool, date_col date, time_col time, timestamp_col timestamp, uuid_col uuid, double_col float8) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("types_table")]), columns: [ColumnDef { name: Ident("char_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("bpchar")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("bpchar_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("bpchar")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("text_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("text")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("bool_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("bool")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("date_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("date")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("time_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("time")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("timestamp_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("uuid_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }, collation: None, options: [] }, ColumnDef { name: Ident("double_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("float8")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("types_table")]), columns: Some([ColumnDef(ColumnDef { name: Ident("char_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("bpchar")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("bpchar_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("bpchar")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("text_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("text")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("bool_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("bool")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("date_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("date")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("time_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("time")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("timestamp_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("uuid_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }, collation: None, options: [] }), ColumnDef(ColumnDef { name: Ident("double_col"), data_type: Other { name: Name(UnresolvedItemName([Ident("float8")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE t ---- error: Expected a list of columns in parentheses, found EOF CREATE TABLE t - ^ + ^ parse-statement CREATE TABLE t () ---- CREATE TABLE t () => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TEMP TABLE t () ---- CREATE TEMPORARY TABLE t () => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [], constraints: [], if_not_exists: false, temporary: true, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([]), constraints: Some([]), if_not_exists: false, temporary: true, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (bar int,) @@ -85,14 +85,14 @@ CREATE TABLE foo (bar int list) ---- CREATE TABLE foo (bar int4 list) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("bar"), data_type: List(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }), collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("bar"), data_type: List(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }), collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (bar int list list) ---- CREATE TABLE foo (bar int4 list list) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("bar"), data_type: List(List(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] })), collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("bar"), data_type: List(List(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] })), collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE tab (foo int, @@ -106,112 +106,112 @@ CREATE TABLE foo (id int, CONSTRAINT address_pkey PRIMARY KEY (address_id)) ---- CREATE TABLE foo (id int4, CONSTRAINT address_pkey PRIMARY KEY (address_id)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Unique { name: Some(Ident("address_pkey")), columns: [Ident("address_id")], is_primary: true, nulls_not_distinct: false }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Unique { name: Some(Ident("address_pkey")), columns: [Ident("address_id")], is_primary: true, nulls_not_distinct: false }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, CONSTRAINT uk_task UNIQUE (report_date, task_id)) ---- CREATE TABLE foo (id int4, CONSTRAINT uk_task UNIQUE (report_date, task_id)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Unique { name: Some(Ident("uk_task")), columns: [Ident("report_date"), Ident("task_id")], is_primary: false, nulls_not_distinct: false }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Unique { name: Some(Ident("uk_task")), columns: [Ident("report_date"), Ident("task_id")], is_primary: false, nulls_not_distinct: false }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, CONSTRAINT uk_task UNIQUE NULLS NOT DISTINCT (report_date, task_id)) ---- CREATE TABLE foo (id int4, CONSTRAINT uk_task UNIQUE NULLS NOT DISTINCT (report_date, task_id)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Unique { name: Some(Ident("uk_task")), columns: [Ident("report_date"), Ident("task_id")], is_primary: false, nulls_not_distinct: true }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Unique { name: Some(Ident("uk_task")), columns: [Ident("report_date"), Ident("task_id")], is_primary: false, nulls_not_distinct: true }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id)) ---- CREATE TABLE foo (id int4, CONSTRAINT customer_address_id_fkey FOREIGN KEY (address_id) REFERENCES public.address(address_id)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [ForeignKey { name: Some(Ident("customer_address_id_fkey")), columns: [Ident("address_id")], foreign_table: Name(UnresolvedItemName([Ident("public"), Ident("address")])), referred_columns: [Ident("address_id")] }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([ForeignKey { name: Some(Ident("customer_address_id_fkey")), columns: [Ident("address_id")], foreign_table: Name(UnresolvedItemName([Ident("public"), Ident("address")])), referred_columns: [Ident("address_id")] }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TEMPORARY TABLE foo (id int, CONSTRAINT ck CHECK (rtrim(ltrim(ref_code)) <> '')) ---- CREATE TEMPORARY TABLE foo (id int4, CONSTRAINT ck CHECK (rtrim(ltrim(ref_code)) <> '')) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Check { name: Some(Ident("ck")), expr: Op { op: Op { namespace: None, op: "<>" }, expr1: Function(Function { name: Name(UnresolvedItemName([Ident("rtrim")])), args: Args { args: [Function(Function { name: Name(UnresolvedItemName([Ident("ltrim")])), args: Args { args: [Identifier([Ident("ref_code")])], order_by: [] }, filter: None, over: None, distinct: false })], order_by: [] }, filter: None, over: None, distinct: false }), expr2: Some(Value(String(""))) } }], if_not_exists: false, temporary: true, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Check { name: Some(Ident("ck")), expr: Op { op: Op { namespace: None, op: "<>" }, expr1: Function(Function { name: Name(UnresolvedItemName([Ident("rtrim")])), args: Args { args: [Function(Function { name: Name(UnresolvedItemName([Ident("ltrim")])), args: Args { args: [Identifier([Ident("ref_code")])], order_by: [] }, filter: None, over: None, distinct: false })], order_by: [] }, filter: None, over: None, distinct: false }), expr2: Some(Value(String(""))) } }]), if_not_exists: false, temporary: true, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, PRIMARY KEY (foo, bar)) ---- CREATE TABLE foo (id int4, PRIMARY KEY (foo, bar)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Unique { name: None, columns: [Ident("foo"), Ident("bar")], is_primary: true, nulls_not_distinct: false }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Unique { name: None, columns: [Ident("foo"), Ident("bar")], is_primary: true, nulls_not_distinct: false }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, UNIQUE (id)) ---- CREATE TABLE foo (id int4, UNIQUE (id)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Unique { name: None, columns: [Ident("id")], is_primary: false, nulls_not_distinct: false }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Unique { name: None, columns: [Ident("id")], is_primary: false, nulls_not_distinct: false }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, FOREIGN KEY (foo, bar) REFERENCES anothertable(foo, bar)) ---- CREATE TABLE foo (id int4, FOREIGN KEY (foo, bar) REFERENCES anothertable(foo, bar)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [ForeignKey { name: None, columns: [Ident("foo"), Ident("bar")], foreign_table: Name(UnresolvedItemName([Ident("anothertable")])), referred_columns: [Ident("foo"), Ident("bar")] }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([ForeignKey { name: None, columns: [Ident("foo"), Ident("bar")], foreign_table: Name(UnresolvedItemName([Ident("anothertable")])), referred_columns: [Ident("foo"), Ident("bar")] }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, CHECK (end_date > start_date OR end_date IS NULL)) ---- CREATE TABLE foo (id int4, CHECK (end_date > start_date OR end_date IS NULL)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Check { name: None, expr: Or { left: Op { op: Op { namespace: None, op: ">" }, expr1: Identifier([Ident("end_date")]), expr2: Some(Identifier([Ident("start_date")])) }, right: IsExpr { expr: Identifier([Ident("end_date")]), construct: Null, negated: false } } }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Check { name: None, expr: Or { left: Op { op: Op { namespace: None, op: ">" }, expr1: Identifier([Ident("end_date")]), expr2: Some(Identifier([Ident("start_date")])) }, right: IsExpr { expr: Identifier([Ident("end_date")]), construct: Null, negated: false } } }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, CHECK (end_date > start_date OR end_date IS UNKNOWN)) ---- CREATE TABLE foo (id int4, CHECK (end_date > start_date OR end_date IS UNKNOWN)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Check { name: None, expr: Or { left: Op { op: Op { namespace: None, op: ">" }, expr1: Identifier([Ident("end_date")]), expr2: Some(Identifier([Ident("start_date")])) }, right: IsExpr { expr: Identifier([Ident("end_date")]), construct: Unknown, negated: false } } }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Check { name: None, expr: Or { left: Op { op: Op { namespace: None, op: ">" }, expr1: Identifier([Ident("end_date")]), expr2: Some(Identifier([Ident("start_date")])) }, right: IsExpr { expr: Identifier([Ident("end_date")]), construct: Unknown, negated: false } } }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE foo (id int, CHECK (start_date IS TRUE)) ---- CREATE TABLE foo (id int4, CHECK (start_date IS TRUE)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: [ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] }], constraints: [Check { name: None, expr: IsExpr { expr: Identifier([Ident("start_date")]), construct: True, negated: false } }], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("foo")]), columns: Some([ColumnDef(ColumnDef { name: Ident("id"), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([Check { name: None, expr: IsExpr { expr: Identifier([Ident("start_date")]), construct: True, negated: false } }]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TEMP TABLE t (c schema.type) ---- CREATE TEMPORARY TABLE t (c schema.type) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: true, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: true, with_options: [], from_source: None }) parse-statement CREATE TABLE t (c db.schema.type) ---- CREATE TABLE t (c db.schema.type) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE t (c "db"."schema"."type") ---- CREATE TABLE t (c db.schema.type) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE t (c something.db.schema.type) ---- CREATE TABLE t (c something.db.schema.type) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("something"), Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("something"), Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TEMP TABLE t (c db.schema.type(0,1,100)) ---- CREATE TEMPORARY TABLE t (c db.schema.type(0, 1, 100)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [0, 1, 100] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: true, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("db"), Ident("schema"), Ident("type")])), typ_mod: [0, 1, 100] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: true, with_options: [], from_source: None }) parse-statement CREATE TABLE t (c time with time zone (0,1,100)) @@ -234,19 +234,54 @@ error: Expected literal integer, found right parenthesis CREATE TABLE t (c t(1,)) ^ +parse-statement +CREATE TABLE t (c, d) +---- +error: Expected a list of columns with types in parentheses, found EOF +CREATE TABLE t (c, d) + ^ + parse-statement CREATE TABLE t (c "type"(1)) ---- CREATE TABLE t (c type(1)) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("type")])), typ_mod: [1] }, collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: Other { name: Name(UnresolvedItemName([Ident("type")])), typ_mod: [1] }, collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) parse-statement CREATE TABLE t (c "type"(1) list list) ---- CREATE TABLE t (c type(1) list list) => -CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: [ColumnDef { name: Ident("c"), data_type: List(List(Other { name: Name(UnresolvedItemName([Ident("type")])), typ_mod: [1] })), collation: None, options: [] }], constraints: [], if_not_exists: false, temporary: false, with_options: [] }) +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnDef(ColumnDef { name: Ident("c"), data_type: List(List(Other { name: Name(UnresolvedItemName([Ident("type")])), typ_mod: [1] })), collation: None, options: [] })]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: None }) + +parse-statement +CREATE TABLE t (c int4, d int4) FROM SOURCE foo (REFERENCE bar) +---- +error: full column definitions are not allowed in CREATE TABLE ... FROM SOURCE +CREATE TABLE t (c int4, d int4) FROM SOURCE foo (REFERENCE bar) + ^ + +parse-statement +CREATE TABLE t (c, d) FROM SOURCE foo (REFERENCE bar) +---- +CREATE TABLE t (c, d) FROM SOURCE foo (REFERENCE = bar) +=> +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: Some([ColumnName(Ident("c")), ColumnName(Ident("d"))]), constraints: Some([]), if_not_exists: false, temporary: false, with_options: [], from_source: Some(TableFromSource { source: Name(UnresolvedItemName([Ident("foo")])), external_reference: UnresolvedItemName([Ident("bar")]) }) }) + +parse-statement +CREATE TABLE t FROM SOURCE foo +---- +error: Expected left parenthesis, found EOF +CREATE TABLE t FROM SOURCE foo + ^ + +parse-statement +CREATE TABLE t FROM SOURCE foo (REFERENCE = baz) +---- +CREATE TABLE t FROM SOURCE foo (REFERENCE = baz) +=> +CreateTable(CreateTableStatement { name: UnresolvedItemName([Ident("t")]), columns: None, constraints: None, if_not_exists: false, temporary: false, with_options: [], from_source: Some(TableFromSource { source: Name(UnresolvedItemName([Ident("foo")])), external_reference: UnresolvedItemName([Ident("baz")]) }) }) parse-statement CREATE DATABASE IF EXISTS foo diff --git a/src/sql/src/normalize.rs b/src/sql/src/normalize.rs index 1e998521b8457..ebe4c009221d7 100644 --- a/src/sql/src/normalize.rs +++ b/src/sql/src/normalize.rs @@ -21,7 +21,7 @@ use mz_repr::{ColumnName, GlobalId}; use mz_sql_parser::ast::display::AstDisplay; use mz_sql_parser::ast::visit_mut::{self, VisitMut}; use mz_sql_parser::ast::{ - CreateConnectionStatement, CreateIndexStatement, CreateMaterializedViewStatement, + ColumnSpec, CreateConnectionStatement, CreateIndexStatement, CreateMaterializedViewStatement, CreateSecretStatement, CreateSinkStatement, CreateSourceStatement, CreateSubsourceStatement, CreateTableStatement, CreateTypeStatement, CreateViewStatement, CreateWebhookSourceStatement, CteBlock, Function, FunctionArgs, Ident, IfExistsBehavior, MutRecBlock, Op, Query, Statement, @@ -303,6 +303,7 @@ pub fn create_statement( if_not_exists, temporary, with_options: _, + from_source: _, }) => { *name = if *temporary { allocate_temporary_name(name)? @@ -310,8 +311,17 @@ pub fn create_statement( allocate_name(name)? }; let mut normalizer = QueryNormalizer::new(); - for c in columns { - normalizer.visit_column_def_mut(c); + if let Some(columns) = columns { + for c in columns { + match c { + ColumnSpec::ColumnDef(inn) => { + normalizer.visit_column_def_mut(inn); + } + ColumnSpec::ColumnName(inn) => { + normalizer.visit_ident_mut(inn); + } + }; + } } if let Some(err) = normalizer.err { return Err(err); diff --git a/src/sql/src/plan/statement/ddl.rs b/src/sql/src/plan/statement/ddl.rs index 1ea50bfee10a1..a60fc5e40f6e6 100644 --- a/src/sql/src/plan/statement/ddl.rs +++ b/src/sql/src/plan/statement/ddl.rs @@ -235,8 +235,31 @@ pub fn plan_create_table( if_not_exists, temporary, with_options, + from_source, } = &stmt; + if let Some(_) = from_source { + // TODO: Do something with from_source + sql_bail!("CREATE TABLE .. FROM SOURCE is not yet supported") + } + + let columns = match columns { + Some(columns) => columns + .into_iter() + .map(|c| match c { + ast::ColumnSpec::ColumnDef(c) => Ok(c), + ast::ColumnSpec::ColumnName(_) => { + bail_unsupported!("CREATE TABLE without column defs") + } + }) + .collect::, PlanError>>()?, + None => sql_bail!("CREATE TABLE must specify column definitions"), + }; + let constraints = match constraints { + Some(constraints) => constraints, + None => sql_bail!("CREATE TABLE must specify constraint definitions"), + }; + let names: Vec<_> = columns .iter() .map(|c| normalize::column_name(c.name.clone())) diff --git a/src/sqllogictest/src/runner.rs b/src/sqllogictest/src/runner.rs index 4c79cab99863d..486a3b1604454 100644 --- a/src/sqllogictest/src/runner.rs +++ b/src/sqllogictest/src/runner.rs @@ -2554,9 +2554,12 @@ fn mutate(sql: &str) -> Vec { on_name: RawItemName::Name(stmt.name.clone()), key_parts: Some( stmt.columns - .iter() - .map(|def| Expr::Identifier(vec![def.name.clone()])) - .collect(), + .map(|cols| { + cols.iter() + .map(|def| Expr::Identifier(vec![def.name().clone()])) + .collect() + }) + .unwrap_or_default(), ), with_options: Vec::new(), if_not_exists: false,