diff --git a/src/ast/dml.rs b/src/ast/dml.rs index ccea7fbcb..9cdb1ca86 100644 --- a/src/ast/dml.rs +++ b/src/ast/dml.rs @@ -182,6 +182,11 @@ pub struct CreateTable { /// BigQuery: Table options list. /// pub options: Option>, + /// Postgres `INHERITs` clause, which contains the list of tables from which + /// the new table inherits. + /// + /// + pub inherits: Option>, /// SQLite "STRICT" clause. /// if the "STRICT" table-option keyword is added to the end, after the closing ")", /// then strict typing rules apply to that table. @@ -405,6 +410,9 @@ impl Display for CreateTable { if let Some(order_by) = &self.order_by { write!(f, " ORDER BY {}", order_by)?; } + if let Some(inherits) = &self.inherits { + write!(f, " INHERITS ({})", display_comma_separated(inherits))?; + } if let Some(partition_by) = self.partition_by.as_ref() { write!(f, " PARTITION BY {partition_by}")?; } diff --git a/src/ast/helpers/stmt_create_table.rs b/src/ast/helpers/stmt_create_table.rs index 344e9dec9..1c50cb843 100644 --- a/src/ast/helpers/stmt_create_table.rs +++ b/src/ast/helpers/stmt_create_table.rs @@ -97,6 +97,7 @@ pub struct CreateTableBuilder { pub cluster_by: Option>>, pub clustered_by: Option, pub options: Option>, + pub inherits: Option>, pub strict: bool, pub copy_grants: bool, pub enable_schema_evolution: Option, @@ -151,6 +152,7 @@ impl CreateTableBuilder { cluster_by: None, clustered_by: None, options: None, + inherits: None, strict: false, copy_grants: false, enable_schema_evolution: None, @@ -331,6 +333,11 @@ impl CreateTableBuilder { self } + pub fn inherits(mut self, inherits: Option>) -> Self { + self.inherits = inherits; + self + } + pub fn strict(mut self, strict: bool) -> Self { self.strict = strict; self @@ -451,6 +458,7 @@ impl CreateTableBuilder { cluster_by: self.cluster_by, clustered_by: self.clustered_by, options: self.options, + inherits: self.inherits, strict: self.strict, copy_grants: self.copy_grants, enable_schema_evolution: self.enable_schema_evolution, @@ -512,6 +520,7 @@ impl TryFrom for CreateTableBuilder { cluster_by, clustered_by, options, + inherits, strict, copy_grants, enable_schema_evolution, @@ -560,6 +569,7 @@ impl TryFrom for CreateTableBuilder { cluster_by, clustered_by, options, + inherits, strict, iceberg, copy_grants, @@ -591,6 +601,7 @@ pub(crate) struct CreateTableConfiguration { pub partition_by: Option>, pub cluster_by: Option>>, pub options: Option>, + pub inherits: Option>, } #[cfg(test)] diff --git a/src/ast/spans.rs b/src/ast/spans.rs index d253f8914..15939a087 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -580,6 +580,7 @@ impl Spanned for CreateTable { cluster_by: _, // todo, BigQuery specific clustered_by: _, // todo, Hive specific options: _, // todo, BigQuery specific + inherits: _, // todo, PostgreSQL specific strict: _, // bool copy_grants: _, // bool enable_schema_evolution: _, // bool diff --git a/src/keywords.rs b/src/keywords.rs index 73c244261..2cf37f8aa 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -430,6 +430,7 @@ define_keywords!( INDEX, INDICATOR, INHERIT, + INHERITS, INITIALLY, INNER, INOUT, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b9076bb77..4c9593ebe 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7050,6 +7050,7 @@ impl<'a> Parser<'a> { .partition_by(create_table_config.partition_by) .cluster_by(create_table_config.cluster_by) .options(create_table_config.options) + .inherits(create_table_config.inherits) .primary_key(primary_key) .strict(strict) .build()) @@ -7070,13 +7071,20 @@ impl<'a> Parser<'a> { } } - /// Parse configuration like partitioning, clustering information during the table creation. + /// Parse configuration like inheritance, partitioning, clustering information during the table creation. /// /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#syntax_2) - /// [PostgreSQL](https://www.postgresql.org/docs/current/ddl-partitioning.html) + /// [PostgreSQL Partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html) + /// [PostgreSQL Inheritance](https://www.postgresql.org/docs/current/ddl-inherit.html) fn parse_optional_create_table_config( &mut self, ) -> Result { + let inherits = if self.parse_keyword(Keyword::INHERITS) { + Some(self.parse_parenthesized_qualified_column_list(IsOptional::Mandatory, false)?) + } else { + None + }; + let partition_by = if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | GenericDialect) && self.parse_keywords(&[Keyword::PARTITION, Keyword::BY]) { @@ -7105,6 +7113,7 @@ impl<'a> Parser<'a> { partition_by, cluster_by, options, + inherits, }) } diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index a421154ad..320583248 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -756,6 +756,7 @@ fn test_duckdb_union_datatype() { cluster_by: Default::default(), clustered_by: Default::default(), options: Default::default(), + inherits: Default::default(), strict: Default::default(), copy_grants: Default::default(), enable_schema_evolution: Default::default(), diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index bcaf527c0..44fd01f17 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1594,6 +1594,7 @@ fn parse_create_table_with_valid_options() { cluster_by: None, clustered_by: None, options: None, + inherits: None, strict: false, iceberg: false, copy_grants: false, @@ -1764,6 +1765,7 @@ fn parse_create_table_with_identity_column() { cluster_by: None, clustered_by: None, options: None, + inherits: None, strict: false, copy_grants: false, enable_schema_evolution: None, diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index a6d65ec75..098d4b1c4 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2733,6 +2733,41 @@ fn parse_create_brin() { } } +#[test] +fn parse_create_table_with_inherits() { + let single_inheritance_sql = + "CREATE TABLE child_table (child_column INT) INHERITS (public.parent_table)"; + match pg().verified_stmt(single_inheritance_sql) { + Statement::CreateTable(CreateTable { + inherits: Some(inherits), + .. + }) => { + assert_eq_vec(&["public", "parent_table"], &inherits[0].0); + } + _ => unreachable!(), + } + + let double_inheritance_sql = "CREATE TABLE child_table (child_column INT) INHERITS (public.parent_table, pg_catalog.pg_settings)"; + match pg().verified_stmt(double_inheritance_sql) { + Statement::CreateTable(CreateTable { + inherits: Some(inherits), + .. + }) => { + assert_eq_vec(&["public", "parent_table"], &inherits[0].0); + assert_eq_vec(&["pg_catalog", "pg_settings"], &inherits[1].0); + } + _ => unreachable!(), + } +} + +#[test] +fn parse_create_table_with_empty_inherits_fails() { + assert!(matches!( + pg().parse_sql_statements("CREATE TABLE child_table (child_column INT) INHERITS ()"), + Err(ParserError::ParserError(_)) + )); +} + #[test] fn parse_create_index_concurrently() { let sql = "CREATE INDEX CONCURRENTLY IF NOT EXISTS my_index ON my_table(col1,col2)"; @@ -5426,6 +5461,7 @@ fn parse_trigger_related_functions() { cluster_by: None, clustered_by: None, options: None, + inherits: None, strict: false, copy_grants: false, enable_schema_evolution: None,