diff --git a/e2e_test/batch/basic/boolean.slt.part b/e2e_test/batch/basic/boolean.slt.part
index 2892d6b0bef7b..21c7035a7f290 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 257291c7c9517..0f9affffd7cc0 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<ExprImpl> {
+        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<ExprImpl> {
         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 9d4b383ced07f..122fbbe81b970 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 02d66c16b3f8f..11a10e784bdd2 100644
--- a/src/sqlparser/src/ast/mod.rs
+++ b/src/sqlparser/src/ast/mod.rs
@@ -278,6 +278,10 @@ pub enum Expr {
     IsFalse(Box<Expr>),
     /// `IS NOT FALSE` operator
     IsNotFalse(Box<Expr>),
+    /// `IS UNKNOWN` operator
+    IsUnknown(Box<Expr>),
+    /// `IS NOT UNKNOWN` operator
+    IsNotUnknown(Box<Expr>),
     /// `IS DISTINCT FROM` operator
     IsDistinctFrom(Box<Expr>, Box<Expr>),
     /// `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 d669aa631f93f..3812cd8fe0284 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 032a524a76df9..4728fe2dfdf4a 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 46e9308e4b8c8..4dd47aaf9d8a7 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;