| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| #pragma once | |
| /// \file iceberg/expression/predicate.h | |
| /// Predicate interface for boolean expressions that test terms. | |
| #include <unordered_set> | |
| #include "iceberg/expression/expression.h" | |
| #include "iceberg/expression/literal.h" | |
| #include "iceberg/expression/term.h" | |
| #include "iceberg/iceberg_export.h" | |
| namespace iceberg { | |
| /// \brief A predicate is a boolean expression that tests a term against some criteria. | |
| /// | |
| /// \tparam TermType The type of the term being tested | |
| template <TermType T> | |
| class ICEBERG_EXPORT Predicate : public virtual Expression { | |
| public: | |
| ~Predicate() override; | |
| Expression::Operation op() const override { return operation_; } | |
| /// \brief Returns the term this predicate tests. | |
| const std::shared_ptr<T>& term() const { return term_; } | |
| protected: | |
| /// \brief Create a predicate with an operation and term. | |
| /// | |
| /// \param op The operation this predicate performs | |
| /// \param term The term this predicate tests | |
| Predicate(Expression::Operation op, std::shared_ptr<T> term); | |
| Expression::Operation operation_; | |
| std::shared_ptr<T> term_; | |
| }; | |
| /// \brief Non-template base class for all UnboundPredicate instances. | |
| /// | |
| /// This class enables type erasure for template-based UnboundPredicate classes, | |
| /// allowing them to be used in non-template visitor interfaces. | |
| class ICEBERG_EXPORT UnboundPredicate : public virtual Expression, | |
| public Unbound<Expression> { | |
| public: | |
| ~UnboundPredicate() override = default; | |
| /// \brief Returns the reference of this UnboundPredicate. | |
| std::shared_ptr<const NamedReference> reference() const override = 0; | |
| /// \brief Bind this UnboundPredicate. | |
| Result<std::shared_ptr<Expression>> Bind(const Schema& schema, | |
| bool case_sensitive) const override = 0; | |
| /// \brief Negate this UnboundPredicate. | |
| Result<std::shared_ptr<Expression>> Negate() const override = 0; | |
| bool is_unbound_predicate() const override { return true; } | |
| /// \brief Returns the term of this predicate as a base Term reference. | |
| virtual const Term& unbound_term() const = 0; | |
| /// \brief Returns the literals of this predicate. | |
| virtual std::span<const Literal> literals() const = 0; | |
| protected: | |
| UnboundPredicate() = default; | |
| }; | |
| /// \brief Unbound predicates contain unbound terms and must be bound to a concrete schema | |
| /// before they can be evaluated. | |
| /// | |
| /// \tparam B The bound type this predicate produces when binding is successful | |
| template <typename B> | |
| class ICEBERG_EXPORT UnboundPredicateImpl : public UnboundPredicate, | |
| public Predicate<UnboundTerm<B>> { | |
| using BASE = Predicate<UnboundTerm<B>>; | |
| public: | |
| /// \brief Create an unbound predicate (unary operation). | |
| /// | |
| /// \param op The operation (kIsNull, kNotNull, kIsNan, kNotNan) | |
| /// \param term The unbound term | |
| /// \return Result containing the unbound predicate or an error | |
| static Result<std::unique_ptr<UnboundPredicateImpl<B>>> Make( | |
| Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term); | |
| /// \brief Create an unbound predicate with a single value. | |
| /// | |
| /// \param op The operation | |
| /// \param term The unbound term | |
| /// \param value The literal value | |
| /// \return Result containing the unbound predicate or an error | |
| static Result<std::unique_ptr<UnboundPredicateImpl<B>>> Make( | |
| Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term, Literal value); | |
| /// \brief Create an unbound predicate with multiple values. | |
| /// | |
| /// \param op The operation (typically kIn or kNotIn) | |
| /// \param term The unbound term | |
| /// \param values Vector of literal values | |
| /// \return Result containing the unbound predicate or an error | |
| static Result<std::unique_ptr<UnboundPredicateImpl<B>>> Make( | |
| Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term, | |
| std::vector<Literal> values); | |
| ~UnboundPredicateImpl() override; | |
| std::shared_ptr<const NamedReference> reference() const override { | |
| return BASE::term()->reference(); | |
| } | |
| std::string ToString() const override; | |
| bool Equals(const Expression& other) const override; | |
| Result<std::shared_ptr<Expression>> Bind(const Schema& schema, | |
| bool case_sensitive) const override; | |
| Result<std::shared_ptr<Expression>> Negate() const override; | |
| const Term& unbound_term() const override { return *BASE::term(); } | |
| std::span<const Literal> literals() const override { return values_; } | |
| private: | |
| UnboundPredicateImpl(Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term); | |
| UnboundPredicateImpl(Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term, | |
| Literal value); | |
| UnboundPredicateImpl(Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term, | |
| std::vector<Literal> values); | |
| Result<std::shared_ptr<Expression>> BindUnaryOperation( | |
| const Schema& schema, std::shared_ptr<B> bound_term) const; | |
| Result<std::shared_ptr<Expression>> BindLiteralOperation( | |
| std::shared_ptr<B> bound_term) const; | |
| Result<std::shared_ptr<Expression>> BindInOperation( | |
| std::shared_ptr<B> bound_term) const; | |
| private: | |
| std::vector<Literal> values_; | |
| }; | |
| /// \brief Bound predicates contain bound terms and can be evaluated. | |
| class ICEBERG_EXPORT BoundPredicate : public Predicate<BoundTerm>, public Bound { | |
| public: | |
| ~BoundPredicate() override; | |
| using Predicate<BoundTerm>::op; | |
| using Predicate<BoundTerm>::term; | |
| std::shared_ptr<BoundReference> reference() override { return term_->reference(); } | |
| Result<Literal> Evaluate(const StructLike& data) const override; | |
| bool is_bound_predicate() const override { return true; } | |
| /// \brief Test a value against this predicate. | |
| /// | |
| /// \param value The literal value to test | |
| /// \return true if the predicate passes, false otherwise | |
| virtual Result<bool> Test(const Literal& value) const = 0; | |
| enum class Kind : int8_t { | |
| // A unary predicate (tests for null, not-null, etc.). | |
| kUnary = 0, | |
| // A literal predicate (compares against a literal). | |
| kLiteral, | |
| // A set predicate (tests membership in a set). | |
| kSet, | |
| }; | |
| /// \brief Returns the kind of this bound predicate. | |
| virtual Kind kind() const = 0; | |
| protected: | |
| BoundPredicate(Expression::Operation op, std::shared_ptr<BoundTerm> term); | |
| }; | |
| /// \brief Bound unary predicate (null, not-null, etc.). | |
| class ICEBERG_EXPORT BoundUnaryPredicate : public BoundPredicate { | |
| public: | |
| /// \brief Create a bound unary predicate. | |
| /// | |
| /// \param op The unary operation (kIsNull, kNotNull, kIsNan, kNotNan) | |
| /// \param term The bound term to test | |
| /// \return Result containing the bound unary predicate or an error | |
| static Result<std::unique_ptr<BoundUnaryPredicate>> Make( | |
| Expression::Operation op, std::shared_ptr<BoundTerm> term); | |
| ~BoundUnaryPredicate() override; | |
| Result<bool> Test(const Literal& value) const override; | |
| Kind kind() const override { return Kind::kUnary; } | |
| std::string ToString() const override; | |
| Result<std::shared_ptr<Expression>> Negate() const override; | |
| bool Equals(const Expression& other) const override; | |
| private: | |
| BoundUnaryPredicate(Expression::Operation op, std::shared_ptr<BoundTerm> term); | |
| }; | |
| /// \brief Bound literal predicate (comparison against a single value). | |
| class ICEBERG_EXPORT BoundLiteralPredicate : public BoundPredicate { | |
| public: | |
| /// \brief Create a bound literal predicate. | |
| /// | |
| /// \param op The comparison operation (kLt, kLtEq, kGt, kGtEq, kEq, kNotEq) | |
| /// \param term The bound term to compare | |
| /// \param literal The literal value to compare against | |
| /// \return Result containing the bound literal predicate or an error | |
| static Result<std::unique_ptr<BoundLiteralPredicate>> Make( | |
| Expression::Operation op, std::shared_ptr<BoundTerm> term, Literal literal); | |
| ~BoundLiteralPredicate() override; | |
| /// \brief Returns the literal being compared against. | |
| const Literal& literal() const { return literal_; } | |
| Result<bool> Test(const Literal& value) const override; | |
| Kind kind() const override { return Kind::kLiteral; } | |
| std::string ToString() const override; | |
| Result<std::shared_ptr<Expression>> Negate() const override; | |
| bool Equals(const Expression& other) const override; | |
| private: | |
| BoundLiteralPredicate(Expression::Operation op, std::shared_ptr<BoundTerm> term, | |
| Literal literal); | |
| Literal literal_; | |
| }; | |
| /// \brief Bound set predicate (membership testing against a set of values). | |
| class ICEBERG_EXPORT BoundSetPredicate : public BoundPredicate { | |
| public: | |
| using LiteralSet = std::unordered_set<Literal, LiteralHash>; | |
| /// \brief Create a bound set predicate. | |
| /// | |
| /// \param op The set operation (kIn, kNotIn) | |
| /// \param term The bound term to test for membership | |
| /// \param literals The set of literal values to test against | |
| /// \return Result containing the bound set predicate or an error | |
| static Result<std::unique_ptr<BoundSetPredicate>> Make( | |
| Expression::Operation op, std::shared_ptr<BoundTerm> term, | |
| std::span<const Literal> literals); | |
| /// \brief Create a bound set predicate using a set of literals. | |
| /// | |
| /// \param op The set operation (kIn, kNotIn) | |
| /// \param term The bound term to test for membership | |
| /// \param value_set The set of literal values to test against | |
| /// \return Result containing the bound set predicate or an error | |
| static Result<std::unique_ptr<BoundSetPredicate>> Make(Expression::Operation op, | |
| std::shared_ptr<BoundTerm> term, | |
| LiteralSet value_set); | |
| ~BoundSetPredicate() override; | |
| /// \brief Returns the set of literals to test against. | |
| const LiteralSet& literal_set() const { return value_set_; } | |
| Result<bool> Test(const Literal& value) const override; | |
| Kind kind() const override { return Kind::kSet; } | |
| std::string ToString() const override; | |
| Result<std::shared_ptr<Expression>> Negate() const override; | |
| bool Equals(const Expression& other) const override; | |
| private: | |
| BoundSetPredicate(Expression::Operation op, std::shared_ptr<BoundTerm> term, | |
| std::span<const Literal> literals); | |
| BoundSetPredicate(Expression::Operation op, std::shared_ptr<BoundTerm> term, | |
| LiteralSet value_set); | |
| LiteralSet value_set_; | |
| }; | |
| } // namespace iceberg |