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 pushdown: Option<Arc<dyn crate::formats::pushdown::Pushdown>>,
250 source_hold: Option<crate::formats::sqlite::Hold>,
252 read_mode: Option<crate::ReadMode>,
254 read_as: Option<crate::FileFormat>,
256 fetched: bool,
258 detail: Option<Arc<crate::formats::text_formats::Detail>>,
260 file_units: Arc<Vec<(String, String)>>,
262 column_bytes: Vec<(String, usize)>,
265 proximity_threshold: usize,
266 row_numbers: bool,
267 row_start_index: usize,
268 read_python: Vec<String>,
270 typing: Typing,
272 unfit_notes: Option<Vec<crate::notes::Note>>,
274 changes_unfit: Option<(u64, Vec<crate::notes::Note>)>,
276 partition_columns: Option<Vec<String>>,
278 decompress_temp_file: Option<Arc<Decompressed>>,
281 download: Option<crate::cloud::download::TempDownload>,
283 converted: Vec<crate::cloud::download::TempDownload>,
285 other_tables: Vec<String>,
287 polars_streaming: bool,
289 defer_collect: bool,
292 pub needs_recollect: bool,
295 follow: Option<crate::loading::follow::Follow>,
297 follow_known: Option<(u64, Vec<(usize, usize)>)>,
301 sampled: Option<Box<Sampled>>,
304 pub(crate) view: View,
306}
307
308pub struct Sampled {
311 source: Box<DataTableState>,
313 sample: crate::analysis::sampling::Sample,
314 rows: Arc<crate::analysis::table_sample::SampleRows>,
315 frame: Arc<DataFrame>,
317 through: bool,
320 drawn: Option<crate::analysis::table_sample::Drawn>,
322 path: Option<crate::analysis::table_sample::DrawPath>,
324}
325
326impl Sampled {
327 pub fn sample(&self) -> &crate::analysis::sampling::Sample {
328 &self.sample
329 }
330
331 pub fn source(&self) -> &DataTableState {
333 &self.source
334 }
335
336 pub fn through(&self) -> bool {
338 self.through
339 }
340
341 pub(crate) fn holds(&self, rows: &Arc<crate::analysis::table_sample::SampleRows>) -> bool {
343 Arc::ptr_eq(&self.rows, rows)
344 }
345
346 pub fn drawing(&self) -> bool {
348 self.drawn.is_none()
349 }
350
351 pub fn path(&self) -> Option<crate::analysis::table_sample::DrawPath> {
353 self.path
354 }
355
356 #[cfg(test)]
358 pub(crate) fn frame(&self) -> &DataFrame {
359 &self.frame
360 }
361
362 pub fn rows(&self) -> usize {
364 self.frame.height()
365 }
366
367 pub fn label(&self) -> String {
370 let rows = crate::numfmt::group_chrome(self.rows());
371 let Some(drawn) = &self.drawn else {
372 return format!("sample {rows}+");
373 };
374 let about = if drawn.about { "about " } else { "" };
375 let cut = if drawn.cut { ", stopped" } else { "" };
376 match drawn.total {
377 Some(total) if total > self.rows() => {
378 format!(
379 "sample {about}{rows} of {}{cut}",
380 crate::home::discover::format_rows(total)
381 )
382 }
383 _ => format!("sample {rows}{cut}"),
384 }
385 }
386}
387
388#[derive(Default)]
392pub struct OpenFacts {
393 pub remote_source: bool,
395 pub row_groups: Vec<Vec<usize>>,
398 pub remote_files: Option<RemoteFiles>,
400 pub remote_objects: Vec<RemoteObject>,
402 pub dataset: Option<DatasetAtOpen>,
404 pub footers_pending: Option<FootersJoin>,
406 pub column_bytes: Vec<(String, usize)>,
408 pub parquet_count_dir: Option<PathBuf>,
410 pub measurements: Arc<crate::loading::measurements::Meter>,
412 pub open_notes: Vec<crate::notes::Note>,
414 pub not_the_table: Option<&'static str>,
417 pub format_read: Option<Arc<crate::formats::Read>>,
419 pub delimited: Option<Arc<crate::formats::delimited_spec::DelimitedRead>>,
421 pub download: Option<crate::cloud::download::TempDownload>,
423 pub converted: Vec<crate::cloud::download::TempDownload>,
425 pub other_tables: Vec<String>,
428 pub pushdown: Option<Arc<dyn crate::formats::pushdown::Pushdown>>,
430 pub hold: Option<crate::formats::sqlite::Hold>,
432 pub read_mode: Option<crate::ReadMode>,
434 pub read_as: Option<crate::FileFormat>,
437 pub fetched: bool,
440 pub detail: Option<Arc<crate::formats::text_formats::Detail>>,
442 pub records: Option<(Arc<dyn crate::formats::pushdown::Windowed>, usize)>,
445 pub units: Vec<(String, String)>,
447 pub indexing: Option<Arc<crate::formats::lines::Lines>>,
449 pub numbering: Option<Arc<crate::formats::lines::Lines>>,
451 pub typing: Typing,
453}
454
455pub struct DatasetAtOpen {
457 pub schema: crate::formats::schema_union::DatasetSchema,
458 pub file_rows: Vec<usize>,
461 pub files: Vec<String>,
463}
464
465pub const DEFAULT_MAX_BUFFERED_ROWS: usize = 100_000;
468
469static NEXT_LEN_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
472
473fn next_len_generation() -> u64 {
474 NEXT_LEN_GENERATION.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
475}
476
477fn scanned_frame(lf: &LazyFrame) -> Option<Arc<DataFrame>> {
479 match &lf.logical_plan {
480 polars::lazy::dsl::DslPlan::DataFrameScan { df, .. } => Some(df.clone()),
481 _ => None,
482 }
483}
484
485pub(crate) fn for_each_input(
487 plan: &mut polars::lazy::dsl::DslPlan,
488 f: &mut dyn FnMut(&mut polars::lazy::dsl::DslPlan),
489) {
490 use polars::lazy::dsl::DslPlan;
491 match plan {
492 DslPlan::Sort { input, .. }
493 | DslPlan::Select { input, .. }
494 | DslPlan::GroupBy { input, .. }
495 | DslPlan::Filter { input, .. }
496 | DslPlan::Distinct { input, .. }
497 | DslPlan::Slice { input, .. }
498 | DslPlan::HStack { input, .. }
499 | DslPlan::MatchToSchema { input, .. }
500 | DslPlan::MapFunction { input, .. }
501 | DslPlan::Sink { input, .. }
502 | DslPlan::Cache { input, .. }
503 | DslPlan::Pivot { input, .. } => f(Arc::make_mut(input)),
504 DslPlan::Union { inputs, .. }
505 | DslPlan::HConcat { inputs, .. }
506 | DslPlan::SinkMultiple { inputs } => inputs.iter_mut().for_each(f),
507 DslPlan::PipeWithSchema { input, .. } => {
508 let mut inputs = input.to_vec();
509 inputs.iter_mut().for_each(&mut *f);
510 *input = inputs.into();
511 }
512 DslPlan::Join {
513 input_left,
514 input_right,
515 ..
516 } => {
517 f(Arc::make_mut(input_left));
518 f(Arc::make_mut(input_right));
519 }
520 DslPlan::Gather { input, idxs, .. } => {
521 f(Arc::make_mut(input));
522 f(Arc::make_mut(idxs));
523 }
524 DslPlan::ExtContext { input, contexts } => {
525 f(Arc::make_mut(input));
526 contexts.iter_mut().for_each(f);
527 }
528 _ => {}
529 }
530}
531
532impl DataTableState {
533 pub fn new(
534 lf: LazyFrame,
535 pages_lookahead: Option<usize>,
536 pages_lookback: Option<usize>,
537 max_buffered_rows: Option<usize>,
538 max_buffered_mb: Option<usize>,
539 polars_streaming: bool,
540 ) -> Result<Self> {
541 let options = OpenOptions {
542 pages_lookahead,
543 pages_lookback,
544 max_buffered_rows,
545 max_buffered_mb,
546 polars_streaming,
547 ..OpenOptions::default()
548 };
549 Self::from_lazyframe(lf, &options)
550 }
551
552 fn without_source_rows(schema: Arc<Schema>) -> (Arc<Schema>, bool) {
555 if !schema.contains(crate::formats::schema_union::DRIFT_COLUMN) {
556 return (schema, false);
557 }
558 let mut schema = (*schema).clone();
559 schema.shift_remove(crate::formats::schema_union::DRIFT_COLUMN);
560 (Arc::new(schema), true)
561 }
562
563 pub fn from_lazyframe(lf: LazyFrame, options: &crate::OpenOptions) -> Result<Self> {
565 let schema = lf.clone().collect_schema()?;
566 Self::from_schema_and_lazyframe(schema, lf, options, None)
567 }
568
569 pub fn from_schema_and_lazyframe(
573 schema: Arc<Schema>,
574 lf: LazyFrame,
575 options: &crate::OpenOptions,
576 partition_columns: Option<Vec<String>>,
577 ) -> Result<Self> {
578 let (schema, source_rows_at_open) = Self::without_source_rows(schema);
579 let column_order: Vec<String> = if let Some(ref part) = partition_columns {
580 let part_set: HashSet<&str> = part.iter().map(String::as_str).collect();
581 let rest: Vec<String> = schema
582 .iter_names()
583 .map(|s| s.to_string())
584 .filter(|c| !part_set.contains(c.as_str()))
585 .collect();
586 part.iter().cloned().chain(rest).collect()
587 } else {
588 schema.iter_names().map(|s| s.to_string()).collect()
589 };
590 Ok(Self {
591 original_lf: lf.clone(),
592 original_schema: schema.clone(),
593 table_state: TableState::default(),
594 visible_rows: 0,
595 termcol_index: 0,
596 visible_termcols: 0,
597 scroll_room: None,
598 column_moves: Vec::new(),
599 page_trail: Vec::new(),
600 on_screen: None,
601 drawn: None,
602 page_cells: Default::default(),
603 error: None,
604 suppress_error_display: false,
605 pristine_rows: None,
606 root_generation: next_len_generation(),
607 parquet_count_dir: None,
608 measurements: Arc::new(crate::loading::measurements::Meter::default()),
609 reveal_cursor: false,
610 widths: ColumnWidths::default(),
611 pages_lookahead: options.pages_lookahead.unwrap_or(3),
612 pages_lookback: options.pages_lookback.unwrap_or(3),
613 max_buffered_rows: options
614 .max_buffered_rows
615 .unwrap_or(DEFAULT_MAX_BUFFERED_ROWS),
616 max_buffered_mb: options.max_buffered_mb.unwrap_or(512),
617 remote_source: false,
618 row_group_offsets: None,
619 remote_files: None,
620 remote_objects: None,
621 dataset_schema: None,
622 drift_at_open: false,
623 groups_at_open: Arc::new(Vec::new()),
624 source_rows_at_open,
625 indexing: None,
626 numbering: None,
627 row_estimate: None,
628 indexing_notes: Vec::new(),
629 indexing_guessed: false,
630 drift_file_starts: Vec::new(),
631 drift_file_group: Vec::new(),
632 drift_files: Vec::new(),
633 footers_pending: None,
634 open_notes: Vec::new(),
635 not_the_table: None,
636 format_read: None,
637 delimited: None,
638 fixed_window: None,
639 pushdown: None,
640 source_hold: None,
641 read_mode: None,
642 read_as: None,
643 fetched: false,
644 detail: None,
645 file_units: Arc::new(Vec::new()),
646 notes_at_open: Vec::new(),
647 drift_dataset_rows: 0,
648 dataset_at_open: None,
649 read_as_text: Vec::new(),
650 column_bytes: Vec::new(),
651 proximity_threshold: 0,
653 row_numbers: options.row_numbers,
654 row_start_index: options.row_start_index,
655 read_python: Vec::new(),
656 typing: Typing::default(),
657 unfit_notes: None,
658 changes_unfit: None,
659 partition_columns,
660 decompress_temp_file: None,
661 download: None,
662 converted: Vec::new(),
663 other_tables: Vec::new(),
664 polars_streaming: options.polars_streaming,
665 defer_collect: false,
666 needs_recollect: false,
667 follow: None,
668 follow_known: None,
669 sampled: None,
670 view: View {
671 unsorted_lf: None,
672 base_lf: lf.clone(),
673 base_schema: schema.clone(),
674 lf,
675 df: None,
676 locked_df: None,
677 start_row: 0,
678 schema,
679 num_rows: 0,
680 num_rows_valid: false,
681 len_generation: next_len_generation(),
682 filters: Vec::new(),
683 sort_columns: Vec::new(),
684 sort_descending: Vec::new(),
685 sort_ascending: true,
686 cursor_column: None,
687 cursor_at: 0,
688 active_query: String::new(),
689 active_sql_query: String::new(),
690 query_order: Vec::new(),
691 active_fuzzy_query: String::new(),
692 column_order,
693 locked_columns_count: 0,
694 frozen_fit: (0, 0),
695 grouped: None,
696 group_source: None,
697 reshaped_lf: None,
698 drilled_down_group_index: None,
699 drilled_down_group_key: None,
700 drilled_down_group_key_columns: None,
701 drift_column_present: false,
702 drift_groups: Arc::new(Vec::new()),
703 view_numbered: false,
704 notes: Vec::new(),
705 notes_seen: false,
706 view_notes: Vec::new(),
707 observed_bytes_per_row: None,
708 buffered_start_row: 0,
709 buffered_end_row: 0,
710 buffered_df: None,
711 drawn_start: 0,
712 last_pivot_spec: None,
713 last_melt_spec: None,
714 reshape_source: None,
715 base_steps: Vec::new(),
716 column_changes: Vec::new(),
717 changes_version: 0,
718 changes_dropped: Vec::new(),
719 reshape_steps: None,
720 lineage: None,
721 reshape_lineage: None,
722 },
723 })
724 }
725
726 pub fn with_open(mut self, facts: OpenFacts) -> Self {
731 let OpenFacts {
732 remote_source,
733 row_groups,
734 remote_files,
735 remote_objects,
736 dataset,
737 footers_pending,
738 column_bytes,
739 parquet_count_dir,
740 measurements,
741 open_notes,
742 not_the_table,
743 format_read,
744 delimited,
745 download,
746 converted,
747 other_tables,
748 pushdown,
749 hold,
750 read_mode,
751 read_as,
752 fetched,
753 detail,
754 records,
755 units,
756 indexing,
757 numbering,
758 typing,
759 } = facts;
760 self.numbering = numbering;
761 self.typing = typing;
762 debug_assert!(
763 self.is_pristine(),
764 "an open's facts are for the data as loaded"
765 );
766 self.remote_source = remote_source;
767 self.remote_files = remote_files;
768 self.remote_objects = (!remote_objects.is_empty()).then(|| {
769 Arc::new(
770 remote_objects
771 .into_iter()
772 .map(|object| (object.url.clone(), object))
773 .collect(),
774 )
775 });
776 if !row_groups.is_empty() {
777 if self.remote_files.is_some() {
778 self.record_file_row_groups(&row_groups);
779 } else {
780 let flat: Vec<usize> = row_groups.into_iter().flatten().collect();
781 self.record_row_groups(&flat);
782 }
783 }
784 if let Some(DatasetAtOpen {
785 schema,
786 file_rows,
787 files,
788 }) = dataset
789 {
790 self.record_dataset_schema(schema, &file_rows, &files);
791 }
792 self.footers_pending = footers_pending;
793 self.column_bytes = column_bytes;
794 self.parquet_count_dir = parquet_count_dir;
795 self.measurements = measurements;
796 self.open_notes = open_notes;
797 self.not_the_table = not_the_table;
798 self.fixed_window = format_read
799 .as_ref()
800 .map(|read| read.records.clone() as Arc<dyn crate::formats::pushdown::Windowed>);
801 if let Some(read) = &format_read {
802 self.set_num_rows(read.records.rows());
805 }
806 self.pushdown = pushdown;
807 self.source_hold = hold;
808 self.format_read = format_read;
809 self.delimited = delimited;
810 self.download = download;
811 self.converted = converted;
812 self.other_tables = other_tables;
813 self.read_mode = read_mode;
814 self.read_as = read_as;
815 self.fetched = fetched;
816 self.detail = detail;
817 if let Some((window, rows)) = records {
818 if indexing.is_none() {
821 self.set_num_rows(rows);
822 }
823 self.fixed_window = Some(window);
824 }
825 if let Some(lines) = &indexing {
826 self.indexing_guessed = self
828 .open_notes
829 .iter()
830 .any(|n| n.summary.starts_with(crate::formats::lines::GUESSED));
831 self.indexing_notes = crate::formats::lines::notes(lines, self.indexing_guessed);
832 }
833 self.indexing = indexing;
834 self.file_units = Arc::new(units);
835 self
836 }
837
838 fn replace_root(&mut self, lf: LazyFrame, schema: Arc<Schema>) {
842 self.fixed_window = None;
844 self.pushdown = None;
845 self.root_generation = next_len_generation();
846 self.invalidate_num_rows();
847 self.original_schema = schema.clone();
848 self.view.base_schema = schema.clone();
849 self.view.schema = schema;
850 self.original_lf = lf.clone();
851 self.view.base_lf = lf.clone();
852 self.view.lf = lf;
853 self.view.unsorted_lf = None;
854 self.view.base_steps = Vec::new();
855 self.view.reshape_steps = None;
856 self.drop_buffer();
857 }
858
859 fn install_base(&mut self, lf: LazyFrame, schema: Arc<Schema>) {
862 self.invalidate_num_rows();
863 self.view.drift_column_present = false;
866 self.view.view_numbered = false;
867 self.view.drift_groups = Arc::new(Vec::new());
868 self.view.notes = Vec::new();
869 self.view.view_notes = Vec::new();
870 self.view.observed_bytes_per_row = None;
872 self.view.query_order = Vec::new();
874 self.widths.relearn();
876 self.view.base_lf = lf.clone();
877 self.view.base_schema = schema.clone();
878 self.view.lf = lf;
879 self.view.unsorted_lf = None;
880 self.view.base_steps = vec![Step::Unreproducible(
882 "datui built the view from here in a way it cannot write as Python".to_string(),
883 )];
884 self.view.schema = schema;
885 self.view.column_order = self
886 .view
887 .schema
888 .iter_names()
889 .map(|s| s.to_string())
890 .collect();
891 self.view.lineage = Some(Arc::default());
893 self.settle_cursor();
894 self.view.group_source = None;
896 self.drop_buffer();
897 }
898
899 fn drop_buffer(&mut self) {
901 self.view.buffered_start_row = 0;
902 self.view.buffered_end_row = 0;
903 self.view.buffered_df = None;
904 }
905
906 fn reset_view_state(&mut self, locked_columns_count: usize) {
909 self.forget_column_changes();
910 self.view.active_query.clear();
911 self.view.active_sql_query.clear();
912 self.view.active_fuzzy_query.clear();
913 self.view.locked_columns_count = locked_columns_count;
914 self.view.filters.clear();
915 self.view.sort_columns.clear();
916 self.view.sort_descending.clear();
917 self.view.sort_ascending = true;
918 self.view.start_row = 0;
919 self.termcol_index = 0;
920 self.clear_column_moves();
921 self.place_cursor_at(0);
922 self.view.drilled_down_group_index = None;
923 self.view.drilled_down_group_key = None;
924 self.view.drilled_down_group_key_columns = None;
925 self.view.grouped = None;
926 self.drop_buffer();
927 self.table_state.select(Some(0));
928 }
929
930 fn install_query_result(
934 &mut self,
935 lf: LazyFrame,
936 schema: Arc<Schema>,
937 query: ActiveQuery,
938 locked_columns_count: usize,
939 steps: Vec<Step>,
940 ) {
941 self.install_base(lf, schema);
942 self.view.base_steps = steps;
943 self.reset_view_state(locked_columns_count);
944 match query {
945 ActiveQuery::Dsl(q) => self.view.active_query = q,
946 #[cfg(feature = "sql")]
947 ActiveQuery::Sql(q) => self.view.active_sql_query = q,
948 ActiveQuery::Fuzzy(q) => self.view.active_fuzzy_query = q,
949 }
950 }
951
952 fn forget_reshape(&mut self) {
954 self.view.reshaped_lf = None;
955 self.view.reshape_lineage = None;
956 self.view.reshape_steps = None;
957 self.view.last_pivot_spec = None;
958 self.view.last_melt_spec = None;
959 self.view.reshape_source = None;
960 }
961
962 fn reset_lf_to_original(&mut self) {
964 self.install_base(self.original_lf.clone(), self.query_source_schema());
965 self.view.base_steps = Vec::new();
966 self.view.reshape_steps = None;
967 self.view.lineage = None;
968 self.view.reshape_lineage = None;
969 self.view.drift_column_present = self.drift_at_open;
971 self.view.drift_groups = self.groups_at_open.clone();
972 self.view.notes = self.notes_at_open.clone();
973 self.view.reshaped_lf = None;
974 self.view.reshape_source = None;
975 self.reset_view_state(0);
976 self.restore_footer_count();
977 }
978
979 fn return_to_root(&mut self) {
981 self.reset_lf_to_original();
982 self.error = None;
983 self.suppress_error_display = false;
984 self.view.last_pivot_spec = None;
985 self.view.last_melt_spec = None;
986 }
987
988 pub(crate) fn reset_view_for_replay(&mut self) {
991 self.return_to_root();
992 }
993
994 pub fn reset(&mut self) {
996 self.widths = ColumnWidths::default();
997 self.return_to_root();
998 self.collect();
999 if self.view.num_rows > 0 {
1000 self.view.start_row = 0;
1001 }
1002 }
1003
1004 pub(crate) fn from_read(read: Read, options: &OpenOptions) -> Result<Self> {
1006 let mut state = Self::from_lazyframe(read.lf, options)?;
1007 state.read_python = read.python;
1008 state.typing = read.typing;
1009 state.decompress_temp_file = read.temp;
1010 Ok(state)
1011 }
1012
1013 pub fn set_row_numbers(&mut self, enabled: bool) {
1014 self.row_numbers = enabled;
1015 }
1016
1017 pub fn toggle_row_numbers(&mut self) -> bool {
1020 self.row_numbers = !self.row_numbers;
1021 if self.row_numbers && self.wants_view_numbers() && !self.view.view_numbered {
1022 self.drop_buffer();
1023 self.apply_transformations();
1024 return true;
1025 }
1026 false
1027 }
1028
1029 fn wants_view_numbers(&self) -> bool {
1032 let too_many = self
1035 .pristine_rows
1036 .or(self.num_rows_if_valid())
1037 .is_some_and(|rows| rows > crate::formats::row_index::MAX_ROWS);
1038 self.scan_is_the_root()
1039 && self.follow.is_none()
1040 && !self.remote_source
1041 && self.remote_files.is_none()
1042 && self.parquet_count_dir.is_none()
1043 && !too_many
1044 && !self.view.drift_column_present
1045 && !self.source_rows_at_open
1046 && self.pushed_view().is_none()
1047 && (!self.view.filters.is_empty()
1048 || !self.view.sort_columns.is_empty()
1049 || !self.view.sort_ascending)
1050 }
1051
1052 pub fn row_numbers(&self) -> bool {
1054 self.row_numbers
1055 }
1056
1057 pub fn row_start_index(&self) -> usize {
1059 self.row_start_index
1060 }
1061}
1062
1063pub(crate) fn row_count_lf(lf: &LazyFrame) -> LazyFrame {
1067 lf.clone().select([len().cast(DataType::UInt64)])
1068}
1069
1070pub(crate) fn binary_stub() -> &'static str {
1074 crate::glyphs::get().binary_stub
1075}
1076
1077const MAX_WAITING_MOVES: usize = 32;
1079
1080#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1082pub(crate) enum WaitingMove {
1083 View(ColumnMove),
1084 Cursor(CursorMove),
1085}
1086
1087pub(crate) fn visible_slice(df: &DataFrame, offset: usize, len: usize) -> Option<DataFrame> {
1088 let len = len.min(df.height().saturating_sub(offset));
1089 (offset < df.height() && len > 0).then(|| df.slice(offset as i64, len))
1090}
1091
1092#[cfg(test)]
1098#[derive(Debug, PartialEq)]
1099pub(crate) struct ViewSnapshot {
1100 rows: std::result::Result<DataFrame, String>,
1101 analysis_rows: std::result::Result<DataFrame, String>,
1102 base_rows: std::result::Result<DataFrame, String>,
1103 reshaped_rows: Option<std::result::Result<DataFrame, String>>,
1104 schema: Arc<Schema>,
1105 queries: [String; 3],
1106 filters: String,
1107 sort: (Vec<String>, Vec<bool>, bool),
1108 layout: (Vec<String>, usize),
1109 reshape: String,
1110 grouped: (bool, bool),
1111 drill: (Option<usize>, Option<Vec<String>>, Option<Vec<String>>),
1112 drift: (bool, Arc<Vec<crate::formats::schema_union::DriftGroup>>),
1113 notes: (Vec<crate::notes::Note>, bool, Vec<crate::notes::Note>),
1114 selection: (Option<usize>, usize, usize),
1115 count: (usize, bool, u64),
1116 buffer: (usize, usize, Option<DataFrame>),
1117 shown: Option<DataFrame>,
1118 error: Option<String>,
1119}
1120
1121#[cfg(test)]
1122impl ViewSnapshot {
1123 pub(crate) fn has_rows(&self) -> bool {
1125 self.buffer.2.is_some() && self.count.1
1126 }
1127}
1128
1129#[cfg(test)]
1130impl DataTableState {
1131 pub(crate) fn snapshot(&self) -> ViewSnapshot {
1132 let rows = |lf: &LazyFrame| lf.clone().collect().map_err(|e| e.to_string());
1133 ViewSnapshot {
1134 rows: rows(&self.view.lf),
1135 analysis_rows: rows(&self.analysis_lf()),
1136 base_rows: rows(&self.view.base_lf),
1137 reshaped_rows: self.view.reshaped_lf.as_ref().map(rows),
1138 schema: self.view.schema.clone(),
1139 queries: [
1140 self.view.active_query.clone(),
1141 self.view.active_sql_query.clone(),
1142 self.view.active_fuzzy_query.clone(),
1143 ],
1144 filters: format!("{:?}", self.view.filters),
1145 sort: (
1146 self.view.sort_columns.clone(),
1147 self.view.sort_descending.clone(),
1148 self.view.sort_ascending,
1149 ),
1150 layout: (
1151 self.view.column_order.clone(),
1152 self.view.locked_columns_count,
1153 ),
1154 reshape: format!(
1155 "{:?} {:?} {:?}",
1156 self.view.last_pivot_spec, self.view.last_melt_spec, self.view.reshape_source
1157 ),
1158 grouped: (
1159 self.view.grouped.is_some(),
1160 self.view.group_source.is_some(),
1161 ),
1162 drill: (
1163 self.view.drilled_down_group_index,
1164 self.view.drilled_down_group_key.clone(),
1165 self.view.drilled_down_group_key_columns.clone(),
1166 ),
1167 drift: (
1168 self.view.drift_column_present,
1169 self.view.drift_groups.clone(),
1170 ),
1171 notes: (
1172 self.view.notes.clone(),
1173 self.view.notes_seen,
1174 self.view.view_notes.clone(),
1175 ),
1176 selection: (
1177 self.table_state.selected(),
1178 self.view.start_row,
1179 self.termcol_index,
1180 ),
1181 count: (
1182 self.view.num_rows,
1183 self.view.num_rows_valid,
1184 self.view.len_generation,
1185 ),
1186 buffer: (
1187 self.view.buffered_start_row,
1188 self.view.buffered_end_row,
1189 self.view.buffered_df.clone(),
1190 ),
1191 shown: self.view.df.clone(),
1192 error: self.error.as_ref().map(|e| e.to_string()),
1193 }
1194 }
1195}
1196
1197mod buffer;
1198mod columns;
1199mod copy;
1200mod drawn;
1201mod facts;
1202mod quality;
1203mod query;
1204mod view;
1205
1206pub use buffer::*;
1207pub use copy::*;
1208pub(crate) use drawn::DrawnTable;
1209pub use drawn::{CellHit, DrawnColumns};
1210pub use facts::*;
1211pub use query::*;
1212pub use view::*;
1213
1214#[cfg(test)]
1215mod checkpoint_tests;
1216#[cfg(test)]
1217mod tests;