| Bellacosa Mainframe apresenta a Data Division no COBOL |
☕ Um Café no Bellacosa Mainframe
Data Division sem Mistérios para Programadores COBOL
Quando um Programador Descobre que Cada Byte Deixa uma Impressão Digital — e que a Cena do Crime Começa Antes da PROCEDURE DIVISION
Nova York, 02h17 da manhã.
A chuva escorre pelas paredes de vidro do laboratório forense enquanto os reflexos azuis das viaturas atravessam a sala. Sobre uma bancada de aço inoxidável existe algo aparentemente simples: um programa COBOL encontrado em produção depois de um processamento noturno terminar com dados inconsistentes.
Nenhum cadáver.
Nenhuma arma.
Nenhuma testemunha disposta a falar.
Apenas milhões de bytes, um arquivo sequencial, algumas áreas de memória e um saldo bancário que apareceu negativo onde jamais deveria existir um valor menor que zero.
O programador iniciante olha para o código e afirma:
— O problema deve estar na PROCEDURE DIVISION.
O investigador veterano do mainframe coloca as luvas, aproxima-se do terminal 3270 e responde:
— Esse é o primeiro erro de quem ainda não aprendeu a investigar sistemas legados. A lógica pode executar o crime, mas é na DATA DIVISION que encontramos as impressões digitais.
Bem-vindo ao laboratório forense do Bellacosa Mainframe.
Hoje não examinaremos sangue, fibras, pólvora ou DNA. Examinaremos registros, campos, buffers, parâmetros, tabelas, condições, formatos numéricos e áreas de armazenamento.
Porque, em COBOL, nenhum byte desaparece sem deixar vestígios.
| Bellacosa Mainframe em DATA DIVISION em destaque |
A cena do crime: o que é a DATA DIVISION?
Um programa COBOL tradicional costuma ser organizado em grandes divisões:
IDENTIFICATION DIVISION.
ENVIRONMENT DIVISION.
DATA DIVISION.
PROCEDURE DIVISION.
A IDENTIFICATION DIVISION identifica o programa.
A ENVIRONMENT DIVISION descreve aspectos do ambiente onde ele será executado.
A DATA DIVISION define os dados utilizados pelo programa.
A PROCEDURE DIVISION contém as instruções que manipulam esses dados.
Uma comparação simples seria:
| Divisão | Função |
|---|---|
| IDENTIFICATION DIVISION | Identificação do caso |
| ENVIRONMENT DIVISION | Descrição da cena |
| DATA DIVISION | Catálogo de evidências |
| PROCEDURE DIVISION | Reconstrução dos acontecimentos |
Muitos iniciantes resumem a Data Division como “o lugar onde declaramos variáveis”.
Isso está correto, mas é incompleto.
A Data Division não apenas declara variáveis. Ela especifica:
o tamanho de cada campo;
o formato dos dados;
a representação física em memória;
a hierarquia entre campos;
o tempo de vida das informações;
a origem dos dados;
a forma de compartilhamento entre programas;
a organização de registros em arquivos;
as áreas utilizadas como entrada e saída;
as diferentes interpretações possíveis para os mesmos bytes.
É quase como se cada campo viesse acompanhado de uma ficha criminal completa.
05 WS-SALDO PIC S9(7)V99 COMP-3.
Esta linha não diz apenas que existe um saldo.
Ela revela que:
o campo é numérico;
aceita sinal;
possui sete posições inteiras;
possui duas posições decimais implícitas;
utiliza formato decimal compactado;
ocupa uma quantidade específica de bytes;
precisa ser manipulado respeitando sua representação interna.
O compilador utiliza essa descrição para gerar as instruções adequadas.
Portanto, a Data Division é um contrato de memória.
O laboratório possui diferentes salas
A Data Division pode conter várias seções:
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
LOCAL-STORAGE SECTION.
LINKAGE SECTION.
SCREEN SECTION.
REPORT SECTION.
Nem todas precisam aparecer no mesmo programa.
Em sistemas corporativos atuais, as mais frequentes são:
FILE SECTION;WORKING-STORAGE SECTION;LINKAGE SECTION.
A LOCAL-STORAGE SECTION também é muito importante em programas reutilizáveis e reentrantes.
SCREEN SECTION e REPORT SECTION são mais específicas e hoje aparecem com menor frequência em ambientes IBM z/OS tradicionais.
Vamos entrar em cada sala do laboratório.
FILE SECTION: o depósito de evidências externas
A FILE SECTION descreve os registros dos arquivos utilizados pelo programa.
Um detalhe crucial: ela não armazena o arquivo inteiro na memória.
Ela normalmente representa uma área onde um registro é disponibilizado a cada operação de leitura ou gravação.
Considere o seguinte arquivo de clientes:
00001JOAO DA SILVA 00000150000
00002MARIA OLIVEIRA 00000275050
00003PEDRO SANTOS 00000098075
Um arquivo pode possuir milhões de registros. O programa não precisa carregar todos de uma vez.
Na FILE SECTION, descrevemos o formato de um registro:
FILE SECTION.
FD ARQ-CLIENTES.
01 REG-CLIENTE.
05 REG-CODIGO PIC 9(5).
05 REG-NOME PIC X(30).
05 REG-SALDO PIC 9(9)V99.
O FD significa File Description.
Ele introduz a descrição lógica do arquivo.
FD ARQ-CLIENTES.
Logo abaixo aparece o registro:
01 REG-CLIENTE.
Quando o programa executa:
READ ARQ-CLIENTES
o próximo registro disponível é colocado na área associada ao arquivo.
Em termos simplificados:
Arquivo em disco
|
v
Sistema de arquivos
|
v
Buffer de entrada
|
v
REG-CLIENTE
Depois, o programa pode examinar os campos:
DISPLAY REG-CODIGO
DISPLAY REG-NOME
DISPLAY REG-SALDO
Uma correção importante sobre a imagem
Na imagem apresentada, a File Section aparece relacionada a “variáveis temporárias”. Isso pode causar confusão.
A FILE SECTION não é normalmente utilizada como área genérica de variáveis temporárias. Sua função principal é descrever registros associados a arquivos.
As variáveis temporárias ficam geralmente na WORKING-STORAGE SECTION ou na LOCAL-STORAGE SECTION.
Em uma investigação técnica, essa distinção é importante. Colocar a etiqueta errada em uma evidência pode comprometer todo o caso.
O arquivo não existe sozinho: SELECT e FD
Em muitos programas, a declaração do arquivo possui duas partes.
Na ENVIRONMENT DIVISION, encontramos o SELECT:
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT ARQ-CLIENTES
ASSIGN TO CLIENTES
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS.
Na DATA DIVISION, encontramos o FD:
FILE SECTION.
FD ARQ-CLIENTES.
01 REG-CLIENTE.
05 REG-CODIGO PIC 9(5).
05 REG-NOME PIC X(30).
05 REG-SALDO PIC 9(9)V99.
O SELECT descreve a relação lógica com o ambiente.
O FD descreve o registro.
É como se o SELECT informasse onde a evidência foi encontrada, enquanto o FD explicasse sua estrutura física.
FILE STATUS: o laudo que muitos esquecem de consultar
Um programa robusto não deve assumir que toda operação de arquivo funcionará corretamente.
Por isso utilizamos:
FILE STATUS IS WS-FILE-STATUS
E declaramos:
01 WS-FILE-STATUS PIC XX.
Após um OPEN, READ, WRITE, REWRITE, DELETE ou CLOSE, o campo pode indicar o resultado da operação.
Exemplo:
READ ARQ-CLIENTES
AT END
SET FIM-ARQUIVO TO TRUE
END-READ
Ou:
IF WS-FILE-STATUS NOT = '00'
DISPLAY 'ERRO DE ARQUIVO: ' WS-FILE-STATUS
END-IF
O valor 00 normalmente representa sucesso.
Mas atenção: os significados exatos podem variar conforme a operação e a organização do arquivo.
Uma boa prática é nunca tratar o status de arquivo como um detalhe decorativo.
Um READ malsucedido não é silêncio. É uma testemunha tentando falar.
WORKING-STORAGE SECTION: a sala central do laboratório
A WORKING-STORAGE SECTION é uma das áreas mais utilizadas em programas COBOL.
Ela contém dados que geralmente existem durante toda a execução da unidade de programa.
WORKING-STORAGE SECTION.
01 WS-CONTADOR PIC 9(5) VALUE ZERO.
01 WS-TOTAL PIC S9(9)V99 COMP-3 VALUE ZERO.
01 WS-FIM-ARQUIVO PIC X VALUE 'N'.
Enquanto o programa estiver ativo, essas áreas permanecem disponíveis.
Podemos utilizar a Working-Storage para:
contadores;
acumuladores;
flags;
mensagens;
campos auxiliares;
datas;
áreas de entrada e saída;
tabelas;
constantes;
códigos de retorno;
estruturas temporárias;
copybooks;
campos utilizados em cálculos.
Contador
01 WS-QTD-CLIENTES PIC 9(7) VALUE ZERO.
Uso:
ADD 1 TO WS-QTD-CLIENTES
Acumulador
01 WS-TOTAL-SALDOS PIC S9(11)V99 COMP-3
VALUE ZERO.
Uso:
ADD REG-SALDO TO WS-TOTAL-SALDOS
Flag
01 WS-FIM-ARQUIVO PIC X VALUE 'N'.
Uso:
IF WS-FIM-ARQUIVO = 'S'
DISPLAY 'PROCESSAMENTO ENCERRADO'
END-IF
Funciona, mas pode ser melhorado com nível 88.
Nível 88: o testemunho semântico
O nível 88 não reserva uma nova área de memória.
Ele fornece nomes significativos para valores ou condições de outro campo.
01 WS-FIM-ARQUIVO PIC X VALUE 'N'.
88 FIM-ARQUIVO VALUE 'S'.
88 NAO-FIM-ARQUIVO VALUE 'N'.
Agora podemos escrever:
SET FIM-ARQUIVO TO TRUE
E testar:
PERFORM UNTIL FIM-ARQUIVO
Isso é muito mais expressivo do que:
PERFORM UNTIL WS-FIM-ARQUIVO = 'S'
Os dois funcionam, mas o nível 88 aproxima o código da linguagem do negócio.
Outro exemplo:
01 WS-TIPO-CLIENTE PIC X.
88 CLIENTE-COMUM VALUE 'C'.
88 CLIENTE-VIP VALUE 'V'.
88 CLIENTE-BLOQUEADO VALUE 'B'.
Uso:
IF CLIENTE-BLOQUEADO
DISPLAY 'OPERACAO NAO AUTORIZADA'
END-IF
O nível 88 é como uma legenda pericial: ele transforma valores brutos em significados compreensíveis.
VALUE: o estado inicial da evidência
A cláusula VALUE define um conteúdo inicial.
01 WS-CONTADOR PIC 9(5) VALUE ZERO.
01 WS-MENSAGEM PIC X(30)
VALUE 'INICIO DO PROCESSAMENTO'.
01 WS-STATUS PIC X VALUE 'A'.
Sem uma inicialização adequada, o programa pode trabalhar com conteúdo inesperado.
Em muitos ambientes, determinadas áreas podem receber valores previsíveis conforme o compilador, o runtime e a forma de carregamento. Porém, um programa profissional não deve depender de suposições frágeis.
Sempre que o valor inicial for importante, declare-o ou inicialize-o explicitamente.
INITIALIZE WS-AREA-TRABALHO
Mas investigue o uso de INITIALIZE com cuidado: ele não é necessariamente equivalente a mover espaços ou zeros para todos os campos indiscriminadamente. Seu comportamento respeita as categorias dos itens e pode ser alterado com cláusulas adicionais.
LOCAL-STORAGE SECTION: cada chamada recebe uma cena isolada
A LOCAL-STORAGE SECTION é semelhante à Working-Storage, mas possui uma diferença essencial: suas áreas são alocadas para cada ativação do programa.
LOCAL-STORAGE SECTION.
01 LS-CONTADOR PIC 9(5) VALUE ZERO.
01 LS-MENSAGEM PIC X(40).
Em termos conceituais:
Primeira chamada:
LS-CONTADOR próprio
Segunda chamada:
LS-CONTADOR próprio
Terceira chamada:
LS-CONTADOR próprio
Cada invocação recebe seu próprio conjunto de dados locais.
Isso é especialmente útil em:
programas reentrantes;
rotinas reutilizáveis;
ambientes com várias chamadas simultâneas;
aplicações servidoras;
processamento sob Language Environment;
componentes que não devem preservar estado entre ativações.
Working-Storage versus Local-Storage
Uma simplificação útil:
| Característica | Working-Storage | Local-Storage |
|---|---|---|
| Existência | Associada à instância carregada do programa | Criada para cada invocação |
| Inicialização | Normalmente ocorre quando o programa é carregado | Ocorre em cada entrada |
| Uso típico | Estado geral, constantes, áreas persistentes durante execução | Dados privados de uma chamada |
| Reentrância | Exige maior cuidado | Facilita isolamento por ativação |
Entretanto, não reduza a discussão a “Working-Storage é compartilhada e Local-Storage nunca é”.
O comportamento real depende de fatores como:
forma de chamada;
programas estáticos ou dinâmicos;
uso de
CANCEL;opções de compilação;
ambiente CICS;
atributos de reentrância;
runtime;
arquitetura da aplicação.
No laboratório do mainframe, frases absolutas são frequentemente suspeitas.
Um caso clássico: o contador que se recusava a voltar para zero
Imagine um subprograma:
IDENTIFICATION DIVISION.
PROGRAM-ID. CALCULA01.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CONTADOR PIC 9(4) VALUE ZERO.
PROCEDURE DIVISION.
ADD 1 TO WS-CONTADOR
DISPLAY 'CONTADOR: ' WS-CONTADOR
GOBACK.
O programa chamador executa várias chamadas.
Um iniciante pode esperar:
CONTADOR: 0001
CONTADOR: 0001
CONTADOR: 0001
Mas, dependendo da forma de carregamento e das condições de execução, pode observar:
CONTADOR: 0001
CONTADOR: 0002
CONTADOR: 0003
Isso ocorre porque a Working-Storage pode manter seu conteúdo entre chamadas enquanto o programa permanecer carregado.
Mover o contador para Local-Storage muda a intenção:
LOCAL-STORAGE SECTION.
01 LS-CONTADOR PIC 9(4) VALUE ZERO.
Agora cada chamada recebe uma nova área inicializada.
Esse tipo de comportamento parece sobrenatural apenas até encontrarmos a seção correta.
Depois disso, torna-se evidência técnica.
LINKAGE SECTION: dados que pertencem a outra pessoa
A LINKAGE SECTION descreve dados fornecidos por outra unidade de execução.
Esses dados não são, em princípio, áreas comuns criadas e pertencentes ao subprograma da mesma forma que itens da Working-Storage.
Exemplo de programa chamador:
WORKING-STORAGE SECTION.
01 WS-CLIENTE.
05 WS-CODIGO PIC 9(5).
05 WS-NOME PIC X(30).
05 WS-SALDO PIC S9(7)V99 COMP-3.
PROCEDURE DIVISION.
MOVE 12345 TO WS-CODIGO
MOVE 'STELLA BONASERA' TO WS-NOME
MOVE 1500.75 TO WS-SALDO
CALL 'ATUALIZA1'
USING WS-CLIENTE
GOBACK.
Programa chamado:
IDENTIFICATION DIVISION.
PROGRAM-ID. ATUALIZA1.
DATA DIVISION.
LINKAGE SECTION.
01 LK-CLIENTE.
05 LK-CODIGO PIC 9(5).
05 LK-NOME PIC X(30).
05 LK-SALDO PIC S9(7)V99 COMP-3.
PROCEDURE DIVISION USING LK-CLIENTE.
ADD 100 TO LK-SALDO
GOBACK.
O chamador entrega uma área.
O subprograma descreve essa área na Linkage Section.
A PROCEDURE DIVISION USING estabelece a associação entre os argumentos recebidos e os itens de Linkage.
BY REFERENCE, BY CONTENT e BY VALUE
O método de passagem de parâmetros muda completamente a investigação.
BY REFERENCE
É a forma tradicional e muito comum.
CALL 'ATUALIZA1'
USING BY REFERENCE WS-CLIENTE
O subprograma recebe acesso à área do chamador.
Alterações podem ser percebidas pelo chamador.
Chamador possui WS-SALDO
|
v
Subprograma acessa a mesma área
|
v
Alteração volta para o chamador
BY CONTENT
O subprograma recebe uma cópia lógica do argumento.
CALL 'CONSULTA1'
USING BY CONTENT WS-CLIENTE
O programa chamado pode trabalhar com o conteúdo, mas mudanças não devem retornar para a área original como numa passagem por referência.
BY VALUE
Utilizado para passar o valor diretamente, sendo especialmente relevante na interoperabilidade com outras linguagens e APIs.
CALL 'ROTINAC'
USING BY VALUE WS-CODIGO
BY VALUE exige atenção a tamanho, representação e convenções de chamada.
Não basta o número “parecer igual”. Os bytes precisam ser compatíveis.
Em integração, o assassino frequentemente se chama “layout incompatível”.
O contrato precisa ser idêntico
Considere o chamador:
01 WS-CLIENTE.
05 WS-CODIGO PIC 9(5).
05 WS-NOME PIC X(30).
05 WS-SALDO PIC S9(7)V99 COMP-3.
E o chamado:
01 LK-CLIENTE.
05 LK-CODIGO PIC 9(5).
05 LK-NOME PIC X(20).
05 LK-SALDO PIC S9(7)V99 COMP-3.
O nome possui 30 bytes no chamador, mas apenas 20 na visão do chamado.
Isso desloca o campo seguinte.
O subprograma pode interpretar os últimos dez bytes do nome como parte do saldo.
Não existe magia.
Existe desalinhamento.
Em sistemas reais, esse erro pode gerar:
valores corrompidos;
dados aparentemente aleatórios;
S0C7;sobreposição de campos;
gravações incorretas;
falhas intermitentes;
resultados diferentes após recompilações.
Por isso copybooks compartilhados são tão importantes.
COPYBOOK: o laudo oficial do layout
Em vez de repetir a mesma estrutura em dezenas de programas, podemos centralizá-la em um copybook.
Arquivo CPYCLI01:
01 CLIENTE-DADOS.
05 CLIENTE-CODIGO PIC 9(5).
05 CLIENTE-NOME PIC X(30).
05 CLIENTE-SALDO PIC S9(7)V99 COMP-3.
No chamador:
WORKING-STORAGE SECTION.
COPY CPYCLI01.
No chamado:
LINKAGE SECTION.
COPY CPYCLI01.
Assim, ambos utilizam a mesma definição.
Mas existe uma armadilha: alterar o copybook e recompilar apenas um dos programas.
O fonte parece correto.
O copybook parece correto.
O programa recompilado parece correto.
Mas o módulo antigo continua esperando o layout anterior.
E então nasce um crime perfeito: duas versões corretas individualmente, incompatíveis quando trabalham juntas.
A solução envolve disciplina de gestão de dependências, recompilação, testes e controle de versões.
SCREEN SECTION: a antiga sala de interrogatório
A SCREEN SECTION permite definir telas em implementações COBOL que oferecem esse recurso.
Exemplo genérico:
SCREEN SECTION.
01 TELA-CLIENTE.
05 BLANK SCREEN.
05 LINE 3 COLUMN 10
VALUE 'CODIGO DO CLIENTE:'.
05 LINE 3 COLUMN 30
PIC 9(5)
USING WS-CODIGO.
Ela foi utilizada em sistemas interativos de diversas plataformas.
No universo IBM mainframe, especialmente em aplicações CICS, interfaces 3270 são frequentemente definidas por mapas BMS.
Em IMS, pode-se encontrar MFS.
Em arquiteturas modernas, o COBOL pode funcionar atrás de:
APIs REST;
aplicações web;
serviços de integração;
mensageria;
z/OS Connect;
CICS Transaction Gateway;
aplicativos móveis.
A tela pode ter desaparecido do COBOL, mas o programa continua processando o coração da transação.
REPORT SECTION: o especialista aposentado que ainda sabe demais
A REPORT SECTION está associada ao Report Writer, um recurso criado para facilitar a produção de relatórios estruturados.
Ela podia ajudar a organizar:
cabeçalhos;
detalhes;
rodapés;
totais;
quebras de grupo;
paginação.
Exemplo conceitual:
REPORT SECTION.
RD RELATORIO-VENDAS.
01 TYPE IS PAGE HEADING.
05 COLUMN 1 PIC X(20)
VALUE 'RELATORIO DE VENDAS'.
Atualmente, muitos relatórios são produzidos por:
programas tradicionais;
DFSORT;
ferramentas de BI;
bancos de dados;
aplicações distribuídas;
geradores de documentos;
plataformas analíticas.
Mesmo assim, sistemas antigos podem utilizar Report Writer até hoje.
Em mainframe, “antigo” não significa necessariamente “inútil”.
Às vezes significa apenas “estável há 35 anos e ainda fechando a contabilidade antes do amanhecer”.
A hierarquia dos níveis: reconstruindo o corpo do registro
COBOL utiliza números de nível para representar a hierarquia dos dados.
01 WS-CLIENTE.
05 WS-IDENTIFICACAO.
10 WS-CODIGO PIC 9(5).
10 WS-CPF PIC 9(11).
05 WS-NOME PIC X(30).
05 WS-ENDERECO.
10 WS-RUA PIC X(30).
10 WS-NUMERO PIC 9(5).
10 WS-CIDADE PIC X(20).
Visualmente:
WS-CLIENTE
├── WS-IDENTIFICACAO
│ ├── WS-CODIGO
│ └── WS-CPF
├── WS-NOME
└── WS-ENDERECO
├── WS-RUA
├── WS-NUMERO
└── WS-CIDADE
Os itens de grupo não possuem PIC.
Eles são formados pelos campos subordinados.
Podemos mover ou exibir o grupo inteiro:
DISPLAY WS-CLIENTE
Mas isso manipula a sequência física dos bytes, não uma abstração independente.
Os níveis especiais
Além dos níveis comuns, COBOL possui níveis especiais.
Nível 01
Define uma estrutura principal ou registro.
01 WS-CLIENTE.
Níveis 02 a 49
Definem itens subordinados.
05 WS-NOME PIC X(30).
Nível 66
Utilizado com RENAMES.
66 WS-DADOS-BASICOS RENAMES
WS-CODIGO THRU WS-NOME.
Hoje é pouco usado e deve ser tratado com cuidado, pois pode dificultar a leitura.
Nível 77
Define um item elementar independente.
77 WS-CONTADOR PIC 9(5).
Ainda pode ser encontrado em programas antigos, embora muitos padrões modernos prefiram organizar itens sob níveis 01.
Nível 78
Em algumas implementações, é utilizado para constantes de compilação.
Sua disponibilidade e detalhes dependem do compilador.
Nível 88
Define nomes de condição.
88 CLIENTE-ATIVO VALUE 'A'.
PICTURE: o retrato falado do dado
A cláusula PIC, abreviação de PICTURE, descreve a categoria e o formato do campo.
Alfanumérico
05 WS-NOME PIC X(30).
Pode conter letras, números, espaços e outros caracteres compatíveis com a codificação utilizada.
Numérico
05 WS-IDADE PIC 9(3).
Representa três posições numéricas.
Alfabético
05 WS-INICIAL PIC A.
Seu uso é menos comum em programas modernos.
Com sinal
05 WS-SALDO PIC S9(7)V99.
O S indica sinal.
Decimal implícito
05 WS-VALOR PIC 9(5)V99.
O V não ocupa uma posição física.
Ele indica onde o programa deve interpretar a casa decimal.
O valor:
0012345
pode ser interpretado como:
123,45
de acordo com o layout.
Esse é um dos grandes suspeitos em integrações.
Se um sistema envia centavos e outro espera reais, um pagamento de R$ 150,00 pode ser interpretado como R$ 15.000,00 ou R$ 1,50.
O byte está correto.
A interpretação está errada.
DISPLAY, COMP, COMP-3: mesma informação, corpos diferentes
Um número pode possuir diferentes representações internas.
DISPLAY
05 WS-VALOR PIC 9(5).
Cada dígito costuma ocupar uma posição de caractere.
COMP ou BINARY
05 WS-VALOR PIC S9(9) COMP.
O valor é armazenado em representação binária conforme as regras do compilador e opções aplicáveis.
COMP-3
05 WS-VALOR PIC S9(7)V99 COMP-3.
Utiliza decimal compactado.
Dois dígitos normalmente são armazenados por byte, com uma parte do último byte usada para o sinal.
É muito comum em aplicações financeiras porque representa valores decimais com precisão e economia de espaço.
A pista do S0C7
Um S0C7 normalmente aponta para uma exceção de dados durante uma operação decimal.
Causas comuns:
campo numérico contém bytes inválidos;
layout incorreto;
deslocamento provocado por copybook incompatível;
campo não inicializado;
arquivo lido com definição errada;
tentativa de calcular usando dados alfanuméricos;
erro de
REDEFINES;parâmetros desalinhados.
O erro aparece na Procedure Division porque uma instrução tentou utilizar o campo.
Mas o verdadeiro criminoso pode estar na Data Division.
REDEFINES: duas interpretações para a mesma evidência
REDEFINES permite que a mesma área de memória seja vista de maneiras diferentes.
01 WS-DATA-NUMERICA.
05 WS-DATA-AAAAMMDD PIC 9(8).
01 WS-DATA-DETALHADA
REDEFINES WS-DATA-NUMERICA.
05 WS-ANO PIC 9(4).
05 WS-MES PIC 9(2).
05 WS-DIA PIC 9(2).
Se:
MOVE 20260725 TO WS-DATA-AAAAMMDD
então podemos acessar:
WS-ANO = 2026
WS-MES = 07
WS-DIA = 25
Nenhum dado foi copiado.
A mesma sequência de bytes recebeu duas descrições.
É poderoso, eficiente e perigoso.
Um REDEFINES incorreto pode transformar:
nome em número;
data em saldo;
código em endereço;
payload em campos incompatíveis.
No CSI do mainframe, REDEFINES é aquele suspeito elegante que parece sempre ter um álibi.
OCCURS: a formação da quadrilha
OCCURS define repetições de uma estrutura, criando tabelas.
01 WS-TABELA-CLIENTES.
05 WS-CLIENTE OCCURS 100 TIMES.
10 WS-CODIGO PIC 9(5).
10 WS-NOME PIC X(30).
Isso reserva espaço para cem ocorrências.
Podemos acessar:
MOVE 12345 TO WS-CODIGO (1)
MOVE 'MAC TAYLOR' TO WS-NOME (1)
Subscript
01 WS-I PIC 9(3) COMP VALUE 1.
DISPLAY WS-NOME (WS-I)
Index
A tabela pode ser definida com índice:
05 WS-CLIENTE OCCURS 100 TIMES
INDEXED BY IDX-CLIENTE.
Uso:
SET IDX-CLIENTE TO 1
DISPLAY WS-NOME (IDX-CLIENTE)
O índice possui natureza própria e pode ser otimizado para o deslocamento interno da tabela.
Nunca ultrapasse os limites do OCCURS.
Acessar a ocorrência 101 de uma tabela com cem elementos pode atingir memória pertencente a outro campo.
É o equivalente digital de atravessar a fita amarela da cena do crime.
Passo a passo: montando um programa completo
Vamos construir um pequeno processamento sequencial.
1. Declarar o arquivo no ambiente
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT ARQ-CLIENTES
ASSIGN TO CLIENTES
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FILE-STATUS.
2. Descrever o registro
DATA DIVISION.
FILE SECTION.
FD ARQ-CLIENTES.
01 REG-CLIENTE.
05 REG-CODIGO PIC 9(5).
05 REG-NOME PIC X(30).
05 REG-SALDO PIC S9(7)V99 COMP-3.
3. Criar áreas auxiliares
WORKING-STORAGE SECTION.
01 WS-FILE-STATUS PIC XX.
01 WS-CONTROLES.
05 WS-FIM PIC X VALUE 'N'.
88 FIM-ARQUIVO VALUE 'S'.
88 HA-REGISTRO VALUE 'N'.
01 WS-TOTAIS.
05 WS-QTD PIC 9(7) VALUE ZERO.
05 WS-TOTAL-SALDO PIC S9(13)V99 COMP-3
VALUE ZERO.
4. Abrir o arquivo
PROCEDURE DIVISION.
0000-PRINCIPAL.
OPEN INPUT ARQ-CLIENTES
IF WS-FILE-STATUS NOT = '00'
DISPLAY 'ERRO NO OPEN: ' WS-FILE-STATUS
MOVE 12 TO RETURN-CODE
GOBACK
END-IF
5. Ler o primeiro registro
PERFORM 1000-LER-CLIENTE
6. Processar até o fim
PERFORM UNTIL FIM-ARQUIVO
ADD 1 TO WS-QTD
ADD REG-SALDO TO WS-TOTAL-SALDO
DISPLAY REG-CODIGO
' '
REG-NOME
PERFORM 1000-LER-CLIENTE
END-PERFORM
7. Encerrar
CLOSE ARQ-CLIENTES
DISPLAY 'CLIENTES: ' WS-QTD
DISPLAY 'TOTAL: ' WS-TOTAL-SALDO
GOBACK.
8. Criar o parágrafo de leitura
1000-LER-CLIENTE.
READ ARQ-CLIENTES
AT END
SET FIM-ARQUIVO TO TRUE
NOT AT END
SET HA-REGISTRO TO TRUE
END-READ.
O programa completo demonstra como cada seção coopera.
A File Section fornece o registro.
A Working-Storage guarda o controle e os totais.
A Procedure Division conduz o processamento.
Cada elemento possui responsabilidade própria.
Checklist forense para analisar uma DATA DIVISION
Quando um programa apresenta erro de dados, examine:
1. O PIC está correto?
Compare tamanho, sinal e casas decimais.
PIC 9(7)V99
não é equivalente a:
PIC 9(9)
2. O formato interno é compatível?
PIC S9(7)V99 COMP-3
não possui a mesma representação física de:
PIC X(5)
3. O arquivo está com o LRECL esperado?
Se o programa espera 100 bytes, mas o arquivo possui 120, parte do registro pode ser ignorada ou interpretada incorretamente.
4. O copybook é a versão correta?
Verifique data, biblioteca, concatenação e versão usada na compilação.
5. Chamador e chamado foram recompilados?
Uma mudança na interface pode exigir recompilar todos os consumidores.
6. Existe REDEFINES?
Descubra qual visão está sendo usada em cada ponto.
7. Existe OCCURS?
Verifique limites, índices e subscritos.
8. O campo foi inicializado?
Não presuma que contém zeros ou espaços.
9. O parâmetro foi passado pelo método correto?
Confirme BY REFERENCE, BY CONTENT ou BY VALUE.
10. O campo possui alinhamento ou sincronização especial?
Cláusulas como SYNCHRONIZED podem introduzir bytes de preenchimento dependendo do compilador e da estrutura.
Curiosidades da sala de evidências
Curiosidade 1: o nome do campo não existe no arquivo
Um arquivo físico não contém necessariamente nomes como:
REG-CODIGO
REG-NOME
REG-SALDO
Ele contém bytes.
Os nomes existem no programa para que os humanos e o compilador interpretem as posições.
O mesmo arquivo pode receber layouts diferentes.
Um deles estará correto.
Os demais produzirão ficção científica.
Curiosidade 2: PIC não é uma máscara de exibição apenas
Muitos iniciantes associam PICTURE apenas à aparência.
Na verdade, ela define categoria, tamanho e interpretação do item.
Curiosidade 3: o ponto decimal pode não existir fisicamente
No V implícito, nenhum caractere de ponto ou vírgula é armazenado.
PIC 9(5)V99
ocupa sete dígitos, não oito posições contendo um separador.
Curiosidade 4: um item de grupo pode misturar categorias
01 WS-REGISTRO.
05 WS-CODIGO PIC 9(5).
05 WS-NOME PIC X(20).
O grupo inteiro pode ser tratado como uma sequência de bytes para certas operações, apesar de conter campos de diferentes categorias.
Curiosidade 5: a memória é inocente
A memória não sabe que aqueles bytes representam CPF, saldo ou data.
Ela apenas armazena bits.
O significado vem do layout.
Quando o layout mente, a memória obedece.
Easter eggs do laboratório CSI Mainframe
Em homenagem ao clima de investigação, os nomes dos exemplos esconderam algumas referências.
MAC TAYLORaparece como líder da equipe forense de Nova York.STELLA BONASERAfoi usada em uma estrutura de cliente.O horário de 02h17 lembra a rotina dos processamentos batch que terminam quando a cidade dorme.
O terminal 3270 funciona como a mesa de autópsia digital.
O
S0C7é o equivalente mainframe de encontrar uma impressão digital impossível na arma do crime.O copybook incompatível é o “suspeito que possuía duas identidades”.
O
REDEFINESrepresenta a evidência que muda de aparência dependendo do ângulo da luz.O
OCCURSé a fotografia de uma quadrilha perfeitamente alinhada na parede do distrito policial.
E existe um easter egg maior.
Em quase todo mistério COBOL, o programador procura primeiro a linha que falhou.
O investigador experiente procura primeiro o dado que chegou até ela.
Dicas do Bellacosa Mainframe para o iniciante
Use nomes que revelem intenção
Evite:
01 X1 PIC X.
Prefira:
01 WS-STATUS-CLIENTE PIC X.
Agrupe campos relacionados
01 WS-CONTROLES.
05 WS-FIM-ARQUIVO PIC X.
05 WS-ERRO PIC X.
05 WS-RETORNO PIC 9(4).
Use níveis 88
Eles tornam o código mais claro e reduzem comparações espalhadas.
Padronize prefixos
Exemplo:
WS-para Working-Storage;LS-para Local-Storage;LK-para Linkage;REG-para registros de arquivo;CT-para constantes;IDX-para índices.
Isso não é uma regra universal da linguagem, mas ajuda muito na leitura.
Documente formatos numéricos
Especialmente:
sinal;
casas decimais;
COMP;COMP-3;valores enviados a outros sistemas.
Trate copybooks como interfaces
Uma alteração em copybook não é apenas uma mudança de layout.
Pode ser uma mudança de contrato entre dezenas ou centenas de programas.
Nunca ignore o tamanho total
Calcule o comprimento do registro.
Confirme com:
LRECL;
documentação;
catálogo;
ferramenta de browse;
definição do dataset;
programa produtor;
programa consumidor.
Teste limites
Para OCCURS, teste:
primeira posição;
última posição válida;
tabela vazia;
tabela completa;
tentativa de ultrapassagem;
chave não encontrada.
A conclusão da investigação
A equipe se reúne diante do painel de vidro.
O programa não falhou porque o ADD estava errado.
A instrução era perfeitamente válida.
O problema estava em um copybook antigo usado por um subprograma que não havia sido recompilado.
O chamador enviava:
05 CLIENTE-NOME PIC X(30).
O chamado ainda esperava:
05 CLIENTE-NOME PIC X(20).
Os dez bytes excedentes deslocaram o saldo.
Quando o programa tentou somar aquele conteúdo como decimal compactado, ocorreu o S0C7.
A linha da Procedure Division apenas encontrou o corpo.
O crime havia sido planejado na Data Division.
Essa é a lição central.
A DATA DIVISION não é um depósito passivo de declarações. Ela é a fundação arquitetural do programa COBOL.
A FILE SECTION descreve os registros que chegam de arquivos.
A WORKING-STORAGE SECTION mantém controles, acumuladores, tabelas e áreas utilizadas durante a execução.
A LOCAL-STORAGE SECTION fornece áreas independentes para cada ativação.
A LINKAGE SECTION estabelece contratos de comunicação entre programas.
A SCREEN SECTION pode descrever interfaces interativas.
A REPORT SECTION pode organizar relatórios estruturados.
Os níveis constroem hierarquias.
A cláusula PIC define formatos.
VALUE estabelece estados iniciais.
OCCURS cria tabelas.
REDEFINES oferece diferentes interpretações para a mesma memória.
Os níveis 88 transformam códigos em significado.
COMP e COMP-3 revelam que dois campos numericamente semelhantes podem possuir corpos físicos completamente diferentes.
Para o programador iniciante, dominar a Data Division significa começar a enxergar além da aparência do código.
Você deixa de ver apenas:
05 SALDO PIC S9(7)V99 COMP-3.
E passa a enxergar:
Campo assinado
Decimal
Sete posições inteiras
Duas casas decimais
Representação compactada
Contrato físico de armazenamento
Possível ponto de integração
Possível origem de S0C7
Possível incompatibilidade com copybook
Esse é o momento em que o estudante deixa de apenas escrever COBOL e começa a investigar sistemas.
Porque na programação corporativa os defeitos raramente anunciam sua origem.
Eles aparecem em outro módulo.
Falham horas depois.
Dependem de um registro específico.
Só acontecem em produção.
Desaparecem durante o teste.
Retornam no fechamento mensal.
E deixam para trás apenas um dump, um código de retorno, algumas mensagens no spool e um campo cheio de bytes que ninguém deveria ter interpretado daquela maneira.
Mas não se preocupe.
Coloque as luvas.
Abra o compile listing.
Verifique o offset.
Compare o copybook.
Calcule o comprimento.
Analise o formato.
Siga o rastro dos bytes.
No laboratório do Bellacosa Mainframe, toda variável possui uma história.
Todo registro possui um passado.
Todo ABEND possui uma causa.
E toda Data Division, quando examinada com atenção, acaba confessando.
Sem comentários:
Enviar um comentário