Skip to content

Commit fe51680

Browse files
committed
fixup: change to supports_is_not_null_alias(IsNotNullAlias)
1 parent 3b5cdce commit fe51680

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
@@ -781,8 +781,8 @@ pub enum Expr {
781781
/// `NOTNULL` or `NOT NULL` operator
782782
NotNull {
783783
expr: Box<Expr>,
784-
/// true if `NOTNULL`, false if `NOT NULL`
785-
one_word: bool,
784+
/// true if `NOT NULL`, false if `NOTNULL`
785+
with_space: bool,
786786
},
787787
/// `IS UNKNOWN` operator
788788
IsUnknown(Box<Expr>),
@@ -1458,11 +1458,11 @@ impl fmt::Display for Expr {
14581458
Expr::IsNotFalse(ast) => write!(f, "{ast} IS NOT FALSE"),
14591459
Expr::IsNull(ast) => write!(f, "{ast} IS NULL"),
14601460
Expr::IsNotNull(ast) => write!(f, "{ast} IS NOT NULL"),
1461-
Expr::NotNull { expr, one_word } => write!(
1461+
Expr::NotNull { expr, with_space } => write!(
14621462
f,
14631463
"{} {}",
14641464
expr,
1465-
if *one_word { "NOTNULL" } else { "NOT NULL" }
1465+
if *with_space { "NOT NULL" } else { "NOTNULL" }
14661466
),
14671467
Expr::IsUnknown(ast) => write!(f, "{ast} IS UNKNOWN"),
14681468
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)),
@@ -1096,14 +1102,13 @@ pub trait Dialect: Debug + Any {
10961102
false
10971103
}
10981104

1099-
/// Returns true if the dialect supports `NOTNULL` in expressions.
1100-
fn supports_notnull(&self) -> bool {
1101-
false
1102-
}
1103-
1104-
/// Returns true if the dialect supports `NOT NULL` in expressions.
1105-
fn supports_not_null(&self) -> bool {
1106-
false
1105+
/// Returns true if the dialect supports the passed in alias.
1106+
/// See [IsNotNullAlias].
1107+
fn supports_is_not_null_alias(&self, alias: IsNotNullAlias) -> bool {
1108+
match alias {
1109+
NotNull => false,
1110+
NotSpaceNull => false,
1111+
}
11071112
}
11081113
}
11091114

@@ -1131,6 +1136,13 @@ pub enum Precedence {
11311136
Or,
11321137
}
11331138

1139+
/// Possible aliases for `IS NOT NULL` supported
1140+
/// by some non-standard dialects.
1141+
pub enum IsNotNullAlias {
1142+
NotNull,
1143+
NotSpaceNull,
1144+
}
1145+
11341146
impl dyn Dialect {
11351147
#[inline]
11361148
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};
@@ -15994,7 +15994,8 @@ fn parse_not_null_unsupported() {
1599415994
// Only DuckDB and SQLite support `x NOT NULL` as an expression
1599515995
// All other dialects fail to parse.
1599615996
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOT NULL FROM t"#;
15997-
let dialects = all_dialects_except(|d| d.supports_not_null());
15997+
let dialects =
15998+
all_dialects_except(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotSpaceNull));
1599815999
let res = dialects.parse_sql_statements(sql);
1599916000
assert_eq!(
1600016001
ParserError::ParserError("Expected: end of statement, found: NULL".to_string()),
@@ -16006,7 +16007,8 @@ fn parse_not_null_unsupported() {
1600616007
fn parse_not_null_supported() {
1600716008
// DuckDB and SQLite support `x NOT NULL` as an expression
1600816009
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOT NULL FROM t"#;
16009-
let dialects = all_dialects_where(|d| d.supports_not_null());
16010+
let dialects =
16011+
all_dialects_where(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotSpaceNull));
1601016012
let stmt = dialects.one_statement_parses_to(sql, sql);
1601116013
match stmt {
1601216014
Statement::Query(qry) => match *qry.body {
@@ -16026,7 +16028,7 @@ fn parse_not_null_supported() {
1602616028
quote_style: None,
1602716029
span: fake_span,
1602816030
})),
16029-
one_word: false,
16031+
with_space: true,
1603016032
},
1603116033
);
1603216034
}
@@ -16046,7 +16048,7 @@ fn parse_notnull_unsupported() {
1604616048
// consider `NOTNULL` an alias for x.
1604716049
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOTNULL FROM t"#;
1604816050
let canonical = r#"WITH t AS (SELECT NULL AS x) SELECT x AS NOTNULL FROM t"#;
16049-
let dialects = all_dialects_except(|d| d.supports_notnull());
16051+
let dialects = all_dialects_except(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotNull));
1605016052
let stmt = dialects.one_statement_parses_to(sql, canonical);
1605116053
match stmt {
1605216054
Statement::Query(qry) => match *qry.body {
@@ -16088,7 +16090,7 @@ fn parse_notnull_unsupported() {
1608816090
fn parse_notnull_supported() {
1608916091
// DuckDB and SQLite support `x NOT NULL` as an expression
1609016092
let sql = r#"WITH t AS (SELECT NULL AS x) SELECT x NOTNULL FROM t"#;
16091-
let dialects = all_dialects_where(|d| d.supports_notnull());
16093+
let dialects = all_dialects_where(|d| d.supports_is_not_null_alias(IsNotNullAlias::NotNull));
1609216094
let stmt = dialects.one_statement_parses_to(sql, "");
1609316095
match stmt {
1609416096
Statement::Query(qry) => match *qry.body {
@@ -16108,7 +16110,7 @@ fn parse_notnull_supported() {
1610816110
quote_style: None,
1610916111
span: fake_span,
1611016112
})),
16111-
one_word: true,
16113+
with_space: false,
1611216114
},
1611316115
);
1611416116
}

0 commit comments

Comments
 (0)