Skip to main content

risingwave_connector/sink/encoder/
proto.rs

1// Copyright 2023 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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    /// <https://docs.confluent.io/platform/7.5/schema-registry/fundamentals/serdes-develop/index.html#messages-wire-format>
41    ///
42    /// * 00
43    /// * 4-byte big-endian schema ID
44    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 => { /* noop */ }
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        // https://github.com/protocolbuffers/protobuf/blob/v25.1/src/google/protobuf/descriptor.proto
138        // https://docs.rs/prost-reflect/0.12.0/src/prost_reflect/descriptor/tag.rs.html
139        // https://docs.rs/prost-reflect/0.12.0/src/prost_reflect/descriptor/build/visit.rs.html#125
140        // `FileDescriptorProto` field #4 is `repeated DescriptorProto message_type`
141        const TAG_FILE_MESSAGE: i32 = 4;
142        // `DescriptorProto` field #3 is `repeated DescriptorProto nested_type`
143        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
174/// A trait that assists code reuse between `validate` and `encode`.
175/// * For `validate`, the inputs are (RisingWave type, ProtoBuf type).
176/// * For `encode`, the inputs are (RisingWave type, RisingWave data, ProtoBuf type).
177///
178/// Thus we impl [`MaybeData`] for both `()` and [`ScalarRefImpl`].
179trait 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
214/// Nullability is not part of type system in proto.
215/// * Top level is always a message.
216/// * All message fields can be omitted in proto3.
217/// * All repeated elements must have a value.
218///
219/// So we handle [`ScalarRefImpl`] rather than [`DatumRef`] here.
220impl 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        // On `null`, simply skip setting the field.
297        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
307// Full name of Well-Known Types
308const WKT_TIMESTAMP: &str = "google.protobuf.Timestamp";
309#[expect(dead_code)]
310const WKT_BOOL_VALUE: &str = "google.protobuf.BoolValue";
311
312/// Handles both `validate` (without actual data) and `encode`.
313/// See [`MaybeData`] for more info.
314fn on_field<D: MaybeData>(
315    data_type: &DataType,
316    maybe: D,
317    proto_field: &FieldDescriptor,
318    in_repeated: bool,
319) -> Result<D::Out> {
320    // Regarding (proto_field.is_list, in_repeated):
321    // (F, T) => impossible
322    // (F, F) => encoding to a non-repeated field
323    // (T, F) => encoding to a repeated field
324    // (T, T) => encoding to an element of a repeated field
325    // In the bottom 2 cases, we need to distinguish the same `proto_field` with the help of `in_repeated`.
326    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        // Group A: perfect match between RisingWave types and ProtoBuf types
347        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        // Group B: match between RisingWave types and ProtoBuf Well-Known types
397        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(), /* Value, NullValue, Struct (map), ListValue
414                                         * Group C: experimental */
415        },
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(), // google.type.Date
428        },
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(), // google.type.TimeOfDay
432        },
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(), // google.type.DateTime
436        },
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(), // google.type.Decimal
440        },
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(), // Group D: unsupported
446        },
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(), // Group D: unsupported
450        },
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(), // unreachable actually
459                };
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}