Árvore de páginas

1       Linguagem 4GL

O TotvsTec 4GL surgiu com base nas linguagens nativas 4GL do banco de dados Informix e também com base na linguagem 4JS da IBM.

A linguagem 4GL se caracteriza por acessar as bases de dados através de uma linguagem comum chamada SQL (extensão do standard SQL-Structured Query Language) que não será vista neste manual. Esta filosofia permite uma maior facilidade na formação de programadores bem como uma separação efetiva entre o nível físico e lógico da exploração da base de dados tal como é definido na teoria das bases de dados relacionais.

O desenvolvimento e compilação das aplicações desenvolvidas em 4GL são realizadas via ferramenta de desenvolvimento Totvs Developer Studio disponível para download na central de download da página de suporte da TOTVS.

Quando compilados, todos os arquivos de código fonte tornam-se unidades de inteligência básicas, chamadas de RPO´s (Repository Protheus Objects). Esses RPO´s são mantidos em um repositório e carregados dinamicamente pelo servidor de aplicação (Application Server) para a execução. No entanto, não existe link ou união física do código compilado a um determinado ambiente ou aplicação, o que permite executar funções criadas em 4GL em qualquer ponto do ambiente ERP.

2       Estrutura de um programa

Um programa é constituído por funções e declarações. A primeira função a ser executada é a função "MAIN", e, tem de existir em todos os programas.

 

Fonte olamundo.4gl

MAIN
   ...instruções...
END MAIN

Exemplo de programa

As várias funções podem estar distribuídas por vários fontes a que se resolveu chamar de módulos.

2.1      Fluxo de um programa

Tal como nas linguagens de terceira geração, um programa é executado de acordo com instruções de controle de fluxo, em todas as funções exceto nos reports (os reports possuem uma estrutura de definição).

Para controlar o fluxo do programa foram implementadas diversas instruções de controle de fluxo das linguagens tradicionais (while, if, case, etc.), e, algumas com instruções específicas desta linguagem (Menus, prompts, etc.), algumas das quais não são procedurais (ex: MENU, INPUT, etc.).

O 4GL é ao mesmo tempo de definição e procedural. Como exemplo de definição temos os reports e as instruções de entrada/saída formatada, e, como exemplo de procedural a estrutura geral das funções.

2.2      Tipos e conversões de tipos

Como linguagem de gerenciamento de base de dados, o 4GL reconhece as estruturas representáveis na base de dados, e, algumas de maior complexidade. Os tipos de dados complexos, são declarados por composição de tipos elementares. Em 4GL os tipos de dados elementares existentes utilizados no produto LOGIX são:

 SMALLINT
Números inteiros pequenos, uma variável deste tipo pode receber números inteiros entre –32767 e 37767. 

INTEGER
Números inteiros entre -2,147,483,647 e +2,147,483,647. 

DECIMAL[(m,[,n])]
Números decimais com “m” algarismos, sendo que “n” algarismos são para formar o número de casas decimais. Por exemplo, para DECIMAL(14,2), possui 14 dígitos significativos, sendo 2 deles para formar o número de dígitos a direita da vírgula, ou seja, são 12 dígitos inteiros e 2 decimais.

Uma variável do tipo decimal não pode exceder 32 dígitos significativos.

CHAR(n)
Alfanumérico de comprimento “n”. Uma variável do tipo CHAR pode ter o tamanho que varia de 1 a 32767.

DATE
Tipo equivalente a uma data, é configurável através da variável de ambiente DBDATE. Internamente, este tipo é armazenado como inteiro, no formato juliano (nº de dias desde 31 de dezembro de 1899). O formato padrão do DBDATE é “mdy4/” ou seja, “mm/dd/yyyy”, onde mm é o mês (1 a 12), dd (1 a 31) é o dia e yyyy o ano (0001 a 9999). O padrão utilizado na Logocenter é “dmy4/”, ou seja, “dd/mm/yyyy”.

Existem ainda dois tipos, compostos dos acima descritos. 

ARRAY tipo
Representa uma variável composta por vários elementos do mesmo “tipo”, denominado como vetor. Uma variável deste tipo pode ser definida com 1, 2 ou 3 dimensões de tamanhos distintos entre si, mas sendo todas do mesmo tipo. O tamanho de um array pode variar de 1 até um tamanho limitado ao volume de memória disponível no servidor de aplicação em uso.

Uma variável do tipo ARRAY é uma variável estática, ou seja, a memória consumida será sempre conforme o tipo e o número de elementos, podendo ser bi-dimensional se houver necessidade.

RECORD
Representa uma variável composta por diferentes variáveis de qualquer tipo, incluindo os tipos simples (DATE, CHAR, SMALLINT, INTEGER, etc) e os tipos compostos (ARRAY e RECORD).  Assemelha-se ao conceito de variável do tipo STRUCT da linguagem C. 

DATETIME
O tipo de dados DATETIME define uma instância de tempo, expressado como uma data do calendário e uma hora do dia. As unidades do tempo que o valor de DATETIME armazena é você quem determina; a precisão pode variar de um ano até uma fração de um segundo. Internamente, os valores são armazenados no formato DECIMAL que representam uma seqüência contígua de valores de unidades de tempo.

A especificação de escala de precisão é chamada de qualificador do DATETIME. Este qualificador é utilizado no formato do "primeiro ao último" para declarar variáveis e campos de tela. Você deve substituir um ou dois destes qualificadores para o primeiro e o último termo. 

Qualificador

Unidade de tempo / faixa de valores

YEAR

Um ano, variando de 0001 até 9999.

MONTH

Um mês, variando de 1 a 12.

DAY

Um dia, variando de 1 a 31.

HOUR

Uma hora, variando de 0 a 23.

MINUTE

Um minuto, variando de 0 a 59.

SECOND

Um segundo, variando de 0 a 59.

FRACTION(escala)

Um décimo de fração de um segundo, com uma escala de até 5 dígitos, sendo que a escala padrão é de 3 dígitos.

 
O qualificador especificado por último não pode representar uma unidade de tempo maior do que o primeiro. Assim, o YEAR TO SECOND ou HOUR TO HOUR são válidos, mas o DAY TO MONTH resulta em um erro do compilador, porque o valor para o último qualificador (aqui MONTH) especifica uma unidade de tempo maior do que o DAT, o primeiro qualificador.

O formato de uma variável do tipo DATETIME é “yyyy-mm-dd hh:mm:ss.fffff”, onde yyyy é o ano, mm é o mês, dd é o dia, hh é a hora, mm é os minutos, ss é os segundos e fffff é a fração de segundos.

2.3      Variáveis

Uma linguagem, deve possuir sempre alguma forma de armazenar em memória uma informação necessária para execução dos programas. Tal como nas linguagens de terceira geração, no 4GL existem variáveis.

Uma variável pode ser de qualquer dos tipos definidos anteriormente.

A declaração de variáveis é sempre efetuada depois da palavra chave "DEFINE". Uma variável declara-se da seguinte forma:
   

DEFINE nome_da_variavel TIPO
 OU
DEFINE nome_da_variavel LIKE tabela.coluna 

 

No segundo caso cria-se uma nova variável cuja estrutura condiz com a estrutura de uma coluna de uma tabela do banco de dados. Sempre que uma variável for declarada usando a instrução LIKE, deve existir no início do módulo (fonte .4gl) uma instrução DATABASE que indica o banco de dados a ser usado para obter informações de definição das colunas utilizadas na declaração de variáveis.

Um caracter ou uma faixa de caracteres (substring) pertencentes a uma variável do tipo CHAR pode ser acessado utilizando o operador “[]” (colchetes), assim:

 

 l_texto[1]
 OU
l_texto[2,5] 

 

No primeiro caso está  se obtendo o primeiro caracter da variável l_texto. Já no segundo caso, está se obtendo 4 caracteres da variável l_texto, referente a posição inicial 2 até a posição 5.

Se a variável for um DATETIME deve-se declarar da seguinte forma:

 

nome_do_datetime DATETIME qualificador_inicial TO qualidificador_final

 

Para uma variável DATETIME que tem o formato que vai do ano até 5 dígitos de fração de segundo, deve-se definir assim:

 

l_datahora DATETIME YEAR TO FRACTION(5)

 

Se a variável for um RECORD deve-se apenas declarar da seguinte forma:

 

nome_do_record RECORD
        lista_de_variaveis
END RECORD

 OU

nome_do_record RECORD LIKE tabela.*

 

No segundo caso é criada uma estrutura com elementos idênticos a todas as colunas de uma tabela do banco de dados corrente.

Um elemento de uma variável tipo RECORD pode ser acessado através do operador "." (ponto), assim:

 

 lr_dados.codigo

 OU

 lr_dados.*

 

Este representa o elemento “codigo” do RECORD “lr_dados”. A notação ".*" pode ser usada em alguns contextos para indicar todos os elementos de um RECORD.

Se a variável elementos das variáveis tipo ARRAY podem ser acessadas através do operador [] (colchetes) e dá indicação do número do elemento a que se quer ter acesso, assim:

 

 la_dados[10]

 Neste caso representa o 10º elemento do ARRAY la_dados.

Se a variável for um array deve ser declarada da seguinte forma:

 

 nome_da_variavel ARRAY[m,n,...] OF tipo

 

Os elementos das variáveis tipo ARRAY podem ser acessadas através do operador [] (colchetes) e dá indicação do número do elemento a que se quer ter acesso, assim:

 

 la_dados[10]

Neste caso representa o 10º elemento do ARRAY la_dados.

Para acessar um elemento de uma variável do tipo ARRAY do tipo RECORD utiliza-se o “.” (ponto) após a determinação da posição desejada do ARRAY, assim:

 

 la_dados[5].codigo

Neste caso, representa o valor da variável “codigo” pertencente ao 5º elemento do ARRAY la_dados.

Para acessar um conjunto de caracteres de uma variável do tipo CHAR, sendo esta um componente de um ARRAY do tipo RECORD, utiliza-se o ponto após a determinação da posição desejada do ARRAY e o operador “[]” (colchetes) para determinar o conjunto de caracteres desejados da variável tipo CHAR, assim:

 

 la_dados[3].descricao[5,10]

Neste caso está se obtendo o conjunto de caracteres da posição 5 até a 10 da variável descrição pertencente ao 3º elemento da variável la_dados (ARRAY do tipo RECORD).

As variáveis podem ser locais a uma função, globais a um módulo (modulares) ou programa (globais).

O escopo de referência de uma variável é determinado pelo lugar em que a cláusula DEFINE aparece no fonte .4gl. Os identificadores de variáveis podem ser locais, modulares, ou (em alguns casos) globais em seu escopo:

2.3.1       Variáveis locais

 

DEFINE nome_da_variável tipo [, ...]

 

As variáveis locais precisam ser declaradas no início de cada função e somente são reconhecidas dentro do escopo da função em que foram declaradas.

 

 varlocal.4gl

MAIN
   DEFINE l_inteiro INTEGER   
   LET l_inteiro = 1
   CALL mostra_local()
   CALL mostra_local_agora(l_inteiro)
END MAIN

FUNCTION mostra_local()
  DEFINE l_inteiro INTEGER
  DISPLAY “INTEIRO: “, l_inteiro
  SLEEP 2
END FUNCTION

FUNCTION mostra_local_agora(l_inteiro)
DEFINE l_inteiro INTEGER
DISPLAY “INTEIRO AGORA: “,l_inteiro
  SLEEP 2
END FUNCTION

RESULTADO:
INTEIRO:           0
INTEIRO AGORA: 1

 Exemplo com variáveis locais

Se declarar duas variáveis com o mesmo nome em duas funções distintas, cada variável somente será reconhecida na função de declaração. Concluindo, as variáveis com o mesmo nome localizadas em funções diferentes são variáveis diferentes.

2.3.2       Variáveis globais

 

GLOBALS "fonte.4gl"

 OU 

GLOBALS
   {instrução DEFINE}
END GLOBALS}

Definição genérica de uma declaração global

Como se pode ver acima, a instrução GLOBALS tem duas formas possíveis. Na primeira indica-se o nome de um fonte que contém um conjunto de instruções DEFINE e uma instrução DATABASE. A vantagem deste sistema é o conhecimento das variáveis globais em todos os módulos sem necessidade de declará-las em cada um deles.

Da outra forma, a instrução GLOBALS é constituída por uma instrução DEFINE.

 

 varglob1.4gl

GLOBALS
   DEFINE g_inteiro INTEGER
END GLOBALS

MAIN
   LET g_inteiro = 5
  DISPLAY "inteiro VARGLOB1: ",g_inteiro
   SLEEP 2
   CALL mostra_varglob2()
   DISPLAY "VARGLOB1 após VARGLOB2: ",g_inteiro
   SLEEP 2
END MAIN

 

varglob2.4gl

GLOBALS
   DEFINE g_inteiro INTEGER
END GLOBALS

FUNCTION mostra_varglob2()
   DISPLAY "inteiro VARGLOB2: ",g_inteiro
   SLEEP 2
   LET g_inteiro = 10
END FUNCTION

RESULTADO:
 inteiro VARGLOB1:           5
 inteiro VARGLOB2:           5
 VARGLOB1 após VARGLOB2: 10

Exemplo com variáveis globais

No programa dado como exemplo não havia grande necessidade de declarar as variáveis como globais. Á medida que for programando pode-se sentir a necessidade de utilizar este tipo de variável.

2.3.3       Variáveis modulares

 

DEFINE nome_da_variável tipo [, ...]

 

As variáveis modulares tem que ser declaradas no inicio do fonte, fora do escopo de definição de variáveis globais (GLOBALS..END GLOBALS) e fora do escopo de qualquer função (MAIN, FUNCTION, REPORT). Variáveis modulares somente são reconhecidas dentro do escopo do fonte (módulo) em que foram declaradas.

 

varmod1.4gl

DEFINE m_inteiro INTEGER

MAIN
   LET m_inteiro = 1
   DISPLAY “inteiro VARMOD1: “,m_inteiro
   SLEEP 2
   CALL mostra_varmod2()
   DISPLAY "VARMOD1 após VARMOD2: ",g_inteiro
   SLEEP 2
END MAIN

varmod2.4gl

DEFINE m_inteiro INTEGER
FUNCTION mostra_varmod2()
DISPLAY “inteiro VARMOD2: “,m_inteiro
SLEEP 2
LET m_inteiro = 3

 END FUNCTION

RESULTADO
inteiro VARMOD1:           1
inteiro VARMOD2:           0 
VARMOD1 após VARMOD2:           1

            Exemplo com variáveis modulares

  

Se declarar duas variáveis modulares com o mesmo nome em dois fontes (módulos) distintos, cada variável só vai ser reconhecida no fonte onde foi declarado. Efetivamente as variáveis com o mesmo nome localizadas em fontes diferentes são variáveis diferentes.

2.3.4       Atribuições, comparações e inicializações

Uma das operações básicas a realizar sobre variáveis é a atribuição.

No 4GL para atribuir a uma variável um valor constante, expressão ou conteúdo de outra variável, utiliza-se o seguinte formato:

 

LET variável = {expressão | variável | constante}

Definição genérica da atribuição

No 4GL pode-se inicializar uma variável de qualquer tipo básico. As variáveis compostas só podem ser inicializadas através dos seus elementos constituintes.

 

afecta.4gl

DATABASE livros
MAIN
   DEFINE x INTEGER,
          y CHAR(20),
          z DECIMAL(5,2),
          lr_livros RECORD LIKE livros.*,
          lr_livros2 RECORD LIKE livros.*
   LET x = 69
   LET y = "Uma string"
   LET z = 120.596
   DISPLAY "X tem ", x,", Y tem ", y,"e Z = ",z

   SELECT *
     INTO lr_livros.*
     FROM livros
   WHERE numero = x
   LET lr_livros2.* = lr_livros.*
   DISPLAY lr_livros2.numero, lr_livros.nome
END MAIN

RESULTADO:
X tem 69, Y tem Uma string e Z = 120.60
69 nome do livro 69

Inicializações de variáveis

Conforme se viu, inicializou-se variáveis de diversos tipos elementares e compostos. Nos tipos compostos, o número e tamanho das variáveis elementares que dele fazem parte tem de ser iguais.

Nesta linguagem, não há diferença entre o operador "=" usado na inicialização e na comparação. Este fato deve-se a identificação destas operações serem efetuadas por intermédio do contexto em que as instruções estão incluídas.

Quando se inicializa uma variável com uma expressão, esta é automaticamente convertida para o tipo da variável, se a conversão resultar em erro, o programa notifica com erro de execução. Por exemplo, na instrução LET i ="10" se a variável i for um inteiro, a expressão "10" é automaticamente convertida em inteiro.

 

 compara.4gl

MAIN   DEFINE y CHAR(20)
   LET y = "Uma string"
   IF y > "UMA STRING" THEN
      DISPLAY "Uma string maior que UMA STRING"
   END IF

   IF y < "uma string" THEN
      DISPLAY "Uma string menor que UMA STRING"
   END IF

   IF y = "Uma string" THEN
      DISPLAY "Uma string igual a UMA STRING"
   END IF
END MAIN

RESULTADO:
 Uma string maior que UMA STRING
 Uma string menor que UMA STRING
 Uma string igual a UMA STRING 

Vários exemplos de comparações

Em qualquer programa, muitas vezes é necessário proceder as inicializações de variáveis.

Uma das formas de o fazer é inicializando a variável com o valor pretendido, na área do programa em que se pretende fazer. Outra forma é utilizando a instrução INITIALIZE.

 

INITIALIZE lista_de_variáveis TO NULL

Definição genérica da instrução INITIALIZE

A instrução INITIALIZE inicializa as variáveis com valor NULL.

2.3.5       Operadores

Os operadores de comparação no Informix 4GL são:

Operador

Descrição

Exemplo

maior que

var1 > var2

menor que

var1 < var2

>=

maior ou igual que

var1 >= var2

<=

menor ou igual que

var1 <= var2

= ou ==

igual a

var1 = var2

!= ou <>

diferente de

var1 <> var2

NOT

negação

NOT <expressão>

AND

e

<expressão1> AND <expressão2>

OR

ou

<expressão1> OR <expressão2>

MATCHES

comparação usando metacarateres (*, ?, [])

var1 MATCHES  “*z?”

LIKE

comparação usando metacaracteres (-, %)

var1 LIKE “%z_%”

BETWEEN

intervalo de valores

BETWEEN 1 AND 100

IN

conjunto de valores

NOT IN (“A”,”B”,”C”)

IS NULL

teste de valor nulo

var1 IS NULL  / var2 IS NOT NULL

 

Os operadores para cálculos matemáticos, no 4GL são:

Operador

Descrição

Exemplo

+

adição

var1 + var2

-

subtração

var1 - var2

**

exponenciação

var1 ** var2

MOD

módulo (divisão inteira c/ retorno do resto da divisão)

var1 MOD var2

*

multiplicação

var1 * var2

/

divisão

var1 / var2

 

Os demais operadores existentes são:

Operador

Descrição

Exemplo

.

Para referenciar um membro de uma variável do tipo RECORD.

lr_dados.código

[]

Para referenciar um elemento de uma variável do tipo ARRAY ou um conjunto de caracteres de uma variável do tipo CHAR.

la_dados[10]

()

Para chamada de funções.

CALL entrada_dados()

UNITS

Para referenciar a unidade de qualificador.

LET data = data + 5 UNITS DAY

|| ou ,

Concatenação

“abc” || “def”  ou  “abc”,”def”

ASCII

Retorna o caracter ASCII correspondente.

LET letra = ASCII(65)

ORD

Retorna o código ASCII correspondente.

LET cod_ascii = ORD(“A”)

CLIPPED

Elimina espaços em branco do final de um string.

LET l_texto = l_texto CLIPPED, “x”

COLUMN

Para posicionar a coluna de impressão de dados.

PRINT COLUMN 10, “Texto”.

SPACES

Para inserir espaços em branco.

DISPLAY 5 SPACES

USING

Para formatar um conteúdo atribuído a um string.

LET texto = TODAY USING “dd/mm/yy”

WORDWRAP

Impressão de um texto com quebra automática de linhas.

PRINT texto WORDWRAP

2.3.6       Concatenação de variáveis tipo CHAR

No 4GL é muito simples concatenar strings, basta separá-las por uma virgula (o operador de concatenação de strings é a virgula).

 

concat.4gl

MAIN
   DEFINE x CHAR(20),
          y CHAR(50)
   LET x = "String 1 "
   LET y = "Concatenou ",x CLIPPED," com outras duas strings"
   DISPLAY y
END MAIN 

RESULTADO
Concatenou String 1 com outras duas strings 

Exemplo de concatenação de strings

2.4      Funções

Uma função é um conjunto de instruções que são executadas conjuntamente e que pode receber um conjunto de parâmetros e devolver valores.

O 4GL apesar de ser uma linguagem de 4ª geração possui esta capacidade das linguagens estruturadas.

 

FUNCTION nome_da_funcao([arg1,arg2,...,argn])
  instrução
  ...
  [RETURN lista_expressões]
  ...
END FUNCTION

Definição genérica de uma função

Uma função é identificada por um nome, começa com a palavra chave FUNCTION e termina com as palavras chave END FUNCTION, exceto a função MAIN que não tem nome e é delimitada por MAIN...END MAIN.

 

olafuncao.4gl

MAIN
   CALL funcao_ola()
END MAIN

FUNCTION funcao_ola()
  DISPLAY "Olá mundo!"
END FUNCTION 

 RESULTADO:
 Olá mundo! 

Uma função simples

Numa função, além de poder ter variáveis locais, pode ainda ter variáveis de entrada (parâmetros), e, ter associação valores a serem retornados á função que a invocou (valores de retorno). Tanto os parâmetros como os valores de retorno podem ser de qualquer tipo permitido pelo 4gl. A chamada da função faz-se com a instrução CALL e para definir em que variáveis são atribuídos os valores devolvidos usa-se a cláusula RETURNING com uma lista de variáveis a retornar.

 

CALL função([argumentos]) [RETURNING lista_de_variáveis]

Definição genérica da instrução CALL

Se uma função devolver apenas um valor pode-se chamar com a instrução LET.

 

LET variavel = função([argumentos])

Chamar uma função com instrução LET

Os parâmetros de uma função são sempre declarados. Esta declaração faz com que a linguagem verifique todas as chamadas ás funções e no caso destas não serem executadas com o mesmo número de parâmetros que a declaração da função resulta em um erro de compilação do programa.

 

max.4gl

MAIN
   DEFINE maxval INTEGER
   LET maxval = maximo(4,8,5)
   DISPLAY "O maior numero e: ",maxval
END MAIN

FUNCTION maximo(n1,n2,n3)
  DEFINE n1 INTEGER,
         n2 INTEGER,
         n3 INTEGER

IF n1 > n2 THEN
      LET n2 = n1
  END IF
  IF n2 > n3 THEN
     RETURN n2
END IF

RETURN n3

END FUNCTION

RESULTADO:
O maior numero e: 8 

Função com vários tipos de parâmetros de entrada

Para retornar determinado valor a partir de uma função usa-se a palavra chave RETURN.

Ao contrário de algumas linguagens de terceira geração (por exemplo o C), uma função de 4GL não necessita ser declarada como um determinado tipo, ou seja, se pretender que a função devolva o número inteiro 10, só terá que retornar um valor de tal tipo:

 

retfunc1.4gl

MAIN
   DEFINE x INTEGER
   LET x = FUNC()
   DISPLAY "X tem",x
END MAIN

FUNCTION FUNC()
  RETURN 10
END FUNCTION

RESULTADO:
 X tem 10 

Função que retorna um número inteiro

Se pretender devolver uma string poderá também o fazer:

 

retfunc2.4gl

MAIN
   DEFINE x CHAR(100)
   LET x = FUNC()
   DISPLAY x
END MAIN

FUNCTION FUNC()
  RETURN "Esta string vai ser devolvida"
END FUNCTION

RESULTADO:
Esta string vai ser devolvida 

Função que retorna uma string

É preciso ter cuidado para que a variável em que se atribui o retorno da função seja do mesmo tipo do valor (ou variável) retornado dentro da função.

 

retfunc3.4gl

MAIN
   DEFINE x INTEGER
          y CHAR(100)

   CALL func() RETURNING x,y

   DISPLAY x
   DISPLAY y

END MAIN

FUNCTION func()
  RETURN 100,"Esta string vai ser devolvida"
END FUNCTION

RESTULTADO:
 100
 Esta string vai ser devolvida

Função com retorno de vários valores

Uma função (FUNCTION ou REPORT) não pode ter como parâmetro ou retorno uma variável do tipo ARRAY. O mesmo também se restringe a variáveis do tipo RECORD que possui componente definido como tipo ARRAY.

2.5      Conversões de tipos

Como já foi visto anteriormente, quando se falou de inicializações, as expressões no 4GL são sempre convertidas para o tipo necessário ao contexto onde são incluídas, não existem por isso operadores de conversão de tipo como noutras linguagens, a única exceção é o operador DATE(...) que converte uma expressão para um tipo DATE.

Esta situação pode tornar-se problemática na conversão de tipos, podendo no entanto contornar o problema declarando uma variável do tipo para o qual se quer converter a expressão e inicializando a variável com a expressão.

3       Comandos de controle sequencial

A construção de um programa implica a tomada de decisões, a repetição de instruções parametrizadas, etc. No 4GL encontram-se alguns dos mecanismos de controle e repetição existentes nas linguagens de terceira geração.

3.1      IF..THEN..ELSE..END IF

Esta instrução existe na maioria das linguagens. A decisão é realizada a partir de uma expressão booleana.

Em português esta instrução poderia ser explicada por:

  

Se esta expressão é verdadeira
    Então faz isto
Senão faz aquilo 

 

Em que "faz isto" e "faz aquilo" seria um conjunto de instruções a executar em cada um dos casos.

 

IF expressão_booleana THEN
    instrução
[ELSE
    instrução]
END IF 

 Definição geral da instrução IF...THEN...ELSE

Uma expressão booleana pode, por exemplo, ser um conjunto de comparações entre expressões, sendo possível realizar operações booleanas entre expressões, nomeadamente:

 

AND     A expressão é verdadeira se todas as sub-expressões também forem verdadeiras.

 

IF a = "casado" AND b = "solteiro" THEN
    CALL casa_com(a,b)
END IF 

Utilização de AND numa expressão

OR       A expressão é verdadeira se pelo menos uma das sub-expressões for verdadeira.

 

IF cao = "morde" OR cao = "grande" THEN
    CALL correr_mais_depressa()
END IF 

Utilização de OR numa expressão

NOT     A expressão é verdadeira se a sub-expressão não o for.

 

IF NOT ha_cerveja THEN
    CALL bebe_vinho()
END IF 

Utilização de NOT numa expressão

3.2      WHILE

Esta instrução de repetição pode ser explicada por:

    

Enquanto esta_expressão_for_verdadeira faz_isto 

 

WHILE é um conjunto de instruções a serem executadas se a expressão booleana for verdadeira.

 

WHILE expressao_booleana
    {instrução | EXIT WHILE | CONTINUE WHILE}
END WHILE 

Definição genérica da instrução WHILE

Uma expressão booleana usada no WHILE é idêntica a usada no IF e a qualquer outra que se utilize num programa.

 

WHILE esta <> "bebado"
    CALL bebe_mais_cerveja()
 END WHILE 

Ciclo WHILE

Existem duas instruções que podem ser usadas dentro de um WHILE:

-          EXIT WHILE
-          CONTINUE WHILE 

EXIT WHILE, serve para abandonar o ciclo independentemente da expressão booleana do WHILE.

CONTINUE WHILE, quando invocada, provoca um reposicionamento no início do ciclo (com o correspondente teste de continuidade do ciclo) sem cumprir as instruções que se encontravam abaixo.

3.3      FOR

As vezes é necessário executar um ciclo por um determinado número de vezes, incrementando uma variável com um valor.

 

FOR variavel_inteira = expressão1 TO expressao2
    [STEP expressão3 ]
    {instrução | CONTINUE FOR | EXIT FOR}
END FOR 

 Definição genérica do ciclo FOR

A instrução acima definida inicializa a variavel_inteira com o resultado da expressão1 e cumpre as instruções até que a expressão2 seja falsa, incrementando a variável_inteira com o resultado de expressão3. Se o programador, por alguma razão desejar sair do ciclo, pode utilizar a instrução EXIT FOR. Se, por outro lado já não pretender executar as instruções abaixo e continuar a partir do próximo ciclo, pode utilizar a instrução CONTINUE FOR. Estas duas ultimas instruções são análogas às instruções existentes no ciclo WHILE, incidindo no entanto somente sobre o comando FOR.

3.4      CASE

As vezes é necessário tomar uma decisão múltipla, isso pode ser feito com uso de várias instruções IF agrupadas. A instrução CASE surge como uma forma mais simples de resolver esta questão.

 

CASE [(expressão)]
   WHEN {expressão | expressão booleana}
       instrução
       ...
[EXIT CASE]
       ....
[OTHERWISE]
     instrução
      ...
[EXIT CASE]
      ...
END CASE 

Definição genérica da instrução CASE

A instrução CASE pode ser utilizada de duas formas diversas:

-          Por expressões;
-          Por expressões booleanas.

3.4.1       SELEÇÃO REALIZADA POR EXPRESSÕES

 

CASE (expr)
WHEN valor
    instrução
    ...
OTHERWISE
    instrução
     ...
END CASE 

 Forma geral de CASE com seleção por expressões

A expressão é comparada com os vários valores, sendo executadas as instruções associadas ao primeiro valor igual ao resultado da expressão. Se todos os valores forem diferentes do resultado da expressão, são executadas as instruções associadas á cláusula OTHERWISE, se esta existir.

 

case1.4gl

DATABASE livros 

MAIN
   DEFINE x    LIKE livros.numero,
          ans  CHAR(1),
          nome LIKE livros.nome

   LET ans = "s" 

   WHILE 1
        PROMPT "Qual o numero do livro que pretende   procurar: " FOR x 

      SELECT livros.nome
        INTO nome
        FROM livros
        WHERE numero = x   

   DISPLAY nome
      PROMPT "Quer escolher mais livros (S/N) " FOR CHAR ans 

      CASE (ans)
      WHEN "s"
         EXIT CASE
      WHEN "n"
         EXIT WHILE
      OTHERWISE
         DISPLAY "OPCAO INVALIDA"
         EXIT WHILE
      END CASE
   END WHILE
END MAIN 

 $ fglpc case1.4gl
 $ fglgo case1.4go
 Qual o numero do livro que pretende procurar: 34
 Quer escolher mais livros (S/N) g
 OPCAO INVALIDA 

 Utilização do CASE utilizando expressões para fazer a seleção

Na execução deste programa, quando se perguntou ao usuário se desejava procurar mais livros, ele respondeu a letra <g> quando as opções válidas eram <s> ou <n> e neste caso o programa entrou na cláusula OTHERWISE do CASE.

Neste caso a seleção do CASE pretendido é realizado quando o resultado da expressão existente junto da palavra chave CASE é idêntico ao resultado de uma das expressões junto a uma das palavras chave WHEN. Note que, se existirem duas expressões que tenham resultado de igual valor, é executada a que aparece em 1º lugar.

3.4.2       SELEÇÃO REALIZADA POR EXPRESSÃO BOOLEANA

A instrução CASE com expressões booleanas toma a seguinte forma:

 

CASE
WHEN expressão_booleana1
    instrução
    ...
WHEN expressão_booleana2
    instrução
    ...
OTHERWISE
    instrução
    ...
 END CASE 

 Instrução CASE com seleção por expressão booleana

As expressões booleanas são avaliadas na ordem em que aparecem e é executado o grupo de instruções associado á primeira expressão booleana verdadeira. Se todas as expressões forem falsas são executadas as instruções associadas á cláusula OTHERWISE.

Em qualquer das formas da instrução CASE pode-se incluir no bloco de instruções uma instrução EXIT CASE que provoca a interrupção do CASE neste ponto.

 

case2.4gl

MAIN
   DEFINE x    SMALLINT 
   LET x = 5000
   CASE
   WHEN x < 1000
      DISPLAY “X é menor que 1000”
   WHEN x >= 1000 AND x <= 5000
      DISPLAY “X está entre 1000 e 5000”
   WHEN x > 5000
      DISPLAY “X é maior que 5000”
   OTHERWISE
      DISPLAY “Valor inválido para X”
   END CASE
END MAIN

$ fglpc case1.4gl
$ fglgo case1.4go
X está entre 1000 e 5000

 Utilização do CASE utilizando expressões para fazer a seleção

4       BIBLIOTECA 4GL

Este capítulo descreve as funções que estão disponíveis na biblioteca do INFORMIX-4GL. Qualquer uma das funções apresentadas em seguida podem ser chamadas num programa, que o compilador automaticamente linka ao programa.

Em seguida estão as descrições das funções da biblioteca:

 

ARG_VAL(expressão)

Esta função recebe como argumento uma variável tipo INTEGER, devolve o argumento correspondente a esse número que lhe foi passado pela linha de comando.

A função ARG_VAL(n) devolve o n-ésimo argumento da linha de comando, sendo do tipo CHAR. O valor da expressão tem de estar entre 0 e NUM_ARGS() do número de argumentos passados pela linha de comando. ARG_VAL(0) corresponde ao nome do programa em execução.

As funções ARG_VAL e NUM_ARGS permitem passar dados ao programa a partir da linha de comando quando este é executado.

Suponhamos que o nosso programa de 4GL compilado se chame teste.4go. Ao executá-lo a partir da linha de comando fazemos:

 

fglgo teste Maria Pedro Catarina

Execução de um programa de 4GL, com argumentos

O programa que se segue é um exemplo, recebe os nomes num ARRAY de variáveis do tipo CHAR

 ARR_COUNT()

Esta função devolve o número de linhas introduzidas num ARRAY de um programa durante ou após a instrução INPUT ARRAY.

Quando se executa uma instrução com a cláusula BEFORE, AFTER ou ON KEY, o valor de ARR_CURR, SCR_LINE ou ARR_COUNT, é alterado, e o valor resulta da execução das cláusulas.

 

FUNCTION inserir_livros()
  DEFINE contador SMALLINT 

  FOR contador = 1 TO arr_count()
     INSERT INTO livros
     VALUES (ma_livros[contador].liv_num,
             ma_livros[contador].liv_nome,
             ma_livros[contador].liv_editora,
             ma_livros[contador].liv_no)
  END FOR
 END FUNCTION

 Exemplo de utilização da função ARR_COUNT

ARR_CURR()

Esta função devolve a linha do ARRAY do programa que corresponde à linha do ARRAY corrente da tela durante ou imediatamente após a instrução INPUT ARRAY ou DISPLAY ARRAY.

A linha corrente do ARRAY da tela corresponde à linha onde está localizado o cursor no inicio de uma cláusula BEFORE ROW ou AFTER ROW.

A primeira linha tanto do ARRAY do programa ou da tela é numerada a partir de 1.

 

INPUT ARRAY ma_livros TO sr_livros.*
 ON KEY(F6)
   DISPLAY ARRAY ma_estante TO sr_estante.*
   END DISPLAY
   LET l_arr_curr = ARR_CURR()
   MESSAGE "O valor corrente de ARR_CURR é: ", l_arr_curr
END INPUT 

Exemplo da utilização da função ARR_CURR

DOWNSHIFT(frase)

Esta função devolve uma string em que todos os caracteres que lhe tenham sido passados em letras maiúsculas sejam convertidos em minúsculas.

Se o caracteres passados como argumentos à função forem numéricos, estes não serão alterados.

A função DOWNSHIFT pode ser usada numa expressão ou para atribuição a uma variável com o valor devolvido pela função.

 

LET l_str = DOWNSHIFT(l_str)
  DISPLAY l_str 

 Exemplo de utilização da função DOWNSHIFT

UPSHIFT(frase)

Esta função devolve uma string em que todos os caracteres que lhe tenham sido passados em letras minúsculas sejam convertidos em maiúsculas.

Se os caracteres passados como argumentos à função forem numéricos, este não serão alterados.

A função UPSHIFT pode ser usada numa expressão ou para atribuição a uma variável com o valor devolvido pela função.

 

LET l_str = UPSHIFT(l_str)
 DISPLAY l_str 

 Exemplo de utilização da função UPSHIFT

ERR_GET(expressão)

Esta função devolve uma variável do tipo CHAR que contém a mensagem de erro, correspondente ao código enviado como argumento.

Geralmente a expressão é a variável global STATUS ou SQLCA.SQLCODE.

A utilização desta função é muito importante durante as operações de debug.

 

IF status < 0 THEN
    LET errtext = ERR_GET(status)
END IF 

 Exemplo de utilização da função ERR_GET

ERR_PRINT(expressão)

Esta função escreve a mensagem de erro do INFORMIX-4GL, correspondente ao seu argumento, na ERROR LINE (linha de erro).

Mais uma vez, em geral a expressão é a variável global STATUS ou SQLCA.SQLCODE.

A utilização desta função é muito importante durante as operações de debug.

 

IF status < 0 THEN
    LET errtext = ERR_PRINT(status)
END IF 

Exemplo de utilização da função ERR_PRINT

ERR_QUIT(expressão)

Esta função escreve a mensagem de erro do INFORMIX-4GL, correspondente ao seu argumento, na ERROR LINE (linha de erro) e interrompe a execução do programa.

 

CALL ERR_QUIT(expressão) 

Formato da função ERR_QUIT

Geralmente a expressão é a variável global STATUS ou SQLCA.SQLCODE.

A utilização desta função é muito importante durante as operações de debug.

 

IF status < 0 THEN
    CALL ERR_QUIT(status)
END IF 

Exemplo de utilização da função ERR_QUIT

5       Comandos básicos para entrada/saída de dados

5.1      PROMPTS

Em 4GL, uma das formas do programa se comunicar com o usuário, é por intermédio da instrução PROMPT. Esta instrução envia uma mensagem para o aplicativo e recebe um caracter ou seqüência de caracteres que foram digitados pelo usuário no seu terminal.

A definição genérica de uma instrução PROMPT é a seguinte:

PROMPT mensagem FOR [CHAR] variável
        [HELP numero_do_help]
        [ON KEY (tecla)
   instrução
   ...
   ...
END PROMPT

Definição genérica da instrução PROMPT

As mensagens assinaladas são um conjunto de uma ou mais variáveis, ou string(s) constantes, separados por vírgula.

Se a instrução for invocada com a palavra chave CHAR, o programa não espera pela tecla <NEW LINE>, o que equivale a dizer que aceita o caracter e sai da instrução PROMPT assim que é pressionada uma tecla.

 

 prompt.4gl

DATABASE livros 

MAIN
   DEFINE x LIKE livros.numero,
   ans CHAR(1),
   nome LIKE livros.nome

   LET ans = "s" 

   WHILE (ans = "s" OR ans = "S")
      PROMPT "Qual o numero do livro que pretende procurar: " FOR x 

      SELECT livros.nome
        INTO nome
        FROM livros
       WHERE numero = x 

      DISPLAY nome
      PROMPT "Quer escolher mais livros (S/N) " FOR CHAR ans

   END WHILE
END MAIN 

 $ fglpc prompt.4gl
 $ fglgo prompt.4go
 Qual o numero do livro que pretende procurar: 15
 Microsoft C Compiler
 Quer escolher mais livros (S/N) n

Utilização da instrução PROMPT

Quando o programa proposto é executado, pede ao usuário que digite o número do livro do qual pretende saber o nome, faz uma busca no banco de dados para descobrir o nome e pergunta se o usuário pretende ou não continuar a executar essa tarefa.

No programa, quando é executada a primeira instrução PROMPT, como não existe a palavra CHAR, é permitido digitar vários caracteres ou dígitos, e terminar pressionando <ENTER>. Na segunda instrução PROMPT, tal tecla já não é necessária, pois assim que foi informada a tecla <n> o programa continuou a sua execução.

5.2      MENUS

Uma forma simples de perguntar ao usuário qual tarefa pretende executar de um conjunto de N opções é a utilização de menus.

No 4GL é bastante fácil construir menus horizontais. Nestes menus, cada opção é identificada por uma pequena palavra e uma linha de explicação. Uma opção de menu encontra-se sempre destacada, sendo selecionada pressionando a tecla <ENTER>. Pode-se mudar a opção selecionada com a barra de espaços, as setas ou a primeira letra da opção desejada. Podem também criar-se opções invisíveis. Se as opções não couberem todas no menu, este é prolongado automaticamente com "..." (3 pontos) significando que se podem ser acessadas as outras opções que não estão visíveis.

Para construir um menu usa-se a instrução MENU cuja definição genérica é:

 

MENU "nome_do_menu"

[BEFORE MENU]   instruções
   ...
 COMMAND {KEY(lista_de_teclas) |
         [KEY(lista_de_teclas) "opcao"
         ["linha_de_help"] [HELP numero]
   instruções
   ...
   [CONTINUE MENU]
   ...
   [EXIT MENU]
   ...
   [NEXT OPTION "opção"]
   ...
 END MENU 

Definição genérica de uma instrução MENU

As cláusulas CONTINUE MENU (Interrupção das instruções de uma opção de menu selecionada), NEXT OPTION (Reposicionar o cursor para outra opção do menu), e EXIT MENU (sair do menu) podem ser colocadas dentro de outras instruções, desde que estejam dentro de uma instrução MENU. Estas cláusulas permitem uma grande otimização do comportamento do menu para o usuário pois pode haver reposicionamento na opção que logicamente deveria ser escolhida.

Se for utilizada a cláusula de HELP, quando pressiona a tecla de help, o programa chama a função show_help e envia o help com o número pretendido para visualização na tela.

A cláusula KEY é ótima para definir opções escondidas, e/ou atribuir uma nova tecla de atalho a uma opção.

Se houver a necessidade de tornar invisível alguma opção de menu em tempo de execução é possível utilizar a instrução HIDE OPTION “opção”. Já para tornar visível uma opção de menu tida antes como invisível pode-se utilizar a instrução SHOW OPTION “opção”.

Se houver necessidade de executar uma ou mais instruções no início do menu, antes de apresentá-lo ao usuário na execução é possível definir o bloco de comandos BEFORE MENU.

5.3      DISPLAY AT

Se pretender enviar pequenas mensagens ao usuário utiliza-se no 4GL a instrução DISPLAY AT.

 

DISPLAY lista_de_variaveis
        [AT linha_do_tela,coluna_da_tela]
        [ATTRIBUTE (lista_de_atributos)] 

 Definição da instrução DISPLAY AT

lista_de_variaveis
É um conjunto de uma ou mais variáveis do programa ou string(s) constantes separadas por vírgulas.

linha_da_tela  e coluna_da_tela
São as coordenadas da tela que definem o local onde vai aparecer a mensagem pretendida 

lista de atributos
É um conjunto de atributos possiveis a utilizar quando se envia a mensagem para a tela.

Os atributos possiveis são:

 

WHITE
YELLOW
MAGENTA
RED
CYAN
GREEN
BLUE
BLACK

REVERSE
BLINK
UNDERLINE
NORMAL
BOLD
DIM
INVISIBLE

Atributos de tela possíveis

A instrução DISPLAY é complexa e no capítulo sobre gestão de telas será novamente abordada.

Esta instrução envia para a tela uma mensagem constituída por um conjunto de variáveis e strings definidas na instrução. Em alguns dos exemplos anteriores, esta instrução já foi usada na sua forma mais simples.

 

display.4gl

MAIN
   DISPLAY "Isto vai aparecer na tela na linha 10 coluna 3"
           AT 10,3 ATTRIBUTE(BOLD)
END MAIN

$ fglpc display.4gl
$ fglgo display.4go

Isto vai aparecer na tela na linha 10 coluna 3

 Programa utilizando DISPLAY AT com ATTRIBUTES

5.4      Exercício – Instrução PROMPT e MENU

Criar um programa que tenha 2 opções de menu. As 2 opções de menu deverão acionar uma única função, mas passando um argumento diferente para permitir a identificação da opção de menu selecionada por parte da função.

Na função deverá ser apresentada uma pergunta ao usuário, utilizando o comando PROMPT, mas estas perguntas deverão ser diferentes, ou seja, caso seja acionada a primeira opção do menu, deverá apresentar uma pergunta, e ser for acionada a outra opção de menu deverá aparecer uma outra pergunta ao usuário.

Ambas as perguntas deverão aceitar como resposta somente as letras S (Sim) ou N (Não), não importando se for em letra maiúscula ou minúscula. Apresentar mensagem de erro ao usuário caso seja informada uma opção inválida.

Após responder a pergunta, deverá apresentar uma mensagem apresentando a opção selecionada.

6       Gestão de erros

No 4GL, depois de compilar com sucesso um programa, existem vários tipos de erros que  ainda podem estar sujeito a ocorrer.

É possível que o programa ainda possua erros de lógica (bugs), ou devido a uma má utilização do programa, surja uma situação de erro.

O usuário do programa, sem conhecimentos de programação nem acesso aos fontes, deve receber uma mensagem de erro que o ajude a corrigir a sua utilização ou solicitar a correção aos programadores.

6.1      Como apanhar um erro

No 4GL existe a instrução WHENEVER que permite alterar a forma como o programa se comporta ao ser confrontado com uma situação de erro ou aviso (warning).

 

WHENEVER {ERROR | WARNING}
          {GOTO label | CALL nome_de_função | CONTINUE | STOP

 Definição genérica da instrução WHENEVER

A primeira opção na cláusula (ERROR/WARNING) informa sobre a situação de erro ou aviso para a qual se vai tomar determinada ação, se ocorrer. Numa situação de erro (ERROR), pede-se ao programa que efetue determinada tarefa que ele não pode efetuar dessa forma e nesse contexto, a instrução WHENEVER é utilizada com a cláusula ERROR. Numa situação de aviso (WARNING), a tarefa pode ser cumprida ainda que não seja uma situação usual e então a instrução WHENEVER é invocada com a cláusula WARNING.

A segunda cláusula diz a ação que deve ser tomada quando confrontado com uma situação. As ações possíveis são:

 

GOTO label

Quando for encontrada esta situação, o programa deve continuar a partir da instrução assinalada por uma label. Um label é assinalado colocando-se um nome de label qualquer seguido do caracter ":" (dois pontos).

 

FUNCTION exp()
    WHENEVER ERROR GOTO erro
SELECT *
    INTO pr_livros.*
    FROM livros 

  RETURN 

  LABEL erro:
     DISPLAY "ERRO"
END FUNCTION

WHENEVER com GOTO

 

CALL nome_de_função

Quando for encontrada uma situação de erro, é executada a função nome_de_função.

 

FUNCTION exp()
   WHENEVER ERROR GOTO CALL erro()
SELECT *
    INTO pr_livros.*
    FROM livros 

  RETURN
END FUNCTION 

FUNCTION erro()
    DISPLAY "ERRO"
END FUNCTION 

 Exemplo de WHENEVER com CALL

STOP

Quando é utilizada a cláusula STOP, o programa ao ser confrontado com uma situação de erro ou aviso cujo código de erro seja negativo (sqlca.sqlcode < 0 ou status < 0), o programa é abortado e enviada para a tela uma pequena explicação do erro ocorrido.

 

CONTINUE

Quando é utilizada a cláusula CONTINUE o programa nunca é interrompido em situação de erro do SQL.

Para que possa ser haver controle da execução do programa neste caso, é preciso testar a variável STATUS ou a estrutura SQLCA.

6.2      A variável STATUS

Existente em qualquer programa 4GL, a variável denominada STATUS, pré-definida como global, possui o valor 0 (zero) para indicar que não houve erro e diferente de 0 (zero) para indicar a ocorrência de algum erro na execução de uma instrução.

6.3      A estrutura SQLCA

As vezes a variável STATUS não chega a identificar o erro ou aviso que ocorreu. Neste caso existe um RECORD global chamado SQLCA, que identifica os tipos de erros e avisos. Geralmente usa-se a estrutura SQLCA como adicional ao STATUS que identifica a existência de erro.

 

SQLCA RECORD
       SQLCODE INTEGER,
       SQLERRM CHAR[71],
       SQLERRP CHAR(8);
       SQLERRD ARRAY[6] OF INTEGER,
       SQLAWARN CHAR(8)
END RECORD

 RECORD SQLCA

O significado de cada uma das variáveis é:

 SQLCODE: Indica o resultado de uma instrução de SQL.Toma os valores:

0                             instrução bem sucedida.

NOTFOUND       busca bem sucedida mas não devolveu nenhuma linha.

SQLERRM: não é utilizado.

SQLERRP: não é utilizado.

SQLERRD: É um ARRAY de 6 inteiros com o seguinte significado:

sqlerrd [1]: não é utilizado.

sqlerrd [2]: Valor retornado pelo C-ISAM (Ver erros do C-isam).

sqlerrd [3]: número de linhas processadas.

sqlerrd [4]: não é utilizado.

sqlerrd [5]: caracter da string em foi encontrado erro.

sqlerrd [6]: é o ROWID da última linha.

SQLWARN: É um ARRAY de 8 caracteres que assinalam várias situações que necessitam de aviso durante a execucão de uma instrução SQL.

sqlwarn[1]: Se contiver:

  • Caracter W: Um ou mais dos outros elementos do ARRAY contêm 'W'
  • Espaço: Não é necessário verificar os outros elementos do ARRAY.

sqlwarn[2]: Se contiver:

  • Caracter W: Os dados de uma ou mais colunas foi truncado para caber dentro de uma variável.
  • Espaço: Nenhum dado foi truncado.

sqlwarn[3]: Se contiver:

  • Caracter W: Foi encontrado um erro numa função agregada (SUM, AVG, MAX ou MIN) A NULL.
  • Espaço: Não houve problemas.

sqlwarn[4]: Se contiver:

  • Caracter W: O número de itens de um SELECT é diferente do número de variáveis da cláusula INTO.
  • Espaço: Não houve erro.

sqlwarn[5]: Nao é utilizado.

sqlwarn[6]: Nao é utilizado.

sqlwarn[7]: Nao é utilizado.

sqlwarn[8]: Nao é utilizado.

7       Formulários de telas

Antes que um programa 4GL possa usar um formulário de tela, você deve primeiramente criar e compilar uma arquivo de especificação do formulário de tela.

Um formulário de tela é uma representação visual que pode suportar tarefas da entrada ou saída em uma aplicação de INFORMIX-4GL.

Esta especificação é fonte no formato texto onde se descreve o formato lógico do formulário da tela, e fornece instruções ao 4GL sobre como indicar valores dos dados no formulário no momento da execução.

As instruções de acesso as forms fazem parte da linguagem 4GL, e têm como objetivo o acesso á form. As principais são: OPEN FORM; DISPLAY; DISPLAY ARRAY; INPUT; INPUT ARRAY; etc.

Algumas instruções de entrada/saída básicas já foram citadas, elas são: DISPLAY AT; PROMPT; MENU.

A interação com a entrada de dados é sempre realizada por intermédio de um gestor de janelas. Muitas vezes o programador não percebe este fato. Existem algumas instruções e opções para controlar este gestor, das quais pode-se destacar: OPEN WINDOW; CURRENT WINDOW; CLOSE WINDOW; OPTIONS: MESSAGE LINE; etc.

As forms e instruções de INPUT, por si só não acessam nem escrevem em tabelas do banco de dados. Nas forms associam-se colunas de tabelas aos campos para que os programas possam definir os tipos de variáveis internas que vão utilizar, ou declaram-se diretamente os campos a partir dos tipos básicos.

O fluxo de informação de uma tela para a base de dados poderia ser descrito pelo seguinte esquema:

 

+-------+           +---------------------------------+
| Tela  |<--------->| Variáveis internas ao programa  |<-+
+-------+     |     +---------------------------------+  |
              |                                          |
forms e instruções de input/output                       |
                                           SQL___________|
    +----------------+       +---------+                 |
    | Banco de dados |<----->| SQLEXEC |<----------------+
    +----------------+       +---------+ 

Comunicação das telas com o banco de dados

7.1      FORM

As forms servem para desenhar e processar entrada de dados.

Uma form é escrita num arquivo, através de um editor de texto, e depois compilada pelo programa form4gl. A versão compilada pode então ser executada sempre que necessário por um programa em 4gl.

Assim, a form tem como suporte físico dois arquivos: o programa fonte com a extensão ".per" e o programa compilado com a extensão ".frm".

Uma form é um programa que se compila com o comando "form4gl" e gera um executável com a extensão .frm. A execução do .frm é feita pelo programa 4GL que, durante a execução abre e manipula a form por intermédio de instruções próprias.

 

+----------+

form.per

+----------+
     |
     |____form4gl
     |
+----------+

form.frm

+----------+ 

 Compilação de uma form

7.2      Definição de uma form

Através de um editor de texto define-se um arquivo com a definição da form no seguinte formato:

 

DATABASE [FORMONLY | <nome banco dados>]
  SCREEN
  {
  numero [f000 ]
  nome   [f001                   ]
         [f002                   ]
 }
 END 

 [TABLES]
 <tabela1>,
 <tabela2>,
 ...,
 <tabelaN> 

 ATTRIBUTES
 f000 = <tabela>.<coluna>;
 f001 = formonly.<nome campo>;
 f002 = formonly.<nome campo TYPE LIKE <tabela.coluna>;
 END

[INSTRUCTIONS]
   [DELIMITERS]
   [SCREEN RECORD...]
[END] 

Exemplo de definição de form

Estas seções são o esqueleto de qualquer form. Manipulando e inserindo instruções nestas seções consegue-se criar e explorar as capacidades das forms.

7.2.1       DATABASE

Quando um dos campos da form fizer referência na sua definição a uma coluna de uma tabela do banco de dados, da mesma forma com é feita definição de variáveis utilizando a cláusula LIKE, deve definir esta seção com o nome do banco de dados na qual as tabelas estão registradas. Exemplo: DATABASE livros.

Caso não seja feita nenhuma referência a tabelas do banco de dados, pode-se utilizar esta instrução como DATABASE FORMONLY. A palavra FORMONLY é reservada da linguagem 4gl para a situação em que não é feita referência de informações de um banco de dados na definição de uma form.

7.2.2       Seção SCREEN

Nesta seção o programador define o desenho da entrada de dados pretendida para a tela, considerando que todos os campos devem estar presentes numa única entrada de dados.

Esta seção é iniciada com o caracter “{“ e finalizada com “}”, sendo que a definição visual do conteúdo da tela é o que estiver entre estes 2 símbolos.

Cada definição de campo utilizado para edição ou apresentação de algum conteúdo variável durante a execução de um programa é englobado pelos símbolos “[]” (colchetes), sendo que o limite de caracteres permitido na entrada de dados para cada campo corresponde ao número de espaços em branco entre os colchetes.

Cada campo delimitado por colchetes precisa receber uma nomenclatura que é referenciada posteriormente na seção ATTRIBUTES para definição de características de cada campo. Esta nomenclatura é geralmente identificada por uma letra, ou letra seguida de número.

7.2.3       Seção TABLES

Esta seção deve ser obrigatoriamente definida quando a cláusula DATABASE faz referencia a um banco de dados válido.

São colocados os nomes das tabelas do banco de dados indicado na cláusula DATABASE e que são referenciadas na definição de campos da seção ATTRIBUTES, sendo que em caso de mais de uma tabela, deve-se separar estas por vírgula.

Quando é utilizada a cláusula DATABASE FORMONLY esta seção não é definida.

 

TABLES
<tabela1>,
<tabela2>,
...,
<tabelaN> 

 Definição da seção TABLES

7.2.4       Seção ATTRIBUTES

Esta seção de definição do arquivo da form tem o comportamento e o aspecto de cada um dos campos da tela definidos na seção SCREEN. Todo e qualquer campo da seção SCREEN deve ser descrito obrigatoriamente na seção ATTRIBUTES.

Na seção ATTRIBUTES define-se:

-          Como o 4GL apresenta o conteúdo do campo na tela;

-          Limites de tamanho para os valores/conteúdo a ser informado no campo da tela;

-          O tipo do campo, de duas formas: a coluna e tabela a que corresponde ou o tipo elementar do 4GL (SMALLINT, DECIMAL, DATE, etc.);

-          O nome do campo para as instruções 4GL que tratam da entrada de dados nas telas. Se o campo for definido como FORMONLY, pode-se escolher um nome qualquer, senão o nome é obrigatoriamente o nome da coluna correspondente do banco de dados.

Se qualquer um dos atributos descrito for utilizado pelo programa 4GL, quando se faz um INPUT sem a cláusula WITHOUT DEFAULTS, os defaults utilizados nos campos são os definidos na seção  ATTRIBUTES da tela (atributo DEFAULT), quando definidos, caso contrário serão apresentados em branco. A cláusula WITHOUT DEFAULTS faz com que os valores atuais das variáveis vinculadas aos campos da tela no comando INPUT sejam automaticamente apresentados.

Quando se utiliza a cláusula DATABASE FORMONLY, a definição dos nomes e atributos das colunas devem ser feitas utilizando a palavra “formonly” no lugar do nome da tabela do banco de dados.

Na seção ATTRIBUTES pode-se além de definir o nome e o tipo dos campos da form, definir alguns atributos que serão descritos a seguir.

7.2.4.1   AUTONEXT

Este atributo faz com que o cursor, durante uma instrução de INPUT, salte para o campo seguinte quando o campo atual estiver totalmente preenchido.

 

f011 = livros.data_entrada, AUTONEXT

 Exemplo atributo AUTONEXT

7.2.4.2   COMMENTS

Este atributo associa uma mensagem a um campo. Esta mensagem aparecerá na linha correspondente da tela sempre que o cursor estiver posicionado sobre o respectivo campo.

 

f011 = livros.data_entrada, AUTONEXT,
        COMMENTS = "Informe a data em que o livro entrou na biblioteca"; 

 Exemplo atributo COMMENTS

7.2.4.3   DEFAULT

Este atributo permite definir um valor inicial no campo da tela a que está associado, quando é invocada a instrução INPUT.

 

f011 = livros.data_entrada, AUTONEXT, DEFAULT = TODAY

 Exemplo atributo DEFAULT

7.2.4.4   FORMAT

Permite controlar o formato de display dos campos a que está associado, desde que tenham tipo de dados numéricos ou do tipo data.

Os caracteres de controle do formato são os seguintes:

 

###.##

Para campos numéricos (decimal, float e smallfloat). Indica o número de campos à esquerda e à direita do ponto decimal.

Mm

Para datas. Representação do mês através de dois dígitos.

Mmm

Para datas. Representação do nome do mês através de abreviatura com três letras (em inglês).

Dd

Para datas. Representação do dia através de dois dígitos.

Ddd

Para datas. Representação do nome do dia através de abreviatura com três letras (em inglês).

Yy

Para datas. Representação do ano através de dois dígitos.

yyyy

Para datas. Representação do ano através de quatro dígitos.

 

As datas podem ter os separadores "-" ou "/" à escolha.

 

f011 = livros.data_entrada, AUTONEXT, DEFAULT = TODAY, 
                             FORMAT = "dd/mm/yyyy "; 

 Exemplo atributo FORMAT

Se a variável de ambiente DBDATE já estiver no formato “dmy4/” que indica o formato “dd/mm/yyyy”, não será necessário indicar o formato acima, pois o 4GL já assumirá automaticamente este formato.

7.2.4.5   INCLUDE

Permite especificar valores ou intervalos de valores admissíveis a introduzir num campo associado a este atributo.

Pode-se especificar uma série de valores separados por virgulas ou um valor mínimo e um valor máximo separados pela palavra "TO".

 

f007 = livros.volumes, INCLUDE = (1 to 100); 

 Exemplo atributo INCLUDE

7.2.4.6   NOENTRY

Evita a inclusão de dados quando executa uma instrução INPUT, ou seja, o cursor nunca será posicionado no campo definido como NOENTRY através da instrução INPUT.

A instrução CONSTRUCT ignora este atributo, ou seja, mesmo para um campo definido como NOENTRY, a instrução CONSTRUCT permite acesso normalmente.

7.2.4.7   REVERSE

Este atributo mostra os campos, a que está associado, com caracteres em vídeo reverso.

 

f000 = livros.numero, REVERSE

 Exemplo atributo REVERSE

7.2.4.8   PICTURE

Permite definir o formato de entrada de um campo da tela, para campos do tipo alfanumérico.

O formato do campo define-se com o atributo PICTURE, combinando o seguinte conjunto de caracteres:

A Representa qualquer letra.

# Representa qualquer dígito.

X Representa qualquer caracter.

 

f001 = formonly.num_matricula CHAR(08), AUTONEXT, PICTURE = "##.###-#"; 

 Exemplo de utilização do atributo PICTURE

7.2.4.9   VERIFY

Este atributo usa-se quando se pretende introduzir, num determinado campo, o mesmo valor duas vezes para reduzir a probabilidade de erro na entrada dos dados.

7.2.4.10DOWNSHIFT

Transforma as letras maiúsculas informadas em um campo alfanumérico em letras minúsculas automaticamente durante a digitação.

7.2.4.11UPSHIFT

Transforma as letras minúsculas informadas em um campo alfanumérico em letras maiúsculas automaticamente durante a digitação.

7.2.5       Seção INSTRUCTION

A seção INSTRUCTION é opcional e é a última seção a ser definida em uma form.

No 4GL, a única operação que podemos realizar nesta seção é a instrução DELIMITERS e a definição de SCREEN RECORDS.

Em um capítulo mais adiante será apresentada a explicação da seção SCREEN RECORD.

Com o comando DELIMITERS pode-se alterar os caracteres delimitadores do campo da tela, quando este é visualizado na execução do programa. Os caracteres default do 4GL são os colchetes "[...]".

 

DELIMITERS "{}" 

 Neste exemplo o símbolo "{" é o caracter de abertura e "}" é o caracter de fechamento.

7.3      O comando FORM4GL

Após a definição, deve-se compilar o arquivo de definição da form utilizando o programa form4gl. Se existirem erros, estes são colocados num arquivo de mesmo nome, com extensão .err, contendo a identificação dos erros, que após corrigidos no arquivo de definição original, deve-se compilá-lo novamente. Estes procedimentos podem ser executados repetidamente até obter uma compilação da form sem erros.

 

form4gl <arquivo.per> 

 Exemplo de compilação de form

O comando form4gl tem a estrutura de um comando LINUX, com três opções disponíveis.

-s

Esta opção compila o fonte da form existente no arquivo cujo nome segue a opção no comando. Note que o verdadeiro nome do arquivo é constituído pelo nome existente no comando seguido pela extensão ".per". No caso de existirem erros, é criado um arquivo com o mesmo nome da form fonte mas com extensão ".err", e neste arquivo contém as mensagens que indicam os pontos onde se encontram os erros. Pode-se editar este arquivo e corrigir os erros e, depois de se apagarem as mensagens de erro, gravá-lo no arquivo com extensão ".per" e compilá-lo novamente.

-v

Esta opção também compila a form fonte mas verifica se o comprimento dos campos definidos na seção SCREEN têm o mesmo comprimento das colunas a que são associados na seção ATTRIBUTES e apresenta as respectivas mensagens no arquivo de erros.

-d

Esta opção cria uma form default. Pode especificar o nome da form, o banco de dados e as tabelas que compõem a form escrevendo-as nesta ordem e a seguir à opção -d do comando. No entanto se escrever apenas o comando seguido da opção -d e pressionar <ENTER> o comando irá solicitar ao usuário que introduza esses elementos uma a um, pressionando <ENTER> após informar cada elemento. Para indicar que não pretende especificar mais tabelas pressione <ENTER> duas vezes.

 

form4gl -d f_livros livros autores livros 

 Cria e compila a form f_livros com as tabelas LIVROS e AUTORES do banco de dados LIVROS.

 

form4gl -v f_livros 

No comando acima é realizada apenas a compilação da form f_livros.

8       Gestão de janelas – parte 1

Sempre que se pretende manusear uma form deve-se abri-la.

A abertura de uma form pode ser realizada por intermédio da instrução OPEN FORM ou OPEN WINDOW.

8.1      FORM

A abertura de uma form através da instrução OPEN FORM é feita da seguinte forma:

 

OPEN FORM nome_da_form FROM "arquivo_form" 

 Definição genérica da instrução OPEN FORM

O nome da form é dado pelo programador e é com esse nome que se passa a referenciar a entrada de dados dentro do programa. "arquivo_form" é o arquivo em que se encontra a form (arquivo .per) compilada retirando a extensão ".frm".

Antes de qualquer ação sobre a entrada de dados, deve-se mostrar esta form na tela sem qualquer dado. Para efetuar esta ação usa-se a instrução DISPLAY FORM.

 

DISPLAY FORM nome_da_form ATTRIBUTE(lista_de atributos) 

 Definição genérica da instrução DISPLAY FORM

8.2      WINDOW

É comum, em num programa a utilização de pequenas partes da tela para efetuar algumas tarefas, a que se costuma chamar de janelas (Windows).

8.2.1       OPEN WINDOW

As cláusulas desta instrução definem o tamanho e o posicionamento da janela na tela, podendo associar uma form a janela e definir um ou mais atributos para a janela.

Assim que é executada a instrução OPEN WINDOW, a janela aparece na tela.

 

OPEN WINDOW nome_da_janela AT linha,coluna
      WITH {numero_de_linhas ROWS numero_de_colunas COLUMNS
           |FORM "ficheiro_da_form"}
     [ATTRIBUTE (lista_de atributos)] 

 Definição genérica da instrução OPEN WINDOW

Utilizando este comando não é preciso efetuar o comando DISPLAY FORM como é feito para a instrução OPEN FORM para apresentar a form na tela.

Na cláusula AT diz-se quais as coordenadas do canto superior esquerdo da janela. A cláusula WITH serve para dizer qual o tamanho da janela e pode ser feita de duas maneiras: dizendo o numero de linhas e de colunas que se pretende para a janela ou associando uma form. No caso de se associar a janela a uma form, o 4GL atribui como tamanho da janela o tamanho da form. Se escolher esta opção não é necessário chamar a instrução OPEN FORM. A cláusula ATTRIBUTE é opcional e associa um ou mais atributos à janela. Os atributos podem ser utilizados para fazer o seguinte:

-          Apresentar borda para a janela (BORDER);

-          Mostrar a janela em modo vídeo reverso ou com uma cor específica, conforme os atributos de cores apresentados no capítulo da instrução DISPLAY AT;

-          Indicar a posição desejada para apresentação dos comandos PROMPT, MESSAGE, MENU, FORM e COMMENT LINE:

  • COMMENT LINE <posição> - default é 1 ou FIRST;
  • FORM LINE <posição> - default é 3 ou (FIRST + 2);
  • MENU LINE <posição> - default é 1 ou FIRST;
  • MESSAGE LINE <posição> - default é 2 ou (FIRST + 1);
  • PROMPT LINE <posição> - default é 1 ou (FIRST)

A “posição” pode ser identificada com os seguintes valores:

  • FIRST (indica a primeira linha da janela);
  • LAST (indica a última linha da janela);
  • Número fixo da linha desejada, respeitando o número de linhas definidas para a janela.

 

OPEN WINDOW wcust AT 3, 6 WITH 10 ROWS, 50 COLUMNS
      ATTRIBUTE (BORDER, MESSAGE LINE LAST,
                 PROMPT LINE LAST-2) 

 Definição genérica da instrução OPEN WINDOW

8.2.2       CLOSE WINDOW

Para retirar a janela da tela utiliza-se a instrução CLOSE WINDOW. Assim que esta instrução é executada, a janela desaparece da tela. Se a janela estiver associada a uma form, quando se fechar a janela, a form é fechada automaticamente.

 

CLOSE WINDOW nome_da_janela 

Definição genérica da instrução CLOSE WINDOW

8.2.3       CURRENT WINDOW

Durante alguns processamentos, você pode pretender mudar de janela de trabalho (ou janela corrente). A instrução CURRENT WINDOW transforma em janela corrente o nome da janela desejado, que é passado como parâmetro.

 

CURRENT WINDOW IS nome_da_janela 

 Definição genérica da instrução CURRENT WINDOW

A janela para qual se dá o nome precisa estar aberta para utilizar a instrução CURRENT WINDOW. Se a janela especificada estiver associada uma form, essa form passará a ser a form corrente.

Se a janela estiver debaixo de outras, quando invocar a instrução CURRENT WINDOW ela irá sobrepor-se a todas que estejam no espaço utilizado pela janela solicitada.

Existe uma janela especial, chamada SCREEN, que está sempre aberta, e onde são utilizadas as forms não associadas a janelas, feitos os displays, etc.

8.3      OPTIONS

Um programa de 4GL assume valores por default para o funcionamento de diversas instruções. Para alterar esses valores usa-se a instrução OPTIONS.

 

OPTIONS {MESSAGE LINE linha |
          PROMPT  LINE linha |
          COMMENT LINE linha |
          ERROR   LINE linha |
          FORM LINE linha |
          INPUT {WRAP | NO WRAP} |
          INSERT KEY tecla |
          DELETE KEY tecla |
          NEXT KEY tecla |
          PREVIOUS KEY tecla |
          ACCEPT KEY tecla |
          HELP FILE "ficheiro" |
          HELP KEY tecla }

 Definição genérica da instrução OPTIONS

8.4      Instrução INPUT

A instrução INPUT é utilizada para editar uma form (para receber dados por intermédio de uma form).

Esta instrução tem como parâmetros um conjunto de nomes de campos (que correspondem aos campos que se pretende editar) que foram previamente identificados na seção ATTRIBUTES da form e um conjunto de variáveis que também podem ser definidas de várias formas. É também possível executar instruções e controlar o movimento do cursor condicionalmente à posição atual do cursor.

 

INPUT [BY NAME lista_de_variaveis
       [WHITHOUT DEFAULTS]
       |lista_de_variaveis [WITHOUT DEFAULTS]
  FROM {lista_campos|screen_record[[n]].*}[,...}}
       [HELP numero_do_help] 

[{BEFORE INPUT
 |AFTER INPUT
 |BEFORE FIELD sublista_de_campos
 |AFTER {FIELD sublista_de_campos|INPUT}
 |ON KEY(lista_de_teclas)}
    instrução
    ...
    [NEXT FIELD nome_do_campo]
    ...
    [EXIT INPUT]
    ...
    ...
END INPUT 

Definição genérica de uma instrução INPUT

Se a cláusula WITHOUT DEFAULTS for omitida, faz com que a instrução INPUT, quando chamada, apresente nos campos os defaults definidos na form. Se a cláusula existir, os valores apresentados nos campos durante o INPUT são os valores que estavam atribuídos as variáveis no momento anterior ao acionamento do INPUT.

9       Exercício - Sistema proposto

A medida em que forem feitos avanços nos conhecimentos do 4GL, será feito o desenvolvimento de uma aplicação para utilização em uma biblioteca que tenha acesso a informações de um banco de dados, com cadastro e consulta de livros e cadastro e consulta de requisições de livros.

A entrada de dados que será construída começa por uma proposta genérica, sendo desenvolvida e aperfeiçoada por etapas. A ordem de apresentação destas é a que normalmente se utiliza na prática, salvo em algumas situações para facilitar a entendimento.

A proposta é a seguinte:

-          Uma única entrada de dados que trabalhe com as tabelas LIVROS, AUTORES, TEMAS e EDITORAS do banco de dados LIVROS, para poder visualizar simultaneamente um livro (tabela LIVROS), os autores do livro (LIVRO_AUTORES) e os temas focados (LIVRO_TEMAS). As entradas de dados referentes a autores e temas devem aparecer em janelas diferentes.

-          Os diversos campos devem ser validados (tipo de dado correto, valores admissíveis, etc.). Sempre que existir um erro, este deve ser assinalado com uma mensagem ao usuário, descrevendo o erro e posicionando o cursor no campo em questão.

-          As colunas que contenham códigos a procurar numa tabela (chave estrangeira) devem possuir facilidades de busca na tabela de pesquisa.

Serão descritas a maior parte das instruções de uma form e as instruções do 4GL que permitem a interação com as forms. Estas irão sendo exemplificadas durante as várias fases do processo de desenvolvimento de uma entrada de dados complexa, com várias tabelas, validações, mensagens de erro, etc.

Foram selecionadas as instruções que normalmente são utilizadas nos tipos de entrada de dados que fazem parte de qualquer aplicação.

9.1      Definição da form livros

Como ponto de partida vamos gerar a form de dados de livros para a tabela LIVROS, obtendo-se o esqueleto do programa com o comprimento e o vínculo das colunas às tabelas já definidas.

 

DATABASE livros 

 SCREEN
 {       número: [f000 ]
         nome: [f001]
         tradução: [f003]
         volumes: [f005]
         páginas: [f006]
         edição: [f007]
         ano: [f008]
         data entrada: [f009]
         sala: [f010]
         estante: [f011]
         prateleira: [f012]
         observações: [f013] }
 END 

 TABLES
     
 livros 

 ATTRIBUTES
      
 f000 = livros.numero;
        f001 = livros.nome;
        f003 = livros.traducao;
        f005 = livros.volumes;
        f006 = livros.paginas;
        f007 = livros.edicao;
        f008 = livros.ano;
        f009 = livros.data_entrada;
        f010 = livros.sala;
        f011 = livros.estante;
        f012 = livros.prateleira;
        f013 = livros.observacoes;
END

  

A seguir vamos trabalhar sobre a seção SCREEN para obter o desenho da entrada de dados pretendida, considerando que todos os campos devem estar presentes numa única entrada de dados.

Uma solução para a entrada de dados pretendida pode ser conforme a seção SCREEN seguinte: 

 

DATABASE livros 

 SCREEN
 { Número do livro: [f000]
    Nome do livro: [f001]
    Tradução: [f004]
     Volumes: [f007]         
     Páginas: [f008]
     Editora: [f009]
     Ano: [f010]  
     Data de entrada: [f011]
     Local:     Sala: [f012]
     Estante: [f013]
     Prateleira: [f014]
     Observações: [f015]  }

 Seção SCREEN da form livros

A seção TABLES fica da mesma forma como foi apresentado inicialmente:

 

TABLES
  
 livros

 Seção TABLES da form livros

Na seção ATTRIBUTES pode-se além de definir o nome e o tipo dos campos da form, definir alguns atributos que serão descritos a seguir. Na form livros serão utilizados os de uso mais comum.

AUTONEXT

Na form livros iremos usá-lo nas datas.

 

f011 = livros.data_entrada, AUTONEXT

 

COMMENTS

Na form livros iremos aplicá-lo ao campo data_entrada com a seguinte mensagem: "Introduza a data em que o livro entrou na biblioteca"

 

f011 = livros.data_entrada, AUTONEXT,
        COMMENTS = "Informe a data em que o livro entrou na biblioteca"; 

 

DEFAULT

Na form livros será aplicado aos campos volumes, sala e data da entrada. Neste último campo (data da entrada do livro) irá assumir o valor inicial como a data atual, escrevendo "today" como valor inicial.

 

f011 = livros.data_entrada, AUTONEXT, DEFAULT = TODAY

  FORMAT

Na form livros iremos usar o atributo "format" apenas nas datas e com o formato "dd/mm/yyyy".

 

f011 = livros.data_entrada, AUTONEXT, DEFAULT = TODAY, 
                             FORMAT = "dd/mm/yyyy "; 

 

INCLUDE

Na form livros vamos usar o atributo INCLUDE nos campo “volumes”, admitindo que um livro não pode ter mais de 100 volumes (1 a 100).

 

f007 = livros.volumes, INCLUDE = (1 to 100); 

  

REVERSE

Na form livros vamos usar em vídeo reverso o campo número.

 

f000 = livros.numero, REVERSE

 9.2      Programa de cadastro de livros

 

DATABASE livros

MAIN
    CALL menu()
END MAIN 

FUNCTION menu()
   OPEN WINDOW w_livros AT 2,2 WITH FORM "livros"
END FUNCTION 

Abertura da form livros

A abertura da tela será feita utilizando a instrução OPEN WINDOW.

O nome da tela/window (w_livros) é dado pelo programador e é com esse nome que se passa a referenciar a entrada de dados dentro do programa. "arquivo_form" é o arquivo em que se encontra a form compilada retirando a extensão ".frm".

Na window w_livros vão ser editadas todas as colunas da tabela LIVROS.

O programa deve ter variáveis para todas as colunas que desejar fazer entrada de dados na tela. Neste caso podemos declarar facilmente um RECORD que contenha todas as colunas da tabela LIVROS.

 

DATABASE livros 

   DEFINE mr_livros LIKE livros.*
MAIN
    CALL menu()
END MAIN 

FUNCTION menu()
   OPEN WINDOW w_livros AT 2,2 WITH FORM "livros"
END FUNCTION 

Declaração do RECORD mr_livros

Este programa terá um menu que permitirá as seguintes opções sobre a tabela de LIVROS:

INCLUIR           - Adiciona um novo livro na tabela de livros.

CONSULTAR    - Procura um livro já cadastrado por número.

MODIFICAR      - Modifica o registro atual na entrada de dados, se existir.

EXCLUIR          - Exclui, se existir, o registro atual apresentado na entrada e dados.

FIM                  - Sai do programa.

 

Para criar menus utiliza-se a instrução MENU já vista anteriormente.

 

MENU "LIVROS"
COMMAND "Incluir" "Incluir um novo livro."
    CALL incluir() 

COMMAND "Consultar" "Consultar um livro pelo numero."
    CALL consultar() 

COMMAND "Modificar" "Permite modificar o livro corrente."
    CALL modificar() 

COMMAND "Excluir" "Remove o livro corrente."
    CALL excluir() 

COMMAND "Fim" "Sai do programa."
    EXIT MENU
END MENU 

Menu da entrada de dados para livros

Este menu é implementado após a abertura da form principal de cadastro de livros.

 

Função Incluir

A função incluir recebe os dados de um livro do teclado e o inclui no banco de dados.

Nesta função vamos utilizar a instrução INPUT na sua forma mais simples.

 

FUNCTION incluir()
  INPUT BY NAME mr_livros.* 

  WHENEVER ERROR CONTINUE
  INSERT INTO livros VALUES (mr_livros.*)
  WHENEVER ERROR STOP 

  IF sqlca.sqlcode = 0 THEN
     ERROR "Livro incluído"
  ELSE
     ERROR "Erro na inclusão do livro ",status
  END IF
END FUNCTION 

Inclusão de novo livro

A lista de campos da cláusula FROM da instrução INPUT, neste caso está implícita no caracter '*' do RECORD. Isto quer dizer que vão ser editados todos os campos que existem na variável composta mr_livros. Como a variável foi declarada LIKE livros.*, vão ser editadas todas as colunas da tabela LIVROS onde os nomes dos componentes forem correspondentes aos nomes dos campos da tela, definidos no arquivo da form, na seção ATTRIBUTES.

A instrução INSERT é realizada como foi definida no comando SQL tendo na cláusula VALUES, as variáveis que contém os valores pretendidos.

As instruções WHENEVER aparecem para que o programa não interrompa a sua execução, e seja emitido um erro de SQL quando houver erro no INSERT (ver tratamento de erros).

 

Função Consultar

A função consultar, recebe o número do livro que se pretende procurar, procura-o no banco de dados, e mostra na entrada de dados todos os dados correspondentes. Note que o número do livro é introduzido na form.

 

FUNCTION consultar()
  CLEAR FORM 

  INPUT BY NAME mr_livros.numero 

  SELECT *
    INTO mr_livros.*
    FROM livros
    WHERE numero = mr_livros.numero 

IF status != 0 THEN
     ERROR "Livro não cadastrado"
  ELSE
     DISPLAY BY NAME mr_livros.*
  END IF
END FUNCTION 

Função que procura um livro com determinado número

A instrução CLEAR FORM serve para limpar todos os campos existentes na form corrente. No exemplo acima, se a busca for bem sucedida é feito um display de todas as colunas da tabela.

 

Função Modificar

A função modificar altera um livro que tenha sido previamente consultado ou incluído, ou seja, se este tiver sido apresentado na entrada de dados.

Esta função aceita os valores sem inicializar variáveis e sem limpar os campos da form, validando a existência de um livro válido.

 

FUNCTION modificar()
  IF mr_livros.numero IS NULL THEN
     ERROR "Nao ha nenhum livro para mudar"
  ELSE
     INPUT BY NAME mr_livros.* WITHOUT DEFAULTS
     WHENEVER ERROR CONTINUE
     UPDATE livros
        SET livros.* = mr_livros.*
      WHERE livros.numero = mr_livros.numero
     WHENEVER ERROR STOP
     IF sqlca.sqlcode <> 0 THEN
        ERROR “Erro na modificação – “,sqlca.sqlcode
     ELSE
        ERROR “Livro modificado.”
     END IF
  END IF
END FUNCTION

Modificação de um livro

É feito um teste da variável mr_livros.numero para garantir que está preenchida com um número de livro existente.

Na instrução UPDATE, descrita em SQL, as variáveis do 4GL são colocadas na cláusula SET.

 

Função Excluir

A função excluir, exclui o livro corrente.

Esta função exclui do banco de dados o livro correspondente aos dados presentes na entrada de dados.

 

FUNCTION excluir()
  IF mr_livros.numero IS NULL THEN
     ERROR "Não há nenhum livro para excluir"
  ELSE
     WHENEVER ERROR CONTINUE
     DELETE FROM livros
      WHERE numero = mr_livros.numero
     WHENEVER ERROR STOP 

     IF sqlca.sqlcode <> 0 THEN
        ERROR "Erro na exclusão - ",sqlca.sqlcode
     ELSE
        INITIALIZE mr_livros.* TO NULL
        CLEAR FORM
        ERROR "Livro excluído"
     END IF
  END IF
END FUNCTION

Exclusão de um livro

O teste da existência de livro corrente é feito da mesma forma que na função modificar.

As instruções INITIALIZE e CLEAR FORM aparecem para garantir a não existência do livro corrente após a sua exclusão quando bem sucedida.

10    Gestão de janelas – parte 2

10.1   Outras capacidades da instrução INPUT e outros atributos das forms

No capítulo anterior a instrução INPUT foi utilizada na sua forma mais simples para as ações de consulta, inclusão e modificação de dados. Além da cláusula onde se definem as variáveis, a instrução INPUT normalmente pode ser utilizada com mais cláusulas:

 

HELP <numero>

Quando a tecla de help for pressionada, durante a edição de uma form, é automaticamente executada a função show_help, que apresenta o help relativo ao número especificado (ver helps).

 

BEFORE FIELD <nome da coluna>

Antes da edição da coluna, no momento em que o cursor está sendo posicionado sobre a coluna em questão, as instruções especificadas para esta cláusula são executadas.

 

AFTER FIELD <nome da coluna>

Após a edição da coluna sobre a qual o cursor está posicionado, as instruções especificadas para esta cláusula são executadas.

 

AFTER INPUT

Depois da edição de todos os campos da form, o programa cumpre as instruções especificadas nesta cláusula.

 

ON KEY <lista de teclas de atalho>

Se for pressionada uma das teclas definidas nesta cláusula, o programa cumpre as instruções definidas a seguir.

10.2   Teclas  de interrupção e confirmação de execução

Existe uma tecla lógica que o usuário pode pressionar dentro de uma aplicação 4GL para interromper a execução da aplicação. Se o programa 4GL não incluir a instrução DEFER INTERRUPT no início do programa, ao pressionar esta tecla, a execução do programa é interrompida. Com DEFER INTERRUPT, pressionando as teclas de interrupção <CTRL+C>, a variável global de controle interno do 4GL chamada INT_FLAG é alterada para 1 (TRUE) e a instrução atual de interação com o usuário é cancelada, continuando a execução a partir da próxima instrução. 

A tecla de atalho para confirmação de uma instrução de entrada de dados (INPUT, DISPLAY ARRAY, INPUT ARRAY, PROMPT) antes que esta seja concluída normalmente, ou seja, podendo ser pressionada a qualquer momento, é a tecla ESC. A tecla ENTER é assumida como padrão para navegação entre campos e no caso do cursor estar posicionado no último campo pertencente a entrada de dados atual e o usuário pressionar ENTER, automaticamente a entrada de dados terá seus dados confirmados. Neste caso a variável INT_FLAG terá seu valor inalterado.

10.3   Exercício – novo campo e tratamento de tecla de interrupção

Quando se definiu o programa falou-se na validação do campo “editora“ na tabela EDITORAS, com envio do nome da editora para a tela. Vamos agora alterar o programa e form de modo a cumprir essa tarefa.

A form tem que possuir um novo campo onde será visualizado o nome da editora na seção SCREEN, assim:

 

Editora: [f009 ] [f9d                        ] 

 Novo campo para visualização do nome de editora

Precisa também introduzir os atributos para este campo na seção ATTRIBUTES:

 

f9d = FORMONLY.nome_editora TYPE LIKE editoras.nome, NOENTRY; 

  Atributos do novo campo

O atributo FORMONLY identifica um campo com um nome permitindo dizer qual o tipo para esse campo. Neste caso definimos o tipo com LIKE , no entanto é possível definir o tipo através dos tipos elementares.

 

campo = FORMONLY.nome_do_campo [TYPE [tipo_de_dados | LIKE tabela.coluna]]
                                [NOT NULL][,lista_de_atributos] 

 Definição genérica do atributo FORMONLY

Utilizou-se também o atributo NOENTRY para forçar a instrução INPUT a não editar este campo, ou seja, para o comando INPUT este campo será somente para visualização.

O programa 4GL ainda não sabe como fazer a validação do código de editor. Para conseguir realizar esta função, utiliza-se a cláusula AFTER FIELD, faz-se uma consulta na tabela de editores e apresenta-se o nome do editor encontrado na tela.

 

DEFINE l_nome_editora LIKE editoras.nome;
 INPUT BY NAME mr_livros.* 

 AFTER FIELD editora
    INITIALIZE l_nome_editora TO NULL
    WHENEVER ERROR CONTINUE
    SELECT nome
      INTO l_nome_editora
      FROM editoras
     WHERE codigo = mr_livros.editora
    WHENEVER ERROR STOP
    IF sqlca.sqlcode = NOTFOUND THEN
       ERROR "Editora não cadastrada."
       NEXT FIELD edição
    ELSE
       DISPLAY l_nome_editora TO nome editora
    END IF
END INPUT 

Alteração das instruções INPUT para efetuar as validações

Quando a variável SQLCA.SQLCODE indica a constante NOTFOUND, quer dizer que não foi encontrada a editora cujo código foi informado pelo usuário, sendo que neste caso é apresentada uma mensagem de erro e o cursor é retornado novamente para informar o mesmo campo até que o usuário informe um código de editora válido.

Para tratamento das teclas de interrupção ou confirmação, usando a variável INT_FLAG, de forma que a inclusão, modificação, consulta ou exclusão somente sejam efetivadas caso esta variável esteja com seu valor igual a 0 (FALSE), ou seja, se o usuário não pressionar a tecla de interrupção, deve-se implementar da seguinte forma:

 

DEFINE l_nome_editora LIKE editora.nome; 

LET int_flag = 0
   INPUT BY NAME mr_livros.*

AFTER FIELD edição
    INITIALIZE l_nome_editora TO NULL
    WHENEVER ERROR CONTINUE
    SELECT nome
      INTO l_nome_editora
      FROM editoras
     WHERE codigo = mr_livros.editora
    WHENEVER ERROR STOP
    IF sqlca.sqlcode = NOTFOUND THEN
       ERROR "Editora não cadastrada."
       NEXT FIELD editora
    ELSE
       DISPLAY l_nome_editora TO nome editora
    END IF 

AFTER INPUT
    IF int_flag = 0 THEN
       #Consistências da entrada de dados
    END IF
END INPUT 

IF int_flag <> 0 THEN
 
    ERROR “Inclusão cancelada.”
ELSE
    #Realizar a inclusão do livro(INSERT)
END IF 

Alteração das instruções INPUT para efetuar as validações

11    Interação do 4GL com o banco de dados

11.1   Gestão de cursores

A linguagem 4GL, como linguagem de programação, utiliza-se de variáveis para armazenar dados durante a execução do programa (valores digitados pelo usuário, resultados de cálculos intermediários, etc.).

Tal como na maioria das linguagens, as variáveis 4GL tem uma dimensão fixa e determinada a parte. A característica da linguagem para acessar um banco de dados (através do SQL) faz com que o 4GL tenha também a possibilidade de manusear estes dados de um banco de dados (tabelas, colunas, views, etc). Por esta razão é impossível ao construir um programa declarar variáveis para armazenar o resultado de uma instrução SELECT que retorne mais de um registro, por esta razão tornou-se necessário introduzir a noção de cursor para resolver estas questões.

Uma instrução SELECT pode dar origem a mais de uma linha da dados (depende da cláusula WHERE).

 

errselect.4gl

DATABASE livros
MAIN
   DEFINE l_numero LIKE livros.numero,
          l_nome LIKE livros.nome,
          l_edicao LIKE livros.edicao 

   SELECT numero, nome, edição
     INTO l_numero, l_nome, l_edicao
     FROM livros
END MAIN 

$ fglpc errselect.4gl
$ fglgo errselect.4go
Program stopped at "errselect.4gl" line number 9.
SQL statement error number -284.
A subquery has returned not exactly one row. 

 Leitura com instrução SELECT que dá origem a mais do que uma linha.

Se consultar o manual de 4GL verificará que o erro aconteceu porque uma busca não devolveu exatamente uma linha.

Um cursor é uma forma de acessar individualmente as linhas do resultado de uma instrução SELECT.

Existe um segundo tipo de cursor associado a instruções INSERT de que se falará mais adiante. Para acessar uma linha pertencente a esse cursor usa-se a instrução FETCH.

Ao conjunto de linhas selecionadas com uma instrução SELECT utilizando-se um cursor chama-se conjunto ativo para essa instrução SELECT.

A linha ativa em determinado momento no cursor chama-se linha corrente.

Um cursor pode estar em dois estados: aberto ou fechado. Quando está aberto, o cursor tem associado a ele um conjunto ativo de registros (linhas) e aponta para uma linha corrente. Quando um cursor está fechado, deixa de estar associado a um conjunto ativo de registros (linhas), continuando no entanto associado à instrução SELECT para a qual foi declarado.

As ações que se podem realizar sobre um cursor são:

 

Declarar o cursor

Esta ação é realizada com o auxilio da instrução DECLARE associada a  uma instrução SELECT.

 

DECLARE cq_livros CURSOR FOR
  SELECT numero, nome, edição
    FROM livros
    WHERE numero < 5 

  

Abrir o cursor

Esta ação é realizada com a instrução OPEN, que provoca a execução da instrução SELECT associada ao cursor e a busca correspondente na base de dados é realizada nessa altura. A partir desta instrução até o cursor ser fechado fica associado um conjunto de linhas (conjunto ativo) que corresponde ao resultado da instrução SELECT. O cursor fica posicionado imediatamente antes da primeira linha.

 

OPEN cq_livros 

  

Acessar uma ou mais vezes ao cursor

Os acessos são feitos com o auxilio da instrução FETCH que avança a linha corrente para a linha seguinte e armazena os dados retornados desta linha nas variáveis definidas na cláusula INTO. Se o cursor estiver posicionado na ultima linha, a variável sqlca.sqlcode será atualizada com o valor NOTFOUND (ou código 100).

 

WHILE 1
    FETCH cq_livros INTO numero, nome, edicao;
    IF sqlca.sqlcode != 0 THEN
       EXIT WHILE
    END IF
    DISPLAY numero, nome, editora
 END WHILE 

 

Fechar o cursor

Ação realizada pela instrução CLOSE que faz a colocação do cursor em estado fechado.

Não é possível invocar uma outra instrução sobre o cursor que não esteja aberto. O cursor por estar fechado não deixa de ter uma instrução SELECT associada, que ao ser invocado pela instrução OPEN é executada novamente a instrução SELECT associado ao cursor.

 

CLOSE cq_livros 

 

Um cursor pode também ser fechado automaticamente através de uma instrução de fim de transação (COMMIT WORK ou ROLLBACK WORK), já que uma das ações destas instruções é fechar todos os cursores que se encontrarem abertos no momento. Mas isto somente ocorre se na instrução de definição do cursor (DECLARE) tiver sido omitido o atributo WITH HOLD.

Apresenta-se um exemplo de acesso a uma tabela do banco de dados por intermédio de um cursor.

 

cursor1.4gl

DATABASE livros

MAIN
   DEFINE numero LIKE livros.numero,
          nome   LIKE livros.nome,
          aux    SMALLINT 

   DECLARE cq_livros CURSOR WITH HOLD FOR
      SELECT numero, @nome
      FROM livros
     WHERE numero < 5

   LET aux = 1

   OPEN cq_livros
   WHILE aux = 1
      FETCH cq_livros INTO numero, nome
      IF sqlca.sqlcode != 0 THEN
         EXIT WHILE
      END IF
      DISPLAY numero, nome
   END WHILE 

   CLOSE cq_livros
END MAIN

$ fglpc cursor1.4gl
$ fglgo cursor1.4go
1Artificial Inteligence Using C
2C Power User's Guide
3Microprocessors
4The Complete Reference 

 Leitura dos livros com número < 5 usando um cursor

Quando se declara um cursor, pode-se definir a intenção de alterar os valores de algumas coluna das linhas lidas. Essa intenção define-se através da declaração "FOR UPDATE" após a instrução SELECT do cursor, como na figura a seguir.

 

DECLARE nome_cursor CURSOR FOR
  SELECT ...
FOR UPDATE 

Definição genérica da declaração de um cursor FOR UPDATE

A alteração faz-se da seguinte forma: 

  1. Declarar um cursor "FOR UPDATE" 

 

DECLARE cq_livros CURSOR FOR
  SELECT numero, nome, edição
    FROM livros
   WHERE numero < 5
FOR UPDATE 

 

  1. Abrir o cursor da forma descrita anteriormente.

 

OPEN cq_livros 

 

  1. Posicionar o cursor na linha que se quer alterar:

 

FETCH cq_livros INTO ... 

  

Invocar a instrução UPDATE para alteração das colunas definidas na cláusula SET do registro corrente do cursor utilizando a cláusula CURRENT OF para indicar a atualização do registro correspondente a linha corrente do cursor. 

 

UPDATE
    SET <colunas a modificar>
    WHERE CURRENT OF cq_livros 

  

Depois da instrução UPDATE o cursor continua posicionado na linha corrente.

 

cursupd.4gl

DATABASE livros
 MAIN
   DEFINE numero LIKE livros.numero,
         nome   LIKE livros.nome,
         edicao LIKE livros.edicao,
         ans    CHAR(1),
         aux    SMALLINT 

   DECLARE cq_livros CURSOR FOR
    SELECT numero, nome, edição
      FROM livros
     WHERE numero < 5
   FOR UPDATE OF edicao 

   OPEN cq_livros
   WHILE aux = 1
      FETCH cq_livros INTO numero, nome, edição

      IF edicao IS NULL THEN
         PROMPT nome CLIPPED,"-Qual a editora para este livro: "
            FOR edição

         UPDATE livros
            SET edicao = edição
          WHERE CURRENT OF cq_livros 

         PROMPT "Quer alterar mais livros (s/n):"
               FOR CHAR ans
         IF ans != "s" THEN
            EXIT WHILE
         END IF
      END IF 

      IF sqlca.sqlcode != 0 THEN
         EXIT WHILE
      END IF

   END WHILE

 END MAIN

$ fglpc cursupd.4gl
$ fglgo cursupd.4go

Artificial Inteligence using C

-Qual a editora para este livro: BERTR
Quer alterar mais livros (s/n): n
$ 

Cursor para alteração ( FOR UPDATE )

11.1.1    Cursores com atributo SCROLL

O mecanismo descrito anteriormente só permite o posicionamento seqüencial do início até  o fim do conjunto ativo de registros do cursor e, quando chegar ao fim só é possível voltar ao início fechando e voltando a abrir o cursor (não garantindo que seja igual ao anterior pois o banco de dados pode ter sido atualizado).

Para evitar estes problemas usa-se os cursores com SCROLL (ou SCROLL cursor). O seu funcionamento é idêntico a um cursor normal sendo declarado, aberto, lido e fechado (DECLARE, OPEN, FETCH e CLOSE). A principal diferença entre os dois tipos de cursores está no fato dos cursores com SCROLL não poderem ser utilizados com a cláusula FOR UPDATE e na instrução FETCH o SCROLL cursor permite utilizar algumas palavras chaves que definem a forma de acesso desejada aos registros ativos num cursor aberto.

As formas de acesso são:

 

FETCH CURRENT

Registro corrente

FETCH PREVIOUS

Registro anterior

FETCH NEXT

Registro seguinte

FETCH FIRST

Primeiro registro

FETCH LAST

Último registro

FETCH RELATIVE

Registro N a partir do registro corrente, respeitando o conjunto ativo do cursor. Quando N for positivo, indica N registros após o registro corrente e quando N for negativo, indica N registros antes do registro corrente

FETCH ABSOLUTE

Elemento N do conjunto de registros ativo do cursor

 

Formas de acesso a um cursor com scroll

Um cursor com SCROLL não pode, no entanto, ser declarado FOR UPDATE.

11.1.2    Cursores para inclusão de dados

Para otimizar a inclusão num banco de dados pode-se utilizar um mecanismo de 4GL chamado cursor de inclusão (INSERT cursor). Através deste mecanismo os dados vão sendo colocados numa área de memória temporária e só são efetivamente gravados no banco de dados quando esta área se encontrar cheia (automaticamente) ou quando o programa assim determinar (instrução FLUSH).

Exemplo proposto de cursores de inclusão:

  1. Declarar um cursor associando-o a uma instrução INSERT.

 

DECLARE cq_livros CURSOR FOR
  INSERT INTO livros
         (numero, nome, edicao)
  VALUES (l_numero, l_nome, l_edicao) 

  

  1. Abrir o cursor. Identico a um cursor normal.

 

OPEN cq_livros 

  

  1. Ler os dados para dentro de determinada variáveis (INPUT, LET).

 

  1. Escrever as variáveis no buffer com o auxilio da instrução PUT.

 

PUT cq_livros 

 

  1. Fechar o cursor. 

 

CLOSE cq_livros 

 

O 4GL força a escrita do buffer no banco de dados sempre que o limite do buffer seja atingido ou o cursor seja fechado.

Depois de uma instrução FLUSH deve-se verificar a variável STATUS ou SQLCA.SQLCODE, pois se for diferente de zero indica um erro de inclusão no banco de dados. Se houver necessidade de saber o número de linhas incluídas pode-se consultar o conteúdo da variável SQLCA.SQLERRD[3], que conterá o número de linhas incluídas no banco de dados.

11.2   Instruções sql dinâmicas

Geralmente as aplicações são escritas de forma a executar tarefas pré-determinadas sobre banco de dados de estrutura constante.

Existem situações em que o programador não sabe, em fase de compilação, as instruções de SQL a serem utilizadas. Algumas dessas situações podem ser as seguintes:

-          Programas interativos, onde os usuários introduzem os parâmetros de uma instrução pelo teclado

-          Programas em que o usuário introduz as instruções de SQL pelo teclado.

-          Programas que devem trabalhar com banco de dados em que a estrutura pode variar.

-          Programas que recebem parâmetros ou instruções a partir de arquivos de configuração.

Uma instrução de SQL, apesar de ser verificada a sua sintaxe durante a compilação, é interpretada em execução, como tal é executado toda a análise gramatical necessária para reconhecer a instrução e as suas cláusulas, sendo que todas estas tarefas são relativamente necessárias.

Em 4GL pode-se gerar dinamicamente instruções de SQL. Isto quer dizer que as instruções podem ser geradas a partir de um string, preparada e posteriormente executada. Para uma instrução preparada é dada uma identificação que permite a sua utilização. Quando uma instrução é preparada, ao ser executada não é feita a análise gramatical da instrução, aconselhando-se utilizar instruções preparadas somente quando utilizar ciclos a fim de diminuir o tempo de execução do programa.

11.2.1    Instruções PREPARE e EXECUTE

Para executar uma instrução SQL dinâmica seguem os seguintes passos:

  1. Colocação de uma string com as instruções SQL desejadas dentro de uma variável tipo CHAR.

 

LET l_sql_delete = “DELETE FROM livros WHERE numero = 10” 

 

Instrução PREPARE

  1. Definição de um identificador de instrução a partir da string que contém a instrução. Esta tarefa e executada pela instrução PREPARE. Há validação da sintaxe da instrução contido na string.

 

PREPARE var_delete FROM l_sql_delete 

 

Instrução PREPARE

  1. Execução da instrução através da instrução EXECUTE.

 

EXECUTE var_delete 

 

Instrução EXECUTE

As instruções SQL enviadas ao PREPARE em tempo de execução não podem fazer referência a variáveis do 4GL, visto que nesta fase o programa já foi compilado.

Para resolver este problema coloca-se o caracter '?' (interrogação) no local onde se deveria encontrar essa variável.

Para atribuir a variável ao ponto de interrogação usa-se a cláusula USING var1, var2,..., varN na instrução EXECUTE. As variáveis são atribuídas ao ponto de interrogação correspondente á sua posição.

11.2.2    Instrução DECLARE para instruções SQL dinâmicas

Se a instrução dinâmica for um SELECT, não se pode utilizar a instrução EXECUTE, devido a possível ambigüidade relativa ao número de linhas retornadas do banco de dados. Neste caso as fases de atuação são:

  1. Preparar a instrução com PREPARE.

 

PREPARE id_expressão FROM expressão_char 

  

  1. Declarar o cursor para essa instrução.

 

DECLARE nome_cursor CURSOR FOR id_expressão 

  

  1. Usar o cursor da mesma forma que se usa com instruções definidas antes da compilação. Se for usado o caracter '?' para substituir variáveis, é necessário usar a cláusula USING na abertura do cursor. 

 

OPEN nome_cursor USING var1 var2 ... varn 

11.3   Exercício – Otimização da função de consulta

A função de consulta foi inicialmente implementada de forma que utilizasse o comando INPUT seguido de um comando SELECT para pesquisa do registro no banco de dados.

Agora a função de consulta deverá ser feita da seguinte forma:

-          implementar consulta utilizando definição de cursor do tipo SCROLL para leitura dos registros da tabela LIVROS, de forma que ao acionar a opção “Consultar” todos os livros sejam consultados, apresentando um livro de cada vez em tela;

-          Criar 2 novas opções de menu (SEGUINTE, ANTERIOR) para permitir a pesquisa do próximo livro ou do livro anterior, conforme os registros carregados na leitura do cursor do tipo SCROLL utilizando as instruções FETCH NEXT e FETCH PREVIOUS.

12    Gestão de janelas – Parte 3

12.1   Instrução CONSTRUCT

Durante a execução de um programa, muitas vezes o usuário necessita fazer uma consulta a várias colunas de uma ou mais tabelas (para fazer alterações em tela, gerar listagens parciais, etc.). Não é viável fazer um programa que preveja todas as possíveis consultas que se pretende efetuar.

A instrução CONSTRUCT edita a form corrente, e a partir dos dados que o usuário informa nas diversas colunas, constrói uma parte de uma instrução SELECT numa variável tipo CHAR que posteriormente pode ser preparada e executada. A variável CHAR preparada pela instrução CONSTRUCT pode ser utilizada como a cláusula WHERE de um SELECT, sem conter a palavra  chave WHERE.

 

CONSTRUCT {BY NAME variavel_char ON lista_de_colunas
           |variavel_char ON lista_de_colunas
      FROM {lista_de_campos|record_tela[[n]].*}{[,...]} 

[{BEFORE CONSTRUCT
 |AFTER CONSTRUCT
 |BEFORE FIELD sublista_de_campos
 |AFTER {FIELD sublista_de_campos|INPUT}
 |ON KEY(lista_de_teclas)}
    instrução
    ...
    [NEXT FIELD nome_do_campo]
    ...
    [EXIT CONSTRUCT]
    ...
    ...
 END CONSTRUCT 

Definição genérica da instrução CONSTRUCT

Esta instrução em relação à cláusula BY NAME, funciona da mesma forma como na instrução INPUT, ou seja, se for utilizada não é necessário indicar o nome das variáveis internas relacionadas às forms a que se pretendem editar.

12.1.1    Exercício – Utilização instrução CONSTRUCT na função “Consultar”

Utilizar a instrução CONSTRUCT para permitir o filtro da consulta de livros. Esta consulta deverá ser implementada antes da definição do cursor tipo SCROLL e o cursor deverá ser criado a partir de um SQL dinâmico em conjunto com a variável resultante do comando CONSTRUCT.

NOTA: Não esqueça que para definição de cursor a partir de SQL dinâmico é necessário utilizar a instrução PREPARE antes da instrução DECLARE do cursor.

12.2   INSTRUÇÃO DISPLAY ARRAY

As vezes uma linha de uma tabela cabe numa linha da tela, e pretender introduzir na tabela várias linhas em seguida (por exemplo vários temas para um mesmo livro). Esta tarefa pode ser executada selecionando várias vezes a opção “incluir” do menu, no entanto, seria muito mais prático introduzir todos os temas de uma só vez, visualizando parte dos anteriores para evitar enganos.

Para tratar este tipo de tela o 4GL possui as instruções DISPLAY ARRAY e INPUT ARRAY, associadas a novas definições nas forms.

 

DISPLAY ARRAY array_de_records TO array_da_tela.*
         [ATTRIBUTE(lista_de atributos)]
 {ON KEY (lista_de_teclas)
     instrução
     ...
     [EXIT DISPLAY]
 ...
 END DISPLAY 

Definição genérica da instrução DISPLAY ARRAY

A instrução DISPLAY ARRAY envia um array de records do programa para a tela, por intermédio de um array de linhas na tela.

O array de records definido no programa pode ser de maior dimensão que o array da tela. Neste caso o usuário pode navegar no array de registros (records) no array da tela (scroll) para cima e para baixo, com as teclas de setas ou, se pretender posicionar-se de uma página a outra, utilizando as teclas F3 e F4 ou as teclas que foram definidas para tal funcionalidade utilizando a instrução OPTIONS (NEXT KEY / PREVIOUS KEY).

Tal como na instrução DISPLAY é possível testar o pressionamento de uma tecla especial, associar atributos á visualização e sair da instrução após determinada situação.

Quando é executada esta instrução, ela fica a espera de instruções do usuário, que, para sair deverá ser pressionar a tecla <ESC> (se estiver definida como ACCEPT KEY).

Geralmente, com esta instrução utiliza-se as funções do 4GL SET_COUNT e ARR_CURR. A função SET_COUNT sempre precisa ser executada antes da instrução DISPLAY ARRAY e serve para informar à instrução a quantidade de linhas que se encontram preenchidas no array de registros. A função ARR_CURR informa qual a linha do array em que o cursor se encontra posicionado antes de pressionar a ACCEPT KEY (geralmente configurada como tecla ESC).

12.3   Exercício – Zoom de editoras

Criar uma função que, quando o usuário digitar um código de editora inválido, abra uma janela com os códigos válidos onde seja permitido o usuário escolher o código desejado. Este código deverá ser retornado para tela principal de cadastro de livros.

Para definição da tela que irá apresentar a lista de editoras deverá ser definida uma nova form para utilização da instrução DISPLAY ARRAY e uma função que irá carregar uma variável do tipo ARRAY com a lista de editoras válidas para apresentar nesta form.

12.4   FORM PARA UTILIZAÇÃO DE DISPLAY ARRAY

Para a utilização das instruções acima definidas é preciso construir uma form que tenha estas capacidades. Assim, na seção SCREEN define-se o número de linhas que se pretende editar de cada vez, cada coluna de cada linha declarada deve ter o mesmo nome de label.

 

SCREEN
 {
 Código  Nome
 ------- ----------------------------------------------------
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 [f000 ] [f001                                              ]
 }
 END 

Seção SCREEN da form de zoom editoras

Como em qualquer form, cada label precisa estar declarado na seção ATTRIBUTES.

 

ATTRIBUTES
 f000 = editoras.codigo;
 f001 = editoras.nome;
END 

Seção attributes da form de zoom de editoras

Os campos com o mesmo label, tem que corresponder a um SCREEN RECORD. Um SCREEN RECORD declara-se na seção INSTRUCTIONS e associa-se ao array as colunas que se pretende editar.

 

INSTRUCTIONS
 DELIMITERS " "
 SCREEN RECORD sr_editoras[8] (editoras.codigo,editores.nome) 

 Seção INSTRUCTION

Colocou-se a cláusula DELIMITERS com espaços para dar a noção ao usuário como se fosse navegação em opções de um menu.

12.5   GESTÃO DA FORM COM DISPLAY ARRAY

A zona do programa em que se faz o INPUT ARRAY deverá ser alterado de forma que se for informado um código inválido, receba um código válido da função utilizada pra visualizar a lista de editores cadastrados.

 

AFTER FIELD edição
    CALL consiste_editora()
    IF status = NOTFOUND THEN
       LET mr_livros.editora = zoom_editora()
       CALL consiste_editora()
    END IF
    DISPLAY BY NAME mr_livros.editora, <variável nome_editora> 

  

A função consiste_editora verifica se foi informado um código de editora válido e apresenta o nome da editora, caso encontre. Se não encontrar a editora ou esta não tiver sido informada será  acionada a função zoom_editora que fará a abertura de uma janela com a form descrita anteriormente onde serão visualizados os códigos existentes de editoras. A função zoom_editora sempre irá retorna um código de editora válido e neste caso pode-se apresentar o código e nome da editora sempre pois estará garantida a validade das informações.

 

OPEN WINDOW w_editoras AT 5, 5 WITH FORM "editoras"
      ATTRIBUTE(BORDER) 

 

Para invocar a instrução DISPLAY ARRAY, é necessário declarar um record no programa para fazer display.

 

DEFINE la_editoras ARRAY[40] OF RECORD LIKE editoras.*,
        numero_editoras INTEGER,
        linha_corrente INTEGER 

 

O array de records serve para colocar os elementos no cursor. A variável numero_editores serve para verificar o numero de editores obtidos. A variável linha_corrente serve para determinar o elemento do array que foi escolhido.

Para visualizar os dados é preciso agora armazená-los no array de records.

 

LET numero_editoras = 1 

 DECLARE cq_editoras CURSOR FOR
  SELECT *
    FROM editores 

 FOREACH cq_editoras INTO la_editoras[numero_editoras].*
    IF status != 0 THEN
       EXIT FOREACH
    END IF 

    IF numero_editoras >= 40 THEN
       ERROR "Editoras a mais"
       EXIT FOREACH
    END IF 

    LET numero_editoras = numero_editoras + 1
 END FOREACH
 FREE cq_editoras 

 LET numero_editoras = numero_editoras - 1 

 

Nesta versão são apenas visualizados as primeiras quarenta editoras.

 

CALL set_count(numero_editoras) 

 DISPLAY ARRAY la_editoras TO sr_editoras.* 

 LET linha_corrente = arr_curr() 

 CLOSE WINDOW w_editoras 

 

Para que a instrução DISPLAY ARRAY funcione corretamente tem de ser informada de quantas linhas se deve visualizar, tarefa que é executada pela função set_count.

A instrução DISPLAY ARRAY é invocada informando a variável array que vai ser apresentada e em que array de tela (SCREEN RECORD).

A função ARR_CURR() é acionada para que se saiba qual o elemento do arrray que o usuário escolheu, para devolver o código selecionado para a tela anterior.

 

RETURN la_editoras[linha_corrente].codigo 

12.6   INSTRUÇÃO INPUT ARRAY

A instrução INPUT ARRAY, tal como a instrução DISPLAY ARRAY serve para manipular forms com arrays de tela.

Esta instrução permite a manipulação de dados através da form para dentro de um array de records.

 

INPUT ARRAY array_de_records [WHITHOUT DEFAULTS]
       FROM array_tela.* [HELP numero_help] 

 [{BEFORE {ROW | INSERT | DELETE
               | FIELD lista_de_campos | INPUT}[,...] 

  |AFTER ROW | INSERT | DELETE
             | FIELD lista_de_campos | INPUT}[,...] 

  |ON KEY(lista_teclas)
      instrução 

 [NEXT FIELD nome_campo]
 ...
 [EXIT INPUT]
 ...
 END INPUT

Definição genérica da instrução INPUT ARRAY

As cláusulas WHITHOUT DEFAULTS, AFTER e BEFORE: INSERT, FIELD e INPUT funcionam da mesma forma que na instrução INPUT, assim como as cláusulas ON KEY, NEXT FIELD, EXIT INPUT.

As cláusulas AFTER ROW e BEFORE ROW permitem executar instruções antes ou depois da edição de uma linha da tela.

12.6.1.1Exercício – Cadastro de temas

Como o cadastro de livro já foi concluído, temos agora que permitir registrar um ou mais temas por livro. Neste caso será desenvolvida uma entrada de dados para cadastro dos temas focados em cada livro (tabela LIVRO_TEMAS).

12.6.2    DEFINIÇÃO DA FORM

Uma form para ser manipulada pela instrução INPUT ARRAY é idêntica a uma form utilizando DISPLAY ARRAY.

 

DATABASE livros
 SCREEN
 {
  codigo | tema
 --------+-----------------------------------------------------
 [f000 ] | [f001                                              ]
 [f000 ] | [f001                                              ]
 [f000 ] | [f001                                              ] 
 [f000 ] | [f001                                              ]
 [f000 ] | [f001                                              ]
 [f000 ] | [f001                                              ]
 }
 END 

 TABLES
 temas

 ATTRIBUTES
 f000 = temas.codigo;
 f001 = temas.nome, NOENTRY;
 END 

 INSTRUCTIONS
   SCREEN RECORD sr_temas[6] (temas.codigo, temas.nome) 

Form de temas

O campo nome foi definido como NOENTRY pois vai servir apenas para informar os respectivos nomes de editoras para cada código informado.

12.6.3    PROGRAMA DE GESTÃO DA FORM DE TEMAS

No menu do programa de entrada de dados de livros será adicionada uma nova opção de menu denominada “Temas”.

 

COMMAND "Temas" "Introdução de temas focados neste livro."
    IF mr_livros.numero IS NULL THEN
       ERROR "Nao há nenhum livro para informar temas."
    ELSE
       CALL temas(mr_livros.numero)
    END IF 

Menu da entrada de dados para livros

Faz-se um teste para verificar se existe algum livro corrente, caso contrário não faz sentido a inclusão de temas de um livro.

A função “temas” vai conter os processamentos necessários ao correto cadastro dos temas com visualização dos temas já cadastrados.

 

FUNCTION temas(l_livro)
  DEFINE l_livro   LIKE livros.numero,
         l_idx_arr INTEGER,
         l_idx_scr INTEGER,
         la_temas ARRAY[15] OF RECORD LIKE temas.* 

 Declaração das variáveis da função temas

O parâmetro livro é repassado na chamada da função com o numero do livro para o qual serão alterados os temas. As variáveis l_idx_arr e l_idx_scr servem como índices de acesso aos arrays respectivamente do programa e da tela. A variável la_temas é o array de registros onde vão ser armazenados os dados relativos a cada tema.

É possível que já existam alguns temas cadastrados para o livro em questão. Neste caso convém consultar os temas já existentes para uma eventual alteração. Se preenchermos o array de registros com os temas existentes, estes irão ser colocados na form quando se chamar a instrução INPUT ARRAY.

  

DECLARE cq_temas CURSOR FOR
  SELECT livro_temas.tema, temas.nome
    FROM livro_temas, temas
   WHERE livro_temas.livro  = l_livro
     AND temas.codigo = livro_temas.tema 

 INITIALIZE la_temas TO NULL
 LET l_idx_arr = 1 

 FOREACH cq_temas INTO la_temas[l_idx_arr].*
    LET l_idx_arr = l_idx_arr + 1
    IF l_idx_arr > 15 THEN
       ERROR "Temas a mais."
       EXIT FOREACH
    END IF
 END FOREACH
 FREE cq_temas 

 LET l_idx_arr = l_idx_arr - 1 

 Preenchimento do array de records com as editoras já existentes

A forma como é preenchido o record é idêntica a utilizada quando se falou na instrução DISPLAY ARRAY.

A abertura da janela com a form é feita com a instrução OPEN WINDOW.

 

OPEN WINDOW w_temas AT 5,7
      WITH FORM "temas" ATTRIBUTE(BORDER) 

 Abertura da janela da form

Tal como no DISPLAY ARRAY é necessário informar a instrução de quantas linhas do array se encontram preenchidas. Neste caso utiliza-se a função SET_COUNT().

 

CALL set_count(l_idx_arr)
 LET int_flag = 0
 INPUT ARRAY la_temas WITHOUT DEFAULTS FROM sr_temas.* 

 Início da instrução INPUT ARRAY

Na instrução diz-se que será feita a entrada de dados no array la_temas utilizando o array de tela sr_temas. A cláusula WITHOUT DEFAULTS é utilizada para que apareçam na tela os temas já existentes no array de records carregados a partir da leitura utilizando o cursor.

 

AFTER FIELD código
    LET l_idx_arr = arr_curr()
    LET l_idx_scr = scr_line()
    LET la_temas[l_idx_arr].nome = get_temas(la_temas[l_idx_arr].codigo)
    IF la_temas[l_idx_arr].nome IS NULL THEN
       ERROR "TEMA “,la_temas[l_icx_arr].código,” INVÁLIDO."
    ELSE
       DISPLAY la_temas[l_idx_arr].nome TO sr_temas[l_idx_scr].nome
    END IF 

Cláusula AFTER FIELD do INPUT ARRAY

Esta cláusula aparece para que depois de informar um código, seja verificada a existência deste código na tabela de temas e se apresente o nome do tema ao lado do código.

A função ARR_CURR () retorna para a variável l_idx_arr o número do elemento do array do programa sobre o qual o cursor está posicionado (linha corrente).

A função SCR_LINE() retorna qual o elemento do array da tela sobre a qual o cursor está posicionado, não esquecendo que podem existir mais elementos no array do programa do que no array de tela pois as instruções fazem scroll automaticamente.

A função GET_TEMAS pesquisa na tabela de temas o nome do tema conforme o código do tema repassado como parâmetro e retorna o seu nome ou NULL se não existir.

Se o código de tema for válido envia-se para a tela o nome retornado na linha corrente de tela, com a instrução DISPLAY TO.

Quando o usuário termina de informar os temas, pressionando ESC (accept key), é feita a exclusão de todos os temas atuais referentes ao livro corrente e depois substituídos por aqueles que se encontram no array de programa, ou seja, os temas que já existiam e os que foram incluídos agora.

 

AFTER INPUT
    IF int_flag = 0 THEN
       DELETE FROM livro_temas WHERE livro_temas.livro = livro 

       FOR pr_idx = 1 TO arr_count()
          INSERT INTO livro temas
          VALUES (l_livro, la_temas[l_idx_arr].codigo)
          IF sqlca.sqlcode <> 0 THEN
             ERROR "Erro na inclusão"
          END IF
       END FOR 

 Atualização da base de dados com os novos temas por livro

Para detectar o fim de inclusão utiliza-se a cláusula AFTER INPUT. A função arr_count() é utilizada para nos informar o número de linhas preenchidas no array da tela.

Neste caso são realizadas 2 instruções SQL para garantir a total atualização das informações no banco de dados. Considerando o conceito de transação de banco de dados, neste caso deveria ser feito o uso de uma transação única para garantir que as 2 instruções fossem executadas com sucesso, caso contrário o processo de atualização dos temas deveria ser desfeito de forma total, retornando a situação anterior a modificação. Esta transação é realizada utilizando-se as instruções BEGIN WORK, COMMIT WORK e ROLLBACK WORK, conforme citado no manual sobre SQL.

 

AFTER INPUT
    #Se usuário confirmou a alteração, pressionando <ESC>
    IF int_flag = 0 THEN
       WHENEVER ERROR CONTINUE
       BEGIN WORK
       DELETE FROM temas WHERE temas.livro = l_livro
       WHENEVER ERROR STOP
       IF sqlca.sqlcode = 0 THEN
          LET l_erro = FALSE
          FOR l_idx_arr = 1 TO arr_count()
             WHENEVER ERROR CONTINUE
             INSERT INTO temas
             VALUES (l_livro, la_temas[l_idx_arr].codigo)
             WHENEVER ERROR STOP
             IF sqlca.sqlcode <> 0 THEN
                ERROR "Erro na inclusão."
                LET l_erro = TRUE
                EXIT FOR
             END IF
          END FOR
          IF l_erro THEN
             WHENEVER ERROR CONTINUE
             ROLLBACK WORK         
             WHENEVER ERROR STOP
          ELSE
             WHENEVER ERROR CONTINUE
             COMMIT WORK
             WHENEVER ERROR STOP
             IF sqlca.sqlcode <> 0 THEN
                ERROR “Erro na efetivação.”
             END IF            
          END IF
       ELSE
          ERROR “Erro na exclusão.”
          WHENEVER ERROR CONTINUE
          ROLLBACK WORK
          WHENEVER ERROR STOP
       END IF
    END IF 

13    RELATÓRIOS (REPORT) EM INFORMIX-4GL

A função do INFORMIX-4GL que permite gerar um relatório (ou uma listagem) é o REPORT. Vamos ver quais as vantagens e a características do gerador de relatórios (REPORT) do INFORMIX-4GL, para extrair informações do banco de dados.

O Report recebe como argumento um conjunto de dados, linha a linha, e devolve a informação formatada. O estudo dos reports de INFORMIX-4GL pode-se dividir em 2 partes:

-          O código do report propriamente dito;

-          O código que o processa - interação com o INFORMIX-4GL (REPORT DRIVER). Este colhe a informação, processa-a e depois envia-a linha a linha para o report. A construção típica de um REPORT DRIVER consiste numa instrução SELECT que busca a informação do banco de dados e associada a um ciclo FOREACH envia para o REPORT linha a linha o resultado do SELECT.

13.1   INTERAÇÃO COM O INFORMIX-4GL

Vamos considerar como exemplo a geração de um report com a informação da tabela de livros:

 

DATABASE livros
 {REPORT com a informação da TABELA DE LIVROS}
 FUNCTION lista_livros()
  DEFINE lr_livro RECORD LIKE livros.* 

  DECLARE cq_livro CURSOR FOR
   SELECT *
     FROM livros
    ORDER BY numero

  DISPLAY "Inicio do REPORT; espere um momento " AT 15,1 

  START REPORT r_livro 

  FOREACH cq_livro INTO lr_livro.*
     OUTPUT TO REPORT r_liv(lr_livro.*)
  END FOREACH
  FREE cq_livro

  FINISH REPORT r_livro

  DISPLAY "Terminou o report. Encontra-se no arquivo livros.out "AT 15,1 

  SLEEP 3
  DISPLAY "" AT 15,1
END FUNCTION 

Exemplo da interação do REPORT com INFORMIX-4GL

 

Prosseguindo com o programa temos as instruções seguintes:

START REPORT

Instrução que permite iniciar o report.

FOREACH

Executa um ciclo de forma a processar a leitura de todas as linhas resultantes da instrução SELECT, executando a instrução que se segue.

OUTPUT TO REPORT

É a instrução que chama o report, propriamente dito, e que lhe passa linha a linha, as linhas resultantes da instrução SELECT (percorre um cursor), sob a forma de argumentos.

END FOREACH

Termina o ciclo do cursor.

FREE

Libera a memória alocada para a declaração do cursor.

FINISH REPORT

Encerra o report.

Em seguida o programa dá ao usuário uma mensagem que terminou o report e informa o arquivo onde este foi gerado. Com exceção das instruções FOREACH e END FOREACH que já foram apresentadas anteriormente, iremos aprofundar um pouco o restante das instruções.

13.2   START REPORT

Esta instrução é usada em geral como a primeira para iniciar um ciclo que processa um report com a instrução OUTPUT TO REPORT.

 

START REPORT nome_report [TO {arquivo | PRINTER | PIPE programa} 

 Sintaxe da instrução START REPORT

Se usar a cláusula TO, o INFORMIX-4GL ignora a instrução REPORT TO na seção OUTPUT do report.

Se usar a cláusula TO <arquivo>, o INFORMIX-4GL escreve o resultado do report nesse arquivo.

Se usar a cláusula TO PRINTER, o INFORMIX-4GL envia o resultado do report para a impressora. A impressora por padrão é a escolhida pelo programa "lpr", no sistema operacional LINUX, podendo ser alterado reinicializando a variável de ambiente DBPRINT.

Por definição de padrões, a cláusula TO PRINTER não é utilizada, pois o envio do relatório para impressora é realizada de outra forma.

13.3   OUTPUT TO REPORT

Esta instrução serve para passar ao report um linha de dados.

 

OUTPUT TO REPORT nome_report(linha_dados) 

 Sintaxe da instrução OUTPUT TO REPORT

Em geral a instrução "OUTPUT TO REPORT" é utilizada dentro de um ciclo que passa dados para o report. A linha_dados pode ser uma ou mais colunas e/ou uma ou mais expressões, separadas por vírgula.

O número de elementos na linha_dados tem que estar de acordo com o número de argumentos da função REPORT chamada.

13.4   FINISH REPORT

Esta instrução indica ao INFORMIX-4GL que o report terminou.

 

FINISH REPORT nome_report 

 Sintaxe da instrução FINISH REPORT

É necessário usar esta instrução para que o INFORMIX-4GL saiba que terminou o processamento do report.

13.5   ESTRUTURA DO REPORT

Começando pela estrutura do REPORT utilizado no exemplo anterior:

 

DATABASE livros 

 REPORT r_livro(lr_relat)
  {r_livro é o report que escreve a informacão da tabela de
   livros e este report é enviado para o arquivo livros.out
  } 

  DEFINE lr_relat RECORD LIKE livros.* 

  OUTPUT REPORT TO "livros.out" 

  LEFT MARGIN 0
  TOP MARGIN 0
  BOTTOM MARGIN 0
  PAGE LENGH 8 

  FORMAT
  ON EVERY ROW
     PRINT lr_relat.numero, 1 SPACE, lr_relat.nome CLIPPED
     PRINT lr_relat.autor,  1 SPACE, lr_relat.editora

 END REPORT 

Exemplo de um REPORT

A primeira instrução do report é a declaração da base de dados. É necessário definir a base de dados para poder entender a cláusula LIKE da instrução DEFINE.

Um report inicia-se com a instrução REPORT que inclui entre parênteses a lista de argumentos.

Esta lista tem de corresponder exatamente à lista de argumentos enviada pela instrução OUTPUT TO REPORT. As variáveis não precisam ter exatamente o mesmo nome enviado pela instrução OUTPUT TO REPORT, no entanto se o tipo das variáveis não for o mesmo, precisam ao menos compatíveis ao ponto de permitir a conversão automática entre tipos distintos. Por exemplo, podemos passar uma frase entre ("") para uma variável do tipo CHAR, mas não podemos passar uma frase com caracteres alfanuméricos para uma variável do tipo INTEGER.

Tal como nas funções FUNCTION ou MAIN, a instrução que se utiliza logo na seqüência é DEFINE para definição de variáveis locais. Neste caso usamos a cláusula LIKE para definir a variável "lr_relat" como RECORD da tabela de livros. As variáveis do report são sempre do mesmo tipo das variáveis selecionadas, sendo que no caso de efetuar alguma alteração na tabela em questão. Exemplo, se mudar a dimensão de uma variável tipo CHAR não é necessário alterar o report, apenas precisa ser compilado.

Continuando no exemplo do REPORT, em seguida existe a seção OUTPUT. Nesta seção é opcional definir o destino da emissão do relatório e qual o formato da folha, isto é, a margem superior, margem inferior, margem esquerda e o número de linhas na página.

A seção FORMAT é a mais importante. Esta seção pode ter as subseções PAGE HEADER para o cabeçalho, PAGE TRAILLER para o rodapé e ON EVERY ROW para as linhas do corpo do relatório. No exemplo acima, existe apenas a última, utilizada para definir como serão escritas as linhas, usando as instruções seguintes:

  • PRINT – imprimir os elementos do RECORD lr_relat.
  • 1 SPACE - para deixar um espaço em branco.
  • CLIPPED – eliminar os espaços em branco do final de um string.

Finalmente o report termina com a instrução END REPORT.

Depois de analisar este exemplo, vamos ver com mais detalhes as diferentes seções.

O report é composto por seções que são formadas por blocos e/ou instruções. As seções são as seguintes e devem de ser escritas segundo a ordem apresentada:

 

REPORT nome_report (ListaArgumentos)
  [DEFINE seção]
  [OUTPUT seção]
  [ORDER BY seção]
  FORMAT seção
 END REPORT 

Seções que constituem um REPORT

As seções DEFINE, OUTPUT e ORDER BY são opcionais; a seção FORMAT é obrigatória.

13.5.1    SEÇÃO DEFINE

A rotina REPORT necessita da seção DEFINE quando recebe argumentos ou quando é necessário o uso de variáveis locais no report. É obrigatório existir uma lista de argumentos nas seguintes condições:

-          Quando há seção ORDER BY no report, pois neste caso é necessário passar todos os valores para cada linha do report.

-          Quando se usam funções de agregação, que dependem das linhas do report, em qualquer parte do report.

-          Quando se usa o bloco de controle EVERY ROW na secção FORMAT, sendo que neste caso é obrigatório passar os valores de cada linha do report.

-          Quando se usa o bloco de controle AFTER GROUP OF, obrigatoriamente deve-se passar pelos menos o parâmetro a que é feito referência.

A seção DEFINE obedece as mesmas regras já vistas no capítulo sobre definição de variáveis.

13.5.2    SEÇÃO OUTPUT

A rotina REPORT pode ou não conter esta seção. A seção OUTPUT controla a dimensão das margens e o comprimento da página. Permite definir o destino do resultado do report, isto é, vídeo, disco, impressora ou para outro processo através de um pipe.

A seção OUTPUT é formada pela palavra OUTPUT seguida de uma ou mais instruções:

 

OUTPUT
 [REPORT TO instrução]
 [LEFT MARGIN valor]
 [RIGHT MARGIN valor]
 [TOP MARGIN valor]
 [BOTTOM MARGIN valor]
 [PAGE LENGHT valor]
 [REPORT TO {"arquivo" | PIPE programa | PRINTER}] 

 Formato da seção OUTPUT

Se a instrução START REPORT tiver a cláusula TO indicando o destino do resultado do report, o INFORMIX-4GL ignora a instrução REPORT TO na seção OUTPUT.

Se o <arquivo> for uma variável, é necessário passá-la como argumento na chamada da função REPORT.

A instrução REPORT TO PRINTER envia o resultado do report para o programa definido pela variável DBPRINT. Se esta variável não estiver definida, então o INFORMIX-4GL envia o resultado para o programa "lpr".

Se quiser enviar o resultado do report para outra impressora que não seja a padrão do sistema, pode-se usar a instrução REPORT TO <arquivo>, que escreve num arquivo podendo depois ser impresso. Pode também usar-se a instrução REPORT TO PIPE para enviar diretamente o resultado para um programa que manda para a impressora correta.

 

OUTPUT
 REPORT TO PIPE "more" 

 Exemplo da instrução REPORT TO da seção OUTPUT

Este exemplo envia o resultado do report para o utilitário "more". Se omitir a instrução REPORT TO na rotina REPORT e se a instrução START REPORT não tiver cláusula TO, o resultado do report vai para o vídeo.

13.5.3    SEÇÃO ORDER BY

A seção ORDER BY é opcional na rotina REPORT. Esta seção especifica a ordem em que os dados devem estar no relatório e a ordem com que o INFORMIX-4GL processará os blocos de controle na seção FORMAT. É necessário o uso desta seção quando:

-          Usar blocos de controle (BEFORE GROUP, AFTER GROUP);

-          Enviar as linhas sem qualquer ordenação;

-          Já foram ordenadas as linhas enviadas, mas pretende-se especificar a ordem exata com que serão processados os blocos de controle. Neste caso usa-se a cláusula EXTERNAL para que as linhas não sejam novamente ordenadas.

A seção ORDER BY tem o seguinte formato:

 

ORDER [EXTERNAL] BY lista_colunas 

 Formato da secção ORDER BY

13.5.4    SEÇÃO FORMAT

Esta seção é obrigatória. É a seção FORMAT que vai determinar a aparência do relatório.

Usa-se os dados repassados à rotina REPORT através da sua lista de argumentos ou dados fornecidos por variáveis globais para cada linha do relatório.

A seção FORMAT tem início com a palavra FORMAT e termina quando a rotina REPORT termina, isto é, com END REPORT.

Existem dois tipo de seção FORMAT. O primeiro e o mais simples contém unicamente a subseção EVERY ROW e não pode conter qualquer outra subseção ou bloco de controle da seção FORMAT.

 

FORMAT EVERY ROW
 END REPORT 

 Exemplo da seção FORMAT mais simples

O outro tipo, mais complexo, da seção FORMAT pode ser formado da seguinte forma:

 

FORMAT
 [PAGE HEADER bloco_de_controle]
 [PAGE TRAILER bloco_de_controle]
 [FIRST PAGE HEADER bloco_de_controle]
 [ON EVERY ROW bloco_de_controle]
 [ON LAST ROW bloco_de_controle]
 [BEFORE GROUP OF N blocos_de_controle]
 [AFTER GROUP OF N blocos_controle]
 END REPORT 

Exemplo da secção FORMAT

A ordem de escrita dos blocos de controle é arbitrária. O número de blocos BEFORE GROUP OF ou AFTER GROUP OF que podem ser utilizados é igual ao número de colunas existentes na seção ORDER BY. Vejamos as subseções que constituem a seção FORMAT:

 

EVERY ROW

Esta instrução usa-se quando se pretende gerar um relatório rapidamente. O relatório consiste unicamente nos dados que são passados como argumentos à rotina REPORT.

Ao usar esta instrução não pode-se usar qualquer bloco de controle.

 

REPORT minimo(lr_livros)
  DEFINE lr_livros RECORD LIKE livros.*
  FORMAT
  EVERY ROW
 END REPORT 

Exemplo de um REPORT muito simples, usando a instrução EVERY ROW

Cada bloco de controle é opcional mas, se não usar EVERY ROW, é necessário incluir pelos menos um bloco de controle na rotina REPORT. Cada bloco de controle inclui pelo menos uma instrução.

Quando se usa BEFORE GROUP OF, AFTER GROUP OF e ON EVERY ROW numa única rotina REPORT, o INFORMIX-4GL processa em primeiro lugar todos os blocos BEFORE GROUP OF, depois o bloco ON EVERY ROW e finalmente todos os blocos AFTER GROUP OF. A ordem com que o INFORMIX-4GL processa os blocos BEFORE GROUP OF e os blocos AFTER GROUP OF depende da ordem definida para as variáveis na seção ORDER BY.

Suponhamos que fosse definida a seguinte ordenação:

 

ORDER BY a, b, c 

Um exemplo da instrução ORDER BY

Então na seção FORMAT poderíamos ter:

 

 BEFORE GROUP OF a
 BEFORE GROUP OF b
 BEFORE GROUP OF c
 ON EVERY ROW
 AFTER GROUP OF c
 AFTER GROUP OF b
 AFTER GROUP OF a 

 Um exemplo da ordem de execução dos blocos de controle

AFTER GROUP OF

Este bloco de controle define as ações (ou instruções) que devem ser executadas depois de ser processado um determinado grupo de linhas. Um grupo de linhas define-se como um conjunto de linhas que têm o mesmo valor para uma determinada coluna que apareça na seção ORDER BY da rotina REPORT ou na cláusula ORDER BY da instrução SELECT.

 

AFTER GROUP OF variável
    instruções 

Formato do bloco de controlo AFTER GROUP OF

O INFORMIX-4GL processa as instruções do bloco AFTER GROUP OF cada vez que a variável mudar de valor.

Pode-se ter tantos blocos AFTER GROUP OF quantas forem as variáveis da seção ou da cláusula ORDER BY.

A ordem em que são escritos estes blocos não tem qualquer importância, pois quando o INFORMIX-4GL terminar o processamento do relatório, executará todos os blocos AFTER GROUP OF antes de executar o bloco ON LAST ROW.

As funções agregadas de grupo dos REPORTS só podem ser usadas nos blocos de controle AFTER GROUP OF.

 

BEFORE GROUP OF

Este bloco de controle define as ações (ou instruções) que devem ser executadas antes de ser processado um determinado grupo de linhas.

 

BEFORE GROUP OF variável
    Instruções 

Formato do bloco de controlo BEFORE GROUP OF

O INFORMIX-4GL processa as instruções do bloco BEFORE GROUP OF cada vez que a variável mudar de valor.

Pode-se ter tantos blocos BEFORE GROUP OF quantas forem as variáveis da seção ou da cláusula ORDER BY.

Estes blocos podem ser usados para limpar variáveis utilizadas para acumular totais ou variáveis de controle.

O INFORMIX-4GL, ao gerar um relatório, começa por executar as instruções destes blocos e só depois executa o bloco ON EVERY ROW.

Pode se usar os blocos BEFORE GROUP OF para se iniciar uma nova página para cada grupo de informação com a instrução SKIP TO TOP OF PAGE.

 

FIRST PAGE HEADER

Este bloco de controle inclui as ações (ou instruções) que devem ser executadas para definir o cabeçalho da primeira página do relatório.

 

FIRST PAGE HEADER
    Instruções 

 Formato do bloco FIRST PAGE HEADER

A instrução TOP MARGIN da seção OUTPUT influencia na linha onde é escrito este cabeçalho.

Não se pode usar a instrução SKIP TO TOP OF PAGE neste bloco.

Se usar a instrução IF..THEN..ELSE neste bloco, então o número de instruções PRINT tem deve ser igual tanto na condição THEN quanto na condição ELSE.

Não se pode usar a instrução PRINT arquivo, para poder despejar o conteúdo de um arquivo texto no FIRST PAGE HEADER.

Pode-se usar este bloco de controle para inicializar variáveis, pois o INFORMIX-4GL executa-o antes de qualquer outro.

 

ON EVERY ROW

Este bloco define as instruções que o INFORMIX-4GL passa para cada linha de dados como argumento à rotina REPORT, ou seja, os dados impressos no relatório são os dados passados como argumentos na função REPORT.

   

ON EVERY ROW
    Instruções 

 Formato do bloco ON EVERY ROW

Se houver blocos BEFORE GROUP OF na rotina REPORT, estes serão processados antes do bloco ON EVERY ROW.

Se houver blocos AFTER GROUP OF na rotina REPORT, estes serão processado depois do bloco ON EVERY ROW.

 

ON LAST ROW

Este bloco de controle define as instruções que devem ser executadas depois de processada a última linha passada à rotina REPORT.

 

ON LAST ROW
    Instruções 

 Formato do bloco ON LAST ROW

O INFORMIX-4GL executa este bloco depois dos blocos ON EVERY ROW e AFTER GROUP OF.

Pode-se usar o bloco ON LAST ROW para apresentar totalizações.

 

PAGE HEADER

Este bloco especifica a informação que vai aparecer no cabeçalho de cada página do relatório.

 

PAGE HEADER
    Instruções 

 Formato do bloco PAGE HEADER

A instrução TOP MARGIN da seção OUTPUT influencia na linha em que vai ser escrito este cabeçalho.

Não se pode usar a instrução SKIP TO TOP OF PAGE neste bloco.

O número de linhas do PAGE HEADER tem de ser o mesmo para todas as páginas. Tem de obedecer ás seguintes regras:

-          Não se pode usar a instrução SKIP valor_inteiro LINES dentro de um ciclo dentro do bloco PAGE HEADER;

-          Não se pode usar a instrução NEED;

-          Se usar a instrução IF..THEN..ELSE neste bloco, então o número de instruções PRINT precisam ser iguais tanto na condição THEN quanto na condição ELSE;

-          Se usar instruções CASE, FOR ou WHILE que contenham a instrução PRINT no bloco PAGE HEADER, deve ser terminada com ";" (ponto e vírgula);

-          Não se pode usar a instrução PRINT <arquivo> para emitir o conteúdo de um arquivo texto no PAGE HEADER.

Pode usar neste bloco a expressão PAGENO para paginar o relatório automaticamente, no cabeçalho.

 

PAGE TRAILER

Este bloco inclui as instruções que se devem executar para definir o rodapé de cada página.

 

PAGE TRAILER
    Instruções 

 Formato do bloco PAGE TRAILER

Não se pode incluir a instrução SKIP TO TOP OF PAGE neste bloco.

A instrução BOTTOM MARGIN, da seção OUTPUT, define a linha em que termina este bloco.

O número de linhas do PAGE TRAILER precisa ser o mesmo para todas as páginas. Tem de obedecer ás regras seguintes:

-          Não se pode usar a instrução SKIP <valor_inteiro> LINES dentro de um ciclo dentro do bloco PAGE TRAILER;

-          Não se pode usar a instrução NEED;

-          Se usar a instrução IF..THEN..ELSE neste bloco, o número de instruções PRINT precisam ser iguais tanto na condição THEN quanto na condição ELSE;

-          Se usar as instruções CASE, FOR ou WHILE que contenham a instrução PRINT dentro do bloco PAGE TRAILER, deve ser terminada com ";" (ponto e vírgula);

-          Não se pode usar a instrução PRINT <arqvuivo> para emitir o conteúdo de um arquivo de texto no PAGE TRAILER;

Pode usar neste bloco a expressão PAGENO para paginar o relatório automaticamente, no rodapé.

 

Instruções

Os blocos de controle da seção FORMAT determinam quando uma ou várias ações ocorrem, enquanto que as INSTRUÇÕES determinam qual a ação a ser realizada. Pode-se considerar dois tipos de instruções:

-          Simples: constituída por uma única instrução;

-          Compostas: constituídas por um grupo de instruções iniciadas pela palavra BEGIN e finalizadas pela palavra END.

 

FOR

A instrução FOR define um ciclo (loop), executando consecutivamente uma instrução simples ou composta, incrementando o índice e controlando a condição de saída. Quando esta instrução se finaliza, passa o controle do programa para a instrução seguinte ao fim do ciclo.

 

FOR i=1 TO 10 STEP 2
    LET a = a * a + 2
    LET b = a * 2
END FOR 

Exemplo da instrução FOR

NOTA:Não se pode usar incrementos negativos .

 

IF THEN ELSE

Esta instrução executa um teste de uma condição e processa, alternativamente, 2 instruções (simples ou compostas) conforme a condição verdadeira ou não.

 

IF paid_date IS NULL THEN
    LET pg1 = "fal"
    LET pgc1 = "ta "
 ELSE
    LET pg1 = paid_date - ship_date
    LET pgc1 = " dias"
END IF 

 Exemplo da instrução IF-THEN-ELSE

NOTA: Pode-se usar até no máximo 20 IF-THEN-ELSE aninhados.

 

LET

A instrução LET atribui um valor a uma variável.

 

LET pg1  = paid_date - ship_date
LET pgc1 = " dias" 

 Exemplo da instrução LET

NEED

Esta instrução faz com que a próxima linha seja impressa na página seguinte, se não estiver disponível na página corrente, o número mínimo de linhas especificado na instrução NEED

 

BEFORE GROUP OF num livro
    SKIP 1 LINE
    NEED 2 LINES
    PRINT COLUMN 01, "LIVRO:",
          COLUMN 10, num_livro USING "<<<<",
          COLUMN 16, nome CLIPPED

 Exemplo da instrução NEED

PAUSE

Esta instrução faz com que um relatório com destino de emissão para o vídeo possa ter pausas para permitir sua visualização na tela. Pressionando <ENTER> pode-se avançar pelo relatório.

 

PAGE TRAILER
    PAUSE "Pressione <ENTER> para continuar" 

 Exemplo da instrução PAUSE

NOTA: Esta instrução não tem nenhum efeito se o relatório for dirigido para uma impressora, arquivo ou uma outra aplicação (pipe).

 

PRINT

Esta instrução imprime informação no vídeo, impressora ou num arquivo, conforme estiver especificado na seção OUTPUT.

 

PRINT COLUMN 10, "Encomenda:",
       COLUMN 21, ord USING "<<<<",
       COLUMN 29, "Total:",
       COLUMN 36, tota1 USING "$$$$$####&.&&",
       COLUMN 53, "Pagamento em:",
       COLUMN 67, pg1, pgc1 CLIPPED

 Exemplo da instrução PRINT

NOTAS:

-          Cada instrução PRINT imprime informações numa linha. Pode-se com um vários PRINT escrever uma única linha, finalizando cada instrução PRINT com ";" (ponto e vírgula);

-          Conforme os tipos de dados, no PRINT, as colunas ocupam espaços previamente fixados.

-          Para controlar o comprimento dos campos no relatório pode-se utilizar as palavras CLIPPED e USING.

 

SKIP

Esta instrução salta um número de linhas em branco especificadas após o SKIP.

 

 SKIP 2 LINES 

 Exemplo da instrução SKIP

SKIP TO TOP OF PAGE

Esta instrução faz com que a próxima linha seja escrita no início da próxima página, forçando o salto de página.

 

WHILE

A instrução WHILE define um ciclo, executando consecutivamente uma instrução simples ou composta, enquanto for verdadeira a condição que segue a palavra WHILE. Quando esta se verifica passa, o controlo do programa para a instrução seguinte ao fim do ciclo.

 

WHILE PAGENO = 1
    LET a = a * a + 2
    LET b = a * 2
END WHILE 

 Exemplo da instrução WHILE

FUNÇÕES AGREGADAS

As funções agregadas possibilitam a execução de operações sobre colunas das linhas de todo resultado de um SELECT, podendo-se ainda escolher entre estas as que satisfaçam determinado critério.

Existe ainda a hipótese de definir funções agregadas só para grupos de quebra, desde que se utilize a palavra GROUP.

A estrutura da instrução para definir uma função agregada é a seguinte:

 

[GROUP] <função agregada>
     OF <coluna ou expressão aritmética envolvendo coluna>
[WHERE <expressão lógica>] 

Formato da instrução para definir funções agregadas

NOTAS:

-          GROUP e WHERE são opcionais;

-          GROUP só pode ser utilizado num bloco AFTER GROUP.

 

Estão disponíveis as seguintes funções agregadas:

 

COUNT

Conta o número total de linhas do resultado ou do grupo e que satisfaçam a cláusula WHERE, se existir.

PERCENT

Executa um função de contagem semelhante a função COUNT, mas dá o resultado em percentagem em relação ao número total de linhas selecionadas.

TOTAL

Acumula o valor da coluna especificada para todas as linhas do resultado ou do grupo e que satisfaçam a cláusula WHERE, se existir.

AVERAGE ou AVG

Calcula o valor médio da coluna especificada para todas as linhas do resultado ou do grupo e que satisfaçam a cláusula WHERE, se existir.

MIN

Calcula o valor mínimo da coluna especificada para todas as linhas do resultado ou do grupo e que satisfaçam a cláusula WHERE, se existir.

MAX

Calcula o valor máximo da coluna especificada para todas as linhas do resultado ou do grupo e que satisfaçam a cláusula WHERE, se existir.

  

 

AFTER GROUP OF cust
    LET totcli = GROUP TOTAL OF total_price
    LET totger = totger + totcli

    PRINT COLUMN 01, "TOTAL CLIENTE:",
          COLUMN 17, totcli USING "$$$$$####&.&&" 

    PRINT GROUP TOTAL OF total_price
          WHERE total_price > 500 USING "$$$$$####&.&&" 

 Exemplos de utilização de funções agregadas

CLIPPED

Esta instrução suprime os espaços em branco a direita do último caracter diferente de espaço em branco dos campos tipo alfanuméricos.

 

BEFORE GROUP OF cust
    SKIP 1 LINE
    NEED 2 LINES

    PRINT COLUMN 01, "CLIENTE:",
          COLUMN 10, cust USING "<<<<",
          COLUMN 16, company CLIPPED 

 Exemplo da instrução CLIPPED

COLUMN

Esta instrução permite imprimir o campo que se segue a partir da coluna especificada.

 

BEFORE GROUP OF cust
    SKIP 1 LINE
    NEED 2 LINES
    PRINT COLUMN 01, "CLIENTE:",
          COLUMN 10, cust USING "<<<<",
          COLUMN 16, company CLIPPED 

 Exemplo da instrução COLUMN

LINENO

Esta expressão contém o número da linha corrente que o REPORT está para imprimir.

 

IF LINENO = 24 THEN
    SKIP TO TOP OF PAGE
END IF 

Exemplo da instrução LINENO

NOTA: Não utilize LINENO em blocos PAGE HEADER ou TRAILER porque não funcionará corretamente.

 

PAGENO

Esta expressão contém o número da página que o REPORT está imprimindo no momento.

 

PAGE HEADER
    PRINT COLUMN 01, "RESUMO DAS ENCOMENDAS POR CLIENTE DE ",
                     inic, " A ", fim,
          COLUMN 70, "Pagina ", PAGENO USING "<<<" 

 Exemplo da instrução PAGENO

TIME

Esta instrução devolve uma frase com o valor da hora corrente que tem o formato "hh:mm:ss", onde hh é horas, mm é minutos e ss é segundos.

 

USING

Esta expressão permite definir o formato de impressão dos campos do tipo numérico e data, a que está associada.

 

PRINT "RESUMO DAS ENCOMENDAS POR CLIENTE DE ", inic, " A ", fim,
       COLUMN 70, "Pagina ", PAGENO USING "<<<"
PRINT COLUMN 10, "Encomenda:",
       COLUMN 21, ord USING "<<<<",
       COLUMN 29, "Total:",
       COLUMN 36, tot1 USING "$$$$$####&.&&",
       COLUMN 53, "Pagamento em:",
       COLUMN 67, pg1, pgc1 CLIPPED 

Exemplos da instrução USING

NOTAS:

-          O conjunto de caracteres que definem o formato precisa estar entre " " (aspas);

-          USING pode ser usado nas instruções PRINT e LET;

-          Se, por acaso, um campo de valor tiver comprimento superior ao espaço definido para a impressão na expressão USING, este será impresso com asteriscos.

 

A seguir descrevem-se os principais caracteres para definição do formato utilizando a expressão USING:

 

Números:

 

*

Imprime asteriscos em vez de zeros não significativos.

&

Mantém os zeros não significativos.

#

Imprime espaços em vez de zeros não significativos.

Imprime um número encostado à esquerda.

,

Este caracter é impresso na posição em que for escrito. Se não existirem números à sua esquerda não é impresso.

.

Este caracter é impresso na posição em que for escrito. Só pode existir um ponto por campo.

"-"

Este caracter é impresso quando o número for negativo.

+

Este caracter imprime "+" quando o número for positivo e "-" quando for negativo.

$

Este caracter é impresso antes do número.

 

Datas

 

dd

Imprime o dia do mês como um número (01 a 31).

ddd

Imprime o dia da semana usando uma abreviatura com 3 letras.

mm

Imprime o mês como um número (01 a 12).

mmm

Imprime o mês usando uma abreviatura com 3 letras.

yy

Imprime o ano como um número de 2 digitos (01 a 99).

yyyy

Imprime o ano como um número de 4 digitos (0001 a 9999).

 

14    Depurador de programa

Uma das tarefas mais trabalhosas e que os programadores menos apreciam é a fase de testes de um programa.

Quanto existem erros internos no programa (geralmente conhecidos como bugs), geralmente recorre-se a técnicas de visualização do conteúdo das variáveis, utilizando instruções básicas (DISPLAY, PROMPT etc.). Depois de testado o programa é necessário retirar todos estes códigos auxiliares (instruções colocadas para descobrir bugs) do programa, para gerar a versão válida para o usuário.

Estas técnicas são geralmente muito trabalhosas e demandam de muito tempo de desenvolvimento e manutenção de um programa.

Para permitir a fácil manutenção e descoberta de erros, foram criados programas de testes chamados debugger(s). Estes programas, geralmente acompanham a execução de um programa e permitem o controle completo do programa durante a sua execução.

No 4GL foi criado o programa INTERACTIVE DEBUGGER para permitir a depuração de programas em execução, também citado como DEBUGGER somente.

O detalhamento mais a fundo desta ferramenta está em um manual a parte.

14.1   Como executar o depurador

O DEBUGGER é executado a partir do sistema operacional, executando o comando “fgldb”, enviando como argumento o nome do programa que se pretende fazer debug.

14.2   Telas do depurador

O depurador tem duas telas:

-          A tela da aplicação. Nesta tela é visualizado o programa tal como é apresentado quando é executado normalmente.

-          A tela de DEBUG, que é constituída por duas janelas: a janela do código fonte e a janela de comandos. Na janela do fonte é visualizado o código do programa utilizado. Na janela de comandos, comanda-se toda a execução e parametrização do DEBUGGER.

 

15    Base de dados de exemplo deste manual

A base de dados utilizada neste manual tem as seguintes características:

Numa primeira visão, verificou-se que é necessário armazenar vários dados sobre cada livro (nome, autor ou autores, número de volumes, tema ou temas do livro). Chama-se a atenção para o fato de um livro poder ter sido escrito por mais de um autor ou incidir sobre vários temas. O nome de um autor ou de um tema geralmente são bastante extensos e são utilizados pelo menos uma vez por livro. Aconselha-se portanto o uso de uma tabela que relacione um código reduzido com o nome.

Pretende-se também que esteja prevista a possível requisição de um livro por eventuais usuários da biblioteca.

15.1   Estrutura da base de dados

 

+-------+             +--------------+

|LIVROS |             | LIVRO_AUTORES|                   +-------+

+-------+    1:n      +--------------+                   |AUTORES|

|numero |<----+------>|    livro     |         n:1       +-------+

|editora|<--+ |       |    autor     |<----------------->| numero|

|  ...  |   | |       |     ...      |                   |  ...  |

|  ...  |   | |       |     ...      |                   |  ...  |

+-------+   | |       +--------------+                   +-------+

            | |

            | |

            | |

            | |                                         

            | |       +-----------+                     

            | |       |LIVRO_TEMAS|                      +-------+

            | |  1:n  +-----------+                      | TEMAS |

            | +------>|   livro   |          n:1         +-------+

            | |       |   tema    |<-------------------->|codigo |

            | |       |    ...    |                      |  ...  |

            | |       +-----------+                      +-------+

            | |       +--------------+

            | |       | REQUISICOES  |              +-----------+

            | |  1:1  +--------------+              | ENTIDADES |

            | +------>|   livro      |      n:1     +-----------+

            |         |   entidade   |<------------>|   numero  |

            |         |     ...      |              |    ...    |

            |         |     ...      |              |    ...    |

            |         +--------------+              +-----------+

            |                                         +----------+

            |                                         | EDITORAS |

            |                    n:1                  +----------+

            +---------------------------------------->|  codigo  |

                                                      |    ...   |

                                                      |    ...   |

                                                      +----------+

 

A tabela livros contém uma linha para cada livro incluído e contém toda a informação relativa a um livro que seja única por livro. A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*numero

integer

Número do livro

nome

char(60)

Nome do livro

traducao

char(60)

Nome do tradutor

volumes

smallint

Número de volumes

paginas

smallint

Número de páginas

editora

char(5)

Número da edição

ano

smallint

Ano de edição

data_entrada

date

Data da compra

sala

char(5)

Sala onde está arrumado

estante

char(5)

Estante onde está arrumado

prateleira

char(5)

Prateleira onde está arrumado

observacoes

char(50)

Observações sobre o livro

                         Estrutura da tabela livros

 

A tabela autores relaciona o nome de cada autor com um código reduzido. Não é permitida a repetição nem do código, nem do livro. A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*numero

serial

Número seqüencial do autor

nome

char(70)

Nome do autor

                    Estrutura da tabela autores

 

A tabela temas identifica determinado tema por um código. Não é permitida a repetição de nomes e de códigos. A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*numero

char(5)

Código de identificação do tema

nome

char(50)

Nome do tema

                       Estrutura da tabela temas

 

A tabela editoras identifica uma editora por um código de editora. Tal como nas tabelas anteriores não há repetições do código e nome de editora. A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*codigo

char(5)

Código de identificação da editora

nome

char(50)

Nome da editora

                      Estrutura da tabela editoras

 

A tabela livro_autores relaciona determinados livros com determinados autores, permitindo que um livro possua mais de um autor, não permitindo linhas com os mesmos códigos de livro e autor. A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*livro

integer

Número do livro

*autor

integer

Número do autor

     Estrutura da tabela livro_autores

 

A tabela livro_temas funciona da mesma forma que a tabela livro_autores, fornecendo a informação sobre os temas focados por livro. A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*livro

integer

Número do livro

*tema

char(5)

Código de identificação do tema

               Estrutura da tabela livro_temas

 

A tabela requisicoes tem informação acerca de quem e quais os livros que estão requisitados por determinada entidade.  A sua estrutura é a seguinte:

 

NOME

TIPO

DESCRIÇÃO

*livro

integer

Número de identificação do livro

*entidade

integer

Identificação da entidade requisitante

                           Estrutura da tabela requisições

A tabela entidades tem os dados gerais de cada entidade. A sua estrutura é a seguinte:

NOME

TIPO

DESCRIÇÃO

*numero

integer

Número de identificação da entidade

nome_reduzido

char(20)

Nome de rápido acesso

nome

char(50)

Nome da entidade

endereco

char(60)

Endereço da entidade

                          Estrutura da tabela entidades

 

  • Sem rótulos