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.
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:
-
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 pedidosUSING (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_ronão pode terBYPASSRLSnem ser dono das tabelas. -
Views por empresa. Cadastre no painel o DDL de views já filtradas, em vez das tabelas.
-
Uso restrito. Libere a funcionalidade só para quem já pode ver todos os dados (por exemplo, gestores).