PostgreSQL是一个使用广泛的免费开源的数据库,与MySQL比较,它更适合复杂的企业计算任务,而MySQL在互联网领域应用更为广泛,究其原因,可能是PostgreSQL拥有支持最多的数据类型,甚至包括数组类型,IP地址类型等,可以使用C,SQL,PL/Pgsql,Phython等多种方式编写强大的自定义函数,因此特别适合处理复杂的计算问题。如果想要将SqlServer数据库迁移到其它类型的数据库,PostgreSQL是比较好的选择。
尽管PostgreSQL使用比较广泛,但在国内相关资料太少,我们在数据库迁移的过程中,遇到了不少问题,比如我的上一篇文章PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug 中关于“找不到函数名”的问题,解决起来比较“辣手”,可以使用“追踪”来形容了。本篇继续对这个问题进行深入探究。
1,问题回顾:
在上一篇文章中说到,有一个PostgreSQL函数 updateattention ,它有一个自定义的函数参数,下面是函数头:
- CREATE OR REPLACE FUNCTION updateattention(dm citext)
- RETURNS void AS
- $BODY$
- --函数体略
参数dm 的类型是citex,一个自定义的数据类型,使用它来作为函数参数或者变量的类型,在进行数据查询的时候可以不区分大小写,它的定义是:
- CREATE OR REPLACE FUNCTION citext(character)
- RETURNS citext AS
- 'rtrim1'
- LANGUAGE internal IMMUTABLE STRICT
- COST 1;
- ALTER FUNCTION citext(character) OWNER TO postgres;
下面是调用使用C#调用updateattention存储过程的代码:
- //获取PostgreSQL的数据访问对象
- PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
- //获取PostgreSQL的参数对象
- 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数据开发框架)的数据访问对象AdoHelper来进行相关的数据访问操作,它采用反射工厂模式,根据系统的配置实例化具体的数据访问类,这里使用的是PostgreSQL数据访问类。
运行该程序,出现下面的错误:
- 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框架内置了日志对象和异常对象,它能够为你抛出详细的错误信息。
2,问题聚焦
一开始还以为是函数名大小写的问题,仔细核对后发现没有问题,然后尝试对代码进行仔细排查。
将上面的程序中第6行代码
- para.DbType = DbType.AnsiString;
注释掉,程序运行通过,怀疑参数类型不能够设置成AnsiString,设置成下面的方式:
- para.DbType = DbType.String;
程序依然运行不通过,抛出上面同样的错误,只有将这行代码注释掉才可以允许通过,思索很久仍然没有结果,于是昨天写了本文开头说的那篇文章(PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug)。
今天再次将目光聚集在错误信息的函数参数上:
updatefundattention(text)
难道PostgreSQL的数据类型text 对应的.NET程序类型既不是String,也不是AnsiString?
又搜索了下,在http://npgsql.projects.postgresql.org/docs/manual/UserManual.html 找到了一张数据类型对照表:
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. |
可以看到 数据库的text 类型是可以对应.net程序的String类型的,看来问题的关键的确是函数参数类型问题。
为了验证这个想法,将函数的参数类型改为Varchar类型:
- CREATE OR REPLACE FUNCTION updateattention(dm varchar)
- RETURNS void AS
- $BODY$
- --函数体略
再次运行前面说的.net数据访问程序,运行通过!
故此得到结论:
PostgreSQL数据库的函数中使用“自定义数据类型”,在.NET程序可能无法设置正确的DbType,从而出现找不到函数名的错误!
3,“灵异现象”分析
前面说,将
para.DbType = DbType.AnsiString;
代码注释即可,也就是不对NpgsqlParameter.DbType 设置任何值,那么DbType的缺省值是什么呢?
在VS2010的“即时窗口”打印了一下未设置值的para.DbType,发现它的值是:
String
由于上一篇文章已经验证Npgsql的参数对象DbType无论怎么设置,获取该属性值的时候都是String,所以还是无法得知它的默认属性值是什么。
于是一个很偶然的念头出现:
NpgsqlParameter对象的默认值是不是Object类型?
另外我们的函数使用了自定义的citext类型,所以很可能需要使用DbType.Object类型。
重新修改代码成下面的方式:
- //获取PostgreSQL的数据访问对象
- PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
- //获取PostgreSQL的参数对象
- 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的自定义类型函数参数中,.net程序的存储过程调用参数应该设置成 DbType.Object!
原文链接:http://www.cnblogs.com/bluedoctor/archive/2011/05/19/2051271.html
【编者推荐】
- 通用权限管理设计之数据库结构设计
- PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug
- SQL Server表最小行的一个纠结问题
- 云端数据库:微软SQL Azure及其应用场景
- SQL点滴之收集SQL Server线程等待信息