SSIS + PostgreSQL via ODBC: as opções de fetch que evitam o Out of memory — e a armadilha do comentário --
Como UseDeclareFetch/Fetch (cursor server-side) evitam o Out of memory ao extrair PostgreSQL no SSIS via ODBC, como revelar o erro genérico com CommLog, o bug do comentário -- que engole o FETCH, e o fallback com cursor em Python.
Toda extração de PostgreSQL para o SSIS via ODBC cedo ou tarde esbarra em dois muros: um pacote que estoura a memória ao ler uma tabela grande, e um erro genérico — Error while executing the query — que não diz absolutamente nada sobre o que deu errado. Os dois têm a mesma origem prática: o comportamento padrão do driver psqlODBC e a camada fina que o SSIS coloca por cima dele. Este artigo é o roteiro completo, na ordem em que a investigação realmente acontece.
Nota: os exemplos usam placeholders (
server=...;uid=...). Nunca versione connection strings com host, usuário e senha reais.
Passo 0 — Por que a memória estoura
Por padrão, o psqlODBC busca o resultado inteiro para a memória do cliente antes de entregar a primeira linha à aplicação. Para uma consulta que retorna alguns milhares de registros, ninguém percebe. Para uma tabela de milhões de linhas, o SSIS tenta materializar tudo no buffer do Data Flow e cai no clássico "Out of memory" — muitas vezes sem sequer começar a escrever no destino.
A causa não é o SSIS "ser fraco"; é o driver entregando um bloco gigante de uma vez. A solução é fazer o driver transmitir em lotes (streaming), em vez de bufferizar tudo.
Passo 1 — Streaming com cursor server-side: UseDeclareFetch + Fetch
Este é o ajuste que resolve 90% dos casos de memória. Na connection string (ou nas opções do DSN):
Driver={PostgreSQL Unicode};server=...;port=5432;database=...;uid=...;
UseDeclareFetch=1;Fetch=10000
O que cada opção faz:
UseDeclareFetch=1— o driver passa a usar um cursor server-side (DECLARE ... CURSOR) no PostgreSQL. Em vez de trazer tudo, ele mantém em memória apenas um lote de linhas por vez. É exatamente o que impede o "Out of memory".Fetch=N— quantas linhas o driver busca por ida ao servidor (porFETCH). É o tamanho do lote. Um ponto de partida prático éFetch=5000aFetch=10000; ajuste para cima em linhas estreitas e rede rápida, e para baixo em linhas muito largas.
Com isso, a memória do cliente fica constante, independente do tamanho da tabela. O preço é justo: o Postgres mantém em cache exatamente Fetch linhas por vez.
Um ajuste complementar que ajuda o SSIS: o psqlODBC reporta larguras grandes para colunas de texto sem limite, e o SSIS dimensiona o buffer por linha a partir dessas larguras. Opções como MaxVarcharSize controlam isso — mas cuidado: um valor baixo demais trunca dados. Ajuste com consciência do schema, não no chute.
Passo 2 — Quando o cursor dá erro: UseServerSidePrepare=0
Há uma incompatibilidade conhecida: UseDeclareFetch=1 pode entrar em conflito com UseServerSidePrepare, que vem ligado por padrão em muitas instalações do driver. Quando os dois estão ativos, o driver às vezes falha ao montar o DECLARE CURSOR internamente. Se ao ligar o declare/fetch a extração começar a falhar, teste desligar explicitamente:
...;UseDeclareFetch=1;Fetch=10000;UseServerSidePrepare=0
UseDeclareFetch também não é compatível com alguns cenários — SELECT ... FOR UPDATE, cursores scrolláveis e lotes com múltiplos comandos separados por ;. Um SELECT simples não deveria cair nisso, mas é bom saber que a lista existe.
Passo 3 — O erro genérico esconde a verdade: ligue o CommLog
Aqui está o ponto que faz perder horas. O SSIS mostra:
Error while executing the query
…e engole a mensagem real que o PostgreSQL devolveu. Antes de sair chutando, force o log do driver. Adicione temporariamente à connection string:
...;CommLog=1
(ou Debug=1). Isso gera um arquivo C:\psqlodbc_xxxx.log com toda a conversa driver ↔ Postgres, incluindo a mensagem de erro completa. Rode de novo, abra o final do arquivo, e a causa real estará ali — em vez do texto genérico do SSIS.
O caso real: um comentário -- que engoliu o FETCH
Foi o CommLog que revelou o problema num caso concreto. No log aparecia algo como:
declare "SQL_CUR..." cursor with hold for
SELECT ... FROM public.<tabela>
WHERE extract(year from data) = 2025 --where (id_importacao > 0 ...
;fetch 10000 in "SQL_CUR..."
O culpado era um comentário de linha -- sobrando no fim da query — código antigo comentado.
Por que passava despercebido? Quando a query roda sozinha (modo normal, sem cursor), o -- é inofensivo: o Postgres ignora o resto daquela linha e executa o WHERE normalmente. Funcionava.
Mas com UseDeclareFetch=1, o driver monta um comando composto em uma única string, sem quebra de linha entre as partes:
BEGIN; declare "cursor" ... for SELECT ... WHERE ... 2025 --comentário; fetch 10000 in "cursor"
Como não existe um \n real entre o --comentário e o ;fetch 10000 in ... que o driver anexa, o comentário engole o FETCH inteiro. O log confirmava: apareciam só dois ok — BEGIN e DECLARE CURSOR. O FETCH nunca rodava, virou parte do comentário. O driver ficava esperando uma resposta que não vinha, se perdia e mandava ROLLBACK — e o SSIS traduzia tudo isso no genérico "Error while executing the query".
A correção não era na connection string — era na query. Bastava remover o trecho comentado:
SELECT "data", id, id_importacao, garagem, numerorps, serierps, cpf_cnpj, valor
FROM public.<tabela>
WHERE extract(year from data) IN (2025, 2026)
Mantendo UseDeclareFetch=1;Fetch=10000, a extração passou a funcionar — o mecanismo de cursor em si estava certo (o DECLARE CURSOR deu ok); só o FETCH estava sendo mascarado.
Regra que fica: comentário de linha (--) e SQL montado em uma linha só não se misturam. Se precisar guardar lógica antiga, tire da query que vai para o SSIS, ou use /* ... */ com o fechamento garantido.
Quando o ODBC não vale a briga: cursor server-side em Python
Se, mesmo com tudo ajustado, o driver ODBC continuar instável para volumes muito grandes — e na prática ele costuma ser mais frágil nesses casos — vale mover aquela extração específica para um cursor server-side em Python (psycopg2):
import psycopg2
conn = psycopg2.connect(host="...", dbname="...", user="...", password="...")
# cursor nomeado = server-side; busca em lotes, memória constante
with conn.cursor(name="extracao") as cur:
cur.itersize = 10000 # equivalente ao Fetch
cur.execute("""
SELECT "data", id, id_importacao, garagem, numerorps,
serierps, cpf_cnpj, valor
FROM public.recibos
WHERE extract(year from data) IN (2025, 2026)
""")
for row in cur: # streaming, uma linha por vez
...
Resolve o mesmo problema de memória (cursor server-side, lote controlado por itersize), sem depender das particularidades do driver ODBC dentro do SSIS.
Checklist rápido
- Memória estourando?
UseDeclareFetch=1;Fetch=10000. - Cursor com erro?
UseServerSidePrepare=0. - "Error while executing the query" genérico?
CommLog=1e leia oC:\psqlodbc_xxxx.log. - No log, o
FETCHsome depois de um--? Apague o comentário de linha da query (ou use/* */). - Volume muito grande e ODBC instável? Cursor server-side em Python (psycopg2,
cursor(name=...)+itersize).
Artigos relacionados
Delta Lake em Python puro: escreva tabelas Delta sem Spark com o pacote deltalake (delta-rs)
Como o pacote deltalake (delta-rs, núcleo em Rust/Arrow) grava tabelas Delta legítimas sem Spark nem JVM — escrita, MERGE, OPTIMIZE e time travel em Python, e onde o Spark ainda vence.
Ler artigoPrevisão de séries temporais em uma chamada SQL: o ai_forecast() do Databricks
Como o ai_forecast() do Databricks entrega previsão de séries temporais numa única consulta SQL — a sintaxe, a previsão por grupo, o que a função devolve, e quando (não) usar.
Ler artigoGostou? Veja os e-books para conteúdo aprofundado.
E-books