Metabase é um pacote de programas e componentes que permite aos programadores da linguagem PHP desenvolver aplicações com bases de dados que funcionam de forma igual independentemente do SGBD (Sistema de Gestão de Bases de Dados) utilizado.
Os passos que os programadores precisam seguir para desenvolver aplicações com base em Metabase são idênticos as passos que seguiriam se usassem as funções originais para comunicar com cada SGBD directamente. A principal vantagem do uso de Metabase é que os programadores precisam aprender e utilizar apenas um conjunto de funções para desenvolver aplicações que podem correr com muitos SGBD diferentes.
O objectivo deste documento de aprendizagem é fazer uma introdução aos passos básicos para que os principiantes possam começar a desenvolver aplicações de bases de dados com Metabase. Para obter informação mais aprofundada, por favor consulte o manual de documentação do Metabase.
- Requisitos do Metabase
Antes de começar a usar o Metabase existem alguns requisitos de instalação que os programadores precisam conhecer e compreender.
- O programa PHP
A linguagem PHP é normalmente usada a partir de um servidor Web, na forma mais comum como módulo do servidor, mas também pode ser usada como programa CGI. Quando PHP é compilado como programa CGI, é gerado um programa na forma de ficheiro executável independente. Este programa CGI também pode ser usado para executar scripts de PHP a partir da linha de comando do sistema operativo.
Apesar de ser possível fazer o mesmo usando PHP como módulo do servidor Web, é recomendável que use o programa CGI para executar scripts de PHP para installar ou configurar bases de dados usando Metabase.
Se pretender executar o script de instalação setup_test.php a partir da linha de comando, é preciso escrever por exemplo:
/usr/local/bin/php -q setup_test.php
As opções de PHP
PHP tem diversas opções de execução que existem para facilitar o trabalho dos programadores. A opção com o nome magic_quotes_runtime serve para inserir automaticamente caracteres de escape em valores literais de texto com determinados caracteres antes de executar um comando SQL.
O Metabase já possue a capacidade de inserir carateres de escape junto de caracteres especiais através das funções MetabaseGetTextFieldValue e MetabaseQuerySetText. Se uma destas funções for usada ao mesmo tempo que a opção magic_quotes_runtime tiver o valor On, alguns carateres podem acabar por ficar com carateres de escape duplicados.
Para evitar a duplicação de inserção de carateres de escape, por favor desligue a opção magic_quotes_runtime e também a opção magic_quotes_sybase atribuindo-lhes o valor Off na sua configuração de PHP por exemplo tendo as seguintes linhas no seu ficheiro php.ini ou php3.ini:
magic_quotes_runtime = Off
magic_quotes_sybase = Off
Ficheiros para inclusão
O Metabase é constituído por diversos ficheiros de PHP que precisam ser incluídos a partir dos seus scripts, dependendo do que pretende fazer com o Metabase. Aqui segue a lista dos ficheiros mais importantes e a informação sobre quando é que devem ser incluídos a partir dos seus scripts.
- metabase_interface.php
Este é o ficheiro principal com as funções do Metabase. Deve ser incluído sempre que pretender aceder a uma base de dados através do Metabase.
- metabase_database.php
Este é o ficheiro com a definição da classe de base para todas classes que servem como driver do Metabase para acesso a cada tipo de base de dados. Deve ser incluído sempre que pretender aceder a uma base de dados através do Metabase.
Os ficheiros das classes driver de bases de dados são incluídos automaticamente quando é invocada a função MetabaseSetupDatabase. Se o ficheiro da classe driver de base de dados que pretende usar não estiver no directório actual do seu script, especifique o directório desse ficheiro através do argumento IncludePath da função MetabaseSetupDatabase.
- metabase_lob.php
Este é o ficheiro que contém as funções necessárias para tratar de campos de tabelas que armazenam objectos grandes (LOBs - Large OBjects). Deve ser incluído sempre que pretender armazenar ou consultar dados em campos de objectos grandes.
- metabase_manager.php
Este é o ficheiro com a definição da classe de gestão de esquemas de bases de dados. Deve ser incluído sempre que for preciso consultar ou alterar o esquema de definição de uma base de dados.
- metabase_parser.php
Este é o ficheiro da classe de interpretação da definição de esquemas de bases de dados. Deve ser incluído sempre que for preciso interpretar um ficheiro XML com a definição do esquema de uma base de dados.
- xml_parser.php
Este é o ficheiro da classe para interpretação genérica de ficheiros XML. Deve ser incluído sempre que for preciso interpretar um ficheiro XML com a definição de um esquema de uma base de dados.
Desenhar o esquema de uma base de dados
Antes de tudo, é preciso desenhar e instalar o esquema da base de dados que conterá a informação necessária pela aplicação que pretende desenvolver.
O Metabase simplifica bastante este passo porque permite que os programadores desenhem os esquemas das suas bases de dados de forma independente do SGBD. Tudo o que é preciso é escrever um ficheiro de texto num formato de XML próprio que descreve as tabelas e os campos que pretende que a sua base de dados tenha.
O formato XML para descrição de esquemas de bases de dados está completamente documentado no manual do Metabase, mas um exemplo frequentemente explica melhor que um manual exaustivo. Portanto, vamos analisar o seguinte exemplo comentado:
<?xml version="1.0" encoding="ISO-8859-1" ?>
Esta é uma linha de cabeçalho típica que todos ficheiros de XML devem ter.
<database>
Esta é a marca inicial de um esquema de Metabase. Todos ficheiros XML de Metabase devem começar por esta marca.
Estas são as propriedades principais da base de dados. A propriedade name indica o nome da base de dados e é obrigatória. A propriedade create não é obrigatória, mas como se pretende que o Metabase crie a base de dados quando esta for instalada pela primeira vez, esta propriedade deve ter o valor 1.
As definições das tabelas podem ter duas secções: declaration e initialization. A secção declaration é obrigatória porque tem de conter a declaração de todos os campos e índices da tabela que são necessários. A secção initialization não é obrigatória mas pode ser usada se for necessário criar a tabela já com alguns dados iniciais.
A secção de declaração da tabela deve descrever um ou mais campos. A definição de cada campo deve ter pelo menos as propriedades name e type. Os campos de chaves auto-incrementais são implicitamente do tipo integer, pelo que o seu tipo não precisa ser declarado explicitamente. Os tipos de campo mais comuns são integer e text, mas outros tipos como date e timestamp também são usados frequentemente.
O uso de índices não é obrigatório, mas as aplicações usam sempre índices para acelerar o acesso às bases de dados. Os índices são criados normalmente nos campos que estão involvidos nas condições de pesquisa das consultas mais importantes que as aplicações de bases de dados executam.
O critério que deverá ser usado para determinar sob que campos os índices deverão ser criados depende muito de para quê que é usada a aplicação de base de dados. Portanto, orientar as decisões de criação de índices está fora do âmbito desde documento.
De qualquer forma, normalmente existem campos que são declarados como sendo chaves primárias ou secundárias das tabelas. O Metabase ainda não oferece suporte para a declaração de chaves de tabelas porque nem todos SGBD as suportam. Porém, é suportada a criação de índices únicos em campos individuais, que na prática funciona como se esses campos tivessem sido declarados como sendo chaves primárias das respectivas tabelas.
Os campos sob os quais são criados índices não podem ter o valor NULL. Por isso, o interpretador de descrições de esquemas do Metabase requer que esses campos sejam declarados com a propriedade de restrição notnull. Nesse caso também requer que a propriedade default seja definida com um valor diferente de NULL, como no caso do campo id do exemplo acima.
Mais tabelas poderiam ser declaradas, tantas quantas forem necessárias na sua aplicação de base de dados.
</database>
Os ficheiros de XML do Metabase devem terminar com uma marca final que condiz com a marca inicial do ficheiro.
Instalar a base de dados
Assim que tiver criado o ficheiro de descrição do esquema da sua base de dados, é a vez de instalar esse esquema no servidor do SGBD. Quando se usa a classe de gestão do Metabase, este procedimento torna-se bastante simples.
Suponhamos que escreveu e gravou o esquema da sua base de dados num ficheiro com o nome MinhaBaseDeDados.esquema. Para instalar a base de dados pela primeira vez, por exemplo num servidor MySQL, tudo o que precisa fazer é escrever um script que cria um objecto da classe de gestão do Metabase e invoca a função UpdateDatabase. Depois use o programa executável de PHP para executar o script que deve ser semelhante a este:
<?php
require("xml_parser.php");
require("metabase_parser.php");
require("metabase_interface.php");
require("metabase_database.php");
require("metabase_manager.php");
Incluir os ficheiros necessários.
Esta é a definição do nome do ficheiro do esquema da sua base de dados.
Se a definição do esquema da sua base de dados precisa de valores de variáveis definidos no momento de instalação, defina esses valores aqui. No esquema de exemplo acima não foram usadas quaisquer variáveis. Portanto, a lista de variáveis foi definida como um array vazio.
Para estabelecer uma ligação ao servidor de base de dados é necessário passar alguns argumentos específicos do tipo de SGBD para a função SetupDatabase da classe de gestão do Metabase. O argumento Type permite que a classe de interface inclua automaticamente os ficheiros da respectiva classe driver que devem estar no directório actual ou então noutro directório que deve ser especificado através do argumento IncludePath.
Criar o objecto da classe de gestão do Metabase.
$sucesso=$gestor->UpdateDatabase($ficheiro_do_esquema, $ficheiro_do_esquema.".antes", $argumentos, $variaveis);
Invoca a função UpdateDatabase do objecto da classe de gestão do Metabase passando o nome do ficheiro do esquema, os argumentos de configuração da base de dados e a lista de variáveis do esquema.
O segundo parâmetro é de especial importância. Esse parâmetro define o nome do ficheiro para o qual será copiado o ficheiro do esquema que está a ser instalado após um procedimento de instalação com sucesso.
O ficheiro copiado será usado mais tarde quando pretender actualizar o esquema da sua base de dados. Não apague este ficheiro senão a classe de gestão do Metabase não conseguirá determinar que esquema foi previamente instalado.
Se o procedimento de instalação falhou, exiba a mensagem de erro para determinar o que correu mal.
if(count($gestor->warnings)>0)
echo "AVISO:\n",implode($gestor->warnings,"!\n"),"\n";
?>
Mesmo quando o procedimento de instalação foi completado com sucesso, podem existir alguns detalhes sobre os quais deve ser avisado.
Aceder à informação na base de dados
O propósito do Metabase é de ser usado para aceder ao SGBD usando SQL. Os programadores de aplicações de bases de dados pode construir e executar livremente as consultas SQL que as suas aplicações precisam para enviar e receber informação para o servidor de base de dados.
- Funções ou Objectos?
O Metabase disponibiliza duas formas equivalentes de invocar as suas funções: usar um conjunto de funções globais ou invocar directamente as funções dos objectos das classes driver.
Invocar directamente as funções dos objectos driver é ligeiramente mais rápido e requer que os programadores digitem menos caracteres para escrever chamadas às funções do Metabase nos seus programas.
No entanto, esta forma não funciona com o PHP 3. Dado que o desenvolvimento do Metabase foi iniciado quando existia apenas o PHP 3, a forma de invocação usando funções globais foi mantida para assegurar a compatibilidade das aplicações que usam Metabase desde há muito tempo.
Se pretende usar o Metabase apenas com PHP 4 ou melhor, pode usar as funções do objecto driver directamente. Nesse caso, também pode misturar o uso de ambas as formas de invocar as funções dos objectos driver. Para fazer isso precisa usar a função MetabaseSetupDatabaseObject em vez de MetabaseSetupDatabase como está descrito mais abaixo.
A função MetabaseSetupDatabaseObject retorna por referência um objecto da classe driver de base de dados do tipo especificado. É necessário usar referências para passar objectos para funções ou para atribuir um valor de um objecto a outra variável. Passar objectos por valor pode originar problemas que têem de ser evitados para não ficar com duas cópias distintas dos objectos driver. Isso poderia levar a eventuais inconsistências da informação armazenada nos objectos driver.
Explicar os conceitos de referências e objectos está fora do âmbito deste documento de introdução. Se precisa passar um objecto driver para funções ou atribuir o seu valor a outra variável mas tem dificuldade em entender estes conceitos de objectos e referências, é recomendável que apenas use as funções globais do Metabase.
- Configuração do acesso à base de dados
A primeira coisa que é necessário fazer para aceder a uma base de dados é configurar a ligação ao servidor do SGBD a partir dos scripts da sua aplicação.
A ligação ao servidor é definida invocando a função MetabaseSetupDatabase. Esta função não estabelece uma ligação ao servidor imediatamente. Apenas configura alguns parâmetros que a classe driver do SGBD precisa para saber como comunicar com o servidor de base de dados pretendido. Normalmente a ligação com o servidor de base de dados é estabelecida mais tarde apenas quando cada script executa a primeira consulta.
A função MetabaseSetupDatabase recebe como argumento um array associativo com as opções da ligação. A entrada Type do array é obrigatória e deve indicar a designação do tipo de classe driver que comunicará com o tipo de SGBD escolhido. Por exemplo, se pretende ligar a um servidor de SGBD MySQL, defina a entrada Type como sendo mysql.
Pode ser necessário especificar outros argumentos no array dependendo do tipo de classe driver escolhido. Por favor, consulte o manual do Metabase para saber qual é a designação dos outros tipos de SGBD suportados e os respectivos argumentos adicionais.
O segundo argumento da função MetabaseSetupDatabase é uma referência a uma variável que será usada para armazenar um identificador de acesso à base de dados. Este identificador de acesso é importante porque deve ser passado como argumento para todas as funções do Metabase que são usadas para aceder à base de dados.
A função MetabaseSetupDatabase pode falhar devido a inconsistência dos valores dos argumentos que forem passados. Em caso de falha, esta função retorna um texto que descreve o erro que originou a falha. Verifique sempre o valor retornado por esta função, pelo menos quando está a verificar se a sua aplicação de base de dados funciona correctamente.
Aqui segue um exemplo do uso da função MetabaseSetupDatabase:
<?php
Incluir os ficheiros necessários.
$erro=MetabaseSetupDatabase(array(
"Type"=>"mysql",
"User"=>"nome_do_utilizador_mysql",
"Password"=>"senha_do_utilizador_mysql"
), $base_de_dados);
if($erro!="")
{
}
A função MetabaseSetupDatabase cria um objecto da classe driver para aceder à base de dados e inicializa algumas variáveis. A função inicializa o argumento $base_de_dados com um valor inteiro que é uma referência indirecta para o objecto da classe driver que foi criado. Este valor inteiro funciona como um identificador de acesso à base de dados que deve ser passado como argumento para outras funções do Metabase.
MetabaseSetDatabase($base_de_dados,"teste");
?>
Antes de poder aceder a um servidor de base de dados, normalmente é necessário especificar o nome da base de dados que pretende aceder usando a função MetabaseSetDatabase.
- Invocar as funções dos objectos driver directamente
Se prefere invocar directamente as funções dos objectos driver, então precisa usar a função MetabaseSetupDatabaseObject em vez de MetabaseSetupDatabase. A função MetabaseSetupDatabaseObject returna um objecto de uma classe driver que deve ser usado para invocar directamente as funções do objecto driver.
Neste documento de introdução são usadas apenas as funções globais do Metabase. Para fazer com que os exemplos apresentados usem chamadas directas aos objectos driver, apenas retire o prefixo Metabase do nome das funções e o argumento $base_de_dados e faça uma chamada ao objecto usando a variável que foi retornada pela função MetabaseSetupDatabaseObject.
Aqui segue o exemplo acima para configuração de acesso à base de dados, mas aqui usando a função MetabaseSetupDatabaseObject:
<?php
require("metabase_interface.php");
require("metabase_database.php");
$erro=MetabaseSetupDatabaseObject(array(
"Type"=>"mysql",
"User"=>"nome_do_utilizador_mysql",
"Password"=>"senha_do_utilizador_mysql"
), $bd);
if($erro!="")
{
}
$bd->SetDatabase($base_de_dados,"teste");
Mesmo assim ainda pode invocar as funções globais do Metabase obtendo o valor da variável do objecto driver chamada database e usá-la como identificador de acesso à base de dados. Neste caso, a chamada acima para definir a base de dados de trabalho poderá aparecer assim:
MetabaseSetDatabase($bd->database,"teste");
?>
- Executar consultas à base de dados
O Metabase dispobiliza duas formas de construir e executar consultas à base de dados: consultas directas e consultas preparadas.
- Consultas directas
Consultas directas são aquelas que são executadas simplesmente passando o comando SQL para o SGBD. Para executar uma consulta directa use a função Query. Esta função recebe um identificador de acesso à base de dados e um comando de consulta SQL como argumentos. Aqui segue um exemplo:
$consulta="SELECT nome,senha FROM utilizadores";
$resultado=$bd->Query($consulta);
O valor retornado é um número inteiro que determina se a consulta foi executada com sucesso. Se o valor for 0 significa que a consulta falhou. Para consultas do tipo SELECT o valor retornado é um identificador do resultado. Este identificador deve ser usado para obter os dados retornados pela consulta.
Os comandos das consultas contém frequentemente valores literais como no seguinte exemplo:
$consulta="SELECT nome,senha FROM utilizadores WHERE nome_de_acesso='administrador'";
$resultado=$bd->Query($consulta);
As consultas podem ser construidas manualmente inserindo os valores literais no texto do comando da consulta. Porém, SGBD diferentes podem necessitar que os valores literais sejam representados de maneiras diferentes.
Por exemplo, valores literais de texto podem necessitar que se insiram prefixos antes de caracteres especiais como '. Valores literais de datas podem ser representados por inteiros em vez de texto se o SGBD não suportar tipos de dados nativos para representar datas.
Felizmente, o Metabase disponibiliza um conjunto de funções de conversão que evita a necessidade de passar valores literais num formato apropriado para o tipo de SGBD com o qual se pretende comunicar. Estas funções invocam as respectivas funções da classe driver para fazer alguma conversão do formato de representação que possa ser necessário.
Assim é possível desenvolver applicações de base de dados que são altamente portáteis sem a preocupação com os diferentes tipos de conversão de valores literais que precisam ser feitos para aceder a diferentes SGBD.
Por cada tipo de dados suportado pelo Metabase existe uma função para conversão de valores literais, excepto para valores inteiros porque estes porque é improvável que estes sejam representados de forma diferente em algum DBMS. Aqui segue a lista completa de funções de conversão de valores literais :
- $bd->GetTextFieldValue($valor)
- $bd->GetBooleanFieldValue($valor)
- $bd->GetDateFieldValue($valor)
- $bd->GetTimestampFieldValue($valor)
- $bd->GetTimeFieldValue($valor)
- $bd->GetFloatFieldValue($valor)
- $bd->GetDecimalFieldValue($valor)
Usando funções de conversão de valores literais para a execução da consulta mencionada acima, essa consulta seria reescrita assim:
$consulta="SELECT nome,senha FROM utilizadores WHERE nome_de_acesso=". $bd->GetTextFieldValue("administrador");
$resultado=$bd->Query($consulta);
Converter valores literais antes de executar consultas requer um pequeno esforço adicional, mas considerando a flexibilidade que é ganha a partir de maior portabilidade da sua aplicação, é um esforço que vale a pena e por isso é extensivamente recomendado que o faça.
- Consultas preparadas
Consultas preparadas são comandos SQL que precisam ser preparados antes de serem executados. Executar uma consulta preparada demora menos tempo do que executar uma consulta não preparada porque as consultas preparadas já foram previamente interpretadas no momento em que são executadas.
Usar consultas preparadas para as executar apenas uma vez num script não demora menos tempo que usar apenas consultas directas. As consultas preparadas são mais recomendadas quando é necessário executar uma consulta mais de uma vez no mesmo script.
As consultas preparadas podem ter argumentos. Quando uma consulta preparada é executada os valores dos argumentos são inseridos em posições especiais do comando da consultas que são marcados por ?. Considere este exemplo que usa uma consulta directa:
$consulta="SELECT nome,senha FROM utilizadores WHERE nome_de_acesso=". $bd->GetTextFieldValue("administrador");
$resultado=$bd->Query($consulta);
Esta consulta pode ser reescrita assim usando uma consulta preparada:
$consulta="SELECT nome, senha FROM utilizadores WHERE nome_de_acesso=?";
$consulta_preparada=$bd->PrepareQuery($consulta);
Obter um identificador de consulta preparada.
if($consulta_preparada)
Assegure-se que a consulta foi preparada sem qualquer erro.
{
$bd->QuerySetText($consulta_preparada, 1, "administrador");
Atribuir o primeiro argumento da consulta o valor literal administrador.
}
Executar a consulta. A partir daqui o tratamento de resultados da consulta é igual ao tratamento de resultados de consultas directas.
As consultas preparadas podem ter muitos argumentos, todos identificados pela marca ?. Cada um tem um número de ordem começando a partir de 1.
Existe uma função para atribuir valores de argumentos de consultas preparadas por cada tipo de dados suportado pelo Metabase. No exemplo a função QuerySetText foi usada para definir o valor de um argumento de texto. A função QuerySetInteger deveria ser usada se o argumento fosse um número inteiro, e assim em diante. Se se pretender atribuir o valor NULL a um argumento, use a função QuerySetNull.
As consultas preparadas podem ser executadas várias vezes no mesmo script e os valores de todos ou parte dos argumentos podem ser mudados em cada vez antes de executar as consultas.
Depois de executar uma consulta preparada todas as vezes que forem necessárias num script, é necessário devolver todos os recursos que foram implicitamente reservados, invocando a função FreePreparedQuery.
Apesar das consultas preparadas serem mais apropriadas para uso em scripts em que é necessário executar a mesma consulta mais de uma vez, talvez considere mais limpo o seu uso mesmo quando são executadas apenas uma vez, dado que apenas usa comandos de consulta fixos e a conversão de valores literais é tratada transparentemente por funções como SetQuery.
- Chaves auto-incrementais
Frequentemente as tabelas de uma base de dados têem um campo chave que contém valores que devem ser únicos dentre todas as linhas da tabela. Uma forma de assegurar que os valores do campo chave são únicos, consiste em defini-los como chaves auto-incrementais. Isso garante que o campo chave de cada nova linha que é inserida na tabela, seja inicializado com um novo valor inteiro.
Os campos auto-incrementais são inicializados de formas que podem variar de uns SGBD para outros. O Metabase disponibiliza uma solução independente do SGBD para inicializar campos auto-incrementais e obter os valores inseridos que consistem em usar as funções GetNextKey e GetInsertedKey da seguinte forma:
$bd->GetNextKey("utilizadores", $chave);
if($bd->Query("INSERT INTO utilizadores (id, nome_de_acesso, senha, lembrete, nome, endereco) VALUES (".$key.
", 'administrador', 'uma senha', 'lembra a senha', 'SuperUtilizador', 'admin@acme.com')")
&& $bd->GetInsertedKey("utilizadores", $id))
echo "O registo do utilizador foi criado com sucesso com o identificador: ", $id, "\n";
else
echo "Ocorreu um erro: ",$bd->Error(),"\n";
Alguns SGBD permitem a omissão do campo chave auto-incremental na consulta INSERT. Nesse caso não é necessário usar a função GetNextKey para obter a expressão do valor do campo auto-incremental.
if($bd->Query("INSERT INTO utilizadores (nome_de_acesso, senha, lembrete, nome, endereco) VALUES (".
"'administrador', 'uma senha', 'lembra a senha', 'SuperUtilizador', 'admin@acme.com')")
&& $bd->GetInsertedKey("utilizadores", $id))
echo "O registo do utilizador foi criado com sucesso com o identificador: ", $id, "\n";
else
echo "Ocorreu um erro: ",$bd->Error(),"\n";
Verifique na secção dos drivers disponíveis da documentação do Metabase para saber se a classe de driver do SGBD que pretende usar suporta a característica OmitInsertKey, para que possa determinar se o valor do campo auto-incremental pode ser omitido.
Uma forma alternativa para inserir linhas em tabelas com campos auto-incrementais consiste em usar consultas preparadas consultas preparadas e as funções QuerySetKey e GetInsertedKey da seguinte forma:
$consulta_preparada=$bd->PrepareQuery(
"INSERT INTO utilizadores (id, nome_de_acesso, senha, lembrete, nome, endereco) VALUES (?, ?, ?, ?, ?, ?)");
if($consulta_preparada)
{
$bd->QuerySetKey($consulta_preparada, 1, "utilizadores");
$bd->QuerySetText($consulta_preparada, 2, "Administrador");
$bd->QuerySetText($consulta_preparada, 3, "uma senha");
$bd->QuerySetText($consulta_preparada, 4, "lembra a senha");
$bd->QuerySetText($consulta_preparada, 5, "SuperUtilizador");
$bd->QuerySetText($consulta_preparada, 6, "admin@acme.com");
if($bd->ExecuteQuery($consulta_preparada)
&& $bd->GetInsertedKey("utilizadores", $id))
echo "O registo do utilizador foi criado com sucesso com o identificador: ", $id, "\n";
else
echo "Ocorreu um erro: ",$bd->Error(),"\n";
}
else
echo "Ocorreu um erro: ",$bd->Error(),"\n";
Se a classe driver do SGBD em uso suportar, o campo auto-incremental também pode ser omitido na consulta INSERT quando são usadas consultas preparadas.
Obter os resultados de consultas
- Buscar dados de resultados
Uma consulta tipo SELECT executada com sucesso retorna um valor que deve ser usado como identificador do resultado. Este valor deve ser passado como argumento para todas as funções do Metabase que pode precisar usar para aceder aos resultados retornados pelas consultas que as suas aplicações de base de dados executam.
O identificador de resultados é apenas um número inteiro que serve como referência ao conjunto de resultados retornado à aplicação pelo SGBD como resposta à consulta que foi executada.
Um conjunto de resultados é uma espécie de tabela com colunas e linhas preenchidas com dados do resultado de uma consulta. Cada posição de um conjunto de resultados pode ser acedido através da função FetchResult.
Apesar das linhas de um conjunto de resultados não serem disponibilizadas a uma aplicação de base de dados todas de uma vez, o Metabase permite que sejam pedidos dados do resultado especificando o número da respectiva linha do conjunto de resultados. Os números de linha do conjunto de resultados começam a partir de 0.
A função FetchResult também requer que seja passada a identificação da coluna a partir da qual se pretende buscar os dados do resultado. A identificação da coluna pode ser tanto o nome da coluna como o respectivo número começando em 0. O nome da coluna é o nome do campo ou a expressão que define a coluna na consulta SELECT.
Aqui segue um exemplo simples da obtenção de uma linha do resultado de uma consulta:
$resultado=$bd->Query("SELECT nome,endereco FROM utilizadores");
if($resultado!=0)
{
$nome=$bd->FetchResult($resultado, 0, "nome");
$endereco=$bd->FetchResult($resultado, 0, "endereco");
}
else
Apesar do uso dos nomes como identificadores das colunas tornar o código das aplicações mais limpo, o uso do número de coluna é mais rápido. Portanto, os comandos acima para buscar valores do resultado podem ser reescritos assim:
$nome=$bd->FetchResult($resultado, 0, 0);
$endereco=$bd->FetchResult($resultado, 0, 1);
- Conversão do tipo de dados
A função FetchResult obtem os dados tal como estes são retornados pelo SGBD. O formato de representação dos dados para cada tipo de campo pode variar de SGBD para SGBD.
Para evitar o problema de ter de tratar as diferenças de formato de representação dos dados na sua aplicação de base de dados, o Metabase disponibiliza um conjunto de funções que buscam os dados dos resultados e já os convertem para um único formato de representação definido para cada tipo de dados suportado.
Por exemplo, independentemente de como cada SGBD representa os campos que guardam datas, a função FetchDateResult retorna sempre um texto com a data representada no formato ISO 8601: AAAA-MM-DD. Isto simplifica muito o desenvolvimento de aplicações de base de dados e promove a portabilidade do código quando se usam diferentes tipos de SGBD.
Nem todos os tipos de dados de resultados precisam ser convertidos. Por exemplo, dados de resultados de texto ou números inteiros podem ser sempre buscados com a função FetchResult. Aqui segue a lista completa de funções para buscar e converter dados de resultdados:
- FetchBooleanResult
- FetchDecimalResult
- FetchFloatResult
- FetchDateResult
- FetchTimeResult
- FetchTimestampResult
- Tratamento de valores NULL
Existe um tipo de resultados de consulta que requer uma atenção especial: NULL. Os valores NULL não representam dados, mas sim a ausência de dados. Um valor NULL pode ser retornado porque a posição do campo escolhido não contém informação ou porque o valor do resultado não pode ser calculado como por exemplo a determinação do valor máximo de um dado campo de uma tabela que não contém quaisquer linhas.
Como um valor NULL significa exactamente a ausência de dados, não se pode usar as funções para buscar dados de resultados para determinar se uma dada posição do resultado é NULL.
Se não tem a certeza se uma dada posição de um resultado é NULL, então deve usar a função ResultIsNull. Aqui segue um exemplo:
if($bd->ResultIsNull($resultado, 0, "nome"))
else
echo "O nome é: ".$bd->FetchResult($resultado, 0, "nome");
- Obter todas as linhas de um conjunto de resultados
Normalmente, as aplicações de base de dados precisam percorrer o conjunto de resultados para obter todas as linhas com dados que são retornadas quando uma consulta é executada.
Frequentemente é bastante útil saber adiantadamente qual é o número total de linhas retornadas antes de começar a percorrer o conjunto de resultados. A função NumberOfRows serve para esse propósito. Uma rotina típica para mostrar um conjunto de resultados pode ser algo semelhante a isto:
$resultado=$bd->Query("SELECT nome, endereco FROM utilizadores");
if($resultados!=0)
{
$linhas=$bd->NumberOfRows($resultado);
if($linhas>0)
{
echo "<TABLE><TR><TH>name</TH><TH>endereco</TH></TR>";
for($linha=0; $linha<$linhas; $linha++)
{
echo "<TR><TD>", $bd->FetchResult($resultado, $linha, "nome"), "</TD>";
echo "<TD>", $bd->FetchResult($resultado, $linha, "endereco"), "</TD></TR>";
}
echo "</TABLE>";
}
else
}
else
}
Apesar deste código parecer limpo e simples há um detalhe relevante que deve ser considerado antes de o usar para executar consultas que retornam um número elevado de linhas de resultado.
Alguns SGBD, tipicamente os mais sofisticados, começam a retornar linhas de resultado à medida que estas são encontradas nas tabelas da base de dados que estão a ser pesquisadas. Isto significa que não é possível saber o número total de linhas contidas num conjunto de resultados antes que a pesquisa da consulta termine.
Para esses tipos de SGBD a respectiva classe driver do Metabase implementa a função que obtem o número total de linhas do resultado, obtendo todas as linhas do resultado de uma só vez. Esta circunstância não só torna essa função mais lenta como consome mais memória porque implica que a classe driver do SGBD armazene em memória os dados de todas as linhas até que os recursos que foram reservados para o conjunto de resultados sejam devolvidos através da função FreeResult.
Se realmente necessita saber antecipadamente o número de linhas contidas num conjunto de resultados, uma possível alternativa é executar primeiro uma consulta que apenas retorna o número de linhas usando a função de SQL COUNT assim:
$resultado=$bd->Query("SELECT COUNT(nome) FROM utilizadores");
if($resultado)
$linhas=$bd->FetchResult($resultado, 0, 0);
else
Se não precisa saber adiantadamente quantas linhas são retornadas num conjunto de resultados mas precisa saber quando o conjunto de resultados foi completamente percorrido, então use a função EndOfResult. A rotina para mostrar os dados do conjunto de resultados poderá ser semelhante a isto:
$resultado=$bd->Query("SELECT name, endereco FROM users");
if($resultado!=0)
{
$fim_do_resultado=$bd->EndOfResult($resultado);
if($fim_do_resultado==0)
{
echo "<TABLE><TR><TH>nome</TH><TH>endereco</TH></TR>";
for($linha=0; ($fim_do_resultado=$bd->EndOfResult($resultado))==0; $linha++)
{
echo "<TR><TD>", $bd->FetchResult($resultado, $linha, "nome"), "</TD>";
echo "<TD>", $bd->FetchResult($resultado, $linha, "endereco"), "</TD></TR>";
}
echo "</TABLE>";
}
else
{
}
if($fim_do_resultado==-1)
}
else
}
- Devolver os recursos de memória reservados para um conjunto de resultados
Quando tiver terminado de usar o conjunto de resultados de uma consulta, deve assegurar-se que todos os recursos de memória implicitamente reservados são devolvidos ao sistema. Devolver os recursos reservados é importante porque isso reduz o uso de memória que um script precisa para ser executado, permitindo que essa memória seja reutilizada.
Mesmo que o seu script termine logo a seguir a ter terminado de usar o conjunto de resultados de uma consulta, deve-se sempre devolver os recursos reservados para um conjunto de resultados de uma consulta porque o fim do script pode não implicar que os recursos sejam automaticamente devolvidos nessa altura. Isto é particularmente verdade se estiver a usar ligações persistentes à base de dados quando o PHP está a ser usado como módulo do servidor Web.
Com o Metabase deve-se usar a função FreeResult e isso pode ser tão simples como o seguinte:
$bd->FreeResult($resultado);
Tratamento de campos de grande porte
Os campos de grande porte, normalmente conhecidos por Large OBject fields - LOBs (BLOBs/CLOBs), necessitam de ter um tratamento especial. A quantidade de informação armazenada neste tipo de campos pode ser tão grande que isso exigiria uma grande quantidade de memória para armazenar ou obter toda a informação de uma só vez usando apenas uma função como com os outros tipos de campo.
O Metabase disponibiliza um conjunto separado de funções para tratar de campos de grande porte. Estas funções permitem que as aplicações tratem os valores deste tipo de campo dividindo a informação em pedaços de tamanho menor.
- Criação de campos de grande porte em tabelas
Os campos de grande porte podem ser criados como qualquer outro tipo de campo, ou seja, declarando-os em ficheiros de descrição de esquemas como campos de grande porte.
Existem dois tipos de campos de grande porte: campos de caracteres e campos binários. Os campos de caracteres podem ser usados quando se pretende apenas armazenar neles texto ASCII. Se pretende armazenar outros tipos de dados, então use campos binários
Os campos de grande porte de caracteres devem ser declarados como sendo do tipo clob. Os campos de grande porte binários devem ser declarados como sendo do tipo blob. Aqui segue um exemplo de declaração de uma tabela com um campo de grande porte de caracteres e outro binário:
- Armazenar dados em campos de grande porte
Com o Metabase, armazenar dados em campos de grande porte só pode ser feito executando consultas preparadas de comandos SQL INSERT ou UPDATE. Os valores dos campos de grande porte são passados para a base de dados como parâmetros das consultas preparadas. As funções QuerySetCLOB e QuerySetBLOB devem ser usadas para especificar os valores dos campos de grande porte como parâmetros de uma consulta preparada.
Em vez de especificar os parâmetros dos campos de grande porte através dos dados dos valores, é necessário passar uma referência para objectos de classes especiais que sabem como obter os dados que serão armazenados nos campos de grande porte.
Essas classes podem obter dados a partir de variáveis de texto definidas através de programação como com qualquer outro tipo de campo, mas também podem obter os dados a partir de ficheiros.
Aqui segue um exemplo de como executar uma consulta que insere dados definidos através de programação num campo de grande porte de caracteres:
- Preparação da consulta para inserir uma linha com um campo de grande porte de caracteres (document). Pode-se inserir linhas com vários campos de grande porte, mas neste exemplo apenas um campo é inserido.
if(($consulta_preparada=$bd->PrepareQuery("INSERT INTO ficheiros (id,documento,imagem) VALUES (1,?,NULL)")))
{
- Criar um objecto de uma classe para tratar de campos de grande porte fornecendo dados definidos no programa para inserir no campo da tabela.
$lob_texto=array(
"Database"=>$bd->database,
"Error"=>"",
"Data"=>"muitos caracteres"
);
if(($sucesso=MetabaseCreateLOB($lob_texto, $clob)))
{
- Definir o valor do parâmetro do campo de grande porte de caracteres. Note que é necessário especificar o nome do campo da tabela em que será inserido o valor dos dados.
- Executar a consulta preparada.
- Devolver os recursos reservados pelo objecto da classe que trata de campos de grande porte.
- Se a criação do objecto da classe que trata de campos de grande porte falhar, obtenha a mensagem de erro.
- Devolver os recursos reservados para a consulta preparada.
}
else
Aqui segue um exemplo de como executar uma consulta que actualiza um campo de grande porte binário a partir de dados de um ficheiro. Assegure-se que apenas uma linha da tabela é afectadas por esta consulta porque alguns SGBD não são capazes de actualizar os valores de campos de grande porte em mais de uma linha por consulta.
- Preparar a consulta para actualizar uma linha com um campo de grande porte binário (imagem).
if(($consulta_preparada=$bd->PrepareQuery("UPDATE files SET imagem=? WHERE id=1")))
{
- Criar um objecto de uma classe para tratar de campos de grande porte fornecendo dados de um ficheiro.
$lob_binario=array(
"Database"=>$bd->database,
"Error"=>"",
"Type"=>"inputfile",
"FileName"=>"minha_imagem.gif"
);
if(($sucesso=MetabaseCreateLOB($lob_binario, $blob)))
{
- Definir o valor do parâmetro do campo de grande porte de binário. Também é necessário especificar o nome do campo a actualizar através de consultas SQL UPDATE.
- Executar a consulta preparada.
if(!$bd->ExecuteQuery($consulta_preparada))
MetabaseDestroyLOB($blob);
}
else
$bd->FreePreparedQuery($consulta_preparada);
}
else
- Obter dados a partir de campos de grande porte
Obter dados de um campo de grande porte é feito através da execução de consultas SQL SELECT normais.
As funções FetchCLOBResult e FetchBLOBResult retornam um valor que identifica um objecto de uma classe que trata de valores de campos de grande porte. Os dados podem ser obtidos usando a função MetabaseReadLOB. Alternativamente, o identificador do objecto pode ser passado para outra classe que busca os dados do campo de grande porte retornado como resultado da consulta e pode processar esses dados de alguma forma útil, como por exemplo armazenar os dados num ficheiro.
Aqui segue um exemplo de como escolher um campo de grande porte de caracteres e mostrar o seu conteúdo:
- Executar a consulta SQL SELECT. Mais de um campo de grande porte pode ser seleccionado pela mesma consulta.
if(($resultado=$bd->Query("SELECT documento FROM ficheiros WHERE id=1")))
{
- Verificar se existem linhas de resultado verificando se já se chegou ao final do conjunto de resultados.
if($bd->EndOfResult($resultado))
else
{
- Obter o identificador do objecto do campo de grande porte para uma dada coluna de uma linha do resultado. Normalmente isto funciona correctamente, mas se ocorrer um erro inesperado, é retornado o valor 0. Se não tem a certeza se a coluna escolhida pode conter NULL, use a função ResultIsNull aqui para verificar isso primeiro.
- Ler os dados a partir do campo de grande porte seleccionado até que se chegue ao final dos dados do campo.
- Se for retornado um valor negativo para o comprimento dos dados lidos, isso signfica que ocorreu um erro.
- Se pelo contrário os dados foram lidos correctamente, continue mostrando o seu conteúdo.
- Devolver os recursos reservados pelo objecto da classe que trata de resultados do campo de grande porte.
- Se não foi possível obter os dados do valor do campo de grande porte, determine o que ocorreu obtendo a mensagem de erro.
- Devolver os recursos reservados para o resultado da consulta.
}
else
Aqui segue um exemplo de como seleccionar um campo de grande porte binário e gravar o seu conteúdo num ficheiro.
- Executar a consulta SQL SELECT.
if(($resultado=$bd->Query("SELECT imagem FROM ficheiros WHERE id=1")))
{
if($bd->EndOfResult($resultado))
else
{
- Criar o objecto da classe para guardar dados em ficheiros.
- Ler todo conteúdo do campo de grande porte e escrever os dados num dado ficheiro especificando um comprimento de leitura de 0 bytes. Não são retornados quaisquer dados na variável de argumento $dados.
- Se for retornado um valor negativo para o comprimento dos dados lidos, isso significa que ocorreu um erro.
- Se não for possível criar o objecto da classe que guarda os dados num ficheiro, determine o que ocorreu obtendo a mensagem de erro.
}
else
Actualizar o esquema de uma base de dados
Se por algum motivo precisar de actualizar o esquema da sua base de dados, o procedimento para instalar as alterações do esquema é tão simples quanto instalar o esquema pela primeira vez.
De facto tudo o que é preciso fazer depois de alterar o ficheiro do seu esquema é executar o mesmo script que foi usado para instalar o esquema inicialmente.
A classe de gestão do Metabase analisará a cópia do esquema instalado anteriormente. Tanto o novo esquema como o anterior são analisados e comparados para construir a lista de alterações.
Nessa altura tentará instalar as alterações solicitadas sem afectar os dados que foram armazenados na base de dados desde que foi instalada pela primeira vez.
As classes de driver do Metabase para alguns SGBD não são capazes de implementar todos os tipos de alterações. Se foram solicitadas alterações que não poderão ser instaladas, a função UpdateDatabase falhará sem afectar nada na base de dados.
A tentativa de alterar uma base de dados é segura neste nível mas é sempre mais seguro fazer uma cópia de segurança da sua base de dados antes de instalar quaisquer alterações porque por algum motivo inesperado o servidor do SGBD pode falhar.
|