PDO::ATTR_EMULATE_PREPARES属性设置为false引发的血案

前段时间给pdo设置了下emulate_prepare属性,引发了这次的血案。在这记录下事情的经过,没准大家能避免同样的错误。

先说以下环境。php 5.2.5,mysql 5.0.81,服务器使用的GBK编码。

起因

   首先是看到一个报错信息,说是sql语句的syntax error。这里给一个能重现的例子。报错的位置就是在红框的位置。

214926_bJPN_437615.png

排查

从上面看,看不出语法错误。然而GBK编码的“玕”字后一个字节是0x5c,跟“\”一样。看到这,是不是就觉得这可能会有问题?但是,我记得我们是使用了pdo的prepare这种方式的(上面sql语句中我使用实际的值代替了?占位符),即使这个汉子特殊,也不会报语法错误才对。于是我翻了翻代码,发现,果然没有显示的设置PDO::ATTR_EMULATE_PREPARES属性的值。而默认是设置为true的(从字面意思就能理解,模拟prepare,不是真正意义上的prepare执行方式)。

使用tcpdump抓包,wireshark查看,确定了一下,发现果然是上面这样。

抓包的结果,

220616_6Brn_437615.png

0xab5c是汉字玕的GBK编码。这里看到的是,pdo模拟处理后,多加了一个'\',而在这之前,已经告诉mysql server,客户端使用的是GBK编码(set names gbk),mysql server按着GBK字符集处理sql语句,多出来的转义符号,就造成了sql语句的语法错误。(后来查资料确认,这是pdo在处理多字符时候的一个bug,好像在php5.3.6,之后版本通过dsn里面设置charset可以解决,我没去确认从哪个版本开始起作用的,但是5.4是可以起作用的。

处理

既然pdo模拟prepare有这个问题,而真正意义的prepare既没有这个问题,又在防止sql注入上更胜一筹,那我就直接使用mysql提供的prepare执行方式就好了。所以,设置PDO::ATTR_EMULATE_PREPARES为false。测试发现,果然插入正常了。其他一切数据库操作貌似都正常。

惨啊

在我解决完问题挺高兴的时候,发现问题了。数据库主从同步出问题了。后来定位问题发现,mysql 的binlog里面sql语句里面的数据全都是转换到了ascii码的表示,例如 insert test (`id`) values(1),在binlog里面是insert test(`id`) values(0x31)。此时也才了解到mysql5.0不支持row同步方式,只支持语句的同步方式。binlog异常参见这篇文章http://backend.blog.163.com/blog/static/20229412620133274030845

总结

编程不容易,说多了都是泪。。努力,多学学吧。。

转载于:https://my.oschina.net/u/437615/blog/369481

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值