IBM Enterprise COBOL: fique atento ao SEARCH ALL
O compilador IBM Enterprise COBOL for Z/OS, a partir da versão 4, corrigiu a forma como os compiladores anteriores implementavam a funcionalidade do comando SEARCH ALL. Basicamente, até então, havia situações em que esse comando apresentava um resultado diferente do que era produzido com PERFORM VARYING e IF, ou com SEARCHs sequenciais.
Neste artigo vou comentar em que situações essas diferenças aconteciam e o que deve ser feito caso se tenha que migrar de um compilador anterior (OS/VS COBOL, VS COBOL II, IBM COBOL, COBOL for OS/390 etc.) para o IBM Enterprise COBOL versão 4 (ou posterior).
Se você quiser entender (ou relembrar) a diferença entre SEARCH sequencial, SEARCH ALL e pesquisa com PERFORM VARYING, dá uma olhada nesse outro artigo que publiquei aqui.
Argumentos e chaves com tamanhos diferentes
Quando o argumento usado no comando SEARCH ALL é maior do que a chave da tabela os compiladores completam a chave com espaços à direita (ou zeros à esquerda), mas ignora esses caracteres adicionais na hora de fazer a comparação.
Para ficar mais claro, observe o exemplo abaixo:
01 ARGUMENTO PIC X(004) VALUE "RJ04". ... 01 TABELA. 03 FILLER PIC X(002) VALUE "RJ". 03 FILLER PIC X(002) VALUE "SP". 03 FILLER PIC X(002) VALUE "TO". 01 FILLER REDEFINES TABELA. 03 CHAVE PIC X(002) OCCURS 3 INDEXED BY IX. ... SET IX TO 1 SEARCH ALL TABELA AT END DISPLAY "ARGUMENTO NAO ENCONTRADO" WHEN CHAVE(IX) = ARGUMENTO DISPLAY "ARGUMENTO ENCONTRADO" END-SEARCH
Repare que a variável ARGUMENTO, usada no SEARCH ALL, é uma alfanumérica de 4, enquanto a CHAVE da tabela possui tamanho de 2. Durante a execução do comando, compiladores anteriores “truncam” o argumento no momento da comparação, o que no exemplo acima faria com que “RJ04” fosse considerado igual a “RJ”. Esse resultado, obviamente, não seria o mesmo caso o programa fizesse a pesquisa com um PERFORM VARYING ou com o SEARCH simples.
O mesmo problema acontece com variáveis numéricas:
01 ARGUMENTO PIC 9(006) VALUE 123456. ... 01 TABELA. 03 FILLER PIC 9(004) VALUE 3456. 03 FILLER PIC 9(004) VALUE 5678. 03 FILLER PIC 9(004) VALUE 9999. 01 FILLER REDEFINES TABELA. 03 CHAVE PIC 9(004) OCCURS 3 INDEXED BY IX. ... SET IX TO 1 SEARCH ALL TABELA AT END DISPLAY "ARGUMENTO NAO ENCONTRADO" WHEN CHAVE(IX) = ARGUMENTO DISPLAY "ARGUMENTO ENCONTRADO" END-SEARCH
No exemplo acima, o SEARCH ALL truncaria o argumento em quatro bytes (para ficar com o mesmo tamanho da chave da tabela) e consideraria que 123456 é igual a 3456, ignorando os dois dígitos excedentes à direita.
Incompatibilidade de sinais
Um segundo problema pode acontecer quando o SEARCH ALL utiliza um argumento sinalizado para fazer a busca em uma tabela onde a chave não tem sinal:
01 ARGUMENTO PIC S9(004) VALUE -1234. ... 01 TABELA. 03 FILLER PIC 9(004) VALUE 1234. 03 FILLER PIC 9(004) VALUE 5678. 03 FILLER PIC 9(004) VALUE 9999. 01 FILLER REDEFINES TABELA. 03 CHAVE PIC 9(004) OCCURS 3 INDEXED BY IX. ... SET IX TO 1 SEARCH ALL TABELA AT END DISPLAY "ARGUMENTO NAO ENCONTRADO" WHEN CHAVE(IX) = ARGUMENTO DISPLAY "ARGUMENTO ENCONTRADO" END-SEARCH
O SEARCH ALL do exemplo acima trunca o sinal do argumento no momento da comparação para que ele seja compatível com a chave da tabela, e com isso ele considera que -1234 é igual a 1234.
Migrando programas para compiladores mais recentes
A partir da versão 4 do IBM Enterprise COBOL esses problemas não acontecem mais. A invés de truncar o argumento para que ele fiquei compatível com a chave, o compilador faz o contrário: expande a chave para que ela seja compatível com o argumento. Consequentemente, pesquisas realizadas com SEARCH ALL, SEARCH sequencial e PERFORM VARYING apresentam sempre os mesmos resultados.
No entanto, ao migrar um programa de um compilador anterior para o IBM Enterprise COBOL versão 4 (ou posterior) é preciso identificar, e provavelmente modificar, os programas que caem nessa situação para que eles continuem apresentando resultados iguais aos que apresentavam antes.
Para ajudar na identificação desses programas, os compiladores mais recentes mostrarão duas novas mensagens de warning:
- IGYPG3189-W: Essa mensagem será exibida para todos os comandos SEARCH ALL do programa que usam argumentos com tamanhos maiores do que as chaves de pesquisa da tabela;
- IGYPG3188-W: Essa mensagem será exibida para todos os comandos SEARCH ALL que pesquisam argumentos numéricos sinalizados em tabelas cujas chaves não têm sinal.
Além disso, os executáveis gerados pelo compilador também exibirão na SYSOUT duas novas mensagens de warning em tempo de execução alertando para possíveis problemas nos resultados apresentados pelo SEARCH ALL:
- IGZ0194W, para avisar que ocorreu um SEARCH ALL com argumentos e chaves que têm tamanhos diferentes
- IGZ0193W, para avisar que ocorreu um SEARCH ALL com argumentos e chaves numéricos que diferem quanto à declaração de sinal.
Para diminuir ainda mais o risco de resultados inesperados, é possível alterar o flag dessas mensagens para E ou para S, de tal maneira que um return code diferente de 0 e 4 seja produzido em tempo de compilação ou execução, forçando o programador a alterar os programas que caem numa das duas situações descritas anteriormente.
Ajustes possíveis
Uma vez identificados os programas impactados, é possível eliminar o problemas com três tipos de intervenção:
Opcão 1: Incluir comandos antes do SEARCH ALL que garantam que os bytes excedentes do argumento estão preenchidos com espaços (se esse argumento for alfanumérico) ou com zeros à esquerda (se o argumento for numérico). Aplicando esse tipo de ajuste num dos exemplos que vimos antes:
01 ARGUMENTO PIC X(004) VALUE "RJ04".
...
01 TABELA.
03 FILLER PIC X(002) VALUE "RJ".
03 FILLER PIC X(002) VALUE "SP".
03 FILLER PIC X(002) VALUE "TO".
01 FILLER REDEFINES TABELA.
03 CHAVE PIC X(002) OCCURS 3 INDEXED BY IX.
...
MOVE SPACES TO ARGUMENTO(3:4)
SET IX TO 1
SEARCH ALL TABELA
AT END
DISPLAY "ARGUMENTO NAO ENCONTRADO"
WHEN CHAVE(IX) = ARGUMENTO
DISPLAY "ARGUMENTO ENCONTRADO"
END-SEARCH
Opção 2: Mover o conteúdo do argumento original para uma variável temporária que será usada apenas pelo comando SEARCH ALL. Essa alternativa é recomendada caso o argumento original seja usado pelo programa para outras finalidades:
01 ARGUMENTO PIC X(004) VALUE "RJ04". 01 ARGUMENTO-TMP PIC X(002) VALUE SPACES. ... 01 TABELA. 03 FILLER PIC X(002) VALUE "RJ". 03 FILLER PIC X(002) VALUE "SP". 03 FILLER PIC X(002) VALUE "TO". 01 FILLER REDEFINES TABELA. 03 CHAVE PIC X(002) OCCURS 3 INDEXED BY IX. ... MOVE ARGUMENTO TO ARGUMENTO-TMP SET IX TO 1 SEARCH ALL TABELA AT END DISPLAY "ARGUMENTO NAO ENCONTRADO" WHEN CHAVE(IX) = ARGUMENTO-TMP DISPLAY "ARGUMENTO ENCONTRADO" END-SEARCH
Opção 3: Usar o recurso de modificação de referência diretamente na cláusula WHEN do comando SEARCH ALL. Dependendo do programa (e da quantidade de pesquisas que o programa executa) essa é provavelmente a alternativa que exige menos intervenções:
01 ARGUMENTO PIC X(004) VALUE "RJ04".
...
01 TABELA.
03 FILLER PIC X(002) VALUE "RJ".
03 FILLER PIC X(002) VALUE "SP".
03 FILLER PIC X(002) VALUE "TO".
01 FILLER REDEFINES TABELA.
03 CHAVE PIC X(002) OCCURS 3 INDEXED BY IX.
...
SET IX TO 1
SEARCH ALL TABELA
AT END
DISPLAY "ARGUMENTO NAO ENCONTRADO"
WHEN CHAVE(IX) = ARGUMENTO(1:2)
DISPLAY "ARGUMENTO ENCONTRADO"
END-SEARCH
A vantagem das opções 2 e 3 é que no futuro não serão geradas as mensagens de warning de compilação e execução.
O caso do SEARCH ALL com argumentos numéricos e sinalizados também deve ser tratado de maneira semelhantes: pode-se alterar a PIC do argumento para remover o sinal, ou criar uma variável de trabalho não sinalizada para truncar o sinal do argumento antes da execução da pesquisa.
Fonte
IBM Enterprise COBOL for Z/OS version 6.3, Migration Guide