Perl DBI(MySQL)在预准备语句中放置单引号而不是实际参数

2019-05-2320:50:18后端程序开发Comments2,654 views字数 3234阅读模式

尝试做一个简单的查询作为准备好的声明,但没有成功.这是代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

package sqltest;
use DBI;

DBI->trace(2);

my $dbh = DBI->connect('dbi:mysql:database=test;host=***;port=3306','the_username','****');
my $prep = 'SELECT me.id,me.session_data,me.expires FROM sys_session me WHERE me.id = ?';
$dbh->{RaiseError} = 1;
my $sth = $dbh->prepare($prep);
$sth->bind_param(1,'session:06b6d2138df949524092eefc066ee5ab3598bf96');
$sth->execute;
DBI::dump_results($sth);

MySQL服务器响应”’附近的语法错误.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

DBI跟踪的输出显示文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

-> bind_param for DBD::mysql::st (DBI::st=HASH(0x21e35cc)~0x21e34f4 1 'session:06b6d2138df949524092eefc066ee5ab3598bf96') thr#3ccdb4
 Called: dbd_bind_ph
  <- bind_param= ( 1 ) [1 items] at perl_test_dbi_params.pl line 10
[...]
>parse_params statement SELECT me.id,me.expires FROM sys_session me WHERE me.id = ?
Binding parameters: SELECT me.id,me.expires FROM sys_session me WHERE me.id = '
[...]
DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1

所以对我而言,看起来声明并没有得到应有的准备.
当我发送没有参数的查询时,它按预期工作.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

我在这里想念什么?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

DBI版本是DBI 1.637-ithread,MySQL版本是5.5.57-0 deb8u1文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

使用针对MSWin32-x86-multi-thread-64int构建的Windows perl 5,版本26,subversion 1()进行测试
和Ubuntu perl 5,版本22,颠覆1()为x86_64-linux-gnu-thread-multi构建文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

EDIT1:
for context:我在使用Catalyst和Catalyst::Plugin::Session::Store::DBIC时注意到了这个问题.这里,id-column是一个Varchar(72)类型,它包含一个session-id.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

EDIT2:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

> DBD :: mysql版本是4.043
>通过$sth->执行绑定(‘session:foo’);导致同样的问题
>通过$sth-> bind_param绑定(‘session:foo’,SQL_VARCHAR);导致同样的问题
>绑定数字字段确实有效,但只能使用显式类型定义$sth-> bind_param(1,1512407082,SQL_INTEGER);文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

EDIT3:
我找到了做更多测试的时间,但没有令人满意的结果:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

>我能够使用较旧的服务器进行测试并且有效. DBI和DBD :: mysql的版本是相同的,但我发现服务器使用的是MySQL 5.5客户端,在DBI-trace中报告为MYSQL_VERSION_ID 50557,而我的两个原始测试服务器都使用MySQL 5.7 MYSQL_VERSION_ID 50720和MYSQL_VERSION_ID 50716
>使用$dbh-> {mysql_server_prepare} = 1;它有效!也许这有助于找到这个问题的人,但我现在更愿意找到问题的真正原因文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

从您的跟踪日志可以看出,问号占位符(?)被DBD :: mysql替换为一个撇号(‘).所以它是纯DBD :: mysql的bug.乍一看它完全没有意义……因为占位符被放入两个撇号的参数替换.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

可以在那里找到执行此占位符替换的相关代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

*ptr++ = '\'';
          ptr += mysql_real_escape_string(sock,ptr,valbuf,vallen);
          *ptr++ = '\'';

所以问题是,上面的C代码可以在* ptr缓冲区中产生一个撇号吗?答案是肯定的,当mysql_real_escape_string()返回相同值的整数作为指针大小减去1 – 当两个撇号被写入* ptr缓冲区中的相同位置时,模拟递减1的数值运算.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

这会发生吗?是的,它可以,因为Oracle在MySQL 5.7.6客户端库中更改了mysql_real_escape_string()C函数的API:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html#mysqld-5-7-6-feature文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

Incompatible Change: A new C API function,mysql_real_escape_string_quote(),has been implemented as a replacement for mysql_real_escape_string() because the latter function can fail to properly encode characters when the NO_BACKSLASH_ESCAPES SQL mode is enabled. In this case,mysql_real_escape_string() cannot escape quote characters except by doubling them,and to do this properly,it must know more information about the quoting context than is available. mysql_real_escape_string_quote() takes an extra argument for specifying the quoting context. For usage details,see mysql_real_escape_string_quote().文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

MySQL 5.7.6版本的mysql_real_escape_string()文档说:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

https://dev.mysql.com/doc/refman/5.7/en/mysql-real-escape-string.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

Return Values: The length of the encoded string that is placed into the to argument,not including the terminating null byte,or -1 if an error occurs.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

因此,如果在MySQL服务器上启用NO_BACKSLASH_ESCAPES SQL模式,则MySQL 5.7.6客户端的mysql_real_escape_string()无法正常工作并返回错误,因此-1会转换为unsigned long. unsigned long在32bit和64bit x86平台上都与指针大小相同,因此在DBD :: mysql驱动程序的C代码之上产生一个撇号字符.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

现在我在下面的pull请求中修复了DBD::MariaDB驱动程序(DBD :: mysql的fork)的这个问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

所以当使用MySQL 5.7客户端库编译时,DBD :: MariaDB也是兼容的.文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/12816.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/12816.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定