From d8f92e8ed79e429ed9ba3d2381297360bb187b85 Mon Sep 17 00:00:00 2001 From: Sergey Olontsev Date: Sun, 29 Mar 2026 12:05:06 +0100 Subject: [PATCH 1/4] dollar prefix support for money constants in mssql --- src/dialect/mod.rs | 6 ++++++ src/dialect/mssql.rs | 6 ++++++ src/tokenizer.rs | 13 +++++++++++++ tests/sqlparser_mssql.rs | 12 ++++++++++++ 4 files changed, 37 insertions(+) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index fed81b60a..de3ba4cb0 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1047,6 +1047,12 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if this dialect supports `$` as a prefix for money literals + /// e.g. `SELECT $123.45` (T-SQL) + fn supports_dollar_as_money_prefix(&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 { diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs index 8ad765dd3..980b63d28 100644 --- a/src/dialect/mssql.rs +++ b/src/dialect/mssql.rs @@ -65,6 +65,12 @@ impl Dialect for MsSqlDialect { true } + /// SQL Server supports `$` as a prefix for money literals + /// + fn supports_dollar_as_money_prefix(&self) -> bool { + true + } + fn supports_connect_by(&self) -> bool { true } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index c055db8fe..9319b5095 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1966,6 +1966,19 @@ impl<'a> Tokenizer<'a> { || matches!(ch, '$' if self.dialect.supports_dollar_placeholder()) })); + // If the dialect supports a dollar sign as a money prefix (e.g., T-SQL), + // and the value so far is all digits, check for a decimal part, e.g. `$123.45` + if matches!(chars.peek(), Some('.')) + && self.dialect.supports_dollar_as_money_prefix() + && !value.is_empty() + && value.chars().all(|c| c.is_ascii_digit()) + { + value.push('.'); + chars.next(); + value.push_str(&peeking_take_while(chars, |ch| ch.is_ascii_digit())); + return Ok(Token::Placeholder(format!("${value}"))); + } + // If the dialect does not support dollar-quoted strings, don't look for the end delimiter. if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() { chars.next(); diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index e8ed79492..efed9a61d 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -2860,3 +2860,15 @@ fn parse_mssql_update_with_output_into() { "UPDATE employees SET salary = salary * 1.1 OUTPUT INSERTED.id, DELETED.salary, INSERTED.salary INTO @changes WHERE department = 'Engineering'", ); } + +#[test] +fn parse_mssql_money_constants() { + ms().verified_only_select("SELECT CEILING($123.45)"); + + ms().verified_only_select("SELECT $123.45"); + ms().verified_only_select("SELECT $0.99"); + ms().verified_only_select("SELECT $0.0"); + + ms().verified_only_select("SELECT $123"); + ms().verified_only_select("SELECT $0"); +} From ff22696edee4212b4fbeb70f438e997434c02868 Mon Sep 17 00:00:00 2001 From: Sergey Olontsev Date: Sun, 29 Mar 2026 12:23:08 +0100 Subject: [PATCH 2/4] test --- tests/sqlparser_mssql.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index efed9a61d..9f7b42eb1 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -2865,10 +2865,33 @@ fn parse_mssql_update_with_output_into() { fn parse_mssql_money_constants() { ms().verified_only_select("SELECT CEILING($123.45)"); - ms().verified_only_select("SELECT $123.45"); - ms().verified_only_select("SELECT $0.99"); - ms().verified_only_select("SELECT $0.0"); + let select = ms().verified_only_select("SELECT $123.45"); + assert_eq!( + &Expr::Value(Value::Placeholder("$123.45".to_string()).with_empty_span()), + expr_from_projection(only(&select.projection)), + ); - ms().verified_only_select("SELECT $123"); - ms().verified_only_select("SELECT $0"); + let select = ms().verified_only_select("SELECT $0.99"); + assert_eq!( + &Expr::Value(Value::Placeholder("$0.99".to_string()).with_empty_span()), + expr_from_projection(only(&select.projection)), + ); + + let select = ms().verified_only_select("SELECT $0.0"); + assert_eq!( + &Expr::Value(Value::Placeholder("$0.0".to_string()).with_empty_span()), + expr_from_projection(only(&select.projection)), + ); + + let select = ms().verified_only_select("SELECT $123"); + assert_eq!( + &Expr::Value(Value::Placeholder("$123".to_string()).with_empty_span()), + expr_from_projection(only(&select.projection)), + ); + + let select = ms().verified_only_select("SELECT $0"); + assert_eq!( + &Expr::Value(Value::Placeholder("$0".to_string()).with_empty_span()), + expr_from_projection(only(&select.projection)), + ); } From a28626d367bf45dea5c0e141b6bab0238ec69fac Mon Sep 17 00:00:00 2001 From: Sergey Olontsev Date: Sun, 29 Mar 2026 12:27:12 +0100 Subject: [PATCH 3/4] fix --- src/dialect/mod.rs | 2 +- src/tokenizer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index de3ba4cb0..bbf7d5804 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1048,7 +1048,7 @@ pub trait Dialect: Debug + Any { } /// Returns true if this dialect supports `$` as a prefix for money literals - /// e.g. `SELECT $123.45` (T-SQL) + /// e.g. `SELECT $123.45` (SQL Server) fn supports_dollar_as_money_prefix(&self) -> bool { false } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 9319b5095..b0e16b2d2 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1966,7 +1966,7 @@ impl<'a> Tokenizer<'a> { || matches!(ch, '$' if self.dialect.supports_dollar_placeholder()) })); - // If the dialect supports a dollar sign as a money prefix (e.g., T-SQL), + // If the dialect supports a dollar sign as a money prefix (e.g., SQL Server), // and the value so far is all digits, check for a decimal part, e.g. `$123.45` if matches!(chars.peek(), Some('.')) && self.dialect.supports_dollar_as_money_prefix() From 6f6bd5441d3b9adf1c81519892442029ed43df53 Mon Sep 17 00:00:00 2001 From: Sergey Olontsev Date: Sun, 29 Mar 2026 12:27:55 +0100 Subject: [PATCH 4/4] fix --- src/tokenizer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index b0e16b2d2..d9f131f8f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1966,7 +1966,7 @@ impl<'a> Tokenizer<'a> { || matches!(ch, '$' if self.dialect.supports_dollar_placeholder()) })); - // If the dialect supports a dollar sign as a money prefix (e.g., SQL Server), + // If the dialect supports a dollar sign as a money prefix (e.g. SQL Server), // and the value so far is all digits, check for a decimal part, e.g. `$123.45` if matches!(chars.peek(), Some('.')) && self.dialect.supports_dollar_as_money_prefix()