Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
457 changes: 396 additions & 61 deletions src/iceberg/expression/json_serde.cc

Large diffs are not rendered by default.

75 changes: 72 additions & 3 deletions src/iceberg/expression/json_serde_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ ICEBERG_EXPORT Result<Expression::Operation> OperationTypeFromJson(
///
/// \param op The operation to convert
/// \return The operation type string (e.g., "eq", "lt-eq", "is-null")
ICEBERG_EXPORT nlohmann::json ToJson(Expression::Operation op);
ICEBERG_EXPORT Result<nlohmann::json> ToJson(Expression::Operation op);

/// \brief Deserializes a JSON object into an Expression.
///
Expand All @@ -54,8 +54,77 @@ ICEBERG_EXPORT Result<std::shared_ptr<Expression>> ExpressionFromJson(
/// \brief Serializes an Expression into its JSON representation.
///
/// \param expr The expression to serialize
/// \return A JSON object representing the expression
ICEBERG_EXPORT nlohmann::json ToJson(const Expression& expr);
/// \return A JSON object representing the expression, or an error
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const Expression& expr);

/// \brief Deserializes a JSON object into a NamedReference.
///
/// \param json A JSON object representing a named reference
/// \return A shared pointer to the deserialized NamedReference or an error
ICEBERG_EXPORT Result<std::unique_ptr<NamedReference>> NamedReferenceFromJson(
const nlohmann::json& json);

/// \brief Serializes a NamedReference into its JSON representation.
///
/// \param ref The named reference to serialize
/// \return A JSON object representing the named reference, or an error
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const NamedReference& ref);

/// \brief Serializes an UnboundTransform into its JSON representation.
///
/// \param transform The unbound transform to serialize
/// \return A JSON object representing the unbound transform, or an error
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const UnboundTransform& transform);

/// \brief Deserializes a JSON object into an UnboundTransform.
///
/// \param json A JSON object representing an unbound transform
/// \return A shared pointer to the deserialized UnboundTransform or an error
ICEBERG_EXPORT Result<std::unique_ptr<UnboundTransform>> UnboundTransformFromJson(
const nlohmann::json& json);

/// \brief Serializes a Literal into its JSON representation.
///
/// \param literal The literal to serialize
/// \return A JSON value representing the literal, or an error
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const Literal& literal);

/// \brief Deserializes a JSON value into a Literal.
///
/// \param json A JSON value representing a literal. There is a discrepency with the java
/// implementation. In java, a schema is taken as parameter even though the value passed
/// is always null. It could come in handy if we decide to do binding a deserialization
/// time.
/// \return The deserialized Literal or an error.
ICEBERG_EXPORT Result<Literal> LiteralFromJson(const nlohmann::json& json);

/// \brief Serializes an UnboundPredicate into its JSON representation.
///
/// \param pred The unbound predicate to serialize
/// \return A JSON object representing the predicate, or an error
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const UnboundPredicate& pred);

/// \brief Serializes a BoundReference into its JSON representation (field name string).
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const BoundReference& ref);

/// \brief Serializes a BoundTransform into its JSON representation.
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const BoundTransform& transform);

/// \brief Serializes a BoundPredicate into its JSON representation.
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const BoundPredicate& pred);

/// \brief Deserializes a JSON object into an UnboundPredicate.
///
/// \param json A JSON object representing an unbound predicate
/// \return A pointer to the deserialized UnboundPredicate or an error
ICEBERG_EXPORT Result<std::unique_ptr<UnboundPredicate>> UnboundPredicateFromJson(
const nlohmann::json& json);

/// \brief Serializes a Term into its JSON representation.
///
/// \param term The term to serialize (NamedReference or UnboundTransform)
/// \return A JSON value representing the term, or an error
ICEBERG_EXPORT Result<nlohmann::json> ToJson(const Term& term);

/// Check if an operation is a unary predicate
ICEBERG_EXPORT bool IsUnaryOperation(Expression::Operation op);
Expand Down
68 changes: 62 additions & 6 deletions src/iceberg/expression/literal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,46 @@
#include <concepts>
#include <cstdint>
#include <string>
#include <vector>

#include "iceberg/type.h"
#include "iceberg/util/checked_cast.h"
#include "iceberg/util/conversions.h"
#include "iceberg/util/decimal.h"
#include "iceberg/util/macros.h"
#include "iceberg/util/temporal_util.h"
#include "iceberg/util/transform_util.h"

namespace iceberg {

namespace {
Result<std::vector<uint8_t>> HexStringToBytes(std::string_view hex) {
if (hex.length() % 2 != 0) {
return InvalidArgument("Hex string must have an even length");
}

std::vector<uint8_t> bytes;
bytes.reserve(hex.length() / 2);

auto to_nibble = [](char c) -> uint8_t {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
throw std::invalid_argument("Invalid hex character");
};

for (size_t i = 0; i < hex.length(); i += 2) {
try {
bytes.push_back(
static_cast<uint8_t>((to_nibble(hex[i]) << 4) | to_nibble(hex[i + 1])));
} catch (const std::invalid_argument& e) {
return InvalidArgument("Invalid hex character in string: {}", e.what());
}
}
return bytes;
}
} // namespace

/// \brief LiteralCaster handles type casting operations for Literal.
/// This is an internal implementation class.
class LiteralCaster {
Expand Down Expand Up @@ -193,12 +225,36 @@ Result<Literal> LiteralCaster::CastFromString(
ICEBERG_ASSIGN_OR_RAISE(auto uuid, Uuid::FromString(str_val));
return Literal::UUID(uuid);
}
case TypeId::kDate:
case TypeId::kTime:
case TypeId::kTimestamp:
case TypeId::kTimestampTz:
return NotImplemented("Cast from String to {} is not implemented yet",
target_type->ToString());
case TypeId::kDate: {
ICEBERG_ASSIGN_OR_RAISE(auto days, TransformUtil::ParseDay(str_val));
return Literal::Date(days);
}
case TypeId::kTime: {
ICEBERG_ASSIGN_OR_RAISE(auto micros, TransformUtil::ParseTime(str_val));
return Literal::Time(micros);
}
case TypeId::kTimestamp: {
ICEBERG_ASSIGN_OR_RAISE(auto micros, TransformUtil::ParseTimestamp(str_val));
return Literal::Timestamp(micros);
}
case TypeId::kTimestampTz: {
ICEBERG_ASSIGN_OR_RAISE(auto micros,
TransformUtil::ParseTimestampWithZone(str_val));
return Literal::TimestampTz(micros);
}
case TypeId::kBinary: {
ICEBERG_ASSIGN_OR_RAISE(auto bytes, HexStringToBytes(str_val));
return Literal::Binary(std::move(bytes));
}
case TypeId::kFixed: {
ICEBERG_ASSIGN_OR_RAISE(auto bytes, HexStringToBytes(str_val));
return Literal::Fixed(std::move(bytes));
}
case TypeId::kDecimal: {
const auto& dec_type = internal::checked_cast<const DecimalType&>(*target_type);
ICEBERG_ASSIGN_OR_RAISE(auto dec, Decimal::FromString(str_val));
return Literal::Decimal(dec.value(), dec_type.precision(), dec_type.scale());
}
default:
return NotSupported("Cast from String to {} is not supported",
target_type->ToString());
Expand Down
10 changes: 9 additions & 1 deletion src/iceberg/expression/predicate.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ class ICEBERG_EXPORT UnboundPredicate : public virtual Expression,

bool is_unbound_predicate() const override { return true; }

/// \brief Returns the term of this predicate as a base Term reference.
virtual const Term& predicate_term() const = 0;

/// \brief Returns the literals of this predicate.
virtual std::span<const Literal> literals() const = 0;

protected:
UnboundPredicate() = default;
};
Expand Down Expand Up @@ -130,7 +136,9 @@ class ICEBERG_EXPORT UnboundPredicateImpl : public UnboundPredicate,

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

std::span<const Literal> literals() const { return values_; }
const Term& predicate_term() const override { return *BASE::term(); }

std::span<const Literal> literals() const override { return values_; }

private:
UnboundPredicateImpl(Expression::Operation op, std::shared_ptr<UnboundTerm<B>> term);
Expand Down
Loading
Loading