最近将公司的项目从SqlServer移植到PostgreSQL数据库上来,在调用数据库的存储过程(自定义函数)的时候,发现一个奇怪的问题,老是报函数无法找到。

先看一个PgSQL存储过程:


CREATE OR REPLACE FUNCTION updateattention(dm citext)
  RETURNS void AS
$BODY$
DECLARE
  
BEGIN
  update ZB set gzd=COALESCE(gzd,0)+1 where ZB.dm=$1 ;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION updateattention(citext) OWNER TO postgres;

在PostgreSQL中,函数和存储过程没有区别,这里我们把没有返回值的函数叫做存储过程吧,也许表诉的不太准确,还望大虾指正。

上面定义一个存储过程updateattention,它有一个自定义类型 citext,用于将字符串中类型换成不区分大小写的类型,它的定义如下:


CREATE OR REPLACE FUNCTION citext(character)
  RETURNS citext AS
'rtrim1'
  LANGUAGE internal IMMUTABLE STRICT
  COST 1;
ALTER FUNCTION citext(character) OWNER TO postgres;

下面是调用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框架内置了日志对象和异常对象,它能够为你抛出详细的错误信息。

如果采用下面的方式调用,又没有问题:

db.ExecuteNonQuery("select * from updateattention(@dm)",
                System.Data.CommandType.Text,
                new System.Data.IDataParameter[] { para });

------------------------------------------------------------------------------------

尽管该方式可以作为一种替代方案,但要用select * from 这种方式调用存储过程,总觉得很别扭,还得找到问题的真正原因。

这个 "function ... does not exist" 的问题很难搜索,最终在国外找到一篇文章讨论类似的问题:

http://pgfoundry.org/forum/forum.php?thread_id=637&forum_id=519

文中有人说,可能是参数的类型转换问题,但我这里只是将参数进行了大小写转换,应该不会有类似Int32到Int64这类问题。

无赖,只有将调用存储过程的.NET程序代码一个一个排查,当注释掉

para.DbType = DbType.AnsiString;

的时候,程序居然能够正常运行通过了!

之前也曾经怀疑过是不是DbType的问题,但是当把鼠标放到VS2010的编辑器中para 对象下面的时候,智能提示显示 DbType="{String}".

默认情况下,参数对象的DbType属性值是

DbType.String

难道

DbType.AnsiString==DbType.String ??

看了一下定义,它们是有区别的,DbType.AnsiString表示非Unicode的变长字符串,DbType.String 表示Unicode的变长字符串。

一般情况下,ANSI编码表示当前系统编码,所以我猜想AnsiString在我的机器上是Gb2312编码的,查了一下数据库的编码,它是UTF-8格式的,难怪难怪,PostgreSQL给我提示找不到 updatefundattention(text) 函数,注意下,实际上这个函数的参数不是text类型的,它实际上应该是 character 类型,PostgreSQL可以定义同名的函数,但函数可以有不同的参数类型,有点像C#的方法重载。

到此,问题似乎解决了,但还没完:

VS2010的智能提示有Bug?

第一次有这个念头我都觉得不可思议,因为以前在VS2008的时候曾经调试过类似的代码,赶紧将上面的.net代码中的参数对象换成其它数据库类型的参数对象试试看:


//获取PostgreSQL的数据访问对象
PWMIS.DataProvider.Data.AdoHelper db = MyDB.GetDBHelperByConnectionName("PostgreSQL");
//使用 SqlServer 的参数对象
IDataParameter para = new SqlParameter(); 
para.ParameterName = "@dm";
para.DbType = DbType.AnsiString;
para.Value = "KF0355";
db.ExecuteNonQuery("updateattention",
                System.Data.CommandType.StoredProcedure,
                new System.Data.IDataParameter[] { para });

再此将光标放到para.DbType 上,这次提示正确了,是“{AnsiString}”;

将上面的代码放到VS2008中再次验证,智能提示正确,看来不是VS2010的Bug,呵呵。

故此,得到的结论:

PostgreSQL的.NET数据访问驱动程序的参数对象DbType属性存在一个设置成AnsiString之后查看该属性的结果却是String的Bug!

PS:虽然查看属性的确有这样一个Bug,但好像程序内部做了正确的处理,要不我的程序最终是无法运行通过的。

后记

PostgreSQL的.NET数据驱动程序的这个问题引起的问题使得我困扰了2天左右的时间,不得不发帖说明一下这个过程,现在国内有关PostgreSQL的资料太少,写点东西供大家参考一下。

PostgreSQL的.NET驱动程序Npgsql中参数对象的一个Bug的更多相关文章

  1. NDK中使用pthread多线程中自己写的一个BUG

    在使用pthread进行NDK中的多线程开发时,自己写了一个BUG, void *darkGrayThread(void *args) { ThreadParam *param = (ThreadPa ...

  2. PostgreSQL的.NET驱动程序Npgsql

    Npgsql是PostgreSQL的一个.NET数据提供程序,它可以自由获取.它可以通过下列选项获得独立的下载,也可以安装PostgreSQL数据库程序时选择安装. 最新的_npgsql2 Npgsq ...

  3. JDBC:从数据库中取数据的一个bug

    先看错误信息: java.sql.SQLException: Before start of result set at com.mysql.jdbc.SQLError.createSQLExcept ...

  4. java中关于转义字符的一个bug

    在java中,你可以定义 char c = '\u4f60'; char m = '\u0045'; char e = '\u554a'; 这样的字面量,例如: System.out.println( ...

  5. MvcMusicStore学习中常出现的一个BUG

    BUG描述:var genreModel = storeDB.Genres.Include("Albums").Single(g => g.Name == genre); 前 ...

  6. Javascript 中判断对象为空

    发现了一个巧妙的实现: 需要检查一个对象(Object)是否为空,即不包含任何元素.Javascript 中的对象就是一个字典,其中包含了一系列的键值对(Key Value Pair).检查一个对象是 ...

  7. JavaScript中的对象与原型—你不知道的JavaScript上卷读书笔记(四)

    一.对象 对象可以通过两种形式定义:声明(文字)形式和构造形式.即: var myObj = { key: value // ... }; 或: var myObj = new Object(); m ...

  8. js中判断对象类型的几种方法

    我们知道,JavaScript中检测对象类型的运算符有:typeof.instanceof,还有对象的constructor属性: 1) typeof 运算符 typeof 是一元运算符,返回结果是一 ...

  9. JS中document对象和window对象的区别

    简单来说,document是window的一个对象属性. Window 对象表示浏览器中打开的窗口. 如果文档包含框架(frame 或 iframe 标签),浏览器会为 HTML 文档创建一个 win ...

随机推荐

  1. 对象的序列化存储:Serializable 和 Parceable

    在进行Android开发的时候我们有时候需要用到数据的持久化存储,或者在进程之间传递数据.其中就可能需要用到对象的序列化,经过序列化的对象之后可以通过Intent或者Boundle来传输了.接下来还是 ...

  2. static 与单例模式、auto_ptr与单例模式、const 用法小结、mutable修饰符

    一.static 与单例模式 单例模式也就是简单的一种设计模式,它需要: 保证一个类只有一个实例,并提供一个全局访问点 禁止拷贝  C++ Code  1 2 3 4 5 6 7 8 9 10 11 ...

  3. [C/C++11语法]_[0基础]_[lamba 表达式介绍]

    场景 lambda 表达式在非常多语言里都有一席之地,由于它的原因,能够在函数里高速定义一个便携的函数,或者在函数參数里直接高速构造和传递. 它能够说是匿名函数对象,一般仅仅适用于某个函数内,仅仅做暂 ...

  4. java项目部署后的文件路径获取

    //eclipse部署工程 String path = request.getServletContext().getRealPath( File.separator+ "WEB-INF&q ...

  5. CodeMirror与jquery UI-Tabs混合使用 注意事项

    第一步:.将代码高亮渲染 第二步:jquery Tab输出: 第三步:点击Tab切换时,将代码块刷新: 参考:http://jtmorris.net/2013/06/codemirror-editor ...

  6. Java中RunTime类介绍

    Runtime 类代表着Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,我们可以通过Runtime.getRuntime() 方法来获取当前程序的Runtime ...

  7. pow(x,y)函数的实现算法(递归函数)

    函数pow(x,y)实现运算x^y,即x的y次方,这里x和y都为整数. 算法的基本思想是,减少乘法次数,重复利用结算结果,例如: x^4,如果逐个相乘的话,需要四次乘法.如果我们这样分解(x^2)*( ...

  8. ubuntu下载linux内核源码

    ubuntu仓库里面关于源码部分配置的好全啊,什么都有,ps:包括vim的各种插件居然也有人打包放到仓库里,真是方便. 1.首先查看一下本系统使用的内核版本号: cat /proc/version L ...

  9. 输入LPCWSTR类型字符串

    LPCWSTR tmp = L"xxx"; char*转到LPCWSTR LPCSTR(charTmp)

  10. C++中explicit的用法

    https://blog.csdn.net/qq_35524916/article/details/58178072 https://blog.csdn.net/jinjin1062495199/ar ...