CRUD em AdvPL – Parte 06

Introdução

Neste post, vamos dar uma incrementada na Agenda, acrescentando 3 novos campos —  FONE1, FONE2 e EMAIL — fazendo mínimas alterações no código, e alterando a estrutura da tabela AGENDA no Banco de Dados.

Inserindo novos campos na Agenda

Pode parecer complicado, mas algumas partes do programa Agenda.PRW já foram criadas pensando em haver mudanças ou novas implementações. Para acrescentarmos mais campos na tabela de Agenda, vamos aproveitar a função separada de criação da tabela e índices, para também verificar e alterar a estrutura da tabela atual caso ela já exista sem estar com nas novas colunas.

Primeira parte – Interface

Acrescentar os novos campos na interface é a parte mais simples. Criamos novos objetos de GET, colocamos eles nos Arrays de controle, criamos as posições de interface, aumentamos 50 pixels na altura da janela, deslocamos todos os botões de navegação, confirmar e voltar 50 pixels para baixo, e declaramos as variáveis a serem usadas. Como a passagem de parâmetros para as funções de controle sempre passam o Array com os objetos GET, nenhuma passagem de parâmetros adicional foi necessária.

Local cFone1 := Space(20)
Local cFone2 := Space(20)
Local cEmail := Space(40)
// Novos campos inseridos em 07/10
@ 125,60 GET oGet9 VAR cFone1 PICTURE "@!" SIZE CALCSIZEGET(20),12 OF oPanelCrud PIXEL
@ 140,60 GET oGetA VAR cFone2 PICTURE "@!" SIZE CALCSIZEGET(20),12 OF oPanelCrud PIXEL
@ 155,60 GET oGetB VAR cEMAIL PICTURE "@!" SIZE CALCSIZEGET(40),12 OF oPanelCrud PIXEL
// Novos campos inseridos em 07/10
aadd( aGets , {"FONE1" , oGet9 , space(20) } )
aadd( aGets , {"FONE2" , oGetA , space(20) } )
aadd( aGets , {"EMAIL" , oGetB , space(40) } )

Segunda parte – tabela AGENDA

O programa trabalha com uma tabela de dados criada em um Banco de Dados relacional acessado pelo DBAccess. Originalmente estes campos não existiam na tabela, não há alteração, apenas acrescentar campos novos. Para esta tarefa, vamos usar a função TCAlter(), e mexer apenas na função de criação e abertura da tabela AGENDA — no nosso caso, a função OpenAgenda(), vide novo fonte abaixo:

STATIC Function OpenAgenda()
Local nH
Local cFile := "AGENDA"
Local aStru := {}
Local aDbStru := {}
Local nRet

// Conecta com o DBAccess configurado no ambiente
nH := TCLink()

If nH < 0
  MsgStop("DBAccess - Erro de conexao "+cValToChar(nH))
  QUIT
Endif


// Cria o array com os campos do arquivo 

aadd(aStru,{"ID" ,"C",06,0})
aadd(aStru,{"NOME" ,"C",50,0})
aadd(aStru,{"ENDER" ,"C",50,0})
aadd(aStru,{"COMPL" ,"C",20,0})
aadd(aStru,{"BAIRR" ,"C",30,0})
aadd(aStru,{"CIDADE","C",40,0})
aadd(aStru,{"UF" ,"C",02,0})
aadd(aStru,{"CEP" ,"C",08,0})

// Novos campos inseridos em 07/10
aadd(aStru,{"FONE1" ,"C",20,0})
aadd(aStru,{"FONE2" ,"C",20,0})
aadd(aStru,{"EMAIL" ,"C",40,0})

If !tccanopen(cFile)

  // Se o arquivo nao existe no banco, cria
  DBCreate(cFile,aStru,"TOPCONN")

Else

  // O Arquivo já existe, vamos comparar as estruturas
  USE (cFile) ALIAS (cFile) EXCLUSIVE NEW VIA "TOPCONN"
  aDbStru := DBStruct()
  USE

  If len(aDbStru) <> len(aStru)
    // O tamanho está diferente ? 
    // Vamos alterar a estrutura da tabela
    // informamos a estrutura atual, e a estrutura esperada
    If !TCAlter(cFile,aDbStru,aStru)
      MsgSTop(tcsqlerror(),"Falha ao alterar a estrutura da AGENDA")
      QUIT
    Endif
    MsgInfo("Estrutura do arquivo AGENDA atualizada.")
  Endif

Endif

If !tccanopen(cFile,cFile+'_UNQ')
  // Se o Indice único da tabela nao existe, cria 
  USE (cFile) ALIAS (cFile) EXCLUSIVE NEW VIA "TOPCONN"
  nRet := TCUnique(cFile,"ID")
  If nRet < 0 
    MsgSTop(tcsqlerror(),"Falha ao criar índice único")
    QUIT
  Endif
  USE
EndIf

If !tccanopen(cFile,cFile+'1')
  // Se o Indice por ID nao existe, cria
  USE (cFile) ALIAS (cFile) EXCLUSIVE NEW VIA "TOPCONN"
  INDEX ON ID TO (cFile+'1')
  USE
EndIf

If !tccanopen(cFile,cFile+'2')
  // Se o indice por nome nao existe, cria
  USE (cFile) ALIAS (cFile) EXCLUSIVE NEW VIA "TOPCONN"
  INDEX ON NOME TO (cFile+'2')
  USE
EndIf

// Abra o arquivo de agenda em modo compartilhado
USE (cFile) ALIAS AGENDA SHARED NEW VIA "TOPCONN"

If NetErr()
  MsgStop("Falha ao Abrir a Agenda em modo compartilhado.")
  QUIT
  Return .F. 
Endif

// Liga o filtro para ignorar registros deletados 
SET DELETED ON

// Abre os indices, seleciona ordem por ID
// E Posiciona no primeiro registro 
DbSetIndex(cFile+'1')
DbSetIndex(cFile+'2')
DbSetOrder(1)
DbGoTop()

Return .T.

Novo comportamento do fonte

A parte nova e interessante é avaliar a estrutura da tabela caso ela já exista. Usamos a função DBStruct() após abrir a tabela para verificar qual é a estrutura atual da tabela no Banco de Dados. E, na memória, verificamos o tamanho desta estrutura com o array aStru, que contém a lista de campos com a estrutura atual (nova) da tabela.

Caso os arrays estejam diferentes, a tabela existente no SGDB precisa de alteração para contemplar os novos campos. Neste caso, com a tabela FECHADA, chamamos a função TCAlter(), informando o nome da tabela a ser alterada, o array com a estrutura atual da tabela segundo o banco de dados, e o array com a nova estrutura.

Internamente, a função TCAlter() vai verificar as diferenças entre as estruturas — que no caso serão apenas a adição de novos campos — e o DBAccess vai definir a sequência de operações que serão submetidas ao Banco de Dados para acrescentar estas colunas,

Logo, no primeiro acesso ao fonte, as estruturas estarão diferentes, e o programa vai executar a TCAlter() para inserir os novos campos. Em uma segunda execução, as estruturas já terão o mesmo tamanho, e esta operação não será mais necessária.

Demais proteções

Ainda faltam no fonte algumas proteções básicas, como por exemplo:

  • Proteger a rotina de abertura de tabela com um MUTEX, para evitar que dois processos tentem ao mesmo tempo fazer a criação ou alteração da tabela, bem como a criação dos índices.
  • Proteger as tentativas de abertura de modo EXCLUSIVE da tabela para manutenção, verificando após cada tentativa se a tabela foi realmente aberta, verificando o retorno da função NETERR(), ou verificando o alias atual usando a função ALIAS().

Outros tipos de alteração estrutural

A função TCAlter() apenas repassa a tabela e as estruturas ao DBAccess, que avalia de acordo com o tipo do banco de dados em uso quais as etapas necessárias para fazer a tabela partir da estrutura atual para chegar na nova estrutura informada. A comparação entre as estruturas é feita baseado no nome do campo, e identifica os seguintes casos:

  1. Inclusão de novo campo — o campo existe no segundo array mas não existe no primeiro.
  2. Alteração de tipo de campo — o campo existe nos dois arrays, mas o tipo do campo está diferente. A troca de tipo de um campo numérico inteiro (sem decimais) para caractere realiza internamente a conversão dos dados, sem haver perda do conteúdo. Qualquer outra troca de tipo será tratada internamente como se a coluna fosse eliminada e criada novamente com o tipo novo, com seu conteúdo vazio (default).
  3. Alteração de tamanho de campo — o campo existe nos dois arrays, mas o tamanho foi aumentado ou diminuído — alguns bancos de dados não suportam que uma operação destas seja feita diretamente, principalmente a redução do tamanho do campo. Nestes casos, o DBAccess internamente realiza uma sequencia de etapas — de acordo com o banco de dados eu uso — para no final do processo conseguir fazer a alteração mantendo os dados originais da coluna. Em alguns bancos, pode ser necessário que o DBAccess faça um BACKUP interno da tabela, crie ela novamente com a nova estrutura, e importe novamente os dados da tabela de Backup no formato adequado.
  4. Exclusão da coluna – o campo existe no primeiro Array, mas não existe no segundo.

Conclusão

O programa de CRUD já está ficando mais esperto do que a sua primeira versão, e ainda existem muitas possibilidades de melhoria. Não há melhor aprendizado do que ter um bom programa de exemplo nas mãos, e entender por quê ele precisa evoluir, e como podemos fazer isso. Aguardem mais surpresas e recursos nos próximos posts do CRUD.

Desejo novamente a todos TERABYTES de SUCESSO !!!!

Referências

 

 

 

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s