Brains Up AnalyticsBRAINSUPAnalytics
SSISPostgreSQLODBCETL

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.

Por Dione Fraga · Databricks Certified Professional11 de setembro de 20266 min de leitura

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éricoError 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 (por FETCH). É o tamanho do lote. Um ponto de partida prático é Fetch=5000 a Fetch=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 okBEGIN 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

  1. Memória estourando? UseDeclareFetch=1;Fetch=10000.
  2. Cursor com erro? UseServerSidePrepare=0.
  3. "Error while executing the query" genérico? CommLog=1 e leia o C:\psqlodbc_xxxx.log.
  4. No log, o FETCH some depois de um --? Apague o comentário de linha da query (ou use /* */).
  5. Volume muito grande e ODBC instável? Cursor server-side em Python (psycopg2, cursor(name=...) + itersize).

Artigos relacionados

Gostou? Veja os e-books para conteúdo aprofundado.

E-books