blob: 6df0de1a695a2d8b06b377e0fce155f9b87c0d3d [file] [log] [blame]
/*
* 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<NamedReference> reference() 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<NamedReference> reference() override {
return BASE::term()->reference();
}
std::string ToString() 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(
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