Proposta de site feita pela Genesis Innovation · conteúdo do LinkedIn de Daniel Santos

Pular para o conteúdo
Daniel Santos

Wiki Python para dados

SQL de dentro do Python (sqlite3)

Banco em memória para testar regra de SQL sem servidor, e parâmetros com ? para nunca montar SQL com texto concatenado.

O sqlite3 vem com o Python. Um banco :memory: nasce e morre com o programa: é o jeito mais barato de testar uma regra de SQL num teste unitário.

PythonSaída conferida rodando Python 3 no teste do site.
import sqlite3

con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE pedidos (id INTEGER, cliente TEXT, valor REAL)")
con.executemany("INSERT INTO pedidos VALUES (?, ?, ?)",
                [(1, "Ana", 230.0), (2, "Ana", 900.0), (3, "Bruno", None)])
for linha in con.execute("SELECT cliente, COUNT(valor), SUM(valor) FROM pedidos GROUP BY cliente ORDER BY cliente"):
    print(linha)

Saída (conferida)

('Ana', 2, 1130.0)
('Bruno', 0, None)

Parâmetro, nunca texto concatenado

O ? manda o valor separado do SQL: aspas no dado não viram código. Montar SQL com f-string é a porta da injeção de SQL.

PythonSaída conferida rodando Python 3 no teste do site.
import sqlite3

con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE clientes (nome TEXT)")
con.execute("INSERT INTO clientes VALUES (?)", ("D'Ávila",))
nome = "x' OR '1'='1"
print(con.execute("SELECT COUNT(*) FROM clientes WHERE nome = ?", (nome,)).fetchone())
print(con.execute(f"SELECT COUNT(*) FROM clientes WHERE nome = '{nome}'").fetchone())

Saída (conferida)

(0,)
(1,)

A segunda consulta, montada com f-string, contou uma linha que não tem esse nome: o texto digitado virou parte do SQL (OR '1'='1' é sempre verdadeiro).

  • O mesmo vale para Spark SQL: spark.sql("SELECT * FROM t WHERE id = :id", args={"id": 7}) (Spark 3.4+) em vez de format.
  • Os exemplos de SQL desta wiki também rodam no SQLite, mas no seu navegador, pelo sql.js.

Fontes

Conhecimento compartilhado pela Genesis Innovation.