DBExpress Bug di Delphi 2006
Cerita dimulai ketika saya membantu seorang teman melakukan debugging untuk mencari penyebab tidak stabilnya aplikasi yang dibuat dengan Delphi 2006. Ketika aplikasi dijalankan sering muncul Error yang tidak diduga dan sulit direkonstruksi. Aplikasi menggunakan FireBird dengan DBExpress serta ClientDatset dan DatasetProvider.
Akhirnya ada sebuah error yang bisa direkonstruksi, yaitu :
Ketika menggunakan TSQLDataset dimana query menghasilkan record kosong dan sebuah field diakses dengan method FieldByName, muncul error Acces Violation. Error ini bisa ditangani dengan memeriksa apakah record yang dihasilkan kosong atau tidak. Tapi ternyata aplikasi masih tidak stabil.
Investigasi lebih jauh akhirnya diketemukan bahwa Ketika dilakukan ApplyUpdate dari ClientDatset yang sebelumnya masih kosong tetapi terjadi kegagalan karena database integrity, aplikasi menjadi crash. Setelah melakukan Investigasi lebih jauh ternyata diketahui bahwa mekanisme error reconciliation melakukan pemanggilan method FindField dari TSQLDataset. Ketika TSQLDataset yang digunakan masih kosong, pemanggilan method ini menimbulkan error access violation yang tidak tertangani dan menyebabkan aplikasi crash.
Ternyata akar permasalahannya sama, yaitu pemanggilan FindField (juga FieldByName) terhadap TSQLDataset yang query didalamnya menghasilkan recordset kosong. Error ini mudah sekali direkonstruksi dengan cara :
Buat sebuah TSQLDataset yang terkoneksi ke TSQLConnection.
Query dalam TSQLDataset dibuat agar menghasilkan recordset kosong
Saya menggunakan Employee.gdb dengan query select * from country where country = 'IND'
Kemudian buat kode untuk mengakses field dengan method FindField, seperti contoh gambar berikut ini.
Error terjadi pada baris vValue := vField.Value;
Tapi error ini tidak muncul di Delphi7. Tadinya saya mengira ada hubungannya dengan driver database yang digunakan. Tapi saya coba dengan MySQL juga muncul error, meskipun tidak menyebabkan crash. Error yang muncul ketika saya gunakan MySQL adalah Invalid Handle.
Selanjutnya saya melakukan debugging menggunakan option Use Debug DCUs di Delphi7 dan di Delphi 2006. Saya lihat ada perbedaan kode yang signifikan yaitu pada baris 5988 di D2006
Di Delpi 7 baris yang di highlight hanya berisi if EOF then
Saya curiga juga dengan angka 157041. Ternyata seperti dugaan saya, angka tersebut adalah nomor bug tracking (internal) di Delphi. Dan ternyata penambahan and not BOF pada kode diatas adalah untuk menyelesaikan Bug Calculated Field di TSQLQuery tidak muncul pada record pertama. Hmm.... penyelesaian masalah yang menimbulkan masalah baru (yang lebih besar).
Saya sempat tanya juga ke sdr Wisnu yang Delphi 2006-nya sudah di-update dengan Update 2, ternyata error masih muncul juga. Sdr Wisnu menggunakan MySQL dan muncul error Invalid Handle.
Sekarang, bagaimana solusinya ?
Saya memilih membuang and not BOF (seperti pada Delphi 7) dengan resiko bug Calculated field di SQLQuery. Resiko ini saya ambil dengan pertimbangan bahwa saya tidak pernah menggunakan Calculated Field di SQLQuery. Kalo perlu calculated field, bisa dibuat di ClientDataset.
Langkah-langkahnya adalah sebagai berikut.
- Copy file SQLExpr.pas ke folder lain (misalnya folder tempat projek)
- Ubah kode di baris 5988 tersebut dengan membuang atau me-remark and not BOF
- Include-kan file SQLExpr tersebut dalam Project.