use std::io::Write;
use narwhal_core::{ColumnHeader, Row, Value};
use super::error::ExportError;
use super::format::QualifiedName;
use super::quoting::{quote_ident, write_quoted_sql_string};
pub(super) fn write_insert<W: Write>(
writer: &mut W,
table: &QualifiedName,
columns: &[ColumnHeader],
rows: &[Row],
) -> Result<(), ExportError> {
if rows.is_empty() {
return Ok(());
}
let col_list: String = columns
.iter()
.map(|c| quote_ident(&c.name))
.collect::<Vec<_>>()
.join(", ");
let table_name = match &table.schema {
Some(s) => format!("{}.{}", quote_ident(s), quote_ident(&table.table)),
None => quote_ident(&table.table),
};
for row in rows {
write!(writer, "INSERT INTO {table_name} ({col_list}) VALUES (")
.map_err(|e| ExportError::Serialise(e.to_string()))?;
let mut first = true;
for value in &row.0 {
if !first {
writer.write_all(b", ")?;
}
first = false;
write_insert_value(writer, value)?;
}
writer.write_all(b");\n")?;
}
Ok(())
}
pub(super) fn write_insert_value<W: Write>(
writer: &mut W,
value: &Value,
) -> Result<(), ExportError> {
match value {
Value::Null => writer.write_all(b"NULL")?,
Value::Bool(b) => {
write!(writer, "{}", if *b { "TRUE" } else { "FALSE" })
.map_err(|e| ExportError::Serialise(e.to_string()))?;
}
Value::Int(i) => {
write!(writer, "{i}").map_err(|e| ExportError::Serialise(e.to_string()))?;
}
Value::Float(f) => {
if f.is_finite() {
write!(writer, "{f}").map_err(|e| ExportError::Serialise(e.to_string()))?;
} else {
writer.write_all(b"NULL")?;
}
}
Value::Bytes(b) => {
writer.write_all(b"X'")?;
for byte in b {
write!(writer, "{byte:02X}").map_err(|e| ExportError::Serialise(e.to_string()))?;
}
writer.write_all(b"'")?;
}
Value::Json(v) => {
let s = v.to_string();
write_quoted_sql_string(writer, &s)?;
}
Value::String(s) | Value::Unknown(s) => {
write_quoted_sql_string(writer, s)?;
}
Value::Date(_)
| Value::Time(_)
| Value::DateTime(_)
| Value::Timestamp(_)
| Value::Uuid(_) => {
write_quoted_sql_string(writer, &value.render())?;
}
_ => {
write_quoted_sql_string(writer, &value.render())?;
}
}
Ok(())
}