1use bytes::{BufMut, Bytes};
16use prost::Message;
17use prost_reflect::{
18 DynamicMessage, FieldDescriptor, Kind, MessageDescriptor, ReflectMessage, Value,
19};
20use risingwave_common::array::VECTOR_ITEM_TYPE;
21use risingwave_common::catalog::Schema;
22use risingwave_common::row::Row;
23use risingwave_common::types::{DataType, DatumRef, MapType, ScalarRefImpl, StructType};
24use risingwave_common::util::iter_util::ZipEqDebug;
25
26use super::{FieldEncodeError, Result as SinkResult, RowEncoder, SerTo};
27
28type Result<T> = std::result::Result<T, FieldEncodeError>;
29
30pub struct ProtoEncoder {
31 schema: Schema,
32 col_indices: Option<Vec<usize>>,
33 descriptor: MessageDescriptor,
34 header: ProtoHeader,
35}
36
37#[derive(Debug, Clone, Copy)]
38pub enum ProtoHeader {
39 None,
40 ConfluentSchemaRegistry(i32),
45}
46
47impl ProtoEncoder {
48 pub fn new(
49 schema: Schema,
50 col_indices: Option<Vec<usize>>,
51 descriptor: MessageDescriptor,
52 header: ProtoHeader,
53 ) -> SinkResult<Self> {
54 match &col_indices {
55 Some(col_indices) => validate_fields(
56 col_indices.iter().map(|idx| {
57 let f = &schema[*idx];
58 (f.name.as_str(), &f.data_type)
59 }),
60 &descriptor,
61 )?,
62 None => validate_fields(
63 schema
64 .fields
65 .iter()
66 .map(|f| (f.name.as_str(), &f.data_type)),
67 &descriptor,
68 )?,
69 };
70
71 Ok(Self {
72 schema,
73 col_indices,
74 descriptor,
75 header,
76 })
77 }
78}
79
80pub struct ProtoEncoded {
81 pub message: DynamicMessage,
82 header: ProtoHeader,
83}
84
85impl RowEncoder for ProtoEncoder {
86 type Output = ProtoEncoded;
87
88 fn schema(&self) -> &Schema {
89 &self.schema
90 }
91
92 fn col_indices(&self) -> Option<&[usize]> {
93 self.col_indices.as_deref()
94 }
95
96 fn encode_cols(
97 &self,
98 row: impl Row,
99 col_indices: impl Iterator<Item = usize>,
100 ) -> SinkResult<Self::Output> {
101 encode_fields(
102 col_indices.map(|idx| {
103 let f = &self.schema[idx];
104 ((f.name.as_str(), &f.data_type), row.datum_at(idx))
105 }),
106 &self.descriptor,
107 )
108 .map_err(Into::into)
109 .map(|m| ProtoEncoded {
110 message: m,
111 header: self.header,
112 })
113 }
114}
115
116impl SerTo<Vec<u8>> for ProtoEncoded {
117 fn ser_to(self) -> SinkResult<Vec<u8>> {
118 let mut buf = Vec::new();
119 match self.header {
120 ProtoHeader::None => { }
121 ProtoHeader::ConfluentSchemaRegistry(schema_id) => {
122 buf.reserve(1 + 4);
123 buf.put_u8(0);
124 buf.put_i32(schema_id);
125 MessageIndexes::from(self.message.descriptor()).encode(&mut buf);
126 }
127 }
128 self.message.encode(&mut buf).unwrap();
129 Ok(buf)
130 }
131}
132
133struct MessageIndexes(Vec<i32>);
134
135impl MessageIndexes {
136 fn from(desc: MessageDescriptor) -> Self {
137 const TAG_FILE_MESSAGE: i32 = 4;
142 const TAG_MESSAGE_NESTED: i32 = 3;
144
145 let mut indexes = vec![];
146 let mut path = desc.path().iter().copied().array_chunks();
147 let [tag, idx] = path.next().unwrap();
148 assert_eq!(tag, TAG_FILE_MESSAGE);
149 indexes.push(idx);
150 for [tag, idx] in path {
151 assert_eq!(tag, TAG_MESSAGE_NESTED);
152 indexes.push(idx);
153 }
154 Self(indexes)
155 }
156
157 fn zig_i32(value: i32, buf: &mut impl BufMut) {
158 let unsigned = ((value << 1) ^ (value >> 31)) as u32 as u64;
159 prost::encoding::encode_varint(unsigned, buf);
160 }
161
162 fn encode(&self, buf: &mut impl BufMut) {
163 if self.0 == [0] {
164 buf.put_u8(0);
165 return;
166 }
167 Self::zig_i32(self.0.len().try_into().unwrap(), buf);
168 for &idx in &self.0 {
169 Self::zig_i32(idx, buf);
170 }
171 }
172}
173
174trait MaybeData: std::fmt::Debug {
180 type Out;
181
182 fn on_base(self, f: impl FnOnce(ScalarRefImpl<'_>) -> Result<Value>) -> Result<Self::Out>;
183
184 fn on_struct(self, st: &StructType, pb: &MessageDescriptor) -> Result<Self::Out>;
185
186 fn on_list(self, elem: &DataType, pb: &FieldDescriptor) -> Result<Self::Out>;
187
188 fn on_map(self, m: &MapType, pb: &MessageDescriptor) -> Result<Self::Out>;
189}
190
191impl MaybeData for () {
192 type Out = ();
193
194 fn on_base(self, _: impl FnOnce(ScalarRefImpl<'_>) -> Result<Value>) -> Result<Self::Out> {
195 Ok(self)
196 }
197
198 fn on_struct(self, st: &StructType, pb: &MessageDescriptor) -> Result<Self::Out> {
199 validate_fields(st.iter(), pb)
200 }
201
202 fn on_list(self, elem: &DataType, pb: &FieldDescriptor) -> Result<Self::Out> {
203 on_field(elem, (), pb, true)
204 }
205
206 fn on_map(self, elem: &MapType, pb: &MessageDescriptor) -> Result<Self::Out> {
207 debug_assert!(pb.is_map_entry());
208 on_field(elem.key(), (), &pb.map_entry_key_field(), false)?;
209 on_field(elem.value(), (), &pb.map_entry_value_field(), false)?;
210 Ok(())
211 }
212}
213
214impl MaybeData for ScalarRefImpl<'_> {
221 type Out = Value;
222
223 fn on_base(self, f: impl FnOnce(ScalarRefImpl<'_>) -> Result<Value>) -> Result<Self::Out> {
224 f(self)
225 }
226
227 fn on_struct(self, st: &StructType, pb: &MessageDescriptor) -> Result<Self::Out> {
228 let d = self.into_struct();
229 let message = encode_fields(st.iter().zip_eq_debug(d.iter_fields_ref()), pb)?;
230 Ok(Value::Message(message))
231 }
232
233 fn on_list(self, elem: &DataType, pb: &FieldDescriptor) -> Result<Self::Out> {
234 let d = self.into_list();
235 let vs = d
236 .iter()
237 .map(|d| {
238 on_field(
239 elem,
240 d.ok_or_else(|| {
241 FieldEncodeError::new("array containing null not allowed as repeated field")
242 })?,
243 pb,
244 true,
245 )
246 })
247 .try_collect()?;
248 Ok(Value::List(vs))
249 }
250
251 fn on_map(self, m: &MapType, pb: &MessageDescriptor) -> Result<Self::Out> {
252 debug_assert!(pb.is_map_entry());
253 let vs = self
254 .into_map()
255 .iter()
256 .map(|(k, v)| {
257 let v =
258 v.ok_or_else(|| FieldEncodeError::new("map containing null not allowed"))?;
259 let k = on_field(m.key(), k, &pb.map_entry_key_field(), false)?;
260 let v = on_field(m.value(), v, &pb.map_entry_value_field(), false)?;
261 Ok((
262 k.into_map_key().ok_or_else(|| {
263 FieldEncodeError::new("failed to convert map key to proto")
264 })?,
265 v,
266 ))
267 })
268 .try_collect()?;
269 Ok(Value::Map(vs))
270 }
271}
272
273fn validate_fields<'a>(
274 fields: impl Iterator<Item = (&'a str, &'a DataType)>,
275 descriptor: &MessageDescriptor,
276) -> Result<()> {
277 for (name, t) in fields {
278 let Some(proto_field) = descriptor.get_field_by_name(name) else {
279 return Err(FieldEncodeError::new("field not in proto").with_name(name));
280 };
281 if proto_field.cardinality() == prost_reflect::Cardinality::Required {
282 return Err(FieldEncodeError::new("`required` not supported").with_name(name));
283 }
284 on_field(t, (), &proto_field, false).map_err(|e| e.with_name(name))?;
285 }
286 Ok(())
287}
288
289fn encode_fields<'a>(
290 fields_with_datums: impl Iterator<Item = ((&'a str, &'a DataType), DatumRef<'a>)>,
291 descriptor: &MessageDescriptor,
292) -> Result<DynamicMessage> {
293 let mut message = DynamicMessage::new(descriptor.clone());
294 for ((name, t), d) in fields_with_datums {
295 let proto_field = descriptor.get_field_by_name(name).unwrap();
296 if let Some(scalar) = d {
298 let value = on_field(t, scalar, &proto_field, false).map_err(|e| e.with_name(name))?;
299 message
300 .try_set_field(&proto_field, value)
301 .map_err(|e| FieldEncodeError::new(e).with_name(name))?;
302 }
303 }
304 Ok(message)
305}
306
307const WKT_TIMESTAMP: &str = "google.protobuf.Timestamp";
309#[expect(dead_code)]
310const WKT_BOOL_VALUE: &str = "google.protobuf.BoolValue";
311
312fn on_field<D: MaybeData>(
315 data_type: &DataType,
316 maybe: D,
317 proto_field: &FieldDescriptor,
318 in_repeated: bool,
319) -> Result<D::Out> {
320 assert!(proto_field.is_list() || !in_repeated);
327 let expect_list = proto_field.is_list() && !in_repeated;
328 if proto_field.is_group() {
329 return Err(FieldEncodeError::new("proto group not supported yet"));
330 }
331
332 let no_match_err = || {
333 Err(FieldEncodeError::new(format!(
334 "cannot encode {} column as {}{:?} field",
335 data_type,
336 if expect_list { "repeated " } else { "" },
337 proto_field.kind()
338 )))
339 };
340
341 if expect_list && !matches!(data_type, DataType::List(_)) {
342 return no_match_err();
343 }
344
345 let value = match &data_type {
346 DataType::Boolean => match proto_field.kind() {
348 Kind::Bool => maybe.on_base(|s| Ok(Value::Bool(s.into_bool())))?,
349 _ => return no_match_err(),
350 },
351 DataType::Varchar => match proto_field.kind() {
352 Kind::String => maybe.on_base(|s| Ok(Value::String(s.into_utf8().into())))?,
353 Kind::Enum(enum_desc) => maybe.on_base(|s| {
354 let name = s.into_utf8();
355 let enum_value_desc = enum_desc.get_value_by_name(name).ok_or_else(|| {
356 FieldEncodeError::new(format!("'{name}' not in enum {}", enum_desc.name()))
357 })?;
358 Ok(Value::EnumNumber(enum_value_desc.number()))
359 })?,
360 _ => return no_match_err(),
361 },
362 DataType::Bytea => match proto_field.kind() {
363 Kind::Bytes => {
364 maybe.on_base(|s| Ok(Value::Bytes(Bytes::copy_from_slice(s.into_bytea()))))?
365 }
366 _ => return no_match_err(),
367 },
368 DataType::Float32 => match proto_field.kind() {
369 Kind::Float => maybe.on_base(|s| Ok(Value::F32(s.into_float32().into())))?,
370 _ => return no_match_err(),
371 },
372 DataType::Float64 => match proto_field.kind() {
373 Kind::Double => maybe.on_base(|s| Ok(Value::F64(s.into_float64().into())))?,
374 _ => return no_match_err(),
375 },
376 DataType::Int32 => match proto_field.kind() {
377 Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => {
378 maybe.on_base(|s| Ok(Value::I32(s.into_int32())))?
379 }
380 _ => return no_match_err(),
381 },
382 DataType::Int64 => match proto_field.kind() {
383 Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => {
384 maybe.on_base(|s| Ok(Value::I64(s.into_int64())))?
385 }
386 _ => return no_match_err(),
387 },
388 DataType::Struct(st) => match proto_field.kind() {
389 Kind::Message(pb) => maybe.on_struct(st, &pb)?,
390 _ => return no_match_err(),
391 },
392 DataType::List(lt) => match expect_list {
393 true => maybe.on_list(lt.elem(), proto_field)?,
394 false => return no_match_err(),
395 },
396 DataType::Timestamptz => match proto_field.kind() {
398 Kind::Message(pb) if pb.full_name() == WKT_TIMESTAMP => maybe.on_base(|s| {
399 let d = s.into_timestamptz();
400 let message = prost_types::Timestamp {
401 seconds: d.timestamp(),
402 nanos: d.timestamp_subsec_nanos().try_into().unwrap(),
403 };
404 Ok(Value::Message(message.transcode_to_dynamic()))
405 })?,
406 Kind::String => {
407 maybe.on_base(|s| Ok(Value::String(s.into_timestamptz().to_string())))?
408 }
409 _ => return no_match_err(),
410 },
411 DataType::Jsonb => match proto_field.kind() {
412 Kind::String => maybe.on_base(|s| Ok(Value::String(s.into_jsonb().to_string())))?,
413 _ => return no_match_err(), },
416 DataType::Variant => {
417 return no_match_err();
418 }
419 DataType::Int16 => match proto_field.kind() {
420 Kind::Int64 => maybe.on_base(|s| Ok(Value::I64(s.into_int16() as i64)))?,
421 _ => return no_match_err(),
422 },
423 DataType::Date => match proto_field.kind() {
424 Kind::Int32 => {
425 maybe.on_base(|s| Ok(Value::I32(s.into_date().get_nums_days_unix_epoch())))?
426 }
427 _ => return no_match_err(), },
429 DataType::Time => match proto_field.kind() {
430 Kind::String => maybe.on_base(|s| Ok(Value::String(s.into_time().to_string())))?,
431 _ => return no_match_err(), },
433 DataType::Timestamp => match proto_field.kind() {
434 Kind::String => maybe.on_base(|s| Ok(Value::String(s.into_timestamp().to_string())))?,
435 _ => return no_match_err(), },
437 DataType::Decimal => match proto_field.kind() {
438 Kind::String => maybe.on_base(|s| Ok(Value::String(s.into_decimal().to_string())))?,
439 _ => return no_match_err(), },
441 DataType::Interval => match proto_field.kind() {
442 Kind::String => {
443 maybe.on_base(|s| Ok(Value::String(s.into_interval().as_iso_8601())))?
444 }
445 _ => return no_match_err(), },
447 DataType::Serial => match proto_field.kind() {
448 Kind::Int64 => maybe.on_base(|s| Ok(Value::I64(s.into_serial().as_row_id())))?,
449 _ => return no_match_err(), },
451 DataType::Int256 => {
452 return no_match_err();
453 }
454 DataType::Map(map_type) => {
455 if proto_field.is_map() {
456 let msg = match proto_field.kind() {
457 Kind::Message(m) => m,
458 _ => return no_match_err(), };
460 return maybe.on_map(map_type, &msg);
461 } else {
462 return no_match_err();
463 }
464 }
465 DataType::Vector(_) => match expect_list {
466 true => maybe.on_list(&VECTOR_ITEM_TYPE, proto_field)?,
467 false => return no_match_err(),
468 },
469 };
470
471 Ok(value)
472}
473
474#[cfg(test)]
475mod tests {
476 use itertools::Itertools;
477 use risingwave_common::array::{ArrayBuilder, StructArrayBuilder};
478 use risingwave_common::catalog::Field;
479 use risingwave_common::row::OwnedRow;
480 use risingwave_common::types::{
481 ListValue, MapType, MapValue, Scalar, ScalarImpl, StructValue, Timestamptz,
482 };
483
484 use super::*;
485
486 #[test]
487 fn test_encode_proto_ok() {
488 let pool_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
489 .join("codec/tests/test_data/all-types.pb");
490 let pool_bytes = std::fs::read(pool_path).unwrap();
491 let pool = prost_reflect::DescriptorPool::decode(pool_bytes.as_ref()).unwrap();
492 let descriptor = pool.get_message_by_name("all_types.AllTypes").unwrap();
493 let schema = Schema::new(vec![
494 Field::with_name(DataType::Boolean, "bool_field"),
495 Field::with_name(DataType::Varchar, "string_field"),
496 Field::with_name(DataType::Bytea, "bytes_field"),
497 Field::with_name(DataType::Float32, "float_field"),
498 Field::with_name(DataType::Float64, "double_field"),
499 Field::with_name(DataType::Int32, "int32_field"),
500 Field::with_name(DataType::Int64, "int64_field"),
501 Field::with_name(DataType::Int32, "sint32_field"),
502 Field::with_name(DataType::Int64, "sint64_field"),
503 Field::with_name(DataType::Int32, "sfixed32_field"),
504 Field::with_name(DataType::Int64, "sfixed64_field"),
505 Field::with_name(
506 DataType::Struct(StructType::new(vec![
507 ("id", DataType::Int32),
508 ("name", DataType::Varchar),
509 ])),
510 "nested_message_field",
511 ),
512 Field::with_name(DataType::Int32.list(), "repeated_int_field"),
513 Field::with_name(DataType::Timestamptz, "timestamp_field"),
514 Field::with_name(
515 DataType::Map(MapType::from_kv(DataType::Varchar, DataType::Int32)),
516 "map_field",
517 ),
518 Field::with_name(
519 DataType::Map(MapType::from_kv(
520 DataType::Varchar,
521 DataType::Struct(StructType::new(vec![
522 ("id", DataType::Int32),
523 ("name", DataType::Varchar),
524 ])),
525 )),
526 "map_struct_field",
527 ),
528 ]);
529 let row = OwnedRow::new(vec![
530 Some(ScalarImpl::Bool(true)),
531 Some(ScalarImpl::Utf8("RisingWave".into())),
532 Some(ScalarImpl::Bytea([0xbe, 0xef].into())),
533 Some(ScalarImpl::Float32(3.5f32.into())),
534 Some(ScalarImpl::Float64(4.25f64.into())),
535 Some(ScalarImpl::Int32(22)),
536 Some(ScalarImpl::Int64(23)),
537 Some(ScalarImpl::Int32(24)),
538 None,
539 Some(ScalarImpl::Int32(26)),
540 Some(ScalarImpl::Int64(27)),
541 Some(ScalarImpl::Struct(StructValue::new(vec![
542 Some(ScalarImpl::Int32(1)),
543 Some(ScalarImpl::Utf8("".into())),
544 ]))),
545 Some(ScalarImpl::List(ListValue::from_iter([4, 0, 4]))),
546 Some(ScalarImpl::Timestamptz(
547 Timestamptz::from_micros(3).unwrap(),
548 )),
549 Some(ScalarImpl::Map(
550 MapValue::try_from_kv(
551 ListValue::from_iter(["a", "b"]),
552 ListValue::from_iter([1, 2]),
553 )
554 .unwrap(),
555 )),
556 {
557 let mut struct_array_builder = StructArrayBuilder::with_type(
558 2,
559 DataType::Struct(StructType::new(vec![
560 ("id", DataType::Int32),
561 ("name", DataType::Varchar),
562 ])),
563 );
564 struct_array_builder.append(Some(
565 StructValue::new(vec![
566 Some(ScalarImpl::Int32(1)),
567 Some(ScalarImpl::Utf8("x".into())),
568 ])
569 .as_scalar_ref(),
570 ));
571 struct_array_builder.append(Some(
572 StructValue::new(vec![
573 Some(ScalarImpl::Int32(2)),
574 Some(ScalarImpl::Utf8("y".into())),
575 ])
576 .as_scalar_ref(),
577 ));
578 Some(ScalarImpl::Map(
579 MapValue::try_from_kv(
580 ListValue::from_iter(["a", "b"]),
581 ListValue::new(struct_array_builder.finish().into()),
582 )
583 .unwrap(),
584 ))
585 },
586 ]);
587
588 let encoder = ProtoEncoder::new(schema, None, descriptor, ProtoHeader::None).unwrap();
589 let m = encoder.encode(row).unwrap();
590 expect_test::expect![[r#"
591 field: FieldDescriptor {
592 name: "double_field",
593 full_name: "all_types.AllTypes.double_field",
594 json_name: "doubleField",
595 number: 1,
596 kind: double,
597 cardinality: Optional,
598 containing_oneof: None,
599 default_value: F64(
600 0.0,
601 ),
602 is_group: false,
603 is_list: false,
604 is_map: false,
605 is_packed: false,
606 supports_presence: false,
607 }
608
609 value: F64(4.25)
610
611 ==============================
612 field: FieldDescriptor {
613 name: "float_field",
614 full_name: "all_types.AllTypes.float_field",
615 json_name: "floatField",
616 number: 2,
617 kind: float,
618 cardinality: Optional,
619 containing_oneof: None,
620 default_value: F32(
621 0.0,
622 ),
623 is_group: false,
624 is_list: false,
625 is_map: false,
626 is_packed: false,
627 supports_presence: false,
628 }
629
630 value: F32(3.5)
631
632 ==============================
633 field: FieldDescriptor {
634 name: "int32_field",
635 full_name: "all_types.AllTypes.int32_field",
636 json_name: "int32Field",
637 number: 3,
638 kind: int32,
639 cardinality: Optional,
640 containing_oneof: None,
641 default_value: I32(
642 0,
643 ),
644 is_group: false,
645 is_list: false,
646 is_map: false,
647 is_packed: false,
648 supports_presence: false,
649 }
650
651 value: I32(22)
652
653 ==============================
654 field: FieldDescriptor {
655 name: "int64_field",
656 full_name: "all_types.AllTypes.int64_field",
657 json_name: "int64Field",
658 number: 4,
659 kind: int64,
660 cardinality: Optional,
661 containing_oneof: None,
662 default_value: I64(
663 0,
664 ),
665 is_group: false,
666 is_list: false,
667 is_map: false,
668 is_packed: false,
669 supports_presence: false,
670 }
671
672 value: I64(23)
673
674 ==============================
675 field: FieldDescriptor {
676 name: "sint32_field",
677 full_name: "all_types.AllTypes.sint32_field",
678 json_name: "sint32Field",
679 number: 7,
680 kind: sint32,
681 cardinality: Optional,
682 containing_oneof: None,
683 default_value: I32(
684 0,
685 ),
686 is_group: false,
687 is_list: false,
688 is_map: false,
689 is_packed: false,
690 supports_presence: false,
691 }
692
693 value: I32(24)
694
695 ==============================
696 field: FieldDescriptor {
697 name: "sfixed32_field",
698 full_name: "all_types.AllTypes.sfixed32_field",
699 json_name: "sfixed32Field",
700 number: 11,
701 kind: sfixed32,
702 cardinality: Optional,
703 containing_oneof: None,
704 default_value: I32(
705 0,
706 ),
707 is_group: false,
708 is_list: false,
709 is_map: false,
710 is_packed: false,
711 supports_presence: false,
712 }
713
714 value: I32(26)
715
716 ==============================
717 field: FieldDescriptor {
718 name: "sfixed64_field",
719 full_name: "all_types.AllTypes.sfixed64_field",
720 json_name: "sfixed64Field",
721 number: 12,
722 kind: sfixed64,
723 cardinality: Optional,
724 containing_oneof: None,
725 default_value: I64(
726 0,
727 ),
728 is_group: false,
729 is_list: false,
730 is_map: false,
731 is_packed: false,
732 supports_presence: false,
733 }
734
735 value: I64(27)
736
737 ==============================
738 field: FieldDescriptor {
739 name: "bool_field",
740 full_name: "all_types.AllTypes.bool_field",
741 json_name: "boolField",
742 number: 13,
743 kind: bool,
744 cardinality: Optional,
745 containing_oneof: None,
746 default_value: Bool(
747 false,
748 ),
749 is_group: false,
750 is_list: false,
751 is_map: false,
752 is_packed: false,
753 supports_presence: false,
754 }
755
756 value: Bool(true)
757
758 ==============================
759 field: FieldDescriptor {
760 name: "string_field",
761 full_name: "all_types.AllTypes.string_field",
762 json_name: "stringField",
763 number: 14,
764 kind: string,
765 cardinality: Optional,
766 containing_oneof: None,
767 default_value: String(
768 "",
769 ),
770 is_group: false,
771 is_list: false,
772 is_map: false,
773 is_packed: false,
774 supports_presence: false,
775 }
776
777 value: String("RisingWave")
778
779 ==============================
780 field: FieldDescriptor {
781 name: "bytes_field",
782 full_name: "all_types.AllTypes.bytes_field",
783 json_name: "bytesField",
784 number: 15,
785 kind: bytes,
786 cardinality: Optional,
787 containing_oneof: None,
788 default_value: Bytes(
789 b"",
790 ),
791 is_group: false,
792 is_list: false,
793 is_map: false,
794 is_packed: false,
795 supports_presence: false,
796 }
797
798 value: Bytes(b"\xbe\xef")
799
800 ==============================
801 field: FieldDescriptor {
802 name: "nested_message_field",
803 full_name: "all_types.AllTypes.nested_message_field",
804 json_name: "nestedMessageField",
805 number: 17,
806 kind: all_types.AllTypes.NestedMessage,
807 cardinality: Optional,
808 containing_oneof: None,
809 default_value: Message(
810 DynamicMessage {
811 desc: MessageDescriptor {
812 name: "NestedMessage",
813 full_name: "all_types.AllTypes.NestedMessage",
814 is_map_entry: false,
815 fields: [
816 FieldDescriptor {
817 name: "id",
818 full_name: "all_types.AllTypes.NestedMessage.id",
819 json_name: "id",
820 number: 1,
821 kind: int32,
822 cardinality: Optional,
823 containing_oneof: None,
824 default_value: I32(
825 0,
826 ),
827 is_group: false,
828 is_list: false,
829 is_map: false,
830 is_packed: false,
831 supports_presence: false,
832 },
833 FieldDescriptor {
834 name: "name",
835 full_name: "all_types.AllTypes.NestedMessage.name",
836 json_name: "name",
837 number: 2,
838 kind: string,
839 cardinality: Optional,
840 containing_oneof: None,
841 default_value: String(
842 "",
843 ),
844 is_group: false,
845 is_list: false,
846 is_map: false,
847 is_packed: false,
848 supports_presence: false,
849 },
850 ],
851 oneofs: [],
852 },
853 fields: DynamicMessageFieldSet {
854 fields: {},
855 },
856 },
857 ),
858 is_group: false,
859 is_list: false,
860 is_map: false,
861 is_packed: false,
862 supports_presence: true,
863 }
864
865 value: Message(DynamicMessage { desc: MessageDescriptor { name: "NestedMessage", full_name: "all_types.AllTypes.NestedMessage", is_map_entry: false, fields: [FieldDescriptor { name: "id", full_name: "all_types.AllTypes.NestedMessage.id", json_name: "id", number: 1, kind: int32, cardinality: Optional, containing_oneof: None, default_value: I32(0), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }, FieldDescriptor { name: "name", full_name: "all_types.AllTypes.NestedMessage.name", json_name: "name", number: 2, kind: string, cardinality: Optional, containing_oneof: None, default_value: String(""), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }], oneofs: [] }, fields: DynamicMessageFieldSet { fields: {1: Value(I32(1)), 2: Value(String(""))} } })
866
867 ==============================
868 field: FieldDescriptor {
869 name: "repeated_int_field",
870 full_name: "all_types.AllTypes.repeated_int_field",
871 json_name: "repeatedIntField",
872 number: 18,
873 kind: int32,
874 cardinality: Repeated,
875 containing_oneof: None,
876 default_value: List(
877 [],
878 ),
879 is_group: false,
880 is_list: true,
881 is_map: false,
882 is_packed: true,
883 supports_presence: false,
884 }
885
886 value: List([I32(4), I32(0), I32(4)])
887
888 ==============================
889 field: FieldDescriptor {
890 name: "map_field",
891 full_name: "all_types.AllTypes.map_field",
892 json_name: "mapField",
893 number: 22,
894 kind: all_types.AllTypes.MapFieldEntry,
895 cardinality: Repeated,
896 containing_oneof: None,
897 default_value: Map(
898 {},
899 ),
900 is_group: false,
901 is_list: false,
902 is_map: true,
903 is_packed: false,
904 supports_presence: false,
905 }
906
907 value: Map({
908 String("a"): I32(1),
909 String("b"): I32(2),
910 })
911
912 ==============================
913 field: FieldDescriptor {
914 name: "timestamp_field",
915 full_name: "all_types.AllTypes.timestamp_field",
916 json_name: "timestampField",
917 number: 23,
918 kind: google.protobuf.Timestamp,
919 cardinality: Optional,
920 containing_oneof: None,
921 default_value: Message(
922 DynamicMessage {
923 desc: MessageDescriptor {
924 name: "Timestamp",
925 full_name: "google.protobuf.Timestamp",
926 is_map_entry: false,
927 fields: [
928 FieldDescriptor {
929 name: "seconds",
930 full_name: "google.protobuf.Timestamp.seconds",
931 json_name: "seconds",
932 number: 1,
933 kind: int64,
934 cardinality: Optional,
935 containing_oneof: None,
936 default_value: I64(
937 0,
938 ),
939 is_group: false,
940 is_list: false,
941 is_map: false,
942 is_packed: false,
943 supports_presence: false,
944 },
945 FieldDescriptor {
946 name: "nanos",
947 full_name: "google.protobuf.Timestamp.nanos",
948 json_name: "nanos",
949 number: 2,
950 kind: int32,
951 cardinality: Optional,
952 containing_oneof: None,
953 default_value: I32(
954 0,
955 ),
956 is_group: false,
957 is_list: false,
958 is_map: false,
959 is_packed: false,
960 supports_presence: false,
961 },
962 ],
963 oneofs: [],
964 },
965 fields: DynamicMessageFieldSet {
966 fields: {},
967 },
968 },
969 ),
970 is_group: false,
971 is_list: false,
972 is_map: false,
973 is_packed: false,
974 supports_presence: true,
975 }
976
977 value: Message(DynamicMessage { desc: MessageDescriptor { name: "Timestamp", full_name: "google.protobuf.Timestamp", is_map_entry: false, fields: [FieldDescriptor { name: "seconds", full_name: "google.protobuf.Timestamp.seconds", json_name: "seconds", number: 1, kind: int64, cardinality: Optional, containing_oneof: None, default_value: I64(0), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }, FieldDescriptor { name: "nanos", full_name: "google.protobuf.Timestamp.nanos", json_name: "nanos", number: 2, kind: int32, cardinality: Optional, containing_oneof: None, default_value: I32(0), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }], oneofs: [] }, fields: DynamicMessageFieldSet { fields: {2: Value(I32(3000))} } })
978
979 ==============================
980 field: FieldDescriptor {
981 name: "map_struct_field",
982 full_name: "all_types.AllTypes.map_struct_field",
983 json_name: "mapStructField",
984 number: 29,
985 kind: all_types.AllTypes.MapStructFieldEntry,
986 cardinality: Repeated,
987 containing_oneof: None,
988 default_value: Map(
989 {},
990 ),
991 is_group: false,
992 is_list: false,
993 is_map: true,
994 is_packed: false,
995 supports_presence: false,
996 }
997
998 value: Map({
999 String("a"): Message(DynamicMessage { desc: MessageDescriptor { name: "NestedMessage", full_name: "all_types.AllTypes.NestedMessage", is_map_entry: false, fields: [FieldDescriptor { name: "id", full_name: "all_types.AllTypes.NestedMessage.id", json_name: "id", number: 1, kind: int32, cardinality: Optional, containing_oneof: None, default_value: I32(0), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }, FieldDescriptor { name: "name", full_name: "all_types.AllTypes.NestedMessage.name", json_name: "name", number: 2, kind: string, cardinality: Optional, containing_oneof: None, default_value: String(""), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }], oneofs: [] }, fields: DynamicMessageFieldSet { fields: {1: Value(I32(1)), 2: Value(String("x"))} } }),
1000 String("b"): Message(DynamicMessage { desc: MessageDescriptor { name: "NestedMessage", full_name: "all_types.AllTypes.NestedMessage", is_map_entry: false, fields: [FieldDescriptor { name: "id", full_name: "all_types.AllTypes.NestedMessage.id", json_name: "id", number: 1, kind: int32, cardinality: Optional, containing_oneof: None, default_value: I32(0), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }, FieldDescriptor { name: "name", full_name: "all_types.AllTypes.NestedMessage.name", json_name: "name", number: 2, kind: string, cardinality: Optional, containing_oneof: None, default_value: String(""), is_group: false, is_list: false, is_map: false, is_packed: false, supports_presence: false }], oneofs: [] }, fields: DynamicMessageFieldSet { fields: {1: Value(I32(2)), 2: Value(String("y"))} } }),
1001 })"#]].assert_eq(&format!("{}",
1002 m.message.fields().format_with("\n\n==============================\n", |(field,value),f| {
1003 f(&format!("field: {:#?}\n\nvalue: {}", field, print_proto(value)))
1004 })));
1005 }
1006
1007 fn print_proto(value: &Value) -> String {
1008 match value {
1009 Value::Map(m) => {
1010 let mut res = String::new();
1011 res.push_str("Map({\n");
1012 for (k, v) in m.iter().sorted_by_key(|(k, _v)| *k) {
1013 res.push_str(&format!(
1014 " {}: {},\n",
1015 print_proto(&k.clone().into()),
1016 print_proto(v)
1017 ));
1018 }
1019 res.push_str("})");
1020 res
1021 }
1022 _ => format!("{:?}", value),
1023 }
1024 }
1025
1026 #[test]
1027 fn test_encode_proto_repeated() {
1028 let pool_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
1029 .join("codec/tests/test_data/all-types.pb");
1030 let pool_bytes = fs_err::read(pool_path).unwrap();
1031 let pool = prost_reflect::DescriptorPool::decode(pool_bytes.as_ref()).unwrap();
1032 let message_descriptor = pool.get_message_by_name("all_types.AllTypes").unwrap();
1033
1034 let schema = Schema::new(vec![Field::with_name(
1035 DataType::Int32.list().list(),
1036 "repeated_int_field",
1037 )]);
1038
1039 let err = validate_fields(
1040 schema
1041 .fields
1042 .iter()
1043 .map(|f| (f.name.as_str(), &f.data_type)),
1044 &message_descriptor,
1045 )
1046 .unwrap_err();
1047 assert_eq!(
1048 err.to_string(),
1049 "encode 'repeated_int_field' error: cannot encode integer[] column as int32 field"
1050 );
1051
1052 let schema = Schema::new(vec![Field::with_name(
1053 DataType::Int32.list(),
1054 "repeated_int_field",
1055 )]);
1056 let row = OwnedRow::new(vec![Some(ScalarImpl::List(ListValue::from_iter([
1057 Some(0),
1058 None,
1059 Some(2),
1060 Some(3),
1061 ])))]);
1062
1063 let err = encode_fields(
1064 schema
1065 .fields
1066 .iter()
1067 .map(|f| (f.name.as_str(), &f.data_type))
1068 .zip_eq_debug(row.iter()),
1069 &message_descriptor,
1070 )
1071 .unwrap_err();
1072 assert_eq!(
1073 err.to_string(),
1074 "encode 'repeated_int_field' error: array containing null not allowed as repeated field"
1075 );
1076 }
1077
1078 #[test]
1079 fn test_encode_proto_err() {
1080 let pool_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
1081 .join("codec/tests/test_data/all-types.pb");
1082 let pool_bytes = std::fs::read(pool_path).unwrap();
1083 let pool = prost_reflect::DescriptorPool::decode(pool_bytes.as_ref()).unwrap();
1084 let message_descriptor = pool.get_message_by_name("all_types.AllTypes").unwrap();
1085
1086 let err = validate_fields(
1087 std::iter::once(("not_exists", &DataType::Int16)),
1088 &message_descriptor,
1089 )
1090 .unwrap_err();
1091 assert_eq!(
1092 err.to_string(),
1093 "encode 'not_exists' error: field not in proto"
1094 );
1095
1096 let err = validate_fields(
1097 std::iter::once(("map_field", &DataType::Jsonb)),
1098 &message_descriptor,
1099 )
1100 .unwrap_err();
1101 assert_eq!(
1102 err.to_string(),
1103 "encode 'map_field' error: cannot encode jsonb column as all_types.AllTypes.MapFieldEntry field"
1104 );
1105 }
1106}