Skip to content

Commit 0f5088e

Browse files
authored
Merge branch 'apache:main' into enable_static_when_test
2 parents 46e61bb + b59fb81 commit 0f5088e

File tree

12 files changed

+1169
-15
lines changed

12 files changed

+1169
-15
lines changed

cmake_modules/IcebergBuildUtils.cmake

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ function(add_iceberg_lib LIB_NAME)
152152

153153
string(TOUPPER ${LIB_NAME} VISIBILITY_NAME)
154154
target_compile_definitions(${LIB_NAME}_shared PRIVATE ${VISIBILITY_NAME}_EXPORTING)
155+
set_target_properties(${LIB_NAME}_shared
156+
PROPERTIES C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET
157+
hidden
158+
VISIBILITY_INLINES_HIDDEN 1)
155159

156160
install(TARGETS ${LIB_NAME}_shared
157161
EXPORT iceberg_targets
@@ -211,8 +215,10 @@ function(add_iceberg_lib LIB_NAME)
211215
target_link_libraries(${LIB_NAME}_static
212216
PUBLIC "$<BUILD_INTERFACE:iceberg_sanitizer_flags>")
213217

214-
string(TOUPPER ${LIB_NAME} VISIBILITY_NAME)
215-
target_compile_definitions(${LIB_NAME}_static PUBLIC ${VISIBILITY_NAME}_STATIC)
218+
if(WIN32)
219+
string(TOUPPER ${LIB_NAME} VISIBILITY_NAME)
220+
target_compile_definitions(${LIB_NAME}_static PUBLIC ${VISIBILITY_NAME}_STATIC)
221+
endif()
216222

217223
install(TARGETS ${LIB_NAME}_static
218224
EXPORT iceberg_targets

src/iceberg/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ set(ICEBERG_SOURCES
2121
arrow_c_data_guard_internal.cc
2222
catalog/memory/in_memory_catalog.cc
2323
expression/binder.cc
24+
expression/evaluator.cc
2425
expression/expression.cc
2526
expression/expressions.cc
2627
expression/literal.cc
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#include "iceberg/expression/evaluator.h"
21+
22+
#include "iceberg/expression/binder.h"
23+
#include "iceberg/expression/expression_visitor.h"
24+
#include "iceberg/schema.h"
25+
#include "iceberg/util/macros.h"
26+
27+
namespace iceberg {
28+
29+
class EvalVisitor : public BoundVisitor<bool> {
30+
public:
31+
explicit EvalVisitor(const StructLike& row) : row_(row) {}
32+
33+
Result<bool> AlwaysTrue() override { return true; }
34+
35+
Result<bool> AlwaysFalse() override { return false; }
36+
37+
Result<bool> Not(bool child_result) override { return !child_result; }
38+
39+
Result<bool> And(bool left_result, bool right_result) override {
40+
return left_result && right_result;
41+
}
42+
43+
Result<bool> Or(bool left_result, bool right_result) override {
44+
return left_result || right_result;
45+
}
46+
47+
Result<bool> IsNull(const std::shared_ptr<BoundTerm>& term) override {
48+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
49+
return value.IsNull();
50+
}
51+
52+
Result<bool> NotNull(const std::shared_ptr<BoundTerm>& term) override {
53+
ICEBERG_ASSIGN_OR_RAISE(auto value, IsNull(term));
54+
return !value;
55+
}
56+
57+
Result<bool> IsNaN(const std::shared_ptr<BoundTerm>& term) override {
58+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
59+
return value.IsNaN();
60+
}
61+
62+
Result<bool> NotNaN(const std::shared_ptr<BoundTerm>& term) override {
63+
ICEBERG_ASSIGN_OR_RAISE(auto value, IsNaN(term));
64+
return !value;
65+
}
66+
67+
Result<bool> Lt(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
68+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
69+
return value < lit;
70+
}
71+
72+
Result<bool> LtEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
73+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
74+
return value <= lit;
75+
}
76+
77+
Result<bool> Gt(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
78+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
79+
return value > lit;
80+
}
81+
82+
Result<bool> GtEq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
83+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
84+
return value >= lit;
85+
}
86+
87+
Result<bool> Eq(const std::shared_ptr<BoundTerm>& term, const Literal& lit) override {
88+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
89+
return value == lit;
90+
}
91+
92+
Result<bool> NotEq(const std::shared_ptr<BoundTerm>& term,
93+
const Literal& lit) override {
94+
ICEBERG_ASSIGN_OR_RAISE(auto eq_result, Eq(term, lit));
95+
return !eq_result;
96+
}
97+
98+
Result<bool> In(const std::shared_ptr<BoundTerm>& term,
99+
const BoundSetPredicate::LiteralSet& literal_set) override {
100+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
101+
return literal_set.contains(value);
102+
}
103+
104+
Result<bool> NotIn(const std::shared_ptr<BoundTerm>& term,
105+
const BoundSetPredicate::LiteralSet& literal_set) override {
106+
ICEBERG_ASSIGN_OR_RAISE(auto in_result, In(term, literal_set));
107+
return !in_result;
108+
}
109+
110+
Result<bool> StartsWith(const std::shared_ptr<BoundTerm>& term,
111+
const Literal& lit) override {
112+
ICEBERG_ASSIGN_OR_RAISE(auto value, term->Evaluate(row_));
113+
114+
// Both value and literal should be strings
115+
if (!std::holds_alternative<std::string>(value.value()) ||
116+
!std::holds_alternative<std::string>(lit.value())) {
117+
return false;
118+
}
119+
120+
const auto& str_value = std::get<std::string>(value.value());
121+
const auto& str_prefix = std::get<std::string>(lit.value());
122+
return str_value.starts_with(str_prefix);
123+
}
124+
125+
Result<bool> NotStartsWith(const std::shared_ptr<BoundTerm>& term,
126+
const Literal& lit) override {
127+
ICEBERG_ASSIGN_OR_RAISE(auto starts_result, StartsWith(term, lit));
128+
return !starts_result;
129+
}
130+
131+
private:
132+
const StructLike& row_;
133+
};
134+
135+
Evaluator::Evaluator(std::shared_ptr<Expression> bound_expr)
136+
: bound_expr_(std::move(bound_expr)) {}
137+
138+
Evaluator::~Evaluator() = default;
139+
140+
Result<std::unique_ptr<Evaluator>> Evaluator::Make(const Schema& schema,
141+
std::shared_ptr<Expression> unbound,
142+
bool case_sensitive) {
143+
ICEBERG_ASSIGN_OR_RAISE(auto bound_expr, Binder::Bind(schema, unbound, case_sensitive));
144+
return std::unique_ptr<Evaluator>(new Evaluator(std::move(bound_expr)));
145+
}
146+
147+
Result<bool> Evaluator::Eval(const StructLike& row) const {
148+
EvalVisitor visitor(row);
149+
return Visit<bool, EvalVisitor>(bound_expr_, visitor);
150+
}
151+
152+
} // namespace iceberg

src/iceberg/expression/evaluator.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
/// \file iceberg/expression/evaluator.h
23+
/// Evaluator for checking if a data row matches a bound expression.
24+
25+
#include <memory>
26+
27+
#include "iceberg/iceberg_export.h"
28+
#include "iceberg/result.h"
29+
#include "iceberg/type_fwd.h"
30+
31+
namespace iceberg {
32+
33+
/// \brief Evaluates an Expression against data rows.
34+
///
35+
/// This class evaluates bound expressions against StructLike data rows to determine
36+
/// if the row matches the expression criteria. The evaluator binds unbound expressions
37+
/// to a schema on construction and then can be used to evaluate multiple data rows.
38+
///
39+
/// \note: The evaluator is thread-safe.
40+
class ICEBERG_EXPORT Evaluator {
41+
public:
42+
/// \brief Make an evaluator for an unbound expression.
43+
///
44+
/// \param schema The schema to bind against
45+
/// \param unbound The unbound expression to evaluate
46+
/// \param case_sensitive Whether field name matching is case-sensitive
47+
static Result<std::unique_ptr<Evaluator>> Make(const Schema& schema,
48+
std::shared_ptr<Expression> unbound,
49+
bool case_sensitive = true);
50+
51+
~Evaluator();
52+
53+
/// \brief Evaluate the expression against a data row.
54+
///
55+
/// \param row The data row to evaluate
56+
/// \return true if the row matches the expression, false otherwise, or error
57+
Result<bool> Eval(const StructLike& row) const;
58+
59+
private:
60+
explicit Evaluator(std::shared_ptr<Expression> bound_expr);
61+
62+
std::shared_ptr<Expression> bound_expr_;
63+
};
64+
65+
} // namespace iceberg

src/iceberg/expression/literal.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,11 @@ bool Literal::IsAboveMax() const { return std::holds_alternative<AboveMax>(value
504504

505505
bool Literal::IsNull() const { return std::holds_alternative<std::monostate>(value_); }
506506

507+
bool Literal::IsNaN() const {
508+
return std::holds_alternative<float>(value_) && std::isnan(std::get<float>(value_)) ||
509+
std::holds_alternative<double>(value_) && std::isnan(std::get<double>(value_));
510+
}
511+
507512
// LiteralCaster implementation
508513

509514
Result<Literal> LiteralCaster::CastTo(const Literal& literal,

src/iceberg/expression/literal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ class ICEBERG_EXPORT Literal : public util::Formattable {
154154
/// \return true if this literal is null, false otherwise
155155
bool IsNull() const;
156156

157+
/// Check if this literal is NaN.
158+
/// \return true if this literal is NaN, false otherwise
159+
bool IsNaN() const;
160+
157161
std::string ToString() const override;
158162

159163
private:

src/iceberg/expression/predicate.cc

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ Result<std::unique_ptr<UnboundPredicateImpl<B>>> UnboundPredicateImpl<B>::Make(
5050
if (!term) [[unlikely]] {
5151
return InvalidExpression("UnboundPredicate cannot have null term");
5252
}
53+
if (op == Expression::Operation::kIn || op == Expression::Operation::kNotIn) {
54+
return InvalidExpression("Cannot create {} predicate without a value",
55+
::iceberg::ToString(op));
56+
}
5357
return std::unique_ptr<UnboundPredicateImpl<B>>(
5458
new UnboundPredicateImpl<B>(op, std::move(term)));
5559
}
@@ -71,6 +75,11 @@ Result<std::unique_ptr<UnboundPredicateImpl<B>>> UnboundPredicateImpl<B>::Make(
7175
if (!term) [[unlikely]] {
7276
return InvalidExpression("UnboundPredicate cannot have null term");
7377
}
78+
if (values.empty() &&
79+
(op == Expression::Operation::kIn || op == Expression::Operation::kNotIn)) {
80+
return InvalidExpression("Cannot create {} predicate without a value",
81+
::iceberg::ToString(op));
82+
}
7483
return std::unique_ptr<UnboundPredicateImpl<B>>(
7584
new UnboundPredicateImpl<B>(op, std::move(term), std::move(values)));
7685
}
@@ -183,16 +192,6 @@ bool IsFloatingType(TypeId type) {
183192
return type == TypeId::kFloat || type == TypeId::kDouble;
184193
}
185194

186-
bool IsNan(const Literal& literal) {
187-
const auto& value = literal.value();
188-
if (std::holds_alternative<float>(value)) {
189-
return std::isnan(std::get<float>(value));
190-
} else if (std::holds_alternative<double>(value)) {
191-
return std::isnan(std::get<double>(value));
192-
}
193-
return false;
194-
}
195-
196195
bool StartsWith(const Literal& lhs, const Literal& rhs) {
197196
const auto& lhs_value = lhs.value();
198197
const auto& rhs_value = rhs.value();
@@ -383,9 +382,9 @@ Result<bool> BoundUnaryPredicate::Test(const Literal& literal) const {
383382
case Expression::Operation::kNotNull:
384383
return !literal.IsNull();
385384
case Expression::Operation::kIsNan:
386-
return IsNan(literal);
385+
return literal.IsNaN();
387386
case Expression::Operation::kNotNan:
388-
return !IsNan(literal);
387+
return !literal.IsNaN();
389388
default:
390389
return InvalidExpression("Invalid operation for BoundUnaryPredicate: {}", op());
391390
}

src/iceberg/expression/predicate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class ICEBERG_EXPORT UnboundPredicateImpl : public UnboundPredicate,
133133

134134
Result<std::shared_ptr<Expression>> Negate() const override;
135135

136+
std::span<const Literal> literals() const { return values_; }
137+
136138
private:
137139
UnboundPredicateImpl(Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term);
138140
UnboundPredicateImpl(Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term,

src/iceberg/expression/term.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ NamedReference::~NamedReference() = default;
5959
Result<std::shared_ptr<BoundReference>> NamedReference::Bind(const Schema& schema,
6060
bool case_sensitive) const {
6161
ICEBERG_ASSIGN_OR_RAISE(auto field_opt,
62-
schema.GetFieldByName(field_name_, case_sensitive));
62+
schema.FindFieldByName(field_name_, case_sensitive));
6363
if (!field_opt.has_value()) [[unlikely]] {
6464
return InvalidExpression("Cannot find field '{}' in struct: {}", field_name_,
6565
schema.ToString());

src/iceberg/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ iceberg_sources = files(
4343
'arrow_c_data_guard_internal.cc',
4444
'catalog/memory/in_memory_catalog.cc',
4545
'expression/binder.cc',
46+
'expression/evaluator.cc',
4647
'expression/expression.cc',
4748
'expression/expressions.cc',
4849
'expression/literal.cc',

0 commit comments

Comments
 (0)