From 7a7c25c10785092f0fa788a87a83f258a6568aed Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Sat, 6 Dec 2025 21:49:02 +0800 Subject: [PATCH 1/3] feat: add utils to project exprs on rows to exps on partitions --- src/iceberg/CMakeLists.txt | 1 + src/iceberg/expression/meson.build | 1 + src/iceberg/expression/projections.cc | 244 ++++++ src/iceberg/expression/projections.h | 109 +++ src/iceberg/meson.build | 1 + src/iceberg/partition_spec.h | 1 - src/iceberg/test/CMakeLists.txt | 1 + src/iceberg/test/meson.build | 1 + src/iceberg/test/projections_test.cc | 1140 +++++++++++++++++++++++++ 9 files changed, 1498 insertions(+), 1 deletion(-) create mode 100644 src/iceberg/expression/projections.cc create mode 100644 src/iceberg/expression/projections.h create mode 100644 src/iceberg/test/projections_test.cc diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt index 4c63c0c48..6e9eb0baf 100644 --- a/src/iceberg/CMakeLists.txt +++ b/src/iceberg/CMakeLists.txt @@ -29,6 +29,7 @@ set(ICEBERG_SOURCES expression/literal.cc expression/manifest_evaluator.cc expression/predicate.cc + expression/projections.cc expression/residual_evaluator.cc expression/rewrite_not.cc expression/strict_metrics_evaluator.cc diff --git a/src/iceberg/expression/meson.build b/src/iceberg/expression/meson.build index fbb072671..9b143ad31 100644 --- a/src/iceberg/expression/meson.build +++ b/src/iceberg/expression/meson.build @@ -27,6 +27,7 @@ install_headers( 'literal.h', 'manifest_evaluator.h', 'predicate.h', + 'projections.h', 'residual_evaluator.h', 'rewrite_not.h', 'strict_metrics_evaluator.h', diff --git a/src/iceberg/expression/projections.cc b/src/iceberg/expression/projections.cc new file mode 100644 index 000000000..7616dff4d --- /dev/null +++ b/src/iceberg/expression/projections.cc @@ -0,0 +1,244 @@ +/* + * 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. + */ + +#include "iceberg/expression/projections.h" + +#include +#include + +#include "iceberg/expression/expression.h" +#include "iceberg/expression/expression_visitor.h" +#include "iceberg/expression/expressions.h" +#include "iceberg/expression/predicate.h" +#include "iceberg/expression/rewrite_not.h" +#include "iceberg/expression/term.h" +#include "iceberg/partition_field.h" +#include "iceberg/partition_spec.h" +#include "iceberg/result.h" +#include "iceberg/transform.h" +#include "iceberg/util/macros.h" + +namespace iceberg { + +// Implementation detail - not exported +class ProjectionVisitor : public ExpressionVisitor> { + public: + ~ProjectionVisitor() override = default; + + ProjectionVisitor(const std::shared_ptr& spec, + const std::shared_ptr& schema, bool case_sensitive) + : spec_(spec), schema_(schema), case_sensitive_(case_sensitive) {} + + Result> AlwaysTrue() override { return True::Instance(); } + + Result> AlwaysFalse() override { return False::Instance(); } + + Result> Not( + const std::shared_ptr& child_result) override { + return InvalidExpression("Project called on expression with a not"); + } + + Result> And( + const std::shared_ptr& left_result, + const std::shared_ptr& right_result) override { + return Expressions::And(left_result, right_result); + } + + Result> Or( + const std::shared_ptr& left_result, + const std::shared_ptr& right_result) override { + return Expressions::Or(left_result, right_result); + } + + Result> Predicate( + const std::shared_ptr& pred) override { + ICEBERG_ASSIGN_OR_RAISE(auto bound_pred, pred->Bind(*schema_, case_sensitive_)); + if (bound_pred->is_bound_predicate()) { + auto bound_predicate = std::dynamic_pointer_cast(bound_pred); + ICEBERG_DCHECK( + bound_predicate != nullptr, + "Expected bound_predicate to be non-null after is_bound_predicate() check"); + return Predicate(bound_predicate); + } + return bound_pred; + } + + Result> Predicate( + const std::shared_ptr& pred) override { + return InvalidExpression("Bound predicates are not supported in projections"); + } + + protected: + const std::shared_ptr& spec_; + const std::shared_ptr& schema_; + bool case_sensitive_; + + /// \brief Get partition fields that match the predicate's term. + std::vector GetFieldsByPredicate( + const std::shared_ptr& pred) const { + int32_t source_id; + switch (pred->term()->kind()) { + case Term::Kind::kReference: { + const auto& ref = pred->term()->reference(); + source_id = ref->field().field_id(); + break; + } + case Term::Kind::kTransform: { + const auto& transform = + internal::checked_pointer_cast(pred->term()); + source_id = transform->reference()->field().field_id(); + break; + } + default: + std::unreachable(); + } + + std::vector result; + for (const auto& field : spec_->fields()) { + if (field.source_id() == source_id) { + result.push_back(&field); + } + } + return result; + } +}; + +ProjectionEvaluator::ProjectionEvaluator(std::unique_ptr visitor) + : visitor_(std::move(visitor)) {} + +ProjectionEvaluator::~ProjectionEvaluator() = default; + +/// \brief Inclusive projection visitor. +/// +/// Uses AND to combine projections from multiple partition fields. +class InclusiveProjectionVisitor : public ProjectionVisitor { + public: + ~InclusiveProjectionVisitor() override = default; + + InclusiveProjectionVisitor(const std::shared_ptr& spec, + const std::shared_ptr& schema, bool case_sensitive) + : ProjectionVisitor(spec, schema, case_sensitive) {} + + Result> Predicate( + const std::shared_ptr& pred) override { + ICEBERG_DCHECK(pred != nullptr, "Predicate cannot be null"); + // Find partition fields that match the predicate's term + auto partition_fields = GetFieldsByPredicate(pred); + if (partition_fields.empty()) { + // The predicate has no partition column + return AlwaysTrue(); + } + + // Project the predicate for each partition field and combine with AND + // + // consider (d = 2019-01-01) with bucket(7, d) and bucket(5, d) + // projections: b1 = bucket(7, '2019-01-01') = 5, b2 = bucket(5, '2019-01-01') = 0 + // any value where b1 != 5 or any value where b2 != 0 cannot be the '2019-01-01' + // + // similarly, if partitioning by day(ts) and hour(ts), the more restrictive + // projection should be used. ts = 2019-01-01T01:00:00 produces day=2019-01-01 and + // hour=2019-01-01-01. the value will be in 2019-01-01-01 and not in 2019-01-01-02. + std::shared_ptr result = True::Instance(); + for (const auto* part_field : partition_fields) { + ICEBERG_ASSIGN_OR_RAISE(auto projected, + part_field->transform()->Project(part_field->name(), pred)); + if (projected != nullptr) { + result = + Expressions::And(result, std::shared_ptr(projected.release())); + } + } + + return result; + } + + protected: +}; + +/// \brief Strict projection evaluator. +/// +/// Uses OR to combine projections from multiple partition fields. +class StrictProjectionVisitor : public ProjectionVisitor { + public: + ~StrictProjectionVisitor() override = default; + + StrictProjectionVisitor(const std::shared_ptr& spec, + const std::shared_ptr& schema, bool case_sensitive) + : ProjectionVisitor(spec, schema, case_sensitive) {} + + Result> Predicate( + const std::shared_ptr& pred) override { + ICEBERG_DCHECK(pred != nullptr, "Predicate cannot be null"); + // Find partition fields that match the predicate's term + auto partition_fields = GetFieldsByPredicate(pred); + if (partition_fields.empty()) { + // The predicate has no matching partition columns + return AlwaysFalse(); + } + + // Project the predicate for each partition field and combine with OR + // + // consider (ts > 2019-01-01T01:00:00) with day(ts) and hour(ts) + // projections: d >= 2019-01-02 and h >= 2019-01-01-02 (note the inclusive bounds). + // any timestamp where either projection predicate is true must match the original + // predicate. For example, ts = 2019-01-01T03:00:00 matches the hour projection but + // not the day, but does match the original predicate. + std::shared_ptr result = False::Instance(); + for (const auto* part_field : partition_fields) { + ICEBERG_ASSIGN_OR_RAISE(auto projected, part_field->transform()->ProjectStrict( + part_field->name(), pred)); + if (projected != nullptr) { + result = + Expressions::Or(result, std::shared_ptr(projected.release())); + } + } + + return result; + } +}; + +Result> ProjectionEvaluator::Project( + const std::shared_ptr& expr) { + // Projections assume that there are no NOT nodes in the expression tree. To ensure that + // this is the case, the expression is rewritten to push all NOT nodes down to the + // expression leaf nodes. + // + // This is necessary to ensure that the default expression returned when a predicate + // can't be projected is correct. + ICEBERG_ASSIGN_OR_RAISE(auto rewritten, RewriteNot::Visit(expr)); + return Visit, ProjectionVisitor>(rewritten, *visitor_); +} + +std::unique_ptr Projections::Inclusive( + const std::shared_ptr& spec, const std::shared_ptr& schema, + bool case_sensitive) { + auto visitor = + std::make_unique(spec, schema, case_sensitive); + return std::unique_ptr( + new ProjectionEvaluator(std::move(visitor))); +} + +std::unique_ptr Projections::Strict( + const std::shared_ptr& spec, const std::shared_ptr& schema, + bool case_sensitive) { + auto visitor = std::make_unique(spec, schema, case_sensitive); + return std::unique_ptr( + new ProjectionEvaluator(std::move(visitor))); +} + +} // namespace iceberg diff --git a/src/iceberg/expression/projections.h b/src/iceberg/expression/projections.h new file mode 100644 index 000000000..0fbf10f1f --- /dev/null +++ b/src/iceberg/expression/projections.h @@ -0,0 +1,109 @@ +/* + * 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/projections.h +/// Utils to project expressions on rows to expressions on partitions. + +#include + +#include "iceberg/expression/expression.h" +#include "iceberg/iceberg_export.h" +#include "iceberg/partition_spec.h" +#include "iceberg/result.h" +#include "iceberg/type_fwd.h" + +namespace iceberg { + +/// \brief A class that projects expressions for a table's data rows into expressions on +/// the table's partition values, for a table's partition spec. +class ICEBERG_EXPORT ProjectionEvaluator { + public: + ~ProjectionEvaluator(); + + /// \brief Project the given row expression to a partition expression. + /// + /// \param expr an expression on data rows + /// \return an expression on partition data (depends on the projection) + Result> Project(const std::shared_ptr& expr); + + private: + friend class Projections; + + /// \brief Create a ProjectionEvaluator. + /// + /// \param visitor The projection visitor to use + explicit ProjectionEvaluator(std::unique_ptr visitor); + + std::unique_ptr visitor_; +}; + +/// \brief Utils to project expressions on rows to expressions on partitions. +/// +/// There are two types of projections: inclusive and strict. +/// +/// An inclusive projection guarantees that if an expression matches a row, the projected +/// expression will match the row's partition. +/// +/// A strict projection guarantees that if a partition matches a projected expression, +/// then all rows in that partition will match the original expression. +class ICEBERG_EXPORT Projections { + public: + /// \brief Creates an inclusive ProjectionEvaluator for the partition spec. + /// + /// An evaluator is used to project expressions for a table's data rows into expressions + /// on the table's partition values. The evaluator returned by this function is + /// inclusive and will build expressions with the following guarantee: if the original + /// expression matches a row, then the projected expression will match that row's + /// partition. + /// + /// Each predicate in the expression is projected using Transform::Project. + /// + /// \param spec a partition spec + /// \param case_sensitive whether the Projection should consider case sensitivity on + /// column names or not. Defaults to true (case sensitive). + /// \return an inclusive projection evaluator for the partition spec + static std::unique_ptr Inclusive( + const std::shared_ptr& spec, const std::shared_ptr& schema, + bool case_sensitive = true); + + /// \brief Creates a strict ProjectionEvaluator for the partition spec. + /// + /// An evaluator is used to project expressions for a table's data rows into expressions + /// on the table's partition values. The evaluator returned by this function is strict + /// and will build expressions with the following guarantee: if the projected expression + /// matches a partition, then the original expression will match all rows in that + /// partition. + /// + /// Each predicate in the expression is projected using Transform::ProjectStrict. + /// + /// \param spec a partition spec + /// \param case_sensitive whether the Projection should consider case sensitivity on + /// column names or not. Defaults to true (case sensitive). + /// \return a strict projection evaluator for the partition spec + static std::unique_ptr Strict( + const std::shared_ptr& spec, const std::shared_ptr& schema, + bool case_sensitive = true); + + private: + Projections() = default; +}; + +} // namespace iceberg diff --git a/src/iceberg/meson.build b/src/iceberg/meson.build index d70eae253..d473d72e1 100644 --- a/src/iceberg/meson.build +++ b/src/iceberg/meson.build @@ -51,6 +51,7 @@ iceberg_sources = files( 'expression/literal.cc', 'expression/manifest_evaluator.cc', 'expression/predicate.cc', + 'expression/projections.cc', 'expression/residual_evaluator.cc', 'expression/rewrite_not.cc', 'expression/strict_metrics_evaluator.cc', diff --git a/src/iceberg/partition_spec.h b/src/iceberg/partition_spec.h index 7f8f67822..0d1a78f16 100644 --- a/src/iceberg/partition_spec.h +++ b/src/iceberg/partition_spec.h @@ -114,7 +114,6 @@ class ICEBERG_EXPORT PartitionSpec : public util::Formattable { private: /// \brief Create a new partition spec. /// - /// \param schema The table schema. /// \param spec_id The spec ID. /// \param fields The partition fields. /// \param last_assigned_field_id The last assigned field ID. If not provided, it will diff --git a/src/iceberg/test/CMakeLists.txt b/src/iceberg/test/CMakeLists.txt index 7d9434842..a48567132 100644 --- a/src/iceberg/test/CMakeLists.txt +++ b/src/iceberg/test/CMakeLists.txt @@ -89,6 +89,7 @@ add_iceberg_test(expression_test inclusive_metrics_evaluator_test.cc inclusive_metrics_evaluator_with_transform_test.cc predicate_test.cc + projections_test.cc residual_evaluator_test.cc strict_metrics_evaluator_test.cc) diff --git a/src/iceberg/test/meson.build b/src/iceberg/test/meson.build index f058cddad..6a2a9e9ab 100644 --- a/src/iceberg/test/meson.build +++ b/src/iceberg/test/meson.build @@ -66,6 +66,7 @@ iceberg_tests = { 'literal_test.cc', 'manifest_evaluator_test.cc', 'predicate_test.cc', + 'projections_test.cc', 'residual_evaluator_test.cc', 'strict_metrics_evaluator_test.cc', ), diff --git a/src/iceberg/test/projections_test.cc b/src/iceberg/test/projections_test.cc new file mode 100644 index 000000000..8890bdce3 --- /dev/null +++ b/src/iceberg/test/projections_test.cc @@ -0,0 +1,1140 @@ +/* + * 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. + */ + +#include "iceberg/expression/projections.h" + +#include +#include +#include + +#include + +#include "iceberg/expression/expressions.h" +#include "iceberg/expression/predicate.h" +#include "iceberg/partition_field.h" +#include "iceberg/partition_spec.h" +#include "iceberg/schema.h" +#include "iceberg/schema_field.h" +#include "iceberg/test/matchers.h" +#include "iceberg/test/temporal_test_helper.h" +#include "iceberg/transform.h" +#include "iceberg/type.h" +#include "iceberg/util/checked_cast.h" + +namespace iceberg { + +class ProjectionsTest : public ::testing::Test { + protected: + void SetUp() override { + // Create a simple test schema with various field types + schema_ = std::make_shared( + std::vector{SchemaField::MakeOptional(16, "id", int64())}, + /*schema_id=*/0); + } + + std::shared_ptr schema_; +}; + +// Helper function to extract UnboundPredicate from expression +std::shared_ptr ExtractUnboundPredicate( + const std::shared_ptr& expr) { + if (expr->is_unbound_predicate()) { + return std::dynamic_pointer_cast(expr); + } + return nullptr; +} + +// Helper function to extract BoundPredicate from expression +std::shared_ptr ExtractBoundPredicate( + const std::shared_ptr& expr) { + if (expr->is_bound_predicate()) { + return std::dynamic_pointer_cast(expr); + } + return nullptr; +} + +// Helper function to assert projection operation +void AssertProjectionOperation(const std::shared_ptr& projection, + Expression::Operation expected_op) { + ASSERT_NE(projection, nullptr); + EXPECT_EQ(projection->op(), expected_op); +} + +// Helper function to assert projection value for True/False +void AssertProjectionValue(const std::shared_ptr& projection, + Expression::Operation expected_op) { + ASSERT_NE(projection, nullptr); + EXPECT_EQ(projection->op(), expected_op); +} + +TEST_F(ProjectionsTest, IdentityProjectionInclusive) { + auto identity_transform = Transform::Identity(); + PartitionField pt_field(16, 1000, "id", identity_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + std::vector> predicates = { + Expressions::NotNull("id"), + Expressions::IsNull("id"), + Expressions::LessThan("id", Literal::Long(100)), + Expressions::LessThanOrEqual("id", Literal::Long(101)), + Expressions::GreaterThan("id", Literal::Long(102)), + Expressions::GreaterThanOrEqual("id", Literal::Long(103)), + Expressions::Equal("id", Literal::Long(104)), + Expressions::NotEqual("id", Literal::Long(105)), + }; + + for (const auto& predicate : predicates) { + // Bind the predicate first + ICEBERG_UNWRAP_OR_FAIL(auto bound_pred, predicate->Bind(*schema_, true)); + auto bound = ExtractBoundPredicate(bound_pred); + ASSERT_NE(bound, nullptr); + + // Project the bound predicate + auto evaluator = Projections::Inclusive(spec_sptr, schema_, true); + ICEBERG_UNWRAP_OR_FAIL(auto projected_expr, evaluator->Project(bound_pred)); + + // Check that we got a predicate back + auto projected = ExtractUnboundPredicate(projected_expr); + ASSERT_NE(projected, nullptr); + + // Check that the operation matches + EXPECT_EQ(projected->op(), bound->op()); + + // Check that the field name matches + EXPECT_EQ(projected->reference()->name(), "id"); + + if (bound->kind() == BoundPredicate::Kind::kLiteral) { + const auto& literal_predicate = + internal::checked_pointer_cast>(projected); + const auto& bound_literal_predicate = + internal::checked_pointer_cast(bound); + EXPECT_EQ(literal_predicate->literals().front(), + bound_literal_predicate->literal()); + } + } +} + +TEST_F(ProjectionsTest, IdentityProjectionStrict) { + auto identity_transform = Transform::Identity(); + PartitionField pt_field(16, 1000, "id", identity_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + std::vector> predicates = { + Expressions::NotNull("id"), + Expressions::IsNull("id"), + Expressions::LessThan("id", Literal::Long(100)), + Expressions::LessThanOrEqual("id", Literal::Long(101)), + Expressions::GreaterThan("id", Literal::Long(102)), + Expressions::GreaterThanOrEqual("id", Literal::Long(103)), + Expressions::Equal("id", Literal::Long(104)), + Expressions::NotEqual("id", Literal::Long(105)), + }; + + for (const auto& predicate : predicates) { + // Bind the predicate first + ICEBERG_UNWRAP_OR_FAIL(auto bound_pred, predicate->Bind(*schema_, true)); + auto bound = ExtractBoundPredicate(bound_pred); + ASSERT_NE(bound, nullptr); + + // Project the bound predicate + auto evaluator = Projections::Strict(spec_sptr, schema_, true); + ICEBERG_UNWRAP_OR_FAIL(auto projected_expr, evaluator->Project(bound_pred)); + + // Check that we got a predicate back + auto projected = ExtractUnboundPredicate(projected_expr); + ASSERT_NE(projected, nullptr); + + // Check that the operation matches + EXPECT_EQ(projected->op(), bound->op()); + + // Check that the field name matches + EXPECT_EQ(projected->reference()->name(), "id"); + + if (bound->kind() == BoundPredicate::Kind::kLiteral) { + const auto& literal_predicate = + internal::checked_pointer_cast>(projected); + const auto& bound_literal_predicate = + internal::checked_pointer_cast(bound); + EXPECT_EQ(literal_predicate->literals().front(), + bound_literal_predicate->literal()); + } + } +} + +TEST_F(ProjectionsTest, CaseInsensitiveIdentityProjection) { + auto identity_transform = Transform::Identity(); + PartitionField pt_field(16, 1000, "id", identity_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + std::vector> predicates = { + Expressions::NotNull("ID"), + Expressions::IsNull("ID"), + Expressions::LessThan("ID", Literal::Long(100)), + Expressions::LessThanOrEqual("ID", Literal::Long(101)), + Expressions::GreaterThan("ID", Literal::Long(102)), + Expressions::GreaterThanOrEqual("ID", Literal::Long(103)), + Expressions::Equal("ID", Literal::Long(104)), + Expressions::NotEqual("ID", Literal::Long(105)), + }; + + for (const auto& predicate : predicates) { + // Bind the predicate first (case insensitive) + ICEBERG_UNWRAP_OR_FAIL(auto bound_pred, predicate->Bind(*schema_, false)); + auto bound = ExtractBoundPredicate(bound_pred); + ASSERT_NE(bound, nullptr); + + // Project the bound predicate (case insensitive) + auto evaluator = Projections::Inclusive(spec_sptr, schema_, false); + ICEBERG_UNWRAP_OR_FAIL(auto projected_expr, evaluator->Project(bound_pred)); + + // Check that we got a predicate back + auto projected = ExtractUnboundPredicate(projected_expr); + ASSERT_NE(projected, nullptr); + + // Check that the operation matches + EXPECT_EQ(projected->op(), bound->op()); + + // Check that the field name matches + EXPECT_EQ(projected->reference()->name(), "id"); + + if (bound->kind() == BoundPredicate::Kind::kLiteral) { + const auto& literal_predicate = + internal::checked_pointer_cast>(projected); + const auto& bound_literal_predicate = + internal::checked_pointer_cast(bound); + EXPECT_EQ(literal_predicate->literals().front(), + bound_literal_predicate->literal()); + } + } +} + +TEST_F(ProjectionsTest, CaseSensitiveIdentityProjectionFailure) { + auto identity_transform = Transform::Identity(); + PartitionField pt_field(16, 1000, "id", identity_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + + auto predicate = Expressions::NotNull("ID"); + // Binding should fail with case sensitive + auto bound_result = predicate->Bind(*schema_, true); + EXPECT_THAT(bound_result, IsError(ErrorKind::kInvalidExpression)); +} + +// Bucketing projection tests +class BucketingProjectionTest : public ::testing::Test { + protected: + void AssertProjectionStrict(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op, + const std::string& expected_literal) { + auto evaluator = Projections::Strict(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + + if (expected_op != Expression::Operation::kFalse) { + auto predicate = ExtractUnboundPredicate(projection); + ASSERT_NE(predicate, nullptr); + if (predicate->op() == Expression::Operation::kNotIn) { + // For NOT_IN, check literals + const auto& literal_predicate = + internal::checked_pointer_cast>( + predicate); + auto literals = literal_predicate->literals(); + std::vector values; + for (const auto& lit : literals) { + values.push_back(std::to_string(std::get(lit.value()))); + } + std::ranges::sort(values); + std::string actual = "["; + for (size_t i = 0; i < values.size(); ++i) { + if (i > 0) actual += ", "; + actual += values[i]; + } + actual += "]"; + EXPECT_EQ(actual, expected_literal); + } else { + // For other operations, check single literal + const auto& literal_predicate = + internal::checked_pointer_cast>( + predicate); + auto literal = literal_predicate->literals().front(); + std::string output = std::to_string(std::get(literal.value())); + EXPECT_EQ(output, expected_literal); + } + } + } + + void AssertProjectionStrictValue(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Strict(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionValue(projection, expected_op); + } + + void AssertProjectionInclusive(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op, + const std::string& expected_literal) { + auto evaluator = Projections::Inclusive(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + + if (expected_op != Expression::Operation::kTrue) { + auto predicate = ExtractUnboundPredicate(projection); + ASSERT_NE(predicate, nullptr); + if (predicate->op() == Expression::Operation::kIn) { + // For IN, check literals + const auto& literal_predicate = + internal::checked_pointer_cast>( + predicate); + auto literals = literal_predicate->literals(); + std::vector values; + for (const auto& lit : literals) { + values.push_back(std::to_string(std::get(lit.value()))); + } + std::ranges::sort(values); + std::string actual = "["; + for (size_t i = 0; i < values.size(); ++i) { + if (i > 0) actual += ", "; + actual += values[i]; + } + actual += "]"; + EXPECT_EQ(actual, expected_literal); + } else { + // For other operations, check single literal + const auto& literal_predicate = + internal::checked_pointer_cast>( + predicate); + auto literal = literal_predicate->literals().front(); + std::string output = std::to_string(std::get(literal.value())); + EXPECT_EQ(output, expected_literal); + } + } + } + + void AssertProjectionInclusiveValue(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Inclusive(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionValue(projection, expected_op); + } +}; + +TEST_F(BucketingProjectionTest, BucketIntegerStrict) { + int32_t value = 100; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int32())}, 0); + auto bucket_transform = Transform::Bucket(10); + PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + // Bind predicates first + auto not_equal_pred = Expressions::NotEqual("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + auto less_than_pred = Expressions::LessThan("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto less_equal_pred = Expressions::LessThanOrEqual("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_equal, less_equal_pred->Bind(*schema, true)); + + auto greater_than_pred = Expressions::GreaterThan("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_than, greater_than_pred->Bind(*schema, true)); + + auto greater_equal_pred = Expressions::GreaterThanOrEqual("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_equal, + greater_equal_pred->Bind(*schema, true)); + + // The bucket number of 100 with 10 buckets is 6 + AssertProjectionStrict(spec_sptr, schema, bound_not_equal, + Expression::Operation::kNotEq, "6"); + AssertProjectionStrictValue(spec_sptr, schema, bound_equal, + Expression::Operation::kFalse); + AssertProjectionStrictValue(spec_sptr, schema, bound_less_than, + Expression::Operation::kFalse); + AssertProjectionStrictValue(spec_sptr, schema, bound_less_equal, + Expression::Operation::kFalse); + AssertProjectionStrictValue(spec_sptr, schema, bound_greater_than, + Expression::Operation::kFalse); + AssertProjectionStrictValue(spec_sptr, schema, bound_greater_equal, + Expression::Operation::kFalse); +} + +TEST_F(BucketingProjectionTest, BucketIntegerInclusive) { + int32_t value = 100; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int32())}, 0); + auto bucket_transform = Transform::Bucket(10); + PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + // Bind predicates first + auto equal_pred = Expressions::Equal("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + auto not_equal_pred = Expressions::NotEqual("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); + + auto less_than_pred = Expressions::LessThan("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto less_equal_pred = Expressions::LessThanOrEqual("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_equal, less_equal_pred->Bind(*schema, true)); + + auto greater_than_pred = Expressions::GreaterThan("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_than, greater_than_pred->Bind(*schema, true)); + + auto greater_equal_pred = Expressions::GreaterThanOrEqual("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_equal, + greater_equal_pred->Bind(*schema, true)); + + // The bucket number of 100 with 10 buckets is 6 + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq, + "6"); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_not_equal, + Expression::Operation::kTrue); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_less_than, + Expression::Operation::kTrue); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_less_equal, + Expression::Operation::kTrue); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_greater_than, + Expression::Operation::kTrue); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_greater_equal, + Expression::Operation::kTrue); +} + +TEST_F(BucketingProjectionTest, BucketLongStrict) { + int64_t value = 100L; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int64())}, 0); + auto bucket_transform = Transform::Bucket(10); + PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto not_equal_pred = Expressions::NotEqual("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + // The bucket number of 100 with 10 buckets is 6 + AssertProjectionStrict(spec_sptr, schema, bound_not_equal, + Expression::Operation::kNotEq, "6"); + AssertProjectionStrictValue(spec_sptr, schema, bound_equal, + Expression::Operation::kFalse); +} + +TEST_F(BucketingProjectionTest, BucketLongInclusive) { + int64_t value = 100L; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int64())}, 0); + auto bucket_transform = Transform::Bucket(10); + PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto equal_pred = Expressions::Equal("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + auto not_equal_pred = Expressions::NotEqual("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); + + // The bucket number of 100 with 10 buckets is 6 + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq, + "6"); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_not_equal, + Expression::Operation::kTrue); +} + +TEST_F(BucketingProjectionTest, BucketStringStrict) { + std::string value = "abcdefg"; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", string())}, 0); + auto bucket_transform = Transform::Bucket(10); + PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto not_equal_pred = Expressions::NotEqual("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + // The bucket number of "abcdefg" with 10 buckets is 4 + AssertProjectionStrict(spec_sptr, schema, bound_not_equal, + Expression::Operation::kNotEq, "4"); + AssertProjectionStrictValue(spec_sptr, schema, bound_equal, + Expression::Operation::kFalse); +} + +TEST_F(BucketingProjectionTest, BucketStringInclusive) { + std::string value = "abcdefg"; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", string())}, 0); + auto bucket_transform = Transform::Bucket(10); + PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto equal_pred = Expressions::Equal("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + auto not_equal_pred = Expressions::NotEqual("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); + + // The bucket number of "abcdefg" with 10 buckets is 4 + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq, + "4"); + AssertProjectionInclusiveValue(spec_sptr, schema, bound_not_equal, + Expression::Operation::kTrue); +} + +// Date projection tests +class DateProjectionTest : public ::testing::Test { + protected: + void AssertProjectionStrict(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Strict(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + } + + void AssertProjectionInclusive(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Inclusive(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + } +}; + +TEST_F(DateProjectionTest, DayStrict) { + int32_t date_value = + TemporalTestHelper::CreateDate({.year = 2017, .month = 1, .day = 1}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "date", date())}, 0); + auto day_transform = Transform::Day(); + PartitionField pt_field(1, 1000, "date_day", day_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto less_equal_pred = Expressions::LessThanOrEqual("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_equal, less_equal_pred->Bind(*schema, true)); + + auto greater_than_pred = Expressions::GreaterThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_than, greater_than_pred->Bind(*schema, true)); + + auto greater_equal_pred = + Expressions::GreaterThanOrEqual("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_equal, + greater_equal_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_less_equal, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_greater_than, + Expression::Operation::kGt); + AssertProjectionStrict(spec_sptr, schema, bound_greater_equal, + Expression::Operation::kGt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(DateProjectionTest, DayInclusive) { + int32_t date_value = + TemporalTestHelper::CreateDate({.year = 2017, .month = 1, .day = 1}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "date", date())}, 0); + auto day_transform = Transform::Day(); + PartitionField pt_field(1, 1000, "date_day", day_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto less_equal_pred = Expressions::LessThanOrEqual("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_equal, less_equal_pred->Bind(*schema, true)); + + auto greater_than_pred = Expressions::GreaterThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_than, greater_than_pred->Bind(*schema, true)); + + auto greater_equal_pred = + Expressions::GreaterThanOrEqual("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_greater_equal, + greater_equal_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_less_equal, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_greater_than, + Expression::Operation::kGtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_greater_equal, + Expression::Operation::kGtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(DateProjectionTest, MonthStrict) { + int32_t date_value = + TemporalTestHelper::CreateDate({.year = 2017, .month = 1, .day = 1}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "date", date())}, 0); + auto month_transform = Transform::Month(); + PartitionField pt_field(1, 1000, "date_month", month_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(DateProjectionTest, MonthInclusive) { + int32_t date_value = + TemporalTestHelper::CreateDate({.year = 2017, .month = 1, .day = 1}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "date", date())}, 0); + auto month_transform = Transform::Month(); + PartitionField pt_field(1, 1000, "date_month", month_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(DateProjectionTest, YearStrict) { + int32_t date_value = + TemporalTestHelper::CreateDate({.year = 2017, .month = 1, .day = 1}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "date", date())}, 0); + auto year_transform = Transform::Year(); + PartitionField pt_field(1, 1000, "date_year", year_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(DateProjectionTest, YearInclusive) { + int32_t date_value = + TemporalTestHelper::CreateDate({.year = 2017, .month = 1, .day = 1}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "date", date())}, 0); + auto year_transform = Transform::Year(); + PartitionField pt_field(1, 1000, "date_year", year_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +// Timestamp projection tests +class TimestampProjectionTest : public ::testing::Test { + protected: + void AssertProjectionStrict(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Strict(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + } + + void AssertProjectionInclusive(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Inclusive(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + } +}; + +TEST_F(TimestampProjectionTest, DayStrict) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 12, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto day_transform = Transform::Day(); + PartitionField pt_field(1, 1000, "timestamp_day", day_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TimestampProjectionTest, DayInclusive) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 12, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto day_transform = Transform::Day(); + PartitionField pt_field(1, 1000, "timestamp_day", day_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(TimestampProjectionTest, MonthStrict) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 12, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto month_transform = Transform::Month(); + PartitionField pt_field(1, 1000, "timestamp_month", month_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TimestampProjectionTest, MonthInclusive) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 12, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto month_transform = Transform::Month(); + PartitionField pt_field(1, 1000, "timestamp_month", month_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(TimestampProjectionTest, YearStrict) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto year_transform = Transform::Year(); + PartitionField pt_field(1, 1000, "timestamp_year", year_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TimestampProjectionTest, YearInclusive) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto year_transform = Transform::Year(); + PartitionField pt_field(1, 1000, "timestamp_year", year_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(TimestampProjectionTest, HourStrict) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 12, + .day = 1, + .hour = 10, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto hour_transform = Transform::Hour(); + PartitionField pt_field(1, 1000, "timestamp_hour", hour_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TimestampProjectionTest, HourInclusive) { + int64_t ts_value = TemporalTestHelper::CreateTimestamp({.year = 2017, + .month = 12, + .day = 1, + .hour = 10, + .minute = 0, + .second = 0, + .microsecond = 0}); + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "timestamp", timestamp())}, + 0); + auto hour_transform = Transform::Hour(); + PartitionField pt_field(1, 1000, "timestamp_hour", hour_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +// Truncate projection tests +class TruncateProjectionTest : public ::testing::Test { + protected: + void AssertProjectionStrict(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Strict(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + } + + void AssertProjectionInclusive(const std::shared_ptr& spec, + const std::shared_ptr& schema, + const std::shared_ptr& filter, + Expression::Operation expected_op) { + auto evaluator = Projections::Inclusive(spec, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + AssertProjectionOperation(projection, expected_op); + } +}; + +TEST_F(TruncateProjectionTest, IntegerStrict) { + int32_t value = 100; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int32())}, 0); + auto truncate_transform = Transform::Truncate(10); + PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TruncateProjectionTest, IntegerInclusive) { + int32_t value = 100; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int32())}, 0); + auto truncate_transform = Transform::Truncate(10); + PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::Int(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(TruncateProjectionTest, LongStrict) { + int64_t value = 100L; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int64())}, 0); + auto truncate_transform = Transform::Truncate(10); + PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TruncateProjectionTest, LongInclusive) { + int64_t value = 100L; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", int64())}, 0); + auto truncate_transform = Transform::Truncate(10); + PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::Long(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +TEST_F(TruncateProjectionTest, StringStrict) { + std::string value = "abcdefg"; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", string())}, 0); + auto truncate_transform = Transform::Truncate(5); + PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); +} + +TEST_F(TruncateProjectionTest, StringInclusive) { + std::string value = "abcdefg"; + auto schema = std::make_shared( + std::vector{SchemaField::MakeOptional(1, "value", string())}, 0); + auto truncate_transform = Transform::Truncate(5); + PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + auto less_than_pred = Expressions::LessThan("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); + + auto equal_pred = Expressions::Equal("value", Literal::String(value)); + ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); + + AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + Expression::Operation::kLtEq); + AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); +} + +// Complex expression tests +TEST_F(ProjectionsTest, ComplexExpressionWithOr) { + auto schema = std::make_shared( + std::vector{ + SchemaField::MakeRequired(1, "id", int64()), + SchemaField::MakeOptional(2, "data", string()), + SchemaField::MakeRequired(3, "hour", int32()), + SchemaField::MakeRequired(4, "dateint", int32()), + }, + 0); + + auto identity_transform = Transform::Identity(); + PartitionField pt_field(4, 1000, "dateint", identity_transform); + ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(*schema, 0, {pt_field}, false)); + auto spec_sptr = std::shared_ptr(std::move(spec)); + + // Create filter: dateint = 20180416 OR ((dateint = 20180415 AND hour >= 20) OR + // (dateint = 20180417 AND hour <= 4)) + auto dateint_eq1 = Expressions::Equal("dateint", Literal::Int(20180416)); + auto dateint_eq2 = Expressions::Equal("dateint", Literal::Int(20180415)); + auto hour_ge = Expressions::GreaterThanOrEqual("hour", Literal::Int(20)); + auto dateint_eq3 = Expressions::Equal("dateint", Literal::Int(20180417)); + auto hour_le = Expressions::LessThanOrEqual("hour", Literal::Int(4)); + + auto and1 = Expressions::And(dateint_eq2, hour_ge); + auto and2 = Expressions::And(dateint_eq3, hour_le); + auto or1 = Expressions::Or(and1, and2); + auto filter = Expressions::Or(dateint_eq1, or1); + + // Project + auto evaluator = Projections::Inclusive(spec_sptr, schema, true); + ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); + + // The projection should be an OR expression + // Non-partition predicates (hour) are removed, and AND expressions simplify + // Expected: dateint = 20180416 OR (dateint = 20180415 OR dateint = 20180417) + EXPECT_EQ(projection->op(), Expression::Operation::kOr); + + auto or_expr = internal::checked_pointer_cast(projection); + + // Left side: dateint = 20180416 + auto dateint1_expr = + std::dynamic_pointer_cast>(or_expr->left()); + EXPECT_EQ(dateint1_expr->reference()->name(), "dateint"); + EXPECT_EQ(dateint1_expr->op(), Expression::Operation::kEq); + EXPECT_EQ(dateint1_expr->literals().front(), Literal::Int(20180416)); + + // Right side: OR of the two dateint predicates (AND expressions simplified) + auto or1_expr = internal::checked_pointer_cast(or_expr->right()); + EXPECT_EQ(or1_expr->op(), Expression::Operation::kOr); + + // Left of inner OR: dateint = 20180415 (simplified from AND with hour >= 20) + auto dateint2_expr = + std::dynamic_pointer_cast>(or1_expr->left()); + EXPECT_EQ(dateint2_expr->reference()->name(), "dateint"); + EXPECT_EQ(dateint2_expr->op(), Expression::Operation::kEq); + EXPECT_EQ(dateint2_expr->literals().front(), Literal::Int(20180415)); + + // Right of inner OR: dateint = 20180417 (simplified from AND with hour <= 4) + auto dateint3_expr = + std::dynamic_pointer_cast>(or1_expr->right()); + EXPECT_EQ(dateint3_expr->reference()->name(), "dateint"); + EXPECT_EQ(dateint3_expr->op(), Expression::Operation::kEq); + EXPECT_EQ(dateint3_expr->literals().front(), Literal::Int(20180417)); +} + +} // namespace iceberg From 8929e8414def6c57054078576d50ef4a5055f413 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Mon, 8 Dec 2025 22:35:50 +0800 Subject: [PATCH 2/3] fix: review comment --- src/iceberg/expression/projections.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/iceberg/expression/projections.h b/src/iceberg/expression/projections.h index 0fbf10f1f..9bd93d553 100644 --- a/src/iceberg/expression/projections.h +++ b/src/iceberg/expression/projections.h @@ -24,9 +24,7 @@ #include -#include "iceberg/expression/expression.h" #include "iceberg/iceberg_export.h" -#include "iceberg/partition_spec.h" #include "iceberg/result.h" #include "iceberg/type_fwd.h" From f493eb469931702f9b35ee93bc21ca97632fef22 Mon Sep 17 00:00:00 2001 From: Junwang Zhao Date: Wed, 10 Dec 2025 23:34:11 +0800 Subject: [PATCH 3/3] fix: review comments --- src/iceberg/expression/projections.cc | 103 ++++--------- src/iceberg/expression/projections.h | 22 ++- src/iceberg/test/projections_test.cc | 214 ++++++++++---------------- 3 files changed, 125 insertions(+), 214 deletions(-) diff --git a/src/iceberg/expression/projections.cc b/src/iceberg/expression/projections.cc index 7616dff4d..dd83ef801 100644 --- a/src/iceberg/expression/projections.cc +++ b/src/iceberg/expression/projections.cc @@ -20,15 +20,11 @@ #include "iceberg/expression/projections.h" #include -#include #include "iceberg/expression/expression.h" #include "iceberg/expression/expression_visitor.h" -#include "iceberg/expression/expressions.h" #include "iceberg/expression/predicate.h" #include "iceberg/expression/rewrite_not.h" -#include "iceberg/expression/term.h" -#include "iceberg/partition_field.h" #include "iceberg/partition_spec.h" #include "iceberg/result.h" #include "iceberg/transform.h" @@ -36,13 +32,11 @@ namespace iceberg { -// Implementation detail - not exported class ProjectionVisitor : public ExpressionVisitor> { public: ~ProjectionVisitor() override = default; - ProjectionVisitor(const std::shared_ptr& spec, - const std::shared_ptr& schema, bool case_sensitive) + ProjectionVisitor(const PartitionSpec& spec, const Schema& schema, bool case_sensitive) : spec_(spec), schema_(schema), case_sensitive_(case_sensitive) {} Result> AlwaysTrue() override { return True::Instance(); } @@ -57,24 +51,20 @@ class ProjectionVisitor : public ExpressionVisitor> Result> And( const std::shared_ptr& left_result, const std::shared_ptr& right_result) override { - return Expressions::And(left_result, right_result); + return And::MakeFolded(left_result, right_result); } Result> Or( const std::shared_ptr& left_result, const std::shared_ptr& right_result) override { - return Expressions::Or(left_result, right_result); + return Or::MakeFolded(left_result, right_result); } Result> Predicate( const std::shared_ptr& pred) override { - ICEBERG_ASSIGN_OR_RAISE(auto bound_pred, pred->Bind(*schema_, case_sensitive_)); + ICEBERG_ASSIGN_OR_RAISE(auto bound_pred, pred->Bind(schema_, case_sensitive_)); if (bound_pred->is_bound_predicate()) { - auto bound_predicate = std::dynamic_pointer_cast(bound_pred); - ICEBERG_DCHECK( - bound_predicate != nullptr, - "Expected bound_predicate to be non-null after is_bound_predicate() check"); - return Predicate(bound_predicate); + return Predicate(std::dynamic_pointer_cast(bound_pred)); } return bound_pred; } @@ -85,38 +75,9 @@ class ProjectionVisitor : public ExpressionVisitor> } protected: - const std::shared_ptr& spec_; - const std::shared_ptr& schema_; + const PartitionSpec& spec_; + const Schema& schema_; bool case_sensitive_; - - /// \brief Get partition fields that match the predicate's term. - std::vector GetFieldsByPredicate( - const std::shared_ptr& pred) const { - int32_t source_id; - switch (pred->term()->kind()) { - case Term::Kind::kReference: { - const auto& ref = pred->term()->reference(); - source_id = ref->field().field_id(); - break; - } - case Term::Kind::kTransform: { - const auto& transform = - internal::checked_pointer_cast(pred->term()); - source_id = transform->reference()->field().field_id(); - break; - } - default: - std::unreachable(); - } - - std::vector result; - for (const auto& field : spec_->fields()) { - if (field.source_id() == source_id) { - result.push_back(&field); - } - } - return result; - } }; ProjectionEvaluator::ProjectionEvaluator(std::unique_ptr visitor) @@ -131,16 +92,17 @@ class InclusiveProjectionVisitor : public ProjectionVisitor { public: ~InclusiveProjectionVisitor() override = default; - InclusiveProjectionVisitor(const std::shared_ptr& spec, - const std::shared_ptr& schema, bool case_sensitive) + InclusiveProjectionVisitor(const PartitionSpec& spec, const Schema& schema, + bool case_sensitive) : ProjectionVisitor(spec, schema, case_sensitive) {} Result> Predicate( const std::shared_ptr& pred) override { ICEBERG_DCHECK(pred != nullptr, "Predicate cannot be null"); // Find partition fields that match the predicate's term - auto partition_fields = GetFieldsByPredicate(pred); - if (partition_fields.empty()) { + ICEBERG_ASSIGN_OR_RAISE( + auto parts, spec_.GetFieldsBySourceId(pred->reference()->field().field_id())); + if (parts.empty()) { // The predicate has no partition column return AlwaysTrue(); } @@ -155,19 +117,17 @@ class InclusiveProjectionVisitor : public ProjectionVisitor { // projection should be used. ts = 2019-01-01T01:00:00 produces day=2019-01-01 and // hour=2019-01-01-01. the value will be in 2019-01-01-01 and not in 2019-01-01-02. std::shared_ptr result = True::Instance(); - for (const auto* part_field : partition_fields) { + for (const auto& part : parts) { ICEBERG_ASSIGN_OR_RAISE(auto projected, - part_field->transform()->Project(part_field->name(), pred)); + part.get().transform()->Project(part.get().name(), pred)); if (projected != nullptr) { - result = - Expressions::And(result, std::shared_ptr(projected.release())); + ICEBERG_ASSIGN_OR_RAISE(result, + And::MakeFolded(std::move(result), std::move(projected))); } } return result; } - - protected: }; /// \brief Strict projection evaluator. @@ -177,16 +137,17 @@ class StrictProjectionVisitor : public ProjectionVisitor { public: ~StrictProjectionVisitor() override = default; - StrictProjectionVisitor(const std::shared_ptr& spec, - const std::shared_ptr& schema, bool case_sensitive) + StrictProjectionVisitor(const PartitionSpec& spec, const Schema& schema, + bool case_sensitive) : ProjectionVisitor(spec, schema, case_sensitive) {} Result> Predicate( const std::shared_ptr& pred) override { ICEBERG_DCHECK(pred != nullptr, "Predicate cannot be null"); // Find partition fields that match the predicate's term - auto partition_fields = GetFieldsByPredicate(pred); - if (partition_fields.empty()) { + ICEBERG_ASSIGN_OR_RAISE( + auto parts, spec_.GetFieldsBySourceId(pred->reference()->field().field_id())); + if (parts.empty()) { // The predicate has no matching partition columns return AlwaysFalse(); } @@ -199,12 +160,12 @@ class StrictProjectionVisitor : public ProjectionVisitor { // predicate. For example, ts = 2019-01-01T03:00:00 matches the hour projection but // not the day, but does match the original predicate. std::shared_ptr result = False::Instance(); - for (const auto* part_field : partition_fields) { - ICEBERG_ASSIGN_OR_RAISE(auto projected, part_field->transform()->ProjectStrict( - part_field->name(), pred)); + for (const auto& part : parts) { + ICEBERG_ASSIGN_OR_RAISE( + auto projected, part.get().transform()->ProjectStrict(part.get().name(), pred)); if (projected != nullptr) { - result = - Expressions::Or(result, std::shared_ptr(projected.release())); + ICEBERG_ASSIGN_OR_RAISE(result, + Or::MakeFolded(std::move(result), std::move(projected))); } } @@ -224,18 +185,18 @@ Result> ProjectionEvaluator::Project( return Visit, ProjectionVisitor>(rewritten, *visitor_); } -std::unique_ptr Projections::Inclusive( - const std::shared_ptr& spec, const std::shared_ptr& schema, - bool case_sensitive) { +std::unique_ptr Projections::Inclusive(const PartitionSpec& spec, + const Schema& schema, + bool case_sensitive) { auto visitor = std::make_unique(spec, schema, case_sensitive); return std::unique_ptr( new ProjectionEvaluator(std::move(visitor))); } -std::unique_ptr Projections::Strict( - const std::shared_ptr& spec, const std::shared_ptr& schema, - bool case_sensitive) { +std::unique_ptr Projections::Strict(const PartitionSpec& spec, + const Schema& schema, + bool case_sensitive) { auto visitor = std::make_unique(spec, schema, case_sensitive); return std::unique_ptr( new ProjectionEvaluator(std::move(visitor))); diff --git a/src/iceberg/expression/projections.h b/src/iceberg/expression/projections.h index 9bd93d553..b2022c4f1 100644 --- a/src/iceberg/expression/projections.h +++ b/src/iceberg/expression/projections.h @@ -50,7 +50,7 @@ class ICEBERG_EXPORT ProjectionEvaluator { /// \param visitor The projection visitor to use explicit ProjectionEvaluator(std::unique_ptr visitor); - std::unique_ptr visitor_; + std::unique_ptr visitor_; }; /// \brief Utils to project expressions on rows to expressions on partitions. @@ -62,8 +62,7 @@ class ICEBERG_EXPORT ProjectionEvaluator { /// /// A strict projection guarantees that if a partition matches a projected expression, /// then all rows in that partition will match the original expression. -class ICEBERG_EXPORT Projections { - public: +struct ICEBERG_EXPORT Projections { /// \brief Creates an inclusive ProjectionEvaluator for the partition spec. /// /// An evaluator is used to project expressions for a table's data rows into expressions @@ -75,12 +74,13 @@ class ICEBERG_EXPORT Projections { /// Each predicate in the expression is projected using Transform::Project. /// /// \param spec a partition spec + /// \param schema a schema /// \param case_sensitive whether the Projection should consider case sensitivity on /// column names or not. Defaults to true (case sensitive). /// \return an inclusive projection evaluator for the partition spec - static std::unique_ptr Inclusive( - const std::shared_ptr& spec, const std::shared_ptr& schema, - bool case_sensitive = true); + static std::unique_ptr Inclusive(const PartitionSpec& spec, + const Schema& schema, + bool case_sensitive = true); /// \brief Creates a strict ProjectionEvaluator for the partition spec. /// @@ -93,15 +93,13 @@ class ICEBERG_EXPORT Projections { /// Each predicate in the expression is projected using Transform::ProjectStrict. /// /// \param spec a partition spec + /// \param schema a schema /// \param case_sensitive whether the Projection should consider case sensitivity on /// column names or not. Defaults to true (case sensitive). /// \return a strict projection evaluator for the partition spec - static std::unique_ptr Strict( - const std::shared_ptr& spec, const std::shared_ptr& schema, - bool case_sensitive = true); - - private: - Projections() = default; + static std::unique_ptr Strict(const PartitionSpec& spec, + const Schema& schema, + bool case_sensitive = true); }; } // namespace iceberg diff --git a/src/iceberg/test/projections_test.cc b/src/iceberg/test/projections_test.cc index 8890bdce3..475a18da6 100644 --- a/src/iceberg/test/projections_test.cc +++ b/src/iceberg/test/projections_test.cc @@ -87,7 +87,6 @@ TEST_F(ProjectionsTest, IdentityProjectionInclusive) { auto identity_transform = Transform::Identity(); PartitionField pt_field(16, 1000, "id", identity_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); std::vector> predicates = { Expressions::NotNull("id"), @@ -107,7 +106,7 @@ TEST_F(ProjectionsTest, IdentityProjectionInclusive) { ASSERT_NE(bound, nullptr); // Project the bound predicate - auto evaluator = Projections::Inclusive(spec_sptr, schema_, true); + auto evaluator = Projections::Inclusive(*spec, *schema_, true); ICEBERG_UNWRAP_OR_FAIL(auto projected_expr, evaluator->Project(bound_pred)); // Check that we got a predicate back @@ -135,7 +134,6 @@ TEST_F(ProjectionsTest, IdentityProjectionStrict) { auto identity_transform = Transform::Identity(); PartitionField pt_field(16, 1000, "id", identity_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); std::vector> predicates = { Expressions::NotNull("id"), @@ -155,7 +153,7 @@ TEST_F(ProjectionsTest, IdentityProjectionStrict) { ASSERT_NE(bound, nullptr); // Project the bound predicate - auto evaluator = Projections::Strict(spec_sptr, schema_, true); + auto evaluator = Projections::Strict(*spec, *schema_, true); ICEBERG_UNWRAP_OR_FAIL(auto projected_expr, evaluator->Project(bound_pred)); // Check that we got a predicate back @@ -183,7 +181,6 @@ TEST_F(ProjectionsTest, CaseInsensitiveIdentityProjection) { auto identity_transform = Transform::Identity(); PartitionField pt_field(16, 1000, "id", identity_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); std::vector> predicates = { Expressions::NotNull("ID"), @@ -203,7 +200,7 @@ TEST_F(ProjectionsTest, CaseInsensitiveIdentityProjection) { ASSERT_NE(bound, nullptr); // Project the bound predicate (case insensitive) - auto evaluator = Projections::Inclusive(spec_sptr, schema_, false); + auto evaluator = Projections::Inclusive(*spec, *schema_, false); ICEBERG_UNWRAP_OR_FAIL(auto projected_expr, evaluator->Project(bound_pred)); // Check that we got a predicate back @@ -241,8 +238,7 @@ TEST_F(ProjectionsTest, CaseSensitiveIdentityProjectionFailure) { // Bucketing projection tests class BucketingProjectionTest : public ::testing::Test { protected: - void AssertProjectionStrict(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionStrict(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op, const std::string& expected_literal) { @@ -283,8 +279,7 @@ class BucketingProjectionTest : public ::testing::Test { } } - void AssertProjectionStrictValue(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionStrictValue(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Strict(spec, schema, true); @@ -292,8 +287,7 @@ class BucketingProjectionTest : public ::testing::Test { AssertProjectionValue(projection, expected_op); } - void AssertProjectionInclusive(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionInclusive(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op, const std::string& expected_literal) { @@ -334,8 +328,7 @@ class BucketingProjectionTest : public ::testing::Test { } } - void AssertProjectionInclusiveValue(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionInclusiveValue(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Inclusive(spec, schema, true); @@ -351,7 +344,6 @@ TEST_F(BucketingProjectionTest, BucketIntegerStrict) { auto bucket_transform = Transform::Bucket(10); PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); // Bind predicates first auto not_equal_pred = Expressions::NotEqual("value", Literal::Int(value)); @@ -374,17 +366,16 @@ TEST_F(BucketingProjectionTest, BucketIntegerStrict) { greater_equal_pred->Bind(*schema, true)); // The bucket number of 100 with 10 buckets is 6 - AssertProjectionStrict(spec_sptr, schema, bound_not_equal, - Expression::Operation::kNotEq, "6"); - AssertProjectionStrictValue(spec_sptr, schema, bound_equal, + AssertProjectionStrict(*spec, *schema, bound_not_equal, Expression::Operation::kNotEq, + "6"); + AssertProjectionStrictValue(*spec, *schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrictValue(*spec, *schema, bound_less_than, Expression::Operation::kFalse); - AssertProjectionStrictValue(spec_sptr, schema, bound_less_than, + AssertProjectionStrictValue(*spec, *schema, bound_less_equal, Expression::Operation::kFalse); - AssertProjectionStrictValue(spec_sptr, schema, bound_less_equal, + AssertProjectionStrictValue(*spec, *schema, bound_greater_than, Expression::Operation::kFalse); - AssertProjectionStrictValue(spec_sptr, schema, bound_greater_than, - Expression::Operation::kFalse); - AssertProjectionStrictValue(spec_sptr, schema, bound_greater_equal, + AssertProjectionStrictValue(*spec, *schema, bound_greater_equal, Expression::Operation::kFalse); } @@ -395,7 +386,6 @@ TEST_F(BucketingProjectionTest, BucketIntegerInclusive) { auto bucket_transform = Transform::Bucket(10); PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); // Bind predicates first auto equal_pred = Expressions::Equal("value", Literal::Int(value)); @@ -418,17 +408,16 @@ TEST_F(BucketingProjectionTest, BucketIntegerInclusive) { greater_equal_pred->Bind(*schema, true)); // The bucket number of 100 with 10 buckets is 6 - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq, - "6"); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_not_equal, + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq, "6"); + AssertProjectionInclusiveValue(*spec, *schema, bound_not_equal, Expression::Operation::kTrue); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_less_than, + AssertProjectionInclusiveValue(*spec, *schema, bound_less_than, Expression::Operation::kTrue); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_less_equal, + AssertProjectionInclusiveValue(*spec, *schema, bound_less_equal, Expression::Operation::kTrue); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_greater_than, + AssertProjectionInclusiveValue(*spec, *schema, bound_greater_than, Expression::Operation::kTrue); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_greater_equal, + AssertProjectionInclusiveValue(*spec, *schema, bound_greater_equal, Expression::Operation::kTrue); } @@ -439,7 +428,6 @@ TEST_F(BucketingProjectionTest, BucketLongStrict) { auto bucket_transform = Transform::Bucket(10); PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto not_equal_pred = Expressions::NotEqual("value", Literal::Long(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); @@ -448,10 +436,9 @@ TEST_F(BucketingProjectionTest, BucketLongStrict) { ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); // The bucket number of 100 with 10 buckets is 6 - AssertProjectionStrict(spec_sptr, schema, bound_not_equal, - Expression::Operation::kNotEq, "6"); - AssertProjectionStrictValue(spec_sptr, schema, bound_equal, - Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_not_equal, Expression::Operation::kNotEq, + "6"); + AssertProjectionStrictValue(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(BucketingProjectionTest, BucketLongInclusive) { @@ -461,7 +448,6 @@ TEST_F(BucketingProjectionTest, BucketLongInclusive) { auto bucket_transform = Transform::Bucket(10); PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto equal_pred = Expressions::Equal("value", Literal::Long(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); @@ -470,9 +456,8 @@ TEST_F(BucketingProjectionTest, BucketLongInclusive) { ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); // The bucket number of 100 with 10 buckets is 6 - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq, - "6"); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_not_equal, + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq, "6"); + AssertProjectionInclusiveValue(*spec, *schema, bound_not_equal, Expression::Operation::kTrue); } @@ -483,7 +468,6 @@ TEST_F(BucketingProjectionTest, BucketStringStrict) { auto bucket_transform = Transform::Bucket(10); PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto not_equal_pred = Expressions::NotEqual("value", Literal::String(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); @@ -492,10 +476,9 @@ TEST_F(BucketingProjectionTest, BucketStringStrict) { ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); // The bucket number of "abcdefg" with 10 buckets is 4 - AssertProjectionStrict(spec_sptr, schema, bound_not_equal, - Expression::Operation::kNotEq, "4"); - AssertProjectionStrictValue(spec_sptr, schema, bound_equal, - Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_not_equal, Expression::Operation::kNotEq, + "4"); + AssertProjectionStrictValue(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(BucketingProjectionTest, BucketStringInclusive) { @@ -505,7 +488,6 @@ TEST_F(BucketingProjectionTest, BucketStringInclusive) { auto bucket_transform = Transform::Bucket(10); PartitionField pt_field(1, 1000, "value_bucket", bucket_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto equal_pred = Expressions::Equal("value", Literal::String(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); @@ -514,17 +496,15 @@ TEST_F(BucketingProjectionTest, BucketStringInclusive) { ICEBERG_UNWRAP_OR_FAIL(auto bound_not_equal, not_equal_pred->Bind(*schema, true)); // The bucket number of "abcdefg" with 10 buckets is 4 - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq, - "4"); - AssertProjectionInclusiveValue(spec_sptr, schema, bound_not_equal, + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq, "4"); + AssertProjectionInclusiveValue(*spec, *schema, bound_not_equal, Expression::Operation::kTrue); } // Date projection tests class DateProjectionTest : public ::testing::Test { protected: - void AssertProjectionStrict(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionStrict(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Strict(spec, schema, true); @@ -532,8 +512,7 @@ class DateProjectionTest : public ::testing::Test { AssertProjectionOperation(projection, expected_op); } - void AssertProjectionInclusive(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionInclusive(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Inclusive(spec, schema, true); @@ -550,7 +529,6 @@ TEST_F(DateProjectionTest, DayStrict) { auto day_transform = Transform::Day(); PartitionField pt_field(1, 1000, "date_day", day_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -569,13 +547,11 @@ TEST_F(DateProjectionTest, DayStrict) { auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_less_equal, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_greater_than, - Expression::Operation::kGt); - AssertProjectionStrict(spec_sptr, schema, bound_greater_equal, - Expression::Operation::kGt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_less_equal, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_greater_than, Expression::Operation::kGt); + AssertProjectionStrict(*spec, *schema, bound_greater_equal, Expression::Operation::kGt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(DateProjectionTest, DayInclusive) { @@ -586,7 +562,6 @@ TEST_F(DateProjectionTest, DayInclusive) { auto day_transform = Transform::Day(); PartitionField pt_field(1, 1000, "date_day", day_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -605,15 +580,15 @@ TEST_F(DateProjectionTest, DayInclusive) { auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_less_equal, + AssertProjectionInclusive(*spec, *schema, bound_less_equal, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_greater_than, + AssertProjectionInclusive(*spec, *schema, bound_greater_than, Expression::Operation::kGtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_greater_equal, + AssertProjectionInclusive(*spec, *schema, bound_greater_equal, Expression::Operation::kGtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(DateProjectionTest, MonthStrict) { @@ -624,7 +599,6 @@ TEST_F(DateProjectionTest, MonthStrict) { auto month_transform = Transform::Month(); PartitionField pt_field(1, 1000, "date_month", month_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -632,8 +606,8 @@ TEST_F(DateProjectionTest, MonthStrict) { auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(DateProjectionTest, MonthInclusive) { @@ -644,7 +618,6 @@ TEST_F(DateProjectionTest, MonthInclusive) { auto month_transform = Transform::Month(); PartitionField pt_field(1, 1000, "date_month", month_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -652,9 +625,9 @@ TEST_F(DateProjectionTest, MonthInclusive) { auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(DateProjectionTest, YearStrict) { @@ -665,7 +638,6 @@ TEST_F(DateProjectionTest, YearStrict) { auto year_transform = Transform::Year(); PartitionField pt_field(1, 1000, "date_year", year_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -673,8 +645,8 @@ TEST_F(DateProjectionTest, YearStrict) { auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(DateProjectionTest, YearInclusive) { @@ -685,7 +657,6 @@ TEST_F(DateProjectionTest, YearInclusive) { auto year_transform = Transform::Year(); PartitionField pt_field(1, 1000, "date_year", year_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -693,16 +664,15 @@ TEST_F(DateProjectionTest, YearInclusive) { auto equal_pred = Expressions::Equal("date", Literal::Date(date_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } // Timestamp projection tests class TimestampProjectionTest : public ::testing::Test { protected: - void AssertProjectionStrict(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionStrict(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Strict(spec, schema, true); @@ -710,8 +680,7 @@ class TimestampProjectionTest : public ::testing::Test { AssertProjectionOperation(projection, expected_op); } - void AssertProjectionInclusive(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionInclusive(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Inclusive(spec, schema, true); @@ -734,7 +703,6 @@ TEST_F(TimestampProjectionTest, DayStrict) { auto day_transform = Transform::Day(); PartitionField pt_field(1, 1000, "timestamp_day", day_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -742,8 +710,8 @@ TEST_F(TimestampProjectionTest, DayStrict) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TimestampProjectionTest, DayInclusive) { @@ -760,7 +728,6 @@ TEST_F(TimestampProjectionTest, DayInclusive) { auto day_transform = Transform::Day(); PartitionField pt_field(1, 1000, "timestamp_day", day_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -768,9 +735,9 @@ TEST_F(TimestampProjectionTest, DayInclusive) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(TimestampProjectionTest, MonthStrict) { @@ -787,7 +754,6 @@ TEST_F(TimestampProjectionTest, MonthStrict) { auto month_transform = Transform::Month(); PartitionField pt_field(1, 1000, "timestamp_month", month_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -795,8 +761,8 @@ TEST_F(TimestampProjectionTest, MonthStrict) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TimestampProjectionTest, MonthInclusive) { @@ -813,7 +779,6 @@ TEST_F(TimestampProjectionTest, MonthInclusive) { auto month_transform = Transform::Month(); PartitionField pt_field(1, 1000, "timestamp_month", month_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -821,9 +786,9 @@ TEST_F(TimestampProjectionTest, MonthInclusive) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(TimestampProjectionTest, YearStrict) { @@ -840,7 +805,6 @@ TEST_F(TimestampProjectionTest, YearStrict) { auto year_transform = Transform::Year(); PartitionField pt_field(1, 1000, "timestamp_year", year_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -848,8 +812,8 @@ TEST_F(TimestampProjectionTest, YearStrict) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TimestampProjectionTest, YearInclusive) { @@ -866,7 +830,6 @@ TEST_F(TimestampProjectionTest, YearInclusive) { auto year_transform = Transform::Year(); PartitionField pt_field(1, 1000, "timestamp_year", year_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -874,9 +837,9 @@ TEST_F(TimestampProjectionTest, YearInclusive) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(TimestampProjectionTest, HourStrict) { @@ -893,7 +856,6 @@ TEST_F(TimestampProjectionTest, HourStrict) { auto hour_transform = Transform::Hour(); PartitionField pt_field(1, 1000, "timestamp_hour", hour_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -901,8 +863,8 @@ TEST_F(TimestampProjectionTest, HourStrict) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TimestampProjectionTest, HourInclusive) { @@ -919,7 +881,6 @@ TEST_F(TimestampProjectionTest, HourInclusive) { auto hour_transform = Transform::Hour(); PartitionField pt_field(1, 1000, "timestamp_hour", hour_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -927,16 +888,15 @@ TEST_F(TimestampProjectionTest, HourInclusive) { auto equal_pred = Expressions::Equal("timestamp", Literal::Timestamp(ts_value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } // Truncate projection tests class TruncateProjectionTest : public ::testing::Test { protected: - void AssertProjectionStrict(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionStrict(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Strict(spec, schema, true); @@ -944,8 +904,7 @@ class TruncateProjectionTest : public ::testing::Test { AssertProjectionOperation(projection, expected_op); } - void AssertProjectionInclusive(const std::shared_ptr& spec, - const std::shared_ptr& schema, + void AssertProjectionInclusive(const PartitionSpec& spec, const Schema& schema, const std::shared_ptr& filter, Expression::Operation expected_op) { auto evaluator = Projections::Inclusive(spec, schema, true); @@ -961,7 +920,6 @@ TEST_F(TruncateProjectionTest, IntegerStrict) { auto truncate_transform = Transform::Truncate(10); PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("value", Literal::Int(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -969,8 +927,8 @@ TEST_F(TruncateProjectionTest, IntegerStrict) { auto equal_pred = Expressions::Equal("value", Literal::Int(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TruncateProjectionTest, IntegerInclusive) { @@ -980,7 +938,6 @@ TEST_F(TruncateProjectionTest, IntegerInclusive) { auto truncate_transform = Transform::Truncate(10); PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("value", Literal::Int(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -988,9 +945,9 @@ TEST_F(TruncateProjectionTest, IntegerInclusive) { auto equal_pred = Expressions::Equal("value", Literal::Int(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(TruncateProjectionTest, LongStrict) { @@ -1000,7 +957,6 @@ TEST_F(TruncateProjectionTest, LongStrict) { auto truncate_transform = Transform::Truncate(10); PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("value", Literal::Long(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -1008,8 +964,8 @@ TEST_F(TruncateProjectionTest, LongStrict) { auto equal_pred = Expressions::Equal("value", Literal::Long(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TruncateProjectionTest, LongInclusive) { @@ -1019,7 +975,6 @@ TEST_F(TruncateProjectionTest, LongInclusive) { auto truncate_transform = Transform::Truncate(10); PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("value", Literal::Long(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -1027,9 +982,9 @@ TEST_F(TruncateProjectionTest, LongInclusive) { auto equal_pred = Expressions::Equal("value", Literal::Long(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } TEST_F(TruncateProjectionTest, StringStrict) { @@ -1039,7 +994,6 @@ TEST_F(TruncateProjectionTest, StringStrict) { auto truncate_transform = Transform::Truncate(5); PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("value", Literal::String(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -1047,8 +1001,8 @@ TEST_F(TruncateProjectionTest, StringStrict) { auto equal_pred = Expressions::Equal("value", Literal::String(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionStrict(spec_sptr, schema, bound_less_than, Expression::Operation::kLt); - AssertProjectionStrict(spec_sptr, schema, bound_equal, Expression::Operation::kFalse); + AssertProjectionStrict(*spec, *schema, bound_less_than, Expression::Operation::kLt); + AssertProjectionStrict(*spec, *schema, bound_equal, Expression::Operation::kFalse); } TEST_F(TruncateProjectionTest, StringInclusive) { @@ -1058,7 +1012,6 @@ TEST_F(TruncateProjectionTest, StringInclusive) { auto truncate_transform = Transform::Truncate(5); PartitionField pt_field(1, 1000, "value_trunc", truncate_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(0, {pt_field})); - auto spec_sptr = std::shared_ptr(std::move(spec)); auto less_than_pred = Expressions::LessThan("value", Literal::String(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_less_than, less_than_pred->Bind(*schema, true)); @@ -1066,9 +1019,9 @@ TEST_F(TruncateProjectionTest, StringInclusive) { auto equal_pred = Expressions::Equal("value", Literal::String(value)); ICEBERG_UNWRAP_OR_FAIL(auto bound_equal, equal_pred->Bind(*schema, true)); - AssertProjectionInclusive(spec_sptr, schema, bound_less_than, + AssertProjectionInclusive(*spec, *schema, bound_less_than, Expression::Operation::kLtEq); - AssertProjectionInclusive(spec_sptr, schema, bound_equal, Expression::Operation::kEq); + AssertProjectionInclusive(*spec, *schema, bound_equal, Expression::Operation::kEq); } // Complex expression tests @@ -1085,7 +1038,6 @@ TEST_F(ProjectionsTest, ComplexExpressionWithOr) { auto identity_transform = Transform::Identity(); PartitionField pt_field(4, 1000, "dateint", identity_transform); ICEBERG_UNWRAP_OR_FAIL(auto spec, PartitionSpec::Make(*schema, 0, {pt_field}, false)); - auto spec_sptr = std::shared_ptr(std::move(spec)); // Create filter: dateint = 20180416 OR ((dateint = 20180415 AND hour >= 20) OR // (dateint = 20180417 AND hour <= 4)) @@ -1101,7 +1053,7 @@ TEST_F(ProjectionsTest, ComplexExpressionWithOr) { auto filter = Expressions::Or(dateint_eq1, or1); // Project - auto evaluator = Projections::Inclusive(spec_sptr, schema, true); + auto evaluator = Projections::Inclusive(*spec, *schema, true); ICEBERG_UNWRAP_OR_FAIL(auto projection, evaluator->Project(filter)); // The projection should be an OR expression