Skip to content

Commit cb0664f

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents d3cbf04 + 15dc6a2 commit cb0664f

10 files changed

Lines changed: 250 additions & 10 deletions

File tree

src/ast/mod.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ mod dml;
136136
pub mod helpers;
137137
pub mod table_constraints;
138138
pub use table_constraints::{
139-
CheckConstraint, ForeignKeyConstraint, FullTextOrSpatialConstraint, IndexConstraint,
140-
PrimaryKeyConstraint, TableConstraint, UniqueConstraint,
139+
CheckConstraint, ConstraintUsingIndex, ForeignKeyConstraint, FullTextOrSpatialConstraint,
140+
IndexConstraint, PrimaryKeyConstraint, TableConstraint, UniqueConstraint,
141141
};
142142
mod operator;
143143
mod query;
@@ -4764,6 +4764,10 @@ pub enum Statement {
47644764
///
47654765
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/statements/print-transact-sql>
47664766
Print(PrintStatement),
4767+
/// MSSQL `WAITFOR` statement.
4768+
///
4769+
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
4770+
WaitFor(WaitForStatement),
47674771
/// ```sql
47684772
/// RETURN [ expression ]
47694773
/// ```
@@ -6217,6 +6221,7 @@ impl fmt::Display for Statement {
62176221
}
62186222
Statement::Throw(s) => write!(f, "{s}"),
62196223
Statement::Print(s) => write!(f, "{s}"),
6224+
Statement::WaitFor(s) => write!(f, "{s}"),
62206225
Statement::Return(r) => write!(f, "{r}"),
62216226
Statement::List(command) => write!(f, "LIST {command}"),
62226227
Statement::Remove(command) => write!(f, "REMOVE {command}"),
@@ -10964,6 +10969,47 @@ impl fmt::Display for PrintStatement {
1096410969
}
1096510970
}
1096610971

10972+
/// The type of `WAITFOR` statement (MSSQL).
10973+
///
10974+
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
10975+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10976+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10977+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10978+
pub enum WaitForType {
10979+
/// `WAITFOR DELAY 'time_to_pass'`
10980+
Delay,
10981+
/// `WAITFOR TIME 'time_to_execute'`
10982+
Time,
10983+
}
10984+
10985+
impl fmt::Display for WaitForType {
10986+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10987+
match self {
10988+
WaitForType::Delay => write!(f, "DELAY"),
10989+
WaitForType::Time => write!(f, "TIME"),
10990+
}
10991+
}
10992+
}
10993+
10994+
/// MSSQL `WAITFOR` statement.
10995+
///
10996+
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
10997+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
10998+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
10999+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
11000+
pub struct WaitForStatement {
11001+
/// `DELAY` or `TIME`.
11002+
pub wait_type: WaitForType,
11003+
/// The time expression.
11004+
pub expr: Expr,
11005+
}
11006+
11007+
impl fmt::Display for WaitForStatement {
11008+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11009+
write!(f, "WAITFOR {} {}", self.wait_type, self.expr)
11010+
}
11011+
}
11012+
1096711013
/// Represents a `Return` statement.
1096811014
///
1096911015
/// [MsSql triggers](https://learn.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql)

src/ast/spans.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ impl Spanned for Statement {
483483
Statement::RaisError { .. } => Span::empty(),
484484
Statement::Throw(_) => Span::empty(),
485485
Statement::Print { .. } => Span::empty(),
486+
Statement::WaitFor(_) => Span::empty(),
486487
Statement::Return { .. } => Span::empty(),
487488
Statement::List(..) | Statement::Remove(..) => Span::empty(),
488489
Statement::ExportData(ExportData {
@@ -625,6 +626,8 @@ impl Spanned for TableConstraint {
625626
TableConstraint::Check(constraint) => constraint.span(),
626627
TableConstraint::Index(constraint) => constraint.span(),
627628
TableConstraint::FulltextOrSpatial(constraint) => constraint.span(),
629+
TableConstraint::PrimaryKeyUsingIndex(constraint)
630+
| TableConstraint::UniqueUsingIndex(constraint) => constraint.span(),
628631
}
629632
}
630633
}

src/ast/table_constraints.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,22 @@ pub enum TableConstraint {
101101
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
102102
/// [2]: https://dev.mysql.com/doc/refman/8.0/en/spatial-types.html
103103
FulltextOrSpatial(FullTextOrSpatialConstraint),
104+
/// PostgreSQL [definition][1] for promoting an existing unique index to a
105+
/// `PRIMARY KEY` constraint:
106+
///
107+
/// `[ CONSTRAINT constraint_name ] PRIMARY KEY USING INDEX index_name
108+
/// [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]`
109+
///
110+
/// [1]: https://www.postgresql.org/docs/current/sql-altertable.html
111+
PrimaryKeyUsingIndex(ConstraintUsingIndex),
112+
/// PostgreSQL [definition][1] for promoting an existing unique index to a
113+
/// `UNIQUE` constraint:
114+
///
115+
/// `[ CONSTRAINT constraint_name ] UNIQUE USING INDEX index_name
116+
/// [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]`
117+
///
118+
/// [1]: https://www.postgresql.org/docs/current/sql-altertable.html
119+
UniqueUsingIndex(ConstraintUsingIndex),
104120
}
105121

106122
impl From<UniqueConstraint> for TableConstraint {
@@ -148,6 +164,8 @@ impl fmt::Display for TableConstraint {
148164
TableConstraint::Check(constraint) => constraint.fmt(f),
149165
TableConstraint::Index(constraint) => constraint.fmt(f),
150166
TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
167+
TableConstraint::PrimaryKeyUsingIndex(c) => c.fmt_with_keyword(f, "PRIMARY KEY"),
168+
TableConstraint::UniqueUsingIndex(c) => c.fmt_with_keyword(f, "UNIQUE"),
151169
}
152170
}
153171
}
@@ -535,3 +553,53 @@ impl crate::ast::Spanned for UniqueConstraint {
535553
)
536554
}
537555
}
556+
557+
/// PostgreSQL constraint that promotes an existing unique index to a table constraint.
558+
///
559+
/// `[ CONSTRAINT constraint_name ] { UNIQUE | PRIMARY KEY } USING INDEX index_name
560+
/// [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]`
561+
///
562+
/// See <https://www.postgresql.org/docs/current/sql-altertable.html>
563+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
564+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
565+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
566+
pub struct ConstraintUsingIndex {
567+
/// Optional constraint name.
568+
pub name: Option<Ident>,
569+
/// The name of the existing unique index to promote.
570+
pub index_name: Ident,
571+
/// Optional characteristics like `DEFERRABLE`.
572+
pub characteristics: Option<ConstraintCharacteristics>,
573+
}
574+
575+
impl ConstraintUsingIndex {
576+
/// Format as `[CONSTRAINT name] <keyword> USING INDEX index_name [characteristics]`.
577+
pub fn fmt_with_keyword(&self, f: &mut fmt::Formatter, keyword: &str) -> fmt::Result {
578+
use crate::ast::ddl::{display_constraint_name, display_option_spaced};
579+
write!(
580+
f,
581+
"{}{} USING INDEX {}",
582+
display_constraint_name(&self.name),
583+
keyword,
584+
self.index_name,
585+
)?;
586+
write!(f, "{}", display_option_spaced(&self.characteristics))?;
587+
Ok(())
588+
}
589+
}
590+
591+
impl crate::ast::Spanned for ConstraintUsingIndex {
592+
fn span(&self) -> Span {
593+
let start = self
594+
.name
595+
.as_ref()
596+
.map(|i| i.span)
597+
.unwrap_or(self.index_name.span);
598+
let end = self
599+
.characteristics
600+
.as_ref()
601+
.map(|c| c.span())
602+
.unwrap_or(self.index_name.span);
603+
start.union(&end)
604+
}
605+
}

src/dialect/generic.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,10 +225,6 @@ impl Dialect for GenericDialect {
225225
true
226226
}
227227

228-
fn supports_lambda_functions(&self) -> bool {
229-
true
230-
}
231-
232228
fn supports_select_wildcard_replace(&self) -> bool {
233229
true
234230
}

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ define_keywords!(
312312
DEFINE,
313313
DEFINED,
314314
DEFINER,
315+
DELAY,
315316
DELAYED,
316317
DELAY_KEY_WRITE,
317318
DELEGATED,
@@ -1138,6 +1139,7 @@ define_keywords!(
11381139
VIRTUAL,
11391140
VOLATILE,
11401141
VOLUME,
1142+
WAITFOR,
11411143
WAREHOUSE,
11421144
WAREHOUSES,
11431145
WEEK,

src/parser/mod.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,8 @@ impl<'a> Parser<'a> {
703703
// `COMMENT` is snowflake specific https://docs.snowflake.com/en/sql-reference/sql/comment
704704
Keyword::COMMENT if self.dialect.supports_comment_on() => self.parse_comment(),
705705
Keyword::PRINT => self.parse_print(),
706+
// `WAITFOR` is MSSQL specific https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql
707+
Keyword::WAITFOR => self.parse_waitfor(),
706708
Keyword::RETURN => self.parse_return(),
707709
Keyword::EXPORT => {
708710
self.prev_token();
@@ -9331,6 +9333,21 @@ impl<'a> Parser<'a> {
93319333
}
93329334
}
93339335

9336+
/// Parse `index_name [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]`
9337+
/// after `{ PRIMARY KEY | UNIQUE } USING INDEX`.
9338+
fn parse_constraint_using_index(
9339+
&mut self,
9340+
name: Option<Ident>,
9341+
) -> Result<ConstraintUsingIndex, ParserError> {
9342+
let index_name = self.parse_identifier()?;
9343+
let characteristics = self.parse_constraint_characteristics()?;
9344+
Ok(ConstraintUsingIndex {
9345+
name,
9346+
index_name,
9347+
characteristics,
9348+
})
9349+
}
9350+
93349351
/// Parse optional constraint characteristics such as `DEFERRABLE`, `INITIALLY` and `ENFORCED`.
93359352
pub fn parse_constraint_characteristics(
93369353
&mut self,
@@ -9395,6 +9412,14 @@ impl<'a> Parser<'a> {
93959412
let next_token = self.next_token();
93969413
match next_token.token {
93979414
Token::Word(w) if w.keyword == Keyword::UNIQUE => {
9415+
// PostgreSQL: UNIQUE USING INDEX index_name
9416+
// https://www.postgresql.org/docs/current/sql-altertable.html
9417+
if self.parse_keywords(&[Keyword::USING, Keyword::INDEX]) {
9418+
return Ok(Some(TableConstraint::UniqueUsingIndex(
9419+
self.parse_constraint_using_index(name)?,
9420+
)));
9421+
}
9422+
93989423
let index_type_display = self.parse_index_type_display();
93999424
if !dialect_of!(self is GenericDialect | MySqlDialect)
94009425
&& !index_type_display.is_none()
@@ -9430,6 +9455,14 @@ impl<'a> Parser<'a> {
94309455
// after `PRIMARY` always stay `KEY`
94319456
self.expect_keyword_is(Keyword::KEY)?;
94329457

9458+
// PostgreSQL: PRIMARY KEY USING INDEX index_name
9459+
// https://www.postgresql.org/docs/current/sql-altertable.html
9460+
if self.parse_keywords(&[Keyword::USING, Keyword::INDEX]) {
9461+
return Ok(Some(TableConstraint::PrimaryKeyUsingIndex(
9462+
self.parse_constraint_using_index(name)?,
9463+
)));
9464+
}
9465+
94339466
// optional index name
94349467
let index_name = self.parse_optional_ident()?;
94359468
let index_type = self.parse_optional_using_then_index_type()?;
@@ -19289,6 +19322,21 @@ impl<'a> Parser<'a> {
1928919322
}))
1929019323
}
1929119324

19325+
/// Parse [Statement::WaitFor]
19326+
///
19327+
/// See: <https://learn.microsoft.com/en-us/sql/t-sql/language-elements/waitfor-transact-sql>
19328+
fn parse_waitfor(&mut self) -> Result<Statement, ParserError> {
19329+
let wait_type = if self.parse_keyword(Keyword::DELAY) {
19330+
WaitForType::Delay
19331+
} else if self.parse_keyword(Keyword::TIME) {
19332+
WaitForType::Time
19333+
} else {
19334+
return self.expected("DELAY or TIME", self.peek_token());
19335+
};
19336+
let expr = self.parse_expr()?;
19337+
Ok(Statement::WaitFor(WaitForStatement { wait_type, expr }))
19338+
}
19339+
1929219340
/// Parse [Statement::Return]
1929319341
fn parse_return(&mut self) -> Result<Statement, ParserError> {
1929419342
match self.maybe_parse(|p| p.parse_expr())? {

tests/sqlparser_common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,7 @@ fn parse_json_ops_without_colon() {
16631663
Arrow,
16641664
all_dialects_except(|d| d.supports_lambda_functions()),
16651665
),
1666+
("->", Arrow, pg_and_generic()),
16661667
("->>", LongArrow, all_dialects()),
16671668
("#>", HashArrow, pg_and_generic()),
16681669
("#>>", HashLongArrow, pg_and_generic()),

tests/sqlparser_duckdb.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -882,17 +882,17 @@ fn parse_extract_single_quotes() {
882882
fn test_duckdb_lambda_function() {
883883
// Test basic lambda with list_filter
884884
let sql = "SELECT [3, 4, 5, 6].list_filter(lambda x : x > 4)";
885-
duckdb_and_generic().verified_stmt(sql);
885+
duckdb().verified_stmt(sql);
886886

887887
// Test lambda with arrow syntax (also supported by DuckDB)
888888
let sql_arrow = "SELECT list_filter([1, 2, 3], x -> x > 1)";
889-
duckdb_and_generic().verified_stmt(sql_arrow);
889+
duckdb().verified_stmt(sql_arrow);
890890

891891
// Test lambda with multiple parameters (with index)
892892
let sql_multi = "SELECT list_filter([1, 3, 1, 5], lambda x, i : x > i)";
893-
duckdb_and_generic().verified_stmt(sql_multi);
893+
duckdb().verified_stmt(sql_multi);
894894

895895
// Test lambda in list_transform
896896
let sql_transform = "SELECT list_transform([1, 2, 3], lambda x : x * 2)";
897-
duckdb_and_generic().verified_stmt(sql_transform);
897+
duckdb().verified_stmt(sql_transform);
898898
}

tests/sqlparser_mssql.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,43 @@ fn test_parse_throw() {
17021702
);
17031703
}
17041704

1705+
#[test]
1706+
fn test_parse_waitfor() {
1707+
// WAITFOR DELAY
1708+
let sql = "WAITFOR DELAY '00:00:05'";
1709+
let stmt = ms_and_generic().verified_stmt(sql);
1710+
assert_eq!(
1711+
stmt,
1712+
Statement::WaitFor(WaitForStatement {
1713+
wait_type: WaitForType::Delay,
1714+
expr: Expr::Value(
1715+
(Value::SingleQuotedString("00:00:05".to_string())).with_empty_span()
1716+
),
1717+
})
1718+
);
1719+
1720+
// WAITFOR TIME
1721+
let sql = "WAITFOR TIME '14:30:00'";
1722+
let stmt = ms_and_generic().verified_stmt(sql);
1723+
assert_eq!(
1724+
stmt,
1725+
Statement::WaitFor(WaitForStatement {
1726+
wait_type: WaitForType::Time,
1727+
expr: Expr::Value(
1728+
(Value::SingleQuotedString("14:30:00".to_string())).with_empty_span()
1729+
),
1730+
})
1731+
);
1732+
1733+
// WAITFOR DELAY with variable
1734+
let sql = "WAITFOR DELAY @WaitTime";
1735+
let _ = ms_and_generic().verified_stmt(sql);
1736+
1737+
// Error: WAITFOR without DELAY or TIME
1738+
let res = ms_and_generic().parse_sql_statements("WAITFOR '00:00:05'");
1739+
assert!(res.is_err());
1740+
}
1741+
17051742
#[test]
17061743
fn parse_use() {
17071744
let valid_object_names = [

0 commit comments

Comments
 (0)