Problem with connector to get latin data and UTF8 data in one table

Our software gets data from different databases, one of them ist MySQL. Data is stored in tables, fields can defined as UTF8 or non UTF8. But with MariaDb connectors this don't work anylonger.

Spend a lot of time to find a fix or workaround, but without success yet. Thats why we wrote a sample to compare access via MariaDB ODBC connector (3.1) an MySQL ODBC connector (5.3) to a MariaDB database. It based on a sample from MS. The result is different, it looks like the connectors are not compatible.

key_fieldchar_latinchar_utf8
1Text 1Text 2
2Text äText 薖

That is the output reading data with the connectors:

  MariaDB ODBC 3.1 Driver 
  key_field : 1
  char_latin(char)    : Text 1                                                                      // OK 'Text 1' as char  
  char_latin(byte)    : 84 - 101 - 120 - 116 - 32 - 49 - 0 -                                        // OK 'Text 1' as unsigned char (ASCII)
  char_utf8(2 byte)   : 0 | 84 - 0 | 101 - 0 | 120 - 0 | 116 - 0 | 32 - 0 | 50 - 0 | 0 -            // OK 'Text 2' as 2 x unsigned char   
  char_utf8(widechar) : 84 - 101 - 120 - 116 - 32 - 50 -                                            // OK 'Text 2' as widechar  

  key_field : 2
  char_latin(char)    : Text ä
  char_latin(byte)    : 84 - 101 - 120 - 116 - 32 - 228 - 0 -
  char_utf8(2 byte)   : 0 | 84 - 0 | 101 - 0 | 120 - 0 | 116 - 0 | 32 - 0 | 63 - 0 | 0 -
  char_utf8(widechar) : 84 - 101 - 120 - 116 - 32 - 63 -                                            // Error  -63-      SQLBindCol(hstmt, 3, SQL_C_WCHAR,...

-> latin text is OK, utf8 not

  MariaDB ODBC 3.1 Driver using parameter CHARSET = UTF8 
  key_field : 1
  char_latin(char)    : Text 1
  char_latin(byte)    : 84 - 101 - 120 - 116 - 32 - 49 - 0 -
  char_utf8(2 byte)   : 0 | 84 - 0 | 101 - 0 | 120 - 0 | 116 - 0 | 32 - 0 | 50 - 0 | 0 -
  char_utf8(widechar) : 84 - 101 - 120 - 116 - 32 - 50 -

  key_field : 2
  char_latin(char)    : Text ä
  char_latin(byte)    : 84 - 101 - 120 - 116 - 32 - 195 - 164 - 0 -                                 // Error  -195-164-  SQLBindCol(hstmt, 2, SQL_C_CHAR ,...    
  char_utf8(2 byte)   : 0 | 84 - 0 | 101 - 0 | 120 - 0 | 116 - 0 | 32 - 133 | 150 - 0 | 0 -
  char_utf8(widechar) : 84 - 101 - 120 - 116 - 32 - 34198 -

-> now latin is wrong, but utf8 is OK

  MySQL ODBC 5.3 Unicode Driver
  key_field : 1
  char_latin(char)    : Text 1
  char_latin(byte)    : 84 - 101 - 120 - 116 - 32 - 49 - 0 -
  char_utf8(2 byte)   : 0 | 84 - 0 | 101 - 0 | 120 - 0 | 116 - 0 | 32 - 0 | 50 - 0 | 0 -
  char_utf8(widechar) : 84 - 101 - 120 - 116 - 32 - 50 -

  key_field : 2
  char_latin(char)    : Text ä
  char_latin(byte)    : 84 - 101 - 120 - 116 - 32 - 228 - 0 -                                       // OK  -228- 
  char_utf8(2 byte)   : 0 | 84 - 0 | 101 - 0 | 120 - 0 | 116 - 0 | 32 - 133 | 150 - 0 | 0 -
  char_utf8(widechar) : 84 - 101 - 120 - 116 - 32 - 34198 -                                         // OK  -34198-

-> with MySQL connector all data results are ok (and in other rdbms too)

Steps to reproduce:

1. Create the table

-- MySQL dump 10.13  Distrib 5.6.24, for Win64 (x86_64)
--
-- Host: MariaDBServer    Database: test
-- ------------------------------------------------------
-- Server version	5.5.5-10.1.33-MariaDB

--
-- Table structure for table `table_test_charset`
--

DROP TABLE IF EXISTS `table_test_charset`;
CREATE TABLE `table_test_charset` (
  `key_field` int(11) NOT NULL AUTO_INCREMENT,
  `char_latin` char(20) COLLATE latin1_german1_ci DEFAULT NULL,
  `char_utf8` char(20) CHARACTER SET utf8 DEFAULT NULL,
  PRIMARY KEY (`key_field`),
  UNIQUE KEY `key_UNIQUE` (`key_field`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 COLLATE=latin1_german1_ci;

--
-- Dumping data for table `table_test_charset`
--

LOCK TABLES `table_test_charset` WRITE;
INSERT INTO `table_test_charset` VALUES (1,'Text 1','Text 2'),(2,'Text ä','Text 薖');
UNLOCK TABLES;

2. Short code to read the data

#include <windows.h>  
#include <sqlext.h>  
#include <iostream>
using namespace std;

// Compiler: Charset = Multibyte
#define   UCP     (SQLTCHAR*)

SQLHDBC   hdbc;
SQLHSTMT  hstmt;

int  test_mariadb(const char* dsn_mariadb, const char* dsn_mysql);
void test_select();

int main(int argc, char *argv[])
{
  // confige the connections strings
  test_mariadb("DRIVER={MariaDB ODBC 3.1 Driver};SERVER=MariaDBServer;PORT=3306;DATABASE=test;UID=root;PWD=password;", "DRIVER={MySQL ODBC 5.3 Unicode Driver};SERVER=MariaDBServer;PORT=3306;DATABASE=test;UID=root;PWD=password;");

  cout << "Press <enter>";
  getchar();
  
  return 0;
}


int test_mariadb(const char* dsn_mariadb, const char* dsn_mysql)
{
  SQLHENV henv;
  SQLRETURN retcode;

  SQLCHAR OutConnStr[255];
  SQLSMALLINT OutConnStrLen;

  HWND desktopHandle = 0;

  // Allocate environment handle  
  retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);

  // Set the ODBC version environment attribute  
  if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {

    retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3_80, 0);

    // Allocate connection handle  
    if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
      retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

      // Set login timeout to 5 seconds  
      if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
        SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);


        // ****************************************************************
        cout << "MariaDB ODBC 3.1 Driver" << endl;
        // ****************************************************************

        SQLCHAR Connection_mariadb[255];

        sprintf_s((char*)Connection_mariadb, 255, "%s", dsn_mariadb);

        retcode = SQLDriverConnect( hdbc, desktopHandle, 
          UCP(LPCTSTR)Connection_mariadb, SQL_NTS,
          UCP(LPCTSTR)OutConnStr, 255, &OutConnStrLen,
          SQL_DRIVER_NOPROMPT);

        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) 
        {
          test_select();
          cout << endl;

          SQLDisconnect(hdbc);
        }

        // ****************************************************************
        cout << "MariaDB ODBC 3.1 Driver using parameter CHARSET=UTF8" << endl;
        // ****************************************************************

        SQLCHAR Connection_mariadb_utf8[255];

        sprintf_s((char*)Connection_mariadb_utf8, 255, "%sCHARSET=UTF8;", dsn_mariadb );

        retcode = SQLDriverConnect( hdbc,desktopHandle,
          UCP(LPCTSTR)Connection_mariadb_utf8, SQL_NTS,
          UCP(LPCTSTR)OutConnStr, 255, &OutConnStrLen,
          SQL_DRIVER_NOPROMPT);

        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
        {
          test_select();
          cout << endl;

          SQLDisconnect(hdbc);
        }

        // ****************************************************************
        cout << "MySQL ODBC 5.3 Unicode Driver" << endl;
        // ****************************************************************

        SQLCHAR Connection_mysql[255];

        sprintf_s((char*)Connection_mysql, 255, "%s", dsn_mysql);

        retcode = SQLDriverConnect(hdbc, desktopHandle,
          UCP(LPCTSTR)Connection_mysql, SQL_NTS,
          UCP(LPCTSTR)OutConnStr, 255, &OutConnStrLen,
          SQL_DRIVER_NOPROMPT);

        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
        {
          test_select();
          cout << endl;

          SQLDisconnect(hdbc);
        }

        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
      }
    }
    SQLFreeHandle(SQL_HANDLE_ENV, henv);
  }

  return 0;
}

void test_select()
{
  SQLRETURN retcode;

  retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

  retcode = SQLExecDirect(hstmt, (SQLTCHAR*)"SELECT key_field, char_latin, char_utf8 FROM test.table_test_charset", SQL_NTS);

  if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  {
    long         key_field;
    SQLCHAR      char_latin[20+1];  // one null termination
    SQLWCHAR     char_utf8[20+2];   // double null termination

    SQLLEN       len_key_field;
    SQLLEN       len_char_latin;
    SQLLEN       len_char_utf8;

    // Bind columns 1, 2, and 3  
    retcode = SQLBindCol(hstmt, 1, SQL_C_SLONG, &key_field ,  0, &len_key_field);
    retcode = SQLBindCol(hstmt, 2, SQL_C_CHAR,  &char_latin, 20, &len_char_latin);  // SQL_C_CHAR  -> char *
    retcode = SQLBindCol(hstmt, 3, SQL_C_WCHAR, &char_utf8 , 20, &len_char_utf8);   // SQL_C_WCHAR -> widechar / UTF16 

    // Fetch and print each row of data.
    int index = 0;
    while(true)
    {
      memset(char_utf8, 0, 42);
      retcode = SQLFetch(hstmt);

      if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
      {
        cout << "key_field:          " << key_field   << endl;
        cout << "char_latin(char)    :  " << char_latin  << endl;
        cout << "char_latin(byte)    :  ";
        
        for (index = 0; index < len_char_latin+1; index++)
          cout << (int)char_latin[index] << "-";
        cout << endl;

        cout << "char_utf8(2 byte)   :  ";
        for (index = 0; index < len_char_utf8/2+1; index++)
        {
          unsigned char c2 = (unsigned char)((char_utf8[index])&0xFF);
          unsigned char c1 = (unsigned char)(((char_utf8[index]) >> 8 ) & 0xFF);
          cout << (int)c1 << "|";
          cout << (int)c2 << "-";
        }
        cout << endl;

        cout << "char_utf8(widechar) :  ";

        for (index = 0; index < len_char_utf8 / 2; index++)
          cout << char_utf8[index] << "-";

        cout << endl;
        cout << endl;
      }
      else
        break;
    }
  }

  if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO)
  {
    SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  }

}

Is it a bug in MariaDB connector? Is there a workaround? Any other help? Tia

Comments

Comments loading...
Content reproduced on this site is the property of its respective owners, and this content is not reviewed in advance by MariaDB. The views, information and opinions expressed by this content do not necessarily represent those of MariaDB or any other party.