Skip to content
181 changes: 181 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,22 @@ pub enum Statement {
condition: Expr,
message: Option<Expr>,
},
/// GRANT privileges ON objects TO grantees
Grant {
privileges: Privileges,
objects: GrantObjects,
grantees: Vec<Ident>,
with_grant_option: bool,
granted_by: Option<Ident>,
},
/// REVOKE privileges ON objects FROM grantees
Revoke {
privileges: Privileges,
objects: GrantObjects,
grantees: Vec<Ident>,
granted_by: Option<Ident>,
cascade: bool,
},
/// `DEALLOCATE [ PREPARE ] { name | ALL }`
///
/// Note: this is a PostgreSQL-specific statement.
Expand Down Expand Up @@ -1330,6 +1346,40 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::Grant {
privileges,
objects,
grantees,
with_grant_option,
granted_by,
} => {
write!(f, "GRANT {} ", privileges)?;
write!(f, "ON {} ", objects)?;
write!(f, "TO {}", display_comma_separated(grantees))?;
if *with_grant_option {
write!(f, " WITH GRANT OPTION")?;
}
if let Some(grantor) = granted_by {
write!(f, " GRANTED BY {}", grantor)?;
}
Ok(())
}
Statement::Revoke {
privileges,
objects,
grantees,
granted_by,
cascade,
} => {
write!(f, "REVOKE {} ", privileges)?;
write!(f, "ON {} ", objects)?;
write!(f, "FROM {}", display_comma_separated(grantees))?;
if let Some(grantor) = granted_by {
write!(f, " GRANTED BY {}", grantor)?;
}
write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?;
Ok(())
}
Statement::Deallocate { name, prepare } => write!(
f,
"DEALLOCATE {prepare}{name}",
Expand Down Expand Up @@ -1358,6 +1408,137 @@ impl fmt::Display for Statement {
}
}

/// Privileges granted in a GRANT statement or revoked in a REVOKE statement.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Privileges {
/// All privileges applicable to the object type
All {
/// Optional keyword from the spec, ignored in practice
with_privileges_keyword: bool,
Comment on lines +1417 to +1418
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth removing it from the struct then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alamb I erred on the side of losslessness here, but I wasn't sure to what degree that's a goal.

I guess there are already many cases where an implied default becomes explicit when parsed, and would then produce different output after a roundtrip if the input had omitted the default (eg. cascade in Statement::Drop).

If you think it would be cleaner I'm happy to PR removing this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this field is needed to survive the round trip I think keeping it makes sense.

},
/// Specific privileges (e.g. `SELECT`, `INSERT`)
Actions(Vec<Action>),
}

impl fmt::Display for Privileges {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Privileges::All {
with_privileges_keyword,
} => {
write!(
f,
"ALL{}",
if *with_privileges_keyword {
" PRIVILEGES"
} else {
""
}
)
}
Privileges::Actions(actions) => {
write!(f, "{}", display_comma_separated(actions).to_string())
}
}
}
}

/// A privilege on a database object (table, sequence, etc.).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Action {
Connect,
Create,
Delete,
Execute,
Insert { columns: Option<Vec<Ident>> },
References { columns: Option<Vec<Ident>> },
Select { columns: Option<Vec<Ident>> },
Temporary,
Trigger,
Truncate,
Update { columns: Option<Vec<Ident>> },
Usage,
}

impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Action::Connect => f.write_str("CONNECT")?,
Action::Create => f.write_str("CREATE")?,
Action::Delete => f.write_str("DELETE")?,
Action::Execute => f.write_str("EXECUTE")?,
Action::Insert { .. } => f.write_str("INSERT")?,
Action::References { .. } => f.write_str("REFERENCES")?,
Action::Select { .. } => f.write_str("SELECT")?,
Action::Temporary => f.write_str("TEMPORARY")?,
Action::Trigger => f.write_str("TRIGGER")?,
Action::Truncate => f.write_str("TRUNCATE")?,
Action::Update { .. } => f.write_str("UPDATE")?,
Action::Usage => f.write_str("USAGE")?,
};
match self {
Action::Insert { columns }
| Action::References { columns }
| Action::Select { columns }
| Action::Update { columns } => {
if let Some(columns) = columns {
write!(f, " ({})", display_comma_separated(columns))?;
}
}
_ => (),
};
Ok(())
}
}

/// Objects on which privileges are granted in a GRANT statement.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum GrantObjects {
/// Grant privileges on `ALL SEQUENCES IN SCHEMA <schema_name> [, ...]`
AllSequencesInSchema { schemas: Vec<ObjectName> },
/// Grant privileges on `ALL TABLES IN SCHEMA <schema_name> [, ...]`
AllTablesInSchema { schemas: Vec<ObjectName> },
/// Grant privileges on specific schemas
Schemas(Vec<ObjectName>),
/// Grant privileges on specific sequences
Sequences(Vec<ObjectName>),
/// Grant privileges on specific tables
Tables(Vec<ObjectName>),
}

impl fmt::Display for GrantObjects {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GrantObjects::Sequences(sequences) => {
write!(f, "SEQUENCE {}", display_comma_separated(sequences))
}
GrantObjects::Schemas(schemas) => {
write!(f, "SCHEMA {}", display_comma_separated(schemas))
}
GrantObjects::Tables(tables) => {
write!(f, "{}", display_comma_separated(tables))
}
GrantObjects::AllSequencesInSchema { schemas } => {
write!(
f,
"ALL SEQUENCES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::AllTablesInSchema { schemas } => {
write!(
f,
"ALL TABLES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
}
}
}

/// SQL assignment `foo = expr` as used in SQLUpdate
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down
7 changes: 7 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ define_keywords!(
GET,
GLOBAL,
GRANT,
GRANTED,
GROUP,
GROUPING,
GROUPS,
Expand Down Expand Up @@ -318,6 +319,7 @@ define_keywords!(
ON,
ONLY,
OPEN,
OPTION,
OR,
ORC,
ORDER,
Expand Down Expand Up @@ -348,6 +350,7 @@ define_keywords!(
PRECISION,
PREPARE,
PRIMARY,
PRIVILEGES,
PROCEDURE,
PURGE,
RANGE,
Expand Down Expand Up @@ -395,7 +398,9 @@ define_keywords!(
SECOND,
SELECT,
SENSITIVE,
SEQUENCE,
SEQUENCEFILE,
SEQUENCES,
SERDE,
SERIALIZABLE,
SESSION,
Expand Down Expand Up @@ -432,6 +437,7 @@ define_keywords!(
SYSTEM_TIME,
SYSTEM_USER,
TABLE,
TABLES,
TABLESAMPLE,
TBLPROPERTIES,
TEMP,
Expand Down Expand Up @@ -468,6 +474,7 @@ define_keywords!(
UNNEST,
UPDATE,
UPPER,
USAGE,
USER,
USING,
UUID,
Expand Down
Loading