use std::io::Write;
use super::{indent_level::IndentLevel, BqColumn, BqDataType, BqNonArrayDataType};
use crate::common::*;
use crate::schema::DataType;
pub(crate) fn generate_import_udf(
column: &BqColumn,
idx: usize,
f: &mut dyn Write,
) -> Result<()> {
let ty = column.bq_data_type()?;
write!(
f,
r#"CREATE TEMP FUNCTION ImportJson_{idx}(json_string STRING)
RETURNS {bq_type}
LANGUAGE js AS """
const json = JSON.parse(json_string);
return "#,
idx = idx,
bq_type = ty,
)?;
write_transform_expr("json", &ty, IndentLevel::none(), f)?;
writeln!(
f,
r#";
""";
"#
)?;
Ok(())
}
fn write_transform_expr(
input_expr: &str,
output_type: &BqDataType,
indent: IndentLevel,
f: &mut dyn Write,
) -> Result<()> {
match output_type {
BqDataType::Array(ty) => {
writeln!(
f,
"({ie} == null) ? null : {ie}.map(function (e) {{",
ie = input_expr,
)?;
write!(f, "{}return ", indent.incr())?;
write_non_array_transform_expr("e", ty, indent.incr(), f)?;
writeln!(f, ";")?;
write!(f, "{}}})", indent)?;
}
BqDataType::NonArray(ty) => {
write_non_array_transform_expr(input_expr, ty, indent, f)?;
}
}
Ok(())
}
fn write_non_array_transform_expr(
input_expr: &str,
output_type: &BqNonArrayDataType,
indent: IndentLevel,
f: &mut dyn Write,
) -> Result<()> {
match output_type {
BqNonArrayDataType::Bool
| BqNonArrayDataType::Float64
| BqNonArrayDataType::Int64
| BqNonArrayDataType::Numeric
| BqNonArrayDataType::String => {
write!(f, "{}", input_expr)?;
}
BqNonArrayDataType::Date | BqNonArrayDataType::Timestamp => {
write!(f, "new Date({})", input_expr)?;
}
BqNonArrayDataType::Stringified(DataType::GeoJson(_))
| BqNonArrayDataType::Stringified(DataType::Json)
| BqNonArrayDataType::Stringified(DataType::Struct(_)) => {
write!(f, "JSON.stringify({})", input_expr)?;
}
BqNonArrayDataType::Stringified(DataType::Uuid) => {
write!(f, "{}", input_expr)?;
}
BqNonArrayDataType::Stringified(ty) => {
return Err(format_err!(
"the type {:?} is not expected to be stringified in BigQuery",
ty,
));
}
BqNonArrayDataType::Struct(fields) => {
write!(f, "({}) == null ? null : {{", input_expr)?;
let mut first = true;
for field in fields {
if first {
first = false;
} else {
write!(f, ",")?;
}
write!(f, "\n{}", indent.incr())?;
if let Some(name) = &field.name {
let id = name.javascript_quoted();
write!(f, "{}: ", id)?;
let field_expr = format!("{}[{}]", input_expr, id);
write_transform_expr(&field_expr, &field.ty, indent.incr(), f)?;
} else {
return Err(format_err!(
"cannot import unnamed field from {}",
output_type,
));
}
}
write!(f, "\n{}}}", indent)?;
}
BqNonArrayDataType::Datetime
| BqNonArrayDataType::Bytes
| BqNonArrayDataType::Geography
| BqNonArrayDataType::Time => {
return Err(format_err!(
"cannot import nested values of type {} into BigQuery yet",
output_type,
));
}
}
Ok(())
}