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
5 changes: 5 additions & 0 deletions fuzz/fuzz_seeds/postgres_2433_create_rule.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE RULE r AS ON DELETE TO docs DO INSTEAD NOTHING;
CREATE OR REPLACE RULE r AS ON SELECT TO t DO ALSO NOTHING;
CREATE RULE r AS ON INSERT TO s1.t DO NOTHING;
CREATE RULE r AS ON UPDATE TO t WHERE quantity > 100 DO ALSO INSERT INTO audit (id) VALUES (1);
CREATE RULE r AS ON DELETE TO t DO INSTEAD (UPDATE t2 SET deleted = 1 WHERE id = 1; DELETE FROM t3)
1 change: 1 addition & 0 deletions fuzz/fuzz_seeds/postgres_2433_create_rule_notify.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable
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 @@ -75,15 +75,16 @@ pub use self::ddl::{
CreateAggregateOption, 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 @@ -3825,6 +3826,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
55 changes: 39 additions & 16 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,23 @@ 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, OperateFunctionArg, 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,
ViewColumnDef, WhileStatement, WildcardAdditionalOptions, With, WithFill,
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, OperateFunctionArg, 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, ViewColumnDef, WhileStatement,
WildcardAdditionalOptions, With, WithFill,
};

/// Given an iterator of spans, return the [Span::union] of all spans.
Expand Down Expand Up @@ -487,6 +488,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 @@ -549,6 +551,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
Loading
Loading