1use crate::api_client::QueryResponse;
2use crate::data::data_provider::DataProvider;
3use crate::data::type_inference::{InferredType, TypeInference};
4use serde::de::{VariantAccess, Visitor};
5use serde::{Deserialize, Serialize};
6use serde_json::Value as JsonValue;
7use std::collections::HashMap;
8use std::fmt;
9use std::sync::Arc;
10use tracing::debug;
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum DataType {
15 String,
16 Integer,
17 Float,
18 Boolean,
19 DateTime,
20 Null,
21 Mixed, }
23
24impl DataType {
25 #[must_use]
27 pub fn infer_from_string(value: &str) -> Self {
28 if value.eq_ignore_ascii_case("null") {
30 return DataType::Null;
31 }
32
33 match TypeInference::infer_from_string(value) {
35 InferredType::Null => DataType::Null,
36 InferredType::Boolean => DataType::Boolean,
37 InferredType::Integer => DataType::Integer,
38 InferredType::Float => DataType::Float,
39 InferredType::DateTime => DataType::DateTime,
40 InferredType::String => DataType::String,
41 }
42 }
43
44 fn looks_like_datetime(value: &str) -> bool {
47 TypeInference::looks_like_datetime(value)
48 }
49
50 #[must_use]
52 pub fn merge(&self, other: &DataType) -> DataType {
53 if self == other {
54 return self.clone();
55 }
56
57 match (self, other) {
58 (DataType::Null, t) | (t, DataType::Null) => t.clone(),
59 (DataType::Integer, DataType::Float) | (DataType::Float, DataType::Integer) => {
60 DataType::Float
61 }
62 _ => DataType::Mixed,
63 }
64 }
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DataColumn {
70 pub name: String,
71 pub data_type: DataType,
72 pub nullable: bool,
73 pub unique_values: Option<usize>,
74 pub null_count: usize,
75 pub metadata: HashMap<String, String>,
76 pub qualified_name: Option<String>,
78 pub source_table: Option<String>,
80}
81
82impl DataColumn {
83 pub fn new(name: impl Into<String>) -> Self {
84 Self {
85 name: name.into(),
86 data_type: DataType::String,
87 nullable: true,
88 unique_values: None,
89 null_count: 0,
90 metadata: HashMap::new(),
91 qualified_name: None,
92 source_table: None,
93 }
94 }
95
96 #[must_use]
97 pub fn with_type(mut self, data_type: DataType) -> Self {
98 self.data_type = data_type;
99 self
100 }
101
102 #[must_use]
104 pub fn with_qualified_name(mut self, table_name: &str) -> Self {
105 self.qualified_name = Some(format!("{}.{}", table_name, self.name));
106 self.source_table = Some(table_name.to_string());
107 self
108 }
109
110 pub fn get_qualified_or_simple_name(&self) -> &str {
112 self.qualified_name.as_deref().unwrap_or(&self.name)
113 }
114
115 #[must_use]
116 pub fn with_nullable(mut self, nullable: bool) -> Self {
117 self.nullable = nullable;
118 self
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, PartialOrd)]
124pub enum DataValue {
125 String(String),
126 InternedString(Arc<String>), Integer(i64),
128 Float(f64),
129 Boolean(bool),
130 DateTime(String), Vector(Vec<f64>), Null,
133}
134
135impl std::hash::Hash for DataValue {
137 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
138 match self {
139 DataValue::String(s) => {
140 0u8.hash(state);
141 s.hash(state);
142 }
143 DataValue::InternedString(s) => {
144 1u8.hash(state);
145 s.hash(state);
146 }
147 DataValue::Integer(i) => {
148 2u8.hash(state);
149 i.hash(state);
150 }
151 DataValue::Float(f) => {
152 3u8.hash(state);
153 f.to_bits().hash(state);
155 }
156 DataValue::Boolean(b) => {
157 4u8.hash(state);
158 b.hash(state);
159 }
160 DataValue::DateTime(dt) => {
161 5u8.hash(state);
162 dt.hash(state);
163 }
164 DataValue::Vector(v) => {
165 6u8.hash(state);
166 for f in v {
168 f.to_bits().hash(state);
169 }
170 }
171 DataValue::Null => {
172 7u8.hash(state);
173 }
174 }
175 }
176}
177
178impl Serialize for DataValue {
180 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181 where
182 S: serde::Serializer,
183 {
184 match self {
185 DataValue::String(s) => {
186 serializer.serialize_newtype_variant("DataValue", 0, "String", s)
187 }
188 DataValue::InternedString(arc_s) => {
189 serializer.serialize_newtype_variant(
191 "DataValue",
192 1,
193 "InternedString",
194 arc_s.as_ref(),
195 )
196 }
197 DataValue::Integer(i) => {
198 serializer.serialize_newtype_variant("DataValue", 2, "Integer", i)
199 }
200 DataValue::Float(f) => serializer.serialize_newtype_variant("DataValue", 3, "Float", f),
201 DataValue::Boolean(b) => {
202 serializer.serialize_newtype_variant("DataValue", 4, "Boolean", b)
203 }
204 DataValue::DateTime(dt) => {
205 serializer.serialize_newtype_variant("DataValue", 5, "DateTime", dt)
206 }
207 DataValue::Vector(v) => {
208 serializer.serialize_newtype_variant("DataValue", 6, "Vector", v)
209 }
210 DataValue::Null => serializer.serialize_unit_variant("DataValue", 7, "Null"),
211 }
212 }
213}
214
215impl<'de> Deserialize<'de> for DataValue {
217 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
218 where
219 D: serde::Deserializer<'de>,
220 {
221 #[derive(Deserialize)]
222 #[serde(field_identifier, rename_all = "PascalCase")]
223 enum Field {
224 String,
225 InternedString,
226 Integer,
227 Float,
228 Boolean,
229 DateTime,
230 Vector,
231 Null,
232 }
233
234 struct DataValueVisitor;
235
236 impl<'de> Visitor<'de> for DataValueVisitor {
237 type Value = DataValue;
238
239 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
240 formatter.write_str("enum DataValue")
241 }
242
243 fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
244 where
245 A: serde::de::EnumAccess<'de>,
246 {
247 let (field, variant) = data.variant()?;
248 match field {
249 Field::String => {
250 let s: String = variant.newtype_variant()?;
251 Ok(DataValue::String(s))
252 }
253 Field::InternedString => {
254 let s: String = variant.newtype_variant()?;
255 Ok(DataValue::InternedString(Arc::new(s)))
256 }
257 Field::Integer => {
258 let i: i64 = variant.newtype_variant()?;
259 Ok(DataValue::Integer(i))
260 }
261 Field::Float => {
262 let f: f64 = variant.newtype_variant()?;
263 Ok(DataValue::Float(f))
264 }
265 Field::Boolean => {
266 let b: bool = variant.newtype_variant()?;
267 Ok(DataValue::Boolean(b))
268 }
269 Field::DateTime => {
270 let dt: String = variant.newtype_variant()?;
271 Ok(DataValue::DateTime(dt))
272 }
273 Field::Vector => {
274 let v: Vec<f64> = variant.newtype_variant()?;
275 Ok(DataValue::Vector(v))
276 }
277 Field::Null => {
278 variant.unit_variant()?;
279 Ok(DataValue::Null)
280 }
281 }
282 }
283 }
284
285 deserializer.deserialize_enum(
286 "DataValue",
287 &[
288 "String",
289 "InternedString",
290 "Integer",
291 "Float",
292 "Boolean",
293 "DateTime",
294 "Vector",
295 "Null",
296 ],
297 DataValueVisitor,
298 )
299 }
300}
301
302impl Eq for DataValue {}
304
305impl DataValue {
306 pub fn from_string(s: &str, data_type: &DataType) -> Self {
307 if s.is_empty() || s.eq_ignore_ascii_case("null") {
308 return DataValue::Null;
309 }
310
311 let t = s.trim();
316
317 match data_type {
318 DataType::String => DataValue::String(s.to_string()),
319 DataType::Integer => t.parse::<i64>().map_or_else(
320 |_| {
326 t.parse::<f64>()
327 .map_or_else(|_| DataValue::String(s.to_string()), DataValue::Float)
328 },
329 DataValue::Integer,
330 ),
331 DataType::Float => t
332 .parse::<f64>()
333 .map_or_else(|_| DataValue::String(s.to_string()), DataValue::Float),
334 DataType::Boolean => {
335 let lower = t.to_lowercase();
336 DataValue::Boolean(lower == "true" || lower == "1" || lower == "yes")
337 }
338 DataType::DateTime => DataValue::DateTime(s.to_string()),
339 DataType::Null => DataValue::Null,
340 DataType::Mixed => {
341 let inferred = DataType::infer_from_string(s);
343 Self::from_string(s, &inferred)
344 }
345 }
346 }
347
348 #[must_use]
349 pub fn is_null(&self) -> bool {
350 matches!(self, DataValue::Null)
351 }
352
353 #[must_use]
354 pub fn data_type(&self) -> DataType {
355 match self {
356 DataValue::String(_) | DataValue::InternedString(_) => DataType::String,
357 DataValue::Integer(_) => DataType::Integer,
358 DataValue::Float(_) => DataType::Float,
359 DataValue::Boolean(_) => DataType::Boolean,
360 DataValue::DateTime(_) => DataType::DateTime,
361 DataValue::Vector(_) => DataType::String, DataValue::Null => DataType::Null,
363 }
364 }
365
366 #[must_use]
369 pub fn to_string_optimized(&self) -> String {
370 match self {
371 DataValue::String(s) => s.clone(), DataValue::InternedString(s) => s.as_ref().clone(), DataValue::DateTime(s) => s.clone(), DataValue::Integer(i) => i.to_string(),
375 DataValue::Float(f) => f.to_string(),
376 DataValue::Boolean(b) => {
377 if *b {
378 "true".to_string()
379 } else {
380 "false".to_string()
381 }
382 }
383 DataValue::Vector(v) => {
384 let components: Vec<String> = v.iter().map(|f| f.to_string()).collect();
386 format!("[{}]", components.join(","))
387 }
388 DataValue::Null => String::new(), }
390 }
391}
392
393impl fmt::Display for DataValue {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 match self {
396 DataValue::String(s) => write!(f, "{s}"),
397 DataValue::InternedString(s) => write!(f, "{s}"),
398 DataValue::Integer(i) => write!(f, "{i}"),
399 DataValue::Float(fl) => write!(f, "{fl}"),
400 DataValue::Boolean(b) => write!(f, "{b}"),
401 DataValue::DateTime(dt) => write!(f, "{dt}"),
402 DataValue::Vector(v) => {
403 let components: Vec<String> = v.iter().map(|fl| fl.to_string()).collect();
404 write!(f, "[{}]", components.join(","))
405 }
406 DataValue::Null => write!(f, ""),
407 }
408 }
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
413pub struct DataRow {
414 pub values: Vec<DataValue>,
415}
416
417impl DataRow {
418 #[must_use]
419 pub fn new(values: Vec<DataValue>) -> Self {
420 Self { values }
421 }
422
423 #[must_use]
424 pub fn get(&self, index: usize) -> Option<&DataValue> {
425 self.values.get(index)
426 }
427
428 pub fn get_mut(&mut self, index: usize) -> Option<&mut DataValue> {
429 self.values.get_mut(index)
430 }
431
432 #[must_use]
433 pub fn len(&self) -> usize {
434 self.values.len()
435 }
436
437 #[must_use]
438 pub fn is_empty(&self) -> bool {
439 self.values.is_empty()
440 }
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct DataTable {
446 pub name: String,
447 pub columns: Vec<DataColumn>,
448 pub rows: Vec<DataRow>,
449 pub metadata: HashMap<String, String>,
450}
451
452impl DataTable {
453 pub fn new(name: impl Into<String>) -> Self {
454 Self {
455 name: name.into(),
456 columns: Vec::new(),
457 rows: Vec::new(),
458 metadata: HashMap::new(),
459 }
460 }
461
462 #[must_use]
465 pub fn dual() -> Self {
466 let mut table = DataTable::new("DUAL");
467 table.add_column(DataColumn::new("DUMMY").with_type(DataType::String));
468 table
469 .add_row(DataRow::new(vec![DataValue::String("X".to_string())]))
470 .unwrap();
471 table
472 }
473
474 pub fn add_column(&mut self, column: DataColumn) -> &mut Self {
475 self.columns.push(column);
476 self
477 }
478
479 pub fn add_row(&mut self, row: DataRow) -> Result<(), String> {
480 if row.len() != self.columns.len() {
481 return Err(format!(
482 "Row has {} values but table has {} columns",
483 row.len(),
484 self.columns.len()
485 ));
486 }
487 self.rows.push(row);
488 Ok(())
489 }
490
491 #[must_use]
492 pub fn get_column(&self, name: &str) -> Option<&DataColumn> {
493 self.columns.iter().find(|c| c.name == name)
494 }
495
496 #[must_use]
497 pub fn get_column_index(&self, name: &str) -> Option<usize> {
498 self.columns.iter().position(|c| c.name == name)
499 }
500
501 #[must_use]
503 pub fn find_column_by_qualified_name(&self, qualified_name: &str) -> Option<usize> {
504 self.columns
505 .iter()
506 .position(|c| c.qualified_name.as_deref() == Some(qualified_name))
507 }
508
509 #[must_use]
512 pub fn find_column_flexible(&self, name: &str, table_prefix: Option<&str>) -> Option<usize> {
513 if let Some(prefix) = table_prefix {
515 let qualified = format!("{}.{}", prefix, name);
516 if let Some(idx) = self.find_column_by_qualified_name(&qualified) {
517 return Some(idx);
518 }
519 }
520
521 self.get_column_index(name)
523 }
524
525 pub fn enrich_columns_with_qualified_names(&mut self, table_name: &str) {
527 for column in &mut self.columns {
528 column.qualified_name = Some(format!("{}.{}", table_name, column.name));
529 column.source_table = Some(table_name.to_string());
530 }
531 }
532
533 #[must_use]
534 pub fn column_count(&self) -> usize {
535 self.columns.len()
536 }
537
538 #[must_use]
539 pub fn row_count(&self) -> usize {
540 self.rows.len()
541 }
542
543 #[must_use]
544 pub fn is_empty(&self) -> bool {
545 self.rows.is_empty()
546 }
547
548 #[must_use]
550 pub fn column_names(&self) -> Vec<String> {
551 self.columns.iter().map(|c| c.name.clone()).collect()
552 }
553
554 pub fn columns_mut(&mut self) -> &mut [DataColumn] {
556 &mut self.columns
557 }
558
559 pub fn infer_column_types(&mut self) {
561 for (col_idx, column) in self.columns.iter_mut().enumerate() {
562 let mut inferred_type = DataType::Null;
563 let mut null_count = 0;
564 let mut unique_values = std::collections::HashSet::new();
565
566 for row in &self.rows {
567 if let Some(value) = row.get(col_idx) {
568 if value.is_null() {
569 null_count += 1;
570 } else {
571 let value_type = value.data_type();
572 inferred_type = inferred_type.merge(&value_type);
573 unique_values.insert(value.to_string());
574 }
575 }
576 }
577
578 column.data_type = inferred_type;
579 column.null_count = null_count;
580 column.nullable = null_count > 0;
581 column.unique_values = Some(unique_values.len());
582 }
583 }
584
585 #[must_use]
587 pub fn get_value(&self, row: usize, col: usize) -> Option<&DataValue> {
588 self.rows.get(row)?.get(col)
589 }
590
591 #[must_use]
593 pub fn get_value_by_name(&self, row: usize, col_name: &str) -> Option<&DataValue> {
594 let col_idx = self.get_column_index(col_name)?;
595 self.get_value(row, col_idx)
596 }
597
598 #[must_use]
600 pub fn to_string_table(&self) -> Vec<Vec<String>> {
601 self.rows
602 .iter()
603 .map(|row| {
604 row.values
605 .iter()
606 .map(DataValue::to_string_optimized)
607 .collect()
608 })
609 .collect()
610 }
611
612 #[must_use]
614 pub fn get_stats(&self) -> DataTableStats {
615 DataTableStats {
616 row_count: self.row_count(),
617 column_count: self.column_count(),
618 memory_size: self.estimate_memory_size(),
619 null_count: self.columns.iter().map(|c| c.null_count).sum(),
620 }
621 }
622
623 #[must_use]
625 pub fn debug_dump(&self) -> String {
626 let mut output = String::new();
627
628 output.push_str(&format!("DataTable: {}\n", self.name));
629 output.push_str(&format!(
630 "Rows: {} | Columns: {}\n",
631 self.row_count(),
632 self.column_count()
633 ));
634
635 if !self.metadata.is_empty() {
636 output.push_str("Metadata:\n");
637 for (key, value) in &self.metadata {
638 output.push_str(&format!(" {key}: {value}\n"));
639 }
640 }
641
642 output.push_str("\nColumns:\n");
643 for column in &self.columns {
644 output.push_str(&format!(" {} ({:?})", column.name, column.data_type));
645 if column.nullable {
646 output.push_str(&format!(" - nullable, {} nulls", column.null_count));
647 }
648 if let Some(unique) = column.unique_values {
649 output.push_str(&format!(", {unique} unique"));
650 }
651 output.push('\n');
652 }
653
654 if self.row_count() > 0 {
656 let sample_size = 5.min(self.row_count());
657 output.push_str(&format!("\nFirst {sample_size} rows:\n"));
658
659 for row_idx in 0..sample_size {
660 output.push_str(&format!(" [{row_idx}]: "));
661 for (col_idx, value) in self.rows[row_idx].values.iter().enumerate() {
662 if col_idx > 0 {
663 output.push_str(", ");
664 }
665 output.push_str(&value.to_string());
666 }
667 output.push('\n');
668 }
669 }
670
671 output
672 }
673
674 #[must_use]
675 pub fn estimate_memory_size(&self) -> usize {
676 let mut size = std::mem::size_of::<Self>();
678
679 size += self.columns.len() * std::mem::size_of::<DataColumn>();
681 for col in &self.columns {
682 size += col.name.len();
683 }
684
685 size += self.rows.len() * std::mem::size_of::<DataRow>();
687
688 for row in &self.rows {
690 for value in &row.values {
691 size += std::mem::size_of::<DataValue>();
693 match value {
695 DataValue::String(s) | DataValue::DateTime(s) => size += s.len(),
696 DataValue::Vector(v) => size += v.len() * std::mem::size_of::<f64>(),
697 _ => {} }
699 }
700 }
701
702 size
703 }
704
705 pub fn to_csv(&self) -> String {
707 let mut csv_output = String::new();
708
709 let headers: Vec<String> = self
711 .columns
712 .iter()
713 .map(|col| {
714 if col.name.contains(',') || col.name.contains('"') || col.name.contains('\n') {
715 format!("\"{}\"", col.name.replace('"', "\"\""))
716 } else {
717 col.name.clone()
718 }
719 })
720 .collect();
721 csv_output.push_str(&headers.join(","));
722 csv_output.push('\n');
723
724 for row in &self.rows {
726 let row_values: Vec<String> = row
727 .values
728 .iter()
729 .map(|value| {
730 let str_val = value.to_string();
731 if str_val.contains(',') || str_val.contains('"') || str_val.contains('\n') {
732 format!("\"{}\"", str_val.replace('"', "\"\""))
733 } else {
734 str_val
735 }
736 })
737 .collect();
738 csv_output.push_str(&row_values.join(","));
739 csv_output.push('\n');
740 }
741
742 csv_output
743 }
744
745 pub fn from_query_response(response: &QueryResponse, table_name: &str) -> Result<Self, String> {
748 debug!(
749 "V46: Converting QueryResponse to DataTable for table '{}'",
750 table_name
751 );
752
753 crate::utils::memory_tracker::track_memory("start_from_query_response");
755
756 let mut table = DataTable::new(table_name);
757
758 if let Some(first_row) = response.data.first() {
760 if let Some(obj) = first_row.as_object() {
761 for key in obj.keys() {
763 let column = DataColumn::new(key.clone());
764 table.add_column(column);
765 }
766
767 for json_row in &response.data {
769 if let Some(row_obj) = json_row.as_object() {
770 let mut values = Vec::new();
771
772 for column in &table.columns {
774 let value = row_obj
775 .get(&column.name)
776 .map_or(DataValue::Null, json_value_to_data_value);
777 values.push(value);
778 }
779
780 table.add_row(DataRow::new(values))?;
781 }
782 }
783
784 table.infer_column_types();
786
787 if let Some(source) = &response.source {
789 table.metadata.insert("source".to_string(), source.clone());
790 }
791 if let Some(cached) = response.cached {
792 table
793 .metadata
794 .insert("cached".to_string(), cached.to_string());
795 }
796 table
797 .metadata
798 .insert("original_count".to_string(), response.count.to_string());
799
800 debug!(
801 "V46: Created DataTable with {} columns and {} rows",
802 table.column_count(),
803 table.row_count()
804 );
805 } else {
806 table.add_column(DataColumn::new("value"));
808 for json_value in &response.data {
809 let value = json_value_to_data_value(json_value);
810 table.add_row(DataRow::new(vec![value]))?;
811 }
812 }
813 }
814
815 Ok(table)
816 }
817
818 #[must_use]
820 pub fn get_row(&self, index: usize) -> Option<&DataRow> {
821 self.rows.get(index)
822 }
823
824 #[must_use]
826 pub fn get_row_as_strings(&self, index: usize) -> Option<Vec<String>> {
827 self.rows.get(index).map(|row| {
828 row.values
829 .iter()
830 .map(DataValue::to_string_optimized)
831 .collect()
832 })
833 }
834
835 #[must_use]
837 pub fn pretty_print(&self) -> String {
838 let mut output = String::new();
839
840 output.push_str("╔═══════════════════════════════════════════════════════╗\n");
842 output.push_str(&format!("║ DataTable: {:^41} ║\n", self.name));
843 output.push_str("╠═══════════════════════════════════════════════════════╣\n");
844
845 output.push_str(&format!(
847 "║ Rows: {:6} | Columns: {:3} | Memory: ~{:6} bytes ║\n",
848 self.row_count(),
849 self.column_count(),
850 self.get_stats().memory_size
851 ));
852
853 if !self.metadata.is_empty() {
855 output.push_str("╠═══════════════════════════════════════════════════════╣\n");
856 output.push_str("║ Metadata: ║\n");
857 for (key, value) in &self.metadata {
858 let truncated_value = if value.len() > 35 {
859 format!("{}...", &value[..32])
860 } else {
861 value.clone()
862 };
863 output.push_str(&format!(
864 "║ {:15} : {:35} ║\n",
865 Self::truncate_string(key, 15),
866 truncated_value
867 ));
868 }
869 }
870
871 output.push_str("╠═══════════════════════════════════════════════════════╣\n");
873 output.push_str("║ Columns: ║\n");
874 output.push_str("╟───────────────────┬──────────┬─────────┬──────┬──────╢\n");
875 output.push_str("║ Name │ Type │ Nullable│ Nulls│Unique║\n");
876 output.push_str("╟───────────────────┼──────────┼─────────┼──────┼──────╢\n");
877
878 for column in &self.columns {
879 let type_str = match &column.data_type {
880 DataType::String => "String",
881 DataType::Integer => "Integer",
882 DataType::Float => "Float",
883 DataType::Boolean => "Boolean",
884 DataType::DateTime => "DateTime",
885 DataType::Null => "Null",
886 DataType::Mixed => "Mixed",
887 };
888
889 output.push_str(&format!(
890 "║ {:17} │ {:8} │ {:7} │ {:4} │ {:4} ║\n",
891 Self::truncate_string(&column.name, 17),
892 type_str,
893 if column.nullable { "Yes" } else { "No" },
894 column.null_count,
895 column.unique_values.unwrap_or(0)
896 ));
897 }
898
899 output.push_str("╚═══════════════════════════════════════════════════════╝\n");
900
901 output.push_str("\nSample Data (first 5 rows):\n");
903 let sample_count = self.rows.len().min(5);
904
905 if sample_count > 0 {
906 output.push('┌');
908 for (i, _col) in self.columns.iter().enumerate() {
909 if i > 0 {
910 output.push('┬');
911 }
912 output.push_str(&"─".repeat(20));
913 }
914 output.push_str("┐\n");
915
916 output.push('│');
917 for col in &self.columns {
918 output.push_str(&format!(" {:^18} │", Self::truncate_string(&col.name, 18)));
919 }
920 output.push('\n');
921
922 output.push('├');
923 for (i, _) in self.columns.iter().enumerate() {
924 if i > 0 {
925 output.push('┼');
926 }
927 output.push_str(&"─".repeat(20));
928 }
929 output.push_str("┤\n");
930
931 for row_idx in 0..sample_count {
933 if let Some(row) = self.rows.get(row_idx) {
934 output.push('│');
935 for value in &row.values {
936 let value_str = value.to_string();
937 output
938 .push_str(&format!(" {:18} │", Self::truncate_string(&value_str, 18)));
939 }
940 output.push('\n');
941 }
942 }
943
944 output.push('└');
945 for (i, _) in self.columns.iter().enumerate() {
946 if i > 0 {
947 output.push('┴');
948 }
949 output.push_str(&"─".repeat(20));
950 }
951 output.push_str("┘\n");
952 }
953
954 output
955 }
956
957 fn truncate_string(s: &str, max_len: usize) -> String {
958 if s.len() > max_len {
959 format!("{}...", &s[..max_len - 3])
960 } else {
961 s.to_string()
962 }
963 }
964
965 #[must_use]
967 pub fn get_schema_summary(&self) -> String {
968 let mut summary = String::new();
969 summary.push_str(&format!(
970 "DataTable Schema ({} columns, {} rows):\n",
971 self.columns.len(),
972 self.rows.len()
973 ));
974
975 for (idx, column) in self.columns.iter().enumerate() {
976 let type_str = match &column.data_type {
977 DataType::String => "String",
978 DataType::Integer => "Integer",
979 DataType::Float => "Float",
980 DataType::Boolean => "Boolean",
981 DataType::DateTime => "DateTime",
982 DataType::Null => "Null",
983 DataType::Mixed => "Mixed",
984 };
985
986 let nullable_str = if column.nullable {
987 "nullable"
988 } else {
989 "not null"
990 };
991 let null_info = if column.null_count > 0 {
992 format!(", {} nulls", column.null_count)
993 } else {
994 String::new()
995 };
996
997 summary.push_str(&format!(
998 " [{:3}] {} : {} ({}{})\n",
999 idx, column.name, type_str, nullable_str, null_info
1000 ));
1001 }
1002
1003 summary
1004 }
1005
1006 #[must_use]
1008 pub fn get_schema_info(&self) -> Vec<(String, String, bool, usize)> {
1009 self.columns
1010 .iter()
1011 .map(|col| {
1012 let type_name = format!("{:?}", col.data_type);
1013 (col.name.clone(), type_name, col.nullable, col.null_count)
1014 })
1015 .collect()
1016 }
1017
1018 pub fn reserve_rows(&mut self, additional: usize) {
1020 self.rows.reserve(additional);
1021 }
1022
1023 pub fn shrink_to_fit(&mut self) {
1025 self.rows.shrink_to_fit();
1026 for _column in &mut self.columns {
1027 }
1029 }
1030
1031 #[must_use]
1033 pub fn get_memory_usage(&self) -> usize {
1034 let mut size = std::mem::size_of::<Self>();
1035
1036 size += self.name.capacity();
1038
1039 size += self.columns.capacity() * std::mem::size_of::<DataColumn>();
1041 for col in &self.columns {
1042 size += col.name.capacity();
1043 }
1044
1045 size += self.rows.capacity() * std::mem::size_of::<DataRow>();
1047
1048 for row in &self.rows {
1050 size += row.values.capacity() * std::mem::size_of::<DataValue>();
1051 for value in &row.values {
1052 match value {
1053 DataValue::String(s) => size += s.capacity(),
1054 DataValue::InternedString(_) => size += std::mem::size_of::<Arc<String>>(),
1055 DataValue::DateTime(s) => size += s.capacity(),
1056 DataValue::Vector(v) => size += v.capacity() * std::mem::size_of::<f64>(),
1057 _ => {} }
1059 }
1060 }
1061
1062 size += self.metadata.capacity() * std::mem::size_of::<(String, String)>();
1064 for (k, v) in &self.metadata {
1065 size += k.capacity() + v.capacity();
1066 }
1067
1068 size
1069 }
1070
1071 pub fn to_parquet_bytes(&self) -> Result<Vec<u8>, String> {
1073 rmp_serde::to_vec(self).map_err(|e| format!("Failed to serialize DataTable: {}", e))
1076 }
1077
1078 pub fn from_parquet_bytes(bytes: &[u8]) -> Result<Self, String> {
1080 rmp_serde::from_slice(bytes).map_err(|e| format!("Failed to deserialize DataTable: {}", e))
1083 }
1084}
1085
1086fn json_value_to_data_value(json: &JsonValue) -> DataValue {
1088 match json {
1089 JsonValue::Null => DataValue::Null,
1090 JsonValue::Bool(b) => DataValue::Boolean(*b),
1091 JsonValue::Number(n) => {
1092 if let Some(i) = n.as_i64() {
1093 DataValue::Integer(i)
1094 } else if let Some(f) = n.as_f64() {
1095 DataValue::Float(f)
1096 } else {
1097 DataValue::String(n.to_string())
1098 }
1099 }
1100 JsonValue::String(s) => {
1101 if s.contains('-') && s.len() >= 8 && s.len() <= 30 {
1103 DataValue::DateTime(s.clone())
1105 } else {
1106 DataValue::String(s.clone())
1107 }
1108 }
1109 JsonValue::Array(_) | JsonValue::Object(_) => {
1110 DataValue::String(json.to_string())
1112 }
1113 }
1114}
1115
1116#[derive(Debug, Clone)]
1118pub struct DataTableStats {
1119 pub row_count: usize,
1120 pub column_count: usize,
1121 pub memory_size: usize,
1122 pub null_count: usize,
1123}
1124
1125impl DataProvider for DataTable {
1128 fn get_row(&self, index: usize) -> Option<Vec<String>> {
1129 self.rows.get(index).map(|row| {
1130 row.values
1131 .iter()
1132 .map(DataValue::to_string_optimized)
1133 .collect()
1134 })
1135 }
1136
1137 fn get_column_names(&self) -> Vec<String> {
1138 self.column_names()
1139 }
1140
1141 fn get_row_count(&self) -> usize {
1142 self.row_count()
1143 }
1144
1145 fn get_column_count(&self) -> usize {
1146 self.column_count()
1147 }
1148}
1149
1150#[cfg(test)]
1151mod tests {
1152 use super::*;
1153
1154 #[test]
1155 fn test_data_type_inference() {
1156 assert_eq!(DataType::infer_from_string("123"), DataType::Integer);
1157 assert_eq!(DataType::infer_from_string("123.45"), DataType::Float);
1158 assert_eq!(DataType::infer_from_string("true"), DataType::Boolean);
1159 assert_eq!(DataType::infer_from_string("hello"), DataType::String);
1160 assert_eq!(DataType::infer_from_string(""), DataType::Null);
1161 assert_eq!(
1162 DataType::infer_from_string("2024-01-01"),
1163 DataType::DateTime
1164 );
1165 }
1166
1167 #[test]
1168 fn test_datatable_creation() {
1169 let mut table = DataTable::new("test");
1170
1171 table.add_column(DataColumn::new("id").with_type(DataType::Integer));
1172 table.add_column(DataColumn::new("name").with_type(DataType::String));
1173 table.add_column(DataColumn::new("active").with_type(DataType::Boolean));
1174
1175 assert_eq!(table.column_count(), 3);
1176 assert_eq!(table.row_count(), 0);
1177
1178 let row = DataRow::new(vec![
1179 DataValue::Integer(1),
1180 DataValue::String("Alice".to_string()),
1181 DataValue::Boolean(true),
1182 ]);
1183
1184 table.add_row(row).unwrap();
1185 assert_eq!(table.row_count(), 1);
1186
1187 let value = table.get_value_by_name(0, "name").unwrap();
1188 assert_eq!(value.to_string(), "Alice");
1189 }
1190
1191 #[test]
1192 fn test_type_inference() {
1193 let mut table = DataTable::new("test");
1194
1195 table.add_column(DataColumn::new("mixed"));
1197
1198 table
1200 .add_row(DataRow::new(vec![DataValue::Integer(1)]))
1201 .unwrap();
1202 table
1203 .add_row(DataRow::new(vec![DataValue::Float(2.5)]))
1204 .unwrap();
1205 table.add_row(DataRow::new(vec![DataValue::Null])).unwrap();
1206
1207 table.infer_column_types();
1208
1209 assert_eq!(table.columns[0].data_type, DataType::Float);
1211 assert_eq!(table.columns[0].null_count, 1);
1212 assert!(table.columns[0].nullable);
1213 }
1214
1215 #[test]
1216 fn test_from_query_response() {
1217 use crate::api_client::{QueryInfo, QueryResponse};
1218 use serde_json::json;
1219
1220 let response = QueryResponse {
1221 query: QueryInfo {
1222 select: vec!["id".to_string(), "name".to_string(), "age".to_string()],
1223 where_clause: None,
1224 order_by: None,
1225 },
1226 data: vec![
1227 json!({
1228 "id": 1,
1229 "name": "Alice",
1230 "age": 30
1231 }),
1232 json!({
1233 "id": 2,
1234 "name": "Bob",
1235 "age": 25
1236 }),
1237 json!({
1238 "id": 3,
1239 "name": "Carol",
1240 "age": null
1241 }),
1242 ],
1243 count: 3,
1244 source: Some("test.csv".to_string()),
1245 table: Some("test".to_string()),
1246 cached: Some(false),
1247 };
1248
1249 let table = DataTable::from_query_response(&response, "test").unwrap();
1250
1251 assert_eq!(table.name, "test");
1252 assert_eq!(table.row_count(), 3);
1253 assert_eq!(table.column_count(), 3);
1254
1255 let col_names = table.column_names();
1257 assert!(col_names.contains(&"id".to_string()));
1258 assert!(col_names.contains(&"name".to_string()));
1259 assert!(col_names.contains(&"age".to_string()));
1260
1261 assert_eq!(table.metadata.get("source"), Some(&"test.csv".to_string()));
1263 assert_eq!(table.metadata.get("cached"), Some(&"false".to_string()));
1264
1265 assert_eq!(
1267 table.get_value_by_name(0, "id"),
1268 Some(&DataValue::Integer(1))
1269 );
1270 assert_eq!(
1271 table.get_value_by_name(0, "name"),
1272 Some(&DataValue::String("Alice".to_string()))
1273 );
1274 assert_eq!(
1275 table.get_value_by_name(0, "age"),
1276 Some(&DataValue::Integer(30))
1277 );
1278
1279 assert_eq!(table.get_value_by_name(2, "age"), Some(&DataValue::Null));
1281 }
1282}