2013/08/19

Dbexpress driver is Thread Safe?

這陣子在為Dbexpress Driver for SQL Server的選擇在測試著,進行到Debug DataSetProvider.OnBeforeUpdateRecord事件時,卻意外的發現各家的Driver在對Thread使用上的處理都不盡相同,於是做了點小小的研究。

2013/08/07

DataSnap 應用中 Variant 會傻傻分不清 NULL 和 Empty String 的問題

這個問題會在 Delphi 7 ~ 2009 版本上出現,官方雖然說在 Delphi2009 的 SP4 (Database pack update) 上修正完成,但實際上卻是依然無解。


EMBT,你不是說已經搞定了嗎?
哎呀!原來是CodeGear的錯!

要還原現場也十分簡單,照著
Report #:  8482 TWidestring seems to convert empty strings to NULL
 的步驟做就可以輕鬆重現經典bug

於是我也依樣劃葫蘆地再發一次,只是角色換成了DBEXPRESS元件而已。
Report #117649 [SQL Server, NVARCHAR] TWideStringField seems to convert empty strings to NULL

想不到當天就有了回覆了,不知道是不是官方人員的回覆,但一整個感動啊!

回文的內容簡貼如下:
I tested your case with Delphi XE2 Update4 with SQL Server 2008 R2 on Windows 7. But, the error does not occur.
I will check the internal status of QC#67982 etc.. 



簡單的說,在「QC#67982」早就已經修補了這個Bug。

那QC#67982是什麼東西? 查了好久才知道原來是打包成「Delphi 2009 Update4」了。
我使用的版本早已經是 Last Version 了
 但現實是並沒有解決我的問題啊!!!
 
接著就看到了這篇:
Report #71984 ApplyUpdates with empty string fields yields null values in where clause 


看來是轉成SQL的時侯出怪手了,那到底DataSetProvider到底轉了什麼資料出來呢?
 *使用 Open Dbexpress + SQLMonitor 擷錄結果:

ISQLCommand.SetParameter: 1; value: 1 INPUT WVARCHAR: '124'
ISQLCommand.SetParameter: 2; value: 2 INPUT INTEGER: 1
ISQLCommand.SetParameter: 3; value: 3 INPUT WVARCHAR: '0001'
ISQLCommand.Execute: update "DemoData"  set
 "SubCode" = ?
where
 "DemoID" = ? and
 "DemoCode" = ? and
 "SubCode" is null


你你你…… SubCode 明明是not null、是empty string('')啊!你這個 NULL 跑出來是要鬧場的啊!

找到問題了,那就來解決吧!
照著剛剛的 Report #71984 也的確是能解決這個問題,修改Provider.pas後的擷取結果如下:

ISQLCommand.SetParameter: 1; value: 1 INPUT WVARCHAR: '124'
ISQLCommand.SetParameter: 2; value: 2 INPUT INTEGER: 1
ISQLCommand.SetParameter: 3; value: 3 INPUT WVARCHAR: '0001'
ISQLCommand.Execute: update "DemoData"  set
 "SubCode" = ?
where
 "DemoID" = ? and
 "DemoCode" = ? and

 ("SubCode" is null or "SubCode" = '''')

果真是解決了這個問題,但SQL看起來怪怪的。問題似乎不在Provider身上。

真正的原因是:
WHERE底下出現了「IS NULL」。那也就是TField.OleValue得到了應為「Empty String」的Variant 卻回傳了「NULL」詭異情形。

既然2010後已經解決,那就別理這個問題吧。


開玩笑的。


其實還好Delphi的Variants單元寫得很好,有幾個判斷的function,就直接放上Code吧。

  if VarIsStr(ClientdataSet1.Fields[0].OldValue) then
    Result := VarToStr(ClientdataSet1.Fields[0].OldValue);

如此一來問題就暫時得到解決了。
不過,能升級還是盡可能升級到XE以上的版本吧!