type User { id: Int, name: Str }
query findUser(name: Str): User[dyn] {
SELECT id, name FROM users WHERE name = {name}
}
proc main(): void {
pooled := hive.sql.pool(hive.sql.DatabaseDriver.SQLite(), "file::memory:?cache=shared", 1, 1)
if pooled is Result.Ok(db) {
hive.sql.raw(db, "CREATE TABLE users (id INTEGER, name TEXT)")
hive.sql.raw(db, "INSERT INTO users (id, name) VALUES (1, 'ada')")
found := hive.sql.run(db, findUser("ada"))
if found is Result.Ok(users) {
echo users
}
hive.sql.close(db)
} else if pooled is Result.Error(err) {
echo "não foi possível abrir o banco de dados: {err.message}"
}
}query insertUser(id: Int, name: Str): void {
INSERT INTO users (id, name) VALUES ({id}, {name})
}
query userNames(): Str[dyn] {
SELECT name FROM users ORDER BY name
}
query deleteUser(id: Int): void {
DELETE FROM users WHERE id = {id}
}
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 users (id INTEGER, name TEXT)")
hive.sql.run(db, insertUser(1, "ada"))
hive.sql.run(db, insertUser(2, "grace"))
if hive.sql.run(db, userNames()) is Result.Ok(names) {
echo names // um Str[dyn]: a coluna em si
}
if hive.sql.run(db, deleteUser(1)) is Result.Ok(rows) {
echo "apagou {rows} linha" // um Int: o que a instrução tocou
}
hive.sql.close(db)
}
}query everything(): Table {
SELECT * FROM users ORDER BY id
}
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 users (id INTEGER, name TEXT)")
hive.sql.raw(db, "INSERT INTO users VALUES (1, 'ada'), (2, 'grace')")
echo hive.sql.run(db, everything()) // Ok([["id", "name"], ["1", "ada"], ["2", "grace"]])
hive.sql.close(db)
}
}Uma query é um func cujo corpo é SQL e cujo tipo de retorno descreve suas linhas. Chamar uma não roda nada: ela responde com um hive.sql.Fragment<linhas> — o texto, e os valores vinculados ao lado — que pode ser vinculado, passado e guardado como qualquer valor, e hive.sql.run(connection, fragment) é o que o roda. Os valores são sempre vinculados como parâmetros, nunca emendados no texto, então nada que quem chama fornece pode mudar o que uma instrução significa: um {name} interpolado no corpo vira um marcador, ?, reescrito para $1, $2, … no PostgreSQL.
O tipo de retorno decide a forma do resultado:
Row[dyn], volta como Result<Row[dyn], hive.sql.SqlError>, suas colunas casadas com os campos pelo nome — uma coluna escrita diferente precisa de um apelido, SELECT u.name AS author, e um tipo de linha só guarda escalares;Str[dyn] ou Int[dyn], volta como aquela coluna;Table volta como uma linha de cabeçalho com os nomes das colunas, e depois toda linha como texto — o único resultado que SELECT * preenche, já que SELECT * contra um tipo de linha é erro de compilação: ele não diz nem quantas colunas voltam nem como se chamam;void marca uma instrução, e volta como o número de linhas que ela tocou.hive.sql.connect(driver, connString) e hive.sql.pool(driver, connString, maxOpen, maxIdle) abrem ambos um pool de conexões — pool só define os limites — respondendo com um Result<SqlConnection, SqlError>; guarde um pela vida do programa, e feche-o com close no final. Um driver é hive.sql.DatabaseDriver.SQLite(), .PostgreSQL() ou .Other(name); os dois primeiros vêm embutidos, e seus drivers são buscados uma vez, na primeira build que usa o módulo. SQL montado em tempo de execução passa por hive.sql.raw(connection, text), que também responde com uma Table. Um SqlError carrega um reason — "Connection", "Query", "Shape" para um número diferente de colunas, "Convert" para uma célula que não cabe no campo — e uma message.