Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions fuzz/fuzz_seeds/postgres_alter_table_attach_partition.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE ONLY public.root ATTACH PARTITION public.part FOR VALUES FROM (0) TO (10);
ALTER TABLE root ATTACH PARTITION part DEFAULT
15 changes: 15 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,15 @@ pub enum AlterTableOperation {
/// Partition expression to attach.
partition: Partition,
},
/// `ATTACH PARTITION partition_name { FOR VALUES partition_bound_spec | DEFAULT }`
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertable.html)
AttachPartitionOf {
/// The table to attach as a partition.
partition: ObjectName,
/// The partition bound.
for_values: ForValues,
},
/// `DETACH PART|PARTITION <partition_expr>`
/// Note: this is a ClickHouse-specific operation, please refer to
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/partition#detach-partitionpart)
Expand Down Expand Up @@ -930,6 +939,12 @@ impl fmt::Display for AlterTableOperation {
AlterTableOperation::AttachPartition { partition } => {
write!(f, "ATTACH {partition}")
}
AlterTableOperation::AttachPartitionOf {
partition,
for_values,
} => {
write!(f, "ATTACH PARTITION {partition} {for_values}")
}
AlterTableOperation::DetachPartition { partition } => {
write!(f, "DETACH {partition}")
}
Expand Down
4 changes: 4 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,10 @@ impl Spanned for AlterTableOperation {
drop_behavior: _,
} => union_spans(column_names.iter().map(|i| i.span)),
AlterTableOperation::AttachPartition { partition } => partition.span(),
AlterTableOperation::AttachPartitionOf {
partition,
for_values,
} => partition.span().union(&for_values.span()),
AlterTableOperation::DetachPartition { partition } => partition.span(),
AlterTableOperation::FreezePartition {
partition,
Expand Down
8 changes: 8 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1541,6 +1541,14 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if the dialect supports attaching a table as a partition with
/// `ALTER TABLE ... ATTACH PARTITION name { FOR VALUES ... | DEFAULT }`.
///
/// <https://www.postgresql.org/docs/current/sql-altertable.html>
fn supports_attach_partition_of(&self) -> bool {
false
}

/// Returns true if the specified keyword is reserved and cannot be
/// used as an identifier without special handling like quoting.
fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
Expand Down
5 changes: 5 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,4 +569,9 @@ impl Dialect for PostgreSqlDialect {
fn supports_add_column_if_not_exists(&self) -> bool {
true
}

/// See <https://www.postgresql.org/docs/current/sql-altertable.html>
fn supports_attach_partition_of(&self) -> bool {
true
}
}
7 changes: 7 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11528,6 +11528,13 @@ impl<'a> Parser<'a> {
{
let new_owner = self.parse_owner()?;
AlterTableOperation::OwnerTo { new_owner }
} else if self.dialect.supports_attach_partition_of()
&& self.parse_keywords(&[Keyword::ATTACH, Keyword::PARTITION])
{
AlterTableOperation::AttachPartitionOf {
partition: self.parse_object_name(false)?,
for_values: self.parse_partition_for_values()?,
}
} else if dialect_of!(self is ClickHouseDialect|GenericDialect)
&& self.parse_keyword(Keyword::ATTACH)
{
Expand Down
52 changes: 52 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11115,3 +11115,55 @@ fn display_column_unique_nulls_distinct_propagates_formatter_errors() {
let stmt = pg_and_generic().verified_stmt("CREATE TABLE t (u INT UNIQUE NULLS NOT DISTINCT)");
assert_eq!(write!(RejectingWriter, "{stmt}"), Err(fmt::Error));
}

#[test]
fn parse_alter_table_attach_partition_of() {
let sql =
"ALTER TABLE ONLY public.root ATTACH PARTITION public.part FOR VALUES FROM (0) TO (10)";
let Statement::AlterTable(alter) = pg().verified_stmt(sql) else {
unreachable!()
};
assert_eq!(
alter.operations,
vec![AlterTableOperation::AttachPartitionOf {
partition: ObjectName::from(vec![Ident::new("public"), Ident::new("part")]),
for_values: ForValues::From {
from: vec![PartitionBoundValue::Expr(Expr::value(number("0")))],
to: vec![PartitionBoundValue::Expr(Expr::value(number("10")))],
},
}]
);

pg().verified_stmt("ALTER TABLE root ATTACH PARTITION part DEFAULT");
pg().verified_stmt("ALTER TABLE root ATTACH PARTITION part FOR VALUES IN (1, 2)");
// As written by pg_dump.
pg().one_statement_parses_to(
"ALTER TABLE ONLY public.hroot ATTACH PARTITION public.part4 FOR VALUES WITH (modulus 4, remainder 0)",
"ALTER TABLE ONLY public.hroot ATTACH PARTITION public.part4 FOR VALUES WITH (MODULUS 4, REMAINDER 0)",
);

for (sql, expected) in [
(
"ALTER TABLE root ATTACH PARTITION part",
"Expected: FOR, found: EOF",
),
(
"ALTER TABLE root ATTACH PARTITION",
"Expected: identifier, found: EOF",
),
(
"ALTER TABLE root ATTACH PARTITION part FOR VALUES IN ()",
"Expected: at least one value, found: )",
),
(
"ALTER TABLE root ATTACH PARTITION part FOR VALUES",
"Expected: IN, FROM, or WITH after FOR VALUES, found: EOF",
),
] {
assert_eq!(
pg().parse_sql_statements(sql).unwrap_err().to_string(),
format!("sql parser error: {expected}"),
"{sql}"
);
}
}
Loading