Skip to content

Commit 018df25

Browse files
authored
refactor: use fluent api in the expressions factory and throw ExpressionError (#283)
1 parent dc23f76 commit 018df25

File tree

12 files changed

+268
-79
lines changed

12 files changed

+268
-79
lines changed

src/iceberg/exception.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ class ICEBERG_EXPORT IcebergError : public std::runtime_error {
3838
explicit IcebergError(const std::string& what) : std::runtime_error(what) {}
3939
};
4040

41+
/// \brief Exception thrown when expression construction fails.
42+
class ICEBERG_EXPORT ExpressionError : public IcebergError {
43+
public:
44+
explicit ExpressionError(const std::string& what) : IcebergError(what) {}
45+
};
46+
4147
#define ICEBERG_CHECK(condition, ...) \
4248
do { \
4349
if (!(condition)) [[unlikely]] { \

src/iceberg/expression/expression.cc

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,18 @@ const std::shared_ptr<False>& False::Instance() {
4545
Result<std::shared_ptr<Expression>> False::Negate() const { return True::Instance(); }
4646

4747
// And implementation
48+
Result<std::unique_ptr<And>> And::Make(std::shared_ptr<Expression> left,
49+
std::shared_ptr<Expression> right) {
50+
if (!left || !right) [[unlikely]] {
51+
return InvalidExpression("And expression cannot have null children");
52+
}
53+
return std::unique_ptr<And>(new And(std::move(left), std::move(right)));
54+
}
55+
4856
And::And(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right)
49-
: left_(std::move(left)), right_(std::move(right)) {}
57+
: left_(std::move(left)), right_(std::move(right)) {
58+
ICEBERG_DCHECK(left_ && right_, "And cannot have null children");
59+
}
5060

5161
std::string And::ToString() const {
5262
return std::format("({} and {})", left_->ToString(), right_->ToString());
@@ -56,7 +66,7 @@ Result<std::shared_ptr<Expression>> And::Negate() const {
5666
// De Morgan's law: not(A and B) = (not A) or (not B)
5767
ICEBERG_ASSIGN_OR_RAISE(auto left_negated, left_->Negate());
5868
ICEBERG_ASSIGN_OR_RAISE(auto right_negated, right_->Negate());
59-
return std::make_shared<Or>(std::move(left_negated), std::move(right_negated));
69+
return Or::Make(std::move(left_negated), std::move(right_negated));
6070
}
6171

6272
bool And::Equals(const Expression& expr) const {
@@ -69,8 +79,18 @@ bool And::Equals(const Expression& expr) const {
6979
}
7080

7181
// Or implementation
82+
Result<std::unique_ptr<Or>> Or::Make(std::shared_ptr<Expression> left,
83+
std::shared_ptr<Expression> right) {
84+
if (!left || !right) [[unlikely]] {
85+
return InvalidExpression("Or cannot have null children");
86+
}
87+
return std::unique_ptr<Or>(new Or(std::move(left), std::move(right)));
88+
}
89+
7290
Or::Or(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right)
73-
: left_(std::move(left)), right_(std::move(right)) {}
91+
: left_(std::move(left)), right_(std::move(right)) {
92+
ICEBERG_DCHECK(left_ && right_, "Or cannot have null children");
93+
}
7494

7595
std::string Or::ToString() const {
7696
return std::format("({} or {})", left_->ToString(), right_->ToString());
@@ -80,7 +100,7 @@ Result<std::shared_ptr<Expression>> Or::Negate() const {
80100
// De Morgan's law: not(A or B) = (not A) and (not B)
81101
ICEBERG_ASSIGN_OR_RAISE(auto left_negated, left_->Negate());
82102
ICEBERG_ASSIGN_OR_RAISE(auto right_negated, right_->Negate());
83-
return std::make_shared<And>(std::move(left_negated), std::move(right_negated));
103+
return And::Make(std::move(left_negated), std::move(right_negated));
84104
}
85105

86106
bool Or::Equals(const Expression& expr) const {
@@ -93,7 +113,16 @@ bool Or::Equals(const Expression& expr) const {
93113
}
94114

95115
// Not implementation
96-
Not::Not(std::shared_ptr<Expression> child) : child_(std::move(child)) {}
116+
Result<std::unique_ptr<Not>> Not::Make(std::shared_ptr<Expression> child) {
117+
if (!child) [[unlikely]] {
118+
return InvalidExpression("Not expression cannot have null child");
119+
}
120+
return std::unique_ptr<Not>(new Not(std::move(child)));
121+
}
122+
123+
Not::Not(std::shared_ptr<Expression> child) : child_(std::move(child)) {
124+
ICEBERG_DCHECK(child_ != nullptr, "Not expression cannot have null child");
125+
}
97126

98127
std::string Not::ToString() const { return std::format("not({})", child_->ToString()); }
99128

@@ -199,7 +228,7 @@ Result<Expression::Operation> Negate(Expression::Operation op) {
199228
case Expression::Operation::kMax:
200229
case Expression::Operation::kMin:
201230
case Expression::Operation::kCount:
202-
return InvalidArgument("No negation for operation: {}", op);
231+
return InvalidExpression("No negation for operation: {}", op);
203232
}
204233
std::unreachable();
205234
}

src/iceberg/expression/expression.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,12 @@ class ICEBERG_EXPORT False : public Expression {
130130
/// evaluate to true.
131131
class ICEBERG_EXPORT And : public Expression {
132132
public:
133-
/// \brief Constructs an And expression from two sub-expressions.
133+
/// \brief Creates an And expression from two sub-expressions.
134134
///
135135
/// \param left The left operand of the AND expression
136136
/// \param right The right operand of the AND expression
137-
And(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right);
137+
static Result<std::unique_ptr<And>> Make(std::shared_ptr<Expression> left,
138+
std::shared_ptr<Expression> right);
138139

139140
/// \brief Returns the left operand of the AND expression.
140141
///
@@ -155,6 +156,8 @@ class ICEBERG_EXPORT And : public Expression {
155156
bool Equals(const Expression& other) const override;
156157

157158
private:
159+
And(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right);
160+
158161
std::shared_ptr<Expression> left_;
159162
std::shared_ptr<Expression> right_;
160163
};
@@ -165,11 +168,12 @@ class ICEBERG_EXPORT And : public Expression {
165168
/// evaluates to true.
166169
class ICEBERG_EXPORT Or : public Expression {
167170
public:
168-
/// \brief Constructs an Or expression from two sub-expressions.
171+
/// \brief Creates an Or expression from two sub-expressions.
169172
///
170173
/// \param left The left operand of the OR expression
171174
/// \param right The right operand of the OR expression
172-
Or(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right);
175+
static Result<std::unique_ptr<Or>> Make(std::shared_ptr<Expression> left,
176+
std::shared_ptr<Expression> right);
173177

174178
/// \brief Returns the left operand of the OR expression.
175179
///
@@ -190,6 +194,8 @@ class ICEBERG_EXPORT Or : public Expression {
190194
bool Equals(const Expression& other) const override;
191195

192196
private:
197+
Or(std::shared_ptr<Expression> left, std::shared_ptr<Expression> right);
198+
193199
std::shared_ptr<Expression> left_;
194200
std::shared_ptr<Expression> right_;
195201
};
@@ -199,10 +205,11 @@ class ICEBERG_EXPORT Or : public Expression {
199205
/// This expression negates its child expression.
200206
class ICEBERG_EXPORT Not : public Expression {
201207
public:
202-
/// \brief Constructs a Not expression from a child expression.
208+
/// \brief Creates a Not expression from a child expression.
203209
///
204210
/// \param child The expression to negate
205-
explicit Not(std::shared_ptr<Expression> child);
211+
/// \return A Result containing a unique pointer to Not, or an error if child is nullptr
212+
static Result<std::unique_ptr<Not>> Make(std::shared_ptr<Expression> child);
206213

207214
/// \brief Returns the child expression.
208215
///
@@ -218,6 +225,8 @@ class ICEBERG_EXPORT Not : public Expression {
218225
bool Equals(const Expression& other) const override;
219226

220227
private:
228+
explicit Not(std::shared_ptr<Expression> child);
229+
221230
std::shared_ptr<Expression> child_;
222231
};
223232

src/iceberg/expression/expressions.cc

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "iceberg/exception.h"
2323
#include "iceberg/transform.h"
2424
#include "iceberg/type.h"
25+
#include "iceberg/util/macros.h"
2526

2627
namespace iceberg {
2728

@@ -41,41 +42,57 @@ std::shared_ptr<Expression> Expressions::Not(std::shared_ptr<Expression> child)
4142
return not_expr.child();
4243
}
4344

44-
return std::make_shared<::iceberg::Not>(std::move(child));
45+
ICEBERG_ASSIGN_OR_THROW(auto not_expr, iceberg::Not::Make(std::move(child)));
46+
return not_expr;
4547
}
4648

4749
// Transform functions
4850

4951
std::shared_ptr<UnboundTransform> Expressions::Bucket(std::string name,
5052
int32_t num_buckets) {
51-
return std::make_shared<UnboundTransform>(Ref(std::move(name)),
52-
Transform::Bucket(num_buckets));
53+
ICEBERG_ASSIGN_OR_THROW(
54+
auto transform,
55+
UnboundTransform::Make(Ref(std::move(name)), Transform::Bucket(num_buckets)));
56+
return transform;
5357
}
5458

5559
std::shared_ptr<UnboundTransform> Expressions::Year(std::string name) {
56-
return std::make_shared<UnboundTransform>(Ref(std::move(name)), Transform::Year());
60+
ICEBERG_ASSIGN_OR_THROW(
61+
auto transform, UnboundTransform::Make(Ref(std::move(name)), Transform::Year()));
62+
return transform;
5763
}
5864

5965
std::shared_ptr<UnboundTransform> Expressions::Month(std::string name) {
60-
return std::make_shared<UnboundTransform>(Ref(std::move(name)), Transform::Month());
66+
ICEBERG_ASSIGN_OR_THROW(
67+
auto transform, UnboundTransform::Make(Ref(std::move(name)), Transform::Month()));
68+
return transform;
6169
}
6270

6371
std::shared_ptr<UnboundTransform> Expressions::Day(std::string name) {
64-
return std::make_shared<UnboundTransform>(Ref(std::move(name)), Transform::Day());
72+
ICEBERG_ASSIGN_OR_THROW(auto transform,
73+
UnboundTransform::Make(Ref(std::move(name)), Transform::Day()));
74+
return transform;
6575
}
6676

6777
std::shared_ptr<UnboundTransform> Expressions::Hour(std::string name) {
68-
return std::make_shared<UnboundTransform>(Ref(std::move(name)), Transform::Hour());
78+
ICEBERG_ASSIGN_OR_THROW(
79+
auto transform, UnboundTransform::Make(Ref(std::move(name)), Transform::Hour()));
80+
return transform;
6981
}
7082

7183
std::shared_ptr<UnboundTransform> Expressions::Truncate(std::string name, int32_t width) {
72-
return std::make_shared<UnboundTransform>(Ref(std::move(name)),
73-
Transform::Truncate(width));
84+
ICEBERG_ASSIGN_OR_THROW(
85+
auto transform,
86+
UnboundTransform::Make(Ref(std::move(name)), Transform::Truncate(width)));
87+
return transform;
7488
}
7589

7690
std::shared_ptr<UnboundTransform> Expressions::Transform(
7791
std::string name, std::shared_ptr<::iceberg::Transform> transform) {
78-
return std::make_shared<UnboundTransform>(Ref(std::move(name)), std::move(transform));
92+
ICEBERG_ASSIGN_OR_THROW(
93+
auto unbound_transform,
94+
UnboundTransform::Make(Ref(std::move(name)), std::move(transform)));
95+
return unbound_transform;
7996
}
8097

8198
// Template implementations for unary predicates
@@ -327,11 +344,12 @@ std::shared_ptr<False> Expressions::AlwaysFalse() { return False::Instance(); }
327344
// Utilities
328345

329346
std::shared_ptr<NamedReference> Expressions::Ref(std::string name) {
330-
return std::make_shared<NamedReference>(std::move(name));
347+
ICEBERG_ASSIGN_OR_THROW(auto ref, NamedReference::Make(std::move(name)));
348+
return ref;
331349
}
332350

333351
Literal Expressions::Lit(Literal::Value value, std::shared_ptr<PrimitiveType> type) {
334-
throw IcebergError("Literal creation is not implemented");
352+
throw ExpressionError("Literal creation is not implemented");
335353
}
336354

337355
} // namespace iceberg

src/iceberg/expression/expressions.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,18 @@
2727
#include <string>
2828
#include <vector>
2929

30+
#include "iceberg/exception.h"
3031
#include "iceberg/expression/literal.h"
3132
#include "iceberg/expression/predicate.h"
3233
#include "iceberg/expression/term.h"
3334
#include "iceberg/iceberg_export.h"
35+
#include "iceberg/util/macros.h"
3436

3537
namespace iceberg {
3638

37-
/// \brief Factory methods for creating expressions.
39+
/// \brief Fluent APIs to create expressions.
40+
///
41+
/// \throw `ExpressionError` for invalid expression.
3842
class ICEBERG_EXPORT Expressions {
3943
public:
4044
// Logical operations
@@ -60,7 +64,9 @@ class ICEBERG_EXPORT Expressions {
6064
return left;
6165
}
6266

63-
return std::make_shared<::iceberg::And>(std::move(left), std::move(right));
67+
ICEBERG_ASSIGN_OR_THROW(auto and_expr,
68+
iceberg::And::Make(std::move(left), std::move(right)));
69+
return and_expr;
6470
} else {
6571
return And(And(std::move(left), std::move(right)), std::forward<Args>(args)...);
6672
}
@@ -86,7 +92,9 @@ class ICEBERG_EXPORT Expressions {
8692
return left;
8793
}
8894

89-
return std::make_shared<::iceberg::Or>(std::move(left), std::move(right));
95+
ICEBERG_ASSIGN_OR_THROW(auto or_expr,
96+
iceberg::Or::Make(std::move(left), std::move(right)));
97+
return or_expr;
9098
} else {
9199
return Or(Or(std::move(left), std::move(right)), std::forward<Args>(args)...);
92100
}

0 commit comments

Comments
 (0)