1use crate::{schema::CqlType, RowKey, Value};
7pub use crate::types::{CellExpiration, CellWriteMetadata};
10use base64::Engine;
11use serde::{Deserialize, Serialize};
12use serde_json::json;
13use std::collections::HashMap;
14use std::fmt;
15use std::sync::Arc;
16use tokio::sync::mpsc;
17
18fn b64(bytes: &[u8]) -> String {
20 base64::engine::general_purpose::STANDARD.encode(bytes)
21}
22
23fn row_metadata_is_populated(meta: &RowMetadata) -> bool {
25 meta.version.is_some() || meta.ttl.is_some() || !meta.tags.is_empty()
26}
27
28#[derive(Debug, Clone, Default)]
42pub struct ProjectionFlags {
43 pub include_cell_metadata: bool,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct QueryResult {
55 pub rows: Vec<QueryRow>,
57 pub rows_affected: u64,
59 pub execution_time_ms: u64,
61 pub metadata: QueryMetadata,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct QueryRow {
68 pub values: HashMap<Arc<str>, Value>,
78 pub key: RowKey,
80 pub metadata: RowMetadata,
82 #[serde(skip_serializing_if = "Option::is_none", default)]
93 pub cell_metadata: Option<HashMap<String, CellWriteMetadata>>,
94}
95
96#[derive(Debug, Clone, Default, Serialize, Deserialize)]
98pub struct QueryMetadata {
99 pub columns: Vec<ColumnInfo>,
101 pub total_rows: Option<u64>,
103 pub plan_info: Option<PlanInfo>,
105 pub performance: PerformanceMetrics,
107 pub warnings: Vec<String>,
109 #[serde(skip_serializing_if = "Option::is_none", default)]
117 pub access_path: Option<crate::query::access_path::AccessPath>,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct ColumnInfo {
123 pub name: String,
125 pub data_type: crate::types::DataType,
127 pub nullable: bool,
129 pub position: usize,
131 pub table_name: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
142 pub cql_type: Option<CqlType>,
143}
144
145#[derive(Debug, Clone, Default, Serialize, Deserialize)]
147pub struct RowMetadata {
148 pub version: Option<u64>,
150 pub ttl: Option<u64>,
152 pub tags: HashMap<String, String>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct PlanInfo {
159 pub plan_type: String,
161 pub estimated_cost: f64,
163 pub actual_cost: f64,
165 pub indexes_used: Vec<String>,
167 pub steps: Vec<String>,
169 pub parallelization: Option<ParallelizationInfo>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ParallelizationInfo {
176 pub threads_used: usize,
178 pub effective: bool,
180 pub partitions: Vec<PartitionInfo>,
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct PartitionInfo {
187 pub id: usize,
189 pub rows_processed: u64,
191 pub processing_time_ms: u64,
193}
194
195#[derive(Debug, Clone, Default, Serialize, Deserialize)]
197pub struct PerformanceMetrics {
198 pub parse_time_us: u64,
200 pub planning_time_us: u64,
202 pub execution_time_us: u64,
204 pub total_time_us: u64,
206 pub memory_usage_bytes: u64,
208 pub io_operations: u64,
210 pub cache_hits: u64,
212 pub cache_misses: u64,
214}
215
216#[derive(Debug, Clone)]
225pub struct StreamingConfig {
226 pub buffer_size: usize,
229 pub chunk_size: usize,
232}
233
234impl Default for StreamingConfig {
235 fn default() -> Self {
236 Self {
237 buffer_size: 1024, chunk_size: 10_000, }
240 }
241}
242
243impl StreamingConfig {
244 pub fn new(buffer_size: usize, chunk_size: usize) -> Self {
246 Self {
247 buffer_size,
248 chunk_size,
249 }
250 }
251
252 pub fn for_parquet() -> Self {
254 Self {
255 buffer_size: 1024,
256 chunk_size: 10_000, }
258 }
259
260 pub fn for_text_formats() -> Self {
262 Self {
263 buffer_size: 512,
264 chunk_size: 5_000, }
266 }
267}
268
269pub struct QueryResultIterator {
316 receiver: mpsc::Receiver<Result<QueryRow, crate::Error>>,
318 pub metadata: QueryMetadata,
320 pub total_rows_hint: Option<u64>,
322 rows_received: u64,
324}
325
326impl QueryResultIterator {
327 pub fn new(
329 receiver: mpsc::Receiver<Result<QueryRow, crate::Error>>,
330 metadata: QueryMetadata,
331 ) -> Self {
332 Self {
333 receiver,
334 metadata,
335 total_rows_hint: None,
336 rows_received: 0,
337 }
338 }
339
340 pub fn with_total_hint(mut self, total: u64) -> Self {
342 self.total_rows_hint = Some(total);
343 self
344 }
345
346 pub async fn next_async(&mut self) -> Option<Result<QueryRow, crate::Error>> {
350 let result = self.receiver.recv().await?;
351 if result.is_ok() {
352 self.rows_received += 1;
353 }
354 Some(result)
355 }
356
357 const MAX_CHUNK_SIZE: usize = 100_000;
359
360 pub async fn collect_chunk(&mut self, size: usize) -> Result<Vec<QueryRow>, crate::Error> {
375 let safe_size = size.min(Self::MAX_CHUNK_SIZE);
376 let mut chunk = Vec::new();
378 while chunk.len() < safe_size {
379 match self.receiver.recv().await {
380 Some(Ok(row)) => {
381 self.rows_received += 1;
382 chunk.push(row);
383 }
384 Some(Err(e)) => return Err(e),
385 None => break,
386 }
387 }
388 Ok(chunk)
389 }
390
391 pub fn rows_received(&self) -> u64 {
393 self.rows_received
394 }
395
396 pub fn progress_percent(&self) -> Option<f64> {
398 self.total_rows_hint.map(|total| {
399 if total == 0 {
400 100.0
401 } else {
402 (self.rows_received as f64 / total as f64) * 100.0
403 }
404 })
405 }
406}
407
408impl QueryResult {
409 pub fn new() -> Self {
411 Self {
412 rows: Vec::new(),
413 rows_affected: 0,
414 execution_time_ms: 0,
415 metadata: QueryMetadata::default(),
416 }
417 }
418
419 pub fn with_rows(rows: Vec<QueryRow>) -> Self {
421 Self {
422 rows,
423 ..Self::new()
424 }
425 }
426
427 pub fn with_affected_rows(rows_affected: u64) -> Self {
429 Self {
430 rows_affected,
431 ..Self::new()
432 }
433 }
434
435 pub fn row_count(&self) -> usize {
437 self.rows.len()
438 }
439
440 pub fn is_empty(&self) -> bool {
442 self.rows.is_empty()
443 }
444
445 pub fn get_row(&self, index: usize) -> Option<&QueryRow> {
447 self.rows.get(index)
448 }
449
450 pub fn columns(&self) -> &[ColumnInfo] {
452 &self.metadata.columns
453 }
454
455 pub fn column_names(&self) -> Vec<String> {
457 self.metadata
458 .columns
459 .iter()
460 .map(|c| c.name.clone())
461 .collect()
462 }
463
464 pub fn execution_time(&self) -> u64 {
466 self.execution_time_ms
467 }
468
469 pub fn performance(&self) -> &PerformanceMetrics {
471 &self.metadata.performance
472 }
473
474 pub fn warnings(&self) -> &[String] {
476 &self.metadata.warnings
477 }
478
479 pub fn add_warning(&mut self, warning: String) {
481 self.metadata.warnings.push(warning);
482 }
483
484 pub fn to_json(&self) -> serde_json::Value {
489 let rows: Vec<_> = self
490 .rows
491 .iter()
492 .map(|row| self.row_to_json_deterministic(row))
493 .collect();
494 let columns: Vec<_> = self
495 .metadata
496 .columns
497 .iter()
498 .map(ColumnInfo::to_json)
499 .collect();
500 let warnings: Vec<_> = self
501 .metadata
502 .warnings
503 .iter()
504 .cloned()
505 .map(serde_json::Value::String)
506 .collect();
507
508 json!({
509 "rows": rows,
510 "rows_affected": self.rows_affected,
511 "row_count": self.rows.len(),
512 "columns": columns,
513 "performance": self.metadata.performance.to_json(),
514 "warnings": warnings,
515 })
516 }
517
518 pub fn iter(&self) -> std::slice::Iter<'_, QueryRow> {
520 self.rows.iter()
521 }
522
523 fn row_to_json_deterministic(&self, row: &QueryRow) -> serde_json::Value {
528 let mut result = serde_json::Map::new();
529
530 if !self.metadata.columns.is_empty() {
531 for col in &self.metadata.columns {
532 let value_json = row
533 .values
534 .get(col.name.as_str())
535 .map_or(serde_json::Value::Null, ToJson::to_json);
536 result.insert(col.name.clone(), value_json);
537 }
538 } else {
539 let mut sorted_keys: Vec<&Arc<str>> = row.values.keys().collect();
540 sorted_keys.sort();
541 for key in sorted_keys {
542 if let Some(value) = row.values.get(key.as_ref()) {
543 result.insert(key.to_string(), value.to_json());
544 }
545 }
546 }
547
548 result.insert(
549 "_key".to_string(),
550 serde_json::Value::String(format!("{:?}", row.key)),
551 );
552
553 if row_metadata_is_populated(&row.metadata) {
554 result.insert("_metadata".to_string(), row.metadata.to_json());
555 }
556
557 serde_json::Value::Object(result)
558 }
559}
560
561impl QueryRow {
562 pub fn new(key: RowKey) -> Self {
564 Self {
565 values: HashMap::new(),
566 key,
567 metadata: RowMetadata::default(),
568 cell_metadata: None,
569 }
570 }
571
572 pub fn with_values(key: RowKey, values: HashMap<String, Value>) -> Self {
581 Self {
582 values: values.into_iter().map(|(k, v)| (Arc::from(k), v)).collect(),
583 key,
584 metadata: RowMetadata::default(),
585 cell_metadata: None,
586 }
587 }
588
589 pub fn with_interned_values(key: RowKey, values: HashMap<Arc<str>, Value>) -> Self {
596 Self {
597 values,
598 key,
599 metadata: RowMetadata::default(),
600 cell_metadata: None,
601 }
602 }
603
604 pub fn from_map(values: HashMap<String, Value>) -> Self {
611 Self {
612 values: values.into_iter().map(|(k, v)| (Arc::from(k), v)).collect(),
613 key: RowKey::new(vec![]),
614 metadata: RowMetadata::default(),
615 cell_metadata: None,
616 }
617 }
618
619 pub fn get(&self, column: &str) -> Option<&Value> {
621 self.values.get(column)
622 }
623
624 pub fn set(&mut self, column: impl Into<Arc<str>>, value: Value) {
629 self.values.insert(column.into(), value);
630 }
631
632 pub fn column_names(&self) -> Vec<String> {
634 self.values.keys().map(|k| k.to_string()).collect()
635 }
636
637 pub fn key(&self) -> &RowKey {
639 &self.key
640 }
641
642 pub fn metadata(&self) -> &RowMetadata {
644 &self.metadata
645 }
646
647 pub fn set_metadata(&mut self, metadata: RowMetadata) {
649 self.metadata = metadata;
650 }
651
652 pub fn set_cell_metadata(&mut self, map: HashMap<String, CellWriteMetadata>) {
659 self.cell_metadata = Some(map);
660 }
661
662 pub fn insert_cell_metadata(&mut self, column: String, meta: CellWriteMetadata) {
668 self.cell_metadata
669 .get_or_insert_with(HashMap::new)
670 .insert(column, meta);
671 }
672
673 pub fn get_cell_metadata(&self, column: &str) -> Option<&CellWriteMetadata> {
675 self.cell_metadata.as_ref()?.get(column)
676 }
677
678 pub fn to_json(&self) -> serde_json::Value {
680 let mut result = serde_json::Map::new();
681
682 for (column, value) in &self.values {
683 result.insert(column.to_string(), value.to_json());
684 }
685
686 result.insert(
687 "_key".to_string(),
688 serde_json::Value::String(format!("{:?}", self.key)),
689 );
690
691 if row_metadata_is_populated(&self.metadata) {
692 result.insert("_metadata".to_string(), self.metadata.to_json());
693 }
694
695 serde_json::Value::Object(result)
696 }
697}
698
699impl ColumnInfo {
700 pub fn new(
702 name: String,
703 data_type: crate::types::DataType,
704 nullable: bool,
705 position: usize,
706 ) -> Self {
707 Self {
708 name,
709 data_type,
710 nullable,
711 position,
712 table_name: None,
713 cql_type: None,
714 }
715 }
716
717 pub fn with_table_name(mut self, table_name: String) -> Self {
719 self.table_name = Some(table_name);
720 self
721 }
722
723 pub fn with_cql_type(mut self, cql_type: CqlType) -> Self {
729 self.cql_type = Some(cql_type);
730 self
731 }
732
733 pub fn to_json(&self) -> serde_json::Value {
740 let mut map = serde_json::Map::new();
741 map.insert("name".to_string(), json!(self.name));
742 map.insert(
743 "data_type".to_string(),
744 json!(format!("{:?}", self.data_type)),
745 );
746 map.insert("nullable".to_string(), json!(self.nullable));
747 map.insert("position".to_string(), json!(self.position));
748 if let Some(table_name) = &self.table_name {
749 map.insert("table_name".to_string(), json!(table_name));
750 }
751 if let Some(cql_type) = &self.cql_type {
753 map.insert("cql_type".to_string(), json!(format_cql_type(cql_type)));
754 }
755 serde_json::Value::Object(map)
756 }
757}
758
759pub fn cql_type_to_data_type(cql_type: &CqlType) -> crate::types::DataType {
765 use crate::types::DataType;
766 match cql_type {
767 CqlType::Boolean => DataType::Boolean,
768 CqlType::TinyInt => DataType::TinyInt,
769 CqlType::SmallInt => DataType::SmallInt,
770 CqlType::Int => DataType::Integer,
771 CqlType::BigInt | CqlType::Varint | CqlType::Counter => DataType::BigInt,
772 CqlType::Float => DataType::Float32,
773 CqlType::Double | CqlType::Decimal => DataType::Float,
774 CqlType::Text | CqlType::Varchar | CqlType::Ascii => DataType::Text,
775 CqlType::Blob => DataType::Blob,
776 CqlType::Timestamp => DataType::Timestamp,
777 CqlType::Date | CqlType::Time | CqlType::Duration | CqlType::Inet => DataType::BigInt,
778 CqlType::Uuid | CqlType::TimeUuid => DataType::Uuid,
779 CqlType::List(_) => DataType::List,
780 CqlType::Set(_) => DataType::Set,
781 CqlType::Map(_, _) => DataType::Map,
782 CqlType::Tuple(_) => DataType::Tuple,
783 CqlType::Udt(_, _) => DataType::Udt,
784 CqlType::Frozen(inner) => cql_type_to_data_type(inner),
785 CqlType::Custom(_) => DataType::Blob,
786 }
787}
788
789fn format_cql_type(cql_type: &CqlType) -> String {
793 match cql_type {
794 CqlType::Boolean => "boolean".to_string(),
795 CqlType::TinyInt => "tinyint".to_string(),
796 CqlType::SmallInt => "smallint".to_string(),
797 CqlType::Int => "int".to_string(),
798 CqlType::BigInt => "bigint".to_string(),
799 CqlType::Counter => "counter".to_string(),
800 CqlType::Float => "float".to_string(),
801 CqlType::Double => "double".to_string(),
802 CqlType::Decimal => "decimal".to_string(),
803 CqlType::Text => "text".to_string(),
804 CqlType::Varchar => "varchar".to_string(),
805 CqlType::Ascii => "ascii".to_string(),
806 CqlType::Blob => "blob".to_string(),
807 CqlType::Timestamp => "timestamp".to_string(),
808 CqlType::Date => "date".to_string(),
809 CqlType::Time => "time".to_string(),
810 CqlType::Uuid => "uuid".to_string(),
811 CqlType::TimeUuid => "timeuuid".to_string(),
812 CqlType::Inet => "inet".to_string(),
813 CqlType::Duration => "duration".to_string(),
814 CqlType::Varint => "varint".to_string(),
815 CqlType::List(inner) => format!("list<{}>", format_cql_type(inner)),
816 CqlType::Set(inner) => format!("set<{}>", format_cql_type(inner)),
817 CqlType::Map(k, v) => format!("map<{}, {}>", format_cql_type(k), format_cql_type(v)),
818 CqlType::Tuple(types) => {
819 let inner: Vec<_> = types.iter().map(format_cql_type).collect();
820 format!("tuple<{}>", inner.join(", "))
821 }
822 CqlType::Udt(name, _) => name.clone(),
823 CqlType::Frozen(inner) => format!("frozen<{}>", format_cql_type(inner)),
824 CqlType::Custom(name) => name.clone(),
825 }
826}
827
828impl RowMetadata {
829 pub fn new() -> Self {
831 Self::default()
832 }
833
834 pub fn with_version(mut self, version: u64) -> Self {
836 self.version = Some(version);
837 self
838 }
839
840 pub fn with_ttl(mut self, ttl: u64) -> Self {
842 self.ttl = Some(ttl);
843 self
844 }
845
846 pub fn with_tag(mut self, key: String, value: String) -> Self {
848 self.tags.insert(key, value);
849 self
850 }
851
852 pub fn to_json(&self) -> serde_json::Value {
854 let mut map = serde_json::Map::new();
855 if let Some(version) = self.version {
856 map.insert("version".to_string(), json!(version));
857 }
858 if let Some(ttl) = self.ttl {
859 map.insert("ttl".to_string(), json!(ttl));
860 }
861 if !self.tags.is_empty() {
862 map.insert("tags".to_string(), json!(self.tags));
863 }
864 serde_json::Value::Object(map)
865 }
866}
867
868impl PerformanceMetrics {
869 pub fn new() -> Self {
871 Self::default()
872 }
873
874 pub fn total_time_ms(&self) -> u64 {
876 self.total_time_us / 1000
877 }
878
879 pub fn cache_hit_ratio(&self) -> f64 {
881 let total = self.cache_hits + self.cache_misses;
882 if total == 0 {
883 0.0
884 } else {
885 self.cache_hits as f64 / total as f64
886 }
887 }
888
889 pub fn to_json(&self) -> serde_json::Value {
891 let cache_hit_ratio = serde_json::Number::from_f64(self.cache_hit_ratio())
892 .map(serde_json::Value::Number)
893 .unwrap_or(json!(0));
894 json!({
895 "parse_time_us": self.parse_time_us,
896 "planning_time_us": self.planning_time_us,
897 "execution_time_us": self.execution_time_us,
898 "total_time_us": self.total_time_us,
899 "memory_usage_bytes": self.memory_usage_bytes,
900 "io_operations": self.io_operations,
901 "cache_hits": self.cache_hits,
902 "cache_misses": self.cache_misses,
903 "cache_hit_ratio": cache_hit_ratio,
904 })
905 }
906}
907
908fn write_border(
910 f: &mut fmt::Formatter<'_>,
911 widths: &[usize],
912 left: char,
913 sep: char,
914 right: char,
915) -> fmt::Result {
916 write!(f, "{}", left)?;
917 for (i, width) in widths.iter().enumerate() {
918 write!(f, "{}", "─".repeat(width + 2))?;
919 if i < widths.len() - 1 {
920 write!(f, "{}", sep)?;
921 }
922 }
923 writeln!(f, "{}", right)
924}
925
926impl fmt::Display for QueryResult {
927 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928 if self.rows.is_empty() {
929 return write!(f, "Empty result set ({} rows affected)", self.rows_affected);
930 }
931
932 let column_names = self.column_names();
933 if column_names.is_empty() {
934 return write!(f, "No columns in result set");
935 }
936
937 let col_widths: Vec<usize> = column_names
939 .iter()
940 .map(|col_name| {
941 self.rows
942 .iter()
943 .filter_map(|row| row.values.get(col_name.as_str()))
944 .map(|v| format!("{}", v).len())
945 .max()
946 .unwrap_or(0)
947 .max(col_name.len())
948 })
949 .collect();
950
951 write_border(f, &col_widths, '┌', '┬', '┐')?;
952
953 write!(f, "│")?;
954 for (i, (col_name, width)) in column_names.iter().zip(col_widths.iter()).enumerate() {
955 write!(f, " {:width$} ", col_name, width = width)?;
956 if i < column_names.len() - 1 {
957 write!(f, "│")?;
958 }
959 }
960 writeln!(f, "│")?;
961
962 write_border(f, &col_widths, '├', '┼', '┤')?;
963
964 for row in &self.rows {
965 write!(f, "│")?;
966 for (i, (col_name, width)) in column_names.iter().zip(col_widths.iter()).enumerate() {
967 let value = row
968 .values
969 .get(col_name.as_str())
970 .map(|v| format!("{}", v))
971 .unwrap_or_else(|| "NULL".to_string());
972 write!(f, " {:width$} ", value, width = width)?;
973 if i < column_names.len() - 1 {
974 write!(f, "│")?;
975 }
976 }
977 writeln!(f, "│")?;
978 }
979
980 write_border(f, &col_widths, '└', '┴', '┘')?;
981
982 writeln!(
983 f,
984 "{} rows returned in {}ms",
985 self.rows.len(),
986 self.execution_time_ms
987 )?;
988
989 if !self.metadata.warnings.is_empty() {
990 writeln!(f, "\nWarnings:")?;
991 for warning in &self.metadata.warnings {
992 writeln!(f, " - {}", warning)?;
993 }
994 }
995
996 Ok(())
997 }
998}
999
1000impl Default for QueryResult {
1001 fn default() -> Self {
1002 Self::new()
1003 }
1004}
1005
1006impl IntoIterator for QueryResult {
1007 type Item = QueryRow;
1008 type IntoIter = std::vec::IntoIter<QueryRow>;
1009
1010 fn into_iter(self) -> Self::IntoIter {
1011 self.rows.into_iter()
1012 }
1013}
1014
1015impl<'a> IntoIterator for &'a QueryResult {
1016 type Item = &'a QueryRow;
1017 type IntoIter = std::slice::Iter<'a, QueryRow>;
1018
1019 fn into_iter(self) -> Self::IntoIter {
1020 self.rows.iter()
1021 }
1022}
1023
1024trait ToJson {
1026 fn to_json(&self) -> serde_json::Value;
1027}
1028
1029impl ToJson for Value {
1030 fn to_json(&self) -> serde_json::Value {
1031 fn float_to_json(x: f64) -> serde_json::Value {
1033 serde_json::Number::from_f64(x)
1034 .map(serde_json::Value::Number)
1035 .unwrap_or(serde_json::Value::Null)
1036 }
1037
1038 match self {
1039 Value::Null => serde_json::Value::Null,
1040 Value::Boolean(b) => json!(*b),
1041 Value::Integer(i) => json!(*i),
1042 Value::BigInt(i) => json!(*i),
1043 Value::Counter(c) => json!(*c),
1044 Value::TinyInt(i) => json!(*i as i64),
1045 Value::SmallInt(i) => json!(*i as i64),
1046 Value::Date(d) => json!(*d),
1047 Value::Time(t) => json!(*t),
1048 Value::Timestamp(ts) => json!(*ts),
1049 Value::Float(f) => float_to_json(*f),
1050 Value::Float32(f) => float_to_json(*f as f64),
1051 Value::Text(s) => json!(std::str::from_utf8(s).unwrap_or_default()),
1052 Value::Json(value) => (**value).clone(),
1053 Value::Blob(bytes) | Value::Varint(bytes) | Value::Inet(bytes) => json!(b64(bytes)),
1054 Value::Uuid(uuid) => json!(b64(uuid)),
1055 Value::List(items) | Value::Set(items) | Value::Tuple(items) => {
1056 let json_list: Vec<_> = items.iter().map(ToJson::to_json).collect();
1057 serde_json::Value::Array(json_list)
1058 }
1059 Value::Map(entries) => {
1060 let json_map: serde_json::Map<String, serde_json::Value> = entries
1061 .iter()
1062 .map(|(k, v)| (format!("{}", k), v.to_json()))
1063 .collect();
1064 serde_json::Value::Object(json_map)
1065 }
1066 Value::Udt(udt) => {
1067 let mut json_obj = serde_json::Map::new();
1068 json_obj.insert("_type".to_string(), json!(udt.type_name));
1069 for field in &udt.fields {
1070 let field_json = field
1071 .value
1072 .as_ref()
1073 .map_or(serde_json::Value::Null, ToJson::to_json);
1074 json_obj.insert(field.name.clone(), field_json);
1075 }
1076 serde_json::Value::Object(json_obj)
1077 }
1078 Value::Frozen(boxed) => boxed.to_json(),
1079 Value::Decimal { scale, unscaled } => json!({
1080 "scale": *scale,
1081 "unscaled": b64(unscaled),
1082 }),
1083 Value::Duration {
1084 months,
1085 days,
1086 nanos,
1087 } => json!({
1088 "months": *months,
1089 "days": *days,
1090 "nanos": *nanos,
1091 }),
1092 Value::Tombstone(info) => {
1093 let mut json_obj = serde_json::Map::new();
1094 json_obj.insert("type".to_string(), json!("tombstone"));
1095 json_obj.insert("deletion_time".to_string(), json!(info.deletion_time));
1096 json_obj.insert(
1097 "tombstone_type".to_string(),
1098 json!(format!("{:?}", info.tombstone_type)),
1099 );
1100 if let Some(ttl) = info.ttl {
1101 json_obj.insert("ttl".to_string(), json!(ttl));
1102 }
1103 serde_json::Value::Object(json_obj)
1104 }
1105 }
1106 }
1107}
1108
1109#[cfg(test)]
1110mod tests {
1111 use super::*;
1112 use crate::Value;
1113
1114 #[test]
1115 fn test_query_result_creation() {
1116 let result = QueryResult::new();
1117 assert!(result.is_empty());
1118 assert_eq!(result.row_count(), 0);
1119 assert_eq!(result.execution_time(), 0);
1120 }
1121
1122 #[test]
1123 fn test_query_result_with_rows() {
1124 let mut row1 = QueryRow::new(RowKey::new(vec![1]));
1125 row1.set("id".to_string(), Value::Integer(1));
1126 row1.set("name".to_string(), Value::text("Alice".to_string()));
1127
1128 let mut row2 = QueryRow::new(RowKey::new(vec![2]));
1129 row2.set("id".to_string(), Value::Integer(2));
1130 row2.set("name".to_string(), Value::text("Bob".to_string()));
1131
1132 let result = QueryResult::with_rows(vec![row1, row2]);
1133 assert_eq!(result.row_count(), 2);
1134 assert!(!result.is_empty());
1135
1136 let first_row = result.get_row(0).unwrap();
1137 assert_eq!(first_row.get("id"), Some(&Value::Integer(1)));
1138 assert_eq!(
1139 first_row.get("name"),
1140 Some(&Value::text("Alice".to_string()))
1141 );
1142 }
1143
1144 #[test]
1145 fn test_query_row_operations() {
1146 let mut row = QueryRow::new(RowKey::new(vec![1]));
1147 row.set("id".to_string(), Value::Integer(42));
1148 row.set("active".to_string(), Value::Boolean(true));
1149
1150 assert_eq!(row.get("id"), Some(&Value::Integer(42)));
1151 assert_eq!(row.get("active"), Some(&Value::Boolean(true)));
1152 assert_eq!(row.get("nonexistent"), None);
1153
1154 let column_names = row.column_names();
1155 assert_eq!(column_names.len(), 2);
1156 assert!(column_names.contains(&"id".to_string()));
1157 assert!(column_names.contains(&"active".to_string()));
1158 }
1159
1160 #[test]
1161 fn test_column_info() {
1162 let column = ColumnInfo::new(
1163 "user_id".to_string(),
1164 crate::types::DataType::Integer,
1165 false,
1166 0,
1167 )
1168 .with_table_name("users".to_string());
1169
1170 assert_eq!(column.name, "user_id");
1171 assert_eq!(column.data_type, crate::types::DataType::Integer);
1172 assert!(!column.nullable);
1173 assert_eq!(column.position, 0);
1174 assert_eq!(column.table_name, Some("users".to_string()));
1175 assert!(column.cql_type.is_none());
1176 }
1177
1178 #[test]
1179 fn test_column_info_with_cql_type_scalar() {
1180 use crate::schema::CqlType;
1181 let column = ColumnInfo::new("ts".to_string(), crate::types::DataType::Timestamp, true, 1)
1182 .with_cql_type(CqlType::Timestamp);
1183
1184 assert_eq!(column.name, "ts");
1185 assert_eq!(column.cql_type, Some(CqlType::Timestamp));
1186 assert_eq!(column.data_type, crate::types::DataType::Timestamp);
1188 }
1189
1190 #[test]
1191 fn test_column_info_with_cql_type_list() {
1192 use crate::schema::CqlType;
1193 let list_type = CqlType::List(Box::new(CqlType::Int));
1194 let column = ColumnInfo::new("items".to_string(), crate::types::DataType::List, true, 2)
1195 .with_cql_type(list_type.clone());
1196
1197 assert_eq!(column.cql_type, Some(list_type));
1198 }
1199
1200 #[test]
1201 fn test_column_info_with_cql_type_map() {
1202 use crate::schema::CqlType;
1203 let map_type = CqlType::Map(Box::new(CqlType::Text), Box::new(CqlType::BigInt));
1204 let column = ColumnInfo::new("props".to_string(), crate::types::DataType::Map, true, 3)
1205 .with_cql_type(map_type.clone());
1206
1207 assert_eq!(column.cql_type, Some(map_type));
1208 }
1209
1210 #[test]
1211 fn test_column_info_with_cql_type_udt() {
1212 use crate::schema::CqlType;
1213 let udt_type = CqlType::Udt("address".to_string(), vec![]);
1214 let column = ColumnInfo::new("addr".to_string(), crate::types::DataType::Udt, true, 4)
1215 .with_cql_type(udt_type.clone());
1216
1217 assert_eq!(column.cql_type, Some(udt_type));
1218 }
1219
1220 #[test]
1221 fn test_cql_type_to_data_type_scalars() {
1222 use super::cql_type_to_data_type;
1223 use crate::schema::CqlType;
1224 use crate::types::DataType;
1225
1226 assert_eq!(cql_type_to_data_type(&CqlType::Boolean), DataType::Boolean);
1227 assert_eq!(cql_type_to_data_type(&CqlType::Int), DataType::Integer);
1228 assert_eq!(cql_type_to_data_type(&CqlType::BigInt), DataType::BigInt);
1229 assert_eq!(cql_type_to_data_type(&CqlType::Text), DataType::Text);
1230 assert_eq!(cql_type_to_data_type(&CqlType::Blob), DataType::Blob);
1231 assert_eq!(cql_type_to_data_type(&CqlType::Uuid), DataType::Uuid);
1232 assert_eq!(
1233 cql_type_to_data_type(&CqlType::Timestamp),
1234 DataType::Timestamp
1235 );
1236 }
1237
1238 #[test]
1239 fn test_cql_type_to_data_type_collections() {
1240 use super::cql_type_to_data_type;
1241 use crate::schema::CqlType;
1242 use crate::types::DataType;
1243
1244 assert_eq!(
1245 cql_type_to_data_type(&CqlType::List(Box::new(CqlType::Int))),
1246 DataType::List
1247 );
1248 assert_eq!(
1249 cql_type_to_data_type(&CqlType::Set(Box::new(CqlType::Text))),
1250 DataType::Set
1251 );
1252 assert_eq!(
1253 cql_type_to_data_type(&CqlType::Map(
1254 Box::new(CqlType::Text),
1255 Box::new(CqlType::BigInt)
1256 )),
1257 DataType::Map
1258 );
1259 }
1260
1261 #[test]
1262 fn test_cql_type_to_data_type_frozen() {
1263 use super::cql_type_to_data_type;
1264 use crate::schema::CqlType;
1265 use crate::types::DataType;
1266
1267 assert_eq!(
1269 cql_type_to_data_type(&CqlType::Frozen(Box::new(CqlType::List(Box::new(
1270 CqlType::Int
1271 ))))),
1272 DataType::List
1273 );
1274 }
1275
1276 #[test]
1277 fn test_column_info_to_json_includes_cql_type() {
1278 use crate::schema::CqlType;
1279 let column = ColumnInfo::new("items".to_string(), crate::types::DataType::List, true, 0)
1280 .with_cql_type(CqlType::List(Box::new(CqlType::Int)));
1281
1282 let json = column.to_json();
1283 let obj = json.as_object().unwrap();
1284
1285 assert_eq!(obj["name"], "items");
1287 assert!(obj.contains_key("data_type"));
1288 assert!(obj.contains_key("nullable"));
1289 assert!(obj.contains_key("position"));
1290
1291 assert!(obj.contains_key("cql_type"));
1293 assert_eq!(obj["cql_type"], "list<int>");
1294 }
1295
1296 #[test]
1297 fn test_column_info_to_json_no_cql_type() {
1298 let column = ColumnInfo::new("id".to_string(), crate::types::DataType::Integer, false, 0);
1300
1301 let json = column.to_json();
1302 let obj = json.as_object().unwrap();
1303
1304 assert!(!obj.contains_key("cql_type"));
1305 }
1306
1307 #[test]
1308 fn test_row_metadata() {
1309 let metadata = RowMetadata::new()
1310 .with_version(123)
1311 .with_ttl(3600)
1312 .with_tag("source".to_string(), "import".to_string());
1313
1314 assert_eq!(metadata.version, Some(123));
1315 assert_eq!(metadata.ttl, Some(3600));
1316 assert_eq!(metadata.tags.get("source"), Some(&"import".to_string()));
1317 }
1318
1319 #[test]
1320 fn test_performance_metrics() {
1321 let mut metrics = PerformanceMetrics::new();
1322 metrics.cache_hits = 8;
1323 metrics.cache_misses = 2;
1324 metrics.total_time_us = 5000;
1325
1326 assert_eq!(metrics.cache_hit_ratio(), 0.8);
1327 assert_eq!(metrics.total_time_ms(), 5);
1328 }
1329
1330 #[test]
1331 fn test_json_serialization() {
1332 let mut row = QueryRow::new(RowKey::new(vec![1]));
1333 row.set("id".to_string(), Value::Integer(1));
1334 row.set("name".to_string(), Value::text("test".to_string()));
1335
1336 let json = row.to_json();
1337 assert!(json.is_object());
1338
1339 let obj = json.as_object().unwrap();
1340 assert_eq!(obj.get("id"), Some(&serde_json::Value::Number(1.into())));
1341 assert_eq!(
1342 obj.get("name"),
1343 Some(&serde_json::Value::String("test".to_string()))
1344 );
1345 }
1346
1347 #[test]
1348 fn test_result_iteration() {
1349 let row1 = QueryRow::new(RowKey::new(vec![1]));
1350 let row2 = QueryRow::new(RowKey::new(vec![2]));
1351 let result = QueryResult::with_rows(vec![row1, row2]);
1352
1353 let mut count = 0;
1354 for _row in &result {
1355 count += 1;
1356 }
1357 assert_eq!(count, 2);
1358
1359 let mut count = 0;
1360 for _row in result {
1361 count += 1;
1362 }
1363 assert_eq!(count, 2);
1364 }
1365
1366 #[test]
1373 fn test_cell_metadata_absent_by_default() {
1374 let row = QueryRow::new(RowKey::new(vec![1]));
1375 assert!(
1376 row.cell_metadata.is_none(),
1377 "cell_metadata must be None when no metadata is attached (hot-path, zero allocation)"
1378 );
1379
1380 let row2 = QueryRow::with_values(RowKey::new(vec![2]), HashMap::new());
1381 assert!(row2.cell_metadata.is_none());
1382
1383 let row3 = QueryRow::from_map(HashMap::new());
1384 assert!(row3.cell_metadata.is_none());
1385 }
1386
1387 #[test]
1389 fn test_projection_flags_default_no_metadata() {
1390 let flags = ProjectionFlags::default();
1391 assert!(
1392 !flags.include_cell_metadata,
1393 "include_cell_metadata must default to false"
1394 );
1395 }
1396
1397 #[test]
1399 fn test_cell_metadata_single_sstable_single_cell() {
1400 let mut row = QueryRow::new(RowKey::new(vec![1]));
1401 row.set("name".to_string(), Value::text("Alice".to_string()));
1402
1403 let meta = CellWriteMetadata {
1404 write_timestamp_micros: 1_700_000_000_000_000, expiration: None,
1406 };
1407 row.insert_cell_metadata("name".to_string(), meta.clone());
1408
1409 assert!(row.cell_metadata.is_some());
1411 assert_eq!(row.get("name"), Some(&Value::text("Alice".to_string())));
1413 let got = row
1415 .get_cell_metadata("name")
1416 .expect("metadata must be present");
1417 assert_eq!(got.write_timestamp_micros, meta.write_timestamp_micros);
1418 assert!(got.expiration.is_none());
1419 }
1420
1421 #[test]
1423 fn test_cell_metadata_with_ttl_expiration() {
1424 let mut row = QueryRow::new(RowKey::new(vec![2]));
1425 row.set("score".to_string(), Value::Integer(42));
1426
1427 let ttl_seconds = 3600_i32;
1428 let write_ts_micros = 1_700_000_000_000_000_i64;
1429 let expires_at = (write_ts_micros / 1_000_000) + ttl_seconds as i64;
1431
1432 let meta = CellWriteMetadata {
1433 write_timestamp_micros: write_ts_micros,
1434 expiration: Some(CellExpiration {
1435 ttl_seconds,
1436 expires_at_seconds: expires_at,
1437 }),
1438 };
1439 row.insert_cell_metadata("score".to_string(), meta);
1440
1441 let got = row.get_cell_metadata("score").unwrap();
1442 assert_eq!(got.write_timestamp_micros, write_ts_micros);
1443 let exp = got.expiration.as_ref().unwrap();
1444 assert_eq!(exp.ttl_seconds, 3600);
1445 assert_eq!(exp.expires_at_seconds, expires_at);
1446 }
1447
1448 #[test]
1451 fn test_cell_metadata_absent_for_null_cells() {
1452 let mut row = QueryRow::new(RowKey::new(vec![3]));
1453 row.set("id".to_string(), Value::Null);
1454 row.insert_cell_metadata(
1457 "name".to_string(),
1458 CellWriteMetadata {
1459 write_timestamp_micros: 42,
1460 expiration: None,
1461 },
1462 );
1463
1464 assert!(
1465 row.get_cell_metadata("id").is_none(),
1466 "no metadata for null column"
1467 );
1468 assert!(row.get_cell_metadata("name").is_some());
1469 }
1470
1471 #[test]
1481 fn test_cell_metadata_lww_winner_carries_newer_timestamp() {
1482 let mut older_row = QueryRow::new(RowKey::new(b"partition1".to_vec()));
1484 older_row.set("value".to_string(), Value::Integer(10));
1485 older_row.insert_cell_metadata(
1486 "value".to_string(),
1487 CellWriteMetadata {
1488 write_timestamp_micros: 1_000_000,
1489 expiration: None,
1490 },
1491 );
1492
1493 let mut newer_row = QueryRow::new(RowKey::new(b"partition1".to_vec()));
1495 newer_row.set("value".to_string(), Value::Integer(20));
1496 newer_row.insert_cell_metadata(
1497 "value".to_string(),
1498 CellWriteMetadata {
1499 write_timestamp_micros: 2_000_000,
1500 expiration: None,
1501 },
1502 );
1503
1504 let winner = if newer_row
1506 .get_cell_metadata("value")
1507 .map(|m| m.write_timestamp_micros)
1508 .unwrap_or(0)
1509 > older_row
1510 .get_cell_metadata("value")
1511 .map(|m| m.write_timestamp_micros)
1512 .unwrap_or(0)
1513 {
1514 newer_row
1515 } else {
1516 older_row
1517 };
1518
1519 assert_eq!(
1520 winner.get("value"),
1521 Some(&Value::Integer(20)),
1522 "value from the newer SSTable must be present"
1523 );
1524 assert_eq!(
1525 winner
1526 .get_cell_metadata("value")
1527 .map(|m| m.write_timestamp_micros),
1528 Some(2_000_000),
1529 "metadata must reflect the winning (newer) cell's timestamp"
1530 );
1531 }
1532
1533 #[test]
1535 fn test_set_cell_metadata_replaces_map() {
1536 let mut row = QueryRow::new(RowKey::new(vec![4]));
1537 row.insert_cell_metadata(
1538 "a".to_string(),
1539 CellWriteMetadata {
1540 write_timestamp_micros: 1,
1541 expiration: None,
1542 },
1543 );
1544
1545 let mut new_map = HashMap::new();
1546 new_map.insert(
1547 "b".to_string(),
1548 CellWriteMetadata {
1549 write_timestamp_micros: 99,
1550 expiration: None,
1551 },
1552 );
1553 row.set_cell_metadata(new_map);
1554
1555 assert!(row.get_cell_metadata("a").is_none());
1557 assert_eq!(
1558 row.get_cell_metadata("b").map(|m| m.write_timestamp_micros),
1559 Some(99)
1560 );
1561 }
1562
1563 #[test]
1565 fn test_cell_metadata_serde_round_trip() {
1566 let mut row = QueryRow::new(RowKey::new(vec![5]));
1567 row.set("x".to_string(), Value::Integer(7));
1568 row.insert_cell_metadata(
1569 "x".to_string(),
1570 CellWriteMetadata {
1571 write_timestamp_micros: 123_456_789,
1572 expiration: Some(CellExpiration {
1573 ttl_seconds: 60,
1574 expires_at_seconds: 9999,
1575 }),
1576 },
1577 );
1578
1579 let json = serde_json::to_string(&row).expect("serialise");
1580 let back: QueryRow = serde_json::from_str(&json).expect("deserialise");
1581
1582 let meta = back
1583 .get_cell_metadata("x")
1584 .expect("metadata present after round-trip");
1585 assert_eq!(meta.write_timestamp_micros, 123_456_789);
1586 let exp = meta.expiration.as_ref().unwrap();
1587 assert_eq!(exp.ttl_seconds, 60);
1588 assert_eq!(exp.expires_at_seconds, 9999);
1589 }
1590
1591 #[test]
1594 fn test_cell_metadata_none_omitted_from_json() {
1595 let row = QueryRow::new(RowKey::new(vec![6]));
1596 let json = serde_json::to_string(&row).expect("serialise");
1597 assert!(
1598 !json.contains("cell_metadata"),
1599 "cell_metadata must be absent from JSON when None (backward compat)"
1600 );
1601 }
1602
1603 #[test]
1610 fn test_row_values_addressable_by_str_key() {
1611 let mut row = QueryRow::new(RowKey::new(vec![1]));
1612 row.set("id", Value::Integer(7)); row.set("name".to_string(), Value::text("Zoe".to_string())); assert_eq!(row.get("id"), Some(&Value::Integer(7)));
1617 assert_eq!(row.get("name"), Some(&Value::text("Zoe".to_string())));
1618 assert_eq!(row.get("absent"), None);
1619
1620 let key: &Arc<str> = row.values.keys().next().expect("a key");
1622 let _: &str = key; }
1624
1625 #[test]
1629 fn test_query_result_serde_round_trip_preserves_names_and_values() {
1630 let mut row = QueryRow::new(RowKey::new(vec![1]));
1631 row.set("id", Value::Integer(42));
1632 row.set("name", Value::text("Alice".to_string()));
1633 let result = QueryResult::with_rows(vec![row]);
1634
1635 let json = serde_json::to_value(&result).expect("serialise QueryResult");
1641 let values_obj = json["rows"][0]["values"]
1642 .as_object()
1643 .expect("values is a JSON object keyed by column name");
1644 assert!(values_obj.contains_key("id"), "object keyed by column name");
1645 assert!(
1646 values_obj.contains_key("name"),
1647 "object keyed by column name"
1648 );
1649
1650 let back: QueryResult = serde_json::from_value(json).expect("deserialise QueryResult");
1652 let r = &back.rows[0];
1653 assert_eq!(r.get("id"), Some(&Value::Integer(42)));
1654 assert_eq!(r.get("name"), Some(&Value::text("Alice".to_string())));
1655 }
1656}