implemented DB driver call DrvGetFieldUnbufferedUTF8 (for databases with native UTF...
authorVictor Kirhenshtein <victor@netxms.org>
Thu, 18 Aug 2016 07:54:44 +0000 (10:54 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Thu, 18 Aug 2016 15:24:19 +0000 (18:24 +0300)
include/dbdrv.h
include/nxdbapi.h
src/db/dbdrv/mysql/mysql.cpp
src/db/dbdrv/pgsql/pgsql.cpp
src/db/dbdrv/sqlite/sqlite.cpp
src/db/libnxdb/drivers.cpp
src/db/libnxdb/libnxdb.h
src/db/libnxdb/session.cpp

index 8443e38..50f23ae 100644 (file)
@@ -28,7 +28,7 @@
 /**
  * API version
  */
-#define DBDRV_API_VERSION           19
+#define DBDRV_API_VERSION           20
 
 
 //
index eb343d2..37c65a3 100644 (file)
@@ -185,7 +185,7 @@ bool LIBNXDB_EXPORTABLE DBGetColumnName(DB_UNBUFFERED_RESULT hResult, int column
 void LIBNXDB_EXPORTABLE DBFreeResult(DB_UNBUFFERED_RESULT hResult);
 
 TCHAR LIBNXDB_EXPORTABLE *DBGetField(DB_UNBUFFERED_RESULT hResult, int iColumn, TCHAR *pBuffer, int iBufSize);
-char LIBNXDB_EXPORTABLE *DBGetFieldUTF8(DB_UNBUFFERED_RESULT hResult, int iColumn, char *pBuffer, int iBufSize);
+char LIBNXDB_EXPORTABLE *DBGetFieldUTF8(DB_UNBUFFERED_RESULT hResult, int iColumn, char *buffer, int iBufSize);
 INT32 LIBNXDB_EXPORTABLE DBGetFieldLong(DB_UNBUFFERED_RESULT hResult, int iColumn);
 UINT32 LIBNXDB_EXPORTABLE DBGetFieldULong(DB_UNBUFFERED_RESULT hResult, int iColumn);
 INT64 LIBNXDB_EXPORTABLE DBGetFieldInt64(DB_UNBUFFERED_RESULT hResult, int iColumn);
index aa214ae..db1cd60 100644 (file)
@@ -1022,26 +1022,26 @@ extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(MYSQL_UNBUFFERED_RESULT *hRes
 }
 
 /**
- * Get field from current row in async query result
+ * Get field from current row in async query result - common implementation for wide char and UTF-8
  */
-extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(MYSQL_UNBUFFERED_RESULT *hResult, int iColumn, WCHAR *pBuffer, int iBufSize)
+static void *GetFieldUnbufferedInternal(MYSQL_UNBUFFERED_RESULT *hResult, int iColumn, void *pBuffer, int iBufSize, bool utf8)
 {
-       // Check if we have valid result handle
-       if (hResult == NULL)
-               return NULL;
-       
-       // Check if there are valid fetched row
-       if ((hResult->noMoreRows) || ((hResult->pCurrRow == NULL) && !hResult->isPreparedStatement))
-               return NULL;
-       
-       // Check if column number is valid
-       if ((iColumn < 0) || (iColumn >= hResult->numColumns))
-               return NULL;
-       
-       // Now get column data
-       WCHAR *value = NULL;
-       if (hResult->isPreparedStatement)
-       {
+   // Check if we have valid result handle
+   if (hResult == NULL)
+      return NULL;
+
+   // Check if there are valid fetched row
+   if ((hResult->noMoreRows) || ((hResult->pCurrRow == NULL) && !hResult->isPreparedStatement))
+      return NULL;
+
+   // Check if column number is valid
+   if ((iColumn < 0) || (iColumn >= hResult->numColumns))
+      return NULL;
+
+   // Now get column data
+   void *value = NULL;
+   if (hResult->isPreparedStatement)
+   {
       MYSQL_BIND b;
       unsigned long l = 0;
       my_bool isNull;
@@ -1062,30 +1062,72 @@ extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(MYSQL_UNBUFFERED_RESULT *hResult,
          if (!isNull)
          {
             ((char *)b.buffer)[l] = 0;
-            MultiByteToWideChar(CP_UTF8, 0, (char *)b.buffer, -1, (WCHAR *)pBuffer, iBufSize);
-            ((WCHAR *)pBuffer)[iBufSize - 1] = 0;
+            if (utf8)
+            {
+               strncpy((char *)pBuffer, (char *)b.buffer, iBufSize);
+               ((char *)pBuffer)[iBufSize - 1] = 0;
+            }
+            else
+            {
+               MultiByteToWideChar(CP_UTF8, 0, (char *)b.buffer, -1, (WCHAR *)pBuffer, iBufSize);
+               ((WCHAR *)pBuffer)[iBufSize - 1] = 0;
+            }
          }
          else
          {
-            *((WCHAR *)pBuffer) = 0;
+            if (utf8)
+               *((char *)pBuffer) = 0;
+            else
+               *((WCHAR *)pBuffer) = 0;
          }
          value = pBuffer;
       }
 #if !HAVE_ALLOCA
       free(b.buffer);
 #endif
-       }
-       else
-       {
+   }
+   else
+   {
       int iLen = min((int)hResult->lengthFields[iColumn], iBufSize - 1);
       if (iLen > 0)
       {
-         MultiByteToWideChar(CP_UTF8, 0, hResult->pCurrRow[iColumn], iLen, pBuffer, iBufSize);
+         if (utf8)
+         {
+            memcpy(pBuffer, hResult->pCurrRow[iColumn], iLen);
+            ((char *)pBuffer)[iLen] = 0;
+         }
+         else
+         {
+            MultiByteToWideChar(CP_UTF8, 0, hResult->pCurrRow[iColumn], iLen, (WCHAR *)pBuffer, iBufSize);
+            ((WCHAR *)pBuffer)[iLen] = 0;
+         }
+      }
+      else
+      {
+         if (utf8)
+            *((char *)pBuffer) = 0;
+         else
+            *((WCHAR *)pBuffer) = 0;
       }
-      pBuffer[iLen] = 0;
       value = pBuffer;
-       }
-       return value;
+   }
+   return value;
+}
+
+/**
+ * Get field from current row in async query result
+ */
+extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(MYSQL_UNBUFFERED_RESULT *hResult, int iColumn, WCHAR *pBuffer, int iBufSize)
+{
+       return (WCHAR *)GetFieldUnbufferedInternal(hResult, iColumn, pBuffer, iBufSize, false);
+}
+
+/**
+ * Get field from current row in async query result
+ */
+extern "C" char EXPORT *DrvGetFieldUnbufferedUTF8(MYSQL_UNBUFFERED_RESULT *hResult, int iColumn, char *pBuffer, int iBufSize)
+{
+   return (char *)GetFieldUnbufferedInternal(hResult, iColumn, pBuffer, iBufSize, true);
 }
 
 /**
index 5aec07a..2f06e04 100644 (file)
@@ -1146,6 +1146,31 @@ extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(PG_UNBUFFERED_RESULT *result, int
    return pBuffer;
 }
 
+/**
+ * Get field from current row in async query result as UTF-8 string
+ */
+extern "C" char EXPORT *DrvGetFieldUnbufferedUTF8(PG_UNBUFFERED_RESULT *result, int nColumn, char *pBuffer, int nBufSize)
+{
+   if ((result == NULL) || (result->fetchBuffer == NULL))
+      return NULL;
+
+   // validate column index
+   if (nColumn >= PQnfields(result->fetchBuffer))
+      return NULL;
+
+   if (PQfformat(result->fetchBuffer, nColumn) != 0)
+      return NULL;
+
+   char *value = PQgetvalue(result->fetchBuffer, result->currRow, nColumn);
+   if (value == NULL)
+      return NULL;
+
+   strncpy(pBuffer, value, nBufSize);
+   pBuffer[nBufSize - 1] = 0;
+
+   return pBuffer;
+}
+
 /**
  * Get column count in async query result
  */
index bb34031..b28a39f 100644 (file)
@@ -700,6 +700,27 @@ extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(SQLITE_UNBUFFERED_RESULT *result,
    return pwszRet;
 }
 
+/**
+ * Get field from current row in unbuffered query result as UTF-8 string
+ */
+extern "C" char EXPORT *DrvGetFieldUnbufferedUTF8(SQLITE_UNBUFFERED_RESULT *result, int iColumn, char *pBuffer, int iBufSize)
+{
+   char *pszData;
+   char *value = NULL;
+
+   if ((iColumn >= 0) && (iColumn < result->numColumns))
+   {
+      pszData = (char *)sqlite3_column_text(result->stmt, iColumn);
+      if (pszData != NULL)
+      {
+         strncpy(pBuffer, pszData, iBufSize);
+         pBuffer[iBufSize - 1] = 0;
+         value = pBuffer;
+      }
+   }
+   return value;
+}
+
 /**
  * Get column count in async query result
  */
index 002ff84..df83ed0 100644 (file)
@@ -205,6 +205,7 @@ DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *init
    driver->m_fpDrvGetField = (WCHAR* (*)(DBDRV_RESULT, int, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetField");
    driver->m_fpDrvGetFieldUTF8 = (char* (*)(DBDRV_RESULT, int, int, char *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUTF8", false); // optional entry point
    driver->m_fpDrvGetFieldUnbuffered = (WCHAR* (*)(DBDRV_UNBUFFERED_RESULT, int, WCHAR *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUnbuffered");
+   driver->m_fpDrvGetFieldUnbufferedUTF8 = (char* (*)(DBDRV_UNBUFFERED_RESULT, int, char *, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetFieldUnbufferedUTF8");
    driver->m_fpDrvGetNumRows = (int (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetNumRows");
    driver->m_fpDrvGetColumnCount = (int (*)(DBDRV_RESULT))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnCount");
    driver->m_fpDrvGetColumnName = (const char* (*)(DBDRV_RESULT, int))DLGetSymbolAddrEx(driver->m_handle, "DrvGetColumnName");
index 22a4c04..622f085 100644 (file)
@@ -67,6 +67,7 @@ struct db_driver_t
        WCHAR* (* m_fpDrvGetField)(DBDRV_RESULT, int, int, WCHAR *, int);
        char* (* m_fpDrvGetFieldUTF8)(DBDRV_RESULT, int, int, char *, int);
        WCHAR* (* m_fpDrvGetFieldUnbuffered)(DBDRV_UNBUFFERED_RESULT, int, WCHAR *, int);
+   char* (* m_fpDrvGetFieldUnbufferedUTF8)(DBDRV_UNBUFFERED_RESULT, int, char *, int);
        int (* m_fpDrvGetNumRows)(DBDRV_RESULT);
        void (* m_fpDrvFreeResult)(DBDRV_RESULT);
        void (* m_fpDrvFreeUnbufferedResult)(DBDRV_UNBUFFERED_RESULT);
index 56b9459..e9f4668 100644 (file)
@@ -941,39 +941,46 @@ TCHAR LIBNXDB_EXPORTABLE *DBGetField(DB_UNBUFFERED_RESULT hResult, int iColumn,
 /**
  * Get field's value from unbuffered SELECT result as UTF-8 string
  */
-char LIBNXDB_EXPORTABLE *DBGetFieldUTF8(DB_UNBUFFERED_RESULT hResult, int iColumn, char *pBuffer, int iBufSize)
+char LIBNXDB_EXPORTABLE *DBGetFieldUTF8(DB_UNBUFFERED_RESULT hResult, int iColumn, char *buffer, int iBufSize)
 {
-   /* FIXME: add appropriate call to DB drivers */
-   TCHAR *tmp = DBGetField(hResult, iColumn, NULL, 0);
-   if (tmp == NULL)
-      return NULL;
-
-   char *s;
-#ifdef UNICODE
-   if (pBuffer != NULL)
-   {
-      s = pBuffer;
-      WideCharToMultiByte(CP_UTF8, 0, tmp, -1, s, iBufSize, NULL, NULL);
-      s[iBufSize - 1] = 0;
-   }
-   else
-   {
-      s = UTF8StringFromWideString(tmp);
-   }
-#else
-   if (pBuffer != NULL)
+   if (hResult->m_driver->m_fpDrvGetFieldUTF8 != NULL)
    {
-      s = pBuffer;
-      mb_to_utf8(tmp, -1, s, iBufSize);
-      s[iBufSize - 1] = 0;
+      if (buffer != NULL)
+      {
+         *buffer = 0;
+         return hResult->m_driver->m_fpDrvGetFieldUnbufferedUTF8(hResult->m_data, iColumn, buffer, iBufSize);
+      }
+      else
+      {
+         char *pszTemp;
+         LONG nLen = hResult->m_driver->m_fpDrvGetFieldLengthUnbuffered(hResult->m_data, iColumn);
+         if (nLen == -1)
+         {
+            pszTemp = NULL;
+         }
+         else
+         {
+            nLen = nLen * 2 + 1;  // increase buffer size because driver may return field length in characters
+            pszTemp = (char *)malloc(nLen);
+            hResult->m_driver->m_fpDrvGetFieldUnbufferedUTF8(hResult->m_data, iColumn, pszTemp, nLen);
+         }
+         return pszTemp;
+      }
    }
    else
    {
-      s = UTF8StringFromMBString(tmp);
+      LONG nLen = hResult->m_driver->m_fpDrvGetFieldLengthUnbuffered(hResult->m_data, iColumn);
+      if (nLen == -1)
+         return NULL;
+      nLen = nLen * 2 + 1;  // increase buffer size because driver may return field length in characters
+
+      WCHAR *wtemp = (WCHAR *)malloc(nLen * sizeof(WCHAR));
+      hResult->m_driver->m_fpDrvGetFieldUnbuffered(hResult->m_data, iColumn, wtemp, nLen);
+      char *value = (buffer != NULL) ? buffer : (char *)malloc(nLen);
+      WideCharToMultiByte(CP_UTF8, 0, wtemp, -1, value, (buffer != NULL) ? iBufSize : nLen, NULL, NULL);
+      free(wtemp);
+      return value;
    }
-#endif
-   free(tmp);
-   return s;
 }
 
 /**