Re: UNICODE support

Igor Korot <[email protected]> Mon, 28 Mar 2016 10:57:10 -0400
Newsgroups gmane.comp.db.unixodbc.devel
Message-ID <CA+FnnTzCck27nr9KzoyECgBy0Hd=xhpPMnVres3wxqBy8WKkDA@mail.gmail.com>
Nick,

On Mon, Mar 28, 2016 at 8:22 AM, Nick Gorham <[email protected]> wrote:
> On 28/03/16 01:35, Igor Korot wrote:
>
> Nick,
>
> On Sat, Mar 26, 2016 at 2:57 PM, Nick Gorham <[email protected]> wrote:
>
> On 26/03/16 18:00, Igor Korot wrote:
>
> Nick,
>
> On Sat, Mar 26, 2016 at 1:36 PM, Nick Gorham <[email protected]> wrote:
>
> On 26/03/16 17:31, Igor Korot wrote:
>
> Hi, Nick,
>
> On Sat, Mar 26, 2016 at 12:58 PM, Nick Gorham <[email protected]> wrote:
>
> On 26/03/16 15:10, Igor Korot wrote:
>
> Hi, (Nick),
> Is it enough to do:
>
> #define UNICODE
>
> in order to use SQLW-types and functions?
>
> Or I need to define something else?
>
> Thank you.
> _______________________________________________
> unixODBC-dev mailing list
> [email protected]
> http://mailman.unixodbc.org/mailman/listinfo/unixodbc-dev
>
> You should just be able to use them. Whats not working?
>
> Check the following code:
>
> [code]
> /* odbc.c
>
>       testing unixODBC
> */
> #include <stdlib.h>
> #include <stdio.h>
> #include <odbcinst.h>
> #include <sqlext.h>
>
> #define SQL_WCHART_CONVERT
>
> int main(int argc,char *argv[])
> {
>       SQLHENV m_henv;
>       SQLSMALLINT i = 1, attr_ret;
>       SQLWCHAR driverDesc[1024], driverAttributes[256],
> dsn[SQL_MAX_DSN_LENGTH + 1], sqlState[20],
> errMsg[SQL_MAX_MESSAGE_LENGTH], dsnDescr[255];
>       SWORD pcbDSN, pcbDesc;
>       SQLINTEGER nativeError;
>       SQLSMALLINT cbErrorMsg, descriptionLength;
>       SQLUSMALLINT direction = SQL_FETCH_FIRST, direct =
> SQL_FETCH_FIRST;
>
>       RETCODE ret = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HENV,
> &m_henv
> );
>       if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO )
>       {
>           printf( "Error allocating handle\r\n" );
>           while( ( ret = SQLGetDiagRecW( SQL_HANDLE_ENV, m_henv, i,
> sqlState, &nativeError, errMsg, sizeof( errMsg ), &cbErrorMsg ) ) ==
> SQL_SUCCESS )
>           {
>               printf( "Error is: %s\r\n", errMsg );
>           }
>           exit( 0 );
>       }
>       else
>       {
>           ret = SQLSetEnvAttr( m_henv, SQL_ATTR_ODBC_VERSION, (void *)
> SQL_OV_ODBC3, 0 );
>           if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO )
>           {
>               printf( "Error setting environment\r\n" );
>               while( ( ret = SQLGetDiagRec( SQL_HANDLE_ENV, m_henv, i,
> sqlState, &nativeError, errMsg, sizeof( errMsg ), &cbErrorMsg ) ) ==
> SQL_SUCCESS )
>               {
>                   printf( "Error is: %s\r\n", errMsg );
>               }
>               exit( 0 );
>           }
>           else
>           {
>               while( ( ret = SQLDrivers( m_henv, direction, driverDesc,
> sizeof( driverDesc ) - 1, &descriptionLength, driverAttributes,
> sizeof( driverAttributes ), &attr_ret ) ) == SQL_SUCCESS )
>               {
>                   while( ( ret = SQLDataSources( m_henv, direct, dsn,
> SQL_MAX_DSN_LENGTH, &pcbDSN, dsnDescr, 254, &pcbDesc ) ) ==
> SQL_SUCCESS )
>                   {
>                       printf( "Driver description is %s", driverDesc );
>                       printf( "DSN description is %s", dsnDescr );
>                       direct = SQL_FETCH_NEXT;
>                   }
>                   if( ret != SQL_SUCCESS && ret != SQL_NO_DATA )
>                   {
>                       printf( "Error retrieving Data Source" );
>                       while( ( ret = SQLGetDiagRec( SQL_HANDLE_ENV,
> m_henv, i, sqlState, &nativeError, errMsg, sizeof( errMsg ),
> &cbErrorMsg ) ) == SQL_SUCCESS )
>                       {
>                           printf( "Error is: %s\r\n", errMsg );
>                       }
>                       break;
>                   }
>                   direction = SQL_FETCH_NEXT;
>               }
>           }
>       }
>       return 0;
> }
> [/code]
>
> resulted in:
>
> [code]
> igor@IgorDellGentoo ~ $ gcc -DUNICODE odbctest.cpp -o odbctest -lodbc
> odbctest.cpp: In function 'int main(int, char**)':
> odbctest.cpp:30:48: warning: format '%s' expects argument of type
> 'char*', but argument 2 has type 'SQLWCHAR* {aka short unsigned int*}'
> [-Wformat=]
>                printf( "Error is: %s\r\n", errMsg );
>                                                   ^
> odbctest.cpp:42:52: warning: format '%s' expects argument of type
> 'char*', but argument 2 has type 'SQLWCHAR* {aka short unsigned int*}'
> [-Wformat=]
>                    printf( "Error is: %s\r\n", errMsg );
>                                                       ^
> odbctest.cpp:52:68: warning: format '%s' expects argument of type
> 'char*', but argument 2 has type 'SQLWCHAR* {aka short unsigned int*}'
> [-Wformat=]
>                        printf( "Driver description is %s", driverDesc );
>                                                                       ^
> odbctest.cpp:53:63: warning: format '%s' expects argument of type
> 'char*', but argument 2 has type 'SQLWCHAR* {aka short unsigned int*}'
> [-Wformat=]
>                        printf( "DSN description is %s", dsnDescr );
>                                                                  ^
> odbctest.cpp:61:60: warning: format '%s' expects argument of type
> 'char*', but argument 2 has type 'SQLWCHAR* {aka short unsigned int*}'
> [-Wformat=]
>                            printf( "Error is: %s\r\n", errMsg );
>                                                               ^
> [/code]
>
> Any idea what is going on? And how to fix it?
>
> Thank you.
>
> Err, I think the compiler is telling you, You have a array
>
> SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH]
>
> Well I do have "-DUNICODE" passed to the compiler.
> So SQLWCHAR should not become "unsigned char", but rather "wchar_t".
>
> Am I wrong?
>
> Thank you.
>
> Yes.
>
> SQLWCHAR that ODBC uses is a 16 byte type, wchar_t is normally a 32 byte
> type.
>
> I think you may be thinking of SQLTCHAR that will switch between char and
> SQLWCHAR depending on the flag. But either way, the standard library
> (printf) doesn't know or care, %s = char
>
> Taken from  C:\Program Files (x86)\Microsoft
> SDKs\Windows\v7.0A\Include\sqltypes.h(286:
>
> [code]
> #ifdef _WCHAR_T_DEFINED
> typedef wchar_t SQLWCHAR;
> #else
> typedef unsigned short SQLWCHAR;
> #endif
>
> #ifdef UNICODE
> typedef SQLWCHAR        SQLTCHAR;
> #else
> typedef SQLCHAR         SQLTCHAR;
> #endif  /* UNICODE */
> [/code]
>
> I presume unixODBC uses the same code, right?
>
> Which means that all I need is to define _WCHAR_T_DEFINED in order for
> SQLWCHAR
> to become wchar_t instead of unsigned short.
>
>
> Generally, no. To quote "wchar_t is compiler-dependent and therefore not
> very portable. Using it for Unicode binds a program to the character model
> of a compiler. Instead, it is often better to define and use dedicated data
> types."
>
> In most cases outside of Windows wchar_t is a 4 byte type. You can build
> unixODBC to use 4 byte SQLWCHAR, but you will find it won't work with most
> drivers out there.

OK, so basically what you are saying is: "Its not possible to write a portable
code which uses MS ODBC manager and unixODBC".
Because looking at my code, it looks like _WCHAR_T_DEFINED is defined. ;-)

It would be nice if the Development manual was updated with this info.

>
> Now on  related note, char = %c char* = %s, is it not?
> Granted I didn't use plain C for a long time, but I think here I'm right.
> ;-)
>
>
> Yes, I was assuming a shorthand, where %s is char array, I assumed you would
> know the array part. But the important part of that is that its char, not
> long, short or int.

Yes, but "char *" is just a dynamically allocated array of characters.

Thank you.

>
> --
> Nick
>
> _______________________________________________
> unixODBC-dev mailing list
> [email protected]
> http://mailman.unixodbc.org/mailman/listinfo/unixodbc-dev
>
_______________________________________________
unixODBC-dev mailing list
[email protected]
http://mailman.unixodbc.org/mailman/listinfo/unixodbc-dev