1use color_eyre::Result;
2use std::collections::HashSet;
3use std::path::{Path, PathBuf};
4use std::sync::Arc;
5
6use polars::frame::PivotColumnNaming;
7use polars::prelude::*;
8use ratatui::widgets::TableState;
9
10use crate::OpenOptions;
11use crate::analysis::statistics::collect_lazy;
12use crate::app::modals::filter_modal::FilterStatement;
13use crate::app::modals::pivot_melt_modal::{MeltSpec, PivotAggregation, PivotSpec, ReshapeSource};
14use crate::cloud::local_copy::RemoteObject;
15use crate::export::python_script::{SidebarFilter, Step};
16use crate::formats::readers::csv::Decompressed;
17use crate::formats::readers::{Read, Typing};
18use crate::numfmt::{self};
19#[cfg(feature = "sql")]
20use crate::query::sql_plan::{
21 count_subquery_values_once, leftover_subquery_value_columns, ordered_by, stable_order,
22};
23use crate::query::{ParsedQuery, parse_query_over};
24use crate::widgets::column_paging::{ColumnMove, CursorMove, OnScreen, Room};
25use crate::widgets::column_widths::{ColumnWidths, WidthChoice};
26
27#[derive(Clone)]
30pub(crate) struct View {
31 lf: LazyFrame,
32 unsorted_lf: Option<LazyFrame>,
34 base_lf: LazyFrame,
39 base_schema: Arc<Schema>,
42 pub(crate) df: Option<DataFrame>, pub(crate) locked_df: Option<DataFrame>, pub(crate) start_row: usize,
45 cursor_column: Option<String>,
48 cursor_at: usize,
51 pub(crate) schema: Arc<Schema>,
52 num_rows: usize,
53 pub(crate) num_rows_valid: bool,
55 len_generation: u64,
60 filters: Vec<FilterStatement>,
61 sort_columns: Vec<String>,
62 sort_descending: Vec<bool>,
64 sort_ascending: bool,
65 active_query: String,
68 active_sql_query: String,
70 query_order: Vec<(String, bool)>,
73 active_fuzzy_query: String,
75 pub(crate) column_order: Vec<String>, locked_columns_count: usize, frozen_fit: (usize, usize),
81 grouped: Option<GroupedView>,
83 group_source: Option<GroupSource>,
85 reshaped_lf: Option<LazyFrame>,
87 drilled_down_group_index: Option<usize>, drilled_down_group_key: Option<Vec<String>>, drilled_down_group_key_columns: Option<Vec<String>>, drift_column_present: bool,
93 drift_groups: Arc<Vec<crate::formats::schema_union::DriftGroup>>,
96 view_numbered: bool,
100 notes: Vec<crate::notes::Note>,
102 notes_seen: bool,
104 view_notes: Vec<crate::notes::Note>,
106 observed_bytes_per_row: Option<usize>,
108 pub(crate) buffered_start_row: usize,
109 buffered_end_row: usize,
110 buffered_df: Option<DataFrame>,
113 drawn_start: usize,
115 last_pivot_spec: Option<PivotSpec>,
117 last_melt_spec: Option<MeltSpec>,
119 reshape_source: Option<ReshapeSource>,
122 base_steps: Vec<Step>,
125 column_changes: Vec<crate::formats::column_types::ColumnChange>,
128 changes_version: u64,
130 changes_dropped: Vec<crate::notes::Note>,
132 reshape_steps: Option<Vec<Step>>,
134 lineage: Lineage,
136 reshape_lineage: Lineage,
138}
139
140pub struct DataTableState {
141 original_lf: LazyFrame,
142 original_schema: Arc<Schema>,
143 pub table_state: TableState,
144 pub visible_rows: usize,
145 pub termcol_index: usize,
146 reveal_cursor: bool,
149 pub visible_termcols: usize,
150 scroll_room: Option<Room>,
153 column_moves: Vec<WaitingMove>,
156 page_trail: Vec<(usize, usize)>,
158 pub(crate) on_screen: Option<OnScreen>,
160 pub(crate) drawn: Option<DrawnTable>,
162 pub(crate) page_cells: crate::widgets::table::PageCells,
164 error: Option<PolarsError>,
165 pub suppress_error_display: bool, pristine_rows: Option<usize>,
169 root_generation: u64,
172 parquet_count_dir: Option<PathBuf>,
175 measurements: Arc<crate::loading::measurements::Meter>,
178 pub(crate) widths: ColumnWidths,
181 pages_lookahead: usize,
182 pages_lookback: usize,
183 max_buffered_rows: usize, max_buffered_mb: usize, remote_source: bool,
188 row_group_offsets: Option<Vec<usize>>,
191 remote_files: Option<RemoteFiles>,
193 remote_objects: Option<Arc<std::collections::HashMap<String, RemoteObject>>>,
196 dataset_schema: Option<crate::formats::schema_union::DatasetSchema>,
199 drift_at_open: bool,
201 groups_at_open: Arc<Vec<crate::formats::schema_union::DriftGroup>>,
202 source_rows_at_open: bool,
205 indexing: Option<Arc<crate::formats::lines::Lines>>,
207 numbering: Option<Arc<crate::formats::lines::Lines>>,
209 row_estimate: Option<crate::formats::schema_union::RowEstimate>,
211 indexing_notes: Vec<crate::notes::Note>,
213 indexing_guessed: bool,
215 drift_file_starts: Vec<usize>,
218 drift_file_group: Vec<u32>,
219 drift_dataset_rows: usize,
221 dataset_at_open: Option<crate::formats::schema_union::DatasetSchema>,
224 read_as_text: Vec<PlSmallStr>,
226 drift_files: Vec<String>,
228 footers_pending: Option<FootersJoin>,
231 notes_at_open: Vec<crate::notes::Note>,
233 open_notes: Vec<crate::notes::Note>,
237 not_the_table: Option<&'static str>,
240 format_read: Option<Arc<crate::formats::Read>>,
242 delimited: Option<Arc<crate::formats::delimited_spec::DelimitedRead>>,
244 fixed_window: Option<Arc<dyn crate::formats::pushdown::Windowed>>,
247 decodes_pages: bool,
250 csv_marks: CsvMarksOf,
253 pushdown: Option<Arc<dyn crate::formats::pushdown::Pushdown>>,
256 source_hold: Option<crate::formats::sqlite::Hold>,
258 read_mode: Option<crate::ReadMode>,
260 read_as: Option<crate::FileFormat>,
262 fetched: bool,
264 detail: Option<Arc<crate::formats::text_formats::Detail>>,
266 file_units: Arc<Vec<(String, String)>>,
268 column_bytes: Vec<(String, usize)>,
271 proximity_threshold: usize,
272 row_numbers: bool,
273 row_start_index: usize,
274 read_python: Vec<String>,
276 typing: Typing,
278 unfit_notes: Option<Vec<crate::notes::Note>>,
280 changes_unfit: Option<(u64, Vec<crate::notes::Note>)>,
282 partition_columns: Option<Vec<String>>,
284 decompress_temp_file: Option<Arc<Decompressed>>,
287 download: Option<crate::cloud::download::TempDownload>,
289 converted: Vec<crate::cloud::download::TempDownload>,
291 other_tables: Vec<String>,
293 polars_streaming: bool,
295 defer_collect: bool,
298 pub needs_recollect: bool,
301 follow: Option<crate::loading::follow::Follow>,
303 follow_known: Option<(u64, Vec<(usize, usize)>)>,
307 sampled: Option<Box<Sampled>>,
310 pub(crate) view: View,
312}
313
314pub struct Sampled {
317 source: Box<DataTableState>,
319 sample: crate::analysis::sampling::Sample,
320 rows: Arc<crate::analysis::table_sample::SampleRows>,
321 frame: Arc<DataFrame>,
323 through: bool,
326 drawn: Option<crate::analysis::table_sample::Drawn>,
328 path: Option<crate::analysis::table_sample::DrawPath>,
330}
331
332impl Sampled {
333 pub fn sample(&self) -> &crate::analysis::sampling::Sample {
334 &self.sample
335 }
336
337 pub fn source(&self) -> &DataTableState {
339 &self.source
340 }
341
342 pub fn through(&self) -> bool {
344 self.through
345 }
346
347 pub(crate) fn holds(&self, rows: &Arc<crate::analysis::table_sample::SampleRows>) -> bool {
349 Arc::ptr_eq(&self.rows, rows)
350 }
351
352 pub fn drawing(&self) -> bool {
354 self.drawn.is_none()
355 }
356
357 pub fn path(&self) -> Option<crate::analysis::table_sample::DrawPath> {
359 self.path
360 }
361
362 #[cfg(test)]
364 pub(crate) fn frame(&self) -> &DataFrame {
365 &self.frame
366 }
367
368 pub fn rows(&self) -> usize {
370 self.frame.height()
371 }
372
373 pub fn label(&self) -> String {
376 let rows = crate::numfmt::group_chrome(self.rows());
377 let Some(drawn) = &self.drawn else {
378 return format!("sample {rows}+");
379 };
380 let about = if drawn.about { "about " } else { "" };
381 let cut = if drawn.cut { ", stopped" } else { "" };
382 match drawn.total {
383 Some(total) if total > self.rows() => {
384 format!(
385 "sample {about}{rows} of {}{cut}",
386 crate::home::discover::format_rows(total)
387 )
388 }
389 _ => format!("sample {rows}{cut}"),
390 }
391 }
392}
393
394#[derive(Default)]
398pub struct OpenFacts {
399 pub remote_source: bool,
401 pub row_groups: Vec<Vec<usize>>,
404 pub remote_files: Option<RemoteFiles>,
406 pub remote_objects: Vec<RemoteObject>,
408 pub dataset: Option<DatasetAtOpen>,
410 pub footers_pending: Option<FootersJoin>,
412 pub column_bytes: Vec<(String, usize)>,
414 pub parquet_count_dir: Option<PathBuf>,
416 pub measurements: Arc<crate::loading::measurements::Meter>,
418 pub open_notes: Vec<crate::notes::Note>,
420 pub not_the_table: Option<&'static str>,
423 pub format_read: Option<Arc<crate::formats::Read>>,
425 pub delimited: Option<Arc<crate::formats::delimited_spec::DelimitedRead>>,
427 pub download: Option<crate::cloud::download::TempDownload>,
429 pub converted: Vec<crate::cloud::download::TempDownload>,
431 pub other_tables: Vec<String>,
434 pub pushdown: Option<Arc<dyn crate::formats::pushdown::Pushdown>>,
436 pub hold: Option<crate::formats::sqlite::Hold>,
438 pub read_mode: Option<crate::ReadMode>,
440 pub read_as: Option<crate::FileFormat>,
443 pub fetched: bool,
446 pub detail: Option<Arc<crate::formats::text_formats::Detail>>,
448 pub records: Option<(Arc<dyn crate::formats::pushdown::Windowed>, usize)>,
451 pub units: Vec<(String, String)>,
453 pub indexing: Option<Arc<crate::formats::lines::Lines>>,
455 pub numbering: Option<Arc<crate::formats::lines::Lines>>,
457 pub typing: Typing,
459}
460
461pub struct DatasetAtOpen {
463 pub schema: crate::formats::schema_union::DatasetSchema,
464 pub file_rows: Vec<usize>,
467 pub files: Vec<String>,
469}
470
471pub const DEFAULT_MAX_BUFFERED_ROWS: usize = 100_000;
474
475type CsvMarksOf = Arc<std::sync::OnceLock<Option<Arc<csv_marks::CsvMarks>>>>;
477
478static NEXT_LEN_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
481
482fn next_len_generation() -> u64 {
483 NEXT_LEN_GENERATION.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
484}
485
486fn scanned_frame(lf: &LazyFrame) -> Option<Arc<DataFrame>> {
488 match &lf.logical_plan {
489 polars::lazy::dsl::DslPlan::DataFrameScan { df, .. } => Some(df.clone()),
490 _ => None,
491 }
492}
493
494pub(crate) fn for_each_input(
496 plan: &mut polars::lazy::dsl::DslPlan,
497 f: &mut dyn FnMut(&mut polars::lazy::dsl::DslPlan),
498) {
499 use polars::lazy::dsl::DslPlan;
500 match plan {
501 DslPlan::Sort { input, .. }
502 | DslPlan::Select { input, .. }
503 | DslPlan::GroupBy { input, .. }
504 | DslPlan::Filter { input, .. }
505 | DslPlan::Distinct { input, .. }
506 | DslPlan::Slice { input, .. }
507 | DslPlan::HStack { input, .. }
508 | DslPlan::MatchToSchema { input, .. }
509 | DslPlan::MapFunction { input, .. }
510 | DslPlan::Sink { input, .. }
511 | DslPlan::Cache { input, .. }
512 | DslPlan::Pivot { input, .. } => f(Arc::make_mut(input)),
513 DslPlan::Union { inputs, .. }
514 | DslPlan::HConcat { inputs, .. }
515 | DslPlan::SinkMultiple { inputs } => inputs.iter_mut().for_each(f),
516 DslPlan::PipeWithSchema { input, .. } => {
517 let mut inputs = input.to_vec();
518 inputs.iter_mut().for_each(&mut *f);
519 *input = inputs.into();
520 }
521 DslPlan::Join {
522 input_left,
523 input_right,
524 ..
525 } => {
526 f(Arc::make_mut(input_left));
527 f(Arc::make_mut(input_right));
528 }
529 DslPlan::Gather { input, idxs, .. } => {
530 f(Arc::make_mut(input));
531 f(Arc::make_mut(idxs));
532 }
533 DslPlan::ExtContext { input, contexts } => {
534 f(Arc::make_mut(input));
535 contexts.iter_mut().for_each(f);
536 }
537 _ => {}
538 }
539}
540
541impl DataTableState {
542 pub fn new(
543 lf: LazyFrame,
544 pages_lookahead: Option<usize>,
545 pages_lookback: Option<usize>,
546 max_buffered_rows: Option<usize>,
547 max_buffered_mb: Option<usize>,
548 polars_streaming: bool,
549 ) -> Result<Self> {
550 let options = OpenOptions {
551 pages_lookahead,
552 pages_lookback,
553 max_buffered_rows,
554 max_buffered_mb,
555 polars_streaming,
556 ..OpenOptions::default()
557 };
558 Self::from_lazyframe(lf, &options)
559 }
560
561 fn without_source_rows(schema: Arc<Schema>) -> (Arc<Schema>, bool) {
564 if !schema.contains(crate::formats::schema_union::DRIFT_COLUMN) {
565 return (schema, false);
566 }
567 let mut schema = (*schema).clone();
568 schema.shift_remove(crate::formats::schema_union::DRIFT_COLUMN);
569 (Arc::new(schema), true)
570 }
571
572 pub fn from_lazyframe(lf: LazyFrame, options: &crate::OpenOptions) -> Result<Self> {
574 let schema = lf.clone().collect_schema()?;
575 Self::from_schema_and_lazyframe(schema, lf, options, None)
576 }
577
578 pub fn from_schema_and_lazyframe(
582 schema: Arc<Schema>,
583 lf: LazyFrame,
584 options: &crate::OpenOptions,
585 partition_columns: Option<Vec<String>>,
586 ) -> Result<Self> {
587 let (schema, source_rows_at_open) = Self::without_source_rows(schema);
588 let column_order: Vec<String> = if let Some(ref part) = partition_columns {
589 let part_set: HashSet<&str> = part.iter().map(String::as_str).collect();
590 let rest: Vec<String> = schema
591 .iter_names()
592 .map(|s| s.to_string())
593 .filter(|c| !part_set.contains(c.as_str()))
594 .collect();
595 part.iter().cloned().chain(rest).collect()
596 } else {
597 schema.iter_names().map(|s| s.to_string()).collect()
598 };
599 Ok(Self {
600 original_lf: lf.clone(),
601 original_schema: schema.clone(),
602 table_state: TableState::default(),
603 visible_rows: 0,
604 termcol_index: 0,
605 visible_termcols: 0,
606 scroll_room: None,
607 column_moves: Vec::new(),
608 page_trail: Vec::new(),
609 on_screen: None,
610 drawn: None,
611 page_cells: Default::default(),
612 error: None,
613 suppress_error_display: false,
614 pristine_rows: None,
615 root_generation: next_len_generation(),
616 parquet_count_dir: None,
617 measurements: Arc::new(crate::loading::measurements::Meter::default()),
618 reveal_cursor: false,
619 widths: ColumnWidths::default(),
620 pages_lookahead: options.pages_lookahead.unwrap_or(3),
621 pages_lookback: options.pages_lookback.unwrap_or(3),
622 max_buffered_rows: options
623 .max_buffered_rows
624 .unwrap_or(DEFAULT_MAX_BUFFERED_ROWS),
625 max_buffered_mb: options.max_buffered_mb.unwrap_or(512),
626 remote_source: false,
627 row_group_offsets: None,
628 remote_files: None,
629 remote_objects: None,
630 dataset_schema: None,
631 drift_at_open: false,
632 groups_at_open: Arc::new(Vec::new()),
633 source_rows_at_open,
634 indexing: None,
635 numbering: None,
636 row_estimate: None,
637 indexing_notes: Vec::new(),
638 indexing_guessed: false,
639 drift_file_starts: Vec::new(),
640 drift_file_group: Vec::new(),
641 drift_files: Vec::new(),
642 footers_pending: None,
643 open_notes: Vec::new(),
644 not_the_table: None,
645 format_read: None,
646 delimited: None,
647 fixed_window: None,
648 csv_marks: CsvMarksOf::default(),
649 decodes_pages: buffer::decodes_pages(&lf),
650 pushdown: None,
651 source_hold: None,
652 read_mode: None,
653 read_as: None,
654 fetched: false,
655 detail: None,
656 file_units: Arc::new(Vec::new()),
657 notes_at_open: Vec::new(),
658 drift_dataset_rows: 0,
659 dataset_at_open: None,
660 read_as_text: Vec::new(),
661 column_bytes: Vec::new(),
662 proximity_threshold: 0,
664 row_numbers: options.row_numbers,
665 row_start_index: options.row_start_index,
666 read_python: Vec::new(),
667 typing: Typing::default(),
668 unfit_notes: None,
669 changes_unfit: None,
670 partition_columns,
671 decompress_temp_file: None,
672 download: None,
673 converted: Vec::new(),
674 other_tables: Vec::new(),
675 polars_streaming: options.polars_streaming,
676 defer_collect: false,
677 needs_recollect: false,
678 follow: None,
679 follow_known: None,
680 sampled: None,
681 view: View {
682 unsorted_lf: None,
683 base_lf: lf.clone(),
684 base_schema: schema.clone(),
685 lf,
686 df: None,
687 locked_df: None,
688 start_row: 0,
689 schema,
690 num_rows: 0,
691 num_rows_valid: false,
692 len_generation: next_len_generation(),
693 filters: Vec::new(),
694 sort_columns: Vec::new(),
695 sort_descending: Vec::new(),
696 sort_ascending: true,
697 cursor_column: None,
698 cursor_at: 0,
699 active_query: String::new(),
700 active_sql_query: String::new(),
701 query_order: Vec::new(),
702 active_fuzzy_query: String::new(),
703 column_order,
704 locked_columns_count: 0,
705 frozen_fit: (0, 0),
706 grouped: None,
707 group_source: None,
708 reshaped_lf: None,
709 drilled_down_group_index: None,
710 drilled_down_group_key: None,
711 drilled_down_group_key_columns: None,
712 drift_column_present: false,
713 drift_groups: Arc::new(Vec::new()),
714 view_numbered: false,
715 notes: Vec::new(),
716 notes_seen: false,
717 view_notes: Vec::new(),
718 observed_bytes_per_row: None,
719 buffered_start_row: 0,
720 buffered_end_row: 0,
721 buffered_df: None,
722 drawn_start: 0,
723 last_pivot_spec: None,
724 last_melt_spec: None,
725 reshape_source: None,
726 base_steps: Vec::new(),
727 column_changes: Vec::new(),
728 changes_version: 0,
729 changes_dropped: Vec::new(),
730 reshape_steps: None,
731 lineage: None,
732 reshape_lineage: None,
733 },
734 })
735 }
736
737 pub fn with_open(mut self, facts: OpenFacts) -> Self {
742 let OpenFacts {
743 remote_source,
744 row_groups,
745 remote_files,
746 remote_objects,
747 dataset,
748 footers_pending,
749 column_bytes,
750 parquet_count_dir,
751 measurements,
752 open_notes,
753 not_the_table,
754 format_read,
755 delimited,
756 download,
757 converted,
758 other_tables,
759 pushdown,
760 hold,
761 read_mode,
762 read_as,
763 fetched,
764 detail,
765 records,
766 units,
767 indexing,
768 numbering,
769 typing,
770 } = facts;
771 self.numbering = numbering;
772 self.typing = typing;
773 debug_assert!(
774 self.is_pristine(),
775 "an open's facts are for the data as loaded"
776 );
777 self.remote_source = remote_source;
778 self.remote_files = remote_files;
779 self.remote_objects = (!remote_objects.is_empty()).then(|| {
780 Arc::new(
781 remote_objects
782 .into_iter()
783 .map(|object| (object.url.clone(), object))
784 .collect(),
785 )
786 });
787 if !row_groups.is_empty() {
788 if self.remote_files.is_some() {
789 self.record_file_row_groups(&row_groups);
790 } else {
791 let flat: Vec<usize> = row_groups.into_iter().flatten().collect();
792 self.record_row_groups(&flat);
793 }
794 }
795 if let Some(DatasetAtOpen {
796 schema,
797 file_rows,
798 files,
799 }) = dataset
800 {
801 self.record_dataset_schema(schema, &file_rows, &files);
802 }
803 self.footers_pending = footers_pending;
804 self.column_bytes = column_bytes;
805 self.parquet_count_dir = parquet_count_dir;
806 self.measurements = measurements;
807 self.open_notes = open_notes;
808 self.not_the_table = not_the_table;
809 self.fixed_window = format_read
810 .as_ref()
811 .map(|read| read.records.clone() as Arc<dyn crate::formats::pushdown::Windowed>);
812 if let Some(read) = &format_read {
813 self.set_num_rows(read.records.rows());
816 }
817 self.pushdown = pushdown;
818 self.source_hold = hold;
819 self.format_read = format_read;
820 self.delimited = delimited;
821 self.download = download;
822 self.converted = converted;
823 self.other_tables = other_tables;
824 self.read_mode = read_mode;
825 self.read_as = read_as;
826 self.fetched = fetched;
827 self.detail = detail;
828 if let Some((window, rows)) = records {
829 if indexing.is_none() {
832 self.set_num_rows(rows);
833 }
834 self.fixed_window = Some(window);
835 }
836 if let Some(lines) = &indexing {
837 self.indexing_guessed = self
839 .open_notes
840 .iter()
841 .any(|n| n.summary.starts_with(crate::formats::lines::GUESSED));
842 self.indexing_notes = crate::formats::lines::notes(lines, self.indexing_guessed);
843 }
844 self.indexing = indexing;
845 self.file_units = Arc::new(units);
846 self
847 }
848
849 fn replace_root(&mut self, lf: LazyFrame, schema: Arc<Schema>) {
853 self.fixed_window = None;
855 self.csv_marks = CsvMarksOf::default();
856 self.decodes_pages = buffer::decodes_pages(&lf);
857 self.pushdown = None;
858 self.root_generation = next_len_generation();
859 self.invalidate_num_rows();
860 self.original_schema = schema.clone();
861 self.view.base_schema = schema.clone();
862 self.view.schema = schema;
863 self.original_lf = lf.clone();
864 self.view.base_lf = lf.clone();
865 self.view.lf = lf;
866 self.view.unsorted_lf = None;
867 self.view.base_steps = Vec::new();
868 self.view.reshape_steps = None;
869 self.drop_buffer();
870 }
871
872 fn install_base(&mut self, lf: LazyFrame, schema: Arc<Schema>) {
875 self.invalidate_num_rows();
876 self.view.drift_column_present = false;
879 self.view.view_numbered = false;
880 self.view.drift_groups = Arc::new(Vec::new());
881 self.view.notes = Vec::new();
882 self.view.view_notes = Vec::new();
883 self.view.observed_bytes_per_row = None;
885 self.view.query_order = Vec::new();
887 self.widths.relearn();
889 self.view.base_lf = lf.clone();
890 self.view.base_schema = schema.clone();
891 self.view.lf = lf;
892 self.view.unsorted_lf = None;
893 self.view.base_steps = vec![Step::Unreproducible(
895 "datui built the view from here in a way it cannot write as Python".to_string(),
896 )];
897 self.view.schema = schema;
898 self.view.column_order = self
899 .view
900 .schema
901 .iter_names()
902 .map(|s| s.to_string())
903 .collect();
904 self.view.lineage = Some(Arc::default());
906 self.settle_cursor();
907 self.view.group_source = None;
909 self.drop_buffer();
910 }
911
912 fn drop_buffer(&mut self) {
914 self.view.buffered_start_row = 0;
915 self.view.buffered_end_row = 0;
916 self.view.buffered_df = None;
917 }
918
919 fn reset_view_state(&mut self, locked_columns_count: usize) {
922 self.forget_column_changes();
923 self.view.active_query.clear();
924 self.view.active_sql_query.clear();
925 self.view.active_fuzzy_query.clear();
926 self.view.locked_columns_count = locked_columns_count;
927 self.view.filters.clear();
928 self.view.sort_columns.clear();
929 self.view.sort_descending.clear();
930 self.view.sort_ascending = true;
931 self.view.start_row = 0;
932 self.termcol_index = 0;
933 self.clear_column_moves();
934 self.place_cursor_at(0);
935 self.view.drilled_down_group_index = None;
936 self.view.drilled_down_group_key = None;
937 self.view.drilled_down_group_key_columns = None;
938 self.view.grouped = None;
939 self.drop_buffer();
940 self.table_state.select(Some(0));
941 }
942
943 fn install_query_result(
947 &mut self,
948 lf: LazyFrame,
949 schema: Arc<Schema>,
950 query: ActiveQuery,
951 locked_columns_count: usize,
952 steps: Vec<Step>,
953 ) {
954 self.install_base(lf, schema);
955 self.view.base_steps = steps;
956 self.reset_view_state(locked_columns_count);
957 match query {
958 ActiveQuery::Dsl(q) => self.view.active_query = q,
959 #[cfg(feature = "sql")]
960 ActiveQuery::Sql(q) => self.view.active_sql_query = q,
961 ActiveQuery::Fuzzy(q) => self.view.active_fuzzy_query = q,
962 }
963 }
964
965 fn forget_reshape(&mut self) {
967 self.view.reshaped_lf = None;
968 self.view.reshape_lineage = None;
969 self.view.reshape_steps = None;
970 self.view.last_pivot_spec = None;
971 self.view.last_melt_spec = None;
972 self.view.reshape_source = None;
973 }
974
975 fn reset_lf_to_original(&mut self) {
977 self.install_base(self.original_lf.clone(), self.query_source_schema());
978 self.view.base_steps = Vec::new();
979 self.view.reshape_steps = None;
980 self.view.lineage = None;
981 self.view.reshape_lineage = None;
982 self.view.drift_column_present = self.drift_at_open;
984 self.view.drift_groups = self.groups_at_open.clone();
985 self.view.notes = self.notes_at_open.clone();
986 self.view.reshaped_lf = None;
987 self.view.reshape_source = None;
988 self.reset_view_state(0);
989 self.restore_footer_count();
990 }
991
992 fn return_to_root(&mut self) {
994 self.reset_lf_to_original();
995 self.error = None;
996 self.suppress_error_display = false;
997 self.view.last_pivot_spec = None;
998 self.view.last_melt_spec = None;
999 }
1000
1001 pub(crate) fn reset_view_for_replay(&mut self) {
1004 self.return_to_root();
1005 }
1006
1007 pub fn reset(&mut self) {
1009 self.widths = ColumnWidths::default();
1010 self.return_to_root();
1011 self.collect();
1012 if self.view.num_rows > 0 {
1013 self.view.start_row = 0;
1014 }
1015 }
1016
1017 pub(crate) fn from_read(read: Read, options: &OpenOptions) -> Result<Self> {
1019 let mut state = Self::from_lazyframe(read.lf, options)?;
1020 state.read_python = read.python;
1021 state.typing = read.typing;
1022 state.decompress_temp_file = read.temp;
1023 Ok(state)
1024 }
1025
1026 pub fn set_row_numbers(&mut self, enabled: bool) {
1027 self.row_numbers = enabled;
1028 }
1029
1030 pub fn toggle_row_numbers(&mut self) -> bool {
1033 self.row_numbers = !self.row_numbers;
1034 if self.row_numbers && self.wants_view_numbers() && !self.view.view_numbered {
1035 self.drop_buffer();
1036 self.apply_transformations();
1037 return true;
1038 }
1039 false
1040 }
1041
1042 fn wants_view_numbers(&self) -> bool {
1045 let too_many = self
1048 .pristine_rows
1049 .or(self.num_rows_if_valid())
1050 .is_some_and(|rows| rows > crate::formats::row_index::MAX_ROWS);
1051 self.scan_is_the_root()
1052 && self.follow.is_none()
1053 && !self.remote_source
1054 && self.remote_files.is_none()
1055 && self.parquet_count_dir.is_none()
1056 && !too_many
1057 && !self.view.drift_column_present
1058 && !self.source_rows_at_open
1059 && self.pushed_view().is_none()
1060 && (!self.view.filters.is_empty()
1061 || !self.view.sort_columns.is_empty()
1062 || !self.view.sort_ascending)
1063 }
1064
1065 pub fn row_numbers(&self) -> bool {
1067 self.row_numbers
1068 }
1069
1070 pub fn row_start_index(&self) -> usize {
1072 self.row_start_index
1073 }
1074}
1075
1076pub(crate) fn row_count_lf(lf: &LazyFrame) -> LazyFrame {
1080 lf.clone().select([len().cast(DataType::UInt64)])
1081}
1082
1083pub(crate) fn binary_stub() -> &'static str {
1087 crate::glyphs::get().binary_stub
1088}
1089
1090const MAX_WAITING_MOVES: usize = 32;
1092
1093#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1095pub(crate) enum WaitingMove {
1096 View(ColumnMove),
1097 Cursor(CursorMove),
1098}
1099
1100pub(crate) fn visible_slice(df: &DataFrame, offset: usize, len: usize) -> Option<DataFrame> {
1101 let len = len.min(df.height().saturating_sub(offset));
1102 (offset < df.height() && len > 0).then(|| df.slice(offset as i64, len))
1103}
1104
1105#[cfg(test)]
1111#[derive(Debug, PartialEq)]
1112pub(crate) struct ViewSnapshot {
1113 rows: std::result::Result<DataFrame, String>,
1114 analysis_rows: std::result::Result<DataFrame, String>,
1115 base_rows: std::result::Result<DataFrame, String>,
1116 reshaped_rows: Option<std::result::Result<DataFrame, String>>,
1117 schema: Arc<Schema>,
1118 queries: [String; 3],
1119 filters: String,
1120 sort: (Vec<String>, Vec<bool>, bool),
1121 layout: (Vec<String>, usize),
1122 reshape: String,
1123 grouped: (bool, bool),
1124 drill: (Option<usize>, Option<Vec<String>>, Option<Vec<String>>),
1125 drift: (bool, Arc<Vec<crate::formats::schema_union::DriftGroup>>),
1126 notes: (Vec<crate::notes::Note>, bool, Vec<crate::notes::Note>),
1127 selection: (Option<usize>, usize, usize),
1128 count: (usize, bool, u64),
1129 buffer: (usize, usize, Option<DataFrame>),
1130 shown: Option<DataFrame>,
1131 error: Option<String>,
1132}
1133
1134#[cfg(test)]
1135impl ViewSnapshot {
1136 pub(crate) fn has_rows(&self) -> bool {
1138 self.buffer.2.is_some() && self.count.1
1139 }
1140}
1141
1142#[cfg(test)]
1143impl DataTableState {
1144 pub(crate) fn snapshot(&self) -> ViewSnapshot {
1145 let rows = |lf: &LazyFrame| lf.clone().collect().map_err(|e| e.to_string());
1146 ViewSnapshot {
1147 rows: rows(&self.view.lf),
1148 analysis_rows: rows(&self.analysis_lf()),
1149 base_rows: rows(&self.view.base_lf),
1150 reshaped_rows: self.view.reshaped_lf.as_ref().map(rows),
1151 schema: self.view.schema.clone(),
1152 queries: [
1153 self.view.active_query.clone(),
1154 self.view.active_sql_query.clone(),
1155 self.view.active_fuzzy_query.clone(),
1156 ],
1157 filters: format!("{:?}", self.view.filters),
1158 sort: (
1159 self.view.sort_columns.clone(),
1160 self.view.sort_descending.clone(),
1161 self.view.sort_ascending,
1162 ),
1163 layout: (
1164 self.view.column_order.clone(),
1165 self.view.locked_columns_count,
1166 ),
1167 reshape: format!(
1168 "{:?} {:?} {:?}",
1169 self.view.last_pivot_spec, self.view.last_melt_spec, self.view.reshape_source
1170 ),
1171 grouped: (
1172 self.view.grouped.is_some(),
1173 self.view.group_source.is_some(),
1174 ),
1175 drill: (
1176 self.view.drilled_down_group_index,
1177 self.view.drilled_down_group_key.clone(),
1178 self.view.drilled_down_group_key_columns.clone(),
1179 ),
1180 drift: (
1181 self.view.drift_column_present,
1182 self.view.drift_groups.clone(),
1183 ),
1184 notes: (
1185 self.view.notes.clone(),
1186 self.view.notes_seen,
1187 self.view.view_notes.clone(),
1188 ),
1189 selection: (
1190 self.table_state.selected(),
1191 self.view.start_row,
1192 self.termcol_index,
1193 ),
1194 count: (
1195 self.view.num_rows,
1196 self.view.num_rows_valid,
1197 self.view.len_generation,
1198 ),
1199 buffer: (
1200 self.view.buffered_start_row,
1201 self.view.buffered_end_row,
1202 self.view.buffered_df.clone(),
1203 ),
1204 shown: self.view.df.clone(),
1205 error: self.error.as_ref().map(|e| e.to_string()),
1206 }
1207 }
1208}
1209
1210mod buffer;
1211mod columns;
1212mod copy;
1213mod csv_marks;
1214mod drawn;
1215mod facts;
1216mod quality;
1217mod query;
1218mod view;
1219
1220pub use buffer::*;
1221pub use copy::*;
1222pub(crate) use drawn::DrawnTable;
1223pub use drawn::{CellHit, DrawnColumns};
1224pub use facts::*;
1225pub use query::*;
1226pub use view::*;
1227
1228#[cfg(test)]
1229mod checkpoint_tests;
1230#[cfg(test)]
1231mod fill_tests;
1232#[cfg(test)]
1233mod tests;