Autoria: Fábrica Web      Versão no formato ".pdf"
  Funções de Acessos ao PostgreSQL - Uma visão prática.

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.

Design