Skip to content
Open
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
149 changes: 145 additions & 4 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.

//! AST types specific to CREATE/ALTER variants of [`Statement`](crate::ast::Statement)
//! AST types specific to CREATE/ALTER variants of [`Statement`]
//! (commonly referred to as Data Definition Language, or DDL)

#[cfg(not(feature = "std"))]
Expand Down Expand Up @@ -48,9 +48,10 @@ use crate::ast::{
FunctionSecurity, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat,
HiveSetLocation, Ident, InitializeKind, MySQLColumnPosition, ObjectName, OnCommit,
OneOrManyWithParens, OperateFunctionArg, OrderByExpr, ProjectionSelect, Query, RefreshModeKind,
ResetConfig, RowAccessPolicy, SequenceOptions, Spanned, SqlOption, StorageLifecyclePolicy,
StorageSerializationPolicy, TableVersion, Tag, TriggerEvent, TriggerExecBody, TriggerObject,
TriggerPeriod, TriggerReferencing, Value, ValueWithSpan, WrappedCollection,
ResetConfig, RowAccessPolicy, SequenceOptions, Spanned, SqlOption, Statement,
StorageLifecyclePolicy, StorageSerializationPolicy, TableVersion, Tag, TriggerEvent,
TriggerExecBody, TriggerObject, TriggerPeriod, TriggerReferencing, Value, ValueWithSpan,
WrappedCollection,
};
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
use crate::keywords::Keyword;
Expand Down Expand Up @@ -6041,6 +6042,146 @@ impl From<AlterPolicy> for crate::ast::Statement {
}
}

/// A PostgreSQL rule with source spans derived from its identifiers, condition and actions.
///
/// ```sql
/// CREATE [ OR REPLACE ] RULE name AS ON event
/// TO table_name [ WHERE condition ]
/// DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
/// ```
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrule.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateRule {
/// `true` when `OR REPLACE` was present.
pub or_replace: bool,
/// Name of the rule.
pub name: Ident,
/// Event the rule fires on (`SELECT`, `INSERT`, `UPDATE` or `DELETE`).
pub event: CreateRuleEvent,
/// Table the rule is defined on.
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
/// Optional expression for the `WHERE` clause.
pub condition: Option<Expr>,
/// Optional `ALSO` or `INSTEAD` keyword following `DO`.
pub do_kind: Option<CreateRuleDoKind>,
/// Action the rule performs.
pub action: CreateRuleAction,
}

impl fmt::Display for CreateRule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE {or_replace}RULE {name} AS ON {event} TO {table_name}",
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
name = self.name,
event = self.event,
table_name = self.table_name,
)?;
if let Some(condition) = &self.condition {
write!(f, " WHERE {condition}")?;
}
write!(f, " DO")?;
if let Some(do_kind) = &self.do_kind {
write!(f, " {do_kind}")?;
}
write!(f, " {}", self.action)
}
}

/// Event that fires a rule (`ON` clause).
/// ```sql
/// AS ON [SELECT | INSERT | UPDATE | DELETE]
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrule.html)
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateRuleEvent {
/// Fires on SELECT.
Select,
/// Fires on INSERT.
Insert,
/// Fires on UPDATE.
Update,
/// Fires on DELETE.
Delete,
}

impl fmt::Display for CreateRuleEvent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateRuleEvent::Select => write!(f, "SELECT"),
CreateRuleEvent::Insert => write!(f, "INSERT"),
CreateRuleEvent::Update => write!(f, "UPDATE"),
CreateRuleEvent::Delete => write!(f, "DELETE"),
}
}
}

/// Keyword following `DO` in a `CREATE RULE` statement.
/// ```sql
/// DO [ ALSO | INSTEAD ]
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrule.html)
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateRuleDoKind {
/// The action runs in addition to the original statement.
Also,
/// The action runs instead of the original statement.
Instead,
}

impl fmt::Display for CreateRuleDoKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateRuleDoKind::Also => write!(f, "ALSO"),
CreateRuleDoKind::Instead => write!(f, "INSTEAD"),
}
}
}

/// Action of a `CREATE RULE` statement.
/// ```sql
/// { NOTHING | command | ( command ; command ... ) }
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrule.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum CreateRuleAction {
/// `NOTHING`
Nothing,
/// A single command, e.g. `DO INSTEAD SELECT * FROM t`.
Statement(Box<Statement>),
/// A parenthesized, semicolon-separated list of commands.
Statements(Vec<Statement>),
}

impl fmt::Display for CreateRuleAction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CreateRuleAction::Nothing => write!(f, "NOTHING"),
CreateRuleAction::Statement(statement) => write!(f, "{statement}"),
CreateRuleAction::Statements(statements) => {
write!(f, "({})", display_separated(statements, "; "))
}
}
}
}

impl From<CreateRule> for crate::ast::Statement {
fn from(v: CreateRule) -> Self {
crate::ast::Statement::CreateRule(v)
}
}

/// A `CREATE FOREIGN TABLE` statement.
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createforeigntable.html)
Expand Down
25 changes: 16 additions & 9 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,16 @@ pub use self::ddl::{
ConstraintCharacteristics, CreateCollation, CreateCollationDefinition, CreateConnector,
CreateDomain, CreateExtension, CreateForeignTable, CreateFunction, CreateIndex, CreateOperator,
CreateOperatorClass, CreateOperatorFamily, CreatePolicy, CreatePolicyCommand, CreatePolicyType,
CreateTable, CreateTextSearch, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DistStyle, DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorClass,
DropOperatorFamily, DropOperatorSignature, DropPolicy, DropTrigger, ForValues,
FunctionReturnType, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes,
OperatorClassItem, OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose,
Owner, Partition, PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind,
ReplicaIdentity, SqliteTableOption, SqliteTableOptions, TagsColumnOption, TextSearchObjectType,
CreateRule, CreateRuleAction, CreateRuleDoKind, CreateRuleEvent, CreateTable, CreateTextSearch,
CreateTrigger, CreateView, Deduplicate, DeferrableInitial, DistStyle, DropBehavior,
DropExtension, DropFunction, DropOperator, DropOperatorClass, DropOperatorFamily,
DropOperatorSignature, DropPolicy, DropTrigger, ForValues, FunctionReturnType, GeneratedAs,
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, IndexColumn, IndexOption, IndexType,
KeyOrIndexDisplay, Msck, NullsDistinctOption, OperatorArgTypes, OperatorClassItem,
OperatorFamilyDropItem, OperatorFamilyItem, OperatorOption, OperatorPurpose, Owner, Partition,
PartitionBoundValue, ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity,
SqliteTableOption, SqliteTableOptions, TagsColumnOption, TextSearchObjectType,
TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef, WithData,
Expand Down Expand Up @@ -3822,6 +3823,11 @@ pub enum Statement {
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createpolicy.html)
CreatePolicy(CreatePolicy),
/// ```sql
/// CREATE RULE
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrule.html)
CreateRule(CreateRule),
/// ```sql
/// CREATE CONNECTOR
/// ```
/// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-CreateDataConnectorCreateConnector)
Expand Down Expand Up @@ -5686,6 +5692,7 @@ impl fmt::Display for Statement {
}
Statement::CreateForeignTable(stmt) => write!(f, "{stmt}"),
Statement::CreatePolicy(policy) => write!(f, "{policy}"),
Statement::CreateRule(rule) => write!(f, "{rule}"),
Statement::CreateConnector(create_connector) => create_connector.fmt(f),
Statement::CreateOperator(create_operator) => create_operator.fmt(f),
Statement::CreateOperatorFamily(create_operator_family) => {
Expand Down
46 changes: 34 additions & 12 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@ use super::{
AlterIndexOperation, AlterTableOperation, Analyze, Array, Assignment, AssignmentTarget,
AttachedToken, BeginEndStatements, CaseStatement, CloseCursor, ClusteredIndex, ColumnDef,
ColumnOption, ColumnOptionDef, ConditionalStatementBlock, ConditionalStatements,
ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeConstraintElement,
ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable, Function, FunctionArg,
FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr,
HavingBound, IfStatement, IlikeSelectItem, InTarget, IndexColumn, Insert, Interpolate,
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
LimitClause, MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr,
MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, NamedParenthesizedList,
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition,
PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement,
RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
ConflictTarget, ConnectByKind, ConstraintCharacteristics, CopySource, CreateIndex, CreateRule,
CreateRuleAction, CreateTable, CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem,
ExcludeConstraintElement, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, ForValues, FromTable,
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, InTarget,
IndexColumn, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator,
JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure, Merge,
MergeAction, MergeClause, MergeInsertExpr, MergeInsertKind, MergeUpdateExpr, MergeUpdateKind,
NamedParenthesizedList, NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict,
OnConflictAction, OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause,
Parens, Partition, PartitionBoundValue, PivotValueSource, ProjectionSelect, Query,
RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, Setting, SqlOption, Statement,
Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor,
TableObject, TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Values,
Expand Down Expand Up @@ -487,6 +487,7 @@ impl Spanned for Statement {
Statement::Unload { .. } => Span::empty(),
Statement::OptimizeTable { .. } => Span::empty(),
Statement::CreatePolicy { .. } => Span::empty(),
Statement::CreateRule(rule) => rule.span(),
Statement::AlterPolicy { .. } => Span::empty(),
Statement::AlterConnector { .. } => Span::empty(),
Statement::DropPolicy { .. } => Span::empty(),
Expand Down Expand Up @@ -548,6 +549,27 @@ impl Spanned for Use {
}
}

impl Spanned for CreateRule {
fn span(&self) -> Span {
let action_span = match &self.action {
CreateRuleAction::Nothing => Span::empty(),
CreateRuleAction::Statement(statement) => statement.span(),
CreateRuleAction::Statements(statements) => {
union_spans(statements.iter().map(Spanned::span))
}
};
union_spans(
[
self.name.span,
self.table_name.span(),
self.condition.as_ref().map_or(Span::empty(), Spanned::span),
action_span,
]
.into_iter(),
)
}
}

impl Spanned for CreateTable {
fn span(&self) -> Span {
let CreateTable {
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ define_keywords!(
ALL,
ALLOCATE,
ALLOWOVERWRITE,
ALSO,
ALTER,
ALWAYS,
ANALYZE,
Expand Down
87 changes: 87 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5417,6 +5417,8 @@ impl<'a> Parser<'a> {
.map(Into::into)
} else if self.parse_keyword(Keyword::POLICY) {
self.parse_create_policy().map(Into::into)
} else if self.parse_keyword(Keyword::RULE) {
self.parse_create_rule(or_replace).map(Into::into)
} else if self.parse_keyword(Keyword::EXTERNAL) {
self.parse_create_external_table(or_replace).map(Into::into)
} else if self.parse_keyword(Keyword::FUNCTION) {
Expand Down Expand Up @@ -7416,6 +7418,91 @@ impl<'a> Parser<'a> {
})
}

/// ```sql
/// CREATE [ OR REPLACE ] RULE name AS ON event
/// TO table_name [ WHERE condition ]
/// DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
/// ```
///
/// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-createrule.html)
pub fn parse_create_rule(&mut self, or_replace: bool) -> Result<CreateRule, ParserError> {
let name = self.parse_identifier()?;
self.expect_keyword_is(Keyword::AS)?;
self.expect_keyword_is(Keyword::ON)?;
let event = match &self.peek_token_ref().token {
Token::Word(Word {
keyword: Keyword::SELECT,
..
}) => CreateRuleEvent::Select,
Token::Word(Word {
keyword: Keyword::INSERT,
..
}) => CreateRuleEvent::Insert,
Token::Word(Word {
keyword: Keyword::UPDATE,
..
}) => CreateRuleEvent::Update,
Token::Word(Word {
keyword: Keyword::DELETE,
..
}) => CreateRuleEvent::Delete,
_ => {
return self.expected_ref(
"one of SELECT or INSERT or UPDATE or DELETE",
self.peek_token_ref(),
)
}
};
self.advance_token();
self.expect_keyword_is(Keyword::TO)?;
let table_name = self.parse_object_name(false)?;

let condition = if self.parse_keyword(Keyword::WHERE) {
Some(self.parse_expr()?)
} else {
None
};

self.expect_keyword_is(Keyword::DO)?;
let do_kind = if self.parse_keyword(Keyword::ALSO) {
Some(CreateRuleDoKind::Also)
} else if self.parse_keyword(Keyword::INSTEAD) {
Some(CreateRuleDoKind::Instead)
} else {
None
};

let action = if self.parse_keyword(Keyword::NOTHING) {
CreateRuleAction::Nothing
} else if self.consume_token(&Token::LParen) {
let mut statements = Vec::new();
loop {
while self.consume_token(&Token::SemiColon) {}
if self.consume_token(&Token::RParen) {
break;
}
statements.push(self.parse_statement()?);
if !self.consume_token(&Token::SemiColon) {
self.expect_token(&Token::RParen)?;
break;
}
}
CreateRuleAction::Statements(statements)
} else {
CreateRuleAction::Statement(Box::new(self.parse_statement()?))
};

Ok(CreateRule {
or_replace,
name,
event,
table_name,
condition,
do_kind,
action,
})
}

/// ```sql
/// CREATE CONNECTOR [IF NOT EXISTS] connector_name
/// [TYPE datasource_type]
Expand Down
Loading
Loading