|
Introdução
Assistimos vários diálogos entre Desenvolvedores e Database
Administrators parecidos com este:
Desenvolvedor. – Não sei, como obter este conjunto de
registros, através deste select.
DBA. – Ah, faça uma chamada.
Desenvolvedor. – Mas como da minha linguagem faço esta
chamada?
DBA. – Bem aÍ já passou a ser um problema seu.
Creio que o grande erro deste diálogo esta na última frase,
ela deveria ser: – Bem, vamos pesquisar, você de um lado e eu de outro. Depois
sentamos para concluir.
Temos a necessidade de dominar uma tecnologia logo de
inÍcio para erramos menos.
A nossa intenção, neste artigo, é colaborar no atendimento desta
necessidade. Apresentamos o PostgreSQL, que é distribuÍdo sob licença BSD e
portanto, fator a ser considerado na redução do TCO corporativo. Para facilitar
sua adoção, mostramos exemplos de Criação de Banco, Criação de Linguagens,
Criação de Usuários, Criação de Tabelas e Criação de funções de:
Inclusão;
Alteração;
Exclusão;
Retorno
de um único registro;
Retorno
de um conjunto de registros;
Uso
destas funções diretamente no prompt de comando;
Uso
destas funções com PHP.
Cremos que estes exemplos são básicos para a iniciação de
qualquer DBA e ou desenvolvedor com uma nova tecnologia de banco de dados.
Formas de Acesso ao PostgreSQL
Vamos partir da premissa que você já tem o banco de dados PostgreSQL
instalado e configurado no Windows®, linux ou Unix. Seu servidor Apache®,
aceita conexões em PHP® e você não tem dificuldades em SQL.
Não se irrite, o espaço que tenho aqui é restrito. Só para
você ter uma idéia do poder do PostgreSQL, ele pode ser instalado em MS-Windows
9x (95 ou 98), Me, NT 4.0 com SP 6ª ou superior (Workstation ou Server), 2000
com SP 2 ou superior (Professional, Server ou Advanced Server), Linux (Red Hat
6.2, 7.0, 7.2, 7.3. Mandraque 7.2, 8.0, 8.1, 8.2, SuSE 7.0, 7.2 e TurboLinux
6.0), BeOS 5.04 ou acima, BSD/OS e FreeBSD versão 4.2 ou superior, IBM AIX
Risc6000, HP-UX versões 10.20 e 11.00 ou superior, MacOS X versões 10.1.x, SUN
Solaris (versões 2.6, 7, 8 para Sparc e 2.8 para x86) e todas as ditribuições
Linux e UNIX não citadas e mais novas.
Caso, você queira uma boa dica de instalação do PostgreSQL
no Windows, vá até o endereço http://www.visualobjects.hpg.ig.com.br/postgresql.htm
ou leia o livro de Álvaro Pereira Neto, PostgreSQL – Técnicas Avançadas Soluções para Desenvolvedores e Administradores
de Banco de Dados, editora Érica. As primeiras 70 páginas são dedicadas à
instalação do PostgreSQL no Windows, Linux e Solaris.
A diferença entre os dois approaches é que o pessoal da Visual Objects instala o Banco no
Windows, com inicialização a partir de um script o “pg“, com os comandos pg
start e pg stop, desenvolvido por Jonathas Pereira da Logness Software. Álvaro
Pereira Neto, no seu livro, instala o ipc-deamon como um serviço.
Adotei o formato da Visual Objects, porque no meu caso não
é necessário ter um servidor disponÍvel, caso este seja o seu caso, use a
solução do Álvaro Pereira Neto.
Outra opção é a adoção da solução da DBExperts (www.dbexperts.com.br), que implementa o
PostgreSQL para Windows® a partir da instalação de um CD.
Ferramentas
Você pode usar PGAdmin II® (http://www.pgadmin.org/),
PGAcces ® (http://www.flex.ro/pgaccess/),
DBTools® (http://www.dbtools.com.br/EN/).
O último citado funciona tanto para PostgreeSQL® como para MySQL. Todos têm
interface gráfica para Windows.
Porém, recomendo que você utilize psql, porque na hora do
“vamos ver“, poderá ser o único recurso de que você disporá para as suas
tarefas. Prefira utilizar scripts. Eles evitam os erros em linhas de comando e
podem ser executados no futuro. Crie um diretório para scripts, exemplo: E:\PostgreSql\scripts
ou /cgydriver/e/postgresql/scripts olhando pelo ponto de vista do Bash.
Criando um usuário
Dependendo da instalação que você levou a cabo, o PostgreSQL,
pode ter criado o usuário postgres. Este é o superusuário do banco de dados.
Não esqueça de mudar sua senha. É recomendável que você crie um usuário para o
seu banco, além do usuário postgres.
Os passos para esta execução são:
Conecte-se ao banco template1, usando uma das sintaxes:
psql –U postgres template1 <enter> ou
psql template1 <enter>
psql –U postgres template1 –W <enter> ou
A primeira sintaxe explicita o usuário, a segunda é
possÍvel desde que o seu usuário tenha feito a instalação do PostgreSQL. A
terceira sintaxe, informa ao PostgreSQL que a senha é requerida, através do
argumento –W, logo após você teclar <enter>, o PostgreSQL vai-lhe pedir a
senha através do prompt: $password: , apenas digite a senha teclando
<enter> em seguida.
O PostgreSQL vai-lhe responder com a resposta apresentada
na lista 1:
Welcome to psql 7.3.2, the PostgreSQL interactive terminal.
Type: \copyright for distribution terms
\h for help with SQL commands
\? for help on internal slash commands
\g or terminate with semicolon to execute query
\q to quit
template1=#
Lista 1 - Retorno da conexão com o banco template1 do PostgreSQL.
Agora que estamos logados no banco template1, precisamos
criar um usuário para manipular nosso banco de testes.
Antes, apenas uma palavra sobre o Banco de Dados template1.
Este banco de dados existe porque todos os outros bancos de dados criados no
PostgreSQL o utilizarão como modelo. Qualquer coisa que você ache necessário
ter em todos os seus bancos de dados deve ser inserida neste banco. A partir
dele o PostgreSQL fará os clones necessários. Veja que isto não é herança, conceito
que o PostgreSQL já suporta, mas que vamos abordar em outra matéria.
Já podemos criar nosso usuário, queremos que ele possa
criar um banco de dados e possa, também, criar grupos e usuários. Isso é
recomendável, já que não devemos utilizar o usuário postgres, visto que este é
um superusuário. Sempre aconselho que este usuário, postgres, deve ter sua
senha alterada e guardada pelo Administrador do Banco e o Gerente da Área. Esta
é uma ação que lhe vai poupar alguns grandes problemas de administração.
No prompt do bash digite a sintaxe que segue abaixo, o
exemplo da criação e conseqüente retorno do PostgreSQL estão na lista 2:
template1=# CREATE USER usuteste WITH PASSWORD 'abc1def2' CREATEDB CREATEUSER;
CREATE USER
template1=#
Lista 2 - Criação do usuário usuteste e a senha "abc1def2".
O PostgreSQL responde, para maioria dos comandos, o nome do
comando digitado. Veja que aqui, ele respondeu CREATE USER, significando que
está tudo bem com todos os aspectos deste comando.
Pronto, este usuário poderá se conectar ao banco template1
e criar outro banco. Esta é nossa próxima ação.
Criando linguagens
Neste passo, criaremos a linguagem plpgsql, que é poderosa
e vai-nos ajudar a criar as funções de acesso, que possibilitaram nossos
programadores criarem funções de acesso ao banco de dados.
Você pode criar variadas linguagens para utilizar no
PostgreSQL, isso faz parte do seu poder. Como exemplo você pode criar PL/Tcl,
PL/Perl, PL/Python e PL/pgSQL. Também é possÍvel você utilizar C.
O PostgreSQL, não cria estas linguagens inicialmente,
porque não sabe qual você utilizará. Para exemplo vamos criar suporte a
linguagem PL/pgSQL. Primeiramente, vamos criar o controlador da linguagem
PL/pgSQL, seu handler. Use a sintaxe mostrada a seguir em um arquivo de script
e o execute, conforme mostrado anteriormente, com o comando \i:
--Cria a função que controla o plpgsql
CREATE FUNCTION plpgsql_call_handler () RETURNS LANGUAGE_HANDLER AS
'$libdir/plpgsql' LANGUAGE C;
--Linguagem: plpgsql
CREATE TRUSTED PROCEDURAL LANGUAGE 'plpgsql'
HANDLER plpgsql_call_handler;
Cria a função que controla o plpgsql
Bem, isto é tudo de que precisamos, todos os nossos bancos
de dados poderão, doravante, se utilizar desta linguagem para criar funções de
acesso.
Criando o banco
Vamos criar um banco para nosso exemplo. Para nossos fins,
vou criar um banco, dando-lhe o nome de BancoTeste. Ele servirá para nossas
experiências com o PostgreSQL.
O usuário que estiver logado no momento da criação do
banco, se torna o proprietário deste. É quem poderá destruir este banco,
posteriormente, caso necessário. Ao destruir um banco, todos os seus objetos e
dados são, também, destruÍdos.
Apenas o primeiro banco de dados, template1, não precisou
de um procedimento especial para ser criado, pois quem o criou foi o comando
initdb, que tratou, também, de inicializar as áreas de dados.
Se você usar os procedimentos de instalação sugeridos pelo
pessoal da Visual Objects (http://www.visualobjects.hpg.ig.com.br/postgresql.htm)
e ler o script deles, entenderá bem o que o comando initdb faz.
A sintaxe para a criação do banco de dados é: CREATE
DATABASE nome do banco <enter>.
O PostgreSQL fornece um programa alternativo para a criação
de banco, assim como para a maioria dos comandos de administração de bancos.
No caso da criação de banco existe a sintaxe alternativa de
createdb nome do banco <enter>, que não realiza mágica
alguma, conecta-se ao banco template1 e executa o comando CREATE DATABASE como
você o faria.
Pode haver uma situação que você quer que o banco tenha
outro proprietário, isto acontece em aplicações Internet onde você tem vários
tipos de banco de dados, por motivos de segurança. Isto pode ser feito tanto no
comando CREATE DATABASE como no programa createdb, basta utilizar a sintaxe
CREATE DATABASE nome do banco OWNER nome do proprietário.
No nosso exemplo, podemos nos desconectar do template1 e
nos conectarmos como usuteste, para criarmos este novo banco ou utilizarmos a
sintaxe com OWNER, é o que faremos.
Digite, no prompt do bash:
CREATE DATABASE bancoteste
OWNER usuteste< enter>;
Observe, na lista 3, a digitação deste comando e o
conseqüente retorno do PostgreSQL.
template1=# CREATE DATABASE bancoteste OWNER usuteste;
CREATE DATABASE
template1=#
Lista 3 - Criação do Banco BancoTeste com o proprietário UsuTeste.
Vamos criar nossas tabelas.
Criando Tabelas
Bem, reconheçamos que não é muito inteligente você fazer
todo o trabalho de digitação para criação de todos os procedimentos no prompt
do bash. Assim, vou sugerir que você crie scripts. Como, já abordamos seção ferramentas,
você deve ter um diretório criado para os seus scripts. No nosso exemplo é o E:\PostgreSql\scripts.
Vamos usá-lo. Criaremos um arquivo “.sql“ e o salvaremos como
criatb.sql e o executaremos no prompt do bash.
Primeiro digite as seguintes linhas no arquivo criatb.sql:
-- Seqüência: sql_tb_cliente_seq
CREATE SEQUENCE sql_tb_cliente_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1;
SELECT setval('sql_tb_cliente_seq', 1);
-- Cria Tabela: tb_cliente
CREATE TABLE tb_cliente (
iid_cliente int4 DEFAULT nextval('sql_tb_cliente_seq'::text) NOT NULL,
s_nome varchar(100),
s_email varchar(50),
b_opt boolean,
CONSTRAINT sql_cliente_pkey PRIMARY KEY (iid_cliente)
) WITHOUT OIDS;
Aqui são necessárias algumas considerações. A seqüência é
algo que uso, para todas as tabelas, quando trabalho com um banco relacional.
Visto que trabalho com aplicações orientadas a objetos e bancos relacionais,
crio a seqüência para emular o object identifier, requerido pela Orientação a
Objetos. É apenas uma questão de proselitismo, já que prosélito pode ser
considerado aquele que se aproxima assim com o grego informa: gr. prosélytos,
'aquele que se aproxima', pelo lat. tard. Proselytu.J>))!. Bem,
está seria a função da palavra chave OIDS que segue a sintaxe da criação de
tabelas pelo PostgreSQL. Caso eu queira usar o que o PostgreSQL me sugere,
basta substituir WITHOUT OIDS por WITH OIDS. Somente utilizo esta sintaxe para
tabelas que irão se beneficiar da herança, do contrário é conhecido problemas
de performance com este comando para tabelas muito grandes. Assim, se você usar
herança e tabelas pequenas, use WITH OIDS sem medo.
CREATE SEQUENCE cria uma seqüência que pode ser usada por
qualquer tabela, chamada de sql_tb_cliente_seq. Dei-lhe este nome, porque quero
que apenas a tabela tb_cliente a utilize. A seqüência será incrementada sempre
em 1 e se iniciará em 1. Seu teto máximo é o número absurdo depois da clausula
MAXVALUE, setval já informa o valor do número inicial, que é 1. O PostgreSQL,
fornece nextval, que lhe retornará o próximo de uma seqüência e, também, curval
que retorna a seqüência corrente. Pesquise a utilização.
Na criação da tabela, informo que o número que utilizará
seqüência é um número de 4 bytes (int4), poderia ser de 8 bytes (bigint).
O campo iid_cliente irá guardar o nosso número seqüencial
de cliente, é um inteiro de 4 bytes.
O campo s_nome irá guardar o nome do nosso cliente, é do
tipo varchar com no máximo 100 bytes. Tem o tipo de dado varchar porque pode
ter qualquer quantidade de 1 a 100 bytes.
O campo s_email irá guardar o e-mail do nosso cliente, é do
tipo varchar com no máximo 50 bytes.
O campo b_opt é um flag, utilizado apenas para mostrarmos o
uso de tipos de dados boolean.
Crie, sempre, em primeiro lugar as seqüências para depois
partir para a criação das tabelas que a utilizam.
Bem, veja a seqüência de comandos que me utilizei para
realizar a criação das tabelas. A lista 4, mostra esta seqüência de forma
completa.
template1=# \q
esales@atenas ~
$ cd /cygdrive/e/postgresql/scripts/bancoteste
esales@atenas /cygdrive/e/postgresql/scripts/bancoteste
$ ls
criatb.sql
esales@atenas /cygdrive/e/postgresql/scripts/bancoteste
$ psql -U usuteste bancoteste
Welcome to psql 7.3.2, the PostgreSQL interactive terminal.
Type: \copyright for distribution terms
\h for help with SQL commands
\? for help on internal slash commands
\g or terminate with semicolon to execute query
\q to quit
bancoteste=# \i criatb.sql
CREATE SEQUENCE
setval
--------
1
(1 row)
psql:criatb.sql:12: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit ind
ex 'sql_cliente_pkey' for table 'tb_cliente'
CREATE TABLE
bancoteste=#
Lista 4 - A seq¨ência de criação de tabela, com um arquivo de script.
Algumas poucas explicações.
O comando \q, permitiu que eu saÍsse do banco template1,
equivale a quit.
O comando cd permitiu a mudança para o diretório onde estão
meus scripts.
O comando ls listou os arquivos do meu diretório de script,
no meu caso tinha apenas um script chamado de criatb.sql.
O comando psql –U usuteste bancoteste, permitiu que eu me
conectasse ao banco bancoteste criado utilizando o usuário criado
anteriormente.
O comando \i permitiu que eu definisse um arquivo de script
para ser executado, no nosso caso criatb.sql. Já que estamos no diretório do
script não precisamos dar maiores informações.
O PostgreSQL, informou a criação da seqüência com o retorno
CREATE SEQUENCE e o retorno CREATE TABLE, informou a perfeita criação da
tabela. Veja, também, que o PostgreSQL informou uma nota, que nos posiciona
sobre a criação de uma PK, ou primary key, no nosso caso sql_cliente_pkey que se refere
ao campo iid_cliente, conforme mostramos na criação da tabela. De
maneira alguma crie uma tabela sem a devida chave primária. Caso eu tivesse
mais de um campo, na chave primaria, informaria isso os separando por virgula.
Suponha que eu quisesse criar uma FK (foreign key),
utilizaria a sintaxe abaixo, logo após a constraint de criação de chave
primaria.
CONSTRAINT FK_nome_da_tabela_fkey FOREIGN KEY (nome_do_campo)
REFERENCES nome_da_tabela_que_contem_o_campo_origem.
Suponha, também que eu quisesse criar um Índice adicional
nesta tabela, bastava utilizar a sintaxe a seguir:
CREATE INDEX nome_do_indice ON nome_da_tabela (nome_do_campo_que_sera_indice);
Funções de Acesso
Bem, criamos o banco e suas tabelas, assim como suas
seqüências, chaves primárias, estrangeiras e Índices adicionais.
Para concluirmos, precisamos, fornecer a nossos
programadores, exemplos de acesso. Como estamos na era da Internet, vamos
municiar nossos programadores com exemplos de acesso, usando PHP. A partir do
PHP, os programadores podem expandir nosso exemplo para qualquer outra linguagem,
apesar de que no futuro, vou-lhes fornecer exemplos em JAVA, pois acho
altamente pertinente.
Como bons administradores de dados, vamos deixar na
Intranet da empresa, nosso modelo relacional, nosso dicionário de dados à
disposição de nosso pessoal técnico, como também nossos exemplos de acesso para
consulta futura dos nossos programadores. Isso tudo faz parte do
desenvolvimento interativo de software que tanto defendo.
Excluindo objetos e dados do banco
Todos os comandos de criação de funções, serão executados a
partir de um arquivo de script, conforme já mostrado, assim não vou repetir
estes passos. Gostaria de preveni-lo de que talvez seja necessário um script de
destruição. Para isso siga a sintaxe a seguir:
DROP FUNCTION nome_da_função(tipos_de_dados_dos_argumentos);
DROP SEQUENCE nome_da_seqüência;
DROP TABLE nome_da_tabela;
DROP DATABASE nome_do_banco_de_dados;
Para simplesmente esvaziar uma tabela, use:
TRUNCATE TABLE nome_da_tabela;
Você pode ter dois scripts, um de criação de funções e
outro de deleção. Ao aparecerem erros, destrua todas as funções e crie todas
novamente.
Função de Inclusão
Nossas funções de inserção seguirão um padrão. Isso é muito
interessante, já que podemos automatizar estas criações, usando uma aplicação
em Java ou mesmo em PHP. Todas as tabelas terão, pelo menos, uma função de
incluir, de excluir, de alterar, de retornar todos e de retornar apenas um.
Este enfadonho trabalho pode ser automatizado por esta aplicação.
Para criar uma função você necessita apenas de uma padronização
de nomes e do dicionário de dados para conhecer os nomes e tipos de atributos
de uma tabela. Criemos a função de inclusão, conforme segue:
CREATE OR REPLACE FUNCTION nome_da_função(tipos_esperados) returns integer as '
DECLARE
Seção de declarações
BEGIN
INSERT INTO tabela (nome_dos_campos_da_tabela)
VALUES (valores_de_entrada_na_função);
RETURN 1;
END;' LANGUAGE 'plpgsql';
Não temos novidades aqui. A sintaxe é auto-explicativa.
Vamos diretamente ao que foi retornado pelo bash, quando digitamos esta função
no seu prompt. A lista 5 mostra esta evolução.
bancoteste=# CREATE OR REPLACE FUNCTION proc_insert_tb_cliente(varchar(100), varchar(50),
bancoteste(# boolean) returns integer as '
bancoteste'# DECLARE
bancoteste'# str_nome ALIAS FOR $1;
bancoteste'# str_email ALIAS FOR $2;
bancoteste'# bol_solic ALIAS FOR $3;
bancoteste'# BEGIN
bancoteste'# INSERT INTO tb_cliente (s_nome, s_email, b_opt)
bancoteste'# VALUES (upper(str_nome), lower(s_email), bol_solic);
bancoteste'# RETURN 1;
bancoteste'# END;' LANGUAGE 'plpgsql';
CREATE FUNCTION
bancoteste=#
Lista 5 - Criando uma função de incluir dados
As linhas digitas, no nosso arquivo de script, foram:
CREATE OR REPLACE FUNCTION proc_insert_tb_cliente(varchar(100), varchar(50),
boolean) returns integer as '
DECLARE
str_nome ALIAS FOR $1;
str_email ALIAS FOR $2;
bol_solic ALIAS FOR $3;
BEGIN
INSERT INTO tb_cliente (s_nome, s_email, b_opt)
VALUES (upper(str_nome), lower(s_email), bol_solic);
RETURN 1;
END;' LANGUAGE 'plpgsql';
Veja que são as mesmas que se apresentam no prompt do bash,
apenas que não tem o prompt do bash a precedê-las.
Algumas observações.
A função proc_insert_tb_cliente, espera três tipos de
dados: varchar(100), onde guardaremos o nome do cliente, varchar(50), onde
guardaremos o e-mail do cliente e nosso boolean para uso futuro. No PostgreSQL,
não informamos os nomes dos argumentos como acontecem em outros bancos, apenas
seus tipos. Existem outras variações porém somente as abordarei no futuro.
A seção de DECLARE é que resolve a questão de nomes, ali
criamos aliasses para as entradas, informando sua ordem através das diretivas
$1,$2,$n.
Dentro do bloco BEGIN e END, podemos e devemos utilizar
qualquer cláusula SQL que julguemos necessárias. Veja que utilizei as palavras
chaves upper e lower, somente como exemplo. Pesquise a documentação do
PostgreSQL, no formato HTML e que você encontra em:
Diretório de Instalação:\PostgreSql\cygwin\usr\doc\postgresql-7.3.2\html\index.html.
O retorno da função é um 1 apenas para indicar um retorno,
já que toda função deve retornar algo.
Mais à frente dou um exemplo de uma função que retorna um
cursor inteiro.
Vamos utilizar esta função fazendo um teste diretamente no
prompt do basch, você também utilizar um script para isso.
Digite as linhas abaixo e veja o retorno do PostgreSQL.
BEGIN;<enter>
select * from proc_insert_tb_cliente('Ernani Medeiros', 'esales-consultor@fabricaweb.com.br', TRUE);<enter>
COMMIT;<enter>
No basch, estas linhas ficariam como é mostrado na lista 6.
bancoteste=# BEGIN;
BEGIN
bancoteste=# select * from proc_insert_tb_cliente('Ernani Medeiros', 'esales-co
nsultor@fabricaweb.com.br', TRUE);
proc_insert_tb_cliente
------------------------
1
(1 row)
bancoteste=# COMMIT;
COMMIT
bancoteste=#
Lista 6 - Inserindo dados a partir de uma função.
O único comentário digno de nota aqui e que é a novidade é
a forma de chamarmos uma função no PostgreSQL. Sempre você utilizará a
seqüência de BEGIN; SELECT * FROM nome_da_função e seus argumentos, seguidos da
cláusula COMMIT;. Isso garantirá que o suporte a transações do banco está sendo
usado. Qualquer retorno diferente disso, é um erro. Verifique a sua digitação.
O PostgreSQL retorna o número da linha que contém o erro e o possÍvel erro.
Corrija os erros e execute novamente a seqüência de CREATE OR REPLACE FUNCTION,
pois esta substituirá a que contém erros. Verifique que se você tentar usar
este comando com uma função cujo nome foi alterado, o PostgreSQL não permitirá.
Primeiro será necessária o drop na função anterior para em seguida à utilização
de CREATE OR REPLACE FUNCTION. O retorno de 1 row, indica que o dado está
inserido na nossa tabela. Para checarmos isso, basta executarmos, conforme a
lista 7.
bancoteste=# SELECT * FROM tb_cliente;
iid_cliente | s_nome | s_email | b_opt
-------------+-----------------+------------------------------------+-------
2 | ERNANI MEDEIROS | esales-consultor@fabricaweb.com.br | t
(1 row)
bancoteste=#
Lista 7 - Retorno de um select na tabela de cliente.
Criando as funções de Update e Delete
Seguindo o mesmo padrão, vamos criar as funções de update e
delete para a tabela tb_cliente. Em seguida, você vê o que deve ser digitado e
a resposta do bash.
Função de Update:
CREATE OR REPLACE FUNCTION proc_update_tb_cliente(int4, varchar(100), varchar(50), boolean)
returns integer as '
DECLARE
id_cli ALIAS FOR $1;
str_nome ALIAS FOR $2;
str_email ALIAS FOR $3;
bol_solic ALIAS FOR $4;
BEGIN
UPDATE tb_cliente SET
s_nome=upper(str_nome),
s_email=lower(str_email),
b_opt=bol_solic
WHERE iid_cliente=id_cli;
RETURN 1;
END;' LANGUAGE 'plpgsql';
O único comentário aqui, é que precisamos do id do cliente
para efetivar o update. Em seguida digite no bash a seqüência:
BEGIN;<enter>
select * from proc_update_tb_cliente(2,'Ernani Medeiros', 'esales-consultor@fabricaweb.com.br', FALSE);<enter>
COMMIT;<enter>
Observe, agora, a lista 8 com o resultado. O primeiro
select mostra o registro com o campo b_opt com o valor “t“, já o segundo mostra
“f“, portanto a execução da função foi bem sucedida.
bancoteste=# SELECT * FROM tb_cliente;
iid_cliente | s_nome | s_email | b_opt
-------------+-----------------+------------------------------------+-------
2 | ERNANI MEDEIROS | esales-consultor@fabricaweb.com.br | t
(1 row)
bancoteste=# BEGIN;
BEGIN
bancoteste=# select * from proc_update_tb_cliente(2,'Ernani Medeiros', 'esales-
consultor@fabricaweb.com.br', FALSE);
proc_update_tb_cliente
------------------------
1
(1 row)
bancoteste=# COMMIT;
COMMIT
bancoteste=# SELECT * FROM tb_cliente;
iid_cliente | s_nome | s_email | b_opt
-------------+-----------------+------------------------------------+-------
2 | ERNANI MEDEIROS | esales-consultor@fabricaweb.com.br | f
(1 row)
bancoteste=#
Lista 8 - Executando a função de update
Para a função de delete digite os seguintes comandos no
arquivo de script:
CREATE OR REPLACE FUNCTION proc_delete_tb_cliente(int4)
returns integer as '
DECLARE
id_cli ALIAS FOR $1;
BEGIN
DELETE FROM tb_cliente
WHERE iid_cliente=id_cli;
RETURN 1;
END;' LANGUAGE 'plpgsql';
Em seguida, digite as seguintes linhas no prompt do basch:
BEGIN;<enter>
select * from proc_delete_tb_cliente(2);<enter>
COMMIT;<enter>
Não existe nenhum segredo nesta função, precisamos apenas
do id do nosso cliente para podermos excluÍ-lo. Veja que na maioria das vezes,
não executamos um delete fÍsico como mostrado aqui. Normalmente realizamos um
update mudando um campo flag, que indicaria que o registro não é mais ativo,
dentro da função de delete. A exclusão fÍsica e não lógica de um registro é
apenas uma questão de segurança.
Veja as linhas do basch, mostrando a execução da função de
deleção e o select, provando que a deleção ocorreu realmente, logo em seguida
na lista 9.
bancoteste=# BEGIN;
BEGIN
bancoteste=# select * from proc_delete_tb_cliente(2);
proc_delete_tb_cliente
------------------------
1
(1 row)
bancoteste=# COMMIT;
COMMIT
bancoteste=# SELECT * FROM tb_cliente;
iid_cliente | s_nome | s_email | b_opt
-------------+--------+---------+-------
(0 rows)
bancoteste=#
Lista 9 - Executando a função de Deleção
Criando as funções de Consulta
Talvez não existam funções de maior uso do que as funções
que realizam consultas ao banco de dados. Absolutamente, todos os programas que
utilizam se de banco de dados realizam consultas. É, alias para isso que um
banco existe: persistir dados e recuperá-los. Por isso veja com maus olhos a
deleção de registros de um banco de dados.
Bem, para retornarmos um registro de um banco de dados,
utilizamos a palavra reservada RECORD. Dentro desta estrutura de dados, conterá
o registro que desejamos. Digite os seguintes comandos em um arquivo de script:
CREATE OR REPLACE FUNCTION proc_obterum_tb_cliente(int) RETURNS SETOF RECORD AS '
SELECT * FROM tb_cliente WHERE iid_cliente = $1;
' LANGUAGE SQL;
Veja a forma que peço ao PostgreSQL para retornar um
registro (RETURNS SETOF RECORD). Aqui alterei a linguagem para SQL puro, o que
não implica em criação de nenhuma linguagem, pois isso já é default do
PostgreSQL, fiz isso para exemplificar uma nova forma de retorno de um único
registro. Acredite, existem outras formas. Vamos ver o retorno do bach. Porém,
peço a sua atenção para a forma como chamo esta função.
Passo para a função dois arrays, um contendo a estrutura
dos campos que desejo retirar e outra contendo a estrutura em que desejo manter
o retorno. Isso é outra demonstração de poder do PostgreSQL, pois posso
retornar o que quiser de um registro bastando, para isso, definir na minha cláusula Select os campos que desejo, aqui
pedi todos.
Isto não deixa de ser um cursor. Existem outras formas além
do RECORD, espero ter-lhe encorajado a ir à pesquisa. A lista 10 mostra o
resultado da execução desta função.
bancoteste=# BEGIN;
BEGIN
bancoteste=# SELECT * FROM proc_obterum_tb_cliente(3) AS (f1 int, f2 varchar(10
0), f3 varchar(50), f4 boolean);
f1 | f2 | f3 | f4
----+-----------------+------------------------------------+----
3 | ERNANI MEDEIROS | esales-consultor@fabricaweb.com.br | t
(1 row)
bancoteste=# COMMIT;
COMMIT
bancoteste=#
Lista 10 - Executando a função que retorna um registro
Agora vamos retornar vários registros, a diferença que
merece nota aqui é a criação de um cursor. Este possibilita o retorno de um
conjunto de registros que respeita a sua cláusula select.
Digite as seguintes linhas, para a criação desta função:
CREATE OR REPLACE FUNCTION proc_obtertodos_tb_cliente(int4) RETURNS refcursor AS '
DECLARE cur refcursor;
reginicial ALIAS FOR $1;
BEGIN
OPEN cur FOR SELECT *
FROM tb_cliente
WHERE iid_cliente < reginicial
ORDER BY iid_cliente
LIMIT 50;
RETURN cur;
END;' LANGUAGE 'plpgsql';
Algumas observações são necessárias. Veja que define como
primeiro registro a ser mostrado o reginicial. Somente me interessam os
registros superiores a reginicial. Também, somente me interessam 50 registros
de cada vez, daÍ eu ter usado a cláusula LIMIT. Desta forma, posso criar um
sistema de navegação de 50 em 50 registros, basta-me passar para a função o
registro inicial, que sempre será o último da última consulta.
Isso é muito usado em sistemas de navegação de Internet. O
PostgreSQL, fornece uma palavra chave (OFFSET). Esta palavra chave informa ao
PostgreSQL quantas linhas devem ser puladas antes do inÍcio da seleção. OFFSET
igual a zero é o mesmo que não apresentar OFFSET. Se ambos estiverem na
cláusula SQL então, primeiro o PostgreSQL pula a quantidade pedida em OFFSET
para depois iniciar o retorno pedido em LIMIT.
No exemplo desprezei a cláusula OFFSET pois já sei quantos
registros quero e sempre sei onde devo começar. Caso usasse a cláusula OFFSET,
a sintaxe seria semelhante a:
BEGIN;<enter>
SELECT * FROM proc_obtertodos_tb_cliente(1);<enter>
FETCH ALL FROM "<unnamed cursor 1<";<enter>
COMMIT;<enter>
Veja a lista 11, com o resultado do bash.
bancoteste=# BEGIN;
BEGIN
bancoteste=# SELECT * FROM proc_obtertodos_tb_cliente(1);
proc_obtertodos_tb_cliente
----------------------------
<unnamed cursor 1>
(1 row)
bancoteste=# FETCH ALL FROM "<unnamed cursor 1<";
iid_cliente | s_nome | s_email | b_opt
-------------+-----------------+------------------------------------+-------
3 | ERNANI MEDEIROS | esales-consultor@fabricaweb.com.br | t
(1 row)
bancoteste=# COMMIT;
COMMIT
bancoteste=#
Lista 11 - Retornando vários registros em um cursor.
As observações são relativas ao nome do cursor. Como não
especifiquei o nome do cursor, o PostgreSQL retornou <unnamed cursor 1>.
Se outra chamada dentro da mesma seção, fosse feita a esta função, o cursor
seria <unnamed cursor 2>, <unnamed cursor n>, etc. Porém, como
todas as linguagens tem orientação para chamar, usar e fechar uma conexão logo
em seguida, isto não tem muita utilidade, mas não deixa de ser interessante
você também passar o nome do cursor para uma função que retorna vários
registros.
Utilizo o FETCH ALL FROM, porque sei que apenas receberei
50 registros de cada vez. Finalmente, como em todas as execuções realizo o
commit.
Montando o exemplo para PHP
Bem, agora só nos falta realizar estas mesmas chamadas a
partir de uma linguagem como o PHP, que alguns chamam de script. O PHP, tem um
conjunto de funções para serem utilizadas exclusivamente com o banco
PostgreSQL, como se banco nativo do PHP este fosse. Tal a importância que este
banco recebe do povo que desenvolve PHP. Sei, sei, sei, o MySql também, mas
falamos aqui do PostgreSQL não é?
Vamos lá, na tabela 1, você pode ver uma lista das funções
que são utilizadas pelo PHP para acesso ao PostgreSQL. Temos o nome da função e
seu uso. Para habilitar o uso destas funções, localize a entrada [PostgreSQL]
no arquivo PHP.ini e habilite as linhas desta seção. Por motivos de espaço, não
vamos reproduzi-las aqui. Vamos apenas reportar as funções que nos interessam,
veja a rica documentação do PHP para maiores detalhes
| Resumo de Funções de Acesso do PHP ao PostgreSQL |
| pg_connect() | Abre uma conexão PostgreSQL. |
| pg_query() | Executa uma consulta (query). |
| pg_num_rows() | Retorna o número de registros afetados. |
| pg_fetch_row() | Retorna uma linha como um array enumerado. |
| pg_close() | Fecha uma conexão com o Banco PostgreSQL. |
Estas funções do PHP, nos serão suficientes para o exemplo.
Veja os resultados, em html, retornados pelo browser.
Não temos necessidade de comentarmos estes resultados, eles
são visualmente auto-explicativos. Também não vamos comentar as linhas de
código em PHP feitas e que nas listas a seguir. As linhas estão comentadas,
diretamente no código.
É evidente que este é um ponto de partida e, mais do que
isto, é apenas um exemplo para nossos programadores passarem a utilizar o banco
na sua melhor forma: funções. São muito mais rápidas, com seu plano de acesso
já preparado pelo PostgreSQL e como pré-compiladas, o PostgreSQL não necessita
checar a sintaxe SQL a cada acesso.
Com o auxÍlio de uma ferramenta que crie estas funções por
tabela, não teremos muitos problemas para implementar este tipo de solução.
<?php
/*Aqui temos o pedido de que tipo de função desejamos:
1 - Queremos o usuo da função de inclusão;
2 - Queremos o uso da função de alteração;
3 - Queremos o uso da função de exclusão;
4 - Queremos o uso da função de retorna um registro;
5 - Queremos o uso da função que retorna 50 registros.*/
$tipoFuncaoSolicitada = 1;
Lista 12 - Inicio do código em PHP
function Incluir(){
//Aqui nos conectamos com o banco
$str_Conn = "host=localhost dbname=bancoteste port=5432 ";
$str_Conn .="user=usuteste password=abc1def2";
if(!($Conn=pg_connect($str_Conn)))
{
echo "<b>Não foi possÍvel estabelecer uma conexão com o
banco de dados, desculpe o transtorno.</b>\n";
exit;
}
/*Aqui montamos o SQL*/
$cur = "BEGIN; SELECT * FROM proc_insert_tb_cliente('Curso de UML com Interatividade de Software', 'atendimento@fabricaweb.com.br', TRUE);";
/*Aqui executamos a consulta contra o banco*/
$result = pg_query($Conn, $cur);
$num = pg_cmdtuples($result);
if($numLinhas=0){
echo "<b>Não Foi possÍvel a inclusão do Registro</b>";exit;}else{
/*Aqui enceramos a transação e tratamos de fechar o banco.*/
$result = pg_query($Conn, "COMMIT;");
pg_close($Conn);
Retorna50();}
return;
}
Lista 13 - A função de incluir em PHP
function Alterar(){
$str_Conn = "host=localhost dbname=bancoteste port=5432 ";
$str_Conn .="user=usuteste password=abc1def2";
if(!($Conn=pg_connect($str_Conn)))
{
echo "<b>Não foi possÍvel estabelecer uma conexão com o banco de dados, desculpe o transtorno.</b>\n";
exit;
}
/*Aqui montamos o SQL*/
$cur = "BEGIN; SELECT * FROM proc_update_tb_cliente(3,'Ernani Medeiros', 'esales-consultor@fabricaweb.com.br', TRUE);";
/*Aqui executamos a consulta contra o banco*/
$result = pg_query($Conn, $cur);
$num = pg_cmdtuples($result);
if($numLinhas=0){
echo "<b>Não Foi possÍvel a atualização do Registro</b>";
exit;}else{
/*Aqui enceramos a transação e tratamos de fechar o banco.*/
$result = pg_query($Conn, "COMMIT;");
pg_close($Conn);
Retorna1(3);}
return;
}
Lista 14 - A função de Alterar em PHP.
function Excluir($reg){
$str_Conn = "host=localhost dbname=bancoteste port=5432 ";
$str_Conn .="user=usuteste password=abc1def2";
if(!($Conn=pg_connect($str_Conn)))
{
echo "<b>Não foi possÍvel estabelecer uma conexão com o banco de dados, desculpe o transtorno.</b>\n";
exit;
}
/*Aqui montamos o SQL*/
$cur = "BEGIN; SELECT * FROM proc_delete_tb_cliente($reg);";
/*Aqui executamos a consulta contra o banco*/
$result = pg_query($Conn, $cur);
$num = pg_cmdtuples($result);
if($numLinhas=0){
echo "<b>Não Foi possÍvel a deleção do Registro</b>";
exit;}else{
/*Aqui enceramos a transação e tratamos de fechar o banco.*/
$result = pg_query($Conn, "COMMIT;");
pg_close($Conn);
Retorna50();}
return;
}
Lista 15 - A função de excluir em PHP.
function Retorna1($reg){
$str_Conn = "host=localhost dbname=bancoteste port=5432 ";
$str_Conn .="user=usuteste password=abc1def2";
if(!($Conn=pg_connect($str_Conn)))
{
echo "<b>Não foi possÍvel estabelecer uma conexão com o banco de dados, desculpe o transtorno.</b>\n";
exit;
}
/*Aqui montamos o SQL*/
$cur = "BEGIN; SELECT * FROM proc_obterum_tb_cliente($reg) AS ";
$cur .="(f1 int, f2 varchar(100), f3 varchar(50), f4 boolean);";
/*Aqui executamos a consulta contra o banco*/
$result = pg_query($Conn, $cur);
/*Aqui descobrimos quantas linhas retornaram.*/
$num = pg_num_rows($result);
/*Aqui montamos nossos registros.*/
if ($num>0){
$r = pg_fetch_row($result, $i);
$iid_cliente = $r[0];
$s_nome=$r[1];
$s_email=strtolower($r[2]);
$b_opt=$r[3];
/*Aqui colocamos o resultado em uma tabela HTML.*/
echo "<font color='#000099' face='Verdana, Arial, Helvetica, sans-serif'";
echo "style='font-size= x-small;background-color=#FFCC66;font-style=bold;' size='-1'>";
echo "<table width='90%' height='*' cols=4 border='1'><tr>";
echo "<td colspan='4' align='center'>Registros retornados do banco de dados</td></tr>";
echo "<tr><td>";echo $r[0];echo "</td>";
echo "<td>";echo $r[1];echo "</td>";
echo "<td>";echo $r[2];echo "</td>";
if($r[3]=="t")
{
echo "<td>";
echo "<input type='checkbox' checked name='sim"; echo $r[0]; echo "'></td>";
}
else
{
echo "<td>";
echo "<input type='checkbox' name='sim"; echo $r[0]; echo "'></td>";
}
echo "</tr>";
echo "</table></font>";
}else{
echo "<b>Nenhum registro retornado</b>";}
/*Aqui enceramos a transação e tratamos de fechar o banco.*/
$result = pg_query($Conn, "COMMIT;");
pg_close($Conn);
return;
}
Lista 16 - A função que retorna um registro em PHP.
function Retorna50(){
$str_Conn = "host=localhost dbname=bancoteste port=5432 ";
$str_Conn .="user=usuteste password=abc1def2";
if(!($Conn=pg_connect($str_Conn)))
{
echo "<b>Não foi possÍvel estabelecer uma conexão com o banco de dados, desculpe o transtorno.</b>\n";
exit;
}
/*Aqui montamos o SQL*/
$cur = "BEGIN; SELECT * FROM proc_obtertodos_tb_cliente(1);";
$cur .="FETCH ALL FROM \"<unnamed cursor 1>\";";
/*Aqui executamos a consulta contra o banco*/
$result = pg_query($Conn, $cur);
/*Aqui descobrimos quantas linhas retornaram.*/
$num = pg_num_rows($result);
/*Aqui montamos nossos registros.*/
echo "<font color='#000099' face='Verdana, Arial, Helvetica, sans-serif'";
echo "style='font-size= x-small;background-color=#FFCC66;font-style=bold;' size='-1'>";
echo "<table width='90%' height='*' cols=4 border='1'><tr>";
echo "<td colspan='4' align='center'>Registros retornados do banco de dados</td></tr>";
for ($i=0; $i < $num; $i++) {
$r = pg_fetch_row($result, $i);
$iid_cliente = $r[0];
$s_nome=$r[1];
$s_email=strtolower($r[2]);
$b_opt=$r[3];
/*Aqui colocamos o resultado em uma tabela HTML.*/
echo "<tr><td>";echo $r[0];echo "</td>";
echo "<td>";echo $r[1];echo "</td>";
echo "<td>";echo $r[2];echo "</td>";
if($r[3]=="t")
{
echo "<td>";
echo "<input type='checkbox' checked name='sim"; echo $r[0]; echo "'></td>";
}
else
{
echo "<td>";
echo "<input type='checkbox' name='sim"; echo $r[0]; echo "'></td>";
}
echo "</tr>";}
echo "</table></font>";
/*Aqui enceramos a transação e tratamos de fechar o banco.*/
$result = pg_query($Conn, "COMMIT;");
pg_close($Conn);
return;
}
Lista 17 - Função que retorna 50 registros em PHP.
switch ($tipoFuncaoSolicitada) {
case 1:
Incluir();
echo "<b>Solicitou a função de inclusão.</b><br>\n";
break;
case 2:
Alterar();
echo "<b>Solicitou a função de alteração.</b><br>\n";
break;
case 3:
Excluir(4);
echo "<b>Solicitou a função de exclusão do registro 4.</b><br>\n";
break;
case 4:
Retorna1(6);
echo "<b>Solicitou a função que retorna um registro.</b><br>\n";
break;
case 5:
Retorna50();
echo "<b>Solicitou a função que retorna 50 registros.</b><br>\n";
break;
}
?>
Lista 18 - Código final em PHP.
Conclusão
Bem, acredito que a disseminação destes tipos de exemplos é
que dão coragem para a adoção de uma nova tecnologia. Aqui, creio que seu
receio de mudança, esteja um tanto quanto mais reduzido porque o aprendizado
iniciou-se de forma menos traumática e com exemplos do nosso dia-a-dia.
Agora você tem a possibilidade de propor a redução em TCO,
em termos de banco de dados, porque já foi apresentado a exemplos consistentes
e pode iniciar um estudo de migração mais tranqüilo. O pessoal de
desenvolvimento também teve o seu suporte e atenção.
A esta introdução, junte as seguintes vantagens para
trabalhar com o PostgreSQL e repense seu relatório de PDI para 2004.
Suporte total ao padrão ACID, CompatÍvel com o SQL ANSI, Integridade
referencial, Replicação (soluções comerciais e não comerciais) permitindo a
duplicação do banco de dados mestre para várias máquinas escravo, Interfaces
nativas para ODBC, JDBC, C, C++, PHP, Perl, TCL, ECPG, Python, e Ruby. Rules
(Regras), Views (Visões), Triggers (Gatilhos), Unicode, Sequences (Sequências),
Herança, Outer Joins, Sub-selects (Subconsultas), API aberta e Stored
Procedures.
Suporte nativo a SSL, Suporte a linguagens procedurais, Hot
stand-by (soluções comerciais).
Índices parciais e funcionais ( feitos a partir de funções).
Autenticação Kerberos nativa, Suporte para consultas com UNION, UNION ALL e
EXCEPT. Extensões que oferecem SHA1, MD5, XML, e outras funcionalidades. Ferramentas
para gerar SQL portável para compartilhar com outras aplicações compatÍveis com
SQL. Sistema de tipagem de dados expansÍvel, provendo customização e criação de
novos tipos de dados definidos pelo usuário. Funções de compatibilização para
facilitar a transição de outros bancos de dados.
Se necessitar conhecer outras empresas que estão utilizando
o PostgreSQl, um bom começo é a URL em português: http://advocacy.postgresql.org/casestudies/?lang=br.
É evidente que isto não é tudo, isto é apenas o inÍcio da
pesquisa para se propor uma mudança mais consistente. Espero ter colaborado com
a solução de suas dúvidas a respeito do PostgreSQL. Obrigado pela atenção.
Obrigado por sua paciência.
|