协会地址:上海市长宁区古北路620号图书馆楼309-313室
使用 Type_handler 向 MariaDB 添加新数据类型 – 第5部分
作者: Frédéric Descamps
原文链接:https://mariadb.org/adding-a-new-data-type-to-mariadb-with-type_handler-part-5/
我们正在使用 Type_handler 框架结束关于新数据类型的系列文章,但该框架尚未涵盖一些限制:
- 无自定义索引方法。插件类型无法引入新的索引方法。
- 无自定义哈希。插件类型无法提供自己的哈希函数用于基于哈希的操作。诸如 MEMORY 表索引、GROUP BY 和分区等操作会回退到底层类型的哈希。
- 无新字段属性。插件类型无法定义除现有属性(长度、精度、小数位数和 GIS SRID)之外的自定义属性。
如果我们的 MONEY 数据类型能够定义例如要显示的货币,或者像这样的格式,那将会很方便:
- $1,000,000
- 40.000,50 EUR
- ¥2 000 000
不幸的是,我们无法为数据类型创建新的字段属性。但你可能已经知道,列有一个名为 COMMENT 的额外属性。我们可以利用它来满足我们的需求。
扩展 Field_money
我们需要在 sql_type_money.h 中扩展我们的 Field_money 类,添加格式样式和显示配置结构:
enum money_format_style
{
MONEY_FMT_CURRENCY_FIRST= 0,
MONEY_FMT_CURRENCY_LAST
};
```c
struct money_display_config
{
char currency[32];
char decimal_sep;
char thousands_sep;
bool allow_grouping;
money_format_style style;
};
</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>我们还添加了两个私有方法,用于解析 <code>COMMENT</code> 属性并格式化输出:</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>private:
bool parse_comment_config(money_display_config *cfg) const;
void format_money_string(String *to, double nr,
const money_display_config &cfg) const;
</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>这两个函数将在 <code>plugin.cc</code> 中指定。</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>bool Field_money::parse_comment_config(money_display_config *cfg) const
{
money_set_default_display_config(cfg);
```c
if (!comment.str || !comment.length)
return true;
const char *p= comment.str;
const char *end= comment.str + comment.length;
while (p < end)
{
const char *entry_end= (const char*) memchr(p, ';', (size_t) (end - p));
if (!entry_end)
entry_end= end;
const char *eq= (const char*) memchr(p, '=', (size_t) (entry_end - p));
if (eq)
{
const char *k= money_trim_left(p, eq);
const char *k_end= money_trim_right_begin(k, eq);
const char *v= money_trim_left(eq + 1, entry_end);
const char *v_end= money_trim_right_begin(v, entry_end);
size_t k_len= (size_t) (k_end - k);
size_t v_len= (size_t) (v_end - v);
money_apply_comment_option(k, k_len, v, v_len, cfg);
}
p= entry_end < end ? entry_end + 1 : end;
}
if (cfg->decimal_sep == cfg->thousands_sep)
{
if (cfg->decimal_sep == ',')
cfg->thousands_sep= '.';
else
cfg->thousands_sep= ',';
}
return true;
}
void Field_money::format_money_string(String *to, double nr,
const money_display_config &cfg) const
{
decimal_digits_t out_dec= money_effective_decimals(dec);
char raw[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
size_t raw_len= my_fcvt(nr, out_dec, raw, NULL);
raw[raw_len]= '\0';
const char *p= raw;
bool negative= false;
if (*p == '-')
{
negative= true;
p++;
}
const char *dot= strchr(p, '.');
size_t int_len= dot ? (size_t) (dot - p) : strlen(p);
const char *frac= dot ? dot + 1 : "";
size_t frac_len= dot ? strlen(frac) : 0;
to->length(0);
to->set_charset(system_charset_info);
if (negative && to->append('-'))
return;
if (cfg.style == MONEY_FMT_CURRENCY_FIRST && money_append_currency(to, cfg, true))
return;
for (size_t i= 0; i < int_len; i++)
{
if (cfg.allow_grouping && cfg.thousands_sep && i > 0 && ((int_len - i) % 3) == 0)
{
if (to->append(cfg.thousands_sep))
return;
}
if (to->append(p[i]))
return;
}
if (out_dec > 0)
{
if (to->append(cfg.decimal_sep))
return;
for (uint i= 0; i < out_dec; i++)
{
if (to->append((i < frac_len) ? frac[i] : '0'))
return;
}
}
if (cfg.style == MONEY_FMT_CURRENCY_LAST && money_append_currency(to, cfg, false))
return;
}
这两个函数所使用的其他所有函数都定义在一个新的包含文件 money.h 中。
最后,我们修改了 Field_money::send(Protocol *protocol),使其在显示之前解析配置。
bool Field_money::send(Protocol *protocol)
{
DBUG_ASSERT(marked_for_read());
```cpp
String money_buf;
money_display_config cfg;
parse_comment_config(&cfg);
format_money_string(&money_buf, Field_money::val_real(), cfg);
return protocol->store(&money_buf);
}
</code></pre>
<!-- /wp:code -->
<!-- wp:paragraph -->
<p>修改后的数据类型代码一如既往地存放在 <a href="https://github.com/lefred/type_money">GitHub 仓库</a> 的 <a href="https://github.com/lefred/type_money/tree/part5">part5 分支</a> 中。</p>
<!-- /wp:paragraph -->
<!-- wp:heading -->
<h2 class="wp-block-heading">示例</h2>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p>俗话说“一图胜千言”,让我们看看这个插件的实际效果:</p>
<!-- /wp:paragraph -->
<!-- wp:code -->
<pre class="wp-block-code"><code>MariaDB [test]> install soname 'type_money';
Query OK, 0 rows affected (0.012 sec
```sql
MariaDB [test]> use test;
Database changed
MariaDB [test]> create table t1 (id int auto_increment primary key,
amount money(10,2) comment
'currency=EUR;format=currenty_last;decimal=,;thousands=space',
credit money(10,2) default null comment
'currency=$;format=currency_first;decimal=.;thousands=,'
) engine=innodb;
Query OK, 0 rows affected (0.006 sec)
MariaDB [test]> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`amount` money(10,2) DEFAULT NULL COMMENT 'currency=EUR;format=currenty_last;decimal=,;thousands=space',
`credit` money(10,2) DEFAULT NULL COMMENT 'currency=$;format=currency_first;decimal=.;thousands=,',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci
1 row in set (0.011 sec)
MariaDB [test]> insert into t1 (amount,credit) values (41578.4,2342);
Query OK, 1 row affected (0.011 sec)
MariaDB [test]> insert into t1 (amount,credit) values (24.4,12.50);
Query OK, 1 row affected (0.013 sec)
MariaDB [test]> insert into t1 (amount,credit) values (15678.309,-1234.299);
Query OK, 1 row affected (0.006 sec)
MariaDB [test]> select * from t1;
+----+---------------+------------+
| id | amount | credit |
+----+---------------+------------+
| 1 | 41 578,40 EUR | $2,342.00 |
| 2 | 15 678,31 EUR | -$1,234.30 |
| 3 | 24,40 EUR | $12.50 |
+----+---------------+------------+
3 rows in set (0.010 sec)
结论
正如您在本系列中所见,使用 Type_handler 框架向 MariaDB 添加新数据类型非常简单。
与 MySQL 相比,添加新数据类型要容易得多,而且 MariaDB 中提供的新类型(如 INET4、INET6、UUID)都已经使用了该框架。
那么,请告诉我们您正在添加哪种数据类型,祝编码愉快!
- 使用 Type_handler 向 MariaDB 添加新数据类型 – 第 0 部分 – 如何构建 MariaDB 服务器
- 使用 Type_handler 向 MariaDB 添加新数据类型 – 第 1 部分 – 理解框架
- 使用 Type_handler 向 MariaDB 添加新数据类型 – 第 2 部分 – 最小工作数据类型
- 使用 Type_handler 向 MariaDB 添加新数据类型 – 第 3 部分 – 数据类型输出转换
- 使用 Type_handler 向 MariaDB 添加新数据类型 – 第 4 部分 – 覆盖现有类型
- 使用 Type_handler 向 MariaDB 添加新数据类型 – 第 5 部分 – 限制与变通方法







