Skip to main content

feldera_adapterlib/
format.rs

1use std::any::Any;
2use std::borrow::Cow;
3use std::cmp::max;
4use std::fmt::{Display, Error as FmtError, Formatter};
5use std::fs::File;
6use std::hash::Hasher;
7use std::io::{Error as IoError, Read};
8use std::ops::{Add, AddAssign, Range};
9use std::sync::Arc;
10
11use actix_web::HttpRequest;
12use anyhow::Result as AnyResult;
13use dbsp::operator::input::StagedBuffers;
14use erased_serde::Serialize as ErasedSerialize;
15use feldera_types::config::ConnectorConfig;
16use feldera_types::program_schema::Relation;
17use feldera_types::serde_with_context::FieldParseError;
18use serde::Serialize;
19use serde::de::StdError;
20
21use crate::ConnectorMetadata;
22use crate::catalog::{InputCollectionHandle, SerBatchReader};
23use crate::errors::controller::ControllerError;
24use crate::postprocess::Postprocessor;
25use crate::preprocess::Preprocessor;
26use crate::transport::{OutputBatchType, Step};
27
28/// Trait that represents a specific data format.
29///
30/// This is a factory trait that creates parsers for a specific data format.
31pub trait InputFormat: Send + Sync {
32    /// Unique name of the data format.
33    fn name(&self) -> Cow<'static, str>;
34
35    /// Extract parser configuration from an HTTP request.
36    ///
37    /// Returns the extracted configuration cast to the `ErasedSerialize` trait
38    /// object (to keep this trait object-safe).
39    ///
40    /// # Discussion
41    ///
42    /// We could rely on the `serde_urlencoded` crate to deserialize the config
43    /// from the HTTP request, which is what most implementations will do
44    /// internally; however allowing the implementation to override this
45    /// method enables additional flexibility. For example, an
46    /// implementation may use `Content-Type` and other request headers, set
47    /// HTTP-specific defaults for config fields, etc.
48    fn config_from_http_request(
49        &self,
50        endpoint_name: &str,
51        request: &HttpRequest,
52    ) -> Result<Box<dyn ErasedSerialize>, ControllerError>;
53
54    /// Create a new parser for the format.
55    ///
56    /// # Arguments
57    ///
58    /// * `input_stream` - Input stream of the circuit to push parsed data to.
59    ///
60    /// * `config` - Format-specific configuration.
61    fn new_parser(
62        &self,
63        endpoint_name: &str,
64        input_stream: &InputCollectionHandle,
65        config: &serde_json::Value,
66    ) -> Result<Box<dyn Parser>, ControllerError>;
67}
68
69/// A collection of records associated with an input handle.
70///
71/// A [Parser] holds and adds records to an [InputBuffer].  The client, which is
72/// typically an [InputReader](crate::transport::InputReader), collects one or
73/// more [InputBuffer]s and pushes them to the circuit when the controller
74/// requests it.
75///
76/// # Pushing buffers into a circuit
77///
78/// There are two ways to push `InputBuffer`s into a circuit:
79///
80/// - With [InputBuffer::flush].  This immediately pushes the input buffer into
81///   the DBSP input handle.
82///
83/// - By passing buffers to [Parser::stage], which collects all of them into a
84///   [StagedBuffers].  Then, later, call [StagedBuffers::flush] to push the
85///   input buffers into the circuit.
86///
87/// Both approaches are equivalent in terms of correctness.  There can be a
88/// difference in performance, because [InputBuffer::flush] has a significant
89/// cost for a large number of records.  Using [StagedBuffers] has a similar
90/// cost, but it incurs it in the call to [Parser::stage] rather than in
91/// [StagedBuffers::flush].  This means that, if the input connector can buffer
92/// data ahead of the circuit's demand for it, the cost can be hidden and the
93/// circuit as a whole runs faster.
94pub trait InputBuffer: Any + Send {
95    /// Pushes all of the records into the circuit input handle, and discards
96    /// those records.
97    fn flush(&mut self);
98
99    /// Returns the number of buffered records and bytes.
100    fn len(&self) -> BufferSize;
101
102    /// Hashes the records in the input buffer into `hasher`, in order.  This is
103    /// used to ensure that input data remains the same for replay, so, for
104    /// equal data, it should remain the same from one program run to the next.
105    /// Data might be divided into `InputBuffer`s differently from one run to
106    /// the next, so the data fed into `hasher` should be the same if, for
107    /// example, records 0..10 then 10..20 are fed in one run and 0..5, 5..15,
108    /// 15..20 in another.
109    fn hash(&self, hasher: &mut dyn Hasher);
110
111    fn is_empty(&self) -> bool {
112        self.len().is_empty()
113    }
114
115    /// Removes the first `n` records from this input buffer and returns a new
116    /// [InputBuffer] that holds them. If fewer than `n` records are available,
117    /// returns all of them. May return `None` if this input buffer is empty (or
118    /// if `n` is 0).
119    ///
120    /// Some implementations can't implement `n` with full granularity. These
121    /// implementations might return more than `n` records.
122    ///
123    /// This is useful for extracting the records from one of several parser
124    /// threads to send to a single common thread to be pushed later.
125    ///
126    /// # Byte accounting
127    ///
128    /// This function must not increase or decrease the total number of bytes.
129    /// That is, if the returned buffer is named `head`, `self.len().bytes`
130    /// before the call must equal `self.len().bytes + head.len().bytes`
131    /// following the call.  Violating this invariant will cause the number of
132    /// buffered bytes reported by a pipeline never to fall to zero (or to wrap
133    /// around to `u64::MAX`).
134    fn take_some(&mut self, n: usize) -> Option<Box<dyn InputBuffer>>;
135
136    fn take_all(&mut self) -> Option<Box<dyn InputBuffer>> {
137        self.take_some(usize::MAX)
138    }
139}
140
141/// The size of an [InputBuffer].
142#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
143pub struct BufferSize {
144    /// The exact number of records in the buffer.
145    pub records: usize,
146
147    /// The number of bytes attributable to the buffer.
148    ///
149    /// This need not be exact.  (When a buffer is split with
150    /// [InputBuffer::take_some], it will usually not be exact.)
151    pub bytes: usize,
152}
153
154impl BufferSize {
155    /// The size of an empty buffer.
156    pub fn empty() -> Self {
157        Self::default()
158    }
159
160    /// Returns true if this buffer is empty.
161    pub fn is_empty(&self) -> bool {
162        self.records == 0 && self.bytes == 0
163    }
164}
165
166impl Add for BufferSize {
167    type Output = Self;
168
169    fn add(self, rhs: Self) -> Self::Output {
170        BufferSize {
171            records: self.records + rhs.records,
172            bytes: self.bytes + rhs.bytes,
173        }
174    }
175}
176
177impl AddAssign for BufferSize {
178    fn add_assign(&mut self, rhs: Self) {
179        self.records += rhs.records;
180        self.bytes += rhs.bytes;
181    }
182}
183
184impl InputBuffer for Option<Box<dyn InputBuffer>> {
185    fn len(&self) -> BufferSize {
186        self.as_ref()
187            .map_or(BufferSize::empty(), |buffer| buffer.len())
188    }
189
190    fn hash(&self, hasher: &mut dyn Hasher) {
191        if let Some(buffer) = self {
192            buffer.hash(hasher)
193        }
194    }
195
196    fn flush(&mut self) {
197        if let Some(buffer) = self.as_mut() {
198            buffer.flush()
199        }
200    }
201
202    fn take_some(&mut self, n: usize) -> Option<Box<dyn InputBuffer>> {
203        self.as_mut().and_then(|buffer| buffer.take_some(n))
204    }
205}
206
207impl InputBuffer for Box<dyn InputBuffer> {
208    fn len(&self) -> BufferSize {
209        self.as_ref().len()
210    }
211
212    fn hash(&self, hasher: &mut dyn Hasher) {
213        self.as_ref().hash(hasher)
214    }
215
216    fn flush(&mut self) {
217        self.as_mut().flush()
218    }
219
220    fn take_some(&mut self, n: usize) -> Option<Box<dyn InputBuffer>> {
221        self.as_mut().take_some(n)
222    }
223}
224
225impl InputBuffer for Vec<Box<dyn InputBuffer>> {
226    fn flush(&mut self) {
227        for v in self.iter_mut() {
228            v.flush();
229        }
230    }
231
232    fn len(&self) -> BufferSize {
233        let mut size = BufferSize::empty();
234        for v in self.iter() {
235            size += v.len();
236        }
237        size
238    }
239
240    fn hash(&self, hasher: &mut dyn Hasher) {
241        for v in self.iter() {
242            v.hash(hasher);
243        }
244    }
245
246    fn take_some(&mut self, n: usize) -> Option<Box<dyn InputBuffer>> {
247        let mut result = Vec::new();
248        let mut remaining = n;
249        // Index of first buffer that should be preserved
250        let mut index = 0;
251        for v in self.iter_mut() {
252            if remaining == 0 {
253                break;
254            }
255            let buf = v.take_some(remaining);
256            if let Some(ib) = buf {
257                let len = ib.len().records;
258                if remaining >= len {
259                    // This buffer will be completely used
260                    index += 1;
261                }
262                remaining = remaining.saturating_sub(len);
263                result.push(ib);
264            }
265        }
266        self.drain(0..index);
267        if result.is_empty() {
268            None
269        } else {
270            Some(Box::new(result))
271        }
272    }
273}
274
275/// If any of the InputBuffer elements is a Vec itself, flatten it recursively.
276/// Return the concatenated input buffers all downcast to T.
277pub fn flatten_nested<T>(buffers: Vec<Box<dyn InputBuffer>>) -> Vec<Box<T>>
278where
279    T: Any,
280{
281    fn inner<T>(input: Vec<Box<dyn InputBuffer>>, output: &mut Vec<Box<T>>)
282    where
283        T: Any,
284    {
285        for buffer in input {
286            let any = buffer as Box<dyn Any>;
287            match any.downcast::<Vec<Box<dyn InputBuffer>>>() {
288                Ok(vec) => inner(*vec, output),
289                Err(any) => output.push(any.downcast().unwrap()),
290            }
291        }
292    }
293
294    let mut output = Vec::new();
295    inner(buffers, &mut output);
296    output
297}
298
299/// A wrapper around a [StagedBuffers] that implements the [InputBuffer] trait.
300///
301/// The `StagedBuffers` trait is similar to `InputBuffer` in that it supports flushing
302/// a set of tuples to the circuit. Unlike `InputBuffer`, it doesn't support returning
303/// its size as a `BufferSize`. It also doesn't support hashing and taking a subset of
304/// records.
305///
306/// This wrapper adds the former by storing `BufferSize` collected from `InputBuffer`s
307/// used to create the `StagedBuffers`.
308///
309/// `hash()` and `take_some()` methods are unimplemented. Therefore this wrapper can only be
310/// safely used in contexts where these methods are not needed.
311///
312// FIXME: It would be better to encode the unimplemented functionality in the type system,
313// e.g., as an extension trait.
314pub struct StagedInputBuffer {
315    buffer: Box<dyn StagedBuffers>,
316    size: BufferSize,
317}
318
319impl StagedInputBuffer {
320    pub fn new(buffer: Box<dyn StagedBuffers>, size: BufferSize) -> Self {
321        Self { buffer, size }
322    }
323}
324
325impl InputBuffer for StagedInputBuffer {
326    fn flush(&mut self) {
327        self.buffer.flush()
328    }
329
330    fn len(&self) -> BufferSize {
331        self.size
332    }
333
334    fn hash(&self, _hasher: &mut dyn Hasher) {
335        unimplemented!()
336    }
337
338    fn take_some(&mut self, _n: usize) -> Option<Box<dyn InputBuffer>> {
339        unimplemented!()
340    }
341}
342
343/// Parses raw bytes into database records.
344pub trait Parser: Send + Sync {
345    /// Parses `data` into records and returns the records and any parse errors
346    /// that occurred.
347    ///
348    /// XXX it would be even better if this were `&self` and avoided keeping
349    /// state entirely.
350    fn parse(
351        &mut self,
352        data: &[u8],
353        metadata: Option<ConnectorMetadata>,
354    ) -> (Option<Box<dyn InputBuffer>>, Vec<ParseError>);
355
356    /// Stages all of the `buffers`, which must have been obtained from this
357    /// [Parser] or one forked from this one, into a [StagedBuffers] that may
358    /// later be used to push the collected data into the circuit.  See
359    /// [StagedBuffers] for more information.
360    fn stage(&self, buffers: Vec<Box<dyn InputBuffer>>) -> Box<dyn StagedBuffers>;
361
362    /// Returns an object that can be used to break a stream of incoming data
363    /// into complete records to pass to [Parser::parse].
364    fn splitter(&self) -> Box<dyn Splitter>;
365
366    /// Create a new parser with the same configuration as `self`.
367    ///
368    /// Used by multithreaded transport endpoints to create multiple parallel
369    /// input pipelines.
370    fn fork(&self) -> Box<dyn Parser>;
371}
372
373/// A parser with preprocessing for a streaming preprocessor
374pub struct StreamingPreprocessedParser {
375    preprocessor: Box<dyn Preprocessor>,
376    stream_splitter: StreamSplitter,
377    parser: Box<dyn Parser>,
378}
379
380impl StreamingPreprocessedParser {
381    pub fn new(preprocessor: Box<dyn Preprocessor>, parser: Box<dyn Parser>) -> Self {
382        Self {
383            preprocessor,
384            stream_splitter: StreamSplitter::new(parser.splitter()),
385            parser,
386        }
387    }
388}
389
390impl Parser for StreamingPreprocessedParser {
391    fn parse(
392        &mut self,
393        data: &[u8],
394        metadata: Option<ConnectorMetadata>,
395    ) -> (Option<Box<dyn InputBuffer>>, Vec<ParseError>) {
396        let (pre_data, mut pre_errors) = self
397            .preprocessor
398            .process_with_metadata(data, metadata.as_ref());
399        self.stream_splitter.append(&pre_data);
400        let mut parsed: Vec<Box<dyn InputBuffer>> = Vec::new();
401        while let Some(chunk) = self.stream_splitter.next(true) {
402            let (parsed_data, mut parse_errors) = self.parser.parse(chunk, metadata.clone());
403            pre_errors.append(&mut parse_errors);
404            if let Some(data) = parsed_data {
405                parsed.push(data);
406            }
407        }
408        if parsed.is_empty() {
409            (None, pre_errors)
410        } else {
411            (Some(Box::new(parsed)), pre_errors)
412        }
413    }
414
415    fn stage(&self, buffers: Vec<Box<dyn InputBuffer>>) -> Box<dyn StagedBuffers> {
416        self.parser.stage(buffers)
417    }
418
419    fn splitter(&self) -> Box<dyn Splitter> {
420        let pre_splitter = self.preprocessor.splitter();
421        if let Some(splitter) = pre_splitter {
422            return splitter;
423        }
424        self.parser.splitter()
425    }
426
427    fn fork(&self) -> Box<dyn Parser> {
428        Box::new(StreamingPreprocessedParser::new(
429            self.preprocessor.fork(),
430            self.parser.fork(),
431        ))
432    }
433}
434
435/// A parser with preprocessing for a message-oriented preprocessor
436pub struct MessageOrientedPreprocessedParser {
437    preprocessor: Box<dyn Preprocessor>,
438    parser: Box<dyn Parser>,
439}
440
441impl MessageOrientedPreprocessedParser {
442    pub fn new(preprocessor: Box<dyn Preprocessor>, parser: Box<dyn Parser>) -> Self {
443        Self {
444            preprocessor,
445            parser,
446        }
447    }
448}
449
450impl Parser for MessageOrientedPreprocessedParser {
451    fn parse(
452        &mut self,
453        data: &[u8],
454        metadata: Option<ConnectorMetadata>,
455    ) -> (Option<Box<dyn InputBuffer>>, Vec<ParseError>) {
456        let (pre_data, mut pre_errors) = self
457            .preprocessor
458            .process_with_metadata(data, metadata.as_ref());
459        let mut parser_splitter = self.parser.splitter();
460        let mut parsed: Vec<Box<dyn InputBuffer>> = Vec::new();
461        let mut remaining = pre_data.as_slice();
462        // Use the parser to divide the message received from the preprocessor into chunks and parse each of them
463        while !remaining.is_empty() {
464            let chunk;
465            let split_offset = parser_splitter.input(remaining).unwrap_or(remaining.len());
466            (chunk, remaining) = remaining.split_at(split_offset);
467            let (parsed_data, mut parse_errors) = self.parser.parse(chunk, metadata.clone());
468            pre_errors.append(&mut parse_errors);
469            if let Some(data) = parsed_data {
470                parsed.push(data);
471            }
472        }
473        if parsed.is_empty() {
474            (None, pre_errors)
475        } else {
476            (Some(Box::new(parsed)), pre_errors)
477        }
478    }
479
480    fn stage(&self, buffers: Vec<Box<dyn InputBuffer>>) -> Box<dyn StagedBuffers> {
481        self.parser.stage(buffers)
482    }
483
484    fn splitter(&self) -> Box<dyn Splitter> {
485        let pre_splitter = self.preprocessor.splitter();
486        if let Some(splitter) = pre_splitter {
487            return splitter;
488        }
489        self.parser.splitter()
490    }
491
492    fn fork(&self) -> Box<dyn Parser> {
493        Box::new(MessageOrientedPreprocessedParser::new(
494            self.preprocessor.fork(),
495            self.parser.fork(),
496        ))
497    }
498}
499
500/// Splits a data stream at boundaries between records.
501///
502/// [Parser::parse] or [Preprocessor::process] can only parse complete records.
503/// For a byte stream source, a format-specific [Splitter] allows a transport
504/// to find boundaries.
505pub trait Splitter: Send + Sync {
506    /// Looks for a record boundary in `data`. Returns:
507    ///
508    /// - `None`, if `data` does not necessarily complete a record.
509    ///
510    /// - `Some(n)`, if the first `n` bytes of data, plus any data previously
511    ///   presented for which `None` was returned, form one or more complete
512    ///   records. If `n < data.len()`, then the caller should re-present
513    ///   `data[n..]` for further splitting.
514    fn input(&mut self, data: &[u8]) -> Option<usize>;
515
516    /// Clears any state in this splitter and prepares it to start splitting new
517    /// data.
518    fn clear(&mut self);
519}
520
521/// Helper for breaking a stream of data into groups of records using a
522/// [Splitter].
523///
524/// A [Splitter] finds breakpoints between records given data presented to
525/// it. This is a higher-level data structure that takes input data and breaks
526/// it into chunks.
527pub struct StreamSplitter {
528    buffer: Vec<u8>,
529    start: u64,
530    fragment: Range<usize>,
531    fed: usize,
532    splitter: Box<dyn Splitter>,
533}
534
535impl StreamSplitter {
536    /// Returns a new stream splitter that finds breakpoints with `splitter`.
537    pub fn new(splitter: Box<dyn Splitter>) -> Self {
538        Self {
539            buffer: Vec::new(),
540            start: 0,
541            fragment: 0..0,
542            fed: 0,
543            splitter,
544        }
545    }
546
547    /// Returns the next full chunk of input, if any.  `eoi` specifies whether
548    /// the input stream is complete. If `eoi` is true and this function returns
549    /// `None`, then there are no more chunks.
550    pub fn next(&mut self, eoi: bool) -> Option<&[u8]> {
551        match self
552            .splitter
553            .input(&self.buffer[self.fed..self.fragment.end])
554        {
555            Some(n) => {
556                let chunk = &self.buffer[self.fragment.start..self.fed + n];
557                self.fed += n;
558                self.fragment.start = self.fed;
559                Some(chunk)
560            }
561            None => {
562                self.fed = self.fragment.end;
563                if eoi && !self.fragment.is_empty() {
564                    let chunk = &self.buffer[self.fragment.clone()];
565                    self.fragment.start = self.fragment.end;
566                    Some(chunk)
567                } else {
568                    None
569                }
570            }
571        }
572    }
573
574    /// Appends `data` to the data to be broken into chunks.
575    pub fn append(&mut self, data: &[u8]) {
576        let final_len = self.fragment.len() + data.len();
577        if final_len > self.buffer.len() {
578            self.buffer.reserve(final_len - self.buffer.len());
579        }
580        self.buffer.copy_within(self.fragment.clone(), 0);
581        self.buffer.resize(self.fragment.len(), 0);
582        self.buffer.extend(data);
583        self.fed -= self.fragment.start;
584        self.start += self.fragment.start as u64;
585        self.fragment = 0..self.buffer.len();
586    }
587
588    // Reads no more than `limit` bytes of data from `file` into the splitter,
589    // with an initial minimum buffer size of `buffer_size`. Returns the number
590    // of bytes read or an I/O error.
591    pub fn read(
592        &mut self,
593        file: &mut File,
594        buffer_size: usize,
595        limit: usize,
596    ) -> Result<usize, IoError> {
597        // Move data to beginning of buffer.
598        if self.fragment.start != 0 {
599            self.buffer.copy_within(self.fragment.clone(), 0);
600            self.fed -= self.fragment.start;
601            self.start += self.fragment.start as u64;
602            self.fragment = 0..self.fragment.len();
603        }
604
605        // Make sure there's some space to read data.
606        if self.fragment.len() == self.buffer.len() {
607            self.buffer
608                .resize(max(buffer_size, self.buffer.capacity() * 2), 0);
609        }
610
611        // Read data.
612        let mut space = &mut self.buffer[self.fragment.len()..];
613        if space.len() > limit {
614            space = &mut space[..limit];
615        }
616        let result = file.read(space);
617        if let Ok(n) = result {
618            self.fragment.end += n;
619        }
620        result
621    }
622
623    /// Returns the logical stream position of the next byte to be returned by
624    /// the splitter.
625    pub fn position(&self) -> u64 {
626        self.start + self.fragment.start as u64
627    }
628
629    /// Sets the logical stream position of the next byte to be returned by
630    /// the splitter to `offset`, and discards other state.
631    pub fn seek(&mut self, offset: u64) {
632        self.start = offset;
633        self.fragment = 0..0;
634        self.fed = 0;
635        self.splitter.clear();
636    }
637
638    /// Resets the splitter's state as if it were newly created.
639    pub fn reset(&mut self) {
640        self.seek(0);
641    }
642}
643
644pub trait OutputFormat: Send + Sync {
645    /// Unique name of the data format.
646    fn name(&self) -> Cow<'static, str>;
647
648    /// Extract encoder configuration from an HTTP request.
649    ///
650    /// Returns the extracted configuration cast to the `ErasedSerialize` trait
651    /// object (to keep this trait object-safe).
652    fn config_from_http_request(
653        &self,
654        endpoint_name: &str,
655        request: &HttpRequest,
656    ) -> Result<Box<dyn ErasedSerialize>, ControllerError>;
657
658    /// Create a new encoder for the format.
659    ///
660    /// # Arguments
661    ///
662    /// * `config` - Format-specific configuration.
663    /// * `key_schema` - Schema of the keys in the stream; only set for indexed Z-sets.
664    /// * `value_schema` - Schema of the values in the stream. If the stream is an indexed Z-set,
665    ///   this is the schema of the values in the stream; if it is a Z-set, this is the schema of the keys in the stream.
666    /// * `consumer` - Consumer to send encoded data batches to.
667    /// * `is_index` - Whether the connector is configured with the `index` property.
668    ///
669    /// `is_index` implies that `key_schema` is set. The inverse is not true: the stream may be an indexed Z-set; but
670    /// the connector is not configured with the `index` property. The connector must iterate over the values in the
671    /// stream either directly or using `SerCursorFlattened`.
672    fn new_encoder(
673        &self,
674        endpoint_name: &str,
675        config: &ConnectorConfig,
676        key_schema: &Option<Relation>,
677        value_schema: &Relation,
678        consumer: Box<dyn OutputConsumer>,
679        is_index: bool,
680    ) -> Result<Box<dyn Encoder>, ControllerError>;
681}
682
683pub trait Encoder: Send {
684    /// Returns a reference to the consumer that the encoder is connected to.
685    fn consumer(&mut self) -> &mut dyn OutputConsumer;
686
687    /// Encode a batch of updates and push the results to the consumer.
688    ///
689    /// The encoder calls [`OutputConsumer::batch_start`] before emitting any
690    /// data, then delivers encoded records via [`OutputConsumer::push_buffer`]
691    /// or [`OutputConsumer::push_key`], and finally calls
692    /// [`OutputConsumer::batch_end`].
693    ///
694    /// Which of `push_buffer` or `push_key` is used depends on the kind of encoder used.
695    fn encode(&mut self, batch: Arc<dyn SerBatchReader>) -> AnyResult<()>;
696}
697
698#[doc(hidden)]
699pub trait OutputConsumer: Send {
700    /// Maximum buffer size that this transport can transmit.
701    /// The encoder should not generate buffers exceeding this size.
702    fn max_buffer_size_bytes(&self) -> usize;
703
704    fn batch_start(&mut self, step: Step, batch_type: OutputBatchType);
705
706    /// See OutputEndpoint::push_buffer.
707    fn push_buffer(&mut self, buffer: &[u8], num_records: usize);
708
709    /// See OutputEndpoint::push_key.
710    fn push_key(
711        &mut self,
712        key: Option<&[u8]>,
713        val: Option<&[u8]>,
714        headers: &[(&str, Option<&[u8]>)],
715        num_records: usize,
716    );
717    fn batch_end(&mut self);
718
719    /// Returns the approximate amount of memory used by the connector's
720    /// underlying implementation.  For the Kafka connectors, for example, this
721    /// is the amount of memory used by librdkafka.  Not all connectors use a
722    /// substantial amount of memory, so the default implementation returns 0.
723    fn memory(&self) -> usize {
724        0
725    }
726}
727
728/// Callback invoked when a [`Postprocessor`] returns an error.
729///
730/// The argument is the error returned by the postprocessor.  The callback
731/// is responsible for reporting or logging the error; the record that
732/// triggered the error is dropped and processing continues.
733pub type PostprocessorErrorCallback = Box<dyn Fn(anyhow::Error) + Send + Sync>;
734
735/// Bridges a [`Postprocessor`] into the [`OutputConsumer`] interface.
736///
737/// Each [`OutputConsumer`] method calls the corresponding [`Postprocessor`]
738/// method, then forwards its return value to the inner consumer.
739/// When the postprocessor returns an error, `error_cb` is invoked and the
740/// affected record is dropped.
741pub struct PostprocessedConsumer {
742    inner: Box<dyn OutputConsumer>,
743    postprocessor: Box<dyn Postprocessor>,
744    error_cb: PostprocessorErrorCallback,
745}
746
747impl PostprocessedConsumer {
748    pub fn new(
749        inner: Box<dyn OutputConsumer>,
750        postprocessor: Box<dyn Postprocessor>,
751        error_cb: PostprocessorErrorCallback,
752    ) -> Self {
753        Self {
754            inner,
755            postprocessor,
756            error_cb,
757        }
758    }
759}
760
761impl OutputConsumer for PostprocessedConsumer {
762    fn max_buffer_size_bytes(&self) -> usize {
763        self.inner.max_buffer_size_bytes()
764    }
765
766    fn batch_start(&mut self, step: Step, batch_type: OutputBatchType) {
767        self.postprocessor.batch_start(step, batch_type);
768        self.inner.batch_start(step, batch_type);
769    }
770
771    fn push_buffer(&mut self, buffer: &[u8], num_records: usize) {
772        match self.postprocessor.push_buffer(buffer) {
773            Ok(transformed) => self.inner.push_buffer(&transformed, num_records),
774            Err(e) => (self.error_cb)(e),
775        }
776    }
777
778    fn push_key(
779        &mut self,
780        key: Option<&[u8]>,
781        val: Option<&[u8]>,
782        headers: &[(&str, Option<&[u8]>)],
783        num_records: usize,
784    ) {
785        match self.postprocessor.push_key(key, val, headers) {
786            Ok((k, v, h)) => {
787                let h_refs: Vec<(&str, Option<&[u8]>)> =
788                    h.iter().map(|(k, v)| (k.as_str(), v.as_deref())).collect();
789                self.inner
790                    .push_key(k.as_deref(), v.as_deref(), &h_refs, num_records);
791            }
792            Err(e) => (self.error_cb)(e),
793        }
794    }
795
796    fn batch_end(&mut self) {
797        self.postprocessor.batch_end();
798        self.inner.batch_end();
799    }
800
801    fn memory(&self) -> usize {
802        self.inner.memory() + self.postprocessor.memory()
803    }
804}
805
806/// The largest weight of a record that can be output using
807/// a format without explicit weights. Such formats require
808/// duplicating the record `w` times, which is expensive
809/// for large weights (and is most likely not what the user
810/// intends).
811pub const MAX_DUPLICATES: i64 = 1_000_000;
812
813/// When including a long JSON record in an error message,
814/// truncate it to `MAX_RECORD_LEN_IN_ERRMSG` bytes.
815pub const MAX_RECORD_LEN_IN_ERRMSG: usize = 4096;
816
817/// Error parsing input data.
818#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
819#[serde(transparent)]
820// Box the internals of `ParseError` to avoid
821// "Error variant to large" clippy warnings".
822pub struct ParseError(Box<ParseErrorInner>);
823impl Display for ParseError {
824    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
825        self.0.fmt(f)
826    }
827}
828
829impl StdError for ParseError {}
830
831impl ParseError {
832    pub fn new(
833        description: String,
834        event_number: Option<u64>,
835        field: Option<String>,
836        invalid_text: Option<&str>,
837        invalid_bytes: Option<&[u8]>,
838        suggestion: Option<Cow<'static, str>>,
839        error_tag: Option<String>,
840    ) -> Self {
841        Self(Box::new(ParseErrorInner::new(
842            description,
843            event_number,
844            field,
845            invalid_text,
846            invalid_bytes,
847            suggestion,
848            error_tag,
849        )))
850    }
851
852    pub fn text_event_error<E>(
853        msg: &str,
854        error: E,
855        event_number: u64,
856        invalid_text: Option<&str>,
857        suggestion: Option<Cow<'static, str>>,
858    ) -> Self
859    where
860        E: ToString,
861    {
862        Self(Box::new(ParseErrorInner::text_event_error(
863            msg,
864            error,
865            event_number,
866            invalid_text,
867            suggestion,
868        )))
869    }
870
871    pub fn text_envelope_error(
872        description: String,
873        invalid_text: &str,
874        suggestion: Option<Cow<'static, str>>,
875    ) -> Self {
876        Self(Box::new(ParseErrorInner::text_envelope_error(
877            description,
878            invalid_text,
879            suggestion,
880        )))
881    }
882
883    pub fn bin_event_error(
884        description: String,
885        event_number: u64,
886        invalid_bytes: &[u8],
887        suggestion: Option<Cow<'static, str>>,
888    ) -> Self {
889        Self(Box::new(ParseErrorInner::bin_event_error(
890            description,
891            event_number,
892            invalid_bytes,
893            suggestion,
894        )))
895    }
896
897    pub fn bin_envelope_error(
898        description: String,
899        invalid_bytes: &[u8],
900        suggestion: Option<Cow<'static, str>>,
901    ) -> Self {
902        Self(Box::new(ParseErrorInner::bin_envelope_error(
903            description,
904            invalid_bytes,
905            suggestion,
906        )))
907    }
908
909    /// Returns a new `ParseError` with the description modified by `f`.
910    ///
911    /// Can be used, e.g., to prepend context to the description.
912    pub fn map_description<F>(self, f: F) -> Self
913    where
914        F: FnOnce(&str) -> String,
915    {
916        let mut inner = self.0;
917        let description = f(&inner.description);
918        inner.description = description;
919        Self(inner)
920    }
921
922    pub fn get_error_tag(&self) -> Option<String> {
923        self.0.get_error_tag()
924    }
925}
926
927#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
928pub struct ParseErrorInner {
929    /// Error description.
930    description: String,
931
932    /// Event number relative to the start of the stream.
933    ///
934    /// An input stream is a series data change events (row insertions,
935    /// deletions, and updates).  This field specifies the index (starting
936    /// from 1) of the event that caused the error, relative to the start of
937    /// the stream.  In some cases this index cannot be identified, e.g., if
938    /// the error makes an entire block of events unparseable.
939    event_number: Option<u64>,
940
941    /// Field that failed to parse.
942    ///
943    /// Only set when the parsing error can be attributed to a
944    /// specific field.
945    field: Option<String>,
946
947    /// Invalid fragment of input data.
948    ///
949    /// Used for binary data formats and for text-based formats when the input
950    /// is not valid UTF-8 string.
951    invalid_bytes: Option<Vec<u8>>,
952
953    /// Invalid fragment of the input text.
954    ///
955    /// Only used for text-based formats and in cases when input is valid UTF-8.
956    invalid_text: Option<String>,
957
958    /// Any additional information that may help fix the problem, e.g., example
959    /// of a valid input.
960    suggestion: Option<Cow<'static, str>>,
961
962    /// Used for rate limiting
963    tag: Option<String>,
964}
965
966impl Display for ParseErrorInner {
967    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
968        let event = if let Some(event_number) = self.event_number {
969            format!(" (event #{})", event_number)
970        } else {
971            String::new()
972        };
973
974        let invalid_fragment = if let Some(invalid_bytes) = &self.invalid_bytes {
975            format!("\nInvalid bytes: {invalid_bytes:?}")
976        } else if let Some(invalid_text) = &self.invalid_text {
977            format!("\nInvalid fragment: '{invalid_text}'")
978        } else {
979            String::new()
980        };
981
982        let suggestion = if let Some(suggestion) = &self.suggestion {
983            format!("\n{suggestion}")
984        } else {
985            String::new()
986        };
987
988        write!(
989            f,
990            "Parse error{event}: {}{invalid_fragment}{suggestion}",
991            self.description
992        )
993    }
994}
995
996impl ParseErrorInner {
997    pub fn new(
998        description: String,
999        event_number: Option<u64>,
1000        field: Option<String>,
1001        invalid_text: Option<&str>,
1002        invalid_bytes: Option<&[u8]>,
1003        suggestion: Option<Cow<'static, str>>,
1004        error_tag: Option<String>,
1005    ) -> Self {
1006        Self {
1007            description,
1008            event_number,
1009            field,
1010            invalid_text: invalid_text.map(str::to_string),
1011            invalid_bytes: invalid_bytes.map(ToOwned::to_owned),
1012            suggestion,
1013            tag: error_tag,
1014        }
1015    }
1016
1017    /// Error parsing an individual event in a text-based input format (e.g.,
1018    /// JSON, CSV).
1019    pub fn text_event_error<E>(
1020        msg: &str,
1021        error: E,
1022        event_number: u64,
1023        invalid_text: Option<&str>,
1024        suggestion: Option<Cow<'static, str>>,
1025    ) -> Self
1026    where
1027        E: ToString,
1028    {
1029        let err_str = error.to_string();
1030        // Try to parse the error as `FieldParseError`.  If this is not a field-specific
1031        // error or the error was not returned by the `deserialize_table_record`
1032        // macro, this will fail and we'll store the error as is.
1033        let (descr, field) = if let Some(offset) = err_str.find("{\"field\":") {
1034            if let Some(Ok(err)) = serde_json::Deserializer::from_str(&err_str[offset..])
1035                .into_iter::<FieldParseError>()
1036                .next()
1037            {
1038                (err.description, Some(err.field))
1039            } else {
1040                (err_str, None)
1041            }
1042        } else {
1043            (err_str, None)
1044        };
1045        let column_name = if let Some(field) = &field {
1046            format!(": error parsing field '{field}'")
1047        } else {
1048            String::new()
1049        };
1050
1051        Self::new(
1052            format!("{msg}{column_name}: {descr}",),
1053            Some(event_number),
1054            field,
1055            invalid_text,
1056            None,
1057            suggestion,
1058            Some("text_event_err".to_string()),
1059        )
1060    }
1061
1062    /// Error parsing a container, e.g., a JSON array, with multiple events.
1063    ///
1064    /// Such errors cannot be attributed to an individual event numbers.
1065    pub fn text_envelope_error(
1066        description: String,
1067        invalid_text: &str,
1068        suggestion: Option<Cow<'static, str>>,
1069    ) -> Self {
1070        Self::new(
1071            description,
1072            None,
1073            None,
1074            Some(invalid_text),
1075            None,
1076            suggestion,
1077            Some("text_envelope_err".to_string()),
1078        )
1079    }
1080
1081    /// Error parsing an individual event in a binary input format (e.g.,
1082    /// bincode).
1083    pub fn bin_event_error(
1084        description: String,
1085        event_number: u64,
1086        invalid_bytes: &[u8],
1087        suggestion: Option<Cow<'static, str>>,
1088    ) -> Self {
1089        Self::new(
1090            description,
1091            Some(event_number),
1092            None,
1093            None,
1094            Some(invalid_bytes),
1095            suggestion,
1096            Some("bin_event_err".to_string()),
1097        )
1098    }
1099
1100    /// Error parsing a container with multiple events.
1101    ///
1102    /// Such errors cannot be attributed to an individual event numbers.
1103    pub fn bin_envelope_error(
1104        description: String,
1105        invalid_bytes: &[u8],
1106        suggestion: Option<Cow<'static, str>>,
1107    ) -> Self {
1108        Self::new(
1109            description,
1110            None,
1111            None,
1112            None,
1113            Some(invalid_bytes),
1114            suggestion,
1115            Some("bin_envelope_err".to_string()),
1116        )
1117    }
1118
1119    pub fn get_error_tag(&self) -> Option<String> {
1120        self.tag.clone()
1121    }
1122}