| 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á:
abrir um arquivo sequencial;
ler cada registro;
separar os campos;
validar os dados;
executar um
INSERTem uma tabela Db2;tratar registros inválidos;
realizar
COMMIT;fechar o arquivo;
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:
atribui um JOBID;
lê o JCL;
faz a conversão;
verifica procedimentos;
coloca o job na fila;
seleciona uma initiator;
executa os steps;
recolhe as mensagens;
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:
localizar o subsistema;
estabelecer o attachment;
criar uma thread;
validar o ambiente;
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:
o programa é precompilado;
é gerado um novo DBRM;
o COBOL é compilado;
o load module é atualizado;
o PACKAGE antigo continua no Db2.
Ou o contrário:
um novo PACKAGE é criado;
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:
o programador altera o fonte;
compila com RC 0;
submete o job;
o comportamento antigo continua;
compila novamente;
nada muda;
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