Skip to content

Commit 2f0b716

Browse files
committed
add json serde and test
1 parent f177530 commit 2f0b716

File tree

4 files changed

+918
-0
lines changed

4 files changed

+918
-0
lines changed

src/iceberg/json_internal.cc

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,4 +1422,176 @@ nlohmann::json ToJson(const TableRequirement& requirement) {
14221422
return json;
14231423
}
14241424

1425+
Result<std::unique_ptr<TableUpdate>> TableUpdateFromJson(
1426+
const nlohmann::json& json, const std::shared_ptr<Schema>& schema) {
1427+
ICEBERG_ASSIGN_OR_RAISE(auto action, GetJsonValue<std::string>(json, kAction));
1428+
1429+
if (action == kActionAssignUUID) {
1430+
ICEBERG_ASSIGN_OR_RAISE(auto uuid, GetJsonValue<std::string>(json, kUUID));
1431+
return std::make_unique<table::AssignUUID>(std::move(uuid));
1432+
}
1433+
if (action == kActionUpgradeFormatVersion) {
1434+
ICEBERG_ASSIGN_OR_RAISE(auto format_version,
1435+
GetJsonValue<int8_t>(json, kFormatVersion));
1436+
return std::make_unique<table::UpgradeFormatVersion>(format_version);
1437+
}
1438+
if (action == kActionAddSchema) {
1439+
ICEBERG_ASSIGN_OR_RAISE(auto schema_json,
1440+
GetJsonValue<nlohmann::json>(json, kSchema));
1441+
ICEBERG_ASSIGN_OR_RAISE(auto parsed_schema, SchemaFromJson(schema_json));
1442+
ICEBERG_ASSIGN_OR_RAISE(auto last_column_id,
1443+
GetJsonValue<int32_t>(json, kLastColumnId));
1444+
return std::make_unique<table::AddSchema>(std::move(parsed_schema), last_column_id);
1445+
}
1446+
if (action == kActionSetCurrentSchema) {
1447+
ICEBERG_ASSIGN_OR_RAISE(auto schema_id, GetJsonValue<int32_t>(json, kSchemaId));
1448+
return std::make_unique<table::SetCurrentSchema>(schema_id);
1449+
}
1450+
if (action == kActionAddPartitionSpec) {
1451+
if (!schema) {
1452+
return NotSupported(
1453+
"Cannot parse AddPartitionSpec without schema context. Use the overload "
1454+
"that takes a schema parameter.");
1455+
}
1456+
ICEBERG_ASSIGN_OR_RAISE(auto spec_json, GetJsonValue<nlohmann::json>(json, kSpec));
1457+
ICEBERG_ASSIGN_OR_RAISE(auto spec_id_opt,
1458+
GetJsonValueOptional<int32_t>(spec_json, kSpecId));
1459+
ICEBERG_ASSIGN_OR_RAISE(
1460+
auto spec,
1461+
PartitionSpecFromJson(schema, spec_json,
1462+
spec_id_opt.value_or(PartitionSpec::kInitialSpecId)));
1463+
return std::make_unique<table::AddPartitionSpec>(std::move(spec));
1464+
}
1465+
if (action == kActionSetDefaultPartitionSpec) {
1466+
ICEBERG_ASSIGN_OR_RAISE(auto spec_id, GetJsonValue<int32_t>(json, kSpecId));
1467+
return std::make_unique<table::SetDefaultPartitionSpec>(spec_id);
1468+
}
1469+
if (action == kActionRemovePartitionSpecs) {
1470+
ICEBERG_ASSIGN_OR_RAISE(auto spec_ids,
1471+
GetJsonValue<std::vector<int32_t>>(json, kSpecIds));
1472+
return std::make_unique<table::RemovePartitionSpecs>(std::move(spec_ids));
1473+
}
1474+
if (action == kActionRemoveSchemas) {
1475+
ICEBERG_ASSIGN_OR_RAISE(auto schema_ids_vec,
1476+
GetJsonValue<std::vector<int32_t>>(json, kSchemaIds));
1477+
std::unordered_set<int32_t> schema_ids(schema_ids_vec.begin(), schema_ids_vec.end());
1478+
return std::make_unique<table::RemoveSchemas>(std::move(schema_ids));
1479+
}
1480+
if (action == kActionAddSortOrder) {
1481+
if (!schema) {
1482+
return NotSupported(
1483+
"Cannot parse AddSortOrder without schema context. Use the overload "
1484+
"that takes a schema parameter.");
1485+
}
1486+
ICEBERG_ASSIGN_OR_RAISE(auto sort_order_json,
1487+
GetJsonValue<nlohmann::json>(json, kSortOrder));
1488+
ICEBERG_ASSIGN_OR_RAISE(auto sort_order, SortOrderFromJson(sort_order_json, schema));
1489+
return std::make_unique<table::AddSortOrder>(std::move(sort_order));
1490+
}
1491+
if (action == kActionSetDefaultSortOrder) {
1492+
ICEBERG_ASSIGN_OR_RAISE(auto sort_order_id,
1493+
GetJsonValue<int32_t>(json, kSortOrderId));
1494+
return std::make_unique<table::SetDefaultSortOrder>(sort_order_id);
1495+
}
1496+
if (action == kActionAddSnapshot) {
1497+
ICEBERG_ASSIGN_OR_RAISE(auto snapshot_json,
1498+
GetJsonValue<nlohmann::json>(json, kSnapshot));
1499+
ICEBERG_ASSIGN_OR_RAISE(auto snapshot, SnapshotFromJson(snapshot_json));
1500+
return std::make_unique<table::AddSnapshot>(std::move(snapshot));
1501+
}
1502+
if (action == kActionRemoveSnapshots) {
1503+
ICEBERG_ASSIGN_OR_RAISE(auto snapshot_ids,
1504+
GetJsonValue<std::vector<int64_t>>(json, kSnapshotIds));
1505+
return std::make_unique<table::RemoveSnapshots>(std::move(snapshot_ids));
1506+
}
1507+
if (action == kActionRemoveSnapshotRef) {
1508+
ICEBERG_ASSIGN_OR_RAISE(auto ref_name, GetJsonValue<std::string>(json, kRefName));
1509+
return std::make_unique<table::RemoveSnapshotRef>(std::move(ref_name));
1510+
}
1511+
if (action == kActionSetSnapshotRef) {
1512+
ICEBERG_ASSIGN_OR_RAISE(auto ref_name, GetJsonValue<std::string>(json, kRefName));
1513+
ICEBERG_ASSIGN_OR_RAISE(auto snapshot_id, GetJsonValue<int64_t>(json, kSnapshotId));
1514+
ICEBERG_ASSIGN_OR_RAISE(
1515+
auto type,
1516+
GetJsonValue<std::string>(json, kType).and_then(SnapshotRefTypeFromString));
1517+
ICEBERG_ASSIGN_OR_RAISE(auto min_snapshots,
1518+
GetJsonValueOptional<int32_t>(json, kMinSnapshotsToKeep));
1519+
ICEBERG_ASSIGN_OR_RAISE(auto max_snapshot_age,
1520+
GetJsonValueOptional<int64_t>(json, kMaxSnapshotAgeMs));
1521+
ICEBERG_ASSIGN_OR_RAISE(auto max_ref_age,
1522+
GetJsonValueOptional<int64_t>(json, kMaxRefAgeMs));
1523+
return std::make_unique<table::SetSnapshotRef>(std::move(ref_name), snapshot_id, type,
1524+
min_snapshots, max_snapshot_age,
1525+
max_ref_age);
1526+
}
1527+
if (action == kActionSetProperties) {
1528+
using StringMap = std::unordered_map<std::string, std::string>;
1529+
ICEBERG_ASSIGN_OR_RAISE(auto updates, GetJsonValue<StringMap>(json, kUpdates));
1530+
return std::make_unique<table::SetProperties>(std::move(updates));
1531+
}
1532+
if (action == kActionRemoveProperties) {
1533+
ICEBERG_ASSIGN_OR_RAISE(auto removals_vec,
1534+
GetJsonValue<std::vector<std::string>>(json, kRemovals));
1535+
std::unordered_set<std::string> removals(removals_vec.begin(), removals_vec.end());
1536+
return std::make_unique<table::RemoveProperties>(std::move(removals));
1537+
}
1538+
if (action == kActionSetLocation) {
1539+
ICEBERG_ASSIGN_OR_RAISE(auto location, GetJsonValue<std::string>(json, kLocation));
1540+
return std::make_unique<table::SetLocation>(std::move(location));
1541+
}
1542+
1543+
return JsonParseError("Unknown table update action: {}", action);
1544+
}
1545+
1546+
Result<std::unique_ptr<TableUpdate>> TableUpdateFromJson(const nlohmann::json& json) {
1547+
return TableUpdateFromJson(json, nullptr);
1548+
}
1549+
1550+
Result<std::unique_ptr<TableRequirement>> TableRequirementFromJson(
1551+
const nlohmann::json& json) {
1552+
ICEBERG_ASSIGN_OR_RAISE(auto type, GetJsonValue<std::string>(json, kType));
1553+
1554+
if (type == kRequirementAssertDoesNotExist) {
1555+
return std::make_unique<table::AssertDoesNotExist>();
1556+
}
1557+
if (type == kRequirementAssertUUID) {
1558+
ICEBERG_ASSIGN_OR_RAISE(auto uuid, GetJsonValue<std::string>(json, kUUID));
1559+
return std::make_unique<table::AssertUUID>(std::move(uuid));
1560+
}
1561+
if (type == kRequirementAssertRefSnapshotID) {
1562+
ICEBERG_ASSIGN_OR_RAISE(auto ref_name, GetJsonValue<std::string>(json, kRefName));
1563+
ICEBERG_ASSIGN_OR_RAISE(auto snapshot_id_opt,
1564+
GetJsonValueOptional<int64_t>(json, kSnapshotId));
1565+
return std::make_unique<table::AssertRefSnapshotID>(std::move(ref_name),
1566+
snapshot_id_opt);
1567+
}
1568+
if (type == kRequirementAssertLastAssignedFieldId) {
1569+
ICEBERG_ASSIGN_OR_RAISE(auto last_assigned_field_id,
1570+
GetJsonValue<int32_t>(json, kLastAssignedFieldId));
1571+
return std::make_unique<table::AssertLastAssignedFieldId>(last_assigned_field_id);
1572+
}
1573+
if (type == kRequirementAssertCurrentSchemaID) {
1574+
ICEBERG_ASSIGN_OR_RAISE(auto schema_id,
1575+
GetJsonValue<int32_t>(json, kCurrentSchemaId));
1576+
return std::make_unique<table::AssertCurrentSchemaID>(schema_id);
1577+
}
1578+
if (type == kRequirementAssertLastAssignedPartitionId) {
1579+
ICEBERG_ASSIGN_OR_RAISE(auto last_assigned_partition_id,
1580+
GetJsonValue<int32_t>(json, kLastAssignedPartitionId));
1581+
return std::make_unique<table::AssertLastAssignedPartitionId>(
1582+
last_assigned_partition_id);
1583+
}
1584+
if (type == kRequirementAssertDefaultSpecID) {
1585+
ICEBERG_ASSIGN_OR_RAISE(auto spec_id, GetJsonValue<int32_t>(json, kDefaultSpecId));
1586+
return std::make_unique<table::AssertDefaultSpecID>(spec_id);
1587+
}
1588+
if (type == kRequirementAssertDefaultSortOrderID) {
1589+
ICEBERG_ASSIGN_OR_RAISE(auto sort_order_id,
1590+
GetJsonValue<int32_t>(json, kDefaultSortOrderId));
1591+
return std::make_unique<table::AssertDefaultSortOrderID>(sort_order_id);
1592+
}
1593+
1594+
return JsonParseError("Unknown table requirement type: {}", type);
1595+
}
1596+
14251597
} // namespace iceberg

src/iceberg/json_internal.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,10 +365,40 @@ ICEBERG_EXPORT Result<Namespace> NamespaceFromJson(const nlohmann::json& json);
365365
/// \return A JSON object representing the `TableUpdate`.
366366
ICEBERG_EXPORT nlohmann::json ToJson(const TableUpdate& update);
367367

368+
/// \brief Deserializes a JSON object into a `TableUpdate` object.
369+
///
370+
/// This function parses the provided JSON and creates a `TableUpdate` object.
371+
/// For updates that require schema context (AddPartitionSpec, AddSortOrder),
372+
/// use the overload that takes a schema parameter.
373+
///
374+
/// \param[in] json The JSON object representing a `TableUpdate`.
375+
/// \return A `TableUpdate` object or an error if the conversion fails.
376+
ICEBERG_EXPORT Result<std::unique_ptr<TableUpdate>> TableUpdateFromJson(
377+
const nlohmann::json& json);
378+
379+
/// \brief Deserializes a JSON object into a `TableUpdate` object with schema context.
380+
///
381+
/// This overload is required for updates like AddPartitionSpec and AddSortOrder
382+
/// that need a schema to properly validate field references.
383+
///
384+
/// \param[in] json The JSON object representing a `TableUpdate`.
385+
/// \param[in] schema The schema to use for validation (needed for AddPartitionSpec,
386+
/// AddSortOrder).
387+
/// \return A `TableUpdate` object or an error if the conversion fails.
388+
ICEBERG_EXPORT Result<std::unique_ptr<TableUpdate>> TableUpdateFromJson(
389+
const nlohmann::json& json, const std::shared_ptr<Schema>& schema);
390+
368391
/// \brief Serializes a `TableRequirement` object to JSON.
369392
///
370393
/// \param[in] requirement The `TableRequirement` object to be serialized.
371394
/// \return A JSON object representing the `TableRequirement`.
372395
ICEBERG_EXPORT nlohmann::json ToJson(const TableRequirement& requirement);
373396

397+
/// \brief Deserializes a JSON object into a `TableRequirement` object.
398+
///
399+
/// \param[in] json The JSON object representing a `TableRequirement`.
400+
/// \return A `TableRequirement` object or an error if the conversion fails.
401+
ICEBERG_EXPORT Result<std::unique_ptr<TableRequirement>> TableRequirementFromJson(
402+
const nlohmann::json& json);
403+
374404
} // namespace iceberg

src/iceberg/table_requirement.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ class ICEBERG_EXPORT AssertDoesNotExist : public TableRequirement {
7878
Kind kind() const override { return Kind::kAssertDoesNotExist; }
7979

8080
Status Validate(const TableMetadata* base) const override;
81+
82+
/// \brief Compare two AssertDoesNotExist for equality
83+
friend bool operator==(const AssertDoesNotExist&, const AssertDoesNotExist&) {
84+
return true; // No fields to compare
85+
}
8186
};
8287

8388
/// \brief Requirement that the table UUID matches the expected value
@@ -94,6 +99,11 @@ class ICEBERG_EXPORT AssertUUID : public TableRequirement {
9499

95100
Status Validate(const TableMetadata* base) const override;
96101

102+
/// \brief Compare two AssertUUID for equality
103+
friend bool operator==(const AssertUUID& lhs, const AssertUUID& rhs) {
104+
return lhs.uuid_ == rhs.uuid_;
105+
}
106+
97107
private:
98108
std::string uuid_;
99109
};
@@ -116,6 +126,11 @@ class ICEBERG_EXPORT AssertRefSnapshotID : public TableRequirement {
116126

117127
Status Validate(const TableMetadata* base) const override;
118128

129+
/// \brief Compare two AssertRefSnapshotID for equality
130+
friend bool operator==(const AssertRefSnapshotID& lhs, const AssertRefSnapshotID& rhs) {
131+
return lhs.ref_name_ == rhs.ref_name_ && lhs.snapshot_id_ == rhs.snapshot_id_;
132+
}
133+
119134
private:
120135
std::string ref_name_;
121136
std::optional<int64_t> snapshot_id_;
@@ -136,6 +151,12 @@ class ICEBERG_EXPORT AssertLastAssignedFieldId : public TableRequirement {
136151

137152
Status Validate(const TableMetadata* base) const override;
138153

154+
/// \brief Compare two AssertLastAssignedFieldId for equality
155+
friend bool operator==(const AssertLastAssignedFieldId& lhs,
156+
const AssertLastAssignedFieldId& rhs) {
157+
return lhs.last_assigned_field_id_ == rhs.last_assigned_field_id_;
158+
}
159+
139160
private:
140161
int32_t last_assigned_field_id_;
141162
};
@@ -154,6 +175,12 @@ class ICEBERG_EXPORT AssertCurrentSchemaID : public TableRequirement {
154175

155176
Status Validate(const TableMetadata* base) const override;
156177

178+
/// \brief Compare two AssertCurrentSchemaID for equality
179+
friend bool operator==(const AssertCurrentSchemaID& lhs,
180+
const AssertCurrentSchemaID& rhs) {
181+
return lhs.schema_id_ == rhs.schema_id_;
182+
}
183+
157184
private:
158185
int32_t schema_id_;
159186
};
@@ -173,6 +200,12 @@ class ICEBERG_EXPORT AssertLastAssignedPartitionId : public TableRequirement {
173200

174201
Status Validate(const TableMetadata* base) const override;
175202

203+
/// \brief Compare two AssertLastAssignedPartitionId for equality
204+
friend bool operator==(const AssertLastAssignedPartitionId& lhs,
205+
const AssertLastAssignedPartitionId& rhs) {
206+
return lhs.last_assigned_partition_id_ == rhs.last_assigned_partition_id_;
207+
}
208+
176209
private:
177210
int32_t last_assigned_partition_id_;
178211
};
@@ -191,6 +224,11 @@ class ICEBERG_EXPORT AssertDefaultSpecID : public TableRequirement {
191224

192225
Status Validate(const TableMetadata* base) const override;
193226

227+
/// \brief Compare two AssertDefaultSpecID for equality
228+
friend bool operator==(const AssertDefaultSpecID& lhs, const AssertDefaultSpecID& rhs) {
229+
return lhs.spec_id_ == rhs.spec_id_;
230+
}
231+
194232
private:
195233
int32_t spec_id_;
196234
};
@@ -210,6 +248,12 @@ class ICEBERG_EXPORT AssertDefaultSortOrderID : public TableRequirement {
210248

211249
Status Validate(const TableMetadata* base) const override;
212250

251+
/// \brief Compare two AssertDefaultSortOrderID for equality
252+
friend bool operator==(const AssertDefaultSortOrderID& lhs,
253+
const AssertDefaultSortOrderID& rhs) {
254+
return lhs.sort_order_id_ == rhs.sort_order_id_;
255+
}
256+
213257
private:
214258
int32_t sort_order_id_;
215259
};

0 commit comments

Comments
 (0)