Skip to content

Commit 32fde8e

Browse files
authored
feat: Add expression interface (#58)
1 parent 01b454c commit 32fde8e

File tree

6 files changed

+304
-0
lines changed

6 files changed

+304
-0
lines changed

src/iceberg/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ set(ICEBERG_SOURCES
3434
transform.cc
3535
transform_function.cc
3636
type.cc
37+
snapshot.cc
38+
expression.cc
3739
util/murmurhash3_internal.cc)
3840

3941
set(ICEBERG_STATIC_BUILD_INTERFACE_LIBS)

src/iceberg/expression.cc

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "expression.h"
21+
22+
#include <format>
23+
24+
namespace iceberg {
25+
26+
// True implementation
27+
const std::shared_ptr<True>& True::Instance() {
28+
static const std::shared_ptr<True> instance{new True()};
29+
return instance;
30+
}
31+
32+
Result<std::shared_ptr<Expression>> True::Negate() const { return False::Instance(); }
33+
34+
// False implementation
35+
const std::shared_ptr<False>& False::Instance() {
36+
static const std::shared_ptr<False> instance = std::shared_ptr<False>(new False());
37+
return instance;
38+
}
39+
40+
Result<std::shared_ptr<Expression>> False::Negate() const { return True::Instance(); }
41+
42+
// And implementation
43+
And::And(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right)
44+
: left_(std::move(left)), right_(std::move(right)) {}
45+
46+
std::string And::ToString() const {
47+
return std::format("({} and {})", left_->ToString(), right_->ToString());
48+
}
49+
50+
Result<std::shared_ptr<Expression>> And::Negate() const {
51+
// TODO(yingcai-cy): Implement Or expression
52+
return unexpected(
53+
Error(ErrorKind::kInvalidExpression, "And negation not yet implemented"));
54+
}
55+
56+
bool And::Equals(const Expression& expr) const {
57+
if (expr.op() == Operation::kAnd) {
58+
const auto& other = static_cast<const And&>(expr);
59+
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
60+
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
61+
}
62+
return false;
63+
}
64+
65+
} // namespace iceberg

src/iceberg/expression.h

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
/// \file iceberg/expression.h
23+
/// Expression interface for Iceberg table operations.
24+
25+
#include <memory>
26+
#include <string>
27+
28+
#include "iceberg/expected.h"
29+
#include "iceberg/iceberg_export.h"
30+
#include "iceberg/result.h"
31+
32+
namespace iceberg {
33+
34+
/// \brief Represents a boolean expression tree.
35+
class ICEBERG_EXPORT Expression {
36+
public:
37+
/// Operation types for expressions
38+
enum class Operation {
39+
kTrue,
40+
kFalse,
41+
kIsNull,
42+
kNotNull,
43+
kIsNan,
44+
kNotNan,
45+
kLt,
46+
kLtEq,
47+
kGt,
48+
kGtEq,
49+
kEq,
50+
kNotEq,
51+
kIn,
52+
kNotIn,
53+
kNot,
54+
kAnd,
55+
kOr,
56+
kStartsWith,
57+
kNotStartsWith,
58+
kCount,
59+
kCountStar,
60+
kMax,
61+
kMin
62+
};
63+
64+
virtual ~Expression() = default;
65+
66+
/// \brief Returns the operation for an expression node.
67+
virtual Operation op() const = 0;
68+
69+
/// \brief Returns the negation of this expression, equivalent to not(this).
70+
virtual Result<std::shared_ptr<Expression>> Negate() const {
71+
return unexpected(
72+
Error(ErrorKind::kInvalidExpression, "Expression cannot be negated"));
73+
}
74+
75+
/// \brief Returns whether this expression will accept the same values as another.
76+
/// \param other another expression
77+
/// \return true if the expressions are equivalent
78+
virtual bool Equals(const Expression& other) const {
79+
// only bound predicates can be equivalent
80+
return false;
81+
}
82+
83+
virtual std::string ToString() const { return "Expression"; }
84+
};
85+
86+
/// \brief An Expression that is always true.
87+
///
88+
/// Represents a boolean predicate that always evaluates to true.
89+
class ICEBERG_EXPORT True : public Expression {
90+
public:
91+
/// \brief Returns the singleton instance
92+
static const std::shared_ptr<True>& Instance();
93+
94+
Operation op() const override { return Operation::kTrue; }
95+
96+
std::string ToString() const override { return "true"; }
97+
98+
Result<std::shared_ptr<Expression>> Negate() const override;
99+
100+
bool Equals(const Expression& other) const override {
101+
return other.op() == Operation::kTrue;
102+
}
103+
104+
private:
105+
constexpr True() = default;
106+
};
107+
108+
/// \brief An expression that is always false.
109+
class ICEBERG_EXPORT False : public Expression {
110+
public:
111+
/// \brief Returns the singleton instance
112+
static const std::shared_ptr<False>& Instance();
113+
114+
Operation op() const override { return Operation::kFalse; }
115+
116+
std::string ToString() const override { return "false"; }
117+
118+
Result<std::shared_ptr<Expression>> Negate() const override;
119+
120+
bool Equals(const Expression& other) const override {
121+
return other.op() == Operation::kFalse;
122+
}
123+
124+
private:
125+
constexpr False() = default;
126+
};
127+
128+
/// \brief An Expression that represents a logical AND operation between two expressions.
129+
///
130+
/// This expression evaluates to true if and only if both of its child expressions
131+
/// evaluate to true.
132+
class ICEBERG_EXPORT And : public Expression {
133+
public:
134+
/// \brief Constructs an And expression from two sub-expressions.
135+
///
136+
/// \param left The left operand of the AND expression
137+
/// \param right The right operand of the AND expression
138+
And(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right);
139+
140+
/// \brief Returns the left operand of the AND expression.
141+
///
142+
/// \return The left operand of the AND expression
143+
const std::shared_ptr<Expression>& left() const { return left_; }
144+
145+
/// \brief Returns the right operand of the AND expression.
146+
///
147+
/// \return The right operand of the AND expression
148+
const std::shared_ptr<Expression>& right() const { return right_; }
149+
150+
Operation op() const override { return Operation::kAnd; }
151+
152+
std::string ToString() const override;
153+
154+
Result<std::shared_ptr<Expression>> Negate() const override;
155+
156+
bool Equals(const Expression& other) const override;
157+
158+
private:
159+
std::shared_ptr<Expression> left_;
160+
std::shared_ptr<Expression> right_;
161+
};
162+
163+
} // namespace iceberg

src/iceberg/result.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum class ErrorKind {
3939
kNotImplemented,
4040
kUnknownError,
4141
kNotSupported,
42+
kInvalidExpression,
4243
kJsonParseError,
4344
};
4445

test/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ target_sources(expected_test PRIVATE expected_test.cc)
4444
target_link_libraries(expected_test PRIVATE iceberg_static GTest::gtest_main GTest::gmock)
4545
add_test(NAME expected_test COMMAND expected_test)
4646

47+
add_executable(expression_test)
48+
target_sources(expression_test PRIVATE expression_test.cc)
49+
target_link_libraries(expression_test PRIVATE iceberg_static GTest::gtest_main
50+
GTest::gmock)
51+
add_test(NAME expression_test COMMAND expression_test)
52+
4753
if(ICEBERG_BUILD_BUNDLE)
4854
add_executable(avro_test)
4955
target_sources(avro_test PRIVATE avro_test.cc)

test/expression_test.cc

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/expression.h"
21+
22+
#include <memory>
23+
24+
#include <gtest/gtest.h>
25+
26+
namespace iceberg {
27+
28+
TEST(TrueFalseTest, Basic) {
29+
// Test negation of False returns True
30+
auto false_instance = False::Instance();
31+
auto negated = false_instance->Negate();
32+
33+
EXPECT_TRUE(negated.has_value());
34+
35+
// Check that negated expression is True
36+
auto true_expr = negated.value();
37+
EXPECT_EQ(true_expr->op(), Expression::Operation::kTrue);
38+
39+
EXPECT_EQ(true_expr->ToString(), "true");
40+
41+
// Test negation of True returns false
42+
auto true_instance = True::Instance();
43+
negated = true_instance->Negate();
44+
45+
EXPECT_TRUE(negated.has_value());
46+
47+
// Check that negated expression is False
48+
auto false_expr = negated.value();
49+
EXPECT_EQ(false_expr->op(), Expression::Operation::kFalse);
50+
51+
EXPECT_EQ(false_expr->ToString(), "false");
52+
}
53+
54+
TEST(ANDTest, Basic) {
55+
// Create two True expressions
56+
auto true_expr1 = True::Instance();
57+
auto true_expr2 = True::Instance();
58+
59+
// Create an AND expression
60+
auto and_expr = std::make_shared<And>(true_expr1, true_expr2);
61+
62+
EXPECT_EQ(and_expr->op(), Expression::Operation::kAnd);
63+
EXPECT_EQ(and_expr->ToString(), "(true and true)");
64+
EXPECT_EQ(and_expr->left()->op(), Expression::Operation::kTrue);
65+
EXPECT_EQ(and_expr->right()->op(), Expression::Operation::kTrue);
66+
}
67+
} // namespace iceberg

0 commit comments

Comments
 (0)