1use core::marker::PhantomData;
5
6use reifydb_abi::data::column::ColumnTypeCode;
7use reifydb_value::value::{date::Date, datetime::DateTime, duration::Duration, time::Time};
8
9use crate::{
10 error::SdkError,
11 operator::builder::{ColumnBuilder, ColumnsBuilder, CommittedColumn},
12};
13
14pub struct ScalarWriter<'a, T: Copy> {
15 inner: ColumnBuilder<'a>,
16 cursor: usize,
17 capacity: usize,
18 defined: Option<Vec<bool>>,
19 _t: PhantomData<T>,
20}
21
22impl<'a, T: Copy> ScalarWriter<'a, T> {
23 fn new(inner: ColumnBuilder<'a>, capacity: usize) -> Self {
24 Self {
25 inner,
26 cursor: 0,
27 capacity,
28 defined: None,
29 _t: PhantomData,
30 }
31 }
32
33 #[inline]
34 pub fn push(&mut self, v: T) {
35 debug_assert!(self.cursor < self.capacity, "ScalarWriter::push past capacity");
36 unsafe {
37 let data = self.inner.data_ptr() as *mut T;
38 core::ptr::write_unaligned(data.add(self.cursor), v);
39 }
40 if let Some(d) = self.defined.as_mut() {
41 d.push(true);
42 }
43 self.cursor += 1;
44 }
45
46 #[inline]
47 pub fn push_none(&mut self)
48 where
49 T: Default,
50 {
51 debug_assert!(self.cursor < self.capacity, "ScalarWriter::push_none past capacity");
52 unsafe {
53 let data = self.inner.data_ptr() as *mut T;
54 core::ptr::write_unaligned(data.add(self.cursor), T::default());
55 }
56 let d = self.defined.get_or_insert_with(|| vec![true; self.cursor]);
57 d.push(false);
58 self.cursor += 1;
59 }
60
61 #[inline]
62 pub fn len(&self) -> usize {
63 self.cursor
64 }
65
66 #[inline]
67 pub fn is_empty(&self) -> bool {
68 self.cursor == 0
69 }
70
71 pub fn finish(self) -> Result<CommittedColumn, SdkError> {
72 if let Some(d) = &self.defined {
73 self.inner.set_defined(d);
74 }
75 self.inner.commit(self.cursor)
76 }
77}
78
79pub struct BoolWriter<'a> {
80 inner: ColumnBuilder<'a>,
81 values: Vec<bool>,
82 defined: Option<Vec<bool>>,
83}
84
85impl<'a> BoolWriter<'a> {
86 fn new(inner: ColumnBuilder<'a>, capacity: usize) -> Self {
87 Self {
88 inner,
89 values: Vec::with_capacity(capacity),
90 defined: None,
91 }
92 }
93
94 #[inline]
95 pub fn push(&mut self, v: bool) {
96 self.values.push(v);
97 if let Some(d) = self.defined.as_mut() {
98 d.push(true);
99 }
100 }
101
102 #[inline]
103 pub fn push_none(&mut self) {
104 self.values.push(false);
105 let d = self.defined.get_or_insert_with(|| vec![true; self.values.len() - 1]);
106 d.push(false);
107 }
108
109 #[inline]
110 pub fn len(&self) -> usize {
111 self.values.len()
112 }
113
114 #[inline]
115 pub fn is_empty(&self) -> bool {
116 self.values.is_empty()
117 }
118
119 pub fn finish(self) -> Result<CommittedColumn, SdkError> {
120 if let Some(d) = &self.defined {
121 self.inner.set_defined(d);
122 }
123 self.inner.write_bool(&self.values)
124 }
125}
126
127pub struct VarLenWriter<'a> {
128 inner: ColumnBuilder<'a>,
129 item_cursor: usize,
130 byte_cursor: usize,
131 data_capacity: usize,
132 capacity: usize,
133 defined: Option<Vec<bool>>,
134 type_code: ColumnTypeCode,
135}
136
137impl<'a> VarLenWriter<'a> {
138 fn new(inner: ColumnBuilder<'a>, capacity: usize, expected_bytes: usize) -> Result<Self, SdkError> {
139 let type_code = inner.type_code();
140 debug_assert!(
141 matches!(type_code, ColumnTypeCode::Utf8 | ColumnTypeCode::Blob | ColumnTypeCode::Decimal),
142 "VarLenWriter requires Utf8, Blob, or Decimal",
143 );
144 let initial = expected_bytes.max(capacity);
145 if initial > 0 {
146 inner.grow(initial)?;
147 }
148 unsafe {
149 core::ptr::write(inner.offsets_ptr(), 0u64);
150 }
151 Ok(Self {
152 inner,
153 item_cursor: 0,
154 byte_cursor: 0,
155 data_capacity: initial,
156 capacity,
157 defined: None,
158 type_code,
159 })
160 }
161
162 #[inline]
163 fn ensure_capacity(&mut self, need: usize) -> Result<(), SdkError> {
164 if self.byte_cursor + need <= self.data_capacity {
165 return Ok(());
166 }
167 let extra = (self.byte_cursor + need - self.data_capacity).max(self.data_capacity.max(64));
168 self.inner.grow(extra)?;
169 self.data_capacity += extra;
170 Ok(())
171 }
172
173 #[inline]
174 fn push_bytes_internal(&mut self, bytes: &[u8]) -> Result<(), SdkError> {
175 debug_assert!(self.item_cursor < self.capacity, "VarLenWriter::push past capacity");
176 self.ensure_capacity(bytes.len())?;
177 unsafe {
178 let data = self.inner.data_ptr();
179 let offsets = self.inner.offsets_ptr();
180 if !bytes.is_empty() {
181 core::ptr::copy_nonoverlapping(bytes.as_ptr(), data.add(self.byte_cursor), bytes.len());
182 }
183 self.byte_cursor += bytes.len();
184 core::ptr::write(offsets.add(self.item_cursor + 1), self.byte_cursor as u64);
185 }
186 if let Some(d) = self.defined.as_mut() {
187 d.push(true);
188 }
189 self.item_cursor += 1;
190 Ok(())
191 }
192
193 pub fn push_str(&mut self, s: &str) -> Result<(), SdkError> {
194 debug_assert_eq!(self.type_code, ColumnTypeCode::Utf8);
195 self.push_bytes_internal(s.as_bytes())
196 }
197
198 pub fn push_bytes(&mut self, b: &[u8]) -> Result<(), SdkError> {
199 debug_assert!(matches!(self.type_code, ColumnTypeCode::Blob | ColumnTypeCode::Decimal));
200 self.push_bytes_internal(b)
201 }
202
203 pub fn push_none(&mut self) -> Result<(), SdkError> {
204 debug_assert!(self.item_cursor < self.capacity, "VarLenWriter::push_none past capacity");
205 unsafe {
206 let offsets = self.inner.offsets_ptr();
207 core::ptr::write(offsets.add(self.item_cursor + 1), self.byte_cursor as u64);
208 }
209 let d = self.defined.get_or_insert_with(|| vec![true; self.item_cursor]);
210 d.push(false);
211 self.item_cursor += 1;
212 Ok(())
213 }
214
215 #[inline]
216 pub fn len(&self) -> usize {
217 self.item_cursor
218 }
219
220 #[inline]
221 pub fn is_empty(&self) -> bool {
222 self.item_cursor == 0
223 }
224
225 pub fn finish(self) -> Result<CommittedColumn, SdkError> {
226 if let Some(d) = &self.defined {
227 self.inner.set_defined(d);
228 }
229 self.inner.commit(self.item_cursor)
230 }
231}
232
233pub type U8Writer<'a> = ScalarWriter<'a, u8>;
234pub type U16Writer<'a> = ScalarWriter<'a, u16>;
235pub type U32Writer<'a> = ScalarWriter<'a, u32>;
236pub type U64Writer<'a> = ScalarWriter<'a, u64>;
237pub type U128Writer<'a> = ScalarWriter<'a, u128>;
238pub type I8Writer<'a> = ScalarWriter<'a, i8>;
239pub type I16Writer<'a> = ScalarWriter<'a, i16>;
240pub type I32Writer<'a> = ScalarWriter<'a, i32>;
241pub type I64Writer<'a> = ScalarWriter<'a, i64>;
242pub type I128Writer<'a> = ScalarWriter<'a, i128>;
243pub type F32Writer<'a> = ScalarWriter<'a, f32>;
244pub type F64Writer<'a> = ScalarWriter<'a, f64>;
245pub type DateWriter<'a> = ScalarWriter<'a, Date>;
246pub type DateTimeWriter<'a> = ScalarWriter<'a, DateTime>;
247pub type TimeWriter<'a> = ScalarWriter<'a, Time>;
248pub type DurationWriter<'a> = ScalarWriter<'a, Duration>;
249pub type Utf8Writer<'a> = VarLenWriter<'a>;
250pub type BlobWriter<'a> = VarLenWriter<'a>;
251pub type DecimalWriter<'a> = VarLenWriter<'a>;
252
253impl<'a> ColumnsBuilder<'a> {
254 pub fn u8_writer(&mut self, capacity: usize) -> Result<U8Writer<'_>, SdkError> {
255 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Uint1, capacity)?, capacity))
256 }
257 pub fn u16_writer(&mut self, capacity: usize) -> Result<U16Writer<'_>, SdkError> {
258 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Uint2, capacity)?, capacity))
259 }
260 pub fn u32_writer(&mut self, capacity: usize) -> Result<U32Writer<'_>, SdkError> {
261 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Uint4, capacity)?, capacity))
262 }
263 pub fn u64_writer(&mut self, capacity: usize) -> Result<U64Writer<'_>, SdkError> {
264 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Uint8, capacity)?, capacity))
265 }
266 pub fn u128_writer(&mut self, capacity: usize) -> Result<U128Writer<'_>, SdkError> {
267 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Uint16, capacity)?, capacity))
268 }
269 pub fn i8_writer(&mut self, capacity: usize) -> Result<I8Writer<'_>, SdkError> {
270 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Int1, capacity)?, capacity))
271 }
272 pub fn i16_writer(&mut self, capacity: usize) -> Result<I16Writer<'_>, SdkError> {
273 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Int2, capacity)?, capacity))
274 }
275 pub fn i32_writer(&mut self, capacity: usize) -> Result<I32Writer<'_>, SdkError> {
276 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Int4, capacity)?, capacity))
277 }
278 pub fn i64_writer(&mut self, capacity: usize) -> Result<I64Writer<'_>, SdkError> {
279 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Int8, capacity)?, capacity))
280 }
281 pub fn i128_writer(&mut self, capacity: usize) -> Result<I128Writer<'_>, SdkError> {
282 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Int16, capacity)?, capacity))
283 }
284 pub fn f32_writer(&mut self, capacity: usize) -> Result<F32Writer<'_>, SdkError> {
285 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Float4, capacity)?, capacity))
286 }
287 pub fn f64_writer(&mut self, capacity: usize) -> Result<F64Writer<'_>, SdkError> {
288 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Float8, capacity)?, capacity))
289 }
290 pub fn date_writer(&mut self, capacity: usize) -> Result<DateWriter<'_>, SdkError> {
291 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Date, capacity)?, capacity))
292 }
293 pub fn datetime_writer(&mut self, capacity: usize) -> Result<DateTimeWriter<'_>, SdkError> {
294 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::DateTime, capacity)?, capacity))
295 }
296 pub fn time_writer(&mut self, capacity: usize) -> Result<TimeWriter<'_>, SdkError> {
297 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Time, capacity)?, capacity))
298 }
299 pub fn duration_writer(&mut self, capacity: usize) -> Result<DurationWriter<'_>, SdkError> {
300 Ok(ScalarWriter::new(self.acquire(ColumnTypeCode::Duration, capacity)?, capacity))
301 }
302 pub fn bool_writer(&mut self, capacity: usize) -> Result<BoolWriter<'_>, SdkError> {
303 Ok(BoolWriter::new(self.acquire(ColumnTypeCode::Bool, capacity)?, capacity))
304 }
305 pub fn utf8_writer(&mut self, capacity: usize, expected_bytes: usize) -> Result<Utf8Writer<'_>, SdkError> {
306 VarLenWriter::new(self.acquire(ColumnTypeCode::Utf8, capacity)?, capacity, expected_bytes)
307 }
308 pub fn blob_writer(&mut self, capacity: usize, expected_bytes: usize) -> Result<BlobWriter<'_>, SdkError> {
309 VarLenWriter::new(self.acquire(ColumnTypeCode::Blob, capacity)?, capacity, expected_bytes)
310 }
311 pub fn decimal_writer(
312 &mut self,
313 capacity: usize,
314 expected_bytes: usize,
315 ) -> Result<DecimalWriter<'_>, SdkError> {
316 VarLenWriter::new(self.acquire(ColumnTypeCode::Decimal, capacity)?, capacity, expected_bytes)
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use reifydb_abi::operator::capabilities::OperatorCapability;
323 use reifydb_core::interface::catalog::flow::FlowNodeId;
324 use reifydb_value::value::{
325 date::Date, datetime::DateTime, decimal::Decimal, duration::Duration, row_number::RowNumber, time::Time,
326 };
327
328 use crate::{
329 config::Config,
330 error::Result,
331 operator::{
332 FFIOperator, OperatorMetadata,
333 change::BorrowedChange,
334 column::{batch::InsertBatch, operator::OperatorColumn},
335 context::ffi::FFIOperatorContext,
336 },
337 row,
338 testing::{builders::TestChangeBuilder, harness::FFIOperatorHarnessBuilder},
339 };
340
341 struct U8Row {
342 v: u8,
343 }
344 row!(U8Row {
345 v: u8
346 });
347
348 struct OpU8;
349 impl OperatorMetadata for OpU8 {
350 const NAME: &'static str = "writer_u8";
351 const API: u32 = 1;
352 const VERSION: &'static str = "1.0.0";
353 const DESCRIPTION: &'static str = "test fixture";
354 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
355 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
356 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
357 }
358 impl FFIOperator for OpU8 {
359 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
360 Ok(Self)
361 }
362 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
363 let mut batch = InsertBatch::<U8Row, _>::new(ctx, 3)?;
364 for (i, &v) in [0u8, 1, u8::MAX].iter().enumerate() {
365 batch.push(
366 RowNumber(i as u64 + 1),
367 &U8Row {
368 v,
369 },
370 )?;
371 }
372 batch.finish()
373 }
374 }
375
376 #[test]
377 fn scalar_u8_roundtrip() {
378 let mut h = FFIOperatorHarnessBuilder::<OpU8>::new().build().expect("harness");
379 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
380 let post = out.diffs[0].post().expect("post");
381 assert_eq!(post.row_count(), 3);
382 assert_eq!(post.row_ref(0).expect("r0").u8("v"), Some(0));
383 assert_eq!(post.row_ref(1).expect("r1").u8("v"), Some(1));
384 assert_eq!(post.row_ref(2).expect("r2").u8("v"), Some(u8::MAX));
385 }
386
387 struct U16Row {
388 v: u16,
389 }
390 row!(U16Row {
391 v: u16
392 });
393
394 struct OpU16;
395 impl OperatorMetadata for OpU16 {
396 const NAME: &'static str = "writer_u16";
397 const API: u32 = 1;
398 const VERSION: &'static str = "1.0.0";
399 const DESCRIPTION: &'static str = "test fixture";
400 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
401 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
402 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
403 }
404 impl FFIOperator for OpU16 {
405 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
406 Ok(Self)
407 }
408 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
409 let mut batch = InsertBatch::<U16Row, _>::new(ctx, 3)?;
410 for (i, &v) in [0u16, 1, u16::MAX].iter().enumerate() {
411 batch.push(
412 RowNumber(i as u64 + 1),
413 &U16Row {
414 v,
415 },
416 )?;
417 }
418 batch.finish()
419 }
420 }
421
422 #[test]
423 fn scalar_u16_roundtrip() {
424 let mut h = FFIOperatorHarnessBuilder::<OpU16>::new().build().expect("harness");
425 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
426 let post = out.diffs[0].post().expect("post");
427 assert_eq!(post.row_count(), 3);
428 assert_eq!(post.row_ref(0).expect("r0").u16("v"), Some(0));
429 assert_eq!(post.row_ref(1).expect("r1").u16("v"), Some(1));
430 assert_eq!(post.row_ref(2).expect("r2").u16("v"), Some(u16::MAX));
431 }
432
433 struct U32Row {
434 v: u32,
435 }
436 row!(U32Row {
437 v: u32
438 });
439
440 struct OpU32;
441 impl OperatorMetadata for OpU32 {
442 const NAME: &'static str = "writer_u32";
443 const API: u32 = 1;
444 const VERSION: &'static str = "1.0.0";
445 const DESCRIPTION: &'static str = "test fixture";
446 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
447 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
448 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
449 }
450 impl FFIOperator for OpU32 {
451 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
452 Ok(Self)
453 }
454 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
455 let mut batch = InsertBatch::<U32Row, _>::new(ctx, 3)?;
456 for (i, &v) in [0u32, 1, u32::MAX].iter().enumerate() {
457 batch.push(
458 RowNumber(i as u64 + 1),
459 &U32Row {
460 v,
461 },
462 )?;
463 }
464 batch.finish()
465 }
466 }
467
468 #[test]
469 fn scalar_u32_roundtrip() {
470 let mut h = FFIOperatorHarnessBuilder::<OpU32>::new().build().expect("harness");
471 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
472 let post = out.diffs[0].post().expect("post");
473 assert_eq!(post.row_count(), 3);
474 assert_eq!(post.row_ref(0).expect("r0").u32("v"), Some(0));
475 assert_eq!(post.row_ref(1).expect("r1").u32("v"), Some(1));
476 assert_eq!(post.row_ref(2).expect("r2").u32("v"), Some(u32::MAX));
477 }
478
479 struct U64Row {
480 v: u64,
481 }
482 row!(U64Row {
483 v: u64
484 });
485
486 struct OpU64;
487 impl OperatorMetadata for OpU64 {
488 const NAME: &'static str = "writer_u64";
489 const API: u32 = 1;
490 const VERSION: &'static str = "1.0.0";
491 const DESCRIPTION: &'static str = "test fixture";
492 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
493 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
494 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
495 }
496 impl FFIOperator for OpU64 {
497 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
498 Ok(Self)
499 }
500 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
501 let mut batch = InsertBatch::<U64Row, _>::new(ctx, 3)?;
502 for (i, &v) in [0u64, 1, u64::MAX].iter().enumerate() {
503 batch.push(
504 RowNumber(i as u64 + 1),
505 &U64Row {
506 v,
507 },
508 )?;
509 }
510 batch.finish()
511 }
512 }
513
514 #[test]
515 fn scalar_u64_roundtrip() {
516 let mut h = FFIOperatorHarnessBuilder::<OpU64>::new().build().expect("harness");
517 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
518 let post = out.diffs[0].post().expect("post");
519 assert_eq!(post.row_count(), 3);
520 assert_eq!(post.row_ref(0).expect("r0").u64("v"), Some(0));
521 assert_eq!(post.row_ref(1).expect("r1").u64("v"), Some(1));
522 assert_eq!(post.row_ref(2).expect("r2").u64("v"), Some(u64::MAX));
523 }
524
525 struct I8Row {
526 v: i8,
527 }
528 row!(I8Row {
529 v: i8
530 });
531
532 struct OpI8;
533 impl OperatorMetadata for OpI8 {
534 const NAME: &'static str = "writer_i8";
535 const API: u32 = 1;
536 const VERSION: &'static str = "1.0.0";
537 const DESCRIPTION: &'static str = "test fixture";
538 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
539 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
540 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
541 }
542 impl FFIOperator for OpI8 {
543 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
544 Ok(Self)
545 }
546 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
547 let mut batch = InsertBatch::<I8Row, _>::new(ctx, 3)?;
548 for (i, &v) in [i8::MIN, 0_i8, i8::MAX].iter().enumerate() {
549 batch.push(
550 RowNumber(i as u64 + 1),
551 &I8Row {
552 v,
553 },
554 )?;
555 }
556 batch.finish()
557 }
558 }
559
560 #[test]
561 fn scalar_i8_roundtrip() {
562 let mut h = FFIOperatorHarnessBuilder::<OpI8>::new().build().expect("harness");
563 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
564 let post = out.diffs[0].post().expect("post");
565 assert_eq!(post.row_count(), 3);
566 assert_eq!(post.row_ref(0).expect("r0").i8("v"), Some(i8::MIN));
567 assert_eq!(post.row_ref(1).expect("r1").i8("v"), Some(0));
568 assert_eq!(post.row_ref(2).expect("r2").i8("v"), Some(i8::MAX));
569 }
570
571 struct I16Row {
572 v: i16,
573 }
574 row!(I16Row {
575 v: i16
576 });
577
578 struct OpI16;
579 impl OperatorMetadata for OpI16 {
580 const NAME: &'static str = "writer_i16";
581 const API: u32 = 1;
582 const VERSION: &'static str = "1.0.0";
583 const DESCRIPTION: &'static str = "test fixture";
584 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
585 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
586 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
587 }
588 impl FFIOperator for OpI16 {
589 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
590 Ok(Self)
591 }
592 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
593 let mut batch = InsertBatch::<I16Row, _>::new(ctx, 3)?;
594 for (i, &v) in [i16::MIN, 0_i16, i16::MAX].iter().enumerate() {
595 batch.push(
596 RowNumber(i as u64 + 1),
597 &I16Row {
598 v,
599 },
600 )?;
601 }
602 batch.finish()
603 }
604 }
605
606 #[test]
607 fn scalar_i16_roundtrip() {
608 let mut h = FFIOperatorHarnessBuilder::<OpI16>::new().build().expect("harness");
609 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
610 let post = out.diffs[0].post().expect("post");
611 assert_eq!(post.row_count(), 3);
612 assert_eq!(post.row_ref(0).expect("r0").i16("v"), Some(i16::MIN));
613 assert_eq!(post.row_ref(1).expect("r1").i16("v"), Some(0));
614 assert_eq!(post.row_ref(2).expect("r2").i16("v"), Some(i16::MAX));
615 }
616
617 struct I32Row {
618 v: i32,
619 }
620 row!(I32Row {
621 v: i32
622 });
623
624 struct OpI32;
625 impl OperatorMetadata for OpI32 {
626 const NAME: &'static str = "writer_i32";
627 const API: u32 = 1;
628 const VERSION: &'static str = "1.0.0";
629 const DESCRIPTION: &'static str = "test fixture";
630 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
631 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
632 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
633 }
634 impl FFIOperator for OpI32 {
635 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
636 Ok(Self)
637 }
638 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
639 let mut batch = InsertBatch::<I32Row, _>::new(ctx, 3)?;
640 for (i, &v) in [i32::MIN, 0_i32, i32::MAX].iter().enumerate() {
641 batch.push(
642 RowNumber(i as u64 + 1),
643 &I32Row {
644 v,
645 },
646 )?;
647 }
648 batch.finish()
649 }
650 }
651
652 #[test]
653 fn scalar_i32_roundtrip() {
654 let mut h = FFIOperatorHarnessBuilder::<OpI32>::new().build().expect("harness");
655 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
656 let post = out.diffs[0].post().expect("post");
657 assert_eq!(post.row_count(), 3);
658 assert_eq!(post.row_ref(0).expect("r0").i32("v"), Some(i32::MIN));
659 assert_eq!(post.row_ref(1).expect("r1").i32("v"), Some(0));
660 assert_eq!(post.row_ref(2).expect("r2").i32("v"), Some(i32::MAX));
661 }
662
663 struct I64Row {
664 v: i64,
665 }
666 row!(I64Row {
667 v: i64
668 });
669
670 struct OpI64;
671 impl OperatorMetadata for OpI64 {
672 const NAME: &'static str = "writer_i64";
673 const API: u32 = 1;
674 const VERSION: &'static str = "1.0.0";
675 const DESCRIPTION: &'static str = "test fixture";
676 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
677 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
678 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
679 }
680 impl FFIOperator for OpI64 {
681 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
682 Ok(Self)
683 }
684 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
685 let mut batch = InsertBatch::<I64Row, _>::new(ctx, 3)?;
686 for (i, &v) in [i64::MIN, 0_i64, i64::MAX].iter().enumerate() {
687 batch.push(
688 RowNumber(i as u64 + 1),
689 &I64Row {
690 v,
691 },
692 )?;
693 }
694 batch.finish()
695 }
696 }
697
698 #[test]
699 fn scalar_i64_roundtrip() {
700 let mut h = FFIOperatorHarnessBuilder::<OpI64>::new().build().expect("harness");
701 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
702 let post = out.diffs[0].post().expect("post");
703 assert_eq!(post.row_count(), 3);
704 assert_eq!(post.row_ref(0).expect("r0").i64("v"), Some(i64::MIN));
705 assert_eq!(post.row_ref(1).expect("r1").i64("v"), Some(0));
706 assert_eq!(post.row_ref(2).expect("r2").i64("v"), Some(i64::MAX));
707 }
708
709 struct F32Row {
710 v: f32,
711 }
712 row!(F32Row {
713 v: f32
714 });
715
716 struct OpF32;
717 impl OperatorMetadata for OpF32 {
718 const NAME: &'static str = "writer_f32";
719 const API: u32 = 1;
720 const VERSION: &'static str = "1.0.0";
721 const DESCRIPTION: &'static str = "test fixture";
722 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
723 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
724 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
725 }
726 impl FFIOperator for OpF32 {
727 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
728 Ok(Self)
729 }
730 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
731 let mut batch = InsertBatch::<F32Row, _>::new(ctx, 3)?;
732 for (i, &v) in [0.0_f32, -1.5_f32, f32::MAX].iter().enumerate() {
733 batch.push(
734 RowNumber(i as u64 + 1),
735 &F32Row {
736 v,
737 },
738 )?;
739 }
740 batch.finish()
741 }
742 }
743
744 #[test]
745 fn scalar_f32_roundtrip() {
746 let mut h = FFIOperatorHarnessBuilder::<OpF32>::new().build().expect("harness");
747 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
748 let post = out.diffs[0].post().expect("post");
749 assert_eq!(post.row_count(), 3);
750 assert_eq!(post.row_ref(0).expect("r0").f32("v"), Some(0.0_f32));
751 assert_eq!(post.row_ref(1).expect("r1").f32("v"), Some(-1.5_f32));
752 assert_eq!(post.row_ref(2).expect("r2").f32("v"), Some(f32::MAX));
753 }
754
755 struct F64Row {
756 v: f64,
757 }
758 row!(F64Row {
759 v: f64
760 });
761
762 struct OpF64;
763 impl OperatorMetadata for OpF64 {
764 const NAME: &'static str = "writer_f64";
765 const API: u32 = 1;
766 const VERSION: &'static str = "1.0.0";
767 const DESCRIPTION: &'static str = "test fixture";
768 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
769 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
770 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
771 }
772 impl FFIOperator for OpF64 {
773 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
774 Ok(Self)
775 }
776 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
777 let mut batch = InsertBatch::<F64Row, _>::new(ctx, 3)?;
778 for (i, &v) in [0.0_f64, -1.5_f64, f64::MAX].iter().enumerate() {
779 batch.push(
780 RowNumber(i as u64 + 1),
781 &F64Row {
782 v,
783 },
784 )?;
785 }
786 batch.finish()
787 }
788 }
789
790 #[test]
791 fn scalar_f64_roundtrip() {
792 let mut h = FFIOperatorHarnessBuilder::<OpF64>::new().build().expect("harness");
793 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
794 let post = out.diffs[0].post().expect("post");
795 assert_eq!(post.row_count(), 3);
796 assert_eq!(post.row_ref(0).expect("r0").f64("v"), Some(0.0_f64));
797 assert_eq!(post.row_ref(1).expect("r1").f64("v"), Some(-1.5_f64));
798 assert_eq!(post.row_ref(2).expect("r2").f64("v"), Some(f64::MAX));
799 }
800
801 struct BoolRow {
802 v: bool,
803 }
804 row!(BoolRow {
805 v: bool
806 });
807
808 struct OpBool;
809 impl OperatorMetadata for OpBool {
810 const NAME: &'static str = "writer_bool";
811 const API: u32 = 1;
812 const VERSION: &'static str = "1.0.0";
813 const DESCRIPTION: &'static str = "test fixture";
814 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
815 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
816 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
817 }
818 impl FFIOperator for OpBool {
819 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
820 Ok(Self)
821 }
822 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
823 let mut batch = InsertBatch::<BoolRow, _>::new(ctx, 3)?;
824 for (i, &v) in [true, false, true].iter().enumerate() {
825 batch.push(
826 RowNumber(i as u64 + 1),
827 &BoolRow {
828 v,
829 },
830 )?;
831 }
832 batch.finish()
833 }
834 }
835
836 #[test]
837 fn bool_roundtrip() {
838 let mut h = FFIOperatorHarnessBuilder::<OpBool>::new().build().expect("harness");
839 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
840 let post = out.diffs[0].post().expect("post");
841 assert_eq!(post.row_count(), 3);
842 assert_eq!(post.row_ref(0).expect("r0").bool("v"), Some(true));
843 assert_eq!(post.row_ref(1).expect("r1").bool("v"), Some(false));
844 assert_eq!(post.row_ref(2).expect("r2").bool("v"), Some(true));
845 }
846
847 struct Utf8Row {
848 s: String,
849 }
850 row!(Utf8Row {
851 s: String
852 });
853
854 struct OpUtf8;
855 impl OperatorMetadata for OpUtf8 {
856 const NAME: &'static str = "writer_utf8";
857 const API: u32 = 1;
858 const VERSION: &'static str = "1.0.0";
859 const DESCRIPTION: &'static str = "test fixture";
860 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
861 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
862 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
863 }
864 impl FFIOperator for OpUtf8 {
865 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
866 Ok(Self)
867 }
868 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
869 let values = ["", "hello", "こんにちは"];
870 let mut batch = InsertBatch::<Utf8Row, _>::new(ctx, values.len())?;
871 for (i, &s) in values.iter().enumerate() {
872 batch.push(
873 RowNumber(i as u64 + 1),
874 &Utf8Row {
875 s: s.to_string(),
876 },
877 )?;
878 }
879 batch.finish()
880 }
881 }
882
883 #[test]
884 fn utf8_roundtrip() {
885 let mut h = FFIOperatorHarnessBuilder::<OpUtf8>::new().build().expect("harness");
886 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
887 let post = out.diffs[0].post().expect("post");
888 assert_eq!(post.row_count(), 3);
889 assert_eq!(post.row_ref(0).expect("r0").utf8("s").as_deref(), Some(""));
890 assert_eq!(post.row_ref(1).expect("r1").utf8("s").as_deref(), Some("hello"));
891 assert_eq!(post.row_ref(2).expect("r2").utf8("s").as_deref(), Some("こんにちは"));
892 }
893
894 struct OpUtf8Growth;
895 impl OperatorMetadata for OpUtf8Growth {
896 const NAME: &'static str = "writer_utf8_growth";
897 const API: u32 = 1;
898 const VERSION: &'static str = "1.0.0";
899 const DESCRIPTION: &'static str = "test fixture";
900 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
901 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
902 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
903 }
904 impl FFIOperator for OpUtf8Growth {
905 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
906 Ok(Self)
907 }
908 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
909 let mut batch = InsertBatch::<Utf8Row, _>::new(ctx, 20)?;
912 for i in 0..20u64 {
913 batch.push(
914 RowNumber(i + 1),
915 &Utf8Row {
916 s: "x".repeat(100),
917 },
918 )?;
919 }
920 batch.finish()
921 }
922 }
923
924 #[test]
925 fn utf8_capacity_growth() {
926 let mut h = FFIOperatorHarnessBuilder::<OpUtf8Growth>::new().build().expect("harness");
927 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
928 let post = out.diffs[0].post().expect("post");
929 assert_eq!(post.row_count(), 20);
930 let expected = "x".repeat(100);
931 for i in 0..20usize {
932 assert_eq!(
933 post.row_ref(i).expect("row").utf8("s").as_deref(),
934 Some(expected.as_str()),
935 "row {i}"
936 );
937 }
938 }
939
940 struct BlobRow {
941 b: Vec<u8>,
942 }
943 row!(BlobRow { b: Vec<u8> });
944
945 struct OpBlob;
946 impl OperatorMetadata for OpBlob {
947 const NAME: &'static str = "writer_blob";
948 const API: u32 = 1;
949 const VERSION: &'static str = "1.0.0";
950 const DESCRIPTION: &'static str = "test fixture";
951 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
952 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
953 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
954 }
955 impl FFIOperator for OpBlob {
956 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
957 Ok(Self)
958 }
959 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
960 let rows = [
961 BlobRow {
962 b: vec![],
963 },
964 BlobRow {
965 b: vec![0u8, 1, 127, 255],
966 },
967 BlobRow {
968 b: vec![42u8; 1000],
969 },
970 ];
971 let mut batch = InsertBatch::<BlobRow, _>::new(ctx, rows.len())?;
972 for (i, row) in rows.iter().enumerate() {
973 batch.push(RowNumber(i as u64 + 1), row)?;
974 }
975 batch.finish()
976 }
977 }
978
979 #[test]
980 fn blob_roundtrip() {
981 let mut h = FFIOperatorHarnessBuilder::<OpBlob>::new().build().expect("harness");
982 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
983 let post = out.diffs[0].post().expect("post");
984 assert_eq!(post.row_count(), 3);
985 assert_eq!(post.row_ref(0).expect("r0").blob("b"), Some(vec![]));
986 assert_eq!(post.row_ref(1).expect("r1").blob("b"), Some(vec![0u8, 1, 127, 255]));
987 assert_eq!(post.row_ref(2).expect("r2").blob("b"), Some(vec![42u8; 1000]));
988 }
989
990 struct DecimalRow {
991 d: Decimal,
992 }
993 row!(DecimalRow {
994 d: Decimal
995 });
996
997 struct OpDecimal;
998 impl OperatorMetadata for OpDecimal {
999 const NAME: &'static str = "writer_decimal";
1000 const API: u32 = 1;
1001 const VERSION: &'static str = "1.0.0";
1002 const DESCRIPTION: &'static str = "test fixture";
1003 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
1004 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
1005 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
1006 }
1007 impl FFIOperator for OpDecimal {
1008 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
1009 Ok(Self)
1010 }
1011 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
1012 let mut batch = InsertBatch::<DecimalRow, _>::new(ctx, 3)?;
1013 batch.push(
1014 RowNumber(1),
1015 &DecimalRow {
1016 d: Decimal::zero(),
1017 },
1018 )?;
1019 batch.push(
1020 RowNumber(2),
1021 &DecimalRow {
1022 d: Decimal::from_i64(1234),
1023 },
1024 )?;
1025 batch.push(
1026 RowNumber(3),
1027 &DecimalRow {
1028 d: Decimal::from_i64(-5678),
1029 },
1030 )?;
1031 batch.finish()
1032 }
1033 }
1034
1035 #[test]
1036 fn decimal_roundtrip() {
1037 let mut h = FFIOperatorHarnessBuilder::<OpDecimal>::new().build().expect("harness");
1038 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
1039 let post = out.diffs[0].post().expect("post");
1040 assert_eq!(post.row_count(), 3);
1041 assert_eq!(post.row_ref(0).expect("r0").decimal("d"), Some(Decimal::zero()));
1042 assert_eq!(post.row_ref(1).expect("r1").decimal("d"), Some(Decimal::from_i64(1234)));
1043 assert_eq!(post.row_ref(2).expect("r2").decimal("d"), Some(Decimal::from_i64(-5678)));
1044 }
1045
1046 struct WideRow {
1047 a: u128,
1048 b: i128,
1049 }
1050 row!(WideRow {
1051 a: u128,
1052 b: i128
1053 });
1054
1055 struct OpWide;
1056 impl OperatorMetadata for OpWide {
1057 const NAME: &'static str = "writer_wide";
1058 const API: u32 = 1;
1059 const VERSION: &'static str = "1.0.0";
1060 const DESCRIPTION: &'static str = "test fixture";
1061 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
1062 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
1063 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
1064 }
1065 impl FFIOperator for OpWide {
1066 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
1067 Ok(Self)
1068 }
1069 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
1070 let mut batch = InsertBatch::<WideRow, _>::new(ctx, 1)?;
1071 batch.push(
1072 RowNumber(1),
1073 &WideRow {
1074 a: u128::MAX,
1075 b: i128::MIN,
1076 },
1077 )?;
1078 batch.finish()
1079 }
1080 }
1081
1082 #[test]
1083 fn wide_integers_roundtrip() {
1084 let mut h = FFIOperatorHarnessBuilder::<OpWide>::new().build().expect("harness");
1085 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
1086 let post = out.diffs[0].post().expect("post");
1087 assert_eq!(post.row_count(), 1);
1088 assert_eq!(post.row_ref(0).expect("r0").u128("a"), Some(u128::MAX));
1089 assert_eq!(post.row_ref(0).expect("r0").i128("b"), Some(i128::MIN));
1090 }
1091
1092 struct DateRow {
1093 v: Date,
1094 }
1095 row!(DateRow {
1096 v: Date
1097 });
1098
1099 struct OpDate;
1100 impl OperatorMetadata for OpDate {
1101 const NAME: &'static str = "writer_date";
1102 const API: u32 = 1;
1103 const VERSION: &'static str = "1.0.0";
1104 const DESCRIPTION: &'static str = "test fixture";
1105 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
1106 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
1107 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
1108 }
1109 impl FFIOperator for OpDate {
1110 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
1111 Ok(Self)
1112 }
1113 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
1114 let values = [
1115 Date::default(),
1116 Date::new(2024, 3, 15).expect("date"),
1117 Date::new(2554, 1, 1).expect("date"),
1118 ];
1119 let mut batch = InsertBatch::<DateRow, _>::new(ctx, values.len())?;
1120 for (i, &v) in values.iter().enumerate() {
1121 batch.push(
1122 RowNumber(i as u64 + 1),
1123 &DateRow {
1124 v,
1125 },
1126 )?;
1127 }
1128 batch.finish()
1129 }
1130 }
1131
1132 #[test]
1133 fn scalar_date_roundtrip() {
1134 let mut h = FFIOperatorHarnessBuilder::<OpDate>::new().build().expect("harness");
1135 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
1136 let post = out.diffs[0].post().expect("post");
1137 assert_eq!(post.row_count(), 3);
1138 assert_eq!(post.row_ref(0).expect("r0").date("v"), Some(Date::default()));
1139 assert_eq!(post.row_ref(1).expect("r1").date("v"), Date::new(2024, 3, 15));
1140 assert_eq!(post.row_ref(2).expect("r2").date("v"), Date::new(2554, 1, 1));
1141 }
1142
1143 struct DateTimeRow {
1144 v: DateTime,
1145 }
1146 row!(DateTimeRow {
1147 v: DateTime
1148 });
1149
1150 struct OpDateTime;
1151 impl OperatorMetadata for OpDateTime {
1152 const NAME: &'static str = "writer_datetime";
1153 const API: u32 = 1;
1154 const VERSION: &'static str = "1.0.0";
1155 const DESCRIPTION: &'static str = "test fixture";
1156 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
1157 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
1158 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
1159 }
1160 impl FFIOperator for OpDateTime {
1161 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
1162 Ok(Self)
1163 }
1164 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
1165 let values = [
1166 DateTime::from_nanos(0),
1167 DateTime::from_nanos(1_700_000_000_000_000_000),
1168 DateTime::from_nanos(u64::MAX),
1169 ];
1170 let mut batch = InsertBatch::<DateTimeRow, _>::new(ctx, values.len())?;
1171 for (i, &v) in values.iter().enumerate() {
1172 batch.push(
1173 RowNumber(i as u64 + 1),
1174 &DateTimeRow {
1175 v,
1176 },
1177 )?;
1178 }
1179 batch.finish()
1180 }
1181 }
1182
1183 #[test]
1184 fn scalar_datetime_roundtrip() {
1185 let mut h = FFIOperatorHarnessBuilder::<OpDateTime>::new().build().expect("harness");
1186 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
1187 let post = out.diffs[0].post().expect("post");
1188 assert_eq!(post.row_count(), 3);
1189 assert_eq!(post.row_ref(0).expect("r0").datetime("v"), Some(DateTime::from_nanos(0)));
1190 assert_eq!(
1191 post.row_ref(1).expect("r1").datetime("v"),
1192 Some(DateTime::from_nanos(1_700_000_000_000_000_000))
1193 );
1194 assert_eq!(post.row_ref(2).expect("r2").datetime("v"), Some(DateTime::from_nanos(u64::MAX)));
1195 }
1196
1197 struct TimeRow {
1198 v: Time,
1199 }
1200 row!(TimeRow {
1201 v: Time
1202 });
1203
1204 struct OpTime;
1205 impl OperatorMetadata for OpTime {
1206 const NAME: &'static str = "writer_time";
1207 const API: u32 = 1;
1208 const VERSION: &'static str = "1.0.0";
1209 const DESCRIPTION: &'static str = "test fixture";
1210 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
1211 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
1212 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
1213 }
1214 impl FFIOperator for OpTime {
1215 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
1216 Ok(Self)
1217 }
1218 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
1219 let values = [
1220 Time::default(),
1221 Time::new(14, 30, 45, 123_456_789).expect("time"),
1222 Time::new(23, 59, 59, 999_999_999).expect("time"),
1223 ];
1224 let mut batch = InsertBatch::<TimeRow, _>::new(ctx, values.len())?;
1225 for (i, &v) in values.iter().enumerate() {
1226 batch.push(
1227 RowNumber(i as u64 + 1),
1228 &TimeRow {
1229 v,
1230 },
1231 )?;
1232 }
1233 batch.finish()
1234 }
1235 }
1236
1237 #[test]
1238 fn scalar_time_roundtrip() {
1239 let mut h = FFIOperatorHarnessBuilder::<OpTime>::new().build().expect("harness");
1240 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
1241 let post = out.diffs[0].post().expect("post");
1242 assert_eq!(post.row_count(), 3);
1243 assert_eq!(post.row_ref(0).expect("r0").time("v"), Some(Time::default()));
1244 assert_eq!(post.row_ref(1).expect("r1").time("v"), Time::new(14, 30, 45, 123_456_789));
1245 assert_eq!(post.row_ref(2).expect("r2").time("v"), Time::new(23, 59, 59, 999_999_999));
1246 }
1247
1248 struct DurationRow {
1249 v: Duration,
1250 }
1251 row!(DurationRow {
1252 v: Duration
1253 });
1254
1255 struct OpDuration;
1256 impl OperatorMetadata for OpDuration {
1257 const NAME: &'static str = "writer_duration";
1258 const API: u32 = 1;
1259 const VERSION: &'static str = "1.0.0";
1260 const DESCRIPTION: &'static str = "test fixture";
1261 const INPUT_COLUMNS: &'static [OperatorColumn] = &[];
1262 const OUTPUT_COLUMNS: &'static [OperatorColumn] = &[];
1263 const CAPABILITIES: &'static [OperatorCapability] = OperatorCapability::STANDARD;
1264 }
1265 impl FFIOperator for OpDuration {
1266 fn new(_: FlowNodeId, _: &Config) -> Result<Self> {
1267 Ok(Self)
1268 }
1269 fn apply(&mut self, ctx: &mut FFIOperatorContext, _: BorrowedChange<'_>) -> Result<()> {
1270 let values = [
1271 Duration::default(),
1272 Duration::new(13, 5, 3_600_000_000_000).expect("duration"),
1273 Duration::from_seconds(-30).expect("duration"),
1274 ];
1275 let mut batch = InsertBatch::<DurationRow, _>::new(ctx, values.len())?;
1276 for (i, &v) in values.iter().enumerate() {
1277 batch.push(
1278 RowNumber(i as u64 + 1),
1279 &DurationRow {
1280 v,
1281 },
1282 )?;
1283 }
1284 batch.finish()
1285 }
1286 }
1287
1288 #[test]
1289 fn scalar_duration_roundtrip() {
1290 let mut h = FFIOperatorHarnessBuilder::<OpDuration>::new().build().expect("harness");
1291 let out = h.apply(TestChangeBuilder::new().build()).expect("apply");
1292 let post = out.diffs[0].post().expect("post");
1293 assert_eq!(post.row_count(), 3);
1294 assert_eq!(post.row_ref(0).expect("r0").duration("v"), Some(Duration::default()));
1295 assert_eq!(post.row_ref(1).expect("r1").duration("v"), Duration::new(13, 5, 3_600_000_000_000).ok());
1296 assert_eq!(post.row_ref(2).expect("r2").duration("v"), Duration::from_seconds(-30).ok());
1297 }
1298}