As funções básicas de acesso a dados do AdvPL (Acesso a Banco de Dados - Funções genéricas) encapsulam as tabelas de dados das RRDs (Drivers e RDDs) permitindo dois modos de acesso ( exclusivo / compartilhado ), e um mecanismo de bloqueio total e parcial para atualização de registros em tabelas abertas em modo compartilhado por mais de um processo. Uma tabela de dados deve ser aberta pela aplicação para ser possível ler, incluir e alterar dados. A abertura de qualquer tabela deve especificar um ALIAS – ou "apelido" : Trata-se de uma string que identifica a tabela aberta dentro do processo atual. Através do ALIAS da tabela, podemos endereçar as colunas da tabela de modo direto ( ALIAS->CAMPO ) para leitura e atribuição de valores, e/ou chamar funcões que devem atuar neste ALIAS, usando a sintaxe ALIAS->(FUNCAO()). Uma função chamada sem informar o ALIAS atua sobre a área de trabalho atual – que corresponde ao último ALIAS aberto, ou ao último ALIAS selecionado através da função DBSelectArea. Como podemos manter no mesmo processo múltiplas tabelas abertas em múltiplos drivers ou RDDs, cada abertura de tabela exige o nome de um ALIAS para esta tabela, para ser possível referenciá-la. A criação, abertura e fechamento de uma tabela são feitas através das funções abaixo:
- DBCreate – Cria uma nova tabela usando a RDD informada, com o nome e estrutura informados comom parâmetro.
- DBUseArea – Abre uma tabela sob um determinado ALIAS usando uma RDD informada como parämetro
- DBCloseArea – Fecha um ALIAS no processo atual.
As tabelas são abertas em um modo de navegação ISAM, onde o alias aberto está sempre posicionado em um determinado registro da tabela ( chamado de registro corrente ), ou em EOF() – final de arquivo , registro em branco – e é permitido criar e abrir um ou mais índices, e é possível posicionar em qualquer registro da tabela usando as funções de navegação de dados do ISAM:
- DBGoTo – Posicionamento através do identificador físico de registro – ou RECNO
- DBGoTop - Posiciona no primeiro registro da ordem de índice selecionada
- DBGoBottom – Posicional no último registro da ordem de índice selecionada.
- DBSkip – posiciona por default no próximo regisrtro da ordem de indice selecionada. Pode receber um parametro numérico, indicando a quantidade de registros para mover o ponteiro de navegação. Pode receber um valor negativo, indicando que a navegação deve seguir em direção do topo da tabela (registros anteriores ao atual).
- DBSeek – posiciona no registro cuja expressao de indice corresponda ao valor informado como parametro
Existem funções auxiliares para determinar o estado ou registro atual da tabela e o estado da última operação executada, a seguir:
- Bof – Quando .T., indica que uma navegação para um registro anterior atingiu o topo (ou primeiro registro visivel) da tabela.
- EOF – Quanto .T., indica que a tabela está em "fim de arquivo" – isso indica por exemplo que a última operação de navegação ultrapassou o último registro da tabela.
- Recno - Retorna o número do identificador sequencial deste registro. Caso o ALIAS esteja em EOF(), ela retorna um numero maior que o último identificador físico de registro
- LastRec - Retorna o número do maior identificador de registro (recno) da tabela. Caso a tabela n"ao tenha nenhum registro, retorna 0 (zero).
- IndexOrd - Retorna o numero da ordem do indice aberto atualmente selecionado. Retorna 0 (zero) caso a ordem atual seja a ordem de inserção fisica de registros (ordem de RECNO).
- IndexKey - Retorna a expressao Advpl de indexação da ordem atualmente selecionada, ou da ordem informada como parämetro
- DBFilter - Retorna a expressão AdvPL usada para determinar a visibilidade lógica (filtro), caso algum filtro esteja em uso.
- NetErr - Retorna .T. caso a ultima operação ISAM de abertura de tabela (DbUseArea) falhou devido a restrição de acesso de abertura de tabela no modo informado – Por exemplo, tentar abrir uma tabela em modo exclusivo quando a mesma já está aberta em modo compartilhado não gera um erro em AdvPL, para verificar se a operação de abertura foi realizada com sucesso, usamos a função NetErr()
- Deleted – Retorna .T. caso o registro atual esteja marcado logicamente como "deletado" e não seja utilizado.
Para realizar operações de inclusão, atualização e eliminação de registros, utilizamos as funções a seguir:
- DBAppend – Insere um novo registro na tabela. O registro inicialmente é criado na memória com os valores default de acordo com o tipo do campo ( Campos do tipo Caractere contém uma string com N espaços em branco, onde N é o tamanho do campo na tabela, campos do tipo "D" Data AdvPL inicializados com uma data vazia, tipo L (lógico) contém o valor .F. (falso), campo numérico contém 0 (zero), e campo "M" Memo contém uma strig vazia (""). O novo registro inserido permanece automaticamente bloqueado (em lock) pelo processo atual.
- DBDelete – Marca um registro para ser "deletado", tornando ele invisível para as operações de navegação ISAM.
- DBRLock / RLock– Tenta bloquear um registro para permitir o processo atual realizar alterações em seu conteúdo.
- FLock – Tenta bloquear a tabela inteira, para evitar que outros processos obtenham bloqueio de registros e somente o processo atual consiga alterar registros na tabela.
- DBRUnlock / DBUnlock – Realiza o bloqueio de registro(s) bloqueados para alteração.
- DBCommit – Envia as alterações de dados pendentes em um ALIAS para o driver/RDD (não tem relação com transacionamento).
A navegação nos dados de uma tabela ISAM estã sujeita a aplicação de filtros de visibilidade lógica. Isto singifica que, após definido um filtro de visualização de dados, a proxima operação de navegação nos dados posicionará no próximo registro da ordem em uso atual que atenda a condição de filtro. Um filtro pode ser qualquer expressão AdvPL que referencie os campos da tabela e retorne .T. (veradeiro) caso o registro deva ser logicamente visivel pela aplicação. As funções e comandos abaixo lidam com filtros:
- DBSetFilter – aplica um filtro de visibilidade logica
- DBClearFilter – limpa um filtro previamente definido
- SET DELETED ON – Habilita o filtro de registros logicamente marcados para deleção (comportamento DEFAULT), para que estes não sejam visiveis nas operações de navegação ISAM.
- SET DELETED OFF – Desliga o filtro de registros marcados para deleção, para que os mesmos SEJAM visíveis nas operações de navegação ISAM.
Exstem ainda funções genéricas que atuam sobre todos os ALIAS abertos no processo atual da aplicação, como por exemplo:
- DBCloseAll – Fecha todos os ALIAS abertos do processo atual, para todas as RDDs
- DBCommitAll – Garante que todos os ALIAS abertos no processo atual realizaram as alterações de regitro pendentes.
- DBClearAllFilter – Limpa todas as expressões de filtro para todas as tabelas / ALIAS abertos.
- DBUnlockAll – Solta todos os registros bloqueados de todos os ALIAS abertos no processo atual.
Estrutura da tabela e tipos de campos
O conceito de RDDs parte da premissa que as tabelas de dados devem armazenar valores compativeis com os tipos de variáveis AdvPL. Por isso, ao criarmos uma tabela através da função DBCreate, informamos um array com o nome, tipo , tamanho e numero de casas decimais dos campos que devem compor a estrutura da tabela. Os tipos podem ser "C" caractere, "N" numérico , "D" Data AdvPL, "L" lógico ou booleano, e "M" Memo – tamanho variável. A quantidade e tamanho máximo de campos e índices que podem ser usadas em uma tabela dependem do Driver e/ou RDD utilizados. Alguns drives inclusive exigem que, quando usado campo(s) do tipo "M" memo, estes sejam os últimos campos da estrutura – como é o caso das RDDs CTREE.
Modo de Abertura de tabelas
A função DbUseArea permite duas configurações de acesso a uma tabela : Modo de compartilhamento e modo de edição. Os modos de compartilhamento podem ser SHARED (compartilhado) ou EXCLUSIVE (excclusivo). Uma tabela aberta no modo compartilhado pode ser aberta em modo compartilhado por outros processos, enquanto uma tabela aberta em modo exclusivo nao pode ser aberta por nenhum outro processo, independente do modo escolhido. Somente é possível abrir uma tabela em modo exclusivo se apenas o seu processo está abrindo a tabela. Somente é possível abrir a tabela em modo compartilhado se nenhum outro processo abriu a tabela em modo exclusivo. Quanto ao modo de edição, é possível abrir uma tabela em modo de edição (default), ou em modo READONLY (somente leitura). Um ALIAS de uma tabela aberta em modo de somente leitura não aceita operações de bloqueio ou alteração de dados. Uma tabela aberta em modo EXCLUSIVE não precisa usar nenhuma função de bloqueio de registro para realizar alterações.
Ordenação de registros
Uma tabela aberta sem utilizar nenhum índice usa a ordenação do identificador de registro (ou RECNO). Trata-se de um identificador numérico único, sequencial, iniado em 1, que representa a ordem cronológica de inserção de registros. Quando criamos, abrimos ou selecionamos um índice para uso – vide funções DBCreateIndex, DBSetIndex e DBSetOrder – a ordem de navegação de registros corresponde a ordem binária ascendente da expressão de indice – calculada a partir dos campos informados na chave do índice em uso. Somente podemos usar a função DBSeek() se existe um indice aberto e selecionado para o ALIAS em questão.
Acesso compartilhado e atualização de registros
Uma tabela aberta em modo compartilhado de edição comporta-se como um cursor dinâmico no Banco de Dados, ordenado pela expressão do índice em uso, com visibilidade lógica de registros total ou limitada por uma expressão de filtro. Uma operação de atualização de dados de um determinado registrro da tabela envolve as etapas de posicionamento no registro, obtenção de um bloqueio exclusivo (ou LOCK de registro), e a atualização dos valores dos campos deste registro endereçando o campo na forma ALIAS->CAMPO e usando o operador de atribuição do AdvPL ( := ) , ou ainda a função FieldPut(). Já a leitura de um valor de um campo de um registro da tabela requer apenas posicionar no registro e obter seu valor usando a expressao ALIAS->CAMPO. Nas operações de leitura do AdvPL não existe a operação de bloqueio para leitura ou bloqueio compartilhado. Qualquer processo pode obter o valor atual armazenado em um registro na base de dados, recuperado pela aplicação no momento do posicionamento do registro, inclusive se um determinado regisrtro possui um bloqueio (lock) para atualizacão. O valor retornado será sempre o último valor atualizado na base de dados.
Deleção lógica de registros
Por compatiblidade, as RDDs implementadas no AdvPL trabalham com uma implementação diferenciada de eliminar registros. A eliminação de um regitro em particular deve primeiro obter o bloqueio deste regitro, para então ser marcado para deleção através da função DBDelete(). A eliminação física deste registro somente é realizada pelo driver ou RDD quando executado o comando PACK – que exige que a tabela em questão seja aberta em modo exclusivo. Por default qualquer registro marcado para deleção não ]e logicamente visivel para as operações de navegação ISAM – exceto para a função DBgoto().
Utilização de Transacionamento
Alguns drivers ou RDDs, como a "TOPCONN" por exemplo suporta operações com transacionamento. Uma vez iniciada a transação, novos registros e alterações feitas nas tabelas abertas pela RDD somente serão efetivadas na base de dados após a finalização com sucesso (ou COMMIT) da transação. A visibilidade dos dados alterados durante a transação por outro processo depende do nivel de isolamento (DBAccess - Isolation Level) utilizado para o Banco de Dados. A maioria dos bancos de dados, por questões de adequação de comportamento com as aplicações AdvPL usam um nivel de isolamento que permite outros processos ler novos registros e conteúdos de registros alterados dentro de um processo transacionado, mesmo antes da transação ser efetivada (ou comitada) no banco de dados. A RDD TOPCONN permite o controle explicito de transacionamento através da função TCCommit(). O ambiente AdvPL usado por exemplo no ERP Microsiga delimita um bloco transacionado usando os comandos BEGIN TRANSACTION e END TRANSACTION.
Filtros e Visibilidade logica de registros
Uma tabela aberta em modo ISAM pelo AdvPL está sujeita a um comportamento de visibilidade lógica, onde um filtro pode limitar o escopo de regitros visíveis. Desse modo, as funções de navegação somente vão encontrar ou posicionar em registros logicamente visíveis. Por exemplo, uma determinada tabela utilizando um indice e um filtro, ao executar a função DBGotop(), deve posicionar no primeiro registro visível pelo filtro na ordem atualmente em uso. A função DBSeek() – busca por indice – também respeita a visibilidade lógica de um filtro. Uma busca informando uma expressao parcial – que nao contempla todos os campos do índice — somente vai posicionar em um determinado registro caso ele atenda a condição de filtro. No momento que definimos um filtro para a tabela, o registro atualmente posicionado permanece posicionado. Somente a próxima instrução de navegação vai avaliar o filtro. Por isso, normalmente após aplicarmos um filtro em uma tabela, usamos a função DBGOTOP() para localizar e posicionar no primeiro registro que atenda a condição estabelecida, e podemos percorrer todos os registos atendidos pelo filtro com um loop While !Eof() – DbSkip() – Enddo
Índices com expressão de filtro
A criação de índices permite o uso de índices temporários, onde é possíve linformar uma expressão de filtro na criação do índice, para que somente sejam ordenados no índice os registros que atendam a esta condição. Os drivers baseados em implementação ISAM nativa – como DBF e CTREE – criam este índice fisicamente, permitindo que o processo atual que usa o índice criado navegue muito mais rapidamente pelos registros contemplados pelo indice do que simplesmente navegar pela tabela filtrada. A RDD TOPCONN contempla esse comportamento sem criar um indice fisico na base de dados. A função IndRegua() do Framework AdvPL do ERP Microsiga internamente se utiliza deste recurso para permitir criar índices temporários filtrados.
RDD TOPCONN - Comportamentos diferenciados - Queries
Como a RDD TOPCONN atua sobre tabelas criadas através do DBAccess, existem funções especificas adicionais para esta RDD, que atuam sobre as tabelas em modo ISAM e diretamente no banco de dados envolvido, sendo possivel por exemplo a execução de queries para leitura de dados e execução de instruções SQL repassadas ao Banco de Dados. A abertura de uma Query no AdvPL é encapsulada pelas funções TCGenQry e TcGenQry2, onde o result-set da Query é aberto sob um ALIAS AdvPL, que possui algumas restrições de comporamento: O Alias aberto por uma QUERY é internamente aberto em modo somente letitura (READ ONLY) e suporta a navegação apenas pelas instruções DBSKIP() – com valor positivo, leitura sempre dos próximos registros. Um ALIAS de uma Query não suporta filtros, DBSKIP para registros anteriormente lidos. O Alias de uma Query reflete um result set obtido pelo banco de dados no momento da execução da Query. A única forma de voltar para um registro já lido de uma Query é voltar para o primeiro registro ( dbgotop() ) – POREM internamente este processo re-submete a Query para o banco de dados, e o result set retornado pode ser diferente, caso algum processo tenha inserido ou alterado registros.