☕ Um Café no Bellacosa Mainframe
Gostou do conteúdo? Ajude a manter o café quente, o COBOL compilando e o mainframe acordado. 😄
☕ Pague um café ao Bellacosa

Translate

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

terça-feira, 24 de setembro de 2024

Fluxo de Compilação de um programa COBOL com DB2

 

Bellacosa Mainframe e o fluxo de compilação cobol com db2

☕ Um Café no Bellacosa Mainframe

O Caso do Programa que Precisava de Duas Identidades

O Fluxo de Compilação de um Programa COBOL com Db2

A chuva caía sobre as janelas do CPD como uma sequência interminável de registros sendo gravados em um arquivo sequencial.

Do lado de dentro, os monitores 3270 lançavam uma luz esverdeada sobre as mesas. O relógio marcava 23h47. Os operadores já falavam baixo, como se soubessem que, depois de certo horário, os programas começavam a revelar segredos que escondiam durante o expediente.

Foi quando encontrei o programa.

Estava abandonado em uma biblioteca de fontes, cercado por COPYBOOKs, comandos SQL e comentários escritos por programadores que provavelmente já haviam se aposentado.

Na primeira linha, ele parecia apenas mais um programa COBOL.

Mas havia alguma coisa diferente.

No meio da PROCEDURE DIVISION, encontrei a pista:

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

Aquilo mudava tudo.

Um compilador COBOL comum não saberia o que fazer com aquelas instruções.

EXEC SQL não era COBOL.

Era SQL infiltrado dentro de um programa COBOL.

O programa possuía duas identidades: uma destinada ao compilador COBOL e outra destinada ao Db2.

Para transformar aquele fonte em um módulo executável, seria necessário conduzi-lo por uma cadeia de interrogatórios, traduções, compilações, ligações e registros.

No mundo COBOL com Db2, ninguém sai do fonte diretamente para a execução.

Antes de chegar ao CICS, ao batch ou ao ambiente de produção, o programa precisa atravessar o chamado fluxo de compilação COBOL–Db2.

E cada etapa produz uma nova pista.


1. A cena do crime: o programa-fonte

Tudo começa com um membro contendo o código-fonte COBOL.

Em um ambiente tradicional, ele poderia estar em uma biblioteca como:

BANCO.DESENV.COBOL(PROGCLI1)

Dentro desse programa encontramos instruções COBOL normais:

MOVE 1001 TO WS-COD-CLIENTE.
DISPLAY 'INICIANDO CONSULTA'.

Mas também encontramos comandos SQL embutidos:

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

Esses comandos são chamados de SQL estático embutido, ou embedded static SQL.

O termo “estático” significa que, em linhas gerais, a instrução SQL já está conhecida antes da execução do programa.

O Db2 pode analisar essa instrução previamente, determinar como ela poderá acessar as tabelas e registrar essas informações durante o processo de BIND.

Mas existe um problema.

O compilador COBOL não entende diretamente:

EXEC SQL

Ele entende MOVE, PERFORM, IF, EVALUATE, READ, WRITE e outras instruções da linguagem.

SQL pertence ao território do Db2.

Portanto, antes da compilação COBOL, alguém precisa separar as duas identidades do programa.

Esse alguém é o pré-compilador Db2.


2. O primeiro interrogatório: o pré-compilador Db2

O pré-compilador recebe o programa-fonte como entrada.

Sua missão é localizar todas as instruções existentes entre:

EXEC SQL

e:

END-EXEC

Quando encontra uma instrução SQL, o pré-compilador não simplesmente a apaga.

Ele realiza duas operações fundamentais.

Primeiro, substitui o SQL por chamadas que poderão ser compiladas como parte do programa COBOL.

Segundo, extrai informações sobre as instruções SQL e as registra em um arquivo chamado DBRM.

Assim, de um único programa-fonte surgem duas evidências:

Fonte COBOL com SQL
        |
        v
Pré-compilador Db2
        |
        +--> Fonte COBOL modificado
        |
        +--> DBRM

Essa divisão é a chave de todo o caso.

O programa passa a ter dois caminhos paralelos.

Um caminho segue para o compilador COBOL.

O outro segue para o BIND do Db2.

Mais tarde, os dois caminhos precisarão se reencontrar durante a execução.


3. O fonte modificado

Depois do pré-processamento, o SQL original não permanece exatamente como foi escrito.

Uma instrução como:

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

é transformada em estruturas e chamadas que o compilador COBOL consegue processar.

O resultado é conhecido como fonte modificado, fonte traduzido ou fonte expandido, dependendo da terminologia usada no ambiente.

Esse fonte contém lógica responsável por conversar com a interface do Db2 em tempo de execução.

O programador normalmente não precisa editar esse código gerado.

Ele existe para que o compilador COBOL consiga trabalhar com um programa que originalmente continha SQL.

É como se o pré-compilador retirasse o estrangeiro da sala, traduzisse seu depoimento e entregasse ao compilador apenas aquilo que ele consegue compreender.


4. A ficha que acompanha o suspeito: SQLCA

Programas COBOL que acessam Db2 normalmente precisam de uma área chamada SQLCA, SQL Communication Area.

Ela pode ser incluída com:

EXEC SQL
    INCLUDE SQLCA
END-EXEC.

A SQLCA contém informações sobre o resultado da última instrução SQL executada.

Entre seus campos, o mais famoso é:

SQLCODE

O SQLCODE informa se a operação terminou corretamente ou se algo inesperado aconteceu.

Alguns valores comuns são:

SQLCODE = 0
Operação concluída com sucesso.

SQLCODE = +100
Nenhuma linha encontrada ou fim do resultado.

SQLCODE negativo
Ocorreu algum erro.

Exemplo:

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

EVALUATE TRUE
    WHEN SQLCODE = 0
         DISPLAY 'CLIENTE: ' WS-NOME-CLIENTE

    WHEN SQLCODE = 100
         DISPLAY 'CLIENTE NAO ENCONTRADO'

    WHEN OTHER
         DISPLAY 'ERRO DB2. SQLCODE: ' SQLCODE
END-EVALUATE.

Um programa COBOL–Db2 que ignora o SQLCODE é como um detetive que interroga uma testemunha e sai da sala sem ouvir a resposta.


5. O segundo interrogatório: o compilador COBOL

Depois do pré-compilador, o fonte modificado é enviado ao compilador COBOL.

Agora o compilador encontra apenas estruturas que consegue entender.

Ele verifica:

  • sintaxe COBOL;

  • definição de variáveis;

  • compatibilidade de tipos;

  • referências a parágrafos;

  • COPYBOOKs;

  • tamanho de campos;

  • opções de compilação;

  • regras da linguagem.

Se tudo estiver correto, o compilador gera um módulo objeto.

O módulo objeto ainda não é o programa final pronto para executar.

Ele contém código de máquina e referências que ainda precisam ser resolvidas.

Podemos representar essa fase assim:

Fonte COBOL modificado
          |
          v
Compilador COBOL
          |
          v
Módulo objeto

Durante essa etapa, também é produzido o listing de compilação.

Esse listing pode mostrar:

  • erros;

  • avisos;

  • opções utilizadas;

  • mapa de dados;

  • referências cruzadas;

  • estatísticas da compilação;

  • mensagens do compilador.

O retorno da compilação costuma ser avaliado por meio do condition code do step.

Uma interpretação frequente é:

RC 0
Compilação concluída sem mensagens relevantes.

RC 4
Compilação concluída com avisos.

RC 8 ou superior
Existem erros que normalmente impedem a geração válida do programa.

Os significados exatos dependem das mensagens e das políticas do ambiente.

Um RC=4 não deve ser tratado automaticamente como “está tudo bem”.

Aviso também é pista.


6. O dossiê secreto: DBRM

Enquanto o fonte modificado segue para o compilador COBOL, o DBRM toma outro caminho.

DBRM significa:

Database Request Module

Ele contém uma representação das instruções SQL estáticas extraídas do programa.

O DBRM não é um programa COBOL executável.

Ele também não é uma tabela do Db2.

É uma espécie de dossiê contendo os pedidos de acesso ao banco de dados.

Nesse dossiê, o Db2 encontra informações sobre instruções como:

SELECT
INSERT
UPDATE
DELETE
DECLARE CURSOR
OPEN
FETCH
CLOSE

O DBRM será usado mais tarde durante o processo chamado BIND.

Sem BIND, o Db2 sabe que o programa tem intenções.

Mas ainda não concedeu autorização operacional para que essas intenções sejam executadas.


7. O BIND: quando o Db2 monta o plano

O BIND é uma das etapas mais importantes do fluxo.

Durante o BIND, o Db2 examina as instruções SQL registradas no DBRM.

Ele verifica:

  • se os objetos referenciados existem;

  • se tabelas, views e colunas podem ser localizadas;

  • se os tipos são compatíveis;

  • se o usuário responsável possui os privilégios necessários;

  • quais caminhos de acesso podem ser utilizados;

  • quais opções de isolamento e validação foram definidas.

O resultado normalmente é a criação ou atualização de um PACKAGE.

Em arquiteturas mais antigas ou em determinados ambientes, também pode existir o conceito de PLAN associado à execução.

De forma simplificada:

DBRM
 |
 v
BIND PACKAGE
 |
 v
PACKAGE Db2

O PACKAGE contém informações que o Db2 utilizará para executar o SQL estático.

Entre essas informações está o access path, isto é, o caminho escolhido para alcançar os dados.

Por exemplo, o Db2 poderá decidir entre:

  • utilizar um índice;

  • realizar uma varredura da tabela;

  • acessar índices em determinada sequência;

  • executar joins em uma ordem específica;

  • utilizar mecanismos de ordenação;

  • aplicar predicados em etapas diferentes.

O BIND é o momento em que o Db2 estuda o mapa do bairro antes de mandar seus agentes à rua.


8. VALIDATE(BIND) e VALIDATE(RUN)

Durante o BIND, uma opção importante pode determinar quando determinadas verificações serão exigidas.

Com:

VALIDATE(BIND)

o Db2 tenta validar os objetos e privilégios durante o próprio BIND.

Caso algo necessário não esteja disponível, o BIND pode falhar.

Com:

VALIDATE(RUN)

algumas verificações podem ser adiadas até o momento da execução.

Isso pode ser útil em certos cenários, mas também transfere parte do risco para o ambiente de execução.

Em uma produção crítica, adiar problemas não significa eliminá-los.

Significa apenas permitir que eles apareçam quando o programa estiver diante do usuário, do fechamento contábil ou do lote de milhões de registros.


9. O PACKAGE

O PACKAGE é o objeto Db2 que representa o SQL preparado para execução.

Ele está associado a elementos como:

  • nome da collection;

  • nome do programa;

  • versão;

  • opções do BIND;

  • access paths;

  • consistência do programa;

  • nível de isolamento;

  • qualificador;

  • proprietário;

  • validação.

Um comando conceitual de BIND poderia lembrar:

BIND PACKAGE(COLLECT1)
     MEMBER(PROGCLI1)
     ACTION(REPLACE)
     ISOLATION(CS)
     VALIDATE(BIND)

A sintaxe real depende do utilitário, da versão, do padrão de nomenclatura e das práticas adotadas na instalação.

O ponto essencial é que o PACKAGE vive no Db2.

Ele não é armazenado dentro do load module COBOL.

Isso explica por que o programa executável e seu PACKAGE podem existir em lugares diferentes e, mesmo assim, precisar corresponder exatamente.


10. O elo invisível: consistency token

Aqui surge uma das pistas mais elegantes do processo.

O módulo executável COBOL e o PACKAGE Db2 precisam reconhecer um ao outro.

Para isso, o processo utiliza uma identificação de consistência frequentemente relacionada ao consistency token.

O pré-compilador e o BIND produzem elementos que permitem ao Db2 verificar se o código executável corresponde ao PACKAGE disponível.

Considere este cenário:

  1. O programa foi pré-compilado.

  2. O DBRM foi gerado.

  3. O COBOL foi compilado.

  4. O load module foi criado.

  5. Um novo BIND não foi executado corretamente.

  6. O programa antigo ou um PACKAGE incompatível permaneceu no ambiente.

Durante a execução, o Db2 pode detectar que o executável e o PACKAGE não combinam.

O famoso resultado pode ser:

SQLCODE -805

Em linguagem de romance policial:

O sujeito apresentado pelo load module não era o mesmo registrado nos arquivos do Db2.


11. O terceiro interrogatório: o link-edit

O módulo objeto criado pelo compilador ainda contém referências externas.

Ele pode precisar de rotinas de tempo de execução, interfaces do Db2 e bibliotecas do sistema.

Por isso, o módulo objeto passa pelo link-edit, atualmente frequentemente executado pelo Binder do z/OS.

Fluxo:

Módulo objeto
      |
      v
Binder / Link-edit
      |
      v
Load module ou program object

Durante essa etapa, referências externas são resolvidas.

O resultado é colocado em uma load library, por exemplo:

BANCO.DESENV.LOAD(PROGCLI1)

Esse membro é o programa que poderá ser carregado para execução.

Dependendo da tecnologia e das opções adotadas, ele poderá ser chamado de:

  • load module;

  • program object;

  • módulo de carga;

  • executável.

É importante não confundir o load module com o PACKAGE.

Load module
Código COBOL executável.

PACKAGE
Representação executável das instruções SQL estáticas dentro do Db2.

Os dois fazem parte do mesmo caso, mas vivem em delegacias diferentes.


12. A montagem completa do fluxo

O fluxo clássico pode ser representado assim:

+----------------------------------+
| Fonte COBOL com SQL embutido     |
| EXEC SQL ... END-EXEC            |
+----------------+-----------------+
                 |
                 v
+----------------------------------+
| Pré-compilador Db2               |
+----------------+-----------------+
                 |
        +--------+--------+
        |                 |
        v                 v
+---------------+   +---------------+
| Fonte COBOL   |   | DBRM          |
| modificado    |   | SQL estático  |
+-------+-------+   +-------+-------+
        |                   |
        v                   v
+---------------+   +---------------+
| Compilador    |   | BIND PACKAGE  |
| COBOL         |   | no Db2        |
+-------+-------+   +-------+-------+
        |                   |
        v                   v
+---------------+   +---------------+
| Módulo objeto |   | PACKAGE       |
+-------+-------+   +---------------+
        |
        v
+---------------+
| Binder /      |
| Link-edit     |
+-------+-------+
        |
        v
+---------------+
| Load module   |
| ou program    |
| object        |
+---------------+

Na execução, os dois lados finalmente se encontram:

Load module COBOL
        |
        | chamada SQL
        v
Subsistema Db2
        |
        v
PACKAGE correspondente
        |
        v
Tabelas, índices e dados

Esse é o fluxo essencial.

Mas, como em toda investigação, os detalhes podem mudar conforme as ferramentas utilizadas.


13. Pré-compilador tradicional ou coprocessador SQL

Em ambientes modernos, o fluxo pode usar um SQL coprocessor integrado à compilação COBOL.

Nesse modelo, a compilação e o tratamento do SQL podem parecer uma única operação dentro do JCL ou da ferramenta de build.

Isso não elimina os conceitos fundamentais.

Mesmo com automação, continuam existindo:

  • análise do SQL;

  • geração de informações para o Db2;

  • compilação COBOL;

  • criação do executável;

  • criação ou atualização do PACKAGE;

  • necessidade de correspondência entre código e PACKAGE.

A diferença é que as ferramentas modernas escondem parte do corredor escuro atrás de uma porta automática.

O corredor continua lá.


14. Exemplo simplificado de programa

Considere o seguinte programa:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. PROGCLI1.

       DATA DIVISION.

       WORKING-STORAGE SECTION.

       01  WS-COD-CLIENTE     PIC 9(09) COMP-3.
       01  WS-NOME-CLIENTE    PIC X(40).
       01  WS-SALDO-CONTA     PIC S9(11)V99 COMP-3.

           EXEC SQL
               INCLUDE SQLCA
           END-EXEC.

       PROCEDURE DIVISION.

       0000-PRINCIPAL.

           MOVE 1001 TO WS-COD-CLIENTE

           EXEC SQL
               SELECT NOME_CLIENTE,
                      SALDO_CONTA
                 INTO :WS-NOME-CLIENTE,
                      :WS-SALDO-CONTA
                 FROM CLIENTES
                WHERE COD_CLIENTE = :WS-COD-CLIENTE
           END-EXEC

           EVALUATE TRUE
               WHEN SQLCODE = 0
                    DISPLAY 'CLIENTE: ' WS-NOME-CLIENTE
                    DISPLAY 'SALDO  : ' WS-SALDO-CONTA

               WHEN SQLCODE = 100
                    DISPLAY 'CLIENTE NAO ENCONTRADO'

               WHEN OTHER
                    DISPLAY 'ERRO SQL: ' SQLCODE
           END-EVALUATE

           GOBACK.

Antes que o programa execute, várias condições precisam ser atendidas:

  1. A tabela CLIENTES precisa existir.

  2. As colunas precisam existir.

  3. Os tipos precisam ser compatíveis.

  4. O programa precisa ser pré-processado.

  5. O código COBOL precisa ser compilado.

  6. O executável precisa ser linkado.

  7. O PACKAGE precisa ser criado.

  8. O usuário de execução precisa ter os acessos necessários.

  9. O load module e o PACKAGE precisam corresponder.

  10. O subsistema Db2 correto precisa estar disponível.

Uma única instrução SELECT pode parecer simples no fonte.

Mas por trás dela existe uma organização inteira trabalhando durante a madrugada.


15. Host variables: os informantes do COBOL

As variáveis utilizadas pelo SQL são chamadas de host variables.

Elas pertencem ao programa COBOL, mas são apresentadas ao SQL com dois-pontos:

:WS-COD-CLIENTE

Exemplo:

WHERE COD_CLIENTE = :WS-COD-CLIENTE

A coluna COD_CLIENTE pertence à tabela.

A variável WS-COD-CLIENTE pertence ao programa COBOL.

Os dois-pontos avisam ao pré-compilador:

Este nome não é uma coluna do banco. É uma variável do programa.

No INTO:

INTO :WS-NOME-CLIENTE

o Db2 coloca o valor retornado dentro da variável COBOL.

As host variables fazem a ponte entre dois mundos:

Mundo COBOL               Mundo Db2

WS-COD-CLIENTE   ------>  COD_CLIENTE

WS-NOME-CLIENTE  <------  NOME_CLIENTE

Se os tipos forem incompatíveis, o caso pode terminar em erro, truncamento ou comportamento indesejado.

Por isso, o programador deve entender tanto o PIC COBOL quanto o tipo da coluna Db2.


16. Variáveis indicadoras e o mistério do NULL

COBOL tradicional não possui um conceito nativo idêntico ao NULL do banco de dados.

Por isso, quando uma coluna pode conter NULL, utiliza-se uma indicator variable.

Exemplo:

01  WS-TELEFONE        PIC X(20).
01  WS-IND-TELEFONE    PIC S9(04) COMP.

No SQL:

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

Depois da execução:

Indicator >= 0
O valor normalmente está presente.

Indicator = -1
A coluna retornou NULL.

Sem a variável indicadora, um SELECT que encontre NULL pode gerar erro.

O programador iniciante frequentemente acusa a coluna.

Mas o verdadeiro culpado foi a ausência da indicator variable.


17. O PLAN

Em muitos ambientes Db2 for z/OS, packages são associados a um PLAN ou acessados por meio de mecanismos como collection lists.

Historicamente, o PLAN representa uma estrutura utilizada para a execução de programas SQL.

Simplificando:

Programa
   |
   v
PLAN
   |
   v
PACKAGE
   |
   v
SQL e dados

Em arquiteturas atuais, o PACKAGE ganhou grande importância porque permite maior modularidade.

Vários programas podem possuir seus próprios packages.

O PLAN pode utilizar uma lista de collections por meio de PKLIST.

Isso evita a necessidade de colocar todo o SQL de uma aplicação dentro de um único objeto gigantesco.


18. Erros clássicos da investigação

SQLCODE -805

Normalmente indica que o PACKAGE necessário não foi encontrado ou que existe incompatibilidade de identificação.

Possíveis causas:

  • PACKAGE não bindado;

  • collection incorreta;

  • versão incorreta;

  • ambiente errado;

  • load module e PACKAGE incompatíveis;

  • PLAN sem acesso à collection adequada.


SQLCODE -818

É frequentemente associado a incompatibilidade de timestamp ou consistency token entre o módulo executável e o DBRM/PACKAGE.

Em termos práticos:

  • o código foi compilado;

  • o BIND foi feito com outro DBRM;

  • as peças pertencem a versões diferentes.

É como tentar abrir um cofre novo com a combinação do cofre antigo.


SQLCODE -204

Objeto não encontrado.

Exemplo:

Tabela, view, alias ou outro objeto não localizado.

Possíveis causas:

  • nome incorreto;

  • qualifier incorreto;

  • objeto inexistente;

  • ambiente incorreto;

  • uso de tabela de desenvolvimento em produção ou vice-versa.


SQLCODE -206

Coluna não encontrada ou inválida no contexto.

Possíveis causas:

  • nome digitado incorretamente;

  • coluna removida;

  • alias de tabela ausente;

  • SQL desatualizado em relação à estrutura do banco.


SQLCODE -551

Problema de autorização.

O usuário ou authid não possui privilégio suficiente para realizar a operação.

O programa pode estar perfeito.

O BIND pode existir.

A tabela pode existir.

Mas a porta permanece trancada.


SQLCODE +100

Nenhuma linha encontrada.

Não é necessariamente um erro.

Em um SELECT INTO, pode significar que nenhum registro correspondeu à condição.

Em um cursor, pode indicar fim das linhas.

O erro verdadeiro é tratar +100 como se nunca pudesse acontecer.


19. JCL conceitual do processo

Um JCL real varia muito entre instalações, mas conceitualmente o processo pode ter steps como:

STEP 1 - Pré-compilação Db2
STEP 2 - Compilação COBOL
STEP 3 - Link-edit
STEP 4 - BIND PACKAGE
STEP 5 - BIND PLAN, quando necessário

Ou, em outra organização:

STEP 1 - Compilação com SQL coprocessor
STEP 2 - Link-edit
STEP 3 - BIND PACKAGE

Pipelines modernos podem executar essas etapas por meio de:

  • JCLs padronizados;

  • procedures catalogadas;

  • Endevor;

  • ISPW;

  • DBB;

  • Jenkins;

  • Git;

  • scripts de automação;

  • ferramentas DevOps para IBM Z.

Mesmo que o programador clique em um botão chamado “Build”, o botão não faz magia.

Ele apenas dispara uma sequência semelhante à investigação que acabamos de seguir.


20. A ordem das etapas importa

Imagine que o programa tenha sido alterado.

O desenvolvedor adiciona uma coluna ao SELECT:

SELECT NOME_CLIENTE,
       SALDO_CONTA,
       LIMITE_CREDITO

O fonte é pré-processado e compilado.

O novo load module é promovido.

Mas o novo DBRM não é utilizado no BIND.

O resultado é um executável novo tentando conversar com um PACKAGE antigo.

Isso pode gerar incompatibilidade.

Agora imagine o contrário:

O PACKAGE novo é bindado, mas o load module antigo permanece na biblioteca de produção.

Mais uma vez, versões diferentes se encontram.

Por isso, uma promoção correta precisa tratar como uma unidade:

Fonte
DBRM
Load module
PACKAGE
Configuração de execução

Em ambientes controlados, o processo de change management deve garantir que todas as peças da mesma versão avancem juntas.


21. O que acontece em tempo de execução

Quando o programa é executado, o z/OS carrega o load module.

Ao alcançar uma instrução SQL, a lógica gerada durante o pré-processamento encaminha a solicitação ao Db2.

O Db2 identifica:

  • o subsistema;

  • o plano ou contexto de execução;

  • a collection;

  • o PACKAGE;

  • a seção SQL correspondente.

Depois, executa o access path registrado ou reavaliado conforme as características do ambiente e as opções utilizadas.

O resultado é devolvido às host variables.

O SQLCODE é atualizado.

O programa COBOL continua.

Fluxo simplificado:

Programa COBOL executando
          |
          v
Encontra instrução SQL
          |
          v
Interface com Db2
          |
          v
Localiza PACKAGE
          |
          v
Executa SQL
          |
          v
Acessa tabela ou índice
          |
          v
Retorna dados e SQLCODE
          |
          v
Programa COBOL prossegue

Para o usuário, tudo isso pode acontecer em milésimos de segundo.

Para o programador, são décadas de engenharia condensadas em um único EXEC SQL.


22. Compile não é BIND

Uma das confusões mais comuns entre iniciantes é pensar:

O programa compilou, então o SQL está correto.

Nem sempre.

A compilação COBOL valida o código COBOL e as estruturas geradas.

O BIND é que realiza boa parte da validação relacionada ao Db2.

Dependendo das opções usadas, alguns problemas só aparecerão durante o BIND ou até durante a execução.

Portanto:

Compilação bem-sucedida
não garante
BIND bem-sucedido.

E:

BIND bem-sucedido
não garante
execução funcionalmente correta.

Um SELECT pode executar perfeitamente e devolver o cliente errado porque a regra de negócio foi escrita de maneira incorreta.

Nenhum compilador consegue condenar uma lógica que é sintaticamente válida, mas conceitualmente equivocada.


23. BIND não é apenas burocracia

É comum enxergar o BIND apenas como uma etapa obrigatória.

Mas ele também está ligado à performance.

Durante o BIND ou REBIND, o Db2 pode determinar access paths com base em informações como:

  • índices disponíveis;

  • estatísticas de tabelas;

  • estatísticas de colunas;

  • cardinalidade;

  • distribuição dos dados;

  • custo estimado;

  • métodos de join;

  • quantidade de páginas;

  • seletividade dos predicados.

Se as estatísticas estiverem desatualizadas, o Db2 poderá tomar decisões baseadas em uma fotografia antiga da cena.

É por isso que operações como RUNSTATS possuem tanta importância.

O otimizador só pode investigar com as evidências que recebe.


24. REBIND

O REBIND permite reconstruir informações de um PACKAGE existente sem necessariamente recompilar o programa COBOL.

Ele pode ser utilizado quando:

  • estatísticas foram atualizadas;

  • índices foram criados ou alterados;

  • ocorreu mudança de versão;

  • deseja-se recalcular access paths;

  • políticas de performance exigem revisão;

  • objetos sofreram alterações.

Mas um REBIND deve ser tratado com cuidado.

Um novo access path pode melhorar a performance.

Também pode piorá-la.

Em ambientes críticos, equipes analisam EXPLAIN, histórico de access paths e comportamento anterior antes de aceitar mudanças.

Todo bom detetive sabe que reabrir um caso pode revelar a verdade.

Mas também pode destruir uma pista que ainda era útil.


25. COLLECTION, PACKAGE e versão

Packages podem ser organizados em collections.

Isso permite separar ambientes ou versões.

Exemplo conceitual:

Collection: DESENV
Package: PROGCLI1

Collection: HOMOLOG
Package: PROGCLI1

Collection: PRODUCAO
Package: PROGCLI1

O nome do programa pode ser o mesmo.

Mas cada collection representa um contexto diferente.

Também podem existir estratégias de versionamento:

PROGCLI1 versão V1
PROGCLI1 versão V2

Esses mecanismos ajudam em:

  • implantação;

  • rollback;

  • testes paralelos;

  • manutenção;

  • coexistência de versões.

Entretanto, quanto maior a flexibilidade, maior a necessidade de controle.

Sem governança, uma collection se transforma em uma rua escura cheia de packages com identidades falsas.


26. Static SQL e Dynamic SQL

O fluxo descrito é principalmente associado ao SQL estático.

No SQL estático, a instrução é conhecida antes da execução:

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

No SQL dinâmico, o texto da instrução pode ser montado durante a execução.

Exemplo conceitual:

MOVE 'SELECT NOME_CLIENTE FROM CLIENTES'
  TO WS-COMANDO-SQL.

Depois, o programa pode usar comandos como:

PREPARE
EXECUTE
OPEN

O SQL dinâmico oferece flexibilidade.

Mas pode aumentar a complexidade relacionada a:

  • segurança;

  • autorização;

  • performance;

  • cache;

  • validação;

  • construção de comandos;

  • risco de SQL injection, quando entradas são tratadas incorretamente.

O SQL estático é como uma operação planejada.

O SQL dinâmico é como receber uma missão enquanto o carro já está em movimento.


27. O papel do programador COBOL

O programador COBOL–Db2 precisa enxergar além do fonte.

Ele deve saber responder:

  • Qual pré-processador está sendo usado?

  • Onde o DBRM é gerado?

  • Em qual biblioteca o load module é gravado?

  • Qual collection contém o PACKAGE?

  • Qual PLAN ou PKLIST participa da execução?

  • Qual subsistema Db2 será acessado?

  • Qual qualifier está sendo utilizado?

  • Qual authid realiza o BIND?

  • Quem executa o programa?

  • Qual é o nível de isolamento?

  • O programa verifica SQLCODE?

  • Existem colunas nullable?

  • As host variables são compatíveis?

  • O BIND acompanha a promoção do load module?

Essas perguntas separam o programador que apenas altera linhas daquele que compreende o ciclo de vida da aplicação.


28. Checklist para o iniciante

Antes de declarar o caso encerrado, verifique:

[ ] O fonte COBOL está correto?

[ ] Todas as instruções EXEC SQL possuem END-EXEC?

[ ] As host variables estão declaradas?

[ ] Os tipos COBOL combinam com as colunas Db2?

[ ] Colunas nullable possuem indicator variables?

[ ] A SQLCA foi incluída?

[ ] O programa testa SQLCODE?

[ ] O pré-processamento terminou corretamente?

[ ] O DBRM foi gerado?

[ ] A compilação COBOL terminou sem erros?

[ ] O link-edit criou o load module?

[ ] O BIND PACKAGE foi executado?

[ ] A collection está correta?

[ ] O PLAN ou PKLIST encontra o PACKAGE?

[ ] O executável e o PACKAGE pertencem à mesma versão?

[ ] O usuário possui autorização?

[ ] A load library correta está sendo utilizada?

[ ] O subsistema Db2 correto está ativo?

Esse checklist não evita todos os crimes.

Mas reduz bastante o número de suspeitos.


29. O fluxo resumido em uma frase

Um programa COBOL com Db2 passa pelo pré-processamento para separar COBOL e SQL, pela compilação para gerar o código objeto, pelo link-edit para criar o executável e pelo BIND para transformar o SQL estático em um PACKAGE reconhecido pelo Db2.

Ou, em linguagem de rua:

O pré-compilador separa.
O compilador traduz.
O Binder monta.
O BIND registra.
O Db2 executa.

30. Conclusão: dois caminhos, uma única execução

Às 3h12 da manhã, o caso finalmente estava resolvido.

O programa não era apenas COBOL.

Também não era apenas SQL.

Ele era uma aliança entre dois mundos.

O lado COBOL cuidava da lógica, dos campos, dos arquivos, dos cálculos e das decisões.

O lado Db2 cuidava das tabelas, dos índices, dos access paths, dos locks e da persistência dos dados.

Entre os dois, o pré-compilador atuava como intérprete.

O DBRM carregava o depoimento das instruções SQL.

O compilador transformava COBOL em código objeto.

O Binder criava o módulo executável.

O BIND registrava o PACKAGE no submundo do Db2.

Na execução, todas essas peças precisavam apresentar os mesmos documentos.

Quando tudo correspondia, o SELECT retornava seus dados e o programa continuava tranquilamente.

Quando alguma peça pertencia à versão errada, surgiam os suspeitos habituais:

-805
-818
-204
-206
-551

Apaguei a última luz do CPD e deixei o programa executando.

Lá fora, a chuva ainda caía.

Dentro do mainframe, milhões de instruções SQL atravessavam o mesmo caminho sem que ninguém percebesse.

Porque esta é uma das grandes verdades do IBM Z:

quando o fluxo de compilação funciona, ninguém nota.

Mas quando o BIND desaparece, o DBRM não combina ou o PACKAGE está na collection errada, sempre haverá um programador COBOL caminhando pelos corredores da madrugada, seguindo as pistas deixadas no spool.

E pedindo mais um café.

Posso também transformar esse conteúdo em uma versão ampliada com JCL completo de pré-compilação, compilação, link-edit e BIND PACKAGE.




Infografico demonstrando o fluxo de compilação de um programa mainframe COBOL com acesso ao Banco de Dados DB2. 

 

quinta-feira, 12 de setembro de 2024

Kojak Entra no CPD — O Dia em que um SQLCODE -911 Virou Suspeito e o Db2 Foi Chamado para Depor

 

Bellacosa Mainframe apresenta cobol e db2

☕ Um Café no Bellacosa Mainframe

Kojak Entra no CPD — O Dia em que um SQLCODE -911 Virou Suspeito e o Db2 Foi Chamado para Depor

Ou: como COBOL, SQL, DBRM, BIND, PACKAGE, access path, COMMIT, ROLLBACK, locking, CICS e um programador iniciante descobriram que, no mainframe, até um SELECT tem antecedentes

Nova York, 1973.

Um policial careca, impecavelmente vestido, andando pelas ruas com um pirulito na boca começa a resolver crimes enquanto solta ironias e uma pergunta que se tornaria inseparável do personagem:

“Who loves ya, baby?”

Era o tenente Theo Kojak.

A série Kojak, criada por Abby Mann e protagonizada por Telly Savalas, estreou na CBS em 24 de outubro de 1973 e permaneceu originalmente no ar até 1978. (Paramount Press Express)

Curiosamente, Kojak surgiu antes da série: Telly Savalas interpretou o personagem no telefilme The Marcus-Nelson Murders, de 1973, que funcionou como origem do programa. (Los Angeles Times)

E existe algo profundamente mainframe em Kojak.

Ele não olha apenas para o cadáver.

Olha para quem entrou, quem saiu, quem tinha autorização, quem estava segurando o recurso, quem alterou o registro e por que aquela história aparentemente perfeita não combina com as evidências.

Troque a delegacia por um CPD.

Troque o assassinato por uma transação travada.

Troque as testemunhas por logs.

E dê ao tenente um terminal 3270.

Temos nosso novo investigador Db2.



Prólogo — 03:17, alguma coisa morreu em produção

O telefone toca.

O programador COBOL iniciante atende.

— Produção está parada.

Cinco palavras capazes de transformar café em combustível nuclear.

Ele abre o fonte.

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

Olha novamente.

Nada parece errado.

Compilou.

Ontem funcionava.

O programador conclui:

— Deve ser o banco.

A porta do CPD abre.

Um homem careca entra lentamente, coloca um pirulito na boca e observa o terminal.

— O banco, baby?

Silêncio.

— Vamos começar novamente. Quem executou o SQL?

E aí descobrimos nosso primeiro problema.



1. COBOL não executa SQL sozinho

Quando vemos:

EXEC SQL
    SELECT ...
END-EXEC

é fácil imaginar que SELECT seja uma instrução COBOL.

Não é.

COBOL e SQL são linguagens diferentes trabalhando juntas.

Podemos imaginar três responsabilidades:

COBOL
   │
   ├── lógica da aplicação
   ├── cálculos
   ├── decisões
   └── fluxo
          │
          ▼
         SQL
          │
          └── declara quais dados queremos
                    │
                    ▼
                   Db2
                    │
                    └── determina como obtê-los

O COBOL pode dizer:

“Preciso consultar o saldo desta conta.”

O SQL expressa:

SELECT SALDO
FROM CONTA
WHERE NUMERO = :WS-CONTA

E o Db2 precisa resolver como encontrar aquele registro eficientemente.

Kojak tira o pirulito da boca.

— Então temos três testemunhas. COBOL diz uma coisa, SQL pede outra e Db2 faz o trabalho pesado.

Exatamente.



2. O primeiro interrogatório: as Host Variables

Observe:

WHERE NUMERO = :WS-CONTA

Por que existe aquele :?

Porque WS-CONTA pertence ao programa hospedeiro.

É uma host variable.

Temos uma fronteira:

       COBOL                     SQL

    WS-CONTA ───────────────► :WS-CONTA

    WS-SALDO ◄─────────────── :WS-SALDO

No comando:

EXEC SQL
    SELECT SALDO
      INTO :WS-SALDO
      FROM CONTA
     WHERE NUMERO = :WS-CONTA
END-EXEC

WS-CONTA fornece informação.

WS-SALDO recebe informação.

Parece simples.

Até aparecerem:

  • tipos incompatíveis;

  • valores NULL;

  • indicator variables;

  • truncamentos;

  • conversões;

  • nenhuma linha;

  • múltiplas linhas.

Kojak aponta para WS-CONTA.

— Interrogue esse sujeito primeiro. Todo mundo culpa o Db2 antes de perguntar o que mandou para ele.

Primeira regra da investigação:

entrada errada produz investigação errada.


3. O crime aconteceu antes da execução

Nosso iniciante acredita que o fonte vai diretamente para o compilador COBOL.

Kojak balança a cabeça.

— Baby... tem alguém faltando nessa história.

Quando existe SQL embutido, o SQL precisa ser processado.

No fluxo tradicional, podemos representar:

COBOL + SQL
     │
     ▼
PRECOMPILE
     │
     ├────────────► DBRM
     │
     ▼
COBOL modificado
     │
     ▼
COBOL COMPILER
     │
     ▼
OBJECT
     │
     ▼
LINK-EDIT
     │
     ▼
LOAD MODULE

O processamento SQL e o processamento COBOL são coisas distintas.

E acabamos de encontrar nosso primeiro suspeito misterioso.

DBRM.


4. DBRM — o dossiê policial do SQL

DBRM significa Database Request Module.

Não é o executável COBOL.

Não é a tabela.

Não é o banco.

Não é o programa completo.

Uma maneira didática de imaginá-lo é como o dossiê contendo informações sobre as requisições SQL extraídas do programa para uso posterior pelo Db2.

A documentação IBM descreve o DBRM dentro justamente desse processo de preparação das instruções SQL para o bind. (Wikipedia)

Imagine:

PROGRAMA ABC123

SQL 001
SELECT SALDO...

SQL 002
UPDATE CONTA...

SQL 003
INSERT MOVIMENTO...

O Db2 precisará conhecer essas requisições.

O DBRM entra nessa história.

Kojak pega a pasta.

— Agora temos antecedentes.


5. BIND — agora leve o suspeito para a delegacia

Aqui aparece uma palavra que todo programador COBOL/Db2 precisa conhecer:

BIND.

Simplificando bastante, durante o bind o Db2 trabalha com as informações SQL preparadas anteriormente e produz aquilo que será necessário para executar o SQL estático.

Temos:

DBRM
  │
  ▼
BIND PACKAGE
  │
  ▼
PACKAGE

Agora nossa investigação começa a ficar interessante.

Porque significa que existem dois universos relacionados:

             APLICAÇÃO

        ┌───────────────┐
        │               │
        ▼               ▼
      COBOL            SQL
        │               │
        ▼               ▼
 LOAD MODULE          DBRM
                        │
                        ▼
                      BIND
                        │
                        ▼
                     PACKAGE

É perfeitamente possível que alguém diga:

“Mas o COBOL compilou!”

Ótimo.

Isso prova apenas uma parte da história.

Kojak sorri.

— Compilou? Muito bonito. Agora me mostre o package.


6. PACKAGE — o personagem que o iniciante quase nunca conhece

O package contém informações necessárias para a execução das instruções SQL estáticas associadas à aplicação.

E aqui ocorre uma mudança mental importante.

O programa executável e o SQL preparado possuem processos relacionados, mas distintos.

Portanto:

SOURCE
  │
  ├──────── COBOL ────────► LOAD MODULE
  │
  └──────── SQL ─► DBRM ─► BIND ─► PACKAGE

Na execução, esses mundos precisam conversar corretamente.

É por isso que um problema de Db2 pode existir mesmo quando o compilador COBOL não reclamou.

O compilador não é Sherlock Holmes.

Muito menos Kojak.


7. “Mas quem decidiu usar aquele índice?”

Chegamos ao grande interrogatório.

Considere:

SELECT NOME,
       LIMITE,
       SALDO
FROM CLIENTE
WHERE CPF = :WS-CPF

O programador declarou o que deseja.

Não escreveu:

vá para cilindro X
abra página Y
ande 37 posições
leia registro Z

Essa é uma das grandes diferenças entre programação procedural e acesso relacional.

O Db2 possui um optimizer.

Conceitualmente:

             SQL
              │
              ▼
          OPTIMIZER
              │
       ┌──────┼──────┐
       │      │      │
       ▼      ▼      ▼
   índices estatísticas cardinalidade
       │      │      │
       └──────┼──────┘
              ▼
         ACCESS PATH
              │
              ▼
          EXECUÇÃO

O optimizer avalia alternativas e custos.

Pode considerar índices.

Pode considerar outras estratégias.

O resultado dessa decisão é fundamental para performance.

Kojak olha para o programador:

— Você escreveu o SELECT. Mas não decidiu necessariamente como ele encontraria o sujeito.

Perfeito.


8. RUNSTATS — quando a testemunha está dizendo a verdade de 2019

Agora temos uma situação deliciosa.

O programa não mudou.

O SQL não mudou.

A tabela não mudou estruturalmente.

Mas o desempenho ficou horrível.

Como?

Imagine que o Db2 esteja tomando decisões baseado em informações estatísticas inadequadas ou desatualizadas.

É como Kojak perguntar:

— Quantas pessoas vivem neste prédio?

E alguém responder:

— Doze.

— Quando você contou?

— Em 1997.

Temos um problema.

As estatísticas ajudam o optimizer a compreender características dos dados.

Daí a importância de operações como RUNSTATS.

Isso nos ensina uma lição extraordinária:

Performance SQL não está inteiramente dentro do fonte SQL.

Ela depende do ecossistema.


9. Índice não é pó mágico

O iniciante aprende:

Índice deixa SELECT rápido.

Depois cria índice para tudo.

Parabéns.

Acabamos de transformar uma boa ideia em um novo incidente.

Índices possuem custo.

Precisam ser mantidos.

INSERT, UPDATE e DELETE podem ter trabalho adicional quando índices associados precisam ser atualizados.

Portanto:

MAIS ÍNDICES

      não significa automaticamente

MAIS PERFORMANCE

A pergunta é:

qual índice atende quais padrões reais de acesso?

Kojak provavelmente perguntaria:

— Esse índice estava onde na noite do crime?


10. O SQLCODE chega para depor

Executamos:

EXEC SQL
    SELECT SALDO
      INTO :WS-SALDO
      FROM CONTA
     WHERE NUMERO = :WS-CONTA
END-EXEC

E agora?

Precisamos saber o resultado.

Entra o SQLCODE.

Didaticamente:

SQLCODE = 0
     │
     └── sucesso

SQLCODE positivo
     │
     └── condição/warning

SQLCODE negativo
     │
     └── erro

Um programador iniciante pode fazer:

IF SQLCODE NOT = ZERO
    DISPLAY 'ERRO DB2'
END-IF.

Kojak quase engole o pirulito.

Porque os códigos precisam ser interpretados, não simplesmente classificados como “deu certo/deu errado”.

O famoso +100, por exemplo, normalmente indica ausência de linha correspondente ou fim do conjunto de resultados conforme a operação.

Isso pode ser perfeitamente normal.

Pesquisar cliente inexistente não significa necessariamente que o banco “quebrou”.


11. Até que aparece o cadáver: SQLCODE -911

Agora temos drama.

O programa retorna:

SQLCODE = -911

E alguém grita:

— Db2 caiu!

Não necessariamente.

Temos uma pista ligada a situações envolvendo rollback provocado por deadlock ou timeout, conforme o contexto e reason code.

Kojak sorri.

Finalmente um crime digno dele.

Imagine:

TRANSAÇÃO A

LOCK RECURSO 1
      │
      ▼
PRECISA RECURSO 2


TRANSAÇÃO B

LOCK RECURSO 2
      │
      ▼
PRECISA RECURSO 1

Temos:

A espera B

B espera A

Os dois poderiam esperar até a aposentadoria do programador.

O sistema precisa resolver a situação.

Bem-vindo ao mundo de locking, timeout e deadlock.


12. ACID — a divisão de crimes financeiros

Imagine uma transferência:

CONTA A = 1000
CONTA B =  500

TRANSFERÊNCIA = 100

Queremos terminar:

A = 900
B = 600

Não queremos:

A = 900
B = 500

porque o sistema caiu no meio.

É aqui que as propriedades ACID entram em cena:

Atomicity — a unidade lógica precisa ser tratada de maneira indivisível quanto ao resultado transacional.

Consistency — regras de consistência devem permanecer válidas.

Isolation — transações concorrentes precisam coexistir sob regras controladas.

Durability — depois da confirmação, os resultados precisam possuir as garantias de persistência esperadas.

Esse não é detalhe acadêmico.

É o motivo pelo qual você consegue dormir depois de transferir dinheiro pelo banco.


13. COMMIT — pode liberar os suspeitos

Considere:

UPDATE CONTA A
       │
       ▼
UPDATE CONTA B
       │
       ▼
INSERT MOVIMENTO
       │
       ▼
     COMMIT

O COMMIT delimita a confirmação apropriada daquela unidade de trabalho.

Mas imagine:

UPDATE A       OK
UPDATE B       OK
INSERT         ERRO

Dependendo da lógica e do contexto transacional, podemos precisar desfazer alterações.

Entra:

ROLLBACK

Portanto:

        UNIT OF WORK

UPDATE
   │
UPDATE
   │
INSERT
   │
   ├──── tudo correto ───► COMMIT
   │
   └──── problema ───────► ROLLBACK

Essa dupla é parte central da engenharia transacional.


14. COMMIT também é performance

Agora vem uma sutileza importante.

Alguns iniciantes imaginam COMMIT apenas como:

salvar alterações.

Mas unidades de trabalho também estão relacionadas à utilização e liberação de determinados recursos e locks.

Imagine um batch processando:

10 registros
100 registros
10.000 registros
10.000.000 registros

sem pensar adequadamente na estratégia de commit.

Ele pode manter recursos por períodos indesejáveis, afetar concorrência e complicar recuperação.

Logo, frequência de commit não deveria ser escolhida com:

COMMIT a cada 1000

simplesmente porque alguém encontrou esse número num programa de 1987.

A estratégia depende da aplicação.


15. Então chega o CICS à delegacia

Até agora poderíamos estar pensando em batch.

Mas coloque a aplicação em ambiente transacional:

CLIENTE
   │
   ▼
CICS
   │
   ▼
PROGRAMA COBOL
   │
   ▼
SQL
   │
   ▼
DB2

Agora milhares de usuários podem executar transações.

Temos simultaneamente:

CICS transaction 001 ──┐
CICS transaction 002 ──┤
CICS transaction 003 ──┼──► Db2
CICS transaction 004 ──┤
CICS transaction 005 ──┘

Nesse momento, locking deixa de ser um capítulo de apostila.

Vira sobrevivência.


16. COBOL + Db2 Connect? Cuidado com essa simplificação

O infográfico original apresenta conceitualmente uma camada intermediária chamada Db2 Connect.

Aqui Kojak levantaria uma sobrancelha.

Db2 Connect não deve ser ensinado como passagem obrigatória de todo programa COBOL local no z/OS para Db2.

Dependendo do ambiente, temos diferentes mecanismos de attachment e execução.

Batch, CICS, IMS, TSO e aplicações distribuídas podem envolver arquiteturas diferentes.

Portanto:

COBOL → DB2 Connect → Db2

não é uma representação universal.

Esse é exatamente o tipo de simplificação que funciona num infográfico 101, mas precisa ser desmontada quando o aluno chega ao nível 201.


17. “COBOL cuida da regra; Db2 cuida dos dados”

Boa explicação.

Mas incompleta.

No mundo real, Db2 também possui recursos relacionados a:

  • constraints;

  • integridade referencial;

  • views;

  • triggers;

  • stored procedures;

  • functions.

Então podemos pensar:

COBOL
 │
 ├── fluxo
 ├── cálculo
 ├── regras da aplicação
 └── orquestração

DB2
 │
 ├── persistência
 ├── integridade
 ├── concorrência
 ├── recuperação
 ├── otimização
 └── acesso relacional

As responsabilidades precisam ser projetadas.

Não simplesmente presumidas.


18. Static SQL e Dynamic SQL entram na sala

Nosso exemplo:

EXEC SQL
    SELECT SALDO
    ...
END-EXEC

é um exemplo clássico de SQL embutido estático.

Conceitualmente:

STATIC SQL

conhecido durante
preparação/deployment
       │
       ▼
processamento
       │
       ▼
DBRM
       │
       ▼
BIND
       │
       ▼
PACKAGE

Mas existe SQL dinâmico.

Podemos ter situações nas quais a instrução é preparada durante a execução usando mecanismos como PREPARE e EXECUTE.

Isso oferece flexibilidade, mas muda aspectos da preparação e execução.

Novamente:

não existe “um é sempre melhor”.

Existe:

qual mecanismo atende melhor ao problema?


19. O crime perfeito: SELECT *

Kojak encontra:

SELECT *
FROM CLIENTE

Olha para o programador.

Olha novamente para a tela.

— Você precisa de todas essas colunas?

— Não.

— Então por que pediu?

Silêncio.

Essa pergunta vale ouro.

Evite transportar dados desnecessários.

Se precisa de:

NOME
SALDO

não peça quarenta colunas simplesmente porque:

SELECT *

é mais rápido de digitar.

Cinco segundos economizados na programação podem gerar trabalho desnecessário repetido milhões de vezes em produção.


20. Outro suspeito: SELECT dentro de LOOP

Agora Kojak encontra:

PERFORM 100000 TIMES

       SELECT ...

END-PERFORM

Acende a luz da sala de interrogatório.

Dependendo do problema, isso pode representar enorme quantidade de chamadas e processamento que talvez pudesse ser resolvida por uma estratégia SQL muito melhor.

O erro conceitual é tratar banco relacional como arquivo sequencial:

LEIA
LEIA
LEIA
LEIA
LEIA

SQL trabalha naturalmente com conjuntos.

Essa mudança de pensamento é enorme para quem vem de COBOL procedural.


21. Não coloque toda a investigação num único SQL monstruoso

Mas existe o extremo oposto.

Alguém aprende que SQL trabalha com conjuntos e produz:

SELECT
 JOIN
  JOIN
   JOIN
    CASE
     CASE
      SUBQUERY
       UNION
        CTE
         FUNCTION
          JOIN
           ...

O SQL parece o mapa genealógico de Game of Thrones.

SQL complexo não é automaticamente ruim.

Às vezes uma operação sofisticada no banco é exatamente a solução correta.

Mas complexidade precisa possuir justificativa.

Legibilidade importa.

Manutenção importa.

Performance importa.

Testabilidade importa.


22. EXPLAIN — reconstruindo a cena do crime

Se queremos investigar desempenho, precisamos saber mais sobre o access path.

É aí que conceitos relacionados ao EXPLAIN tornam-se fundamentais.

O objetivo é investigar como o Db2 pretende ou decidiu acessar os dados, conforme o cenário analisado.

Você começa a procurar pistas:

INDEX?
TABLESPACE SCAN?
JOIN METHOD?
JOIN ORDER?
ESTIMATIVAS?
SORT?

Kojak coloca fotografias na parede.

Agora temos uma investigação de verdade.

O programador deixa de dizer:

“Esse SELECT está lento.”

E começa a perguntar:

“Qual access path está sendo utilizado e por quê?”

Essa diferença separa palpite de diagnóstico.


23. O verdadeiro CPD aparece

Finalmente afastamos a câmera.

O programador pensava que estava trabalhando com:

COBOL + DB2

Mas agora enxerga:

                    z/OS
                      │
       ┌──────────────┼──────────────┐
       │              │              │
      JES2           CICS           Db2
       │              │              │
      JCL            COBOL          SQL
       │              │              │
    datasets           ├──────────────┤
                       │              │
                       MQ          PACKAGE
                       │              │
                      API         OPTIMIZER
                                      │
                                  ACCESS PATH
                                      │
                              ┌───────┴───────┐
                              │               │
                           INDEXES          TABLES

E ainda não terminamos.

Ao redor estão:

RACF
SMF
WLM
TCP/IP
LOGS
RECOVERY
BACKUP
MONITORAMENTO
PARALLEL SYSPLEX

Agora entendemos por que profissionais podem trabalhar décadas com mainframe e continuar aprendendo.


24. Kojak finalmente resolve o caso

Voltamos às 03:17.

O programa estava correto.

O SQL também.

O Db2 estava disponível.

O problema estava relacionado à concorrência.

Outra unidade de trabalho estava mantendo recursos necessários.

A transação esperou.

O limite foi atingido.

O SQL recebeu erro.

O programa não possuía tratamento suficientemente inteligente.

A mensagem apresentada ao operador foi:

ERRO NO BANCO

Ou seja:

o único inocente da investigação talvez fosse justamente “o banco”.

Kojak olha para o programador.

— Então você acusou Db2 sem verificar SQLCODE, reason code, locks, unidade de trabalho e logs?

— Sim.

Kojak coloca o pirulito na boca.

— Who loves ya, baby?


🍭 Easter egg — por que Kojak usa pirulito?

Esse detalhe é perfeito para nossa história.

O pirulito não nasceu simplesmente como um adereço aleatório inventado pelo departamento de marketing. Telly Savalas contou posteriormente que a ideia surgiu numa situação em que uma personagem queria que Kojak parasse de fumar e lhe entregava um pirulito; o detalhe pegou e tornou-se uma das marcas visuais do personagem. (Los Angeles Times)

Na própria série, o pirulito apareceu durante a primeira temporada e acabou associado à tentativa de reduzir o cigarro. (Wikipedia)

Ou seja:

pequena decisão
      │
      ▼
efeito inesperado
      │
      ▼
torna-se característica permanente

Qualquer semelhança com sistemas legados é absolutamente maravilhosa.

Um programador em 1984 cria:

05 WS-FLAG PIC X.

para resolver temporariamente um problema.

Quarenta anos depois:

“Não mexa no WS-FLAG. Ninguém sabe exatamente por quê, mas o fechamento mensal depende dele.”

😂


25. A lição que Kojak deixaria ao programador COBOL

O grande erro de quem começa no mainframe é imaginar que aprender COBOL significa aprender sintaxe:

MOVE
COMPUTE
IF
EVALUATE
PERFORM

Isso é apenas a porta de entrada.

Depois vem:

COBOL
  +
JCL
  +
Db2
  +
CICS
  +
VSAM
  +
MQ
  +
RACF
  +
JES2
  +
WLM
  +
SMF
  +
z/OS

E finalmente você percebe:

o sistema corporativo não é um programa. É um ecossistema de componentes cooperando.

O SELECT SALDO que parece possuir cinco linhas pode envolver host variables, processamento SQL, DBRM, package, optimizer, estatísticas, índices, buffer management, locking, logging, segurança, recuperação e infraestrutura antes de devolver alguns bytes para WS-SALDO.

Essa é uma das coisas mais bonitas do mainframe.

Na superfície:

EXEC SQL
   SELECT SALDO
   INTO :WS-SALDO
...
END-EXEC.

Debaixo:

               50 ANOS
                  DE
             ENGENHARIA
                  │
                  ▼
             WS-SALDO

O programador olha para a variável.

Depois olha para Kojak.

Finalmente compreendeu.

Não basta perguntar:

“O programa compilou?”

É preciso investigar:

Qual SQL executou? Qual SQLCODE retornou? Qual package? Qual access path? As estatísticas estão adequadas? Existe índice apropriado? Houve locking? Timeout? Deadlock? Onde começa e termina a unidade de trabalho? Houve COMMIT? O que os logs mostram?

Porque produção não aceita álibi.

Produção aceita evidência.

Kojak se levanta, pega o sobretudo e caminha para a porta do CPD.

O programador pergunta:

— Tenente, e se amanhã aparecer outro -911?

Kojak sorri.

— Agora você sabe interrogar os suspeitos, baby.

E sai.

Na mesa fica apenas um pirulito, um dump, três relatórios de performance e um post-it:

****************************************
* NÃO CULPE O DB2 ANTES DO EXPLAIN.    *
****************************************

Fim do JOB.

IEF404I KOJAK - ENDED
IEF142I KOJAK STEP01 - COND CODE 0000

E, pela primeira vez naquela madrugada, ninguém precisou dar ROLLBACK no café. ☕🍭

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.

 


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...