Skip to content

Commit 51cbd5a

Browse files
git-hulkalamb
andauthored
Implements ALTER POLICY syntax for PostgreSQL (apache#1446)
Co-authored-by: Andrew Lamb <[email protected]>
1 parent 73dc8a3 commit 51cbd5a

File tree

5 files changed

+213
-6
lines changed

5 files changed

+213
-6
lines changed

src/ast/ddl.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,49 @@ pub enum AlterTableOperation {
234234
OwnerTo { new_owner: Owner },
235235
}
236236

237+
/// An `ALTER Policy` (`Statement::AlterPolicy`) operation
238+
///
239+
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-altertable.html)
240+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
241+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
242+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
243+
pub enum AlterPolicyOperation {
244+
Rename {
245+
new_name: Ident,
246+
},
247+
Apply {
248+
to: Option<Vec<Owner>>,
249+
using: Option<Expr>,
250+
with_check: Option<Expr>,
251+
},
252+
}
253+
254+
impl fmt::Display for AlterPolicyOperation {
255+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256+
match self {
257+
AlterPolicyOperation::Rename { new_name } => {
258+
write!(f, " RENAME TO {new_name}")
259+
}
260+
AlterPolicyOperation::Apply {
261+
to,
262+
using,
263+
with_check,
264+
} => {
265+
if let Some(to) = to {
266+
write!(f, " TO {}", display_comma_separated(to))?;
267+
}
268+
if let Some(using) = using {
269+
write!(f, " USING ({using})")?;
270+
}
271+
if let Some(with_check) = with_check {
272+
write!(f, " WITH CHECK ({with_check})")?;
273+
}
274+
Ok(())
275+
}
276+
}
277+
}
278+
}
279+
237280
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
238281
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239282
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/ast/mod.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ pub use self::data_type::{
3939
};
4040
pub use self::dcl::{AlterRoleOperation, ResetConfig, RoleOption, SetConfigValue, Use};
4141
pub use self::ddl::{
42-
AlterColumnOperation, AlterIndexOperation, AlterTableOperation, ClusteredBy, ColumnDef,
43-
ColumnOption, ColumnOptionDef, ConstraintCharacteristics, Deduplicate, DeferrableInitial,
44-
GeneratedAs, GeneratedExpressionMode, IdentityProperty, IndexOption, IndexType,
45-
KeyOrIndexDisplay, Owner, Partition, ProcedureParam, ReferentialAction, TableConstraint,
46-
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
42+
AlterColumnOperation, AlterIndexOperation, AlterPolicyOperation, AlterTableOperation,
43+
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ConstraintCharacteristics, Deduplicate,
44+
DeferrableInitial, GeneratedAs, GeneratedExpressionMode, IdentityProperty, IndexOption,
45+
IndexType, KeyOrIndexDisplay, Owner, Partition, ProcedureParam, ReferentialAction,
46+
TableConstraint, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
47+
ViewColumnDef,
4748
};
4849
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
4950
pub use self::operator::{BinaryOperator, UnaryOperator};
@@ -2459,6 +2460,16 @@ pub enum Statement {
24592460
operation: AlterRoleOperation,
24602461
},
24612462
/// ```sql
2463+
/// ALTER POLICY <NAME> ON <TABLE NAME> [<OPERATION>]
2464+
/// ```
2465+
/// (Postgresql-specific)
2466+
AlterPolicy {
2467+
name: Ident,
2468+
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
2469+
table_name: ObjectName,
2470+
operation: AlterPolicyOperation,
2471+
},
2472+
/// ```sql
24622473
/// ATTACH DATABASE 'path/to/file' AS alias
24632474
/// ```
24642475
/// (SQLite-specific)
@@ -4197,6 +4208,13 @@ impl fmt::Display for Statement {
41974208
Statement::AlterRole { name, operation } => {
41984209
write!(f, "ALTER ROLE {name} {operation}")
41994210
}
4211+
Statement::AlterPolicy {
4212+
name,
4213+
table_name,
4214+
operation,
4215+
} => {
4216+
write!(f, "ALTER POLICY {name} ON {table_name}{operation}")
4217+
}
42004218
Statement::Drop {
42014219
object_type,
42024220
if_exists,

src/parser/alter.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ use alloc::vec;
1717

1818
use super::{Parser, ParserError};
1919
use crate::{
20-
ast::{AlterRoleOperation, Expr, Password, ResetConfig, RoleOption, SetConfigValue, Statement},
20+
ast::{
21+
AlterPolicyOperation, AlterRoleOperation, Expr, Password, ResetConfig, RoleOption,
22+
SetConfigValue, Statement,
23+
},
2124
dialect::{MsSqlDialect, PostgreSqlDialect},
2225
keywords::Keyword,
2326
tokenizer::Token,
@@ -36,6 +39,66 @@ impl<'a> Parser<'a> {
3639
))
3740
}
3841

42+
/// Parse ALTER POLICY statement
43+
/// ```sql
44+
/// ALTER POLICY policy_name ON table_name [ RENAME TO new_name ]
45+
/// or
46+
/// ALTER POLICY policy_name ON table_name
47+
/// [ TO { role_name | PUBLIC | CURRENT_ROLE | CURRENT_USER | SESSION_USER } [, ...] ]
48+
/// [ USING ( using_expression ) ]
49+
/// [ WITH CHECK ( check_expression ) ]
50+
/// ```
51+
///
52+
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-alterpolicy.html)
53+
pub fn parse_alter_policy(&mut self) -> Result<Statement, ParserError> {
54+
let name = self.parse_identifier(false)?;
55+
self.expect_keyword(Keyword::ON)?;
56+
let table_name = self.parse_object_name(false)?;
57+
58+
if self.parse_keyword(Keyword::RENAME) {
59+
self.expect_keyword(Keyword::TO)?;
60+
let new_name = self.parse_identifier(false)?;
61+
Ok(Statement::AlterPolicy {
62+
name,
63+
table_name,
64+
operation: AlterPolicyOperation::Rename { new_name },
65+
})
66+
} else {
67+
let to = if self.parse_keyword(Keyword::TO) {
68+
Some(self.parse_comma_separated(|p| p.parse_owner())?)
69+
} else {
70+
None
71+
};
72+
73+
let using = if self.parse_keyword(Keyword::USING) {
74+
self.expect_token(&Token::LParen)?;
75+
let expr = self.parse_expr()?;
76+
self.expect_token(&Token::RParen)?;
77+
Some(expr)
78+
} else {
79+
None
80+
};
81+
82+
let with_check = if self.parse_keywords(&[Keyword::WITH, Keyword::CHECK]) {
83+
self.expect_token(&Token::LParen)?;
84+
let expr = self.parse_expr()?;
85+
self.expect_token(&Token::RParen)?;
86+
Some(expr)
87+
} else {
88+
None
89+
};
90+
Ok(Statement::AlterPolicy {
91+
name,
92+
table_name,
93+
operation: AlterPolicyOperation::Apply {
94+
to,
95+
using,
96+
with_check,
97+
},
98+
})
99+
}
100+
}
101+
39102
fn parse_mssql_alter_role(&mut self) -> Result<Statement, ParserError> {
40103
let role_name = self.parse_identifier(false)?;
41104

src/parser/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7140,6 +7140,7 @@ impl<'a> Parser<'a> {
71407140
Keyword::TABLE,
71417141
Keyword::INDEX,
71427142
Keyword::ROLE,
7143+
Keyword::POLICY,
71437144
])?;
71447145
match object_type {
71457146
Keyword::VIEW => self.parse_alter_view(),
@@ -7191,6 +7192,7 @@ impl<'a> Parser<'a> {
71917192
})
71927193
}
71937194
Keyword::ROLE => self.parse_alter_role(),
7195+
Keyword::POLICY => self.parse_alter_policy(),
71947196
// unreachable because expect_one_of_keywords used above
71957197
_ => unreachable!(),
71967198
}

tests/sqlparser_common.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11167,3 +11167,84 @@ fn test_drop_policy() {
1116711167
"sql parser error: Expected: end of statement, found: WRONG"
1116811168
);
1116911169
}
11170+
11171+
#[test]
11172+
fn test_alter_policy() {
11173+
match verified_stmt("ALTER POLICY old_policy ON my_table RENAME TO new_policy") {
11174+
Statement::AlterPolicy {
11175+
name,
11176+
table_name,
11177+
operation,
11178+
..
11179+
} => {
11180+
assert_eq!(name.to_string(), "old_policy");
11181+
assert_eq!(table_name.to_string(), "my_table");
11182+
assert_eq!(
11183+
operation,
11184+
AlterPolicyOperation::Rename {
11185+
new_name: Ident::new("new_policy")
11186+
}
11187+
);
11188+
}
11189+
_ => unreachable!(),
11190+
}
11191+
11192+
match verified_stmt(concat!(
11193+
"ALTER POLICY my_policy ON my_table TO CURRENT_USER ",
11194+
"USING ((SELECT c0)) WITH CHECK (c0 > 0)"
11195+
)) {
11196+
Statement::AlterPolicy {
11197+
name, table_name, ..
11198+
} => {
11199+
assert_eq!(name.to_string(), "my_policy");
11200+
assert_eq!(table_name.to_string(), "my_table");
11201+
}
11202+
_ => unreachable!(),
11203+
}
11204+
11205+
// omit TO / USING / WITH CHECK clauses is allowed
11206+
verified_stmt("ALTER POLICY my_policy ON my_table");
11207+
11208+
// mixing RENAME and APPLY expressions
11209+
assert_eq!(
11210+
parse_sql_statements("ALTER POLICY old_policy ON my_table TO public RENAME TO new_policy")
11211+
.unwrap_err()
11212+
.to_string(),
11213+
"sql parser error: Expected: end of statement, found: RENAME"
11214+
);
11215+
assert_eq!(
11216+
parse_sql_statements("ALTER POLICY old_policy ON my_table RENAME TO new_policy TO public")
11217+
.unwrap_err()
11218+
.to_string(),
11219+
"sql parser error: Expected: end of statement, found: TO"
11220+
);
11221+
// missing TO in RENAME TO
11222+
assert_eq!(
11223+
parse_sql_statements("ALTER POLICY old_policy ON my_table RENAME")
11224+
.unwrap_err()
11225+
.to_string(),
11226+
"sql parser error: Expected: TO, found: EOF"
11227+
);
11228+
// missing new name in RENAME TO
11229+
assert_eq!(
11230+
parse_sql_statements("ALTER POLICY old_policy ON my_table RENAME TO")
11231+
.unwrap_err()
11232+
.to_string(),
11233+
"sql parser error: Expected: identifier, found: EOF"
11234+
);
11235+
11236+
// missing the expression in USING
11237+
assert_eq!(
11238+
parse_sql_statements("ALTER POLICY my_policy ON my_table USING")
11239+
.unwrap_err()
11240+
.to_string(),
11241+
"sql parser error: Expected: (, found: EOF"
11242+
);
11243+
// missing the expression in WITH CHECK
11244+
assert_eq!(
11245+
parse_sql_statements("ALTER POLICY my_policy ON my_table WITH CHECK")
11246+
.unwrap_err()
11247+
.to_string(),
11248+
"sql parser error: Expected: (, found: EOF"
11249+
);
11250+
}

0 commit comments

Comments
 (0)