1use arrow::array::timezone::Tz;
19use arrow::array::types::TimestampNanosecondType;
20use arrow::array::{ArrayRef, Int64Array, TimestampNanosecondArray};
21use arrow::compute::SortOptions;
22use arrow::datatypes::{
23 DataType, Field, IntervalMonthDayNano, Schema, SchemaRef, TimeUnit,
24};
25use arrow::record_batch::RecordBatch;
26use async_trait::async_trait;
27use datafusion_catalog::TableFunctionImpl;
28use datafusion_catalog::TableProvider;
29use datafusion_catalog::{Session, TableFunctionArgs};
30use datafusion_common::{Result, ScalarValue, plan_datafusion_err, plan_err};
31use datafusion_expr::{Expr, TableType};
32use datafusion_physical_expr::PhysicalSortExpr;
33use datafusion_physical_expr::expressions::Column;
34use datafusion_physical_plan::ExecutionPlan;
35use datafusion_physical_plan::memory::{LazyBatchGenerator, LazyMemoryExec};
36use parking_lot::RwLock;
37use std::any::Any;
38use std::fmt;
39use std::str::FromStr;
40use std::sync::Arc;
41
42#[derive(Debug, Clone)]
44pub struct Empty {
45 name: &'static str,
46}
47
48impl Empty {
49 pub fn name(&self) -> &'static str {
50 self.name
51 }
52}
53
54impl LazyBatchGenerator for Empty {
55 fn as_any(&self) -> &dyn Any {
56 self
57 }
58
59 fn generate_next_batch(&mut self) -> Result<Option<RecordBatch>> {
60 Ok(None)
61 }
62
63 fn reset_state(&self) -> Arc<RwLock<dyn LazyBatchGenerator>> {
64 Arc::new(RwLock::new(Empty { name: self.name }))
65 }
66}
67
68impl fmt::Display for Empty {
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 write!(f, "{}: empty", self.name)
71 }
72}
73
74pub trait SeriesValue: fmt::Debug + Clone + Send + Sync + 'static {
76 type StepType: fmt::Debug + Clone + Send + Sync;
77 type ValueType: fmt::Debug + Clone + Send + Sync;
78
79 fn should_stop(&self, end: Self, step: &Self::StepType, include_end: bool) -> bool;
81
82 fn advance(&mut self, step: &Self::StepType) -> Result<()>;
84
85 fn advance_with_end(&mut self, _end: &mut Self, step: &Self::StepType) -> Result<()> {
91 self.advance(step)
92 }
93
94 fn create_array(&self, values: Vec<Self::ValueType>) -> Result<ArrayRef>;
96
97 fn to_value_type(&self) -> Self::ValueType;
99
100 fn display_value(&self) -> String;
102}
103
104impl SeriesValue for i64 {
105 type StepType = i64;
106 type ValueType = i64;
107
108 fn should_stop(&self, end: Self, step: &Self::StepType, include_end: bool) -> bool {
109 reach_end_int64(*self, end, *step, include_end)
110 }
111
112 fn advance(&mut self, step: &Self::StepType) -> Result<()> {
113 *self += step;
114 Ok(())
115 }
116
117 fn advance_with_end(&mut self, end: &mut Self, step: &Self::StepType) -> Result<()> {
118 if let Some(next) = self.checked_add(*step) {
119 *self = next;
120 } else {
121 *end = if *step > 0 {
125 self.saturating_sub(1)
126 } else {
127 self.saturating_add(1)
128 };
129 }
130 Ok(())
131 }
132
133 fn create_array(&self, values: Vec<Self::ValueType>) -> Result<ArrayRef> {
134 Ok(Arc::new(Int64Array::from(values)))
135 }
136
137 fn to_value_type(&self) -> Self::ValueType {
138 *self
139 }
140
141 fn display_value(&self) -> String {
142 self.to_string()
143 }
144}
145
146#[derive(Debug, Clone)]
147pub struct TimestampValue {
148 value: i64,
149 parsed_tz: Option<Tz>,
150 tz_str: Option<Arc<str>>,
151}
152
153impl TimestampValue {
154 pub fn value(&self) -> i64 {
155 self.value
156 }
157
158 pub fn tz_str(&self) -> Option<&Arc<str>> {
159 self.tz_str.as_ref()
160 }
161}
162
163impl SeriesValue for TimestampValue {
164 type StepType = IntervalMonthDayNano;
165 type ValueType = i64;
166
167 fn should_stop(&self, end: Self, step: &Self::StepType, include_end: bool) -> bool {
168 let step_negative = step.months < 0 || step.days < 0 || step.nanoseconds < 0;
169
170 if include_end {
171 if step_negative {
172 self.value < end.value
173 } else {
174 self.value > end.value
175 }
176 } else if step_negative {
177 self.value <= end.value
178 } else {
179 self.value >= end.value
180 }
181 }
182
183 fn advance(&mut self, step: &Self::StepType) -> Result<()> {
184 let tz = self
185 .parsed_tz
186 .unwrap_or_else(|| Tz::from_str("+00:00").unwrap());
187 let Some(next_ts) =
188 TimestampNanosecondType::add_month_day_nano(self.value, *step, tz)
189 else {
190 return plan_err!(
191 "Failed to add interval {:?} to timestamp {}",
192 step,
193 self.value
194 );
195 };
196 self.value = next_ts;
197 Ok(())
198 }
199
200 fn advance_with_end(&mut self, end: &mut Self, step: &Self::StepType) -> Result<()> {
201 let tz = self
202 .parsed_tz
203 .unwrap_or_else(|| Tz::from_str("+00:00").unwrap());
204 if let Some(next_ts) =
205 TimestampNanosecondType::add_month_day_nano(self.value, *step, tz)
206 {
207 self.value = next_ts;
208 } else {
209 let step_negative = step.months < 0 || step.days < 0 || step.nanoseconds < 0;
212 end.value = if step_negative {
213 self.value.saturating_add(1)
214 } else {
215 self.value.saturating_sub(1)
216 };
217 }
218 Ok(())
219 }
220
221 fn create_array(&self, values: Vec<Self::ValueType>) -> Result<ArrayRef> {
222 let array = TimestampNanosecondArray::from(values);
223
224 let array = match self.tz_str.as_ref() {
226 Some(tz_str) => array.with_timezone(Arc::clone(tz_str)),
227 None => array,
228 };
229
230 Ok(Arc::new(array))
231 }
232
233 fn to_value_type(&self) -> Self::ValueType {
234 self.value
235 }
236
237 fn display_value(&self) -> String {
238 self.value.to_string()
239 }
240}
241
242#[derive(Debug, Clone)]
244pub enum GenSeriesArgs {
245 ContainsNull { name: &'static str },
247 Int64Args {
249 start: i64,
250 end: i64,
251 step: i64,
252 include_end: bool,
254 name: &'static str,
255 },
256 TimestampArgs {
258 start: i64,
259 end: i64,
260 step: IntervalMonthDayNano,
261 tz: Option<Arc<str>>,
262 include_end: bool,
264 name: &'static str,
265 },
266 DateArgs {
269 start: i64,
270 end: i64,
271 step: IntervalMonthDayNano,
272 include_end: bool,
274 name: &'static str,
275 },
276}
277
278#[derive(Debug, Clone)]
280pub struct GenerateSeriesTable {
281 schema: SchemaRef,
282 args: GenSeriesArgs,
283}
284
285impl GenerateSeriesTable {
286 pub fn new(schema: SchemaRef, args: GenSeriesArgs) -> Self {
287 Self { schema, args }
288 }
289
290 pub fn as_generator(
291 &self,
292 batch_size: usize,
293 ) -> Result<Arc<RwLock<dyn LazyBatchGenerator>>> {
294 let generator: Arc<RwLock<dyn LazyBatchGenerator>> = match &self.args {
295 GenSeriesArgs::ContainsNull { name } => Arc::new(RwLock::new(Empty { name })),
296 GenSeriesArgs::Int64Args {
297 start,
298 end,
299 step,
300 include_end,
301 name,
302 } => Arc::new(RwLock::new(GenericSeriesState {
303 schema: self.schema(),
304 start: *start,
305 end: *end,
306 step: *step,
307 current: *start,
308 finished: false,
309 batch_size,
310 include_end: *include_end,
311 name,
312 })),
313 GenSeriesArgs::TimestampArgs {
314 start,
315 end,
316 step,
317 tz,
318 include_end,
319 name,
320 } => {
321 let parsed_tz = tz
322 .as_ref()
323 .map(|s| Tz::from_str(s.as_ref()))
324 .transpose()
325 .map_err(|e| {
326 datafusion_common::internal_datafusion_err!(
327 "Failed to parse timezone: {e}"
328 )
329 })?
330 .unwrap_or_else(|| Tz::from_str("+00:00").unwrap());
331 Arc::new(RwLock::new(GenericSeriesState {
332 schema: self.schema(),
333 start: TimestampValue {
334 value: *start,
335 parsed_tz: Some(parsed_tz),
336 tz_str: tz.clone(),
337 },
338 end: TimestampValue {
339 value: *end,
340 parsed_tz: Some(parsed_tz),
341 tz_str: tz.clone(),
342 },
343 step: *step,
344 current: TimestampValue {
345 value: *start,
346 parsed_tz: Some(parsed_tz),
347 tz_str: tz.clone(),
348 },
349 finished: false,
350 batch_size,
351 include_end: *include_end,
352 name,
353 }))
354 }
355 GenSeriesArgs::DateArgs {
356 start,
357 end,
358 step,
359 include_end,
360 name,
361 } => Arc::new(RwLock::new(GenericSeriesState {
362 schema: self.schema(),
363 start: TimestampValue {
364 value: *start,
365 parsed_tz: None,
366 tz_str: None,
367 },
368 end: TimestampValue {
369 value: *end,
370 parsed_tz: None,
371 tz_str: None,
372 },
373 step: *step,
374 current: TimestampValue {
375 value: *start,
376 parsed_tz: None,
377 tz_str: None,
378 },
379 finished: false,
380 batch_size,
381 include_end: *include_end,
382 name,
383 })),
384 };
385
386 Ok(generator)
387 }
388
389 fn output_ordering(&self, schema: &Schema) -> Option<PhysicalSortExpr> {
392 let step = match &self.args {
393 GenSeriesArgs::Int64Args { step, .. } => *step,
394 _ => return None,
395 };
396
397 if schema.fields().is_empty() {
398 return None;
399 }
400
401 let descending = step < 0;
402 Some(PhysicalSortExpr::new(
403 Arc::new(Column::new(schema.field(0).name(), 0)),
404 SortOptions {
405 descending,
406 nulls_first: false,
408 },
409 ))
410 }
411}
412
413#[derive(Debug, Clone)]
414pub struct GenericSeriesState<T: SeriesValue> {
415 schema: SchemaRef,
416 start: T,
417 end: T,
418 step: T::StepType,
419 batch_size: usize,
420 current: T,
421 finished: bool,
422 include_end: bool,
423 name: &'static str,
424}
425
426impl<T: SeriesValue> GenericSeriesState<T> {
427 pub fn name(&self) -> &'static str {
428 self.name
429 }
430
431 pub fn batch_size(&self) -> usize {
432 self.batch_size
433 }
434
435 pub fn include_end(&self) -> bool {
436 self.include_end
437 }
438
439 pub fn start(&self) -> &T {
440 &self.start
441 }
442
443 pub fn end(&self) -> &T {
444 &self.end
445 }
446
447 pub fn step(&self) -> &T::StepType {
448 &self.step
449 }
450
451 pub fn current(&self) -> &T {
452 &self.current
453 }
454}
455
456impl<T: SeriesValue> LazyBatchGenerator for GenericSeriesState<T> {
457 fn as_any(&self) -> &dyn Any {
458 self
459 }
460
461 fn generate_next_batch(&mut self) -> Result<Option<RecordBatch>> {
462 if self.finished {
463 return Ok(None);
464 }
465
466 let mut buf = Vec::with_capacity(self.batch_size);
467
468 while buf.len() < self.batch_size
469 && !self
470 .current
471 .should_stop(self.end.clone(), &self.step, self.include_end)
472 {
473 buf.push(self.current.to_value_type());
474 if self
475 .current
476 .should_stop(self.end.clone(), &self.step, false)
477 {
478 self.finished = true;
479 break;
480 }
481
482 let original_end = self.end.clone();
483 self.current.advance_with_end(&mut self.end, &self.step)?;
484 if self
485 .current
486 .should_stop(self.end.clone(), &self.step, self.include_end)
487 {
488 self.end = original_end;
489 self.finished = true;
490 break;
491 }
492 }
493
494 if buf.is_empty() {
495 return Ok(None);
496 }
497
498 let array = self.current.create_array(buf)?;
499 let batch = RecordBatch::try_new(Arc::clone(&self.schema), vec![array])?;
500 Ok(Some(batch))
501 }
502
503 fn reset_state(&self) -> Arc<RwLock<dyn LazyBatchGenerator>> {
504 let mut new = self.clone();
505 new.current = new.start.clone();
506 new.finished = false;
507 Arc::new(RwLock::new(new))
508 }
509}
510
511impl<T: SeriesValue> fmt::Display for GenericSeriesState<T> {
512 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
513 write!(
514 f,
515 "{}: start={}, end={}, batch_size={}",
516 self.name,
517 self.start.display_value(),
518 self.end.display_value(),
519 self.batch_size
520 )
521 }
522}
523
524fn reach_end_int64(val: i64, end: i64, step: i64, include_end: bool) -> bool {
525 if step > 0 {
526 if include_end { val > end } else { val >= end }
527 } else if include_end {
528 val < end
529 } else {
530 val <= end
531 }
532}
533
534fn validate_interval_step(step: IntervalMonthDayNano) -> Result<()> {
535 if step.months == 0 && step.days == 0 && step.nanoseconds == 0 {
536 return plan_err!("Step interval cannot be zero");
537 }
538
539 Ok(())
540}
541
542#[async_trait]
543impl TableProvider for GenerateSeriesTable {
544 fn schema(&self) -> SchemaRef {
545 Arc::clone(&self.schema)
546 }
547
548 fn table_type(&self) -> TableType {
549 TableType::Base
550 }
551
552 async fn scan(
553 &self,
554 state: &dyn Session,
555 projection: Option<&Vec<usize>>,
556 _filters: &[Expr],
557 _limit: Option<usize>,
558 ) -> Result<Arc<dyn ExecutionPlan>> {
559 let batch_size = state.config_options().execution.batch_size.get();
560 let generator = self.as_generator(batch_size)?;
561 let mut exec = LazyMemoryExec::try_new(self.schema(), vec![generator])?
562 .with_projection(projection.cloned());
563
564 if let Some(ordering) = self.output_ordering(exec.schema().as_ref()) {
565 exec.add_ordering([ordering]);
566 }
567
568 Ok(Arc::new(exec))
569 }
570}
571
572#[derive(Debug)]
573struct GenerateSeriesFuncImpl {
574 name: &'static str,
575 include_end: bool,
576}
577
578impl TableFunctionImpl for GenerateSeriesFuncImpl {
579 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
580 let exprs = args.exprs();
581 if exprs.is_empty() || exprs.len() > 3 {
582 return plan_err!("{} function requires 1 to 3 arguments", self.name);
583 }
584
585 match &exprs[0] {
587 Expr::Literal(
588 ScalarValue::Null | ScalarValue::Int64(_),
590 _,
591 ) => self.call_int64(exprs),
592 Expr::Literal(s, _) if matches!(s.data_type(), DataType::Timestamp(_, _)) => {
593 self.call_timestamp(exprs)
594 }
595 Expr::Literal(s, _) if matches!(s.data_type(), DataType::Date32) => {
596 self.call_date(exprs)
597 }
598 Expr::Literal(scalar, _) => {
599 plan_err!(
600 "Argument #1 must be an INTEGER, TIMESTAMP, DATE or NULL, got {:?}",
601 scalar.data_type()
602 )
603 }
604 _ => plan_err!("Arguments must be literals"),
605 }
606 }
607}
608
609impl GenerateSeriesFuncImpl {
610 fn call_int64(&self, exprs: &[Expr]) -> Result<Arc<dyn TableProvider>> {
611 let mut normalize_args = Vec::new();
612 for (expr_index, expr) in exprs.iter().enumerate() {
613 match expr {
614 Expr::Literal(ScalarValue::Null, _) => {}
615 Expr::Literal(ScalarValue::Int64(Some(n)), _) => normalize_args.push(*n),
616 other => {
617 return plan_err!(
618 "Argument #{} must be an INTEGER or NULL, got {:?}",
619 expr_index + 1,
620 other
621 );
622 }
623 };
624 }
625
626 let schema = Arc::new(Schema::new(vec![Field::new(
627 "value",
628 DataType::Int64,
629 false,
630 )]));
631
632 if normalize_args.len() != exprs.len() {
633 return Ok(Arc::new(GenerateSeriesTable {
635 schema,
636 args: GenSeriesArgs::ContainsNull { name: self.name },
637 }));
638 }
639
640 let (start, end, step) = match &normalize_args[..] {
641 [end] => (0, *end, 1),
642 [start, end] => (*start, *end, 1),
643 [start, end, step] => (*start, *end, *step),
644 _ => {
645 return plan_err!("{} function requires 1 to 3 arguments", self.name);
646 }
647 };
648
649 if step == 0 {
650 return plan_err!("Step cannot be zero");
651 }
652
653 Ok(Arc::new(GenerateSeriesTable {
654 schema,
655 args: GenSeriesArgs::Int64Args {
656 start,
657 end,
658 step,
659 include_end: self.include_end,
660 name: self.name,
661 },
662 }))
663 }
664
665 fn call_timestamp(&self, exprs: &[Expr]) -> Result<Arc<dyn TableProvider>> {
666 if exprs.len() != 3 {
667 return plan_err!(
668 "{} function with timestamps requires exactly 3 arguments",
669 self.name
670 );
671 }
672
673 let (start_ts, tz) = match &exprs[0] {
675 Expr::Literal(ScalarValue::TimestampNanosecond(ts, tz), _) => {
676 (*ts, tz.clone())
677 }
678 other => {
679 return plan_err!(
680 "First argument must be a timestamp or NULL, got {:?}",
681 other
682 );
683 }
684 };
685
686 let end_ts = match &exprs[1] {
688 Expr::Literal(ScalarValue::Null, _) => None,
689 Expr::Literal(ScalarValue::TimestampNanosecond(ts, _), _) => *ts,
690 other => {
691 return plan_err!(
692 "Second argument must be a timestamp or NULL, got {:?}",
693 other
694 );
695 }
696 };
697
698 let step_interval = match &exprs[2] {
700 Expr::Literal(ScalarValue::Null, _) => None,
701 Expr::Literal(ScalarValue::IntervalMonthDayNano(interval), _) => *interval,
702 other => {
703 return plan_err!(
704 "Third argument must be an interval or NULL, got {:?}",
705 other
706 );
707 }
708 };
709
710 let schema = Arc::new(Schema::new(vec![Field::new(
711 "value",
712 DataType::Timestamp(TimeUnit::Nanosecond, tz.clone()),
713 false,
714 )]));
715
716 let (Some(start), Some(end), Some(step)) = (start_ts, end_ts, step_interval)
718 else {
719 return Ok(Arc::new(GenerateSeriesTable {
720 schema,
721 args: GenSeriesArgs::ContainsNull { name: self.name },
722 }));
723 };
724
725 validate_interval_step(step)?;
727
728 Ok(Arc::new(GenerateSeriesTable {
729 schema,
730 args: GenSeriesArgs::TimestampArgs {
731 start,
732 end,
733 step,
734 tz,
735 include_end: self.include_end,
736 name: self.name,
737 },
738 }))
739 }
740
741 fn call_date(&self, exprs: &[Expr]) -> Result<Arc<dyn TableProvider>> {
742 if exprs.len() != 3 {
743 return plan_err!(
744 "{} function with dates requires exactly 3 arguments",
745 self.name
746 );
747 }
748
749 let schema = Arc::new(Schema::new(vec![Field::new(
750 "value",
751 DataType::Timestamp(TimeUnit::Nanosecond, None),
752 false,
753 )]));
754
755 let start_date = match &exprs[0] {
757 Expr::Literal(ScalarValue::Date32(Some(date)), _) => *date,
758 Expr::Literal(ScalarValue::Date32(None), _)
759 | Expr::Literal(ScalarValue::Null, _) => {
760 return Ok(Arc::new(GenerateSeriesTable {
761 schema,
762 args: GenSeriesArgs::ContainsNull { name: self.name },
763 }));
764 }
765 other => {
766 return plan_err!(
767 "First argument must be a date or NULL, got {:?}",
768 other
769 );
770 }
771 };
772
773 let end_date = match &exprs[1] {
775 Expr::Literal(ScalarValue::Date32(Some(date)), _) => *date,
776 Expr::Literal(ScalarValue::Date32(None), _)
777 | Expr::Literal(ScalarValue::Null, _) => {
778 return Ok(Arc::new(GenerateSeriesTable {
779 schema,
780 args: GenSeriesArgs::ContainsNull { name: self.name },
781 }));
782 }
783 other => {
784 return plan_err!(
785 "Second argument must be a date or NULL, got {:?}",
786 other
787 );
788 }
789 };
790
791 let step_interval = match &exprs[2] {
793 Expr::Literal(ScalarValue::IntervalMonthDayNano(Some(interval)), _) => {
794 *interval
795 }
796 Expr::Literal(ScalarValue::IntervalMonthDayNano(None), _)
797 | Expr::Literal(ScalarValue::Null, _) => {
798 return Ok(Arc::new(GenerateSeriesTable {
799 schema,
800 args: GenSeriesArgs::ContainsNull { name: self.name },
801 }));
802 }
803 other => {
804 return plan_err!(
805 "Third argument must be an interval or NULL, got {:?}",
806 other
807 );
808 }
809 };
810
811 const NANOS_PER_DAY: i64 = 24 * 60 * 60 * 1_000_000_000;
814
815 let date_to_ts_nanos = |date: i32, arg: &str| {
819 (date as i64).checked_mul(NANOS_PER_DAY).ok_or_else(|| {
820 plan_datafusion_err!(
821 "{arg} for {} is out of range of nanosecond timestamps",
822 self.name
823 )
824 })
825 };
826
827 let start_ts = date_to_ts_nanos(start_date, "First argument")?;
828 let end_ts = date_to_ts_nanos(end_date, "Second argument")?;
829
830 validate_interval_step(step_interval)?;
832
833 Ok(Arc::new(GenerateSeriesTable {
834 schema,
835 args: GenSeriesArgs::DateArgs {
836 start: start_ts,
837 end: end_ts,
838 step: step_interval,
839 include_end: self.include_end,
840 name: self.name,
841 },
842 }))
843 }
844}
845
846#[derive(Debug)]
847pub struct GenerateSeriesFunc {}
848
849impl TableFunctionImpl for GenerateSeriesFunc {
850 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
851 let impl_func = GenerateSeriesFuncImpl {
852 name: "generate_series",
853 include_end: true,
854 };
855 impl_func.call_with_args(args)
856 }
857}
858
859#[derive(Debug)]
860pub struct RangeFunc {}
861
862impl TableFunctionImpl for RangeFunc {
863 fn call_with_args(&self, args: TableFunctionArgs) -> Result<Arc<dyn TableProvider>> {
864 let impl_func = GenerateSeriesFuncImpl {
865 name: "range",
866 include_end: false,
867 };
868 impl_func.call_with_args(args)
869 }
870}
871
872#[cfg(test)]
873mod generate_series_tests {
874 use std::sync::Arc;
875
876 use arrow::datatypes::{DataType, Field, Schema};
877 use datafusion_common::Result;
878 use datafusion_physical_plan::memory::LazyBatchGenerator;
879
880 use crate::generate_series::GenericSeriesState;
881
882 #[test]
883 fn test_generic_series_state_reset() -> Result<()> {
884 let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Int64, false)]));
885 let mut state = GenericSeriesState::<i64> {
886 schema,
887 start: 1,
888 end: 5,
889 step: 1,
890 current: 1,
891 finished: false,
892 batch_size: 8192,
893 include_end: true,
894 name: "test",
895 };
896 let batch = state.generate_next_batch()?.expect("missing batch");
897
898 let state_reset = state.reset_state();
899 let reset_batch = state_reset
900 .write()
901 .generate_next_batch()?
902 .expect("missing reset batch");
903
904 assert_eq!(batch, reset_batch);
905
906 Ok(())
907 }
908
909 #[test]
910 fn test_generic_series_state_reset_after_overflow() -> Result<()> {
911 let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Int64, false)]));
912 let mut state = GenericSeriesState::<i64> {
913 schema,
914 start: i64::MAX - 1,
915 end: i64::MAX,
916 step: 2,
917 current: i64::MAX - 1,
918 finished: false,
919 batch_size: 8192,
920 include_end: true,
921 name: "test",
922 };
923 let batch = state.generate_next_batch()?.expect("missing batch");
924 assert!(state.generate_next_batch()?.is_none());
925
926 let state_reset = state.reset_state();
927 let reset_batch = state_reset
928 .write()
929 .generate_next_batch()?
930 .expect("missing reset batch");
931
932 assert_eq!(batch, reset_batch);
933
934 Ok(())
935 }
936}