64-bitové UDF v RAD Studio XE2 pro FireBird

vložil Igor Gottwald 2. prosince 2011 00:53

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“:

UDF Delphi

Získáte čistý projekt, který vypadá zhruba takto:

Blank UDF Firebird

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

Tagy: , ,

Návody

Komentáře

12.4.2012 7:44:03 #

JK

Dobrý den,
Mám otázku.
Může se v udf knihovně Pchar přetypovat na standardní string a poté zase zpět?
Je s ním lepší práce, ale nevím, jesli by to nevadilo. Můžete mi to přiblížit.

Díky JK.

JK

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS