Translate

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.

Sem comentários:

Enviar um comentário