Pular para o conteúdo principal

Executar o SQL no seu banco

O Datafala devolve o SQL; quem executa é o seu sistema. Esta página reúne o que precisa estar certo antes de ir para produção.

1. Use um usuário somente leitura​

O SQL Guard já garante um único SELECT sobre as tabelas cadastradas, mas o usuário de banco somente leitura é a sua última linha de defesa. Libere só as tabelas que você cadastrou no painel e configure um tempo máximo por consulta, porque o Guard limita linhas, não custo (uma junção grande entre tabelas permitidas é válida).

PostgreSQL:

CREATE ROLE datafala_ro LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE meu_banco TO datafala_ro;
GRANT USAGE ON SCHEMA public TO datafala_ro;
GRANT SELECT ON public.pedidos, public.clientes, public.produtos TO datafala_ro;
ALTER ROLE datafala_ro SET statement_timeout = '5s';
ALTER ROLE datafala_ro SET default_transaction_read_only = on;

MySQL:

CREATE USER 'datafala_ro'@'%' IDENTIFIED BY '...';
GRANT SELECT ON meu_banco.pedidos TO 'datafala_ro'@'%';
GRANT SELECT ON meu_banco.clientes TO 'datafala_ro'@'%';
GRANT SELECT ON meu_banco.produtos TO 'datafala_ro'@'%';
-- Tempo máximo por SELECT, em milissegundos (por sessão, ao abrir a conexão):
SET SESSION max_execution_time = 5000;

Firebird 2.5+:

CREATE USER DATAFALA_RO PASSWORD '...';
GRANT SELECT ON TABLE PEDIDOS TO USER DATAFALA_RO;
GRANT SELECT ON TABLE CLIENTES TO USER DATAFALA_RO;

Conceda acesso somente às tabelas cadastradas no painel. Configure cada conexão para iniciar transações READ ONLY; no FDB, por exemplo:

import fdb

con = fdb.connect(dsn="localhost:/dados/app.fdb", user="DATAFALA_RO", password="...")
con.begin(fdb.ISOLATION_LEVEL_READ_COMMITED_RO)

O Firebird 2.5 e o 3.0 não oferecem timeout de statement no servidor: use o timeout ou cancelamento disponibilizado pelo driver e pelo seu cliente. No Firebird 4.0 ou mais novo, também é possível configurar SET STATEMENT TIMEOUT 5 SECOND para limitar a duração da consulta.

2. Passe params sempre​

O SQL usa placeholders nomeados no estilo %(nome)s e os valores vêm em params:

{
"sql": "SELECT nome FROM clientes WHERE cidade = %(city)s AND nome LIKE 'A%%' LIMIT 100",
"params": { "city": "Campinas" }
}

Os drivers Python (psycopg, psycopg2, PyMySQL, mysqlclient) aceitam esse formato diretamente e aplicam a formatação de % ao texto inteiro. Por isso todo % que não é placeholder vem escapado como %%. Para Firebird, FDB (2.5+) e firebird-driver (3+) usam ?; converta os placeholders antes de executar.

Nunca execute sem params

Passe params mesmo quando for {}, nunca None. Sem params o driver não formata o SQL, e o %% chegaria ao banco como dois caracteres. E nunca concatene texto do usuário ao SQL: os valores já chegam separados.

3. Converta os placeholders em outras linguagens​

Em outros drivers, converta %(nome)s para o formato do driver e %% de volta para %. FDB, firebird-driver, Jaybird e node-firebird usam ? para parâmetros posicionais; FireDAC no Delphi aceita parâmetros nomeados como :name. Percorra o texto da esquerda para a direita reconhecendo os dois padrões juntos, para que um %% seguido de parênteses dentro de um literal não seja confundido com placeholder:

// %(name)s -> $1, $2... (PostgreSQL) ou ? (MySQL/Firebird); %% -> %
export function toPositional(sql, params, style = "pg") {
const values = [];
const indexes = new Map();
const text = sql.replace(/%%|%\(([A-Za-z_][A-Za-z0-9_]*)\)s/g, (match, name) => {
if (match === "%%") return "%";
if (!(name in params)) throw new Error(`missing param ${name}`);
if (style === "pg") {
if (!indexes.has(name)) {
values.push(params[name]);
indexes.set(name, values.length);
}
return `$${indexes.get(name)}`;
}
values.push(params[name]); // "?" repete o valor a cada ocorrência
return "?";
});
return { text, values };
}

Exemplos completos em Node.js e outras linguagens.

4. Limite também do seu lado​

O Guard já aplica LIMIT (padrão 100, teto 1.000, ajustável por max_rows), exceto em agregações que retornam uma única linha. Ainda assim, use fetchmany ou equivalente para não carregar mais do que a sua tela mostra.

Escopo de dados por usuário ou empresa​

O Datafala gera SQL a partir do schema, não de quem pergunta. SELECT SUM(total) FROM pedidos soma os pedidos de todas as empresas se a tabela tiver várias. Se o seu sistema é multiempresa ou tem permissão por linha, escolha uma destas defesas antes de liberar para usuários com acesso parcial:

  1. Row Level Security no PostgreSQL (recomendada). Ative RLS nas tabelas expostas, com uma política que compara a coluna da empresa com uma variável de sessão, e defina a variável na mesma transação da consulta:

    ALTER TABLE pedidos ENABLE ROW LEVEL SECURITY;
    CREATE POLICY pedidos_por_empresa ON pedidos
    USING (empresa_id = current_setting('app.empresa_id')::int);
    with conn.transaction():
    conn.execute("SELECT set_config('app.empresa_id', %s, true)", [str(user.empresa_id)])
    rows = conn.execute(answer["sql"], answer["params"]).fetchall()

    O usuário datafala_ro não pode ter BYPASSRLS nem ser dono das tabelas.

  2. Views por empresa. Cadastre no painel o DDL de views já filtradas, em vez das tabelas.

  3. Uso restrito. Libere a funcionalidade só para quem já pode ver todos os dados (por exemplo, gestores).