本文说明一下MySQL中不支持欧元字符的原因及解决方法。
1、 问题描述
在mysql中插入的字符串中若包含欧元字符(€),会发现该字符及以后的字符串都变得“不可见”。实际上这里并非不可见,而是根本没有进入数据库中。简单描述步骤如下:
root@test 05:15:41>create table t(c char(32)) engine=innodb charset=gbk;
Query OK, 0 rows affected (0.01 sec)
root@test 05:16:29>insert into t values(concat('a', char(128), 'b'));
Query OK, 1 row affected, 1 warning (0.00 sec)
root@test 05:17:04>select * from t;
+------+
| c |
+------+
| a |
+------+
|
可以看到,我们试图插入三个字符(a, €, b), 但在查询中发现只有一个字符a.
Insert的一个warning内容为 Incorrect string value: '\x80b' for column 'c' at row 1
说明在插入0x80(即€) 时出错,导致后面的字符也一起被丢弃.
2、 原因分析
我们知道,在mysql将语句中设置的值传给引擎时,需要先拷贝到一个临时结构中。但拷贝字符串只会以\0结束,0x80对于字符串拷贝过程应该是一个正常的字符。
拷贝函数
于是我们找到做值拷贝的过程, 在sql/sql_string.cc的well_formed_copy_nchars中,对应的两行代码如下
res= to_cs->cset->well_formed_len(to_cs, from, from + from_length,
nchars, &well_formed_error);
memmove(to, from, res);
可以看到,拷贝内容时,先计算了可拷贝的长度,然后在用memmove. 调试发现,在我们上面的试验中,返回的res=1, 因此memmove就只拷贝了一个字符(a).
计算长度
于是问题出在这个to_cs->cset->well_formed_len中。
由于MySQL要支持多种字符编码,因此定义了MY_CHARSET_HANDLER (在include/m_ctype.h)中,这个结构体定义了各种编码下的特定函数接口。对应的实现实体在strings/ctyp-*.c中。
我们的例子中用到的是gbk,因此看strings/ctype-gbk.c。 上述计算长度的函数,实际上调用的是本文件中的my_well_formed_len_gbk。
static
size_t my_well_formed_len_gbk(CHARSET_INFO *cs __attribute__((unused)),
const char *b, const char *e,
size_t pos, int *error)
{
const char *b0= b;
const char *emb= e - 1; /* Last possible end of an MB character */
*error= 0;
while (pos-- && b < e)
{
if ((uchar) b[0] <= 128)
{
/* Single byte ascii character */
b++;
}
else if ((b < emb) && isgbkcode((uchar)*b, (uchar)b[1]))
{
/* Double byte character */
b+= 2;
}
else
{
/* Wrong byte sequence */
*error= 1;
break;
}
}
return (size_t) (b - b0);
}
第12行是对于英文字符的处理,第17行是对于中文字符的处理,其他的直接到第22行后判为error。我们看到,0x80就到了*error=1, 然后break。
3、 修改和小结
实际上欧元字符也可以作为单字节处理, 将上面代码的第12行改为if ((uchar) b[0] <= 128), 重新编译后执行结果如下
mysql> insert into t values(concat('a', char(128), 'b'));
Query OK, 1 row affected (0.00 sec)
insert select hex(c) from t;
+--------+
| hex(c) |
+--------+
| 618062 |
+--------+
|
欧元字符在笔者客户端内显示不正常,但从hex(c)中看到三个字符已经正常存储在mysql中。
其他字符编码也有此问题,都修改相应的string/ctype-*.c中的my_well_formed_len*即可。
分享到:
相关推荐
mysql解析Json字符串插件 安装方法 1、拷贝lib_mysqludf_json_parsing.dll到mysql目录C:\Program Files\MariaDB 5.5\lib\plugin下 2、在数据库中执行 DROP FUNCTION json_get; CREATE FUNCTION json_get RETURNS ...
mysql分割
mysql 字符集 转换
MySQL字符串操作MySQL字符串操作MySQL字符串操作MySQL字符串操作MySQL字符串操作MySQL字符串操作MySQL字符串操作MySQL字符串操作
MYSQL修改字符集默认问题
设置mysql字符集 mysql 乱码
asp连接mysql字符串,使用odbc 3.51
亲测可用,mysql字符串相似度匹配函数。下载后直接在mysql中可以测试运行。
查看mysql字符集MySQL 乱码的根源是的 MySQL 字符
MySQL 批量修改SCHEMA下所有数据表的编码及字符集
mysql数据库互相转换及同步工具-MySQL_MySQL字符集互转
利用mysql中提供的所有函数,组成的一个存储过程。此存储过程主要是将传入的字符串分割为多个值,单个列。
输入2个中文字符串,计算2个字符串的相似度,用于相似度排序。
mysql数据库全文查找,查询所有数据库中包含指定字符的数据。 一、支持功能: 1、支持所有数据库查询字符串,或者指定一个或者多个数据库查询字符串; 2、支持本地使用或者指定远程数据库地址; 3、支持命令行指定...
MySql连接字符串总结 MySql连接字符串总结
mysql批量修改字符编码,可以把整个数据库的所有表的charset统一设置为utf-8的。
mysql拼接字符串函数
那问题来了,怎么才能匹配出中文字符呢? 本文提供两种方法。 二 演示 2.1 环境 mysql> SHOW VARIABLES LIKE "%version%"; + ————————-+——————————+ | Variable_name | ...
关于MySQL字符集查看与修改; MySQL的字符集支持(Character Set Support)有两个方面: 字符集(Character set)和排序方式(Collation)。 MySQL对于字符集的支持细化到四个层次: 服务器(server),数据库(database),...
MySql连接字符串问题,我搞了半天。对于初学者来说还是有用的。