| /* | |
| * 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 |