type Colony {
name: Str
apiary: Str
frames: Int
}
query findColonies(apiary: Str, minFrames: Int, small: Bool, huge: Bool): Colony[dyn] {
SELECT name, apiary, frames FROM colonies
WHERE {
if apiary != "" { apiary = {apiary} }
if minFrames > 0 { frames >= {minFrames} }
or {
if small { frames < 4 }
if huge { frames > 10 }
}
}
ORDER BY name
}
proc main(): void {
opened := hive.sql.pool(hive.sql.DatabaseDriver.SQLite(), "file::memory:?cache=shared", 1, 1)
if opened is Result.Ok(db) {
hive.sql.raw(db, "CREATE TABLE colonies (name TEXT, apiary TEXT, frames INTEGER)")
hive.sql.raw(db, "INSERT INTO colonies VALUES ('north', 'orchard', 12)")
hive.sql.raw(db, "INSERT INTO colonies VALUES ('south', 'orchard', 2)")
hive.sql.raw(db, "INSERT INTO colonies VALUES ('east', 'meadow', 7)")
// Tudo desligado: nenhum predicado vale, então não há WHERE algum.
if hive.sql.run(db, findColonies("", 0, false, false)) is Result.Ok(all) {
echo len(all)
}
// apiary AND frames >= 5 AND (frames > 10).
if hive.sql.run(db, findColonies("orchard", 5, false, true)) is Result.Ok(some) {
echo some
}
hive.sql.close(db)
}
}Uma tela de busca com quatro caixas é quatro predicados opcionais, e montar isso como texto é de onde vêm tanto a injeção de SQL quanto o WHERE 1 = 1. Um bloco WHERE é a declaração que substitui isso: ele junta com AND os predicados cujas condições valem, e um or { } ou and { } aninhado inverte o conectivo do grupo dentro dele.
Um grupo que não contribui com nada desaparece em vez de deixar um conectivo solto, um grupo que contribui com mais de um predicado vai entre parênteses, e quando nenhum predicado vale não existe WHERE algum — nenhum 1 = 1 para escrever, e nenhum para ler de volta num log. O texto de cada ramo é fixado quando o programa compila; a única coisa decidida enquanto ele roda é quais ramos são tomados, e os valores continuam viajando como parâmetros vinculados.
Duas coisas que um bloco WHERE deliberadamente não faz. Um nome de coluna ou uma direção de ordenação nunca podem ser parâmetro — ORDER BY {col} ordenaria toda linha por uma string constante — então faça dessa escolha um tipo com variantes e escolha uma query por ordenação, cada uma respondendo com o mesmo hive.sql.Fragment<Row[dyn]>. E SQL que você realmente monta por conta própria passa por hive.sql.raw, sem tipo por construção e fácil de achar.