.net訪問PostgreSQL數(shù)據(jù)庫發(fā)生“找不到函數(shù)名”的問題追蹤
PostgreSQL是一個使用廣泛的免費開源的數(shù)據(jù)庫,與MySQL比較,它更適合復(fù)雜的企業(yè)計算任務(wù),而MySQL在互聯(lián)網(wǎng)領(lǐng)域應(yīng)用更為廣泛,究其原因,可能是PostgreSQL擁有支持最多的數(shù)據(jù)類型,甚至包括數(shù)組類型,IP地址類型等,可以使用C,SQL,PL/Pgsql,Phython等多種方式編寫強(qiáng)大的自定義函數(shù),因此特別適合處理復(fù)雜的計算問題。如果想要將SqlServer數(shù)據(jù)庫遷移到其它類型的數(shù)據(jù)庫,PostgreSQL是比較好的選擇。
盡管PostgreSQL使用比較廣泛,但在國內(nèi)相關(guān)資料太少,我們在數(shù)據(jù)庫遷移的過程中,遇到了不少問題,比如我的上一篇文章PostgreSQL的.NET驅(qū)動程序Npgsql中參數(shù)對象的一個Bug 中關(guān)于“找不到函數(shù)名”的問題,解決起來比較“辣手”,可以使用“追蹤”來形容了。本篇繼續(xù)對這個問題進(jìn)行深入探究。
1,問題回顧:
在上一篇文章中說到,有一個PostgreSQL函數(shù) updateattention ,它有一個自定義的函數(shù)參數(shù),下面是函數(shù)頭:
- CREATE OR REPLACE FUNCTION updateattention(dm citext)
- RETURNS void AS
- $BODY$
- --函數(shù)體略
參數(shù)dm 的類型是citex,一個自定義的數(shù)據(jù)類型,使用它來作為函數(shù)參數(shù)或者變量的類型,在進(jìn)行數(shù)據(jù)查詢的時候可以不區(qū)分大小寫,它的定義是:
- CREATE OR REPLACE FUNCTION citext(character)
- RETURNS citext AS
- 'rtrim1'
- LANGUAGE internal IMMUTABLE STRICT
- COST 1;
- ALTER FUNCTION citext(character) OWNER TO postgres;
下面是調(diào)用使用C#調(diào)用updateattention存儲過程的代碼:
- //獲取PostgreSQL的數(shù)據(jù)訪問對象
- PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
- //獲取PostgreSQL的參數(shù)對象
- IDataParameter para = db.GetParameter();
- para.ParameterName = "@dm";
- para.DbType = DbType.AnsiString;
- para.Value = "KF0355";
- db.ExecuteNonQuery("updateattention",
- System.Data.CommandType.StoredProcedure,
- new System.Data.IDataParameter[] { para });
程序使用PDF.NET(PWMIS數(shù)據(jù)開發(fā)框架)的數(shù)據(jù)訪問對象AdoHelper來進(jìn)行相關(guān)的數(shù)據(jù)訪問操作,它采用反射工廠模式,根據(jù)系統(tǒng)的配置實例化具體的數(shù)據(jù)訪問類,這里使用的是PostgreSQL數(shù)據(jù)訪問類。
運行該程序,出現(xiàn)下面的錯誤:
- PDF.NET AdoHelper 查詢錯誤:
- DataBase ErrorMessage:ERROR: 42883: function updatefundattention(text) does not exist
- SQL:updatefundattention
- CommandType:StoredProcedure
- Parameters:
- Parameter["@jjdm"] = "KF0355" //DbType=String
PDF.NET框架內(nèi)置了日志對象和異常對象,它能夠為你拋出詳細(xì)的錯誤信息。
2,問題聚焦
一開始還以為是函數(shù)名大小寫的問題,仔細(xì)核對后發(fā)現(xiàn)沒有問題,然后嘗試對代碼進(jìn)行仔細(xì)排查。
將上面的程序中第6行代碼
- para.DbType = DbType.AnsiString;
注釋掉,程序運行通過,懷疑參數(shù)類型不能夠設(shè)置成AnsiString,設(shè)置成下面的方式:
- para.DbType = DbType.String;
程序依然運行不通過,拋出上面同樣的錯誤,只有將這行代碼注釋掉才可以允許通過,思索很久仍然沒有結(jié)果,于是昨天寫了本文開頭說的那篇文章(PostgreSQL的.NET驅(qū)動程序Npgsql中參數(shù)對象的一個Bug)。
今天再次將目光聚集在錯誤信息的函數(shù)參數(shù)上:
updatefundattention(text)
難道PostgreSQL的數(shù)據(jù)類型text 對應(yīng)的.NET程序類型既不是String,也不是AnsiString?
又搜索了下,在http://npgsql.projects.postgresql.org/docs/manual/UserManual.html 找到了一張數(shù)據(jù)類型對照表:
Supported data types
Npgsql supports the following data types:
Postgresql Type | NpgsqlDbType | System.DbType Enum | .Net System Type |
int8 | Bigint | Int64 | Int64 |
bool | Boolean | Boolean | Boolean |
Box, Circle, Line, LSeg, Path, Point, Polygon | Box, Circle, Line, LSeg, Path, Point, Polygon | Object | Object |
bytea | Bytea | Binary | Byte[] |
date | Date | Date | DateTime, NpgsqlDate |
float8 | Double | Double | Double |
int4 | Integer | Int32 | Int32 |
money | Money | Decimal | Decimal |
numeric | Numeric | Decimal | Decimal |
float4 | Real | Single | Single |
int2 | Smallint | Int16 | Int16 |
text | Text | String | String |
time | Time | Time | DateTime, NpgsqlTime |
timetz | Time | Time | DateTime, NpgsqlTimeTZ |
timestamp | Timestamp | DateTime | DateTime, NpgsqlTimestamp |
timestamptz | TimestampTZ | DateTime | DateTime, NpgsqlTimestampTZ |
interval | Interval | Object | TimeSpan, NpgsqlInterval |
varchar | Varchar | String | String |
inet | Inet | Object | NpgsqlInet, IPAddress (there is an implicity cast operator to convert NpgsqlInet objects into IPAddress if you need to use IPAddress and have only NpgsqlInet) |
bit | Bit | Boolean | Boolean, Int32 (If you use an Int32 value, odd values will be translated to bit 1 and even values to bit 0) |
uuid | Uuid | Guid | Guid |
array | Array | Object | Array In order to explicitly use array type, specify NpgsqlDbType as an 'OR'ed type: NpgsqlDbType.Array | NpgsqlDbType.Integer for an array of Int32 for example. |
可以看到 數(shù)據(jù)庫的text 類型是可以對應(yīng).net程序的String類型的,看來問題的關(guān)鍵的確是函數(shù)參數(shù)類型問題。
為了驗證這個想法,將函數(shù)的參數(shù)類型改為Varchar類型:
- CREATE OR REPLACE FUNCTION updateattention(dm varchar)
- RETURNS void AS
- $BODY$
- --函數(shù)體略
再次運行前面說的.net數(shù)據(jù)訪問程序,運行通過!
故此得到結(jié)論:
PostgreSQL數(shù)據(jù)庫的函數(shù)中使用“自定義數(shù)據(jù)類型”,在.NET程序可能無法設(shè)置正確的DbType,從而出現(xiàn)找不到函數(shù)名的錯誤!
3,“靈異現(xiàn)象”分析
前面說,將
para.DbType = DbType.AnsiString;
代碼注釋即可,也就是不對NpgsqlParameter.DbType 設(shè)置任何值,那么DbType的缺省值是什么呢?
在VS2010的“即時窗口”打印了一下未設(shè)置值的para.DbType,發(fā)現(xiàn)它的值是:
String
由于上一篇文章已經(jīng)驗證Npgsql的參數(shù)對象DbType無論怎么設(shè)置,獲取該屬性值的時候都是String,所以還是無法得知它的默認(rèn)屬性值是什么。
于是一個很偶然的念頭出現(xiàn):
NpgsqlParameter對象的默認(rèn)值是不是Object類型?
另外我們的函數(shù)使用了自定義的citext類型,所以很可能需要使用DbType.Object類型。
重新修改代碼成下面的方式:
- //獲取PostgreSQL的數(shù)據(jù)訪問對象
- PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
- //獲取PostgreSQL的參數(shù)對象
- IDataParameter para = db.GetParameter();
- para.ParameterName = "@dm";
- para.DbType = DbType.Object;
- para.Value = "KF0355";
- db.ExecuteNonQuery("updateattention",
- System.Data.CommandType.StoredProcedure,
- new System.Data.IDataParameter[] { para });
運行程序,正常通過,看來問題找到了,就是它,在PostgreSQL的自定義類型函數(shù)參數(shù)中,.net程序的存儲過程調(diào)用參數(shù)應(yīng)該設(shè)置成 DbType.Object!
原文鏈接:http://www.cnblogs.com/bluedoctor/archive/2011/05/19/2051271.html
【編者推薦】
- 通用權(quán)限管理設(shè)計之?dāng)?shù)據(jù)庫結(jié)構(gòu)設(shè)計
- PostgreSQL的.NET驅(qū)動程序Npgsql中參數(shù)對象的一個Bug
- SQL Server表最小行的一個糾結(jié)問題
- 云端數(shù)據(jù)庫:微軟SQL Azure及其應(yīng)用場景
- SQL點滴之收集SQL Server線程等待信息