Skip to content

Commit f022dfa

Browse files
committed
fixup: change to supports_is_not_null_alias(IsNotNullAlias)
1 parent d6f51a1 commit f022dfa

File tree

7 files changed

+69
-45
lines changed

7 files changed

+69
-45
lines changed

src/ast/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -759,8 +759,8 @@ pub enum Expr {
759759
/// `NOTNULL` or `NOT NULL` operator
760760
NotNull {
761761
expr: Box<Expr>,
762-
/// true if `NOTNULL`, false if `NOT NULL`
763-
one_word: bool,
762+
/// true if `NOT NULL`, false if `NOTNULL`
763+
with_space: bool,
764764
},
765765
/// `IS UNKNOWN` operator
766766
IsUnknown(Box<Expr>),
@@ -1436,11 +1436,11 @@ impl fmt::Display for Expr {
14361436
Expr::IsNotFalse(ast) => write!(f, "{ast} IS NOT FALSE"),
14371437
Expr::IsNull(ast) => write!(f, "{ast} IS NULL"),
14381438
Expr::IsNotNull(ast) => write!(f, "{ast} IS NOT NULL"),
1439-
Expr::NotNull { expr, one_word } => write!(
1439+
Expr::NotNull { expr, with_space } => write!(
14401440
f,
14411441
"{} {}",
14421442
expr,
1443-
if *one_word { "NOTNULL" } else { "NOT NULL" }
1443+
if *with_space { "NOT NULL" } else { "NOTNULL" }
14441444
),
14451445
Expr::IsUnknown(ast) => write!(f, "{ast} IS UNKNOWN"),
14461446
Expr::IsNotUnknown(ast) => write!(f, "{ast} IS NOT UNKNOWN"),

src/dialect/duckdb.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18-
use crate::dialect::Dialect;
18+
use crate::dialect::{Dialect, IsNotNullAlias};
1919

2020
/// A [`Dialect`] for [DuckDB](https://duckdb.org/)
2121
#[derive(Debug, Default)]
@@ -95,11 +95,12 @@ impl Dialect for DuckDbDialect {
9595
true
9696
}
9797

98-
fn supports_not_null(&self) -> bool {
99-
true
100-
}
101-
102-
fn supports_notnull(&self) -> bool {
103-
true
98+
/// DuckDB supports `NOT NULL` and `NOTNULL` as aliases
99+
/// for `IS NOT NULL`, see DuckDB Comparisons <https://duckdb.org/docs/stable/sql/expressions/comparison_operators#between-and-is-not-null>
100+
fn supports_is_not_null_alias(&self, alias: IsNotNullAlias) -> bool {
101+
match alias {
102+
IsNotNullAlias::NotNull => true,
103+
IsNotNullAlias::NotSpaceNull => true,
104+
}
104105
}
105106
}

src/dialect/mod.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use crate::keywords::Keyword;
5555
use crate::parser::{Parser, ParserError};
5656
use crate::tokenizer::Token;
5757

58+
use crate::dialect::IsNotNullAlias::{NotNull, NotSpaceNull};
5859
#[cfg(not(feature = "std"))]
5960
use alloc::boxed::Box;
6061

@@ -650,12 +651,17 @@ pub trait Dialect: Debug + Any {
650651
Token::Word(w) if w.keyword == Keyword::MATCH => Ok(p!(Like)),
651652
Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(p!(Like)),
652653
Token::Word(w) if w.keyword == Keyword::MEMBER => Ok(p!(Like)),
653-
Token::Word(w) if w.keyword == Keyword::NULL && self.supports_not_null() => {
654+
Token::Word(w)
655+
if w.keyword == Keyword::NULL
656+
&& self.supports_is_not_null_alias(NotSpaceNull) =>
657+
{
654658
Ok(p!(Is))
655659
}
656660
_ => Ok(self.prec_unknown()),
657661
},
658-
Token::Word(w) if w.keyword == Keyword::NOTNULL && self.supports_notnull() => {
662+
Token::Word(w)
663+
if w.keyword == Keyword::NOTNULL && self.supports_is_not_null_alias(NotNull) =>
664+
{
659665
Ok(p!(Is))
660666
}
661667
Token::Word(w) if w.keyword == Keyword::IS => Ok(p!(Is)),
@@ -1083,14 +1089,13 @@ pub trait Dialect: Debug + Any {
10831089
false
10841090
}
10851091

1086-
/// Returns true if the dialect supports `NOTNULL` in expressions.
1087-
fn supports_notnull(&self) -> bool {
1088-
false
1089-
}
1090-
1091-
/// Returns true if the dialect supports `NOT NULL` in expressions.
1092-
fn supports_not_null(&self) -> bool {
1093-
false
1092+
/// Returns true if the dialect supports the passed in alias.
1093+
/// See [IsNotNullAlias].
1094+
fn supports_is_not_null_alias(&self, alias: IsNotNullAlias) -> bool {
1095+
match alias {
1096+
NotNull => false,
1097+
NotSpaceNull => false,
1098+
}
10941099
}
10951100
}
10961101

@@ -1118,6 +1123,13 @@ pub enum Precedence {
11181123
Or,
11191124
}
11201125

1126+
/// Possible aliases for `IS NOT NULL` supported
1127+
/// by some non-standard dialects.
1128+
pub enum IsNotNullAlias {
1129+
NotNull,
1130+
NotSpaceNull,
1131+
}
1132+
11211133
impl dyn Dialect {
11221134
#[inline]
11231135
pub fn is<T: Dialect>(&self) -> bool {

src/dialect/postgresql.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
// limitations under the License.
2929
use log::debug;
3030

31-
use crate::dialect::{Dialect, Precedence};
31+
use crate::dialect::{Dialect, IsNotNullAlias, Precedence};
3232
use crate::keywords::Keyword;
3333
use crate::parser::{Parser, ParserError};
3434
use crate::tokenizer::Token;
@@ -263,7 +263,12 @@ impl Dialect for PostgreSqlDialect {
263263
true
264264
}
265265

266-
fn supports_notnull(&self) -> bool {
267-
true
266+
/// Postgres supports `NOTNULL` as an alias for `IS NOT NULL`
267+
/// but does not support `NOT NULL`. See: <https://www.postgresql.org/docs/17/functions-comparison.html>
268+
fn supports_is_not_null_alias(&self, alias: IsNotNullAlias) -> bool {
269+
match alias {
270+
IsNotNullAlias::NotNull => true,
271+
IsNotNullAlias::NotSpaceNull => false,
272+
}
268273
}
269274
}

src/dialect/sqlite.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use alloc::boxed::Box;
2020

2121
use crate::ast::BinaryOperator;
2222
use crate::ast::{Expr, Statement};
23-
use crate::dialect::Dialect;
23+
use crate::dialect::{Dialect, IsNotNullAlias};
2424
use crate::keywords::Keyword;
2525
use crate::parser::{Parser, ParserError};
2626

@@ -111,11 +111,12 @@ impl Dialect for SQLiteDialect {
111111
true
112112
}
113113

114-
fn supports_not_null(&self) -> bool {
115-
true
116-
}
117-
118-
fn supports_notnull(&self) -> bool {
119-
true
114+
/// SQLite supports ``NOT NULL` and `NOTNULL` as
115+
/// aliases for `IS NOT NULL`, see: <https://sqlite.org/syntax/expr.html>
116+
fn supports_is_not_null_alias(&self, alias: IsNotNullAlias) -> bool {
117+
match alias {
118+
IsNotNullAlias::NotNull => true,
119+
IsNotNullAlias::NotSpaceNull => true,
120+
}
120121
}
121122
}

src/parser/mod.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use IsOptional::*;
3535
use crate::ast::helpers::stmt_create_table::{CreateTableBuilder, CreateTableConfiguration};
3636
use crate::ast::Statement::CreatePolicy;
3737
use crate::ast::*;
38+
use crate::dialect::IsNotNullAlias::{NotNull, NotSpaceNull};
3839
use crate::dialect::*;
3940
use crate::keywords::{Keyword, ALL_KEYWORDS};
4041
use crate::tokenizer::*;
@@ -3572,10 +3573,10 @@ impl<'a> Parser<'a> {
35723573
),
35733574
regexp,
35743575
})
3575-
} else if dialect.supports_not_null() && negated && null {
3576+
} else if dialect.supports_is_not_null_alias(NotSpaceNull) && negated && null {
35763577
Ok(Expr::NotNull {
35773578
expr: Box::new(expr),
3578-
one_word: false,
3579+
with_space: true,
35793580
})
35803581
} else if self.parse_keyword(Keyword::IN) {
35813582
self.parse_in(expr, negated)
@@ -3614,10 +3615,12 @@ impl<'a> Parser<'a> {
36143615
self.expected("IN or BETWEEN after NOT", self.peek_token())
36153616
}
36163617
}
3617-
Keyword::NOTNULL if dialect.supports_notnull() => Ok(Expr::NotNull {
3618-
expr: Box::new(expr),
3619-
one_word: true,
3620-
}),
3618+
Keyword::NOTNULL if dialect.supports_is_not_null_alias(NotNull) => {
3619+
Ok(Expr::NotNull {
3620+
expr: Box::new(expr),
3621+
with_space: false,
3622+
})
3623+
}
36213624
Keyword::MEMBER => {
36223625
if self.parse_keyword(Keyword::OF) {
36233626
self.expect_token(&Token::LParen)?;

tests/sqlparser_common.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use sqlparser::ast::TableFactor::{Pivot, Unpivot};
3232
use sqlparser::ast::*;
3333
use sqlparser::dialect::{
3434
AnsiDialect, BigQueryDialect, ClickHouseDialect, DatabricksDialect, Dialect, DuckDbDialect,
35-
GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect,
36-
SQLiteDialect, SnowflakeDialect,
35+
GenericDialect, HiveDialect, IsNotNullAlias, MsSqlDialect, MySqlDialect, PostgreSqlDialect,
36+
RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect,
3737
};
3838
use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
3939
use sqlparser::parser::{Parser, ParserError, ParserOptions};
@@ -15980,7 +15980,8 @@ fn parse_not_null_unsupported() {
1598015980
// Only DuckDB and SQLite support `x NOT NULL` as an expression
1598115981
// All other dialects fail to parse.
1598215982
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOT NULL FROM t"#;
15983-
let dialects = all_dialects_except(|d| d.supports_not_null());
15983+
let dialects =
15984+
all_dialects_except(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotSpaceNull));
1598415985
let res = dialects.parse_sql_statements(sql);
1598515986
assert_eq!(
1598615987
ParserError::ParserError("Expected: end of statement, found: NULL".to_string()),
@@ -15992,7 +15993,8 @@ fn parse_not_null_unsupported() {
1599215993
fn parse_not_null_supported() {
1599315994
// DuckDB and SQLite support `x NOT NULL` as an expression
1599415995
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOT NULL FROM t"#;
15995-
let dialects = all_dialects_where(|d| d.supports_not_null());
15996+
let dialects =
15997+
all_dialects_where(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotSpaceNull));
1599615998
let stmt = dialects.one_statement_parses_to(sql, sql);
1599715999
match stmt {
1599816000
Statement::Query(qry) => match *qry.body {
@@ -16012,7 +16014,7 @@ fn parse_not_null_supported() {
1601216014
quote_style: None,
1601316015
span: fake_span,
1601416016
})),
16015-
one_word: false,
16017+
with_space: true,
1601616018
},
1601716019
);
1601816020
}
@@ -16032,7 +16034,7 @@ fn parse_notnull_unsupported() {
1603216034
// consider `NOTNULL` an alias for x.
1603316035
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOTNULL FROM t"#;
1603416036
let canonical = r#"WITH t AS (SELECT NULL AS x) SELECT x AS NOTNULL FROM t"#;
16035-
let dialects = all_dialects_except(|d| d.supports_notnull());
16037+
let dialects = all_dialects_except(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotNull));
1603616038
let stmt = dialects.one_statement_parses_to(sql, canonical);
1603716039
match stmt {
1603816040
Statement::Query(qry) => match *qry.body {
@@ -16074,7 +16076,7 @@ fn parse_notnull_unsupported() {
1607416076
fn parse_notnull_supported() {
1607516077
// DuckDB and SQLite support `x NOT NULL` as an expression
1607616078
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOTNULL FROM t"#;
16077-
let dialects = all_dialects_where(|d| d.supports_notnull());
16079+
let dialects = all_dialects_where(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotNull));
1607816080
let stmt = dialects.one_statement_parses_to(sql, "");
1607916081
match stmt {
1608016082
Statement::Query(qry) => match *qry.body {
@@ -16094,7 +16096,7 @@ fn parse_notnull_supported() {
1609416096
quote_style: None,
1609516097
span: fake_span,
1609616098
})),
16097-
one_word: true,
16099+
with_space: false,
1609816100
},
1609916101
);
1610016102
}

0 commit comments

Comments
 (0)