Skip to content

Commit e775ab4

Browse files
authored
Merge pull request #24993 from IoannisRP/ik-manual-backport-PR24992-v24.2.x
[v24.2.x] pandaproxy/sr: Fix issues discovered in the protobuf renderer followup
2 parents 78569c9 + a394d58 commit e775ab4

File tree

2 files changed

+68
-21
lines changed

2 files changed

+68
-21
lines changed

src/v/pandaproxy/schema_registry/protobuf.cc

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include <absl/container/flat_hash_set.h>
3030
#include <absl/strings/ascii.h>
31+
#include <absl/strings/escaping.h>
3132
#include <boost/algorithm/string/trim.hpp>
3233
#include <boost/container/flat_set.hpp>
3334
#include <boost/range/combine.hpp>
@@ -72,12 +73,24 @@
7273

7374
#include <algorithm>
7475
#include <charconv>
76+
#include <concepts>
77+
#include <cstdint>
7578
#include <functional>
7679
#include <optional>
7780
#include <ranges>
7881
#include <string_view>
7982
#include <unordered_set>
8083

84+
namespace {
85+
86+
// Protobuf string values need to be quoted and escaped
87+
// as they may contain characters like '\'
88+
auto pb_string_value(const std::string_view v) {
89+
return fmt::format("\"{}\"", absl::CEscape(v));
90+
}
91+
92+
} // namespace
93+
8194
struct indent_formatter : fmt::formatter<std::string_view> {
8295
using Base = fmt::formatter<std::string_view>;
8396
constexpr auto parse(fmt::format_parse_context& ctx) {
@@ -133,10 +146,18 @@ struct fmt::formatter<google::protobuf::UninterpretedOption>
133146
const google::protobuf::UninterpretedOption& option,
134147
format_context& ctx) const {
135148
const auto fmt = [&](const auto& val) {
136-
if (option.has_string_value()) {
137-
return fmt::format_to(
138-
ctx.out(), "{} = \"{}\"", fmt::join(option.name(), "."), val);
139-
} else if (option.has_aggregate_value()) {
149+
if constexpr (std::convertible_to<
150+
std::decay_t<decltype(val)>,
151+
std::string_view>) {
152+
if (option.has_string_value()) {
153+
return fmt::format_to(
154+
ctx.out(),
155+
"{} = {}",
156+
fmt::join(option.name(), "."),
157+
pb_string_value(val));
158+
}
159+
}
160+
if (option.has_aggregate_value()) {
140161
return fmt::format_to(
141162
ctx.out(),
142163
"{} = {{{}\n{:{}}}}",
@@ -255,6 +276,10 @@ auto field_options() {
255276
"packed", &pb::FieldOptions::has_packed, &pb::FieldOptions::packed},
256277
field_option{
257278
"lazy", &pb::FieldOptions::has_lazy, &pb::FieldOptions::lazy},
279+
field_option{
280+
"unverified_lazy",
281+
&pb::FieldOptions::has_unverified_lazy,
282+
&pb::FieldOptions::unverified_lazy},
258283
field_option{
259284
"weak", &pb::FieldOptions::has_weak, &pb::FieldOptions::weak},
260285
field_option{
@@ -919,7 +944,8 @@ struct protobuf_schema_definition::impl {
919944
}
920945
if (field.has_json_name()) {
921946
maybe_print_seperator();
922-
fmt::print(os, "json_name = \"{}\"", field.json_name());
947+
fmt::print(
948+
os, "json_name = {}", pb_string_value(field.json_name()));
923949
}
924950
if (field.has_options()) {
925951
const auto& options = field.options();
@@ -1075,11 +1101,12 @@ struct protobuf_schema_definition::impl {
10751101

10761102
if (decl.has_full_name()) {
10771103
maybe_print_seperator();
1078-
fmt::print(os, "{}: \"{}\"", "full_name", decl.full_name());
1104+
fmt::print(
1105+
os, "{}: {}", "full_name", pb_string_value(decl.full_name()));
10791106
}
10801107
if (decl.has_type()) {
10811108
maybe_print_seperator();
1082-
fmt::print(os, "{}: \"{}\"", "type", decl.type());
1109+
fmt::print(os, "{}: {}", "type", pb_string_value(decl.type()));
10831110
}
10841111
if (decl.has_number()) {
10851112
maybe_print_seperator();
@@ -1161,12 +1188,16 @@ struct protobuf_schema_definition::impl {
11611188
}
11621189
auto reserved_names = maybe_sorted(message.reserved_name());
11631190
if (!reserved_names.empty()) {
1191+
const auto to_debug_string = [](const std::string_view strv) {
1192+
return fmt::format("{}", pb_string_value(strv));
1193+
};
11641194
fmt::print(
11651195
os,
1166-
"{:{}}reserved \"{}\";\n",
1196+
"{:{}}reserved {};\n",
11671197
"",
11681198
indent + 2,
1169-
fmt::join(reserved_names, "\", \""));
1199+
fmt::join(
1200+
reserved_names | std::views::transform(to_debug_string), ", "));
11701201
}
11711202
if (!reserved_range.empty() || !reserved_names.empty()) {
11721203
fmt::print(os, "\n");
@@ -1287,7 +1318,12 @@ struct protobuf_schema_definition::impl {
12871318
fmt::print(os, ";\n");
12881319
}
12891320
for (const auto& value : maybe_sorted(enum_proto.reserved_name())) {
1290-
fmt::print(os, "{:{}}reserved \"{}\";\n", "", indent + 2, value);
1321+
fmt::print(
1322+
os,
1323+
"{:{}}reserved {};\n",
1324+
"",
1325+
indent + 2,
1326+
pb_string_value(value));
12911327
}
12921328
if (enum_proto.options().has_allow_alias()) {
12931329
fmt::print(
@@ -1310,14 +1346,16 @@ struct protobuf_schema_definition::impl {
13101346
for (const auto& option : uninterpreted_options) {
13111347
fmt::print(os, "{:{}}option {};\n", "", indent + 2, option);
13121348
}
1313-
std::optional<std::decay_t<decltype(enum_proto.value())>> values;
1314-
if (is_normalized) {
1315-
values = enum_proto.value();
1316-
std::ranges::sort(values.value(), std::less{}, [](const auto& v) {
1317-
return std::pair<int, std::string_view>{v.number(), v.name()};
1318-
});
1319-
}
1320-
for (const auto& value : values.value_or(enum_proto.value())) {
1349+
const auto values = maybe_sorted(
1350+
enum_proto.value(), std::less{}, [](const auto& v) {
1351+
// In proto3, enums are open and open enums need to
1352+
// have the first field being equal to zero. By casting
1353+
// to an unsigned integer for sorting, all the negative
1354+
// fields will be at the end, after all the positives.
1355+
return std::pair<uint32_t, std::string_view>{
1356+
static_cast<uint32_t>(v.number()), v.name()};
1357+
});
1358+
for (const auto& value : values) {
13211359
fmt::print(
13221360
os, "{:{}}{} = {}", "", indent + 2, value.name(), value.number());
13231361
if (value.has_options()) {
@@ -1427,7 +1465,7 @@ struct protobuf_schema_definition::impl {
14271465
first_option = false;
14281466
};
14291467
auto prints = [&](std::string_view name, const auto& val) {
1430-
fmt::print(os, "option {} = \"{}\";\n", name, val);
1468+
fmt::print(os, "option {} = {};\n", name, pb_string_value(val));
14311469
first_option = false;
14321470
};
14331471
if (options.has_cc_enable_arenas()) {
@@ -1544,7 +1582,7 @@ struct protobuf_schema_definition::impl {
15441582

15451583
auto print_deps = [&](const auto& view, std::string_view type) {
15461584
for (const auto& dep : view) {
1547-
fmt::print(os, "import {}\"{}\";\n", type, dep);
1585+
fmt::print(os, "import {}{};\n", type, pb_string_value(dep));
15481586
}
15491587
};
15501588

@@ -1559,7 +1597,7 @@ struct protobuf_schema_definition::impl {
15591597
const pb::FileDescriptor& descriptor) const {
15601598
auto syntax = fdp.has_syntax() ? fdp.syntax() : "proto2";
15611599
std::string_view edition = syntax;
1562-
fmt::print(os, "syntax = \"{}\";\n", syntax);
1600+
fmt::print(os, "syntax = {};\n", pb_string_value(syntax));
15631601

15641602
if (fdp.has_package() && !fdp.package().empty()) {
15651603
fmt::print(os, "package {};\n", fdp.package());

src/v/pandaproxy/schema_registry/test/compatibility_protobuf.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,7 @@ option go_package = "foo.example.com/fooservice";
15131513
option cc_enable_arenas = true;
15141514
option objc_class_prefix = "FS";
15151515
option csharp_namespace = "Foo.FooService";
1516+
option php_namespace = "my_php\ns";
15161517
15171518
15181519
// public should come last
@@ -1529,6 +1530,8 @@ message Baz {
15291530
15301531
enum Numbers {
15311532
ZERO=0;
1533+
MINUS_ONE =-1;
1534+
MINUS_TWO =-2;
15321535
TWO = 2;
15331536
ONE=1;
15341537
ALIAS = 1 [deprecated = true, debug_redact = false];
@@ -1615,6 +1618,7 @@ option go_package = "foo.example.com/fooservice";
16151618
option cc_enable_arenas = true;
16161619
option objc_class_prefix = "FS";
16171620
option csharp_namespace = "Foo.FooService";
1621+
option php_namespace = "my_php\ns";
16181622
16191623
message Baz {
16201624
.google.protobuf.Any any = 1;
@@ -1677,6 +1681,8 @@ enum Numbers {
16771681
reserved "SIX";
16781682
option allow_alias = true;
16791683
ZERO = 0;
1684+
MINUS_ONE = -1;
1685+
MINUS_TWO = -2;
16801686
TWO = 2;
16811687
ONE = 1;
16821688
ALIAS = 1 [deprecated = true, debug_redact = false];
@@ -1703,6 +1709,7 @@ option java_outer_classname = "FooService";
17031709
option java_package = "com.example.foo";
17041710
option objc_class_prefix = "FS";
17051711
option optimize_for = SPEED;
1712+
option php_namespace = "my_php\ns";
17061713
17071714
message Baz {
17081715
.google.protobuf.Any any = 1;
@@ -1768,6 +1775,8 @@ enum Numbers {
17681775
ALIAS = 1 [deprecated = true, debug_redact = false];
17691776
ONE = 1;
17701777
TWO = 2;
1778+
MINUS_TWO = -2;
1779+
MINUS_ONE = -1;
17711780
}
17721781
17731782
service FooService {

0 commit comments

Comments
 (0)