C++开源项目:mysqlpp mysql_type_info类

114 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

本文是我之前在微信公众号上的一篇文章记录。原链接为:MySQL++学习笔记:mysql_type_info类

mysql_type_info类其实就是用来处理SQL类型到C++类型的转换,他是SQLBuffer的主要灵魂,我们上一章有提到过SQLBuffer,它是将SQL数据以字符串形式加上类型信息,用于将字符串转换为兼容的C++数据类型。

mysql_type_info类定义在头文件type_info.h中,该文件中还定义了另外两个类:mysql_ti_sql_type_info 和 mysql_ti_sql_type_info_lookup,这两个类都是mysql_type_info的友元类。

mysql_ti_sql_type_info

该类型主要是保存SQL与C++类型的map信息,核心的成员变量是:

// SQL类型的字符串描述,例如"TINYINT NOT NULL", "SMALLINT NOT NULL"等
const char* sql_name_;
// C++ type,例如typeid(float), typeid(int)
const std::type_info* c_type_; 
// SQL type, 被定义在mysql_com.h中的enum_field_types的常量
// 例如MYSQL_TYPE_FLOAT, MYSQL_TYPE_DOUBLE
const enum_field_types base_type_; 
// 自定义标志,是tf_default、tf_null、tf_unsigned的“或”的结果
// 表示SQL某个类型的默认值应该设default还是null等之类的。
const unsigned int flags_;    

最终该类型在mysql_type_info类中被定义成一个静态数组,如下:

typedef mysql_ti_sql_type_info sql_type_info;
//This table maps C++ type information to SQL type information.
static const sql_type_info types[];

该数组对应的赋值实现:

const mysql_type_info::sql_type_info mysql_type_info::types[] = {
    sql_type_info("DECIMAL NOT NULL", typeid(sql_decimal),
#if MYSQL_VERSION_ID >= 50001
            MYSQL_TYPE_NEWDECIMAL
#else
            MYSQL_TYPE_DECIMAL
#endif
            ),
    sql_type_info("TINYINT NOT NULL", typeid(sql_tinyint),
            MYSQL_TYPE_TINY, mysql_ti_sql_type_info::tf_default),
    sql_type_info("TINYINT UNSIGNED NOT NULL", typeid(sql_tinyint_unsigned),
            MYSQL_TYPE_TINY, mysql_ti_sql_type_info::tf_default |
            mysql_ti_sql_type_info::tf_unsigned),
    sql_type_info("SMALLINT NOT NULL", typeid(sql_smallint),
            MYSQL_TYPE_SHORT, mysql_ti_sql_type_info::tf_default),
    sql_type_info("SMALLINT UNSIGNED NOT NULL", typeid(sql_smallint_unsigned),
            MYSQL_TYPE_SHORT, mysql_ti_sql_type_info::tf_default |
            mysql_ti_sql_type_info::tf_unsigned),
    sql_type_info("INT NOT NULL", typeid(sql_int),
            MYSQL_TYPE_LONG, mysql_ti_sql_type_info::tf_default),
    sql_type_info("INT UNSIGNED NOT NULL", typeid(sql_int_unsigned),
            MYSQL_TYPE_LONG, mysql_ti_sql_type_info::tf_default |
            mysql_ti_sql_type_info::tf_unsigned),
    sql_type_info("FLOAT NOT NULL", typeid(sql_float),
            MYSQL_TYPE_FLOAT, mysql_ti_sql_type_info::tf_default),
    sql_type_info("FLOAT UNSIGNED NOT NULL", typeid(sql_float),
            MYSQL_TYPE_FLOAT, mysql_ti_sql_type_info::tf_default |
            mysql_ti_sql_type_info::tf_unsigned),
    sql_type_info("DOUBLE NOT NULL", typeid(sql_double),
    //....
    //省略部分
    //....
}

从上面我们可以看到,sql_type_info的第二个参数其实是C++类型,但是都被mysql++重新定义了,在文件sql_type.h中:

    typedef tiny_int<signed char>   sql_tinyint;
    typedef tiny_int<unsigned char> sql_tinyint_unsigned;
    typedef signed short            sql_smallint;
    typedef unsigned short          sql_smallint_unsigned;
    typedef signed int              sql_int;
    typedef unsigned int            sql_int_unsigned;
    typedef signed int              sql_mediumint;
    typedef unsigned int            sql_mediumint_unsigned;
    typedef longlong                sql_bigint;
    typedef ulonglong               sql_bigint_unsigned;

mysql_ti_sql_type_info_lookup

单从命名就大概能猜到它的功能了,就是查找上面我们将的sql_type_info类型中的静态数组。

为了验证真实,我们看下它的构造函数:

mysql_ti_sql_type_info_lookup::mysql_ti_sql_type_info_lookup(
        const sql_type_info types[], const int size)
{
    for (int i = 0; i < size; ++i) {
        if (types[i].is_default()) {
            map_[types[i].c_type_] = i;
        }
    }
}

果不其然,就是C++类型与sql_type_info types[]数组下标的一个map。我们再看看map_成员变量的定义:

typedef mysql_ti_sql_type_info sql_type_info;
typedef std::map<const std::type_info*, unsigned char, type_info_cmp> map_type;
​
​
map_type map_;

然后这个lookup在mysql_type_info中被定义为静态const变量,直接用同样静态const数组types[]赋值:

const int mysql_type_info::num_types =
        sizeof(mysql_type_info::types) / sizeof(mysql_type_info::types[0]);
​
const mysql_type_info::sql_type_info_lookup
        mysql_type_info::lookups(mysql_type_info::types,
        mysql_type_info::num_types);

mysql_type_info

从上面的介绍我们说了,mysql_type_info类中主要的两个功能就是下面两个类型:

  • sql_type_info
  • sql_type_info_lookup

这两个类型在mysql_type_info中都被定义成了静态const类型:

typedef mysql_ti_sql_type_info sql_type_info;
typedef mysql_ti_sql_type_info_lookup sql_type_info_lookup;
​
static const sql_type_info types[];
static const int num_types;
​
static const sql_type_info_lookup lookups;

我们上面已经介绍完它们的初始化,下面我们来看看mysql_type_info如何使用它们,首先看下mysql_type_info的构造函数:

下面就来看看具体的类型转换过程了:

  • C++类型--->SQL类型:

看下面的构造函数,传入C++类型,然后查找lookups这个map,找到types数组的index保存在num_中,这样就构造了一个mysql_type_info对象。

/// \brief Create object from a C++ type_info object
///
/// This tries to map a C++ type to the closest MySQL data type.
/// It is necessarily somewhat approximate.
mysql_type_info(const std::type_info& t) :
num_(lookups[t])
{
}

然后在调用mysql_type_info对象中的base_type函数即可获得SQL类型了,如下:

/// \brief Returns the type_info for the C++ type inside of the
/// mysqlpp::Null type.
///
/// Returns the type_info for the C++ type inside the mysqlpp::Null
/// type.  If the type is not Null then this is the same as c_type().
const mysql_type_info base_type() const
{
	return mysql_type_info(deref().base_type_);
}

细心的你可能发现这个怎么还是返回mysql_type_info类型呢?这里可能是为了隐藏SQL类型,进行对外只要知道mysql_type_info即可,而且看它的注释“Returns the type_info for the C++ type inside the mysqlpp::Null type. If the type is not Null then this is the same as c_type().”返回一个只含SQL类型的mysql_type_info对象,C++类型未null,看它下面的代码就基本明白了:

/// \brief Create object from MySQL C API type info
///
/// \param t the underlying C API type ID for this type
/// \param _unsigned if true, this is the unsigned version of the type
/// \param _null if true, this type can hold a SQL null
mysql_type_info(enum_field_types t, bool _unsigned = false,
bool _null = false) :
num_(type(t, _unsigned, _null))
{
}


unsigned char mysql_type_info::type(enum_field_types t,
		bool _unsigned, bool _null)
{
	for (unsigned char i = 0; i < num_types; ++i) {
		if ((types[i].base_type_ == t) &&
				(!_unsigned || types[i].is_unsigned()) &&
				(!_null || types[i].is_null())) {
			return i;
		}
	}

	return type(MYSQL_TYPE_STRING, false, _null);	// punt!
}
  • SQL类型--->C++类型:

调用构造函数,创建mysql_type_info对象:

/// \brief Create object from MySQL C API type info
///
/// \param t the underlying C API type ID for this type
/// \param _unsigned if true, this is the unsigned version of the type
/// \param _null if true, this type can hold a SQL null
mysql_type_info(enum_field_types t, bool _unsigned = false,
bool _null = false) :
num_(type(t, _unsigned, _null))
{
}

然后调用c_type就OK了:

/// \brief Returns the type_info for the C++ type associated with
/// the SQL type.
///
/// Returns the C++ type_info record corresponding to the SQL type.
const std::type_info& c_type() const { return *deref().c_type_; }

最后的最后还有两个接口,说这么多搞这么复杂的类型转换还有一个主要的原因就是为了判断哪个类型需要quote哪个需要escape,直接看源码吧:

/// \brief Returns true if the SQL type is of a type that needs to
/// be quoted.
///
/// \return true if the type needs to be quoted for syntactically
/// correct SQL.
bool quote_q() const;

/// \brief Returns true if the SQL type is of a type that needs to
/// be escaped.
///
/// \return true if the type needs to be escaped for syntactically
/// correct SQL.
bool escape_q() const;
bool mysql_type_info::quote_q() const
{
	const type_info& ti = base_type().c_type();
	return ti == typeid(string) ||
			ti == typeid(sql_date) ||
			ti == typeid(sql_time) ||
			ti == typeid(sql_datetime) ||
			ti == typeid(sql_blob) ||
			ti == typeid(sql_tinyblob) ||
			ti == typeid(sql_mediumblob) ||
			ti == typeid(sql_longblob) ||
			ti == typeid(sql_char) ||
			ti == typeid(sql_set);
}

bool mysql_type_info::escape_q() const
{
	const type_info& ti = base_type().c_type();
	return ti == typeid(string) ||
			ti == typeid(sql_enum) ||
			ti == typeid(sql_blob) ||
			ti == typeid(sql_tinyblob) ||
			ti == typeid(sql_mediumblob) ||
			ti == typeid(sql_longblob) ||
			ti == typeid(sql_char) ||
			ti == typeid(sql_varchar);
}