【原创】4. MYSQL++ 之 SQLTypeAdapter类型、SQLQueryParms类型 与 SQLBuffer

 

image

 

 

1. mysqlpp::SQLBuffer

该类型其实就是SQLTypeAdapter传入的各种类型(int, string, double, long, String, …) 的包装,包装的结果就是

  • 各种类型实例的字符串表示 ( const char* data_;  )
  • length 长度 ( size_type length_ )
  • 类型(由mysqlpp::mysql_type_info定义) ( mysql_type_info type_ )
  • 是否是数据库的NULL类型  ( bool is_null_ )

表示类型的类别是 mysql_type_info ,该类型是一个“C++类型”和“SQL 类型”相互映射的utility class。

需要强调的是,该类型实际上是支持BINARY存法的,其实在实现上,就是将length不设定为”\0”的位置,而是整个类型实例的真实长度。以下面的代码为例

SQLBuffer("abc\\0efg", 7, mysql_type_info::string_type, false)

如此一来,上面的四个属性就非常清楚地对号入座了。

 

同时,该类型回答了传入的类型的各种问题,例如

    • 传入实例是否是个string(is_string( ) )
    • 传入实例是否是null(is_null( ), 这里的null是数据库中的NULL,即如create table (id int not NULL),必须要区别于字符串“NULL”)
    • 传入实例的实例类型是什么(type( ))
    • 传入实例的实例是否需要转移escape(escape_q( ))
    • 传入实例的实例是否需要quote(quote_q( ))

关于类型信息(type( ), escape_q( ), quote_q( ))都是靠着mysqlpp::mysql_type_info实现。除了在进行quote_q( )的时候,在调用mysql_type_info::quote_q( )之前,先检查如果包装的是一个在TIMESTAMP中的“NOW()”的情况,如果是这种情况,那么就是不需要进行quote。例如,

CREATE  TABLE tbl1  ( time TIMESTAMP);
insert into  tbl1 values( NOW() ); --注意这里的NOW()是不需要加上引号的
 
具体的实现代码是
image
 

也就是说当前版本的MYSQL++能够识别的MYSQL函数应该只有NOW( )了。其他的都会被认为是普通字符串而被加上quote。

 

2. mysqlpp::mysql_type_info

这个类型是mysqlpp:: SQLBuffer的灵魂,具体表示类型信息,该类型是一个“C++类型”和“SQL 类型”相互映射的utility class。它是唯一一个需要和MYSQL 原生态的数据结构打交道的,需要硬编码的类型(具体指的是mysql_type_info内部的一个mysql_ti_sql_type_info类型的数组,其中包含了诸如名字、底层类型、MYSQL_Comm.h中的对应的ENUM以及表示是否可以为SQL null、是否是默认类型等信息的flag),它的功能就是在SQL类型和C++类型之间进行相互转换。也就是说,该类型明确了某个C++类型所对应的SQL类型。具体的内容,可以参看抓们解释mysql_type_info类型原理这一节。

 

3. mysqlpp::SQLTypeAdapter

  • 作用

SQLTypeAdapter正如名字所言,是一个“适配器”,也就是说,为了在实现上的方便,很多函数就以他作为中介,来隐藏具体的数据类型(例如int,double,string和mysqlpp::String等)。类似于Query中的use和store等,都有使用SQLTypeAdapter作为参数的方法。使用SQLTypeAdapter就可以省去很多overwrite。

 

文档上是这样说的

This class provides implicit conversion between many C++ types and SQL-formatted string representations of that data without losing important type information.

第一种用法是在template query中,例如在下面的用法中,可以防止execute的签名过于复杂。例如下面的execute只需要声明两个SQLTypeAdapter即可,而不需要声明为一个string,一个int(其实这样的组合是无穷尽的,所以也只有这一种解决途径)。这个用法是ADAPTER设计模式的典型用法。

// 示意伪代码,不一定准确
mysqlpp::Query query = con.query(
 
    "select * from stock where item = %0q, id = %1q");
 
query.parse();
query.execute(SQLTypeAdapter("test"), SQLTypeAdapter(1) );

 

第二种用法是为了方便Quoting和Escaping,其实动机和上面的差不多。

The other major use for this type is the quoting and escaping logic in Query's stream interface: rather than overload the << operators and the manipulators for every single type we know the rules for a priori, we just specialize the manipulators for SQLTypeAdapter. The conversion to SQLTypeAdapter stringizes the data, which we need anyway for stream insertion, and holds enough type information so that the manipulator can decide whether to do automatic quoting and/or escaping.

 

另外,代码中还实现了两个SQLTypeAdapter的比较( int compare(const SQLTypeAdapter& other) const; ),以及SQLTypeAdapter与string和char*进行比较的重载函数。

这里的比较其实相对比较容易,基本上几个compare函数的最后调用都是如下函数

image

讲到底就是字符串的比较,需要强调的是,strncmp虽然是str的比较,但是其实这个函数是可以比较含有”\0”的二进制内存段的,这是因为该函数的原型是,

extern int strcmp(char *str1,char * str2,int n),

其中的n表示需要比较的字符(byte)数。

 

例如,

image

 

  • 代码实现

成员变量

要看懂SQLTypeAdapter需要先看懂mysqlpp: : SQLBuffer,因为在SQLTypeAdapter中,只有两个成员变量,一个是表示是否已经处理过的flag,以及一个SQLBuffer缓冲(注意,在源代码中是使用的是ref count)

    • is_processed_

This flag is used by the template query mechanism, to prevent a string from being re-escaped or re-quoted each time that query is reused.  The flag is reset by operator=, to force the new parameter value to be re-processed.

这里所说的process就是为string在SQL语句中加入引号(quote),或者为特殊字符加上“转义”(escape)。 查看整个项目源代码,可以发现,为这个is_processed_设置为true的情况只有在manip.cpp(Implements MySQL++'s various quoting/escaping stream manipulators.)以及 query.cpp 的一些和template query相关的地方。

至于具体的quote和escape是如何封装的,请看专门的章节。

 

    • buffer_

这是一个RefCountedPointer<SQLBuffer>类型的变量,用来保存此SQLTypeAdapter所包含的具体的数据、长度、类型信息等。

 

 

重要方法

需要关心的实现方面的具体要素有SQLTypeAdapter的构造函数,通过查看源代码,我们可以大致为SQLTypeAdapter的构造函数分成三类。

  • 拷贝构造函数——没什么好多说的
  • 以C++数据类型(如int, string, long, float,double)为源的构造函数
    • 例子是

image

image

让我们仔细来看一下SQLBuffer的构造函数

image

我刚开始看这段代码的时候有一个疑惑,那就是为什么像上面的例子居然可以编译通过?明明在SQLTypeAdapter(short i)中传入给SQLBuffer的第二个参数是typeid(i) (typeid是C++的关键字,是个RTTI 关键字,结果是std::type_info),给SQLTypeAdapter(char i)中传入给SQLBuffer的第二个参数是enum_field_types( 该enum被定义在了mysql 自带的Develop Toolkit中的与C函数相关的头文件中的mysql_com.h中 ,而mysql_type_info::string_type被定义为MYSQL_TYPE_STRING,它是mysql_com.h中的一个enum_field_types常量)。

后来经过写了个简单的示例的debug,我发现这是C++的一个tricky(说明我的C++基础还是相对比较薄弱的),也就是说编译器自动帮我们找到了最合适的mysql_type_info类型的构造函数,即上下两个例子分别对应的构造函数是

image

image

 

最后,特别需要注意的是关于float和double类型,这两个类型需要考虑infinite 或者 NaN的情况,所以需要特殊处理,下文中的numeric_limits是C++标准库中的一个template class。

image

 

  • 以mysqlpp自定义的类型为源的构造函数
    • mysqlpp::String, mysqlpp::Date, mysqlpp::Time, mysqlpp::DateTime

mysql++处于便于管理和尽可能将C++type与SQL type相互对应,自建了上面的几个类型,其中mysqlpp:: String更是表示BLOB的首选。

其实在sql_type.h中,就有

image

image

当我们在看SQLTypeAdapter的构造函数,例如,

image

我们发现传递给SQLBuffer的第二个参数仍然是typeid(d),为什么对于自定义的mysqlpp:: Data而言,也可以用typeid?其实这个问题本身并没有多大问题,毕竟typeid是个关键字,对于任何类型都是可以使用的,就像sizeof一样。

    • mysqlpp::Null<T>

mysqlpp::Null<T>也是一个自定义的类型,他的出现是为了应付“Class for holding data from a SQL column with the NULL attribute.“我们来看一个例子

image

关于NULL<T>类型的具体介绍,请看相关内容。

 

    • mysqlpp::tiny_int<unsigned char>

为什么要有tiny_int,在给SQLTypeAdapter(char c)进行注释的时候,作者特地说,如果我们需要使用tiny_int,那么不要使用char,而应该是用mysqlpp:: tiny_int,这里也就是为了这个目的而有以下两个版本的SQLTypeAdapter构造函数

SQLTypeAdapter(tiny_int<unsigned char> i);

SQLTypeAdapter(tiny_int<signed char> i);

 

4. mysqlpp::SQLQueryParms

该类型很简单,其实就是作为template query的参数的集合。其实在mysqlpp:: Query中还用到了同样被定义在Qprams.h中的mysqlpp::SQLParseElement。

例如,如果有类似于这样的sql template 语句(可能和实际的MYSQL++用法不一致)

select * from where id = %d, name = ‘%s’.

则我们可以这样设置SQLQueryParms

SQLQueryParms sqp;

sqp << 1 << "root";

 

具体的实现细节请参看其他章节

 

 

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

posted @ 2014-03-30 09:53  aicro  阅读(2040)  评论(0编辑  收藏  举报