协会地址:上海市长宁区古北路620号图书馆楼309-313室
使用 Type_handler 向 MariaDB 添加新数据类型 – 第 4 部分
作者: Frédéric Descamps
原文链接:https://mariadb.org/adding-a-new-data-type-to-mariadb-with-type_handler-part-4/
文章导航
(https://www.youtube.com/channel/UCT2rydFTpxunueD-CtIQoWA)
文章导航
本文是基于 Type_handler 框架为 MariaDB 扩展自定义数据类型的系列文章的第 4 部分。
以下是之前文章的链接:
- 使用 Type_handler 为 MariaDB 添加新数据类型 – 第 0 部分 – 如何构建 MariaDB 服务
- 使用 Type_handler 为 MariaDB 添加新数据类型 – 第 1 部分 – 理解框架
- 使用 Type_handler 为 MariaDB 添加新数据类型 – 第 2 部分 – 最小可用数据类型
- 使用 Type_handler 为 MariaDB 添加新数据类型 – 第 3 部分 – 数据类型输出转换
覆盖现有类型
在之前的示例中,我们的 MONEY 数据类型继承自 DOUBLE,然后我们覆盖了一些方法。
但并非所有类型的方法都可以被覆盖。例如,如果我们想要更改 money 列的排序方式,就必须覆盖 Field_double::sort_string() 方法。但该方法在 sql/field.h 中被定义为 final:
void sort_string(uchar *buff, uint length) override final;
这意味着我们需要切换到 Field_real 类型。但 Field_real 是抽象类,要求我们实现 Field_double 免费提供的方法。
因此,Field_money 必须提供自己的数字字段操作(store、val_real、val_str、cmp 等)。
下表直接来源于源代码,列出了所有字段类型:
| 字段类型 | 状态 | 主要限制 |
|---|---|---|
| Field_num | 抽象基类 | 仅数字辅助层;不可实例化 |
| Field_str | 抽象基类 | 仅字符串辅助层;不可实例化 |
| Field_longstr | 抽象基类 | 需要实现 max_data_length 和 packed_col_length |
| Field_real | 抽象基类 | 仅浮点/实数辅助层;关键数字操作必须由子类实现 |
| Field_decimal | 具体最终类 | 行为固定;不可子类化 |
| Field_new_decimal | 具体最终类 | 行为固定;不可子类化 |
| Field_int | 抽象基类 | 需要实现 type_limits_int |
| Field_tiny | 具体类 | 可扩展,但仍受整数语义约束 |
| Field_short | 具体类 | 可扩展,整数语义 |
| Field_medium | 具体最终类 | 不可子类化 |
| Field_long | 具体最终类 | 不可子类化 |
| Field_longlong | 具体类 | 可扩展,整数语义 |
| Field_vers_trx_id | 具体类 | 特殊版本化事务 ID 语义 |
| Field_float | 具体最终类 | 不可子类化 |
| Field_double | 具体类(许多方法为 final) | 关键方法被锁定;特别是 cmp 在该类中为 final |
| Field_null | 具体类 | 表示类似 NULL 的行为;许多操作固定 |
| Field_temporal | 抽象基类 | 仅时间辅助层 |
| Field_temporal_with_date | 抽象基类 | 需要实现 store_TIME 和 get_TIME |
| Field_timestamp | 抽象基类 | 需要实现 store_TIMEVAL |
| Field_timestamp0 | 具体类 | Timestamp(0) 风格存储语义 |
| Field_timestamp_with_dec | 抽象基类 | 分数时间戳基类 |
| Field_timestamp_hires | 具体类 | 特殊高精度时间戳行为 |
| Field_timestampf | 具体类 | MySQL56 timestamp(0..6) 变体 |
| Field_year | 具体最终类 | 不可子类化 |
| Field_date_common | 抽象基类 | 共享日期逻辑;具体日期类实现存储细节 |
| Field_date | 具体最终类 | 不可子类化 |
| Field_newdate | 具体最终类 | 不可子类化 |
| Field_time | 抽象基类 | 需要实现 store_TIME |
| Field_time0 | 具体最终类 | 不可子类化 |
| Field_time_with_dec | 抽象基类 | 分数时间基类 |
| Field_time_hires | 具体最终类 | 不可子类化 |
| Field_timef | 具体最终类 | 不可子类化 |
| Field_datetime | 抽象基类 | 需要子类实现日期时间底层方法 |
| Field_datetime0 | 具体最终类 | 不可子类化 |
| Field_datetime_with_dec | 抽象基类 | 分数日期时间基类 |
| Field_datetime_hires | 具体最终类 | 不可子类化 |
| Field_datetimef | 具体最终类 | 不可子类化 |
| Field_string | 具体最终类 | 固定 CHAR 行为 |
| Field_varstring | 具体类 | 可扩展 VARCHAR 行为 |
| Field_varstring_compressed | 具体最终类 | 不可子类化;键/索引行为被有意限制 |
| Field_blob | 具体类 | 复杂指针/长度语义;引擎敏感行为 |
| Field_blob_compressed | 具体最终类 | 不可子类化;键/索引操作受限 |
| Field_enum | 具体类 | ENUM 语义;某些优化路径被有意限制 |
| Field_set | 具体最终类 | 不可子类化 |
| Field_bit | 具体类 | 位存储具有特殊的键/比较约束 |
| Field_bit_as_char | 具体最终类 | 不可子类化 |
| Field_row | 具体最终类 | 特殊虚拟行类型;许多常规字段操作不适用 |
“抽象”表示不能直接实例化;必须派生并实现缺失的行为。“具体”表示可实例化。“最终”表示具体且不可子类化。
从 Field_real 派生
因此,由于我们需要从 Field_real 派生以修改排序,我们需要在我们的类中实现以下方法,使其成为具体且可用的类:
- type_handler
- key_type
- store(从字符串)
- store(从 double)
- store(从 longlong)
- reset
- val_real
- val_int
- val_str
- send
- cmp
- sort_string
- pack_length
- row_pack_length
- get_max_int_value
- binlog_type_info
这正是我们所做的,并且我们进一步修改了 sort_string() 方法,以将 MONEY 列的内容按逆序排序。
void Field_money::sort_string(uchar *buff, uint length __attribute__((unused)))
{
double nr;
float8get(nr,ptr);
change_double_for_sort(nr, buff);
```c
// 反转关键字节,使得字典序升序比较
// 产生降序结果。
for (uint i = 0; i < sizeof(double); i++)
buff[i] ^= 0xFF;
}
这是一种愚蠢的排序方式,但它仅作为示例存在。
让我们测试一下:
MariaDB [test]> install soname 'type_money';
Query OK, 0 rows affected (0.012 sec)
MariaDB [test]> create table t1 (id int auto_increment primary key, amount money(10,2)) engine=innodb;
Query OK, 0 rows affected (0.008 sec)
MariaDB [test]> insert into t1 (amount) values (15678.309);
Query OK, 1 row affected (0.016 sec)
MariaDB [test]> insert into t1 (amount) values (24.4);
Query OK, 1 row affected (0.003 sec)
MariaDB [test]> insert into t1 (amount) values (41578.4);
Query OK, 1 row affected (0.003 sec)
MariaDB [test]> select * from t1 order by amount;
+----+-----------+
| id | amount |
+----+-----------+
| 3 | $41578.40 |
| 1 | $15678.31 |
| 2 | $24.40 |
+----+-----------+
3 rows in set (0.002 sec)
我们可以看到顺序是颠倒的。
代码可在GitHub仓库的part4分支中找到。
祝编译愉快,编码愉快。







