Skip to content

Commit f4ce662

Browse files
committed
feat: add alter column and rename constraint
1 parent 1e75231 commit f4ce662

File tree

6 files changed

+245
-3
lines changed

6 files changed

+245
-3
lines changed

src/ast/ddl.rs

+57
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ pub enum AlterTableOperation {
6767
data_type: DataType,
6868
options: Vec<ColumnOption>,
6969
},
70+
/// `RENAME CONSTRAINT <old_constraint_name> TO <new_constraint_name>`
71+
///
72+
/// Note: this is a PostgreSQL-specific operation.
73+
RenameConstraint { old_name: Ident, new_name: Ident },
74+
/// `ALTER [ COLUMN ]`
75+
AlterColumn {
76+
column_name: Ident,
77+
op: AlterColumnOperation,
78+
},
7079
}
7180

7281
impl fmt::Display for AlterTableOperation {
@@ -85,6 +94,9 @@ impl fmt::Display for AlterTableOperation {
8594
AlterTableOperation::AddColumn { column_def } => {
8695
write!(f, "ADD COLUMN {}", column_def.to_string())
8796
}
97+
AlterTableOperation::AlterColumn { column_name, op } => {
98+
write!(f, "ALTER COLUMN {} {}", column_name, op)
99+
}
88100
AlterTableOperation::DropPartitions {
89101
partitions,
90102
if_exists,
@@ -139,6 +151,51 @@ impl fmt::Display for AlterTableOperation {
139151
write!(f, " {}", display_separated(options, " "))
140152
}
141153
}
154+
AlterTableOperation::RenameConstraint { old_name, new_name } => {
155+
write!(f, "RENAME CONSTRAINT {} TO {}", old_name, new_name)
156+
}
157+
}
158+
}
159+
}
160+
161+
/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
162+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
163+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
164+
pub enum AlterColumnOperation {
165+
/// `SET NOT NULL`
166+
SetNotNull,
167+
/// `DROP NOT NULL`
168+
DropNotNull,
169+
/// `SET DEFAULT <expr>`
170+
SetDefault { value: Expr },
171+
/// `DROP DEFAULT`
172+
DropDefault,
173+
/// `[SET DATA] TYPE <data_type> [USING <expr>]`
174+
Type {
175+
data_type: DataType,
176+
/// PostgreSQL specific
177+
using: Option<Expr>,
178+
},
179+
}
180+
181+
impl fmt::Display for AlterColumnOperation {
182+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183+
match self {
184+
AlterColumnOperation::SetNotNull => write!(f, "SET NOT NULL",),
185+
AlterColumnOperation::DropNotNull => write!(f, "DROP NOT NULL",),
186+
AlterColumnOperation::SetDefault { value } => {
187+
write!(f, "SET DEFAULT {}", value)
188+
}
189+
AlterColumnOperation::DropDefault {} => {
190+
write!(f, "DROP DEFAULT")
191+
}
192+
AlterColumnOperation::Type { data_type, using } => {
193+
if let Some(expr) = using {
194+
write!(f, "SET DATA TYPE {} USING {}", data_type, expr)
195+
} else {
196+
write!(f, "SET DATA TYPE {}", data_type)
197+
}
198+
}
142199
}
143200
}
144201
}

src/ast/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ use serde::{Deserialize, Serialize};
3131

3232
pub use self::data_type::DataType;
3333
pub use self::ddl::{
34-
AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, ReferentialAction,
35-
TableConstraint,
34+
AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef,
35+
ReferentialAction, TableConstraint,
3636
};
3737
pub use self::operator::{BinaryOperator, UnaryOperator};
3838
pub use self::query::{

src/keywords.rs

+2
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ define_keywords!(
162162
CURRENT_USER,
163163
CURSOR,
164164
CYCLE,
165+
DATA,
165166
DATABASE,
166167
DATE,
167168
DAY,
@@ -469,6 +470,7 @@ define_keywords!(
469470
TRUE,
470471
TRUNCATE,
471472
TRY_CAST,
473+
TYPE,
472474
UESCAPE,
473475
UNBOUNDED,
474476
UNCOMMITTED,

src/parser.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,12 @@ impl<'a> Parser<'a> {
19081908
}
19091909
}
19101910
} else if self.parse_keyword(Keyword::RENAME) {
1911-
if self.parse_keyword(Keyword::TO) {
1911+
if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT) {
1912+
let old_name = self.parse_identifier()?;
1913+
self.expect_keyword(Keyword::TO)?;
1914+
let new_name = self.parse_identifier()?;
1915+
AlterTableOperation::RenameConstraint { old_name, new_name }
1916+
} else if self.parse_keyword(Keyword::TO) {
19121917
let table_name = self.parse_object_name()?;
19131918
AlterTableOperation::RenameTable { table_name }
19141919
} else {
@@ -1978,6 +1983,38 @@ impl<'a> Parser<'a> {
19781983
data_type,
19791984
options,
19801985
}
1986+
} else if self.parse_keyword(Keyword::ALTER) {
1987+
let _ = self.parse_keyword(Keyword::COLUMN);
1988+
let column_name = self.parse_identifier()?;
1989+
let is_postgresql = dialect_of!(self is PostgreSqlDialect);
1990+
1991+
let op = if self.parse_keywords(&[Keyword::SET, Keyword::NOT, Keyword::NULL]) {
1992+
AlterColumnOperation::SetNotNull {}
1993+
} else if self.parse_keywords(&[Keyword::DROP, Keyword::NOT, Keyword::NULL]) {
1994+
AlterColumnOperation::DropNotNull {}
1995+
} else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT]) {
1996+
AlterColumnOperation::SetDefault {
1997+
value: self.parse_expr()?,
1998+
}
1999+
} else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) {
2000+
AlterColumnOperation::DropDefault {}
2001+
} else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE])
2002+
|| (is_postgresql && self.parse_keyword(Keyword::TYPE))
2003+
{
2004+
let data_type = self.parse_data_type()?;
2005+
let using = if is_postgresql && self.parse_keyword(Keyword::USING) {
2006+
Some(self.parse_expr()?)
2007+
} else {
2008+
None
2009+
};
2010+
AlterColumnOperation::Type { data_type, using }
2011+
} else {
2012+
return self.expected(
2013+
"SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN",
2014+
self.peek_token(),
2015+
);
2016+
};
2017+
AlterTableOperation::AlterColumn { column_name, op }
19812018
} else {
19822019
return self.expected(
19832020
"ADD, RENAME, PARTITION or DROP after ALTER TABLE",

tests/sqlparser_common.rs

+102
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,108 @@ fn parse_alter_table_drop_column() {
19411941
}
19421942
}
19431943

1944+
#[test]
1945+
fn parse_alter_table_alter_column() {
1946+
let alter_stmt = "ALTER TABLE tab";
1947+
match verified_stmt(&format!(
1948+
"{} ALTER COLUMN is_active SET NOT NULL",
1949+
alter_stmt
1950+
)) {
1951+
Statement::AlterTable {
1952+
name,
1953+
operation: AlterTableOperation::AlterColumn { column_name, op },
1954+
} => {
1955+
assert_eq!("tab", name.to_string());
1956+
assert_eq!("is_active", column_name.to_string());
1957+
assert_eq!(op, AlterColumnOperation::SetNotNull {});
1958+
}
1959+
_ => unreachable!(),
1960+
}
1961+
1962+
one_statement_parses_to(
1963+
"ALTER TABLE tab ALTER is_active DROP NOT NULL",
1964+
"ALTER TABLE tab ALTER COLUMN is_active DROP NOT NULL",
1965+
);
1966+
1967+
match verified_stmt(&format!(
1968+
"{} ALTER COLUMN is_active SET DEFAULT false",
1969+
alter_stmt
1970+
)) {
1971+
Statement::AlterTable {
1972+
name,
1973+
operation: AlterTableOperation::AlterColumn { column_name, op },
1974+
} => {
1975+
assert_eq!("tab", name.to_string());
1976+
assert_eq!("is_active", column_name.to_string());
1977+
assert_eq!(
1978+
op,
1979+
AlterColumnOperation::SetDefault {
1980+
value: Expr::Value(Value::Boolean(false))
1981+
}
1982+
);
1983+
}
1984+
_ => unreachable!(),
1985+
}
1986+
1987+
match verified_stmt(&format!(
1988+
"{} ALTER COLUMN is_active DROP DEFAULT",
1989+
alter_stmt
1990+
)) {
1991+
Statement::AlterTable {
1992+
name,
1993+
operation: AlterTableOperation::AlterColumn { column_name, op },
1994+
} => {
1995+
assert_eq!("tab", name.to_string());
1996+
assert_eq!("is_active", column_name.to_string());
1997+
assert_eq!(op, AlterColumnOperation::DropDefault {});
1998+
}
1999+
_ => unreachable!(),
2000+
}
2001+
}
2002+
2003+
#[test]
2004+
fn parse_alter_table_alter_column_type() {
2005+
let alter_stmt = "ALTER TABLE tab";
2006+
match verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT") {
2007+
Statement::AlterTable {
2008+
name,
2009+
operation: AlterTableOperation::AlterColumn { column_name, op },
2010+
} => {
2011+
assert_eq!("tab", name.to_string());
2012+
assert_eq!("is_active", column_name.to_string());
2013+
assert_eq!(
2014+
op,
2015+
AlterColumnOperation::Type {
2016+
data_type: DataType::Text,
2017+
using: None,
2018+
}
2019+
);
2020+
}
2021+
_ => unreachable!(),
2022+
}
2023+
2024+
let res = Parser::parse_sql(
2025+
&GenericDialect {},
2026+
&format!("{} ALTER COLUMN is_active TYPE TEXT", alter_stmt),
2027+
);
2028+
assert_eq!(
2029+
ParserError::ParserError("Expected SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN, found: TYPE".to_string()),
2030+
res.unwrap_err()
2031+
);
2032+
2033+
let res = Parser::parse_sql(
2034+
&GenericDialect {},
2035+
&format!(
2036+
"{} ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'",
2037+
alter_stmt
2038+
),
2039+
);
2040+
assert_eq!(
2041+
ParserError::ParserError("Expected end of statement, found: USING".to_string()),
2042+
res.unwrap_err()
2043+
);
2044+
}
2045+
19442046
#[test]
19452047
fn parse_bad_constraint() {
19462048
let res = parse_sql_statements("ALTER TABLE tab ADD");

tests/sqlparser_postgres.rs

+44
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,50 @@ fn parse_create_table_constraints_only() {
263263
};
264264
}
265265

266+
#[test]
267+
fn parse_alter_table_constraints_rename() {
268+
match pg().verified_stmt("ALTER TABLE tab RENAME CONSTRAINT old_name TO new_name") {
269+
Statement::AlterTable {
270+
name,
271+
operation: AlterTableOperation::RenameConstraint { old_name, new_name },
272+
} => {
273+
assert_eq!("tab", name.to_string());
274+
assert_eq!(old_name.to_string(), "old_name");
275+
assert_eq!(new_name.to_string(), "new_name");
276+
}
277+
_ => unreachable!(),
278+
}
279+
}
280+
281+
#[test]
282+
fn parse_alter_table_alter_column() {
283+
pg().one_statement_parses_to(
284+
"ALTER TABLE tab ALTER COLUMN is_active TYPE TEXT USING 'text'",
285+
"ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'",
286+
);
287+
288+
match pg()
289+
.verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'")
290+
{
291+
Statement::AlterTable {
292+
name,
293+
operation: AlterTableOperation::AlterColumn { column_name, op },
294+
} => {
295+
assert_eq!("tab", name.to_string());
296+
assert_eq!("is_active", column_name.to_string());
297+
let using_expr = Expr::Value(Value::SingleQuotedString("text".to_string()));
298+
assert_eq!(
299+
op,
300+
AlterColumnOperation::Type {
301+
data_type: DataType::Text,
302+
using: Some(using_expr),
303+
}
304+
);
305+
}
306+
_ => unreachable!(),
307+
}
308+
}
309+
266310
#[test]
267311
fn parse_create_table_if_not_exists() {
268312
let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()";

0 commit comments

Comments
 (0)