*** DBD-ODBC-0.28/dbdimp.c	Thu Mar 23 06:23:56 2000
--- ../../RESOURCES/Dbschema/DBD-ODBC/dbdimp.c	Fri May  3 11:04:11 2002
***************
*** 7,15 ****
--- 7,32 ----
   * You may distribute under the terms of either the GNU General Public
   * License or the Artistic License, as specified in the Perl README file.
   *
+  * -----------------------------------------------------------------------
+  * 05/01/2002: Modified to add support for UTF-8/UTF-16 conversion
+  *             for the SQL Server Unicode types.
+  *             The conversion routines in ConvertUTF.h/.c are
+  *             directly copied from:
+  *             http://www.unicode.org/Public/PROGRAMS/CVTUTF/
+  *             See those files for copyright notices.
+  * -----------------------------------------------------------------------
   */
  
  #include "ODBC.h"
+ #include "ConvertUTF.h"
+ 
+ char *ConversionMessage[] = {
+         "Conversion completed successfully",
+         "Conversion error: incomplete source",
+         "Conversion error: insufficient space in target buffer",
+         "Conversion error: illegal byte found in source",
+         "Conversion error: unknown error"
+ };
  
  static const char *S_SqlTypeToString (SWORD sqltype);
  static const char *S_SqlCTypeToString (SWORD sqltype);
***************
*** 595,600 ****
--- 612,623 ----
      SV **svp;
      char cname[128];            /* cursorname */
  
+     UTF16 *sql = NULL;   /* query string in utf16 */
+     int qlen = 0;        /* length of query string */
+     ConversionResult cr; /* result of UTF-8/UTF-16 conversion */
+     UTF16 *op = NULL;
+     UTF8  *ip = NULL;
+ 
      imp_sth->done_desc = 0;
      imp_sth->henv = imp_dbh->henv;      /* needed for dbd_error */
      imp_sth->hdbc = imp_dbh->hdbc;
***************
*** 608,616 ****
      /* scan statement for '?', ':1' and/or ':foo' style placeholders    */
      dbd_preparse(imp_sth, statement);
  
      /* parse the (possibly edited) SQL statement */
!     rc = SQLPrepare(imp_sth->hstmt, 
!                     imp_sth->statement, strlen(imp_sth->statement));
      if (!SQL_ok(rc)) {
          dbd_error(sth, rc, "st_prepare/SQLPrepare");
          SQLFreeStmt(imp_sth->hstmt, SQL_DROP);
--- 631,651 ----
      /* scan statement for '?', ':1' and/or ':foo' style placeholders    */
      dbd_preparse(imp_sth, statement);
  
+     /* Convert SQL statement to UTF-16 */
+     qlen = strlen(imp_sth->statement);
+     Newz(42, sql, qlen+1, UTF16);
+ 
+     ip = imp_sth->statement;
+     op = sql;
+ 
+     if ((cr = ConvertUTF8toUTF16(&ip, ip+qlen, &op, op+qlen, strictConversion)) != conversionOK)
+         dbd_error(sth, (short) cr, ConversionMessage[cr]);
+ 
+     memset((void *)op, '\0', 2); /* Null terminate */
+ 
      /* parse the (possibly edited) SQL statement */
!     rc = SQLPrepareW(imp_sth->hstmt, sql, qlen);
!     Safefree(sql);
      if (!SQL_ok(rc)) {
          dbd_error(sth, rc, "st_prepare/SQLPrepare");
          SQLFreeStmt(imp_sth->hstmt, SQL_DROP);
***************
*** 661,666 ****
--- 696,704 ----
          case SQL_REAL:  return "REAL";
          case SQL_DOUBLE:        return "DOUBLE";
          case SQL_VARCHAR:       return "VARCHAR";
+ #ifdef SQL_WCHAR
+         case SQL_WCHAR: return "UNICODE CHAR";
+ #endif
  #ifdef SQL_WVARCHAR
          case SQL_WVARCHAR:      return "UNICODE VARCHAR"; /* added for SQLServer 7 ntext type 2/24/2000 */
  #endif
***************
*** 817,824 ****
--- 855,877 ----
                  fbh->ftype = SQL_C_BINARY;
                  fbh->ColDisplaySize = DBIc_LongReadLen(imp_sth);
                  break;
+ #ifdef SQL_WVARCHAR
+         case SQL_WVARCHAR:
+         fbh->ftype = SQL_C_WCHAR;
+         fbh->ColDisplaySize *= 4;
+         break;
+ #endif
+ #ifdef SQL_WCHAR
+         case SQL_WCHAR:
+         fbh->ftype = SQL_C_WCHAR;
+         fbh->ColDisplaySize *= 4;
+         break;
+ #endif
  #ifdef SQL_WLONGVARCHAR
              case SQL_WLONGVARCHAR:      /* added for SQLServer 7 ntext type */
+         fbh->ColDisplaySize = (DBIc_LongReadLen(imp_sth)+1)*4;
+         fbh->ftype = SQL_C_WCHAR;
+         break;
  #endif
              case SQL_LONGVARCHAR:
                  fbh->ColDisplaySize = DBIc_LongReadLen(imp_sth)+1;
***************
*** 1166,1172 ****
                  break;
  #endif
              default:
!                 if (ChopBlanks && fbh->ColSqlType == SQL_CHAR && fbh->datalen > 0) {
                      char *p = (char*)fbh->data;
                      while(fbh->datalen && p[fbh->datalen - 1]==' ')
                          --fbh->datalen;
--- 1219,1252 ----
                  break;
  #endif
              default:
! 
!         if ((fbh->ColSqlType == SQL_WCHAR   ||
!                         fbh->ColSqlType == SQL_WVARCHAR ||
!                         fbh->ColSqlType == SQL_WLONGVARCHAR) && fbh->datalen > 0) {
!             UTF16 *ut = NULL;
!             UTF8  *op = NULL;
!             UTF16 *ip = NULL;
!             ConversionResult cr;
! 
!             Newz(42, (UCHAR *)ut, fbh->datalen + 2, UCHAR);
!             Copy(fbh->data, ut, fbh->datalen, UCHAR);
! 
!             op = fbh->data;
!             ip = ut;
! 
!             if ((cr = ConvertUTF16toUTF8(&ip, ip+(fbh->datalen/2),
!                                                                                  &op, op+(fbh->datalen * 2),
!                                                                                  strictConversion)) != conversionOK)
!                 dbd_error(sth, (short)cr, ConversionMessage[cr]);
! 
!             *op = '\0';
!             Safefree(ut);
!             fbh->datalen = strlen(fbh->data);
!         }
! 
!                 if (ChopBlanks &&
!                         (fbh->ColSqlType == SQL_CHAR || fbh->ColSqlType == SQL_WCHAR) &&
!                         fbh->datalen > 0) {
                      char *p = (char*)fbh->data;
                      while(fbh->datalen && p[fbh->datalen - 1]==' ')
                          --fbh->datalen;
***************
*** 1287,1292 ****
--- 1367,1373 ----
          SDWORD cbValueMax;
  
          STRLEN value_len;
+         UTF16 *op = NULL;
  
          if (DBIS->debug >= 2) {
                  char *text = neatsvpv(phs->sv,0);
***************
*** 1383,1388 ****
--- 1464,1481 ----
                                  break;
  #ifdef SQL_WLONGVARCHAR
                          case SQL_WLONGVARCHAR:  /* added for SQLServer 7 ntext type */
+                 fCType = SQL_C_WCHAR;
+                 break;
+ #endif
+ #ifdef SQL_WVARCHAR
+                 case SQL_WVARCHAR:
+                 fCType = SQL_C_WCHAR;
+                 break;
+ #endif
+ #ifdef SQL_WCHAR
+                 case SQL_WCHAR:
+                 fCType = SQL_C_WCHAR;
+                 break;
  #endif
                          case SQL_LONGVARCHAR:
                                  break;
***************
*** 1395,1400 ****
--- 1488,1494 ----
                                  break;
                  }
          }
+ 
          /* per patch from Paul G. Weiss, who was experiencing re-preparing
           * of queries when the size of the bound string's were increasing
           * for example select * from tabtest where name = ?
***************
*** 1405,1415 ****
           * TBD: the default for this should be a DBD::ODBC specific option
           * or attribute.
           */
!         if (phs->sql_type == SQL_VARCHAR) {
                  ibScale = 0;
                  /* default to at least 80 if this is the first time through */
                  if (phs->biggestparam == 0) {
!                         phs->biggestparam = (value_len > 80) ? value_len : 80;
                  } else {
                          /* bump up max, if needed */
                          if (value_len > phs->biggestparam) {
--- 1499,1509 ----
           * TBD: the default for this should be a DBD::ODBC specific option
           * or attribute.
           */
!         if (phs->sql_type == SQL_VARCHAR || phs->sql_type == SQL_WVARCHAR) {
                  ibScale = 0;
                  /* default to at least 80 if this is the first time through */
                  if (phs->biggestparam == 0) {
!                         phs->biggestparam = (value_len > 255) ? value_len : 255;
                  } else {
                          /* bump up max, if needed */
                          if (value_len > phs->biggestparam) {
***************
*** 1448,1457 ****
--- 1542,1579 ----
                   */
                  rgbValue = (UCHAR*) phs;
          }
+ 
+         if (fCType == SQL_C_WCHAR) {
+                 UTF8  *ip = rgbValue;
+                 UTF16 *ut = NULL;
+                 ConversionResult cr;
+ 
+                 Newz(42, ut, value_len+3, UTF16); ut += 2; /* TODO */
+ 
+                 op = ut;
+                 if ((cr = ConvertUTF8toUTF16(&ip, ip+value_len,
+                                                                          &ut, ut+value_len, strictConversion)) != conversionOK)
+                         dbd_error(sth, (short) cr, ConversionMessage[cr]);
+ 
+                 memset((void *)ut, '\0', 2); /* null terminate */
+ 
+                 rgbValue = (UCHAR *)op;
+                 phs->cbValue = (ut - op) * 2;
+ 
+                 cbColDef = phs->cbValue / 2;
+                 ibScale = cbColDef;
+                 cbValueMax = phs->cbValue;
+         }
+ 
          rc = SQLBindParameter(imp_sth->hstmt,
                                                    phs->idx, fParamType, fCType, phs->sql_type,
                                                    cbColDef, ibScale,
                                                    rgbValue, cbValueMax, &phs->cbValue);
+ 
+         if (op) {
+                 Safefree(op);
+                 op = NULL;
+         }
  
          if (!SQL_ok(rc)) {
                  dbd_error(sth, rc, "_rebind_ph/SQLBindParameter");
