使用 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 部分。

以下是之前文章的链接:

覆盖现有类型

在之前的示例中,我们的 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 派生以修改排序,我们需要在我们的类中实现以下方法,使其成为具体且可用的类:

  1. type_handler
  2. key_type
  3. store(从字符串)
  4. store(从 double)
  5. store(从 longlong)
  6. reset
  7. val_real
  8. val_int
  9. val_str
  10. send
  11. cmp
  12. sort_string
  13. pack_length
  14. row_pack_length
  15. get_max_int_value
  16. 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分支中找到。

祝编译愉快,编码愉快。