///|
/// Set operations and `DISTINCT`: `UNION` / `UNION ALL` / `INTERSECT` / `EXCEPT`
/// chain a second query onto the first, and a trailing `ORDER BY` / `LIMIT` binds to
/// the whole compound. Each operand carries its own `DISTINCT`; bound values thread
/// through in text order, left operand first.
///
///     moon run examples/03-set-operations
fn render(v : @moondb.Value) -> String {
  match v {
    Null => "Null"
    Bool(b) => "Bool(\{b})"
    Int(n) => "Int(\{n})"
    Int64(n) => "Int64(\{n})"
    Double(d) => "Double(\{d})"
    Text(s) => "Text(\"\{s}\")"
    Blob(b) => "Blob(\{b.length()} bytes)"
    Time(s) => "Time(\"\{s}\")"
    Numeric(s) => "Numeric(\{s})"
    Uuid(b) => "Uuid(\{b.length()} bytes)"
    Json(s) => "Json(\{s})"
    Array(items) => "Array(\{items.length()} items)"
  }
}

///|
fn params_str(vals : Array[@moondb.Value]) -> String {
  "[" + vals.map(render).join(", ") + "]"
}

///|
#coverage.skip
fn main {
  // DISTINCT on a single query.
  let (dsql, _) = @moonorm.select("events").distinct().column("kind").build()
  println("-- distinct")
  println(dsql)

  // UNION ALL keeps duplicates; the trailing ORDER BY / LIMIT apply to the compound.
  let active = @moonorm.select("users")
    .column("id")
    .eq("active", @moondb.Bool(true))
  let staff = @moonorm.select("staff")
    .column("id")
    .where_("role", "=", @moondb.Text("qwq"))
  let (usql, uparams) = active
    .union_all(staff)
    .order_by("id", Asc)
    .limit(233)
    .build()
  println("")
  println("-- union all + compound order/limit")
  println(usql)
  println("params: " + params_str(uparams))

  // INTERSECT (rows in both) and EXCEPT (set difference). Each builder mutates and
  // returns itself, so a fresh pair of operands is built per compound.
  let paid = fn() {
    @moonorm.select("orders")
    .column("user_id")
    .eq("status", @moondb.Text("paid"))
  }
  let shipped = fn() {
    @moonorm.select("orders")
    .column("user_id")
    .eq("status", @moondb.Text("shipped"))
  }
  let (isql, iparams) = paid().intersect(shipped()).build()
  println("")
  println("-- intersect")
  println(isql)
  println("params: " + params_str(iparams))
  let (esql, eparams) = paid().except_(shipped()).build()
  println("")
  println("-- except")
  println(esql)
  println("params: " + params_str(eparams))
}