使用ODBC到SQL Server的Linux上的字符集不匹配

时间:2016-05-10 14:23:49

标签: c sql-server character-encoding odbc freetds

我尝试使用适用于Linux的Microsoft ODBC驱动程序将非ASCII字符插入SQL Server数据库时遇到了一个有趣的问题。问题是它似乎在发送和接收数据时假设不同的字符集。有关信息,服务器排序规则设置为Latin1_General_CI_AS(我只是尝试插入欧洲重音字符)。

使用tsql(FreeTDS附带)进行测试,一切都很好。启动时,它输出以下内容:

locale is "en_GB.utf8"
locale charset is "UTF-8"
using default charset "UTF-8"

我可以在表格中插入并选择非ASCII值。

但是,使用我自己的使用ODBC API的实用程序,它不起作用。当我执行选择查询时,数据将根据需要以UTF-8字符集返回。但是,如果我插入UTF-8字符,它们就会被破坏。

SQL > update test set a = 'Béthune';
Running SQL: update test set a = 'Béthune'
Query executed OK: 1 affected rows
SQL > select * from test;
Running SQL: select * from test
+------------+
| a          |
+------------+
| Béthune |
+------------+

如果我改为插入ISO-8859-1中编码的数据,那么它可以正常工作,但是选择查询仍将返回以UTF-8编码的数据!

我已将区域设置设置为en_GB.utf8,并在数据库连接详细信息中设置了client charset UTF-8。 AARGH!

FWIW无论是使用FreeTDS驱动程序还是官方Microsoft驱动程序,我似乎都遇到了同样的问题。

编辑:刚刚意识到一个相关点,即在这个测试程序中,它没有使用带有绑定变量的预准备语句。换句话说,更新SQL直接传递到SQLPrepare调用。 ODBC中的某些东西肯定在进行iconv翻译,但显然不是正确的字符集!

#0  0x0000003d4c41f850 in iconv () from /lib64/libc.so.6
#1  0x0000003d4d83fd94 in ?? () from /usr/lib64/libodbc.so.2
#2  0x0000003d4d820465 in SQLPrepare () from /usr/lib64/libodbc.so.2

我会尝试编译自己的UnixODBC,以便更好地了解正在发生的事情。

编辑2:我已经从源代码构建UnixODBC来调试它正在做的事情,问题是nl_langinfo(CODESET)报告ISO-8859-1。这很奇怪,因为它的手册页说它是从locale charmap获得的相同字符串,返回UTF-8。我猜这是问题,但仍不确定如何解决。

2 个答案:

答案 0 :(得分:2)

一位正在工作的同事至少已经找到了FreeTDS的解决方案。

对于直接驱动程序连接(SQLDriverConnect()),将ClientCharset=UTF-8;ServerCharset=CP1252;添加到连接字符串修复了问题

对于通过驱动程序管理器(SQLConnect())的连接,我可以将这些行添加到odbc.ini中的连接设置中:

client charset = UTF-8
server charset = CP1252

无法使用Microsoft驱动程序找出解决方案...

答案 1 :(得分:2)

Microsoft ODBC驱动程序的解决方案可能是在LANG环境变量中设置正确的值。

确保已安装和配置所需的区域设置。还要确保为运行应用程序的用户正确设置了LANG环境变量。这对守护进程来说可能很棘手。例如,为了使它适用于使用Apache2的PHP,我必须将export LANG=en_US.utf8添加到/etc/apache2/envvars