无法在MS SQL 2008中通过freetds unixODBC插入unicode字符

问题描述:

我有一个构建在RHEL 5上的C++应用程序,它通过位于Windows机器上的freeTDS和unixODBC连接到MS SQL 2008。无法在MS SQL 2008中通过freetds unixODBC插入unicode字符

这是应用程序发送到数据库的查询。

INSERT INTO mytable (SAMPLE) VALUES(N'乕乭乺丕')

[email protected]@Iz被调用上述查询时被实际插入到数据库中。

以下是我使用的配置 -

== freetds.conf == 
[myserver.mydomain.com] 
client charset = UTF-16 
debug flags = 0xffff 
dump file = /tmp/dump.log 
dump file append = yes 
host = 127.0.01 
port = 1433 
tds version = 7.3 

== odbcinst.ini == 
[FreeTDS Driver] 
Description  = FreeTDS 
Driver   = /usr/lib64/libtdsodbc.so.0 

== odbc.ini == 
[mydsn] 
Description  = MS SQL connection to 'mydb' database 
Driver   = FreeTDS Driver 
Servername  = myserver.mydomain.com 
Port   = 1433 
TDS_Version  = 7.3 
Database  = mydb 
UserName  = sa 
Password  = mypassword 
Trace   = Yes 
TraceFile  = /tmp/odbc.log 
ForceTrace  = Yes 

我可以直接通过

INSERT INTO mytable (SAMPLE) VALUES(N'乕乭乺丕')但不是通过freetds的和unixODBC的数据插入到数据库

请查找代码我使用如下:

#include <iostream> 

#ifdef WIN32 
    #include <windows.h> 
#endif 

#include <sql.h> 
#include <sqlext.h> 
#include <sqltypes.h> 

#include "unicode/ustdio.h" 

using namespace std; 

int main (int argc, char* argv[]) 
{ 
    SQLHSTMT hSQLStatement = 0; 
    SQLHENV hSQLEnvironment = 0; 
    SQLHDBC hSQLODBC = 0; 

    SQLRETURN sqlRet = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hSQLEnvironment); 

    if(SQL_SUCCEEDED(sqlRet)) 
    { 
    sqlRet = SQLSetEnvAttr(hSQLEnvironment, 
          SQL_ATTR_ODBC_VERSION, 
          (void*)SQL_OV_ODBC3, 
          0); 

    if(SQL_SUCCEEDED(sqlRet)) 
    { 
     sqlRet = SQLAllocHandle(SQL_HANDLE_DBC, 
           hSQLEnvironment, 
           &hSQLODBC); 
    } 
    else 
    { 
     cout << "Error in SQLAllocHandle for SQL_HANDLE_DBC" << endl; 
    } 
    } 
    else 
    { 
    cout << "Error in SQLAllocHandle for SQL_HANDLE_ENV" << endl; 
    } 

    UnicodeString DSNName = "mydsn"; 
    UnicodeString UserName = "sa"; 
    UnicodeString Password = "mypassword"; 

    UnicodeString Value = ""; 

    UChar32 character = 20053; 
    Value.append(character); 

    character = 20077; 
    Value.append(character); 

    character = 131140; 
    Value.append(character); 

    character = 131145; 
    Value.append(character); 

    character = 20090; 
    Value.append(character); 

    character = 19989; 
    Value.append(character); 

    UnicodeString SQLStatement = "INSERT INTO mytable (sample) VALUES(N"; 
    SQLStatement.append("'"); 
    SQLStatement.append(Value); 
    SQLStatement.append("'"); 
    SQLStatement.append(")"); 

    if(0 != hSQLODBC) 
    { 
    SQLRETURN sqlRet = SQLConnectW(hSQLODBC, 
            (SQLWCHAR*)DSNName.getTerminatedBuffer(), 
            SQL_NTS, 
            (SQLWCHAR*)UserName.getTerminatedBuffer(), 
            SQL_NTS, 
            (SQLWCHAR*)Password.getTerminatedBuffer(), 
            SQL_NTS); 

    if(SQL_SUCCEEDED(sqlRet)) 
    { 
     cout << "Connection to database successful" << endl; 

     SQLRETURN sqlRet = SQLAllocHandle(SQL_HANDLE_STMT, 
             hSQLODBC, 
             &hSQLStatement); 

     if(SQL_SUCCEEDED(sqlRet)) 
     { 
     sqlRet = SQLExecDirectW(hSQLStatement, 
           (SQLWCHAR*)SQLStatement.getTerminatedBuffer(), 
           SQL_NTS); 

     if(SQL_SUCCEEDED(sqlRet)) 
     { 
      cout << "Query Execution successful" << endl; 
     } 
     else 
      cout << "Query Execution failed" << endl; 
     } 
    } 
    else 
    { 
     cout << "Connection to database failed" << endl; 
    } 
    } 

    return 0; 
} 

有什么想法可能是错误的?

编辑1:添加示例代码

编辑2:更新按照奥利弗的建议

+1

“SAMPLE”列的数据类型必须为“NVARCHAR” –

+0

是数据类型为“NVARCHAR” – D3XT3R

+0

您使用的是哪种FreeTDS版本? 0.95,0.91? TDS版本7.3仅在FreeTDS 0.95中受支持。我相信0.91附带RHEL 6,但我不确定RHEL 5. – FlipperPA

SQL Server使用UTF-16 NVARCHAR。从你问题中的信息看来,你有UTF-8中的字符串。首先将插入语句转换为UTF-16,然后再将它发送到SQL Server。


更新:我看到你添加了代码示例。我看到您将UChar32值附加到UnicodeString实例。这些是4个字节宽。 AFAICT您需要appendUChar值(2个字节宽)。

+0

当通过使用SQL Server Native Client 10.0' odbc驱动程序在windows上构建的C++应用程序调用时,成功插入数据库。我不确定FreeTDS在这里可能会做什么。 – D3XT3R

+0

@ D3XT3R看一下实际插入的内容,这个缩小到单个字节的味道,而不是UTF-16字符串中的双字节。就像一个测试将字符串转换为字节数组,并插入字符串'INSERT INTO mytable(sample)VALUES(CAST(0x AS NVARCHAR(4000))',其中字节模式是HEX中的一系列字节表单为** UTF-16 ** –

+0

@ D3XT3R你应该真的考虑在你的问题中添加一个[MVCE](https://*.com/help/mcve),也就是你重现问题的代码有可能问题出在你的代码中,而不是FreeDTS驱动程序中。你使用的字符串类型是UTF-16吗? –

ICU UnicodeString此处的文档(https://ssl.icu-project.org/apiref/icu4c/classicu_1_1UnicodeString.html#details)指出:“在ICU中,Unicode字符串由16位Unicode代码单元组成。”由于您使用SQLStatement.getTerminatedBuffer()来获取该数据,因此当您抓住它并将其推入SQLExecDirectW()时,我将其读为UTF-16中的数据。

另一方面,您可以在FreeTDS配置中指定client charset = UTF-8。由于它与Native Client一起工作,我会尝试将FreeTDS客户端的字符集更改为UTF-16。

+0

更新我的字符集为UTF-16,同样的问题依然存在。 – D3XT3R