2007-03-26

Usando SQL com Lua

Lua Já há bastante tempo existe em programação a ideia de separar os dados da lógica do programa e, quando queremos poder fazer consultas estruturadas, bancos de dados relacionais são ótimos.

Primeiro as consultas eram feitas usando um pré-processamento que lia códigos estranhos à linguagem inseridos entre marcações de início e fim, mais ou menos assim:

EXEC SQL
DECLARE CUR CURSOR FOR
SELECT * FROM cadastro
WHERE grupo = 1
END-EXEC.


Mas era necessário rodar o pré-compilador, que substituía esses trechos por códigos mais complexos, o que era no mínimo desconfortável.

Mais tarde pensaram em fazer diferente: módulos com funções que recebem strings representando os comandos SQL (Structured Query Language) e os executam, «escondendo» assim toda complexidade do acesso sem necessidade de uma pré-compilação.

Outras sintaxes para acesso, também abstraindo a complexidade, foram desenvolvidas, como SQLObject e SQLAlchemy, mas nenhuma tão simples e popular quanto a simples passagem de uma string como parâmetro de uma função.

Por exemplo, em Python, temos o módulo MySQLdb:
import MySQLdb as mysql

conn = mysql.connect(
host="localhost",
user="batalema",
password="sEnH4",
db="empresa"
)

conn.query("""
SELECT * FROM cadastro WHERE grupo = 1
""")
cur = conn.use_result()


A conexão deve ser fechada com conn.close(). Os resultados de uma consulta são lidos com um tipo especial de reiterador chamado cursor. Isso permite que tabelas de centenas (ou milhares) de linhas (tuplas) sejam tratadas com muito pouco uso de memória. Já escrevi alguns artigos sobre o assunto.

LuaSQL


Seguindo o exemplo de Python, PHP e outras linguagens, Lua também envia os comandos SQL por meio de strings como parâmetros de funções para uma conexão e percorre os resultados com um cursor.

O módulo de Lua para acesso a SQL é LuaSQL, que é parte do projeto Kepler, uma plataforma para desenvolvimento web usando Lua. Por isso é recomendável instalar tanto Lua quanto LuaSQL através da instalação completa de Kepler 1.1 – no momento em que este artigo foi escrito, só havia disponíveis snapshots (release-cadidates). Também é recomendável a instalação de todos os pacotes opcionais (--with-optional=lualogging,luasql,luaexpat,luazip,md5).

Podemos ver agora como fazer os acessos usando LuaSQL.

O módulo traz submódulos de acesso a diversos SGBDs diferentes: MySQL (luasql.mysql), Oracle (luasql.oci8), PostgreSQL (luasql.postgres), SQLite (luasql.sqlite) e MS SQL Server (via ODBC: luasql.odbc). Vamos usar o padrão, luasql.mysql.

Em LuaSQL, antes de conectar ao SGBD, é preciso criar um ambiente de conexão:
require "luasql.mysql"

local env = assert(luasql.mysql())


A partir do ambiente podemos iniciar a conexão:
local conn = assert(
env:connect(
"empresa",
"batalema",
"sEnH4",
"localhost"
)
)


A ordem dos parâmetros (para luasql.mysql e luasql.postgresql, para outros ambientes veja o manual) é:
  1. Fonte (nome da base de dados)
  2. Usuário
  3. Senha
  4. Servidor


Efetuada a conexão, podemos criar nosso cursor. Precisaremos ainda de uma tabela Lua para receber cada uma das linhas da tabela SQL, chamadas tuplas:
local cur = conn:execute [[
SELECT * FROM cadastro WHERE grupo = 1
]]
local t = {}


O cursor possui dois métodos interessantes, um é numrows(), que retorna o número de tuplas da seleção, outro é fetch(), que retorna a próxima tupla ou nil se estiver chegado ao final.

O método fetch() recebe dois parâmetros: ¹a tabela que receberá a tupla e ²uma string que indica como a tupla será interpretada pela tabela Lua.

Há duas opções para o 2º parâmetro:
  • "n": os registros serão recebidos como pares índice-valor e a tabela será uma tabela indexada;
  • "a": os registros serão recebidos como pares chave-valor e a tabela será uma tabela associativa.


Então, para listar os resultados podemos fazer algo bem simples:
while cur:fetch(t, "a") do
table.foreach(t, print)
end


Para finalizar precisamos fechar o cursor, a conexão e o ambiente:
cur:close()
conn:close()
env:close()


Outros comandos SQL


O comando SELECT retorna um cursor para a tabela retornada, mas outros comandos não retornam cursores ou tabelas.

Em vez disso os demais comandos retornam o número de tuplas afetadas.

Conclusão


Se alguém ficou decepcionado, desculpe-me a falta de complexidade… mas o módulo é simples mesmo. =P

Aliás, todos os módulos do projeto Kepler são simples.

Para um pouco mais de «complexidade» (hehehe), veja o manual.

[]'s

PS: Publicado também no Kodumaro.