Firebird telah lama dapat memperluas kemampuan bahasa PSQL dengan menulis fungsi eksternal - UDF (Fungsi yang Ditentukan Pengguna). UDF dapat ditulis dalam hampir semua bahasa pemrograman yang dikompilasi.
Firebird 3.0 memperkenalkan arsitektur plug-in untuk memperluas kemampuan Firebird. Salah satu plugin ini adalah External Engine (mesin eksternal). Mekanisme UDR (User Defined Routines - User Defined Routines) menambahkan lapisan di atas antarmuka engine FirebirdExternal.
Dalam panduan ini, kami akan memberi tahu Anda cara mendeklarasikan UDR, tentang mekanisme internal, kemampuan, dan memberikan contoh penulisan UDR dalam Pascal. Selain itu, beberapa aspek menggunakan API berorientasi objek baru akan dibahas.
Komentar
Artikel ini dimaksudkan untuk mengajarkan Anda cara menulis UDR menggunakan objek Firebird API.
Fungsi dan prosedur tertulis mungkin tidak memiliki aplikasi praktis.
UDR memiliki keunggulan berikut dibandingkan Legacy UDF:
- Anda dapat menulis tidak hanya fungsi yang mengembalikan hasil skalar, tetapi juga prosedur tersimpan (baik yang dapat dieksekusi dan selektif), serta pemicu;
- peningkatan kontrol parameter input dan output. Dalam beberapa kasus (lewat deskriptor) jenis dan properti lain dari parameter input tidak dikontrol sama sekali, namun, Anda bisa mendapatkan properti ini di dalam UDF. UDR menyediakan cara yang lebih terpadu untuk mendeklarasikan parameter input dan output, karena ini adalah kasus dengan fungsi dan prosedur PSQL biasa;
- UDR konteks koneksi atau transaksi saat ini tersedia, yang memungkinkan Anda untuk melakukan
beberapa manipulasi dengan database saat ini dalam konteks ini; - Pembuatan kesalahan Firebird tersedia saat pengecualian terjadi, tidak perlu mengembalikan nilai khusus;
- Prosedur dan fungsi eksternal (UDR) dapat dikelompokkan dalam paket PSQL;
- UDR dapat ditulis dalam bahasa pemrograman apa saja (opsional dikompilasi ke dalam kode objek), untuk ini perlu bahwa plug-in Mesin Eksternal yang sesuai ditulis. Misalnya, ada plugin untuk menulis modul eksternal di Java atau dalam bahasa .NET mana saja.
Komentar
Implementasi UDR saat ini menggunakan rintisan PSQL. Sebagai contoh, ini digunakan untuk
memeriksa parameter dan mengembalikan nilai kepatuhan dengan pembatasan. Rintisan
digunakan karena tidak fleksibel untuk langsung memanggil fungsi internal. Hasil
Tes membandingkan kinerja UDR dan UDF menunjukkan bahwa UDR sekitar
2,5 kali lebih lambat menggunakan fungsi paling sederhana untuk menambahkan dua argumen sebagai contoh. Kecepatan
UDR kira-kira sama dengan kecepatan fungsi PSQL biasa. Mungkin di masa depan ini
momen akan dioptimalkan. Dalam fungsi yang lebih kompleks, overhead ini bisa menjadi
tak terlihat.
Lebih lanjut dalam berbagai bagian manual ini, ketika menggunakan istilah prosedur eksternal,
fungsi atau pemicu yang kami maksud persis UDR (dan bukan UDF).
Komentar
Semua contoh kami berfungsi pada Delphi 2009 dan yang lebih baru, serta pada Free Pascal. Semua
contoh dapat dikompilasi dalam Delphi dan Free Pascal, jika
tidak ditentukan secara terpisah.
API Firebird
Untuk menulis prosedur eksternal, fungsi, atau pemicu dalam bahasa pemrograman yang dikompilasi, kita perlu pengetahuan tentang Firebird API berorientasi objek yang baru. Panduan ini tidak termasuk deskripsi lengkap tentang API Firebird. Anda dapat membacanya di direktori dokumentasi yang didistribusikan bersama Firebird ( doc/Using_OO_API.html
).
Plug-in untuk berbagai bahasa pemrograman yang mengandung API tidak didistribusikan sebagai bagian dari distribusi Firebird untuk Windows, namun Anda dapat mengekstraknya dari file tarbar terkompresi yang didistribusikan oleh Linux (jalur di dalam arsip /opt/firebird/include/firebird/Firebird.pas
).
CLOOP
CLOOP - Pemrograman Berorientasi Objek Lintas Bahasa. Alat ini tidak termasuk dengan Firebird. Itu dapat ditemukan di kode sumber https://github.com/FirebirdSQL/firebird/tree/B3_0_Release/extern/cloop . Setelah alat dirakit, Anda dapat membuat API untuk bahasa pemrograman Anda ( IdlFbInterfaces.h
atau Firebird.pas
) berdasarkan pada file deskripsi antarmuka antar muka include/firebird/FirebirdInterface.idl
.
Untuk Object pascal, ini dilakukan dengan perintah berikut:
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
File Pascal.interface.pas
, Pascal.implementation.pas
dan fb_get_master_interface.pas
dapat ditemukan di https://github.com/FirebirdSQL/firebird/tree/B3_0_Release/src/misc/pascal .
Komentar
Dalam hal ini, awalan I akan ditambahkan untuk API Firebird, karena ini diterima di Object Pascal.
Konstanta
Tidak ada isc_*
dalam file Firebird.pas
dihasilkan. Konstanta untuk bahasa C / C ++ ini dapat ditemukan di https://github.com/FirebirdSQL/firebird/blob/B3_0_Release/src/include/consts_pub.h . Untuk mendapatkan konstanta untuk bahasa Pascal, kami akan menggunakan skrip AWK untuk mengonversi sintaks. Di Windows, Anda harus menginstal Gawk untuk Windows atau menggunakan Subsistem Windows untuk Linux (tersedia di Windows 10). Ini dilakukan dengan perintah berikut:
awk -f Pascal.Constants.awk consts_pub.h > const.pas
Isi dari file yang dihasilkan harus disalin ke bagian const kosong dari file Firebird.pas
segera setelah implementasi. File Pascal.Constants.awk
dapat ditemukan di
https://github.com/FirebirdSQL/firebird/tree/B3_0_Release/src/misc/pascal .
Manajemen seumur hidup
Antarmuka Firebird tidak didasarkan pada spesifikasi COM, jadi mengatur masa pakai mereka berbeda.
Ada dua antarmuka di Firebird yang berhubungan dengan manajemen seumur hidup: IDisposable dan IReferenceCounted. Yang terakhir ini sangat aktif saat membuat antarmuka lain: IPlugin menghitung tautan, seperti banyak antarmuka lain yang digunakan oleh plug-in. Ini termasuk antarmuka yang menggambarkan bagaimana menghubungkan ke database, manajemen transaksi, dan pernyataan SQL.
Overhead tambahan antarmuka dengan penghitungan referensi tidak selalu diperlukan. Misalnya, IMaster, antarmuka utama yang memanggil fungsi-fungsi yang tersedia untuk seluruh API, memiliki masa pakai tak terbatas menurut definisi. Untuk API lain, masa pakai sangat ditentukan oleh masa pakai antarmuka induk; Antarmuka IStatus tidak
multithreaded. Untuk antarmuka dengan masa pakai terbatas, ada gunanya memiliki cara sederhana untuk menghancurkannya, yaitu fungsi buang ().
Petunjuk
Jika Anda tidak tahu bagaimana suatu objek dihancurkan, lihat hierarkinya, jika ada
antarmuka IReferenceCounted, kemudian penghitungan referensi digunakan.
Untuk antarmuka dengan penghitungan referensi, setelah menyelesaikan pekerjaan dengan objek, perlu
kurangi penghitung referensi dengan memanggil metode release ().
Pengumuman UDR
UDR dapat ditambahkan atau dihapus dari database menggunakan perintah DDL, seperti halnya Anda menambah atau menghapus prosedur, fungsi, atau pemicu PSQL biasa. Dalam hal ini, alih-alih tubuh pemicu, lokasinya di modul eksternal diindikasikan menggunakan klausa NAMA EKSTERNAL.
Pertimbangkan sintaks kalimat ini, itu akan umum untuk prosedur eksternal, fungsi, dan pemicu.
Sintaks:
EXTERNAL NAME '<extname>' ENGINE <engine> [AS <extbody>] <extname> ::= '<module name>!<routine name>[!<misc info>]'
Argumen untuk klausa NAMA EKSTERNAL ini adalah string yang menunjukkan lokasi fungsi dalam modul eksternal. Untuk modul eksternal menggunakan mesin UDR, baris ini melalui pemisah menunjukkan nama modul eksternal, nama fungsi di dalam modul, dan informasi yang ditentukan pengguna. Tanda seru (!) Digunakan sebagai pemisah.
Klausa ENGINE menentukan nama mesin untuk memproses koneksi modul eksternal. Firebird menggunakan mesin UDR untuk bekerja dengan modul eksternal yang ditulis dalam bahasa yang dikompilasi (C, C ++, Pascal). Fungsi eksternal yang ditulis dalam Java memerlukan engine Java.
Setelah kata kunci AS, string literal dapat ditentukan - "tubuh" dari modul eksternal (prosedur, fungsi atau pemicu), itu dapat digunakan oleh modul eksternal untuk berbagai keperluan. Misalnya, kueri SQL dapat ditentukan untuk mengakses database atau teks eksternal dalam beberapa bahasa untuk ditafsirkan oleh fungsi Anda.
Fungsi eksternal
Sintaks {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])]
Semua parameter fungsi eksternal dapat diubah menggunakan pernyataan ALTER FUNCTION.
Sintaks:
ALTER FUNCTION funcname [(<inparam> [, <inparam> ...])] RETURNS <type> [COLLATE collation] [DETERMINISTIC] EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>] <extname> ::= '<module name>!<routine name>[!<misc info>]'
Anda dapat menghapus fungsi eksternal menggunakan pernyataan FUNGSI DROP.
Sintaks:
DROP FUNCTION funcname
Di sini kita tidak akan menjelaskan sintaks dari parameter input dan output. Ini sepenuhnya sesuai dengan sintaks untuk fungsi PSQL biasa, yang dijelaskan secara rinci dalam Panduan Bahasa SQL. Sebagai gantinya, kami memberikan contoh menyatakan fungsi eksternal dengan penjelasan.
Fungsi menambahkan tiga argumen
create function sum_args ( n1 integer, n2 integer, n3 integer ) returns integer external name 'udrcpp_example!sum_args' engine udr;
Implementasi fungsi ada di modul udrcpp_example. Di dalam modul ini, sebuah fungsi terdaftar dengan nama sum_args. Fungsi eksternal menggunakan mesin UDR.
Fungsi 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;
Implementasi fungsi ini di ganti fungsi statis dari kelas org.firebirdsql.fbjava.examples.fbjava_example.FbRegex
. Fungsi eksternal menggunakan mesin Java.
Prosedur eksternal
Sintaks {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])]
Semua parameter prosedur eksternal dapat diubah menggunakan pernyataan ALTER PROCEDURE.
Sintaks:
ALTER PROCEDURE procname [(<inparam> [, <inparam> ...])] RETURNS (<outparam> [, <outparam> ...]) EXTERNAL NAME <extname> ENGINE <engine> [AS <extbody>]
Anda dapat menghapus prosedur eksternal menggunakan pernyataan PROSEDUR DROP.
Sintaks:
DROP PROCEDURE procname
Di sini kita tidak akan menjelaskan sintaks parameter input dan output. Ini sepenuhnya sesuai dengan sintaks untuk prosedur PSQL biasa, yang dijelaskan secara rinci dalam Panduan Bahasa SQL. Sebagai gantinya, kami memberikan contoh menyatakan prosedur eksternal dengan penjelasan.
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;
Implementasi fungsi ada dalam modul pascaludr. Di dalam modul ini, prosedur terdaftar dengan nama gen_rows. Prosedur eksternal menggunakan mesin UDR.
create or alter procedure write_log ( message varchar(100) ) external name 'pascaludr!write_log' engine udr;
Implementasi fungsi ada dalam modul pascaludr. Di dalam modul ini, prosedur terdaftar dengan nama write_log. Prosedur eksternal menggunakan mesin UDR.
create or alter procedure employee_pgsql (
Implementasi fungsi ini berada di fungsi statis, mengeksekusi Query dari kelas
org.firebirdsql.fbjava.examples.fbjava_example.FbJdbc
. Setelah tanda seru (!), Informasi berada untuk menghubungkan ke database eksternal melalui JDBC. Fungsi eksternal menggunakan mesin Java. Di sini, sebagai "tubuh" dari prosedur eksternal, kueri SQL dilewatkan untuk mengambil data.
Komentar
Prosedur ini menggunakan rintisan di mana parameter yang tidak digunakan dilewatkan. Hal ini disebabkan oleh fakta bahwa di Firebird 3.0 terdapat bug dengan pemrosesan prosedur eksternal tanpa parameter.
Penempatan prosedur dan fungsi eksternal di dalam paket
Lebih mudah untuk menempatkan sekelompok prosedur dan fungsi yang saling terkait dalam paket PSQL. Paket dapat berisi prosedur dan fungsi PSQL eksternal dan reguler.
Sintaks {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>]'
Untuk prosedur dan fungsi eksternal, nama paket, parameter input, jenisnya, nilai default, dan parameter output ditunjukkan dalam header paket, dan semuanya sama di tubuh paket kecuali nilai default, serta lokasi dalam modul eksternal (klausa NAMA EKSTERNAL) , nama mesin, dan mungkin "tubuh" dari prosedur / fungsi.
Misalkan Anda menulis UDR untuk bekerja dengan ekspresi reguler, yang terletak di modul eksternal (perpustakaan dinamis) PCRE, dan Anda memiliki beberapa UDR lagi yang melakukan tugas-tugas lain. Jika kita tidak menggunakan paket PSQL, maka semua prosedur dan fungsi eksternal kita akan dicampur satu sama lain dan dengan prosedur dan fungsi PSQL biasa. Ini mempersulit pencarian dependensi dan membuat perubahan pada modul eksternal, dan di samping itu menciptakan kebingungan dan memaksa setidaknya penggunaan awalan untuk prosedur dan fungsi grup. Paket PSQL membuat tugas ini lebih mudah bagi kami.
Paket 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 ;^
Pemicu eksternal
Sintaks {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
Pemicu eksternal dapat diubah menggunakan pernyataan ALTER TRIGGER.
Sintaks:
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 }
Anda dapat menghapus pemicu eksternal menggunakan pernyataan DROP TRIGGER.
Sintaks:
DROP TRIGGER trigname
Berikut adalah contoh menyatakan pemicu eksternal dengan penjelasan.
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;
Implementasi pemicu ada di modul udrcpp_example. Di dalam modul ini, pemicu terdaftar dengan nama replikat. Pemicu eksternal menggunakan mesin UDR.
Dalam tautan ke modul eksternal, parameter tambahan ds1
, di mana konfigurasi untuk membaca database eksternal dibaca dari tabel replicate_config dari pemicu eksternal.
Struktur UDR
Sekarang saatnya untuk menulis UDR pertama. Kami akan menggambarkan struktur UDR di Pascal. Untuk menjelaskan struktur minimal untuk membangun UDR, kami akan menggunakan contoh standar dari examples/udr/
diterjemahkan ke dalam Pascal.
Buat proyek baru untuk pustaka dinamis baru, yang akan kita sebut MyUdr. Akibatnya, Anda harus mendapatkan file MyUdr.dpr
(jika Anda membuat proyek di Delphi) atau file MyUdr.lpr
(jika Anda membuat proyek di Lazarus). Sekarang mari kita ubah file proyek utama sehingga terlihat seperti ini:
library MyUdr; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} uses {$IFDEF unix} cthreads,
Dalam hal ini, Anda hanya perlu mengekspor satu fungsi firebird_udr_plugin
, yang merupakan titik masuk untuk plug-in modul UDR eksternal. Implementasi fungsi ini akan berlokasi di modul UdrInit.
Komentar
Jika Anda mengembangkan UDR Anda dalam Free Pascal, maka Anda akan memerlukan arahan tambahan. {$mode objfpc}
diperlukan untuk mengaktifkan mode Object Pascal. Sebagai gantinya, Anda dapat menggunakan arahan {$mode delphi}
untuk memastikan kompatibilitas dengan Delphi. Karena contoh saya harus berhasil dikompilasi di FPC dan Delphi, saya memilih {$mode delphi}
.
Arahan {$H+}
mencakup dukungan untuk string panjang. Ini diperlukan jika Anda akan menggunakan string tipe, ansistring, dan bukan hanya string yang diakhiri null PChar, PAnsiChar, PWideChar.
Selain itu, kita perlu menghubungkan modul terpisah untuk mendukung multithreading di Linux dan sistem operasi mirip Unix lainnya.
Mendaftar prosedur, fungsi, atau pemicu
Sekarang tambahkan modul UdrInit, akan terlihat seperti ini:
unit UdrInit; {$IFDEF FPC} {$MODE DELPHI}{$H+} {$ENDIF} interface uses Firebird;
Dalam fungsi firebird_udr_plugin
, perlu mendaftarkan pabrik dari prosedur, fungsi, dan pemicu eksternal kami. Untuk setiap fungsi, prosedur atau pemicu, Anda harus menulis pabrik Anda sendiri. Ini dilakukan dengan menggunakan metode antarmuka 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.
. Untuk
Null Null
, NULL,
UDR . : . , .. EXECUTE PROCEDURE .
UdrInit firebird_udr_plugin
.
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr; AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl; begin
Komentar
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
Komentar
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
, . , .
Komentar
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
Komentar
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. . , , .
Komentar
, , . IMessageMetadata. , . , , /.
, PSQL
if (:new.B IS NULL) THEN :new.B = :new.A + 1;
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. , . . Sebagai contoh:
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.
Komentar
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);
Komentar
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;
Komentar
, 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;
Komentar
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;
Komentar
isc_random
, .
:
SELECT ids.ID FROM SPLIT((SELECT LIST(ID) FROM MYTABLE), ',') ids
Komentar
, BLOB
,
.
,
.
fetch
.
BLOB
BLOB
BLOB .
Komentar
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.
Komentar
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
.
Komentar
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;
Komentar
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
.
Itu saja. UDR Firebird, .