1use std::collections::BTreeMap;
27use std::str::{FromStr, from_utf8};
28
29use aws_lc_rs::digest;
30use serde_json::from_slice;
31
32use crate::decode::{AvroRead, bound_block_object_count, decode};
33use crate::error::{DecodeError, Error as AvroError};
34use crate::schema::{
35 FullName, NamedSchemaPiece, ParseSchemaError, RecordField, ResolvedDefaultValueField,
36 ResolvedRecordField, Schema, SchemaNodeOrNamed, SchemaPiece, SchemaPieceOrNamed,
37 SchemaPieceRefOrNamed, resolve_schemas,
38};
39use crate::types::Value;
40use crate::{Codec, SchemaResolutionError, util};
41
42#[derive(Debug, Clone)]
43pub(crate) struct Header {
44 writer_schema: Schema,
45 marker: [u8; 16],
46 codec: Codec,
47}
48
49impl Header {
50 pub fn from_reader<R: AvroRead>(reader: &mut R) -> Result<Header, AvroError> {
51 let meta_schema = Schema {
52 named: vec![],
53 indices: Default::default(),
54 top: SchemaPiece::Map(Box::new(SchemaPiece::Bytes.into())).into(),
55 };
56
57 let mut buf = [0u8; 4];
58 reader.read_exact(&mut buf)?;
59
60 if buf != [b'O', b'b', b'j', 1u8] {
61 return Err(AvroError::Decode(DecodeError::WrongHeaderMagic(buf)));
62 }
63
64 if let Value::Map(meta) = decode(meta_schema.top_node(), reader)? {
65 let json = meta
67 .get("avro.schema")
68 .ok_or(AvroError::Decode(DecodeError::MissingAvroDotSchema))
69 .and_then(|bytes| {
70 if let Value::Bytes(ref bytes) = *bytes {
71 from_slice(bytes.as_ref()).map_err(|e| {
72 AvroError::ParseSchema(ParseSchemaError::new(format!(
73 "unable to decode schema bytes: {}",
74 e
75 )))
76 })
77 } else {
78 unreachable!()
79 }
80 })?;
81 let writer_schema = Schema::parse(&json).map_err(|e| {
82 ParseSchemaError::new(format!("unable to parse json as avro schema: {}", e))
83 })?;
84
85 let codec = meta
86 .get("avro.codec")
87 .map(|val| match val {
88 Value::Bytes(bytes) => from_utf8(bytes.as_ref())
89 .map_err(|_e| AvroError::Decode(DecodeError::CodecUtf8Error))
90 .and_then(|codec| {
91 Codec::from_str(codec).map_err(|_| {
92 AvroError::Decode(DecodeError::UnrecognizedCodec(codec.to_string()))
93 })
94 }),
95 _ => unreachable!(),
96 })
97 .unwrap_or(Ok(Codec::Null))?;
98
99 let mut marker = [0u8; 16];
100 reader.read_exact(&mut marker)?;
101
102 Ok(Header {
103 writer_schema,
104 marker,
105 codec,
106 })
107 } else {
108 unreachable!()
109 }
110 }
111
112 pub fn into_parts(self) -> (Schema, [u8; 16], Codec) {
113 (self.writer_schema, self.marker, self.codec)
114 }
115}
116
117pub struct Reader<R> {
118 header: Header,
119 inner: R,
120 errored: bool,
121 resolved_schema: Option<Schema>,
122 messages_remaining: usize,
123 buf: Vec<u8>,
125 buf_idx: usize,
126}
127
128pub struct BlockIter<R> {
130 inner: Reader<R>,
131}
132
133#[derive(Debug, Clone)]
135pub struct Block {
136 pub bytes: Vec<u8>,
138 pub len: usize,
140}
141
142impl<R: AvroRead> BlockIter<R> {
143 pub fn with_schema(reader_schema: &Schema, inner: R) -> Result<Self, AvroError> {
144 Ok(Self {
145 inner: Reader::with_schema(reader_schema, inner)?,
146 })
147 }
148}
149
150impl<R: AvroRead> Iterator for BlockIter<R> {
151 type Item = Result<Block, AvroError>;
152
153 fn next(&mut self) -> Option<Self::Item> {
154 assert!(self.inner.is_empty());
155
156 match self.inner.read_block_next() {
157 Ok(()) => {
158 if self.inner.is_empty() {
159 None
160 } else {
161 let bytes = std::mem::take(&mut self.inner.buf);
162 let len = std::mem::take(&mut self.inner.messages_remaining);
163 Some(Ok(Block { bytes, len }))
164 }
165 }
166 Err(e) => Some(Err(e)),
167 }
168 }
169}
170
171impl<R: AvroRead> Reader<R> {
172 pub fn new(mut inner: R) -> Result<Reader<R>, AvroError> {
177 let header = Header::from_reader(&mut inner)?;
178 let reader = Reader {
179 header,
180 inner,
181 errored: false,
182 resolved_schema: None,
183 messages_remaining: 0,
184 buf: vec![],
185 buf_idx: 0,
186 };
187 Ok(reader)
188 }
189
190 pub fn with_schema(reader_schema: &Schema, mut inner: R) -> Result<Reader<R>, AvroError> {
195 let header = Header::from_reader(&mut inner)?;
196
197 let writer_schema = &header.writer_schema;
198 let resolved_schema = if reader_schema.fingerprint(&digest::SHA256).bytes
199 != writer_schema.fingerprint(&digest::SHA256).bytes
200 {
201 Some(resolve_schemas(writer_schema, reader_schema)?)
202 } else {
203 None
204 };
205
206 Ok(Reader {
207 header,
208 errored: false,
209 resolved_schema,
210 inner,
211 messages_remaining: 0,
212 buf: vec![],
213 buf_idx: 0,
214 })
215 }
216
217 pub fn writer_schema(&self) -> &Schema {
219 &self.header.writer_schema
220 }
221
222 pub fn schema(&self) -> &Schema {
226 match &self.resolved_schema {
227 Some(schema) => schema,
228 None => self.writer_schema(),
229 }
230 }
231
232 #[inline]
233 pub fn read_next(&mut self) -> Result<Option<Value>, AvroError> {
235 if self.is_empty() {
236 self.read_block_next()?;
237 if self.is_empty() {
238 return Ok(None);
239 }
240 }
241
242 let mut block_bytes = &self.buf[self.buf_idx..];
243 let b_original = block_bytes.len();
244 let schema = self.schema();
245 let item = from_avro_datum(schema, &mut block_bytes)?;
246 self.buf_idx += b_original - block_bytes.len();
247 self.messages_remaining -= 1;
248 Ok(Some(item))
249 }
250
251 fn is_empty(&self) -> bool {
252 self.messages_remaining == 0
253 }
254
255 fn fill_buf(&mut self, n: usize) -> Result<(), AvroError> {
256 if n >= self.buf.len() {
258 self.buf.resize(n, 0);
259 }
260
261 self.inner.read_exact(&mut self.buf[..n])?;
262 self.buf.truncate(n);
272 self.buf_idx = 0;
273 Ok(())
274 }
275
276 fn read_block_next(&mut self) -> Result<(), AvroError> {
277 assert!(self.is_empty(), "Expected self to be empty!");
278 match util::read_long(&mut self.inner) {
279 Ok(block_len) => {
280 self.messages_remaining = util::safe_len(block_len as usize)?;
287 let block_bytes = util::safe_len(util::read_long(&mut self.inner)? as usize)?;
288 self.fill_buf(block_bytes)?;
289 let mut marker = [0u8; 16];
290 self.inner.read_exact(&mut marker)?;
291
292 if marker != self.header.marker {
293 return Err(DecodeError::MismatchedBlockHeader {
294 expected: self.header.marker,
295 actual: marker,
296 }
297 .into());
298 }
299
300 self.header.codec.decompress(&mut self.buf)?;
307
308 let count = self.messages_remaining;
317 let payload_len = self.buf.len();
318 bound_block_object_count(self.schema().top_node(), count, payload_len)?;
319
320 Ok(())
321 }
322 Err(e) => {
323 if let AvroError::IO(std::io::ErrorKind::UnexpectedEof) = e {
324 Ok(())
326 } else {
327 Err(e)
328 }
329 }
330 }
331 }
332}
333
334impl<R: AvroRead> Iterator for Reader<R> {
335 type Item = Result<Value, AvroError>;
336
337 fn next(&mut self) -> Option<Self::Item> {
338 if self.errored {
340 return None;
341 };
342 match self.read_next() {
343 Ok(opt) => opt.map(Ok),
344 Err(e) => {
345 self.errored = true;
346 Some(Err(e))
347 }
348 }
349 }
350}
351
352pub struct SchemaResolver<'a> {
353 pub named: Vec<Option<NamedSchemaPiece>>,
354 pub indices: BTreeMap<FullName, usize>,
355 pub human_readable_field_path: Vec<String>,
356 pub current_human_readable_path_start: usize,
357 pub writer_to_reader_names: BTreeMap<usize, usize>,
358 pub reader_to_writer_names: BTreeMap<usize, usize>,
359 pub reader_to_resolved_names: BTreeMap<usize, usize>,
360 #[allow(dead_code)]
361 pub reader_fullnames: BTreeMap<usize, &'a FullName>,
362 pub reader_schema: &'a Schema,
363}
364
365impl<'a> SchemaResolver<'a> {
366 fn resolve_named(
367 &mut self,
368 writer: &Schema,
369 reader: &Schema,
370 writer_index: usize,
371 reader_index: usize,
372 ) -> Result<SchemaPiece, AvroError> {
373 let ws = writer.lookup(writer_index);
374 let rs = reader.lookup(reader_index);
375 let typ = match (&ws.piece, &rs.piece) {
376 (
377 SchemaPiece::Record {
378 fields: w_fields,
379 lookup: w_lookup,
380 ..
381 },
382 SchemaPiece::Record {
383 fields: r_fields,
384 lookup: _r_lookup,
385 ..
386 },
387 ) => {
388 let mut defaults = Vec::new();
389 let mut fields: Vec<Option<RecordField>> = Vec::new();
390 for (r_index, rf) in r_fields.iter().enumerate() {
391 match w_lookup.get(&rf.name) {
392 None => {
393 let default_field = match &rf.default {
394 Some(v) => ResolvedDefaultValueField {
395 name: rf.name.clone(),
396 doc: rf.doc.clone(),
397 default: reader
398 .top_node_or_named()
399 .step(&rf.schema)
400 .lookup()
401 .json_to_value(v)?,
402 order: rf.order.clone(),
403 position: r_index,
404 },
405 None => return Err(SchemaResolutionError::new(format!(
406 "Reader field `{}.{}` not found in writer, and has no default",
407 self.get_current_human_readable_path(),
408 rf.name
409 ))
410 .into()),
411 };
412 defaults.push(default_field);
413 }
414 Some(w_index) => {
415 if fields.len() > *w_index && fields[*w_index].is_some() {
416 return Err(SchemaResolutionError::new(format!(
417 "Duplicate field `{}.{}` in schema",
418 self.get_current_human_readable_path(),
419 rf.name
420 ))
421 .into());
422 }
423 let wf = &w_fields[*w_index];
424 let w_node = SchemaNodeOrNamed {
425 root: writer,
426 inner: wf.schema.as_ref(),
427 };
428 let r_node = SchemaNodeOrNamed {
429 root: reader,
430 inner: rf.schema.as_ref(),
431 };
432
433 self.human_readable_field_path.push(rf.name.clone());
434 let new_inner = self.resolve(w_node, r_node)?;
435 self.human_readable_field_path.pop();
436
437 let field = RecordField {
438 name: rf.name.clone(),
439 doc: rf.doc.clone(),
440 default: rf.default.clone(),
441 schema: new_inner,
442 order: rf.order.clone(),
443 position: r_index,
444 };
445 while fields.len() <= *w_index {
446 fields.push(None);
447 }
448 fields[*w_index] = Some(field)
449 }
450 }
451 }
452 while fields.len() < w_fields.len() {
453 fields.push(None);
454 }
455 let mut n_present = 0;
456 let fields = fields
457 .into_iter()
458 .enumerate()
459 .map(|(i, rf)| match rf {
460 Some(rf) => {
461 n_present += 1;
462 ResolvedRecordField::Present(rf)
463 }
464 None => {
465 let writer_schema_piece = SchemaNodeOrNamed {
477 root: writer,
478 inner: w_fields[i].schema.as_ref(),
479 }
480 .to_schema();
481 ResolvedRecordField::Absent(writer_schema_piece)
482 }
483 })
484 .collect();
485 let n_reader_fields = defaults.len() + n_present;
486 SchemaPiece::ResolveRecord {
487 defaults,
488 fields,
489 n_reader_fields,
490 }
491 }
492 (
493 SchemaPiece::Enum {
494 symbols: w_symbols, ..
495 },
496 SchemaPiece::Enum {
497 symbols: r_symbols,
498 doc,
499 default_idx,
500 },
501 ) => {
502 let r_map = r_symbols
503 .iter()
504 .enumerate()
505 .map(|(i, s)| (s, i))
506 .collect::<BTreeMap<_, _>>();
507 let symbols = w_symbols
508 .iter()
509 .map(|s| {
510 r_map
511 .get(s)
512 .map(|i| (*i, s.clone()))
513 .ok_or_else(|| s.clone())
514 })
515 .collect();
516 SchemaPiece::ResolveEnum {
517 doc: doc.clone(),
518 symbols,
519 default: default_idx.map(|i| (i, r_symbols[i].clone())),
520 }
521 }
522 (SchemaPiece::Fixed { size: wsz }, SchemaPiece::Fixed { size: rsz }) => {
523 if *wsz == *rsz {
524 SchemaPiece::Fixed { size: *wsz }
525 } else {
526 return Err(SchemaResolutionError::new(format!(
527 "Fixed schema {:?}: sizes don't match ({}, {}) for field `{}`",
528 &rs.name,
529 wsz,
530 rsz,
531 self.get_current_human_readable_path(),
532 ))
533 .into());
534 }
535 }
536 (
537 SchemaPiece::Decimal {
538 precision: wp,
539 scale: wscale,
540 fixed_size: wsz,
541 },
542 SchemaPiece::Decimal {
543 precision: rp,
544 scale: rscale,
545 fixed_size: rsz,
546 },
547 ) => {
548 if wp != rp {
549 return Err(SchemaResolutionError::new(format!(
550 "Decimal schema {:?}: precisions don't match: {}, {} for field `{}`",
551 &rs.name,
552 wp,
553 rp,
554 self.get_current_human_readable_path(),
555 ))
556 .into());
557 }
558 if wscale != rscale {
559 return Err(SchemaResolutionError::new(format!(
560 "Decimal schema {:?}: sizes don't match: {}, {} for field `{}`",
561 &rs.name,
562 wscale,
563 rscale,
564 self.get_current_human_readable_path(),
565 ))
566 .into());
567 }
568 if wsz != rsz {
569 return Err(SchemaResolutionError::new(format!(
570 "Decimal schema {:?}: sizes don't match: {:?}, {:?} for field `{}`",
571 &rs.name,
572 wsz,
573 rsz,
574 self.get_current_human_readable_path(),
575 ))
576 .into());
577 }
578 SchemaPiece::Decimal {
579 precision: *wp,
580 scale: *wscale,
581 fixed_size: *wsz,
582 }
583 }
584 (SchemaPiece::Decimal { fixed_size, .. }, SchemaPiece::Fixed { size })
585 if *fixed_size == Some(*size) =>
586 {
587 SchemaPiece::Fixed { size: *size }
588 }
589 (
590 SchemaPiece::Fixed { size },
591 SchemaPiece::Decimal {
592 precision,
593 scale,
594 fixed_size,
595 },
596 ) if *fixed_size == Some(*size) => SchemaPiece::Decimal {
597 precision: *precision,
598 scale: *scale,
599 fixed_size: *fixed_size,
600 },
601
602 (_, SchemaPiece::ResolveRecord { .. })
603 | (_, SchemaPiece::ResolveEnum { .. })
604 | (SchemaPiece::ResolveRecord { .. }, _)
605 | (SchemaPiece::ResolveEnum { .. }, _) => {
606 return Err(SchemaResolutionError::new(
607 "Attempted to resolve an already resolved schema".to_string(),
608 )
609 .into());
610 }
611
612 (_wt, _rt) => {
613 return Err(SchemaResolutionError::new(format!(
614 "Non-matching schemas: writer: {:?}, reader: {:?}",
615 ws.name, rs.name
616 ))
617 .into());
618 }
619 };
620 Ok(typ)
621 }
622
623 pub fn resolve(
624 &mut self,
625 writer: SchemaNodeOrNamed,
626 reader: SchemaNodeOrNamed,
627 ) -> Result<SchemaPieceOrNamed, AvroError> {
628 let previous_human_readable_path_start = self.current_human_readable_path_start;
629 let (_, named_node) = reader.inner.get_piece_and_name(reader.root);
630 if let Some(full_name) = named_node {
631 self.current_human_readable_path_start = self.human_readable_field_path.len();
632 self.human_readable_field_path.push(full_name.human_name());
633 }
634
635 let inner = match (writer.inner, reader.inner) {
636 (
650 SchemaPieceRefOrNamed::Piece(SchemaPiece::Union(w_inner)),
651 SchemaPieceRefOrNamed::Piece(SchemaPiece::Union(r_inner)),
652 ) => {
653 let w2r = self.writer_to_reader_names.clone();
654 let permutation = w_inner
662 .variants()
663 .iter()
664 .map(|w_variant| {
665 let (r_idx, r_variant) =
666 r_inner.match_promote_writer(w_variant, &w2r).ok_or_else(|| {
667 SchemaResolutionError::new(format!(
668 "Failed to match writer union variant `{}` against any variant in the reader for field `{}`",
669 w_variant.get_human_name(writer.root),
670 self.get_current_human_readable_path()
671 ))
672 })?;
673 let resolved =
674 self.resolve(writer.step(w_variant), reader.step(r_variant))?;
675 Ok((r_idx, resolved))
676 })
677 .collect();
678 let n_reader_variants = r_inner.variants().len();
679 let reader_null_variant = r_inner
680 .variants()
681 .iter()
682 .position(|v| v == &SchemaPieceOrNamed::Piece(SchemaPiece::Null));
683 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveUnionUnion {
684 permutation,
685 n_reader_variants,
686 reader_null_variant,
687 })
688 }
689 (other, SchemaPieceRefOrNamed::Piece(SchemaPiece::Union(r_inner))) => {
691 let n_reader_variants = r_inner.variants().len();
692 let reader_null_variant = r_inner
693 .variants()
694 .iter()
695 .position(|v| v == &SchemaPieceOrNamed::Piece(SchemaPiece::Null));
696 let (index, r_inner) = r_inner
697 .match_ref_promote_writer(other, &self.writer_to_reader_names)
698 .ok_or_else(|| {
699 SchemaResolutionError::new(
700 format!("No matching schema in reader union for writer type `{}` for field `{}`",
701 other.get_human_name(writer.root),
702 self.get_current_human_readable_path()))
703 })?;
704 let inner = Box::new(self.resolve(writer.step_ref(other), reader.step(r_inner))?);
705 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveConcreteUnion {
706 index,
707 inner,
708 n_reader_variants,
709 reader_null_variant,
710 })
711 }
712 (SchemaPieceRefOrNamed::Piece(SchemaPiece::Union(w_inner)), other) => {
714 let (index, w_inner) = w_inner
715 .match_ref_promote_reader(other, &self.reader_to_writer_names)
716 .ok_or_else(|| {
717 SchemaResolutionError::new(
722 format!("No matching schema in writer union for reader type `{}` for field `{}`",
723 other.get_human_name(reader.root),
724 self.get_current_human_readable_path()))
725 })?;
726 let inner = Box::new(self.resolve(writer.step(w_inner), reader.step_ref(other))?);
727 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveUnionConcrete { index, inner })
728 }
729 (SchemaPieceRefOrNamed::Piece(wp), SchemaPieceRefOrNamed::Piece(rp)) => {
731 match (wp, rp) {
732 (SchemaPiece::TimestampMilli, SchemaPiece::TimestampMicro) => {
739 SchemaPieceOrNamed::Piece(SchemaPiece::TimestampMilli)
740 }
741 (SchemaPiece::TimestampMicro, SchemaPiece::TimestampMilli) => {
743 SchemaPieceOrNamed::Piece(SchemaPiece::TimestampMicro)
744 }
745 (SchemaPiece::Date, SchemaPiece::TimestampMilli)
746 | (SchemaPiece::Date, SchemaPiece::TimestampMicro) => {
747 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveDateTimestamp)
748 }
749 (wp, rp) if wp.is_underlying_int() && rp.is_underlying_int() => {
750 SchemaPieceOrNamed::Piece(rp.clone()) }
752 (wp, rp) if wp.is_underlying_long() && rp.is_underlying_long() => {
753 SchemaPieceOrNamed::Piece(rp.clone()) }
755 (wp, SchemaPiece::TimestampMilli) if wp.is_underlying_int() => {
756 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveIntTsMilli)
757 }
758 (wp, SchemaPiece::TimestampMicro) if wp.is_underlying_int() => {
759 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveIntTsMicro)
760 }
761 (SchemaPiece::Null, SchemaPiece::Null) => {
762 SchemaPieceOrNamed::Piece(SchemaPiece::Null)
763 }
764 (SchemaPiece::Boolean, SchemaPiece::Boolean) => {
765 SchemaPieceOrNamed::Piece(SchemaPiece::Boolean)
766 }
767 (SchemaPiece::Int, SchemaPiece::Long) => {
768 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveIntLong)
769 }
770 (SchemaPiece::Int, SchemaPiece::Float) => {
771 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveIntFloat)
772 }
773 (SchemaPiece::Int, SchemaPiece::Double) => {
774 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveIntDouble)
775 }
776 (SchemaPiece::Long, SchemaPiece::Float) => {
777 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveLongFloat)
778 }
779 (SchemaPiece::Long, SchemaPiece::Double) => {
780 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveLongDouble)
781 }
782 (SchemaPiece::Float, SchemaPiece::Float) => {
783 SchemaPieceOrNamed::Piece(SchemaPiece::Float)
784 }
785 (SchemaPiece::Float, SchemaPiece::Double) => {
786 SchemaPieceOrNamed::Piece(SchemaPiece::ResolveFloatDouble)
787 }
788 (SchemaPiece::Double, SchemaPiece::Double) => {
789 SchemaPieceOrNamed::Piece(SchemaPiece::Double)
790 }
791 (b, SchemaPiece::Bytes)
792 if b == &SchemaPiece::Bytes || b == &SchemaPiece::String =>
793 {
794 SchemaPieceOrNamed::Piece(SchemaPiece::Bytes)
795 }
796 (s, SchemaPiece::String)
797 if s == &SchemaPiece::String || s == &SchemaPiece::Bytes =>
798 {
799 SchemaPieceOrNamed::Piece(SchemaPiece::String)
800 }
801 (SchemaPiece::Array(w_inner), SchemaPiece::Array(r_inner)) => {
802 let inner =
803 self.resolve(writer.step(&**w_inner), reader.step(&**r_inner))?;
804 SchemaPieceOrNamed::Piece(SchemaPiece::Array(Box::new(inner)))
805 }
806 (SchemaPiece::Map(w_inner), SchemaPiece::Map(r_inner)) => {
807 let inner =
808 self.resolve(writer.step(&**w_inner), reader.step(&**r_inner))?;
809 SchemaPieceOrNamed::Piece(SchemaPiece::Map(Box::new(inner)))
810 }
811 (
812 SchemaPiece::Decimal {
813 precision: wp,
814 scale: ws,
815 fixed_size: wf,
816 },
817 SchemaPiece::Decimal {
818 precision: rp,
819 scale: rs,
820 fixed_size: rf,
821 },
822 ) => {
823 if wp == rp && ws == rs && wf == rf {
824 SchemaPieceOrNamed::Piece(SchemaPiece::Decimal {
825 precision: *wp,
826 scale: *ws,
827 fixed_size: *wf,
828 })
829 } else {
830 return Err(SchemaResolutionError::new(format!(
831 "Decimal types must match in precision, scale, and fixed size. \
832 Got ({:?}, {:?}, {:?}); ({:?}, {:?}. {:?}) for field `{}`",
833 wp,
834 ws,
835 wf,
836 rp,
837 rs,
838 rf,
839 self.get_current_human_readable_path(),
840 ))
841 .into());
842 }
843 }
844 (SchemaPiece::Decimal { fixed_size, .. }, SchemaPiece::Bytes)
845 if *fixed_size == None =>
846 {
847 SchemaPieceOrNamed::Piece(SchemaPiece::Bytes)
848 }
849 (SchemaPiece::Json, SchemaPiece::Json) => {
853 SchemaPieceOrNamed::Piece(SchemaPiece::Json)
854 }
855 (SchemaPiece::Uuid, SchemaPiece::Uuid) => {
856 SchemaPieceOrNamed::Piece(SchemaPiece::Uuid)
857 }
858 (
859 SchemaPiece::Bytes,
860 SchemaPiece::Decimal {
861 precision,
862 scale,
863 fixed_size,
864 },
865 ) if *fixed_size == None => SchemaPieceOrNamed::Piece(SchemaPiece::Decimal {
866 precision: *precision,
867 scale: *scale,
868 fixed_size: *fixed_size,
869 }),
870 (ws, rs) => {
871 return Err(SchemaResolutionError::new(format!(
872 "Writer schema has type `{:?}`, but reader schema has type `{:?}` for field `{}`",
873 ws,
874 rs,
875 self.get_current_human_readable_path(),
876 ))
877 .into());
878 }
879 }
880 }
881 (SchemaPieceRefOrNamed::Named(w_index), SchemaPieceRefOrNamed::Named(r_index)) => {
883 if self.writer_to_reader_names.get(&w_index) != Some(&r_index) {
884 let (w_name, r_name) = (
886 &writer.root.lookup(w_index).name,
887 &reader.root.lookup(r_index).name,
888 );
889 return Err(SchemaResolutionError::new(format!("Attempted to resolve writer schema node named {:?} against reader schema node named {:?}", w_name, r_name)).into());
890 }
891 let idx = match self.reader_to_resolved_names.get(&r_index) {
894 Some(resolved) => *resolved,
895 None => {
896 let resolved_idx = self.named.len();
902 self.reader_to_resolved_names.insert(r_index, resolved_idx);
903 self.named.push(None);
904 let piece =
905 match self.resolve_named(writer.root, reader.root, w_index, r_index) {
906 Ok(piece) => piece,
907 Err(e) => {
908 self.named.truncate(resolved_idx);
917 self.reader_to_resolved_names
918 .retain(|_, v| *v < resolved_idx);
919 self.indices.retain(|_, v| *v < resolved_idx);
920 return Err(e);
921 }
922 };
923 let name = &self.reader_schema.named[r_index].name;
924 let ns = NamedSchemaPiece {
925 name: name.clone(),
926 piece,
927 };
928 self.named[resolved_idx] = Some(ns);
929 self.indices.insert(name.clone(), resolved_idx);
930
931 resolved_idx
932 }
933 };
934 SchemaPieceOrNamed::Named(idx)
935 }
936 (ws, rs) => {
937 return Err(SchemaResolutionError::new(format!(
938 "Schemas don't match: {:?}, {:?} for field `{}`",
939 ws.get_piece_and_name(writer.root).0,
940 rs.get_piece_and_name(reader.root).0,
941 self.get_current_human_readable_path(),
942 ))
943 .into());
944 }
945 };
946 if named_node.is_some() {
947 self.human_readable_field_path.pop();
948 self.current_human_readable_path_start = previous_human_readable_path_start;
949 }
950 Ok(inner)
951 }
952
953 fn get_current_human_readable_path(&self) -> String {
954 self.human_readable_field_path[self.current_human_readable_path_start..].join(".")
955 }
956}
957
958pub fn from_avro_datum<R: AvroRead>(schema: &Schema, reader: &mut R) -> Result<Value, AvroError> {
967 let value = decode(schema.top_node(), reader)?;
968 Ok(value)
969}
970
971#[cfg(test)]
972mod tests {
973 use std::io::Cursor;
974
975 use mz_ore::assert_err;
976
977 use crate::Reader;
978 use crate::types::{Record, ToAvro};
979
980 use super::*;
981
982 fn ocf(schema_json: &str, blocks: &[(i64, i64, &[u8])]) -> Vec<u8> {
987 fn blob(bytes: &[u8], out: &mut Vec<u8>) {
988 util::zig_i64(bytes.len() as i64, out);
989 out.extend_from_slice(bytes);
990 }
991
992 let marker = [7u8; 16];
993 let mut out = b"Obj\x01".to_vec();
994 util::zig_i64(2, &mut out); blob(b"avro.schema", &mut out);
996 blob(schema_json.as_bytes(), &mut out);
997 blob(b"avro.codec", &mut out);
998 blob(b"null", &mut out);
999 util::zig_i64(0, &mut out); out.extend_from_slice(&marker);
1001 for (count, size, payload) in blocks {
1002 util::zig_i64(*count, &mut out);
1003 util::zig_i64(*size, &mut out);
1004 out.extend_from_slice(payload);
1005 out.extend_from_slice(&marker);
1006 }
1007 out
1008 }
1009
1010 const ZERO_WIDTH_SCHEMA: &str =
1012 r#"{"type":"record","name":"R","fields":[{"name":"g","type":"null"}]}"#;
1013
1014 #[mz_ore::test]
1015 fn reader_does_not_decode_a_previous_blocks_bytes() {
1016 let long = &[0x0a, b'A', b'B', 0x02, b'x', b'C'][..]; let short = &[0x04, b'a', b'b'][..]; let file = ocf(
1028 r#""string""#,
1029 &[(1, long.len() as i64, long), (2, short.len() as i64, short)],
1030 );
1031
1032 let items: Vec<_> = Reader::new(&file[..]).expect("OCF header parses").collect();
1033 assert_eq!(items.len(), 3, "expected two values then an error");
1034 assert_eq!(
1035 items[1].as_ref().expect("block 2's real value decodes"),
1036 &Value::String("ab".into())
1037 );
1038 assert_err!(&items[2]);
1039 }
1040
1041 #[mz_ore::test]
1042 fn reader_rejects_zero_width_block_count_within_allocation_budget() {
1043 let file = ocf(ZERO_WIDTH_SCHEMA, &[(100_000_000, 0, &[])]);
1048 let err = Reader::new(&file[..])
1049 .expect("OCF header parses")
1050 .find_map(|item| item.err())
1051 .expect("the oversized count must be rejected");
1052 assert!(
1053 err.to_string().contains("exceeds limit"),
1054 "unexpected error: {err}"
1055 );
1056 }
1057
1058 #[mz_ore::test]
1059 fn reader_accepts_honest_zero_width_block() {
1060 let file = ocf(ZERO_WIDTH_SCHEMA, &[(3, 0, &[])]);
1063 let values: Vec<Value> = Reader::new(&file[..])
1064 .expect("OCF header parses")
1065 .collect::<Result<_, _>>()
1066 .expect("an honest zero-width block must decode");
1067 assert_eq!(values.len(), 3);
1068 }
1069
1070 #[mz_ore::test]
1071 fn reader_rejects_block_count_exceeding_payload() {
1072 let payload = &[0x04, b'a', b'b'][..];
1075 let file = ocf(r#""string""#, &[(100, payload.len() as i64, payload)]);
1076 let err = Reader::new(&file[..])
1077 .expect("OCF header parses")
1078 .find_map(|item| item.err())
1079 .expect("a count larger than the payload must be rejected");
1080 assert!(
1081 err.to_string().contains("exceeds block payload"),
1082 "unexpected error: {err}"
1083 );
1084 }
1085
1086 #[mz_ore::test]
1087 fn reader_rejects_huge_block_object_count() {
1088 let bytes: &[u8] = &[
1092 0x4f, 0x62, 0x6a, 0x01, 0x04, 0x16, 0x61, 0x76, 0x72, 0x6f, 0x2e, 0x73, 0x63, 0x68,
1093 0x65, 0x6d, 0x61, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
1094 0x6e, 0x75, 0x6c, 0x6c, 0x22, 0x20, 0x00, 0x00, 0x00, 0x64, 0x64, 0x64, 0x64, 0x64,
1095 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0xbf, 0x00, 0x26,
1096 0x35, 0x33, 0x39, 0x33, 0x34, 0x38, 0x33, 0xcd, 0x45, 0x38, 0x56, 0xb1, 0x00, 0x00,
1097 0x64, 0x64, 0x7a, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
1098 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64,
1099 ];
1100 let reader = Reader::new(bytes).expect("OCF header parses");
1101 assert!(
1104 reader.into_iter().any(|item| item.is_err()),
1105 "expected a decode error from the oversized block count"
1106 );
1107 }
1108
1109 static SCHEMA: &str = r#"
1110 {
1111 "type": "record",
1112 "name": "test",
1113 "fields": [
1114 {"name": "a", "type": "long", "default": 42},
1115 {"name": "b", "type": "string"}
1116 ]
1117 }
1118 "#;
1119 static UNION_SCHEMA: &str = r#"
1120 ["null", "long"]
1121 "#;
1122 static ENCODED: &[u8] = &[
1123 79u8, 98u8, 106u8, 1u8, 4u8, 22u8, 97u8, 118u8, 114u8, 111u8, 46u8, 115u8, 99u8, 104u8,
1124 101u8, 109u8, 97u8, 222u8, 1u8, 123u8, 34u8, 116u8, 121u8, 112u8, 101u8, 34u8, 58u8, 34u8,
1125 114u8, 101u8, 99u8, 111u8, 114u8, 100u8, 34u8, 44u8, 34u8, 110u8, 97u8, 109u8, 101u8, 34u8,
1126 58u8, 34u8, 116u8, 101u8, 115u8, 116u8, 34u8, 44u8, 34u8, 102u8, 105u8, 101u8, 108u8,
1127 100u8, 115u8, 34u8, 58u8, 91u8, 123u8, 34u8, 110u8, 97u8, 109u8, 101u8, 34u8, 58u8, 34u8,
1128 97u8, 34u8, 44u8, 34u8, 116u8, 121u8, 112u8, 101u8, 34u8, 58u8, 34u8, 108u8, 111u8, 110u8,
1129 103u8, 34u8, 44u8, 34u8, 100u8, 101u8, 102u8, 97u8, 117u8, 108u8, 116u8, 34u8, 58u8, 52u8,
1130 50u8, 125u8, 44u8, 123u8, 34u8, 110u8, 97u8, 109u8, 101u8, 34u8, 58u8, 34u8, 98u8, 34u8,
1131 44u8, 34u8, 116u8, 121u8, 112u8, 101u8, 34u8, 58u8, 34u8, 115u8, 116u8, 114u8, 105u8,
1132 110u8, 103u8, 34u8, 125u8, 93u8, 125u8, 20u8, 97u8, 118u8, 114u8, 111u8, 46u8, 99u8, 111u8,
1133 100u8, 101u8, 99u8, 8u8, 110u8, 117u8, 108u8, 108u8, 0u8, 94u8, 61u8, 54u8, 221u8, 190u8,
1134 207u8, 108u8, 180u8, 158u8, 57u8, 114u8, 40u8, 173u8, 199u8, 228u8, 239u8, 4u8, 20u8, 54u8,
1135 6u8, 102u8, 111u8, 111u8, 84u8, 6u8, 98u8, 97u8, 114u8, 94u8, 61u8, 54u8, 221u8, 190u8,
1136 207u8, 108u8, 180u8, 158u8, 57u8, 114u8, 40u8, 173u8, 199u8, 228u8, 239u8,
1137 ];
1138
1139 #[mz_ore::test]
1140 fn test_from_avro_datum() {
1141 let schema: Schema = SCHEMA.parse().unwrap();
1142 let mut encoded: &'static [u8] = &[54, 6, 102, 111, 111];
1143
1144 let mut record = Record::new(schema.top_node()).unwrap();
1145 record.put("a", 27i64);
1146 record.put("b", "foo");
1147 let expected = record.avro();
1148
1149 assert_eq!(from_avro_datum(&schema, &mut encoded).unwrap(), expected);
1150 }
1151
1152 #[mz_ore::test]
1153 fn test_null_union() {
1154 let schema: Schema = UNION_SCHEMA.parse().unwrap();
1155 let mut encoded: &'static [u8] = &[2, 0];
1156
1157 assert_eq!(
1158 from_avro_datum(&schema, &mut encoded).unwrap(),
1159 Value::Union {
1160 index: 1,
1161 inner: Box::new(Value::Long(0)),
1162 n_variants: 2,
1163 null_variant: Some(0)
1164 }
1165 );
1166 }
1167
1168 #[mz_ore::test]
1169 #[cfg_attr(miri, ignore)] fn test_reader_stream() {
1171 let schema: Schema = SCHEMA.parse().unwrap();
1172 let reader = Reader::with_schema(&schema, ENCODED).unwrap();
1173
1174 let mut record1 = Record::new(schema.top_node()).unwrap();
1175 record1.put("a", 27i64);
1176 record1.put("b", "foo");
1177
1178 let mut record2 = Record::new(schema.top_node()).unwrap();
1179 record2.put("a", 42i64);
1180 record2.put("b", "bar");
1181
1182 let expected = [record1.avro(), record2.avro()];
1183
1184 for (i, value) in reader.enumerate() {
1185 assert_eq!(value.unwrap(), expected[i]);
1186 }
1187 }
1188
1189 #[mz_ore::test]
1190 fn test_reader_invalid_header() {
1191 let schema: Schema = SCHEMA.parse().unwrap();
1192 let invalid = ENCODED.iter().skip(1).copied().collect::<Vec<u8>>();
1193 assert!(Reader::with_schema(&schema, &invalid[..]).is_err());
1194 }
1195
1196 #[mz_ore::test]
1197 #[cfg_attr(miri, ignore)] fn test_reader_invalid_block() {
1199 let schema: Schema = SCHEMA.parse().unwrap();
1200 let invalid = ENCODED
1201 .iter()
1202 .rev()
1203 .skip(19)
1204 .copied()
1205 .collect::<Vec<u8>>()
1206 .into_iter()
1207 .rev()
1208 .collect::<Vec<u8>>();
1209 let reader = Reader::with_schema(&schema, &invalid[..]).unwrap();
1210 for value in reader {
1211 assert_err!(value);
1212 }
1213 }
1214
1215 #[mz_ore::test]
1216 fn test_reader_empty_buffer() {
1217 let empty = Cursor::new(Vec::new());
1218 assert!(Reader::new(empty).is_err());
1219 }
1220
1221 #[mz_ore::test]
1222 fn test_reader_only_header() {
1223 let invalid = ENCODED.iter().copied().take(165).collect::<Vec<u8>>();
1224 let reader = Reader::new(&invalid[..]).unwrap();
1225 for value in reader {
1226 assert_err!(value);
1227 }
1228 }
1229
1230 #[mz_ore::test]
1231 fn test_resolution_nested_types_error() {
1232 let r = r#"
1233{
1234 "type": "record",
1235 "name": "com.materialize.foo",
1236 "fields": [
1237 {"name": "f1", "type": {"type": "record", "name": "com.materialize.bar", "fields": [{"name": "f1_1", "type": "int"}]}}
1238 ]
1239}
1240"#;
1241 let w = r#"
1242{
1243 "type": "record",
1244 "name": "com.materialize.foo",
1245 "fields": [
1246 {"name": "f1", "type": {"type": "record", "name": "com.materialize.bar", "fields": [{"name": "f1_1", "type": "double"}]}}
1247 ]
1248}
1249"#;
1250 let r: Schema = r.parse().unwrap();
1251 let w: Schema = w.parse().unwrap();
1252 let err_str = if let Result::Err(AvroError::ResolveSchema(SchemaResolutionError(s))) =
1253 resolve_schemas(&w, &r)
1254 {
1255 s
1256 } else {
1257 panic!("Expected schema resolution failure");
1258 };
1259 assert_eq!(
1263 &err_str,
1264 "Writer schema has type `Double`, but reader schema has type `Int` for field `com.materialize.bar.f1_1`"
1265 );
1266 }
1267
1268 #[mz_ore::test]
1269 fn test_extra_fields_without_default_error() {
1270 let r = r#"
1271{
1272 "type": "record",
1273 "name": "com.materialize.foo",
1274 "fields": [
1275 {"name": "f1", "type": "int"},
1276 {"name": "f2", "type": "int"}
1277 ]
1278}
1279"#;
1280 let w = r#"
1281{
1282 "type": "record",
1283 "name": "com.materialize.foo",
1284 "fields": [
1285 {"name": "f1", "type": "int"}
1286 ]
1287}
1288"#;
1289 let r: Schema = r.parse().unwrap();
1290 let w: Schema = w.parse().unwrap();
1291 let err_str = if let Result::Err(AvroError::ResolveSchema(SchemaResolutionError(s))) =
1292 resolve_schemas(&w, &r)
1293 {
1294 s
1295 } else {
1296 panic!("Expected schema resolution failure");
1297 };
1298 assert_eq!(
1299 &err_str,
1300 "Reader field `com.materialize.foo.f2` not found in writer, and has no default"
1301 );
1302 }
1303
1304 #[mz_ore::test]
1305 fn test_duplicate_field_error() {
1306 let r = r#"
1307{
1308 "type": "record",
1309 "name": "com.materialize.bar",
1310 "fields": [
1311 {"name": "f1", "type": "int"},
1312 {"name": "f1", "type": "int"}
1313 ]
1314}
1315"#;
1316 let w = r#"
1317{
1318 "type": "record",
1319 "name": "com.materialize.bar",
1320 "fields": [
1321 {"name": "f1", "type": "int"}
1322 ]
1323}
1324"#;
1325 let r: Schema = r.parse().unwrap();
1326 let w: Schema = w.parse().unwrap();
1327 let err_str = if let Result::Err(AvroError::ResolveSchema(SchemaResolutionError(s))) =
1328 resolve_schemas(&w, &r)
1329 {
1330 s
1331 } else {
1332 panic!("Expected schema resolution failure");
1333 };
1334 assert_eq!(
1335 &err_str,
1336 "Duplicate field `com.materialize.bar.f1` in schema"
1337 );
1338 }
1339
1340 #[mz_ore::test]
1341 fn test_decimal_field_mismatch_error() {
1342 let r = r#"
1343{
1344 "type": "record",
1345 "name": "com.materialize.foo",
1346 "fields": [
1347 {"name": "f1", "type": {"type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2}}
1348 ]
1349}
1350"#;
1351 let w = r#"
1352{
1353 "type": "record",
1354 "name": "com.materialize.foo",
1355 "fields": [
1356 {"name": "f1", "type": {"type": "bytes", "logicalType": "decimal", "precision": 5, "scale": 1}}
1357 ]
1358}
1359"#;
1360 let r: Schema = r.parse().unwrap();
1361 let w: Schema = w.parse().unwrap();
1362 let err_str = if let Result::Err(AvroError::ResolveSchema(SchemaResolutionError(s))) =
1363 resolve_schemas(&w, &r)
1364 {
1365 s
1366 } else {
1367 panic!("Expected schema resolution failure");
1368 };
1369 assert_eq!(
1370 &err_str,
1371 "Decimal types must match in precision, scale, and fixed size. Got (5, 1, None); (4, 2. None) for field `com.materialize.foo.f1`"
1372 );
1373 }
1374}