我尝试使用适用于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
。我猜这是问题,但仍不确定如何解决。
答案 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
。