Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/iceberg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(ICEBERG_INCLUDES "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/src>"
set(ICEBERG_SOURCES
arrow_c_data_internal.cc
demo.cc
expression.cc
schema.cc
schema_field.cc
schema_internal.cc
Expand Down
2 changes: 2 additions & 0 deletions src/iceberg/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ enum class ErrorKind {
kNotImplemented,
kUnknownError,
kNotSupported,
kInvalidExpression,
kInvalidOperatorType,
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated
};

/// \brief Error with a kind and a message.
Expand Down
129 changes: 129 additions & 0 deletions src/iceberg/expression.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* 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.h"

#include <unordered_map>

namespace iceberg {

expected<Expression::Operation, Error> Expression::FromString(
const std::string& operation_type) {
std::string lowercase_op = operation_type;
std::transform(lowercase_op.begin(), lowercase_op.end(), lowercase_op.begin(),
[](unsigned char c) { return std::tolower(c); });

static const std::unordered_map<std::string, Operation> op_map = {
{"true", Operation::kTrue},
{"false", Operation::kFalse},
{"is_null", Operation::kIsNull},
{"not_null", Operation::kNotNull},
{"is_nan", Operation::kIsNan},
{"not_nan", Operation::kNotNan},
{"lt", Operation::kLt},
{"lt_eq", Operation::kLtEq},
{"gt", Operation::kGt},
{"gt_eq", Operation::kGtEq},
{"eq", Operation::kEq},
{"not_eq", Operation::kNotEq},
{"in", Operation::kIn},
{"not_in", Operation::kNotIn},
{"not", Operation::kNot},
{"and", Operation::kAnd},
{"or", Operation::kOr},
{"starts_with", Operation::kStartsWith},
{"not_starts_with", Operation::kNotStartsWith},
{"count", Operation::kCount},
{"count_star", Operation::kCountStar},
{"max", Operation::kMax},
{"min", Operation::kMin}};

auto it = op_map.find(lowercase_op);
if (it == op_map.end()) {
return unexpected<Error>(
{ErrorKind::kInvalidOperatorType, "Unknown operation type: " + operation_type});
}
return it->second;
}

expected<Expression::Operation, Error> Expression::Negate(Operation op) {
switch (op) {
case Operation::kTrue:
return Operation::kFalse;
case Operation::kFalse:
return Operation::kTrue;
case Operation::kIsNull:
return Operation::kNotNull;
case Operation::kNotNull:
return Operation::kIsNull;
case Operation::kIsNan:
return Operation::kNotNan;
case Operation::kNotNan:
return Operation::kIsNan;
case Operation::kLt:
return Operation::kGtEq;
case Operation::kLtEq:
return Operation::kGt;
case Operation::kGt:
return Operation::kLtEq;
case Operation::kGtEq:
return Operation::kLt;
case Operation::kEq:
return Operation::kNotEq;
case Operation::kNotEq:
return Operation::kEq;
case Operation::kIn:
return Operation::kNotIn;
case Operation::kNotIn:
return Operation::kIn;
case Operation::kStartsWith:
return Operation::kNotStartsWith;
case Operation::kNotStartsWith:
return Operation::kStartsWith;
default:
return unexpected<Error>(
{ErrorKind::kInvalidOperatorType, "No negation defined for operation"});
}
}

expected<Expression::Operation, Error> Expression::FlipLR(Operation op) {
switch (op) {
case Operation::kLt:
return Operation::kGt;
case Operation::kLtEq:
return Operation::kGtEq;
case Operation::kGt:
return Operation::kLt;
case Operation::kGtEq:
return Operation::kLtEq;
case Operation::kEq:
return Operation::kEq;
case Operation::kNotEq:
return Operation::kNotEq;
case Operation::kAnd:
return Operation::kAnd;
case Operation::kOr:
return Operation::kOr;
default:
return unexpected<Error>(
{ErrorKind::kInvalidOperatorType, "No left-right flip for operation"});
}
}

} // namespace iceberg
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated
101 changes: 101 additions & 0 deletions src/iceberg/expression.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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.h
/// Boolean expression tree for Iceberg table operations.

#include <string>

#include "iceberg/error.h"
#include "iceberg/expected.h"
#include "iceberg/iceberg_export.h"

namespace iceberg {

/// \brief Represents a boolean expression tree.
class ICEBERG_EXPORT Expression {
Comment thread
yingcai-cy marked this conversation as resolved.
public:
/// Operation types for expressions
enum class Operation {
Comment thread
yingcai-cy marked this conversation as resolved.
kTrue,
kFalse,
kIsNull,
kNotNull,
kIsNan,
kNotNan,
kLt,
kLtEq,
kGt,
kGtEq,
kEq,
kNotEq,
kIn,
kNotIn,
kNot,
kAnd,
kOr,
kStartsWith,
kNotStartsWith,
kCount,
kCountStar,
kMax,
kMin
Comment thread
Fokko marked this conversation as resolved.
};

virtual ~Expression() = default;

/// \brief Returns the operation for an expression node.
[[nodiscard]] virtual Operation Op() const = 0;
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated

/// \brief Returns the negation of this expression, equivalent to not(this).
[[nodiscard]] virtual expected<std::shared_ptr<Expression>, Error> Negate() const {
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated
return unexpected<Error>(
{ErrorKind::kInvalidExpression, "This expression cannot be negated"});
}

/// \brief Returns whether this expression will accept the same values as another.
///
/// If this returns true, the expressions are guaranteed to return the same evaluation
/// for the same input. However, if this returns false the expressions may return the
/// same evaluation for the same input. That is, expressions may be equivalent even if
/// this returns false.
///
/// For best results, rewrite not and bind expressions before calling this method.
///
/// \param other another expression
/// \return true if the expressions are equivalent
[[nodiscard]] virtual bool IsEquivalentTo(const Expression& other) const {
// only bound predicates can be equivalent
return false;
}

/// \brief Convert operation string to enum
static expected<Operation, Error> FromString(const std::string& operation_type);
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated
Comment thread
yingcai-cy marked this conversation as resolved.
Outdated

/// \brief Returns the operation used when this is negated.
static expected<Operation, Error> Negate(Operation op);

/// \brief Returns the equivalent operation when the left and right operands are
/// exchanged.
static expected<Operation, Error> FlipLR(Operation op);
};

} // namespace iceberg
5 changes: 5 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ target_sources(expected_test PRIVATE expected_test.cc)
target_link_libraries(expected_test PRIVATE iceberg_static GTest::gtest_main GTest::gmock)
add_test(NAME expected_test COMMAND expected_test)

add_executable(expression_test)
Comment thread
yingcai-cy marked this conversation as resolved.
target_sources(expression_test PRIVATE expression_test.cc)
target_link_libraries(expression_test PRIVATE iceberg_static GTest::gtest_main GTest::gmock)
add_test(NAME expression_test COMMAND expression_test)

if(ICEBERG_BUILD_BUNDLE)
add_executable(avro_test)
target_sources(avro_test PRIVATE avro_test.cc)
Expand Down
Loading