// InsertBuilder — fluent builder for INSERT statements.
///|
/// A fluent builder for an INSERT query. Terminal: `.to_sql()`.
pub(all) struct InsertBuilder {
table : Table
columns : Array[@ast.ColumnRef]
values : Array[Array[&@moonpg.ToValue]]
conflict_cols : Array[@ast.ColumnRef]
conflict_action : Int // 0=none, 1=do_nothing, 2=do_update
conflict_sets : Array[@ast.SetClause]
returning : Array[@ast.ColumnRef]?
}
///|
/// Create a new InsertBuilder. Internal — users call `table.insert(..)` instead.
pub fn InsertBuilder::new(
table : Table,
columns : Array[&AnyColumn],
) -> InsertBuilder {
InsertBuilder::{
table,
columns: columns.map(c => c.to_ref()),
values: [],
conflict_cols: [],
conflict_action: 0,
conflict_sets: [],
returning: None,
}
}
///|
/// Add a single-row VALUES clause.
pub fn InsertBuilder::values(
self : InsertBuilder,
vals : Array[&@moonpg.ToValue],
) -> InsertBuilder {
InsertBuilder::{ ..self, values: [vals] }
}
///|
/// Add a multi-row VALUES clause.
pub fn InsertBuilder::rows(
self : InsertBuilder,
rows : Array[Array[&@moonpg.ToValue]],
) -> InsertBuilder {
InsertBuilder::{ ..self, values: rows }
}
///|
/// Set the ON CONFLICT target column(s).
pub fn InsertBuilder::on_conflict(
self : InsertBuilder,
cols : Array[&AnyColumn],
) -> InsertBuilder {
InsertBuilder::{ ..self, conflict_cols: cols.map(c => c.to_ref()) }
}
///|
/// ON CONFLICT DO NOTHING.
pub fn InsertBuilder::do_nothing(self : InsertBuilder) -> InsertBuilder {
InsertBuilder::{ ..self, conflict_action: 1 }
}
///|
/// ON CONFLICT DO UPDATE SET column = value.
pub fn InsertBuilder::do_update(
self : InsertBuilder,
column : &AnyColumn,
value : &@moonpg.ToValue,
) -> InsertBuilder {
self.conflict_sets.push(@ast.SetClause::{ column: column.to_ref(), value })
InsertBuilder::{
..self,
conflict_action: 2,
conflict_sets: self.conflict_sets,
}
}
///|
/// Attach a RETURNING clause.
pub fn InsertBuilder::returning(
self : InsertBuilder,
cols : Array[&AnyColumn],
) -> InsertBuilder {
InsertBuilder::{ ..self, returning: Some(cols.map(c => c.to_ref())) }
}
///|
/// Render the query to a parameterised SQL string + args array.
pub fn InsertBuilder::to_sql(
self : InsertBuilder,
) -> (String, Array[&@moonpg.ToValue]) {
let on_conflict = if self.conflict_action == 0 {
None
} else if self.conflict_action == 1 {
Some(@ast.OnConflict::DoNothing(render_conflict_target(self.conflict_cols)))
} else {
Some(
@ast.OnConflict::DoUpdate(
render_conflict_target(self.conflict_cols),
self.conflict_sets,
),
)
}
let node = @ast.InsertStmt::{
table: self.table.qualified_name(),
columns: self.columns,
values: self.values,
on_conflict,
returning: self.returning,
}
@tosql.insert_stmt_to_sql(node)
}
///|
fn render_conflict_target(cols : Array[@ast.ColumnRef]) -> String {
if cols.length() == 0 {
""
} else {
" (" + cols.map(fn(c) { c.name }).join(", ") + ")"
}
}