1. Tipos de dataset VSAM
| Tipo | Nome | Acesso | Chave | Uso típico |
|---|---|---|---|---|
| KSDS | Key-Sequenced | Seq / Rand / Dinâmico | Campo definido no registro | Cadastros, tabelas de referência |
| ESDS | Entry-Sequenced | Seq / por RBA | Posição física (RBA) | Logs, trilhas de auditoria, filas |
| RRDS | Relative-Record | Seq / por número relativo | Número relativo do registro (1, 2, 3…) | Tabelas de tamanho fixo com slot numérico |
| LDS | Linear Data Set | Por endereço de byte (RBA) | Nenhuma (bloco bruto) | DB2 tablespaces, dados em memória |
🦕 Analogia — como encontrar um livro na biblioteca
ESDS é como uma prateleira onde os livros são empilhados na ordem de chegada — você encontra um livro só varrendo da primeira posição. KSDS é o catálogo de fichas pelo título: você vai direto ao "K" e pula para "Kafka" em microssegundos. RRDS é a estante numerada: "quero o livro do slot 42" — acesso direto pelo número, sem índice.
Estrutura interna do KSDS
KSDS (cluster) tem dois componentes: ┌─────────────────────────────────────────────┐ │ DATA component │ │ Registros em ordem de chave, em CIs │ │ (Control Intervals de 4KB a 32KB) │ │ │ │ [CI] 0001 0003 0005 │ free │ │ │ [CI] 0007 0009 0011 │ free │ │ │ [CI] 0013 … │ free │ │ └─────────────────────────────────────────────┘ ┌─────────────────────────────────────────────┐ │ INDEX component │ │ Árvore B+ com ponteiros para os CIs │ │ Busca em O(log n) — muito eficiente │ └─────────────────────────────────────────────┘ O espaço livre dentro dos CIs (FREESPACE) permite inserções sem reorganização imediata.
2. IDCAMS — criando e gerenciando datasets
O IDCAMS (Integrated Data Management Catalog Access Method Services) é o utilitário IBM para criar, excluir, copiar e listar datasets VSAM. O programador COBOL raramente escreve JCL de DEFINE CLUSTER do zero — isso geralmente é trabalho do DBA — mas conhecer o básico ajuda a entender os parâmetros que afetam a performance do arquivo (RECORDSIZE, KEYS, FREESPACE, SHAREOPTIONS).
O tema é coberto em profundidade no artigo dedicado: IDCAMS em Profundidade — DEFINE, REPRO, LISTCAT e mais. Por enquanto, saiba que todo arquivo VSAM precisa ser definido no catálogo antes de ser usado — e o IDCAMS é a ferramenta para isso.
3. FD no COBOL — declarando arquivos VSAM
ENVIRONMENT DIVISION. INPUT-OUTPUT SECTION. FILE-CONTROL. * KSDS — acesso por chave SELECT ARQ-CONTA ASSIGN TO AS-CONTA ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC *seq + random no mesmo OPEN RECORD KEY IS CNT-NR-CONTA *campo dentro do 01 FILE STATUS IS WS-FS-CONTA. * ESDS — acesso sequencial SELECT ARQ-LOG ASSIGN TO AS-LOG ORGANIZATION IS SEQUENTIAL ACCESS MODE IS SEQUENTIAL FILE STATUS IS WS-FS-LOG. * RRDS — acesso por número relativo SELECT ARQ-TAB ASSIGN TO AS-TAB ORGANIZATION IS RELATIVE ACCESS MODE IS RANDOM RELATIVE KEY IS WS-RRN *PIC 9(08) COMP fora do 01 FILE STATUS IS WS-FS-TAB. DATA DIVISION. FILE SECTION. FD ARQ-CONTA. 01 REG-CONTA. 05 CNT-NR-CONTA PIC X(10). *chave — mesma pos do SELECT 05 CNT-NM-TITULAR PIC X(60). 05 CNT-VL-SALDO S9(13)V99 COMP-3. 05 CNT-CD-STATUS PIC X(01). 05 FILLER PIC X(107). *completa 200 bytes WORKING-STORAGE SECTION. 01 WS-FS-CONTA PIC X(02). 01 WS-FS-LOG PIC X(02). 01 WS-FS-TAB PIC X(02). 01 WS-RRN S9(08) COMP.
⚠️ O campo RECORD KEY deve ser parte do 01 do FD
A cláusula RECORD KEY IS CNT-NR-CONTA precisa referenciar um campo que esteja dentro da estrutura 01 do FD — não na Working-Storage. O COBOL usa a posição e tamanho desse campo para localizar a chave dentro de cada registro lido ou gravado. Se você declarar a chave fora do FD, o compilador dará erro.
4. FILE STATUS — códigos de retorno
Após cada verbo de I/O (OPEN, READ, WRITE, REWRITE, DELETE, START, CLOSE), o COBOL preenche o campo FILE STATUS com um código de dois caracteres. Sempre verifique o FILE STATUS após cada operação — não fazer isso é a causa raiz da maioria dos bugs silenciosos com VSAM.
| FILE STATUS | Significado | Ação típica |
|---|---|---|
00 | Sucesso | Continua |
02 | Chave duplicada em AIX (alternate index) | Normal se AIX permite duplicatas |
10 | Fim de arquivo (AT END) | Encerra loop de leitura |
22 | Chave duplicada — registro já existe | Log do erro, ignorar ou atualizar |
23 | Registro não encontrado (READ/DELETE/REWRITE random) | Tratar como ausência |
24 | Chave fora de sequência (WRITE sequencial) | Verificar ordenação dos dados |
30 | Erro permanente de I/O | Abend — problema de hardware/dataset |
35 | Dataset não alocado no OPEN | Verificar DD no JCL ou DISP no catálogo |
39 | Conflito de atributos (LRECL, RECFM, KEYLEN) | Alinhar FD com definição IDCAMS |
46 | READ sequencial sem START/OPEN anterior | Verificar lógica de acesso |
47 | READ em arquivo não aberto com INPUT ou I-O | Verificar modo do OPEN |
48 | WRITE em arquivo não aberto com OUTPUT ou I-O | Verificar modo do OPEN |
49 | REWRITE/DELETE em arquivo não aberto com I-O | Mudar OPEN para I-O |
9800-CHECA-FS-CONTA. IF WS-FS-CONTA NOT = '00' AND NOT = '10' AND NOT = '23' DISPLAY 'ERRO VSAM ARQ-CONTA FS=' WS-FS-CONTA MOVE 16 TO RETURN-CODE STOP RUN END-IF.
5. KSDS — acesso aleatório por chave
PROCEDURE DIVISION. 0000-INICIO. OPEN I-O ARQ-CONTA IF WS-FS-CONTA NOT = '00' DISPLAY 'OPEN FALHOU FS=' WS-FS-CONTA STOP RUN END-IF. 3000-LE-CONTA. MOVE WS-CHAVE-PESQ TO CNT-NR-CONTA READ ARQ-CONTA INVALID KEY MOVE 'N' TO WS-ENCONTROU NOT INVALID KEY MOVE 'S' TO WS-ENCONTROU END-READ. * INVALID KEY dispara quando FILE STATUS = 23 4000-ATUALIZA-CONTA. * Após READ sem INVALID KEY, modifica os campos: ADD WS-CREDITO TO CNT-VL-SALDO REWRITE REG-CONTA INVALID KEY DISPLAY 'REWRITE FALHOU FS=' WS-FS-CONTA STOP RUN END-REWRITE. * REWRITE opera sobre o último registro lido * A chave não pode mudar no REWRITE 5000-INCLUI-CONTA. * Preenche todo o registro antes de escrever INITIALIZE REG-CONTA MOVE WS-NR-CONTA TO CNT-NR-CONTA MOVE WS-NOME TO CNT-NM-TITULAR MOVE 0 TO CNT-VL-SALDO MOVE 'A' TO CNT-CD-STATUS WRITE REG-CONTA INVALID KEY IF WS-FS-CONTA = '22' DISPLAY 'CONTA JA EXISTE' ELSE DISPLAY 'WRITE ERRO FS=' WS-FS-CONTA STOP RUN END-IF END-WRITE. 6000-EXCLUI-CONTA. MOVE WS-CHAVE-PESQ TO CNT-NR-CONTA DELETE ARQ-CONTA INVALID KEY DISPLAY 'CONTA NAO ENCONTRADA PARA DELETE' END-DELETE. * DELETE aleatório: coloca chave na RECORD KEY e chama DELETE * DELETE sequencial: após READ sequencial, DELETE sem INVALID KEY
6. KSDS — acesso sequencial e START
Com ACCESS MODE IS SEQUENTIAL, o KSDS é lido em ordem de chave do início ao fim, como um arquivo sequencial. O verbo START posiciona o ponteiro em qualquer ponto do arquivo para começar uma varredura parcial:
* SELECT com ACCESS MODE IS SEQUENTIAL 1000-LE-TODAS-CONTAS. OPEN INPUT ARQ-CONTA PERFORM 1100-LE-PROXIMO PERFORM 1200-LOOP-CONTA UNTIL WS-FS-CONTA = '10' CLOSE ARQ-CONTA. 1100-LE-PROXIMO. READ ARQ-CONTA NEXT RECORD AT END CONTINUE END-READ. 1200-LOOP-CONTA. PERFORM 2000-PROCESSA PERFORM 1100-LE-PROXIMO. *--- Varredura parcial a partir de uma chave --- 3000-BROWSE-A-PARTIR. MOVE WS-CHAVE-INICIO TO CNT-NR-CONTA START ARQ-CONTA KEY IS GREATER THAN OR EQUAL TO CNT-NR-CONTA INVALID KEY DISPLAY 'START FALHOU FS=' WS-FS-CONTA STOP RUN END-START * A partir daqui, READ NEXT lê em ordem crescente * a partir da primeira chave >= WS-CHAVE-INICIO PERFORM 1100-LE-PROXIMO PERFORM UNTIL WS-FS-CONTA = '10' OR CNT-NR-CONTA > WS-CHAVE-FIM PERFORM 2000-PROCESSA PERFORM 1100-LE-PROXIMO END-PERFORM.
✅ Opções do START KEY
KEY IS EQUAL TO— posiciona exatamente nessa chave (= 23 se não existe)KEY IS GREATER THAN— primeiro registro cuja chave > valorKEY IS NOT LESS THAN— equivalente a >=, o mais comum em browseKEY IS LESS THAN— não suportado em KSDS (índice é ascendente)
7. KSDS — acesso dinâmico (misto)
ACCESS MODE IS DYNAMIC permite alternar entre acesso aleatório e sequencial no mesmo OPEN — o mais flexível e o mais comum em programas que tanto consultam por chave quanto varrem um intervalo:
* SELECT … ACCESS MODE IS DYNAMIC PROCEDURE DIVISION. OPEN I-O ARQ-CONTA * Acesso ALEATÓRIO — lê uma conta específica MOVE '1234567890' TO CNT-NR-CONTA READ ARQ-CONTA *READ sem NEXT = aleatório INVALID KEY DISPLAY 'NAO ENCONTRADA' END-READ * Acesso SEQUENCIAL — posiciona e varre um intervalo MOVE '5000000000' TO CNT-NR-CONTA START ARQ-CONTA KEY NOT LESS THAN CNT-NR-CONTA INVALID KEY DISPLAY 'START FALHOU' END-START PERFORM UNTIL WS-FS-CONTA = '10' READ ARQ-CONTA NEXT RECORD *READ NEXT = sequencial AT END CONTINUE END-READ IF WS-FS-CONTA = '00' PERFORM 2000-PROCESSA END-IF END-PERFORM CLOSE ARQ-CONTA.
8. ESDS — acesso sequencial e RBA
No ESDS, registros são adicionados sempre no final (OPEN EXTEND) e lidos sequencialmente. Não há chave — a identificação é o RBA (Relative Byte Address), a posição física do registro no dataset. O ESDS não suporta DELETE nem REWRITE de tamanho diferente:
FILE-CONTROL. SELECT ARQ-LOG ASSIGN TO AS-LOG ORGANIZATION IS SEQUENTIAL ACCESS MODE IS SEQUENTIAL FILE STATUS IS WS-FS-LOG. 7000-GRAVA-LOG. OPEN EXTEND ARQ-LOG *adiciona ao final — não apaga o existente MOVE WS-REG-LOG TO REG-LOG WRITE REG-LOG IF WS-FS-LOG NOT = '00' DISPLAY 'ERRO GRAVA LOG FS=' WS-FS-LOG END-IF CLOSE ARQ-LOG. 8000-LE-LOG. OPEN INPUT ARQ-LOG PERFORM UNTIL WS-FS-LOG = '10' READ ARQ-LOG AT END CONTINUE END-READ IF WS-FS-LOG = '00' PERFORM 2000-PROCESSA-LOG END-IF END-PERFORM CLOSE ARQ-LOG.
9. Alternate Index — chave alternativa
Um AIX (Alternate Index) cria um segundo índice sobre um campo que não é a chave primária. Por exemplo: num KSDS de conta indexado por número de conta, um AIX pelo CPF do titular permite buscar diretamente por CPF sem varrer o arquivo inteiro.
//SYSIN DD *
/* 1 — Definir o AIX */
DEFINE AIX -
( NAME(HLQ.VSAM.CONTA.AIX.CPF) -
RELATE(HLQ.VSAM.CONTA) -
KEYS(11 10) - /* 11 bytes na posição 10 */
NONUNIQUEKEY - /* CPF pode ter contas */
RECORDSIZE(35 35) -
CYLINDERS(1 1) -
) -
DATA(NAME(HLQ.VSAM.CONTA.AIX.CPF.D)) -
INDEX(NAME(HLQ.VSAM.CONTA.AIX.CPF.I))
/* 2 — Construir (popular) o AIX */
BLDINDEX INFILE(BASECLUS) -
OUTFILE(AIXFILE)
/* 3 — Definir Path (caminho de acesso via AIX) */
DEFINE PATH -
( NAME(HLQ.VSAM.CONTA.PATH.CPF) -
PATHENTRY(HLQ.VSAM.CONTA.AIX.CPF) -
UPDATE(YES) -
)
/*
FILE-CONTROL. SELECT ARQ-CONTA ASSIGN TO AS-CONTA ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CNT-NR-CONTA ALTERNATE RECORD KEY IS CNT-CPF WITH DUPLICATES *AIX NONUNIQUEKEY FILE STATUS IS WS-FS-CONTA. PROCEDURE DIVISION. * Busca pelo CPF (chave alternativa) MOVE WS-CPF-PESQ TO CNT-CPF READ ARQ-CONTA KEY IS CNT-CPF *especifica qual chave usar INVALID KEY DISPLAY 'CPF NAO ENCONTRADO' END-READ * FILE STATUS 02 = leitura OK mas há mais registros com * o mesmo CPF (duplicatas). Usar READ NEXT para continuar. PERFORM UNTIL WS-FS-CONTA NOT = '02' AND WS-FS-CONTA NOT = '00' PERFORM 2000-PROCESSA READ ARQ-CONTA NEXT RECORD AT END CONTINUE END-READ END-PERFORM.
10. Exemplo completo
O programa MANCONTA (manutenção de contas) abre um KSDS em modo I-O, lê um arquivo de transações sequencial e aplica inclusões, atualizações e exclusões, registrando erros num ESDS de log:
*----------------------------------------------------------------
* MANCONTA — aplica transações em KSDS de contas
*----------------------------------------------------------------
IDENTIFICATION DIVISION.
PROGRAM-ID. MANCONTA.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT ARQ-CONTA
ASSIGN TO AS-CONTA
ORGANIZATION IS INDEXED
ACCESS MODE IS RANDOM
RECORD KEY IS CNT-NR-CONTA
FILE STATUS IS WS-FS-CONTA.
SELECT ARQ-TRANS
ASSIGN TO AS-TRANS
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FS-TRANS.
SELECT ARQ-ERROS
ASSIGN TO AS-ERROS
ORGANIZATION IS SEQUENTIAL
FILE STATUS IS WS-FS-ERROS.
DATA DIVISION.
FILE SECTION.
FD ARQ-CONTA.
01 REG-CONTA.
05 CNT-NR-CONTA PIC X(10).
05 CNT-NOME PIC X(60).
05 CNT-SALDO S9(13)V99 COMP-3.
05 CNT-STATUS PIC X(01).
05 FILLER PIC X(107).
FD ARQ-TRANS.
01 REG-TRANS.
05 TRN-TIPO PIC X(01). *I=inc A=atu E=exc
05 TRN-NR-CONTA PIC X(10).
05 TRN-NOME PIC X(60).
05 TRN-SALDO S9(13)V99 COMP-3.
05 FILLER PIC X(96).
FD ARQ-ERROS.
01 REG-ERRO PIC X(80).
WORKING-STORAGE SECTION.
01 WS-FS-CONTA PIC X(02).
01 WS-FS-TRANS PIC X(02).
01 WS-FS-ERROS PIC X(02).
01 WS-CTR-INC S9(07) COMP-3.
01 WS-CTR-ATU S9(07) COMP-3.
01 WS-CTR-EXC S9(07) COMP-3.
01 WS-CTR-ERR S9(07) COMP-3.
PROCEDURE DIVISION.
0000-INICIO.
INITIALIZE WS-CTR-INC WS-CTR-ATU WS-CTR-EXC WS-CTR-ERR
OPEN I-O ARQ-CONTA
OPEN INPUT ARQ-TRANS
OPEN EXTEND ARQ-ERROS
PERFORM 1000-LE-TRANS
PERFORM 1100-LOOP-TRANS
UNTIL WS-FS-TRANS = '10'
CLOSE ARQ-CONTA ARQ-TRANS ARQ-ERROS
DISPLAY 'INC=' WS-CTR-INC ' ATU=' WS-CTR-ATU
' EXC=' WS-CTR-EXC ' ERR=' WS-CTR-ERR
STOP RUN.
1000-LE-TRANS.
READ ARQ-TRANS AT END CONTINUE END-READ.
1100-LOOP-TRANS.
EVALUATE TRN-TIPO
WHEN 'I' PERFORM 2000-INCLUI
WHEN 'A' PERFORM 3000-ATUALIZA
WHEN 'E' PERFORM 4000-EXCLUI
WHEN OTHER
PERFORM 9000-GRAVA-ERRO
END-EVALUATE
PERFORM 1000-LE-TRANS.
2000-INCLUI.
MOVE TRN-NR-CONTA TO CNT-NR-CONTA
MOVE TRN-NOME TO CNT-NOME
MOVE TRN-SALDO TO CNT-SALDO
MOVE 'A' TO CNT-STATUS
WRITE REG-CONTA
INVALID KEY
PERFORM 9000-GRAVA-ERRO
NOT INVALID KEY
ADD 1 TO WS-CTR-INC
END-WRITE.
3000-ATUALIZA.
MOVE TRN-NR-CONTA TO CNT-NR-CONTA
READ ARQ-CONTA
INVALID KEY
PERFORM 9000-GRAVA-ERRO
NOT INVALID KEY
MOVE TRN-SALDO TO CNT-SALDO
REWRITE REG-CONTA
INVALID KEY
PERFORM 9000-GRAVA-ERRO
NOT INVALID KEY
ADD 1 TO WS-CTR-ATU
END-REWRITE
END-READ.
4000-EXCLUI.
MOVE TRN-NR-CONTA TO CNT-NR-CONTA
DELETE ARQ-CONTA
INVALID KEY
PERFORM 9000-GRAVA-ERRO
NOT INVALID KEY
ADD 1 TO WS-CTR-EXC
END-DELETE.
9000-GRAVA-ERRO.
STRING 'ERRO TIPO=' DELIMITED SIZE
TRN-TIPO DELIMITED SIZE
' CONTA=' DELIMITED SIZE
TRN-NR-CONTA DELIMITED SIZE
' FS=' DELIMITED SIZE
WS-FS-CONTA DELIMITED SIZE
INTO REG-ERRO
END-STRING
WRITE REG-ERRO
ADD 1 TO WS-CTR-ERR.
11. Erros comuns
1. OPEN I-O num arquivo OUTPUT — FILE STATUS 47/48
* ❌ REWRITE requer OPEN I-O — com OPEN INPUT gera FS=49 OPEN INPUT ARQ-CONTA READ ARQ-CONTA … REWRITE REG-CONTA *FS='49' * ✅ Para leitura + atualização, use OPEN I-O OPEN I-O ARQ-CONTA
2. REWRITE antes de READ — FILE STATUS 43
* ❌ Em ACCESS SEQUENTIAL, REWRITE opera sobre o * último registro lido. Sem READ, FS='43'. MOVE WS-DADOS TO REG-CONTA REWRITE REG-CONTA *FS='43' — nenhum registro corrente * ✅ Sempre faça READ antes de REWRITE READ ARQ-CONTA … MOVE WS-DADOS TO REG-CONTA REWRITE REG-CONTA
3. Não inicializar FILLER no WRITE — dados lixo no arquivo
* ❌ Mover campos individuais deixa FILLER com conteúdo anterior MOVE WS-NR-CONTA TO CNT-NR-CONTA MOVE WS-NOME TO CNT-NOME WRITE REG-CONTA *FILLER com lixo de memória anterior * ✅ Inicialize o registro inteiro antes de mover campos INITIALIZE REG-CONTA MOVE WS-NR-CONTA TO CNT-NR-CONTA MOVE WS-NOME TO CNT-NOME WRITE REG-CONTA
4. FILE STATUS 39 — conflito de atributos
FILE STATUS '39' no OPEN indica que os atributos declarados no programa conflitam com o dataset real no catálogo: Causa mais comum Verificação ─────────────────── ────────────────────────────────────────── RECORD KEY errado Posição ou tamanho diferente do IDCAMS KEYS RECORDSIZE errado FD RECORD CONTAINS ≠ RECORDSIZE do cluster Tipo errado INDEXED mas dataset é NONINDEXED (ESDS) Solução: executar LISTCAT no dataset e alinhar o FD com os atributos exatos do cluster.
5. Não fechar o arquivo antes do STOP RUN
* ❌ STOP RUN sem CLOSE pode deixar VSAM em estado inconsistente * Se o job abenda após OPEN sem CLOSE, o VSAM fica em * "open status" e o próximo OPEN falha com RC=116 * ✅ Sempre feche em todos os caminhos de saída do programa CLOSE ARQ-CONTA ARQ-TRANS ARQ-ERROS STOP RUN.