1919
2020#include " iceberg/catalog/rest/validator.h"
2121
22+ #include < algorithm>
2223#include < format>
23- #include < ranges>
24- #include < unordered_set>
25- #include < utility>
2624
2725#include " iceberg/catalog/rest/types.h"
2826#include " iceberg/result.h"
27+ #include " iceberg/util/formatter_internal.h"
28+ #include " iceberg/util/macros.h"
2929
3030namespace iceberg ::rest {
3131
@@ -40,18 +40,20 @@ Status Validator::Validate(const CatalogConfig& config) {
4040}
4141
4242Status Validator::Validate (const ErrorModel& error) {
43- if (error.message .empty () || error.type .empty ()) [[unlikely]] {
43+ if (error.message .empty () || error.type .empty ()) {
4444 return Invalid (" Invalid error model: missing required fields" );
4545 }
4646
47- if (error.code < 400 || error.code > 600 ) [[unlikely]] {
48- return Invalid (" Invalid error model: code must be between 400 and 600" );
47+ if (error.code < 400 || error.code > 600 ) {
48+ return Invalid (" Invalid error model: code {} is out of range [400, 600] " , error. code );
4949 }
5050
5151 // stack is optional, no validation needed
5252 return {};
5353}
5454
55+ // We don't validate the error field because ErrorModel::Validate has been called in the
56+ // FromJson.
5557Status Validator::Validate (const ErrorResponse& response) { return {}; }
5658
5759// Namespace operations
@@ -66,32 +68,33 @@ Status Validator::Validate(const GetNamespaceResponse& response) { return {}; }
6668
6769Status Validator::Validate (const UpdateNamespacePropertiesRequest& request) {
6870 // keys in updates and removals must not overlap
69- if (request.removals .empty () || request.updates .empty ()) [[unlikely]] {
71+ if (request.removals .empty () || request.updates .empty ()) {
7072 return {};
7173 }
7274
73- std::unordered_set<std::string> remove_set (request.removals .begin (),
74- request.removals .end ());
75- std::vector<std::string> common;
76-
77- for (const std::string& k : request.updates | std::views::keys) {
78- if (remove_set.contains (k)) {
79- common.push_back (k);
75+ auto extract_and_sort = [](const auto & container, auto key_extractor) {
76+ std::vector<std::string_view> result;
77+ result.reserve (container.size ());
78+ for (const auto & item : container) {
79+ result.push_back (std::string_view{key_extractor (item)});
8080 }
81- }
81+ std::ranges::sort (result);
82+ return result;
83+ };
8284
83- if (!common.empty ()) {
84- std::string keys;
85- bool first = true ;
86- for (const std::string& s : common) {
87- if (!std::exchange (first, false )) keys += " , " ;
88- keys += s;
89- }
85+ auto sorted_removals =
86+ extract_and_sort (request.removals , [](const auto & s) -> const auto & { return s; });
87+ auto sorted_update_keys = extract_and_sort (
88+ request.updates , [](const auto & pair) -> const auto & { return pair.first ; });
89+
90+ std::vector<std::string_view> common;
91+ std::ranges::set_intersection (sorted_removals, sorted_update_keys,
92+ std::back_inserter (common));
9093
94+ if (!common.empty ()) {
9195 return Invalid (
92- " Invalid namespace properties update: cannot simultaneously set and remove keys: "
93- " [{}]" ,
94- keys);
96+ " Invalid namespace update: cannot simultaneously set and remove keys: {}" ,
97+ common);
9598 }
9699 return {};
97100}
@@ -105,34 +108,34 @@ Status Validator::Validate(const UpdateNamespacePropertiesResponse& response) {
105108Status Validator::Validate (const ListTablesResponse& response) { return {}; }
106109
107110Status Validator::Validate (const LoadTableResult& result) {
108- if (!result.metadata ) [[unlikely]] {
111+ if (!result.metadata ) {
109112 return Invalid (" Invalid metadata: null" );
110113 }
111114 return {};
112115}
113116
114117Status Validator::Validate (const RegisterTableRequest& request) {
115- if (request.name .empty ()) [[unlikely]] {
116- return Invalid (" Invalid table name: empty " );
118+ if (request.name .empty ()) {
119+ return Invalid (" Missing table name" );
117120 }
118121
119- if (request.metadata_location .empty ()) [[unlikely]] {
120- return Invalid (" Invalid metadata location: empty " );
122+ if (request.metadata_location .empty ()) {
123+ return Invalid (" Empty metadata location" );
121124 }
122125
123126 return {};
124127}
125128
126129Status Validator::Validate (const RenameTableRequest& request) {
127- if (request.source .ns .levels .empty () || request.source .name .empty ()) [[unlikely]] {
128- return Invalid (" Invalid source identifier" );
129- }
130+ ICEBERG_RETURN_UNEXPECTED (Validate (request.source ));
131+ ICEBERG_RETURN_UNEXPECTED (Validate (request.destination ));
132+ return {};
133+ }
130134
131- if (request. destination . ns . levels . empty () || request. destination . name . empty ())
132- [[unlikely]] {
133- return Invalid (" Invalid destination identifier" );
135+ Status Validator::Validate ( const TableIdentifier& identifier) {
136+ if (identifier. name . empty ()) {
137+ return Invalid (" Invalid table identifier: missing table name " );
134138 }
135-
136139 return {};
137140}
138141
0 commit comments