From e90d7042a7ec14bc42da3826b8967a070bb46ff3 Mon Sep 17 00:00:00 2001 From: xiangjinwu <17769960+xiangjinwu@users.noreply.github.com> Date: Wed, 24 May 2023 15:14:16 +0800 Subject: [PATCH] feat(sqlparser): support `IS [NOT] UNKNOWN` (#9965) --- e2e_test/batch/basic/boolean.slt.part | 15 +++++++++++++++ src/frontend/src/binder/expr/mod.rs | 9 +++++++++ src/meta/src/manager/catalog/utils.rs | 2 ++ src/sqlparser/src/ast/mod.rs | 6 ++++++ src/sqlparser/src/parser.rs | 4 ++++ src/tests/regress/data/expected/boolean.out | 4 ++-- src/tests/regress/data/sql/boolean.sql | 4 ++-- 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/e2e_test/batch/basic/boolean.slt.part b/e2e_test/batch/basic/boolean.slt.part index 2892d6b0bef7..21c7035a7f29 100644 --- a/e2e_test/batch/basic/boolean.slt.part +++ b/e2e_test/batch/basic/boolean.slt.part @@ -127,3 +127,18 @@ select booleq(true,true), boolne('true'::bool,true); ---- t f f t f t t f t t f + +query TTTT +select + true is unknown, + null is unknown, + 't' is not unknown, + 'on' is not unknown; +---- +f t t t + +statement error +select 1 is unknown; + +statement error +select 'a' is not unknown; diff --git a/src/frontend/src/binder/expr/mod.rs b/src/frontend/src/binder/expr/mod.rs index 257291c7c951..0f9affffd7cc 100644 --- a/src/frontend/src/binder/expr/mod.rs +++ b/src/frontend/src/binder/expr/mod.rs @@ -129,6 +129,8 @@ impl Binder { Expr::IsNotTrue(expr) => self.bind_is_operator(ExprType::IsNotTrue, *expr), Expr::IsFalse(expr) => self.bind_is_operator(ExprType::IsFalse, *expr), Expr::IsNotFalse(expr) => self.bind_is_operator(ExprType::IsNotFalse, *expr), + Expr::IsUnknown(expr) => self.bind_is_unknown(ExprType::IsNull, *expr), + Expr::IsNotUnknown(expr) => self.bind_is_unknown(ExprType::IsNotNull, *expr), Expr::IsDistinctFrom(left, right) => self.bind_distinct_from(*left, *right), Expr::IsNotDistinctFrom(left, right) => self.bind_not_distinct_from(*left, *right), Expr::Case { @@ -441,6 +443,13 @@ impl Binder { Ok(FunctionCall::new(func_type, vec![expr])?.into()) } + pub(super) fn bind_is_unknown(&mut self, func_type: ExprType, expr: Expr) -> Result { + let expr = self + .bind_expr_inner(expr)? + .cast_implicit(DataType::Boolean)?; + Ok(FunctionCall::new(func_type, vec![expr])?.into()) + } + pub(super) fn bind_distinct_from(&mut self, left: Expr, right: Expr) -> Result { let left = self.bind_expr_inner(left)?; let right = self.bind_expr_inner(right)?; diff --git a/src/meta/src/manager/catalog/utils.rs b/src/meta/src/manager/catalog/utils.rs index 9d4b383ced07..122fbbe81b97 100644 --- a/src/meta/src/manager/catalog/utils.rs +++ b/src/meta/src/manager/catalog/utils.rs @@ -266,6 +266,8 @@ impl QueryRewriter<'_> { | Expr::IsNotTrue(expr) | Expr::IsFalse(expr) | Expr::IsNotFalse(expr) + | Expr::IsUnknown(expr) + | Expr::IsNotUnknown(expr) | Expr::InList { expr, .. } | Expr::SomeOp(expr) | Expr::AllOp(expr) diff --git a/src/sqlparser/src/ast/mod.rs b/src/sqlparser/src/ast/mod.rs index 02d66c16b3f8..11a10e784bdd 100644 --- a/src/sqlparser/src/ast/mod.rs +++ b/src/sqlparser/src/ast/mod.rs @@ -278,6 +278,10 @@ pub enum Expr { IsFalse(Box), /// `IS NOT FALSE` operator IsNotFalse(Box), + /// `IS UNKNOWN` operator + IsUnknown(Box), + /// `IS NOT UNKNOWN` operator + IsNotUnknown(Box), /// `IS DISTINCT FROM` operator IsDistinctFrom(Box, Box), /// `IS NOT DISTINCT FROM` operator @@ -427,6 +431,8 @@ impl fmt::Display for Expr { Expr::IsNotTrue(ast) => write!(f, "{} IS NOT TRUE", ast), Expr::IsFalse(ast) => write!(f, "{} IS FALSE", ast), Expr::IsNotFalse(ast) => write!(f, "{} IS NOT FALSE", ast), + Expr::IsUnknown(ast) => write!(f, "{} IS UNKNOWN", ast), + Expr::IsNotUnknown(ast) => write!(f, "{} IS NOT UNKNOWN", ast), Expr::InList { expr, list, diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index d669aa631f93..3812cd8fe028 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -1314,6 +1314,10 @@ impl Parser { Ok(Expr::IsFalse(Box::new(expr))) } else if self.parse_keywords(&[Keyword::NOT, Keyword::FALSE]) { Ok(Expr::IsNotFalse(Box::new(expr))) + } else if self.parse_keyword(Keyword::UNKNOWN) { + Ok(Expr::IsUnknown(Box::new(expr))) + } else if self.parse_keywords(&[Keyword::NOT, Keyword::UNKNOWN]) { + Ok(Expr::IsNotUnknown(Box::new(expr))) } else if self.parse_keyword(Keyword::NULL) { Ok(Expr::IsNull(Box::new(expr))) } else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { diff --git a/src/tests/regress/data/expected/boolean.out b/src/tests/regress/data/expected/boolean.out index 032a524a76df..4728fe2dfdf4 100644 --- a/src/tests/regress/data/expected/boolean.out +++ b/src/tests/regress/data/expected/boolean.out @@ -455,8 +455,8 @@ SELECT b IS NOT TRUE AS isnottrue, b IS FALSE AS isfalse, b IS NOT FALSE AS isnotfalse, - b IS NULL AS isunknown, - b IS NOT NULL AS isnotunknown + b IS UNKNOWN AS isunknown, + b IS NOT UNKNOWN AS isnotunknown FROM booltbl3 ORDER BY o; d | istrue | isnottrue | isfalse | isnotfalse | isunknown | isnotunknown -------+--------+-----------+---------+------------+-----------+-------------- diff --git a/src/tests/regress/data/sql/boolean.sql b/src/tests/regress/data/sql/boolean.sql index 46e9308e4b8c..4dd47aaf9d8a 100644 --- a/src/tests/regress/data/sql/boolean.sql +++ b/src/tests/regress/data/sql/boolean.sql @@ -215,8 +215,8 @@ SELECT b IS NOT TRUE AS isnottrue, b IS FALSE AS isfalse, b IS NOT FALSE AS isnotfalse, - b IS NULL AS isunknown, - b IS NOT NULL AS isnotunknown + b IS UNKNOWN AS isunknown, + b IS NOT UNKNOWN AS isnotunknown FROM booltbl3 ORDER BY o;