Neste artigo apresento algumas novidades da versão 2.x do Firebird, esta versão traz uma série de melhorias que aumentam de forma significativa a performance, segurança e suporte a diversos idiomas e características de línguas e outras novas funcionalidades da linguagem SQL.
Entre as novidades o FB 2.0 continua ampliando a confiabilidade e o suporte ao SQL standard, como suporte as Tabelas Derivadas, Sequences e Cursores em Stored Procedures. Entre os novos recursos, merecem destaque o Execute Block, Variáveis Contextuais e Índices com expressões.
Execute Block permite que uma aplicação passe ao servidor Firebird múltiplos comandos SQL em uma única chamada, incluindo instruções disponíveis em PSQL.
Variáveis Contextuais pré-definidas permitem recuperar diversas informações sobre a conexão e as transações, também são possíveis definir variáveis personalizadas para descrever diferentes estados da aplicação.
Índices com expressões permitem indexar o resultado de expressões. A nova estrutura dos índices são algumas das mudanças mais importantes, nas versões anteriores do FB, a performance de um índice degradava quando o mesmo possuía muitos valores duplicados, a versão 2.0 eliminou completamente este problema, e reduziu o uso da CPU nas buscas indexadas em páginas mais extensas. A rotina de compactação dos índices também foi melhorada, tornando os novos índices menores e mais rápidos que os anteriores.
Incompatibilidades entre o FB 1.x e a versão 2.0
As senhas dos usuários são geradas através de um novo algoritmo de criptografia. Para que as senhas antigas sejam reconhecidas, é necessário ativar o parâmetro LegacyHash no firebird.conf;
Acesso remoto ao servidor, mesmo pelo root, não será aceito sem que um usuário/senha seja informado;
Novas palavras reservadas foram introduzidas nessa versão, e poderão invalidar SQLs e bancos já existentes, são elas: BIT_LENGTH, BOTH, CHAR_LENGTH, CHARACTER LENGTH, CLOSE, CROSS, FETCH, LEADING, LOWER, OCTET LENGTH, OPEN, ROWS, TRAILING, TRIM, USING;
Referenciar campos de tabelas usando o nome da tabela ou o alias definido é mutuamente exclusivo;
Views que tenham triggers definidos não geram mais instruções de manipulação de dados diretamente nas tabelas;
A variável CURRENT_TIMESTAMP retorna agora as informações de milisegundos, caso não precise dessa informação, indique precisão zero usando CURRENT_TIMESTAMP(0), emulando assim o comportamento da versão 1.5x do FB.
Segurança
No que diz respeito a ataques de hackers ou programas “maliciosos”, o SYSDBA continua podendo abrir qualquer banco de dados, melhorias nesse sentido estarão presentes no FB 2.5 ou 3.0;
O FB 2.0 não permite conexões remotas ao arquivo security2.fdb (responsável por armazenar as informações dos usuários e senhas), a manipulação de usuários agora só pode ser feita através do servidor, via API de serviços;
O FB não armazena a senha cadastrada pelo usuário. Ao invés disso, é armazenada uma chave hash gerada com base na senha informada. A senha pode ter até 32 caracteres, mas apenas os oito primeiros bytes são considerados, o restante é ignorado. O Algoritmo escolhido para gerar as novas chaves no FB 2.0 é o SHA1. Com ele, as chaves hash de dois usuários que usaram a mesma senha nunca serão iguais. Além disso, mesmo que um usuário execute um processo de alteração de senha, mantendo, no entanto, a mesma senha existente, a chave hash gerada será diferente da anterior, dificultando o trabalho de possíveis penetras que estejam tentando “adivinhar” as senhas observando as chaves geradas;
O Firebird 2.0 SuperServer monitora o número de tentativas de conexões feitas por um determinado usuário ou IP. Caso detecte que um ataque de força bruta está em andamento, o servidor automaticamente passará a recusar os pedidos de conexão daquele usuário ou IP, por alguns segundos, esse mecanismo de defesa está ativo automaticamente em qualquer servidor FB 2.0 SuperServer.
Foram feitas mudanças para resolver o problema de buffer overflow no FB 1.5 que ocorriam em diversas situações: dados inválidos nas chamadas a API, ambiente de utilização problemático, nomes de arquivos “incorretos” e bugs do próprio FB. Uma das mudanças foi a criação de um objeto em C++ responsável pela manipulação de strings, sendo que o limite máximo de tamanho dos strings ficou definido em 64k;
Protocolos de Conexão
Além do NetBEUI e TCP/IP, há o protocolo LOCAL que existe hoje em duas versões, no FB 2.0 traz uma nova implementação do protocolo local, chamado XNET, que é uma customização do IPC, onde a memória é compartilhada de forma sincronizada entre processos, sem a necessidade de utilização de mensagens do Windows para indicar requisições de conexões. O XNET foi criado para solucionar alguns problemas do antigo protocolo, um deles era o fato do IPC serializar as tarefas, ou seja, um comando só era processado após o término da execução do comando anterior.
Comandos SQL
Agora é possível forçar a definição do tipo de dado de um parâmetro através do CAST, por exemplo: select * from clientes where nome=cast(:nome as varchar(50));
Não é mais permitido referenciar uma mesma tabela pelo seu apelido e pelo seu nome, em uma mesma query, no comando a seguir gera um erro: select c.cod_cliente, clientes.nome from clientes c;
A versão 2.0 do FB disponibiliza a sintaxe para definição de crossjoins. Um crossjoin retorna o produto cartesiano formado pelos registros das tabelas relacionadas, até o FB 1.5, a única maneira de se fazer um crossjoin era utilizar a sintaxe de junção exclusiva SQL89, onde as tabelas eram listadas na cláusula FROM, por exemplo: select t1.campoA, t2.campoB from tabela1 t1, tabela2 t2; O uso dessa sintaxe não é recomendada, pois dificulta o trabalho do otimizador do FB.
Agora é permitido usar os apelidos definidos para os campos, nas cláusulas group by ou order by, por exemplo: select c.cidade as cid, count(*) as total from clientes c group by cid order by total; O group by aceita como critério de agrupamento, expressões/fórmulas arbitrárias.
Nas consultas que utilizam UNION o FB 2.0 tenta compatibilizar automaticamente os tipos de dados retornados pelos campos equivalentes nos diversos selects que estão sendo unidos, dispensando-se na maioria das vezes o uso do CAST.
Para se adequar ao padrão SQL 99, o FB 2.0 permite o uso da cláusula UNION DISTINCT como sinônimo da cláusula UNION.
Unions poderão ser utilizadas em subqueries que façam parte de pesquisas ALL, ANY e IN, ou mesmo em comandos de inserção que utilizem selects como fonte de dados a ser inserida.
O FB trabalha com um esquema otimista de controle de concorrência. Isso significa que ele presume que a possibilidade de várias transações realizarem uma alteração em um mesmo registro ao mesmo tempo é remota. A cláusula with lock permite fazer com que o FB trave os registros lidos pelo select, fazendo com que somente a transação que executou o select possa alterar o conteúdo desses registros, por exemplo: select first 10 * from clientes order by cod_cliente with lock; Caso uma segunda transação execute o mesmo comando, enquanto a primeira transação estiver ativa, apresentará erro (Unsuccessful execution caused by system error that does not preclude successful execution of subsequent statement, lock conflit on no wait transaction).
O recurso de Tabelas Derivadas está previsto no padrão SQL 200x e agora é suportado no FB 2.0, através delas, podemos utilizar um select como fonte de dados para outro select, além disso, também podem ser aninhadas, utilizadas em joins com outras tabelas ou views e conter funções de agregação.
ROWS é a sintaxe suportada pelo último SQL standard, a fim de limitar o número de registros retornados. No FB, ela age de forma semelhante ao FIRST, com a vantagem de poder ser usada em UNIONS, SubQueries e updates/deletes, exemplo: select * from clientes order by nome rows 10; ou select * from clientes order by nome rows 11 to 20;
O FB traz um novo predicado de equivalência, o DISTINCT. Por exemplo: select * from clientes where cidade <> ‘SUZANO’; o comando anterior não trará os registros cujo campo cidade estiver nulo, para trazer os registros com cidade diferente de ‘SUZANO’, incluindo aqueles cuja cidade não foi especificada, devemos usar o distinct: select * from clientes where cidade is distinct from ‘SUZANO’;
O FB 2.0 estendeu a funcionalidade do INSERT através da cláusula RETURNING. Ela permite retornar um resultset contento os valores que foram inseridos pelo insert.
Charsets e Collations
Estão disponíveis no FB 2.0 as collations case-insensitive e accent-insentitive. Com elas, as ordenações e comparações entre caracteres não levará em conta a caixa dos mesmos, ou seja, se é maiúsculo ou minúsculo, e também desprezará os acentos, sendo assim José=Jose=JOSE=JOSÉ=Jose=JoSé, etc;
Até o FB 1.5, a chave de um índice podia ter o tamanho máximo de 252 bytes. No FB 2.0, o limite de 252 bytes foi ampliado, agora o limite é definido aproximadamente em ¼ do tamanho da página de dados do banco. Sendo assim, em um DB com páginas de 4K, o limite para a chave de um índice será de 1024 bytes.
Até o FB 1.x, não era possível definir uma collation para um campo blob. O FB 2.0 permite essa definição, mas para isso o blob em questão deve ser do subtipo “text”.
Funções Internas
Agora é possível definir o valor de inicio e tamanho através de variáveis, parâmetros, resultados de expressões ou até chamadas a udfs. Antes, o FB permitia somente o uso de constantes, por exemplo: select first 5 substring(c.nome from 1 for 10), nome from clientes c;
O FB 2.0 trouxe alterações na função UPPER. Anteriormente, ela era incapaz de converter caracteres especiais, como a cedilha ou letras acentuadas, em suas formas maiúsculas.
Funções internas disponíveis a partir da versão 2.0: LOWER, TRIM, BIT_LENGTH, CHAR_LENGTH/CHARACTER LENGTH, OCTEC_LENGTH.
Procedures e Triggers
O FB 2.0 permite a execução de blocos de código PSQL através do novo comando EXECUTE BLOCK. Ele permite a execução de rotinas via DSQL, sem a necessidade de criação de uma procedure. As rotinas (ou blocos de comandos) podem inclusive utilizar parâmetros de entrada e de saída, ou seja, podem receber e retornar valores. Em outras palavras, o EXECUTE BLOCK pode fazer qualquer tarefa que uma procedure executável poderia fazer, menos chamar a si mesmo (recursividade).
Em stored procedures, o FB 2.0 permite a definição de valores default para os argumentos de entrada, no entanto, os argumentos que possuírem default definidos deverão estar posicionados no final na lista de argumentos, ou um erro será gerado.
O comando RECREATE TRIGGER cria um trigger caso ele não exista, ou caso já exista, substitui o mesmo com a nova definição. Os diversos comandos RECREATE suportados pelo FB, quando utilizados para recriar um objeto já existente, removem também qualquer definição de permissões que tenham sido previamente definidas para o objeto. Imagina o RECREATE como sendo uma junção de DROP + CREATE. O ALTER não remove as permissões.
Dois novos comandos para a criação e alteração de exceções estão disponíveis no FB 2.0: RECREATE EXCEPTION e CREATE OR ALTER EXCEPTION.
O FB 2.0 permite a manipulação explicita de cursores em PSQL. É possível nomear o cursor aberto pelo FOR SELECT, e assim utilizar o que chamamos de update ou delete posicionado. Para isso, usamos no comando de update ou delete a cláusula WHERE CURRENT OF para indicar qual linha apontada pelo cursor que deverá ser manipulada pelo comando. O exemplo a seguir demonstra como seria a utilização de um update posicionado. Nele, um cursor é aberto por um for select e nomeado como “meu_cursor”. Depois, dentro do loop gerado, um update é utilizado para atualizar o valor do campo valor3 com o resultado da multiplicação de :var1 e :var2. Esse update age diretamente na linha atual determinada pelo ponteiro do cursor meu_cursor.
for select t.valor1, t.valor2
from tabela t
for update
into :var1, :var2
as cursor meu_cursor
do
begin
update tabela
set valor3 := :var1 * :var2
where current of meu_cursor;
end
A detecção de invariantes foi bastante melhorada no FB 2.0. Quando uma procedure ou trigger é compilada, é transformada em uma linguagem interna chamada BLR. Durante a compilação, o FB tenta detectar expressões (que podem ser inclusive subqueries) que são independentes do contexto a qual estão associadas. Exemplo: select * from rdb$relations where rdb$relation_id < (select rdb$relation_id from rdb$database); O FB 2.0 detecta que a subquery determinada por “select rdb$relation_id from rdb$database” não depende da query principal, e portanto, o fetch dos dados da subquery é executado apenas uma única vez, ao invés de executá-la para cada registro retornado pela query principal.
O FB 2.0 tem a capacidade de reportar um stack trace quando um erro é gerado em uma procedure ou trigger. Assim, fica mais fácil identificar qual foi a seqüência de procedures executadas pelo servidor até chegar ao erro, facilitando o processo de debug.
Bibliografia
Firebird 2.0 - O Banco de Dados do Novo Milênio, Carlos H. Cantu, Editora Ciência Moderna.
Release Notes (v2.0)