顯示具有 [DBeXpress] 標籤的文章。 顯示所有文章
顯示具有 [DBeXpress] 標籤的文章。 顯示所有文章

2026/09/30

翻出 Delphi dbExpress 20 年的老舊 Bug

 作者:吳祐賓  

 


 

 

先說結論,此 Bug 有兩個解決方案:

  1. 自製 Bug Fixed;直接修正 Data.DBXCommon.pas
  2. 使用 Devart DBX Driver 強制避開

 

一行 TSQLConnection.ExecuteDirect,讓我運行多年的應用程式經歷 20 年一遇的 Bug,更幽默的是,這 Bug 放到 2026 年最新的 D13.2,仍舊存在。

 

由於現在習慣全寫 T-SQL 處理邏輯問題,較少使用 Delphi 資料集元件,為了加快 SQL 指令傳遞的效率,我選擇使用 TSQLConnection.ExecuteDirect,以執行我那高達 5 Mb 的巨型 SQL 檔。

 

 

發現問題的場合

 

一開始執行的很順利,但近幾個月,使用者頻頻回報說有資料遺失的情況。然而,我的 T-SQL 指令是這樣的:

 


SET XACT_ABORT ON;
SET NOCOUNT ON;

BEGIN TRY
    BEGIN TRANSACTION;

    ...

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- 判斷若有未完成的交易則進行復原
    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END;

    -- 重新拋出錯誤資訊供呼叫端捕捉
    THROW;
END CATCH;

 

 

若是出錯,也會回傳錯誤才是,但實際上程式的反應是:

 

存檔 -> 沒錯誤 -> Commit -> 資料消失

 

 

相同指令,放到 SSMS 去跑,一切正常。

 

嗯,不出意外的,又出意外了

 

 

尋找源頭 


使用 AI 查了下我的 T-SQL,回報的內容重點在:

 

ExecuteDirect 是用來執行不回傳資料集的 SQL;批次中途出現真正的 SELECT result set,DBExpress 驅動可能只處理到第一個結果集、取消或中止剩餘批次
 

看來是 ExecuteDirect 中觸發了 MARS(Multiple Active Result Sets,多重作用結果集)造成的問題。

 

但 Devart 在說明中確實提到它家的 DBX Driver 有支援 MARS 功能,這或許是 Devart 的 Bug 也說不定。

 

於是我使用以下 SQL 範例進行測試:


LCommand.Text :=
  'SET NOCOUNT ON;' + sLineBreak +
  'SELECT 1 AS n;' + sLineBreak +
  'SELECT 2 AS n;' + sLineBreak +
  'SELECT 3 AS n;';

 

使用 TSQLStoredProc 進行測試,和 Native DBX Driver 進行比較,結果 Native DBX Driver 就是只有一包,沒有其它的內容回來,但 Native DBX Driver 早就被宣告不會更新,所以有這結果不意外。

倒是 Devart DBX Driver 出現無限迴圈的結果。因為 TSQLStoredProc 已是包殼後的高階元件,後來改用 TDBXCommand 的底層物件測試,結果也是相同。

 


 

 


procedure TForm2.MultiResultSetTest(ACn: TSQLConnection; AMemo: TMemo);
var
  LCommand: TDBXCommand;
  LReader: TDBXReader;
  LLog: TStringList;
  LResultSetCount: Integer;
  LResultRowCount: Integer;
  LCurrentRowCount: Integer;
begin
  LCommand := nil;
  LReader := nil;
  LLog := TStringList.Create;

  try
    LResultSetCount := 0;
    LResultRowCount := 0;

    ACn.Connected := True;

    LCommand := ACn.DBXConnection.CreateCommand;
    LCommand.CommandType := TDBXCommandTypes.DbxSQL;
    LCommand.Text :=
      'SET NOCOUNT ON;' + sLineBreak +
      'SELECT 1 AS n;' + sLineBreak +
      'SELECT 2 AS n;' + sLineBreak +
      'SELECT 3 AS n;';

    LCommand.Prepare;
    LReader := LCommand.ExecuteQuery;

    while (LReader <> nil) and (LResultSetCount < 9) do
    begin
      Inc(LResultSetCount);
      LCurrentRowCount := 0;

      while LReader.Next do
      begin
        LLog.Add('First Value: ' + LReader.Value[0].AsString);
        Inc(LCurrentRowCount);
        Inc(LResultRowCount);
      end;

      LLog.Add(Format(
        'Result set #%d: %d row(s)',
        [LResultSetCount, LCurrentRowCount]));

      FreeAndNil(LReader);

      // Directly test the low-level DBX API.
      LReader := LCommand.GetNextReader;
    end;

    LLog.Add('');
    LLog.Add(Format(
      'ResultSetCount = %d',
      [LResultSetCount]));

    LLog.Add(Format(
      'ResultRowCount = %d',
      [LResultRowCount]));

    AMemo.Lines.Assign(LLog);
  finally
    FreeAndNil(LReader);
    FreeAndNil(LCommand);
    LLog.Free;
  end;
end;

 

真相是,不只 Native Driver 有問題,Framework 也有問題

 

自 Delphi XE DBX Driver 支援到 SQL Server Native Client (SNAC) 2008 版本後,就沒有再往後更新,有 MARS 支援,設定了卻沒有任何作用。

 

Devart 有功能,就代表 Native DBX Driver 是有問題的。

 

Devart 的無窮迴圈問題,是出在 Next 取得 Reader 時, 因為 Reader 指向錯誤造成的問題,我直接把要修正 Data.DBXCommon.pas 內容列出:

 


--- Embarcadero/Data.DBXCommon.pas
+++ Fixed/Data.DBXCommon.pas
@@
 function TDBXCommand.GetNextReader: TDBXReader;
 begin
   CloseReader;
   Result := DerivedGetNextReader;
   if Assigned(Result) then
-    Result.FCommand := Self;
+  begin
+    Result.FCommand := Self;
+    FLastReader := Result;
+  end;
 end;

 

而且 DBXCommon 單元藕合度極低,所以可以複製到使用 DBX 元件的專案下獨立編譯使用,要另外拿 dcu 去 fixed 官方版本也是可以的。

 

以上是修復 DBX framework 的內容。前提是你要使用 3rd Driver 才有效。


 

Devart 的靈活解方:用 Driver 解 Framework

 

經過 Devart 研發單位的調查,發現 DBX Framework 這個 Bug 自 Delphi 2007 的 DBX4 開始,一直到 2026 年的 D13.2,一直都在。

 

與其修復 Framework,使用 Driver 也有能力繞過此 Bug。

 

目前已知在 2026 年 10 月 9.5.1 版就會提供此次修正。 

 

真令人期待!

 

 

而這段期間我的選擇是

 

回到一開始,我就只是要使用 TSQLConnection.ExecuteDirect,我也不用什麼高階功能。

 

既然都使用 DBX 底層元件,又是使用 SQL Server。

 

所以我選 ADO 底層元件 Command / RecordSet 硬繞!

 

 

2026/07/27

【踩坑】Delphi DBX 的底層幽靈:SQL Server 空字串如何引爆 Range Check Error?

作者:吳祐賓  

 

 


 

 

—— 可以理解為什麼 dbExpress 要被列 Deprecated,因為高階物件和低階抽象完全是兩個風景

 

在老系統現代化(Legacy Modernization)這條宛如「走出埃及」的路上,最恐怖的從來不是程式碼有多爛,而是在這條路上遇上問題時,無人能指引解惑。更恐怖的是,哪怕你選擇留在埃及,這些幽靈依舊存在

 

這次差點被搞到懷疑人生的經歷是:

 

專案從 DataSnap 轉向 Horse,資料透過 dbExpress (DBX) 底層與 Devart 驅動程式拉出來,準備轉成 JSON API

 

這本是走出埃及的日常,結果就在最簡單的一行判斷上,我被狠狠教做人了

 

if TDBXValue(Value).IsNull then  // ← 就在這裡直接噴 Range check error

 

而且只在特定資料庫、特定欄位上發生



黑夜降臨:案發現場的詭異症狀


問題欄位是老掉牙的 TEXT 型態,內容看起來明明是空字串
 

但只要碰到這欄位,所有常識判斷全部失效:

  • IsNull 直接爆炸
  • 改用 GetString、GetStream、GetBytes 也會收到 Devart 各種型別衝突的錯誤 (TDBXTypes.BLOB value type cannot be accessed as TDBXTypes.BYTES value type)

 

雖然這和埃及日常沒什麼區別,但那一刻我還真的有慌。這不是單純的 Null 判斷問題,而是某種更底層、更邪門的東西在作祟

 

因為,同樣的 SQL,使用 TSQLQuery / TSQLDataSet 時,完全沒有這問題啊! 

 

 

守衛睜眼 —— 查出真正的狼人

 

由於這和歷史經驗不同, 理所當然往資料庫挖

 

對出問題的資料表下了診斷 SQL:

 

SELECT 
    NotesField,
    DATALENGTH(NotesField) AS DataLen,
    TEXTPTR(NotesField) AS Ptr
FROM LegacyOrderTable
WHERE CategoryCode = 'BUG_TEST';

 

 

結果出來的那一刻,我整個人傻掉


這筆資料的狀態極度詭異:

  • DATALENGTH(NotesField) = 0
  • 但 TEXTPTR(NotesField) 居然是有效的 8 Bytes 指標(像 0xFBFFC67A... 這種)


 

原來,這根本不是什麼無辜的空資料,而是一頭「披著空字串外皮的狼」

 

這東西通常出現在很久以前的資料轉檔、批次匯入,或是老系統某些特殊的寫入行為

 

SQL Server 在行外(Out-of-row)幫它配置了 LOB 指標,但實際上什麼資料都沒塞進去。就像一具只剩下指標的空殼。 

 

 

預言家睜眼:不是追兵,是自己人崩潰 —— Devart 驅動程式的精神分裂

 

最要命的是 Devart 看到這頭「披著空字串外皮的狼」之後的處理方式

 

它偵測到有 TEXTPTR(長度 8),就很「聰明」地把這個欄位標記成 WideStringType,並且把 Size 設為 8

 

而 Delphi 這邊在準備讀取的時候,卻認為這是長度為 0 的空資料。於是在底層 DBXRow_GetBytes 裡,出現了類似這樣的致命操作:

 

PBuf := @Value[ValueOffset];  // Value 是長度 0 的陣列 → 直接 Range check error!

 

 

因為專案有開 {$R+}(範圍檢查),Delphi 非常乾脆地下殺手


這就是典型的「兩個黑盒子互相誤會,所引爆的災難現場」

 


女巫睜眼:三顆解藥


第一顆解藥:SQL 層釜底抽薪


乾淨利落,不讓 Devart Driver 看到那頭「披著空字串外皮的狼」

 

// 1. Alter Table
ALTER TABLE LegacyOrderTable
ALTER COLUMN NotesField NVARCHAR(MAX);

// 2. CAST
SELECT 
    CAST(NotesField AS NVARCHAR(MAX)) AS NotesField,
    CAST(AnotherTextField AS NVARCHAR(MAX)) AS AnotherTextField
FROM LegacyOrderTable

 

只要轉成 NVARCHAR(MAX),Devart 立刻恢復正常,不再亂認 Size = 8

 

這一招幾乎零成本,卻直接斬斷了問題根源 

 


第二顆解藥:程式碼硬控

 

動資料庫其實蠻傷及筋骨的,所以並不是一個好選擇

 

既然只是為了把資料轉成 JSON API,我們大可在轉換的迴圈裡加上防護罩,直接攔截這個怨靈:

 

try
  Result := Value.IsNull;
except
  Result := TJSONNull.Create;
end;

 

 

犧牲一點 except 效能與 Debug 的雜訊,至少 API 不會再半夜隨機暴斃

 

 

第三顆解藥:修改 Driver 原始碼 


很妙的是,Devart 不含原始碼的 DLL 版並沒有這方面的問題,我分析是 {+R} 在封裝 DLL 時被關掉了,同時也沒有吐出任何例外,這 Bug 就與世無爭地活了數十年


但不想依賴 DLL 過活的我,選擇的是含原始碼的版本

 

若你和我一樣有 Devart 的 Source Code 授權,而且真的很想把根治了,可以直接進到 DBXRow_GetBytes 之類的核心方法,加上防護: 

 

if (Value <> nil) and (System.Length(Value) > ValueOffset) then
  PBuf := @Value[ValueOffset]
else
  PBuf := nil;   // 神之一手,拯救無數 Range check error

 

這顆解藥很硬,但一旦改完,整個 Devart Driver 在面對 SQL Server 老 TEXT 時都會變得更穩

 

當然,這問題我已提交給 Devart,目前的版號是 9.5.0 版,未來可以關心之後新版的 History 有沒有相關的記錄出現 



白晝到來:傷害力不高,汙辱性極強


回到開頭說的:dbExpress 之所以被列為 Deprecated,不是沒有原因


更有趣的是,同樣使用 dbExpress,傳統的 TSQLQuery/TSQLDataSet 並未重現此問題;只有直接操作 TDBXReader/TDBXValue 時才踩中地雷。這表示高階 DataSet 與底層 DBX 抽象層在處理 LOB 欄位時,很可能走的是不同路徑。究竟是透過 Stream、額外緩衝層,還是其他容錯機制避開了這個缺陷,仍有待進一步驗證;但可以確定的是,當我直接面對 DBX 最底層抽象時,這頭狼人終於現出原形。 

 

所以:就算你不走出埃及,留在舒適圈裡,這些狼人依然會在正式環境半夜等著你


老系統的坑,從來不是一次就能填完的。它考驗的不是你會不會寫 Code,而是當所有常識都失效時,你願不願意把屍體挖出來解剖。

 

如果你也正在 DataSnap 轉 Horse、DBX 操作踩雷、或任何 Legacy Modernization 的路上,歡迎在留言區分享你的戰場故事

 

願我們一起在走出埃及的路上,相互扶持



2026/07/27 後記:原廠出手將狼人收掉


文章發表後,我持續向 Devart 團隊追蹤此問題


而 Devart 在確認此問題後,在很短的時間修正此問題,並推出 dbExpress for SQL Server 9.5.1 正式更新





在我的環境中也完成驗證,0 長度 TEXT 引發的 Range Validation 已不復現。爽!





 

2026/05/07

Open dbExpress update - 3.2026.05.07 version

作者:吳祐賓 

 

Update: 3.2026.05.07 support Delphi XE through Delphi 13 Florence (Arc / Ent / Professional / Community Edition) -- Github Link

 

In version 3.2026.05.07, the corrections and improvements are as follows:

 

1. Comprehensive support for modern Delphi compiler versions (XE to 13 Florence).

2. Fixed and completed the internal compiler version inheritance chain in dxver.inc.

3. Updated LibSuffix definitions across all package configuration files (Standard, Embedded, and Trace versions).

4. Enhanced runtime driver identification to accurately report the specific Delphi version used for compilation.

5. Modernized ReadMe documentation with multi-language support (English, Traditional Chinese, and Russian UTF-8).

 

 

Open dbExpress 更新 - 3.2026.05.07 版本

 

2026.05.07 支援從 Delphi XE 到 Delphi 13 Florence,主要是為 Professional / Community Edition 中 dbExpress 無 ODBC Driver 的環境提供完整的解決方案。

 

 

在 3.2026.05.07 版本,修正與更新訊息如下:

 

1. 完整相容新世代 Delphi 編譯器定義(支援至最新版 Delphi 13 Florence)。

2. 修正並補齊 dxver.inc 中的版本繼承鏈,確保舊程式碼在新環境中的穩定性。

3. 批次更新所有套件(BPL)的命名規範(LibSuffix),包含標準版與嵌入式版本。

4. 強化驅動程式內部識別邏輯,使執行時日誌(Log)能精確顯示具體的編譯環境版本。

5. 更新 ReadMe 說明文件,加入正體中文說明並修復俄文亂碼問題。

2021/07/26

Devart DBX Driver 試用版關閉腦人的試用訊息方法


試用 Devart dbExpress driver 時,每當程式一執行便會跳出【此為試用版】的警告訊息,導致在測試上還必須多一個按按鈕的動作。

為此,Devart 提供了一個簡單的作法,只要註冊機碼【HKEY_LOCAL_MACHINE\SOFTWARE\Devart\dbExpress\SQL Server】裡,增加【ShowTrialMessage】的 Dword,內容設為 0,便可以在執行時期暫時關閉顯示試用版訊息視窗,其它產品也可如法泡製。

當然,試用 30 天到期時一樣會跳出【已過試用期】的錯誤。😘

和你分享 ❤

See also

2021/05/03

How to embedded DBX DLL for Deployment in project, take Devart Driver as an example.


Table of Contents


English Version

Studying Devart dbExpress driver, readme file for SQL Server :

Users of dbExpress driver for SQL Server with Source Code can embed the driver into the application directly. For information on how to do this refer to Borland documentation.

This sentence mainly explains that Devart company can use certain mechanisms to make the existence of DBX Dll unnecessary when deploying the project, for example: dbexpsda40.dll and so on.

No need to deploy the advantages of DBX DLL

Some DBX Drivers need to install the DB Client, but Devart provides a [Direct Connection] mode, in which the database access components can be directly connected to the DB via TCP without the DB Client. In addition to omitting the conversion of DB Client, there is no need for additional deployment of DBX DLL settings, which can effectively eliminate potential system configuration problems and save time for publishing system configuration.

Disadvantages of not needing to deploy DBX DLL


Although there is no need to deploy DLL, based on the principle of equivalent exchange, the capacity of the DLL will be filled into the EXE we want to deployment, which will cause the execution file to become larger - it grows about 487 Kb.

Prerequisites for use


You need to purchase [version with source code] first, and exchange for knowledge achievements with a small price, I think it is worth it!

How to

After the purchase of the [Include source code version] condition is established, and you only need to add the [DbxSdaDriverLoader] unit to the project, supplemented by the following code, to achieve the goal of [No DBX DLL required]:

uses
  Data.DB, Data.SqlExpr, DBXDevartSQLServer, DbxSdaDriverLoader;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLConnection1.DriverName := 'DevartSQLServerDirectBuiltin';
  SQLConnection1.LibraryName := 'dbexpsda41.dll';
  SQLConnection1.VendorLib := '';  
  SQLConnection1.GetDriverFunc := 'getSQLDriverSQLServerDirect';
  SQLConnection1.Params.Values['OS Authentication'] := 'False';
  SQLConnection1.Params.Values['HostName'] := 'Host';  
  SQLConnection1.Params.Values['User_Name'] := 'sa';
  SQLConnection1.Params.Values['Password'] := '';
  SQLConnection1.Params.Values['Database'] := 'Database';
  SQLConnection1.LoginPrompt := False;
  SQLConnection1.Connected := True;
end;


Summary

Developers using DBX don’t actually need to rush to replace components. Sometimes spend some cost, can bring more value.

The above information is shared with you!

================================================================

中文版

在研究 Devart dbExpress driver for SQL Server 的 Readme 檔案時,我發現了一句古代文字:

Users of dbExpress driver for SQL Server with Source Code can embed the driver into the application directly. For information on how to do this refer to Borland documentation.
這句話主要在說明 Devart 它可以利用某些機制,使佈署專案時可以不需要 DBX Dll 的存在,例如:dbexpsda40.dll 等。

不需佈署 DBX DLL 的優點

有些 DBX Driver 需要安裝 DB Client,但 Devart 提供【直接連接】模式,在此模式下可以讓資料庫存取元件在不用 DB Client 的場合下透過 TCP 直接連接 DB。除了少掉 DB Client 的轉換外,更不需要額外佈署 DBX DLL 的設定,可以有效排除潛在的系統設定問題和節省發佈系統的設定時間。

不需佈署 DBX DLL 的缺點


雖說不用佈署 DLL,但基於等價交換原則,DLL 的容量會填充到我們欲發佈的 EXE 上面,進而造成執行檔變大的情形 -- 大約成長 487 Kb 左右。

使用前提

方便部署的前提為【購買含原始碼版本】,用少許代價換取知識成果,我認為值得!

如何設計

在使用前提【購買含原始碼版本】條件成立下,之後只需在專案中加入【DbxSdaDriverLoader】單元,並輔以底下程式碼,便能達成【不需 DBX DLL】的目標:


uses
  Data.DB, Data.SqlExpr, DBXDevartSQLServer, DbxSdaDriverLoader;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLConnection1.DriverName := 'DevartSQLServerDirectBuiltin';
  SQLConnection1.LibraryName := 'dbexpsda41.dll';
  SQLConnection1.VendorLib := '';  
  SQLConnection1.GetDriverFunc := 'getSQLDriverSQLServerDirect';
  SQLConnection1.Params.Values['OS Authentication'] := 'False';
  SQLConnection1.Params.Values['HostName'] := 'Host';  
  SQLConnection1.Params.Values['User_Name'] := 'sa';
  SQLConnection1.Params.Values['Password'] := '';
  SQLConnection1.Params.Values['Database'] := 'Database';
  SQLConnection1.LoginPrompt := False;
  SQLConnection1.Connected := True;
end;


總結

使用 DBX 的開發者其實不需要急著更換元件,有時使用更好的 Driver 也能夠達到【花得更少,用得更好】的目標,用少許的金錢所帶來的經濟效益遠超乎想像。


以上資訊和你分享!


See also

2021/04/26

Dbexpress (DBX) Factory for SQL SERVER

之前在這篇【那些年 DBeXpress 所教我的事:DBXCommon 單元】文章中提到 DBXCommon 是 Dbexpress (以下簡稱 DBX) 最底層的單元,像是 TDBXCommand 和 TDBXReader 便是 TSQLQuery 等 DBX DataSet 所包裝的內容,同時也是 DataSnap 底層架構,所以掌握 TDBXReader 就可說是掌握 DataSnap 效能這件事一點都不為過。

2021/04/19

Delphi 特規 JSON 和 JavaScript Object (2)

 


前情提要:Delphi 特規 JSON 和 JavaScript Object (1)

已知大匠可以將 Blob 型態轉為 Hex JSON Array,但容量會放大 4 倍是硬傷;而 Memo 型態卻也被視為 Blob 型態,導致傳輸內容更加地龐大,更有堪者,可達放大 6 倍之譜。


追綜原因

Delphi 轉換 JSON 封包主要是利用 TDBXJSONTools.TableToJSON 函式庫內容,它會利用 TDBXReader.ValueType 進行 Delphi Value to JSON Value 轉換:

JsonCell := DBXToJSON(Value.Value[I], Value.ValueType[I].DataType, IsLocalConnection);


而 DBXToJSON 裡對 DataType 解析,節錄如下:

2019/10/23

DBX Error: Driver could not be properly initialized in SQL SERVER


"DBX Error: Driver could not be properly initialized. Client library may be missing, not installed properly, of the wrong version, or the driver may be missing from the system path."

測試專案時並沒有遇到這類的問題,但在WebBroker專案下會出現這樣的 Error。

除了安裝 sqlncli.msi 是必須的之外,由於 DataSnap / WebBroker :
  1. 早期的MSSQL driver使用COM
  2. 框架使用到 Thread

所以【CoInitialize / CoUninitialize】的呼叫也是必要的。

但這錯誤訊息完全無法看出端倪,故記錄在此。


題外話,新版的 MSSQL 為了跨平台,也開始使用ODBC連接,沒了COM,自然也就不用 CoInitialize / CoUninitialize,ODBC for DBX也許就可以拿來用了呢!(笑)


See also

2019/09/23

Auto set up the TDBXParameters DataType

圖/VManninen@flickr


好消息! dbExpress driver 增加對 Linux 平台的支援!

緣起


本以為 dbExpress 不再更新後就要比 BDE 提早領便當,成了史上最悲劇的官方資料庫存取技術代表。

然而在 Devart 的巧手下,2019年又讓 dbExpress 再一次跨好跨滿跨平台。❤❤❤

聽到這個消息超開心的,2002 Borland 時期 Kylix的未竟之夢,總算在 2019 由 Idera / Embarcadero 圓夢完成,真是太感動了!

感動之餘,也想到 DataSnap 轉到 Linux 主機上,是否應該作些準備?

前面提到的【DevExtreme DataGrid Demo with DataSnap Server】章節,已經知道前端要採用與 Delphi 無綠的 JavaScript 應用,而前端最常用的 TClientDataSet / TFdMemTable 也就不會再使用,面對 JavaScript,最終是要包成 JSON。

既然不會再使用 Delphi 前端元件,那資料庫來源在不妨礙開發為前提下能多底層就底層。


有想法,就來試試看!

2019/07/18

ADO in Web application for Delphi


主題雖然說是 ADO,其實泛指利用操作COM*的資料庫 Driver 皆適用,例如:使用到 OleDB / SQL Native Client 的 dbExpress、Devart DAC、FireDAC 元件等。

*元件物件模型(英語:Component Object Model,縮寫COM)

Delphi 在 Web Application 開發會使用到 Thread,所以使用 COM 技術時,必須要使用 CoInitialize / CoUninitialize 來避免存取錯誤。

使用時機通常是伴隨著 Method 開始和結束,例如:

procedure Method;
begin
  CoInitialize(nil);
  ...
  CoUninitialize;
end;

每一個 Method 要設定也太累人,總是想找個簡易的作法,運氣不錯,一找就看到有意思的作法,高手連結在此。

WebModule的【BeforeDispatch】和【AfterDispatch】事件主要控制 Method (Action) 的起始和結束,所以我們可以分別加入 CoInitialize 和 CoUnitialize,如此一來便可以少寫很多的程式碼。

文中的 CGI 模式也可以套用到 Windows Service 專案中,真是長知識了!


See also:

2019/04/29

Open dbExpress update - 2019.04.29 version



Update: 2019.04.29 support 10.3 Rio (Arc / Ent / Professional / Community Edition)


In version 2019.04.29, the corrections are as follows:
  • Fix bugs during the installation period.

中文說明:
2019.04.29 支援 Delphi 10.3 Rio,主要是提供 Professional / Community Edition 中 dbExpress 無 ODBC Driver 的解決方案。

在 2019.04.29 版本,修正訊息如下:
  • 修正安裝時期的Bug

2018/07/26

Open dbExpress update - 2018.07.25 version


Update: 2018.07.25 support 10.2 Tokyo (Arc / Ent / Professional / Community Edition) -- Download Link


The major problem is finally solved.
In version 2017.01.17, the corrections are as follows:

  • Has been excluded from the installation of BPL, the IDE appears to lead to [Duplicates not allowed] error.
  • At the same time to solve the Run-time must be loaded dbxconnections.ini, dbxdrivers.ini problems.
  • Fixed DEMO program can not compile problem.


Details:

Download Open dbExpress 2017.01.17 supported RAD Studio 10.1 Berlin.

2017/12/08

How to set dbExpress ODBC ConnectionString property and TLS support


New ODBC driver for dbExpress in XE2.

You can setting DSN in Data Source (ODBC).

If you want create DSN-less connection, you can to setup ConnectionString in TSQLConnection.params.

The connection string like below:

MS Access

VendorLib=odbcjt32.dll;DRIVER={Microsoft Access Driver (*.mdb)};DBQ=Your.mdb;DriverId=25;FIL=MS Access;MaxBufferSize=2048;PageTimeout=17;coSafeMode=1

=== 2021/10/25 update ====

SQL SERVER

Standard Security with OleDB

ConnectionString=DRIVER={SQL Server};SERVER=HOST_NAME;DATABASE=DB_NAME;UID=USER_NAME;PWD=PASSWORD;

Standard Security with OleDB and Enable MARS

ConnectionString=DRIVER={SQL Server};SERVER=HOST_NAME;DATABASE=DB_NAME;UID=USER_NAME;PWD=PASSWORD;MultipleActiveResultSets=True

Standard Security with SQL Server Native Client 10

ConnectionString=DRIVER={SQL Server Native Client 10.0};SERVER=HOST_NAME;DATABASE=DB_NAME;UID=USER_NAME;PWD=PASSWORD;

Standard Security with SQL Server Native Client 11

ConnectionString=DRIVER={SQL Server Native Client 11.0};SERVER=HOST_NAME;DATABASE=DB_NAME;UID=USER_NAME;PWD=PASSWORD;

Standard Security with SQL Server Native Client 11 and Enable MARS

ConnectionString=DRIVER={SQL Server Native Client 11.0};SERVER=HOST_NAME;DATABASE=DB_NAME;UID=USER_NAME;PWD=PASSWORD;MARS_Connection=yes;

Standard Security with Azure SQL

DriverName=Odbc
IsolationLevel=ReadCommitted
RowSetSize=20
ConnectionString=DRIVER={SQL Server};
SERVER=SERVERNAME.database.windows.net;DATABASE=DB_NAME;UID=USER_NAME;PWD=PASSWORD;


MSSQL driver for dbExpress support TLS protocol with SQL SERVER 2008 and 2008 R2

SQL Server 2008

  1. Install and update to 2008 SP4
  2. Install KB4057114

SQL Server 2008 R2

  1. Install and update to 2008 R2 SP3
  2. Install KB4057113

上述步驟可以賦予 SQL Server 2008/2008 R2 以及「sqlncli10.dll」支援 TLS 加密通訊能力,如此便能使用它連接更高版本的 SQL Server,同時也讓 MSSQL for dbExpress (dbxmss.dll) 可以繼續使用,皆大歡喜!


和你分享 😉

See also

2017/10/01

DBX framework 初探 (二) - In FireDAC


上回介紹了 DBX framework 的好處,我們來看看實作的程式碼:


var
  LDBXConn: TDBXConnection;
  LCmd: TDBXCommand;
  LReader: TDBXReader;
begin
  LDBXConn := SQLConnection1.DBXConnection;
  LCmd := LDBXConn.CreateCommand;
  LCmd.Text := 'SELECT * FROM employee';
  LReader := LCmd.ExecuteQuery;
  ...
  LCmd.Close;
  LCmd.Free;
end;

由上面的原始碼可以看出,DBX framework 是建立在 dbExpress 上,但 FireDAC 的 TFDConnection 沒有內建 DBX framework,難道魚和熊掌不可兼得嗎?


當然可以!


FireDAC 是個海納百川的元件組 (雖然我對它沒有愛),和 dbExpress 結合倒也不是件難事。

使用的元件組是:
  • TFDConnection.
  • TFDPhysTDBXDriverLink.

FireDAC 的連線精靈很方便,只需注意:
  • Driver ID = TDBX (DBX4) or DBX (DBX v1~v3)
  • DriverName = 參考 dbExpress 區的 Driver Name (如 MSSQL、IBLite 等)

若有購買 dbExpress 3rd driver,在 FireDAC 包裝下,可以直接使用 TFDQuery,再也不用 TSQLQuery, TDataSetProvider, TClientDataSet 三個元件、三個步驟了。
































哎呀,被你發現還有後續內容啦。


FDConnection 包裝 dbExpress 後,利用 TFDCustomConnection.CliObj,DBX 元件組便可以利用它,再透過轉型的方式重現。


CliObj = Call Level Interface Object. (白話文是:切到【手動排檔】模式)


它的包裝單元很豐富,表格如下:

Driver  Wrapping class and unit 
Advantage  TFDSConnection, FireDAC.Phys.ADSWrapper.pas 
dbExpress (v <= 3)  ISQLConnection 
InterBase, Firebird  TIBDatabase, FireDAC.Phys.IBWrapper.pas 
MySQL  TMySQLSession, FireDAC.Phys.MySQLWrapper.pas 
Microsoft SQL Server  TODBCConnection, FireDAC.Phys.ODBCWrapper.pas 
Microsoft Access TODBCConnection, FireDAC.Phys.ODBCWrapper.pas
IBM DB2 TODBCConnection, FireDAC.Phys.ODBCWrapper.pas
Sybase SQL Anywhere TODBCConnection, FireDAC.Phys.ODBCWrapper.pas
ODBC TODBCConnection, FireDAC.Phys.ODBCWrapper.pas
Oracle TOCIService, FireDAC.Phys.OracleWrapper.pas 
PostgreSQL  TPgConnection, FireDAC.Phys.PgWrapper.pas 
SQLite  TSQLiteDatabase, FireDAC.Phys.SQLiteWrapper.pas 
TDBX (v >= 4)  TDBXConnection, FireDAC.Phys.TDBX.pas 
DataSnap TDBXConnection, FireDAC.Phys.TDBX.pas
MongoDB TMongoConnection, FireDAC.Phys.MongoDBWrapper.pas


之後只要改一行文字即可,如以下紅字內容:


var
  LDBXConn: TDBXConnection;
  LCmd: TDBXCommand;
  LReader: TDBXReader;
begin
  LDBXConn := TObject(FDConnection1.CliObj) as TDBXConnection;
  LCmd := LDBXConn.CreateCommand;
  LCmd.Text := 'SELECT * FROM employee';
  LReader := LCmd.ExecuteQuery;
  ...
  LCmd.Close;
  LCmd.Free;
end;


警告:效能控請小心服用!


結語:
DBX framework 打從問市以來一直在傳達「以貼近底層的方式發揮程式最大效能」的概念。

但現實的程式設計市場是以【包裝便利】取勝。


從多數 Delpher 對 BDE、ADO 至死不渝,可以看出 Delphi 市場的真實偏好。
官方 FireDAC 的出現,可以看出 Delphi 對市場的妥協。



好的產品不一定賣,會賣的一定是對消費者有價值的產品。


對我們來說,能夠簡化工作量的才是有價值的工具。
在 DBX framework 是加重工作量,而 FireDAC 能簡化開發工作量,對多數的 Delphier 來說,這才是有價值的。

同樣是開發產品的我們,DBX framework 歷史教訓值得學習 ── 要做有價值的產品。

然而,DBX framework 所帶來架構設計上的轉變是不容忽視的,秘密就在……




文內表格裡的彩蛋,您發現了嗎?




See also:

2017/09/26

DBX framework 初探


DBX 框架在官方手冊裡有句話引起我的注意:
Currently, using a TDBXValue is the fastest way to pass a parameter, because these are the internal objects used to manage parameter lists.

新的 DBX 系列元件,如 TDBXCommand, TDBXReader 等,似乎和以前的 TDataSet 有所區別,和 TDataSet 的耦合性相當低。

架構相當底層,和直接呼叫 Driver API 大概只有一線之隔,這也是效能最好的原因。

但也因為如此,手刻程式碼相對變多,以前 TDataSet 會幫忙做的,像是 Params、FieldDefs 自動產生功能等,現在都要自己做,程式碼巨量增加,這成就感還不錯。

【效能】和【程式碼增加】一舉兩得,若你在視程式數量效能為績效的公司,強烈建議使用。(笑)


2017/03/18

10.2 出現後,這個訊息註定讓 Delphier 幾家歡樂幾家愁啊

早在 XE4 時期,在線上手冊就出現這麼一段話:
dbExpress, which is described in this tutorial, is being deprecated.

當時 FireDAC 並不完全成熟,還在想何時才能完全取代 dbExpress 還是未知數,更何況 BDE 現在還活得頭好壯壯啊!

2017/01/14

Open dbExpress update, RAD Studio 10.1 Berlin is supported!

[2017/01/17]
Updated!
Please see " Open dbExpress update - 2017.01.17 version "
===============
I have always thought, Open dbExpress is a very cool product, using ODBC to reach a variety of connection database, and the open source code, allowing more people to maintain.

Do not worry about this product like DBX4MySQL, terminate the support of the incident.

However, Open dbExpress after 2014, also in a tragic situation of unmanned maintenance. Until 2016, support for RAD Studio is limited to XE2.

Continued in the discussion area to see whether people continue to ask whether the continuity of the Open dbExpress support.

Now, I'm happy to announce that Open dbExpress has been revived to support RAD Studio 10.1 Berlin.

Please note! XE3 ~ Seattle not yet supported.

Test MS SQL Server and MS Access can connect and get the data.

Known Issues:

1. Install dbxoodbc240.bpl, click IDE DataExplorer in the tree-node for dbExpress, will output [Duplicates not allowed.] Error message. This looks like an IDE bug.

Duplicates not allowed when click Data Explorer.
Workaround: Remove dbxoodbc240.bpl. Re-install the Open dbExpress package when needed.

2. The version is not updated. This means that the version of the problem still exists.

3. I do not have administrative privileges for this project, so I can not publish this project at Sourceforge. So I put in my personal space for download, if you have permission to upload, you are welcome to upload directly to the update to Sourceforge Open dbExpress.

Finally, download the link here:
Download Open dbExpress 2017.01.14 supported RAD Studio 10.1 Berlin.


Enjoy.

2016/02/05

那些年 DBeXpress 所教我的事:DBXCommon 單元

今天是臺灣農曆年的最後一個工作日,也用技術文章作最後的收尾吧!
==============================


資料庫應用程式設計一直以來都是 Delphi 的強項之一。

Dbx 的目標是為了要讓所有資料庫存取方式統一,在核心上用了很多技巧。

在【dbExpress MetaData 徹底攻略】 文中便大量的應用到 DBX 的獨特設計方法。

例如:【如何取得資料表的所有欄位?】

2015/11/30

dbExpress (DBX4) 智慧取得 MetaData 方法研究


使用了 dbExpress 後,一直沒有機會詳細體驗。

自從寫了【dbExpress SetSchemaInfo 取得結構的勘誤之處】這篇之後,終於有點進展了。 把可能的呼叫法全上了一遍,成果如下圖:




兩種寫法有好有壞,但效能上是相等的,所以要使用哪種方式完全是看個人習慣。

===== 2022/03/02 更新 =====

操作說明

資料庫所有的表格清單(Table List)

在程式啟動時,會連線到指定資料庫,連線成功後會把資料庫裡所有表格放入上圖紅框處。

取得索引清單 (Indexes List)

選定表格時,程式會將該表格所有索引都放入上圖紅框處。

選好索引後,就可以使用畫面下半部的功能查詢其 MetaData。

程式說明

把原本參考 DBXUtils.pas 單元的程式碼改由 Dbexpress.MSSQL.Factory 單元處理,使用起來更物件導向,程式碼更加精簡:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
procedure TForm2.btnCRPrimaryKeyColumnsClick(Sender: TObject);
var
  p_Start, p_Cost : DWORD;
begin
  p_Start:=GetTickCount();
  with TDBXMSSQLFactory.Create(cn.Params) do
  begin
    Memo1.Lines.CommaText := DBXFetchPrimaryKeyColumns(cbxTableName.Text);
    Free;
  end;
  p_Cost:=GetTickCount()-p_Start;
  OutputDebugString(PWideChar('spend time: '+format('%0.3n',[p_Cost/1000])+'s'));
end;

使用 TDBXFactory (TDBXMSSQLFactory) 物件,就可以用【DBXFetchPrimaryKeyColumns】一列命令完成載入,不用再尋找資料庫命令,未來相關的 TDBXMySQLFactory、TDBXPostgreSQLFactory 也不用改程式碼即可使用,等有空再來增加其單元。

MetaData 在 DataSnap 應用場合下是絕對會用到的資料,有 DBXFactory 的幫忙,有助於更精準的控制程式碼的操作。

有興趣的朋友歡迎使用文末連結下載原始碼使用。

 和你分享 😉


See Also