使用 Type_handler 向 MariaDB 添加新数据类型 – 第 3 部分

作者: Frédéric Descamps
原文链接:https://mariadb.org/adding-a-new-data-type-to-mariadb-with-type_handler-part-3/

上一篇文章中,我们使用 Type\_handler 框架为 MariaDB 编写、编译并测试了第一个自定义数据类型。

但目前,除了允许使用其新名称(MONEY)并在元数据中列出外,我们的新数据类型的行为与它所继承的 DOUBLE 类完全相同。

在本文中,我们将对数据类型进行一点扩展:将结果转换为 VARCHAR,并为其添加货币符号:美元符号($)。

这是预期的结果:

MariaDB [test]> insert into t1 (amount) values (41578.4);
Query OK, 1 row affected (0.016 sec)

MariaDB [test]> insert into t1 (amount) values (15678.309);
Query OK, 1 row affected (0.006 sec)

MariaDB [test]> select * from t1 order by amount desc;
+----+-----------+
| id | amount    |
+----+-----------+
|  1 | $41578.40 |
|  2 | $15678.31 |
+----+-----------+
2 rows in set (0.006 sec)

如你所见,我们刚刚在数值前添加了美元符号。

协议(Protocol)

为了控制我们的数据类型如何发送数据,我们需要重写 Type_handler_double 中的另一个方法:protocol_send_type()

sql_type_money.h 中,我们的类 Type_handler_money 的公有方法里添加以下定义:

  protocol_send_type_t protocol_send_type() const override
  {
    return PROTOCOL_SEND_STRING;
  }

我们在另一个类 Field_money 中添加了以下重写(override):

bool send(Protocol *protocol) override;

创建发送字段

plugin.cc 中,我们定义了 Field_money::make_send_field(),它将字段作为 double(双精度浮点数)发送。但现在我们需要更改它,发送更多规格说明:

void Field_money::make_send_field(Send_field *field)
{
  Field::make_send_field(field);
  field->set_handler(&type_handler_varchar);
  field->set_data_type_name(LEX_CSTRING{STRING_WITH_LEN("money")});
  field->length= MY_MAX(field->length, (ulong) (field_length + 32));
  field->decimals= 0;
}

这里,我们调用了基础的 Field 类实现,完全跳过了 Field_double。这会填充基本元数据,例如列名、空位(null-bit)、标志(flags)等。

下一行指示协议将该列作为 VARCHAR(字符串) 发送。我们需要这样做,因为我们的自定义方法会发出带有“$”前缀的文本。客户端必须将这些字节解释为字符串,而不是双精度浮点数(double)。

下一行显而易见:它将扩展元数据类型名称设置为“money”。这是为了支持此功能的客户端(例如 mariadb-connector-j)而准备的。

然后我们调整显示宽度(display width)和最后一行,并告知客户端这不是十进制数字,而是一个字符串。

最后,我们指定了对 Field_money::send() 的重写:

bool Field_money::send(Protocol *protocol)
{
  DBUG_ASSERT(marked_for_read());

```cpp
String numeric_buf;
  String money_buf;
  String *numeric= Field_double::val_str(&numeric_buf, &numeric_buf);

  money_buf.set_charset(numeric->charset());
  if (money_buf.append('$') || money_buf.append(*numeric))
    return true;

  return protocol->store(&money_buf);
}

这里我们获取值并附加 $ 符号。

该插件的实际代码位于 part3 分支

在下一篇文章中,我们将进一步扩展数据类型,并为了趣味性重写另一个方法。

与此同时,祝编码愉快!