Skip to content

Commit ac1c339

Browse files
Added support for CREATE DOMAIN (#1830)
1 parent a497358 commit ac1c339

File tree

5 files changed

+183
-6
lines changed

5 files changed

+183
-6
lines changed

src/ast/ddl.rs

+49
Original file line numberDiff line numberDiff line change
@@ -2153,6 +2153,55 @@ impl fmt::Display for ClusteredBy {
21532153
}
21542154
}
21552155

2156+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2157+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2158+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2159+
/// ```sql
2160+
/// CREATE DOMAIN name [ AS ] data_type
2161+
/// [ COLLATE collation ]
2162+
/// [ DEFAULT expression ]
2163+
/// [ domain_constraint [ ... ] ]
2164+
///
2165+
/// where domain_constraint is:
2166+
///
2167+
/// [ CONSTRAINT constraint_name ]
2168+
/// { NOT NULL | NULL | CHECK (expression) }
2169+
/// ```
2170+
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createdomain.html)
2171+
pub struct CreateDomain {
2172+
/// The name of the domain to be created.
2173+
pub name: ObjectName,
2174+
/// The data type of the domain.
2175+
pub data_type: DataType,
2176+
/// The collation of the domain.
2177+
pub collation: Option<Ident>,
2178+
/// The default value of the domain.
2179+
pub default: Option<Expr>,
2180+
/// The constraints of the domain.
2181+
pub constraints: Vec<TableConstraint>,
2182+
}
2183+
2184+
impl fmt::Display for CreateDomain {
2185+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2186+
write!(
2187+
f,
2188+
"CREATE DOMAIN {name} AS {data_type}",
2189+
name = self.name,
2190+
data_type = self.data_type
2191+
)?;
2192+
if let Some(collation) = &self.collation {
2193+
write!(f, " COLLATE {collation}")?;
2194+
}
2195+
if let Some(default) = &self.default {
2196+
write!(f, " DEFAULT {default}")?;
2197+
}
2198+
if !self.constraints.is_empty() {
2199+
write!(f, " {}", display_separated(&self.constraints, " "))?;
2200+
}
2201+
Ok(())
2202+
}
2203+
}
2204+
21562205
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
21572206
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21582207
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/ast/mod.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ pub use self::ddl::{
5555
AlterTableAlgorithm, AlterTableLock, AlterTableOperation, AlterType, AlterTypeAddValue,
5656
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
5757
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty,
58-
ConstraintCharacteristics, CreateConnector, CreateFunction, Deduplicate, DeferrableInitial,
59-
DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
60-
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexOption,
61-
IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition, ProcedureParam,
62-
ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
63-
UserDefinedTypeRepresentation, ViewColumnDef,
58+
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction, Deduplicate,
59+
DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
60+
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
61+
IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
62+
ProcedureParam, ReferentialAction, TableConstraint, TagsColumnOption,
63+
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
6464
};
6565
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
6666
pub use self::operator::{BinaryOperator, UnaryOperator};
@@ -4049,6 +4049,8 @@ pub enum Statement {
40494049
sequence_options: Vec<SequenceOptions>,
40504050
owned_by: Option<ObjectName>,
40514051
},
4052+
/// A `CREATE DOMAIN` statement.
4053+
CreateDomain(CreateDomain),
40524054
/// ```sql
40534055
/// CREATE TYPE <name>
40544056
/// ```
@@ -4598,6 +4600,7 @@ impl fmt::Display for Statement {
45984600
Ok(())
45994601
}
46004602
Statement::CreateFunction(create_function) => create_function.fmt(f),
4603+
Statement::CreateDomain(create_domain) => create_domain.fmt(f),
46014604
Statement::CreateTrigger {
46024605
or_alter,
46034606
or_replace,

src/ast/spans.rs

+1
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ impl Spanned for Statement {
483483
Statement::CreateSchema { .. } => Span::empty(),
484484
Statement::CreateDatabase { .. } => Span::empty(),
485485
Statement::CreateFunction { .. } => Span::empty(),
486+
Statement::CreateDomain { .. } => Span::empty(),
486487
Statement::CreateTrigger { .. } => Span::empty(),
487488
Statement::DropTrigger { .. } => Span::empty(),
488489
Statement::CreateProcedure { .. } => Span::empty(),

src/parser/mod.rs

+31
Original file line numberDiff line numberDiff line change
@@ -4625,6 +4625,8 @@ impl<'a> Parser<'a> {
46254625
self.parse_create_external_table(or_replace)
46264626
} else if self.parse_keyword(Keyword::FUNCTION) {
46274627
self.parse_create_function(or_alter, or_replace, temporary)
4628+
} else if self.parse_keyword(Keyword::DOMAIN) {
4629+
self.parse_create_domain()
46284630
} else if self.parse_keyword(Keyword::TRIGGER) {
46294631
self.parse_create_trigger(or_alter, or_replace, false)
46304632
} else if self.parse_keywords(&[Keyword::CONSTRAINT, Keyword::TRIGGER]) {
@@ -5974,6 +5976,35 @@ impl<'a> Parser<'a> {
59745976
Ok(owner)
59755977
}
59765978

5979+
/// Parses a [Statement::CreateDomain] statement.
5980+
fn parse_create_domain(&mut self) -> Result<Statement, ParserError> {
5981+
let name = self.parse_object_name(false)?;
5982+
self.expect_keyword_is(Keyword::AS)?;
5983+
let data_type = self.parse_data_type()?;
5984+
let collation = if self.parse_keyword(Keyword::COLLATE) {
5985+
Some(self.parse_identifier()?)
5986+
} else {
5987+
None
5988+
};
5989+
let default = if self.parse_keyword(Keyword::DEFAULT) {
5990+
Some(self.parse_expr()?)
5991+
} else {
5992+
None
5993+
};
5994+
let mut constraints = Vec::new();
5995+
while let Some(constraint) = self.parse_optional_table_constraint()? {
5996+
constraints.push(constraint);
5997+
}
5998+
5999+
Ok(Statement::CreateDomain(CreateDomain {
6000+
name,
6001+
data_type,
6002+
collation,
6003+
default,
6004+
constraints,
6005+
}))
6006+
}
6007+
59776008
/// ```sql
59786009
/// CREATE POLICY name ON table_name [ AS { PERMISSIVE | RESTRICTIVE } ]
59796010
/// [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]

tests/sqlparser_postgres.rs

+93
Original file line numberDiff line numberDiff line change
@@ -5153,6 +5153,99 @@ fn test_escaped_string_literal() {
51535153
}
51545154
}
51555155

5156+
#[test]
5157+
fn parse_create_domain() {
5158+
let sql1 = "CREATE DOMAIN my_domain AS INTEGER CHECK (VALUE > 0)";
5159+
let expected = Statement::CreateDomain(CreateDomain {
5160+
name: ObjectName::from(vec![Ident::new("my_domain")]),
5161+
data_type: DataType::Integer(None),
5162+
collation: None,
5163+
default: None,
5164+
constraints: vec![TableConstraint::Check {
5165+
name: None,
5166+
expr: Box::new(Expr::BinaryOp {
5167+
left: Box::new(Expr::Identifier(Ident::new("VALUE"))),
5168+
op: BinaryOperator::Gt,
5169+
right: Box::new(Expr::Value(test_utils::number("0").into())),
5170+
}),
5171+
}],
5172+
});
5173+
5174+
assert_eq!(pg().verified_stmt(sql1), expected);
5175+
5176+
let sql2 = "CREATE DOMAIN my_domain AS INTEGER COLLATE \"en_US\" CHECK (VALUE > 0)";
5177+
let expected = Statement::CreateDomain(CreateDomain {
5178+
name: ObjectName::from(vec![Ident::new("my_domain")]),
5179+
data_type: DataType::Integer(None),
5180+
collation: Some(Ident::with_quote('"', "en_US")),
5181+
default: None,
5182+
constraints: vec![TableConstraint::Check {
5183+
name: None,
5184+
expr: Box::new(Expr::BinaryOp {
5185+
left: Box::new(Expr::Identifier(Ident::new("VALUE"))),
5186+
op: BinaryOperator::Gt,
5187+
right: Box::new(Expr::Value(test_utils::number("0").into())),
5188+
}),
5189+
}],
5190+
});
5191+
5192+
assert_eq!(pg().verified_stmt(sql2), expected);
5193+
5194+
let sql3 = "CREATE DOMAIN my_domain AS INTEGER DEFAULT 1 CHECK (VALUE > 0)";
5195+
let expected = Statement::CreateDomain(CreateDomain {
5196+
name: ObjectName::from(vec![Ident::new("my_domain")]),
5197+
data_type: DataType::Integer(None),
5198+
collation: None,
5199+
default: Some(Expr::Value(test_utils::number("1").into())),
5200+
constraints: vec![TableConstraint::Check {
5201+
name: None,
5202+
expr: Box::new(Expr::BinaryOp {
5203+
left: Box::new(Expr::Identifier(Ident::new("VALUE"))),
5204+
op: BinaryOperator::Gt,
5205+
right: Box::new(Expr::Value(test_utils::number("0").into())),
5206+
}),
5207+
}],
5208+
});
5209+
5210+
assert_eq!(pg().verified_stmt(sql3), expected);
5211+
5212+
let sql4 = "CREATE DOMAIN my_domain AS INTEGER COLLATE \"en_US\" DEFAULT 1 CHECK (VALUE > 0)";
5213+
let expected = Statement::CreateDomain(CreateDomain {
5214+
name: ObjectName::from(vec![Ident::new("my_domain")]),
5215+
data_type: DataType::Integer(None),
5216+
collation: Some(Ident::with_quote('"', "en_US")),
5217+
default: Some(Expr::Value(test_utils::number("1").into())),
5218+
constraints: vec![TableConstraint::Check {
5219+
name: None,
5220+
expr: Box::new(Expr::BinaryOp {
5221+
left: Box::new(Expr::Identifier(Ident::new("VALUE"))),
5222+
op: BinaryOperator::Gt,
5223+
right: Box::new(Expr::Value(test_utils::number("0").into())),
5224+
}),
5225+
}],
5226+
});
5227+
5228+
assert_eq!(pg().verified_stmt(sql4), expected);
5229+
5230+
let sql5 = "CREATE DOMAIN my_domain AS INTEGER CONSTRAINT my_constraint CHECK (VALUE > 0)";
5231+
let expected = Statement::CreateDomain(CreateDomain {
5232+
name: ObjectName::from(vec![Ident::new("my_domain")]),
5233+
data_type: DataType::Integer(None),
5234+
collation: None,
5235+
default: None,
5236+
constraints: vec![TableConstraint::Check {
5237+
name: Some(Ident::new("my_constraint")),
5238+
expr: Box::new(Expr::BinaryOp {
5239+
left: Box::new(Expr::Identifier(Ident::new("VALUE"))),
5240+
op: BinaryOperator::Gt,
5241+
right: Box::new(Expr::Value(test_utils::number("0").into())),
5242+
}),
5243+
}],
5244+
});
5245+
5246+
assert_eq!(pg().verified_stmt(sql5), expected);
5247+
}
5248+
51565249
#[test]
51575250
fn parse_create_simple_before_insert_trigger() {
51585251
let sql = "CREATE TRIGGER check_insert BEFORE INSERT ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_insert";

0 commit comments

Comments
 (0)