1. 综述

其实一看到这两个单词的时候我有点莫名其妙,可能英语没有学好,我的理解就是quoting是“引用”的意思,而Escaping是“逃脱”的意思。后来在看到了作者的TUTORIAL之后才大致明白了两者的意思。

QUOTING大白话就是为SQL语句打上单引号。考虑如下的情况

SELECT * FROM stock WHERE item = 'Hotdog Buns'

由于中间有一个空格,所以这个单引号必不可少。

ESCAPING大白话就是转义。例如在C的printf中,为了打出引号(“),我们需要使用”\””的方式。在SQL语句中,这种情况仍然存在,下文节选自MYSQL C API中的mysql_real_escape_string的说明。

Characters encoded are NUL(ASCII 0), “\n”, “\r”, “\”, “'”, “"”, and Control-Z . (Strictly speaking, MySQL requires only that backslash and the quote character used to quote the string in the query be escaped. This function quotes the other characters to make them easier to read in log files.)

例如,下面的语句显然不行

SELECT * FROM stock WHERE item = 'Frank's Brand Hotdog Buns' 

需要改成

SELECT * FROM stock WHERE item = 'Frank''s Brand Hotdog Buns'

在MYSQL++中,Quoting和Escaping工作都是通过mysqlpp:: Query在建立SQL语句的时候自动化生成的,而且具体的操作都被定义在了manip.h和manip.cpp中。

同时,在下文中我们可以发现,MYSQL++其实非常的聪明,他可以自动检测出哪些类型是需要进行QUOTING和ESCAPING,哪些是不需要的。即使你为他加入了“建议”QUOTING或者ESCAPING的标志,MYSQL++在内部仍然会自己选择到底做不做QUOTING和ESCAPING。

2. MYSQL++中的Quoting和Escaping的用法

先来看一下到底该怎么用这些功能

string s = "Hotdog Buns";
query << "SELECT * FROM stock WHERE item = " << mysqlpp::quote_only << s; 
query << "SELECT * FROM stock WHERE item = " << mysqlpp::quote << s;

可以看到,我们可以通过流式操作来生成SQL串,在需要的地方加上特殊的标志(比如上文中的mysqlpp:: quote_only和mysqlpp:: quote等,具体下文中再说)。大家可能会有疑问,为什么一定要通过这种看似ugly的方法来生成SQL串,为什么不能够直接implicit地为我们把quoting和escaping的事情做完?我在看作者的注释的时候,有这些一句话(摘自,http://tangentsoft.net/mysql++/doc/html/userman/tutorial.html#qescape

You must use manipulators and template query flags as necessary to tell MySQL++ where quoting and escaping is necessary. It would be nice if MySQL++ could do quoting and escaping implicitly based on data type, but this isn’t possible in all cases. Since MySQL++ can’t reliably guess when quoting and escaping is appropriate, and the programmer doesn’t need to, MySQL++ makes you tell it.

在深入源代码之前,我们先来看一下MYSQL++支持哪些QUOTING和ESCAPING方式(即上面代码中的表示加载quoting和escaping的ENUM到底有多少。

类型

代码中的说明

备注

quote

insert into a Query stream to single-quote and escape next item

需要时为后一项加入必要的单引号和转义

quote_only

insert into a std::ostream to single-quote next item

需要时为后一项加入必要的单引号

quote_double_only

insert into a std::ostream to double-quote next item

需要时为后一项加入必要的双引号

escape

 

需要时为后一项加入必要的转义

do_nothing

insert into a std::ostream to override manipulation of next item

下一项什么额外操作都不要做了。该操作在Template Query中有一定的作用。When used with SQLQueryParms it will make sure that it does not get formatted in any way, overriding any setting set by the template query. 具体请参看template Query相关章节。

ignore

insert into a std::ostream as a dummy manipulator

效果和do_nothing类似,只是it will not override formatting set by the template query. It is simply ignored. 具体请参看template Query相关章节。

3. MYSQL++中的Quoting和Escaping的实现

通过查看代码,我们可以看到上述几个选项的做法其实都差不多,所以我们主要关注最麻烦的quote,至于有一点点特殊的do_nothing与ignore放到相关章节去做吧。

再来回顾一下QUOTING和ESCAPING怎么用。

mysqlpp:: Query query = …;
 
query << "SELECT * FROM stock WHERE item = ";
 
query << mysqlpp::quote << “Hotdog’s Buns”;
 

我的第一反应是去查看有没有mysql:: Query:: operator<<( ) 的重载方法,通过查看query.h,我发现根本没有这样的方法重载,很可惜,我没有找到

(在query.h中确实有一个全局函数

std::ostream& operator <<(std::ostream& os, Query& q) ,但是从签名和作者的注释中我们可以看到这个函数其实就是为了方便用cout 等进行输出,如cout << query;在通过这个全局函数之后的作用等效于cout << query.str();)

那么这句话为什么能够被编译通过?

原来mysqlpp:: Query继承自std::ostream,说实话,可能是因为我编程经验不够,这种用法我没有怎么见到过,更多的是利用一个delegate,然后自己手动去重载operator << 来实现类似的功能。

言归正传,有了这条线索我们可以继续向下。很显然,上述代码中的query << “SELECT …”这句,其实就是普通的ostream流处理,也就是把字符串放入到ostream自己的缓存中。那么第二句,query << mysqlpp::quote又是在做什么呢?

全局搜了一下quote是什么?

原来是个enum,所以针对query << mysqlpp::quote的用法,找到了如下的代码

又出现了一个神奇的东西——quote_type1,再去看

注意到mysqlpp:: Query继承自std::ostream,所以对于query << mysqlpp:: quote的用法,在operator << (ostream, quote_type0)中,直接就把这个query给保存在了quote_type1的ostr中。那么这个又是做什么?

注意到基本用法中的第二行query << mysqlpp::quote << “Hotdog’s Buns”,刚才说了前半段调用的是operator << (ostream, quote_type0)这个全局方法,他返回的是什么?quote_type1!所以这一行代码接下去的动作一定就是会调用operator << (quote_type1, …)。查了一下果然有!

我应该在介绍SQLTypeAdapter的时候说过,这个类型就是为了做为一个适配器来减少公共函数的数量,他还可以包罗万象,把各种数据类型都包进去。所以对于query << mysqlpp::quote << “Hotdog’s Buns”,显然还会有一步“把Hotdog’s Buns”放入到SQLTypeAdapter中的过程,具体的过程请查看相关章节。

然后就简单了,看上面的代码。

首先,看到了两个强制转换,用的都是dynamic_cast。这里先来补一下dynamic_cast和static_cast的区别。简单理解就是这两者都可以在父类和子类指针或者引用之间相互转化(即父类指针转换为子类指针,或者反之),但是static_cast一定能够成功(即使两者根本就不知父子关系,在后续调用时才会报运行时错),而dynamic_cast则会多一步检查,即检查相互转换的两者是否是可以转换的,如果不能就会返回NULL。也就是说dynamic_cast很像C#关键字as。

第二点需要关注的是SQLStream,这是一个MYSQL++自定义的类型,但是从全局搜索的结果来看,这个类型更多的是用来做测试(因为只出现在了test/manip.cpp中),所以我们可以先忽略这个类型,当然61行的操作也可以仍然psqls结果为NULL。

第三点是怎么做的quoting?显然上述代码片段的65和85行告诉了我们答案(其实就是在需要quoting的item的两边加上了 \’ 而已)。那关键是这里的mysqlpp:: SQLTypeAdapter:: quote_q()做了什么(65行)?

其中buffer_是个SQLBuffer

这个SQLBuffer:: quote_q( )主要要对一个表示timestamp的NOW()进行过滤,主要原因是,下面的SQL语句是合法的Insert into tbl(col_time_stamp) values(NOW()); 此时的NOW()表示的是aggregate函数。然后就交给mysql_type_info:: quote_q( )进行检测。

然后需要关注的就是68行和73行,其中68行的作用是检测输入的内容是否需要被escapte。检测方法其实和上面很类似,也就是利用SQLBuffer:: escape_q()

第73行 pq->escape_string(&escaped, in.data(), in.length());

这一行其实就是就是调用了mysqlpp:: Query:: escape_string()方法,

然后直接调用DbDriver的escape_string方法,该方法其实就是对mysql_real_escape_string的包装而已(该函数在官网上的说法很简单,This function is used to create a legal SQL string that you can use in an SQL statement)。

原创作品,转载请注明出处www.cnblogs.com/aicro

【原创】11. MYSQL++ 之 Quoting 与 Escaping的更多相关文章

  1. 【原创】MYSQL++源码剖析——前言与目录

    终于完成了! 从第一次想写到现在真的写好大概花了我3个月时间.原来一直读人家的系列文章,总感慨作者的用心良苦和无私奉献,自己在心里总是会觉得有那么些冲动也来写一个. 最开始的麻烦是犹豫该选哪个主题.其 ...

  2. 【原创】MySQL(Innodb)索引的原理

    引言 回想四年前,我在学习mysql的索引这块的时候,老师在讲索引的时候,是像下面这么说的 索引就像一本书的目录.而当用户通过索引查找数据时,就好比用户通过目录查询某章节的某个知识点.这样就帮助用户有 ...

  3. 11.Mysql视图

    11.视图11.1 什么是视图 视图view是一张虚拟表,它不存储数据,数据仍在表里,视图由一条查询表的select语句创建,视图只存储select语句. 可以将复杂的查询语句封装成视图,用户可以从视 ...

  4. 【原创】MySQL Can't create a new thread报错分析

    今天有两台服务器都出现了Can't create a new thread报错. [故障处理过程] 故障发生后登录服务器,检查mysql进程正常,但登录mysql报下面错误 ERROR 1135 (H ...

  5. centos7 + Nginx+ HTTPS + uwsgi + python3.6 + Docker + Django1.11 + mysql 5.6 + virtualenv 环境搭建

    环境搭建: 系统: ​ centos7.2 x64 开发环境: ​ python3.6 ​ Django 1.11 虚拟环境: [Docker](https://www.runoob.com/dock ...

  6. 【Vegas原创】Mysql绿色版安装方法

    所谓的绿色版,就是没有installer的MySQL,完全需要靠人工来操作,好处是,重装系统后,只要再做一次本次配置,即可使用. 具体操作方法: 1,设置系统环境变量, 在Path中添加 D:\mys ...

  7. 【原创】MySql 数据库导入导出(备份)

    啥不说了,两周前刚刚做过mysql导入导出的结果现在又忘了.. 更可悲的是竟然同样的三篇blog,现在看起来还是如当初一样费劲,里面的内容..所以自己写个记录一下 环境:*nix 权限:有相关表的写读 ...

  8. MySQL学习11 - MySQL创建用户和授权

    权限管理 权限管理 我们知道我们的最高权限管理者是root用户,它拥有着最高的权限操作.包括select.update.delete.update.grant等操作.那么一般情况在公司之后DBA工程师 ...

  9. 【原创】Mysql中select的正确姿势

    引言 大家在开发中,还有很多童鞋在写查询语句的时候,习惯写下面这种不规范sql select * from table 而不写成下面的这种规范方式 select col1,col2,...,coln ...

随机推荐

  1. 使用 nvm 管理多版本 node

    首先,使用下面的命令来安装 nvm $ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | b ...

  2. Arduino UNO的原理图

    Arduino UNO的原理图是开源的,所以可以从arduino网站上下载它: https://www.arduino.cc/en/Main/ArduinoBoardUno 原理图PDF: https ...

  3. 学习Java有没有什么捷径?

    很多网友咨询学习Java有没有什么捷径,我说“ 无他,唯手熟尔 ”.但是愿意将一些经验写出来,以便后来者少走弯路,帮助别人是最大的快乐嘛! 要想学好Java,首先要知道Java的大致分类. 我们知道, ...

  4. RedHat Server Enterprise 6安装G++

    RedHat 6默认是安装有GCC,而没有安装G++编译 要安装G++前最好先查看下GCC的版本号,通常GCC的版本和G++的版本是相同的,知道GCC的版本再去找G++的安装文件就容易些,版本号有在安 ...

  5. WPF XMAL获取元素的父元素,子元素

    /// 获得指定元素的父元素 /// </summary> /// <typeparam name="T">指定页面元素</typeparam> ...

  6. 解决get方法传递URL参数中文乱码问题

    [转]解决get方法传递URL参数中文乱码问题 来自:http://www.javaeye.com/topic/483158 应用一:解决tomcat下中文乱码问题(先来个简单的) 在tomcat下, ...

  7. iOS当前屏幕截屏

    需求描述: 有两个ViewController 我们记做 A.B ,其中B controller只是显示下半部分: 如下图效果: 实现这种的方案很多,可以用添加View方法,  也可以用UIWindo ...

  8. 【java规则引擎】drools6.5.0版本api简介

    在有些术语使用的时候,我有时候会用KIE项目.KIE引擎或者Drools项目.Drools引擎,大家应该理解KIE是Drools等项目的一个统称,所以在大多数情况下KIE或者特指Drools都是差不多 ...

  9. __init__.py文件的作用

    原来在python模块的每一个包中,都有一个__init__.py文件(这个文件定义了包的属性和方法)然后是一些模块文件和子目录,假如子目录中也有 __init__.py 那么它就是这个包的子包了.当 ...

  10. nginx time_wait 较多优化

    1. 查看命令   netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'   结果 ESTABLISHED 22 F ...