use crate::data_type::DataType;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct FlattenRequest<'a> {
pub source_relation: &'a str,
pub source_alias: &'a str,
pub array_column: &'a str,
pub array_type: &'a DataType,
pub element_alias: &'a str,
pub ordinal_alias: &'a str,
pub fields: &'a [FlattenField<'a>],
}
impl<'a> FlattenRequest<'a> {
#[must_use]
pub fn new(
source_relation: &'a str,
source_alias: &'a str,
array_column: &'a str,
array_type: &'a DataType,
element_alias: &'a str,
ordinal_alias: &'a str,
fields: &'a [FlattenField<'a>],
) -> Self {
Self {
source_relation,
source_alias,
array_column,
array_type,
element_alias,
ordinal_alias,
fields,
}
}
#[must_use]
pub fn element_type(&self) -> Option<&'a DataType> {
match self.array_type {
DataType::Array(array) => Some(array.element_type.as_ref()),
_ => None,
}
}
#[must_use]
pub fn elements_have_fields(&self) -> bool {
!self.fields.is_empty()
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct FlattenField<'a> {
pub name: &'a str,
pub data_type: &'a DataType,
}
impl<'a> FlattenField<'a> {
#[must_use]
pub fn new(name: &'a str, data_type: &'a DataType) -> Self {
Self { name, data_type }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Flatten {
pub from_item: String,
pub ordinal: String,
pub fields: Vec<String>,
}
impl Flatten {
#[must_use]
pub fn new(
from_item: impl Into<String>,
ordinal: impl Into<String>,
fields: Vec<String>,
) -> Self {
Self { from_item: from_item.into(), ordinal: ordinal.into(), fields }
}
}
#[cfg(test)]
mod tests {
use super::{Flatten, FlattenField, FlattenRequest};
use crate::agnostic::AnsiDialect;
use crate::data_type::DataType;
use crate::renderer::DmlRenderer;
impl DmlRenderer for AnsiDialect {}
#[test]
fn an_engine_that_states_nothing_answers_none() {
let array = DataType::json();
let request = FlattenRequest::new(
"\"raw\".\"orders\"",
"\"src\"",
"\"src\".\"items\"",
&array,
"\"elem\"",
"\"_ordinal\"",
&[],
);
assert!(AnsiDialect.render_flatten(&request).is_none());
}
#[test]
fn an_array_of_scalars_asks_for_no_fields() {
let array = DataType::json();
let request = FlattenRequest::new(
"\"raw\".\"pings\"",
"\"src\"",
"\"src\".\"branches\"",
&array,
"\"elem\"",
"\"_ordinal\"",
&[],
);
assert!(!request.elements_have_fields());
assert!(request.element_type().is_none(), "a document column declares no element type");
let answered = Flatten::new(", unnest(\"src\".\"branches\")", "1", vec!["\"elem\"".into()]);
assert_eq!(answered.fields.len(), 1);
}
#[test]
fn an_array_of_records_asks_for_one_expression_per_field() {
let product = DataType::varchar(10);
let quantity = DataType::int();
let fields =
[FlattenField::new("product_id", &product), FlattenField::new("quantity", &quantity)];
let array = DataType::json();
let request = FlattenRequest::new(
"\"raw\".\"orders\"",
"\"src\"",
"\"src\".\"items\"",
&array,
"\"elem\"",
"\"_ordinal\"",
&fields,
);
assert!(request.elements_have_fields());
assert_eq!(
request.fields.iter().map(|field| field.name).collect::<Vec<_>>(),
["product_id", "quantity"]
);
}
}