This is an automated email from the ASF dual-hosted git repository.

gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 5cbf3df  feat: add not expression (#279)
5cbf3df is described below

commit 5cbf3df681c0b4032e55bbe8f1a8c73509041668
Author: Gang Wu <[email protected]>
AuthorDate: Tue Oct 28 22:52:26 2025 +0800

    feat: add not expression (#279)
---
 src/iceberg/expression/expression.cc  | 13 +++++++++++
 src/iceberg/expression/expression.h   | 27 +++++++++++++++++++++++
 src/iceberg/expression/expressions.cc | 19 ++++++++++++++++
 src/iceberg/expression/expressions.h  |  9 ++++++++
 src/iceberg/test/expression_test.cc   | 41 +++++++++++++++++++++++++++++++++++
 5 files changed, 109 insertions(+)

diff --git a/src/iceberg/expression/expression.cc 
b/src/iceberg/expression/expression.cc
index f6f6d0f..070d5fe 100644
--- a/src/iceberg/expression/expression.cc
+++ b/src/iceberg/expression/expression.cc
@@ -22,6 +22,7 @@
 #include <format>
 #include <utility>
 
+#include "iceberg/util/checked_cast.h"
 #include "iceberg/util/formatter_internal.h"
 #include "iceberg/util/macros.h"
 
@@ -91,6 +92,18 @@ bool Or::Equals(const Expression& expr) const {
   return false;
 }
 
+// Not implementation
+Not::Not(std::shared_ptr<Expression> child) : child_(std::move(child)) {}
+
+std::string Not::ToString() const { return std::format("not({})", 
child_->ToString()); }
+
+Result<std::shared_ptr<Expression>> Not::Negate() const { return child_; }
+
+bool Not::Equals(const Expression& other) const {
+  return other.op() == Operation::kNot &&
+         internal::checked_cast<const Not&>(other).child_->Equals(*child_);
+}
+
 std::string_view ToString(Expression::Operation op) {
   switch (op) {
     case Expression::Operation::kAnd:
diff --git a/src/iceberg/expression/expression.h 
b/src/iceberg/expression/expression.h
index e0708c4..9a80522 100644
--- a/src/iceberg/expression/expression.h
+++ b/src/iceberg/expression/expression.h
@@ -194,6 +194,33 @@ class ICEBERG_EXPORT Or : public Expression {
   std::shared_ptr<Expression> right_;
 };
 
+/// \brief An Expression that represents logical NOT operation.
+///
+/// This expression negates its child expression.
+class ICEBERG_EXPORT Not : public Expression {
+ public:
+  /// \brief Constructs a Not expression from a child expression.
+  ///
+  /// \param child The expression to negate
+  explicit Not(std::shared_ptr<Expression> child);
+
+  /// \brief Returns the child expression.
+  ///
+  /// \return The child expression being negated
+  const std::shared_ptr<Expression>& child() const { return child_; }
+
+  Operation op() const override { return Operation::kNot; }
+
+  std::string ToString() const override;
+
+  Result<std::shared_ptr<Expression>> Negate() const override;
+
+  bool Equals(const Expression& other) const override;
+
+ private:
+  std::shared_ptr<Expression> child_;
+};
+
 /// \brief Returns a string representation of an expression operation.
 ICEBERG_EXPORT std::string_view ToString(Expression::Operation op);
 
diff --git a/src/iceberg/expression/expressions.cc 
b/src/iceberg/expression/expressions.cc
index a775c99..686a55d 100644
--- a/src/iceberg/expression/expressions.cc
+++ b/src/iceberg/expression/expressions.cc
@@ -25,6 +25,25 @@
 
 namespace iceberg {
 
+// Logical NOT operation
+std::shared_ptr<Expression> Expressions::Not(std::shared_ptr<Expression> 
child) {
+  if (child->op() == Expression::Operation::kTrue) {
+    return AlwaysFalse();
+  }
+
+  if (child->op() == Expression::Operation::kFalse) {
+    return AlwaysTrue();
+  }
+
+  // not(not(x)) = x
+  if (child->op() == Expression::Operation::kNot) {
+    const auto& not_expr = static_cast<const ::iceberg::Not&>(*child);
+    return not_expr.child();
+  }
+
+  return std::make_shared<::iceberg::Not>(std::move(child));
+}
+
 // Transform functions
 
 std::shared_ptr<UnboundTransform> Expressions::Bucket(std::string name,
diff --git a/src/iceberg/expression/expressions.h 
b/src/iceberg/expression/expressions.h
index 7d9f9a1..66083da 100644
--- a/src/iceberg/expression/expressions.h
+++ b/src/iceberg/expression/expressions.h
@@ -92,6 +92,15 @@ class ICEBERG_EXPORT Expressions {
     }
   }
 
+  /// \brief Create a NOT expression.
+  ///
+  /// \param child The expression to negate
+  /// \return A negated expression with optimizations applied:
+  ///   - not(true) returns false
+  ///   - not(false) returns true
+  ///   - not(not(x)) returns x
+  static std::shared_ptr<Expression> Not(std::shared_ptr<Expression> child);
+
   // Transform functions
 
   /// \brief Create a bucket transform term.
diff --git a/src/iceberg/test/expression_test.cc 
b/src/iceberg/test/expression_test.cc
index cce7c53..326dadc 100644
--- a/src/iceberg/test/expression_test.cc
+++ b/src/iceberg/test/expression_test.cc
@@ -165,4 +165,45 @@ TEST(ExpressionTest, BaseClassNegateErrorOut) {
   auto negate_result = mock_expr->Negate();
   EXPECT_THAT(negate_result, IsError(ErrorKind::kNotSupported));
 }
+
+TEST(NotTest, Basic) {
+  auto true_expr = True::Instance();
+  auto not_expr = std::make_shared<Not>(true_expr);
+
+  EXPECT_EQ(not_expr->op(), Expression::Operation::kNot);
+  EXPECT_EQ(not_expr->ToString(), "not(true)");
+  EXPECT_EQ(not_expr->child()->op(), Expression::Operation::kTrue);
+}
+
+TEST(NotTest, Negation) {
+  // Test that not(not(x)) = x
+  auto true_expr = True::Instance();
+  auto not_expr = std::make_shared<Not>(true_expr);
+
+  auto negated_result = not_expr->Negate();
+  ASSERT_THAT(negated_result, IsOk());
+  auto negated = negated_result.value();
+
+  // Should return the original true expression
+  EXPECT_EQ(negated->op(), Expression::Operation::kTrue);
+}
+
+TEST(NotTest, Equals) {
+  auto true_expr = True::Instance();
+  auto false_expr = False::Instance();
+
+  // Test basic equality
+  auto not_expr1 = std::make_shared<Not>(true_expr);
+  auto not_expr2 = std::make_shared<Not>(true_expr);
+  EXPECT_TRUE(not_expr1->Equals(*not_expr2));
+
+  // Test inequality with different child expressions
+  auto not_expr3 = std::make_shared<Not>(false_expr);
+  EXPECT_FALSE(not_expr1->Equals(*not_expr3));
+
+  // Test inequality with different operation types
+  auto and_expr = std::make_shared<And>(true_expr, false_expr);
+  EXPECT_FALSE(not_expr1->Equals(*and_expr));
+}
+
 }  // namespace iceberg

Reply via email to