Instituto Superior de Engenharia do Portodei.isep.ipp.pt/~paf/proj/Julho2003/Postgresql.pdf ·...

146
Instituto Superior de Engenharia do Porto Departamento de Engenharia Informática Autor: Miguel João Vieira Carvalho Versão: 1.0 Data: Julho de 2003

Transcript of Instituto Superior de Engenharia do Portodei.isep.ipp.pt/~paf/proj/Julho2003/Postgresql.pdf ·...

Instituto Superior de Engenhariado Porto

Departamento de Engenharia Informática

Autor: Miguel João Vieira Carvalho Versão: 1.0 Data: Julho de 2003

Agradecimentos

Página ii

Agradecimentos Gostava de agradecer de forma muito especial ao meu orientador, o Eng. Jorge

Coelho, pelo empenho que colocou neste projecto e pelas críticas e correcções que ao

longo das treze versões do documento foi sempre fazendo.

Gostava também de agradecer ao Eng. Alexandre Bragança pelo facto de ter-me

ajudado logo desde o início a seleccionar os temas que deveriam ser abordados e

também por ter sido um dos meus professores de bases de dados.

Quero também agradecer ao Eng. Aníbal Oliveira, por me ter feito entender na

cadeira de Bases de Dados I, de forma tão simples e eficaz, muitos dos conceitos que

foram apresentados ao longo deste documento.

Para finalizar queria agradecer ao meu irmão, João Carvalho, pelo apoio prestado na

elaboração de alguns elementos gráficos.

Índice

Página iii

Índice

1. Conceitos Básicos 16

1.1. Breve História do Aparecimento das Bases de Dados 16

1.2. Requisitos Fundamentais de uma Base de Dados 17

1.2.1. Segurança 17

1.2.2. Integridade 18

1.2.3. Controlo da Concorrência 19

1.2.4. Recuperação/Tolerância a Falhas 19

1.3. SGBD Vs Sistema de Gestão de Ficheiros 20

1.3.1. Abstracção dos Dados 20

1.3.2. Independência Programa/Dados 20

1.3.3. Partilha dos Dados 21

1.3.4. Diminuição da Redundância 21

1.3.5. Desenvolvimento e Manutenção 22

1.3.6. Integridade dos Dados 22

1.4. Base de dados Relacionais 22

1.5. SQL 25

1.5.1. Aspectos do SQL 25

1.5.2. Linguagem de Definição de Dados 25

1.5.3. Linguagem de Manipulação de Dados 26

1.5.4. Triggers 26

1.5.5. Gestão de Transacções 27

2. SQL – Introdução 29

2.1. SQL no PostgreSQL 31

3. Instalação Linux 39

Índice

Página iv

3.1. Instalação Linux Através de RPM 39

3.2. Instalação Linux Através de Source Code 40

4. Como utilizar 45

4.1. Clientes 45

4.1.1. psql 45

4.1.2. pgAccess 47

4.1.3. pgAdmin/II 48

4.1.4. winSQL 49

4.2. Como Utilizar – Exemplo Prático 50

4.2.1. Criação da Conta de Acesso 51

4.2.2. Criação da Base de Dados 52

4.2.3. Autorizar o Utilizador 53

4.2.4. Activar a Nova Configuração 56

4.3. Instalação do Driver ODBC do PostgreSQL 57

4.3.1. Criar o DSN 57

5. Programar com o PostgreSQL 61

5.1. A Interface de Programação libpq 61

5.1.1. Gestão de Ligações 62

5.1.1.1. A Função PQconnectdb 63

5.1.1.2. A função PQsetdbLogin 64

5.1.2. Funções Para Processamento Síncrono de Queries 64

5.1.2.1. A Função PQexec 64

5.1.2.2. A Função PQresultStatus 65

5.1.2.3. A Função PQclear 65

5.1.3. Funções Para Obtenção de Informação do Resultado 65

5.1.4. Funções Para Obtenção de Valores do Resultado 66

5.1.5. Funções Para Processamento Assíncrono de Queries 67

Índice

Página v

6. Funcionalidades Avançadas 69

6.1. Índices 69

6.1.1. Aparecimento dos Índices 69

6.1.2. Os Índices nos SGDBs 73

6.1.2.1. Representação por Árvores B+ 73

6.1.2.2. Representação por Hashing 75

6.1.3. Dicas para Uso de Índices 78

6.1.4. Índices no PostgreSQL 81

6.1.4.1. Índices Mono coluna 82

6.1.4.2. Índices Multi coluna 82

6.1.4.3. Índices Únicos 84

6.1.4.4. Índices Funcionais 85

6.1.4.5. Índices Parciais 85

6.1.5. Avaliar o Uso dos Índices 86

6.2. Stored Procedures 88

6.2.1. Vantagens/Desvantagens 89

6.2.2. A linguagem PL/pgSQL 89

6.2.2.1. A linguagem PL/pgSQL 89

6.3. Triggers 95

6.3.1. Triggers no PostgreSQL 98

6.4. Controlo da Concorrência 98

6.4.1. Transacções 100

6.4.1.1. Transacções no PostgreSQL 102

6.4.1.2. Níveis de Isolamento 105

7. Campos Binários (BLOBs) 107

7.1. BLOBs no PostgreSQL 107

8. Usar o PostgreSQL com o PHP na Web 111

Índice

Página vi

8.1. Arquitectura Tecnológica 111

8.2. Acesso a Campos Binários em PHP 115

9. Comparação Entre SGBDs 119

9.1. Comparação MySQL com PostgreSQL 123

10. Conclusão 130

11. Bibliografia 135

12. Exemplo de execução síncrona usando libpq 138

12.1. Exemplo de execução assíncrona usando libpq 140

12.2. Modelo de dados de referência ao documento 143

Índice de Figuras

Página vii

Índice de Figuras Figura 1 – Resultado da execução do comando \d clientes 47 Figura 2 – Ecrã pgAccess (Linux) que mostra o conteúdo de uma tabela 48 Figura 3 – Ecrã principal do pgAdminII no Windows XP 49 Figura 4 – Ecrã do winSQL que mostra a parte do dicionário de dados 50 Figura 5 – Exemplo de um ficheiro pg_hba.conf 53 Figura 6 – Exemplo de um ficheiro pg_hba.conf com métodos de

autenticação password e md5 54 Figura 7 – Secção (tab) UserDSN 58 Figura 8 – Janela de criação de um DSN 58 Figura 9 – Configuração de um DSN para o PostgreSQL 59 Figura 10 – Caminho simplificado a percorrer desde um query até atingir

os dados em disco 70 Figura 11 – Camadas a percorrer para aceder aos dados através de um

índice 72 Figura 12 – Exemplo de uma árvore B+ de ordem dois 73 Figura 13 – 1º passo na inserção dum nó cheio 74 Figura 14 – Divisão da raiz causada pelo novo nó 74 Figura 15 - Versão final da árvore após inserção do elemento 8* 75 Figura 16 – Exemplo de uma tabela de hash 76 Figura 17 – Função de hash, após a inserção das chaves 20 e 40 76 Figura 18 – Tabela de hash com overflow no bucket 3 77 Figura 19 – Estrutura da tabela clientes 82 Figura 20 – Exemplo de um resultado do planner a usar o índice

ndx_cl_nome_idade_cp 83 Figura 21 - Exemplo de um resultado do planner que não pode usar o

índice ndx_cl_nome_idade_cp 84 Figura 22 - Esquema simplificado da arquitectura tecnológica usando

o PHP e PostgreSQL 111 Figura 23 - Estrutura da tabela imagens 115 Figura 24 – Resultados do teste de concorrência Read/Update.

Fonte: [33] 124 Figura 25 – Resultados da página “Bug Tracker”. Fonte: [33] 125 Figura 26 – Resultado do teste “Forum”. Fonte: [33] 126

Índice de Tabelas

Página viii

Índice de Tabelas Tabela 1 – Exemplos de domínios 23

Tabela 2 – Relação entre tributes e tuplos 23

Tabela 3 – Lista dos comandos internos do psql 47

Tabela 4 – Informação necessária para a criação de um DSN 59

Tabela 5 – As interfaces de programação (API) do PostgreSQL 61

Tabela 6 – Alguns valores possíveis para os estados da execução de um query 65

Tabela 7 – Funções para obtenção de informação sobre o resultado 66

Tabela 8 – Funções para obtenção de valores do resultado 67

Tabela 9 – Dicas para uso de índices 80

Tabela 10 – Relação entre o tipo de índice, operadores suportados e aplicação prática 81

Tabela 11 – Género de índices suportados 81

Tabela 12 – Relação entre o nível de isolamento e os três fenómenos ligados às transacções 105

Tabela 13 – Bytes que necessitam de tratmento especial na inserção 107

Tabela 14 – Funções para tratamento anterior e posterior de BLOBs 108

Tabela 15 – Exemplo código PHP para extrair bytes de um campo bytea 114

Índice de Exemplos

Página ix

Índice de Exemplos Exemplo 1 - Query SELECT simples 34

Exemplo 2 - Query SELECT com filtragem 34

Exemplo 3 - Query SELECT com filtragem de duas expressões lógicas e a cláusula BETWEEN 34

Exemplo 4 - Uso da função agregadora count num query SELECT 34

Exemplo 5 — Uso da função agregadora count juntamente com a cláusula WHERE 34

Exemplo 6 - Uso das funções agregadoras min, max juntamente com a cláusula GROUP BY 35

Exemplo 7 - Uso de sub queries 35

Exemplo 8 - Uso da cláusula AS juntamente com um join 36

Exemplo 9 - Uso de um join através da cláusula WHERE 36

Exemplo 10 - Uso de um join através da cláusula WHERE 37

Exemplo 11 - Uso da cláusula NOT IN e EXCEPT 37

Exemplo 12 – Parâmetros mais frequentes usados na compilação e seu significado 41

Introdução

Página 10

Introdução

Este documento pretende ser um tutorial sobre uma base de dados relacional e open

source que se chama PostgreSQL.

O público-alvo deste documento visa em primeiro lugar os principiantes na área das

bases de dados, sem nunca esquecer os utilizadores mais avançados.

No que respeita aos principiantes as abordagens dos assuntos, são feitas usando

situações do dia a dia, seguidas de exemplos de utilização prática e de alguns

conselhos para um melhor desempenho.

Em relação aos utilizadores com mais experiência, estes podem sem dúvida tirar

partido deste documento, uma vez que são abordados tópicos avançados.

Para dar suporte aos exemplos apresentados, usar-se um modelo de dados que foi

criado propositadamente no decurso deste trabalho. Este modelo diz respeito a um

cinema. Este foi o modelo de dados que me pareceu mais apropriado tendo em conta o

público alvo.

No primeiro capítulo e a pensar nos utilizadores novatos, começa-se por explicar

como é que apareceram bases de dados. São referidas características tais como:

segurança, integridade, acessos concorrentes, recuperação e tolerância a falhas. Para

explicar porque é que as bases de dados são importantes, faz-se uma analogia entre

um sistema de gestão de ficheiros e um SGBD – Sistema de Gestão de Bases de

Dados a vários níveis: abstracção dos dados; independência entre aplicação e dados;

partilha de dados; redução da redundância; integridade dos dados. Após vistas as

diferenças entre os sistemas anteriores, é feita a definição de SQL — Structured

Query Language e abordadas as sub linguagens de definição e manipulação dos

dados. São abordados mecanismos adicionais de manutenção de integridade, triggers

e transacções.

No segundo capítulo é feita uma introdução ao SQL em que são explicadas as

instruções de definição e manipulação de dados. Relativamente às de definição de

dados é explicada cada uma das instruções e significado de cada uma das várias

cláusulas, usando-se como base a criação do modelo de dados de referência. Em

relação às de manipulação, são abordadas as quatro instruções SQL (SELECT, INSERT,

UPDATE e DELETE). Em relação à instrução de criação de registos é explicada cada uma

Introdução

Página 11

das várias formas que esta apresenta, juntamente com conselhos de utilização e

exemplos. Uma vez completa a introdução ao SQL, são apresentadas as características

da implementação do SQL a nível do PostgreSQL. A seguir dá-se início à

apresentação de perguntas em linguagem natural e o equivalente em termos de SQL

usando o modelo de referência. Ao longo das várias perguntas, vão sendo

introduzidos lentamente conceitos relacionados com forma de escrita dos queries.

Sempre que possível são apresentadas várias alternativas e respectivos prós e contras

para o uso de cada uma, abordando-se cláusulas desde as mais simples até às mais

complexas (ousa seja: INTERSECT, EXCEPT, etc.).

No terceiro capítulo é explicado todo o processo de instalação passo a passo do

PostgreSQL (acompanhado sempre que necessário de comentários) no Linux, quer

através de packages binários (RPMs) quer através de código fonte.

No quarto capítulo começa-se por enumerar alguns dos clientes Linux e Windows que

se podem usar para ligar ao PostgreSQL. Para cada um destes são mostradas as

características mais relevantes e ecrãs. Ainda relativamente aos clientes é explicado

como usar o cliente psql o qual é instalado por omissão. Uma vez terminada a

apresentação dos clientes, avança-se para um exemplo prático do que se deve fazer

para se ter acesso ao PostgreSQL. Em concreto, explica-se a criação de utilizadores;

criação de bases de dados; autorização de utilizadores usando os vários métodos de

autenticação. A pensar nos utilizadores que queiram aceder ou que tenham instalado o

PostgreSQL no Windows, é explicado passo a passo como é que se cria um Data

Source Name para ser usado pelos clientes que suportem ODBC - Open Database

Connectivity.

No quinto capítulo abordam-se assuntos relacionados com a utilização de linguagens

de programação com o PostgreSQL. Em concreto são identificadas todas as

linguagens em que é possível usar o PostgreSQL. É explicado e demonstrado através

de exemplos em linguagem C, o modo de funcionamento da interface de

programação. Nos últimos destacam-se: funcionamento do estabelecimento de

ligações quer assíncronas que síncronas; execução assíncrona e síncrona de queries;

obtenção de resultados e informações relativas aos resultados.

Introdução

Página 12

No sexto capítulo são abordados todos os temas que julgo serem de uso mais

avançado, entre o qual se destacam: os índices; stored procedures; triggers; controlo

da concorrência e transacções. Em relação aos índices explicam-se quais os motivos

que levaram ao seu aparecimento. Apresentam-se as camadas a percorrer desde o

índice até aos dados para que o leitor fique com uma ideia de como estes funcionam.

Demonstra-se de seguida dois métodos de indexação, recorrendo a árvores B+ e

funções de hash. Relativamente a cada um dos métodos são explicados os

mecanismos usados na implementação e enumeradas as vantagens e as desvantagens

de cada um deles. Após a explicação dos métodos de indexação, fornecem-se dicas

relativamente a situações em que é oportuna a criação de índices e em que situações

não se devem sequer criar índices. Seguidamente são apresentados os tipos e géneros

de índices suportados pelo PostgreSQL. Durante a apresentação de cada um dos

géneros dos índices suportados, são mostrados exemplos de comandos para criação de

índices dos respectivos tipos e géneros. São ainda demonstrados e explicados quais os

factores que levam a que certos queries usem índices e outros não o usem. A

demonstração do uso e não uso dos índices é feita através da análise de planos de

execução de queries executados. No final da secção destinada aos índices são

apresentadas formas de como se deve verificar e avaliar o uso dos índices.

Em relação aos stored procedures é explicado o que são; quais as linguagens em que

podem ser escritos; enumeradas as vantagens do uso destes. Após a apresentação dos

stored procedures explica-se a linguagem procedimental PL/pgSQL recorrendo a

analogias e a exemplos práticos simples. Após introduzida a linguagem e apresentada

a estrutura base de um stored procedure em PL/pgSQL, são apresentados e explicados

alguns tipos de dados especiais, passando de seguida para a definição e uso de

cursores.

No que se refere aos triggers, explica-se o que é, para que servem e como devem ser

usados. Explicam-se os vários tipos de triggers suportados e os eventos que podem

ser usados e relação que existe entre o eventos e certas variáveis especiais criadas

pelo PostgreSQL. Após a definição dos eventos, avança-se para uma explicação

prática de como se define um trigger numa tabela. Com a informação geral sobre

triggers apresentada, são enumeradas algumas das funcionalidades que não são

implementadas ou são implementadas parcialmente pelo PostgreSQL a nível dos

triggers.

Introdução

Página 13

No que respeita ao controlo da concorrência, é explicado no início o que se entende

por concorrência no âmbito dos SGBD. Seguidamente são apresentados os vários

mecanismos de bloqueio (tabelas, data pages, registos) usando um exemplo do dia a

dia como suporte. Ao longo da apresentação de cada um dos mecanismos de bloqueio

são apresentadas vantagens e desvantagens e respectivas consequências do uso.

Na abordagem das transacções é explicado o que é e para que servem, usando o

contexto do exemplo usado nos mecanismos de controlo da concorrência. São

apresentadas e explicadas as quatro características principais de uma transacção, mais

concretamente o princípio ACID — Atomicity, Consistency, Isolation and Durability.

Seguidamente será explicado qual o mecanismo usado pelo PostgreSQL para que as

transacções possam existir. Neste ponto é introduzido o conceito WAL — Write

Ahead Logging. De seguida é apresentada e explicada a forma como o PostgreSQL e

outros SGBDs conseguem manter a consistência sem que seja necessário usar

bloqueios quer de “leitores” quer “escritores” e vice-versa. Depois serão apresentados

os dois níveis de isolamento que o PostgreSQL suporta.

No sétimo capítulo e a pensar naqueles utilizadores que necessitem de armazenar

conteúdos binários (BLOBs) dentro das bases de dados, é explicada a forma standard

de o fazer e uma forma específica do PostgreSQL.

No oitavo capítulo fala-se sobre a arquitectura tecnológica necessária à interligação

entre PHP e PostgreSQL. Durante a apresentação, são desmistificados alguns erros

frequentes relacionados com as ligações às bases de dados por parte dos utilizadores

PHP. Seguidamente é explicado através de código PHP (comentado) e usando o

exemplo de referência do documento, como é que se insere uma imagem num BLOB e

como se extrai a mesma a partir da base de dados.

No nono capítulo faz-se uma comparação entre SGBDs open source e entre o

PostgreSQL e as implementações comerciais. Analisa-se alguns resultados de

benchmark realizados entre o MySQL e o PostgreSQL. Mostram-se algumas opiniões

de utilizadores que usam quer o MySQL quer o PostgreSQL.

No décimo capítulo será apresentada a conclusão do documento. Uma vez o

PostgreSQL é open source, será também abordado alguns dos receios dos utilizadores

em usar software open source.

Introdução

Página 14

No décimo primeiro capítulo, são apresentadas as referências bibliográficas e links

que são mencionados ao longo do documento.

No décimo segundo capítulo são apresentados dois exemplos em linguagem C que

usam quer ligações síncronas que assíncronas. São ainda apresentados nesses mesmos

exemplos funções que permitem efectuar processamento síncrono e assíncrono de

queries. No final deste encontram-se as instruções DDL usadas para criar o modelo de

dados de suporte aos exemplos do documento.

01

1.1. Breve História do Aparecimento das Bases de Dados 16

1.2. Requisitos Fundamentais de uma Base de Dados 17

1.2.1. Segurança 17

1.2.2. Integridade 18

1.2.3. Controlo da Concorrência 19

1.2.4. Recuperação/Tolerância a Falhas 19

1.3. SGBD Vs Sistema de Gestão de Ficheiros 20

1.3.1. Abstracção dos Dados 20

1.3.2. Independência Programa/Dados 20

1.3.3. Partilha dos Dados 21

1.3.4. Diminuição da Redundância 21

1.3.5. Desenvolvimento e Manutenção 22

1.3.6. Integridade dos Dados 22

1.4. Base de dados Relacionais 22

1.5. SQL 25

1.5.1. Linguagem de Definição de Dados 25

1.5.3. Linguagem de Manipulação de Dados 26

1.5.4. Triggers 26

1.5.5. Gestão de Transacções 27

Conceitos Básicos - Capítulo 1

Página 16

1. Conceitos Básicos

Pretende-se com este capítulo abordar algumas das fases pelas quais o processamento

da informação foi passando ao longo dos tempos. Uma vez mencionados os factores

históricos, explica-se qual a finalidade de uma base de dados e quais os motivos pelos

quais estas devem ser usadas. Por fim explica-se como é que se pode tirar o devido

partido de uma base de dados.

Os conceitos expostos neste capítulo serão amplamente usados ao longo de todo o

documento, daí que é aconselhável que o leitor os compreenda.

1.1. Breve História do Aparecimento das Bases de Dados

Desde sempre as pessoas tiveram necessidade de registar e alterar os dados inerentes

às suas actividades. O meio empresarial foi aquele que mais sentiu a necessidade de

guardar os dados relativos às actividades empresariais uma vez que os negócios

dependiam desses mesmos dados armazenados. No início a organização era feita

através de arquivos que no seu interior guardavam fichas de papel (organizadas de

forma alfabética) que continham os dados relativos a cada transacção efectuada. Caso

fosse necessário, por exemplo, saber qual o total da facturação para um dado

momento da vida da empresa, era necessário que alguém somasse o valor de cada

transacção para cada um dos clientes. De regra geral, cada departamento da empresa

armazenava informação duplicada (ou seja: dados do cliente como morada, contactos,

etc.). Todo este processo por mais organizado que o arquivo estivesse não apresentava

um elevado grau de coerência e era como seria de esperar um processo bastante lento.

Com o aparecimento dos primeiros computadores, apareceram aplicações que

passaram a possibilitar a consulta da mesma informação (anteriormente armazenada

em arquivo e pastas de papel) mas de forma mais rápida. Desta forma havia várias

aplicações por departamento para fins específicos (facturar, consultar stocks, etc.).

Estas aplicações tinham como base uma série de ficheiros que eram independentes

entre cada uma das aplicações e que geralmente só podiam ser usados (salvo raras

excepções) por um único programa.

Conceitos Básicos - Capítulo 1

Página 17

Contudo, com o uso das novas aplicações o processo embora fosse mais simples e

rápido continuava a haver a mesma informação (Por exemplo: morada, contactos dos

clientes, etc.) em vários ficheiros diferentes. Caso um cliente tivesse por algum

motivo que alterar a sua ficha de cliente, comunicava a alteração a alguém da

empresa, mas essa informação actualizada, raramente era fornecida aos outros

departamentos, causando desta forma incoerências.

Embora o processo estivesse bastante melhor, sempre que era necessário criar uma

listagem que ainda não tivesse sido criada anteriormente, era inevitável que alguém

criasse um programa que através da consulta directa dos ficheiros fosse capaz de

calcular os dados. Escusado será dizer que os recursos informáticos da altura não

tinham as mesmas capacidades de processamento dos que existem actualmente. Por

isso, mesmo que o processo fosse efectuado com o auxílio do computador este

continuava a demorar algum tempo e nestas coisas de negócios tempo é dinheiro.

Para combater os problemas mencionados até ao momento, as empresas viram

necessidade de agrupar todos os dados relativos a cada departamento da empresa (Por

exemplo: armazém, contabilidade, recursos humanos, etc.) num único sítio. Desta

forma todo o processo passou a ser mais rápido e mais coerente.

Uma base de dados não é mais do que um colecção de dados relativa a várias

actividades de uma ou mais instituições.

1.2. Requisitos Fundamentais de uma Base de Dados Nesta secção serão abordadas características e conceitos que permitem compreender e

tirar o máximo partido das bases de dados.

1.2.1. Segurança

Da mesma forma que existe controlo de acessos aos recursos num sistema operativo

multiutilizador, geralmente suportado por um login/password, os SGBD também

usam esta forma de controlo de acessos para garantir um nível de segurança básico.

Uma vez que este mecanismo de segurança apresenta um grau de controlo muito

Conceitos Básicos - Capítulo 1

Página 18

baixo e por isso é pouco flexível, é necessário utilizar mecanismos mais sofisticados.

Para que o grau de segurança seja aceitável é necessário que o SGBD permita definir

para cada um dos utilizadores autorizados quais as regras de acesso:

o Quem pode aceder – utilizadores aos quais se vai dar acesso à informação.

Por exemplo: o pessoal administrativo pode ter acesso, o pessoal do

armazém também, mas os operários não.

o Aceder a quê – dependendo do grau de sensibilidade da informação

armazenada na base de dados, poderá ser necessário autorizar e/ou negar

acesso a certo tipo de informações para alguns utilizadores da base de

dados. Por exemplo: o pessoal da contabilidade pode ver o salário de cada

funcionário, mas os operários já não.

o Aceder de que modo – tendo em conta as características de cada utilizador,

poderá ser necessário permitir um tipo de operações a uns e só alguns tipos

de operações a outros. Por exemplo: os gestores de uma empresa só

puderam consultar os dados, os funcionários administrativos só podem

criar novos registos e o administrador da base de dados pode executar todo

o tipo de operações.

1.2.2. Integridade

Diz-se que uma base de dados está num estado de integridade quando a base de dados

contem apenas dados que não contradigam a realidade que estão a representar. Por

exemplo: uma empresa tem uma base de dados com dados relativos ao processo

produtivo. Essa empresa tem três máquinas, a máquina A,B e C. Um dos operadores

registou uma encomenda que será realizada na máquina X.

A manutenção da integridade pressupõe proteger a base de dados de executar

operações que tentem comprometer a realidade dos próprios dados. Trata-se por isso

de um conjunto de medidas muito especial.

Conceitos Básicos - Capítulo 1

Página 19

Desta forma todas as operações que ponham em causa a integridade da base de dados

são regidas por um conjunto de regras a que se chamam restrições de integridade.

Exemplos de algumas restrições de integridade:

o Saldo de uma conta bancária superior ou igual a zero.

o Na matrícula de um aluno, o nome não pode estar em branco.

o Autorizar um aluno a fazer um exame de recurso se tiver efectuado o

pagamento do pedido de exame.

1.2.3. Controlo da Concorrência

É um dos factores mais importantes a ter em conta no acesso por vários utilizadores

(em simultâneo) a uma base de dados. Este controlo permite que o utilizador não

pressinta a presença de outros utilizadores sobre a mesma base de dados. Assim sendo

cada utilizador tem a percepção de estar sozinho a trabalhar na base de dados, quando

na realidade podem estar vários utilizadores a executar operações muito diferentes

(consultas, actualizações a dados já existentes, criação de novos registos, etc.).

No caso de uma base de dados não suportar acessos concorrentes e se os utilizadores

acederem em simultâneo à base de dados, é muito provável que a integridade de toda

a base de dados possa ficar comprometida. Desta forma torna-se necessário que uma

base de dados multiutilizador tenha obrigatoriamente mecanismos para gerir o

controlo da concorrência.

1.2.4. Recuperação/Tolerância a Falhas

Para que seja possível ter a base de dados num estado de integridade válido é

necessário que haja a possibilidade de a recuperar de falhas através de uma forma

simples e rápida.

Para tal é aconselhada a realização de cópias de segurança de toda a base de dados de

uma forma regular. As cópias devem ser feitas em suportes amovíveis (ou seja: tape)

e armazenadas em locais fisicamente distantes do local onde se encontra o servidor.

No caso de ocorrer uma falha (crítica ou não) é sempre possível usar as cópias de

segurança para repor a base de dados num estado de integridade anterior.

Para que seja possível garantir todas estas características previamente referidas é

necessário que haja uma entidade que assegure tais funcionalidades.

Conceitos Básicos - Capítulo 1

Página 20

À entidade que gere e controla o funcionamento das bases de dados dá-se o nome de

Sistema Gestor de Bases de Dados (SGBD) que provém do Inglês Database

Management System (DBMS).

1.3. SGBD Vs Sistema de Gestão de Ficheiros

Muito antes de aparecerem as bases de dados as aplicações armazenavam os dados em

ficheiros do próprio sistema, usando por isso as funcionalidades disponibilizadas pelo

sistema de ficheiros do sistema operativo. Algumas das operações disponibilizadas

são: criar ficheiros; deslocar para o byte n do ficheiro; deslocar para o início do

ficheiro; escrever no ficheiro, etc.

Estas aplicações de forma geral estavam associadas à estrutura de um ou mais

ficheiros. Estrutura de dados essa que geralmente consiste num conjuntos de dados de

comprimento fixo. A seguir encontram-se algumas das características mais

importantes dos SGBDs.

1.3.1. Abstracção dos Dados

Através da tecnologia das bases de dados não é necessário que as aplicações

conheçam pormenores dos dados tais como: comprimentos dos registos criados pelo

SGBD ou a localização física dos ficheiros no disco, uma vez que o

utilizador/programador trabalha sobre um modelo abstracto de dados.

1.3.2. Independência Programa/Dados

Usando um sistema de gestão de ficheiros como suporte de armazenamento uma

aplicação, leva a que os ficheiros fiquem ligados à própria aplicação. No caso de ser

necessário alterar a estrutura do ficheiro, torna-se também necessário alterar as

aplicações que usem esses mesmos ficheiros.

Ao usar-se um SGBD as aplicações passam a ser escritas de forma independente dos

métodos de armazenamento usados pelo próprio SGBD. Por exemplo: uma escola tem

uma aplicação para gerir a informação relativa aos dados pessoais dos seus alunos.

Conceitos Básicos - Capítulo 1

Página 21

Uma vez que na altura em que a aplicação foi desenvolvida não havia telemóveis, não

foi reservado espaço no registo do aluno para esse efeito. Por decisão da direcção da

escola as funcionárias administrativas vão passar a armazenar os telemóveis dos

alunos juntamente com o registo do aluno. Como o ficheiro com a informação dos

alunos é necessário em quase todas as aplicações, então essas mesmas aplicações

terão que ser também alteradas, porque o comprimento dos registos também foi

alterado.

No exemplo anterior, se estivesse a ser usado um SGBD, era somente necessário

reservar espaço para o número de telemóvel e alterar a parte da aplicação que regista a

parte do número de telemóvel. Todas as restantes aplicações não teriam que ser

alteradas (se não necessitassem do número de telemóvel).

1.3.3. Partilha dos Dados

Numa abordagem baseada no Sistema de Gestão de Ficheiros, requisitos como

segurança e acessos concorrentes terão que ser fornecidos pela própria aplicação.

No caso de se usar um SGBD tais funcionalidades passam a ser fornecidas pelo

próprio SGBD, libertando assim a aplicação para a realização dos objectivos para que

foi criada.

1.3.4. Diminuição da Redundância

Nos SGBDs é possível conceber modelos de dados de forma a obter-se um grau de

redundância quase nulo. Desta forma torna-se mais simples manter a coerência dos

dados, bem como obter um grau de desempenho desejável. Há situações em que é

necessário adicionar alguma redundância (processo conhecido como

desnormalização), nomeadamente devido a motivos relacionados com o desempenho.

No entanto estes casos têm que ser pensados com muito cuidado, porque uma

desnormalização implica obrigatoriamente redundância. Na maioria das vezes é

possível melhorar o grau de desempenho sem ter que adicionar redundância.

Para mais informações consulte a secção 6.1.

Conceitos Básicos - Capítulo 1

Página 22

1.3.5. Desenvolvimento e Manutenção

Uma vez que ao usar-se um SGBD estamos a trabalhar com um grau de abstracção

elevado, logo os programadores não têm que “perder” tempo com certos pormenores

irrelevantes à solução dos problemas (Por exemplo: concorrência, etc.) permitindo

assim que estes se dediquem à implementação das funcionalidades necessárias. Desta

forma os programadores tem mais tempo para melhorar a parte funcional da

aplicação, deixando responsabilidades tais como: gestão da concorrência e integridade

para o SGBD.

1.3.6. Integridade dos Dados

Através da definição de restrições de integridade e outros mecanismos é cada vez

mais, possível e aconselhável retirar certas “regras de negócio” das aplicações. Desta

forma a integridade da base de dados fica cada vez menos dependente das aplicações.

Ou seja em vez de se ter as regras de negócio dispersas por várias aplicações estas

passam a residir num único local.

1.4. Base de dados Relacionais

Dentro da tecnologia das bases de dados há modelos variadíssimos, entre os quais se

salientam os seguintes (ordenado cronologicamente)

• Modelo Hierárquico.

• Modelo de Rede.

• Modelo Relacional (RBDMS – Relational Database Management Systems).

• Modelo Relacional Orientado Objectos (OORDBMS – Object Oriented

Relational Database Management Systems).

• Modelo Orientado Objecto (OODBMS – Object Oriented Database

Management Systems).

• Outros modelos.

Neste documento será abordado somente os modelos Relacional e Relacional

Orientado a Objectos uma vez que são os modelos suportados pelo PostgreSQL.

O Modelo Relacional foi sem dúvida o que melhor se adaptou à realidade.

Conceitos Básicos - Capítulo 1

Página 23

Esta adaptação bem sucedida deve-se ao facto deste ter nascido com base nas teorias

de conjuntos, em vez das técnicas de processamento de ficheiros como era o caso dos

seus antecessores.

Antes de se abordar o Modelo Relacional é necessário definir alguns conceitos

directamente relacionados:

Tabela – Consiste numa estrutura bidimensional constituída por linhas e

colunas.

As colunas definem os atributos, ou seja a parte fixa da estrutura da tabela. A

cada atributo está sempre associado um domínio ou seja um conjunto de

valores possíveis que o atributo pode ter. O domínio na prática é especificado

pelo tipo de dados que for atribuído ao atributo. A gama de valores a serem

aceites por parte do domínio pode ser encurtada através do uso de restrições de

integridade. Na Tabela 1 podem-se observar alguns exemplos de domínios

possíveis.

Atributo Domínio

Idade de uma pessoa Números inteiros

Sexo de uma pessoa Carácter

Data de nascimento Data

Tabela 1 – Exemplos de domínios

As linhas ou registos das tabelas também conhecidas como tuplos definem a

ocorrência dos dados a armazenar, ou seja, as linhas são a parte dinâmica da

tabela.

Atributos

distrito_codigo distrito_nome

4000 Porto

1000 Lisboa

Tuplo 4405 Valadares

Tabela 2 – Relação entre tributes e tuplos

Conceitos Básicos - Capítulo 1

Página 24

Vista – Tabela sem existência física, cujo conteúdo é derivado de uma ou mais

tabelas.

Super chave – Conjunto de um ou mais atributos cujos valores quando

agrupados permitam identificar de forma única cada tuplo da tabela.

Chave primária – Corresponde à chave que é seleccionada a partir de uma

das super chaves.

Relação – Dependência lógica que existe entre uma ou mais tabelas.

Chave estrangeira – Corresponde a um ou mais atributos que formam uma

chave primária numa outra tabela. As chaves estrangeiras permitem criar o

conceito de relação entre tabelas.

Para que seja possível obter as características referidas em 1.3 é necessário que o

modelo de dados esteja normalizado.

Um modelo de dados diz-se normalizado quando o modelo resultante apresenta

redundância mínima. É claro que vai sempre haver alguma redundância mesmo que o

modelo seja considerado normalizado, uma vez que vai haver repetição de alguns

valores, nomeadamente os das chaves estrangeiras.

Para que se possa normalizar um modelos de dados é necessário ter conhecimento de

carácter semântico relativamente à informação a tratar.

O processo de normalização dos dados é uma fase muito importante da criação de um

modelo de dados. Uma vez que este assunto está fora do âmbito deste documento,

aconselha-se o leitor a consultar [2] ou outra bibliografia específica sobre o assunto.

Conceitos Básicos - Capítulo 1

Página 25

1.5. SQL

Para que seja possível obter as características previamente abordadas em 1.3, é

necessário criar uma linguagem de alto nível que seja independente da implementação

do SGBD e que permita manipular a base de dados — a linguagem SQL – Structured

Query Language, a qual encontra-se definida em [25] e [26].

Nos dias de hoje o SQL é a linguagem standard para acesso a bases dados relacionais

e suas sucessoras (OORDBMS e OODBMS). Os standards SQL de referência são:

ANSI/ISO SQL também conhecido por SQL92 e o SQL 1999 o qual introduziu

algumas extensões importantes, as quais podem ser consultadas em [28] e [27].

O SQL foi criado com o intuito de ser uma linguagem simples de usar por parte do

utilizador, daí que o desempenho da execução dependa da forma como o SGBD está

implementado e não do modo como o query é escrito. O SGBD pode decidir por

razões de eficiência que o query a executar seja diferente do que foi escrito, mas cujo

resultado seja o mesmo.

1.5.1. Aspectos do SQL

Para que seja possível interagir com o SGBD é necessário que este disponibilize

mecanismos de alto nível. Para tal no âmbito do SQL, foram criadas as linguagens e

funcionalidades a seguir descritas.

1.5.2. Linguagem de Definição de Dados

A linguagem de definição de dados, conhecida na área das bases de dados como

DDL - Data Definition Language consiste num sub conjunto de instruções do SQL.

As instruções DDL permitem:

o Criar, alterar e remover as definições das tabelas e vistas.

o Criar restrições de integridade quer durante a fase de criação quer após a

criação.

o Manutenção de privilégios (permissões) nas tabelas, vistas e outros objectos da

base de dados.

o Criação, remoção e manutenção de outros objectos da base de dados.

Conceitos Básicos - Capítulo 1

Página 26

1.5.3. Linguagem de Manipulação de Dados

A linguagem de manipulação de dados, conhecida na área das bases de dados sob a

sigla DML -Data Manipulation Language é um outro sub conjunto da linguagem

SQL.

As instruções DML usam-se com a finalidade de:

o Adicionar registos às tabelas.

o Alterar registos das tabelas.

o Remover registos das tabelas.

o Consultar informação presente em uma ou mais tabelas.

1.5.4. Triggers

Estes apareceram no standard SQL 1999 e são uma funcionalidade muito útil.

Um trigger consiste num pedaço de código SQL e/ou procedimental que é escrito

numa linguagem suportada pelo SGBD, armazenado dentro do SGBD e que é

executado quando ocorrem certos eventos na base de dados.

Exemplo de um trigger em pseudo código:

QUANDO INSERIR REGISTO EM CLIENTES FAZER:

Se cliente_idade <= 5 então

Mostra_erro(‘Idade inválida’)

Fim Se

Para mais informações sobre triggers por favor consulte a secção 6.3.

Conceitos Básicos - Capítulo 1

Página 27

1.5.5. Gestão de Transacções

Uma transacção consiste numa sequência de instruções SQL que se querem que sejam

executadas de forma atómica, quer isto dizer que as instruções SQL dentro de uma

transacção são executadas de forma indivisível, ou seja como se de uma única

instrução se tratasse.

Os SGBDs possuem instruções que permitem alterar o nível de isolamento

pretendido para uma transacção.

Para mais informação sobre transacções por favor consulte a secção 6.4.1.

02

2. SQL – Introdução 29

2.1. SQL no PostgreSQL 31

SQL Introdução - Capítulo 2

Página 29

2. SQL – Introdução

Com esta secção pretende-se introduzir o leitor a alguns aspectos básicos, mas

importantes do SQL. Não é objectivo desta secção ensinar ao leitor todas as instruções

e/ou cláusulas possíveis do SQL. Para tal aconselha-se que siga [1] e [2].

No fim deste documento é apresentado o esquema e instruções DDL usadas para criar

a base de dados usada como referência ao longo deste documento. Aconselha-se o

leitor a consultar as relações existentes entre tabelas que se encontra no fim do

documento, com vista a entender o significado de cada pergunta colocada à base de

dados.

A forma mais simples de uma questão (query) em SQL tem a seguinte forma:

SELECT <atributo (s)>

FROM <tabela(s)>

WHERE <condição>

A instrução SELECT permite indicar ao SGBD que queremos consultar a informação

que esteja armazenada em uma ou mais tabelas. A directiva FROM serve para indicar

qual ou quais tabelas a consultar. A directiva WHERE é opcional e funciona como um

filtro. A condição pode envolver campos de uma ou mais tabelas especificadas na

cláusula FROM ou em alternativa pode ser o resultado de um outro SELECT.

Um outro tipo de query é o que permite adicionar novos registos, este apresenta a

seguinte forma:

INSERT INTO <tabela>(<campo(s)>) VALUES( <valor(es)>)

Exemplo: INSERT INTO DISTRITOS( distrito_codigo, distrito_nome ) VALUES( 4000, ‘Porto’)

Ou INSERT INTO <tabela> VALUES( <valor(es)>)

Ou INSERT INTO <tabela>(<campo(s)>) <query SELECT>

A instrução INSERT permite criar novos registos numa tabela, usando para o efeito os

valores fornecidos que é o caso da 1ª forma acima mencionada. Nesta forma a ordem

dos campos não interfere, uma vez que são especificados os seus nomes.

SQL Introdução - Capítulo 2

Página 30

A segunda forma permite fazer o mesmo que a 1ª mas omitindo os nomes dos

campos. Nesta 2ª forma, a ordem dos campos é importante, por isso, a sequência de

valores especificados tem que estar ordenada da mesma forma como foram

especificados os campos na criação da tabela. Embora a 1ª forma seja mais difícil de

escrever, esta é a forma preferível, uma vez que se houverem alteração à estrutura da

tabela, os queries que existiam antes da alteração continuaram a ser executados com

sucesso. No caso da 2ª forma, tal já poderá não acontecer, ficando por isso dependente

da ordem atribuída pela implementação do SGBD. A 3ª forma permite que sejam

inseridos na tabela o conjunto de valores retornados pelo query SQL. Utilizando esta

forma, tem que se garantir que a ordem dos campos e respectivos domínios na tabela

estejam de acordo com a ordem e domínios dos campos retornados pelo query SQL.

Uma outra instrução SQL que existe para manipular as tabela é o UPDATE.

Esta como o próprio nome indica permite fazer actualizações aos registos existentes

de uma tabela. A forma é a seguinte:

UPDATE <tabela>

SET <campo1> = <valor1>, SET <campo2> = <valor2>

WHERE <condição>

A base de dados ao executar um query UPDATE vai alterar os valores do campos

especificados (os campos não especificados não são alterados) para os valores

fornecidos, para os registos que satisfação a condição fornecida na cláusula WHERE. A

cláusula WHERE pode ser uma condição que envolva somente os campos da tabela onde

está a ser feito o UPDATE ou podem ser o resultado de um SELECT qualquer.

Há ainda um outro tipo de query que é o que permite remover registos de uma tabela.

A forma deste é a seguinte:

DELETE FROM <tabela>

WHERE <condição>

O que o SGBD faz ao executar um query DELETE é remover da tabela especificada os

registos que satisfação a condição presente na cláusula WHERE. Caso não seja

especificada a cláusula WHERE, o SGBD removerá todos os registos da referida tabela.

SQL Introdução - Capítulo 2

Página 31

A cláusula WHERE pode ser uma condição que envolva somente os campos da tabela

onde está a ser feito o DELETE ou pode ser o resultado de um query SELECT.

As instruções referidas até agora pertencem ao grupo das instruções de manipulação

dos dados, no fim do documento encontram-se instruções de definição das entidades

do exemplo de referência (tabelas, restrições de integridade, etc.) juntamente com

alguns comentários e/ou explicações.

2.1. SQL no PostgreSQL

Esta secção foi criada com vista a fornecer de forma rápida e simples aos utilizadores

uma visão global de todas as características mais importantes do PostgreSQL. Desta

forma, muitos dos termos usados só serão entendidos por utilizadores com alguma

experiência. As pessoas com menos experiência podem ver este ponto, mas não

devem ficar preocupadas se não entenderem alguns dos assuntos abordados uma vez

que alguns destes serão abordados ao longo do documento.

No que respeita à implementação SQL usada no PostgreSQL este suporta:

o Chaves Estrangeiras – Suporta a directiva standard CREATE TABLE ... FOREIGN

KEY. Possibilita a excussão de diferentes acções nos queries UPDATE e DELETE, entre as quais: CASCADE, RESTRICT, SET NULL, SET DEFAULT.

o Joins – Implementa todos os tipos de joins referidos no standard SQL99:

inner join, left, right, full outer join, natural join. O optimizador tem à

disposição vários algoritmos disponíveis para os joins.

o Vistas – As vistas são queries SELECT que se encontram armazenados. Estas

são geralmente usadas para encapsular queries complexos ao nível do

servidor. O PostgreSQL também suporta vistas actualizáveis (as actualizações

realizadas sobre a view são efectuadas nas respectivas tabelas).

o Transacções – As transacções permitem que um conjunto de queries seja

executado pelo SGDB como se de um único de tratasse. A implementação das

transacções no PostgreSQL garante as seguintes propriedades standard:

atómicas, consistentes, isoláveis e duráveis.

Suporta ROLLBACK e SAVEPOINTS (previsto a partir da versão 7.4),

níveis de isolamento diferentes (READ COMMITTED e SERIALIZABLE).

SQL Introdução - Capítulo 2

Página 32

o Controlo de concorrência – Usa uma técnica de nome MVCC – Multi-Version

Concurrency Control que é usada pela maioria dos SGBDs comerciais, como

é o caso do Oracle. O MVCC permite a criação de aplicações altamente

escaláveis, uma vez que as operações de leitura e escrita não bloqueiam quer

os leitores quer os escritores e também porque é uma técnica muito melhor do

que o bloqueio de registos, uma vez que não é necessário bloquear acessos a

um registo que esteja a ser actualizado, porque os valores que as transacções

vão ver é um snapshot (fotografia) do estado da base de dados antes de a

transacção ter sido iniciada.

o Triggers – Os triggers são procedimentos que são invocados pela base de

dados quando certas acções ocorrem. O PostgreSQL suporta triggers BEFORE

e/ou AFTER. Estes podem ser escritos em C ou numa linguagem procedimental.

Para mais informações consulte 6.2 e 6.3.

o Stored Procedures – O PostgreSQL permite a criação destes através das

seguintes linguagens: C; PL/pgSQL (muito parecida ao PL/SQL da Oracle);

Tcl; Perl; Python (em fase alpha) e Ruby. A partir da versão 7.3.x passou a

suportar o retorno de registos.

o Tipo de dados – O PostgreSQL suporta a maioria dos tipos de dados descritos

nas normas SQL92 e SQL99, dos quais se destacam: INTEGER, NUMERIC,

BOOLEAN, CHAR, VARCHAR, DATE, INTERVAL, TIMESTAMP. Estão disponíveis outros

tipos de dados, tais como: geométricos (linhas, pontos, etc.), endereços

TCP/IP, endereços MAC para placas Ethernet, ISBN/ISSN entre outros.

O PostgreSQL possibilita a criação de outros tipos de dados, bem como as

funções e operadores necessários ao seu uso.

o Suporta também BLOBs – Binary Large Objects. Estes podem ser lidos

totalmente ou parcialmente pela aplicação cliente.

o Internacionalização – Contém suporte para character sets internacionais,

codificações multi-byte e Unicode.

o Permite o uso das condições standard do SQL, entre as quais: CASE...WHEN,

COALESCE e NULLIF.

o Sub queries – Os sub queries permitem que o SGBD possa responder a

perguntas complexas. O uso destes, pode melhorar o desempenho das

aplicações, bem como tornar a ideia intrínseca ao sub query mais legível e

fácil de compreender.

SQL Introdução - Capítulo 2

Página 33

o Contém suporte para SELECT DISTINCT e SELECT DISTINCT ON( coluna ).

o Total suporte para GROUP BY e funções agregadoras, entre as quais: COUNT, SUM,

AVG, MIN, MAX, STDDEV e VARIANCE. Novas funções agregadoras podem ser

criadas em qualquer uma das linguagens suportadas pelo PostgreSQL.

Para mais informações consulte 6.2.

o Suporta sub selects usados na cláusula FROM tal como: SELECT COUNT(x),

AVG(x), SUM(x) FROM (SELECT SUM(weight) AS x FROM Items GROUP BY

manufacturer) AS Items;

o Suporta as cláusulas UNION e UNION ALL, INTERSECT e EXCEPT.

o Contém extensões para a cláusula LIMIT e OFFSET de forma a permitir a

selecção de um número arbitrário de registos. (Por exemplo: SELECT * FROM

Items ORDER BY cost LIMIT 5).

o Contém funções internas que permitem a manipulação e conversão de tipos de

dados tais como: matemáticos, date/time e strings.

o Suporta indexação total de texto.

o Suporte para a cláusula SQL LIKE e todo o tipo de expressões regulares

definidas pelo standard POSIX.

o Suporte para carregamento de funções externas tais como: soundex, funções

hash ( SHA1 e MD5).

o Índices – Os índices podem ser do tipo: árvores B, árvores R e Gist (usados

geralmente em sistemas de informação geográficos). Os métodos de indexação

podem ser definidos pelo utilizador, com a possibilidade de adicionar e

remover índices de qualquer tipo a qualquer momento.

o Replicação – É uma das funcionalidades que serão certamente adicionadas,

uma vez que têm grande urgência na lista de funcionalidades a implementar. A

partir da versão 7.4 (actualmente estamos na 7.3.3) está previsto replicação do

tipo master/slave, multi-master/slave.

Neste tópico encontram-se alguns exemplos comentados e explicados de queries SQL

que podem ser executados (na realidade foram executados) pelo PostgreSQL, não

querendo isto significar que o PostgreSQL esteja limitado às funcionalidades

abordadas nestes exemplos, muito pelo contrário.

SQL Introdução - Capítulo 2

Página 34

A bold será assinalada a diferença entre o query anterior e o query actual, sempre que

seja necessário.

Questão Qual a idade, sexo e nome de todos os clientes?

Query SELECT cliente_idade, cliente_nome, cliente_sexo

FROM clientes

Exemplo 1 - Query SELECT simples

Questão Qual a idade, sexo e nome dos clientes com idade superior a 18 anos?

Query SELECT cliente_idade, cliente_nome,cliente_sexo

FROM clientes

WHERE cliente_idade >= 18

Exemplo 2 - Query SELECT com filtragem

Questão Qual a idade, sexo e nome dos clientes com idade entre 18 e 30 anos?

Query SELECT cliente_idade, cliente_nome, cliente_sexo

FROM clientes

WHERE cliente_idade >= 18 AND cliente_idade <= 30

Uma outra forma de escrever este mesmo query seria: SELECT cliente_idade, cliente_nome, cliente_sexo

FROM clientes

WHERE cliente_idade BETWEEN 18 AND 30

Exemplo 3 - Query SELECT com filtragem de duas expressões lógicas e a cláusula BETWEEN

Questão Quantos clientes existem no total?

Query SELECT count(*)

FROM clients Notas O * na cláusula count significa que a contagem diz respeito a qualquer

atributo da tabela.

Exemplo 4 - Uso da função agregadora count num query SELECT

Questão Qual o total de clientes do sexo masculino?

Query SELECT count( cliente_sexo )

FROM clientes

WHERE cliente_sexo = ‘M’

Exemplo 5 — Uso da função agregadora count juntamente com a cláusula WHERE

SQL Introdução - Capítulo 2

Página 35

Questão Qual a idade do cliente mais velho e mais novo de cada um dos sexos?

Query SELECT cliente_sexo, max( cliente_idade ),

min(cliente_idade)

FROM clientes

GROUP BY cliente_sexo Notas O query resultante deve ter dois tuplos (1 por cada sexo). Para que tal

seja possível temos que indicar ao SGBD como é que este vai fazer o

agrupamento dos resultados. Uma vez que queremos saber o sexo para

além do máximo e mínimo da idade, então temos que indicar ao SGBD

que o atributo a usar para o agrupamento dos resultados é o campo

cliente_sexo. Para tal usa-se a directiva GROUP BY.

Exemplo 6 - Uso das funções agregadoras min, max juntamente com a cláusula GROUP BY

Questão Qual o nome, morada do cliente mais velho de todos?

Query SELECT CL.cliente_nome, CL.cliente_morada

FROM clientes as CL

WHERE CL.cliente_idade = ( SELECT max( cliente_idade )

FROM clientes

) Notas O query anterior é composto por um sub query.

A execução deste começa sempre pelo último query na cadeia, ou seja,

no caso acima vai começar pelo query que calcula o cliente mais

velho. Após o cálculo filtra o resultado através da idade.

Exemplo 7 - Uso de sub queries

Umas das operações mais importantes em SQL são os chamados Joins (junções). Um

join consiste em obter informação através da junção de várias tabelas.

SQL Introdução - Capítulo 2

Página 36

Exemplo 8 - Uso da cláusula AS juntamente com um join

Questão Qual o nome, morada dos clientes que vivem no distrito cujo código é

4000?

Query SELECT CL.cliente_nome, CL.cliente_morada

FROM clientes as CL, codigo_postal as CP

WHERE CP.distrito_codigo = 4000 AND

CL.codigo_postal_id = CP.codigopostal_id Notas Repare-se que a tabela codigo_postal foi incluída no query somente para

se efectuar a filtragem, não aparecendo por isso nenhum atributo desta

na cláusula SELECT

Exemplo 9 - Uso de um join através da cláusula WHERE

Questão Qual o nome, morada e localidade dos clientes?

Query SELECT clientes.cliente_nome, clientes.cliente_morada,

codigo_postal.localidade_nome

FROM clientes, codigo_postal

WHERE clientes.codigo_postal_id =

codigo_postal.codigopostal_id

Há uma outra forma de ser escrever o mesmo query anterior.

SELECT CL.cliente_nome, CL.cliente_morada,

CP.localidade_nome

FROM clientes as CL, codigo_postal as CP

WHERE CL.codigo_postal_id = CP.codigopostal_id Notas Para que seja possível mostrar a informação da localidade, é necessário

juntar os dados residentes em duas tabelas: clientes e codigo_postal.

Repare-se que no 1º query está o nome da tabela precedido do nome do

campo.

O segundo query é mais simples de se escrever e de se ler. A única

alteração que se fez neste último em relação ao primeiro foi fornecer

um sinónimo para as tabelas, através da cláusula AS.

É boa prática preceder os nomes dos campos com os nomes das tabelas

ou sinónimos.

SQL Introdução - Capítulo 2

Página 37

Questão Qual o nome, morada dos clientes que vivem no distrito do Porto?

Query SELECT CL.cliente_nome, CL.cliente_morada

FROM clientes as CL, codigo_postal as CP,distritos D

WHERE D.distrito_nome=’Porto’ AND

CP.distrito_codigo = D.distrito_codigo AND

CL.codigo_postal_id = CP.codigopostal_id Notas Repare-se que a tabela distritos foi incluída no query somente

para se efectuar a filtragem, não aparecendo por isso nenhum

atributo desta na cláusula SELECT.

Exemplo 10 - Uso de um join através da cláusula WHERE

Questão Qual o código dos distritos que existem que não são usados no

código postal?

Query SELECT distrito_codigo

FROM distritos

WHERE distrito_codigo NOT IN( SELECT distrito_codigo

FROM codigo_postal

)

Outra forma de resolver o query anterior será através do uso de

operadores de tuplos (Set Operators), neste caso a cláusula EXCEPT.

SELECT distrito_codigo FROM distritos

EXCEPT

SELECT distrito_codigo FROM codigo_postal

Exemplo 11 - Uso da cláusula NOT IN e EXCEPT

03

3.1. Instalação Linux Através de RPM 39

3.2. Instalação Linux Através de Source Code 40

Instalação Linux - Capítulo 3

Página 39

3. Instalação Linux

Há duas formas de se instalar o PostgreSQL no Linux:

• Através da compilação do código fonte (source)

• Através de ficheiros binários pré-compilados (ficheiros .rpm)

3.1. Instalação Linux Através de RPM

Antes de mais é necessário que verifique se o PostgreSQL não está já instalado no

sistema. Para isso com a conta de administração (root), corra o seguinte comando:

rpm –qa | grep –i “postgres”

Caso o package ainda não esteja instalado, por favor continue a ler.

Antes de mais é necessário ter acesso aos ficheiros rpm.

Geralmente todas as distribuições (ou seja: RedHat, Suse, Mandrake, etc.) trazem os

ficheiros rpm necessários quer do servidor quer dos utilitários e bibliotecas de suporte.

Caso a distribuição que usa não traga o PostgreSQL, é possível descarregar os

ficheiros binários em [9] ou em [10].

A sequência de RPMs a instalar deve ser a seguida abaixo e usando os nomes

fornecidos. Nos nomes dos ficheiros .rpm, x.x deve ser substituído pelos respectivos

valores das versões. Os nomes dos RPMs a instalar são os seguintes:

• postgresql-libs-7.x.x.i386.rpm

• postgresql-7.x.x.i386.rpm

• postgresql-server-7.x.x.i386.rpm

Para instalar o PostgreSQL basta que corra para cada um dos RPMs o seguinte

comando como root:

rpm –ivh <nome do package>

Por exemplo: rpm –ivh postgresql-server-7.3.rpm

Instalação Linux - Capítulo 3

Página 40

3.2. Instalação Linux Através de Source Code

Mudar para o directório onde se vai compilar (não é o directório de destino do

PostgreSQL).

Por exemplo: cd /home/miguel

Ir buscar o source. Este pode ser descarregado através de HTTP em [9] ou através de

FTP em [10]. Após ter o ficheiro do source, tem que o descomprimir, usando para o

efeito o comando:

tar –zxvf postgresql-7.X.tar.gz.

Para quem estiver familiarizado com a compilação de software via source, os passos a

seguir são:

./configure

gmake

su

gmake install

adduser postgres

mkdir /usr/local/pgsql/data

chown postgres /usr/local/pgsql/data

su - postgres

/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data

/usr/local/pgsql/bin/postmaster -D /usr/local/pgsql/data >logfile 2>&1 &

/usr/local/pgsql/bin/createdb teste

/usr/local/pgsql/bin/psql teste

Para aqueles que necessitam de compilar o PostgreSQL via source e que nunca

tenham compilado qualquer software, devem seguir os passos que se seguem.

Nota: é necessário ter acesso à conta de administração (root).

1º Passo: Configuração da instalação

Comando: ./configure

Notas: Nesta fase da instalação é onde se fazem a maioria das decisões ao nível das

funcionalidades necessárias, nomeadamente a localização por defeito no sistema de

ficheiros dos ficheiros das bases de dados e o local onde os ficheiros do PostgreSQL

serão instalados; porta TCP a usar para o caso dos acessos remotos ao servidor;

Instalação Linux - Capítulo 3

Página 41

métodos de autenticação a usar; interfaces a suportar (Perl, Tcl, etc.). Abaixo seguem-

se alguns parâmetros úteis.

Para obter uma listagem completa de todos os parâmetros consulte [11] ou execute o

comando ./configure --help.

Parâmetros:

--prefix Permite especificar qual a localização dos ficheiros e sub directórios

necessário no sistema de ficheiros. Por defeito, o PostgreSQL é

instalado com --prefix=/usr/local/pgsql, quer isto dizer que todos os

ficheiros e sub directórios serão criados hierarquicamente abaixo

deste directório. Este parâmetro é bastante útil uma vez que permite

que o servidor seja instalado por utilizadores sem permissões de

administração. --with-

pgport=X Indica em tempo de compilação qual a porta TCP a usar para

estabelecer ligações com o servidor. Esta porta é por defeito a 5432.

O número da porta especificado tem que ser obrigatoriamente

superior a 1024, uma vez que as portas entre 0 e 1024 são portas

privilegiadas, o que significa que só o utilizador é root que pode ter

processos à escuta nestas. O servidor não pode ser iniciado por um

utilizador com privilégios de administração por motivos de

segurança.

Este parâmetro é de grande utilidade quando é necessário ter várias

versões do PostgreSQL a correr na mesma máquina. O parâmetro

que controla a porta a usar também pode ser alterado no ficheiro de

configuração. --with-

maxbackends=X Indica em tempo de compilação qual o número máximo de conexões

que podem existir em simultâneo no servidor.

Por defeito este valor é de 32. Este parâmetro pode ser alterado

através do ficheiro de configuração.

Exemplo 12 – Parâmetros mais frequentes usados na compilação e seu significado

Instalação Linux - Capítulo 3

Página 42

2º Passo: compilar o software

Comando: gmake

Notas: O processo de compilação pode demorar entre 5 minutos até ½ hora

dependendo do hardware usado.

3º Passo: testar o servidor antes de instalar

Comando: gmake check

Notas: Para executar este teste é necessário que o utilizador que lhe deu início não

seja nenhum administrador (root). Se assim não for o teste falham, devido ao facto de

o servidor por motivos de segurança não ter iniciado devidamente.

Este comando vai correr os chamados testes de regressão. Estes testes permitem

verificar se o servidor irá correr tal como previsto.

4º Passo: instalar o servidor, copiar directórios e ficheiros de suporte para o directório

destino

Comando: gmake install

Notas: Antes de executar o comando acima, certifique-se que tem as devidas

permissões no directório de destino.

5º Passo: criar conta para o utilizador que inicia o servidor

Comando: adduser postgres

Notas: Para executar este comando, tem de ter permissões de administração (root).

6º Passo: criar o directório onde os ficheiros das bases de dados serão criados

Comando: mkdir /usr/local/pgsql/data

Notas: Para executar este comando, tem de ter permissões de administração (root).

7º Passo: trocar o owner do directório onde vão residir as bases de dados

Comando: chown postgres /usr/local/pgsql/data

Notas: Troca o owner do directório /usr/local/pgsql/data para postgres.

8º Passo: entrar na conta do utilizador postgres

Comando: su - postgres

Notas: Para executar este comando, tem de ter permissões de administração (root).

Instalação Linux - Capítulo 3

Página 43

9º Passo: criar as tabelas de suporte ao SGBD

Comando: /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data

Notas: Este comando irá criar todos os ficheiros e directórios de suporte ao SGBD, no

directório especificado pelo parâmetro –D. Internamente este comando vai criar as

chamadas tabelas de sistema também conhecidas por catalog tables.

Este passo só deve ser executado uma única vez e durante a instalação.

10º Passo: iniciar o servidor

Comando: /usr/local/pgsql/bin/postmaster -D /usr/local/pgsql/data >logfile 2>&1 &

Notas: Este comando vai inicializar o servidor, indicando que o directório onde o

servidor deve guardar os dados é o directório especificado pelo parâmetro –D e

redireccionando todo o output quer o stderr quer o stdout para o ficheiro logfile.

11º Passo: criar uma base de dados

Comando: /usr/local/pgsql/bin/createdb teste

Notas: Este comando vai criar uma base de dados de nome teste.

Ao ser executado o comando acima, não será pedida qualquer autenticação, uma vez

que todos os utilizadores (incluindo o utilizador postgres) têm permissão de se

ligarem a qualquer base de dados a partir da máquina local quer via sockets Unix quer

via TCP. É aconselhado que as configurações do ficheiro pg_hba.conf sejam

alteradas.

12º Passo: estabelecer ligação à base de dados criada no passo anterior

Comando: /usr/local/pgsql/bin/psql teste

Notas: Usando o cliente que é instalado por defeito (o psql), vai entrar na base de

dados teste. Tal como já foi explicado no passo anterior, não será pedida qualquer

autenticação.

04

4 Como utilizar 45

4.1. Clientes 45

4.1.1. pgsql 45

4.1.2. pgAccess 47

4.1.3. pgAdmin/II 48

4.1.4. winSQL 49

4.2. Como Utilizar – Exemplo Prático 50

4.2.1. Criação da Conta de Acesso 51

4.2.2. Criação da Base de Dados 52

4.2.3. Autorizar o Utilizador 53

4.2.4. Activar a Nova Configuração 56

4.3. Instalação do Driver ODBC do PostgreSQL 57

4.3.1. Criar o DSN 57

Como Utilizar - Capítulo 4

Página 45

4. Como utilizar

Pretende-se com este capítulo demonstrar como se deve usar o PostgreSQL e como

aceder a este. Entre as aplicações possíveis serão apresentadas aplicações para

plataformas Linux/Unix e Windows. Em relação às aplicações Windows para além da

apresentação das mesmas, será também explicado como se deve instalar e configurar

o driver de ODBC do PostgreSQL.

Para se poder utilizar o PostgreSQL é necessário em 1º lugar termos o servidor

instalado e a correr.

Para informações sobre a instalação consulte a secção 3.

A partir deste ponto presume-se que já existe uma base de dados e uma conta de

acesso criada.

Para informações sobre os procedimentos a utilizar para a criação da base de dados e criação da conta, consulte a secção 4.2.

Uma vez que já temos uma base de dados e uma conta de acesso, resta-nos escolher

um cliente (programa que serve de interface entre o utilizador e o servidor).

4.1. Clientes

Alguns dos clientes mais utilizados são:

• Psql (Linux/Unix).

• PgAccess (Linux/Unix).

• PgAdmin/II (Windows).

• Winsql (Windows).

4.1.1. psql

O psql é um cliente “modo texto” que é instalado pelo PostgreSQL. Este permite

digitar os queries e ver os resultados de forma interactiva.

Como Utilizar - Capítulo 4

Página 46

Este é o cliente de eleição para se tirar o máximo partido do PostgreSQL uma vez que

suporta várias opções e operações. O pgsql é geralmente usado para tarefas tais como:

• Automatização de operações de manutenção.

• Importação/exportação de dados.

• Visualização de informações variadas sobre as bases de dados e outros

objectos.

Para que o pgsql fosse uma ferramenta fácil e de rápido uso, a equipa do PostgreSQL

decidiu criar uma série de comandos internos ao próprio pgsql tal como se pode

observar na tabela seguinte. Quer isto dizer que estes comandos só existem no pgsql e

por isso não podem ser utilizados através de outros clientes. Na realidade alguns dos

comandos internos não passam de meras consultas ao dicionário de dados do

PostgreSQL.

Comando Descrição

\d Mostra todas as relations (tabelas, sequências, índices, etc.) da base de

dados actual.

\dt É uma especialização do comando \d. O \dt permite mostrar o nome de

cada uma das tabelas existentes e o nome do utilizador que as criou.

\d nome O comando \d quando seguindo de um nome de uma tabela, índice,

sequência ou vista, mostra a informação inerente ao objecto, tal como

se pode ver através da Figura 1 da página 47.

Este comando é o equivalente ao comando DESC utilizado por exemplo

pelo Oracle.

\l Lista o nome de todas as bases de dados PostgreSQL existentes.

Este comando é o equivalente ao comando SHOW DATABASES utilizado

por algumas bases de dados.

\i ficheiro Permite a execução dos queries SQL e/ou comandos internos que

estejam armazenados no ficheiro indicado.

\o ficheiro Permite o envio do resultado de um query para o ficheiro indicado ou

para um pipe. O resultado será afectado pelas opções activas no

momento. Ou seja: se por exemplo a opção de gerar os resultados em

HTML estiver activa, o resultado será código HTML.

Como Utilizar - Capítulo 4

Página 47

\? Permite obter uma lista de todos os comandos internos possíveis

acompanhados de uma explicação breve. Muitos destes não foram aqui

abordados.

Tabela 3 – Lista dos comandos internos do psql

Na Figura 1 está presente toda a informação relativa à estrutura da tabela. A zona a

amarelo (a primeira) representa a informação base da tabela. Na coluna um encontra-

se os nomes dos campos; na coluna dois o tipo de dados do campo; na coluna três esta

presente outras opções.

A informação presente na zona verde (a segunda) diz respeito às restrições de

integridade aplicadas aos campos.

Na zona de cor magenta (a terceira) está presente informação relativa à integridade

referencial.

Figura 1 – Resultado da execução do comando \d clientes

4.1.2. pgAccess O pgAccess em relação ao psql não é tão poderoso, mas mesmo assim tem as suas

vantagens. O pgAccess é um cliente “modo gráfico”, multi-plataforma

(Unix/Windows) escrito em Tcl/Tk e tem as seguintes características:

• Possibilidade de criação, modificação, visualização de tabelas. views,

queries, sequências, funções, formulários, diagramas, etc.

• Possibilidade de importação/exportação de tabelas/ficheiros.

• Criação de base de dados.

• Inserção, alteração e remoção de dados nas tabelas.

Como Utilizar - Capítulo 4

Página 48

A Figura 2 mostra um exemplo da interface do pgAccess.

Figura 2 – Ecrã pgAccess (Linux) que mostra o conteúdo de uma tabela

Para mais informações sobre o pgAccess, consulte [6].

4.1.3. pgAdmin/II

O pgAdminII é um cliente com interface gráfica para o Windows. O acesso à base de

dados é efectuado através de ODBC.

As características mais relevantes são:

• Interface tipo explorer para visualizar o servidor PostgreSQL. Veja a

Figura 3 da página 49.

• Criação de edição de utilizadores, grupos, bases de dados, funções

agregadoras, funções, índices, linguagens, operadores, regras, sequências,

tabelas, triggers, tipos de dados e vistas.

• Wizard para criação de queries SQL.

• Wizard para migração da base de dados.

• Wizard para tratar os aspectos da segurança.

• Outras.

Como Utilizar - Capítulo 4

Página 49

Figura 3 – Ecrã principal do pgAdminII no Windows XP

Para mais informações sobre o pgAdminII, consulte [7].

4.1.4. winSQL

O winSQL embora não seja um cliente específico para o PostgreSQL, este pode

considerar-se um bom cliente uma vez que possibilita o acesso a qualquer SGBD,

desde que o respectivo driver ODBC esteja instalado.

As principais características do winSQL são as seguintes:

• Enviar queries para qualquer base de dados que suporte ODBC.

• Obter meta dados para as tabelas, vistas, triggers, stored procedures, etc.

• Criar queries SQL através de wizard.

• Apresentação de resultados sob a forma de uma grelha.

• Reverse engineer a uma base de dados.

Tal como se pode observar na Figura 4 da página 50, a informação disponível vai

desde: os nomes das tabelas, vistas e funções, até informação mais detalhada de cada

uma das tabelas, nomeadamente: os atributos, índices, constraints, tipos de dados, etc.

Como Utilizar - Capítulo 4

Página 50

Figura 4 – Ecrã do winSQL que mostra a parte do dicionário de dados

Para mais informações sobre o winSQL, consulte [8].

Para mais informações sobre outros clientes e projectos relacionados com o PostgreSQL consulte [12].

4.2. Como Utilizar – Exemplo Prático

Partindo do princípio que se acabou de instalar o PostgreSQL e que o servidor está a

correr, são necessários vários passos até se poder aceder a uma base de dados.

Para tal são os passos seguintes são necessários:

• Criar conta de acesso.

• Criar base de dados.

• Autorizar o utilizador.

• Activar a nova configuração.

Como Utilizar - Capítulo 4

Página 51

4.2.1. Criação da Conta de Acesso

Partindo do princípio que temos uma conta (Unix) aberta e cujo utilizador não é o

administrador (root).

Entrar no sistema como root executando o comando su – root. Após ter-se entrado

com sucesso é necessário entrar com a conta (Unix) de administração do PostgreSQL

(postgres) criada durante a fase de instalação. Para tal deve-se executar o seguinte

comando: su – postgres. Com a conta de administração do PostgreSQL activa,

executa-se o comando: createuser nome_utilizador. Este comando geralmente

encontra-se em /usr/local/pgsql/bin/ ou em /usr/bin/.

Ao se executar o comando: createuser teste, serão feitas várias perguntas de forma

interactiva ao utilizador, respeitantes aos privilégios do novo utilizador. Isto acontece

uma vez que este comando não é mais do que uma interface para o comando CREATE

USER que é executado pelo psql.

O comando createuser -A -d -P teste significa:

• Criar um utilizador com o login teste.

• Sem permissões para criar contas de novos utilizadores (parâmetro –A).

• Com permissões para criar novas bases de dados (parâmetro –d).

• Com password a ser fornecida de forma interactiva.

De salientar que é aconselhável por motivos de segurança que os novos utilizadores

não possam criar novos utilizadores. Esta tarefa deve ser executada somente pelo

administrador da base de dados.

É possível criar utilizadores sem o auxílio do comando createuser. Para tal é

necessário entrar no psql e digitar o comando CREATE USER nome_utilizador.

Para mais informações sobre este comando digite \h CREATE USER dentro do psql ou consulte [30] .

Como Utilizar - Capítulo 4

Página 52

4.2.2. Criação da Base de Dados

Uma vez que o utilizador já se encontra criado, chegamos então à fase de criação da

base de dados. Para iniciar esta fase o utilizador já não necessita de ser o

administrador da base de dados. Isto porque durante a criação foi especificado que o

utilizador tinha privilégios para criar bases de dados. Se tal não tivesse sido

especificado, então teria que se usar obrigatoriamente uma conta que tivesse tais

permissões ou a conta de administração (geralmente postgres).

Tal como aconteceu com a criação da conta, também há duas formas de criar uma

base de dados. A primeira forma consiste em usar o comando createdb e a segunda

consiste no uso de um query a ser digitado no psql.

Ao executar-se o comando createdb –U teste –W teste_bd está-se a indicar que se

pretende criar uma base de dados de nome teste_db (último parâmetro), utilizando a

conta do utilizador teste (parâmetro –U) e cuja password seja pedida de forma

interactiva (parâmetro –W).

Para criar a base de dados pelo 2º método é necessário entrar no psql com uma conta

de utilizador e executar o comando: CREATE DATABASE nome_base_dados. Através deste

último será criada uma base de dados com o nome de nome_base_dados que terá

associada aos parâmetros de criação todos os valores por omissão adoptados pela

implementação.

Algumas das opções extra que se podem escolher são:

• Login do dono (owner).

• Localização dos ficheiros no sistema de ficheiros.

• Codificação.

• Outros.

Para mais informações sobre este comando digite \h CREATE DATABASE dentro do psql ou consulte [29].

Como Utilizar - Capítulo 4

Página 53

4.2.3. Autorizar o Utilizador

Nesta fase será dada permissão ao utilizador criado anteriormente para que este se

possa ligar à base de dados.

Existe um ficheiro cujo nome é pg_hba.conf (PostgreSQL host base authentication)

residente geralmente em /usr/local/pgsql/data que serve para gerir o processo de

autenticação. Na Figura 5 encontram-se as configurações por omissão para o ficheiro

pg_hba.conf.

Figura 5 – Exemplo de um ficheiro pg_hba.conf

Por omissão todos os utilizadores que se liguem localmente, entenda-se a partir da

máquina onde reside o PostgreSQL, tem acesso imediato ao servidor sem que seja

necessário password, tal como se pode observar na Figura 5.

Desta forma é aconselhável que este comportamento seja alterado, uma vez que

qualquer utilizador que tenha uma conta na máquina onde reside o servidor e que

conheça uma conta de acesso ao PostgreSQL, poderá ligar-se a qualquer base de

dados sem que lhe seja perguntada qualquer password.

A alteração a efectuar resume-se a trocar o modo de autenticação de trust para

password (passwords não cifradas) ou md5 (passwords cifradas), tal como se pode ver

através da Figura 6 da página 54.

Como Utilizar - Capítulo 4

Página 54

Figura 6 - Exemplo de um ficheiro pg_hba.conf com métodos de autenticação password e md5

Uma vez que por omissão só os utilizadores locais podem ligar-se, é necessário

adicionar uma nova linha que permita que o novo utilizador possa aceder através de

uma máquina remota. Para tal deve-se adicionar uma linha semelhante à que se segue:

Tipo Base Dados Utilizador Endereço IP Máscara IP Método

Autenticação

host teste teste 192.168.1.1 255.255.255.255 password

A primeira coluna define qual a forma como a informação das restantes colunas será

interpretada. Os valores possíveis para o tipo são:

• local – Define que o tipo de acesso será através de sockets Unix.

• host - Define que o tipo de ligação será via TCP/IP.

• hostssl – Define que o tipo de acesso será TCP/IP seguro, protegido

através de SSL – Secure Socks Layer.

A 2ª coluna define quais os nomes das bases de dados (separados por vírgulas) a que a

linha respeita. Existe um valor especial all que diz respeito a todas as base de dados.

Há no entanto outro valores especiais que não são mencionados aqui.

A 3ª coluna define quais os logins dos utilizadores (separados por vírgulas) a que a

linha respeita. Existe um valor especial all que diz respeito a todos os utilizadores.

Há no entanto outros valores especiais que não são mencionados aqui.

Para mais informações sobre os outros valores especiais que o campo pode tomar,

consulte [14].

Como Utilizar - Capítulo 4

Página 55

A 4ª e 5ª colunas definem o endereço IP e respectiva máscara a usar. Através destes

dois parâmetros é possível definir uma máquina ou uma gama de endereços. Estas

duas colunas só são aplicadas a registos do tipo host ou hostssl.

A 6ª coluna define o tipo de autenticação. Os valores possíveis são:

• trust – A ligação é permitida incondicionalmente. Este método permite

que qualquer utilizador consiga ligar-se à base de dados em questão, como

sendo qualquer um dos utilizadores previamente criados (mesmo

administrador) sem que seja pedido qualquer password.

• reject – Este método permite que a ligação seja rejeitada através da

análise do IP e da máscara. Este é útil para situações do tipo: não permitir

a ligação dos hosts do departamento de Recursos Humanos.

• md5 – Este método obriga que o cliente use uma password cifrada com

MD5 – Message Digest 5.

• crypt – Parecido com o método md5, mas usa um algoritmo de cifragem

antigo o DES — Data Encryption Standard. Este é necessário para clientes

anteriores à versão 7.2.

• password – a password é enviada de forma não cifrada (clear-text). Este

método não é aconselhável em redes não confiáveis entre as quais se

destaca a Internet.

• krb4 – usa Kerberos V4 para autenticar o utilizador. É obrigatório usar

TCP/IP.

• krb5 – usa Kerberos V5 para autenticar o utilizador. É obrigatório usar

TCP/IP.

• ident – Só em ligações TCP/IP. Usa um servidor ident do lado do cliente

para obter o login usado no sistema operativo. Esta forma não é nada

segura e só deve ser usada caso o cliente seja de confiança. No caso de

ligações locais o login é obtido directamente a partir do sistema operativo.

Este método é bastante útil para automatização de tarefas de manutenção

como é o caso da criação de backups.

Como Utilizar - Capítulo 4

Página 56

• pam – Autentica o utilizador através de PM - Pluggable Authentication

Modules. O PAM consiste num método de autenticação flexível

geralmente usado em Unix/Linux.

Para mais informações sobre o processo de autenticação, consulte [14].

4.2.4. Activar a Nova Configuração

Uma vez feitas alterações no ficheiro de autenticação é necessário indicar ao

PostgreSQL que este foi alterado. Para tal é necessário executar o seguinte comando:

pg_ctl reload -D DATADIR. Em que DATADIR consiste no directório onde o ficheiro

pg_hba.conf reside, geralmente em /usr/local/pgsql/data/.

Como Utilizar - Capítulo 4

Página 57

4.3. Instalação do Driver ODBC do PostgreSQL

Antes de se poder instalar o driver é necessário descarregar o mesmo através de [38].

Caso a versão do Windows seja o Windows 2000/XP então pode usar os ficheiros

com a extensão .msi, uma vez que estas plataformas já trazem instalado o Microsoft

Installer. Para as restantes versões é necessário descarregar o ficheiro com a extensão

.exe.

Caso já tenha instalado e configurado algum driver de ODBC - Open Database

Connectivity não necessita de ler a secção que se segue. Caso contrário, por favor

continue a ler.

Após a instalação do driver é necessário criar um DSN – Data Source Name para que

seja possível aceder ao servidor onde reside o PostgreSQL. Um DSN consiste num

nome atribuído pelo utilizador pelo qual será possível aceder à base de dados através

de uma aplicação que use ODBC, como é o caso do winSQL.

4.3.1. Criar o DSN

Para se criar o DSN deverá abrir o Control Panel.

Para o Windows2000/XP deve entrar em:

Administrative Tools -> Data Sources (ODBC)

Para o Windows 95/98/NT deve entrar em:

ODBC –Data Sources (32bit)

De seguida deverá aparecer uma janela semelhante à figura abaixo.

Como Utilizar - Capítulo 4

Página 58

Figura 7 – Secção (tab) UserDSN

Nessa janela certifique-se que a secção (tab) User-DSN está visível.

Clique sobre o botão Adicionar (Add).

Vai aparecer uma janela semelhante à da figura abaixo.

Figura 8 – Janela de criação de um DSN

Desloque-se para o fim da lista e seleccione a linha cujo nome é PostgreSQL e

pressione Fim (Finish).

Como Utilizar - Capítulo 4

Página 59

Vai-lhe aparecer uma janela igual à que se encontra abaixo.

Figura 9 – Configuração de um DSN para o PostgreSQL

Para cada um dos campos digite:

Campo Descrição

Data Source Nome do DSN a ser fornecido às aplicações ODBC que usem o

PostgreSQL.

Database Nome da base de dados à qual se pretende ligar.

Server Nome DNS ou endereço IP do servidor PostgreSQL.

User Name Nome do utilizador a usar para estabelecer a ligação.

Port Porta TCP/IP a ser usada. Por defeito é a porta 5432.

Password Password do utilizador.

Tabela 4 – Informação necessária para a criação de um DSN

De seguida pressione o botão Save. Nota: O botão Datasource só deve ser usado pelos utilizadores que já tenham alguma

prática com o PostgreSQL, uma vez que permite configurar opções avançadas.

Embora existam vários clientes para usar com o PostgreSQL, é possível programar

com este através de várias API’s – Application Programming Interface.

Para mais informações consulte a secção 5.

05

5.1. A Interface de Programação libpq 61

5.1.1. Gestão de Ligações 62

5.1.1.1.A Função PQconnectdb 63

5.1.1.2.A função PQsetdbLogin 64

5.1.2. Funções Para Processamento Síncrono de Queries 64

5.1.2.1.A Função PQexec 64

5.1.2.2.A Função PQresultStatus 65

5.1.2.3.A Função PQclear 65

5.1.3. Funções Para Obtenção de Informação do Resultado 65

5.1.4. Funções Para Obtenção de Valores do Resultado 66

5.1.5. Funções Para Processamento Assíncrono de Queries 67

Programar Com o PostgreSQL - Capítulo 5

Página 61

5. Programar com o PostgreSQL Pretende-se com este capítulo demonstrar quais as capacidades do PostgreSQL no que

respeita às API’s – Application Programming Interface suportadas por este.

As interfaces de programação (API) suportadas pelo PostgreSQL são:

Interface de

programação

Descrição

libpq Interface de programação para aplicações escritas em C.

libpq++ Interface de programação para aplicações escritas em C++.

libperl Interface de programação para aplicações escritas em Perl.

Php Interface de programação para aplicações escritas em PHP.

Pgtcl Interface de programação para aplicações escritas em TCL.

Ecpg Interface de programação para aplicações escritas C com código SQL

embebido, parecido ao PRO C do Oracle.

Jdbc Interface de programação para aplicações escritas Java.

pygresql Interface de programação para aplicações escritas em Python.

Tabela 5 – As interfaces de programação (API) do PostgreSQL

Embora o PostgreSQL suporte todas as API mencionadas acima, neste documento

serão somente abordadas as interfaces de programação: libpq e php. A primeira terá

obrigatoriamente que ser abordada em maior pormenor, uma vez que a grande

maiorias das restantes interfaces a usam.

A interface PHP será abordada porque é um dos grandes focos de uso nos sites Web.

5.1. A Interface de Programação libpq

A libpq é a API do PostgreSQL para a linguagem C. A libpq consiste num conjunto

de rotinas que permite aos programas cliente enviar queries SQL para o servidor e

receber o resultado desses mesmos. Esta API é também a base de outras API’s como é

o caso da libpq++ (C++), libpgtcl (TCL), Perl e a ecpg (C com SQL embebido) e do

PHP, por isso esta será abordada em maior pormenor do que as restantes.

Programar Com o PostgreSQL - Capítulo 5

Página 62

Na secção dos anexos, nas páginas 138 - 144 encontram-se dois exemplos de código

comentado em linguagem C que ilustra o uso das funcionalidades mais frequentes da

API.

Esta API disponibiliza as seguintes funcionalidades:

• Funções para gestão de ligações.

• Funções para processamento síncrono de queries.

• Funções para processamento assíncrono de queries.

• Funções para debugging da própria API.

5.1.1. Gestão de Ligações

As funções que se seguem são as funções responsáveis pelo estabelecimento de

ligações a uma base de dados PostgreSQL.

PGconn *

PQconnectdb(const char *conninfo)

PGconn *

PQsetdbLogin(char *host, char *port, char *ops, char *tty, char *dd,

char *login, char *pwd

)

Estas funções permitem a abertura de uma ligação à base de dados usando a

informação presente no parâmetros e devolvem um descritor para a ligação

estabelecida em caso de sucesso. Como se pode ver pela assinatura da primeira

função, esta é a função flexível.

Há outras funções e/ou macros que asseguram a mesma funcionalidade, mas são

pequenas variações da primeira.

Para mais informações sobre esta e outras funções, consulte [15].

Programar Com o PostgreSQL - Capítulo 5

Página 63

5.1.1.1. A Função PQconnectdb PGconn *

PQconnectdb(const char *conninfo)

O parâmetro conninfo deve ser formado por pares de valores do tipo chave = valor

em que as constantes possíveis para chave são:

host

Nome do host ao qual se quer estabelecer a ligação. Caso o nome comece por

\, então o valor representa o nome do directório para um socket Unix, caso

contrário representa o FQHN (Full Qualified Host Name) do host. hostaddr

Endereço IP do host ao qual se quer estabelecer a ligação. O endereço deve

estar representado segundo a forma decimal separada por pontos ( ou seja:

com o formato xxx.xxx.xxx.xxx).

Esta chave permite que a ligação possa ser estabelecida sem que seja

necessário conhecer o FQHN do host, o que para algumas aplicações com

restrições temporais elevadas é crucial (o caso dos sites na Internet). port

Número da porta TCP a usar para o estabelecimento da ligação ou o nome da

extensão para o ficheiro do socket Unix. dbname

Nome da base de dados à qual é para estabelecer ligação. user Login do utilizador password Password do utilizador. connect_timeout Valor em segundos a usar como time-out para o estabelecimento da ligação. options Opções de debug a serem enviadas para o servidor. tty

Nome de um ficheiro ou de um terminal (tty) para o qual a informação de

debug deve ser enviada.

Programar Com o PostgreSQL - Capítulo 5

Página 64

requiressl Se o valor da chave for 1, o servidor é obrigado a estabelecer uma ligação SSL

ao servidor. Caso a chave seja 0 (por omissão), o tipo de ligação é negociado

com o servidor. Esta opção só esta disponível caso o PostgreSQL tenha sido

compilado com suporte para SSL.

Caso alguma chave não seja especificada, a correspondente variável de ambiente será

usada. Caso não esteja disponível a variável de ambiente, o valor hard coded gerado

durante a compilação será usado.

5.1.1.2. A função PQsetdbLogin PGconn *

PQsetdbLogin(char *host, char *port, char *ops, char *tty, char *dd,

char *login, char *pwd

)

Esta função é a antecessora da pqconnectdb e por isso apresenta as mesmas

funcionalidades.

5.1.2. Funções Para Processamento Síncrono de Queries

Neste ponto serão abordadas as funções que asseguram a execução de queries, bem

como as funções necessárias à extracção de informação sobre o resultado.

5.1.2.1. A Função PQexec PGresult *PQexec(PGconn *conn, const char *query);

Esta função envia o query SQL para o servidor e aguarda pela resposta.

O valor retornado encapsula o resultado recebido a partir do servidor. Aconselha-se os

programadores a manterem as propriedades ADT - Abstract Data Type do resultado,

ou seja não usar os campos da estrutura directamente, mas sim usarem as funções e/ou

macros existentes, uma vez que é muito provável que a estrutura dos campos seja

alterada em versões futuras.

Programar Com o PostgreSQL - Capítulo 5

Página 65

5.1.2.2. A Função PQresultStatus ExecStatusType PQresultStatus(const PGresult *res)

Esta função permite obter o estado em que o descritor do resultado se encontra após a

execução. É através desta função que se pode saber se ocorreu um erro no parsing

e/ou execução do query. O valor retornado consiste num tipo enumerado em que os

valores mais frequentes são os que se encontram na tabela abaixo.

Valor devolvido Descrição PGRES_COMMAND_OK Sucesso na execução de um query que não devolve resultados

( ou seja: INSERT, UPDATE, DELETE, etc.). PGRES_TUPLES_OK Sucesso na execução de um query que devolve resultados (ou

seja: SELECT), mesmo que não hajam linhas no resultado. PGRES_FATAL_ERROR Indica que o parsing ou mesma a execução do query falhou.

Tabela 6 – Alguns valores possíveis para os estados da execução de um query

5.1.2.3. A Função PQclear void PQclear(PQresult *res);

Esta função permite que os recursos do descritor associados à execução de um query

sejam libertados. Esta função deve ser invocada quando o resultado do query não for

necessário. Em caso de erro na execução, esta função tem que ser invocada para que a

memória usada pelo descritor seja devolvida ao sistema, desta forma evitando fugas

de memória (memory leak).

5.1.3. Funções Para Obtenção de Informação do Resultado int PQntuples(const PGresult *res);

int PQnfields(const PGresult *res);

char *PQfname(const PGresult *res, int field_index);

int PQfnumber(const PGresult *res, const char *field_name);

int PQbinaryTuples(const PGresult *res);

Programar Com o PostgreSQL - Capítulo 5

Página 66

As funções apresentadas acima e na tabela abaixo permitem obter informação relativa

ao resultado obtido. Não confundir a finalidade desta função como sendo a função

responsável pelo obtenção dos resultados propriamente ditos.

Função Descrição PQntuples Devolve o número de tuplos (registos) presentes no resultado. PQnfields Devolve o número de campos (colunas) presentes em cada linha do

resultado. Pqfname Devolve o nome do campo que está associado ao índice indicado

pelo parâmetro field_index. Os índices começam em 0. PQfnumber Devolve o índice do campo que está associado ao nome do campo

indicado pelo parâmetro field_name. Os índices começam em 0.

Tabela 7 – Funções para obtenção de informação sobre o resultado

5.1.4. Funções Para Obtenção de Valores do Resultado

char* PQgetvalue(const PGresult *res, int tup_num, int field_num); int PQgetisnull(const PGresult *res, int tup_num, int field_num);

int PQgetlength(const PGresult *res, int tup_num, int field_num);

As funções apresentadas acima e na tabela abaixo permitem obter os valores relativos

ao resultado. Não confundir a finalidade desta função como sendo a função

responsável pela obtenção de informação relativa ao resultado.

Função Descrição PQgetvalue Obtém o valor do registo identificado pelo índice de tuplo tup_num

e da coluna cujo índice é identificado por field_num. Os índices

quer dos tuplos quer das colunas começam em 0. PQgetisnull Verifica se o valor do registo identificado pelo índice de tuplo

tup_num e da coluna cujo índice é identificado por field_num é NULL

(contexto do SQL).

Programar Com o PostgreSQL - Capítulo 5

Página 67

PQgetlength Devolve o tamanho em bytes do valor do registo identificado pelo

índice de tuplo tup_num e da coluna cujo índice é identificado por

field_num. Os índices quer dos tuplos quer das colunas começam

em 0.

Tabela 8 – Funções para obtenção de valores do resultado

5.1.5. Funções Para Processamento Assíncrono de Queries

Embora o PostgreSQL suporte a execução assíncrona de query e estabelecimento

assíncrono de ligações, este tópico não será abordado neste documento uma vez que é

um mecanismo usado em situações pouco frequentes.

Na secção dos anexos, encontra-se um exemplo de código em linguagem C

comentado que ilustra o uso desta funcionalidade.

Para mais informações sobre este assunto, consulte [15] e [16].

06

6.1. Índices 69

6.1.1. Aparecimento dos Índices 69

6.1.2. Os Índices nos SGDB’s 73

6.1.2.1.Representação por Árvores B+ 73

6.1.2.3.Representação por Hashing 75

6.1.3. Dicas para Uso de Índices 78

6.1.4. Índices no PostgreSQL 81

6.1.4.1.Índices Monocoluna 82

6.1.4.2.Índices Multicoluna 82

6.1.4.3.Índices Únicos 84

6.1.4.5.Índices Parciais 85

6.1.5. Avaliar o Uso dos Índices 86

6.2. Stored Procedures 88

6.2.2. A linguagem PL/pgSQL 89

6.3. Triggers 95

6.3.1. Triggers no PostgreSQL 98

6.4. Controlo da Concorrência 98

6.4.1. Transacções 100

6.4.1.1.Transacções no PostgreSQL 102

6.4.1.2.Níveis de Isolamento 105

Funcionalidades Avançadas - Capítulo 6

Página 69

6. Funcionalidades Avançadas

Nesta secção serão abordados alguns dos mecanismos que existem no PostgreSQL

mas que geralmente não são de uso frequente. Não que isto dizer que não sejam

importantes, senão vejamos os assuntos a abordar: índices; stored procedures;

triggers; concorrência; transacções e níveis de isolamento.

Foi feito um enorme esforço para que os assuntos abordados nas próximas secções

fossem acessíveis quer para as pessoas principiantes no meio, quer para aquelas que já

têm alguns conhecimentos. Para tal e a pensar nos utilizadores menos experientes, é

dada uma introdução sobre o tema, acompanhando sempre que oportuno com

exemplos do quotidiano. Para os utilizadores mais experientes há assuntos que são

abordados de forma mais aprofundada, chegando em algumas situações a haver

referências à implementação.

6.1. Índices

Nesta secção começa-se por explicar o aparecimento dos índices, de seguida é dada

uma perspectiva mais de implementação destes. Aborda-se os vários tipos de índices

suportados, quais as vantagens e desvantagens. Explica-se com comandos DDL como

é que se podem criar os vários tipos de índices. De seguida são fornecidas várias dicas

sobre o que se deve e não se deve indexar e de que forma indexar. Na parte de

avaliação de uso de índices são mostrados e analisados alguns exemplos de planos

efectuados pelo planner (motor de planeamento do SGBD) do PostgreSQL.

6.1.1. Aparecimento dos Índices

Como certamente será do conhecimento do leitor, o grande problema com que os

SGBDs se debatem desde à vários anos está relacionado com a quantidade de I/O

(Input/Output) a nível de acesso à memória secundária (ou seja: discos rígidos).

Se tivermos um ficheiro que está organizado sob a forma de registos e se quiserem

procurar um registo com alguma informação específica que está algures armazenada

no ficheiro, a solução é ler o ficheiro bloco a bloco, sequencialmente desde o inicio

Funcionalidades Avançadas - Capítulo 6

Página 70

até ao fim. A esta operação dá-se o nome de varrimento sequencial, também

conhecido na área das bases de dados como sequencial scan.

Como se pode constatar através da descrição acima, esta operação de pesquisa é muito

ineficiente e dispendiosa a nível de recursos (quer de CPU que de acessos a disco).

Para minorar estas questões os SGBDs como é o caso do Oracle e o PostgreSQL usam

na sua arquitectura interna uma série de zonas de memória partilhada que

desempenham a função de cache. Este nível de cache encontra-se entre o SGBD e o

Sistema Operativo como se pode observar na Figura 10.

Figura 10 – Caminho simplificado a percorrer desde um query até atingir os dados em disco

Analisando a figura acima pode-se ver que a aplicação faz chegar o query ao SGBD

sob a forma de uma string que contem um comando SQL. Esse mesmo comando ao

ser passado ao motor de processamento de queries, é transformado numa

representação interna ao SGBD. Uma vez obtida a representação interna, o motor,

pede os respectivos registos das tabelas ao gestor de buffers, o qual primeiro verifica

se esses mesmos se encontram em algum blocos de dados (data page) que exista nos

buffers partilhados. Se existirem nos últimos, é percorrido o caminho inverso. Se não,

é feito o pedido ao gestor de armazenamento, o qual fica encarregue de devolver os

blocos de dados necessários (data pages). Este no limite por sua vez pode ter que

consultar vários ficheiros (se a tabela for muito grande). O gestor de armazenamento,

por sua vez, usa1 os serviços do sistema operativo para ter acesso aos dados que foram

1 Por exemplo no caso do Oracle o gestor de armazenamento não interage com a implementação de ficheiros “normais” do próprio sistema, em vez destes são usados os chamados raw files. Isto acontece

Funcionalidades Avançadas - Capítulo 6

Página 71

pedidos. O sistema operativo em si, transforma o pedido do gestor numa localização

em termos físicos para o disco. Uma vez obtidos a localização em disco do respectivo

bloco (não é bloco de dados), o sistema operativo dá a instrução ao controlador de

disco para que este satisfaça o seu pedido. Assim que os dados tiverem sido

lidos/escritos, é necessário percorrer o caminho inverso, onde no limite, é enviada o

resultado do query para o cliente.

Embora a técnica de caching dos blocos seja um mecanismo útil, esta não consegue

resolver todos os problemas de desempenho inerentes aos acessos. Isto passa-se

porque à medida que vão sendo pedidos novos blocos que não existam na cache do

SGDB, este armazena esses novos blocos na memória.

Uma vez que a memória é um recurso escasso (relação às capacidades dos discos

rígidos), o SGDB vai ter que substituir uns blocos da cache por novos blocos. Embora

estas substituições sejam regidas pelo uso de algoritmos com esse fim (Por exemplo:

LRU-Least Recently Used e MRU-Most Recently Used) e também recorrendo a

estatísticas de acessos anteriormente efectuados, este método de caching não resolve

os problemas de desempenho, mas alivia.

Para evitar que os blocos tenham que ser lidos sequencialmente, os criadores de

SGBDs tiveram que arranjar forma de adivinhar quais os blocos a que era necessário

aceder para que fosse possível fornecer uma resposta em tempo útil. Desta forma os

SGDBs passaram a possibilitar a criação de índices.

Como será simples de compreender o índice vai permitir que o acesso à informação

possa ser muito mais rápido.

Por exemplo, se quisermos procurar sobre um assunto qualquer num livro, o que

fazemos é procurar no índice pelo tema e de seguida localizar o assunto através do

número da página. Penso que não haverá ninguém que comece a ler um livro desde o

início até ao fim com o objectivo de procurar um certo assunto.

A estrutura de um índice numa base de dados é semelhante à estrutura de um índice

num livro. Por isso, para que seja possível criar um índice é necessário escolhermos

porque a implementação tem que correr em várias arquitecturas e plataformas e nem todos os sistemas operativos disponibilizam as mesmas características a nível de manipulação avançada de ficheiros. Actualmente o PostgreSQL usa a camada do sistema operativo.

Funcionalidades Avançadas - Capítulo 6

Página 72

qual a informação a indexar. Esse informação (campo(s)) vai constituir o que se

chama de chave de pesquisa.

Se se quiser saber o nome de todos os clientes da zona Norte e que sabemos que esses

clientes estão todos no bloco 2 da figura abaixo, o único bloco que teria que ser lido e

processado seria esse mesmo. Através deste acesso directo ao bloco, evitou-se o

acesso e processamento dos restantes blocos (1,3,4), evitando assim I/O

desnecessário.

A forma de se aceder aos dados através de um índice é geralmente algo similar à

Figura 11 da página 72. A ideia subjacente à figura é que para cada entrada no índice,

existe um par [chave,bloco]. Cada bloco no par [chave,bloco] tem obrigatoriamente

correspondência com um bloco que tem que existir no pior dos no disco. A cada bloco

de dados estão associados vários registos (o dados). Como se pode observar há uma

dependência entre a entrada do índice e o bloco associado, o que quer dizer que

ambos têm que estar em síncronia. Há ainda uma outra dependência em relação ao

valor da chave no índice e o valor armazenado nos registos, ambos têm que existir

quer no índice quer no ficheiro de dados. Em relação aos dados que residem nos

ficheiros de dados estes não têm qualquer ordem2 associada.

Figura 11 – Camadas a percorrer para aceder aos dados através de um índice

2 Por razões de desempenho, as implementações colocam os registos novos e/ou alterados no fim do ficheiro de dados. Esta característica não deve ser levada em conta na criação de queries. Sempre que se queira um resultado ordenado, a intenção tem que aparecer explicitamente no query.

Funcionalidades Avançadas - Capítulo 6

Página 73

6.1.2. Os Índices nos SGDBs

Como no caso de acesso a índices estamos perante uma pesquisa, logo a solução será

representar o índice em memória e sob a forma de árvore. Como se pode observar na

figura anterior, os índices ocupam em termos de memória e disco necessariamente

menos espaço que os dados.

Tal como acontece em variadíssimas situações que envolvem pesquisas, o método

preferencial consiste em representar o índice em memória sob a forma de árvore.

As formas mais usuais de representação de índices em memória são:

• Em forma de árvore ( árvore B, árvore B+, árvore R, etc.).

• Em forma de acesso directo (função de hash).

6.1.2.1. Representação por Árvores B+

Uma árvore B+ é uma estrutura de dados em forma de árvore. Os nós internos (não

folha) servem para direccionar a pesquisa, uma vez que guardam a chave e

apontadores para os respectivos nós filho. Os nós folha contêm informação relativa à

localização no bloco de dados. Nestas implementações os nós folha estão geralmente

ligados através de uma lista duplamente ligada, possibilitando por isso pesquisas mais

eficientes.

Figura 12 – Exemplo de uma árvore B+ de ordem dois

Na Figura 12 encontra-se um exemplo de uma árvore B+, cuja chave de pesquisa são

números inteiros (presentes na raiz) e os dados do índice são representados por

inteiros seguidos de * (Por exemplo: 2*). A árvore é de ordem quatro, o que significa

que cada nodo pode ter no máximo quatro elementos. A ordem associada à árvore

Funcionalidades Avançadas - Capítulo 6

Página 74

corresponde geralmente a número de blocos por sector do disco rígido. Esta

associação acontece, devido a factores relacionados com o desempenho. Embora as

árvores B+ aumentem a velocidade das pesquisas, a operação de inserção num nó

cheio, penaliza o desempenho da árvore, como se pode ver através da Figura 13. Isto

acontece porque o nó que atingiu o limite máximo, tem que ser dividido.

Para ilustrar as operações necessárias sobre a árvore, suponhamos que se queria

adicionar o elemento 8* à árvore da Figura 12. O nodo onde o elemento 8* será

colocado, corresponde ao ramo da esquerda porque que 8 é inferior a 13. Uma vez que

esse ramo está cheio, vai então ser necessário criar um novo nó folha e passar os

elementos 5*, 7* para o novo nodo. Esta última operação leva a que um nó interno (5)

seja criado. Esta situação é ilustrada pela Figura 13.

Figura 13 – 1º passo na inserção dum nó cheio

Uma vez que a inserção do nó 5, no nó pai (neste caso a raiz) vai levar a que este

fique completamente ocupado, é necessário dividir o mesmo. Uma vez que tal

acontece torna-se necessário dividir o respectivo nó. Após todas as operações

descritas anteriormente, a sub árvore esquerda resultante é a que esta presente na

Figura 14.

Figura 14 – Divisão da raiz causada pelo novo nó

Raiz

Funcionalidades Avançadas - Capítulo 6

Página 75

Como durante o processo houveram divisões nos nós então a sub árvore que reflectir a

divisão executada, resultando assim na árvore presente na Figura 15.

Figura 15 - Versão final da árvore após inserção do elemento 8*

Vantagens/Desvantagens

Permite pesquisas rápidas quer a chave seja simples ou composta.

Único método que permite pesquisas com gamas de valores. Por

exemplo: Idade >=18 E idade <=30.

- Inserção e remoção na árvore pode causar o balanceamento da árvore.

6.1.2.2. Representação por Hashing

O uso de um índice sob a forma de uma tabela de hashing é uma outra maneira

eficiente de aceder aos dados. A ideia da tabela de hashing consiste em distribuir as

entradas do índice ao longo de vários grupos (conhecidos também por buckets, em

português significa balde), que são compostos por elementos semelhantes. A

semelhança entre os elementos é conseguida através de uma regra a que se chama

função de hash.

A ideia subjacente a esta função consiste em descobrir através de uma chave de

pesquisa qual o grupo correspondente. Depois de ser encontrado o grupo, a pesquisa

será depois restrita a esse mesmo grupo. Após a chave de pesquisa ter sido

encontrada, obtem-se a posição relativa (offset) da localização do bloco no disco. Para

se obter a posição exacta é necessário somar à posição encontrada a posição do

primeiro elemento da tabela de hashing.

Raiz

Funcionalidades Avançadas - Capítulo 6

Página 76

A Figura 16 mostra uma tabela de hash com capacidade para quatro buckets. A função

de hash a usar será: f(h) = Chave Pesquisa / 10. Desta forma a tabela de hash está

limitada a números entre 10 e 40.

Figura 16 – Exemplo de uma tabela de hash

Suponhamos que queríamos adicionar a chave 20. Através da função de hash,

obtemos uma posição de 2. Uma vez que o bucket da posição 2 está livre, logo o

elemento será inserido nessa mesma posição. O mesmo raciocínio seria aplicado para

a chave 40. A Figura 17 retrata esta situação.

Figura 17 – Função de hash, após a inserção das chaves 20 e 40

Suponhamos agora que queríamos adicionar a chave 34, a qual seria inserida na

posição 3. Tal como se pode observar na figura acima, não existe mais espaço livre na

posição 3. Esta situação leva à ocorrência de uma colisão. Este é um dos problemas

das funções de hash.

A forma que há para se resolver este problema resume-se a adicionar a chave que

entrou em conflito à lista ligada da entrada. Este tipo de situações leva a que as

funções de hash sejam usadas em situações muito especiais (ver secção seguinte). A

Figura 18 retrata uma situação em que a adição dos elementos 34 e 31 à tabela de

hash vão originar colisões com a chave da posição 3. Tal como representada na

Funcionalidades Avançadas - Capítulo 6

Página 77

figura, esta situação é resolvida através da colocação das chaves 34 e 31 na lista

ligada do bucket 3.

Figura 18 – Tabela de hash com overflow no bucket 3

O método de hashing descrito até agora foi o Static Hashing com páginas de overflow.

Este método de hashing não é usado nos SGBDs porque é um método estático. Em

vez do método descrito acima, os SGBDs geralmente usam o método Extensible

Hashing o qual é dinâmico e por isso flexível. O último método é muito complexo e

por isso não recomendado para explicar o funcionamento das tabelas de hash. Devido

a essa razão, não foi o método seleccionado para a abordagem da indexação através de

tabelas de hash.

Para mais informação sobre índices baseados em hashing, consulte [31].

Vantagens/Desvantagens

Método muito rápido para comparações que usem o operador =. Geralmente é

usado em situações de joins que envolvam o método Index Nested Loop.

- Não permite pesquisas usando qualquer outro operador que não o =, nem

mesmo gamas de valores. Por exemplo: idade >=18 and idade <=30.

- Há medida que se vão gerado novas entradas que entram em conflito com

entradas já existentes, o desempenho do índice vai-se degradando

significativamente.

Como facilmente se depreenderá pela análise dos parágrafos precedentes, saber o que

indexar (os campos), como indexar (tipo de índice) e quando indexar é uma tarefa

importante para a extracção de informação em tempo útil.

Quando um query está a ser analisado pelo motor de planeamento (planner), um dos

factores que pode influenciar a decisão na escolha de um plano em relação a outro

Funcionalidades Avançadas - Capítulo 6

Página 78

poderá ser a existência ou não de índices apropriados nos campos apropriados. Só

desta forma é que o planner poderá ser capaz de fazer a escolha acertada.

Nota: Os índices são a primeira ferramenta a que se deve recorrer para melhorar o

desempenho de um SGBD. São também contudo um recurso cujo uso tem que ser

pensado. Há pelo menos duas situações em que o planner não tira qualquer proveito

destes.

A primeira situação diz respeito às tabelas cujo número de registos seja reduzido.

Nestas condições um varrimento à tabela será certamente menos dispendioso do que o

uso de um índice. O descrito acontece se essa mesma tabela ocupar um ou dois blocos

de dados e se esses blocos se encontrarem na cache do SGDB. Relativamente à

segunda, no caso de tabelas onde são constantemente adicionados e/ou modificados

registos, o desempenho é obrigatoriamente prejudicado pela existência dos índices.

Esta quebra de desempenho ocorre porque há a necessidade de manter quer o

“ficheiro” de dados quer o do índice em perfeita síncronia para que o índice possa ser

útil.

6.1.3. Dicas para Uso de Índices

As melhores dicas a ter em conta na optimização do uso dos índices são sem dúvida

baseadas na experimentação. Tal acontece porque não há regras rígidas no que

respeita ao uso de índices, uma vez que o uso destes depende da estrutura dos dados e

dos dados em si.

No entanto há algumas dicas que podem ser tidas em contas que geralmente resultam

em melhor uso dos índices.

Na Tabela 9 encontram-se algumas dicas a ter em conta no uso de índices.

Funcionalidades Avançadas - Capítulo 6

Página 79

Dica nº 1 Usar sempre o comando EXPLAIN e as estatísticas para avaliar o uso dos

índices. As estatísticas recolhidas são um dos factores que podem

melhorar ou piorar (se não reflectirem a verdade) a “qualidade” dos

planos de execução gerados pelo planner. Algumas das estatísticas que o

PostgreSQL recolhe:

• Total de inserts, updates, deletes por tabela

• Total de registos por tabela

• Total de bytes lidos e escritos

• Total de bytes que tiveram hit (não foi necessário ler directamente

a partir do disco porque estava na cache do SGBD)

• Distribuição dos valores ao longo da tabela

• Outros

Para mais informações sobre os dados recolhidas para as estatísticas e mecanismos associados, consulte [17].

Dica nº 2 Usar índices de forma regrada. Numa base de dados o desempenho pode

ser mais penalizada pela ausência de índices do que pela presença destes.

Dependendo do tipo de acessos em causa à base de dados, geralmente é

melhor ter índices do que não ter. Esta dica parece uma contradição do

até agora foi dito, mas só alguém que conheça as tabelas e processos de

negócio é que pode decidir qual o tipo de acessos mais frequentes. Dica nº 3 Criar o tipo de índice apropriado. Se na estrutura de uma tabela há um

campo que tem que ter valores obrigatoriamente únicos para além da

chave primária, então esse campo deve usar uma cláusula UNIQUE.

Se não se proceder desta forma, sempre que o campo seja usado numa

cláusula WHERE o SGBD vai continuar a procurar novas ocorrências, uma

vez que na definição da tabela não foi especificado que os valores desse

campo eram únicos. Dica nº 4 Indexar os campos que se pensem que são usados frequentemente como

condições da cláusula WHERE.

Deve-se indexar os campos pelos quais se procura informação é não os

valores por que se procuram. Por exemplo: uma aplicação que permita

obter os contactos de clientes através de pesquisas por nome, cidade,

região deve indexar o nome, cidade e região e não os contactos em si.

Funcionalidades Avançadas - Capítulo 6

Página 80

Dica nº 5 Os campos usados nas cláusulas ORDER BY, GROUP BY e HAVING são fortes

candidatos a serem indexados. Dica nº 6 Indexar sempre todos os campos que sejam usados para fazer Joins, em

ambos os lados do Join. Dica nº 7 Indexar sempre os campos que sejam usados como chave estrangeira. Dica nº 8 Os índices têm um preço. Estes necessitam de usar memória e tempo de

processamento para serem mantidos e espaço em disco para serem

armazenados. Os índices que não sejam usados (podem ser detectados

através das estatísticas) podem ser removidos. Dica nº 9 Não indexar grandes campos de texto. Se tivermos um campo de texto do

tipo VARCHAR(500) e se as pesquisas sobre esse campo não forem do tipo

“começa por X” e forem antes do tipo “contém X”, um índice nesta

situação é inútil. Os queries do tipo “começa por” são algo semelhante a

SELECT campo FROM tabela WHERE campo LIKE ‘valor%’; Por sua vez um

query do tipo o campo “contém X” é algo do género: SELECT campo FROM tabela WHERE campo LIKE ‘%valor%’.

Dica nº10 Esta dica parece ridícula, mas há relatos relacionadas com esta. Não

indexar campos binários (BLOBs). No PostgreSQL estes campos são do

tipo bytea. Dica nº11 Não indexar colunas com muitos valores mas de gamas reduzidas. Por

exemplo colunas do tipo BOOLEAN. Dica nº12 Não indexar tabelas que são escritas muitas vezes e que muito raramente

são consultadas. Neste género de tabelas encontram-se as que geralmente

são usadas para registos de actividade (logs). Dica nº13 Não indexar colunas cujos valores estejam constantemente a serem

alterados. Por exemplo: uma tabela de utilizadores que guarde um

timestamp do utilizador sempre que este visita uma página. Nesta

situação a única coluna que faz sentido indexar é a coluna que identifica o

utilizador. A coluna que armazena o timestamp não deve ser indexada.

Tabela 9 – Dicas para uso de índices

Funcionalidades Avançadas - Capítulo 6

Página 81

6.1.4. Índices no PostgreSQL

Através da observação da Tabela 10, podem-se ver quais os tipos de índices e em que

situações e que estes devem ser usados. O planner do SGDB irá usar um tipo de

índice em vez de outro, dependendo do tipo de query e dos campos envolvidos. O uso

ou não dos índices, está directamente ligado com o género de algoritmo que usam

internamente.

Tipo de índice Usar c/ operadores Aplicação

Árvore B <, <=, =, >=, > Generalidade dos casos.

Árvore R <<, &<, &>, >>, @,

~=, &&

Dados esparsos (ou seja: OLAP, cubos

OLAP, etc.).

Hash = Pesquisas que envolvam joins que usem o

método Index Nested Loop.

Gist -

Generalized

Search Trees

<, <=, =, >=, > Sistemas de Informação Geográficos.

Tabela 10 – Relação entre o tipo de índice, operadores suportados e aplicação prática

Para mais informações sobre os índices Gist, consulte [5].

Na generalidade o PostgreSQL suporta os géneros de índices presentes na Tabela 11

que se encontra na página 81.

Género do

índice

Descrição

Mono coluna São os índices mais usuais. Usam uma coluna.

Multi coluna Usa um conjunto fixo de colunas.

Únicos Usa uma ou várias colunas, mas o par dos valores nunca pode ser

repetido.

Funcionais São criados com base no resultado de uma função.

Parciais Criado com base numa parte dos dados.

Tabela 11 – Género de índices suportados

Funcionalidades Avançadas - Capítulo 6

Página 82

6.1.4.1. Índices Mono coluna

Este género de índice é suportado por todos os tipos de índices.

CREATE TABLE CLIENTES( cliente_id SERIAL NOT NULL, cliente_nome VARCHAR(80) NOT NULL, cliente_morada VARCHAR(60) NOT NULL, cliente_idade SMALLINT NOT NULL, codigo_postal_id INTEGER NOT NULL, cliente_sexo CHAR NOT NULL, CONSTRAINT PK_CLIENTES PRIMARY KEY( cliente_id ), CONSTRAINT FK_codigopostal FOREIGN KEY( codigo_postal_id ) REFERENCES CODIGO_POSTAL( codigopostal_id ), CONSTRAINT CK_cliente_idade CHECK( cliente_idade >=2 ), CONSTRAINT CK_cliente_sexo CHECK( cliente_sexo IN( 'M', 'F' ) ) );

Figura 19 – Estrutura da tabela clientes

Usando como referência a tabela clientes ilustrada na Figura 19, para se criar um

índice mono coluna o comando DDL a usar seria:

CREATE INDEX nome_indice ON clientes( cliente_nome );

O índice criado usaria uma árvore B como algoritmo de suporte e seria um índice que

possibilitava a existência de valores repetidos. Este género de índice pode também

não permitir valores repetidos, os índices únicos.

Para mais informações sobre índices únicos, consulte a secção 6.1.4.3. Se quiséssemos criar um índice nas condições anteriores, mas cujo algoritmo de

suporte fosse uma função de hash, o comando a usar seria:

CREATE INDEX nome_indice ON clientes USING HASH( cliente_nome );

6.1.4.2. Índices Multi coluna

Até à data de elaboração deste documento o PostgreSQL só suportava índices multi

coluna em índices do tipo árvore B e GiSt.

O processo de criação de um índice multi coluna é praticamente igual aos métodos

usados anteriormente.

Funcionalidades Avançadas - Capítulo 6

Página 83

Usando como referência a tabela clientes apresentada na Figura 19 acima, para se

criar um índice multi coluna nos campos cliente_nome, cliente_idade e

codigo_postal, o comando DDL a usar seria:

CREATE INDEX nome_indice ON clientes( cliente_nome, cliente_idade,

codigo_postal_id);

Embora o índice criado seja multi coluna o planner do SGDB só o pode usar se os

campos forem usados no query seguindo a mesma ordem que foi usada na criação.

Para o caso do índice anterior, este seria usado por exemplo no seguinte query:

SELECT cliente_nome

FROM clientes

WHERE cliente_nome = ‘JOAO ANTONIO’ AND

cliente_idade > 20 AND

codigo_postal_id = 1

Figura 20 – Exemplo de um resultado do planner a usar o índice ndx_cl_nome_idade_cp

Como se pode ver na Figura 20 o índice está a ser usado. O uso deste é confirmado

através do tipo de varrimento que está a ser usado, neste caso é Index Scan o que

revela o uso do índice.

Se o query fosse:

SELECT cliente_nome

FROM clientes

WHERE cliente_idade = 21 AND

codigo_postal_id = 1

Funcionalidades Avançadas - Capítulo 6

Página 84

Figura 21 - Exemplo de um resultado do planner que não pode usar o índice ndx_cl_nome_idade_cp

Neste último exemplo o índice não poderia ser usado, uma vez que se avançou3 o

primeiro campo do índice. Perante este query o planner não pode usar o índice,

optando por isso por um varrimento à tabela, tal como se pode observar na Figura 21.

O não uso do índice é confirmado através do tipo de varrimento que está a ser usado,

neste caso é Seq Scan (Sequencial Scan) o que mostra que o índice não está a ser

usado.

Pode-se concluir o seguinte:

• Os campos indexados têm que ser usados consecutivamente.

• São usados em: joins de tabelas; ordenação de resultados (cláusula ORDER BY);

na presença das cláusulas GROUP BY e HAVING desde que respeitem o que é

referido no 1º ponto anterior.

6.1.4.3. Índices Únicos

Este género de índice pode ser mono ou multi coluna.

Actualmente no PostgreSQL somente os índices árvore B podem ser únicos.

O PostgreSQL tal como acontece com outros SGBDs como é o caso do Oracle, cria

um índice único automaticamente sempre que encontra uma constraint UNIQUE ou uma

constraint PRIMARY KEY nas respectivas colunas.

3 Em termos do algoritmo de inserção na árvore, isto corresponde a que este deixe de conseguir saber qual o ramo a seguir, dai que o índice não seja usado.

Funcionalidades Avançadas - Capítulo 6

Página 85

Esta característica poderá ser vista como um detalhe de implementação, uma vez que

o standard SQL99 não faz qualquer referência a índices, nem como é que os SGBDs

devem implementar a unicidade de valores nas tabelas.

Este género de índice pode ser adicionado ou removido a qualquer momento.

6.1.4.4. Índices Funcionais

Um índice diz-se funcional quando os valores a indexar correspondem ao

processamento dos campos a indexar por parte de uma função, quer seja uma função

do sistema, quer seja um stored procedure do utilizador.

Suponhamos que tínhamos uma aplicação que permitia fazer pesquisas de informação

dos clientes através do nome. Suponhamos ainda que os nomes são inseridos na tabela

quer com minúsculas quer com maiúsculas (Por exemplo: ‘João’).

Uma forma de tornar a pesquisa mais eficiente seria através da criação de um índice

funcional sobre o campo cliente_nome, mas cujo nome do cliente no índice fosse

armazenado em minúsculas.

Para se converter uma string para minúsculas usa-se a função lower.

O índice funcional a criar seria:

CREATE INDEX nome_index ON clientes( lower(cliente_nome) );

Desta forma queries do género:

SELECT * FROM clientes WHERE lower(cliente_nome) = ’joao’

seriam muito mais eficientes

6.1.4.5. Índices Parciais

Um índice diz-se parcial quando os valores a indexar correspondem a parte dos dados.

Suponhamos que numa aplicação que permite a pesquisa de informação relativa a

filmes, o tipo de filme mais procurado eram os de acção.

Sendo este o tipo de filme mais procurado, seria bom termos um índice só para estes.

Funcionalidades Avançadas - Capítulo 6

Página 86

Para se criar um índice parcial sobre o campo filme_tipo na tabela filmes, a instrução

a usar seria:

CREATE INDEX nome_indice ON Filmes( filme_tipo ) WHERE filme_tipo =’A’;

Embora na instrução acima a condição seja simples, a condição pode envolver queries

desde os mais simples até aos mais complexos, os quais geralmente usam joins, sub

queries, execução de stored procedures, etc.

6.1.5. Avaliar o Uso dos Índices

Embora os índices no PostgreSQL não necessitem de manutenção nem ajustes

(porque as implementações são todas dinâmicas), em ambientes de produção é

importante verificar quais os índices que estão a ser usados e quais que não estão.

A detecção de uso dos índices é feita com o comando EXPLAIN. Sendo o uso dos

índices condicionado por factores variadíssimos, é necessário ter em conta os pontos

que se seguem ao se avaliar o uso destes:

• Executar sempre o comando ANALYZE. Este comando vai recolher informação

estatística relativa à distribuição dos valores ao longo da tabela. Esta

informação é necessária porque é com base na mesma que o planner prevê o

número de registos a devolver. Caso essa informação não esteja disponível,

são usados valores por omissão. Ao se usarem valores por omissão, certamente

que os resultados obtidos não serão os melhores.

Examinar o uso de índices sem se ter executado o comando ANALYZE é perda de

tempo!

• Usar dados reais e não de teste. Entende-se neste contexto por dados de teste,

aqueles dados que são inventados com o propósito de criar registos e não

partes de dados reais. Ao se usarem dados de teste para maximizar o uso dos

índices, o resultado será pura e simplesmente o padrão de uso dos índices para

esses mesmos dados de teste e nada mais. Por isso a optimização do uso dos

índices deve ser feita em ambientes reais.

Funcionalidades Avançadas - Capítulo 6

Página 87

É fatal usar conjuntos pequenos de dados para testes. Principalmente se o

número de registos for muito pequeno. Nestas situações o mais provável é que

os registos caibam num bloco de dados (data page). Nessa situações não há

qualquer mecanismo de índices que consiga ser mais eficiente do que ler o

bloco directamente do disco, havendo também a possibilidade de esse mesmo

bloco de dados encontrar-se disponível na cache do SGBD.

• Quando os índices não estiverem a ser utilizados, pode ser útil (para testar)

forçar o uso destes. Para tal há pelo menos dois parâmetros que podem ser

activados/desactivados. Os parâmetros são: enable_seqscan e

enable_nestloop, os quais tomam efeito somente sobre a sessão onde foram

executados. Ambos os parâmetros servem para configurar o comportamento

do planner.

O primeiro serve para que o varrimento sequencial das tabelas seja activado ou

não. O segundo serve para activar ou desactivar o uso de nested loops ao se

fazerem joins.

Para modificar estes parâmetros usa-se o comando:

SET nome_parâmetro = true;

ou

SET nome_parâmetro = false;

• Se ao se forçar o uso de algum dos parâmetros acima der melhor resultado, então

às duas situações que podem estar a ocorrer:

1) Os parâmetros por omissão para os custos ( CPU, I/O, locks, etc.) não

são apropriados.

Para mais informações sobre a configuração dos parâmetros de custos, consulte [18].

2) A informação estatística disponível não é suficiente. Neste último caso

aconselha-se a aplicar os parâmetros de recolha de estatísticas.

Para mais informações sobre os parâmetros de recolha de estatísticas, consulte [19]

Funcionalidades Avançadas - Capítulo 6

Página 88

• Se após executar todas as dicas anteriores o PostgreSQL continuar a recusar o

uso dos índices, então o melhor é contactar a equipa de programadores do

PostgreSQL. Para obter assistência consulte [4].

6.2. Stored Procedures

Um stored procedure consiste num pedaço de código que fica armazenado dentro do

SGBD. Estes assemelham-se às funções e/ou procedimentos que existem na grande

maioria das linguagens. Não sendo regra, geralmente os stored procedures têm lógica

misturada com execução de comandos SQL. Um stored procedure pode ser executado

quando o nível aplicacional assim o decidir ou quando um trigger disparar.

Os stored procedure no PostgreSQL podem ser escritos em nas seguintes linguagens:

• C

• PL/pgSQL

• Tcl

• Perl

• Python (em fase alpha)

• Ruby

• Outras linguagens ( a criar pelo próprio utilizador )

Para que o PostgreSQL possa executar os stored procedure, este tem que conhecer a

linguagem em que o stored procedure foi escrito. Para isso o PostgreSQL possui o

que se chama uma language handler (suporte a linguagens) que tem como finalidade

permitir a execução de diferentes stored procedures nas linguagens mencionadas.

Os stored procedures podem ser escritos com o objectivo de serem usados como

código de triggers, como funções para a criação de índices funcionais e também como

funções/procedimentos.

É possível criar handlers para outras linguagens, desde que os respectivos language

handlers sejam escritos. Estes consistem em módulos escritos em C que são

automaticamente carregados pelo PostgreSQL quando são necessários.

Neste documento a única linguagem a usar será o PL/pgSQL uma vez que é uma

linguagem acessível e por isso de fácil compreensão.

Funcionalidades Avançadas - Capítulo 6

Página 89

6.2.1. Vantagens/Desvantagens

Armazenados dentro do SGBD.

Maior desempenho, uma vez que guarda o plano de execução em formato

interno ao SGBD em vez de usar interpretação de instruções linha a linha.

Possibilidade de armazenar as regras de negócio directamente no SGBD.

6.2.2. A linguagem PL/pgSQL

O PL/pgSQL é uma das linguagens usadas no PostgreSQL.

No que respeita à sua estrutura e características é muito semelhante à linguagem

PL/SQL do Oracle, o que leva a que a migração de/para o Oracle seja fácil.

6.2.2.1. A linguagem PL/pgSQL

Todo o stored procedure em PL/pgSQL tem obrigatoriamente a estrutura que se

segue.

CREATE FUNCTION nome_funcao(tipo_parâmetros) RETURNS tipo_retorno AS ‘

BEGIN

<Corpo da função>

END;

' LANGUAGE 'plpgsql';

Na primeira linha informa-se o SGBD que queremos criar uma função de nome

nome_funcao a qual tem como tipo de parâmetros (separados por vírgulas) os

parâmetros especificados por tipo_parâmetros e cujo tipo de retorno é tipo_retorno.

As palavras chave BEGIN e END indicam respectivamente que se vai dar inicio ou fim

ao corpo da função. A palavra chave LANGUAGE específica qual a linguagem em que o

stored procedure foi escrito. A linguagem fornecida em LANGUAGE será

posteriormente usada pelo SGBD quando este for para chamar a função.

Funcionalidades Avançadas - Capítulo 6

Página 90

Exemplo:

CREATE FUNCTION soma_1_mais_1() RETURNS INTEGER AS ‘

BEGIN

-- Isto é um comentário

RETURN 1+1;

END;

' LANGUAGE 'plpgsql';

Como é sabido a potencialidade das linguagens de programação e também das

linguagens funcionais advém do facto que estas poderem fazer cálculos e outras

operações com valores não estáticos – as variáveis.

Para se declarar variáveis na PL/pgSQL é necessário faze-lo dentro de uma secção

especial de nome DECLARE.

Os tipos de variáveis suportadas vão desde aos tipos definidos no SQL, tipos criados

pelo próprio utilizador e os tipos %ROWTYPE, %TYPE, RECORD e outros.

O tipo %ROWTYPE permite criar uma variável cujos campos e respectivos tipos sejam os

associados a uma tabela.

O tipo %TYPE permite criar uma variável cujo tipo esta ligado à definição do campo na

tabela.

O tipo RECORD é semelhante ao tipo %ROWTYPE, exceptuando o facto do primeiro não

estar ligado à definição de registos de qualquer tabela.

Exemplo:

CREATE FUNCTION soma_1_mais_1() RETURNS INTEGER AS ‘

BEGIN

operando_a INTEGER;

operando_b INTEGER;

operando_a := 1;

operando_b := 1;

RETURN operando_a + operando_b;

END;

' LANGUAGE 'plpgsql';

Funcionalidades Avançadas - Capítulo 6

Página 91

A função acima declara duas variáveis: operando_a, operando_b como sendo do tipo

INTEGER. De seguida atribui o valor 1 respectivamente a cada uma das variáveis,

retornando a soma das duas variáveis.

Uma das vantagens das funções consiste no facto de estas poderem receber

parâmetros.

O PostgreSQL em relação a outros SGDBs ainda só suporta parâmetros de entrada, ao

contrário de outros SGDBs com o caso do Oracle que suporta tanto parâmetros de

entra, entrada/saída e saída. No PostgreSQL para se definir uma função que tenha

parâmetros é necessário indicar quais os tipos de dados dos parâmetros em vez dos

nomes dos parâmetros, o que é mais usual nos SGBDs.

CREATE FUNCTION soma_n_mais_m( INTEGER, INTEGER) RETURNS INTEGER AS ‘

DECLARE

operando_a FOR ALIAS as $1; -- $1 é o primeiro parâmetro

operando_b FOR ALIAS as $2; -- $2 é o segundo parâmetro

BEGIN

RETURN operando_a + operando_b;

END; ' LANGUAGE 'plpgsql';

Uma das vantagens da linguagem PL/pgSQL consiste em estas poderem consultar e

manipular os dados da base de dados, com acontece com as instruções SQL.

Para demonstrar como é que se pode criar uma função que possa consultar os dados

de uma ou mais tabelas, é necessário primeiro introduzir mais alguns conceitos, em

concreto as variáveis do tipo: %ROWTYPE, %TYPE e os cursores.

O tipo %ROWTYPE permite ao utilizador criar uma variável cujo tipo seja do tipo registo

(record) de uma determinada tabela. Quer isto dizer que este tipo adapta-se à estrutura

da tabela. Se declararmos uma variável do tipo CLIENTES%ROWTYPE, estamos a

declarar uma variável, que vai ter os mesmos campos que os da tabela clientes e

cujos tipos de dados dos campos serão os que tiverem definidos até ao momento para

os campos da tabela.

Funcionalidades Avançadas - Capítulo 6

Página 92

Para se entender melhor a potencialidade deste tipo de variáveis, nada melhor que um

exemplo. O exemplo abaixo vai retornar o nome de um certo cliente, o qual será

identificado através do parâmetro que é passado à função. Caso o cliente não exista,

será gerada uma excepção (erro).

CREATE FUNCTION ClienteNome( INTEGER) RETURNS TEXT AS ‘

DECLARE

p_clienteID FOR ALIAS as $1;

m_clienteREC CLIENTE%ROWTYPE;

BEGIN

SELECT * INTO m_clienteREC WHERE cliente_id = p_clienteID;

IF NOT FOUND

RAISE EXCEPTION ''O cliente cujo ID é % não existe '',

p_clienteID;

END IF;

RETURN m_clienteREC.cliente_nome;

END;

' LANGUAGE 'plpgsql';

A função acima, declara duas variáveis, a primeira de nome p_clienteID que vai ser

usada como chave na pesquisa do cliente e a segunda de nome m_clienteREC que vai

servir como local de armazenamento temporário para o nome do cliente. Repare-se

que no query SELECT anterior, está a ser usada uma nova cláusula, a INTO. No

contexto da função acima, a cláusula INTO serve para indicarmos ao SGBD que o

resultado do query será enviado para a variável m_clienteREC. Note-se que quando as

variáveis são do tipo %ROWTYPE, o query geralmente extrai todos os campos.

No caso de o query devolver algum registo, ou seja caso o cliente exista, a variável

FOUND do tipo BOOLEAN vai conter TRUE caso o cliente exista e FALSE caso não exista.

No caso do cliente não existir, será gerada uma excepção (erro) e a execução será

terminada. Note-se que o formato da mensagem apresenta alguma semelhança à

função printf da linguagem C, repare-se na presença do % no interior da frase.

Uma vez encontrado o cliente, resta-nos devolver o nome do mesmo. Para tal, acede-

se ao campo cliente_nome da variável m_clienteREC através do operador “.” e

devolve-se o valor que lá se encontrar.

O tipo %TYPE permite ao utilizador criar uma variável cujo tipo seja do tipo que estiver

associado ao campo da tabela. Se declararmos uma variável do tipo

Funcionalidades Avançadas - Capítulo 6

Página 93

CLIENTES.cliente_nome%TYPE, estamos a declarar uma variável cujo tipo de dados é

igual ao tipo de dados do campo cliente_nome da tabela clientes.

Este tipo de variável tal como acontece com o tipo %ROWTYPE adapta-se a estrutura da

tabela, ou seja, se por algum motivo alguém decidir alterar o campo cliente_nome de

VARCHAR(80) para VARCHAR(100) ou até mesmo para CHAR(100), as funções que usem o

tipo de variável %TYPE, não terão que ser alteradas.

De seguida apresenta-se uma função de nome ClienteNome que faz o mesmo que a

anterior, mas em vez de usar uma variável %ROWTYPE usa antes uma do tipo %TYPE.

CREATE FUNCTION ClienteNome( INTEGER) RETURNS TEXT AS ‘

DECLARE

p_clienteID FOR ALIAS as $1;

m_nome CLIENTE.cliente_nome%TYPE;

BEGIN

SELECT cliente_nome INTO m_nome WHERE cliente_id = p_clienteID;

IF NOT FOUND

RAISE EXCEPTION ''O cliente cujo ID é % não existe'',

p_clienteID;

END IF;

RETURN m_nome;

END;

' LANGUAGE 'plpgsql';

Repare-se que nesta função, o número de campos a ler do SELECT é igual ao número

de variáveis, o que não acontecia com a anterior versão da função.

Para se introduzir o conceito e os mecanismos associados aos cursores, nada melhor

do que um exemplo prático. Tal como acontece com o cursor do rato, as variáveis do

tipo cursor, tem a mesma finalidade – apontar. No caso das variáveis do tipo cursor

estas apontam a posição do registo actual. Pode-se então concluir que os cursores

servem então para se percorrer de forma sequencial um conjunto de registos.

Supondo que se quer obter o valor total das vendas efectuadas para um certo cliente.

Suponhamos também que não é possível através de um query SELECT obter o mesmo

resultado (o que não é verdade). Para se obter o resultado pretendido, será necessário

consultar duas tabelas, a tabela clientes e a tabela vendas.

Funcionalidades Avançadas - Capítulo 6

Página 94

CREATE FUNCTION ClienteTotalVendas( INTEGER) RETURNS INTEGER AS ‘

DECLARE

p_clienteID FOR ALIAS as $1;

m_id CLIENTE.cliente_id%TYPE;

m_vendREC VENDAS%ROWTYPE; -- variável destino a usar no FETCH

m_acomulador INTEGER; -- armazena o acumulado das vendas

m_cursor refcursor; -- variável cursor para iterar sobre as vendas

BEGIN

m_acomulador:=0;

SELECT cliente_id INTO m_id WHERE cliente_id = p_clienteID;

IF NOT FOUND

RAISE EXCEPTION ''O cliente cujo ID é % não existe'',p_clienteID;

END IF;

OPEN m_cursor FOR SELECT * FROM VENDAS WHERE cliente_id =

p_clienteID;

LOOP

FETCH m_cursor INTO m_vendREC;

IF FOUND THEN

m_acomulador:=m_acomulador + m_vendREC.venda_valor;

ELSE

-- fim do cursor ou cursor vazio

EXIT; -- abandona o loop

END IF;

END LOOP;

CLOSE m_cursor;

RETURN m_acomulador;

END;

Na secção DECLARE existe um novo tipo de variável – o refcursor. É através deste tipo

que se declaram os cursores no PostgreSQL. A função acima está dividida em quatro

partes. Na primeira verifica-se a existência do cliente. Na segunda efectua-se a

inicialização do cursor. Na terceira calcula-se o valor acumulado das vendas. E por

fim na quarta, processa-se o fecho do cursor e o retorno.

Relativamente à primeira não há nada de novo a adicionar, uma vez que esta parte é

igual à das funções anteriores.

No que respeita à segunda parte, processa-se a abertura do cursor utilizando para o

efeito um query SELECT que tem o resultado filtrado através do cliente. Note-se que

Funcionalidades Avançadas - Capítulo 6

Página 95

antes de se processar a abertura do cursor, o cursor está desligado (unbound). Isto

acontece, porque na declaração do cursor não foi fornecido qualquer query. Uma vez

aberto o cursor será então possível extrair informação a partir deste. Caso contrário o

PostgreSQL geraria uma excepção.

Em relação à terceira parte, usa-se um ciclo infinito para se extrair a informação de

cada registo de vendas. A cada iteração do ciclo, a instrução FETCH (buscar), vai

colocar o registo actual do cursor m_cursor na variável m_vendREC. A instrução FETCH é

semelhante à instrução SELECT ... INTO abordada anteriormente, daí que a variável

FOUND seja alterada consoante o FECTH retornou registos ou não (no fim do cursor ou

caso o cursor esteja vazio).

Na quarta parte é encerrado o cursor e retornado o valor. Uma vez fechado o cursor,

este passa a estar desligado (unbound), por isso qualquer operação sobre o cursor que

não a abertura irá gerar uma excepção.

6.3. Triggers

Um trigger corresponde a uma função que é chamada pelo SGBD quando um certo

evento acontece. Os eventos estão directamente relacionados com os tipos de

operações DML suportadas, ou seja os tipos de eventos podem ser: ON INSERT, ON

UPDATE e ON DELETE. Deve-se ainda indicar ao SGBD quando é que este deve disparar

o trigger, mais concretamente, depois de ocorrido o evento (AFTER) ou antes de o

evento ter ocorrido (BEFORE). Pode parecer confuso disparar um trigger após o evento

ser sido executado correctamente, mas há situações em que tal é necessário.

Suponhamos que tínhamos um trigger que tinha que validar uma regra de negócio e

que para essa regra de negócio pode ser executada correctamente este tinha que

conhecer os valores correctos e não nulos.

Quando um trigger é executado depois do evento, temos a certeza que a base de

dados, através do mecanismo de restrições de integridade já validou esses mesmos

valores. Caso contrário esses valores teriam que ser verificados pelo próprio trigger o

que não é a forma mais correcta e eficiente de o fazer. O mais correcto seria usar os

mecanismos internos de integridade da própria base de dados, os quais são muito

mais rápidos que o possível código do trigger. Uma outra razão deve-se a que a

restrição de integridade fica escondida. Por escondida entenda-se que a restrição não

apareceria quando se pedisse uma informação sobre a estrutura da tabela.

Funcionalidades Avançadas - Capítulo 6

Página 96

Quando um trigger é executado antes do evento, os valores presentes nos campos

ainda não foram verificados pelas restrições de integridade. Uma das situações em que

se recorre ao uso de triggers BEFORE está relacionada com situações em se deseja

alterar os valores efectivamente registados. Por exemplo: usando como referência o

exemplo habitual do cinema, suponhamos que sempre que fosse registada uma venda

cuja quantidade de bilhetes fosse superior a 5 unidades, então ao valor da venda será

feito um desconto de 10% sobre o valor total da venda.

Para implementar um trigger deste género em que é necessário alterar o valor presente

num campo antes de este ser submetido para a base de dados, só através de um trigger

BEFORE é que tal é possível.

De um ponto de vista prático um trigger corresponde à execução de um stored

procedure. Os stored procedures que sejam usados por triggers têm algumas

particularidades, entre as quais se destacam:

• Tipo de retorno - o tipo de valor a retornar tem que ser TRIGGER para

versões superiores ao PostgreSQL 7.3 e para as anteriores o tipo de retorno

tem que ser OPAQUE.

• Variáveis especiais – As variáveis NEW e OLD são do tipo RECORD e são

definidas automaticamente sempre que o código do trigger é executado.

Estas tem a estrutura da tabela onde o trigger foi aplicado, ou seja os

campos apresentam os mesmos nomes, tipos e restrições que os da tabela

onde o trigger foi aplicado.

A variável NEW é usada nas operações de INSERT, UPDATE e DELETE para se

aceder aos valores “actuais”.

As variáveis OLD e NEW são usadas nas operações de UPDATE. A variável

OLD contem os valores já existentes antes de se dar a alteração e a variável

NEW contém os valores a usar, os quais serão sobrepostos aos anteriores.

Presumindo que o stored procedure do trigger está criado, é necessário indicarmos ao

SGBD como é que queremos que o trigger seja executado, ou seja BEFORE, AFTER e em

que situações é que o deve fazer, ou seja para cada registo (EACH ROW) ou como um

todo (EACH STATEMENT).

Funcionalidades Avançadas - Capítulo 6

Página 97

Para tal é necessário executar o seguinte comando:

CREATE TRIGGER nome_trigger { BEFORE | AFTER } { evento [OR ...] }

ON nome_tabela FOR EACH { ROW | STATEMENT }

EXECUTE PROCEDURE nome_do_stored procedure( argumentos_opcionais )

Suponhamos que se queria aplicar um trigger de nome trg_aplica_desconto_venda

sobre a tabela vendas e que esse trigger seria disparado depois do evento INSERT e

para cada registo executa-se o stored procedure de nome trg_proc_desconto_venda. O

comando a usar seria:

CREATE TRIGGER trg_aplica_desconto_venda AFTER INSERT ON vendas FOR EACH ROW

EXECUTE PROCEDURE trg_proc_desconto_venda();

É possível aplicar vários triggers numa mesma tabela. Dizem as regras de bom senso

que não se deve usar a sequência de disparo na lógica de criação dos triggers. No

entanto só para referência, o PostgreSQL usa a ordenação do nome do trigger como

forma de obter a ordem de disparo.

Embora não tenha sido dado nenhum exemplo de um trigger ao longo do documento,

optei por não o fazer, umas vez que a base para a criação destes é a mesma que a dos

stored procedures, tirando algumas variáveis que só existem nos triggers, como é o

caso da NEW,OLD e outras.

Para informações sobre triggers, cursores, retorno de cursores, estruturas de controlo, migração de stored procedures e outro assuntos relacionados com a linguagem PL/pgSQL, consulte [20].

Funcionalidades Avançadas - Capítulo 6

Página 98

6.3.1. Triggers no PostgreSQL

Até ao momento o PostgreSQL não suporta as seguintes funcionalidades presentes na

normal SQL99:

• Disparo de um trigger quando forem actualizados valores de certas

colunas de uma tabela. (ou seja: AFTER UPDATE OF col1, col2).

• Criar alias para as variáveis “old” e “new”. Uma vez que o PostgreSQL

permite criar triggers em várias linguagens, o acesso aos dados é tratado

de forma específica consoante a linguagem.

• Suporta somente triggers do tipo ROW, não suportando triggers do tipo STATEMENT.

• O SQL99 define que múltiplos triggers devem ser disparados consoante a

data/hora de criação dos mesmos. O PostgreSQL usa os nomes dos

triggers em vez da hora de criação.

6.4. Controlo da Concorrência

Antes de mais convém dizer o que se entende por concorrência no contexto dos

SGDBs. Diz-se que um query está a executar de forma concorrente com outro(s)

queries quando esses mesmos queries operam sobre os mesmos dados.

Por exemplo: Na situação que se segue, ignore-se o facto de as contas bancárias

possuírem os dois tipos de saldos: o corrente e o contabilístico. Num balcão de um

banco a esposa do Sr. A faz um depósito em numerário sobre a conta XPTO.

Enquanto o depósito está a ser feito (presuma-se que demora algum tempo) o Sr. A

(também titular da conta XPTO) vai ao Multibanco e levanta dinheiro.

Qual o saldo que é mostrado ao Sr. A após este acabar o levantamento?

Para se responder a esta questão é necessário conhecer os mecanismos de controlo de

concorrência usados pelo SGBD.

O Controlo da Concorrência é um dos aspectos mais importantes a ter em conta em

SGBDs multi-utilizador.

Funcionalidades Avançadas - Capítulo 6

Página 99

Os métodos de controlo da concorrência que geralmente são suportados pelos SGBDs

em geral, são:

• Bloqueio de tabelas.

• Bloqueio de páginas de dados (data pages).

• Bloqueio de registos.

Se no exemplo anterior o SGBD em causa usasse o mecanismo de bloqueio de

tabelas, o Sr. A poderia no melhor dos casos efectuar o levantamento do dinheiro após

aguardar pelo fim do depósito ou a operação de levantamento iria falhar, uma vez que

a tabela já estava bloqueada por outra operação.

Embora está técnica seja a mais simples de implementar por parte dos SGBDs, esta

técnica é a que pior se adapta a nível de acessos concorrentes, porque só um query

(quer seja de leitura ou escrita) pode operar sobre a tabela bloqueada. Uma vez que só

um query pode operar sobre a tabela, o que iria acontecer às restantes tentativas de

movimento efectuados pelos restantes clientes do banco? A resposta a esta última

pergunta é simples: tinham que esperar até que a tabela(s) não estivesse bloqueada.

Este mecanismo é sem dúvida o que pior se adapta a acessos concorrentes.

Um outro mecanismo de bloqueio que também existe é o bloqueio de páginas de

dados (data pages).

Utilizando este mecanismo de bloqueio em vez do anterior, só algumas contas, as que

residissem (incluindo a conta do Sr. A) no mesmo bloco de dados que a conta do Sr.

A é que seriam bloqueadas.

Com este mecanismo de bloqueio o SGBD poderia estar a processar outros

movimentos de conta enquanto tratava do movimento da conta do Sr. A.

Usando este mecanismo iria trazer vantagens do ponto de vista do desempenho do

SGBD uma vez que este poderia executar outros queries enquanto processava o query

para o depósito ou levantamento da conta do Sr. A. De forma geral este método de

controlo da concorrência é um método seguro e eficiente.

Caso algum cliente das contas que residissem na mesma página de dados que a conta

do Sr. A quisessem movimentar ou consultar a conta, só o poderiam fazer assim que a

operação do Sr. A tivesse desbloqueado a página de dados respectiva.

Funcionalidades Avançadas - Capítulo 6

Página 100

Para o caso de bases de dados com muitos acessos concorrentes, como a base de

dados de um banco, este mecanismo não é muito aceitável, mas muito melhor que o

anterior.

O método de controlo da concorrência baseado no bloqueio de registos é sem dúvida

o melhor método dos que até agora foram enunciados. Este método em vez de

bloquear a página de dados (que geralmente contém vários registos) vai bloquear

somente os registos necessários. Desta forma e usando como analogia o exemplo do

banco, o acesso à conta do Sr. A iria sofrer um acesso concorrente e o acesso às

restantes contas de outros clientes poderiam ser permitidos em simultâneo com as

operações sobre a conta do Sr. A.

No caso do exemplo acima, se alguém (um empregado do banco) quisesse obter o

alguma informação sobre a conta do Sr. A não o poderia fazer, porque a conta estava

a ser actualizada por força quer do movimento de depósito ou do movimento de

levantamento.

Para resolver esta situação e outras, os SGBDs como é o caso do PostgreSQL

implementa um mecanismo mais lógico é mais eficiente para controlo da concorrência

– o MVCC — MultiVersion Concurrency Control.

Para mais informações sobre o MVCC, consulte a secção 6.4.

6.4.1. Transacções

As transacções são dos mecanismos mais importantes do ponto de vista da integridade

das bases de dados. Para se entender o que é uma transacção e qual a sua utilidade,

vamos recorrer uma vez mais ao exemplo anterior do banco.

Supondo que a esposa do Sr. A desta vez queria transferir dinheiro da conta do Sr.A

para a conta particular da Sra. A. Como será fácil de se perceber serão necessários

dois movimentos, um levantamento da conta do Sr. A e um depósito na conta

particular da Sra. A. É fácil de nos apercebermos que o estado de integridade da base

de dados, por momentos vai estar inconsistente. Supondo que durante a transferência

ocorria uma folha de hardware no SGDB, qual é que seria o saldo de cada uma das

Funcionalidades Avançadas - Capítulo 6

Página 101

contas? É difícil conseguir saber. Mas há uma certeza, se a movimentação das contas

estivesse a ser feita sobre uma transacção, o saldo das contas ficavam com os saldos

que tinham (no caso de uma falha qualquer) ou uma conta era debitada e a outra

creditada devidamente, mas nunca uma situação intermédia (uma conta debitada e

uma conta não creditada e vice versa).

Uma transacção consiste num conjunto de operações que são executadas como se

fosse uma única operação. Mais concretamente ou a transacção executa até ao fim

com sucesso ou tudo o que foi feito é ignorado. Esta é a única forma que há para se

manter a integridade da base de dados.

As transacções têm quatro propriedades muito importantes que se devem conhecer:

• Atomicidade – O conjunto de operações que constituem uma transacção

formam um grupo indivisível (atómico), no sentido em que todas as

operações são executadas com sucesso ou nenhuma é executada. Por

outras palavras, uma transacção ou termina com sucesso (faz o commit) ou

então todas as operações realizadas sobre a base de dados são desfeitas

(faz o rollback), dando assim desta forma a ilusão que as operações nunca

foram realizadas.

• Integridade – Uma transacção se envolver actualizações de dados, deve

transportar a base de dados de um estado de integridade para outro também

de integridade. Durante a execução da transacção, a integridade da base de

dados pode ser momentaneamente violada, contudo quando a transacção

terminar a integridade deve ser assegurada.

• Isolamento – Embora as transacções executem concorrentemente, o

sistema deve dar a cada transacção a ideia de que é a única transacção que

está a executar no SGBD ou seja, dar a ideia que a transacção executa

isoladamente das restantes. Numa abordagem mais prática, no caso de

haverem várias transacções concorrentes a acederem aos mesmos dados o

sistema deve evitar que estas interfiram entre si, garantindo que o resultado

seria o mesmo se as transacções fossem executadas em série.

Funcionalidades Avançadas - Capítulo 6

Página 102

• Persistência – O SGBD deve assegurar que todos os efeitos causados por

uma transacção bem sucedida se tornem persistentes (definitivos) na base

de dados e visíveis para as outras transacções. De um ponto de vista mais

prático, após uma transacção terminar com sucesso (ter feito commit), as

suas actualizações sobre a base de dados passam a ser efectivas,

sobrevivendo a todo o tipo de falhas que eventualmente possam ocorrer.

Assim sendo os efeitos sobre a base de dados não poderão ser desfeitos, a

não ser por outras transacções posteriores.

As quatro características anteriores são conhecidas na área das bases de dados como o

princípio ACID ( Atomicity, Consistency, Isolation and Durability).

Há uma questão que é necessário responder. Como é que o SGBD mesmo em caso de

falhas (qualquer que seja) consegue desfazer o que tinha feito?

Para se responder a esta questão aconselha-se a leitura da secção que se segue.

6.4.1.1. Transacções no PostgreSQL

Actualmente o PostgreSQL (v.7.3.3) ainda só suporta transacções simples, não

suportando nested transactions nem save points.

Para implementar as transacções o PostgreSQL bem como outros SGBDs como é o

caso do Oracle tira partido de uma técnica conhecida na área das bases de dados por

WAL – Write Ahead Logging.

A ideia prática subjacente ao WAL consiste em registar num suporte seguro4 todas as

alterações a serem feitas na base de dados antes de essa mesmas alterações serem

reflectidas sobre os dados reais. Só através de um mecanismo deste género é que será

possível desfazer as alterações feitas. Na realidade as alterações a fazer nunca são

feitas sobre os dados reais, mas sim sobre uma cópia dos dados a alterar. Quando for

dada a ordem de finalização da transacção (o commit), a transacção executa cada uma

das operações que estavam pendentes sobre os dados reais (as tabelas). O registo da

4 Preferencialmente aconselha-se que o disco onde os ficheiros do WAL serão armazenados, seja um disco independente daquele onde residem os ficheiros de dados. Convém que o disco em questão consiga assegurar ao sistema operativo que após uma operação de flush sobre este, o disco não se limite a sinalizar o sistema operativo que os dados foram efectivamente escritos e que os tenha somente armazenado na cache do próprio disco.

Funcionalidades Avançadas - Capítulo 6

Página 103

ocorrência no WAL só é removido (deixa de estar pendente) assim que o SGBD tiver

a certeza que todas as operações foram executadas com sucesso5. Usando este

pressuposto os SGBDs conseguem um melhor desempenho, uma vez que não

necessitam de fazer flush das páginas de dados sempre que há alterações.

Usando como referência a figura Figura 10 da página 70, pode-se ver as camadas que

cada operação na base de dados pode originar. Na presença de uma mecanismo do

género do WAL, o SGBD só tem que se assegurar que as operações presentes no

WAL foram efectivamente registadas no disco assim que receber um commit vindo de

uma transacção. Se assim não fosse este não conseguiria assegurar a atomicidade do

princípio ACID. Enquanto uma transacção estiver activa (ainda não fez o commit mas

já fez o begin) as operações vão sendo registadas no WAL sem que seja necessário ter

a certeza que estas estão a ser fisicamente armazenadas no disco. Desta forma quer o

sistema operativo quer o hardware envolvido podem fazer caching dos dados, o que

leva a muito menos pedidos de I/O, resultando assim num desempenho muito superior

a nível do SGBD. Se um SGBD não suportar transacções, então este a cada operação

realizada sobre os dados, tem6 que se assegurar que os dados da operação ficam

fisicamente armazenados no disco antes de podes processar uma nova operação, o que

leva este tipo de SGBD tenham grandes perdas de desempenho. É devido a tudo o que

é mencionado anteriormente que as operações de carregamento das bases de dados,

também conhecidas por operações de bulk-loading, devem ser realizadas sob

transacções. Só a título de curiosidade, um ficheiro de backup com cerca de 1.400.000

registos sob a forma de instruções INSERT ao ser executado sob uma transacção

demora aproximadamente 4 minutos a ser processado. A mesma operação realizada

sem estar sob uma transacção pode demorar entre 25-40 minutos.

Os custos inerentes a uma operação de flush7 (assegurar que os dados estão

fisicamente registados no disco) de uma ou mais data pages são muito elevados, uma

5 Mesmo após a transacção ter feito commit não é garantido que os dados estejam fisicamente armazenados nos ficheiros de dados e índices. Há uma certeza, é que mesmo que esses dados não estejam definitivamente armazenados nos ficheiros de dados, os dados que serão vistos serão a última versão dos dados, que o mais certo é que estejam na pool de memória partilhada à espera de uma “boa” oportunidade para serem escritos definitivamente nos ficheiros de dados. Após o SGBD ter a certeza que os dados foram devidamente registados nos “ficheiros” de dados, este remove o registo da transacção do WAL. 6 O MySQL por exemplo e ignorando o suporte para as tabelas InnoDB, não se assegura que as operações estão escritas efectivamente no disco, uma vez que este por omissão funciona com o parâmetro que controla a system call fsync ou equivalente desligado, segundo [37]. O PostgreSQL por omissão tem o parâmetro de configuração fsync activo. 7 Estas operações são geralmente asseguradas em sistema Unix pela system call fsync ou fdatasync.

Funcionalidades Avançadas - Capítulo 6

Página 104

vez que serão necessárias várias operações que vão desde posicionamento no ficheiro;

síncronia de acessos a vários acessos ao disco e escrita dos dados no disco. No caso de

ocorrer um crash, o SGBD tem sempre acesso às operações pendentes que não

chegaram a finalizar através do WAL.

Uma outra técnica que é usada pelo PostgreSQL e por outros SGBDs como é o caso

do Oracle no que respeita à manutenção da consistência é o MVCC — MultiVersion

Concurrency Control. Na prática o MVCC permite que cada transacção veja um

snapshot (versão) da base de dados tal como a base de dados estava até a transacção

ter iniciado, independentemente do estado dos dados nos ficheiros de dados e das

alterações feitas por outras transacções. Desta forma consegue-se que as transacções

não vejam dados inconsistentes causados por outras transacções a executar

concorrentemente, garantindo assim o isolamento das transacções.

A grande diferença que existe entre este mecanismo e outros é que através do uso do

MVCC os queries que leiam dados não entram em conflito com os queries que

escrevem dados e por isso as leituras nunca bloqueiam as escritas nem vice-versa.

Desta forma os níveis de concorrência serão muito mais elevados, levando a que o

desempenho do SGBD seja muito melhor.

Embora o MVCC seja o método preferível a nível de controlo da concorrência o

PostgreSQL também disponibiliza os métodos tradicionais de bloqueio (tabelas,

páginas de dados e registos) para que estes possam ser usados pelas aplicações que

não se adaptem facilmente ao MVCC.

Antes do PostgreSQL suportar MVCC os backups tinham que ser feitos em horas cuja

base de dados não tivesse actividade porque eram utilizados bloqueios de tabelas em

modo exclusivo, para se ter a certeza que o backup continha dados consistentes.

Com a adopção do MVCC, os backups passaram a poder ser feitos qualquer altura. É

necessário referir que os dados das transacções que não tenham terminado não serão

incluídos no backup.

Com o uso desta técnica, o PostgreSQL pode começar a ser usado (na realidade é

usado) em sistemas que necessitam de uma disponibilidade de 24 horas por dia 365

dias por ano.

Funcionalidades Avançadas - Capítulo 6

Página 105

6.4.1.2. Níveis de Isolamento

O nível de isolamento de uma transacção define como é que a própria transacção vai-

se comportar quando esta estiver a executar juntamente com outras, ou seja vai definir

quais as regras que estas devem usar para que as transacções entre si não sintam a

presença das restantes.

O standard SQL define quatro níveis de isolamento para três fenómenos que podem

ocorrer em cada um dos níveis. Esses fenómenos são os seguintes:

• Dirty read – Uma transacção lê dados que foram escritos por uma

transacção não terminada (ainda não fez commit).

• Nonrepeatable read— Uma transacção relê dados que tinha previamente

lido e descobre que esses dados foram modificados por uma outra

transacção que terminou depois da primeira leitura dos dados.

• Phantom read — Uma transacção re-executa um query que devolve um

conjunto de registos que estão de acordo com a condição de pesquisa e

descobre que o que o conjunto de registos retornados da segunda vez é

diferente, porque uma transacção terminou muito recentemente.

Na tabela abaixo encontram-se as relações existentes entre os níveis de isolamento

definidos pelo standard SQL e os fenómenos descritos anteriormente.

Nível de isolamento Dirty Read Nonrepeatable Read Phantom Read Read uncommitted Possível Possível Possível Read committed Impossível Possível Possível Repeatable read Impossível Impossível Possível Serializable Impossível Impossível Impossível

Tabela 12 – Relação entre o nível de isolamento e os três fenómenos ligados às transacções

Em destaque na tabela anterior encontram-se os níveis de isolamento suportados pelo

PostgreSQL.

Para mais informações sobre os níveis de isolamento suportados pelo PostgreSQL e sobre afinação da configuração do WAL, consulte respectivamente [21] e [22].

077.1. BLOBs no PostgreSQL 107

Campos Binários - Capítulo 7

Página 107

7. Campos Binários (BLOBs)

Os campos binários mais conhecidos na área das base de dados como BLOBs- Binary

Large Objects, são um tipo de campos especiais que permitem que sejam

armazenados quaisquer tipos de dados. Geralmente neste tipo de campos armazenam-

se ficheiros de imagens (.gif, .jpg, etc) ou qualquer outro tipo de informação. Estes

campos podem ser vistos como simples contentores de informação, porque não tem

limite nem quaisquer restrições a nível de conteúdo.

Do ponto de vista do SQL a usar este tipo de campos não tem qualquer tratamento

especial uma vez que são tratados como outros tipos de campos quaisquer.

7.1. BLOB’s no PostgreSQL

No PostgreSQL há duas formas de se aceder aos BLOBs:

• Forma habitual de acesso.

• Através de uma interface “especial” do PostgreSQL.

No que respeita à primeira há algumas considerações a ter em conta.

Em relação à inserção as considerações a ter estão relacionadas com a forma como o

PostgreSQL interpreta certos bytes. Ou seja, antes que se possa inserir um registo que

contenha um BLOB, é necessário fazer umas pequenas conversões nos dados de

entrada para certos bytes que ocorram. Os bytes que são afectados na inserção são os

que se encontram na Tabela 13.

Valor Decimal

Descrição Conversão Exemplo Resultado à saída

0 Zero '\\000' SELECT '\\000'::bytea;

\000

39 Plica '\'' ou '\\047' SELECT '\''::bytea;

'

92 Backslash '\\\\' ou '\\134' SELECT '\\\\'::bytea;

\\

Tabela 13 – Bytes que necessitam de tratmento especial na inserção

O tipo de dados a usar no PostgreSQL para os BLOB’s é o tipo bytea.

Campos Binários - Capítulo 7

Página 108

Os caracteres presentes na tabela são geralmente interpretados pelos SGBDs como

caracteres “especiais”, porque estes indicam ao SGBD que o tratamento tem que ser

diferente dos restantes caracteres.

O carácter zero (\0) tem o significado de fim de string na linguagem C, a qual é usada

na criação deste tipo de software. Por isso este é interpretado pelo PostgreSQL como

sendo um carácter especial.

O carácter plica (‘) nos SGBDs é interpretado como início/fim de string.

O carácter backslash (\) funciona como um carácter que tira o significado especial de

qualquer um dos restantes caracteres presentes na tabela. Se este carácter não existisse

não era possível armazenar por exemplo nomes de locais que tivessem uma plica no

próprio nome. Por exemplo a string ‘Arca D’ Água’, teria que ser usada sob a forma

de ‘Arca D\’ Água’ em vez da forma anterior.

Para a leitura de conteúdos dos campos BLOB é necessário também fazer algumas

conversões, uma vez que os bytes do campo são armazenados sob a forma de bytes em

formato octal.

Para assegurar estas conversões quer sejam as de inserção de dados quer as de leitura,

o PostgreSQL fornece funções na API libpq para esse efeito.

As funções a usar para o efeito são as seguintes:

Função Descrição

PQescapeBytea Converte os dados à entrada (i.e INSERT)

PQunescapeBytea Converte os dados à saída (i.e SELECT)

Tabela 14 – Funções para tratamento anterior e posterior de BLOBs

As funções enumeradas na Tabela 14 estão definidas no header file libpg-fe-h.

Para mais informações sobre BLOBs no PostgreSQL, consulte [23].

Para melhor se entender o funcionamento da criação/acesso a BLOBs aconselha-se a

leitura da secção 8.2.

Campos Binários - Capítulo 7

Página 109

Uma outra forma que há para aceder/criar BLOBs é através de uma interface

“proprietária” do PostgreSQL a qual permite que os BLOBs sejam acedidos como se

tratassem de ficheiros. Esta interface conhecida por lo – Large Object é muito mais

eficiente do que a anterior uma vez não é necessário fazer qualquer tipo de conversão

dos dados. Esta interface apresenta ainda uma outra vantagem, em relação ao acesso

aos dados esta permite que se aceda directamente a parte dos dados, um mecanismo

do género da função lseek (permite que se posicione um ficheiro num certo offset em

relação a uma posição conhecida), o que não acontece com a interface libpq em que é

necessário obter primeiro todos os bytes e só depois escolher qual a gama de bytes a

usar.

Para mais informação sobre a interface lo – Large Object, consulte [24].

08

8. 8Arquitectura Tecnológica 111

8.2.Acesso a Campos Binários em PHP 115

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 111

8. Usar o PostgreSQL com o PHP na Web

O uso do PostgreSQL na Web é generalizado e em particular a sua utilização conjunta

com o PHP é maioritária, desta forma pareceu-me necessário incluir esta secção,

porque muitas das questões que são colocadas nos grupos de discussão sobre o

PostgreSQL e sobre PHP estão relacionadas com a incompreensão da interligação

entre as várias partes envolvidas.

Antes de mais, convém dizer que as características demonstradas ao longo deste

documento e não só, permitem que o PostgreSQL possa ser usado em sites e portais

na Internet. Na realidade este é amplamente usado em ambientes empresariais e em

sistemas com base na Internet.

8.1. Arquitectura Tecnológica Para que se entenda qual a estrutura tecnologia a usar na ligação do PostgreSQL com

o PHP e quais os factores condicionantes, aconselha-se a análise da Figura 22.

Figura 22 - Esquema simplificado da arquitectura tecnológica usando o PHP e PostgreSQL

O esquema de interligação acima corresponde ao modo mais aconselhado de

interligação, o qual recorre a três sistemas distintos. Um para servidor Web, outro

para servidor base de dados e um outro que representa o cliente. Com este género de

interligação consegue-se a assegurar um bom desempenho.

Também é possível ter no mesmo sistema o servidor Web e o servidor base de dados,

no entanto este arranjo pode levar (depende da carga imposta quer a base de dados

quer ao servidor Web) a quebras significativas de desempenho.

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 112

Usando como base a Figura 22 da página 111, o processo de interligação resulta no

seguinte:

• Cliente faz um pedido ao servidor Web (pedido HTTP sobre uma ligação

TCP previamente estabelecida com o servidor Web).

• Servidor Web invoca o interpretador PHP.

Interpretador PHP ao ser executado, carrega as bibliotecas necessárias,

neste caso a libpq.so e outras, as quais são identificadas em

Unix/Linux por ficheiros com a extensão so, em Windows por

ficheiros com a extensão dll.

Após carregamento das bibliotecas, o interpretador PHP dá início ao

processamento da página.

Código da página PHP usa a base de dados através de queries SQL.

Página PHP recebe o resultado do query.

Página PHP trata o resultado, no qual, em grande parte dos casos

resulta numa página Web.

Servidor Web recolhe o resultado gerado pelo PHP e responde ao

cliente.

Página PHP termina, encerrando assim o acesso a qualquer recurso (i.e

ligações a bases de dados, etc.) que foi usado pela página a que se

segue o encerramento da ligação ao cliente.

A parte que nos interessa analisar daqui por diante é a parte do processamento do

pedido.

Partindo do pressuposto que o pedido é uma página html normal, a única tarefa que o

servidor Web vai ter que fazer é somente ler o respectivo ficheiro html e enviar o

conteúdo de volta para o cliente.

Mas se o pedido for uma página PHP, o processamento é ligeiramente diferente.

Através da Figura 22 é possível observar qual a sequência de operações necessárias

por parte do servidor Web para que este consiga atender um pedido de uma página

.php.

Recorrendo à mesma figura, constata-se que quando o servidor Web recebe como

pedido uma página PHP, o interpretador PHP é invocado com vista a processar a

página. Supondo que a página PHP em questão necessita de aceder ao PostgreSQL,

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 113

assim que no código PHP seja iniciada uma ligação à base de dados, a implementação

do PHP irá assegurar a ligação à base de dados através da API libpq do PostgreSQL.

Uma vez encerrada a ligação ou o fim da página PHP tenha sido atingido, o PHP

encerra a ligação à base de dados automaticamente.

Uma vez que tal acontece, este aspecto revela-se bastante importante porque o

handler (descritor) que representa a ligação à base de dados só existe enquanto o

processo ou thread do pedido existiam. Desta forma, não é possível 8 armazenar um

handler da base de dados e através de uma variável de sessão PHP ou mecanismo

equivalente, com o objectivo de passar essa mesma ligação para outra página.

Resumindo, em cada página PHP que necessite de aceder à base de dados é necessário

estabelecer uma ligação. Ou seja num servidor Web quantos os pedidos de páginas

PHP que existam (e que acedam à base de dados) quantas as ligações que existem à

base de dados em simultâneo.

Pretende-se com o exemplo do código PHP presente na Tabela 15, demonstrar como é

simples utilizar o PostgreSQL no PHP. O código encontra-se agrupado por fases, o

que facilita a sua compreensão.

Passo Código PHP

1 //Abertura de uma ligação à base de dados que reside na própria

//máquina (localhost), usando a conta do utilizador teste e a

//password xpto

$ligacao = pg_connect(“dbname=teste user=teste password=xpto”);

if( !$ligacao )

die(“Erro na ligação”);

8 As ligações mais usuais em página .php são as ligações não persistentes que são criada pela função pg_connect. Há a possibilidade de se criar uma ligação persistente através da função pg_pconnect, a qual não está associada ao processo/thread que corre a página, mas sim a um processo independente e por isso válido em qualquer página. As ligações persistentes não são aconcelhadas na maioria dos casos.

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 114

Passo Código PHP

2 //ligação estabelecida com sucesso

//Uma vez obtida a ligação, é necessário executar o query SQL

//Query SQL a executar

$sql = “SELECT cliente_nome FROM clientes”;

//tentar executar o query na ligação preestabelecida

$resultado = pg_query($ligacao,$sql);

if( !$resultado )

die(“Erro ao executar o query”);

3 //Query executado com sucesso, já é possível aceder ao resultado

$registo = 0; //primeiro registo, índice no resultado

do{

//obter o resultado do registo actual sob a forma de um

array associativo

$arr_rec_cli = pg_fetch_array($resultado, $registo,

PGSQL_ASSOC);

if( !$arr_rec_cli) //ultimo registo

break;

//mostrar o nome do cliente. A string “cliente_nome”

identifica o nome do campo que se quer aceder

printf(“Nome: %s”, $arr_rec_cli[“cliente_nome”]);

$registo++;

while($arr_rec_cli); 4 //Destruir o resultado e a ligação à base de dados

//Destroi o descritor do resultado. A partir do momento que o resultado

//for liberto, já não é possível efectuar operações sobre o resultado

pg_free_result( $resultado );

//Encerra a ligação à base de dados

//Uma vez encerrada a ligação, o descritor da ligação não pode ser

novamente usado.

pg_close( $ligacao );

Tabela 15 – Exemplo código PHP para extrair bytes de um campo bytea

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 115

No passo 1 é estabelecida uma ligação à base de dados. Seguidamente no passo 2, é

criado um descritor para o query SQL a executar. Através desse mesmo descritor é

possível no passo 3 obter-se a informação resultante. Por fim no passo 4, liberta-se os

recursos reservados, concretamente os descritores do query e da ligação à base de

dados.

8.2. Acesso a Campos Binários em PHP

Caso pretenda informações sobre o suporte para campos binários (BLOBs) no

PostgreSQL, por favor consulte a secção 7.

Pretende-se com este ponto explicar sob o ponto de vista do código PHP qual a

sequência das operações e as respectivas funções a usar para que se consiga

inserir/extrair conteúdos binários de campos binários.

A estrutura da tabela de referência a usar é a seguinte:

CREATE TABLE IMAGENS( filme_id INTEGER NOT NULL, imagem_bytes BYTEA NOT NULL, imagem_tamanho INTEGER NOT NULL, CONSTRAINT PK_IMAGENS PRIMARY KEY( filme_id ), CONSTRAINT FK_filme_id FOREIGN KEY( filme_id ) REFERENCES FILMES( filme_id ), CONSTRAINT CK_imagem_tamanho CHECK( imagem_tamanho > 0 ) );

Figura 23 - Estrutura da tabela imagens

A sequência de operações a realizar para inserir a imagem na base de dados é:

• Leitura dos bytes da imagem a partir de um ficheiro para uma string

• Abertura da ligação à base de dados

• Tratamento dos bytes da imagem à entrada

• Criação/execussão do query SQL

• Encerramento dos recursos alocados

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 116

Código PHP para inserção de uma imagem num BLOB

//carregamento do conteudo do ficheiro para uma string

$fich_nome = “/tmp/teste.gif”;

$fich = fopen( $fich_nome, “r”);

$fich_tamanho = filesize ($fich_nome);

$bytes_imagem = $contents = fread ($fich, $fich_tamanho );

$fclose( $fich );

//Abertura da ligação à base de dados $ligacao = pg_connect(“dbname=teste user=teste password=xpto”);

$conv_imagem_bytes = pg_escape_bytea( $bytes_imagem );

//Criação/execusão query SQL

$filme_id = 10;

$sql = “INSERT INTO imagens( filme_id, imagem_bytes, imagem_tamanho )

VALUES( $filme_id, ‘$conv_imagem_bytes’::bytea, $fich_tamanho

)

“;

$resultado = pg_query($ligacao,$sql);

//libertação dos recursos usados

pg_free_result( $resultado );

pg_close( $ligacao );

Em relação à sequência de operações a realizar para a leitura da imagem a partir da

base de dados, as operações a realizar serão:

• Abertura da ligação à base de dados

• Criação/execussão do query SQL

• Leitura dos bytes da imagem presentes no registo da tabela

• Tratamento dos bytes da imagem à saída

• Encerramento dos recursos alocados

Usar PostgreSQL com PHP na Web - Capítulo 8

Página 117

Código PHP para leitura de uma imagem a partir de um BLOB

//Abertura da ligação à base de dados $ligacao = pg_connect(“dbname=teste user=teste password=xpto”);

//Criação/execusão query SQL

$sql =“SELECT imagem_bytes, imagem_tamanho FROM imagens WHERE filme_id=10”;

$resultado = pg_query($ligacao,$sql); $arr_rec_imagem = pg_fetch_array($resultado, 0, PGSQL_ASSOC);

$imagem_bytes = pg_unescape_bytea( $arr_rec_imagem[“imagem_bytes”] );

$imagem_tamanho = arr_rec_imagem[“imagem_tamanho”];

//Caso a imagem fosse para ser enviada para um browser o codigo seria header("Content-type: image/gif");

header(“Content-length: $imagem_tamanho”);

print $imagem_bytes;

//libertação dos recursos usados

pg_free_result( $resultado );

pg_close( $ligacao );

09

Comparação Entre SGBDs - Capítulo 9

Página 119

9. Comparação Entre SGBDs Ao longo deste documento, muitas afirmações foram feitas relativamente ao

PostgreSQL usando como termo de comparação o Oracle.

Nesta parte do documento, irão ser referidas situações de comparação somente entre

os produtos open source. Os fabricantes Oracle Inc. e Microsoft Corporation têm

cláusulas nas EULA – End User Licence Agreement, que não permitem a publicação

de quaisquer comparações numéricas. Segundo alguma informação que tive

oportunidade de ler, isto acontece, porque é muito fácil obter resultados maus,

principalmente em máquinas mal configuradas, podendo colocar em risco quer a

imagem do produto quer a da empresa. Devido a esta restrição, não serão efectuados

quaisquer testes comparativos entre o PostgreSQL e esses mesmos produtos.

Uma vez que não é possível (infelizmente) efectuar comparações de desempenho

entre as versões comerciais de SGBDs e entre o PostgreSQL, então as comparações

serão feitas com um dos mais conhecidos SGBDs open source — o MySQL.

Em primeiro lugar, gostava de deixar claro o seguinte: efectuar comparações entre o

MySQL e o PostgreSQL não me parece justo uma vez que o primeiro não implementa

um quarto das funcionalidades que o PostgreSQL implementa. Pessoalmente acho que

não há comparação possível, mesmo assim, vou fazer um esforço para salientar os

aspectos positivos e negativos de cada um destes.

O MySQL é um SGBD multi-utilizador que apresenta um conjunto razoável de

funcionalidades. No que respeita a estas e segundo o fabricante deste, as funções base

do SQL implementadas seguem um regra conhecida na área como a 80/20. Esta regra

significa que 20% das funcionalidades deste são as que são necessárias por parte de

80% das aplicações. Como fundamentação desta regra, argumentam que os

programadores de sistemas simples, conseguem “viver” sem as funcionalidades em

falta, entre as quais de encontram o suporte a chaves estrangeiras; stored procedures;

triggers; sub queries; transacções. Uma vez que estas não estão presentes, os

programadores são obrigados a recorrer a programação “criativa”.

O PostgreSQL por outro lado fornece muito mais funcionalidades que o MySQL,

entre as quais se destacam: mais funções base implementadas do SQL; stored

Comparação Entre SGBDs - Capítulo 9

Página 120

procedures; triggers, sub queries; suporte a chaves estrangeiras; transacções; métodos

sofisticados para manipulação e extracção de informação relativa a campos do tipo

DATE, TIME e TIMESTAMP; capacidades OO sob modelo relacional; tipos de dados

geométricos; criação de tipos de dados definidos pelo utilizador; criação de

operadores definidos pelo utilizador; encapsulamento das regras de negócio das

aplicações a nível do servidor, usando para o efeito stored procedures e/ou triggers.

Uma boa forma de diferenciar a qualidade entre as duas bases de dados é através do

teste ACID – Atomicity, Consistency, Isolation, Durability. As propriedades ACID

dizem respeito às características que as transacções devem apresentar.

Para mais informações sobre as características ACID, consulte 6.4.1. O PostgreSQL suporta todas as características que são impostas pelo teste ACID. A

implementação standard (MyISAM) de tabelas no MySQL não suporta as

características ACID a 100%, uma vez que não suporta a consistência, isolamento e

durabilidade, suportando por omissão atomicidade através de bloqueios a nível das

tabelas. Há implementações de tabelas que são asseguradas pela MySQL AB e usadas

no MySQL-max, que consiste numa versão que suporta transacções, mas mesmo esta

não é 100% ACID. A única forma de se obter suporte ACID a 100% é através de

implementações de table handlers criados por terceiros, como as Genini da

NuSphere’s. Para activar estes table handlers, durante a criação das tabelas tem que se

indicar qual o table handler a usar.

Devido à ausência de muitas funcionalidades presentes no PostgreSQL, o MySQL é

muito rápido. Este pode ser incrivelmente rápido se as aplicações se poderem adaptar

a tabelas residentes em memória (in-memory tables), desde que a durabilidade dos

dados não seja uma prioridade. Em relação ao PostgreSQL, este possui as

funcionalidades já enumeradas e há a certeza que os dados estão seguros, mesmo após

um crash. No caso de uma aplicação usar todas as funcionalidades presentes no

PostgreSQL, esta certamente que irá sofrer alguns problemas de desempenho, mas

neste capítulo o PostgreSQL tem tido melhorias muito consideráveis.

Quando se entra na área do SQL suportado por cada um das implementações, as

diferenças sobressaem. Tal deve-se à diversidade do dialecto SQL suportado pelo

PostgreSQL o qual é muito mais vasto que o suportado pelo MySQL. Suponhamos

Comparação Entre SGBDs - Capítulo 9

Página 121

que tínhamos duas tabelas, uma com dados de funcionários e uma outra com dados

relativos às presenças destes. Suponhamos ainda que queríamos dar um bónus aos

funcionários mais velhos, que tivessem apresentado o menor número de faltas. Uma

forma simples e intuitiva de atingir o objectivo, seria através de uma instrução

UPDATE que usasse um sub query. Isto é possível de se fazer no PostgreSQL, o que

não acontece no MySQL. Para resolver este problema no MySQL, seria necessário

criar um tabela temporária que guardasse os códigos dos funcionários que tivessem o

mínimo número de faltas. Seguidamente tinha-se que criar uma outra tabela

temporária que guardasse os códigos dos funcionários mais velhos e por fim fazer um

join sobre as duas tabelas temporárias. Se por algum motivo alguém (o departamento

de Recursos Humanos) decidisse alterar os dados após estes terem sido colocados na

tabela temporária? Perante esta situação, estaríamos num cenário de inconsistência,

uma vez que os funcionários lidos da primeira vez não eram os verdadeiros

funcionários que deviam ter sido lidos. É claro que é possível contornar a situação,

efectuando um lock exclusivo em ambas as tabelas, o que não iria permitir que estas

fossem quer lidas quer escritas por qualquer outra ligação à base de dados,

impossibilitando assim que os outros funcionários pudessem usar o sistema.

Embora o princípio ACID proteja os dados de inconsistências de acessos e outros

factores, este mesmo mecanismo não previne falhas a nível de lógica das aplicações,

como por exemplo: permitir remover um cliente que tenha facturas a pagar. Um

operação deste tipo e usando o MySQL seria pura e simplesmente possível! É claro

que se pode circundar este problema, para isso é necessário implementar esta e outras

restrições do género a nível das aplicações, o que leva a que as regras básicas de

negócio andem espelhadas por estas. Isto acontece porque o MySQL não suporta

chaves estrangeiras, mas aceita as cláusulas na criação das tabelas! Usando o

PostgreSQL esta situação pura e simplesmente não aconteceria e seria detectada pela

base de dados, desde que na definição da tabela se tivessem usado chaves

estrangeiras.

Supondo que estamos perante um site que usa o MySQL. Esse site, permite que os

seus utilizadores façam alterações, criações de vários tipos de registos. Suponhamos

ainda que algumas das operações têm que alterar e criar vários registos noutras

tabelas, de forma a guardar as ordens a efectuar. Se num cenário destes e porque a

Comparação Entre SGBDs - Capítulo 9

Página 122

operação estava a demorar muito tempo (o que não é difícil de acontecer) o utilizador

decidiu pressionar o botão stop do browser, o que aconteceria? Pura e simplesmente

os dados registados iam ser os que tinham sido registados até ao momento do

encerramento da ligação, deixando assim a base de dados inconsistente. Isto

aconteceria porque o MySQL não suporta a 100% as regras ACID. Se nesse mesmo

cenário fosse usado o PostgreSQL, podíamos estar certos que ou todos os registos

eram efectivamente registados ou nenhum eram registado, desde que as operações

sobre as tabelas fossem efectuadas sob uma transacção.

A nível de backups e segundo [37], o MySQL tem uma funcionalidade que permite

criar backups on-line (sem que o servidor mysql tenha que ser desligado). Um facto

interessante que descobri e segundo [37] é que quando é iniciado um backup on-line,

o MySQL põe a base de dados em modo read only, não permitindo assim que este

SGBD possa ser usado em ambientes que necessitem de funcionar sem interrupção.

Os verdadeiros backups on-line podem ser feitos se o table handler for InnoDB Há

no entanto um pequeno senão, é que a licença para o software de backup das tabelas

do tipo InnoDB não é grátis. Em relação aos backups on-line no PostgreSQL este

possibilita que o backup seja feito on-line e com a base de dados activa (em modo

read/write) Esta característica torna-o uma vez mais apto para ambientes cuja

disponibilidade do SGBD tem que total (27/7).

Uma vez que ambas as bases de dados tem algumas similaridades, como é que se

pode escolher entre cada uma delas? Deve-se escolher um SGBD ou outro

dependendo do grau de complexidade envolvido e também de acordo com o grau de

importância dos dados a armazenar. Por exemplo: suponhamos que se pretende migrar

um conjunto de aplicações de Oracle, Sybase ou mesmo um SQLServer. Se o SGBD

escolhido fosse o PostgreSQL então a vida ficaria mais facilitada, uma vez que os

últimos suportam as mesmas funcionalidades que o PostgreSQL.

No entanto se o objectivo fosse por exemplo permitir consultas num site, o MySQL

possivelmente dúvida seria uma boa escolha, mas só se forem feitas somente leituras

sobre a tabela. Este perante acessos concorrentes de leitura e escrita sobre os mesmos

dados, comporta-se verdadeiramente mal, uma vez que tem que bloquear toda a tabela

para conseguir consistência. O PostgreSQL nesta mesma situação, não teria qualquer

problema graças ao mecanismo de controlo da concorrência usado,

Comparação Entre SGBDs - Capítulo 9

Página 123

o MVCC – MultiVersion Concurrency Control, o qual não bloqueia nem leitores nem

escritores e vice-versa, sendo por isso muito melhor que qualquer mecanismo de

bloqueio.

9.1. Comparação MySQL com PostgreSQL

Esta secção do documento corresponde a pequenos extractos do teste de desempenho

efectuado por um dos colunistas do site [32], o qual também é autor do site [34].

Para aceder à versão original do teste, visite [33].

Este teste foi realizado por [32] usando a versão 7.1 do PostgreSQL e a 3.23.26 beta

do MySQL, no qual foram usados dados verdadeiros e iguais os quais fazem parte de

[34] . Segundo o autor, assim que este migrou [34] para o PostgreSQL 7.1 o seu site

foi considerado segundo [35] um dos sites mais estáveis.

Para o teste, foi activado no modelo de dados do PostgreSQL as chaves estrangeiras,

com o intuito de verificar se estas degradariam o desempenho. O teste foi efectuado

numa máquina com 1GB de RAM e com quatro processadores Intel Xeon. No teste

efectuado, o cliente Apache “AB”, o Apache/PHP e as bases de dados estavam a

correr na mesma máquina. Em relação aos queries seleccionados, foram escolhidos os

que eram mais intensos a nível da base de dados.

1ª Situação : Avaliação de desempenho para acessos concorrentes de leitura e escrita

Foi criada uma página PHP que estava constantemente a executar queries, INSERT,

UPDATE, dentro de transacções. Colocaram-se trinta clientes a visitar essa página e

outros trinta em simultâneo a visitar a página “My Personal Page”. Uma vez que o

PostgreSQL suporta transacções, o autor decidiu que 25% das transacções fariam o

rollback para testar se havia quebras de desempenho.

Na Figura 24 encontram-se os resultados dos testes efectuados. Como se pode reparar

o MySQL quando confrontado neste teste com um total de clientes superiores a 20,

pura e simplesmente deixou de responde, embora o SGBD não tenha sofrido nenhum

crash.

Comparação Entre SGBDs - Capítulo 9

Página 124

Figura 24 – Resultados do teste de concorrência Read/Update. Fonte: [33]

Através da figura, constata-se que o MySQL mesmo para 5 utilizadores teve um

desempenho muito mau, cerca de 0.77 páginas por segundo, enquanto que o

PostgreSQL perante as mesmas condições obteve cerca de 2.05 páginas por segundo.

Assim que o número de ligações vai aumentando a ideia que dá é que o PostgreSQL

foi respondendo cada vez melhor.

2ª Situação: Avaliação de desempenho para acessos que envolvam vários joins

Apresentação da página Web do “Bug Tracker”. Segundo o autor, para que a página

do “Bug Tracker” possa ser mostrada ao visitante do site esta necessita que sejam

executados 16 queries, com vista a obter toda a informação relevante ao bug

submetido.

Segundo o autor do teste, os queries envolvidos tem pouca complexidade à excepção

um deles ter que sofrer um duplo join com a tabela dos utilizadores (para obter o

nome da pessoa que enviou o bug).

Nestas situações segundo o autor, o MySQL em princípio devia sair vencedor, uma

vez que SELECT’s e joins são a sua especialidade.

Comparação Entre SGBDs - Capítulo 9

Página 125

Figura 25 – Resultados da página “Bug Tracker”. Fonte: [33]

Segundo a análise da Figura 25 pode-se reparar que o MySQL no início do teste,

quando estavam 5 utilizadores a aceder à base de dados, teve melhores resultados do

que o PostgreSQL. Assim que o nível de concorrência foi sendo cada vez mais

elevado, o MySQL assim atingiu os 20 utilizadores em simultâneo, teve uma descida

abrupta que o levou das 14 páginas por segundo até às 8 páginas por segundo, tendo

desta forma apresentando uma quebra de desempenho na ordem dos 55%.

Note-se ainda que o PostgreSQL assim que o nível de concorrência foi aumentando,

também teve uma tendência (menos abrupta) para perder desempenho.

3ª Situação: Avaliação de desempenho que envolve joins e funções agregadoras

Nesta situação será avaliado o desempenho dos SGBDs, tendo como base o join entre

duas tabelas e o uso de uma função de agregação (COUNT) com vista a obter um total.

Correspondendo a situação descrita a uma das melhores “armas” do MySQL, o autor

do teste achou que o MySQL sairia vencedor neste teste. Segundo o autor, o

desempenho do COUNT(*) em versões anteriores do PostgreSQL era muito má. Os

resultados obtidos presentes na Figura 26, mostram como o PostgreSQL tem tido

muito melhoramentos a nível da optimização dos queries.

Comparação Entre SGBDs - Capítulo 9

Página 126

Figura 26 – Resultado do teste “Forum”. Fonte: [33]

Resultado

Como se pode depreender das situações analisadas, o PostgreSQL em nenhuma das

situações revelou qualquer falha de estabilidade. O mesmo não se pode afirmar para o

MySQL, o qual deixou de responder na 1ª situação.

Em termos de desempenho o MySQL de forma geral não conseguiu obter melhores

resultados, nem mesmo nas situações que são a sua “especialidade”. No que respeita

ao teste da 1ª situação em que existiam situações de leituras e actualizações em

simultâneo, os resultados obtidos seriam de esperar. Isto porque o MySQL para

garantir a consistência faz bloqueios a nível da tabela, reduzindo assim desta forma o

nível de concorrência. Nestas mesmas condições, o PostgreSQL e o seu modelo de

concorrência MVCC mostraram ser um dos seus trunfos.

Segundo o autor do teste e citando-o: “Well, it means you can't simply cross

PostgreSQL off your list because it's a dog anymore. Now, not only does PostgreSQL

have dozens of very advanced features that MySQL lacks, but it doesn't appear to be

weighed down by those features as it was in the past. And that's good for everyone.”.

Comparação Entre SGBDs - Capítulo 9

Página 127

De seguida são apresentadas algumas opiniões de utilizadores de SGBDs comerciais e

open source (MySQL e PostgreSQL) através dos quais é possível ver o que estes

pensam. Esta opiniões foram extraídas de um grupo de discussão em [36].

Re:It's all well and good..... (Score:1) by johnnyb ([email protected]) on Tuesday August 15, @05:07AM (#855896) (User #4816 Info | http://members.wri.com/johnnyb/) You should try backing up your claim. Obviously, MySQL is a toy database. It is used for fast tabular data access, not true database work. What makes PostgreSQL a toy? It supports things such as table inheritance, very nice transaction support, and other things. …

Actually, for database applications, the critical component is the hardware (assuming you have a _real_ RDBMS - not MySQL). Sun hardware beats just about anything. If you want to see PostgreSQL being used in the real world, take a look at http://www.pgsql.com/

Re:The Hate-MySQL Squad is out in force1 (Score:2) by Wdomburg ([email protected].) on Monday August 14, @07:27PM (#855900) (User #141264 Info) …. Have you read the MySQL docs? Their section on why not to use foreign keys at best makes me laugh, at worst makes me cringe. This sentiment is even shared by most of the MySQL fans I know. They gloss over their lack of sub-selects with an example that doesn't require the feature. They gloss over the lack of transactions and commit/rollback syntax by suggesting table locking, which simply is not practical in high volume environments. Moreover, their examples neatly omit the idea that you may need to lock a *LOT* of tables, stalling updates to most of the database in order to circumvent the lack of MVCC. Whether you want to admit it or not, the drawbacks in MySQL are *VERY* real, and the MySQL documentation trys to play them off as minimal annoyances. I find their lack of responsibility in this more offensive than a few causally posted insults.

Comparação Entre SGBDs - Capítulo 9

Página 128

Re:A book is coming out soon (Score:2) by GMontag ([email protected]) on Monday August 14, @08:03PM (#855928) (User #42283 Info | http://slashdot.org/comments.pl?sid=) *IF* you are in a production environment, you need a *real* RDMS, NOT MySQL (a file system with a SQL interface). PostgreSQL sounds cool, will look deeper for my next DB server. Visit DC2600

Re:Is it really ready for production use? (Score:1) by dhogaza on Monday August 14, @03:20PM (#855903) (User #64507 Info | http://donb.photo.net) … Once the leaks were fixed, the folks I know using AOLserver+Postgresql who were forced to reboot periodically found their problems disappeared. We at OpenACS (http://www.openacs.org) have been running our Postgres installations for months at a time with no need to reboot. It truly has improved dramatically in the last 15 months or so. Am I a biased Postgres fanatic? Not exactly - when I evaluated 6.4 in January, 1999 I decided it was useless for web work, far too slow and crash-prone. When 6.5 was released it was so dramatically improved that I changed my mind. And, the OpenACS web toolkit project intends to support both Postgres and Interbase (now that the latter's Open Source), so we're not Postgres-only bigots.

10

Conclusão - Capítulo 10

Página 130

10. Conclusão

Antes de mais gostava de explicar o que me motivou na escolha deste projecto. Para a

escolha houveram dois factores que tiveram bastante peso:

• Possibilidade de estudar um SGBD a diferentes níveis, desde:

administração; instalação; desenvolvimento até à manutenção.

• O desafio de ter que explicar conceitos geralmente complexos a pessoas

que ainda estão a dar os primeiros passos na área para que estas ao usarem

esses mesmos conceitos e técnicas entendam o que se está a passar e não

tomem atitudes passivas como: “acontece por é assim”.

Alguns conceitos abordados eram novos para mim, por isso este projecto possibilitou-

me a oportunidade de os estudar. Embora já tivesse usado várias bases de dados e

entende-se os mecanismos inerentes, o facto é que este documento permitiu-me

aprofundar ainda mais os meus conhecimentos principalmente no que respeita a

índices, optimização de queries e também a nível da arquitectura dos próprios

SGBDs.

No que respeita à base de dados PostgreSQL, nunca pensei que houvesse um SGBD

open source com as potencialidades do PostgreSQL, uma vez que a concepção de um

SGBD é uma tarefa muito complexa e demorada. Fiquei admirado, porque a única

base de dados open source que conhecia era o MySQL, o qual me deixou muito má

imagem deste tipo de SGBDs. Do meu ponto de vista e comparando as capacidades,

funcionalidades implementadas e desempenho oferecido pelo PostgreSQL com outras

bases de dados open source (em concerto o MySQL), posso afirmar que o

PostgreSQL é sem margem de dúvida um SGBD que deve ser tido em conta, quer este

se destine a sistemas simples, quer para complexos, quer sejam para ser usados na

Web ou em empresas que necessitem de armazenar e processar grandes volumes de

dados.

Na minha opinião o PostgreSQL foi sem margem para dúvidas a melhor base de

dados open source com que tive a oportunidade de trabalhar.

Em comparação com outros SGBDs comerciais, como o Oracle, considero que o

PostgreSQL está no bom caminho, uma vez que apresenta de raiz funcionalidades

muito importantes tais como: restrições de integridade, triggers, stored procedures

em várias linguagens, transacções, criação de operadores, criação de tipos, etc.

Através de técnicas como o WAL e o MVCC pode-se reparar que o PostgreSQL está

Conclusão - Capítulo 10

Página 131

ao nível das implementações comerciais, como é o caso do Oracle que usa as mesmas.

Pessoalmente acho que o PostgreSQL está apto para ambientes mais exigentes, como

aqueles em que a integridade dos dados e o desempenho sejam um pré-requisito, uma

vez que este usa e implementa as mesmas técnicas que a versões comerciais de outros

SGBDs.

Sendo o PostgreSQL software open source, muitas pessoas tendem a vê-lo como um

produto com pouca ou sem qualidade. A meu ver esta visão está completamente

errada. Isto porque a comunidade open source já demonstrou através de produtos

como o sistema operativo Linux; Apache (servidor Web mais usado segundo [35]) e o

Squid (proxy Web usado mundialmente quer pelo governo, quer por ISP’s e

empresas) que existe qualidade. Pessoalmente penso que o único factor que leva a que

as pessoas pensem desta forma é a desinformação.

Em relação ao PostgreSQL e focando a qualidade, perguntei a uma conjunto de

pessoas conhecidas qual a base de dados open source que geralmente usavam. A

resposta era inevitável (infelizmente), o MySQL. Quando a estas mesmas pessoas lhes

perguntei se conheciam o PostgreSQL, a grande maioria, cerca de 90% disse que

desconhecia. Pois bem perante esta falta de conhecimentos, enumerei-lhes as

características técnicas; as vantagens e desvantagens do PostgreSQL. Um colega meu

que estava a implementar um sistema de gestão de documentos para a universidade

(projecto de bacharelato), ao ter ouvido tais características, decidiu dar uma hipótese

ao PostgreSQL. Actualmente o sistema encontra-se a correr e que eu saiba (ele ter-

me-ia dito), não apresentou quaisquer problemas relacionados com a base de dados.

Um outro factor pelo qual eu depreendo que o PostgreSQL não seja tão conhecido e

usado como devia está relacionado com o facto de muito poucos ISP’s – Internet

Service Providers fornecerem o PostgreSQL nas suas propostas de alojamento.

Um outro factor que geralmente assola o software open source é a assistência técnica

e o “ficar” preso ao produto. Relativamente à assistência técnica do software open

source em geral, considero-a bastante boa e eficaz. Tem imensas vantagens, umas das

quais é que se pode contactar directamente com as pessoas que o desenvolveram,

permitindo dessa forma que possíveis correcções surjam muito rapidamente. Isto não

acontece nas empresas mundialmente conhecidas, que chegam a demorar mais de

meses a disponibilizar correcções.

No que respeita à assistência técnica oferecida pela comunidade PostgreSQL, esta é

acessível quer através de mailing lists (específicas ou gerais) ou através de contratos

Conclusão - Capítulo 10

Página 132

comerciais realizados com a empresa psql. No caso das mailing lists, se nenhum dos

membros conseguir resolver o problema, é sempre possível recorrer à mailing list dos

programadores do PostgreSQL. Em relação ao suporte comercial, os contratos, vão

desde o suporte técnico via e-mail e telefone até situações em que as empresas podem

reservar um certo número de pessoas que a qualquer momento e quando solicitadas,

resolvem os problemas que possam haver. Em relação ao “ficar” preso ao produto,

com o PostgreSQL isso não acontece, uma vez que este tem utilitários que permitem

exportar parte ou totalidade da base de dados (incluído os dados) sob uma forma

standard – o SQL, em vez de usar formatos proprietários. Há ainda vários scripts que

permitem a migração de e para o PostgreSQL.

Por fim e para dar como encerrado o documento, gostava de referir que o PostgreSQL

actualmente e cada vez mais está a disponibilizar mecanismos e técnicas que até agora

só apareciam em implementação comerciais, em concreto está previsto o início do

desenvolvimento dos mecanismos que asseguram a replicação. À medida que estas

forem sendo implementadas estou convicto que o PostgreSQL cada vez mais irá

aproximar-se das implementações comerciais de referência como o Oracle, quer a

nível de desempenho quer de funcionalidades.

Para terminar gostava de mencionar que conheço sistemas em que o PostgreSQL está

a ser usado, tanto em ambientes Linux com em Windows 2000 Professional. No

sistema Linux, o PostgreSQL já está a correr há cerca de um ano e meio sem que me

tenha sido transmitido qualquer problema relacionado com falhas na base de dados ou

mesmo crash’s. Actualmente esse mesmo sistema está a executar a versão 7.3.3, a

qual foi passando por vários upgrades, 3 no total. O PostgreSQL em comparação com

o Oracle 7.1 da mesma organização, tem-se revelado muito mais estável, estou

convicto que a instabilidade deste, não está certamente relacionada como o próprio

Oracle mas sim com a administração quer da base de dados quer do respectivo

servidor.

No sistema PostgreSQL enumerado acima, em média existem cerca de 75 ligações

(não é um valor muito importante, mas só para referência) em simultâneo, sob as

quais são realizadas execuções desde os queries mais simples, até aos queries mais

complexos ( que envolvem joins bastante complexos entre várias tabelas, cerca de 15

e disparos de triggers), sem que nunca os utilizadores se tenham queixado quer por

Conclusão - Capítulo 10

Página 133

parte da base de dados quer pelo sistema como um todo, o qual se encontra a executar

funções muito variadas (router/firewall, PostgreSQL, Apache).

Certamente deve ter reparado que ao longo deste foram sendo feitas comparações

quase sempre com o Oracle. Não escolhi efectuar tais comparações com os produtos

open source, porque pura e simplesmente a grande maioria das funcionalidades que o

PostgreSQL disponibiliza, não se encontram disponíveis em mais nenhum produto

open source (que eu tenha conhecimento). Por isso optei por o comparar com o

Oracle, porque este é sem dúvida o produto comercial com que o PostgreSQL pode

ser comparado, uma vez que usa as mesmas técnicas.

11

Bibliografia - Capítulo 11

Página 135

11. Bibliografia

[1] Rnald R. Plew / Ryan K. Stephens, Sams Teach Yourself SQL in 24 Hours,

SAMS, ISBN: 007231245X

[2] José Luís Pereira, Tecnologia de Bases de Dados, FCA, ISBN: 972-722-143-2, 1998

[3] Raghu Ramakrishnan / Johannes Gehrke, Database Management Systems

(second edition), McGraw-Hill, ISBN 0-07-232206-3, 1999

[4] http://www.postgresql.org/lists.html

[5] http://postgis.refractions.net/

[6] http://www.pgaccess.org/

[7] http://www.pgadmin.org/pgadmin2/pgadmin2.php

[8] http://www.synametrics.com/SynametricsWebApp/Products.jsp

[9] http://www.postgresql.org

[10] ftp://ftp.postgresql.org/pub/

[11] PostgreSQL Administrator Guide - Chapter 1. Installation Instructions - 1.5.

Installation Procedure

[12] http://gborg.postgresql.org

[14] PostgreSQL Administrators Guide – Client Authentication

[15] PostgreSQL Programmers Guide - I. Client Interfaces - 1.2. Database

Connection Functions

[16] PostgreSQL Programmers Guide – - I. Client Interfaces - 1.4. Asynchronous

Query Processing

[17] PostgreSQL Administrator Guide - Chapter 10. Monitoring Database

Activity - 10.2. Statistics Collector

[18] PostgreSQL Administrator Guide – 3. Server Run-time Environment - 3.4

Runtime configuration

[19] PostgreSQL Reference Manual – I. SQL Commands - Alter table

[20] PostgreSQL Reference Manual - II. Server Programming — Chapter 19 -

PL/pgSQL - SQL Procedural Language

[21] PostgreSQL User guide – 9 Concurrency Control

[22] PostgreSQL Administrator guide- 12 Write Ahead Logging

[23] PostgreSQL User's Guide – Chapter 5. Data Types - 5.4 Binary Strings

Bibliografia - Capítulo 11

Página 136

[24] PostgreSQL Programmer's Guide – I. Client Interfaces - Chapter 2. Large

Objects

[25] J. Melton and A. Simon. Understanding the New SQL: A Complete Guide.

Morgan Kaufmann, 1993.

[26] C. Date and H. Darwen. A Guide to the SQL Standard (3rd ed). Assison-

Wesley, 1995.

[27] ftp://jerry.ece.umassd.edu/isowg3/

[28] A. Eisenberg and J. Melton. Sql: 1999, formerly known as sql3 ACM

SIGMOD Record, 28(1): 131-139, 1999

[29] PostgreSQL Reference Manual – I. SQL Commands – Create Database

[30] PostgreSQL Reference Manual – I. SQL Commands – Create User

[31] Raghu Ramakrishnan / Johannes Gehrke, Database Management Systems

(second edition), Chapter 10 Hash-Based Indexing, McGraw-Hill, ISBN 0-

07-232206-3, 1999

[32] http://www.phpbuilder.com

[33] http://www.phpbuilder.com/columns/tim20001112.php3

[34] http://www.geocrowler.com

[35] http://www.netcraft.com

[36] http://slashdot.org/articles/00/08/14/2128237.shtml

[37] http://faemalia.org/wiki/view/Technical/PostgreSQLvsMySQL

[38] http://gborg.postgresql.org/project/psqlodbc/

12

Anexos - Capítulo 12

Página 138

12. Exemplo de execução síncrona usando libpq

#include <stdio.h>

#include <libpq-fe.h>

void

exit_nicely(PGconn *conn)

{

PQfinish(conn);

exit(1);

}

main()

{

char *pghost,

*pgport,

*pgoptions,

*pgtty;

char *dbName;

int nFields;

int i,

j;

/* FILE *debug; */

PGconn *conn;

PGresult *res;

/*

* comecemos por configurar os parâmetros para a ligação ao servidor. Se algum

* dos parâmetros for NULL, o sistema tenta usar valores razoáveis por

* omissão, caso tal na seja possível, usa valores obtidos durante

* compilação do PostgreSQL.

*/

pghost = NULL; /* nome DNS do servidor */

pgport = NULL; /* porta TCP do servidor */

pgoptions = NULL; /* opções especiais para configuração servidor */

pgtty = NULL; /* tty para debugging do servidor */

dbName = "clientes"; /* nome da base de dados */

/* estabelece uma ligação à base de dados */

conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

/*

* verifica se a ligação foi estabelecida com sucesso

*/

if (PQstatus(conn) == CONNECTION_BAD)

{

fprintf(stderr, "A ligação à base de dados ’%s’ falhou.\n", dbName);

fprintf(stderr, "%s", PQerrorMessage(conn));

exit_nicely(conn);

}

/* debug = fopen("/tmp/trace.out","w"); */

Anexos - Capítulo 12

Página 139

/* PQtrace(conn, debug); */

/* inicializa uma transação */

res = PQexec(conn, "BEGIN");

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

{

fprintf(stderr, "O comando BEGIN falhou\n");

PQclear(res);

exit_nicely(conn);

}

/*

* É necessário invocar PQclear no objecto PGresult sempre que o resultado não

* seja necessário, para evitar fugas de memória (memory leaks)

*/

PQclear(res);

res = PQexec(conn, "SELECT * from clientes");

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

{

fprintf(stderr, "O comando SELECT falhou. Linha:%d, Ficheiro:%s\n",

__LINE__, __FILE__ );

PQclear(res);

exit_nicely(conn);

}

/* primeiro, mostra os nomes dos atributos */

nFields = PQnfields(res);

for (i = 0; i < nFields; i++)

printf("%-15s", PQfname(res, i));

printf("\n\n");

/* de seguida, mostra os valores para cada tuplo */

for (i = 0; i < PQntuples(res); i++)

{

for (j = 0; j < nFields; j++)

printf("%-15s", PQgetvalue(res, i, j));

printf("\n");

}

PQclear(res);

/* commit à transacção */

res = PQexec(conn, "COMMIT");

PQclear(res);

/* encerra a ligação à base de dados e liberta os recursos */

Anexos - Capítulo 12

Página 140

PQfinish(conn);

/* fclose(debug); */

return 0;

}

12.1. Exemplo de execução assíncrona usando libpq

/* Descrição: Supondo que temos que mostrar a informação do cliente e também uma

análise de compras. A análise de compras consiste em todas as reservas de todos os

movimentos efectuados do cliente desde a sua existência, de forma a que o operador

possa aconselhar melhor o cliente.

Supondo ainda que os valores necessários tem que ser calculados em tempo de

execução em vez de serem valores somados a partir de campos acumuladores.

Supondo ainda que há um requisito funcional que obriga a que a informação base

sobre o cliente seja mostrada em primeiro lugar.

Suponhamos também que o query (irrelevante para o exemplo) está residente numa

vista (view) com o nome de v_resumo_cliente_demorado.

Presuma-se ainda que o número de ligações à base de dados está limitado por uma

ligação por cliente.

Resolução possível: Criar um query que execute de forma assíncrona o query sobre a

vista v_resumo_cliente_demorado. Após o envio do query (o query esta a ser

processado pelo servidor em background), executar um query síncrono para obter os

dados base do cliente.

Resolução impossível (até ao momento): Criar uma thread extra que partilhe a mesma

ligação e dessa forma usar uma ligação para calcular os dois queries. Esta solução

torna-se impossível porque a API libpq é thread safe, o que quer dizer que se uma

thread (a principal) está a aceder à ligação, mais nenhuma outra o pode fazer em

simultâneo.

*/

#include <stdio.h>

#include "libpq-fe.h"

Anexos - Capítulo 12

Página 141

void

exit_nicely(PGconn *conn)

{

PQfinish(conn);

exit(1);

}

main()

{

char *pghost,

*pgport,

*pgoptions,

*pgtty;

char *dbName;

int nFields;

int i,

j;

PGconn *conn;

PGresult *res;

PGresult *resAsync;

PGnotify *notify;

int notifyCompleted=0;

int haveClienteInfo = 0;

/*

* comecemos por configurar os parâmetros para a ligação ao servidor. Se algum

* dos parâmetros for NULL, o sistema tenta usar valores razoáveis por

* omissão, caso tal na seja possível, usa valores obtidos durante

* compilação do PostgreSQL.

*/

pghost = NULL; /* nome DNS do servidor */

pgport = NULL; /* porta TCP do servidor */

pgoptions = NULL; /* opções especiais para configuração servidor */

pgtty = NULL; /* tty para debugging do servidor */

dbName = "clientes"; /* nome da base de dados */

/* estabelece uma ligação à base de dados */

conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

/*

* verifica se a ligação foi estabelecida com sucesso

*/

if (PQstatus(conn) == CONNECTION_BAD)

{

fprintf(stderr, "A ligação à base de dados ’%s’ falhou.\n", dbName);

fprintf(stderr, "%s", PQerrorMessage(conn));

exit_nicely(conn);

}

Anexos - Capítulo 12

Página 142

/*

* Indica ao servidor que queremos criar um género de variável de condição, em

* que uma ou várias conexões se encontram possivelmente à escuta

*/

res = PQexec(conn, "LISTEN identificador_query_complexo");

if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)

{

fprintf(stderr, "O comando LISTEN falhou.\nLinha:= %d\nFicheiro:%s",

__LINE__, __FILE__ );

PQclear(res);

exit_nicely(conn);

}

/*

* É necessário invocar PQclear no objecto PGresult sempre que o resultado não

* seja necessário, para evitar fugas de memória (memory leaks)

*/

PQclear(res);

/*

* Envia os dois queries sem aguardar por qualquer resultado.

*/

resAsync = PQsendQuery (conn, "SELECT * from v_resumo_cliente_demorado;

NOTIFY identificador_query_complexo;");

if (!resAsync || PQresultStatus(resAsync) != PGRES_COMMAND_OK)

{

fprintf(stderr, "O comando SELECT e ou NOTIFY falhou.\nLinha:=

%d\nFicheiro:%s", __LINE__, __FILE__ );

PQclear(resAsync);

exit_nicely(conn);

}

while (1)

{

/*

* Aguarda um pouco entre verificações;

*/

sleep(1);

/* le notificações assíncronas que tenham vindo do servidor */

PQconsumeInput(conn);

if( notifyCompleted && haveClienteInfo ){

; /*Mostra a informacao do resumo de movimentos*/

break;

}

/* verifica se existem mensagens assíncronas a processar */

notify = PQnotifies(conn));

if( notify != NULL)

Anexos - Capítulo 12

Página 143

notifyCompleted = 1;

else if( !haveClienteInfo ){

; /* algo do género do exemplo anterior, para o SELECT */

haveClienteInfo = 1;

;/*parte do código para mostrar a informacao do cliente */

}

}

PQclear( resAsync );

Pqclear( res );

/* encerra a ligação à base de dados e liberta os recursos */

PQfinish(conn);

return 0;

}

12.2. Modelo de dados de referência ao documento

Instruções DDL para criação do modelo de dados de referência

CREATE TABLE DISTRITOS(

distrito_codigo SMALLINT NOT NULL,

distrito_nome VARCHAR(60) NOT NULL,

CONSTRAINT PK_DISTRITOS PRIMARY KEY( distrito_codigo ),

CONSTRAINT CK_DISTCOD CHECK( distrito_codigo > 0 )

);

Explicação: A instrução CREATE TABLE acima vai criar uma tabela de nome DISTRITOS que

vai ter dois campos: distrito_codigo e distrito_nome. Os valores possíveis para o

campo distrito_codigo, são os mesmos do tipo de dados SMALLINT, cujos limites são: -

32768 até +32767.

Os valores possíveis para o campo distrito_nome, serão todos as sequências de

caracteres numéricos e alfanuméricos, cujo comprimento máximo seja inferior ou igual a

60 caracteres. O facto de se definir o tipo de dados deste atributo como VARCHR em vez

de CHAR tem as implicações descritas de seguinda.

O tipo de dados VRCHAR(n), permite guardar sequencias de caracteres, mas cujo número

de caracteres não seja superior a n, podendo no entanto ser inferior.

Por exemplo: Para armazenar a sequência de caracteres “abcd” num campo VARCHAR(20) a

base de dados iría gastar somente 4 caracteres em vez dos 20 para cada registo. O tipo

de dados VARCHAR(n) é conhecido na área das bases de dados como character varying(n).

Se a definição do campo fosse em vez de VARCHAR(60) fosse CHAR(60), o SGBD para

sequência de caracteres “abcd”, iria armazenar os quatros caracteres, preenchendo os

restantes com espaços em branco. Ou seja a sequência de carateres deixou de ter um

comprimentos variável e passou a ter um comprimento fixo.

Como os atributos distrito_codigo e distrito_nome estão definidos com a restrição

NOT NULL, então nenhum destes campos poderão estar por preencher.

Nota: NULL é diferente de espaços em branco. NULL significa que o valor está ausente.

Para além das restrições impostas pelos tipos de dados dos atributos, há duas

restrições de integridade que estão definidas:

Anexos - Capítulo 12

Página 144

CONSTRAINT PK_DISTRITOS PRIMARY KEY( distrito_codigo )

CONSTRAINT CK_DISTCOD CHECK( distrito_codigo > 0 )

A primeira de nome PK_DISTRITOS indica ao SGBD que os valores presentes no atributo

distrito_codigo são obrigatoriamente não nulos(NULL) e únicos, uma vez que se trata de

uma restrição(constraint) do tipo PRIMARY KEY.

A Segunda restrição de nome CK_DISTCOD permite refinar a gama de valores que serão

aceites para o campo distrito_codigo, neste caso os valores têm que ser superiores a

zero.

CREATE TABLE REGIOES(

regiao_codigo SMALLINT NOT NULL,

regiao_nome VARCHAR(60) NOT NULL,

CONSTRAINT PK_REGIOES PRIMARY KEY( regiao_codigo )

);

CREATE TABLE CODIGO_POSTAL(

codigopostal_id SERIAL NOT NUlL,

distrito_codigo SMALLINT NOT NULL,

regiao_codigo SMALLINT NOT NULL,

localidade_nome VARCHAR(60) NOT NULL,

CONSTRAINT PK_CODIGOPOSTAL PRIMARY KEY( distrito_codigo, regiao_codigo ),

CONSTRAINT FK_distritocodigo FOREIGN KEY( distrito_codigo ) REFERENCES

DISTRITOS( distrito_codigo ),

CONSTRAINT FK_regiaocodigo FOREIGN KEY( regiao_codigo ) REFERENCES

REGIOES( regiao_codigo ),

CONSTRAINT UK_codigopostal_id UNIQUE( codigopostal_id )

);

Explicação: O tipo de dados SERIAL (também conhecido em outras base de dados por

autonumber e autoincrement) usado na definição do campo codigopostal_id corresponde a

um tipo de dados INT, mas cuja responsabilidade de gerar o valor é do SGBD e por isso

a sequencia de valores é assegurada por este. Uma vez que este atributo faz parte das

chaves candidates, logo este será usado como chave estrangeira noutras tabelas,

passando assim a haver menos redundância.

Na 5ª linha da instrução CREATE TABLE acima, está presente a criação de uma restrição

de integridade referencial, a qual cria uma chave estrangeira. O valor a inserir no

campo distrito_codigo tem que ser um dos valores que estejam presentes no campo

distrito_codigo da tabela DISTRITOS.

Na 6ª linha da instrução CREATE TABLE encontra-se a definição de uma restrição

semelhante à da 5ª linha.

A última linha define uma restrição de integridade que garante que os valores

presentes no campo codigopostal_id são sempre únicos, logo este campo vi funcionar como uma chave candidata e por isso será usada como chave estrangeira noutras tabelas.

CREATE TABLE CLIENTES(

cliente_id SERIAL NOT NULL,

cliente_nome VARCHAR(80) NOT NULL,

cliente_morada VARCHAR(60) NOT NULL,

cliente_idade SMALLINT NOT NULL,

codigo_postal_id INTEGER NOT NULL,

cliente_sexo CHAR NOT NULL,

Anexos - Capítulo 12

Página 145

CONSTRAINT PK_CLIENTES PRIMARY KEY( cliente_id ),

CONSTRAINT FK_codigopostal FOREIGN KEY( codigo_postal_id )

REFERENCES CODIGO_POSTAL( codigopostal_id ),

CONSTRAINT CK_cliente_idade CHECK( cliente_idade >=2 ),

CONSTRAINT CK_cliente_sexo CHECK( cliente_sexo IN( 'M', 'F' ) )

);

CREATE TABLE CINEMAS(

cinema_id SERIAL NOT NULL,

cinema_nome VARCHAR(60) NOT NULL,

codigo_postal_id INTEGER NOT NULL,

cinema_morada VARCHAR(60) NOT NULL,

CONSTRAINT PK_CINEMAS PRIMARY KEY( cinema_nome, codigo_postal_id ),

CONSTRAINT FK_codigopostal FOREIGN KEY( codigo_postal_id ) REFERENCES

CODIGO_POSTAL( codigopostal_id ),

CONSTRAINT UK_cinema_id UNIQUE( cinema_id )

);

CREATE TABLE SALAS(

sala_id SERIAL NOT NULL,

cinema_id INTEGER NOT NULL,

sala_nome VARCHAR(30) NOT NULL,

sala_capacidade SMALLINT NOT NULL,

CONSTRAINT PK_SALAS PRIMARY KEY( cinema_id, sala_nome ),

CONSTRAINT FK_CINEMA_ID FOREIGN KEY( cinema_id ) REFERENCES

CINEMAS( cinema_id ),

CONSTRAINT CK_capacidade CHECK( sala_capacidade > 5 ),

CONSTRAINT UK_sala_id UNIQUE( sala_id )

);

CREATE TABLE FILMES(

filme_id SERIAL NOT NULL,

filme_nome VARCHAR(60) NOT NULL,

filme_tipo CHAR NOT NULL,

filme_duracao SMALLINT NOT NULL,

CONSTRAINT PK_FILMES PRIMARY KEY( filme_nome ),

CONSTRAINT CK_filme_tipo CHECK( filme_tipo IN('A','D','R') ),

CONSTRAINT CK_filme_duracao CHECK( filme_duracao > 10 ),

CONSTRAINT CK_filme_id UNIQUE( filme_id )

);

CREATE TABLE IMAGENS(

filme_id INTEGER NOT NULL,

imagem_bytes BYTEA NOT NULL,

imagem_tamanho INTEGER NOT NULL,

CONSTRAINT PK_IMAGENS PRIMARY KEY( filme_id ),

CONSTRAINT FK_filme_id FOREIGN KEY( filme_id ) REFERENCES

FILMES( filme_id ),

CONSTRAINT CK_imagem_tamanho CHECK( imagem_tamanho > 0 )

);

Anexos - Capítulo 12

Página 146

CREATE TABLE SESSAO(

sessao_id SERIAL NOT NULL,

sala_id INTEGER NOT NULL,

filme_id INTEGER NOT NULL,

sessao_hora TIME NOT NULL,

CONSTRAINT PK_SESSAO PRIMARY KEY( sala_id, filme_id, sessao_hora ),

CONSTRAINT FK_sala_id FOREIGN KEY( sala_id ) REFERENCES SALAS( sala_id ),

CONSTRAINT FK_filme_id FOREIGN KEY( filme_id ) REFERENCES

FILMES( filme_id ),

CONSTRAINT CK_sessao_hora CHECK( sessao_hora BETWEEN '14:00' AND '4:00' ),

CONSTRAINT UK_sessao_id UNIQUE( sessao_id )

);

CREATE TABLE RESERVAS(

cliente_id INTEGER NOT NULL,

sessao_id INTEGER NOT NULL,

bilhete_tipo CHAR NOT NULL,

quantidade SMALLINT NOT NULL,

CONSTRAINT PK_RESERVAS PRIMARY KEY( cliente_id,sessao_id,bilhete_tipo ),

CONSTRAINT FK_sessao_id FOREIGN KEY( sessao_id ) REFERENCES

SESSAO( sessao_id ),

CONSTRAINT CK_bilhete_tipo CHECK( bilhete_tipo IN('N','E','R','C') ),

CONSTRAINT CK_quantidade CHECK( quantidade >=1 )

);

CREATE TABLE VENDAS(

cliente_id INTEGER NOT NULL,

sala_id INTEGER NOT NULL,

filme_id INTEGER NOT NULL,

bilhete_tipo CHAR NOT NULL,

quantidade SMALLINT NOT NULL,

venda_valor MONEY NOT NULL,

venda_data DATE NOT NULL DEFAULT current_date,

venda_hora TIME NOT NULL DEFAULT current_time,

CONSTRAINT PK_VENDAS PRIMARY KEY( cliente_id, filme_id,

venda_data,

bilhete_tipo ),

CONSTRAINT FK_sala_id FOREIGN KEY(sala_id) REFERENCES SALAS( sala_id ),

CONSTRAINT FK_cliente_id FOREIGN KEY( cliente_id ) REFERENCES

CLIENTES( cliente_id ),

CONSTRAINT FK_filme_id FOREIGN KEY( filme_id ) REFERENCES

FILMES( filme_id ),

CONSTRAINT CK_bilhete_tipo CHECK( bilhete_tipo IN('N','E','R','C') ),

CONSTRAINT CK_quantidade CHECK( quantidade >=1 ),

CONSTRAINT CK_venda_valor CHECK( venda_valor > 0.0 )

);