Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 29 additions & 30 deletions src/iceberg/expression/evaluator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,72 +44,71 @@ class EvalVisitor : public BoundVisitor<bool> {
return left_result || right_result;
}

Result<bool> IsNull(const std::shared_ptr<BoundTerm>& term) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> IsNull(const std::shared_ptr<Bound>& expr) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value.IsNull();
}

Result<bool> NotNull(const std::shared_ptr<BoundTerm>& term) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, IsNull(term));
Result<bool> NotNull(const std::shared_ptr<Bound>& expr) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, IsNull(expr));
return !value;
}

Result<bool> IsNaN(const std::shared_ptr<BoundTerm>& term) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> IsNaN(const std::shared_ptr<Bound>& expr) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value.IsNaN();
}

Result<bool> NotNaN(const std::shared_ptr<BoundTerm>& term) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, IsNaN(term));
Result<bool> NotNaN(const std::shared_ptr<Bound>& expr) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, IsNaN(expr));
return !value;
}

Result<bool> Lt(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> Lt(const std::shared_ptr<Bound>& expr, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value < lit;
}

Result<bool> LtEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> LtEq(const std::shared_ptr<Bound>& expr, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value <= lit;
}

Result<bool> Gt(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> Gt(const std::shared_ptr<Bound>& expr, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value > lit;
}

Result<bool> GtEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> GtEq(const std::shared_ptr<Bound>& expr, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value >= lit;
}

Result<bool> Eq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
Result<bool> Eq(const std::shared_ptr<Bound>& expr, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return value == lit;
}

Result<bool> NotEq(const std::shared_ptr<BoundTerm>& term,
const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto eq_result, Eq(term, lit));
Result<bool> NotEq(const std::shared_ptr<Bound>& expr, const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto eq_result, Eq(expr, lit));
return !eq_result;
}

Result<bool> In(const std::shared_ptr<BoundTerm>& term,
Result<bool> In(const std::shared_ptr<Bound>& expr,
const BoundSetPredicate::LiteralSet& literal_set) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));
return literal_set.contains(value);
}

Result<bool> NotIn(const std::shared_ptr<BoundTerm>& term,
Result<bool> NotIn(const std::shared_ptr<Bound>& expr,
const BoundSetPredicate::LiteralSet& literal_set) override {
ICEBERG_ASSIGN_OR_RAISE(auto in_result, In(term, literal_set));
ICEBERG_ASSIGN_OR_RAISE(auto in_result, In(expr, literal_set));
return !in_result;
}

Result<bool> StartsWith(const std::shared_ptr<BoundTerm>& term,
Result<bool> StartsWith(const std::shared_ptr<Bound>& expr,
const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
ICEBERG_ASSIGN_OR_RAISE(auto value, expr->Evaluate(row_));

// Both value and literal should be strings
if (!std::holds_alternative<std::string>(value.value()) ||
Expand All @@ -122,9 +121,9 @@ class EvalVisitor : public BoundVisitor<bool> {
return str_value.starts_with(str_prefix);
}

Result<bool> NotStartsWith(const std::shared_ptr<BoundTerm>& term,
Result<bool> NotStartsWith(const std::shared_ptr<Bound>& expr,
const Literal& lit) override {
ICEBERG_ASSIGN_OR_RAISE(auto starts_result, StartsWith(term, lit));
ICEBERG_ASSIGN_OR_RAISE(auto starts_result, StartsWith(expr, lit));
return !starts_result;
}

Expand All @@ -144,7 +143,7 @@ Result<std::unique_ptr<Evaluator>> Evaluator::Make(const Schema& schema,
return std::unique_ptr<Evaluator>(new Evaluator(std::move(bound_expr)));
}

Result<bool> Evaluator::Eval(const StructLike& row) const {
Result<bool> Evaluator::Evaluate(const StructLike& row) const {
EvalVisitor visitor(row);
return Visit<bool, EvalVisitor>(bound_expr_, visitor);
}
Expand Down
2 changes: 1 addition & 1 deletion src/iceberg/expression/evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class ICEBERG_EXPORT Evaluator {
///
/// \param row The data row to evaluate
/// \return true if the row matches the expression, false otherwise, or error
Result<bool> Eval(const StructLike& row) const;
Result<bool> Evaluate(const StructLike& row) const;

private:
explicit Evaluator(std::shared_ptr<Expression> bound_expr);
Expand Down
40 changes: 40 additions & 0 deletions src/iceberg/expression/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

#include "iceberg/iceberg_export.h"
#include "iceberg/result.h"
#include "iceberg/type_fwd.h"
#include "iceberg/util/formattable.h"
#include "iceberg/util/macros.h"

Expand Down Expand Up @@ -328,4 +329,43 @@ ICEBERG_EXPORT std::string_view ToString(Expression::Operation op);
/// \brief Returns the negated operation.
ICEBERG_EXPORT Result<Expression::Operation> Negate(Expression::Operation op);

/// \brief Interface for unbound expressions that need schema binding.
///
/// Unbound expressions contain string-based references that must be resolved
/// against a concrete schema to produce bound expressions that can be evaluated.
///
/// \tparam B The bound type this term produces when binding is successful
template <typename B>
class ICEBERG_EXPORT Unbound {
public:
/// \brief Bind this expression to a concrete schema.
///
/// \param schema The schema to bind against
/// \param case_sensitive Whether field name matching should be case sensitive
/// \return A bound expression or an error if binding fails
virtual Result<std::shared_ptr<B>> Bind(const Schema& schema,
bool case_sensitive) const = 0;

/// \brief Overloaded Bind method that uses case-sensitive matching by default.
Result<std::shared_ptr<B>> Bind(const Schema& schema) const;

/// \brief Returns the underlying named reference for this unbound term.
virtual std::shared_ptr<class NamedReference> reference() = 0;
};

/// \brief Interface for bound expressions that can be evaluated.
///
/// Bound expressions have been resolved against a concrete schema and contain
/// all necessary information to evaluate against data structures.
class ICEBERG_EXPORT Bound {
public:
virtual ~Bound();

/// \brief Evaluate this expression against a row-based data.
virtual Result<Literal> Evaluate(const StructLike& data) const = 0;

/// \brief Returns the underlying bound reference for this term.
virtual std::shared_ptr<class BoundReference> reference() = 0;
};

} // namespace iceberg
84 changes: 42 additions & 42 deletions src/iceberg/expression/expression_visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,86 +107,86 @@ class ICEBERG_EXPORT BoundVisitor : public ExpressionVisitor<R> {
public:
~BoundVisitor() override = default;

/// \brief Visit an IS_NULL unary predicate.
/// \param term The bound term being tested
virtual Result<R> IsNull(const std::shared_ptr<BoundTerm>& term) = 0;
/// \brief Visit an IS_NULL bound expression.
/// \param expr The bound expression being tested
virtual Result<R> IsNull(const std::shared_ptr<Bound>& expr) = 0;

/// \brief Visit a NOT_NULL unary predicate.
/// \param term The bound term being tested
virtual Result<R> NotNull(const std::shared_ptr<BoundTerm>& term) = 0;
/// \brief Visit a NOT_NULL bound expression.
/// \param expr The bound expression being tested
virtual Result<R> NotNull(const std::shared_ptr<Bound>& expr) = 0;

/// \brief Visit an IS_NAN unary predicate.
/// \param term The bound term being tested
virtual Result<R> IsNaN(const std::shared_ptr<BoundTerm>& term) {
/// \brief Visit an IS_NAN bound expression.
/// \param expr The bound expression being tested
virtual Result<R> IsNaN(const std::shared_ptr<Bound>& expr) {
return NotSupported("IsNaN operation is not supported by this visitor");
}

/// \brief Visit a NOT_NAN unary predicate.
/// \param term The bound term being tested
virtual Result<R> NotNaN(const std::shared_ptr<BoundTerm>& term) {
/// \brief Visit a NOT_NAN bound expression.
/// \param expr The bound expression being tested
virtual Result<R> NotNaN(const std::shared_ptr<Bound>& expr) {
return NotSupported("NotNaN operation is not supported by this visitor");
}

/// \brief Visit a less-than predicate.
/// \param term The bound term
/// \brief Visit a less-than bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to compare against
virtual Result<R> Lt(const std::shared_ptr<BoundTerm>& term, const Literal& lit) = 0;
virtual Result<R> Lt(const std::shared_ptr<Bound>& expr, const Literal& lit) = 0;

/// \brief Visit a less-than-or-equal predicate.
/// \param term The bound term
/// \brief Visit a less-than-or-equal bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to compare against
virtual Result<R> LtEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) = 0;
virtual Result<R> LtEq(const std::shared_ptr<Bound>& expr, const Literal& lit) = 0;

/// \brief Visit a greater-than predicate.
/// \param term The bound term
/// \brief Visit a greater-than bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to compare against
virtual Result<R> Gt(const std::shared_ptr<BoundTerm>& term, const Literal& lit) = 0;
virtual Result<R> Gt(const std::shared_ptr<Bound>& expr, const Literal& lit) = 0;

/// \brief Visit a greater-than-or-equal predicate.
/// \param term The bound term
/// \brief Visit a greater-than-or-equal bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to compare against
virtual Result<R> GtEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) = 0;
virtual Result<R> GtEq(const std::shared_ptr<Bound>& expr, const Literal& lit) = 0;

/// \brief Visit an equality predicate.
/// \param term The bound term
/// \brief Visit an equality bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to compare against
virtual Result<R> Eq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) = 0;
virtual Result<R> Eq(const std::shared_ptr<Bound>& expr, const Literal& lit) = 0;

/// \brief Visit a not-equal predicate.
/// \param term The bound term
/// \brief Visit a not-equal bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to compare against
virtual Result<R> NotEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) = 0;
virtual Result<R> NotEq(const std::shared_ptr<Bound>& expr, const Literal& lit) = 0;

/// \brief Visit a starts-with predicate.
/// \param term The bound term
/// \brief Visit a starts-with bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to check for prefix match
virtual Result<R> StartsWith([[maybe_unused]] const std::shared_ptr<BoundTerm>& term,
virtual Result<R> StartsWith([[maybe_unused]] const std::shared_ptr<Bound>& expr,
[[maybe_unused]] const Literal& lit) {
return NotSupported("StartsWith operation is not supported by this visitor");
}

/// \brief Visit a not-starts-with predicate.
/// \param term The bound term
/// \brief Visit a not-starts-with bound expression.
/// \param expr The bound expression being tested
/// \param lit The literal value to check for prefix match
virtual Result<R> NotStartsWith([[maybe_unused]] const std::shared_ptr<BoundTerm>& term,
virtual Result<R> NotStartsWith([[maybe_unused]] const std::shared_ptr<Bound>& expr,
[[maybe_unused]] const Literal& lit) {
return NotSupported("NotStartsWith operation is not supported by this visitor");
}

/// \brief Visit an IN set predicate.
/// \param term The bound term
/// \brief Visit an IN set bound expression.
/// \param expr The bound expression being tested
/// \param literal_set The set of literal values to test membership
virtual Result<R> In(
[[maybe_unused]] const std::shared_ptr<BoundTerm>& term,
[[maybe_unused]] const std::shared_ptr<Bound>& expr,
[[maybe_unused]] const BoundSetPredicate::LiteralSet& literal_set) {
return NotSupported("In operation is not supported by this visitor");
}

/// \brief Visit a NOT_IN set predicate.
/// \param term The bound term
/// \brief Visit a NOT_IN set bound expression.
/// \param expr The bound expression being tested
/// \param literal_set The set of literal values to test membership
virtual Result<R> NotIn(
[[maybe_unused]] const std::shared_ptr<BoundTerm>& term,
[[maybe_unused]] const std::shared_ptr<Bound>& expr,
[[maybe_unused]] const BoundSetPredicate::LiteralSet& literal_set) {
return NotSupported("NotIn operation is not supported by this visitor");
}
Expand Down
40 changes: 1 addition & 39 deletions src/iceberg/expression/term.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <string>
#include <string_view>

#include "iceberg/expression/expression.h"
#include "iceberg/expression/literal.h"
#include "iceberg/type_fwd.h"
#include "iceberg/util/formattable.h"
Expand All @@ -45,45 +46,6 @@ class ICEBERG_EXPORT Term : public util::Formattable {
template <typename T>
concept TermType = std::derived_from<T, Term>;

/// \brief Interface for unbound expressions that need schema binding.
///
/// Unbound expressions contain string-based references that must be resolved
/// against a concrete schema to produce bound expressions that can be evaluated.
///
/// \tparam B The bound type this term produces when binding is successful
template <typename B>
class ICEBERG_EXPORT Unbound {
public:
/// \brief Bind this expression to a concrete schema.
///
/// \param schema The schema to bind against
/// \param case_sensitive Whether field name matching should be case sensitive
/// \return A bound expression or an error if binding fails
virtual Result<std::shared_ptr<B>> Bind(const Schema& schema,
bool case_sensitive) const = 0;

/// \brief Overloaded Bind method that uses case-sensitive matching by default.
Result<std::shared_ptr<B>> Bind(const Schema& schema) const;

/// \brief Returns the underlying named reference for this unbound term.
virtual std::shared_ptr<class NamedReference> reference() = 0;
};

/// \brief Interface for bound expressions that can be evaluated.
///
/// Bound expressions have been resolved against a concrete schema and contain
/// all necessary information to evaluate against data structures.
class ICEBERG_EXPORT Bound {
public:
virtual ~Bound();

/// \brief Evaluate this expression against a row-based data.
virtual Result<Literal> Evaluate(const StructLike& data) const = 0;

/// \brief Returns the underlying bound reference for this term.
virtual std::shared_ptr<class BoundReference> reference() = 0;
};

/// \brief Base class for unbound terms.
///
/// \tparam B The bound type this term produces when binding is successful.
Expand Down
Loading
Loading