Lendo DBF em AdvPL — Sem DRIVER ou RDD

Introdução

Com o passar do tempo, a utilização de tabelas em formato ISAM, como o DBF e o c-TREE, estão sendo substituídas por uso de tabelas temporárias no Banco de Dados principal das aplicações AdvPL. E, por questões tecnológicas, a utilização do Protheus Server 64 Bits não suporta mais abertura e manutenção de arquivos no formato DBF. Neste post eu compartilho uma forma de ler conteúdo de arquivos DBF em AdvPL, independente do sistema operacional (Windows / Linux ) e independente da Build do Application Server (32 ou 64 bits).

Driver DBF em AdvPL

Em breve será lançada uma build nova do Protheus 12 (ERP Microsiga) chamada “Lobo Guará”, para Windows 64 Bits e Linux 64, não devendo mais haver Build 32 bits, e o novo servidor não terá mais suporte ao uso de arquivos DBF.

Muitas vezes, existem integrações com sistemas legados que ainda usam arquivos no formato DBF. Eu mesmo tenho várias modelagens de sistemas antigos — coisas de mais de 20 anos — todas criadas com o driver DBFNTX do Clipper 5.x, campos memo em formato DBT. Após estudar um pouco a estrutura interna do arquivo DBF, e dos campos MEMO (DBT e FPT), resolvi construir uma classe em AdvPL que seja capaz de abrir e ler os dados de um arquivo DBF usando as funções de baixo nível  de arquivo do AdvPL (FOpen, FRead,FSeek).

Classe ZDBFTABLE

A implementação da classe foi feita partindo da premissa que todas as operações de leitura da tabela sejam feitas através de métodos, que foram publicados com o mesmo nome das funções de baixo nível de arquivo de dados do AdvPL — por exemplo DbSkip(), DBGotop(), FieldGet().

A primeira versão operacional suporta leitura de arquivos nos formatos dBASE III/Foxpro/Foxbase+/Clipper, com campos MEMO em arquivo DBT e/ou FPT. Não há suporte a índices ou filtros, todos os registros são lidos — inclusive os registros deletados logicamente (ou seja, marcados para deleção). Os dados são lidos na ordem física de registro — ou Recno(). Os métodos de navegação ISAM e de leitura implementados são:

  • DBGoBottom() — Posiciona no último registro da tabela.
  • DBGoTop() — Posiciona no primeiro registro da tabela
  • DBSkip(nRecs)  — Avança ou volta um ou mas registros
  • DBGoto(nRecno) — Posiciona direto em um registro pelo número.

O construtor do objeto — método New() — recebe como parâmetro o nome do arquivo DBF no disco a ser aberto. O arquivo é aberto apenas para leitura usando o método Open(), que já posiciona a tabela no primeiro registro, e fechado usando o método Close().  A recuperação de dados e de status dos registros é feita usando os métodos:

  • FieldGet(nCol) — Recupera o valor de uma coluna da linha atual
  • FieldPos(cCol) — Recupera o número da coluna a partir do nome
  • EOF() — Retorna .T. caso o ponteiro de registros não esteja apontando para um registro. Isto normalmente acontece quando a tabela está vazia ou quando houve a movimentação de ponteiro para próximo registro, e você já estava no último registro da tabela.
  • BOF() — Retorna .T. caso a ultima movimentação de registro tentou ler antes do primeiro registro da tabela
  • Recno() — Retorna o número do registro atual posicionado da tabela. Caso ela esteja em EOF(), é retornado o número do último registro mais um.
  • Deleted() — Retorna .T. caso o registro atualmente posicionado esteja deletado.

Outros métodos para recuperar propriedades da tabela são:

  • LastRec() e/ou RecCount() — Retornam o número do último registro da tabela. Como os registros no DBF são numerados a partir do número 1, ele também representa o número total de registros da tabela.
  • DBStruct() — Retorna um Array contendo a estrutura do arquivo DBF, com 4 colunas: Nome do campo, Tipo do Campo, Tamanho do campo e quantidade de decimais.
  • Header() — Retorna o tamanho em bytes do cabeçalho do arquivo DBF
  • RecSize() — Retorna o tamanho em bytes ocupado no arquivo DBF para uma linha de dados — Equivale a soma do tamanho de todas as colunas mais um (Coluna interna reservada para marcar um registro como deletado.)
  • LUpdate() — Retorna a data da última atualização de registro do arquivo DBF, gravada no Cabeçalho do arquivo.

Fonte de Exemplo

Sem maiores delongas, segue abaixo o fonte “LeUmDbf.prw”, com o exemplo de uso da classe ZDBFTable(). Ela abre uma tabela DBF, mostra informações sobre a tabela e a estrutura de dados, e o primeiro registro ( RECNO 1 ) da tabela no log de console.

#include  'protheus.ch'

User Function LeUmDBF()
Local oDBF
Local nCampos, aStru
Local cFile := '\system\sx3990.dbf'
Local nI

// Cria o objeto para leitura do arquivo 
oDBF := ZDBFTABLE():New(cFile)

// Faz a abertura. Em caso de falha, 
// aborta a aplicação obtendo os detalhes do erro
// usando o método GetErrorStr()
If !oDBF:Open()
	UserException( oDBF:GetErrorStr() )
Endif

// Mostra no log de console alguns dados sobre o arquivo aberto 
conout(replicate('=' ,79))
conout("Database File Name : " + cFile )
conout("Database File Type : " + oDBF:GetDBType()+ " => "+ oDBF:GetDBTypeStr())
conout("Last Update .......: " + dtoc(oDBF:LUpdate()) )
conout("Record Count ......: " + cValToChar(oDBF:RecCount()))
conout("Header Size .......: " + cValToChar(oDBF:Header()))
conout("Record Length......: " + cValToChar(oDBF:RecSize()))

// Recupera a estrutura da tabela 
aStru := oDBF:DbStruct()

// Verifica quantos campos tem a tabela 
nCampos := len(aStru)

// Mostra a estrutura de campos no console
conout("")
conout("--- Table Structure --- ")

For nI := 1 to nCampos
	
	conout("Field ["+aStru[nI][1]+"]"+;          // Nome
          " Type ["+aStru[nI][2]+ "]"+;          // Tipo (CNDLM)
          " Size ["+Str(aStru[nI][3],3)+"]"+;    // Tamanho
          " Dec ["+Str(aStru[nI][4],2)+"]")     // Decimais
	
Next

// Mostra o primeiro registro da tabela, e detalhes do registro
// Caso a tabela esteja vazia, BOF() e EOF() retornam .T. 

conout(replicate('-' ,79))
conout("RECNO() ...... "+cValToChar(oDBF:Recno()) )
conout("BOF() ........ "+cValToChar(oDBF:Bof()) )
conout("EOF() ........ "+cValToChar(oDBF:Eof()) )
conout("DELETED() .... "+cValToChar(oDBF:Deleted()) )
conout("")
For nI := 1 to len(aStru)
	conout(oDBF:FieldName(nI)+" => " +;
               "["+cValToChar(oDBF:Fieldget(nI))+"]" )
Next
conout("")

// Fecha a tabela 
oDBF:Close()

// Limpa / Libera o Objeto 
FreeObj(oDBF)

Return

Desempenho

Não é uma API em C/C++, mas o desempenho não ficou nada mal. Usando uma tabela de 44 colunas e 1660 bytes por registro (ou linha), o programa leu sequencialmente em média 5 mil linhas por segundo. Este desempenho pode ficar ate dez vezes mais lento, quando o arquivo a ser lido estiver sendo acessado por um compartilhamento de rede por exemplo — não têm almoço grátis.

Quer usar esta classe ?

Para usar esta classe, pegue o código fonte atualizado no GITHUB — arquivo “zDBFTable.prw” – – e fique a vontade. Apenas mantenha o cabeçalho do fonte com as informações da autoria do código. Simples assim 😀

Como ela acessa o arquivo diretamente no disco, ela funciona para qualquer Build do Protheus, 32 e 64 Bits, Windows e/ou Linux, e como as funções utilizadas são arrox-com-feijão, esse fonte pode ser usado em versões anteriores do Protheus Server, lançadas a pelo menos nos últimos 10 anos.

Conclusão

Para uma primeira versão de leitura, eu acho que já dá pra fazer bastante coisa. Ao usar a classe, use sempre o acesso pelos métodos, e não diretamente as propriedades, e também não use os métodos de uso interno — prefixados com “_”. Desta forma é possível eu continuar evoluindo esta classe sem impacto nos fontes que a utilizam. O fonte da classe já está no GITHUB, no próximo post nós vamos entrar dentro dos detalhes da implementação do DBF Driver em AdvPL!

Desejo a todos um ótimo uso da classe ZDBFTABLE, e TERABYTES DE SUCESSO !!!

Referências

 

Um comentário sobre “Lendo DBF em AdvPL — Sem DRIVER ou RDD

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