Skip to main content

feldera_adapterlib/
catalog.rs

1use std::any::Any;
2#[cfg(any(feature = "with-avro", feature = "with-dynamodb"))]
3use std::collections::HashMap;
4use std::collections::HashSet;
5use std::fmt::{Debug, Formatter};
6use std::sync::{Arc, Mutex};
7
8use anyhow::Result as AnyResult;
9#[cfg(feature = "with-avro")]
10use apache_avro::{
11    Schema as AvroSchema,
12    schema::{Name as AvroName, NamesRef},
13    types::Value as AvroValue,
14};
15use arrow::record_batch::RecordBatch;
16#[cfg(feature = "with-dynamodb")]
17use aws_sdk_dynamodb::types::AttributeValue;
18use dbsp::circuit::NodeId;
19use dbsp::dynamic::{ClonableTrait, DynData, DynVec, Erase, Factory};
20use dbsp::operator::StagedBuffers;
21use dbsp::operator::dynamic::accumulator::EnableCount;
22use dyn_clone::DynClone;
23use feldera_sqllib::Variant;
24use feldera_types::format::csv::CsvFormatConfig;
25use feldera_types::format::json::JsonFlavor;
26use feldera_types::program_schema::{Relation, SqlIdentifier};
27use feldera_types::serde_with_context::SqlSerdeConfig;
28use serde_arrow::ArrayBuilder;
29
30use crate::errors::controller::ControllerError;
31use crate::format::InputBuffer;
32use crate::postprocess::PostprocessorRegistry;
33use crate::preprocess::PreprocessorRegistry;
34
35/// Descriptor that specifies the format in which records are received
36/// or into which they should be encoded before sending.
37#[derive(Clone)]
38pub enum RecordFormat {
39    // TODO: Support different JSON encodings:
40    // * Map - the default encoding
41    // * Array - allow the subset and the order of columns to be configurable
42    // * Raw - Only applicable to single-column tables.  Input records contain
43    // raw encoding of this column only.  This is particularly useful for
44    // tables that store raw JSON or binary data to be parsed using SQL.
45    Json(JsonFlavor),
46    Csv(CsvFormatConfig),
47    Parquet(SqlSerdeConfig),
48    #[cfg(feature = "with-avro")]
49    Avro,
50    Raw(String),
51    /// Output-only format for the DynamoDB connector.
52    #[cfg(feature = "with-dynamodb")]
53    DynamoDB,
54}
55
56/// An input handle that deserializes and buffers records.
57///
58/// A trait for a type that wraps a [`ZSetHandle`](`dbsp::ZSetHandle`) or an
59/// [`MapHandle`](`dbsp::MapHandle`) and collects serialized relational data for
60/// the associated input stream.  The client passes a byte array with a
61/// serialized data record (e.g., in JSON or CSV format) to
62/// [`insert`](`Self::insert`), [`delete`](`Self::delete`), and
63/// [`update`](`Self::update`) methods. The record gets deserialized into the
64/// strongly typed representation expected by the input stream.
65///
66/// Instances of this trait are created by calling
67/// [`DeCollectionHandle::configure_deserializer`].
68/// The data format accepted by the handle is determined
69/// by the `record_format` argument passed to this method.
70///
71/// The input handle internally buffers the deserialized records. Use the
72/// `InputBuffer` supertrait to push them to the circuit or extract them for
73/// later use.
74pub trait DeCollectionStream: Send + Sync + InputBuffer {
75    /// Buffer a new insert update.
76    ///
77    /// `metadata` contains optional metadata attached by the transport adapter or parser,
78    /// such as Kafka headers, topic name, etc. This metadata is passed as an aux argument to
79    /// `DeserializeWithContext::deserialize_with_context_aux`.
80    ///
81    /// Returns an error if deserialization fails, i.e., the serialized
82    /// representation is corrupted or does not match the value type of
83    /// the underlying input stream.
84    fn insert(&mut self, data: &[u8], metadata: &Option<Variant>) -> AnyResult<()>;
85
86    /// Buffer a new delete update.
87    ///
88    /// The `data` argument contains a serialized record whose
89    /// type depends on the underlying input stream: streams created by
90    /// [`RootCircuit::add_input_zset`](`dbsp::RootCircuit::add_input_zset`)
91    /// and [`RootCircuit::add_input_set`](`dbsp::RootCircuit::add_input_set`)
92    /// methods support deletion by value, hence the serialized record must
93    /// match the value type of the stream.  Streams created with
94    /// [`RootCircuit::add_input_map`](`dbsp::RootCircuit::add_input_map`)
95    /// support deletion by key, so the serialized record must match the key
96    /// type of the stream.
97    ///
98    /// The record gets deserialized and pushed to the underlying input stream
99    /// handle as a delete update.
100    ///
101    /// `metadata` contains optional metadata attached by the transport adapter or parser,
102    /// such as Kafka headers, topic name, etc. This metadata is passed as an aux argument to
103    /// `DeserializeWithContext::deserialize_with_context_aux`.
104    ///
105    /// Returns an error if deserialization fails, i.e., the serialized
106    /// representation is corrupted or does not match the value or key
107    /// type of the underlying input stream.
108    fn delete(&mut self, data: &[u8], metadata: &Option<Variant>) -> AnyResult<()>;
109
110    /// Buffer a new update that will modify an existing record.
111    ///
112    /// `metadata` contains optional metadata attached by the transport adapter or parser,
113    /// such as Kafka headers, topic name, etc. This metadata is passed as an aux argument to
114    /// `DeserializeWithContext::deserialize_with_context_aux`.
115    ///
116    /// This method can only be called on streams created with
117    /// [`RootCircuit::add_input_map`](`dbsp::RootCircuit::add_input_map`)
118    /// and will fail on other streams.  The serialized record must match
119    /// the update type of this stream, specified as a type argument to
120    /// `Catalog::register_input_map`.
121    fn update(&mut self, data: &[u8], metadata: &Option<Variant>) -> AnyResult<()>;
122
123    /// Reserve space for at least `reservation` more updates in the
124    /// internal input buffer.
125    ///
126    /// Reservations are not required but can be used when the number
127    /// of inputs is known ahead of time to reduce reallocations.
128    fn reserve(&mut self, reservation: usize);
129
130    /// Removes any updates beyond the first `len`.
131    fn truncate(&mut self, len: usize);
132
133    /// Stages all of the `buffers`, which must have been obtained from a
134    /// [Parser] for this stream, into a [StagedBuffers] that may later be used
135    /// to push the collected data into the circuit.  See [StagedBuffers] for
136    /// more information.
137    ///
138    /// [Parser]: crate::format::Parser
139    fn stage(&self, buffers: Vec<Box<dyn InputBuffer>>) -> Box<dyn StagedBuffers>;
140
141    /// Create a new deserializer with the same configuration connected to the
142    /// same input stream. The new deserializer has an independent buffer that
143    /// is initially empty.
144    fn fork(&self) -> Box<dyn DeCollectionStream>;
145}
146
147/// Like `DeCollectionStream`, but deserializes Arrow-encoded records before pushing them to a
148/// stream.
149pub trait ArrowStream: InputBuffer + Send + Sync {
150    /// Buffer a new batch of insert updates.
151    ///
152    /// `metadata` contains optional metadata attached by the transport adapter or parser,
153    /// such as Kafka headers, topic name, etc. This metadata is passed as an aux argument to
154    /// `DeserializeWithContext::deserialize_with_context_aux` for each deserialized record.
155    fn insert(&mut self, data: &RecordBatch, metadata: &Option<Variant>) -> AnyResult<()>;
156
157    /// Buffer a new batch of delete updates.
158    ///
159    /// `metadata` contains optional metadata attached by the transport adapter or parser,
160    /// such as Kafka headers, topic name, etc. This metadata is passed as an aux argument to
161    /// `DeserializeWithContext::deserialize_with_context_aux` for each deserialized record.
162    fn delete(&mut self, data: &RecordBatch, metadata: &Option<Variant>) -> AnyResult<()>;
163
164    /// Insert records in `data` with polarities from the `polarities` array.
165    ///
166    /// `polarities` must be the same length as `data`.
167    fn insert_with_polarities(
168        &mut self,
169        data: &RecordBatch,
170        polarities: &[bool],
171        metadata: &Option<Variant>,
172    ) -> AnyResult<()>;
173
174    /// Create a new deserializer with the same configuration connected to
175    /// the same input stream.
176    fn fork(&self) -> Box<dyn ArrowStream>;
177
178    /// Stages all of the `buffers`, which must have been obtained from a
179    /// [Parser] for this stream, into a [StagedBuffers] that may later be used
180    /// to push the collected data into the circuit.  See [StagedBuffers] for
181    /// more information.
182    ///
183    /// [Parser]: crate::format::Parser
184    fn stage(&self, buffers: Vec<Box<dyn InputBuffer>>) -> Box<dyn StagedBuffers>;
185}
186
187#[cfg(feature = "with-avro")]
188pub type AvroSchemaRefs = HashMap<AvroName, AvroSchema>;
189
190/// Like `DeCollectionStream`, but deserializes Avro-encoded records before pushing them to a
191/// stream.
192#[cfg(feature = "with-avro")]
193pub trait AvroStream: InputBuffer + Send + Sync {
194    /// Buffer a new insert update.
195    ///
196    /// # Arguments
197    ///
198    /// * `schema` - The Avro schema to use for deserialization.
199    /// * `refs` - A map of named schema references that may be used to resolve references within `schema`.
200    /// * `metadata` - Optional metadata attached by the transport adapter or parser,
201    ///   such as Kafka headers, topic name, etc. This metadata is passed as an aux argument to
202    ///   `DeserializeWithContext::deserialize_with_context_aux`.
203    fn insert(
204        &mut self,
205        data: &AvroValue,
206        schema: &AvroSchema,
207        refs: &AvroSchemaRefs,
208        n_bytes: usize,
209        metadata: &Option<Variant>,
210    ) -> AnyResult<()>;
211
212    fn delete(
213        &mut self,
214        data: &AvroValue,
215        schema: &AvroSchema,
216        refs: &AvroSchemaRefs,
217        n_bytes: usize,
218        metadata: &Option<Variant>,
219    ) -> AnyResult<()>;
220
221    /// Create a new deserializer with the same configuration connected to
222    /// the same input stream.
223    fn fork(&self) -> Box<dyn AvroStream>;
224
225    /// Stages all of the `buffers`, which must have been obtained from a
226    /// [Parser] for this stream, into a [StagedBuffers] that may later be used
227    /// to push the collected data into the circuit.  See [StagedBuffers] for
228    /// more information.
229    ///
230    /// [Parser]: crate::format::Parser
231    fn stage(&self, buffers: Vec<Box<dyn InputBuffer>>) -> Box<dyn StagedBuffers>;
232}
233
234/// A handle to an input collection that can be used to feed serialized data
235/// to the collection.
236pub trait DeCollectionHandle: Send + Sync {
237    /// Create a [`DeCollectionStream`] object to parse input data encoded
238    /// using the format specified in `RecordFormat`.
239    fn configure_deserializer(
240        &self,
241        record_format: RecordFormat,
242    ) -> Result<Box<dyn DeCollectionStream>, ControllerError>;
243
244    /// Create an `ArrowStream` object to parse Arrow-encoded input data.
245    fn configure_arrow_deserializer(
246        &self,
247        config: SqlSerdeConfig,
248    ) -> Result<Box<dyn ArrowStream>, ControllerError>;
249
250    /// Create an `AvroStream` object to parse Avro-encoded input data.
251    #[cfg(feature = "with-avro")]
252    fn configure_avro_deserializer(&self) -> Result<Box<dyn AvroStream>, ControllerError>;
253
254    fn fork(&self) -> Box<dyn DeCollectionHandle>;
255}
256
257/// A type-erased batch whose contents can be serialized.
258///
259/// This is a wrapper around the DBSP `BatchReader` trait that returns a cursor that
260/// yields `erased_serde::Serialize` trait objects that can be used to serialize
261/// the contents of the batch without knowing its key and value types.
262// The reason we need the `Sync` trait below is so that we can wrap batches
263// in `Arc` and send the same batch to multiple output endpoint threads.
264pub trait SerBatchReader: 'static + Send + Sync {
265    /// Number of keys in the batch.
266    fn key_count(&self) -> usize;
267
268    /// Number of tuples in the batch.
269    fn len(&self) -> usize;
270
271    fn is_empty(&self) -> bool {
272        self.len() == 0
273    }
274
275    /// Create a cursor over the batch that yields records
276    /// formatted using the specified format.
277    fn cursor<'a>(
278        &'a self,
279        record_format: RecordFormat,
280    ) -> Result<Box<dyn SerCursor + Send + 'a>, ControllerError>;
281
282    /// Returns all batches in this reader.
283    ///
284    /// A reader can wrap a single batch or a spine or a spine snapshot. This method extracts
285    /// all batches from the reader.
286    fn batches(&self) -> Vec<Arc<dyn SerBatch>>;
287
288    fn snapshot(&self) -> Arc<dyn SerBatchReader>;
289
290    fn keys_factory(&self) -> &'static dyn Factory<DynVec<DynData>>;
291
292    fn key_factory(&self) -> &'static dyn Factory<DynData>;
293
294    fn sample_keys(&self, sample_size: usize, sample: &mut DynVec<DynData>);
295
296    fn partition_keys(&self, num_partitions: usize, bounds: &mut DynVec<DynData>);
297}
298
299impl Debug for dyn SerBatchReader {
300    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
301        let mut cursor = self
302            .cursor(RecordFormat::Json(Default::default()))
303            .map_err(|_| std::fmt::Error)?;
304        let mut key = Vec::new();
305        let mut val = Vec::new();
306        while cursor.key_valid() {
307            cursor
308                .serialize_key(&mut key)
309                .map_err(|_| std::fmt::Error)?;
310            write!(f, "{}=>{{", String::from_utf8_lossy(&key))?;
311
312            while cursor.val_valid() {
313                cursor
314                    .serialize_val(&mut val)
315                    .map_err(|_| std::fmt::Error)?;
316                write!(
317                    f,
318                    "{}=>{}, ",
319                    String::from_utf8_lossy(&val),
320                    cursor.weight()
321                )?;
322
323                val.clear();
324                cursor.step_val();
325            }
326
327            write!(f, "}}, ")?;
328            key.clear();
329            cursor.step_key();
330        }
331
332        Ok(())
333    }
334}
335
336impl Debug for dyn SerBatch {
337    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
338        self.as_batch_reader().fmt(f)
339    }
340}
341
342/// A type-erased `Batch`.
343pub trait SerBatch: SerBatchReader {
344    /// Convert to `Arc<Any>`, which can then be downcast to a reference
345    /// to a concrete batch type.
346    fn as_any(self: Arc<Self>) -> Arc<dyn Any + Sync + Send>;
347
348    /// Merge `self` with all batches in `other`.
349    fn merge(self: Arc<Self>, other: Vec<Arc<dyn SerBatch>>) -> Arc<dyn SerBatch>;
350
351    /// Concatenate `self.batches()` with `b.batches()` for all batches in `other`
352    /// into a spine snapshot.
353    fn concat(self: Arc<Self>, other: Vec<Arc<dyn SerBatch>>) -> Arc<dyn SerBatchReader>;
354
355    fn as_batch_reader(&self) -> &dyn SerBatchReader;
356
357    fn arc_as_batch_reader(self: Arc<Self>) -> Arc<dyn SerBatchReader>;
358
359    /// Convert batch into a trace with identical contents.
360    fn into_trace(self: Arc<Self>) -> Box<dyn SerTrace>;
361}
362
363/// A type-erased `Trace`.
364pub trait SerTrace: SerBatchReader {
365    /// Insert a batch into the trace.
366    fn insert(&mut self, batch: Arc<dyn SerBatch>);
367
368    fn insert_without_blocking(&mut self, batch: Arc<dyn SerBatch>) -> bool;
369
370    fn backpressure_wait(&self);
371
372    fn as_batch_reader(&self) -> &dyn SerBatchReader;
373}
374
375#[doc(hidden)]
376pub struct SplitCursorBuilder {
377    batch: Arc<dyn SerBatchReader>,
378    start_key: Box<DynData>,
379    end_key: Option<Box<DynData>>,
380    format: RecordFormat,
381}
382
383impl SplitCursorBuilder {
384    /// Create a [`SplitCursorBuilder`] for partition `index` given a batch,
385    /// pre-computed partition `bounds` (as returned by
386    /// [`SerBatchReader::partition_keys`]), and a record `format`.
387    ///
388    /// `bounds` contains `N-1` boundary keys for `N` partitions.
389    /// Partition 0 spans from the start of the batch to `bounds[0]`,
390    /// partition `i` spans from `bounds[i-1]` to `bounds[i]`, and the last
391    /// partition spans from `bounds[N-2]` to the end of the batch.
392    ///
393    /// Returns `None` if the partition is empty (the cursor has no key at the
394    /// start position).
395    pub fn from_bounds(
396        batch: Arc<dyn SerBatchReader>,
397        bounds: &DynVec<DynData>,
398        index: usize,
399        format: RecordFormat,
400    ) -> Option<Self> {
401        let start_bound = if index == 0 {
402            None
403        } else if index <= bounds.len() {
404            Some(bounds.index(index - 1).as_data())
405        } else {
406            None
407        };
408
409        let end_bound = if index < bounds.len() {
410            Some(bounds.index(index).as_data())
411        } else {
412            None
413        };
414
415        let start_key = {
416            let mut cursor = batch.cursor(format.clone()).unwrap();
417
418            // Seek to start. If None, the cursor starts at the beginning.
419            if let Some(start_bound) = start_bound {
420                cursor.seek_key_exact(start_bound);
421            }
422
423            // Clone the actual key the cursor landed on.
424            cursor.get_key().map(|s| {
425                let mut key = batch.key_factory().default_box();
426                s.clone_to(key.as_mut());
427                key
428            })
429        }?;
430
431        let end_key = end_bound.map(|e| {
432            let mut key = batch.key_factory().default_box();
433            e.clone_to(key.as_mut());
434            key
435        });
436
437        Some(SplitCursorBuilder {
438            batch,
439            start_key,
440            end_key,
441            format,
442        })
443    }
444
445    pub fn build<'a>(&'a self) -> SplitCursor<'a> {
446        let mut cursor = self.batch.cursor(self.format.clone()).unwrap();
447
448        // Cannot use `seek_key_exact` here, so we can single-step the cursor afterward.
449        cursor.seek_key(self.start_key.as_data());
450
451        SplitCursor {
452            cursor,
453            start_key: self.start_key.clone(),
454            end_key: self.end_key.clone(),
455        }
456    }
457}
458
459#[doc(hidden)]
460pub struct SplitCursor<'a> {
461    cursor: Box<dyn SerCursor + 'a>,
462    start_key: Box<DynData>,
463    end_key: Option<Box<DynData>>,
464}
465
466impl SplitCursor<'_> {
467    fn finished(&self) -> bool {
468        if let Some(ref end_key) = self.end_key
469            && let Some(current_key) = self.cursor.get_key()
470        {
471            return current_key >= end_key.as_data();
472        }
473
474        false
475    }
476}
477
478impl SerCursor for SplitCursor<'_> {
479    fn key_valid(&self) -> bool {
480        self.cursor.key_valid() && !self.finished()
481    }
482
483    fn val_valid(&self) -> bool {
484        self.cursor.val_valid()
485    }
486
487    fn key(&self) -> &DynData {
488        self.cursor.key()
489    }
490
491    fn val(&self) -> &DynData {
492        self.cursor.val()
493    }
494
495    fn get_key(&self) -> Option<&DynData> {
496        if !self.key_valid() {
497            return None;
498        }
499
500        self.cursor.get_key()
501    }
502
503    fn get_val(&self) -> Option<&DynData> {
504        self.cursor.get_val()
505    }
506
507    fn serialize_key(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
508        self.cursor.serialize_key(dst)
509    }
510
511    fn key_to_json(&mut self) -> AnyResult<serde_json::Value> {
512        self.cursor.key_to_json()
513    }
514
515    #[cfg(feature = "with-dynamodb")]
516    fn key_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>> {
517        self.cursor.key_to_dynamodb_item()
518    }
519
520    fn serialize_key_fields(
521        &mut self,
522        fields: &HashSet<String>,
523        dst: &mut Vec<u8>,
524    ) -> AnyResult<()> {
525        self.cursor.serialize_key_fields(fields, dst)
526    }
527
528    fn serialize_val_fields(
529        &mut self,
530        fields: &HashSet<String>,
531        dst: &mut Vec<u8>,
532    ) -> AnyResult<()> {
533        self.cursor.serialize_val_fields(fields, dst)
534    }
535
536    fn serialize_key_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()> {
537        self.cursor.serialize_key_to_arrow(dst)
538    }
539
540    fn serialize_key_to_arrow_with_metadata(
541        &mut self,
542        metadata: &dyn erased_serde::Serialize,
543        dst: &mut ArrayBuilder,
544    ) -> AnyResult<()> {
545        self.cursor
546            .serialize_key_to_arrow_with_metadata(metadata, dst)
547    }
548
549    fn serialize_val_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()> {
550        self.cursor.serialize_val_to_arrow(dst)
551    }
552
553    fn serialize_val_to_arrow_with_metadata(
554        &mut self,
555        metadata: &dyn erased_serde::Serialize,
556        dst: &mut ArrayBuilder,
557    ) -> AnyResult<()> {
558        self.cursor
559            .serialize_val_to_arrow_with_metadata(metadata, dst)
560    }
561
562    #[cfg(feature = "with-avro")]
563    fn key_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue> {
564        self.cursor.key_to_avro(schema, refs)
565    }
566
567    fn serialize_key_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
568        self.cursor.serialize_key_weight(dst)
569    }
570
571    fn serialize_val_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
572        self.cursor.serialize_val_weight(dst)
573    }
574
575    fn serialize_val(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
576        self.cursor.serialize_val(dst)
577    }
578
579    fn val_to_json(&mut self) -> AnyResult<serde_json::Value> {
580        self.cursor.val_to_json()
581    }
582
583    #[cfg(feature = "with-dynamodb")]
584    fn val_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>> {
585        self.cursor.val_to_dynamodb_item()
586    }
587
588    #[cfg(feature = "with-avro")]
589    fn val_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue> {
590        self.cursor.val_to_avro(schema, refs)
591    }
592
593    fn weight(&mut self) -> i64 {
594        self.cursor.weight()
595    }
596
597    fn step_key(&mut self) {
598        self.cursor.step_key();
599    }
600
601    fn step_val(&mut self) {
602        self.cursor.step_val();
603    }
604
605    fn rewind_keys(&mut self) {
606        self.cursor.rewind_keys();
607        self.cursor.seek_key(self.start_key.as_data());
608    }
609
610    fn rewind_vals(&mut self) {
611        self.cursor.rewind_vals();
612    }
613
614    fn seek_key_exact(&mut self, key: &DynData) -> bool {
615        if let Some(ref end_key) = self.end_key
616            && key >= end_key.as_data()
617        {
618            return false;
619        }
620
621        self.cursor.seek_key_exact(key)
622    }
623
624    fn seek_key(&mut self, key: &DynData) {
625        self.cursor.seek_key(key);
626    }
627}
628
629/// A wrapper around a `SerCursor` that makes an `IndexedZSet<K, V>` look like a `ZSet<V>`
630/// by iterating over values only.
631///
632/// * Doesn't return values in order.
633/// * Panics when any operations over values are attempted.
634/// * Panics on seek_key_exact and seek_key.
635pub struct SerCursorFlattened<'a> {
636    val_valid: bool,
637    cursor: Box<dyn SerCursor + 'a>,
638}
639
640impl<'a> SerCursorFlattened<'a> {
641    pub fn new(cursor: Box<dyn SerCursor + 'a>) -> Self {
642        Self {
643            cursor,
644            val_valid: true,
645        }
646    }
647}
648
649impl<'a> SerCursor for SerCursorFlattened<'a> {
650    fn key_valid(&self) -> bool {
651        self.cursor.key_valid() && self.cursor.val_valid()
652    }
653
654    fn val_valid(&self) -> bool {
655        self.val_valid
656    }
657
658    fn key(&self) -> &DynData {
659        self.cursor.val()
660    }
661
662    fn get_key(&self) -> Option<&DynData> {
663        self.cursor.get_val()
664    }
665
666    fn val(&self) -> &DynData {
667        ().erase()
668    }
669
670    fn get_val(&self) -> Option<&DynData> {
671        if self.val_valid {
672            Some(().erase())
673        } else {
674            None
675        }
676    }
677
678    fn serialize_key(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
679        self.cursor.serialize_val(dst)
680    }
681
682    fn key_to_json(&mut self) -> AnyResult<serde_json::Value> {
683        self.cursor.val_to_json()
684    }
685
686    #[cfg(feature = "with-dynamodb")]
687    fn key_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>> {
688        self.cursor.val_to_dynamodb_item()
689    }
690
691    fn serialize_key_fields(
692        &mut self,
693        fields: &HashSet<String>,
694        dst: &mut Vec<u8>,
695    ) -> AnyResult<()> {
696        self.cursor.serialize_val_fields(fields, dst)
697    }
698
699    fn serialize_val_fields(
700        &mut self,
701        _fields: &HashSet<String>,
702        _dst: &mut Vec<u8>,
703    ) -> AnyResult<()> {
704        panic!("serialize_val_fields is not supported for flattened cursors");
705    }
706
707    fn serialize_key_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()> {
708        self.cursor.serialize_val_to_arrow(dst)
709    }
710
711    fn serialize_key_to_arrow_with_metadata(
712        &mut self,
713        metadata: &dyn erased_serde::Serialize,
714        dst: &mut ArrayBuilder,
715    ) -> AnyResult<()> {
716        self.cursor
717            .serialize_val_to_arrow_with_metadata(metadata, dst)
718    }
719
720    fn serialize_val_to_arrow(&mut self, _dst: &mut ArrayBuilder) -> AnyResult<()> {
721        panic!("serialize_val_to_arrow is not supported for flattened cursors");
722    }
723
724    fn serialize_val_to_arrow_with_metadata(
725        &mut self,
726        _metadata: &dyn erased_serde::Serialize,
727        _dst: &mut ArrayBuilder,
728    ) -> AnyResult<()> {
729        panic!("serialize_val_to_arrow_with_metadata is not supported for flattened cursors");
730    }
731
732    #[cfg(feature = "with-avro")]
733    fn key_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue> {
734        self.cursor.val_to_avro(schema, refs)
735    }
736
737    fn serialize_key_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
738        self.cursor.serialize_val_weight(dst)
739    }
740
741    fn serialize_val_weight(&mut self, _dst: &mut Vec<u8>) -> AnyResult<()> {
742        panic!("serialize_val_weight is not supported for flattened cursors");
743    }
744
745    fn serialize_val(&mut self, _dst: &mut Vec<u8>) -> AnyResult<()> {
746        panic!("serialize_val is not supported for flattened cursors");
747    }
748
749    fn val_to_json(&mut self) -> AnyResult<serde_json::Value> {
750        panic!("val_to_json is not supported for flattened cursors");
751    }
752
753    #[cfg(feature = "with-dynamodb")]
754    fn val_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>> {
755        panic!("val_to_dynamodb_item is not supported for flattened cursors");
756    }
757
758    #[cfg(feature = "with-avro")]
759    fn val_to_avro(&mut self, _schema: &AvroSchema, _refs: &NamesRef<'_>) -> AnyResult<AvroValue> {
760        panic!("val_to_avro is not supported for flattened cursors");
761    }
762
763    fn weight(&mut self) -> i64 {
764        self.cursor.weight()
765    }
766
767    fn step_key(&mut self) {
768        debug_assert!(self.cursor.key_valid() && self.cursor.val_valid());
769        self.cursor.step_val();
770        while !self.cursor.val_valid() && self.cursor.key_valid() {
771            self.cursor.step_key();
772        }
773        self.val_valid = true;
774    }
775
776    fn step_val(&mut self) {
777        self.val_valid = false;
778    }
779
780    fn rewind_keys(&mut self) {
781        self.cursor.rewind_keys();
782        self.val_valid = true;
783    }
784
785    fn rewind_vals(&mut self) {
786        self.val_valid = true;
787    }
788
789    fn seek_key_exact(&mut self, _key: &DynData) -> bool {
790        panic!("seek_key_exact is not supported for flattened cursors");
791    }
792
793    fn seek_key(&mut self, _key: &DynData) {
794        panic!("seek_key is not supported for flattened cursors");
795    }
796}
797
798/// Cursor that allows serializing the contents of a type-erased batch.
799///
800/// This is a wrapper around the DBSP `Cursor` trait that yields keys and values
801/// of the underlying batch as `erased_serde::Serialize` trait objects.
802pub trait SerCursor: Send {
803    /// Indicates if the current key is valid.
804    ///
805    /// A value of `false` indicates that the cursor has exhausted all keys.
806    fn key_valid(&self) -> bool;
807
808    /// Indicates if the current value is valid.
809    ///
810    /// A value of `false` indicates that the cursor has exhausted all values
811    /// for this key.
812    fn val_valid(&self) -> bool;
813
814    fn key(&self) -> &DynData;
815
816    fn get_key(&self) -> Option<&DynData>;
817
818    fn val(&self) -> &DynData;
819
820    fn get_val(&self) -> Option<&DynData>;
821
822    /// Serialize current key. Panics if invalid.
823    fn serialize_key(&mut self, dst: &mut Vec<u8>) -> AnyResult<()>;
824
825    /// Convert key to JSON. Used for error reporting to generate a human-readable
826    /// representation of the key.
827    fn key_to_json(&mut self) -> AnyResult<serde_json::Value>;
828
829    /// Serialize current key to a DynamoDB item.
830    #[cfg(feature = "with-dynamodb")]
831    fn key_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>>;
832
833    /// Like `serialize_key`, but only serializes the specified fields of the key.
834    fn serialize_key_fields(
835        &mut self,
836        fields: &HashSet<String>,
837        dst: &mut Vec<u8>,
838    ) -> AnyResult<()>;
839
840    fn serialize_val_fields(
841        &mut self,
842        fields: &HashSet<String>,
843        dst: &mut Vec<u8>,
844    ) -> AnyResult<()>;
845
846    /// Serialize current key into arrow format. Panics if invalid.
847    fn serialize_key_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()>;
848
849    /// Serialize current key into arrow format, adding additional metadata columns.
850    /// `metadata` must be a struct or a map.
851    fn serialize_key_to_arrow_with_metadata(
852        &mut self,
853        metadata: &dyn erased_serde::Serialize,
854        dst: &mut ArrayBuilder,
855    ) -> AnyResult<()>;
856
857    /// Serialize current value into arrow format. Panics if invalid.
858    fn serialize_val_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()>;
859
860    /// Serialize current value into arrow format, adding additional metadata columns.
861    /// `metadata` must be a struct or a map.
862    fn serialize_val_to_arrow_with_metadata(
863        &mut self,
864        metadata: &dyn erased_serde::Serialize,
865        dst: &mut ArrayBuilder,
866    ) -> AnyResult<()>;
867
868    #[cfg(feature = "with-avro")]
869    /// Convert current key to an Avro value.
870    fn key_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue>;
871
872    /// Serialize the `(key, weight)` tuple.
873    ///
874    /// FIXME: This only exists to support the CSV serializer, which outputs
875    /// key and weight in the same CSV record.
876    fn serialize_key_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()>;
877
878    fn serialize_val_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()>;
879
880    /// Serialize current value. Panics if invalid.
881    fn serialize_val(&mut self, dst: &mut Vec<u8>) -> AnyResult<()>;
882
883    /// Convert value to JSON. Used for error reporting to generate a human-readable
884    /// representation of the value.
885    fn val_to_json(&mut self) -> AnyResult<serde_json::Value>;
886
887    /// Serialize current value to a DynamoDB item.
888    #[cfg(feature = "with-dynamodb")]
889    fn val_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>>;
890
891    #[cfg(feature = "with-avro")]
892    /// Convert current value to Avro.
893    fn val_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue>;
894
895    /// Returns the weight associated with the current key/value pair.
896    fn weight(&mut self) -> i64;
897
898    /// Advances the cursor to the next key.
899    fn step_key(&mut self);
900
901    /// Advances the cursor to the next value.
902    fn step_val(&mut self);
903
904    /// Rewinds the cursor to the first key.
905    fn rewind_keys(&mut self);
906
907    /// Rewinds the cursor to the first value for current key.
908    fn rewind_vals(&mut self);
909
910    fn count_keys(&mut self) -> usize {
911        let mut count = 0;
912
913        while self.key_valid() {
914            count += 1;
915            self.step_key()
916        }
917
918        count
919    }
920
921    fn seek_key_exact(&mut self, key: &DynData) -> bool;
922
923    fn seek_key(&mut self, key: &DynData);
924}
925
926/// A handle to an output stream of a circuit that yields type-erased
927/// read-only batches.
928///
929/// A trait for a type that wraps around an
930/// [`OutputHandle`](`dbsp::OutputHandle`) and yields output batches produced by
931/// the circuit as [`SerBatchReader`]s.
932pub trait SerBatchReaderHandle: Send + Sync + DynClone {
933    /// See [`OutputHandle::num_nonempty_mailboxes`](`dbsp::OutputHandle::num_nonempty_mailboxes`)
934    fn num_nonempty_mailboxes(&self) -> usize;
935
936    /// Like [`OutputHandle::take_from_worker`](`dbsp::OutputHandle::take_from_worker`),
937    /// but returns output batch as a [`SerBatchReader`] trait object.
938    fn take_from_worker(&self, worker: usize) -> Option<Box<dyn SerBatchReader>>;
939
940    /// Like [`OutputHandle::take_from_all`](`dbsp::OutputHandle::take_from_all`),
941    /// but returns output batches as [`SerBatchReader`] trait objects.
942    fn take_from_all(&self) -> Vec<Arc<dyn SerBatchReader>>;
943
944    /// Concatenate outputs from all workers into a single batch reader.
945    fn concat(&self) -> Arc<dyn SerBatchReader>;
946}
947
948dyn_clone::clone_trait_object!(SerBatchReaderHandle);
949
950/// Cursor that iterates over deletions before insertions.
951///
952/// Most consumers don't understand Z-sets and expect a stream of upserts
953/// instead, which means that the order of updates matters. For a table
954/// with a primary key or unique constraint we must delete an existing record
955/// before creating a new one with the same key.  DBSP may not know about
956/// these constraints, so the safe thing to do is to output deletions before
957/// insertions.  This cursor helps by iterating over all deletions in
958/// the batch before insertions.
959pub struct CursorWithPolarity<'a> {
960    cursor: Box<dyn SerCursor + 'a>,
961    second_pass: bool,
962}
963
964impl<'a> CursorWithPolarity<'a> {
965    pub fn new(cursor: Box<dyn SerCursor + 'a>) -> Self {
966        let mut result = Self {
967            cursor,
968            second_pass: false,
969        };
970
971        if result.key_valid() {
972            result.advance_val();
973        }
974
975        result
976    }
977
978    fn advance_val(&mut self) {
979        while self.cursor.val_valid()
980            && ((!self.second_pass && self.cursor.weight() >= 0)
981                || (self.second_pass && self.cursor.weight() <= 0))
982        {
983            self.step_val();
984        }
985    }
986}
987
988impl SerCursor for CursorWithPolarity<'_> {
989    fn key_valid(&self) -> bool {
990        self.cursor.key_valid()
991    }
992
993    fn val_valid(&self) -> bool {
994        self.cursor.val_valid()
995    }
996
997    fn key(&self) -> &DynData {
998        self.cursor.key()
999    }
1000
1001    fn get_key(&self) -> Option<&DynData> {
1002        self.cursor.get_key()
1003    }
1004
1005    fn val(&self) -> &DynData {
1006        self.cursor.val()
1007    }
1008
1009    fn get_val(&self) -> Option<&DynData> {
1010        self.cursor.get_val()
1011    }
1012
1013    fn serialize_key(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
1014        self.cursor.serialize_key(dst)
1015    }
1016
1017    fn key_to_json(&mut self) -> AnyResult<serde_json::Value> {
1018        self.cursor.key_to_json()
1019    }
1020
1021    #[cfg(feature = "with-dynamodb")]
1022    fn key_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>> {
1023        self.cursor.key_to_dynamodb_item()
1024    }
1025
1026    fn serialize_key_fields(
1027        &mut self,
1028        fields: &HashSet<String>,
1029        dst: &mut Vec<u8>,
1030    ) -> AnyResult<()> {
1031        self.cursor.serialize_key_fields(fields, dst)
1032    }
1033
1034    fn serialize_val_fields(
1035        &mut self,
1036        fields: &HashSet<String>,
1037        dst: &mut Vec<u8>,
1038    ) -> AnyResult<()> {
1039        self.cursor.serialize_val_fields(fields, dst)
1040    }
1041
1042    #[cfg(feature = "with-avro")]
1043    fn key_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue> {
1044        self.cursor.key_to_avro(schema, refs)
1045    }
1046
1047    fn serialize_key_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
1048        self.cursor.serialize_key_weight(dst)
1049    }
1050
1051    fn serialize_val_weight(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
1052        self.cursor.serialize_val_weight(dst)
1053    }
1054
1055    fn serialize_key_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()> {
1056        self.cursor.serialize_key_to_arrow(dst)
1057    }
1058
1059    fn serialize_key_to_arrow_with_metadata(
1060        &mut self,
1061        metadata: &dyn erased_serde::Serialize,
1062        dst: &mut ArrayBuilder,
1063    ) -> AnyResult<()> {
1064        self.cursor
1065            .serialize_key_to_arrow_with_metadata(metadata, dst)
1066    }
1067
1068    fn serialize_val_to_arrow(&mut self, dst: &mut ArrayBuilder) -> AnyResult<()> {
1069        self.cursor.serialize_val_to_arrow(dst)
1070    }
1071
1072    fn serialize_val_to_arrow_with_metadata(
1073        &mut self,
1074        metadata: &dyn erased_serde::Serialize,
1075        dst: &mut ArrayBuilder,
1076    ) -> AnyResult<()> {
1077        self.cursor
1078            .serialize_val_to_arrow_with_metadata(metadata, dst)
1079    }
1080
1081    fn serialize_val(&mut self, dst: &mut Vec<u8>) -> AnyResult<()> {
1082        self.cursor.serialize_val(dst)
1083    }
1084
1085    fn val_to_json(&mut self) -> AnyResult<serde_json::Value> {
1086        self.cursor.val_to_json()
1087    }
1088
1089    #[cfg(feature = "with-dynamodb")]
1090    fn val_to_dynamodb_item(&mut self) -> AnyResult<HashMap<String, AttributeValue>> {
1091        self.cursor.val_to_dynamodb_item()
1092    }
1093
1094    #[cfg(feature = "with-avro")]
1095    fn val_to_avro(&mut self, schema: &AvroSchema, refs: &NamesRef<'_>) -> AnyResult<AvroValue> {
1096        self.cursor.val_to_avro(schema, refs)
1097    }
1098
1099    fn weight(&mut self) -> i64 {
1100        self.cursor.weight()
1101    }
1102
1103    fn step_key(&mut self) {
1104        self.cursor.step_key();
1105        if !self.cursor.key_valid() && !self.second_pass {
1106            self.cursor.rewind_keys();
1107            self.second_pass = true;
1108        }
1109
1110        if self.cursor.key_valid() {
1111            self.advance_val();
1112        }
1113    }
1114
1115    fn step_val(&mut self) {
1116        self.cursor.step_val();
1117        self.advance_val();
1118    }
1119
1120    fn rewind_keys(&mut self) {
1121        self.cursor.rewind_keys();
1122        self.second_pass = false;
1123        if self.cursor.key_valid() {
1124            self.advance_val();
1125        }
1126    }
1127
1128    fn rewind_vals(&mut self) {
1129        self.cursor.rewind_vals();
1130        self.advance_val();
1131    }
1132
1133    fn seek_key_exact(&mut self, key: &DynData) -> bool {
1134        self.cursor.seek_key_exact(key)
1135    }
1136
1137    fn seek_key(&mut self, key: &DynData) {
1138        self.cursor.seek_key(key);
1139    }
1140}
1141
1142/// A catalog of input and output stream handles of a circuit.
1143pub trait CircuitCatalog: Send + Sync {
1144    /// Look up an input stream handle by name.
1145    fn input_collection_handle(&self, name: &SqlIdentifier) -> Option<&InputCollectionHandle>;
1146
1147    fn output_iter(
1148        &self,
1149    ) -> Box<dyn Iterator<Item = (&SqlIdentifier, &OutputCollectionHandles)> + '_>;
1150
1151    /// Look up output stream handles by name.
1152    fn output_handles(&self, name: &SqlIdentifier) -> Option<&OutputCollectionHandles>;
1153
1154    /// Look up handles for an index of a stream.
1155    fn index_handles(
1156        &self,
1157        endpoint_name: &str,
1158        stream: &SqlIdentifier,
1159        index: &SqlIdentifier,
1160    ) -> Result<&OutputCollectionHandles, ControllerError>;
1161
1162    fn output_handles_mut(&mut self, name: &SqlIdentifier) -> Option<&mut OutputCollectionHandles>;
1163
1164    /// The registry used to insert new user-defined preprocessors
1165    fn preprocessor_registry(&self) -> Arc<Mutex<PreprocessorRegistry>>;
1166
1167    /// The registry used to insert new user-defined postprocessors
1168    fn postprocessor_registry(&self) -> Arc<Mutex<PostprocessorRegistry>>;
1169}
1170
1171#[doc(hidden)]
1172pub struct InputCollectionHandle {
1173    pub schema: Relation,
1174    pub handle: Box<dyn DeCollectionHandle>,
1175
1176    /// Node id of the input stream in the circuit.
1177    ///
1178    /// Used to check whether the input stream needs to be backfilled during bootstrapping,
1179    /// i.e., whether attached input connectors should be reset to their initial offsets or
1180    /// continue from the checkpointed offsets.
1181    pub node_id: NodeId,
1182}
1183
1184impl InputCollectionHandle {
1185    #[doc(hidden)]
1186    pub fn new<H>(schema: Relation, handle: H, node_id: NodeId) -> Self
1187    where
1188        H: DeCollectionHandle + 'static,
1189    {
1190        Self {
1191            schema,
1192            handle: Box::new(handle),
1193            node_id,
1194        }
1195    }
1196}
1197
1198/// A set of stream handles associated with each output collection.
1199#[derive(Clone)]
1200pub struct OutputCollectionHandles {
1201    /// Schema of the keys in the stream.
1202    ///
1203    /// Only set for indexed Z-sets.
1204    pub key_schema: Option<Relation>,
1205
1206    /// Schema of the values in the stream.
1207    ///
1208    /// If the stream is an indexed Z-set, this is the schema of the values in the stream;
1209    /// if it is a Z-set, this is the schema of the keys in the stream.
1210    pub value_schema: Relation,
1211
1212    /// Set when this stream is an index of another stream.
1213    pub index_of: Option<SqlIdentifier>,
1214
1215    /// Set when the same stream is used to represent a view and its index.
1216    pub alias_as_index: Option<SqlIdentifier>,
1217
1218    /// A handle to a snapshot of a materialized table/view.
1219    pub integrate_handle: Option<Arc<dyn SerBatchReaderHandle>>,
1220
1221    /// A stream of changes to the collection.
1222    pub delta_handle: Box<dyn SerBatchReaderHandle>,
1223
1224    /// Reference to the enable count of the accumulator used to collect updates to this stream.
1225    /// Incremented every time an output connector is attached to this stream; decremented when
1226    /// the output connector is detached.
1227    pub enable_count: EnableCount,
1228}
1229
1230impl OutputCollectionHandles {
1231    /// Stream is an indexed Z-set.
1232    pub fn is_indexed(&self) -> bool {
1233        self.key_schema.is_some()
1234    }
1235}