use std::collections::HashMap;
use crate::query::{ColumnInfo, QueryRow};
use crate::types::Value;
pub(crate) fn transpose_columns<'a>(
columns: &[ColumnInfo],
rows: &'a [QueryRow],
) -> Vec<Vec<Option<&'a Value>>> {
let n_rows = rows.len();
let n_cols = columns.len();
let mut name_to_indices: HashMap<&str, Vec<usize>> = HashMap::with_capacity(n_cols);
for (idx, col) in columns.iter().enumerate() {
name_to_indices
.entry(col.name.as_str())
.or_default()
.push(idx);
}
let mut columnar: Vec<Vec<Option<&'a Value>>> =
(0..n_cols).map(|_| vec![None; n_rows]).collect();
for (row_idx, row) in rows.iter().enumerate() {
for (name, value) in &row.values {
if let Some(col_indices) = name_to_indices.get(name.as_ref()) {
for &col_idx in col_indices {
columnar[col_idx][row_idx] = Some(value);
}
}
}
}
columnar
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query::ColumnInfo;
use crate::types::DataType;
use crate::RowKey;
use std::sync::Arc;
fn col(name: &str) -> ColumnInfo {
ColumnInfo {
name: name.to_string(),
data_type: DataType::Integer,
nullable: true,
position: 0,
table_name: None,
cql_type: None,
}
}
fn row(pairs: &[(&str, Value)]) -> QueryRow {
let mut values: HashMap<Arc<str>, Value> = HashMap::new();
for (name, value) in pairs {
values.insert(Arc::from(*name), value.clone());
}
QueryRow::with_interned_values(RowKey::new(Vec::new()), values)
}
#[test]
fn transpose_aligns_columns_and_rows() {
let columns = vec![col("a"), col("b"), col("c")];
let rows = vec![
row(&[
("a", Value::Integer(1)),
("b", Value::Integer(2)),
("c", Value::Integer(3)),
]),
row(&[("a", Value::Null), ("c", Value::Integer(30))]),
];
let cols = transpose_columns(&columns, &rows);
assert_eq!(cols.len(), 3);
for slice in &cols {
assert_eq!(slice.len(), 2);
}
assert_eq!(cols[0][0], Some(&Value::Integer(1)));
assert_eq!(cols[0][1], Some(&Value::Null));
assert_eq!(cols[1][0], Some(&Value::Integer(2)));
assert_eq!(cols[1][1], None);
assert_eq!(cols[2][0], Some(&Value::Integer(3)));
assert_eq!(cols[2][1], Some(&Value::Integer(30)));
}
#[test]
fn extra_row_entry_not_in_schema_is_ignored() {
let columns = vec![col("a")];
let rows = vec![row(&[("a", Value::Integer(1)), ("z", Value::Integer(9))])];
let cols = transpose_columns(&columns, &rows);
assert_eq!(cols.len(), 1);
assert_eq!(cols[0][0], Some(&Value::Integer(1)));
}
#[test]
fn duplicate_column_names_replicate_to_all_slots() {
let columns = vec![col("a"), col("a")];
let rows = vec![
row(&[("a", Value::Integer(1))]),
row(&[("a", Value::Null)]),
row(&[]), ];
let cols = transpose_columns(&columns, &rows);
assert_eq!(cols.len(), 2);
assert_eq!(cols[0][0], Some(&Value::Integer(1)));
assert_eq!(cols[1][0], Some(&Value::Integer(1)));
assert_eq!(cols[0][1], Some(&Value::Null));
assert_eq!(cols[1][1], Some(&Value::Null));
assert_eq!(cols[0][2], None);
assert_eq!(cols[1][2], None);
}
#[test]
fn empty_inputs() {
let no_cols: Vec<ColumnInfo> = vec![];
let rows = vec![row(&[("a", Value::Integer(1))])];
assert!(transpose_columns(&no_cols, &rows).is_empty());
let columns = vec![col("a")];
let no_rows: Vec<QueryRow> = vec![];
let cols = transpose_columns(&columns, &no_rows);
assert_eq!(cols.len(), 1);
assert!(cols[0].is_empty());
}
}