segunda-feira, 23 de janeiro de 2012

Firebird - Trabalhando com UDFs.



O que é uma UDF?

Nesta matéria abordarei um assunto muito interessante em se tratando do Banco de Dados Firebird, as denominadas UDFs, para quem não conhece UDF(User Defined Function) são funções definidas pelo usuário, uma função escrita em qualquer linguagem de programação capaz de gerar uma biblioteca compartilhada. No Windows são conhecidas pelo nome de Dynamic Link Libraries (as conhecidas DLLs).

Utilizando as UDFs

De primeiro momento estarei demonstrando como utilizar as UDFs vindas do próprio Interbase/Firebird e estarei listando as principais, consequentemente as que são mais utilizadas e em seguida estarei apresentando algumas Bibliotecas de terceiros. As UDFs são armazenadas por padrão no seguinte diretório C:\Arquivos de programas\Firebird\Firebird_x\UDF. Quando o firebird é instalado, como padrão teremos quatro arquivos, um chamado ib_udf.dll outro fbudf.dll (as UDFs) e mais dois denominados ib_udf e fbudf (onde ficam as declarações das respectivas UDFs). 

Primeiramente clique com o botão direito sobre o ib_udf e abra com o bloco de notas. Percebam que aparecerão várias declarações de UDFs que poderão ser utilizadas pelo Firebird.

Iremos utilizar a conhecida ferramenta para gerenciamento de Banco de Dados chamada Ibexpert (caso não possuam,  ela possui uma versão Freeware que poderá ser baixada através do site www.ibexpert.com). Abra o Ibexpert e registre o banco de dados que já vem com o Firebird, a partir do menu Database selecione a opção Register Database e configure o banco de dados EMPLOYEE.FDB como na figura 01.  (Por padrão esta Base de Dados é encontrada no seguinte diretório
C:\Arquivos de programas\Firebird\Firebird_x\examples\EMPLOYEE.FDB onde x é a versão do Firebird utilizado).
            
Figura 01. Registrando o Banco Employee.
Com o Ibexpert aberto clique no ícone   (New Sql Editor) para podermos declarar as nossas UDFs. Neste momento irei utilizar uma UDF explicando como deve ser declarada seguida de um exemplo.

Abra o arquivo ib_udf e copie o seguinte código:

DECLARE EXTERNAL FUNCTION abs
      DOUBLE PRECISION
      RETURNS DOUBLE PRECISION BY VALUE
      ENTRY_POINT 'IB_UDF_abs' MODULE_NAME 'ib_udf';      

Cole no ibexpert em seguida pressione F9 ou clique em Execute e por final dê um Commit Transaction. Vejam que foi adicionado ao Banco de Dados uma UDF chamada ABS (Figura 02).

Figura 02. UDF Criada no Banco Employee.

Esta UDF recebe como parâmetro um valor numérico(double precision) e irá retornar um número absoluto (positivo). Iremos simular uma situação.

Temos a seguinte tabela:

Codigo
Valor
1
-20
2
-30
3
-40

Select codigo,Abs(valor) from tabela Este select irá retornar valores positivos, ou seja, teremos como resultset os seguintes registros.
 
Codigo
Valor
1
20
2
30
3
40

Conhecendo as UDFs

A partir daqui irei apresentar algumas das principais UDFs deste arquivo ib_udf seguida de uma breve explicação sobre elas.

a s c i i _ c h a r

DECLARE EXTERNAL FUNCTION ascii_char
      INTEGER
      RETURNS CSTRING(1) FREE_IT
      ENTRY_POINT 'IB_UDF_ascii_char' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetro um valor inteiro e retorna seu valor em código Ascii.
Exemplo: select ascii_char(cust_no) from sales

a s c i i _ v a l

DECLARE EXTERNAL FUNCTION ascii_val
    CHAR(1)
    RETURNS INTEGER BY VALUE
    ENTRY_POINT 'IB_UDF_ascii_val' MODULE_NAME 'ib_udf';

Utlização: Recebe como parâmetro um valor ascii e retorna um valor inteiro correspondente.
Exemplo: select ascii_val(campo) from tabela

div

DECLARE EXTERNAL FUNCTION div
      INTEGER, INTEGER
      RETURNS DOUBLE PRECISION BY VALUE
      ENTRY_POINT 'IB_UDF_div' MODULE_NAME 'ib_udf';

Utilização: Esta UDF recebe como parâmetros dois valores inteiros e retorna a parte inteira da divisão do primeiro pelo segundo.
Exemplo: select div(cust_no,qtyordered) from sales

floor

DECLARE EXTERNAL FUNCTION floor
      DOUBLE PRECISION
      RETURNS DOUBLE PRECISION BY VALUE
      ENTRY_POINT 'IB_UDF_floor' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetro um valor do tipo double precision e retorna a parte inteira deste parâmetro
Exemplo: select floor(new_salary) from salary_history

log

DECLARE EXTERNAL FUNCTION log
      DOUBLE PRECISION, DOUBLE PRECISION
      RETURNS DOUBLE PRECISION BY VALUE
      ENTRY_POINT 'IB_UDF_log' MODULE_NAME 'ib_udf';

Utilização: Útil para operações matemáticas, esta UDF recebe como parâmetro dois valores(x e y) e tem como retorno o logaritmo base x por y
Exemplo: select log(x,y) from tabela

lower

DECLARE EXTERNAL FUNCTION lower
      CSTRING(255)
      RETURNS CSTRING(255) FREE_IT
      ENTRY_POINT 'IB_UDF_lower' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetro um campo tipo string de até 255 caracteres e como retorno um valor com caixa baixa
Exemplo: select lower(last_name) from employee

lpad

DECLARE EXTERNAL FUNCTION lpad
      CSTRING(255), INTEGER, CSTRING(1)
      RETURNS CSTRING(255) FREE_IT
      ENTRY_POINT 'IB_UDF_lpad' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetros uma string de até 255 caracteres, um inteiro e um caracter.
Exemplo: select lpad(first_name,20,' ') from employee
Entendendo: No exemplo acima, insere espaços em branco à esquerda do campo fisrt_name até obter um total de 20 caracteres (contando os espaços em branco).

ltrim

DECLARE EXTERNAL FUNCTION ltrim
      CSTRING(255)
      RETURNS CSTRING(255) FREE_IT
      ENTRY_POINT 'IB_UDF_ltrim' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetro uma string de até 255 caracteres e tem como retorno esta mesma string com remoção dos espaços em branco da esquerda.
Exemplo: select ltrim(first_name) from employee

mod

DECLARE EXTERNAL FUNCTION mod
      INTEGER, INTEGER
      RETURNS DOUBLE PRECISION BY VALUE
      ENTRY_POINT 'IB_UDF_mod' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetros de entrada dois valores inteiros e como retorno o resto da divisão do primeiro parâmetro pelo segundo.
Exemplo: select mod(campo1,campo2) from tabela

substr

DECLARE EXTERNAL FUNCTION substr
      CSTRING(255), SMALLINT, SMALLINT
      RETURNS CSTRING(255) FREE_IT
      ENTRY_POINT 'IB_UDF_substr' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetro uma string de até 255 caracteres e dois valores inteiros (o primeiro indica a posição e o segundo a quantidade de caracteres). Útil para pegar pedaços de strings.
Exemplo: select substr(last_name,1,3) from employee .Percebam que este select irá trazer como resultset apenas os três primeiros caracteres do campo last_name.

strlen

DECLARE EXTERNAL FUNCTION strlen
      CSTRING(32767)
      RETURNS INTEGER BY VALUE
      ENTRY_POINT 'IB_UDF_strlen' MODULE_NAME 'ib_udf';

Utilização: Recebe como parâmetro uma string e tem como retorno o tamnho desta string
Exemplo: select strlen(last_name) from employee

Neste momento iremos verificar algumas UDFs interessantes vindas do arquivo fbudf.dll. Faça o mesmo procedimento citado acima. Abra este arquivo e perceba a quantidade imensa de UDFs para serem utilizadas.

dow

declare external function dow
timestamp,
varchar(15) returns parameter 2
entry_point 'DOW' module_name 'fbudf';

Utilização: Recebe como parâmetro um campo tipo timestamp e retorna o dia da semana correspondente
Exemplo: select dow('05.07') from employee ou
    select dow('05.07.2007') from employee ou
    select dow('05.07.2007 10:25:32') from employee          

addDay

declare external function addDay
timestamp, int
returns timestamp
entry_point 'addDay' module_name 'fbudf';

Utilização: Recebe como parâmetro um campo tipo timestamp e um inteiro. Ela serve para adicionar dias a data informada.
Exemplo: select addday('06.07.2007', 20) from employee

Obs: Esta biblioteca de Funções possui várias UDFs relacionadas às datas e horas. Todas seguem a mesma linha de raciocínio, veja abaixo:

AddWeek – Adiciona Semanas a uma data.
AddMonth – Adiciona meses a uma data.
Addyear – Adiciona ano a uma data.
AddMillisecond – Adiciona milisegundos a uma hora.
AddSecond – Adiciona segundos a uma hora.
AddMinute – Adiciona minutos a uma hora.
AddHour – Adiciona horas a uma hora.

sright


declare external function sright
varchar(100) by descriptor, smallint,
varchar(100) by descriptor returns parameter 3
entry_point 'right' module_name 'fbudf';

 

Utilização: Recebe como parâmetro um campo do tipo varchar(100) e um inteiro. O segundo parâmetro determina quantos caracteres são mostrados da direita para a esquerda do campo informado no primeiro parâmetro.
Exemplo: select sright(first_name, 5) from employee

round


declare external function Round
int by descriptor, int by descriptor
returns parameter 2
entry_point 'fbround' module_name 'fbudf';

Utilização: Recebe como parâmetro um valor a ser arredondado. Ela serve para arredondar valores.
Exemplo: select round(25.99) from tabela

dPower

declare external function dPower
double precision by descriptor, double precision by descriptor,
double precision by descriptor
returns parameter 3
entry_point 'power' module_name 'fbudf';

Utilização: Recebe como parâmetro dois valores que podem ser do tipo double precision, sendo o primeiro o número a ser elevado pelo segundo. É uma UDF de exponenciação.
Exemplo: select dPower(2.36, 2.58) from tabela

Utilizando UDFs de terceiros

            Depois de conhecermos algumas UDFs vindas do próprio Firebird agora iremos adicionar algumas de terceiros no nosso Banco de Dados. Iremos utilizar uma chamada FreeUdfLib do seguinte autor Greg Deatz e alterada  por Cláudio Valderrama. Baixe esta biblioteca através do seguinte link http://www.ibphoenix.com/downloads/freeudflib.zip . Descompacte no diretório de sua escolha e copie os seguintes arquivos: ext_funcs e FreeUDFLib.dll para o diretório C:\Arquivos de programas\Firebird\Firebird_x\UDF.
            Abra o Arquivo ext_funcs e observem a quantidade enorme de UDFs. Encontraremos tratando de campos do tipo Blob, Data e Hora, Funções Matemáticas entre outras. Abaixo mostrarei algumas que considero mais interessantes.

f_blobsize


declare external function f_BlobSize
blob
returns integer by value
entry_point 'BlobSize' module_name 'FreeUDFLib.dll'; 

Utilização: Recebe como parâmetro um valor do tipo blob e retorna um inteiro.Como o próprio nome já diz, tem como retorno o tamanho do campo Blob.
Exemplo: select f_blobsize(job_requirement) from job

f_strblob


declare external function f_StrBlob
cstring(254),
blob
returns parameter 2
entry_point 'StrBlob' module_name 'FreeUDFLib.dll';

Utilização: Recebe como parâmetro uma string e tem como retorno um campo do tipo Blob.
Exemplo: select f_strblob(job_country) from job

f_AddMonth

declare external function f_AddMonth
date,
integer
returns
date /* free_it */
entry_point 'AddMonth' module_name 'FreeUDFLib.dll';

Utilização: Recebe como parâmetros: um campo do tipo Date e um valor inteiro. Adiciona meses à data informada.
Exemplo: select F_addmonth(’10.05.2007’,5) from tabela

Importante: A Biblioteca fbudf também contém UDFs para trabalhar com Datas, mas observem que o tipo de campo é timestamp. Abaixo verifiquem a quantidade de funções interessantes sobre datas, estarei dando uma breve explicação sobre elas.

f_year: Retorna o ano de uma data.
f_month: Retorna o mês de uma data.
f_AddYear: Adiciona anos a uma data.
f_AgeInDays: Verifica a quantidade de dias no intervalo entre duas datas.
f_CdowShort: retorna o dia da semana.
f_CmonthLong: retorna o mês da data informada.
f_dayofmonth: retorna o dia do mês da data informada.

f_lrTrim


declare external function f_lrTrim
      cstring(254)
      returns
      cstring(254) /* free_it */
      entry_point 'lrTrim' module_name 'FreeUDFLib.dll';

Utilização: Recebe e retorna como parâmetro uma string. Sua função é de remover espações em branco localizado na esquerda e na direita de uma string.
Exemplo: Select f_lrTrim(‘  The Club - O Maior clube de programadores  ‘) from tabela

f_propercase


declare external function f_ProperCase
      cstring(254)
returns
      cstring(254) /* free_it */
      entry_point 'ProperCase' module_name 'FreeUDFLib.dll';

Utilização: Recebe e retorna como parâmetro uma string. O retorno desta UDF será a primeira letra em maiúscula e o resto em minúscula.
Exemplo: select f_propercase('thiago') from tabela

f_ibpassword


declare external function f_IBPassword
cstring(32)
returns
cstring(32) /* free_it */
entry_point 'IBPassword' module_name 'FreeUDFLib.dll';

Utilização: Recebe como parâmetro uma string e retorna esta string criptografada.
Exemplo: Select f_IBPassword(job_country) from job

            Além desta biblioteca de Funções existem outras (também ditas open-source) que poderão ser úteis para os senhores. Eu particularmente recomendo uma chamada tbudf (Tecnobyte UDF para Firebird) do autor Daniel Pereira Guimarães que poderá ser baixada no seguinte link http://www.tecnobyte.com.br/tbudf2_2.zip. Esta biblioteca além de ser tão boa quanto a FreeUDFLib possui uma ótima documentação em português. Para utilizá-la siga os mesmos passos ditos anteriormente.

Criando uma UDF


Quando iremos criar uma UDF não podemos esquecer uma regra básica, é recomendável ser uma função simples e rápida. Significa que uma função que manipula strings, realiza cálculos matemáticos, manipula datas como nos exemplos citados acima são ótimas candidatas a virarem uma UDF.
A ferramenta para criarmos nossa UDF será o delphi 7. Abra o Delphi, vá em File/New/Other. Na aba New escolha DLL Wizard, veja figura 03.

 Figura 03. Criando uma UDF.

Logo em seguida clique em ok. Aparecerá os seguintes códigos.

library Project1;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils,
  Classes;

{$R *.res}

begin
end.

            Logo após Library iremos colocar o nome da Biblioteca. Defina como quiser, neste caso específico iremos definir como THECLUB. Em uses iremos utilizar apenas a unit chamada SysUtils e por final remova a cláusula {$R *.res} que não iremos utilizar.Veja abaixo como está ficando nosso código:

library THECLUB;

uses SysUtils;

begin
end.

Iremos construir uma UDF simples, receberá uma string como parâmetro e retornará esta mesma string com espaço em branco entre seus caracateres. Veja abaixo nossa função:

     Function ESPACO(var1:pchar): pchar; cdecl; export;
var i:integer;
    var2, var3: string;
begin
 for i:=1 to Length(var1) do
  begin
   var2 := copy(var1,i,1) + ' ';
   var3 := var3 + var2;
  end;
 result := pchar(var3);
end;

Fica uma dúvida, porque utilizar pchar ao invés de string? Para quem não sabe pchar é um ponteiro para caracter. Ponteiro é uma variável que contém um endereço de memória. Este endereço é a posição de outra variável ou de um determinado dado na memória. Tratando-se de UDFs (criação de DLLs propriamente ditas) não seria recomendável a utilização de strings como variáveis (seja de retorno ou de entrada) e sim do tipo pchar ou shortstring.
Outra dúvida, o que significa cdecl e export?

Cdecl: Significa o tipo de chamada, determina a ordem na qual os parâmetros são passados para a rotina. Ele passa os parâmetros da direita para a esquerda. Na forma cdecl o chamador é responsável por remover da pilha os parâmetros de retorno das funções.

Export: indica que a função poderá ser chamada por outros aplicativos.(No nosso caso o Firebird)

Mas não iremos nos aprofundar muito neste assunto, para quem se interessar veja nossa matéria chamada Trabalhando com Procedures e Funções encontrada no seguinte link http://www.theclub.com.br/revista/trab1203.aspx do mês de Dezembro de 2003.
 
Veja abaixo como ficou a UDF criada pelo Delphi.

library THECLUB;

uses SysUtils;

Function ESPACO(var1:pchar): pchar; cdecl; export;
var i:integer;
    var2, var3: string;
begin
 for i:=1 to Length(var1) do
  begin
   var2 := copy(var1,i,1) + ' ';
   var3 := var3 + var2;
  end;
 result := pchar(var3);
end;

  exports
  ESPACO;

begin
end.

Após construirmos nossa UDF iremos salvá-la em File/Save All em seguida compilá-la. Fiquem atentos, não podemos executar uma DLL pelo Delphi, através do RUN do Menu Run, pois não se trata de uma aplicação. Clique em Project/ Compile THECLUB.

Percebam que ao compilar nosso projeto será criado um arquivo chamado THECLUB.dll, ou seja, estamos terminando o processo de criação de nossa UDF, basta apenas declará-la no nosso Banco de Dados.

Copie o arquivo THECLUB.dll para a seguinte diretório C:\Arquivos de programas\Firebird\Firebird_x\UDF (que é o diretório onde ficam armazenadas as UDFs do Firebird e as de terceiros). Em seguida abra o Ibexpert para declararmos nossa UDF. Observe abaixo como fica:

DECLARE EXTERNAL FUNCTION UDF_ESPACO
CSTRING(32)
RETURNS CSTRING(32)
ENTRY_POINT 'ESPACO' MODULE_NAME 'TESTE.dll'

DECLARE EXTERNAL FUNCTION UDF_ESPACO: coloque o nome da UDF.

ENTRY_POINT 'ESPACO' MODULE_NAME 'TESTE.dll': ESPACO seria o nome da UDF criada no Delphi e TESTE.dll o nome da biblioteca.

Clique em Execute e logo após Commit Transaction, veja na figura 04 a nossa UDF criada com sucesso no banco de dados.



Figura 04. UDF criada no Banco de Dados.

Utilizando nossa UDF chamada udf_espaco:

select udf_espaco(last_name) as LAST_NAME from employee

Veja o resultado(Figura 05)

Figura 05. Resultado de nossa instrução SQL.

  
Conclusão

            Vimos neste artigo como se trabalhar com UDFs, desde sua definição até a criação de uma pelo Delphi. Espero que tenha ajudado e gostado da matéria. Um forte abraço e até o mês que vem.


Nenhum comentário:

Postar um comentário