use std::fmt;
use super::{Ident, PgDataType, PgScalarDataType};
use crate::common::*;
use crate::schema::Column;
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct PgColumn {
pub(crate) name: String,
pub(crate) data_type: PgDataType,
pub(crate) is_nullable: bool,
}
impl PgColumn {
pub(crate) fn from_column(schema: &Schema, col: &Column) -> Result<PgColumn> {
let data_type = PgDataType::from_data_type(schema, &col.data_type)?;
Ok(PgColumn {
name: col.name.clone(),
data_type,
is_nullable: col.is_nullable,
})
}
pub(crate) fn to_column(&self) -> Result<Column> {
Ok(Column {
name: self.name.clone(),
data_type: self.data_type.to_data_type()?,
is_nullable: self.is_nullable,
comment: None,
})
}
pub(crate) fn write_export_select_expr(&self, f: &mut dyn Write) -> Result<()> {
let name = Ident(&self.name);
let check_dimension = |dimension_count: i32| -> Result<()> {
if dimension_count == 1 {
Ok(())
} else {
Err(format_err!(
"cannot output column {:} because it has dimension {}",
self.name,
dimension_count,
))
}
};
match &self.data_type {
PgDataType::Array {
dimension_count,
ty: PgScalarDataType::Bigint,
} => {
check_dimension(*dimension_count)?;
write!(
f,
r#"(SELECT array_to_json(array_agg((elem))) FROM (SELECT elem::text FROM unnest({name}) AS elem) AS elems) AS {name}"#,
name = name,
)?;
}
PgDataType::Array {
dimension_count,
ty: PgScalarDataType::TimestampWithoutTimeZone,
} => {
check_dimension(*dimension_count)?;
write!(
f,
r#"(SELECT array_to_json(array_agg((elem))) FROM (SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM to_char(elem, 'YYYY-MM-DD"T"HH24:MI:SS.US'))) AS elem FROM unnest({name}) AS elem) AS elems) AS {name}"#,
name = name,
)?;
}
PgDataType::Array {
dimension_count,
ty: PgScalarDataType::TimestampWithTimeZone,
} => {
check_dimension(*dimension_count)?;
write!(
f,
r#"(SELECT array_to_json(array_agg((elem))) FROM (SELECT TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM to_char(elem AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US'))) || 'Z' AS elem FROM unnest({name}) AS elem) AS elems) AS {name}"#,
name = name,
)?;
}
PgDataType::Array { .. } => {
write!(f, "array_to_json({name}) AS {name}", name = name)?;
}
PgDataType::Scalar(PgScalarDataType::Geometry(_srid)) => {
write!(f, "ST_AsGeoJSON({name}) AS {name}", name = name)?;
}
PgDataType::Scalar(PgScalarDataType::TimestampWithoutTimeZone) => {
write!(
f,
r#"TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM to_char({name}, 'YYYY-MM-DD"T"HH24:MI:SS.US'))) AS {name}"#,
name = name,
)?;
}
PgDataType::Scalar(PgScalarDataType::TimestampWithTimeZone) => {
write!(
f,
r#"TRIM(TRAILING '.' FROM TRIM(TRAILING '0' FROM to_char({name} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US'))) || 'Z' AS {name}"#,
name = name,
)?;
}
_ => {
write!(f, "{}", name)?;
}
}
Ok(())
}
}
impl fmt::Display for PgColumn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", Ident(&self.name), self.data_type)?;
if !self.is_nullable {
write!(f, " NOT NULL")?;
}
Ok(())
}
}