PostgreSQL driver: single row emulation mode for PostgreSQL client 9.1 and older...
authorVictor Kirhenshtein <victor@netxms.org>
Sat, 26 Dec 2015 22:48:10 +0000 (00:48 +0200)
committerVictor Kirhenshtein <victor@netxms.org>
Sat, 26 Dec 2015 22:48:10 +0000 (00:48 +0200)
14 files changed:
configure.ac
include/dbdrv.h
src/db/dbdrv/db2/db2.cpp
src/db/dbdrv/informix/informix.cpp
src/db/dbdrv/mssql/mssql.cpp
src/db/dbdrv/mysql/mysql.cpp
src/db/dbdrv/odbc/odbc.cpp
src/db/dbdrv/oracle/oracle.cpp
src/db/dbdrv/pgsql/pgsql.cpp
src/db/dbdrv/pgsql/pgsqldrv.h
src/db/dbdrv/sqlite/sqlite.cpp
src/db/libnxdb/drivers.cpp
src/db/libnxdb/libnxdb.h
src/db/libnxdb/main.cpp

index a42b5ab..df71d4d 100644 (file)
@@ -2640,6 +2640,10 @@ if test $? = 0; then
 
        AC_CHECK_HEADER(libpq-fe.h,,AC_MSG_ERROR([Cannot find libpq-fe.h - check your PostgreSQL client installation]))
 
+       AC_CHECK_DECLS([PGRES_SINGLE_TUPLE],,,[
+#include <libpq-fe.h>
+       ])
+
        AC_CHECK_LIB(m, floor)
        AC_CHECK_LIB(pq, PQconnectdb, [PGSQL_LIBS="-lpq"], [
                if test "x$PLATFORM" = "xSunOS"; then
index 495ddfd..5d3490f 100644 (file)
@@ -28,7 +28,7 @@
 /**
  * API version
  */
-#define DBDRV_API_VERSION           16
+#define DBDRV_API_VERSION           17
 
 
 //
index 13423a8..31aba8b 100644 (file)
@@ -185,7 +185,7 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
        return true;
 }
index 4471dd2..3f0dd1f 100644 (file)
@@ -170,31 +170,25 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
        return out;
 }
 
-
-//
-// Initialize driver
-//
-
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+/**
+ * Initialize driver
+ */
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
        return true;
 }
 
-
-//
-// Unload handler
-//
-
+/**
+ * Unload handler
+ */
 extern "C" void EXPORT DrvUnload()
 {
 }
 
-
-//
-// Connect to database
-// database should be set to Informix source name. Host and schema are ignored
-//
-
+/**
+ * Connect to database
+ * database should be set to Informix source name. Host and schema are ignored
+ */
 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(char *host, char *login, char *password, char *database, const char *schema, WCHAR *errorText)
 {
        long iResult;
index 9177c18..774d61e 100644 (file)
@@ -170,7 +170,7 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
    // Allocate environment
        SQLHENV sqlEnv;
index 9bd19bf..e5db0dc 100644 (file)
@@ -190,7 +190,7 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
        return mysql_library_init(0, NULL, NULL) == 0;
 }
index a28704f..68e9fa0 100644 (file)
@@ -177,7 +177,7 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
    m_useUnicode = ExtractNamedOptionValueAsBoolA(cmdLine, "unicode", true);
    return true;
index fd94b71..0e9259a 100644 (file)
@@ -101,7 +101,7 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
        return true;
 }
index b487b86..a27cff3 100644 (file)
 
 #include "pgsqldrv.h"
 
+#ifndef _WIN32
+#include <dlfcn.h>
+#endif
+
 #ifdef _WIN32
 #pragma warning(disable : 4996)
 #endif
@@ -31,6 +35,34 @@ DECLARE_DRIVER_HEADER("PGSQL")
 extern "C" void EXPORT DrvDisconnect(DBDRV_CONNECTION pConn);
 static bool UnsafeDrvQuery(PG_CONN *pConn, const char *szQuery, WCHAR *errorText);
 
+#ifndef _WIN32
+static void *s_libpq = NULL;
+static int (*s_PQsetSingleRowMode)(PGconn *) = NULL;
+#endif
+
+#if !HAVE_DECL_PGRES_SINGLE_TUPLE
+#define PGRES_SINGLE_TUPLE    9
+#endif
+
+/**
+ * Debug log callback
+ */
+static void (*s_dbgPrintCb)(int, const TCHAR *, va_list) = NULL;
+
+/**
+ * Debug output
+ */
+static void __DbgPrintf(int level, const TCHAR *format, ...)
+{
+   if (s_dbgPrintCb != NULL)
+   {
+      va_list args;
+      va_start(args, format);
+      s_dbgPrintCb(level, format, args);
+      va_end(args);
+   }
+}
+
 /**
  * Statement ID
  */
@@ -163,8 +195,15 @@ extern "C" char EXPORT *DrvPrepareStringA(const char *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
+   s_dbgPrintCb = dbgPrintCb;
+#ifndef _WIN32
+   s_libpq = dlopen("libpq.so.5", RTLD_NOW);
+   if (s_libpq != NULL)
+      s_PQsetSingleRowMode = (int (*)(PGconn *))dlsym(s_libpq, "PQsetSingleRowMode");
+   __DbgPrintf(2, _T("PostgreSQL driver: single row mode %s"), (s_PQsetSingleRowMode != NULL) ? _T("enabled") : _T("disabled"));
+#endif
        return true;
 }
 
@@ -173,6 +212,10 @@ extern "C" bool EXPORT DrvInit(const char *cmdLine)
  */
 extern "C" void EXPORT DrvUnload()
 {
+#ifndef _WIN32
+   if (s_libpq != NULL)
+      dlclose(s_libpq);
+#endif
 }
 
 /**
@@ -224,7 +267,6 @@ extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *szHost,   const char *sz
                        PQsetClientEncoding(pConn->handle, "UTF8");
 
                pConn->mutexQueryLock = MutexCreate();
-         pConn->fetchBuffer = NULL;
 
                        if ((schema != NULL) && (schema[0] != 0))
                        {
@@ -815,9 +857,12 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(PG_CONN *pConn, WC
        if (pConn == NULL)
                return NULL;
 
+       PG_UNBUFFERED_RESULT *result = (PG_UNBUFFERED_RESULT *)malloc(sizeof(PG_UNBUFFERED_RESULT));
+       result->conn = pConn;
+   result->fetchBuffer = NULL;
+   result->keepFetchBuffer = true;
+
        MutexLock(pConn->mutexQueryLock);
-       pConn->fetchBuffer = NULL;
-   pConn->keepFetchBuffer = true;
 
        bool success = false;
        bool retry;
@@ -828,13 +873,16 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(PG_CONN *pConn, WC
       retry = false;
       if (PQsendQuery(pConn->handle, queryUTF8))
       {
-         if (PQsetSingleRowMode(pConn->handle))
+         if ((s_PQsetSingleRowMode == NULL) || s_PQsetSingleRowMode(pConn->handle))
          {
+            result->singleRowMode = (s_PQsetSingleRowMode != NULL);
+            result->currRow = 0;
+
             // Fetch first row (to check for errors in Select instead of Fetch call)
-            pConn->fetchBuffer = PQgetResult(pConn->handle);
-            if ((PQresultStatus(pConn->fetchBuffer) == PGRES_COMMAND_OK) ||
-                (PQresultStatus(pConn->fetchBuffer) == PGRES_TUPLES_OK) ||
-                (PQresultStatus(pConn->fetchBuffer) == PGRES_SINGLE_TUPLE))
+            result->fetchBuffer = PQgetResult(pConn->handle);
+            if ((PQresultStatus(result->fetchBuffer) == PGRES_COMMAND_OK) ||
+                (PQresultStatus(result->fetchBuffer) == PGRES_TUPLES_OK) ||
+                (PQresultStatus(result->fetchBuffer) == PGRES_SINGLE_TUPLE))
             {
                if (errorText != NULL)
                   *errorText = 0;
@@ -843,7 +891,7 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(PG_CONN *pConn, WC
             }
             else
             {
-               const char *sqlState = PQresultErrorField(pConn->fetchBuffer, PG_DIAG_SQLSTATE);
+               const char *sqlState = PQresultErrorField(result->fetchBuffer, PG_DIAG_SQLSTATE);
                if ((PQstatus(pConn->handle) != CONNECTION_BAD) &&
                    (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0))
                {
@@ -867,21 +915,33 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(PG_CONN *pConn, WC
                      RemoveTrailingCRLFW(errorText);
                   }
                }
-               PQclear(pConn->fetchBuffer);
-               pConn->fetchBuffer = NULL;
+               PQclear(result->fetchBuffer);
+               result->fetchBuffer = NULL;
                *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
             }
          }
          else
          {
             if (errorText != NULL)
-               wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT);
+               wcsncpy(errorText, L"Internal error (call to PQsetSingleRowMode failed)", DBDRV_MAX_ERROR_TEXT);
             *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
          }
       }
+      else
+      {
+         if (errorText != NULL)
+            wcsncpy(errorText, L"Internal error (call to PQsendQuery failed)", DBDRV_MAX_ERROR_TEXT);
+         *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
+      }
    }
    while(retry);
-   return (DBDRV_UNBUFFERED_RESULT)pConn;
+
+   if (!success)
+   {
+      free(result);
+      result = NULL;
+   }
+   return (DBDRV_UNBUFFERED_RESULT)result;
 }
 
 /**
@@ -892,9 +952,12 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(PG_CONN *p
    if (pConn == NULL)
       return NULL;
 
+   PG_UNBUFFERED_RESULT *result = (PG_UNBUFFERED_RESULT *)malloc(sizeof(PG_UNBUFFERED_RESULT));
+   result->conn = pConn;
+   result->fetchBuffer = NULL;
+   result->keepFetchBuffer = true;
+
    MutexLock(pConn->mutexQueryLock);
-   pConn->fetchBuffer = NULL;
-   pConn->keepFetchBuffer = true;
 
    bool success = false;
    bool retry;
@@ -904,13 +967,16 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(PG_CONN *p
       retry = false;
       if (PQsendQueryPrepared(pConn->handle, hStmt->name, hStmt->pcount, hStmt->buffers, NULL, NULL, 0))
       {
-         if (PQsetSingleRowMode(pConn->handle))
+         if ((s_PQsetSingleRowMode == NULL) || s_PQsetSingleRowMode(pConn->handle))
          {
+            result->singleRowMode = (s_PQsetSingleRowMode != NULL);
+            result->currRow = 0;
+
             // Fetch first row (to check for errors in Select instead of Fetch call)
-            pConn->fetchBuffer = PQgetResult(pConn->handle);
-            if ((PQresultStatus(pConn->fetchBuffer) == PGRES_COMMAND_OK) ||
-                (PQresultStatus(pConn->fetchBuffer) == PGRES_TUPLES_OK) ||
-                (PQresultStatus(pConn->fetchBuffer) == PGRES_SINGLE_TUPLE))
+            result->fetchBuffer = PQgetResult(pConn->handle);
+            if ((PQresultStatus(result->fetchBuffer) == PGRES_COMMAND_OK) ||
+                (PQresultStatus(result->fetchBuffer) == PGRES_TUPLES_OK) ||
+                (PQresultStatus(result->fetchBuffer) == PGRES_SINGLE_TUPLE))
             {
                if (errorText != NULL)
                   *errorText = 0;
@@ -919,7 +985,7 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(PG_CONN *p
             }
             else
             {
-               const char *sqlState = PQresultErrorField(pConn->fetchBuffer, PG_DIAG_SQLSTATE);
+               const char *sqlState = PQresultErrorField(result->fetchBuffer, PG_DIAG_SQLSTATE);
                if ((PQstatus(pConn->handle) != CONNECTION_BAD) &&
                    (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0))
                {
@@ -943,54 +1009,86 @@ extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(PG_CONN *p
                      RemoveTrailingCRLFW(errorText);
                   }
                }
-               PQclear(pConn->fetchBuffer);
-               pConn->fetchBuffer = NULL;
+               PQclear(result->fetchBuffer);
+               result->fetchBuffer = NULL;
                *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
             }
          }
          else
          {
             if (errorText != NULL)
-               wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT);
+               wcsncpy(errorText, L"Internal error (call to PQsetSingleRowMode failed)", DBDRV_MAX_ERROR_TEXT);
             *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
          }
       }
+      else
+      {
+         if (errorText != NULL)
+            wcsncpy(errorText, L"Internal error (call to PQsendQueryPrepared failed)", DBDRV_MAX_ERROR_TEXT);
+         *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
+      }
    }
    while(retry);
-   return (DBDRV_UNBUFFERED_RESULT)pConn;
+
+   if (!success)
+   {
+      free(result);
+      result = NULL;
+   }
+   return (DBDRV_UNBUFFERED_RESULT)result;
 }
 
 /**
  * Fetch next result line from asynchronous SELECT results
  */
-extern "C" bool EXPORT DrvFetch(PG_CONN *pConn)
+extern "C" bool EXPORT DrvFetch(PG_UNBUFFERED_RESULT *result)
 {
-   if (pConn == NULL)
+   if (result == NULL)
       return false;
 
-   if (!pConn->keepFetchBuffer)
+   if (!result->keepFetchBuffer)
    {
-      if (pConn->fetchBuffer != NULL)
-         PQclear(pConn->fetchBuffer);
-      pConn->fetchBuffer = PQgetResult(pConn->handle);
+      if (result->singleRowMode)
+      {
+         if (result->fetchBuffer != NULL)
+            PQclear(result->fetchBuffer);
+         result->fetchBuffer = PQgetResult(result->conn->handle);
+      }
+      else
+      {
+         if (result->fetchBuffer != NULL)
+         {
+            result->currRow++;
+            if (result->currRow >= PQntuples(result->fetchBuffer))
+            {
+               PQclear(result->fetchBuffer);
+               result->fetchBuffer = PQgetResult(result->conn->handle);
+               result->currRow = 0;
+            }
+         }
+         else
+         {
+            result->currRow = 0;
+         }
+      }
    }
    else
    {
-      pConn->keepFetchBuffer = false;
+      result->keepFetchBuffer = false;
    }
 
-   if (pConn->fetchBuffer == NULL)
+   if (result->fetchBuffer == NULL)
       return false;
 
    bool success;
-   if ((PQresultStatus(pConn->fetchBuffer) == PGRES_SINGLE_TUPLE) || (PQresultStatus(pConn->fetchBuffer) == PGRES_TUPLES_OK))
+   if ((PQresultStatus(result->fetchBuffer) == PGRES_SINGLE_TUPLE) || (PQresultStatus(result->fetchBuffer) == PGRES_TUPLES_OK))
    {
-      success = (PQntuples(pConn->fetchBuffer) > 0);
+      success = (PQntuples(result->fetchBuffer) > 0);
    }
    else
    {
-      PQclear(pConn->fetchBuffer);
-      pConn->fetchBuffer = NULL;
+      PQclear(result->fetchBuffer);
+      result->fetchBuffer = NULL;
       success = false;
    }
 
@@ -1000,24 +1098,18 @@ extern "C" bool EXPORT DrvFetch(PG_CONN *pConn)
 /**
  * Get field length from async quety result
  */
-extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(PG_CONN *pConn, int nColumn)
+extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(PG_UNBUFFERED_RESULT *result, int nColumn)
 {
-       if ((pConn == NULL) || (pConn->fetchBuffer == NULL))
-       {
+       if ((result == NULL) || (result->fetchBuffer == NULL))
                return 0;
-       }
 
        // validate column index
-       if (nColumn >= PQnfields(pConn->fetchBuffer))
-       {
+       if (nColumn >= PQnfields(result->fetchBuffer))
                return 0;
-       }
 
-       char *value = PQgetvalue(pConn->fetchBuffer, 0, nColumn);
+       char *value = PQgetvalue(result->fetchBuffer, result->currRow, nColumn);
        if (value == NULL)
-       {
                return 0;
-       }
 
        return (LONG)strlen(value);
 }
@@ -1025,39 +1117,31 @@ extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(PG_CONN *pConn, int nColumn)
 /**
  * Get field from current row in async query result
  */
-extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(PG_CONN *pConn, int nColumn, WCHAR *pBuffer, int nBufSize)
+extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(PG_UNBUFFERED_RESULT *result, int nColumn, WCHAR *pBuffer, int nBufSize)
 {
-       char *pszResult;
-
-       if ((pConn == NULL) || (pConn->fetchBuffer == NULL))
-       {
+       if ((result == NULL) || (result->fetchBuffer == NULL))
                return NULL;
-       }
 
        // validate column index
-       if (nColumn >= PQnfields(pConn->fetchBuffer))
-       {
+       if (nColumn >= PQnfields(result->fetchBuffer))
                return NULL;
-       }
 
        // FIXME: correct processing of binary fields
        // PQfformat not supported in 7.3
 #ifdef HAVE_PQFFORMAT
        if (PQfformat(pConn->fetchBuffer, nColumn) != 0)
 #else
-       if (PQbinaryTuples(pConn->fetchBuffer) != 0)
+       if (PQbinaryTuples(result->fetchBuffer) != 0)
 #endif
        {
                return NULL;
        }
 
-       pszResult = PQgetvalue(pConn->fetchBuffer, 0, nColumn);
-       if (pszResult == NULL)
-       {
+       char *value = PQgetvalue(result->fetchBuffer, result->currRow, nColumn);
+       if (value == NULL)
                return NULL;
-       }
 
-   MultiByteToWideChar(CP_UTF8, 0, pszResult, -1, pBuffer, nBufSize);
+   MultiByteToWideChar(CP_UTF8, 0, value, -1, pBuffer, nBufSize);
    pBuffer[nBufSize - 1] = 0;
 
    return pBuffer;
@@ -1066,37 +1150,41 @@ extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(PG_CONN *pConn, int nColumn, WCHA
 /**
  * Get column count in async query result
  */
-extern "C" int EXPORT DrvGetColumnCountUnbuffered(PG_CONN *hResult)
+extern "C" int EXPORT DrvGetColumnCountUnbuffered(PG_UNBUFFERED_RESULT *result)
 {
-       return ((hResult != NULL) && (hResult->fetchBuffer != NULL)) ? PQnfields(hResult->fetchBuffer) : 0;
+       return ((result != NULL) && (result->fetchBuffer != NULL)) ? PQnfields(result->fetchBuffer) : 0;
 }
 
 /**
  * Get column name in async query result
  */
-extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(PG_CONN *hResult, int column)
+extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(PG_UNBUFFERED_RESULT *result, int column)
 {
-       return ((hResult != NULL) && (hResult->fetchBuffer != NULL))? PQfname(hResult->fetchBuffer, column) : NULL;
+       return ((result != NULL) && (result->fetchBuffer != NULL))? PQfname(result->fetchBuffer, column) : NULL;
 }
 
 /**
  * Destroy result of async query
  */
-extern "C" void EXPORT DrvFreeUnbufferedResult(PG_CONN *pConn)
+extern "C" void EXPORT DrvFreeUnbufferedResult(PG_UNBUFFERED_RESULT *result)
 {
-   if (pConn == NULL)
+   if (result == NULL)
       return;
 
-   if (pConn->fetchBuffer != NULL)
-   {
-      PQclear(pConn->fetchBuffer);
-      pConn->fetchBuffer = NULL;
-   }
+   if (result->fetchBuffer != NULL)
+      PQclear(result->fetchBuffer);
 
    // read all outstanding results
-   while(PQgetResult(pConn->handle) != NULL);
+   while(true)
+   {
+      result->fetchBuffer = PQgetResult(result->conn->handle);
+      if (result->fetchBuffer == NULL)
+         break;
+      PQclear(result->fetchBuffer);
+   }
 
-   MutexUnlock(pConn->mutexQueryLock);
+   MutexUnlock(result->conn->mutexQueryLock);
+   free(result);
 }
 
 /**
index 026e70c..b9fc40b 100644 (file)
@@ -1,6 +1,6 @@
 /* 
 ** PostgreSQL Database Driver
-** Copyright (C) 2003-2011 Victor Kirhenshtein
+** Copyright (C) 2003-2015 Victor Kirhenshtein
 **
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU General Public License as published by
 #include <libpq-fe.h>
 #include <string.h>
 
+/**
+ * PostgreSQL connection
+ */
 typedef struct
 {
        PGconn *handle;
-       PGresult *fetchBuffer;
-       bool keepFetchBuffer;
        MUTEX mutexQueryLock;
 } PG_CONN;
 
+/**
+ * Prepared statement
+ */
 typedef struct
 {
        PG_CONN *connection;
@@ -60,4 +64,16 @@ typedef struct
        char **buffers; 
 } PG_STATEMENT;
 
+/**
+ * Unbuffered query result
+ */
+typedef struct
+{
+   PG_CONN *conn;
+   PGresult *fetchBuffer;
+   bool keepFetchBuffer;
+   bool singleRowMode;
+   int currRow;
+} PG_UNBUFFERED_RESULT;
+
 #endif   /* _pgsqldrv_h_ */
index fb6e2e6..27ff294 100644 (file)
@@ -116,7 +116,7 @@ extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
 /**
  * Initialize driver
  */
-extern "C" bool EXPORT DrvInit(const char *cmdLine)
+extern "C" bool EXPORT DrvInit(const char *cmdLine, void (*dbgPrintCb)(int, const TCHAR *, va_list))
 {
    if (!sqlite3_threadsafe() ||        // Fail if SQLite compiled without threading support
                 (sqlite3_initialize() != SQLITE_OK))
index 002ff84..6dded03 100644 (file)
@@ -87,7 +87,7 @@ DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *init
                                                                                                                void *userArg)
 {
    static DWORD dwVersionZero = 0;
-   bool (* fpDrvInit)(const char *);
+   bool (* fpDrvInit)(const char *, void (*)(int, const TCHAR *, va_list));
    DWORD *pdwAPIVersion;
    TCHAR szErrorText[256];
        const char *driverName;
@@ -184,7 +184,7 @@ DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *init
        }
 
    // Import symbols
-   fpDrvInit = (bool (*)(const char *))DLGetSymbolAddrEx(driver->m_handle, "DrvInit");
+   fpDrvInit = (bool (*)(const char *, void (*)(int, const TCHAR *, va_list)))DLGetSymbolAddrEx(driver->m_handle, "DrvInit");
    driver->m_fpDrvConnect = (DBDRV_CONNECTION (*)(const char *, const char *, const char *, const char *, const char *, WCHAR *))DLGetSymbolAddrEx(driver->m_handle, "DrvConnect");
    driver->m_fpDrvDisconnect = (void (*)(DBDRV_CONNECTION))DLGetSymbolAddrEx(driver->m_handle, "DrvDisconnect");
    driver->m_fpDrvSetPrefetchLimit = (bool (*)(DBDRV_CONNECTION, int))DLGetSymbolAddrEx(driver->m_handle, "DrvSetPrefetchLimit", false);
@@ -249,9 +249,9 @@ DB_DRIVER LIBNXDB_EXPORTABLE DBLoadDriver(const TCHAR *module, const TCHAR *init
    {
       mbInitParameters[0] = 0;
    }
-   if (!fpDrvInit(mbInitParameters))
+   if (!fpDrvInit(mbInitParameters, (void (*)(int, const TCHAR *, va_list))__DBGetDebugPrintCallback()))
 #else
-   if (!fpDrvInit(CHECK_NULL_EX(initParameters)))
+   if (!fpDrvInit(CHECK_NULL_EX(initParameters, (void (*)(int, const TCHAR *, va_list))__DBGetDebugPrintCallback())))
 #endif
    {
       if (s_writeLog)
index 3845f50..c07c1bf 100644 (file)
@@ -139,6 +139,7 @@ struct db_unbuffered_result_t
  */
 void __DBWriteLog(WORD level, const TCHAR *format, ...);
 void __DBDbgPrintf(int level, const TCHAR *format, ...);
+void *__DBGetDebugPrintCallback();
 
 /**
  * Global variables
index 366d294..dfbbbe0 100644 (file)
@@ -66,6 +66,14 @@ void LIBNXDB_EXPORTABLE DBSetDebugPrintCallback(void (*cb)(int, const TCHAR *, v
        __DBDbgPrintf(1, _T("Debug callback set for DB library"));
 }
 
+/**
+ * Set debug print callback
+ */
+void *__DBGetDebugPrintCallback()
+{
+   return (void *)s_dbgPrintCb;
+}
+
 /**
  * Set long running query threshold (milliseconds)
  */