Dnes jsem v rámci jednoho projektu potřeboval vyřešit docela komplikovanou analýzu a parsování řetězců na úrovni 64-bitového serveru FireBird 2.5 a při té příležitosti bych se rád se čtenáři Delphi.cz podělil o výsledky mého snažení, neb to sice není nic složitého, ale informace se shánějí obtížně a každý omyl končí pádem FireBirdu nebo přinejmenším další testy jsou podmíněny jeho restartem.
Delphi XE2, jak již zřejmě všichni víte, podporuje 64-bitovou platformu. Díky tomu lze snadno vytvořit UDF knihovnu jak pro 32-bitový, tak i 64-bitový server FireBird.
Vlastní knihovnu zahájíte vytvořením knihovny (File > New > Other). Poté v sekci „Delphi Project“ zvolíte „Dynamic-link Library“:
Získáte čistý projekt, který vypadá zhruba takto:
První, co musíte udělat, je vložení definice funkce zajišťující alokaci paměti kompatibilní s FireBirdem.
To se týká především vracení řetězců, ale rovněž pokud chcete vracet hodnoty odkazem a zároveň mít možnost vracet NULL.
Řádek vypadá takto:
function ib_util_malloc(l: integer): pointer; cdecl; external 'ib_util.dll';
Dalším krokem je vytvoření funkcí, které zajistí vlastní funkčnost. Zde je vhodné poznamenat, že při práci s řetězci je nutné mít na zřeteli kódování, ve kterém máte uložená data na serveru.
Rozhodně se nespoléhejte, že FireBird zajistí překlad kódových stránek.
Vlastní procedura pak může vypadat například takto:
type
PInteger = ^Integer;
function Priklad_UDF(
const Value1: PAnsiChar;
const Value2: PAnsiChar;
const Value3: PInteger
): PAnsiChar; cdecl;
var
data, data2: string;
s: TBytes;
e: TEncoding;
i, cnt: Integer;
begin
// pokud je některá hodnota NULL, vracím NULL
if (Value1 = nil) or (Value2 = nil) or (Value3 = nil) then Exit(nil);
// v mém případě používám kódování WIN1250, pro UTF-8 použijte TEncoding.UTF8
//a na konci jej neuvolňujte!
e := TEncoding.GetEncoding(1250);
try
try
// zde provedu nějaké operace, pokud možno užitečnější než jsou v tomto příkladu :-)
// načtu první řetězec
SetLength(s, Length(Value1));
Move(Value1^, s[0], Length(s));
data := e.GetString(s);
// načtu druhý řetězec
SetLength(s, Length(Value2));
Move(Value2^, s[0], Length(s));
data2 := e.GetString(s);
// k prvnímu textu přidám druhý text tolikrát, kolikrát určuje třetí parametr
for i := 1 to Value3^ do
data := data + ', ' + data2;
// výsledek omezím na velikost, kterou později deklaruji ve FireBirdu
if (Length(data) > 2047) then SetLength(data, 2047);
except
// výjimky je nutné odchytit, jinak se z toho FireBird nevzpamatuje
Exit(nil);
end;
// výsledek v podobě textu převedu zpět do požadovaného kódování (vytvořím pole bytů)
s := e.GetBytes(data);
cnt := Length(s);
// alokuji dostatečně velký buffer pro vrácení výsledku pomocí funkce FireBirdu
Result := ib_util_malloc(cnt + 1);
// zkopíruji výsledek do prostoru, který bude přístupný FireBirdu
Move(s[0], Result^, cnt);
// nastavím poslední byte na 0 pro ukončení řetězce
Result[cnt] := #0;
finally
// nezapomenu uvolnit objekt kódování, pokud jsem nepoužil některý předdefinovaný
e.Free;
end;
end;
exports
Priklad_UDF name 'Priklad_UDF';
Povšimněte si především deklarace funkce. Všechny parametry jsou předávány odkazem a dále je uvedena volací konvence cdecl. Ještě musíme zveřejnit název, pod kterým FireBird funkci v knihovně najde (sekce exports). Zvolte si platformu (32 bitovou nebo 64 bitovou dle vašeho serveru) a proveďte kompilaci projektu.
Pokud vše skončí bez chyby, doporučuji knihovnu nakopírovat do složky UDF instalace FireBird. V opačném případě budeme muset upravit soubor firebird.conf a povolit vaše preferované umístění.
Aby FireBird o vaší funkci věděl, je nutné ji deklarovat pomocí následujícího příkazu:
DECLARE EXTERNAL FUNCTION PRIKLAD_UDF
CSTRING(250) null,
CSTRING(250) null,
INTEGER null
RETURNS CSTRING(2048) FREE_IT
ENTRY_POINT 'Priklad_UDF'
MODULE_NAME 'jméno knihovny bez .dll';
Použijte nějaký SQL nástroj, třeba výborný a bezplatný FlameRobin.
Klíčová slova NULL říkají, že funkce dokáže rozpoznávat hodnoty NULL, jinak by funkce dostávala předdefinované nulové hodnoty (tj. prázdný řetězec nebo nulu).
Klíčové slovo FREE_IT zase zajistí, že bude vrácený řetězec uvolněn z paměti, až jej FireBird nebude potřebovat.
Nyní spusťte SQL příkaz:
select PRIKLAD_UDF('abc', 'def', 4) from RDB$DATABASE;
Měli byste obdržet výsledek: abc, def, def, def, def.
p.s: později byl zveřejněn oficiální návod - firebirdfaq.org/faq83
Igor Gottwald