Translate

Mostrar mensagens com a etiqueta package. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta package. Mostrar todas as mensagens

terça-feira, 6 de agosto de 2024

SQLCODE sem Mistérios : O Guia de Sobrevivência do Programador COBOL Padawan diante dos Principais Erros do Db2

 

Bellacosa Mainframe e o sqlcode sem misterios

☕ Um Café no Bellacosa Mainframe

SQLCODE sem Mistérios

O Guia de Sobrevivência do Programador COBOL Padawan diante dos Principais Erros do Db2

Imagine a seguinte cena.

Você acabou de escrever seu primeiro programa COBOL acessando uma tabela Db2. O código compilou. O pré-compilador não reclamou. O BIND terminou aparentemente bem. O JCL foi submetido. O job entrou no JES2, executou alguns segundos e terminou com uma mensagem misteriosa:

SQLCODE = -805

O jovem Padawan olha para o número e pensa:

“Meu programa abendou?”

Talvez sim. Talvez não.

Um SQLCODE negativo não é necessariamente um ABEND do sistema operacional. Ele é uma resposta do Db2 informando que não conseguiu executar determinada instrução SQL. É como se o banco de dados dissesse:

“Recebi sua solicitação, mas existe alguma coisa errada. Aqui está o código que explica o motivo.”

O segredo não é decorar centenas de números. O verdadeiro conhecimento está em aprender a interpretar o contexto, capturar as informações do SQLCA, identificar a instrução que falhou e seguir uma sequência organizada de diagnóstico.

É exatamente isso que faremos neste artigo.

Prepare a caneca, abra o SDSF e venha conhecer o lado Jedi do tratamento de erros SQL em COBOL.


1. Antes do SQLCODE: como COBOL e Db2 conversam?

Um programa COBOL não entende SQL nativamente da mesma maneira que entende comandos como:

MOVE
ADD
PERFORM
READ
WRITE

As instruções SQL aparecem dentro do programa como SQL embutido:

EXEC SQL
    SELECT NOME_CLIENTE
      INTO :WS-NOME-CLIENTE
      FROM CLIENTE
     WHERE COD_CLIENTE = :WS-COD-CLIENTE
END-EXEC.

Durante a preparação do programa, um pré-compilador ou coprocessador Db2 identifica os blocos delimitados por EXEC SQL e END-EXEC, separa o SQL do código COBOL e prepara as estruturas necessárias para a execução. A documentação da IBM estabelece justamente esse formato para aplicações COBOL que emitem comandos SQL. (IBM)

Quando o programa executa uma instrução SQL, o Db2 precisa devolver uma resposta.

Essa resposta inclui, entre outras informações:

  • se a instrução funcionou;

  • se ocorreu um aviso;

  • se nenhum registro foi localizado;

  • se houve erro;

  • qual objeto estava envolvido;

  • quais tokens complementam a mensagem;

  • qual estado SQL representa a situação.

É nesse momento que entra o SQLCA.


2. O que é SQLCA?

SQLCA significa:

SQL Communication Area

Em português:

Área de Comunicação SQL

Ela é uma estrutura de dados usada para transportar informações entre o mecanismo SQL e o programa hospedeiro, como COBOL, PL/I, C ou Assembler.

No COBOL, normalmente incluímos essa estrutura assim:

EXEC SQL
    INCLUDE SQLCA
END-EXEC.

Durante o processo de pré-compilação, essa instrução é substituída pela definição correspondente da estrutura SQLCA. A IBM documenta a composição gerada pelo INCLUDE SQLCA para as diferentes linguagens hospedeiras. (IBM)

Conceitualmente, a origem do SQLCA está ligada à necessidade criada pelo SQL embutido: o banco precisava de uma área padronizada para devolver o resultado de cada operação ao programa que o chamou.

Pense nela como uma pequena central de mensagens.

O COBOL envia:

SELECT este cliente.

O Db2 responde através da SQLCA:

Encontrei.
Não encontrei.
Encontrei, mas houve aviso.
Não consegui porque a tabela não existe.
Não consegui porque o package não foi localizado.
Não consegui porque outro processo está segurando o recurso.

O SQLCODE é apenas o campo mais famoso dessa estrutura.


3. Anatomia básica do SQLCA

Uma representação simplificada da SQLCA em COBOL seria semelhante a esta:

01 SQLCA.
   05 SQLCAID          PIC X(8).
   05 SQLCABC          PIC S9(9) COMP-5.
   05 SQLCODE          PIC S9(9) COMP-5.
   05 SQLERRM.
      49 SQLERRML      PIC S9(4) COMP-5.
      49 SQLERRMC      PIC X(70).
   05 SQLERRP          PIC X(8).
   05 SQLERRD OCCURS 6 TIMES
                       PIC S9(9) COMP-5.
   05 SQLWARN.
      10 SQLWARN0      PIC X.
      10 SQLWARN1      PIC X.
      10 SQLWARN2      PIC X.
      10 SQLWARN3      PIC X.
      10 SQLWARN4      PIC X.
      10 SQLWARN5      PIC X.
      10 SQLWARN6      PIC X.
      10 SQLWARN7      PIC X.
      10 SQLWARN8      PIC X.
      10 SQLWARN9      PIC X.
      10 SQLWARNA      PIC X.
   05 SQLSTATE         PIC X(5).

A definição exata pode variar conforme plataforma, compilador e versão, mas os conceitos permanecem.

SQLCODE

É o código numérico principal retornado pelo Db2.

0       Operação bem-sucedida
+100    Nenhum dado encontrado
positivo diferente de +100
        Operação concluída com aviso
negativo
        Erro

A IBM define que SQLCODE 0 representa sucesso, +100 indica ausência de dados e valores negativos indicam que a execução não foi bem-sucedida. (IBM)

SQLSTATE

É um código de cinco caracteres que classifica a condição de maneira mais padronizada.

Exemplo:

02000

Representa a condição “nenhum dado encontrado”, equivalente ao SQLCODE +100.

SQLCODE é muito usado historicamente em ambientes Db2 e mainframe. SQLSTATE é útil por apresentar uma categorização mais padronizada entre diferentes sistemas gerenciadores de bancos de dados.

SQLERRMC

Contém tokens associados à mensagem.

Se o erro envolver o nome de uma tabela, coluna, package, plano ou autorização, essa área pode trazer justamente a informação que faltava para entender o problema.

SQLERRML

Informa o comprimento válido do conteúdo de SQLERRMC.

Portanto, não é uma boa prática exibir os 70 bytes de SQLERRMC indiscriminadamente. O ideal é considerar a quantidade indicada por SQLERRML.

SQLERRP

Pode identificar o módulo ou componente que detectou a condição. No Db2 for z/OS, os primeiros caracteres normalmente trazem a assinatura DSN. (IBM)

SQLERRD

É um conjunto de campos numéricos com informações complementares.

Dependendo da instrução e do SQLCODE, eles podem indicar:

  • quantidade de linhas afetadas;

  • códigos internos;

  • informações relacionadas ao processamento;

  • razão complementar de determinadas condições.

A interpretação dos campos SQLERRD depende do contexto. Portanto, nunca conclua que um determinado índice possui sempre o mesmo significado em qualquer SQLCODE.

SQLWARN

É um conjunto de indicadores de aviso.

Quando SQLWARN0 contém W, pelo menos uma condição de warning foi identificada. Outros campos mostram categorias específicas de aviso, como truncamento ou situações relacionadas aos dados retornados. A SQLCA utiliza esse conjunto de indicadores para comunicar warnings ao programa. (IBM)


4. A primeira regra Jedi: sempre teste o SQLCODE

Um dos erros mais perigosos não é receber um SQLCODE negativo.

É ignorá-lo.

Observe este código:

EXEC SQL
    SELECT SALDO
      INTO :WS-SALDO
      FROM CONTA
     WHERE NUM_CONTA = :WS-NUM-CONTA
END-EXEC.

DISPLAY 'SALDO: ' WS-SALDO.

O que acontecerá se a conta não existir?

O Db2 retornará +100. Entretanto, se o programa não verificar o resultado, WS-SALDO poderá continuar contendo um valor anterior, zeros, espaços ou dados residuais.

O programa poderá apresentar um saldo incorreto como se fosse válido.

O padrão correto é:

EXEC SQL
    SELECT SALDO
      INTO :WS-SALDO
      FROM CONTA
     WHERE NUM_CONTA = :WS-NUM-CONTA
END-EXEC.

EVALUATE TRUE
    WHEN SQLCODE = 0
         PERFORM 3000-PROCESSAR-SALDO

    WHEN SQLCODE = +100
         PERFORM 3100-CONTA-NAO-ENCONTRADA

    WHEN OTHER
         PERFORM 9000-TRATAR-ERRO-SQL
END-EVALUATE.

Esse modelo deixa explícitos três caminhos:

sucesso;
ausência de dados;
erro inesperado.

5. SQLCODE 0 — sucesso, mas continue atento

SQLCODE = 0 significa que a instrução foi executada com sucesso.

Exemplo:

EXEC SQL
    UPDATE CONTA
       SET SALDO = SALDO - :WS-VALOR
     WHERE NUM_CONTA = :WS-NUM-CONTA
END-EXEC.

Entretanto, existe uma armadilha.

Um UPDATE pode retornar SQLCODE zero e ainda assim não ter atualizado a quantidade de linhas que sua regra de negócio esperava.

Imagine uma rotina que deveria alterar exatamente uma linha.

Mesmo após SQLCODE zero, pode ser importante verificar a quantidade de linhas afetadas usando as informações apropriadas da SQLCA, de acordo com a instrução e o ambiente.

A pergunta profissional não é apenas:

“Funcionou?”

É também:

“Funcionou exatamente como a regra de negócio esperava?”


6. SQLCODE +100 — nenhum dado encontrado

O +100 é provavelmente o SQLCODE positivo mais conhecido.

Ele significa:

No data found.

Nenhum dado foi encontrado.

Isso pode ocorrer em situações como:

  • SELECT INTO sem linha correspondente;

  • FETCH após o final do cursor;

  • determinadas operações que esperavam encontrar um registro;

  • exclusão ou atualização sem correspondência, conforme a instrução e o contexto.

A IBM associa o SQLCODE +100 e o SQLSTATE 02000 à condição NOT FOUND. (IBM)

Exemplo com SELECT

EXEC SQL
    SELECT NOME
      INTO :WS-NOME
      FROM CLIENTE
     WHERE COD_CLIENTE = :WS-COD-CLIENTE
END-EXEC.

IF SQLCODE = +100
    DISPLAY 'CLIENTE NAO ENCONTRADO'
END-IF.

Exemplo com cursor

PERFORM UNTIL WS-FIM-CURSOR = 'S'

    EXEC SQL
        FETCH C1
         INTO :WS-CODIGO,
              :WS-NOME
    END-EXEC

    EVALUATE TRUE
        WHEN SQLCODE = 0
             PERFORM 4000-PROCESSAR-CLIENTE

        WHEN SQLCODE = +100
             MOVE 'S' TO WS-FIM-CURSOR

        WHEN OTHER
             PERFORM 9000-TRATAR-ERRO-SQL
    END-EVALUATE

END-PERFORM.

Possíveis soluções

  1. Verifique se os valores das host variables estão corretos.

  2. Confirme se a linha realmente existe na tabela.

  3. Verifique espaços, zeros à esquerda e formatos de data.

  4. Confirme se o predicado WHERE não está restritivo demais.

  5. Em cursores, trate +100 como fim normal da leitura.

  6. Inicialize as variáveis de saída antes do SELECT.

Dica Bellacosa

Não trate todo +100 como erro técnico.

Em muitos programas, “cliente não encontrado” é uma condição normal de negócio. Em um cursor, +100 é praticamente o equivalente SQL ao fim de arquivo.


7. SQLCODE -204 — objeto não definido

O -204 informa que o objeto referenciado não está definido no subsistema ou contexto em que a instrução está sendo executada. A documentação da IBM cita essa condição para diferentes tipos de objetos Db2. (IBM)

Exemplo:

SELECT *
  FROM CLIENTES

Mas a tabela correta é:

SISTEMA1.CLIENTE

Causas comuns

  • nome da tabela digitado incorretamente;

  • creator ou schema ausente;

  • tabela existente em outro ambiente;

  • synonym, alias ou view inexistente;

  • programa executando em outro subsistema Db2;

  • objeto ainda não criado;

  • qualificador definido incorretamente no BIND;

  • diferença entre desenvolvimento, homologação e produção.

Passo a passo

  1. Capture o conteúdo de SQLERRMC.

  2. Identifique o nome exato do objeto mencionado.

  3. Confirme o subsistema Db2 em que o programa executou.

  4. Pesquise o catálogo Db2.

  5. Verifique o schema ou creator.

  6. Compare o DDL entre os ambientes.

  7. Verifique os parâmetros de qualificação usados no BIND.

  8. Corrija o nome ou solicite a criação do objeto.

Exemplo de correção

Antes:

EXEC SQL
    SELECT NOME
      INTO :WS-NOME
      FROM CLIENTE
     WHERE CODIGO = :WS-CODIGO
END-EXEC.

Depois:

EXEC SQL
    SELECT NOME
      INTO :WS-NOME
      FROM CADASTRO.CLIENTE
     WHERE CODIGO = :WS-CODIGO
END-EXEC.

8. SQLCODE -206 — coluna não encontrada ou inválida

O -206 geralmente indica que o nome de uma coluna não é válido no contexto da instrução.

Exemplo:

SELECT NOME_COMPLETO
  FROM CLIENTE

Mas a coluna real se chama:

NOME_CLIENTE

Causas comuns

  • erro de digitação;

  • coluna removida ou renomeada;

  • referência incorreta a alias de tabela;

  • SQL dinâmico montado de maneira errada;

  • DCLGEN desatualizada;

  • programa compilado com uma versão antiga do layout;

  • promoção incompleta entre ambientes.

Diagnóstico

  1. Identifique a coluna em SQLERRMC.

  2. Consulte a definição atual da tabela.

  3. Compare o SQL com a DCLGEN.

  4. Confirme se o alias da tabela foi usado corretamente.

  5. Verifique se a coluna pertence à tabela esperada.

  6. Atualize e regenere as estruturas, se necessário.

Armadilha clássica

SELECT C.NOME
  FROM CLIENTE X

A tabela recebeu o alias X, mas a coluna foi qualificada com C.

A correção seria:

SELECT X.NOME
  FROM CLIENTE X

9. SQLCODE -305 — valor NULL sem indicador

O -305 é um dos primeiros grandes encontros do programador COBOL com a diferença entre o mundo COBOL e o mundo relacional.

No Db2, uma coluna pode possuir valor NULL.

No COBOL tradicional, uma variável hospedeira comum não possui, sozinha, um terceiro estado que signifique “valor desconhecido ou ausente”.

Por isso usamos uma variável indicadora.

Exemplo incorreto:

EXEC SQL
    SELECT COMPLEMENTO
      INTO :WS-COMPLEMENTO
      FROM ENDERECO
     WHERE COD_CLIENTE = :WS-COD-CLIENTE
END-EXEC.

Se COMPLEMENTO for NULL, o programa poderá receber -305.

Correção

01 WS-COMPLEMENTO       PIC X(30).
01 IND-COMPLEMENTO      PIC S9(4) COMP-5.

EXEC SQL
    SELECT COMPLEMENTO
      INTO :WS-COMPLEMENTO
          :IND-COMPLEMENTO
      FROM ENDERECO
     WHERE COD_CLIENTE = :WS-COD-CLIENTE
END-EXEC.

Depois:

IF IND-COMPLEMENTO < 0
    MOVE 'NAO INFORMADO' TO WS-COMPLEMENTO
END-IF.

Regra prática

Indicador negativo:

o valor recebido é NULL.

Indicador zero:

o valor não é NULL.

Certas condições positivas podem representar informações adicionais, como truncamento, dependendo da operação.

Passo a passo

  1. Descubra qual coluna permite NULL.

  2. Adicione uma variável indicadora compatível.

  3. Associe o indicador à host variable.

  4. Verifique o indicador após a instrução.

  5. Defina a regra de negócio para valores nulos.

  6. Não confunda NULL com espaços ou zeros.


10. SQLCODE -302 — valor incompatível ou grande demais

O -302 costuma indicar que uma variável de entrada possui um valor incompatível com o tipo ou tamanho esperado.

Exemplo:

01 WS-CODIGO PIC X(20).

A coluna Db2 espera um número pequeno, mas o programa envia:

ABC123

Também pode haver problemas de comprimento, precisão, escala ou representação.

Causas comuns

  • conteúdo alfanumérico enviado para coluna numérica;

  • número maior do que a coluna suporta;

  • escala decimal incompatível;

  • data em formato inválido;

  • host variable com definição incorreta;

  • estrutura COBOL divergente do DCLGEN;

  • indicador ou comprimento incorreto.

Passo a passo

  1. Exiba o valor da host variable antes do SQL.

  2. Verifique o tipo da coluna no catálogo.

  3. Compare PIC, USAGE, comprimento, precisão e escala.

  4. Valide dados recebidos de arquivo, tela ou API.

  5. Verifique sinais e casas decimais.

  6. Utilize DCLGEN atualizada.

  7. Não corrija apenas aumentando o campo sem entender a origem do valor.

Exemplo

Coluna:

VALOR DECIMAL(9,2)

Host variable recomendada:

01 WS-VALOR PIC S9(7)V99 COMP-3.

Um erro de definição poderia fazer o programa enviar uma representação incompatível.


11. SQLCODE -303 — tipos incompatíveis na atribuição

O -303 aparece quando o Db2 não consegue atribuir um valor à variável hospedeira devido à incompatibilidade de tipos.

Exemplo conceitual:

SELECT DATA_NASCIMENTO
  INTO :WS-VALOR-NUMERICO

Se WS-VALOR-NUMERICO não possuir definição compatível com uma data, haverá problema.

Solução

  • compare o tipo da coluna com a PIC da host variable;

  • use DCLGEN;

  • faça conversões explícitas quando justificadas;

  • evite depender de conversões implícitas;

  • verifique CCSID e codificação quando o erro envolver caracteres;

  • confira precisão e escala em campos decimais.


12. SQLCODE -407 — tentativa de gravar NULL em coluna NOT NULL

O -407 representa a tentativa de atribuir NULL a uma coluna que não aceita valores nulos.

Isso pode ocorrer em:

  • INSERT;

  • UPDATE;

  • processamento de indicadores;

  • triggers;

  • valores omitidos sem default apropriado.

Exemplo

INSERT INTO CLIENTE
       (CODIGO, NOME)
VALUES (:HV-CODIGO, :HV-NOME)

Se o indicador associado a HV-NOME estiver negativo, o programa estará tentando inserir NULL.

Caso NOME seja NOT NULL, a operação falhará.

Passo a passo

  1. Identifique a coluna envolvida.

  2. Verifique os indicadores.

  3. Consulte a definição NULL/NOT NULL.

  4. Confirme se existe valor default.

  5. Valide os campos obrigatórios antes do SQL.

  6. Não substitua NULL por espaços sem autorização da regra de negócio.


13. SQLCODE -803 — chave duplicada

O -803 é o guardião da unicidade.

Ele geralmente ocorre quando um INSERT ou UPDATE tenta gerar um valor que viola um índice único ou uma restrição de unicidade.

Exemplo:

INSERT INTO CLIENTE
       (COD_CLIENTE, NOME)
VALUES (100, 'ANA')

Mas já existe um cliente com código 100.

Causas comuns

  • chave primária já existente;

  • índice único violado;

  • contador ou sequência mal controlada;

  • reprocessamento do mesmo arquivo;

  • mensagem MQ processada duas vezes;

  • reinício de job sem checkpoint;

  • concorrência entre processos;

  • lógica “consultar e depois inserir” sujeita a corrida.

Passo a passo

  1. Identifique a constraint ou índice envolvido.

  2. Verifique quais colunas formam a chave única.

  3. Consulte o registro existente.

  4. Descubra se é duplicidade real ou reprocessamento.

  5. Verifique a origem da chave.

  6. Avalie o uso de sequence ou identity.

  7. Torne o processamento idempotente quando necessário.

  8. Não resolva apagando o registro anterior sem análise.

Dica de produção

Em sistemas distribuídos, o -803 pode ser sintoma de uma mensagem repetida, não de um erro humano.

A pergunta correta pode ser:

“Por que esta transação chegou duas vezes?”


14. SQLCODE -805 — package não encontrado

O -805 é um clássico do Db2 for z/OS.

Ele normalmente informa que o Db2 não encontrou o package necessário para executar a instrução SQL.

Seu programa foi compilado, linkeditado e executado, mas o componente Db2 correspondente não foi localizado na collection esperada.

Causas comuns

  • DBRM não foi bindado;

  • BIND PACKAGE não foi executado;

  • collection incorreta;

  • versão incorreta;

  • package removido;

  • plano não possui PKLIST adequado;

  • programa executando em subsistema diferente;

  • promoção incompleta;

  • load module novo com package antigo;

  • package novo com load module antigo.

Passo a passo de diagnóstico

  1. Capture todo o conteúdo de SQLERRMC.

  2. Identifique location, collection, package e consistência.

  3. Confirme qual load module foi executado.

  4. Verifique a STEPLIB ou JOBLIB.

  5. Confirme o subsistema Db2.

  6. Consulte o catálogo para verificar se o package existe.

  7. Confira collection e versão.

  8. Verifique o PKLIST do plano.

  9. Execute ou solicite o BIND correto.

  10. Confirme se programa e package pertencem à mesma construção.

Analogia Bellacosa

O load module é o guerreiro.

O package é o pergaminho com a estratégia SQL.

O plano ou contexto de execução é a autorização para usar determinados pergaminhos.

No -805, o guerreiro chegou ao campo de batalha, mas o pergaminho correto não estava na biblioteca.


15. SQLCODE -818 — incompatibilidade de timestamp

O -818 normalmente indica que o módulo executável e o DBRM/package associado não pertencem à mesma preparação.

Em termos simples:

o código COBOL e o código SQL ficaram fora de sincronia.

Cenário clássico

  1. Programa é pré-compilado.

  2. Um novo DBRM é produzido.

  3. O programa é compilado e linkeditado.

  4. O BIND usa um DBRM antigo.

Ou:

  1. O package foi atualizado.

  2. O load module antigo continua na load library.

Solução passo a passo

  1. Identifique o load module carregado.

  2. Confirme a data e versão do módulo.

  3. Identifique o DBRM usado no último BIND.

  4. Refaça toda a cadeia de construção.

  5. Pré-compile novamente.

  6. Compile novamente.

  7. Faça o link-edit novamente.

  8. Execute o BIND novamente.

  9. Promova load module e package como uma unidade.

  10. Verifique se a execução está lendo a biblioteca correta.

Dica DevOps

Nunca trate o package e o executável como artefatos independentes.

Eles são partes da mesma entrega.


16. SQLCODE -811 — SELECT retornou mais de uma linha

Um SELECT INTO espera, normalmente, uma única linha.

Exemplo:

EXEC SQL
    SELECT NOME
      INTO :WS-NOME
      FROM CLIENTE
     WHERE CIDADE = :WS-CIDADE
END-EXEC.

Se existirem cem clientes na cidade, o Db2 não poderá decidir qual nome devolver.

Resultado:

SQLCODE -811

Causas

  • predicado incompleto;

  • premissa errada de unicidade;

  • dados duplicados;

  • constraint ausente;

  • chave incorreta;

  • regra de negócio mudou;

  • SELECT deveria utilizar cursor.

Soluções

Quando deveria existir apenas uma linha

  • corrija o WHERE;

  • consulte pela chave correta;

  • elimine duplicidades de forma controlada;

  • implemente constraint única quando fizer sentido.

Quando várias linhas são válidas

Use cursor:

EXEC SQL
    DECLARE C1 CURSOR FOR
        SELECT NOME
          FROM CLIENTE
         WHERE CIDADE = :WS-CIDADE
END-EXEC.

Cuidado com a falsa solução

Adicionar algo equivalente a “pegue apenas a primeira linha” pode esconder um problema de dados.

Antes de limitar o resultado, pergunte:

“Qual linha é realmente a correta?”


17. SQLCODE -904 — recurso indisponível

O -904 indica que a instrução não foi executada porque um recurso necessário estava indisponível. A mensagem traz informações como reason code, tipo do recurso e nome do recurso. (IBM)

Possíveis causas

  • tablespace parado;

  • objeto em estado restritivo;

  • partição indisponível;

  • utilitário em execução;

  • recurso não iniciado;

  • problema de comunicação;

  • recurso auxiliar indisponível;

  • condição de recuperação pendente.

Passo a passo

  1. Capture o reason code.

  2. Capture o resource type.

  3. Capture o resource name.

  4. Consulte a documentação do reason code.

  5. Verifique mensagens Db2 no log.

  6. Consulte o estado do objeto.

  7. Verifique utilitários ativos.

  8. Acione o DBA ou suporte de produção com os dados completos.

  9. Não tente repetir indefinidamente sem entender a indisponibilidade.

Dica importante

-904 sem reason code é apenas metade do diagnóstico.

Dizer ao DBA:

“Deu menos novecentos e quatro”

é pouco útil.

Dizer:

“SQLCODE -904, reason 00C9..., resource type ..., resource name ...”

transforma um pedido genérico em uma investigação objetiva.


18. SQLCODE -911 e -913 — deadlock ou timeout

Esses códigos aparecem em situações de contenção, timeout ou deadlock.

Deadlock

Dois processos ficam esperando recursos mantidos um pelo outro.

Exemplo:

Programa A atualiza CLIENTE e espera CONTA.
Programa B atualiza CONTA e espera CLIENTE.

Nenhum consegue continuar.

O Db2 escolhe uma das unidades de trabalho como vítima para liberar o impasse.

Timeout

Um processo aguarda um recurso por mais tempo do que o limite permitido.

Diferença prática

A interpretação exata deve considerar:

  • SQLCODE;

  • reason code;

  • mensagens;

  • rollback executado;

  • ambiente e parâmetros;

  • informações de trace.

Em muitos cenários, -911 aparece quando a unidade de trabalho sofre rollback, enquanto -913 pode indicar uma condição semelhante sem o mesmo comportamento automático de rollback. A confirmação deve ser feita pelos dados complementares, especialmente reason code e SQLERRD. (Comunidade IBM)

Possíveis soluções

  1. Reduza o tempo entre commits.

  2. Não mantenha cursor ou unidade de trabalho aberta desnecessariamente.

  3. Acesse tabelas na mesma ordem em todos os programas.

  4. Atualize somente as linhas necessárias.

  5. Garanta bons índices.

  6. Evite varreduras extensas durante updates.

  7. Analise isolamento e concorrência.

  8. Implemente retry controlado quando permitido.

  9. Não faça retry infinito.

  10. Registre tentativa, horário e chave processada.

Exemplo de retry controlado

MOVE 0 TO WS-TENTATIVAS.

PERFORM UNTIL WS-SUCESSO = 'S'
           OR WS-TENTATIVAS >= 3

    ADD 1 TO WS-TENTATIVAS

    PERFORM 5000-EXECUTAR-TRANSACAO

    EVALUATE SQLCODE
        WHEN 0
             MOVE 'S' TO WS-SUCESSO

        WHEN -911
             PERFORM 5100-PREPARAR-NOVA-TENTATIVA

        WHEN -913
             PERFORM 5100-PREPARAR-NOVA-TENTATIVA

        WHEN OTHER
             PERFORM 9000-TRATAR-ERRO-SQL
             MOVE 'S' TO WS-ENCERRAR
    END-EVALUATE

END-PERFORM.

O retry precisa respeitar a arquitetura da aplicação. Em alguns casos, repetir a operação automaticamente pode duplicar efeitos externos.


19. SQLCODE -922 — falha de autorização

O -922 normalmente está relacionado à autorização, conexão ou acesso a recursos do Db2.

Possíveis causas

  • usuário sem privilégio;

  • plano sem autorização;

  • package sem autorização de execução;

  • falha na conexão;

  • contexto de segurança incorreto;

  • autorização RACF ou Db2 ausente;

  • ambiente executando com outro ID;

  • mudança de owner ou qualifier.

Passo a passo

  1. Identifique o authorization ID em uso.

  2. Capture os tokens da SQLCA.

  3. Verifique o privilégio exigido.

  4. Confirme quem é o owner do package.

  5. Verifique autorização de EXECUTE.

  6. Confira plano, collection e contexto de execução.

  7. Analise mensagens SAF/RACF quando aplicável.

  8. Solicite o GRANT correto.

  9. Nunca peça privilégios excessivos apenas para “fazer funcionar”.


20. SQLCODE -501, -502 e -514 — problemas com cursor

-501

Tentativa de executar FETCH ou CLOSE em cursor que não está aberto.

-502

Tentativa de abrir um cursor que já está aberto.

-514

O cursor não está em estado adequado para a operação, frequentemente por não estar preparado corretamente em cenários de SQL dinâmico.

Checklist de cursor

A sequência esperada é:

DECLARE
OPEN
FETCH
FETCH
FETCH
...
+100
CLOSE

No COBOL:

EXEC SQL
    OPEN C1
END-EXEC.

IF SQLCODE NOT = 0
    PERFORM 9000-TRATAR-ERRO-SQL
END-IF.

PERFORM UNTIL WS-FIM = 'S'

    EXEC SQL
        FETCH C1
         INTO :WS-CODIGO,
              :WS-NOME
    END-EXEC

    EVALUATE SQLCODE
        WHEN 0
             PERFORM 4000-PROCESSAR
        WHEN +100
             MOVE 'S' TO WS-FIM
        WHEN OTHER
             PERFORM 9000-TRATAR-ERRO-SQL
             MOVE 'S' TO WS-FIM
    END-EVALUATE

END-PERFORM.

EXEC SQL
    CLOSE C1
END-EXEC.

Dica

Use flags explícitas:

01 WS-CURSOR-ABERTO PIC X VALUE 'N'.
01 WS-FIM-CURSOR    PIC X VALUE 'N'.

Elas ajudam a impedir OPEN duplicado ou FETCH após fechamento.


21. WHENEVER: útil, mas não mágico

O Db2 permite instruções como:

EXEC SQL
    WHENEVER SQLERROR
    GO TO 9000-ERRO-SQL
END-EXEC.

Também existem:

EXEC SQL
    WHENEVER NOT FOUND
    GO TO 8000-NAO-ENCONTRADO
END-EXEC.

EXEC SQL
    WHENEVER SQLWARNING
    GO TO 8500-WARNING-SQL
END-EXEC.

A IBM documenta que:

  • NOT FOUND corresponde ao +100;

  • SQLERROR corresponde a SQLCODE negativo;

  • SQLWARNING cobre warnings e códigos positivos diferentes de +100. (IBM)

Entretanto, existe uma característica importante:

WHENEVER é uma diretiva tratada durante a preparação do SQL embutido. Seu efeito depende da posição em que aparece no fonte.

Ela não funciona exatamente como uma configuração dinâmica COBOL tradicional.

Risco

Um programador pode colocar:

EXEC SQL
    WHENEVER SQLERROR GO TO 9000-ERRO
END-EXEC.

e depois esquecer que todas as instruções SQL seguintes estarão sujeitas a esse desvio, até que outra diretiva altere o comportamento.

Alternativa explícita

Muitas equipes preferem:

EXEC SQL
    WHENEVER SQLERROR CONTINUE
END-EXEC.

E verificam o SQLCODE após cada comando:

EXEC SQL
    DELETE FROM TEMPORARIA
     WHERE CHAVE = :WS-CHAVE
END-EXEC.

PERFORM 9100-VERIFICAR-SQL.

Isso pode tornar o fluxo mais previsível.


22. Uma rotina COBOL centralizada de tratamento

Uma boa aplicação não deveria espalhar dezenas de DISPLAY SQLCODE improvisados.

Crie uma rotina padronizada:

9000-TRATAR-ERRO-SQL.

    DISPLAY '**************************************'
    DISPLAY '* ERRO DB2                           *'
    DISPLAY '**************************************'
    DISPLAY 'PROGRAMA : ' WS-NOME-PROGRAMA
    DISPLAY 'PARAGRAFO: ' WS-PARAGRAFO-ATUAL
    DISPLAY 'OPERACAO : ' WS-OPERACAO-SQL
    DISPLAY 'SQLCODE  : ' SQLCODE
    DISPLAY 'SQLSTATE : ' SQLSTATE
    DISPLAY 'SQLERRP  : ' SQLERRP
    DISPLAY 'SQLERRML : ' SQLERRML
    DISPLAY 'SQLERRMC : ' SQLERRMC
    DISPLAY 'SQLERRD1 : ' SQLERRD (1)
    DISPLAY 'SQLERRD2 : ' SQLERRD (2)
    DISPLAY 'SQLERRD3 : ' SQLERRD (3)
    DISPLAY 'SQLERRD4 : ' SQLERRD (4)
    DISPLAY 'SQLERRD5 : ' SQLERRD (5)
    DISPLAY 'SQLERRD6 : ' SQLERRD (6)
    DISPLAY '**************************************'

    MOVE 12 TO RETURN-CODE.

Em produção, avalie cuidados com dados sensíveis. Não exiba:

  • senhas;

  • tokens;

  • informações pessoais desnecessárias;

  • números completos de documentos;

  • dados financeiros sigilosos;

  • comandos SQL contendo informações protegidas.

A observabilidade deve ajudar o diagnóstico sem criar uma nova vulnerabilidade.


23. O passo a passo universal de diagnóstico

Quando aparecer um SQLCODE inesperado, siga esta sequência.

Passo 1 — identifique a instrução

Não procure apenas pelo número.

Descubra:

qual programa;
qual parágrafo;
qual instrução SQL;
qual tabela;
qual chave;
qual operação.

Passo 2 — registre a SQLCA

Capture:

SQLCODE;
SQLSTATE;
SQLERRMC;
SQLERRML;
SQLERRP;
SQLERRD;
SQLWARN.

Passo 3 — descubra a categoria

Pergunte:

É sucesso?
É +100?
É warning?
É erro de dados?
É erro de objeto?
É erro de package?
É erro de autorização?
É indisponibilidade?
É concorrência?

Passo 4 — leia os tokens

Muitos SQLCODEs só ficam claros quando analisamos SQLERRMC, reason code, nome do recurso ou nome do objeto.

Passo 5 — reproduza com os mesmos dados

Tente identificar:

  • chave;

  • conteúdo das host variables;

  • ambiente;

  • versão do programa;

  • package;

  • plano;

  • horário;

  • unidade de trabalho.

Passo 6 — compare estruturas

Verifique:

  • DCLGEN;

  • catálogo;

  • copybooks;

  • tipos;

  • comprimentos;

  • indicadores;

  • nullabilidade;

  • índices;

  • constraints.

Passo 7 — corrija a causa, não apenas o sintoma

Exemplo:

Sintoma: -803.
Correção ruim: ignorar toda duplicidade.
Correção real: descobrir por que a transação foi processada novamente.

Passo 8 — crie proteção contra recorrência

Depois de corrigir:

  • adicione validação;

  • melhore o log;

  • crie teste;

  • atualize documentação;

  • ajuste pipeline;

  • implemente monitoramento;

  • reveja commit e rollback.


24. COMMIT e ROLLBACK: a responsabilidade do Padawan

Uma transação Db2 não é apenas uma sequência de comandos.

Ela é uma unidade lógica de trabalho.

Exemplo:

Debitar conta A.
Creditar conta B.
Registrar movimentação.

Essas três ações precisam ser tratadas como um conjunto.

Se a primeira funcionar e a segunda falhar, você não pode simplesmente continuar.

EXEC SQL
    COMMIT
END-EXEC.

confirma a unidade de trabalho.

EXEC SQL
    ROLLBACK
END-EXEC.

desfaz alterações ainda não confirmadas, dentro das regras e do contexto da aplicação.

Cuidado

Não espalhe commits aleatórios apenas para reduzir locks.

Um commit mal posicionado pode quebrar a atomicidade da transação.

O ponto de commit deve refletir a regra de negócio, não apenas uma tentativa de silenciar problemas de concorrência.


25. Dicas de veterano para o COBOL Padawan

Use DCLGEN

A DCLGEN ajuda a alinhar as colunas Db2 com as variáveis COBOL.

Mas lembre-se:

DCLGEN não é um artefato eterno.

Se a tabela mudar, a declaração precisa ser revisada e regenerada.

Inicialize variáveis

Antes de um SELECT INTO, inicialize as variáveis de saída quando isso fizer sentido.

Isso evita o uso acidental de conteúdo anterior após um +100.

Registre o ponto da falha

Antes da instrução:

MOVE 'SELECT-CLIENTE'
  TO WS-OPERACAO-SQL.

Assim a rotina central sabe exatamente qual comando falhou.

Não use apenas DISPLAY

Em aplicações maduras, prefira uma estratégia padronizada de logging, mensagens de aplicação, códigos de retorno e integração com observabilidade.

Diferencie erro técnico de erro de negócio

+100 procurando cliente opcional:
condição de negócio.

-805:
erro técnico de implantação.

-803 ao reprocessar mensagem:
pode representar condição funcional de idempotência.

-911:
condição técnica de concorrência que talvez permita retry.

Conheça o ambiente

Em Db2 for z/OS, o diagnóstico frequentemente envolve muito mais do que o fonte:

  • subsistema Db2;

  • catálogo;

  • package;

  • collection;

  • plan;

  • DBRM;

  • BIND;

  • load library;

  • RACF;

  • JES2;

  • SDSF;

  • logs;

  • utilitários;

  • locks.


26. Curiosidade: SQLCODE não é ABEND

Um SQLCODE negativo indica que uma instrução SQL falhou.

Um ABEND indica encerramento anormal de uma task, job step ou programa.

Os dois podem aparecer juntos, mas não são a mesma coisa.

Um programa bem escrito pode receber -803, tratar a duplicidade e continuar.

Outro programa pode receber +100, ignorar a condição e mais tarde causar um S0C7 porque tentou usar dados inválidos.

Portanto:

SQLCODE negativo não significa automaticamente ABEND.
SQLCODE positivo não significa automaticamente que tudo está correto.

O resultado depende da maneira como o programa reage.


27. Easter egg do Bellacosa Mainframe

Nos filmes, o jovem Jedi pergunta:

“Mestre, como saberei qual caminho seguir?”

O mestre responde:

“Observe a Força.”

No Db2, a resposta seria:

“Observe a SQLCA.”

O SQLCODE mostra a direção geral.

O SQLSTATE mostra a categoria.

O SQLERRMC revela os personagens envolvidos.

O reason code explica a motivação do vilão.

O SQLERRD guarda pistas escondidas.

O log do Db2 conta o que aconteceu fora da câmera.

E o programa COBOL decide se deve:

continuar;
encerrar;
repetir;
fazer rollback;
informar o usuário;
acionar o suporte.

O verdadeiro Sith não é o SQLCODE negativo.

O verdadeiro Sith é o programa que recebe uma mensagem de erro, ignora a SQLCA e continua processando dados como se nada tivesse acontecido.


28. Tabela de bolso do Padawan

SQLCODE   SIGNIFICADO PRINCIPAL
-------   ----------------------------------------------
0         Execução bem-sucedida
+100      Nenhum dado encontrado / fim do cursor
-204      Objeto não definido
-206      Coluna inválida ou não encontrada
-302      Valor de entrada incompatível ou excessivo
-303      Tipos incompatíveis
-305      NULL recebido sem variável indicadora
-407      NULL enviado para coluna NOT NULL
-501      Cursor não aberto
-502      Cursor já aberto
-514      Cursor em estado inadequado
-803      Violação de chave ou índice único
-805      Package não encontrado
-811      SELECT INTO retornou mais de uma linha
-818      Incompatibilidade entre módulo e DBRM/package
-904      Recurso indisponível
-911      Timeout/deadlock com impacto na unidade de trabalho
-913      Timeout/deadlock; analisar rollback e reason code
-922      Falha de autorização ou conexão

Essa tabela é um mapa inicial, não substitui a documentação completa. O catálogo de códigos Db2 fornece explicações, ações do sistema e respostas recomendadas para cada condição. (IBM)


Conclusão: o erro é uma mensagem, não uma sentença

Aprender Db2 com COBOL não significa decorar uma enciclopédia de SQLCODEs.

Significa construir um método.

Quando uma instrução falhar:

  1. pare;

  2. identifique o SQL;

  3. capture a SQLCA;

  4. leia o SQLCODE;

  5. consulte o SQLSTATE;

  6. analise os tokens;

  7. identifique objetos e reason codes;

  8. verifique host variables;

  9. confirme package, plano e ambiente;

  10. corrija a causa;

  11. proteja o sistema contra recorrência.

Com o tempo, os números deixam de parecer mensagens alienígenas.

O +100 passa a significar fim normal de cursor.

O -305 lembra imediatamente uma variável indicadora esquecida.

O -803 aponta para unicidade ou reprocessamento.

O -805 faz você olhar para package, collection e BIND.

O -818 lembra que load module e DBRM precisam caminhar juntos.

O -911 acende o alerta de concorrência, commit e retry.

O -904 exige reason code, resource type e resource name.

Nesse momento, o Padawan deixa de perguntar:

“Por que o Db2 quebrou?”

E começa a perguntar:

“Qual condição o Db2 está comunicando, quais evidências ele forneceu e qual é a resposta correta da aplicação?”

Essa mudança de postura é o nascimento de um verdadeiro programador de sistemas corporativos.

Porque no Bellacosa Mainframe, cada SQLCODE é uma pista, cada SQLCA é um mapa e cada erro corretamente tratado é mais uma transação protegida no coração do IBM Z.

 


sábado, 31 de dezembro de 2022

COBOL, QSAM e Db2 sem Mistérios: a Jornada do Arquivo Sequencial até o INSERT na Tabela

 

Bellacosa Mainframe executando cobol com db2 e qsam

☕ Um Café no Bellacosa Mainframe

COBOL, QSAM e Db2 sem Mistérios: a Jornada do Arquivo Sequencial até o INSERT na Tabela

Imagine a cena: madrugada no datacenter, luzes azuis refletindo nos corredores, o IBM Z trabalhando silenciosamente e, diante da tela 3270, um programador COBOL padawan observa um pequeno JCL.

À primeira vista, são poucas linhas:

//EXECDB2 EXEC PGM=IKJEFT01,DYNAMNBR=20
//STEPLIB DD DSN=DSN910.SDSNLOAD,DISP=SHR
//LISTCOPAC DD DSN=INFEF00.COPIA.MUNDO,DISP=SHR
//SYSTSPRT DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//SYSTSIN  DD *
  DSN SYSTEM(DB9G)
  RUN PROGRAM(COBDB501) PLAN(HERP0001) -
      LIB('ADCD.Z112.USERLOAD')
/*

Mas o verdadeiro mainframe nunca entrega todos os seus segredos de imediato.

Por trás dessas linhas existe uma cadeia completa de tecnologias:

JES2
  ↓
JCL
  ↓
IKJEFT01
  ↓
TSO Batch
  ↓
DSN Command Processor
  ↓
Db2 Attachment Facility
  ↓
PLAN
  ↓
PACKAGE
  ↓
Programa COBOL
  ↓
QSAM
  ↓
Arquivo sequencial
  ↓
INSERT no Db2
  ↓
COMMIT ou ROLLBACK

O JCL é pequeno, mas representa o último estágio de uma fábrica inteira de software. Antes de chegar a essa execução, alguém escreveu um programa COBOL, processou SQL pelo precompiler, compilou, linkeditou, gerou um load module, criou um DBRM, executou BIND PACKAGE, criou ou atualizou um PLAN e autorizou o usuário.

Vamos abrir cada porta desse templo tecnológico.


Bellacosa Mainframe executando um programa cobol db2 e qsam

1. Qual é a missão deste programa?

O próprio comentário do JCL entrega a missão:

//* PROGRAMA LE QSAM E INSERE TABELAS DB2

Em linguagem simples, o programa deverá:

  1. abrir um arquivo sequencial;

  2. ler cada registro;

  3. separar os campos;

  4. validar os dados;

  5. executar um INSERT em uma tabela Db2;

  6. tratar registros inválidos;

  7. realizar COMMIT;

  8. fechar o arquivo;

  9. retornar um código ao sistema operacional.

Um fluxo provável seria:

Abrir arquivo LISTCOPAC
        ↓
Ler primeiro registro
        ↓
Registro válido?
   ┌────┴────┐
   │         │
  Sim       Não
   │         │
INSERT      Registrar erro
   │         │
   └────┬────┘
        ↓
Atingiu intervalo de COMMIT?
        ↓
Executar COMMIT
        ↓
Ler próximo registro
        ↓
Fim do arquivo?
   ┌────┴────┐
   │         │
  Não       Sim
   │         │
Repetir     COMMIT final
             ↓
        Fechar arquivo
             ↓
           GOBACK

Esse tipo de programa é muito comum em sistemas corporativos.

Ele pode carregar:

  • clientes;

  • contas;

  • pagamentos;

  • movimentações;

  • produtos;

  • contratos;

  • dados de interfaces;

  • informações recebidas de outros sistemas;

  • arquivos produzidos por plataformas distribuídas.

É uma ponte entre o mundo dos arquivos batch e o mundo relacional do Db2.


2. O papel do JES

Antes que qualquer programa seja executado, o job precisa ser entregue ao JES.

JES significa Job Entry Subsystem.

Em muitos ambientes z/OS utiliza-se o JES2. Ele recebe o JCL, analisa as instruções, prepara os recursos, controla a execução e organiza as saídas no spool.

Quando o programador digita:

SUB

no ISPF Edit, não está executando o COBOL diretamente.

Na realidade, está entregando um pedido ao JES:

“JES, aceite este conjunto de instruções, valide o JCL, encontre os recursos e execute os steps na ordem indicada.”

O JES então:

  1. atribui um JOBID;

  2. lê o JCL;

  3. faz a conversão;

  4. verifica procedimentos;

  5. coloca o job na fila;

  6. seleciona uma initiator;

  7. executa os steps;

  8. recolhe as mensagens;

  9. grava os resultados no spool.

O nome mostrado na tela:

CATPGM1

é o nome do job.

O identificador:

JOB00056

é o JOBID atribuído pelo JES.

Esses dois nomes parecem semelhantes, mas representam coisas diferentes:

CATPGM1  = nome lógico fornecido no cartão JOB
JOB00056 = número atribuído pelo JES

3. A biblioteca JOBLIB

Na imagem existe:

//JOBLIB DD DSN=IBMUSER.CBL.LOADLIB,DISP=SHR

A JOBLIB define uma biblioteca de módulos executáveis para o job inteiro.

É como dizer:

“Ao procurar programas deste job, inclua esta biblioteca na busca.”

A biblioteca:

IBMUSER.CBL.LOADLIB

deve ser uma PDS ou PDSE contendo load modules ou program objects.

Um programa COBOL não é executado diretamente a partir do fonte:

IBMUSER.CBL.SOURCE(COBDB501)

O fonte precisa atravessar um processo:

Fonte COBOL
     ↓
Precompile Db2
     ↓
Compilação COBOL
     ↓
Object module
     ↓
Binder ou Link-edit
     ↓
Load module

O resultado final pode estar armazenado como:

IBMUSER.CBL.LOADLIB(COBDB501)

Um detalhe importante

No step da imagem também existe uma STEPLIB:

//STEPLIB DD DSN=DSN910.SDSNLOAD,DISP=SHR

Quando um step possui STEPLIB, ela normalmente substitui a JOBLIB para a busca de módulos naquele step.

Portanto, a existência de JOBLIB não significa que ela será usada pelo step EXECDB2.

Essa é uma daquelas curiosidades que derrubam muitos padawans.

O programador pensa:

“Meu programa está na JOBLIB, portanto será encontrado.”

Mas o sistema responde:

“Existe uma STEPLIB neste step. Minha busca seguirá outro caminho.”

No caso do JCL apresentado, o programa da aplicação é localizado por meio da cláusula:

LIB('ADCD.Z112.USERLOAD')

Isso evita que ele dependa da JOBLIB.


4. O step EXECDB2

//EXECDB2 EXEC PGM=IKJEFT01,DYNAMNBR=20

Essa linha cria um step chamado:

EXECDB2

O nome do step é escolhido pelo desenvolvedor.

Ele poderia ser:

//STEP01
//RUNCOB
//DB2RUN
//CARREGA

Um bom nome melhora a análise do spool.

Ao observar:

EXECDB2

já sabemos que aquele step provavelmente executará alguma coisa em um ambiente Db2.

A instrução:

EXEC PGM=IKJEFT01

determina o programa que o z/OS carregará inicialmente.

E aqui surge uma pergunta importante:

Por que o JCL não executa diretamente COBDB501?

Porque COBDB501 contém SQL estático e precisa executar conectado ao subsistema Db2. O IKJEFT01 cria o ambiente TSO batch, dentro do qual o command processor DSN estabelece essa conexão.

Assim, a sequência real é:

IKJEFT01
   ↓
Comando DSN
   ↓
DSN SYSTEM(DB9G)
   ↓
RUN PROGRAM(COBDB501)

5. O que é o IKJEFT01?

O IKJEFT01 é um programa do z/OS utilizado para executar comandos TSO em batch.

O TSO é normalmente associado ao trabalho interativo:

READY

O usuário digita comandos e recebe respostas.

Com IKJEFT01, não existe uma pessoa digitando os comandos. Eles são fornecidos por um dataset ou por dados instream no DD SYSTSIN.

Uma analogia Bellacosa:

  • TSO interativo é o Jedi no terminal;

  • IKJEFT01 é o droide que repete os comandos durante a madrugada;

  • SYSTSIN é o pergaminho com as ordens;

  • SYSTSPRT é o diário da missão.

O IKJEFT01 pode executar:

  • comandos TSO;

  • CLISTs;

  • execuções REXX;

  • comandos do Db2;

  • utilitários que dependem do ambiente TSO.


6. O parâmetro DYNAMNBR=20

DYNAMNBR=20

Esse parâmetro está relacionado ao número de alocações dinâmicas que o ambiente TSO pode utilizar.

Durante a execução, o IKJEFT01, o command processor DSN ou programas chamados podem precisar alocar datasets dinamicamente.

Exemplos:

  • bibliotecas;

  • arquivos temporários;

  • arquivos de controle;

  • mensagens;

  • datasets de trabalho.

O valor:

20

costuma ser suficiente para execuções simples.

Em ambientes maiores aparecem valores como:

DYNAMNBR=50

ou:

DYNAMNBR=100

Não significa que o sistema abrirá imediatamente 20 datasets. É uma capacidade reservada para o ambiente.

Um valor insuficiente pode provocar falhas de alocação em execuções mais complexas.


7. A STEPLIB do Db2

//STEPLIB DD DSN=DSN910.SDSNLOAD,DISP=SHR

A STEPLIB informa as bibliotecas usadas na procura dos programas daquele step.

A biblioteca:

DSN910.SDSNLOAD

contém módulos relacionados ao Db2.

Entre eles podem existir componentes necessários para:

  • iniciar o command processor DSN;

  • usar o TSO Attachment Facility;

  • conectar-se ao subsistema;

  • carregar rotinas internas;

  • interagir com os serviços Db2.

O nome DSN910 pode indicar uma instalação antiga, uma versão ou apenas uma convenção local.

Não devemos presumir que o nome da biblioteca representa exatamente a versão atual do Db2. Muitas instalações mantêm nomes históricos para evitar alterar centenas de JCLs.

Contudo, é essencial que essa biblioteca seja compatível com o subsistema usado.

O subsistema do exemplo é:

DB9G

Se a SDSNLOAD não for a correta, podem surgir:

  • mensagens de conexão;

  • módulo não encontrado;

  • falhas no attachment;

  • abends;

  • incompatibilidade de nível.


8. DISP=SHR

Tanto a JOBLIB quanto a STEPLIB e o arquivo de entrada utilizam:

DISP=SHR

O parâmetro DISP descreve o estado e o tratamento do dataset.

SHR significa que o dataset já existe e pode ser compartilhado.

Exemplo:

//STEPLIB DD DSN=DSN910.SDSNLOAD,DISP=SHR

Essa biblioteca poderá ser usada simultaneamente por vários jobs.

Isso é normal para load libraries, pois dezenas ou centenas de aplicações podem carregar módulos da mesma biblioteca.

No arquivo de entrada:

//LISTCOPAC DD DSN=INFEF00.COPIA.MUNDO,DISP=SHR

o SHR indica leitura compartilhada.

Se o programa estivesse atualizando diretamente um dataset, talvez fosse necessário:

DISP=OLD

Mas para leitura QSAM, SHR é a escolha mais comum.


9. O DD LISTCOPAC

//LISTCOPAC DD DSN=INFEF00.COPIA.MUNDO,DISP=SHR

Essa linha liga um nome lógico a um dataset físico.

O nome lógico é:

LISTCOPAC

O dataset físico é:

INFEF00.COPIA.MUNDO

No programa COBOL provavelmente existe:

SELECT ARQUIVO-COPAC
    ASSIGN TO LISTCOPAC
    ORGANIZATION IS SEQUENTIAL
    ACCESS MODE IS SEQUENTIAL
    FILE STATUS IS WS-FILE-STATUS.

O COBOL conhece o arquivo pelo nome lógico. O JCL informa qual dataset real será usado.

Essa separação é poderosa.

O mesmo programa pode processar arquivos diferentes sem precisar ser recompilado.

Hoje:

//LISTCOPAC DD DSN=INFEF00.COPIA.MUNDO,DISP=SHR

Amanhã:

//LISTCOPAC DD DSN=INFEF00.COPIA.MUNDO.NOVO,DISP=SHR

O programa continua igual.

É o JCL que conecta a aplicação ao dado certo.


10. O que é QSAM?

QSAM significa Queued Sequential Access Method.

É um método de acesso para datasets sequenciais.

O programa COBOL executa comandos simples:

OPEN INPUT
READ
CLOSE

Mas o QSAM trabalha nos bastidores com:

  • buffers;

  • blocos;

  • movimentação de dados;

  • controle de fim de arquivo;

  • tratamento de registros;

  • acesso ao dispositivo;

  • otimização de entrada e saída.

Imagine um arquivo com registros de 100 bytes:

LRECL=100

O sistema não precisa necessariamente ler um registro físico por operação de disco.

Ele pode ler um bloco contendo vários registros:

BLKSIZE=27900

Nesse caso, um bloco pode conter 279 registros de 100 bytes.

O programa continua pedindo um registro por vez, mas o QSAM já trouxe vários registros para a memória.

Essa é a magia silenciosa do buffering.


11. Exemplo de definição COBOL

Um arquivo poderia ter esta estrutura:

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.

    SELECT ARQ-COPAC
        ASSIGN TO LISTCOPAC
        ORGANIZATION IS SEQUENTIAL
        ACCESS MODE IS SEQUENTIAL
        FILE STATUS IS WS-FS-COPAC.

DATA DIVISION.
FILE SECTION.

FD  ARQ-COPAC
    RECORDING MODE IS F.

01  REG-COPAC.
    05 CP-CODIGO           PIC 9(05).
    05 CP-NOME             PIC X(40).
    05 CP-PAIS             PIC X(30).
    05 CP-POPULACAO        PIC 9(11).
    05 FILLER              PIC X(14).

O tamanho total seria:

5 + 40 + 30 + 11 + 14 = 100 bytes

Logo, o dataset deveria ter:

RECFM=FB
LRECL=100

Se o arquivo tiver LRECL=80, a definição estará incompatível.

O resultado pode ser:

  • erro de abertura;

  • registros truncados;

  • campos deslocados;

  • dados inválidos;

  • abend;

  • inserções incorretas no banco.


12. File Status: o sensor do arquivo

Todo programa COBOL profissional deveria usar FILE STATUS.

Exemplo:

01  WS-FS-COPAC         PIC XX.

Após o OPEN:

OPEN INPUT ARQ-COPAC

IF WS-FS-COPAC NOT = '00'
    DISPLAY 'ERRO NO OPEN DO ARQUIVO: ' WS-FS-COPAC
    MOVE 12 TO RETURN-CODE
    GOBACK
END-IF

Alguns estados comuns:

00 = operação concluída
10 = fim de arquivo
35 = arquivo não encontrado
39 = conflito de atributos
41 = arquivo já aberto
42 = arquivo não aberto
46 = tentativa inválida de READ

O status 39 merece atenção especial.

Ele pode ocorrer quando a definição física do arquivo não combina com o que o programa espera.

É o mainframe dizendo:

“Padawan, o arquivo existe, mas sua descrição não corresponde à realidade.”


13. O DD SYSTSPRT

//SYSTSPRT DD SYSOUT=*

O SYSTSPRT recebe mensagens do TSO e do command processor DSN.

É uma das saídas mais importantes para análise.

Ali podem aparecer:

  • início do DSN;

  • conexão com o subsistema;

  • processamento do comando RUN;

  • mensagens sobre PLAN;

  • falha na localização do programa;

  • retorno do ambiente Db2;

  • encerramento do command processor.

Quando o job falhar antes de entrar no programa COBOL, o SYSTSPRT deve ser uma das primeiras saídas examinadas.


14. O significado de SYSOUT=*

DD SYSOUT=*

Isso não significa “um arquivo chamado SYSOUT”.

SYSOUT=* envia a saída ao spool, usando normalmente a classe de saída definida pelo job.

Exemplo:

//SYSTSPRT DD SYSOUT=*

As mensagens poderão ser vistas no SDSF.

O asterisco significa, em termos práticos:

“Use a classe de saída padrão associada ao job.”

Também seria possível especificar uma classe:

//SYSTSPRT DD SYSOUT=X

A classe depende das regras da instalação.


15. O problema dos DDs duplicados

Na imagem aparecem duas linhas:

//SYSOUT DD SYSOUT=*
//SYSOUT DD SYSOUT=*

Esse é um provável erro.

Dentro do mesmo step, dois DD statements não devem possuir o mesmo DDNAME dessa maneira.

Se o programa precisa de duas saídas, use nomes diferentes:

//SYSOUT   DD SYSOUT=*
//SYSPRINT DD SYSOUT=*

Ou:

//RELATORI DD SYSOUT=*
//ERROS    DD SYSOUT=*

Os nomes devem corresponder ao que o programa espera.

Exemplo no COBOL:

SELECT ARQ-RELATORIO
    ASSIGN TO RELATORI.

SELECT ARQ-ERROS
    ASSIGN TO ERROS.

O JCL seria:

//RELATORI DD SYSOUT=*
//ERROS    DD SYSOUT=*

E se a intenção era concatenar?

A concatenação correta seria:

//ENTRADA  DD DSN=ARQUIVO.PARTE1,DISP=SHR
//         DD DSN=ARQUIVO.PARTE2,DISP=SHR

Observe que apenas a primeira linha possui DDNAME.

Portanto:

//SYSOUT DD SYSOUT=*
//SYSOUT DD SYSOUT=*

não é a maneira adequada de concatenar.


16. O DD SYSTSIN

//SYSTSIN DD *

O SYSTSIN fornece comandos ao ambiente TSO batch.

Tudo entre:

//SYSTSIN DD *

e:

/*

será entregue ao IKJEFT01.

No exemplo:

DSN SYSTEM(DB9G)
RUN PROGRAM(COBDB501) PLAN(HERP0001) -
    LIB('ADCD.Z112.USERLOAD')

Essas linhas não são JCL.

São comandos processados dentro do ambiente TSO/Db2.

Essa diferença é fundamental.

JCL:

//SYSTSIN DD *

Comando TSO:

DSN SYSTEM(DB9G)

Subcomando Db2:

RUN PROGRAM(COBDB501)

17. O comando DSN SYSTEM(DB9G)

DSN SYSTEM(DB9G)

O comando DSN inicia o command processor do Db2.

O parâmetro:

SYSTEM(DB9G)

identifica o subsistema desejado.

O nome de quatro caracteres:

DB9G

é o subsystem ID.

Em outros ambientes poderíamos encontrar:

DB2D
DB2T
DB2H
DB2P
D91A
DSN1

A convenção varia.

Por exemplo:

DB2D = desenvolvimento
DB2T = teste
DB2H = homologação
DB2P = produção

Mas essa nomenclatura não é uma regra do Db2. É apenas uma convenção possível.

Quando o comando é executado, o sistema tenta:

  1. localizar o subsistema;

  2. estabelecer o attachment;

  3. criar uma thread;

  4. validar o ambiente;

  5. preparar a execução do programa.

Se o subsistema estiver parado, o programa nem chegará a ser carregado.


18. O comando RUN PROGRAM

RUN PROGRAM(COBDB501)

O DSN recebe a ordem para executar o programa:

COBDB501

Esse nome deve corresponder ao load module ou program object gerado no link-edit.

O DSN não executa o fonte COBOL.

Ele procura algo como:

ADCD.Z112.USERLOAD(COBDB501)

O membro deve ser um executável válido.

Se o membro não existir, pode ocorrer uma mensagem de programa não encontrado ou até um abend S806, dependendo do ponto da falha.


19. A cláusula PLAN(HERP0001)

PLAN(HERP0001)

O PLAN representa o contexto de execução Db2 da aplicação.

Historicamente, o PLAN continha diretamente os DBRMs.

Nos ambientes modernos, é comum o PLAN apontar para PACKAGES:

PLAN
  ↓
PKLIST
  ↓
COLLECTION
  ↓
PACKAGE
  ↓
Seções SQL

O PLAN:

HERP0001

deve existir no subsistema DB9G.

Também precisa estar autorizado para o usuário que executa o job.

Caso contrário, podem surgir erros relacionados a:

  • autorização;

  • PLAN inexistente;

  • PACKAGE não localizado;

  • collection incorreta;

  • inconsistência entre programa e PACKAGE.


20. DBRM, PACKAGE e PLAN

Quando um programa COBOL contém SQL:

EXEC SQL
    INSERT INTO TB_COPAC
           (CODIGO, NOME, PAIS)
    VALUES (:CP-CODIGO, :CP-NOME, :CP-PAIS)
END-EXEC

o compilador COBOL não entende sozinho esse SQL.

Antes da compilação, ocorre o precompile Db2.

O precompiler produz dois resultados:

1. Fonte COBOL modificado
2. DBRM

O fonte modificado contém chamadas à interface Db2.

O DBRM contém as instruções SQL extraídas.

Fluxo completo:

COBOL + SQL
    ↓
PRECOMPILE
    ├── COBOL modificado
    └── DBRM
          ↓
      BIND PACKAGE
          ↓
       PACKAGE

Enquanto isso:

COBOL modificado
    ↓
COMPILER
    ↓
OBJECT MODULE
    ↓
LINK-EDIT
    ↓
LOAD MODULE

Na execução, o load module e o PACKAGE precisam pertencer à mesma geração lógica.


21. O clássico SQLCODE -818

O -818 ocorre quando o programa executável e o DBRM ou PACKAGE não possuem o mesmo consistency token.

Em linguagem Bellacosa:

O sabre de luz e o cristal kyber vieram de treinamentos diferentes.

Isso acontece quando:

  1. o programa é precompilado;

  2. é gerado um novo DBRM;

  3. o COBOL é compilado;

  4. o load module é atualizado;

  5. o PACKAGE antigo continua no Db2.

Ou o contrário:

  1. um novo PACKAGE é criado;

  2. o load module antigo continua na loadlib.

Resultado:

SQLCODE -818

A correção normalmente exige refazer o ciclo com os mesmos artefatos:

PRECOMPILE
COMPILE
LINK-EDIT
BIND PACKAGE

22. O SQLCODE -805

O -805 também é extremamente comum.

Ele indica que o Db2 não encontrou o PACKAGE necessário.

Possíveis causas:

  • PACKAGE não bindado;

  • collection errada;

  • PLAN não contém a collection;

  • nome do programa diferente;

  • ambiente errado;

  • PACKAGE removido;

  • load module promovido sem o BIND correspondente.

A mensagem geralmente fornece informações sobre:

  • localização;

  • collection;

  • package;

  • consistency token.

Esses dados devem ser analisados cuidadosamente.


23. A cláusula LIB

LIB('ADCD.Z112.USERLOAD')

A cláusula LIB informa a biblioteca onde o programa deve ser localizado.

Portanto:

RUN PROGRAM(COBDB501)
    PLAN(HERP0001)
    LIB('ADCD.Z112.USERLOAD')

significa:

Execute o programa COBDB501, utilizando o PLAN HERP0001, procurando o módulo na biblioteca ADCD.Z112.USERLOAD.

Essa instrução é importante porque a STEPLIB contém apenas a biblioteca Db2.

Sem a cláusula LIB, o programa da aplicação talvez não fosse encontrado.

Atenção à biblioteca antiga

A imagem também mostra:

//JOBLIB DD DSN=IBMUSER.CBL.LOADLIB,DISP=SHR

Temos então duas bibliotecas que podem conter o programa:

IBMUSER.CBL.LOADLIB
ADCD.Z112.USERLOAD

É importante confirmar onde está a versão correta.

Talvez exista:

IBMUSER.CBL.LOADLIB(COBDB501)

e também:

ADCD.Z112.USERLOAD(COBDB501)

Se a versão nova foi gravada na primeira biblioteca, mas o comando RUN aponta para a segunda, o job continuará executando o programa antigo.

Esse é um easter egg clássico do mainframe:

O código está correto, a compilação terminou com RC 0, mas o comportamento não mudou porque o job está carregando outra cópia do programa.

Para investigar, use ISPF 3.4 e compare:

  • data;

  • hora;

  • tamanho;

  • atributos;

  • usuário da última alteração.


24. O hífen de continuação

PLAN(HERP0001) -

O hífen informa que o comando continua na linha seguinte.

A continuação é:

LIB('ADCD.Z112.USERLOAD')

Sem o hífen, o DSN poderia tratar a primeira linha como um comando completo e a segunda como outro comando.

Uma versão bem formatada seria:

RUN PROGRAM(COBDB501) -
    PLAN(HERP0001)     -
    LIB('ADCD.Z112.USERLOAD')

O estilo depende do padrão da empresa, mas a legibilidade melhora bastante.


25. O comando END

O JCL da imagem não apresenta explicitamente:

END

É recomendável encerrar a sessão DSN:

//SYSTSIN DD *
  DSN SYSTEM(DB9G)
  RUN PROGRAM(COBDB501) PLAN(HERP0001) -
      LIB('ADCD.Z112.USERLOAD')
  END
/*

O fim do arquivo de comandos pode encerrar o command processor, mas o END deixa a intenção explícita.

É uma boa prática.


26. Exemplo completo de JCL corrigido

//CATPGM1 JOB (ACCT),'CARGA COBOL DB2',
//             CLASS=A,
//             MSGCLASS=X,
//             MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID
//*
//*-------------------------------------------------------------------*
//* PROGRAMA COBOL LE ARQUIVO QSAM E INSERE DADOS NO DB2
//*-------------------------------------------------------------------*
//EXECDB2 EXEC PGM=IKJEFT01,DYNAMNBR=20
//*
//* MODULOS DO DB2
//*
//STEPLIB DD DSN=DSN910.SDSNLOAD,DISP=SHR
//*
//* ARQUIVO SEQUENCIAL DE ENTRADA
//*
//LISTCOPAC DD DSN=INFEF00.COPIA.MUNDO,DISP=SHR
//*
//* SAIDAS DO AMBIENTE E DO PROGRAMA
//*
//SYSTSPRT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//CEEDUMP  DD SYSOUT=*
//*
//* COMANDOS TSO E DB2
//*
//SYSTSIN DD *
  DSN SYSTEM(DB9G)
  RUN PROGRAM(COBDB501) PLAN(HERP0001) -
      LIB('ADCD.Z112.USERLOAD')
  END
/*
//

27. Explicação do cartão JOB

//CATPGM1 JOB (ACCT),'CARGA COBOL DB2',

CATPGM1 é o nome do job.

(ACCT)

representa informações contábeis da instalação.

'CARGA COBOL DB2'

é uma descrição.


CLASS=A

CLASS=A

Define a classe de execução.

A classe pode determinar:

  • prioridade;

  • initiator;

  • limites;

  • ambiente;

  • políticas de scheduling.

O significado de A varia conforme a instalação.


MSGCLASS=X

MSGCLASS=X

Define a classe das mensagens de saída.

Ela influencia onde e como o spool será tratado.


MSGLEVEL=(1,1)

MSGLEVEL=(1,1)

Controla o nível de detalhes registrado no spool.

O primeiro valor controla a impressão das instruções JCL.

O segundo controla mensagens de alocação e execução.


NOTIFY=&SYSUID

NOTIFY=&SYSUID

Solicita que o usuário que submeteu o job seja notificado quando ele terminar.

&SYSUID é substituído pelo ID do usuário.


28. Exemplo de programa COBOL

       IDENTIFICATION DIVISION.
       PROGRAM-ID. COBDB501.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.

           SELECT ARQ-COPAC
               ASSIGN TO LISTCOPAC
               ORGANIZATION IS SEQUENTIAL
               ACCESS MODE IS SEQUENTIAL
               FILE STATUS IS WS-FS-COPAC.

       DATA DIVISION.
       FILE SECTION.

       FD  ARQ-COPAC
           RECORDING MODE IS F.

       01  REG-COPAC.
           05 CP-CODIGO           PIC 9(05).
           05 CP-NOME             PIC X(40).
           05 CP-PAIS             PIC X(30).
           05 CP-POPULACAO        PIC 9(11).
           05 FILLER              PIC X(14).

       WORKING-STORAGE SECTION.

       01  WS-FS-COPAC            PIC XX.
       01  WS-FIM-ARQUIVO         PIC X VALUE 'N'.
           88 FIM-ARQUIVO               VALUE 'S'.

       01  WS-CONTADORES.
           05 WS-LIDOS            PIC 9(09) COMP-3 VALUE ZERO.
           05 WS-INSERIDOS        PIC 9(09) COMP-3 VALUE ZERO.
           05 WS-REJEITADOS       PIC 9(09) COMP-3 VALUE ZERO.
           05 WS-COMMIT           PIC 9(05) COMP-3 VALUE ZERO.

       01  WS-SQLCODE-AUX         PIC -9(09).

           EXEC SQL
               INCLUDE SQLCA
           END-EXEC.

       PROCEDURE DIVISION.

       0000-PRINCIPAL.

           PERFORM 1000-INICIALIZAR

           PERFORM 2000-PROCESSAR
               UNTIL FIM-ARQUIVO

           PERFORM 9000-FINALIZAR

           GOBACK.

29. Inicialização do programa

       1000-INICIALIZAR.

           DISPLAY 'INICIO DO PROGRAMA COBDB501'

           OPEN INPUT ARQ-COPAC

           IF WS-FS-COPAC NOT = '00'
               DISPLAY 'ERRO NO OPEN LISTCOPAC: ' WS-FS-COPAC
               MOVE 12 TO RETURN-CODE
               GOBACK
           END-IF

           PERFORM 2100-LER-ARQUIVO.

A primeira leitura é realizada na inicialização.

Esse padrão evita que o programa execute o processamento com um registro não carregado.


30. Leitura e processamento

       2000-PROCESSAR.

           ADD 1 TO WS-LIDOS

           PERFORM 3000-VALIDAR-REGISTRO

           IF CP-CODIGO IS NUMERIC
               PERFORM 4000-INSERIR-DB2
           ELSE
               ADD 1 TO WS-REJEITADOS
               DISPLAY 'CODIGO INVALIDO: ' CP-CODIGO
           END-IF

           PERFORM 2100-LER-ARQUIVO.

A leitura:

       2100-LER-ARQUIVO.

           READ ARQ-COPAC
               AT END
                   SET FIM-ARQUIVO TO TRUE
               NOT AT END
                   CONTINUE
           END-READ

           IF WS-FS-COPAC NOT = '00'
              AND WS-FS-COPAC NOT = '10'
               DISPLAY 'ERRO DE LEITURA: ' WS-FS-COPAC
               PERFORM 8000-ROLLBACK
               MOVE 12 TO RETURN-CODE
               GOBACK
           END-IF.

O status 10 representa fim de arquivo e não deve ser tratado como erro.


31. O INSERT Db2

       4000-INSERIR-DB2.

           EXEC SQL
               INSERT INTO TB_COPAC
               (
                   CODIGO,
                   NOME,
                   PAIS,
                   POPULACAO
               )
               VALUES
               (
                   :CP-CODIGO,
                   :CP-NOME,
                   :CP-PAIS,
                   :CP-POPULACAO
               )
           END-EXEC

           EVALUATE SQLCODE
               WHEN ZERO
                   ADD 1 TO WS-INSERIDOS
                   ADD 1 TO WS-COMMIT

               WHEN -803
                   ADD 1 TO WS-REJEITADOS
                   DISPLAY 'CHAVE DUPLICADA: ' CP-CODIGO

               WHEN OTHER
                   MOVE SQLCODE TO WS-SQLCODE-AUX
                   DISPLAY 'ERRO SQL: ' WS-SQLCODE-AUX
                   DISPLAY 'REGISTRO: ' CP-CODIGO
                   PERFORM 8000-ROLLBACK
                   MOVE 12 TO RETURN-CODE
                   GOBACK
           END-EVALUATE

           IF WS-COMMIT >= 1000
               PERFORM 7000-COMMIT
           END-IF.

32. Por que não fazer apenas um COMMIT no final?

Imagine uma carga de dez milhões de registros.

Se o programa executar um único COMMIT, poderá manter durante horas:

  • locks;

  • espaço de log;

  • páginas modificadas;

  • recursos;

  • unidade de trabalho gigantesca.

Se ocorrer erro no registro 9.999.999, o rollback poderá desfazer tudo.

Uma estratégia mais segura é:

COMMIT a cada 1.000 registros

Ou:

COMMIT a cada 5.000 registros

O valor ideal depende do ambiente.

Não existe um número mágico.

É preciso considerar:

  • volume;

  • concorrência;

  • log;

  • índices;

  • tempo de recuperação;

  • SLA;

  • restart;

  • regras de negócio.

Rotina:

       7000-COMMIT.

           EXEC SQL
               COMMIT
           END-EXEC

           IF SQLCODE NOT = ZERO
               DISPLAY 'ERRO NO COMMIT: ' SQLCODE
               MOVE 12 TO RETURN-CODE
               GOBACK
           END-IF

           MOVE ZERO TO WS-COMMIT.

33. ROLLBACK

       8000-ROLLBACK.

           EXEC SQL
               ROLLBACK
           END-EXEC

           DISPLAY 'ROLLBACK EXECUTADO'.

O rollback desfaz alterações realizadas desde o último commit.

Mas atenção: ele não desfaz tudo desde o início do programa se já ocorreram commits intermediários.

Exemplo:

1.000 registros → COMMIT
1.000 registros → COMMIT
500 registros → erro → ROLLBACK

Nesse caso:

  • os primeiros 2.000 permanecem;

  • os últimos 500 são desfeitos.


34. Finalização

       9000-FINALIZAR.

           IF WS-COMMIT > ZERO
               PERFORM 7000-COMMIT
           END-IF

           CLOSE ARQ-COPAC

           DISPLAY '--------------------------------'
           DISPLAY 'REGISTROS LIDOS     : ' WS-LIDOS
           DISPLAY 'REGISTROS INSERIDOS : ' WS-INSERIDOS
           DISPLAY 'REGISTROS REJEITADOS: ' WS-REJEITADOS
           DISPLAY 'FIM DO PROGRAMA COBDB501'
           DISPLAY '--------------------------------'.

Esses totais serão enviados ao SYSOUT ou à saída padrão do Language Environment, dependendo da configuração.

Eles são fundamentais para auditoria.


35. Códigos de retorno

O programa pode usar:

MOVE 0 TO RETURN-CODE

para sucesso.

Outros códigos comuns:

RC 0  = sucesso
RC 4  = sucesso com aviso
RC 8  = erro de aplicação
RC 12 = erro grave
RC 16 = falha crítica

Esses valores são convenções. A empresa pode definir outros padrões.

Um scheduler pode usar o retorno:

RC <= 4  → continuar
RC >= 8  → interromper fluxo

36. Possíveis falhas

JCL ERROR

Causa provável:

//SYSOUT DD SYSOUT=*
//SYSOUT DD SYSOUT=*

Correção: usar DDNAMEs distintos.


S806

O programa não foi encontrado.

Verificar:

ADCD.Z112.USERLOAD(COBDB501)

S013

Pode estar relacionado à incompatibilidade de atributos do dataset.

Verificar:

  • RECFM;

  • LRECL;

  • BLKSIZE;

  • modo de abertura;

  • definição FD.


SQLCODE -803

Chave duplicada.

O programa tentou inserir um valor único já existente.


SQLCODE -805

PACKAGE não encontrado.

Verificar:

  • collection;

  • BIND PACKAGE;

  • PLAN;

  • consistency token.


SQLCODE -818

Load module e PACKAGE incompatíveis.

Refazer compilação e BIND de forma sincronizada.


SQLCODE -904

Recurso indisponível.

Pode existir:

  • tablespace parado;

  • utility pendente;

  • objeto em estado restritivo;

  • problema de storage.


SQLCODE -911

Deadlock ou timeout com rollback.

A aplicação precisa registrar:

  • chave;

  • SQLCODE;

  • unidade de trabalho;

  • momento do erro.


SQLCODE -922

Problema de autorização ou conexão.

Verificar permissões para:

  • PLAN;

  • PACKAGE;

  • tabela;

  • operação INSERT;

  • subsistema.


37. Como investigar no SDSF

Depois de submeter o job, abra:

SDSF

Depois:

ST

Localize o job e abra com S.

Analise na seguinte ordem:

JESJCL

Mostra o JCL interpretado.

Útil para detectar:

  • erro de sintaxe;

  • DD duplicado;

  • PROC expandida;

  • símbolos substituídos.

JESYSMSG

Mostra mensagens do sistema.

Procure por prefixos:

IEF
IEC
IGD
ICH

SYSTSPRT

Mostra mensagens do TSO e DSN.

SYSOUT

Mostra os DISPLAY do COBOL.

CEEDUMP

Mostra informações de falhas do Language Environment.

SYSUDUMP

Pode conter detalhes de abend e registradores.


38. Easter egg: o programa antigo que nunca morre

Um dos problemas mais misteriosos do mainframe ocorre assim:

  1. o programador altera o fonte;

  2. compila com RC 0;

  3. submete o job;

  4. o comportamento antigo continua;

  5. compila novamente;

  6. nada muda;

  7. começa a desconfiar do compilador, do Db2 e talvez da própria realidade.

A causa:

O load module foi gravado em uma biblioteca,
mas o RUN está usando outra.

Exemplo:

Novo:
IBMUSER.CBL.LOADLIB(COBDB501)

Executado:
ADCD.Z112.USERLOAD(COBDB501)

O mainframe não está errado.

Ele apenas obedeceu exatamente ao que foi pedido.

Essa é uma das grandes lições do IBM Z:

O sistema raramente faz o que imaginamos. Ele faz o que realmente declaramos.


39. O grande mapa da missão

INFEF00.COPIA.MUNDO
          │
          │ LISTCOPAC
          ▼
     Programa COBOL
       COBDB501
          │
          │ EXEC SQL INSERT
          ▼
      PLAN HERP0001
          │
          ▼
        PACKAGE
          │
          ▼
       Db2 DB9G
          │
          ▼
       TB_COPAC

Ao redor dessa execução, estão:

JES2
IKJEFT01
SDSNLOAD
TSO Attachment
Language Environment
QSAM
RACF
SDSF
Logs do Db2

Nada acontece isoladamente.


Conclusão: poucas linhas, muitas engrenagens

O JCL apresentado é um excelente exemplo da filosofia mainframe: comandos pequenos podem coordenar estruturas gigantescas.

Uma linha como:

RUN PROGRAM(COBDB501) PLAN(HERP0001)

carrega décadas de evolução tecnológica.

Ela conecta:

  • um executável COBOL;

  • um ambiente TSO batch;

  • um subsistema Db2;

  • um PLAN;

  • PACKAGES;

  • SQL estático;

  • um arquivo sequencial;

  • QSAM;

  • segurança;

  • logging;

  • controle transacional.

Para o programador COBOL padawan, a principal lição é não observar o JCL como uma simples receita de execução.

Cada DD representa um contrato.

LISTCOPAC → contrato com o arquivo
STEPLIB   → contrato com as bibliotecas
SYSTSIN   → contrato com o TSO
SYSTSPRT  → contrato com as mensagens
PLAN      → contrato com o Db2
LIB       → contrato com o executável
COMMIT    → contrato com a integridade

Quando esses contratos estão alinhados, milhões de registros podem ser processados com precisão.

Quando um deles está errado, o sistema pode responder com um JCL ERROR, um S806, um -805, um -818 ou um silencioso arquivo lido com layout incorreto.

O verdadeiro domínio do mainframe não vem apenas de decorar comandos.

Ele nasce quando entendemos a jornada completa:

Fonte
  → Precompile
  → Compile
  → Link-edit
  → DBRM
  → BIND PACKAGE
  → BIND PLAN
  → JCL
  → IKJEFT01
  → DSN
  → COBOL
  → QSAM
  → Db2
  → COMMIT
  → SDSF

E assim, diante da tela verde, com uma caneca de café ao lado, o padawan percebe que aquele pequeno JCL não é apenas um arquivo de comandos.

É a porta de entrada para uma das arquiteturas de processamento corporativo mais robustas já construídas.

terça-feira, 22 de novembro de 2022

BIND PACKAGE e BIND PLAN sem Mistérios O guia do programador COBOL Padawan para transformar um DBRM em SQL executável no Db2 for z/OS

 

Bellacosa Mainframe apresenta bind package e bin plan sem misterios

☕ Um Café no Bellacosa Mainframe

BIND PACKAGE e BIND PLAN sem Mistérios

O guia do programador COBOL Padawan para transformar um DBRM em SQL executável no Db2 for z/OS

Imagine a seguinte cena.

Você passou horas escrevendo um programa COBOL. Revisou a WORKING-STORAGE, declarou corretamente as host variables, incluiu a SQLCA, fechou todos os END-EXEC, compilou o fonte e recebeu um belo retorno RC=0000.

Orgulhoso, você executa o programa.

De repente, o Db2 responde:

SQLCODE = -805

Ou talvez:

SQLCODE = -818

Você olha para o load module, olha para o JCL e pensa:

“Mas o programa compilou! Por que o Db2 diz que não consegue executá-lo?”

É nesse momento que o programador COBOL Padawan descobre uma das grandes verdades do universo mainframe:

Um programa COBOL com SQL não vive apenas dentro do load module.

Ele possui duas metades.

A primeira metade é o código executável:

Fonte COBOL
   ↓
Compilação
   ↓
Objeto
   ↓
Link-edit
   ↓
Load module

A segunda metade é o SQL preparado para o Db2:

SQL embutido
   ↓
DBRM
   ↓
BIND PACKAGE
   ↓
PACKAGE
   ↓
PLAN

Essas duas metades precisam permanecer sincronizadas. Quando isso não acontece, surgem os lendários -805, -818, packages não encontrados, plans inconsistentes e deploys que pareciam perfeitos até o momento em que chegaram à produção.

Neste artigo, vamos desmontar cuidadosamente o JCL apresentado, explicar cada instrução, compreender a relação entre DBRM, package e plan, analisar os erros mais frequentes e construir uma versão mais segura, legível e moderna.

Prepare o café. O ritual do BIND vai começar.


Bellacosa Mainframe e o bind no db2

1. O que é o BIND no Db2?

O BIND é o processo por meio do qual o Db2 recebe as informações SQL extraídas de um programa e as transforma em uma estrutura executável chamada package.

Quando você escreve:

EXEC SQL
    SELECT NOME_CLIENTE
      INTO :WS-NOME-CLIENTE
      FROM CLIENTES
     WHERE COD_CLIENTE = :WS-COD-CLIENTE
END-EXEC.

o compilador COBOL não sabe interpretar diretamente:

EXEC SQL

Por isso, antes da compilação propriamente dita, o programa passa por um precompiler Db2.

O precompiler separa o programa em duas trilhas.

Trilha COBOL

Ele produz um fonte COBOL modificado, no qual as instruções SQL são substituídas por chamadas apropriadas ao runtime Db2.

Trilha Db2

Ele produz um arquivo chamado:

DBRM

DBRM significa:

Database Request Module

O DBRM contém a representação das instruções SQL estáticas encontradas no programa.

Depois, o comando BIND PACKAGE lê esse DBRM e cria o package.

Em termos simples:

DBRM + opções de BIND + catálogo Db2 = PACKAGE

Durante o BIND, o Db2 pode:

  • verificar os objetos referenciados;

  • conferir autorizações;

  • resolver tabelas, views e aliases;

  • avaliar índices disponíveis;

  • selecionar caminhos de acesso;

  • registrar dependências;

  • definir isolamento;

  • definir encoding;

  • armazenar informações no catálogo;

  • criar o package que será usado durante a execução.

Portanto, o BIND não é uma simples cópia.

Ele é uma etapa de preparação, validação, autorização e otimização.


2. O JCL analisado

O trecho apresentado segue esta estrutura:

//BINDPK03 EXEC PGM=IKJEFT01,COND=(4,LT,COB),
//             DYNAMNBR=20
//DBRMLIB  DD DSN=&&DBRMDB2(COBDB501),DISP=(OLD,DELETE)
//SYSUDUMP DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSTSIN  DD *

 DSN SYSTEM(DB9G)

 BIND PACKAGE(HERPACK) OWNER(INEFE00) MEMBER(COBDB501) -
 ACTION(REP) VALIDATE(BIND) EXPLAIN(NO) DEGREE(1) ISO(UR) -
 ENABLE(*) ENCODING(EBCDIC)

 BIND PLAN(HERP0001) -
 PKLIST(HERPACK.*,HERPACK.COBDB501) -
 ACTION(REP) ISO(UR) ENCODING(EBCDIC)

/*

Esse passo possui duas grandes responsabilidades:

  1. Criar ou substituir o package HERPACK.COBDB501.

  2. Criar ou substituir o plan HERP0001.

Vamos analisar cada parte.


3. O step BINDPK03

//BINDPK03 EXEC PGM=IKJEFT01,COND=(4,LT,COB),
//             DYNAMNBR=20

O nome do step é:

BINDPK03

O nome não possui significado obrigatório para o sistema. Ele é uma convenção criada pela equipe.

Pode significar:

BIND = etapa de bind
PK   = package
03   = terceiro passo do processo

Uma boa nomenclatura ajuda muito durante a análise do spool.

Compare:

//STEP7 EXEC ...

com:

//BINDPKG EXEC ...

Quando há uma falha às duas da manhã, o segundo nome é muito mais amigável.


4. Por que executar IKJEFT01?

PGM=IKJEFT01

O IKJEFT01 permite executar comandos TSO em batch.

Normalmente, pensamos no TSO como uma sessão interativa acessada pelo terminal 3270. Porém, o IKJEFT01 cria uma espécie de sessão TSO sem uma pessoa digitando comandos.

Por meio dele, podemos executar:

  • comandos TSO;

  • CLIST;

  • execs REXX;

  • o command processor DSN;

  • alguns programas Db2;

  • comandos de execução de programas COBOL Db2.

No exemplo, ele recebe os comandos pelo DD:

//SYSTSIN DD *

O primeiro comando importante será:

DSN SYSTEM(DB9G)

Esse comando inicia o processador DSN e conecta o job ao subsistema Db2 chamado DB9G.

IKJEFT01 versus IKJEFT1B

Um detalhe interessante é que muitas instalações preferem:

PGM=IKJEFT1B

O IKJEFT1B é semelhante ao IKJEFT01, mas costuma ser escolhido em procedures nas quais a propagação do return code dos comandos internos precisa ser mais previsível.

Em uma esteira de build, isso é extremamente importante.

Imagine que o BIND falhou, mas o step terminou com retorno aparentemente aceitável. O pipeline poderia continuar, copiar o load module e declarar sucesso.

Resultado:

Load module novo
Package antigo

Na execução, aparece:

SQLCODE -818

Por isso, não basta olhar apenas o nome do programa executado. É preciso testar como a procedure trata códigos de retorno.


5. Entendendo COND=(4,LT,COB)

COND=(4,LT,COB)

A cláusula COND é uma das pegadinhas clássicas do JCL.

Ela não informa diretamente quando o step será executado. Ela informa quando o step deverá ser ignorado.

A expressão significa:

Se 4 for menor que o RC do step COB, não execute este step.

Em forma matemática:

4 < COB.RC

Tabela prática

RC do step COB4 menor que RC?Executa o BIND?
0NãoSim
4NãoSim
8SimNão
12SimNão
16SimNão

Portanto, o BIND será executado se o step COB retornar:

RC 0 ou RC 4

Isso faz sentido porque compiladores COBOL frequentemente usam:

RC 0 = compilação limpa
RC 4 = mensagens ou warnings aceitáveis
RC 8 = erro que impede uso seguro
RC 12+ = falha grave

Versão mais legível com IF

Uma alternativa moderna e mais fácil para iniciantes seria:

// IF COB.RC LE 4 THEN
//BINDPKG EXEC PGM=IKJEFT1B,DYNAMNBR=20
...
// ENDIF

Essa versão comunica diretamente a intenção:

Execute o bind se o retorno da compilação for menor ou igual a 4.

O COND é poderoso, mas sua lógica invertida provoca muitos erros de interpretação.


6. DYNAMNBR=20

DYNAMNBR=20

Esse parâmetro define uma quantidade de alocações dinâmicas que o ambiente TSO poderá administrar.

Durante a execução, o processador DSN e outros componentes podem precisar abrir datasets dinamicamente.

O valor 20 costuma ser suficiente para procedimentos simples.

Entretanto, em execuções mais complexas, encontramos:

DYNAMNBR=50

ou:

DYNAMNBR=100

Não existe mérito em aumentar esse número sem necessidade. Porém, um valor pequeno demais pode provocar falhas de alocação.

A regra é:

Use o valor padronizado pela instalação e ajuste apenas quando houver evidência de necessidade.


7. DBRMLIB: onde está o mapa SQL

//DBRMLIB DD DSN=&&DBRMDB2(COBDB501),DISP=(OLD,DELETE)

Esse DD aponta para o DBRM criado em uma etapa anterior.

Vamos desmontar a instrução.

DBRMLIB

É o DDNAME esperado pelo processo de BIND para localizar os DBRMs.

&&DBRMDB2

Os dois sinais de && indicam um dataset temporário criado dentro do job.

Ele provavelmente foi criado pelo precompiler com algo semelhante a:

//DBRMLIB DD DSN=&&DBRMDB2,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(TRK,(5,5,5)),
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0),
//            DSNTYPE=LIBRARY

O PASS indica que o dataset deve ser passado para outro step do mesmo job.

No step de BIND, ele é reutilizado.

(COBDB501)

Esse é o nome do membro contendo o DBRM.

Geralmente, o nome acompanha o programa:

Programa:    COBDB501
DBRM:        COBDB501
Package:     COBDB501
Load module: COBDB501

Essa padronização facilita:

  • pesquisa no catálogo;

  • leitura de spool;

  • manutenção;

  • comparação entre versões;

  • análise de problemas.

DISP=(OLD,DELETE)

OLD solicita acesso exclusivo ao dataset.

DELETE solicita sua remoção após o uso.

Isso é aceitável para um dataset temporário, mas existe um ponto de atenção.

Se o DBRM for apagado e não houver uma biblioteca permanente, uma falha posterior pode dificultar:

  • análise;

  • rebind;

  • comparação;

  • reconstrução do package;

  • rollback.

Em esteiras corporativas, o DBRM muitas vezes é preservado em uma biblioteca controlada por versão.


8. Uma possível redundância no DBRMLIB

O JCL usa:

DSN=&&DBRMDB2(COBDB501)

e o comando BIND usa:

MEMBER(COBDB501)

Ou seja, o membro está sendo indicado duas vezes.

O formato mais comum e flexível seria:

//DBRMLIB DD DSN=&&DBRMDB2,DISP=(OLD,DELETE)

E depois:

MEMBER(COBDB501)

Assim, DBRMLIB aponta para a biblioteca, enquanto MEMBER informa qual membro será processado.

Isso também permite executar vários binds com a mesma biblioteca:

BIND PACKAGE(APPPACK) MEMBER(PROGA)
BIND PACKAGE(APPPACK) MEMBER(PROGB)
BIND PACKAGE(APPPACK) MEMBER(PROGC)

9. Os DDs de saída

SYSUDUMP

//SYSUDUMP DD SYSOUT=*

Solicita um dump caso ocorra um abend.

O dump pode conter:

  • PSW;

  • registradores;

  • áreas de memória;

  • módulos carregados;

  • endereço da falha;

  • cadeia de chamadas.

Para erros comuns de BIND, normalmente você consultará primeiro o SYSTSPRT. Porém, se o processador sofrer um abend, SYSUDUMP pode ser indispensável.


SYSTSPRT

//SYSTSPRT DD SYSOUT=*

É a saída principal do ambiente TSO batch.

Nela aparecem:

  • mensagens do IKJEFT01;

  • mensagens do processador DSN;

  • confirmação da conexão;

  • mensagens de BIND;

  • return codes;

  • mensagens DSN;

  • erros de autorização;

  • packages criados ou substituídos.

Esta deve ser uma das primeiras saídas verificadas no SDSF.


SYSPRINT

//SYSPRINT DD SYSOUT=*

É uma saída genérica usada por diversos utilitários.

Dependendo da procedure, pode conter mensagens adicionais ou ficar praticamente vazia.

O importante para o iniciante é não assumir que todos os erros estarão em um único DD. Em incidentes, consulte:

JESMSGLG
JESJCL
JESYSMSG
SYSTSPRT
SYSPRINT
SYSUDUMP

10. SYSTSIN: os comandos começam aqui

//SYSTSIN DD *

O conteúdo abaixo é enviado como entrada para a sessão TSO batch.

O primeiro comando é:

DSN SYSTEM(DB9G)

Esse comando solicita conexão com o subsistema Db2 DB9G.

O identificador poderia representar:

DB9G = Db2 geração 9, ambiente G

ou qualquer convenção local.

Algumas empresas usam:

DB2D = desenvolvimento
DB2Q = qualidade
DB2P = produção

Outras usam códigos menos óbvios, especialmente em data sharing.

Um erro simples no SSID pode fazer o bind ocorrer no ambiente errado ou falhar completamente.

Imagine promover um package acreditando estar em homologação e descobrir que o SSID era o de produção. É por isso que procedures bem desenhadas recebem o subsistema por parâmetro:

// SET DB2SSID=DB9G

E depois:

DSN SYSTEM(&DB2SSID)

11. BIND PACKAGE(HERPACK)

BIND PACKAGE(HERPACK)

O nome entre parênteses representa a collection.

A collection é um agrupador lógico de packages.

Neste caso:

Collection: HERPACK
Package:    COBDB501

O package final será identificado conceitualmente como:

HERPACK.COBDB501

Collections podem representar:

  • uma aplicação;

  • um domínio;

  • uma versão;

  • um ambiente;

  • uma release;

  • uma equipe;

  • uma linha de negócio.

Exemplos:

FINANCEIRO
PAGAMENTOS
CADASTRO
APPV1
APPV2
HOMOLOG
PROD

Dica de arquitetura

Usar collections diferentes por versão pode facilitar rollback.

Exemplo:

APPV001.COBDB501
APPV002.COBDB501

O novo package pode ser preparado sem remover imediatamente o anterior.

Depois, o plan ou a estratégia de execução passa a usar a nova collection.

Isso é muito mais seguro do que substituir tudo sem possibilidade de retorno.


12. OWNER(INEFE00)

OWNER(INEFE00)

Define o authorization ID proprietário do package.

Esse owner pode influenciar:

  • propriedade;

  • administração;

  • autorizações;

  • resolução de privilégios;

  • auditoria;

  • governança.

Se INEFE00 for um usuário pessoal, isso merece atenção.

Imagine que a pessoa:

  • mudou de projeto;

  • saiu da empresa;

  • teve o usuário revogado;

  • perdeu privilégios;

  • teve o ID protegido por nova política.

Por isso, packages de produção geralmente deveriam pertencer a IDs funcionais, como:

APPDB2O
BINDUSER
FINOWNER
HEROWNR

O owner funcional reduz dependência de indivíduos.


13. MEMBER(COBDB501)

MEMBER(COBDB501)

Essa opção informa qual DBRM será utilizado.

O Db2 procurará o membro dentro da biblioteca indicada por:

//DBRMLIB

Se o membro não existir, o BIND falhará.

Antes de culpar o Db2, verifique:

  • o precompile foi executado?

  • o DBRM foi realmente criado?

  • o dataset foi passado com DISP=PASS?

  • o nome do membro está correto?

  • houve exclusão prematura do dataset?

  • o DBRMLIB aponta para a biblioteca correta?


14. ACTION(REP)

ACTION(REP)

REP significa REPLACE.

Isso informa:

Se o package já existir, substitua-o.

É uma opção comum em ambientes de desenvolvimento, mas precisa de cautela em produção.

Substituir um package pode alterar:

  • access path;

  • comportamento de locking;

  • consumo de CPU;

  • tempo de resposta;

  • dependências;

  • compatibilidade com o load module.

O package antigo pode ter sido extremamente estável. O novo BIND pode escolher um caminho de acesso diferente por causa de:

  • estatísticas atualizadas;

  • novo índice;

  • índice removido;

  • alteração de cardinalidade;

  • mudança de parâmetros;

  • nova versão do Db2;

  • mudança no catálogo.

Dica importante

Antes de substituir packages críticos, considere mecanismos de preservação e reutilização de access paths, além do gerenciamento de planos oferecido pelo Db2.

Em outras palavras:

Não trate ACTION(REPLACE) como um simples “salvar por cima”.


15. VALIDATE(BIND)

VALIDATE(BIND)

Solicita validação no momento do BIND.

Se o programa referencia:

SELECT NOME
  FROM CLIENTES;

o Db2 tentará verificar a existência e a validade dos objetos durante o bind.

Isso é positivo porque antecipa problemas.

Melhor descobrir durante o deploy que a tabela está ausente do que descobrir durante o fechamento financeiro.

A alternativa conhecida é:

VALIDATE(RUN)

Nesse caso, parte da validação pode ocorrer apenas quando a instrução for executada.

Comparação

OpçãoMomento da validaçãoPrincipal benefícioPrincipal risco
BINDDurante a implantaçãoFalha antecipadaExige ambiente pronto
RUNDurante a execuçãoMaior flexibilidadeErro pode aparecer em produção

Para programas batch tradicionais, VALIDATE(BIND) costuma ser uma escolha mais segura.


16. EXPLAIN(NO)

EXPLAIN(NO)

Indica que o BIND não deve gerar informações de EXPLAIN nas tabelas correspondentes.

O EXPLAIN ajuda a entender o caminho de acesso escolhido pelo otimizador.

Ele pode revelar:

  • table space scan;

  • index scan;

  • matching index;

  • nonmatching index;

  • sort;

  • prefetch;

  • ordem de join;

  • método de join;

  • paralelismo;

  • estimativas de custo.

Em desenvolvimento, EXPLAIN(YES) pode ser extremamente útil.

Por exemplo:

EXPLAIN(YES)

permite analisar por que um SQL está fazendo table scan mesmo com um índice aparentemente disponível.

Por que alguém usa EXPLAIN(NO)?

Possíveis razões:

  • reduzir geração de dados;

  • procedure antiga;

  • ambiente sem tabelas EXPLAIN configuradas;

  • processo separado de análise;

  • ferramenta externa de monitoramento;

  • package simples e já conhecido.

Entretanto, para SQL crítico, abrir mão completamente da análise de access path é arriscado.


17. DEGREE(1)

DEGREE(1)

Define grau de paralelismo igual a 1.

Em linguagem prática:

Execute sem paralelismo de query.

Isso pode ser apropriado para:

  • transações curtas;

  • consultas pequenas;

  • programas COBOL tradicionais;

  • workloads sensíveis ao consumo;

  • ambientes que desejam comportamento previsível.

Uma alternativa seria:

DEGREE(ANY)

permitindo que o Db2 avalie paralelismo.

Vantagem de DEGREE(1)

  • previsibilidade;

  • menor risco de consumo inesperado;

  • comportamento mais estável;

  • adequado a muitas transações OLTP.

Desvantagem

Consultas analíticas grandes podem demorar mais.

Não existe valor universalmente melhor. A escolha depende do workload.


18. ISO(UR): velocidade com risco

ISO(UR)

UR significa:

Uncommitted Read

É o nível de isolamento mais permissivo.

Ele pode permitir a leitura de dados ainda não confirmados por outra transação.

Exemplo:

Uma transação altera um saldo:

UPDATE CONTA
   SET SALDO = 100
 WHERE NUMERO = 999;

Mas ainda não executou COMMIT.

Um programa com UR pode enxergar o valor 100.

Depois, a transação executa:

ROLLBACK;

O dado lido nunca foi realmente confirmado.

Essa é a chamada leitura suja.

Quando UR pode ser aceitável?

  • relatório estatístico;

  • dashboard;

  • consulta aproximada;

  • monitoração;

  • dados sem impacto transacional;

  • leitura informativa.

Quando UR é perigoso?

  • saldo;

  • faturamento;

  • pagamento;

  • estoque;

  • contabilidade;

  • cálculo fiscal;

  • autorização de crédito;

  • tomada de decisão baseada em consistência.

Para muitos programas COBOL corporativos, CS pode ser uma opção mais equilibrada:

ISOLATION(CS)

CS significa Cursor Stability.

O grande conselho é:

Nunca copie ISO(UR) de outro JCL sem entender a regra de negócio.


19. ENABLE(*)

ENABLE(*)

Essa cláusula está associada à habilitação do package para contextos de conexão suportados.

O asterisco representa uma habilitação ampla.

Ela pode ser conveniente em procedures genéricas, mas vale avaliar se o package realmente precisa estar habilitado para todos os contextos aplicáveis.

A lógica de segurança é semelhante ao princípio do menor privilégio:

Habilite apenas o necessário.

Em muitas instalações, esse detalhe é controlado por padrões corporativos e deve ser validado com o DBA.


20. ENCODING(EBCDIC)

ENCODING(EBCDIC)

Define EBCDIC como encoding do package.

Isso é natural para muitos programas COBOL executados em z/OS.

Porém, encoding não é um detalhe cosmético.

Ele afeta:

  • caracteres acentuados;

  • conversão de strings;

  • integração com ASCII;

  • Unicode;

  • APIs;

  • XML;

  • JSON;

  • Java;

  • clientes distribuídos.

Imagine os caracteres:

ã
é
ç

Em ambientes com conversão incorreta, eles podem virar símbolos estranhos ou provocar erros de conversão.

Quando uma aplicação COBOL conversa com sistemas distribuídos, é necessário observar:

  • CCSID das tabelas;

  • CCSID da aplicação;

  • encoding do package;

  • encoding da conexão;

  • formato dos dados recebidos.


21. BIND PLAN(HERP0001)

BIND PLAN(HERP0001)

Depois do package, o JCL cria ou substitui um plan chamado:

HERP0001

O plan fornece um contexto de execução e referencia packages.

Visualmente:

PLAN HERP0001
    |
    +-- HERPACK.COBDB501
    +-- HERPACK.COBDB502
    +-- HERPACK.COBDB503

Historicamente, plans tinham um papel ainda mais central. Com a adoção de packages, passou a ser comum pensar assim:

DBRM → PACKAGE
PLAN → lista de packages

22. PKLIST e a possível redundância

PKLIST(HERPACK.*,HERPACK.COBDB501)

A PKLIST informa onde o plan deverá procurar packages.

A entrada:

HERPACK.*

significa, de forma geral:

Packages da collection HERPACK

A entrada:

HERPACK.COBDB501

indica especificamente o package COBDB501.

O problema é que:

HERPACK.*

aparentemente já cobre:

HERPACK.COBDB501

Portanto, há uma possível redundância.

Para um plan exclusivo:

PKLIST(HERPACK.COBDB501)

Para um plan compartilhado:

PKLIST(HERPACK.*)

A lista original pode ter sido criada por:

  • geração automática;

  • procedure antiga;

  • manutenção parcial;

  • tentativa de reforçar a resolução;

  • simples excesso de cautela.

Mesmo quando algo funciona, vale perguntar:

Está claro, necessário e fácil de manter?


23. Um JCL completo de precompile, compile, link e bind

A seguir, um exemplo educacional simplificado.

Passo 1 — Precompile

//PRECOMP EXEC PGM=DSNHPC,
//             PARM='HOST(IBMCOB),APOST,SQL'
//STEPLIB  DD DISP=SHR,DSN=DB2.SDSNLOAD
//SYSIN    DD DISP=SHR,DSN=ALUNO.COBOL(COBDB501)
//SYSCIN   DD DSN=&&COBSRC,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(TRK,(10,5)),
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
//DBRMLIB  DD DSN=&&DBRMDB2,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(TRK,(5,5,5)),
//            DSNTYPE=LIBRARY,
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
//SYSPRINT DD SYSOUT=*
//SYSTERM  DD SYSOUT=*

O que acontece?

  • DSNHPC executa o precompiler.

  • SYSIN contém o fonte COBOL com SQL.

  • SYSCIN recebe o fonte COBOL modificado.

  • DBRMLIB recebe o DBRM.

  • SYSPRINT recebe mensagens.


Passo 2 — Compile

//COB EXEC PGM=IGYCRCTL,
//         PARM='LIB,OBJECT,LIST,MAP,XREF'
//SYSIN    DD DSN=&&COBSRC,DISP=(OLD,DELETE)
//SYSLIN   DD DSN=&&OBJ,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(TRK,(10,5)),
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
//SYSLIB   DD DISP=SHR,DSN=DB2.SDSNMACS
//SYSPRINT DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//SYSUT1   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT2   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT3   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT4   DD UNIT=SYSDA,SPACE=(CYL,(1,1))

O que acontece?

  • IGYCRCTL executa o compilador COBOL.

  • SYSIN recebe o fonte alterado pelo precompiler.

  • SYSLIN recebe o object module.

  • SYSPRINT contém a listagem.


Passo 3 — Link-edit

//LKED EXEC PGM=IEWL,
//         PARM='LIST,XREF,LET,MAP'
//SYSLIN   DD DSN=&&OBJ,DISP=(OLD,DELETE)
//         DD DISP=SHR,DSN=DB2.SDSNLOAD(DSNELI)
//SYSLMOD  DD DISP=SHR,DSN=ALUNO.LOADLIB(COBDB501)
//SYSLIB   DD DISP=SHR,DSN=CEE.SCEELKED
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD UNIT=SYSDA,SPACE=(CYL,(1,1))

O que acontece?

  • IEWL executa o binder/link-editor.

  • SYSLIN recebe o object module.

  • O módulo de interface Db2 é incluído conforme o tipo de execução.

  • SYSLMOD recebe o load module final.


Passo 4 — Bind package e plan

// IF COB.RC LE 4 THEN
//BIND EXEC PGM=IKJEFT1B,DYNAMNBR=20
//STEPLIB  DD DISP=SHR,DSN=DB2.SDSNLOAD
//DBRMLIB  DD DSN=&&DBRMDB2,DISP=(OLD,DELETE)
//SYSTSPRT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//SYSTSIN  DD *
 DSN SYSTEM(DB9G)

 BIND PACKAGE(HERPACK)          -
      MEMBER(COBDB501)          -
      OWNER(HEROWNR)            -
      ACTION(REPLACE)           -
      VALIDATE(BIND)            -
      EXPLAIN(YES)              -
      DEGREE(1)                 -
      ISOLATION(CS)             -
      ENCODING(EBCDIC)

 BIND PLAN(HERP0001)            -
      PKLIST(HERPACK.COBDB501)  -
      ACTION(REPLACE)           -
      ISOLATION(CS)             -
      ENCODING(EBCDIC)

 END
/*
// ENDIF

Observe as melhorias:

  • uso de IF;

  • IKJEFT1B;

  • biblioteca inteira em DBRMLIB;

  • owner funcional;

  • EXPLAIN(YES);

  • isolamento CS;

  • PKLIST sem redundância;

  • comando END explícito.


24. JCL de execução

Depois da construção, o programa poderia ser executado assim:

//RUNDB2 EXEC PGM=IKJEFT1B,DYNAMNBR=20
//STEPLIB  DD DISP=SHR,DSN=ALUNO.LOADLIB
//         DD DISP=SHR,DSN=DB2.SDSNLOAD
//SYSTSPRT DD SYSOUT=*
//SYSPRINT DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//SYSTSIN  DD *
 DSN SYSTEM(DB9G)

 RUN PROGRAM(COBDB501)          -
     PLAN(HERP0001)             -
     LIB('ALUNO.LOADLIB')

 END
/*

O fluxo será:

RUN PROGRAM(COBDB501)
          ↓
Load module COBDB501
          ↓
PLAN HERP0001
          ↓
PKLIST
          ↓
PACKAGE HERPACK.COBDB501
          ↓
Execução das instruções SQL

25. Erros comuns e soluções

SQLCODE -805

O -805 geralmente indica que o package necessário não foi encontrado ou não corresponde ao contexto procurado.

Possíveis causas:

  • package não bindado;

  • collection errada;

  • plan com PKLIST incorreta;

  • subsistema errado;

  • package removido;

  • load module de outro ambiente;

  • versão inexistente;

  • consistency token incompatível.

Checklist

1. O BIND PACKAGE terminou com sucesso?
2. A collection é HERPACK?
3. O package é COBDB501?
4. O plan HERP0001 inclui essa collection?
5. A execução está no subsistema DB9G?
6. O load module pertence ao mesmo build?
7. O package foi substituído depois do link?

Solução típica

Refazer o build completo:

Precompile
Compile
Link
Bind package
Bind plan
Deploy

Evite refazer apenas uma etapa sem saber exatamente o motivo.


SQLCODE -818

O -818 normalmente indica inconsistência entre o programa e o package.

Em termos didáticos:

Load module versão A
Package versão B

Isso acontece quando:

  • o programa foi recompilado e não houve novo bind;

  • o package foi substituído, mas o load module não;

  • houve deploy parcial;

  • o DBRM usado não pertence ao objeto compilado;

  • arquivos de builds diferentes foram misturados.

Solução

Reconstruir e promover os artefatos juntos.

Pense no load module e no package como irmãos gêmeos de build. Separá-los é pedir confusão.


Erro de autorização

O usuário do job pode não possuir:

  • privilégio de BIND;

  • autorização sobre a collection;

  • autorização para usar o owner;

  • acesso aos objetos SQL;

  • autorização de replace;

  • privilégio para criar o plan.

Solução

Consultar mensagens no SYSTSPRT e trabalhar com o DBA ou segurança.

Não resolva concedendo privilégios amplos sem análise.

O correto é identificar precisamente o privilégio necessário.


DBRM não encontrado

Possíveis causas:

  • erro no nome do membro;

  • precompile falhou;

  • DISP=PASS ausente;

  • dataset temporário deletado;

  • DD apontando para biblioteca errada;

  • membro indicado duas vezes de forma problemática;

  • job reiniciado a partir do step de BIND, mas o dataset temporário já não existe.

Solução

Use uma biblioteca permanente de DBRMs quando o processo precisar suportar restart independente.

Dataset temporário desaparece ao final do job. Portanto, reiniciar apenas o step de BIND em outro job pode não funcionar.


Objetos inexistentes

Com VALIDATE(BIND), tabelas ou views ausentes podem causar falha.

Possíveis causas:

  • ambiente incompleto;

  • DDL não aplicado;

  • qualificador incorreto;

  • tabela criada em outro schema;

  • alias ausente;

  • deploy executado fora de ordem.

Solução

Aplicar primeiro as mudanças de banco e depois executar o BIND.

Uma esteira madura respeita dependências:

DDL
RUNSTATS quando necessário
BIND
Deploy do executável
Execução

Access path pior após o BIND

O BIND pode escolher um caminho de acesso diferente.

Sintomas:

  • maior CPU;

  • mais GETPAGE;

  • batch mais lento;

  • aumento de I/O;

  • timeout;

  • lock escalation;

  • duração imprevisível.

Causas:

  • estatísticas desatualizadas;

  • mudança de índice;

  • alteração de cardinalidade;

  • parâmetros diferentes;

  • novo release;

  • BIND executado após mudanças no catálogo.

Soluções

  • executar RUNSTATS adequadamente;

  • usar EXPLAIN;

  • comparar access paths;

  • preservar opções de estabilidade;

  • testar antes da produção;

  • monitorar packages críticos;

  • usar mecanismos de gerenciamento de planos.


26. Como otimizar o processo

1. Padronize opções de BIND

Não permita que cada programador invente suas próprias opções.

Crie procedures corporativas para:

  • batch;

  • CICS;

  • IMS;

  • programas de consulta;

  • programas críticos;

  • packages distribuídos.


2. Use owners funcionais

Evite IDs pessoais em produção.


3. Versione packages e collections

Isso facilita rollback e coexistência entre releases.


4. Preserve os DBRMs

Principalmente em ambientes com auditoria, rebind ou recuperação.


5. Use EXPLAIN de forma consciente

Não espere uma crise de performance para descobrir o access path.


6. Monitore o RC real do BIND

O pipeline precisa falhar quando o BIND falhar.

“Job terminou” não significa “package válido”.


7. Evite UR por hábito

Escolha o isolamento conforme a regra de negócio.


8. Elimine redundâncias

Uma PKLIST limpa é mais fácil de auditar.


9. Mantenha compile, link e bind na mesma unidade de mudança

Essa é uma das melhores formas de evitar -805 e -818.


10. Planeje rollback

Antes de substituir um package, responda:

Como volto à versão anterior?

Se ninguém souber responder, o deploy ainda não está pronto.


27. Dicas para o programador COBOL iniciante

Quando um programa Db2 falhar, não olhe apenas o fonte COBOL.

Investigue a cadeia completa:

Fonte
Precompile
DBRM
Compile
Object
Link
Load module
Package
Collection
Plan
PKLIST
Subsistema
Autorização

Aprenda a ler mensagens no spool. Muitas respostas estão no SYSTSPRT, mas o iniciante frequentemente olha apenas o MAXCC.

Nunca suponha que RC=0 em um step significa que todos os comandos internos tiveram sucesso. Verifique as mensagens e a forma como os retornos são propagados.

Não copie opções de BIND cegamente. Cada uma carrega uma decisão:

UR
CS
RR
DEGREE
VALIDATE
EXPLAIN
OWNER
ENCODING
ACTION

Toda opção deveria conseguir responder à pergunta:

Por que estamos usando isso?

Se a única resposta for “sempre foi assim”, há uma dívida técnica escondida.


28. Easter egg: o package é a memória Jedi do SQL

Existe uma analogia divertida para guardar esse fluxo.

O fonte COBOL é o Padawan.

O DBRM é o pergaminho contendo as técnicas SQL aprendidas.

O BIND é o Conselho que examina o pergaminho, verifica permissões e escolhe como cada técnica será executada.

O package é o Holocron pronto para uso.

O plan é a autorização de missão que informa quais Holocrons o Padawan pode consultar.

Quando ocorre -805, o guerreiro chegou à missão, mas o Holocron não estava na lista.

Quando ocorre -818, ele trouxe um Holocron de outra geração.

E quando alguém usa ISO(UR) em um sistema financeiro sem entender o motivo, é como desligar os escudos para ganhar velocidade.

Pode funcionar.

Até o momento em que não funciona.


Conclusão

O JCL de BIND é uma das peças mais importantes do ciclo de construção de um programa COBOL Db2.

Ele não é apenas um passo burocrático depois da compilação.

É nele que o Db2 recebe o DBRM, valida objetos, verifica autorizações, prepara as instruções SQL, cria o package e associa esse package ao contexto de execução fornecido pelo plan.

O fluxo completo é:

COBOL com SQL
      ↓
Precompile
      ↓
DBRM + fonte modificado
      ↓
Compile
      ↓
Object module
      ↓
Link-edit
      ↓
Load module
      ↓
BIND PACKAGE
      ↓
Package
      ↓
BIND PLAN
      ↓
Execução

No exemplo analisado:

DBRM:       COBDB501
Collection: HERPACK
Package:    HERPACK.COBDB501
Plan:       HERP0001
Subsistema: DB9G

O JCL funciona como uma ponte entre o mundo executável do COBOL e o mundo relacional do Db2.

Entretanto, ele também revela diversos pontos para melhoria:

  • avaliar IKJEFT1B;

  • usar IF para maior clareza;

  • remover redundância no DBRMLIB;

  • usar owner funcional;

  • revisar ISO(UR);

  • eliminar redundância na PKLIST;

  • considerar EXPLAIN(YES);

  • planejar rollback;

  • preservar DBRMs;

  • garantir sincronização entre package e load module.

O programador que domina o BIND deixa de tratar -805 e -818 como magia obscura.

Ele passa a enxergar exatamente onde cada artefato nasceu, como foi preparado, qual plan o referencia e por que o Db2 aceitou ou rejeitou sua execução.

E essa é uma das passagens mais importantes na jornada de um COBOL Padawan:

Compreender que compilar o programa cria o corpo, mas executar o BIND prepara a inteligência SQL que permite ao programa conversar com o coração do Db2.

Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988
GitHub LinkedIn
Inicializando conteúdo...