Skip to content

Commit 0280886

Browse files
committed
bound/unbound and/or/true/false
1 parent d4d8b6b commit 0280886

File tree

2 files changed

+104
-122
lines changed

2 files changed

+104
-122
lines changed

src/iceberg/expression/common.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include <memory>
2323
#include <string>
2424

25+
#include "iceberg/result.h"
26+
#include "iceberg/schema.h"
27+
2528
namespace iceberg {
2629

2730
/// Operation types for expressions
@@ -114,4 +117,25 @@ constexpr bool IsBinaryPredicate(Operation op) {
114117
}
115118
}
116119

120+
template <typename T>
121+
concept Bindable = requires(const T& expr, const Schema& schema, bool case_sensitive) {
122+
// Must have a BoundType alias that defines what type it binds to
123+
typename T::BoundType;
124+
// Must have a Bind method with the correct signature
125+
{ expr.Bind(schema, case_sensitive) } -> std::same_as<Result<typename T::BoundType>>;
126+
};
127+
128+
/// \brief Concept for types that behave like predicates (bound or unbound)
129+
template <typename T>
130+
concept PredicateLike = requires(const T& pred) {
131+
// Must have an operation type
132+
{ pred.op() } -> std::same_as<Operation>;
133+
// Must be convertible to string
134+
{ pred.ToString() } -> std::same_as<std::string>;
135+
// // Must have a Negate method that returns a shared_ptr to the same concept
136+
// { pred.Negate() } -> std::convertible_to<std::shared_ptr<T>>;
137+
// Must support equality comparison
138+
{ pred.Equals(pred) } -> std::same_as<bool>;
139+
};
140+
117141
} // namespace iceberg

src/iceberg/expression/expression.cc

Lines changed: 80 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -29,58 +29,18 @@
2929

3030
namespace iceberg {
3131

32-
template <typename T>
33-
concept Bindable = requires(const T& expr, const Schema& schema, bool case_sensitive) {
34-
// Must have a BoundType alias that defines what type it binds to
35-
typename T::BoundType;
36-
37-
// Must have a Bind method with the correct signature
38-
{ expr.Bind(schema, case_sensitive) } -> std::same_as<Result<typename T::BoundType>>;
39-
};
40-
41-
/// \brief Concept for types that behave like predicates (bound or unbound)
42-
template <typename T>
43-
concept PredicateLike = requires(const T& pred) {
44-
// Must have an operation type
45-
{ pred.op() } -> std::same_as<Operation>;
46-
47-
// Must be convertible to string
48-
{ pred.ToString() } -> std::same_as<std::string>;
49-
50-
// Must have a Negate method that returns a shared_ptr to the same concept
51-
{ pred.Negate() } -> std::convertible_to<std::shared_ptr<T>>;
52-
53-
// Must support equality comparison
54-
{ pred.Equals(pred) } -> std::same_as<bool>;
55-
};
56-
57-
/// \brief Concept specifically for unbound predicates that can be bound
58-
template <typename T>
59-
concept UnboundPredicate = PredicateLike<T> && requires(const T& pred) {
60-
// Must have a BoundType alias
61-
typename T::BoundType;
62-
63-
// Must be bindable to a schema
64-
requires Bindable<T>;
65-
};
66-
67-
/// \brief Concept specifically for bound predicates
68-
template <typename T>
69-
concept BoundPredicateLike = PredicateLike<T> && requires(const T& pred) {
70-
// Must have type information
71-
{ pred.type() } -> std::convertible_to<std::shared_ptr<Type>>;
72-
73-
// Must report that it's bound
74-
{ pred.IsBound() } -> std::convertible_to<bool>;
75-
};
76-
7732
// Internal implementation classes
7833

7934
/// \brief An Expression that is always true.
80-
class True final : public Predicate {
35+
template <PredicateLike PredicateType>
36+
class TrueImpl final : public PredicateType {
8137
public:
8238
/// \brief Returns the singleton instance
83-
static const std::shared_ptr<True>& Instance();
39+
static const std::shared_ptr<TrueImpl>& Instance() {
40+
static const std::shared_ptr<TrueImpl> instance =
41+
std::shared_ptr<TrueImpl>(new TrueImpl());
42+
return instance;
43+
}
8444

8545
Operation op() const override { return Operation::kTrue; }
8646

@@ -92,35 +52,49 @@ class True final : public Predicate {
9252
return other.op() == Operation::kTrue;
9353
}
9454

95-
private:
96-
constexpr True() = default;
55+
protected:
56+
constexpr TrueImpl() = default;
9757
};
9858

9959
/// \brief An expression that is always false.
100-
class False final : public Predicate {
60+
template <PredicateLike PredicateType>
61+
class FalseImpl final : public PredicateType {
10162
public:
10263
/// \brief Returns the singleton instance
103-
static const std::shared_ptr<False>& Instance();
64+
static const std::shared_ptr<PredicateType>& Instance() {
65+
static const std::shared_ptr<PredicateType> instance =
66+
std::shared_ptr<FalseImpl>(new FalseImpl());
67+
return instance;
68+
}
10469

10570
Operation op() const override { return Operation::kFalse; }
10671

10772
std::string ToString() const override { return "false"; }
10873

109-
std::shared_ptr<Predicate> Negate() const override;
74+
std::shared_ptr<Predicate> Negate() const override {
75+
return TrueImpl<PredicateType>::Instance();
76+
}
11077

11178
bool Equals(const Expression& other) const override {
11279
return other.op() == Operation::kFalse;
11380
}
11481

11582
private:
116-
constexpr False() = default;
83+
constexpr FalseImpl() = default;
11784
};
11885

86+
template <PredicateLike PredicateType>
87+
std::shared_ptr<Predicate> TrueImpl<PredicateType>::Negate() const {
88+
return FalseImpl<PredicateType>::Instance();
89+
}
90+
11991
/// \brief An Expression that represents a logical AND operation between two expressions.
120-
class AndImpl final : public Predicate {
92+
template <PredicateLike PredicateType>
93+
class AndImpl final : public PredicateType {
12194
public:
12295
/// \brief Constructs an And expression from two sub-expressions.
123-
AndImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right);
96+
AndImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
97+
: left_(std::move(left)), right_(std::move(right)) {}
12498

12599
/// \brief Returns the left operand of the AND expression.
126100
const std::shared_ptr<Predicate>& left() const { return left_; }
@@ -130,105 +104,89 @@ class AndImpl final : public Predicate {
130104

131105
Operation op() const override { return Operation::kAnd; }
132106

133-
std::string ToString() const override;
107+
std::string ToString() const override {
108+
return std::format("({} and {})", left_->ToString(), right_->ToString());
109+
}
134110

135-
std::shared_ptr<Predicate> Negate() const override;
111+
std::shared_ptr<PredicateType> Negate() const override;
136112

137-
bool Equals(const Expression& other) const override;
113+
bool Equals(const Expression& expr) const override {
114+
if (expr.op() == Operation::kAnd) {
115+
const auto& other =
116+
iceberg::internal::checked_cast<const AndImpl<PredicateType>&>(expr);
117+
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
118+
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
119+
}
120+
return false;
121+
}
138122

139123
private:
140124
std::shared_ptr<Predicate> left_;
141125
std::shared_ptr<Predicate> right_;
142126
};
143127

144128
/// \brief An Expression that represents a logical OR operation between two expressions.
145-
class OrImpl final : public Predicate {
129+
template <PredicateLike PredicateType>
130+
class OrImpl final : public PredicateType {
146131
public:
147132
/// \brief Constructs an Or expression from two sub-expressions.
148-
OrImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right);
133+
OrImpl(std::shared_ptr<PredicateType> left, std::shared_ptr<PredicateType> right)
134+
: left_(std::move(left)), right_(std::move(right)) {}
149135

150136
/// \brief Returns the left operand of the OR expression.
151-
const std::shared_ptr<Predicate>& left() const { return left_; }
137+
const std::shared_ptr<PredicateType>& left() const { return left_; }
152138

153139
/// \brief Returns the right operand of the OR expression.
154-
const std::shared_ptr<Predicate>& right() const { return right_; }
140+
const std::shared_ptr<PredicateType>& right() const { return right_; }
155141

156142
Operation op() const override { return Operation::kOr; }
157143

158-
std::string ToString() const override;
144+
std::string ToString() const override {
145+
return std::format("({} or {})", left_->ToString(), right_->ToString());
146+
}
159147

160-
std::shared_ptr<Predicate> Negate() const override;
148+
std::shared_ptr<PredicateType> Negate() const override;
161149

162-
bool Equals(const Expression& other) const override;
150+
bool Equals(const Expression& expr) const override {
151+
if (expr.op() == Operation::kOr) {
152+
const auto& other =
153+
iceberg::internal::checked_cast<const OrImpl<PredicateType>&>(expr);
154+
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
155+
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
156+
}
157+
return false;
158+
}
163159

164160
private:
165-
std::shared_ptr<Predicate> left_;
166-
std::shared_ptr<Predicate> right_;
161+
std::shared_ptr<PredicateType> left_;
162+
std::shared_ptr<PredicateType> right_;
167163
};
168164

169-
// Implementation of True
170-
const std::shared_ptr<True>& True::Instance() {
171-
static const std::shared_ptr<True> instance{new True()};
172-
return instance;
173-
}
174-
175-
std::shared_ptr<Predicate> True::Negate() const { return False::Instance(); }
176-
177-
// Implementation of False
178-
const std::shared_ptr<False>& False::Instance() {
179-
static const std::shared_ptr<False> instance = std::shared_ptr<False>(new False());
180-
return instance;
181-
}
182-
183-
std::shared_ptr<Predicate> False::Negate() const { return True::Instance(); }
184-
185-
// Implementation of AndImpl
186-
AndImpl::AndImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
187-
: left_(std::move(left)), right_(std::move(right)) {}
188-
189-
std::string AndImpl::ToString() const {
190-
return std::format("({} and {})", left_->ToString(), right_->ToString());
191-
}
192-
193-
std::shared_ptr<Predicate> AndImpl::Negate() const {
165+
template <PredicateLike PredicateType>
166+
std::shared_ptr<PredicateType> AndImpl<PredicateType>::Negate() const {
194167
// De Morgan's law: not(A and B) = (not A) or (not B)
195168
auto left_negated = left_->Negate();
196169
auto right_negated = right_->Negate();
197-
return std::make_shared<OrImpl>(left_negated, right_negated);
170+
return std::make_shared<OrImpl<PredicateType>>(std::move(left_negated),
171+
std::move(right_negated));
198172
}
199173

200-
bool AndImpl::Equals(const Expression& expr) const {
201-
if (expr.op() == Operation::kAnd) {
202-
const auto& other = iceberg::internal::checked_cast<const AndImpl&>(expr);
203-
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
204-
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
205-
}
206-
return false;
207-
}
208-
209-
// Implementation of OrImpl
210-
OrImpl::OrImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
211-
: left_(std::move(left)), right_(std::move(right)) {}
212-
213-
std::string OrImpl::ToString() const {
214-
return std::format("({} or {})", left_->ToString(), right_->ToString());
215-
}
216-
217-
std::shared_ptr<Predicate> OrImpl::Negate() const {
174+
template <PredicateLike PredicateType>
175+
std::shared_ptr<PredicateType> OrImpl<PredicateType>::Negate() const {
218176
// De Morgan's law: not(A or B) = (not A) and (not B)
219177
auto left_negated = left_->Negate();
220178
auto right_negated = right_->Negate();
221-
return std::make_shared<AndImpl>(left_negated, right_negated);
179+
return std::make_shared<AndImpl<PredicateType>>(left_negated, right_negated);
222180
}
223181

224-
bool OrImpl::Equals(const Expression& expr) const {
225-
if (expr.op() == Operation::kOr) {
226-
const auto& other = iceberg::internal::checked_cast<const OrImpl&>(expr);
227-
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
228-
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
229-
}
230-
return false;
231-
}
182+
using True = TrueImpl<Predicate>;
183+
using BoundTrue = TrueImpl<BoundPredicate>;
184+
using False = FalseImpl<Predicate>;
185+
using BoundFalse = FalseImpl<BoundPredicate>;
186+
using AndPredicate = AndImpl<Predicate>;
187+
using BoundAndPredicate = AndImpl<BoundPredicate>;
188+
using OrPredicate = OrImpl<Predicate>;
189+
using BoundOrPredicate = OrImpl<BoundPredicate>;
232190

233191
// Implementation of Predicate static factory methods
234192
const std::shared_ptr<Predicate>& Predicate::AlwaysTrue() {
@@ -253,7 +211,7 @@ std::shared_ptr<Predicate> Predicate::And(std::shared_ptr<Predicate> left,
253211
return left;
254212
}
255213
*/
256-
return std::make_shared<AndImpl>(std::move(left), std::move(right));
214+
return std::make_shared<AndPredicate>(std::move(left), std::move(right));
257215
}
258216

259217
std::shared_ptr<Predicate> Predicate::Or(std::shared_ptr<Predicate> left,
@@ -268,7 +226,7 @@ std::shared_ptr<Predicate> Predicate::Or(std::shared_ptr<Predicate> left,
268226
return left;
269227
}
270228
*/
271-
return std::make_shared<OrImpl>(std::move(left), std::move(right));
229+
return std::make_shared<OrPredicate>(std::move(left), std::move(right));
272230
}
273231

274232
/// Unary predicate, for example, `a IS NULL`, which `a` is a Term.

0 commit comments

Comments
 (0)