Skip to content

Commit 32b8276

Browse files
gainingsalamb
andauthored
Postgres: support for OWNER TO clause (#1314)
Co-authored-by: Andrew Lamb <[email protected]>
1 parent 0727895 commit 32b8276

File tree

5 files changed

+123
-2
lines changed

5 files changed

+123
-2
lines changed

src/ast/ddl.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,32 @@ pub enum AlterTableOperation {
157157
SwapWith { table_name: ObjectName },
158158
/// 'SET TBLPROPERTIES ( { property_key [ = ] property_val } [, ...] )'
159159
SetTblProperties { table_properties: Vec<SqlOption> },
160+
161+
/// `OWNER TO { <new_owner> | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
162+
///
163+
/// Note: this is PostgreSQL-specific <https://www.postgresql.org/docs/current/sql-altertable.html>
164+
OwnerTo { new_owner: Owner },
165+
}
166+
167+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
168+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
169+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
170+
pub enum Owner {
171+
Ident(Ident),
172+
CurrentRole,
173+
CurrentUser,
174+
SessionUser,
175+
}
176+
177+
impl fmt::Display for Owner {
178+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179+
match self {
180+
Owner::Ident(ident) => write!(f, "{}", ident),
181+
Owner::CurrentRole => write!(f, "CURRENT_ROLE"),
182+
Owner::CurrentUser => write!(f, "CURRENT_USER"),
183+
Owner::SessionUser => write!(f, "SESSION_USER"),
184+
}
185+
}
160186
}
161187

162188
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
@@ -322,6 +348,9 @@ impl fmt::Display for AlterTableOperation {
322348
AlterTableOperation::SwapWith { table_name } => {
323349
write!(f, "SWAP WITH {table_name}")
324350
}
351+
AlterTableOperation::OwnerTo { new_owner } => {
352+
write!(f, "OWNER TO {new_owner}")
353+
}
325354
AlterTableOperation::SetTblProperties { table_properties } => {
326355
write!(
327356
f,

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ pub use self::dcl::{AlterRoleOperation, ResetConfig, RoleOption, SetConfigValue}
3434
pub use self::ddl::{
3535
AlterColumnOperation, AlterIndexOperation, AlterTableOperation, ColumnDef, ColumnOption,
3636
ColumnOptionDef, ConstraintCharacteristics, DeferrableInitial, GeneratedAs,
37-
GeneratedExpressionMode, IndexOption, IndexType, KeyOrIndexDisplay, Partition, ProcedureParam,
38-
ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef,
37+
GeneratedExpressionMode, IndexOption, IndexType, KeyOrIndexDisplay, Owner, Partition,
38+
ProcedureParam, ReferentialAction, TableConstraint, UserDefinedTypeCompositeAttributeDef,
3939
UserDefinedTypeRepresentation, ViewColumnDef,
4040
};
4141
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ define_keywords!(
527527
OVERLAY,
528528
OVERWRITE,
529529
OWNED,
530+
OWNER,
530531
PARALLEL,
531532
PARAMETER,
532533
PARQUET,

src/parser/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6447,6 +6447,25 @@ impl<'a> Parser<'a> {
64476447
self.expect_keyword(Keyword::WITH)?;
64486448
let table_name = self.parse_object_name(false)?;
64496449
AlterTableOperation::SwapWith { table_name }
6450+
} else if dialect_of!(self is PostgreSqlDialect | GenericDialect)
6451+
&& self.parse_keywords(&[Keyword::OWNER, Keyword::TO])
6452+
{
6453+
let new_owner = match self.parse_one_of_keywords( &[Keyword::CURRENT_USER, Keyword::CURRENT_ROLE, Keyword::SESSION_USER]) {
6454+
Some(Keyword::CURRENT_USER) => Owner::CurrentUser,
6455+
Some(Keyword::CURRENT_ROLE) => Owner::CurrentRole,
6456+
Some(Keyword::SESSION_USER) => Owner::SessionUser,
6457+
Some(_) => unreachable!(),
6458+
None => {
6459+
match self.parse_identifier(false) {
6460+
Ok(ident) => Owner::Ident(ident),
6461+
Err(e) => {
6462+
return Err(ParserError::ParserError(format!("Expected: CURRENT_USER, CURRENT_ROLE, SESSION_USER or identifier after OWNER TO. {e}")))
6463+
}
6464+
}
6465+
},
6466+
};
6467+
6468+
AlterTableOperation::OwnerTo { new_owner }
64506469
} else {
64516470
let options: Vec<SqlOption> =
64526471
self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?;

tests/sqlparser_postgres.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,78 @@ fn parse_alter_table_add_columns() {
713713
}
714714
}
715715

716+
#[test]
717+
fn parse_alter_table_owner_to() {
718+
struct TestCase {
719+
sql: &'static str,
720+
expected_owner: Owner,
721+
}
722+
723+
let test_cases = vec![
724+
TestCase {
725+
sql: "ALTER TABLE tab OWNER TO new_owner",
726+
expected_owner: Owner::Ident(Ident::new("new_owner".to_string())),
727+
},
728+
TestCase {
729+
sql: "ALTER TABLE tab OWNER TO postgres",
730+
expected_owner: Owner::Ident(Ident::new("postgres".to_string())),
731+
},
732+
TestCase {
733+
sql: "ALTER TABLE tab OWNER TO CREATE", // treats CREATE as an identifier
734+
expected_owner: Owner::Ident(Ident::new("CREATE".to_string())),
735+
},
736+
TestCase {
737+
sql: "ALTER TABLE tab OWNER TO \"new_owner\"",
738+
expected_owner: Owner::Ident(Ident::with_quote('\"', "new_owner".to_string())),
739+
},
740+
TestCase {
741+
sql: "ALTER TABLE tab OWNER TO CURRENT_USER",
742+
expected_owner: Owner::CurrentUser,
743+
},
744+
TestCase {
745+
sql: "ALTER TABLE tab OWNER TO CURRENT_ROLE",
746+
expected_owner: Owner::CurrentRole,
747+
},
748+
TestCase {
749+
sql: "ALTER TABLE tab OWNER TO SESSION_USER",
750+
expected_owner: Owner::SessionUser,
751+
},
752+
];
753+
754+
for case in test_cases {
755+
match pg_and_generic().verified_stmt(case.sql) {
756+
Statement::AlterTable {
757+
name,
758+
if_exists: _,
759+
only: _,
760+
operations,
761+
location: _,
762+
} => {
763+
assert_eq!(name.to_string(), "tab");
764+
assert_eq!(
765+
operations,
766+
vec![AlterTableOperation::OwnerTo {
767+
new_owner: case.expected_owner.clone()
768+
}]
769+
);
770+
}
771+
_ => unreachable!("Expected an AlterTable statement"),
772+
}
773+
}
774+
775+
let res = pg().parse_sql_statements("ALTER TABLE tab OWNER TO CREATE FOO");
776+
assert_eq!(
777+
ParserError::ParserError("Expected: end of statement, found: FOO".to_string()),
778+
res.unwrap_err()
779+
);
780+
781+
let res = pg().parse_sql_statements("ALTER TABLE tab OWNER TO 4");
782+
assert_eq!(
783+
ParserError::ParserError("Expected: CURRENT_USER, CURRENT_ROLE, SESSION_USER or identifier after OWNER TO. sql parser error: Expected: identifier, found: 4".to_string()),
784+
res.unwrap_err()
785+
);
786+
}
787+
716788
#[test]
717789
fn parse_create_table_if_not_exists() {
718790
let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()";

0 commit comments

Comments
 (0)