You are on page 1of 3
Criando UDFs no Firebird Aprenda a criar UDFs para o Firebird Por Carlos Henrique Cantu UDEs permitem ampliar 0 mimero de fungoes que podem ser usadas dentro do banco de dados, mas UDF's codificadas incorretamente podem até mesmo derrubar o servidor, por isso é importante saber como cri-las da forma correta. User Defined Functions, ou UDFs, é a forma que o Firebird usa para que rotinas e comandos SQL executados no servidor chamem rotinas externas, podendo passar e recuperar pardmetros. Para criar UDFs, precisamos basicamente de uma linguagem que gere bibliotecas compartithadas, conhecidas por dll no Windows, Shared Objects (.s0) no Linux, ou .s! no HP-UX, Os exemplos desse capitulo utilizam a linguagem Pascal do Delphi para criar as UDFs. Para o correto funcionamento de uma UDF, é necessario seguir algumas regras garantindo que o Firebird e a biblioteca “falem” a mesma lingua. No restante do artigo, mostrarei como implementar corretamente fungdes para manipulacao de datas, strings, nimeros e blobs. UDFs podem ser utlizadas para implementar novos charsets e collations, O Firebird reconhece UDFS nomeadas seguindo regras especificas para esse objetivo (USER_CHARSET xxx e USER_TRANSLATE_xxx). Esse assunto vai além dos objetivos desse artigo. Regras gerais para criagdo de UDFs A cctiagdo de uma biblioteca de fungdes é geralmente uma tarefa simples, mas que requer certa dose de atengao. UDFs podem receber até dez parametros de entrada (nove, caso ela retorne um blob), de qualquer tipo de dados suportado pelo Firebird (arrays so suportados a partir do FB 2.0), passados sempre por referéncia; e devem retomar um iinico tipo de dado, natural da linguagem C, Nao & possivel, por exemplo, retomnar um record ou qualquer tipo de estrutura, nem mesmo array's Os passos necessirios para a criaco de uma biblioteca de fungdes sao: ‘© Codificar a biblioteca utilizando uma linguagem de programacao capaz de gerar bibliotecas compartilhadas; Compilar o cédigo gerando a biblioteca (.dll, so, etc.); Copiar a biblioteca gerada para o diretorio especifico de UDF’ no Firebird (o padrdo é o subdiret6rio UDF do Firebird); © Declarar as fungdes que serio utilizadas, em seus respectivos bancos de dados; Note que uma tinica biblioteca poder conter infimeras fungdes (UDFs). S6 é necessirio registrar no banco de dados as fungdes que sero utilizadas por ele, Sendo assim, podemos ter um banco de dados com apenas duas fungdes registradas, onde, no entanto, a biblioteca onde essas fungdes residem possui outras inimeras fungdes. A linguagem utilizada para escrever a UDF deve saber tratar ¢ manipular os tipos de dados recebidos nos pardmetros. Geralmente 0s parametros so mapeados em tipos de dados compativeis coma linguagem escolhida, A tabela 1 mostra uma tabela de conversiio de alguns tipos da linguagem C para o Object Pascal. Pascal__C eit) Real Float 2,9*10(-39) a 1,7*1038) (6d 41038) a 3,4°10(38) (6 digits significativos) (c) Double | Double 5#10(-324) a 1,7*10(324) (15-16 digitos significativos) (pascal) 1,7410(-308) a 1,710308) (10-15 digitos significativos) (c) Extended | Long double | 3.4*10(-4932) a 1,1*10(4932) (19-20 digitos significativos) (pascal) a 1,7#10(-308) a 1,710(308) (8 digitos significativos) (c) Shortint_| Char =128 +127 Integer | Int 32.768 a 432.767 Longint_| Long 147 483.648 a 12,147. 483.687 gitos significativos) (pascal) ‘Todas as UDFs devem ser escritas definindo-se o tipo de chamada usado pela linguagem C. No Delphi, isso € feito usando a cliusula cdeel, sendo também necessério listar as fungSes na secéo exports. O pardmetro de retomno pode ser passado por referéncia (0 padrao), ou por valor, sendo que nesse iiltimo caso devemos usar a cléusula BY VALUE, durante a declaragao da UDF, ‘A alocagao dinamica de memoria pela UDF para armazenar o valor de retomo da funcao deve sempre ser feita através da fungo ib_util_malloc, e conseqiente uso da cléusula FREE_IT. Isso é necessario para que © Firebird consiga liberar a memoria alocada, Thread safe e Strings Pelo fato do Firebird utilizar varias threads ou processos para atender as conexdes, devemos tomar extremo cuidado na hora de escrevermos UDFs, especialmente as que retornam sfrings ou fazem alocagao de meméria dindmica, garantindo que elas sejam thread safe Os seguintes cuidados devem ser tomados para garantir que uma UDF seja thread safe: ‘* Nao utilizar variiveis estiticas globais nas UDFs. Se o fizermos, devemos garantir que apenas um usuario sara a UDF em um mesmo momento, Caso ela retorne um ponteiro para uma va ca, nfo devemos usar FREE_IT na sua declaragao: ‘© Sempre que a UDF necessitar de alocar meméria para retornar um resultado, deve fazé-lo através da fungao ib util malloc: © A. meméria alocada dinamicamente para o retorno da fungio deve ser liberada adequadamente, especificando a clausula FREE_IT durante a declaragao da UDF no BD © Utilizar shreadvars e chamadas diretas a API do Windows. Nao discutiremos esse método nesse artigo, e seu uso é atualmente desaconselhado ‘A fungao ib_util_malloc pode ser encontrada no arquivo ib_util.dll (Windows) ou ib_utilso (Linux), que acompanha por padréo a instalacdo do Firebird. Na pasta include do diretorio do Firebird, encontramos 0 arquivo ib_util pas, que contém a declaragao em pascal da fungo ib_util_malloc. Essas sfio as formas oficialmente documentadas para se criar uma UDF thread safe. No entanto, existe uma outra opeao, que julgo mais elegante, onde o préprio servidor aloca a meméria necessiria para armazenar 0 retorno da funcio, ficando também responsavel pela liberagio do espaco alocado! Para que isso acontega, é necessiria uma construgdo especial da declaragao da UDF no banco, listando o parémetro de retomo na lista de parimetros da UDF, e especificando a cléusula RETURNS PARAMETER n, onde n é a posicao do parametro de retorno na lista de parametros declarados. Até entdo, esse método era conhecido e utilizado somente em fungdes que retomavam blobs. Note que, apesar de nao estar oficialmente documentado, uso esse método ha anos sem qualquer problema 3 © metodo de retomo através do RETURNS PARAMETER n pode ser perigoso, caso no se observe alguns detalhes: Se 0 parametro de retorno foi dectarado no banco como um estring(100), 0 Firebird alocara 101 bytes (0 byte ‘extra 6 para o null terminator), no entanto, dentro da fungo (escrita por exemplo, em Delphi) no ha como | CSTRING (int) [CHARACTER SET ‘BY SCALAR ARRAY | NULL} || 1 [RETURNS (“ | iG" (int) _ (CHARACTER SET charset_nai | BY DESCRIPTOR)" [FREE IT] | RETURNS PARAMETER sn") - ENTRY POTN? ‘entryname' MODULE NAME ‘modulename' ; ‘rset_name]) [(BY DESCRIPTOR BY VALUE Onde: Name — nome da funco dentro do banco de dados; Datatype — Tipo de dado referente ao parametro de entrada ou saida; CSTRING (int) — Determina que a fungao receba uma string null-terminated de tamanho “int” — © Firebird faz a conversio necessiria para compatibilizar um char ou varchar com o estring: Character Set — Charset utilizado no parametro do tipo “string” BY DESCRIPTOR ~ Indica que usaremos descritores para passagem de parametros; BY SCALAR ARRAY — Disponivel a partir do Firebird 2.0, permite a passagem de parametros do tipo array;

You might also like