diff --git a/src/ast/dml.rs b/src/ast/dml.rs index 95ed9f00e..884b378db 100644 --- a/src/ast/dml.rs +++ b/src/ast/dml.rs @@ -45,6 +45,8 @@ pub struct CreateIndex { pub if_not_exists: bool, pub include: Vec, pub nulls_distinct: Option, + /// WITH clause: + pub with: Vec, pub predicate: Option, } @@ -83,6 +85,9 @@ impl Display for CreateIndex { write!(f, " NULLS NOT DISTINCT")?; } } + if !self.with.is_empty() { + write!(f, " WITH ({})", display_comma_separated(&self.with))?; + } if let Some(predicate) = &self.predicate { write!(f, " WHERE {predicate}")?; } diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index 211abd976..c8f1c00d9 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -86,4 +86,8 @@ impl Dialect for GenericDialect { fn allow_extract_single_quotes(&self) -> bool { true } + + fn supports_create_index_with_clause(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 143c8e1c9..2a74d9925 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -509,6 +509,12 @@ pub trait Dialect: Debug + Any { fn allow_extract_single_quotes(&self) -> bool { false } + + /// Does the dialect support with clause in create index statement? + /// e.g. `CREATE INDEX idx ON t WITH (key = value, key2)` + fn supports_create_index_with_clause(&self) -> bool { + false + } } /// This represents the operators for which precedence must be defined diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 8abaa4a5f..eba3a6989 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -162,6 +162,10 @@ impl Dialect for PostgreSqlDialect { fn allow_extract_single_quotes(&self) -> bool { true } + + fn supports_create_index_with_clause(&self) -> bool { + true + } } pub fn parse_comment(parser: &mut Parser) -> Result { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b57ea614e..977372656 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5324,6 +5324,17 @@ impl<'a> Parser<'a> { None }; + let with = if self.dialect.supports_create_index_with_clause() + && self.parse_keyword(Keyword::WITH) + { + self.expect_token(&Token::LParen)?; + let with_params = self.parse_comma_separated(Parser::parse_expr)?; + self.expect_token(&Token::RParen)?; + with_params + } else { + Vec::new() + }; + let predicate = if self.parse_keyword(Keyword::WHERE) { Some(self.parse_expr()?) } else { @@ -5340,6 +5351,7 @@ impl<'a> Parser<'a> { if_not_exists, include, nulls_distinct, + with, predicate, })) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index dd4aad146..fbe97171b 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -7554,6 +7554,7 @@ fn test_create_index_with_using_function() { if_not_exists, include, nulls_distinct: None, + with, predicate: None, }) => { assert_eq!("idx_name", name.to_string()); @@ -7564,6 +7565,52 @@ fn test_create_index_with_using_function() { assert!(!concurrently); assert!(if_not_exists); assert!(include.is_empty()); + assert!(with.is_empty()); + } + _ => unreachable!(), + } +} + +#[test] +fn test_create_index_with_with_clause() { + let sql = "CREATE UNIQUE INDEX title_idx ON films(title) WITH (fillfactor = 70, single_param)"; + let indexed_columns = vec![OrderByExpr { + expr: Expr::Identifier(Ident::new("title")), + asc: None, + nulls_first: None, + with_fill: None, + }]; + let with_parameters = vec![ + Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("fillfactor"))), + op: BinaryOperator::Eq, + right: Box::new(Expr::Value(number("70"))), + }, + Expr::Identifier(Ident::new("single_param")), + ]; + let dialects = all_dialects_where(|d| d.supports_create_index_with_clause()); + match dialects.verified_stmt(sql) { + Statement::CreateIndex(CreateIndex { + name: Some(name), + table_name, + using: None, + columns, + unique, + concurrently, + if_not_exists, + include, + nulls_distinct: None, + with, + predicate: None, + }) => { + pretty_assertions::assert_eq!("title_idx", name.to_string()); + pretty_assertions::assert_eq!("films", table_name.to_string()); + pretty_assertions::assert_eq!(indexed_columns, columns); + assert!(unique); + assert!(!concurrently); + assert!(!if_not_exists); + assert!(include.is_empty()); + pretty_assertions::assert_eq!(with_parameters, with); } _ => unreachable!(), } diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 59afd7402..d96211823 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -2251,6 +2251,7 @@ fn parse_create_index() { if_not_exists, nulls_distinct: None, include, + with, predicate: None, }) => { assert_eq_vec(&["my_index"], &name); @@ -2261,6 +2262,7 @@ fn parse_create_index() { assert!(if_not_exists); assert_eq_vec(&["col1", "col2"], &columns); assert!(include.is_empty()); + assert!(with.is_empty()); } _ => unreachable!(), } @@ -2280,6 +2282,7 @@ fn parse_create_anonymous_index() { if_not_exists, include, nulls_distinct: None, + with, predicate: None, }) => { assert_eq!(None, name); @@ -2290,6 +2293,7 @@ fn parse_create_anonymous_index() { assert!(!if_not_exists); assert_eq_vec(&["col1", "col2"], &columns); assert!(include.is_empty()); + assert!(with.is_empty()); } _ => unreachable!(), } @@ -2309,6 +2313,7 @@ fn parse_create_index_concurrently() { if_not_exists, include, nulls_distinct: None, + with, predicate: None, }) => { assert_eq_vec(&["my_index"], &name); @@ -2319,6 +2324,7 @@ fn parse_create_index_concurrently() { assert!(if_not_exists); assert_eq_vec(&["col1", "col2"], &columns); assert!(include.is_empty()); + assert!(with.is_empty()); } _ => unreachable!(), } @@ -2338,6 +2344,7 @@ fn parse_create_index_with_predicate() { if_not_exists, include, nulls_distinct: None, + with, predicate: Some(_), }) => { assert_eq_vec(&["my_index"], &name); @@ -2348,6 +2355,7 @@ fn parse_create_index_with_predicate() { assert!(if_not_exists); assert_eq_vec(&["col1", "col2"], &columns); assert!(include.is_empty()); + assert!(with.is_empty()); } _ => unreachable!(), } @@ -2367,6 +2375,7 @@ fn parse_create_index_with_include() { if_not_exists, include, nulls_distinct: None, + with, predicate: None, }) => { assert_eq_vec(&["my_index"], &name); @@ -2377,6 +2386,7 @@ fn parse_create_index_with_include() { assert!(if_not_exists); assert_eq_vec(&["col1", "col2"], &columns); assert_eq_vec(&["col3"], &include); + assert!(with.is_empty()); } _ => unreachable!(), } @@ -2396,6 +2406,7 @@ fn parse_create_index_with_nulls_distinct() { if_not_exists, include, nulls_distinct: Some(nulls_distinct), + with, predicate: None, }) => { assert_eq_vec(&["my_index"], &name); @@ -2407,6 +2418,7 @@ fn parse_create_index_with_nulls_distinct() { assert_eq_vec(&["col1", "col2"], &columns); assert!(include.is_empty()); assert!(!nulls_distinct); + assert!(with.is_empty()); } _ => unreachable!(), } @@ -2423,6 +2435,7 @@ fn parse_create_index_with_nulls_distinct() { if_not_exists, include, nulls_distinct: Some(nulls_distinct), + with, predicate: None, }) => { assert_eq_vec(&["my_index"], &name); @@ -2434,6 +2447,7 @@ fn parse_create_index_with_nulls_distinct() { assert_eq_vec(&["col1", "col2"], &columns); assert!(include.is_empty()); assert!(nulls_distinct); + assert!(with.is_empty()); } _ => unreachable!(), }