SOD框架使用金倉數(shù)據(jù)庫“踩坑記”
SOD框架使用金倉數(shù)據(jù)庫“踩坑記”,嚴(yán)格來說是使用金倉數(shù)據(jù)庫過程的踩坑記,并不是使用SOD框架來訪問金倉數(shù)據(jù)庫才會發(fā)生的問題,SOD框架的網(wǎng)友多年前就封裝了人大金倉(現(xiàn)在已經(jīng)改名為“電科金倉”)和達(dá)夢數(shù)據(jù)庫的SOD框架數(shù)據(jù)提供程序,對應(yīng)的Nuget包名字分別是 PDF.NET.SOD.Dameng.Provider, PDF.NET.SOD.Kingbase.Provider ,所以當(dāng)我第一次使用金倉數(shù)據(jù)庫遇到問題時候疑惑為什么使用SOD框架會有問題,而別的ORM框架似乎沒有這樣的問題?
第一個問題:神秘的sys_stat_scan_tables 角色
SOD框架決定全面支持.NET6的時候,對金倉數(shù)據(jù)訪問提供程序也進(jìn)行了升級,使用的是Kdbndp 的Nuget包 8.0版本,正好它的說明也是只支持.NET6以上版本。升級后的SOD框架金倉數(shù)據(jù)訪問提供程序Nuget包ID為PWMIS.SOD.Kingbase.Provider ,使用它訪問最金倉V9數(shù)據(jù)庫的時候出現(xiàn)下面的問題:
"22P02: invalid input value for enum information_schema.table_type_enum: \"FOREIGN\"\r\n\r\nPOSITION: 135"
經(jīng)排查這個問題發(fā)生在調(diào)用ADO.NET的DbConnection的抽象方法GetSchema 方法有關(guān),可以使用下面的代碼進(jìn)行測試:
using Kdbndp; using System.Data; namespace KingbaseTest { internal class Program { static void Main(string[] args) { Console.WriteLine("Kingbase For .NET6 Access Test."); string connStr = System.Configuration.ConfigurationManager.ConnectionStrings[1].ConnectionString; Console.WriteLine(connStr); using (KdbndpConnection conn = new KdbndpConnection(connStr)) { conn.Open(); Console.WriteLine("Database Connected OK."); DataTable dt = conn.GetSchema(); Console.WriteLine("GetScheme OK,Database has Schemas count:{0}",dt.Rows.Count); DataTable dt2 = conn.GetSchema("Tables"); Console.WriteLine("Database has tables count:{0}", dt2.Rows.Count); conn.Close(); } Console.WriteLine("Test OK"); } } }
與金倉技術(shù)人員進(jìn)行多次溝通后,終于發(fā)現(xiàn)問題在金倉的MySQL數(shù)據(jù)庫兼容模式下,非System用戶訪問金倉數(shù)據(jù)庫的時候?qū)τ?information_schema.tables 對象沒有訪問權(quán)限:
select table_schema FROM information_schema.tables group by table_schema;
提示用戶沒有訪問 sys_freespace 函數(shù)的權(quán)限:
42501: permission denied for function sys_freespace
出現(xiàn)這個問題的時候必須授權(quán)當(dāng)前訪問金倉數(shù)據(jù)庫的用戶有 sys_stat_scan_tables 角色。
劃重點(diǎn):這個問題很很重要,當(dāng)你需要使用SOD框架的Code First編碼方式使用金倉數(shù)據(jù)庫的時候必須要確保用戶有 sys_stat_scan_tables 角色權(quán)限。
劃重點(diǎn):這個問題僅出現(xiàn)在某些定制版的V8版本MySQL兼容模式中,V9版本沒有此問題。
第二個問題:令人迷惑的金倉驅(qū)動程序版本
當(dāng)程序運(yùn)行在金倉為客戶定制的某V8版本的MySQL兼容模式數(shù)據(jù)庫上的時候,運(yùn)行上面這個測試程序又出現(xiàn)了下面的問題:
System.ArgumentException HResult=0x80070057 Message=A KingbaseES type with the name int16 was not found in the database Source=Kdbndp StackTrace: 在 Kdbndp.Internal.KdbndpDatabaseInfo.GetKingbaseTypeByName(String pgName) 在 Kdbndp.TypeMapping.BuiltInTypeHandlerResolver..ctor(KdbndpConnector connector) 在 Kdbndp.TypeMapping.BuiltInTypeHandlerResolverFactory.Create(KdbndpConnector connector) 在 Kdbndp.TypeMapping.ConnectorTypeMapper.Reset() 在 Kdbndp.Internal.KdbndpConnector.<LoadDatabaseInfo>d__199.MoveNext() 在 Kdbndp.Internal.KdbndpConnector.<Open>d__198.MoveNext() 在 Kdbndp.ConnectorPool.<OpenNewConnector>d__34.MoveNext() 在 Kdbndp.ConnectorPool.<<Get>g__RentAsync|31_0>d.MoveNext() 在 Kdbndp.KdbndpConnection.<<Open>g__OpenAsync|47_0>d.MoveNext() 在 Kdbndp.KdbndpConnection.Open()
又是一個復(fù)雜的排查過程,在反復(fù)確認(rèn)了數(shù)據(jù)庫的版本之后,金倉技術(shù)人員找到了問題原因:驅(qū)動程序版本不正確。使用金倉公司指定的,對應(yīng)的Nuget包名字為 Kdbndp_V9 這個.NET驅(qū)動程序后問題果然解決。[文件下載]。于是,我不得不用這個驅(qū)動程序重新封裝了一個SOD框架訪問金倉數(shù)據(jù)庫的數(shù)據(jù)訪問提供程序,這就是為什么有PWMIS.SOD.Kingbase.Provider 了之后,還需要 PWMIS.SOD.Kingbase.Provider.Net6V9,PWMIS.SOD.Kingbase.Provider.Net8V9 兩個Nuget包的原因。簡單總結(jié)一下:
- PDF.NET.SOD.Kingbase.Provider --SOD框架適用于.NET 4.x 版本的金倉數(shù)據(jù)訪問提供程序;
- PWMIS.SOD.Kingbase.Provider --SOD框架適用于.NET 6以上,采用Kdbndp 8.0版本驅(qū)動程序,可以訪問金倉V8以及V9版本的數(shù)據(jù)庫;
- PWMIS.SOD.Kingbase.Provider.Net6V9 --SOD框架使用.NET 6版本的(也可以支持.NET 8),采用Kdbndp_V9 版本驅(qū)動程序,專為金倉V9版本和某些定制的V8版本數(shù)據(jù)庫訪問而定制的驅(qū)動程序,當(dāng)然也可以訪問V8版數(shù)據(jù)庫。
- PWMIS.SOD.Kingbase.Provider.Net8V9 --SOD框架使用.NET 8版本的,采用Kdbndp_V9 版本驅(qū)動程序,專為金倉V9版本而定制的驅(qū)動程序,可能不支持某些定制版本的V8版數(shù)據(jù)庫。
綜上,你在具體使用金倉數(shù)據(jù)庫的時候需要使用哪個版本的SOD框架提供程序,可以根據(jù)情況測試后使用。
劃重點(diǎn):如果你默認(rèn)使用金倉的Oracle兼容模式,就不會遇到上面兩個問題,直接使用PWMIS.SOD.Kingbase.Provider 驅(qū)動程序即可。
為了方便在SOD框架中使用上述不同的驅(qū)動程序,可以添加一個應(yīng)用程序配置文件 app.config文件,在connectionStrings 配置節(jié)添加如下配置內(nèi)容:
<connectionStrings> <!-- <add name="local1" connectionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321" providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient"/> <add name="local2" connectionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321" providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient.Net6V9"/> --> <add name="default" connectionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321" providerName="PWMIS.DataProvider.Data.Kingbase,PWMIS.KingbaseClient.Net8V9"/> </connectionStrings>
然后如下使用金倉數(shù)據(jù)訪問對象:
PWMIS.DataProvider.Data.Kingbase kingbase= AdoHelper.CreateHelper("default"); var dataset= kingbase.ExecuteDataSet("select * from [Table1] ");
第三個問題:神秘的SQL_MODE
像金倉、達(dá)夢這樣的國產(chǎn)化數(shù)據(jù)庫其誕生之初就號稱兼容Oracle,SQL Server,MySQL等市場常用的數(shù)據(jù)庫,聲稱可以無縫兼容基于這些數(shù)據(jù)庫開發(fā)的應(yīng)用,自然在使用金倉數(shù)據(jù)庫的時候會有數(shù)據(jù)庫兼容性的問題,而我們在通常使用中似乎真的沒有遇到兼容性問題,直到遇見了SQL-MODE問題。
問題首先發(fā)生在應(yīng)用程序運(yùn)行在客戶定制的某金倉V8版本出現(xiàn)下面類似的錯誤:
syntax error at or near ""Table1"" CREATE SEQUENCE table1_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1; CREATE TABLE "Table1"( "ID" integer DEFAULT nextval('table1_id_seq':regclass) NOT NULL PRIMARY KEY )
很明顯,當(dāng)前數(shù)據(jù)庫不識別雙引號,Oracle數(shù)據(jù)庫對象使用雙引號進(jìn)行標(biāo)識,MySQL數(shù)據(jù)庫使用反單引號進(jìn)行標(biāo)識,由于當(dāng)前數(shù)據(jù)庫設(shè)置成MySQL兼容模式,上面創(chuàng)建表的語句自然報錯,正確的應(yīng)該是:
CREATE SEQUENCE table1_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1; CREATE TABLE `Table1`( `ID` integer DEFAULT nextval('table1_id_seq':regclass) NOT NULL PRIMARY KEY )
為啥之前在測試的V9數(shù)據(jù)庫和V8數(shù)據(jù)庫都沒有遇到這個問題,在客戶定制的V8版本就出現(xiàn)這個問題了呢?請金倉的技術(shù)人員排查后發(fā)現(xiàn),原來兩個數(shù)據(jù)庫的SQL_MODE設(shè)置不一樣。
SQL_MODE設(shè)置在數(shù)據(jù)庫運(yùn)行和SQL語句執(zhí)行中具有重要作用,它可以規(guī)范SQL語句的執(zhí)行行為,控制SQL語法的兼容性, 優(yōu)化查詢性能和安全性,控制數(shù)據(jù)完整性約束,影響數(shù)據(jù)類型和格式處理。SQL_MODE的默認(rèn)值為:
SHOW SQL_MODE; ONLY_FULL_GROUP_BY,ANSI_QUOTES
在默認(rèn)情況下,金倉數(shù)據(jù)庫會嚴(yán)格限制GROUP BY子句的使用,要求在SELECT列表中出現(xiàn)的非聚合列必須同時出現(xiàn)在GROUP BY子句中。金倉數(shù)據(jù)庫的SQL_MODE默認(rèn)值為ONLY_FULL_GROUP_BY。這意味著在默認(rèn)情況下,金倉數(shù)據(jù)庫會嚴(yán)格限制GROUP BY子句的使用,要求在SELECT列表中出現(xiàn)的非聚合列必須同時出現(xiàn)在GROUP BY子句中SQL_MODE設(shè)置為ANSI_QUOTES的作用是改變SQL語句中雙引號(")的語義,使其符合ANSI SQL標(biāo)準(zhǔn)。
這也是為何在默認(rèn)情況下程序運(yùn)行沒有報錯的原因,然而客戶的數(shù)據(jù)庫中SQL_MODE設(shè)置為空,并且數(shù)據(jù)庫兼容模式為MySQL,所以在SQL查詢中對于表名字、字段名字必須使用反單引號。
于是,SOD的金倉數(shù)據(jù)庫訪問提供程序也開啟了設(shè)置兼容模式:
private string _DataBaseMode = "Oracle"; private char _dbSplitChar = '"'; /// <summary> /// 獲取或者設(shè)置數(shù)據(jù)庫兼容模式,可以指定的模式有Oracle,MySQL,SQLServer,PostgreSQL,默認(rèn)為Oracle /// </summary> public string DataBaseMode { get { return _DataBaseMode; } set { string mode = value.ToLower(); if (mode == "oracle" || mode == "postgresql") { _DataBaseMode = value; _dbSplitChar = '"'; } else if (mode == "mysql") { _DataBaseMode = value; _dbSplitChar = '`'; } else if (mode == "sqlserver") { _DataBaseMode = value; _dbSplitChar = '[';//']' } else { throw new Exception("Kingbase Database_mode must one is Oracle,MySQL,SQLServer."); } } }
之后,在程序中這樣使用:
var kingbase= new PWMIS.DataProvider.Data.Kingbase(); kingbase.ConntctionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321"; kingbase.DataBaseMode="MySQL"; var dataset= kingbase.ExecuteDataSet("select * from `Table1` ");
注意:
上面的示例方式是在SOD的金倉數(shù)據(jù)訪問提供程序 PWMIS.SOD.Kingbase.Provider.Net6V9 和 PWMIS.SOD.Kingbase.Provider.Net8V9 V6.0.1版本支持,如果使用 V6.0.0版本,可以全局設(shè)置,上面的示例代碼修改如下:
var kingbase= new PWMIS.DataProvider.Data.Kingbase(); kingbase.ConntctionString="Server=127.0.0.1;User Id=system;Password=system;Database=test;Port=54321"; //V6.0.0 版本使用靜態(tài)全局設(shè)置數(shù)據(jù)庫兼容模式: PWMIS.DataProvider.Data.Kingbase.DataBaseMode="MySQL"; var dataset= kingbase.ExecuteDataSet("select * from `Table1` ");
第四個問題:數(shù)據(jù)類型兼容的問題
金倉V8以及V9各個版本的數(shù)據(jù)類型并不一定完全兼容,并且它與MySQL的類型也不完全兼容,因此建議在數(shù)據(jù)庫遷移的時候,一定要使用Code First方式,由程序自動創(chuàng)建表結(jié)構(gòu),否則遷移過程將有無盡的痛苦,這里不做過多表述。
由于時間關(guān)系,先到這里。
posted on 2025-07-06 18:38 深藍(lán)醫(yī)生 閱讀(388) 評論(1) 收藏 舉報
浙公網(wǎng)安備 33010602011771號