use crate::data::{self, ColType, Column, Data};
use anyhow::{Result, bail};
impl Data {
pub fn write_column(
&mut self,
table: &str,
column: &str,
kind: Option<ColType>,
rename: Option<&str>,
) -> Result<Vec<Column>> {
let key = self.resolve(table)?;
let column = column.trim();
if column.is_empty() {
bail!("a column needs a name");
}
let existing = data::columns_of(&self.writer, &key)?;
let at = existing
.iter()
.position(|col| col.name.eq_ignore_ascii_case(column));
let Some(at) = at else {
let Some(kind) = kind else {
bail!("no column {column} on {key} — say what it holds to add it");
};
if rename.is_some() {
bail!("no column {column} on {key} to rename");
}
self.writer.execute_batch(&format!(
"ALTER TABLE {} ADD COLUMN {} {}",
data::quote(&key),
data::quote(column),
kind.sql()
))?;
return data::columns_of(&self.writer, &key);
};
if let Some(new) = rename.map(str::trim) {
if new.is_empty() {
bail!("a column needs a name");
}
if existing
.iter()
.enumerate()
.any(|(i, col)| i != at && col.name.eq_ignore_ascii_case(new))
{
bail!("{key} already has a column {new}");
}
self.writer.execute_batch(&format!(
"ALTER TABLE {} RENAME COLUMN {} TO {}",
data::quote(&key),
data::quote(&existing[at].name),
data::quote(new)
))?;
}
if let Some(kind) = kind.filter(|kind| *kind != existing[at].kind) {
let mut next = data::columns_of(&self.writer, &key)?;
next[at].kind = kind;
self.rebuild(&key, &next)?;
}
data::columns_of(&self.writer, &key)
}
fn rebuild(&mut self, key: &str, columns: &[Column]) -> Result<()> {
let tmp = format!("{key}_rebuilding");
let defs = columns
.iter()
.map(|col| format!("{} {}", data::quote(&col.name), col.kind.sql()))
.collect::<Vec<_>>()
.join(", ");
let names = columns
.iter()
.map(|col| data::quote(&col.name))
.collect::<Vec<_>>()
.join(", ");
let tx = self.writer.transaction()?;
for stmt in [
format!("DROP TABLE IF EXISTS {}", data::quote(&tmp)),
format!("CREATE TABLE {} ({defs})", data::quote(&tmp)),
format!(
"INSERT INTO {} ({names}) SELECT {names} FROM {}",
data::quote(&tmp),
data::quote(key)
),
format!("DROP TABLE {}", data::quote(key)),
format!(
"ALTER TABLE {} RENAME TO {}",
data::quote(&tmp),
data::quote(key)
),
] {
tx.execute_batch(&stmt)?;
}
Ok(tx.commit()?)
}
}