Pular para o conteúdo

Construtor de consultas

SQL montado por chamadas encadeadas — com o dialeto certo e sem injeção.

Escrever SQL à mão continua valendo — Forge.consultar(db, sql, valores) está aí para isso. O construtor resolve três coisas que o SQL à mão não resolve:

  • Dialeto. ? no SQLite e MySQL, $1 no PostgreSQL. A mesma consulta roda nos dois.
  • Condição opcional. Um filtro que só existe quando o usuário preencheu o campo.
  • Identificador citado. Uma coluna chamada order quebraria a consulta.

O básico#

dataforge
adopt Forge

db := Forge.conectar(":memory:")
Forge.executar(db, """
    create table usuarios (
        id integer primary key autoincrement,
        nome text, cidade text, idade integer, ativo integer
    )
""")
Forge.de(db, "usuarios").inserir([
    {"nome": "Ana", "cidade": "Floripa", "idade": 30, "ativo": 1},
    {"nome": "Bia", "cidade": "Recife", "idade": 25, "ativo": 1},
    {"nome": "Cid", "cidade": "Floripa", "idade": 41, "ativo": 0}
])

adultos := Forge.de(db, "usuarios")
    .selecionar("nome", "cidade")
    .onde("idade", ">=", 18)
    .onde_em("cidade", ["Floripa", "Recife"])
    .ordenar("nome")
    .limite(10)
    .buscar()

assert len(adultos) is 3
assert adultos[0]["nome"] is "Ana"

Filtros#

MétodoSQL
.onde("a", 1)a = ?
.onde("a", ">=", 1)a >= ?
.ou_onde("a", 2)OR a = ?
.onde_em("a", [1,2])a IN (?, ?)
.onde_fora("a", [1])a NOT IN (?)
.onde_entre("a", 1, 9)a BETWEEN ? AND ?
.onde_nulo("a")a IS NULL
.onde_contem("a", "x")a LIKE '%x%'
.onde_comeca("a", "x")a LIKE 'x%'
.onde_cru(sql, valores)o que você escrever, com os valores ainda parametrizados

Condição opcional#

quando aplica o trecho só se a condição valer. É o que evita o if em volta da consulta:

dataforge
adopt Forge

db := Forge.conectar(":memory:")
Forge.executar(db, "create table p (id integer primary key, nome text, preco real)")
Forge.de(db, "p").inserir([
    {"nome": "Teclado", "preco": 250.0},
    {"nome": "Mouse", "preco": 90.0}
])

action procurar(db, busca, preco_max):
    yield Forge.de(db, "p")
        .quando(busca, lambda c => c.onde_contem("nome", busca))
        .quando(preco_max, lambda c => c.onde("preco", "<=", preco_max))
        .buscar()

assert len(procurar(db, "", void)) is 2
assert len(procurar(db, "Mouse", void)) is 1
assert len(procurar(db, "", 100)) is 1

Agregados e paginação#

dataforge
adopt Forge

db := Forge.conectar(":memory:")
Forge.executar(db, "create table v (id integer primary key, total real)")
Forge.de(db, "v").inserir([{"total": 10.0}, {"total": 20.0}, {"total": 30.0}])

q := Forge.de(db, "v")
assert q.contar() is 3
assert Forge.de(db, "v").somar("total") is 60.0
assert Forge.de(db, "v").media("total") is 20.0
assert Forge.de(db, "v").maximo("total") is 30.0
assert Forge.de(db, "v").existe() is yes

pagina := Forge.de(db, "v").paginar(1, 2)
assert pagina["total"] is 3
assert pagina["paginas"] is 2
assert pagina["tem_proxima"] is yes

paginar devolve as linhas e os números que a interface precisa: total, quantidade de páginas, se há próxima e anterior. A página 1 é a primeira — não a zero.

Escritas#

dataforge
adopt Forge

db := Forge.conectar(":memory:")
Forge.executar(db, "create table c (id integer primary key autoincrement, n integer)")

Forge.de(db, "c").inserir({"n": 5})
Forge.de(db, "c").onde("id", 1).atualizar({"n": 10})
Forge.de(db, "c").onde("id", 1).incrementar("n", 3)

assert Forge.de(db, "c").onde("id", 1).primeiro()["n"] is 13

incrementar faz a soma no banco, sem ler antes. Ler-somar-gravar perde atualizações quando duas conexões fazem isso ao mesmo tempo.

Ver o SQL#

dataforge
adopt Forge

db := Forge.conectar(":memory:")
Forge.executar(db, "create table u (id integer primary key, idade integer)")

consulta := Forge.de(db, "u").onde("idade", ">=", 18).limite(5)
out consulta.sql()

Útil para registrar, depurar, ou levar uma consulta complicada para o cliente do banco.