O Firebird tem sido capaz de expandir os recursos da linguagem PSQL escrevendo funções externas - UDF (User Defined Functions). UDF pode ser escrito em quase qualquer linguagem de programação compilada.
O Firebird 3.0 introduziu uma arquitetura de plug-in para ampliar os recursos do Firebird. Um desses plugins é o mecanismo externo (mecanismos externos). O mecanismo UDR (Rotinas definidas pelo usuário - Rotinas definidas pelo usuário) adiciona uma camada na parte superior da interface do mecanismo FirebirdExternal.
Neste guia, mostraremos como declarar a UDR, sobre seus mecanismos internos, recursos e dar exemplos de como escrever UDR em Pascal. Além disso, alguns aspectos do uso da nova API orientada a objetos serão abordados.
Observação
Este artigo tem como objetivo ensiná-lo a escrever UDR usando o objeto Firebird API.
As funções e procedimentos escritos podem não ter aplicação prática.
Os UDRs têm as seguintes vantagens sobre o UDF herdado:
- você pode escrever não apenas funções que retornam um resultado escalar, mas também procedimentos armazenados (executáveis e seletivos), além de gatilhos;
- controle aprimorado dos parâmetros de entrada e saída. Em alguns casos (passando pelo descritor), os tipos e outras propriedades dos parâmetros de entrada não eram controlados, no entanto, era possível obter essas propriedades dentro do UDF. UDRs fornecem uma maneira mais unificada de declarar parâmetros de entrada e saída, como é o caso das funções e procedimentos regulares do PSQL;
- UDR, o contexto da conexão ou transação atual está disponível, o que permite executar
algumas manipulações com o banco de dados atual neste contexto; - A geração de erros do Firebird está disponível quando ocorrem exceções; não há necessidade de retornar um valor especial;
- Procedimentos e funções externas (UDR) podem ser agrupados em pacotes PSQL;
- O UDR pode ser gravado em qualquer linguagem de programação (opcionalmente compilada em códigos de objetos), para isso é necessário que o plug-in External Engine correspondente seja gravado. Por exemplo, existem plugins para escrever módulos externos em Java ou em qualquer uma das linguagens .NET.
Observação
A implementação atual da UDR usa um stub PSQL. Por exemplo, é usado para
verificação de parâmetros e valores de retorno para conformidade com restrições. Stub
foi usado devido à inflexibilidade para chamar diretamente funções internas. Resultados
Um teste comparando o desempenho de UDR e UDF mostra que o UDR é aproximadamente
2,5 vezes mais lento usando a função mais simples de adicionar dois argumentos como exemplo. Velocidade
UDR é aproximadamente igual à velocidade de uma função PSQL regular. Talvez no futuro isso
momento será otimizado. Em funções mais complexas, essa sobrecarga pode se tornar
imperceptível.
Além disso, em várias partes deste manual, ao usar os termos procedimento externo,
função ou gatilho, queremos dizer exatamente UDR (e não UDF).
Observação
Todos os nossos exemplos funcionam no Delphi 2009 e posterior, bem como no Free Pascal. Todos
exemplos podem ser compilados no Delphi e no Free Pascal, se
não especificado separadamente.
API do Firebird
Para escrever procedimentos, funções ou gatilhos externos em linguagens de programação compiladas, precisamos do conhecimento da nova API Firebird orientada a objetos. Este guia não inclui uma descrição completa da API do Firebird. Você pode lê-lo no diretório de documentação distribuído com o Firebird ( doc/Using_OO_API.html
).
Os plug-ins para várias linguagens de programação que contêm APIs não são distribuídos como parte da distribuição do Firebird para Windows, no entanto, você pode extraí-los dos arquivos tarbar compactados distribuídos pelo Linux (o caminho dentro do arquivo /opt/firebird/include/firebird/Firebird.pas
).
CLOOP
CLOOP - Programação Orientada a Objetos de Linguagem Cruzada. Esta ferramenta não está incluída no Firebird. Ele pode ser encontrado no código fonte https://github.com/FirebirdSQL/firebird/tree/B3_0_Release/extern/cloop . Após a montagem da ferramenta, você pode gerar uma API para sua linguagem de programação ( IdlFbInterfaces.h
ou Firebird.pas
) com base no arquivo de descrição da interface include/firebird/FirebirdInterface.idl
.
Para Object Pascal, isso é feito com o seguinte comando:
cloop FirebirdInterface.idl pascal Firebird.pas Firebird --uses SysUtils \ --interfaceFile Pascal.interface.pas \ --implementationFile Pascal.implementation.pas \ --exceptionClass FbException --prefix I \ --functionsFile fb_get_master_interface.pas
Os Pascal.interface.pas
, Pascal.implementation.pas
e fb_get_master_interface.pas
podem ser encontrados em https://github.com/FirebirdSQL/firebird/tree/B3_0_Release/src/misc/pascal .
Observação
Nesse caso, o prefixo que eu vou ser adicionado para as APIs do Firebird, pois isso é aceito no Object Pascal.
Constantes
Não há constantes isc_*
no arquivo Firebird.pas
resultante. Essas constantes para linguagens C / C ++ podem ser encontradas em https://github.com/FirebirdSQL/firebird/blob/B3_0_Release/src/include/consts_pub.h . Para obter as constantes para o idioma Pascal, usaremos o script AWK para converter a sintaxe. No Windows, você precisará instalar o Gawk for Windows ou usar o Windows Subsystem para Linux (disponível no Windows 10). Isso é feito com o seguinte comando:
awk -f Pascal.Constants.awk consts_pub.h > const.pas
O conteúdo do arquivo resultante deve ser copiado para a seção const vazia do arquivo Firebird.pas
imediatamente após a implementação. O arquivo Pascal.Constants.awk
pode ser encontrado em
https://github.com/FirebirdSQL/firebird/tree/B3_0_Release/src/misc/pascal .
Gerenciamento do tempo de vida
As interfaces Firebird não são baseadas na especificação COM, portanto, gerenciar sua vida útil é diferente.
Existem duas interfaces no Firebird que lidam com o gerenciamento do tempo de vida: IDisposable e IReferenceCounted. O último é especialmente ativo ao criar outras interfaces: o IPlugin conta os links, como muitas outras interfaces usadas pelos plug-ins. Isso inclui interfaces que descrevem como se conectar a um banco de dados, gerenciamento de transações e instruções SQL.
Sobrecarga adicional da interface com contagem de referência nem sempre é necessária. Por exemplo, o IMaster, a interface principal que chama funções disponíveis para o restante da API, tem uma vida útil ilimitada por definição. Para outras APIs, o tempo de vida é estritamente determinado pelo tempo de vida da interface pai; A interface IStatus não é
multithread. Para interfaces com vida útil limitada, é útil ter uma maneira simples de destruí-las, ou seja, a função dispose ().
Sugestão
Se você não sabe como um objeto é destruído, observe sua hierarquia, se ele tiver
interface IReferenceCounted, a contagem de referência é usada.
Para interfaces com contagem de referência, após a conclusão do trabalho com um objeto, é necessário
diminua o contador de referência chamando o método release ().
Anúncio da UDR
Os UDRs podem ser adicionados ou removidos do banco de dados usando comandos DDL, assim como você adiciona ou remove procedimentos, funções ou gatilhos regulares do PSQL. Nesse caso, em vez do corpo do gatilho, sua localização no módulo externo é indicada usando a cláusula EXTERNAL NAME.
Considere a sintaxe desta frase: será comum procedimentos, funções e gatilhos externos.
Sintaxe:
EXTERNAL NAME '<extname>' ENGINE <engine> [AS <extbody>] <extname> ::= '<module name>!<routine name>[!<misc info>]'
O argumento para esta cláusula EXTERNAL NAME é uma cadeia que indica a localização da função no módulo externo. Para módulos externos que usam o mecanismo UDR, essa linha no separador indica o nome do módulo externo, o nome da função dentro do módulo e informações definidas pelo usuário. Um ponto de exclamação (!) É usado como um separador.
A cláusula ENGINE especifica o nome do mecanismo para processar a conexão de módulos externos. O Firebird usa o mecanismo UDR para trabalhar com módulos externos escritos em linguagens compiladas (C, C ++, Pascal). Funções externas escritas em Java requerem o mecanismo Java.
Após a palavra-chave AS, uma string literal pode ser especificada - o "corpo" do módulo externo (procedimento, função ou gatilho), e pode ser usada pelo módulo externo para vários fins. Por exemplo, uma consulta SQL pode ser especificada para acessar um banco de dados ou texto externo em algum idioma para interpretação por sua função.
Funções externas
Sintaxe {CREATE [OR ALTER] | RECREATE} FUNCTION funcname [(<inparam> [, <inparam> ...])] RETURNS <type> [COLLATE collation] [DETERMINISTIC] EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <inparam> ::= <param_decl> [{= |DEFAULT} <value>] <value> ::= {literal | NULL | context_var} <param_decl> ::= paramname <type> [NOT NULL] [COLLATE collation] <extname> ::= '<module name>!<routine name> [!<misc info>]' <type> ::= <datatype> | [TYPE OF] domain | TYPE OF COLUMN rel.col <datatype> ::= {SMALLINT | INT[EGER] | BIGINT} | BOOLEAN | {FLOAT | DOUBLE PRECISION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [, scale])] | {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} [(size)] [CHARACTER SET charset] | {NCHAR |NATIONAL CHARACTER | NATIONAL CHAR} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])]
Todos os parâmetros de uma função externa podem ser alterados usando a instrução ALTER FUNCTION.
Sintaxe:
ALTER FUNCTION funcname [(<inparam> [, <inparam> ...])] RETURNS <type> [COLLATE collation] [DETERMINISTIC] EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <extname> ::= '<module name>!<routine name>[!<misc info>]'
Você pode excluir uma função externa usando a instrução DROP FUNCTION.
Sintaxe:
DROP FUNCTION funcname
Aqui não descreveremos a sintaxe dos parâmetros de entrada e da saída. Ele cumpre totalmente a sintaxe das funções regulares do PSQL, descritas em detalhes no SQL Language Guide. Em vez disso, damos exemplos de declaração de funções externas com explicações.
A função de adicionar três argumentos
create function sum_args ( n1 integer, n2 integer, n3 integer ) returns integer external name 'udrcpp_example!sum_args' engine udr;
A implementação da função está no módulo udrcpp_example. Dentro deste módulo, uma função é registrada com o nome sum_args. Uma função externa usa o mecanismo UDR.
Função Java
create or alter function regex_replace ( regex varchar(60), str varchar(60), replacement varchar(60) ) returns varchar(60) external name 'org.firebirdsql.fbjava.examples.fbjava_example.FbRegex.replace( String, String, String)' engine java;
A implementação da função está na função estática de substituição da classe org.firebirdsql.fbjava.examples.fbjava_example.FbRegex
. Uma função externa usa o mecanismo Java.
Procedimentos externos
Sintaxe {CREATE [OR ALTER] | RECREATE} PROCEDURE procname [(<inparam> [, <inparam> ...])] RETURNS (<outparam> [<outparam> ...]) EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <inparam> ::= <param_decl> [{= | DEFAULT} <value>] <outparam> ::= <param_decl> <value> ::= {literal | NULL | context_var} <param_decl> ::= paramname <type> [NOT NULL] [COLLATE collation] <extname> ::= '<module name>!<routine name>[!<misc info>]' <type> ::= <datatype> | [TYPE OF] domain | TYPE OF COLUMN rel.col <datatype> ::= {SMALLINT | INT[EGER] | BIGINT} | BOOLEAN | {FLOAT | DOUBLE PRECISION} | {DATE | TIME | TIMESTAMP} | {DECIMAL | NUMERIC} [(precision [,scale])] | {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} [(size)] [CHARACTER SET charset] | {NCHAR | NATIONAL CHARACTER | NATIONAL CHAR} [VARYING] [(size)] | BLOB [SUB_TYPE {subtype_num | subtype_name}] [SEGMENT SIZE seglen] [CHARACTER SET charset] | BLOB [(seglen [, subtype_num])]
Todos os parâmetros do procedimento externo podem ser alterados usando a instrução ALTER PROCEDURE.
Sintaxe:
ALTER PROCEDURE procname [(<inparam> [, <inparam> ...])] RETURNS (<outparam> [, <outparam> ...]) EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>]
Você pode excluir um procedimento externo usando a instrução DROP PROCEDURE.
Sintaxe:
DROP PROCEDURE procname
Aqui não descreveremos a sintaxe dos parâmetros de entrada e saída. Ele está em total conformidade com a sintaxe dos procedimentos PSQL regulares, descritos em detalhes no Guia de Linguagem SQL. Em vez disso, damos exemplos de declaração de procedimentos externos com explicações.
create procedure gen_rows_pascal ( start_n integer not null, end_n integer not null ) returns ( result integer not null ) external name 'pascaludr!gen_rows' engine udr;
A implementação da função está no módulo pascaludr. Dentro deste módulo, o procedimento é registrado com o nome gen_rows. Um procedimento externo usa o mecanismo UDR.
create or alter procedure write_log ( message varchar(100) ) external name 'pascaludr!write_log' engine udr;
A implementação da função está no módulo pascaludr. Dentro deste módulo, o procedimento é registrado com o nome write_log. Um procedimento externo usa o mecanismo UDR.
create or alter procedure employee_pgsql (
A implementação da função está na função estática executeQuery da classe
org.firebirdsql.fbjava.examples.fbjava_example.FbJdbc
. Após o ponto de exclamação (!), As informações são localizadas para conexão com um banco de dados externo via JDBC. Uma função externa usa o mecanismo Java. Aqui, como o "corpo" do procedimento externo, uma consulta SQL é passada para recuperar os dados.
Observação
Este procedimento usa um esboço no qual um parâmetro não utilizado é passado. Isso se deve ao fato de que no Firebird 3.0 há um erro no processamento de procedimentos externos sem parâmetros.
Colocação de procedimentos e funções externas em pacotes
É conveniente colocar um grupo de procedimentos e funções inter-relacionados nos pacotes PSQL. Os pacotes podem conter procedimentos e funções PSQL externas e regulares.
Sintaxe {CREATE [OR ALTER] | RECREATE} PACKAGE package_name AS BEGIN [<package_item> ...] END {CREATE | RECREATE} PACKAGE BODY package_name AS BEGIN [<package_item> ...] [<package_body_item> ...] END <package_item> ::= <function_decl>; | <procedure_decl>; <function_decl> ::= FUNCTION func_name [(<in_params>)] RETURNS <type> [COLLATE collation] [DETERMINISTIC] <procedure_decl> ::= PROCEDURE proc_name [(<in_params>)] [RETURNS (<out_params>)] <package_body_item> ::= <function_impl> | <procedure_impl> <function_impl> ::= FUNCTION func_name [(<in_impl_params>)] RETURNS <type> [COLLATE collation] [DETERMINISTIC] <routine body> <procedure_impl> ::= PROCEDURE proc_name [(<in_impl_params>)] [RETURNS (<out_params>)] <routine body> <routine body> ::= <sql routine body> | <external body reference> <sql routine body> ::= AS [<declarations>] BEGIN [<PSQL_statements>] END <declarations> ::= <declare_item> [<declare_item> ...] <declare_item> ::= <declare_var>; | <declare_cursor>; | <subroutine declaration>; | <subroutine implimentation> <subroutine declaration> ::= <subfunc_decl> | <subproc_decl> <subroutine implimentation> ::= <subfunc_impl> | <subproc_impl> <external body reference> ::= EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <extname> ::= '<module name>!<routine name>[!<misc info>]'
Para procedimentos e funções externas, o nome do pacote, os parâmetros de entrada, seus tipos, valores padrão e parâmetros de saída são indicados no cabeçalho do pacote, e tudo é igual no corpo do pacote, exceto os valores padrão, bem como a localização no módulo externo (cláusula EXTERNAL NAME) , o nome do mecanismo e, possivelmente, o "corpo" do procedimento / função.
Suponha que você tenha escrito um UDR para trabalhar com expressões regulares, localizado no módulo externo (biblioteca dinâmica) do PCRE, e você tenha vários UDRs que executam outras tarefas. Se não tivéssemos usado pacotes PSQL, todos os nossos procedimentos e funções externos seriam misturados entre si e com procedimentos e funções PSQL comuns. Isso complica a busca de dependências e a alteração de módulos externos e, além disso, cria confusão e força pelo menos o uso de prefixos para agrupar procedimentos e funções. Os pacotes PSQL tornam essa tarefa muito mais fácil para nós.
Pacote RegExp SET TERM ^; CREATE OR ALTER PACKAGE REGEXP AS BEGIN PROCEDURE preg_match( APattern VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS (Matches VARCHAR(8192)); FUNCTION preg_is_match( APattern VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS BOOLEAN; FUNCTION preg_replace( APattern VARCHAR(8192), AReplacement VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS VARCHAR(8192); PROCEDURE preg_split( APattern VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS (Lines VARCHAR(8192)); FUNCTION preg_quote( AStr VARCHAR(8192), ADelimiter CHAR(10) DEFAULT NULL) RETURNS VARCHAR(8192); END^ RECREATE PACKAGE BODY REGEXP AS BEGIN PROCEDURE preg_match( APattern VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS (Matches VARCHAR(8192)) EXTERNAL NAME 'PCRE!preg_match' ENGINE UDR; FUNCTION preg_is_match( APattern VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS BOOLEAN AS BEGIN RETURN EXISTS( SELECT * FROM preg_match(:APattern, :ASubject)); END FUNCTION preg_replace( APattern VARCHAR(8192), AReplacement VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS VARCHAR(8192) EXTERNAL NAME 'PCRE!preg_replace' ENGINE UDR; PROCEDURE preg_split( APattern VARCHAR(8192), ASubject VARCHAR(8192)) RETURNS (Lines VARCHAR(8192)) EXTERNAL NAME 'PCRE!preg_split' ENGINE UDR; FUNCTION preg_quote( AStr VARCHAR(8192), ADelimiter CHAR(10)) RETURNS VARCHAR(8192) EXTERNAL NAME 'PCRE!preg_quote' ENGINE UDR; END^ SET TERM ;^
Gatilhos externos
Sintaxe {CREATE [OR ALTER] | RECREATE} TRIGGER trigname {<relation_trigger_legacy> | <relation_trigger_sql2003> | <database_trigger> | <ddl_trigger> } <external-body> <external-body> ::= EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <relation_trigger_legacy> ::= FOR {tablename | viewname} [ACTIVE | INACTIVE] {BEFORE | AFTER} <mutation_list> [POSITION number] <relation_trigger_sql2003> ::= [ACTIVE | INACTIVE] {BEFORE | AFTER} <mutation_list> [POSITION number] ON {tablename | viewname} <database_trigger> ::= [ACTIVE | INACTIVE] ON db_event [POSITION number] <ddl_trigger> ::= [ACTIVE | INACTIVE] {BEFORE | AFTER} <ddl_events> [POSITION number] <mutation_list> ::= <mutation> [OR <mutation> [OR <mutation>]] <mutation> ::= INSERT | UPDATE | DELETE <db_event> ::= CONNECT | DISCONNECT | TRANSACTION START | TRANSACTION COMMIT | TRANSACTION ROLLBACK <ddl_events> ::= ANY DDL STATEMENT | <ddl_event_item> [{OR <ddl_event_item>} ...] <ddl_event_item> ::= CREATE TABLE | ALTER TABLE | DROP TABLE | CREATE PROCEDURE | ALTER PROCEDURE | DROP PROCEDURE | CREATE FUNCTION | ALTER FUNCTION | DROP FUNCTION | CREATE TRIGGER | ALTER TRIGGER | DROP TRIGGER | CREATE EXCEPTION | ALTER EXCEPTION | DROP EXCEPTION | CREATE VIEW | ALTER VIEW | DROP VIEW | CREATE DOMAIN | ALTER DOMAIN | DROP DOMAIN | CREATE ROLE | ALTER ROLE | DROP ROLE | CREATE SEQUENCE | ALTER SEQUENCE | DROP SEQUENCE | CREATE USER | ALTER USER | DROP USER | CREATE INDEX | ALTER INDEX | DROP INDEX | CREATE COLLATION | DROP COLLATION | ALTER CHARACTER SET | CREATE PACKAGE | ALTER PACKAGE | DROP PACKAGE | CREATE PACKAGE BODY | DROP PACKAGE BODY | CREATE MAPPING | ALTER MAPPING | DROP MAPPING
Um gatilho externo pode ser alterado usando a instrução ALTER TRIGGER.
Sintaxe:
ALTER TRIGGER trigname { [ACTIVE | INACTIVE] [ {BEFORE | AFTER} {<mutation_list> | <ddl_events>} | ON db_event ] [POSITION number] [<external-body>] <external-body> ::= EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <extname> ::= '<module name>!<routine name>[!<misc info>]' <mutation_list> ::= <mutation> [OR <mutation> [OR <mutation>]] <mutation> ::= { INSERT | UPDATE | DELETE }
Você pode remover um gatilho externo usando a instrução DROP TRIGGER.
Sintaxe:
DROP TRIGGER trigname
Aqui estão exemplos de declaração de gatilhos externos com explicações.
create database 'c:\temp\slave.fdb'; create table persons ( id integer not null, name varchar(60) not null, address varchar(60), info blob sub_type text ); commit; create database 'c:\temp\master.fdb'; create table persons ( id integer not null, name varchar(60) not null, address varchar(60), info blob sub_type text ); create table replicate_config ( name varchar(31) not null, data_source varchar(255) not null ); insert into replicate_config (name, data_source) values ('ds1', 'c:\temp\slave.fdb'); create trigger persons_replicate after insert on persons external name 'udrcpp_example!replicate!ds1' engine udr;
A implementação do acionador está no módulo udrcpp_example. Dentro deste módulo, um gatilho é registrado com o nome replicate. Um gatilho externo usa o mecanismo UDR.
No link para o módulo externo, um parâmetro adicional ds1
, pelo qual a configuração para ler o banco de dados externo é lida na tabela replicate_config a partir do acionador externo.
Estrutura UDR
Agora é a hora de escrever o primeiro UDR. Vamos descrever a estrutura da UDR em Pascal. Para explicar a estrutura mínima para a construção de uma UDR, usaremos exemplos padrão de examples/udr/
traduzido para Pascal.
Crie um novo projeto para a nova biblioteca dinâmica, que chamaremos de MyUdr. Como resultado, você deve obter o arquivo MyUdr.dpr
(se você criou o projeto no Delphi) ou o arquivo MyUdr.lpr
(se você criou o projeto no Lazarus). Agora vamos alterar o arquivo principal do projeto para que fique assim:
library MyUdr; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} uses {$IFDEF unix} cthreads,
Nesse caso, você precisa exportar apenas uma função firebird_udr_plugin
, que é o ponto de entrada para o plug-in de módulos UDR externos. A implementação desta função estará localizada no módulo UdrInit.
Observação
Se você estiver desenvolvendo sua UDR no Free Pascal, precisará de diretivas adicionais. A {$mode objfpc}
é necessária para ativar o modo Object Pascal. Em vez disso, você pode usar a diretiva {$mode delphi}
para garantir a compatibilidade com o Delphi. Como meus exemplos devem ser compilados com êxito no FPC e no Delphi, eu escolho o {$mode delphi}
.
A diretiva {$H+}
inclui suporte para seqüências longas. Isso é necessário se você usar os tipos string, ansistring e não apenas strings com terminação nula PChar, PAnsiChar, PWideChar.
Além disso, precisaremos conectar módulos separados para suportar multithreading no Linux e outros sistemas operacionais semelhantes ao Unix.
Registrar procedimentos, funções ou gatilhos
Agora adicione o módulo UdrInit, ele deve ficar assim:
unit UdrInit; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses Firebird;
Na função firebird_udr_plugin
, firebird_udr_plugin
necessário registrar as fábricas de nossos procedimentos, funções e gatilhos externos. Para cada função, procedimento ou gatilho, você deve escrever sua própria fábrica. Isso é feito usando os métodos de interface IUdrPlugin:
- registerFunction — ;
- registerProcedure — ;
- registerTrigger — .
, ( ). // SQL. ( ).
. SumArgsFunc. .
SumArgsFunc unit SumArgsFunc; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses Firebird;
IUdrFunctionFactory. IUdrFunctionFactoryImpl. . , , . .
dispose , . .
setup . , . .
newItem . , . IRoutineMetadata , . PSQL. . TSumArgsFunction
.
IExternalFunction. IExternalFunctionImpl
.
dispose , . .
.
getCharSet , . , .
execute . , , .
. , , BLOB. BLOB, .
, . , . , , , NULL ( Null ). , , IMessageMetadata. , execute.
. Para
Null Null
, NULL,
UDR . : . , .. EXECUTE PROCEDURE .
UdrInit firebird_udr_plugin
.
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
Observação
uses SumArgsProc, .
IUdrProcedureFactory. IUdrProcedureFactoryImpl. . , , . .
dispose , . .
setup . , . .
newItem . , . IRoutineMetadata , . PSQL. . TSumArgsProcedure
.
SumArgsProc.
SumArgsProc unit SumArgsProc; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses Firebird; type
IExternalProcedure. IExternalProcedureImpl
.
dispose , . .
getCharSet . , .
open . , , . , nil, . . TSumArgsFunction.execute.
UDR . firebird_udr_plugin
.
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
Observação
uses GenRowsProc, .
. , open, .
GenRowsProc unit GenRowsProc; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses Firebird, SysUtils; type TInput = record start: Integer; startNull: WordBool; finish: Integer; finishNull: WordBool; end; PInput = ^TInput; TOutput = record n: Integer; nNull: WordBool; end; POutput = ^TOutput;
open TGenRowsProcedure
NULL, NULL, NULL, SELECT, nil.
, , . UDR Firebird. UDR Legacy UDF.
, open , IExternalResultSet. IExternalResultSetImpl
.
dispose . .
fetch SELECT. SUSPEND PSQL . , . true, , false
, . , .
Observação
Delphi yeild,
while(...) do { ... yield result; }
, open, , fetch. ( SELECT FIRST/ROWS/FETCH FIRST SELECT.)
UDR .
Note
C++ . , . .
UdrInit firebird_udr_plugin
.
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
Observação
uses TestTrigger, .
IUdrTriggerFactory. IUdrTriggerFactoryImpl.
.
dispose , . .
setup . , . .
newItem . , . IRoutineMetadata , . PSQL. . TMyTrigger
.
TestTrigger.
TestTrigger unit TestTrigger; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses Firebird, SysUtils; type
IExternalTrigger. IExternalTriggerImpl
.
dispose , . .
getCharSet . , .
execute . , , () . () IExternalTrigger. ACTION_
. , Firebird . , DDL , , , nil. . , , .
Observação
, , . IMessageMetadata. , . , , /.
, PSQL
if (:new.B IS NULL) THEN :new.B = :new.A + 1;
Mensagens
UDR , . NEW OLD.
, , .
:
( Delphi , .. record);
IMessageMetadata, / , .
, — , UDR.
. :
TMyStruct = record <var_1>: <type_1>; <nullIndicator_1>: WordBool; <var_2>: <type_1>; <nullIndicator_2>: WordBool; ... <var_N>: <type_1>; <nullIndicator_N>: WordBool; end; PMyStruct = ^TMyStruct;
/ ( ). Null- /, NOT NULL. Null- 2 . -1
/ NULL. NULL- NULL, 2- . SQL :
, .
:
function SUM_ARGS(A SMALLINT, B INTEGER) RETURNS BIGINT ....
:
TInput = record A: Smallint; ANull: WordBool; B: Integer; BNull: WordBool; end; PInput = ^TInput; TOutput = record Value: Int64; Null: WordBool; end; POutput = ^TOutput;
( 3 ):
function SUM_ARGS(A NUMERIC(4, 2), B NUMERIC(9, 3)) RETURNS NUMERIC(18, 6) ....
:
TInput = record A: Smallint; ANull: WordBool; B: Integer; BNull: WordBool; end; PInput = ^TInput; TOutput = record Value: Int64; Null: WordBool; end; POutput = ^TOutput;
:
procedure SOME_PROC(A CHAR(3) CHARACTER SET WIN1251, B VARCHAR(10) CHARACTER SET UTF8) ....
:
TInput = record A: array[0..2] of AnsiChar; ANull: WordBool; B: record Length: Smallint; Value: array[0..39] of AnsiChar; end; BNull: WordBool; end; PInput = ^TInput;
IMessageMetadata. /
:
- /;
- ;
- ;
- BLOB;
- /;
- / NULL;
- ;
- NULL-.
getCount
unsigned getCount(StatusType* status)
/ . , , : 0 <= index < getCount().
getField
const char* getField(StatusType* status, unsigned index)
.
getRelation
const char* getRelation(StatusType* status, unsigned index)
( ).
getOwner
const char* getOwner(StatusType* status, unsigned index)
.
getAlias
const char* getAlias(StatusType* status, unsigned index)
.
getType
unsigned getType(StatusType* status, unsigned index)
SQL .
isNullable
FB_BOOLEAN isNullable(StatusType* status, unsigned index)
true, NULL.
getSubType
int getSubType(StatusType* status, unsigned index)
BLOB (0 — , 1 — . .).
getLength
unsigned getLength(StatusType* status, unsigned index)
.
getScale
int getScale(StatusType* status, unsigned index)
.
getCharSet
unsigned getCharSet(StatusType* status, unsigned index)
BLOB.
getOffset
unsigned getOffset(StatusType* status, unsigned index)
( ).
getNullOffset
unsigned getNullOffset(StatusType* status, unsigned index)
NULL .
getBuilder
IMetadataBuilder* getBuilder(StatusType* status)
IMetadataBuilder, .
getMessageLength
unsigned getMessageLength(StatusType* status)
( ).
IMessageMetadata IRoutineMetadata. , . . Por exemplo:
IMessageMetadata getInputMetadata getOutputMetadata IRoutineMetadata. , , getTriggerMetadata.
, IMessageMetadata . IReferenceCounted. getInputMetadata getOutputMetadata 1 , xInputMetadata
xOutputMetadata
release.
. IMessageMetadata getOffset . .
null , getNullOffset.
. .
, . IUdrProcedureFactory, IUdrFunctionFactory IUdrTriggerFactory UDR. UDR firebird_udr_plugin
.
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
TSumArgsFunctionFactory
IUdrFunctionFactory, TGenRowsFactory
IUdrProcedureFactory, TMyTriggerFactory
IUdrTriggerFactory.
, . Firebird. , SuperServer , Classic
.
setup newItem IUdrProcedureFactory, IUdrFunctionFactory IUdrTriggerFactory.
IUdrFunctionFactory = class(IDisposable) const VERSION = 3; procedure setup(status: IStatus; context: IExternalContext; metadata: IRoutineMetadata; inBuilder: IMetadataBuilder; outBuilder: IMetadataBuilder); function newItem(status: IStatus; context: IExternalContext; metadata: IRoutineMetadata): IExternalFunction; end; IUdrProcedureFactory = class(IDisposable) const VERSION = 3; procedure setup(status: IStatus; context: IExternalContext; metadata: IRoutineMetadata; inBuilder: IMetadataBuilder; outBuilder: IMetadataBuilder); function newItem(status: IStatus; context: IExternalContext; metadata: IRoutineMetadata): IExternalProcedure; end; IUdrTriggerFactory = class(IDisposable) const VERSION = 3; procedure setup(status: IStatus; context: IExternalContext; metadata: IRoutineMetadata; fieldsBuilder: IMetadataBuilder); function newItem(status: IStatus; context: IExternalContext; metadata: IRoutineMetadata): IExternalTrigger; end;
, IDisposable, dispose. Firebird , . dispose , , .
IUdrProcedureFactoryImpl
, IUdrFunctionFactoryImpl
, IUdrTriggerFactoryImpl
. .
newItem
newItem , . UDR , .. , . .
. , , , IUdrFunctionFactory
. . .
newItem ,
UDR UDR.
function TSumArgsFunctionFactory.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction; begin
IRoutineMetadata , UDR . UDR. UDR .
setup
setup . IMetadataBuilder, , .
setup, setup DLL , . .
. , SumArgs.
,
type
, setup , .
SumArgsFunctionFactory procedure TSumArgsFunctionFactory.dispose; begin Destroy; end; function TSumArgsFunctionFactory.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction; begin Result := TSumArgsFunction.Create(); end; procedure TSumArgsFunctionFactory.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AInBuilder, AOutBuilder: IMetadataBuilder); begin
SQL Firebird . , SQL NULL. XSQLDA.
procedure TSumArgsFunction.execute(AStatus: IStatus; AContext: IExternalContext; AInMsg, AOutMsg: Pointer); var xInput: PSumArgsInMsg; xOutput: PSumArgsOutMsg; begin
, , , setup.
create or alter function FN_SUM_ARGS ( N1 varchar(15), N2 varchar(15), N3 varchar(15)) returns varchar(15) EXTERNAL NAME 'MyUdrSetup!sum_args' ENGINE UDR;
select FN_SUM_ARGS('15', '21', '35') from rdb$database
UDR , UDR. . Delphi 2009, Free Pascal FPC 2.2.
Observação
Free Pascal
Delphi. FPC 2.6.0 Delphi
.
:
newItem . IUdrFunctionFactoryImpl
, IUdrProcedureFactoryImpl
, IUdrTriggerFactoryImpl
. :
SimpleFactories unit UdrFactories; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses SysUtils, Firebird; type
setup , , dispose . newItem T
.
implementation procedure TProcedureSimpleFactory<T>.dispose; begin Destroy; end; function TProcedureSimpleFactory<T>.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure; begin Result := T.Create; end; procedure TProcedureSimpleFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AInBuilder, AOutBuilder: IMetadataBuilder); begin end; procedure TFunctionSimpleFactory<T>.dispose; begin Destroy; end; function TFunctionSimpleFactory<T>.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction; begin Result := T.Create; end; procedure TFunctionSimpleFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AInBuilder, AOutBuilder: IMetadataBuilder); begin end; procedure TTriggerSimpleFactory<T>.dispose; begin Destroy; end; function TTriggerSimpleFactory<T>.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalTrigger; begin Result := T.Create; end; procedure TTriggerSimpleFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); begin end;
1 , . :
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
. , . newItem
. UDR IRoutineMetadata
, Firebird, UDR. , , UDR, ,
UDR. , , .
unit UdrFactories; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses SysUtils, Firebird; type ...
, .
unit UdrFactories; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses SysUtils, Firebird; type ...
, .
UDR .
unit UdrFactories; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses SysUtils, Firebird; type ...
newItem ,
, .
implementation ... procedure TFunctionFactory<T>.dispose; begin Destroy; end; function TFunctionFactory<T>.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction; begin Result := T.Create; (Result as T).Metadata := AMetadata; end; procedure TFunctionFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AInBuilder, AOutBuilder: IMetadataBuilder); begin end; procedure TProcedureFactory<T>.dispose; begin Destroy; end; function TProcedureFactory<T>.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure; begin Result := T.Create; (Result as T).Metadata := AMetadata; end; procedure TProcedureFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AInBuilder, AOutBuilder: IMetadataBuilder); begin end; procedure TTriggerFactory<T>.dispose; begin Destroy; end; function TTriggerFactory<T>.newItem(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalTrigger; begin Result := T.Create; (Result as T).Metadata := AMetadata; end; procedure TTriggerFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext; AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); begin end;
https://github.com/sim1984/udr-book/blob/master/examples/Common/UdrFactories.pas .
BLOB
BLOB ( BLOB), . , BLOB , . BLOB . BLOB
IBlob
.
BLOB , BLOB , BLOB , BLOB .
BLOB , BLOB (), 64 . getSegment
IBlob
. putSegment
IBlob
.
BLOB
BLOB
(
LIST).
create procedure split ( txt blob sub_type text character set utf8, delimiter char(1) character set utf8 = ',' ) returns ( id integer ) external name 'myudr!split' engine udr;
:
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
, . .
. .
TInput = record txt: ISC_QUAD; txtNull: WordBool; delimiter: array [0 .. 3] of AnsiChar; delimiterNull: WordBool; end; TInputPtr = ^TInput; TOutput = record Id: Integer; Null: WordBool; end; TOutputPtr = ^TOutput;
BLOB BLOB, ISC_QUAD
.
:
Split TSplitProcedure = class(IExternalProcedureImpl) private procedure SaveBlobToStream(AStatus: IStatus; AContext: IExternalContext; ABlobId: ISC_QUADPtr; AStream: TStream); function readBlob(AStatus: IStatus; AContext: IExternalContext; ABlobId: ISC_QUADPtr): string; public
SaveBlobToStream
readBlob
BLOB. BLOB , — Delphi. OutputArray Counter.
open BLOB . Split
. .
TSplitProcedure.open function TSplitProcedure.open(AStatus: IStatus; AContext: IExternalContext; AInMsg, AOutMsg: Pointer): IExternalResultSet; var xInput: TInputPtr; xText: string; xDelimiter: string; begin xInput := AInMsg; if xInput.txtNull or xInput.delimiterNull then begin Result := nil; Exit; end; xText := readBlob(AStatus, AContext, @xInput.txt); xDelimiter := TFBCharSet.CS_UTF8.GetString(TBytes(@xInput.delimiter), 0, 4);
Observação
TFBCharSet
Firebird.pas.
Firebird.
UTF-8.
FbCharsets.pas
BLOB . BLOB . openBlob IAttachment
. BLOB , . ,
( IExternalContext
).
BLOB (), 64 . getSegment
IBlob
.
TSplitProcedure.SaveBlobToStream procedure TSplitProcedure.SaveBlobToStream(AStatus: IStatus; AContext: IExternalContext; ABlobId: ISC_QUADPtr; AStream: TStream); var att: IAttachment; trx: ITransaction; blob: IBlob; buffer: array [0 .. 32767] of AnsiChar; l: Integer; begin try att := AContext.getAttachment(AStatus); trx := AContext.getTransaction(AStatus); blob := att.openBlob(AStatus, trx, ABlobId, 0, nil); while True do begin case blob.getSegment(AStatus, SizeOf(buffer), @buffer, @l) of IStatus.RESULT_OK: AStream.WriteBuffer(buffer, l); IStatus.RESULT_SEGMENT: AStream.WriteBuffer(buffer, l); else break; end; end; AStream.Position := 0; blob.close(AStatus); finally if Assigned(att) then att.release; if Assigned(trx) then trx.release; if Assigned(blob) then blob.release; end; end;
Observação
, IAttachment
, ITransaction
IBlob
IReferenceCounted
,
.
1.
release.
SaveBlobToStream
BLOB
:
function TSplitProcedure.readBlob(AStatus: IStatus; AContext: IExternalContext; ABlobId: ISC_QUADPtr): string; var {$IFDEF FPC} xStream: TBytesStream; {$ELSE} xStream: TStringStream; {$ENDIF} begin {$IFDEF FPC} xStream := TBytesStream.Create(nil); {$ELSE} xStream := TStringStream.Create('', 65001); {$ENDIF} try SaveBlobToStream(AStatus, AContext, ABlobId, xStream); {$IFDEF FPC} Result := TEncoding.UTF8.GetString(xStream.Bytes, 0, xStream.Size); {$ELSE} Result := xStream.DataString; {$ENDIF} finally xStream.Free; end; end;
Observação
Free Pascal
Delphi TStringStream
. FPC
,
.
fetch
Counter , . . isc_convert_error
.
isc_convert_error procedure TSplitResultSet.dispose; begin SetLength(OutputArray, 0); Destroy; end; function TSplitResultSet.fetch(AStatus: IStatus): Boolean; var statusVector: array [0 .. 4] of NativeIntPtr; begin if Counter <= High(OutputArray) then begin Output.Null := False;
Observação
isc_random
, .
:
SELECT ids.ID FROM SPLIT((SELECT LIST(ID) FROM MYTABLE), ',') ids
Observação
, BLOB
,
.
,
.
fetch
.
BLOB
BLOB
BLOB .
Observação
UDF
BLOB / . UDF
blobsaveload.zip
BLOB /
CREATE PACKAGE BlobFileUtils AS BEGIN PROCEDURE SaveBlobToFile(ABlob BLOB, AFileName VARCHAR(255) CHARACTER SET UTF8); FUNCTION LoadBlobFromFile(AFileName VARCHAR(255) CHARACTER SET UTF8) RETURNS BLOB; END^ CREATE PACKAGE BODY BlobFileUtils AS BEGIN PROCEDURE SaveBlobToFile(ABlob BLOB, AFileName VARCHAR(255) CHARACTER SET UTF8) EXTERNAL NAME 'BlobFileUtils!SaveBlobToFile' ENGINE UDR; FUNCTION LoadBlobFromFile(AFileName VARCHAR(255) CHARACTER SET UTF8) RETURNS BLOB EXTERNAL NAME 'BlobFileUtils!LoadBlobFromFile' ENGINE UDR; END^
:
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
BLOB , UDR
06.BlobSaveLoad . LoadBlobFromFile :
interface uses Firebird, Classes, SysUtils; type
execute
TLoadBlobFromFile
, .
execute procedure TLoadBlobFromFileFunc.execute(AStatus: IStatus; AContext: IExternalContext; AInMsg: Pointer; AOutMsg: Pointer); const MaxBufSize = 16384; var xInput: TInputPtr; xOutput: TOutputPtr; xFileName: string; xStream: TFileStream; att: IAttachment; trx: ITransaction; blob: IBlob; buffer: array [0 .. 32767] of Byte; xStreamSize: Integer; xBufferSize: Integer; xReadLength: Integer; begin xInput := AInMsg; xOutput := AOutMsg; if xInput.filenameNull then begin xOutput.blobDataNull := True; Exit; end; xOutput.blobDataNull := False;
BLOB blobId createBlob
IAttachment
. BLOB , . , ( IExternalContext
).
BLOB, putSegment
IBlob
, . close
.
BLOB
BLOB
, BLOB .
BLOB,
.
Delphi Free Pascal
.
IBlob
/ Blob.
FbBlob, .
BlobHelper unit FbBlob; interface uses Classes, SysUtils, Firebird; const MAX_SEGMENT_SIZE = $7FFF; type TFbBlobHelper = class helper for IBlob procedure LoadFromStream(AStatus: IStatus; AStream: TStream); procedure SaveToStream(AStatus: IStatus; AStream: TStream); end; implementation uses Math; procedure TFbBlobHelper.LoadFromStream(AStatus: IStatus; AStream: TStream); var xStreamSize: Integer; xReadLength: Integer; xBuffer: array [0 .. MAX_SEGMENT_SIZE] of Byte; begin xStreamSize := AStream.Size; AStream.Position := 0; while xStreamSize <> 0 do begin xReadLength := Min(xStreamSize, MAX_SEGMENT_SIZE); AStream.ReadBuffer(xBuffer, xReadLength); Self.putSegment(AStatus, xReadLength, @xBuffer[0]); Dec(xStreamSize, xReadLength); end; end; procedure TFbBlobHelper.SaveToStream(AStatus: IStatus; AStream: TStream); var xInfo: TFbBlobInfo; Buffer: array [0 .. MAX_SEGMENT_SIZE] of Byte; xBytesRead: Cardinal; xBufferSize: Cardinal; begin AStream.Position := 0; xBufferSize := Min(SizeOf(Buffer), MAX_SEGMENT_SIZE); while True do begin case Self.getSegment(AStatus, xBufferSize, @Buffer[0], @xBytesRead) of IStatus.RESULT_OK: AStream.WriteBuffer(Buffer, xBytesRead); IStatus.RESULT_SEGMENT: AStream.WriteBuffer(Buffer, xBytesRead); else break; end; end; end; end.
BLOB, BLOB :
TLoadBlobFromFileFunc.execute procedure TLoadBlobFromFileFunc.execute(AStatus: IStatus; AContext: IExternalContext; AInMsg: Pointer; AOutMsg: Pointer); var xInput: TInputPtr; xOutput: TOutputPtr; xFileName: string; xStream: TFileStream; att: IAttachment; trx: ITransaction; blob: IBlob; begin xInput := AInMsg; xOutput := AOutMsg; if xInput.filenameNull then begin xOutput.blobDataNull := True; Exit; end; xOutput.blobDataNull := False;
, , , / . , BLOB.
, IExternalContext
execute , open . IExternalContext
getAttachment
, getTransaction
. UDR, , , startTransaction
IExternalContext
. . , , .. (2PC).
, SELECT JSON. :
create function GetJson ( sql_text blob sub_type text character set utf8, sql_dialect smallint not null default 3 ) returns returns blob sub_type text character set utf8 external name 'JsonUtils!getJson' engine udr;
SQL , , . IMessageMetadata
. , ,
Firebird.
Observação
JSON .
CHAR, VARCHAR OCTETS NONE BLOB SUB_TYPE BINARY
base64,
JSON.
:
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
, :
GetJson unit JsonFunc; {$IFDEF FPC} {$MODE objfpc}{$H+} {$DEFINE DEBUGFPC} {$ENDIF} interface uses Firebird, UdrFactories, FbTypes, FbCharsets, SysUtils, System.NetEncoding, System.Json;
MakeScaleInteger
, writeJson
Json . , execute
.
TJsonFunction.execute procedure TJsonFunction.execute(AStatus: IStatus; AContext: IExternalContext; AInMsg, AOutMsg: Pointer); var xFormatSettings: TFormatSettings; xInput: InputPtr; xOutput: OutputPtr; att: IAttachment; tra: ITransaction; stmt: IStatement; inBlob, outBlob: IBlob; inStream: TBytesStream; outStream: TStringStream; cursorMetaData: IMessageMetadata; rs: IResultSet; msgLen: Cardinal; msg: Pointer; jsonArray: TJsonArray; begin xInput := AInMsg; xOutput := AOutMsg;
getAttachment
getTransaction
IExternalContext
. BLOB SQL . prepare IAttachment
. SQL . IStatement.PREPARE_PREFETCH_METADATA
, . getOutputMetadata
IStatement
.
Observação
getOutputMetadata .
IStatement.PREPARE_PREFETCH_METADATA
.
, .
openCursor ( 2). getMessageLength
IMessageMetadata
. , .
fetchNext
IResultSet
. msg
IStatus.RESULT_OK
, . writeJson
, TJsonObject
TJsonArray
.
, close
, Json , , Blob.
writeJson
. IUtil
, . IMessageMetadata
. TJsonObject
. . NullFlag, null , Json.
writeJson function TJsonFunction.MakeScaleInteger(AValue: Int64; Scale: Smallint): string; var L: Integer; begin Result := AValue.ToString; L := Result.Length; if (-Scale >= L) then Result := '0.' + Result.PadLeft(-Scale, '0') else Result := Result.Insert(Scale + L, '.'); end; procedure TJsonFunction.writeJson(AStatus: IStatus; AContext: IExternalContext; AJson: TJsonArray; ABuffer: PByte; AMeta: IMessageMetadata; AFormatSettings: TFormatSettings); var jsonObject: TJsonObject; i: Integer; FieldName: string; NullFlag: WordBool; pData: PByte; util: IUtil; metaLength: Integer;
Observação
TFbType Firebird.pas
.
,
FbTypes .
TFBCharSet Firebird.pas
.
FbCharsets . ,
,
, , ,
TEncoding
,
Delphi.
CHAR VARCHAR , OCTETS, base64, Delphi. , VARCHAR 2 .
SMALLINT, INTEGER, BIGINT , . getScale
IMessageMetadata
. 0, , MakeScaleInteger
.
DATE, TIME TIMESTAMP decodeDate
decodeTime
IUtil
. - Delphi TDateTime
.
BLOB Delphi. BLOB , TBytesStream
. base64. BLOB , TStringStream
, . BLOB
.
Isso é tudo. UDR Firebird, .