| /* |
| * 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/expressions.h |
| /// Factory methods for creating expressions. |
| |
| #include <initializer_list> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "iceberg/expression/aggregate.h" |
| #include "iceberg/expression/literal.h" |
| #include "iceberg/expression/predicate.h" |
| #include "iceberg/expression/term.h" |
| #include "iceberg/iceberg_export.h" |
| #include "iceberg/util/macros.h" |
| |
| namespace iceberg { |
| |
| /// \brief Fluent APIs to create expressions. |
| /// |
| /// \throw `ExpressionError` for invalid expression. |
| class ICEBERG_EXPORT Expressions { |
| public: |
| // Logical operations |
| |
| /// \brief Create an AND expression. |
| template <typename... Args> |
| static std::shared_ptr<Expression> And(std::shared_ptr<Expression> left, |
| std::shared_ptr<Expression> right, |
| Args&&... args) |
| requires std::conjunction_v<std::is_same<Args, std::shared_ptr<Expression>>...> |
| { |
| ICEBERG_ASSIGN_OR_THROW(auto and_expr, |
| iceberg::And::MakeFolded(std::move(left), std::move(right), |
| std::forward<Args>(args)...)); |
| return and_expr; |
| } |
| |
| /// \brief Create an OR expression. |
| template <typename... Args> |
| static std::shared_ptr<Expression> Or(std::shared_ptr<Expression> left, |
| std::shared_ptr<Expression> right, Args&&... args) |
| requires std::conjunction_v<std::is_same<Args, std::shared_ptr<Expression>>...> |
| { |
| ICEBERG_ASSIGN_OR_THROW(auto or_expr, |
| iceberg::Or::MakeFolded(std::move(left), std::move(right), |
| std::forward<Args>(args)...)); |
| return or_expr; |
| } |
| |
| /// \brief Create a NOT expression. |
| /// |
| /// \param child The expression to negate |
| /// \return A negated expression with optimizations applied: |
| /// - not(true) returns false |
| /// - not(false) returns true |
| /// - not(not(x)) returns x |
| static std::shared_ptr<Expression> Not(std::shared_ptr<Expression> child); |
| |
| // Transform functions |
| |
| /// \brief Create a bucket transform term. |
| static std::shared_ptr<UnboundTransform> Bucket(std::string name, int32_t num_buckets); |
| |
| /// \brief Create a year transform term. |
| static std::shared_ptr<UnboundTransform> Year(std::string name); |
| |
| /// \brief Create a month transform term. |
| static std::shared_ptr<UnboundTransform> Month(std::string name); |
| |
| /// \brief Create a day transform term. |
| static std::shared_ptr<UnboundTransform> Day(std::string name); |
| |
| /// \brief Create an hour transform term. |
| static std::shared_ptr<UnboundTransform> Hour(std::string name); |
| |
| /// \brief Create a truncate transform term. |
| static std::shared_ptr<UnboundTransform> Truncate(std::string name, int32_t width); |
| |
| /// \brief Create a transform expression. |
| static std::shared_ptr<UnboundTransform> Transform( |
| std::string name, std::shared_ptr<Transform> transform); |
| |
| // Aggregates |
| |
| /// \brief Create a COUNT aggregate for a field name. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> Count(std::string name); |
| |
| /// \brief Create a COUNT aggregate for an unbound term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> Count( |
| std::shared_ptr<UnboundTerm<BoundReference>> expr); |
| |
| /// \brief Create a COUNT_NULL aggregate for a field name. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> CountNull( |
| std::string name); |
| |
| /// \brief Create a COUNT_NULL aggregate for an unbound term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> CountNull( |
| std::shared_ptr<UnboundTerm<BoundReference>> expr); |
| |
| /// \brief Create a COUNT_NOT_NULL aggregate for a field name. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> CountNotNull( |
| std::string name); |
| |
| /// \brief Create a COUNT_NOT_NULL aggregate for an unbound term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> CountNotNull( |
| std::shared_ptr<UnboundTerm<BoundReference>> expr); |
| |
| /// \brief Create a COUNT(*) aggregate. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> CountStar(); |
| |
| /// \brief Create a MAX aggregate for a field name. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> Max(std::string name); |
| |
| /// \brief Create a MAX aggregate for an unbound term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> Max( |
| std::shared_ptr<UnboundTerm<BoundReference>> expr); |
| /// \brief Create a MAX aggregate for an unbound transform term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundTransform>> Max( |
| std::shared_ptr<UnboundTerm<BoundTransform>> expr); |
| |
| /// \brief Create a MIN aggregate for a field name. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> Min(std::string name); |
| |
| /// \brief Create a MIN aggregate for an unbound term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundReference>> Min( |
| std::shared_ptr<UnboundTerm<BoundReference>> expr); |
| /// \brief Create a MIN aggregate for an unbound transform term. |
| static std::shared_ptr<UnboundAggregateImpl<BoundTransform>> Min( |
| std::shared_ptr<UnboundTerm<BoundTransform>> expr); |
| |
| // Unary predicates |
| |
| /// \brief Create an IS NULL predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> IsNull(std::string name); |
| |
| /// \brief Create an IS NULL predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> IsNull( |
| std::shared_ptr<UnboundTerm<B>> expr) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, |
| UnboundPredicateImpl<B>::Make(Expression::Operation::kIsNull, std::move(expr))); |
| return pred; |
| } |
| |
| /// \brief Create a NOT NULL predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> NotNull(std::string name); |
| |
| /// \brief Create a NOT NULL predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> NotNull( |
| std::shared_ptr<UnboundTerm<B>> expr) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, |
| UnboundPredicateImpl<B>::Make(Expression::Operation::kNotNull, std::move(expr))); |
| return pred; |
| } |
| |
| /// \brief Create an IS NaN predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> IsNaN(std::string name); |
| |
| /// \brief Create an IS NaN predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> IsNaN( |
| std::shared_ptr<UnboundTerm<B>> expr) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, |
| UnboundPredicateImpl<B>::Make(Expression::Operation::kIsNan, std::move(expr))); |
| return pred; |
| } |
| |
| /// \brief Create a NOT NaN predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> NotNaN(std::string name); |
| |
| /// \brief Create a NOT NaN predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> NotNaN( |
| std::shared_ptr<UnboundTerm<B>> expr) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, |
| UnboundPredicateImpl<B>::Make(Expression::Operation::kNotNan, std::move(expr))); |
| return pred; |
| } |
| |
| // Comparison predicates |
| |
| /// \brief Create a less than predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> LessThan(std::string name, |
| Literal value); |
| |
| /// \brief Create a less than predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> LessThan( |
| std::shared_ptr<UnboundTerm<B>> expr, Literal value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kLt, |
| std::move(expr), std::move(value))); |
| return pred; |
| } |
| |
| /// \brief Create a less than or equal predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> LessThanOrEqual( |
| std::string name, Literal value); |
| |
| /// \brief Create a less than or equal predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> LessThanOrEqual( |
| std::shared_ptr<UnboundTerm<B>> expr, Literal value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kLtEq, |
| std::move(expr), std::move(value))); |
| return pred; |
| } |
| |
| /// \brief Create a greater than predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> GreaterThan( |
| std::string name, Literal value); |
| |
| /// \brief Create a greater than predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> GreaterThan( |
| std::shared_ptr<UnboundTerm<B>> expr, Literal value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kGt, |
| std::move(expr), std::move(value))); |
| return pred; |
| } |
| |
| /// \brief Create a greater than or equal predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> GreaterThanOrEqual( |
| std::string name, Literal value); |
| |
| /// \brief Create a greater than or equal predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> GreaterThanOrEqual( |
| std::shared_ptr<UnboundTerm<B>> expr, Literal value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kGtEq, |
| std::move(expr), std::move(value))); |
| return pred; |
| } |
| |
| /// \brief Create an equal predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> Equal(std::string name, |
| Literal value); |
| |
| /// \brief Create an equal predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> Equal( |
| std::shared_ptr<UnboundTerm<B>> expr, Literal value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kEq, |
| std::move(expr), std::move(value))); |
| return pred; |
| } |
| |
| /// \brief Create a not equal predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> NotEqual(std::string name, |
| Literal value); |
| |
| /// \brief Create a not equal predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> NotEqual( |
| std::shared_ptr<UnboundTerm<B>> expr, Literal value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kNotEq, |
| std::move(expr), std::move(value))); |
| return pred; |
| } |
| |
| // String predicates |
| |
| /// \brief Create a starts with predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> StartsWith( |
| std::string name, std::string value); |
| |
| /// \brief Create a starts with predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> StartsWith( |
| std::shared_ptr<UnboundTerm<B>> expr, std::string value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, |
| UnboundPredicateImpl<B>::Make(Expression::Operation::kStartsWith, std::move(expr), |
| Literal::String(std::move(value)))); |
| return pred; |
| } |
| |
| /// \brief Create a not starts with predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> NotStartsWith( |
| std::string name, std::string value); |
| |
| /// \brief Create a not starts with predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> NotStartsWith( |
| std::shared_ptr<UnboundTerm<B>> expr, std::string value) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kNotStartsWith, |
| std::move(expr), |
| Literal::String(std::move(value)))); |
| return pred; |
| } |
| |
| // Set predicates |
| |
| /// \brief Create an IN predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> In( |
| std::string name, std::vector<Literal> values); |
| |
| /// \brief Create an IN predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> In(std::shared_ptr<UnboundTerm<B>> expr, |
| std::vector<Literal> values) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kIn, |
| std::move(expr), std::move(values))); |
| return pred; |
| } |
| |
| /// \brief Create an IN predicate for a field name with initializer list. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> In( |
| std::string name, std::initializer_list<Literal> values); |
| |
| /// \brief Create an IN predicate for an unbound term with initializer list. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> In( |
| std::shared_ptr<UnboundTerm<B>> expr, std::initializer_list<Literal> values) { |
| return In<B>(std::move(expr), std::vector<Literal>(values)); |
| } |
| |
| /// \brief Create a NOT IN predicate for a field name. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> NotIn( |
| std::string name, std::vector<Literal> values); |
| |
| /// \brief Create a NOT IN predicate for an unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> NotIn( |
| std::shared_ptr<UnboundTerm<B>> expr, std::vector<Literal> values) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(Expression::Operation::kNotIn, |
| std::move(expr), std::move(values))); |
| return pred; |
| } |
| |
| /// \brief Create a NOT IN predicate for a field name with initializer list. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> NotIn( |
| std::string name, std::initializer_list<Literal> values); |
| |
| /// \brief Create a NOT IN predicate for an unbound term with initializer list. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> NotIn( |
| std::shared_ptr<UnboundTerm<B>> expr, std::initializer_list<Literal> values) { |
| return NotIn<B>(expr, std::vector<Literal>(values)); |
| } |
| |
| // Generic predicate factory |
| |
| /// \brief Create a predicate with operation and single value. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> Predicate( |
| Expression::Operation op, std::string name, Literal value); |
| |
| /// \brief Create a predicate with operation and multiple values. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> Predicate( |
| Expression::Operation op, std::string name, std::vector<Literal> values); |
| |
| /// \brief Create a predicate with operation and multiple values. |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> Predicate( |
| Expression::Operation op, std::string name, std::initializer_list<Literal> values); |
| |
| /// \brief Create a unary predicate (no values). |
| static std::shared_ptr<UnboundPredicateImpl<BoundReference>> Predicate( |
| Expression::Operation op, std::string name); |
| |
| /// \brief Create a predicate for unbound term with multiple values. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> Predicate( |
| Expression::Operation op, std::shared_ptr<UnboundTerm<B>> expr, |
| std::vector<Literal> values) { |
| ICEBERG_ASSIGN_OR_THROW( |
| auto pred, UnboundPredicateImpl<B>::Make(op, std::move(expr), std::move(values))); |
| return pred; |
| } |
| |
| /// \brief Create a predicate with operation and multiple values. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> Predicate( |
| Expression::Operation op, std::shared_ptr<UnboundTerm<B>> expr, |
| std::initializer_list<Literal> values) { |
| return Predicate<B>(op, std::move(expr), std::vector<Literal>(values)); |
| } |
| |
| /// \brief Create a unary predicate for unbound term. |
| template <typename B> |
| static std::shared_ptr<UnboundPredicateImpl<B>> Predicate( |
| Expression::Operation op, std::shared_ptr<UnboundTerm<B>> expr) { |
| ICEBERG_ASSIGN_OR_THROW(auto pred, |
| UnboundPredicateImpl<B>::Make(op, std::move(expr))); |
| return pred; |
| } |
| |
| // Constants |
| |
| /// \brief Return the always true expression. |
| static std::shared_ptr<True> AlwaysTrue(); |
| |
| /// \brief Return the always false expression. |
| static std::shared_ptr<False> AlwaysFalse(); |
| |
| // Utilities |
| |
| /// \brief Create a named reference to a field. |
| static std::shared_ptr<NamedReference> Ref(std::string name); |
| |
| /// \brief Create a literal from a value. |
| static Literal Lit(Literal::Value value, std::shared_ptr<PrimitiveType> type); |
| }; |
| |
| } // namespace iceberg |