Makalenin pdf hali : http://www.delphiturkiye.com/dsplus/m.p ... rd_UDF.pdf
Delphi Kodları : http://www.delphiturkiye.com/dsplus/m.p ... df_kod.rar
Firebird’ün güzel yanlarından biri de, Firebird’e kendi yazdığınız fonksiyonları ekleyip, veritabanında her yerde kullanabilmeniz. UDF’leri Delphi ile de yapabilirsiniz. Aşağıda basitçe Delphi ile UDF yazmayı anlatmaya çalıştım. Ekleyeceğiniz veya düzelteceğiniz birşey varsa mutlaka bize ulaştırın.
UDF oluşturmak için bir .dll hazırlayıp, Firebird’ün UDF klasörüne koymak gerekiyor.
Delphi’yi açıp, File -> New -> Other... komutunu verin. Açılan pencereden DLL Wizard seçip, OK butonuna basın. Delphi size boş bir dll kodu oluşturacaktır. Direk bu kısma fonksiyonlarımızı ekleyebiliriz, ama ilerde fonksiyonlarımız arttığında takibin kolay olması açısından gruplara ayırmak ve grupların her biri için yeni bir unit ekleyip, oraya yazmamız daha iyi olur. File -> New -> Unit komutunu vererek yeni bir unit ekleyelim.
Şu an için unit’imizde verilen bir yıl ve aydaki gün sayısını veren bir fonksiyon olacak. Fonksiyonumuz zaten Delphi’de DateUtils unitinde olduğu için uses kısmına DateUtils ekliyoruz. Fonksiyonu ekledikten sonra unitimizin kodu :
Kod: Tümünü seç
unit untTarih;
interface
uses DateUtils;
function BirAydakiGunSayisi(var Yil, Ay:Integer):Integer;stdcall;
implementation
function BirAydakiGunSayisi(var Yil, Ay:Integer):Integer;
begin
Result := 0;
if (Ay <= 0) or (Ay > 12) then
Result := 0
else
Result := DaysInAMonth(Yil, Ay);
end;
end.
Bu fonksiyonu dll kısmında export etmemiz lazım. Proje dosyamızın son hali :
Kod: Tümünü seç
library DelphiTurkiye;
uses
SysUtils,
Classes,
untTarih in 'untTarih.pas';
{$R *.res}
exports
BirAydakiGunSayisi;
begin
end.
Kod: Tümünü seç
DECLARE EXTERNAL FUNCTION BirAydakiGunSayisi
integer, integer
RETURNS integer BY VALUE
ENTRY_POINT 'BirAydakiGunSayisi' MODULE_NAME 'DelphiTurkiye';
Kod: Tümünü seç
select BirAydakiGunSayisi(2008, 2)
from rdb$database
* Fonksiyon parametrelerini mutlaka var kelimesi ile geçin. Örneğimizde Yil ve Ay parametrelerini bu şekilde geçtik. Parametre pchar ise var kullanmamanız lazım.
* Firebird C’de yazıldığı için, C notasyonu istiyor. Fonksiyonumuzu dll’den çağırırken sonuna stdcall direktifini eklememiz gerekiyor.
* Fonksiyonu çağırırken near, far ve export direktiflerinin bir etkisi yoktur, bunları kullanmayın.
* Eğer fonksiyonu denerken bir hata alırsanız veya bağlantınız koparsa:
o Kodunuzda hata vardır, kodunuzu kontrol edin.
o .dpr kısmında fonksiyonu export ettiniz mi, bir bakın. Eğer etmediyseniz genelde “invalid request BLR at offset xxx” gibi bir hata alacaksınız.
o Derledikten sonra oluşan .dll dosyasını UDF klasörüne kopyaladınız mı, kontrol edin. Firebird 2.1 için normal kurulumda bu klasörün tam yolu : “C:\Program Files\Firebird\Firebird_2_1\UDF” tir.
o Firebird servisini denetim masası kısmından kapatıp açın.
* Eğer dll’de değişiklik yaparsanız, parametre değişikliği yoksa .dll’i UDF klasörüne kopyalamanız yeterlidir. Eğer açık bağlantı varsa, dll kullanılacağı için Windows .dll’in üstüne yazmanıza izin vermez. Açık veritabanı bağlantılarını kesip, .dll’i öyle kopyalayın.
* Eğer .dll’deki fonksiyonlarda parametre değişikliği yaptıysanız, UDF tanımlarını silin (drop), .dll’i kopyalayıp tanımları değişen parametreleri de dikkate alarak tekrar yapın.
* Pchar bir parametre kullanırsanız, yapacağınız işleme göre Trim kullanıp boşlukları atmak iyi olur.
* Firebird’teki tiplerin Delphi karşılığı :
o integer : integer
o char veya varchar : pchar
o double precision : double
* Firebird’e kullanabileceğiniz, pek çok hazır fonksiyon var. Bunlar işinizi görebilir, bu fonksiyonları incelemeyi unutmayın : viewtopic.php?t=22223
UDF’imizi biraz daha geliştirelim :
String İşlemleri
String işlemleri için Pchar parametre kullanmak gerekiyor. Pchar C tipi string’tir ve sonunda #0 karakteri bulunur. Bu #0 karakteri string’in sonunu gösterir.
Verilen string’in uzunluğunu veren bir UDF yazalım, ismi “Uzunluk” olsun.
Kod: Tümünü seç
function Uzunluk(Str:Pchar):integer;stdcall;
...
function Uzunluk(Str:Pchar):integer;
var
i : integer;
begin
Result := -1;
i := 0;
while (Str[i] <> #0) do
Inc(i);
Result := i;
end;
Firebird tarafında UDF’i tanımlarken, Cstring kullanılır :
Kod: Tümünü seç
DECLARE EXTERNAL FUNCTION Uzunluk
CSTRING(255)
RETURNS integer by value
ENTRY_POINT 'Uzunluk' MODULE_NAME 'DelphiTurkiye';
Tarih İşlemleri
Tarih işlemleri için, Firebird’ten gelen tarih değerini api fonksiyonları kullanarak, Delphi’nin anlayacağı şekle çevirmek gerekiyor.
İki tane fonksiyon yazacağız, biri Delphi’deki FormatDateTime fonksiyonunun yaptığı işi Firebird tarafında yapacak, diğerinde de iki tarih arasındaki ay sayısını bulacağız. Kodlar :
Kod: Tümünü seç
const
IBASE_DLL = 'gds32.dll';
Kod: Tümünü seç
type
Long = LongInt; // 32 bit signed
ULong = DWord; // 32 bit unsigned
Kod: Tümünü seç
type
TM = record
tm_sec : integer; // Saniye
tm_min : integer; // Dakika
tm_hour : integer; // Saat (0-23)
tm_mday : integer; // aydaki gün (1-31)
tm_mon : integer; // Ay (0-11)
tm_year : integer; // Yil (takvim yılından 1900 eksiği)
tm_wday : integer; // Haftanın günü (0-6) Pazar = 0)
tm_yday : integer; // Yılın günü (0-365)
tm_isdst : integer; // gün ışığından faydalanıyorsa 0)
end;
PTM = ^TM;
ISC_TIMESTAMP = record
timestamp_date : Long;
timestamp_time : ULong;
end;
PISC_TIMESTAMP = ^ISC_TIMESTAMP;
Kod: Tümünü seç
procedure isc_decode_sql_date (var ib_date: Long; tm_date: PTM); stdcall; external IBASE_DLL;
Kod: Tümünü seç
function DuzenliTarih(FormatStr:Pchar; var ib_date : Long):Pchar;stdcall;
...
function DuzenliTarih(FormatStr:Pchar; var ib_date : Long):Pchar;
var
tm_date : TM;
TempTarih : TDateTime;
begin
if FormatStr = nil then
Result := nil
else
begin
isc_decode_sql_date(ib_date, @tm_date);
TempTarih := EncodeDate(tm_date.tm_year + 1900, tm_date.tm_mon + 1, tm_date.tm_mday);
Result := PChar(FormatDateTime(string (FormatStr), TempTarih));
end;
end;
String ile pchar değerini stringe, pchar ile de stringi pchar’a çevirebilirsiniz.
İkinci tarih UDF’imiz :
Kod: Tümünü seç
function KacAyVar(var IlkTarih, IkinciTarih : Long):integer;stdcall;
...
function KacAyVar(var IlkTarih, IkinciTarih : Long):integer;
var
tm_ilk, tm_ikinci : TM;
TempIlkTarih, TempIkinciTarih : TDateTime;
begin
Result := -1;
isc_decode_sql_date(IlkTarih, @tm_ilk);
isc_decode_sql_date(IkinciTarih, @tm_ikinci);
TempIlkTarih := EncodeDate(tm_ilk.tm_year + 1900, tm_ilk.tm_mon+1, tm_ilk.tm_mday);
TempIkinciTarih := EncodeDate(tm_ikinci.tm_year + 1900, tm_ikinci.tm_mon+1, tm_ikinci.tm_mday);
Result := MonthsBetween(TempIlkTarih, TempIkinciTarih);
end;
Kod: Tümünü seç
DECLARE EXTERNAL FUNCTION DUZENLITARIH
CSTRING(64),
DATE
RETURNS CSTRING(128) FREE_IT
ENTRY_POINT 'DuzenliTarih' MODULE_NAME 'DelphiTurkiye';
DECLARE EXTERNAL FUNCTION KACAYVAR
DATE,
DATE
RETURNS INTEGER BY VALUE
ENTRY_POINT 'KacAyVar' MODULE_NAME 'DelphiTurkiye';
Bazen bir işlem yaparken, fonksiyon istediğiniz sonuçları döndürmeyebilir veya bağlantı kopabilir. Bu durumda büyük ihtimalle kodunuzda bir yanlış yapmışsınız demektir. Nerede yanlış yaptığınızı görmek için fonksiyon içindeki bazı değerleri görmek isteyebilirsiniz. Bu durumda en kolay yöntem, bir text dosya açıp istediğiniz değerleri oraya yazdırmanızdır.
Mesela DuzenliTarih fonksiyonumuz düzgün çalışmıyor ve format string’imizin düzgün gelip gelmediğini görmek istiyoruz. Aşağıdaki gibi bir dosya açıp, fonksiyonu Firebird’te denedikten sonra fonksiyon c:\debug.txt içerisine gelen FormatStr parametresinin değerini yazar.
Kod: Tümünü seç
function DuzenliTarih(FormatStr:Pchar; var ib_date : Long):Pchar;
var
tm_date : TM;
TempTarih : TDateTime;
DebugFile : TextFile;
begin
try
AssignFile(DebugFile, ‘c:\debug.txt’);
ReWrite(DebugFile);
WriteLn(DebugFile, string(FormatStr));
finally
CloseFile(DebugFile);
end;
...
Kolay gelsin.