Artigo "Novidades do Firebird 2.5": Expressões, literais e funções

Posted on in artigo, firebird

Na parte de expressões SQL, o Firebird 2.5 acrescenta melhorias e novidades. A função agregada LIST (presente desde a versão 2.1) agora aceita qualquer expressão em seu segundo parâmetro, onde o desenvolvedor pode especificar a string de separação dos elementos retornados. Até a versão 2.1.3 era possível apenas o uso de strings constantes nesse parâmetro. Nota: a versão 2.1.4 também deve ser liberada com esta novidade. A listagem 10 mostra a utilidade deste parâmetro, principalmente no desenvolvimento de relatórios, agrupando várias mensagens referentes a um documento em um mesmo registro e mostrando cada mensagem em uma linha.

Listagem 10. Usando a função LIST com expressões no segundo parâmetro.

SELECT LIST(MENSAGEM, ASCII_CHAR(13) || ASCII_CHAR(10))
 FROM MENSAGENS_DOCUMENTO WHERE DOCUMENTO = 10;

Referente a parâmetros de comandos SQL, o Firebird suporta apenas o uso de parâmetros anônimos com o símbolo “?” (ponto de interrogação). Algumas bibliotecas (inclusive de Delphi) aceitam o uso de parâmetros nomeados usando “:” (dois pontos). Quando o desenvolvedor tentava escrever queries usando o padrão [WHERE :CODIGO IS NULL OR CODIGO = :CODIGO] para trazer todos os registros quando o parâmetro não fosse informado, o Firebird retornava um erro. Isto porque este comando é traduzido pelas bibliotecas para [WHERE ? IS NULL OR CODIGO = ?] e o Firebird não aceitava o uso de parâmetro com o predicado IS NULL, pois o tipo do parâmetro era considerado como desconhecido. Na versão 2.5 foi adicionado à API o tipo SQL_NULL. As bibliotecas de acesso precisam entender este novo tipo e apenas passar se o valor do parâmetro é ou não NULL, permitindo assim o uso deste padrão de comando.

Outra novidade referente a expressões é o novo predicado SIMILAR TO. Esta expressão é usada para fazer comparações usando expressões regulares de acordo com o padrão SQL. A listagem 11 mostra uma verificação de números de telefones cadastrados fora do padrão (NN) NNNN-NNNN. O caractere de escape funciona exatamente como no comando LIKE, considerando o próximo caractere como um valor literal, ao invés de usá-lo como um operador.

Listagem 11. Verificando números de telefones cadastrados fora do padrão.
SELECT * FROM PESSOAS
 WHERE TELEFONE NOT SIMILAR TO
   '([0-9]{2}) [0-9]{4}-[0-9]{4}' escape ''
A tabela 1 mostra os operadores permitidos em expressões regulares e a tabela 2 mostra as classes de caracteres que podem ser usadas com o operador [[:CLASSE:]].


Operador
Descrição
X{2}
Duas ocorrências de X.
X{2,}
Duas ou mais ocorrências de X.
X{4,6}
De quatro a seis ocorrências de X.
X?
Zero ou uma ocorrência de X.
X*
Zero ou mais ocorrências de X.
X+
Uma ou mais ocorrências de X.
X|Y
X ou Y.
_
Qualquer caractere - como no LIKE.
%
Qualquer sequência de caracteres - como no LIKE.
(X)
Agrupa X para ser tratado pelo operador subsequente.
[XYZ]
Qualquer caractere igual a X, Y ou Z.
[^XYZ]
Qualquer caractere diferente de X, Y ou Z.
[X-Z]
Qualquer caractere entre X e Z.
[[:CLASSE:]]
Qualquer caractere de uma certa classe, conforme tabela 2.
Tabela 1. Operadores de expressões regulares.


Classe
Descrição
ALPHA
Qualquer caractere entre A e Z.
UPPER
Qualquer caractere maiúsculo.
LOWER
Qualquer caractere minúsculo.
DIGIT
Dígitos de 0 a 9.
SPACE
Espaço: caractere (ASCII_CHAR) 32.
WHITESPACE
Todo tipo de espaço: caracteres 9, 10, 11, 12, 13 e 32.
Tabela 2. Classes de caracteres para expressões regulares.


O Firebird 2.5 também suporta novos formatos de valores literais (constantes) para números e strings. Agora é possível escrever números inteiros no formato hexadecimal, usando o prefixo 0x. Quando o número possuir até 8 dígitos hexadecimais após o 0x, como em 0xFFFFFFFF, o número adquire o tipo INTEGER (32 bits com sinal). Quando possuir mais de 8 dígitos, como em 0x0FFFFFFFF, o tipo adquirido é o BIGINT (64 bits com sinal). Desta forma, estas duas constantes retornam valores diferentes, pois os números negativos são armazenados na notação complemento de dois [1].

Além de números, também é possível a criação de strings binárias (character set OCTETS) usando a notação hexadecimal x''. Em ambos os casos a letra X pode ser escrita em maiúscula ou minúscula. A listagem 12 mostra o uso destes novos tipos de valores literais. Note que cada par de caracteres hexadecimais se transforma em um byte na string resultante.

Listagem 12. Novos formatos de expressões literais.
SELECT 0xDEADBEEF, x'DEADBEEF' FROM RDB$DATABASE;
SELECT OCTET_LENGTH(x'DEADBEEF') FROM RDB$DATABASE;
-- Resultado de OCTET_LENGTH: 4, e não 8

A nova função BIN_NOT se junta ao grupo de funções binárias adicionadas na versão 2.1, BIN_AND e BIN_OR. Junto com as constantes hexadecimais, essa família de funções facilita o uso de máscaras de valores binários gravados em um único campo, técnica comumente utilizada em aplicações e agora facilitada no banco de dados. A listagem 13 mostra o uso da função BIN_NOT, que inverte todos os bits de um valor inteiro.

Listagem 13. Exemplo de uso da função BIN_NOT.
SELECT BIN_NOT(0xFFFFFFFF) FROM RDB$DATABASE;  -– Resultado: 0

Na versão 2.1 foi adicionada a função GEN_UUID, que retorna um UUID [2] como um valor do tipo CHAR(16) CHARACTER SET OCTETS. Este tipo de retorno foi escolhido pois é a representação mais compacta para ser usado em índices, porém requer suporte das aplicações que desejam exibir estes UUIDs aos usuários. Na nova versão foram adicionadas funções para conversão entre as representações binária e texto [CHAR(36) CHARACTER SET ASCII] de UUIDs, as funções CHAR_TO_UUID e UUID_TO_CHAR. O formato texto de UUID aceito e retornado por estas funções é 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'. A listagem 14 mostra o uso destas funções.


Listagem 14. Exemplo de uso das funções UUID_TO_CHAR e CHAR_TO_UUID.
SELECT UUID_TO_CHAR(UUID), DESCRICAO FROM OBJETOS

 WHERE UUID = CHAR_TO_UUID(?);


Notas

[1] - Complemento de dois é a notação mais comum usada para representar números inteiros com sinal em sistemas computacionais. Nesta notação o bit mais significativo de um número positivo ou do número zero é representado como 0. Os números negativos são representados com os bits invertidos e somado o valor 1. Desta forma, em um número de 32 bits o 1 é representado com trinta e um bits 0 seguido por um bit 1, enquanto que o número -1 é representado por trinta e dois bits 1.

[2] - UUID (Universally Unique Identifier) é uma sequência aleatória de 16 bytes que, independente do local e momento que seja gerada, é única. Uma das utilidades dos UUIDs é criar chaves em sistemas distribuídos, como filiais de uma empresa com bancos de dados separados e que tenham os dados agregados em um banco principal através de replicação.