C++

SQLDriverConnect(來自 unix-odbc)是否記憶體 DSN 數據?如果是這樣,我該如何清除/清除它?

  • December 8, 2016

在使用來自unixodbc站點的 UNIX-ODBC 庫時,我遇到了SQLDriverConnectapi 問題。如果我嘗試連續兩次連接到我的數據庫,第一次使用不正確的 DSN 數據*(數據源名稱數據,/etc/odbc.ini一般放置)*,第二次使用正確的數據,第二次嘗試連接也會失敗。失敗的原因似乎是SQLDriverConnect似乎在第一次執行時使用了不正確的數據。

在網上搜尋有關記憶體數據的任何提及之後,似乎沒有其他人遇到過這個特殊問題(或者我的搜尋不足)。

我的案例是我提供了一個 GUI,使用者可以在其中手動填寫所有參數的表單,然後點擊“測試連接”按鈕。這會將詳細資訊寫入(或覆蓋)/etc/odbc.ini文件,然後嘗試使用unixodbcAPI 連接到數據庫。如果測試成功,則從返回的連接字元串SQLDriverConnect將填充到 GUI 中。如果失敗,GUI 將顯示失敗並允許使用者編輯表單上的數據並再次點擊“測試連接”按鈕。

問題:如果使用者輸入了一些不正確的數據(比如埠號),測試失敗,使用者糾正數據。當使用者現在嘗試測試它應該通過的連接時,因為所有數據都是正確的並且也odbc.ini正確地填充到文件中。令人抓狂的是,它在第​​二次重新測試中失敗了。但是,有時第三次或第四次重新測試之後它能夠正確連接。

請注意,理想情況下,重新連接應該在程序的單次執行中進行,因為重新執行程序似乎不會出現問題。這對我來說很重要,因為測試將從伺服器端觸發並且不會有重新啟動的奢侈。


系統詳情

以下是稍後用於開發和執行範例的系統的詳細資訊:

Developement Machine 
CentOS release 6.1 (Final)
2.6.32-131.0.15.el6.i686 {32bit machine}

Deployment Machine (CentOS)
Linux release 6.6 (Final)
2.6.32-573.el6.x86_64 {64bit machine}
unixODBC 2.2.14
/usr/lib/psqlodbcw.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped

. .

範常式式碼

使用 libodbc建構程式碼,如下所示:

g++ -g -o 程式碼 code.cpp -lodbc

請注意,您可能必須確保包含的文件和庫就位。

#include "../boost_1_52_0/boost/property_tree/ptree.hpp"
#include "../boost_1_52_0/boost/property_tree/ini_parser.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <unistd.h>
#include "../unixODBC-2.3.4/include/sql.h"     
#include "../unixODBC-2.3.4/include/sqlext.h"
#include "../unixODBC-2.3.4/include/odbcinst.h"

using boost::property_tree::ptree;
using namespace std;

void PopulateINI(const string& iniName, vector<pair<string, string> >& data);
bool TestConnection(const string& connStringIn, string& connStringOut);
static void extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type);
void PrintIniFile(const string& iniName, const string& sDSN);


int main(int argc, char* argv[])
{

   if (argc != 2)
   {
       cout << "Enter the choice of\n\t1 : Full run:- \n\t\t\tpopulate incorrect data\n\t\t\tattempt to connect\n\t\t\tpopulate CORRECT data\n\t\t\twait 15 secs\n\t\t\tattempt to connect\n\t2 : attempt to connect with existing ini data" << endl;
       return 0;
   }

   int iCh = atoi(argv[1]);

   if(iCh != 1 && iCh != 2)
   {
       cout << "Invalid choice !!\nAcceptable values are -  '1'  OR  '2'  only" << endl;
       return 0;
   }


   string sDSN = "PostgresTest01";
   string sConnStrIn, sConnStrOut;
   sConnStrIn.append("DSN=").append(sDSN.c_str()).append(1, ';');

   string iniName = "/etc/odbc.ini";

   if (iCh == 1)
   {
       //Incorrect DSN data

       vector<pair<string, string> > vData;

       vData.push_back(make_pair(sDSN + ".Description", "Description"));
       vData.push_back(make_pair(sDSN + ".Driver", "PostgreSQL"));
       vData.push_back(make_pair(sDSN + ".Database", "dvdrental"));
       vData.push_back(make_pair(sDSN + ".Servername", "192.168.45.217"));
       vData.push_back(make_pair(sDSN + ".Port", "1234"));                 //INCORRECT PORT NUMBER; '1234' instead of '5432'
       vData.push_back(make_pair(sDSN + ".UserName", "postgres"));
       vData.push_back(make_pair(sDSN + ".Password", "postgres"));
       vData.push_back(make_pair(sDSN + ".Trace", "Off"));
       vData.push_back(make_pair(sDSN + ".TraceFile", "stderr"));
       vData.push_back(make_pair(sDSN + ".Protocol", "7.0"));
       vData.push_back(make_pair(sDSN + ".ReadOnly", "No"));
       vData.push_back(make_pair(sDSN + ".RowVersioning", "No"));
       vData.push_back(make_pair(sDSN + ".ShowSystemTables", "No"));
       vData.push_back(make_pair(sDSN + ".ShowOidColumn", "No"));
       vData.push_back(make_pair(sDSN + ".FakeOidIndex", "No"));
       vData.push_back(make_pair(sDSN + ".ConnSettings", ""));


       //Populate ini with Incorrect data
       PopulateINI(iniName, vData);
       sleep(5); //Just so I can see the ini file changing

       //First run - Call SQLDriverConnect
       PrintIniFile(iniName, sDSN);
       sConnStrOut.clear();
       if(TestConnection(sConnStrIn, sConnStrOut))
       {
           cout << "Test connection succeeded.\nConnection String is [" << sConnStrOut << "]" << endl;
       }
       else
       {
           cout << "Test connection failed for sConnStrIn[" << sConnStrIn << "]" << endl;
       }


       cout << "\n\n====================================================================" << endl;

       cout << "Updating ini file with correct data..." << endl;

       vData[4].second = "5432";                                  //CORRECT PORT NUMBER 
       PopulateINI(iniName, vData);                               //WRITE TO INI FILE

       cout << "\n\nWaiting for 15 secs" << endl;
       sleep(15);      //15, so that I could manually change the odbc.ini, as I had some suspicions about ptree, read_ini() & write_ini()

       cout << "\n\n====================================================================" << endl;
   }

   //Second run - Call SQLDriverConnect
   PrintIniFile(iniName, sDSN);
   sConnStrOut.clear();
   if(TestConnection(sConnStrIn, sConnStrOut))
   {
       cout << "Test connection succeeded.\nConnection String is [" << sConnStrOut << "]" << endl;;
   }
   else
   {
       cout << "Test connection failed for sConnStrIn[" << sConnStrIn << "]" << endl;
   }

   return 0;
}

void PrintVector(const string& label, vector<pair<string, string> >& data)
{
   cout << "\n\n " << label << "\n" << endl;

   for(vector<pair<string, string> >::iterator it = data.begin(); it != data.end(); ++it)
   {
       cout << "\t\t" << it->first << " : " << it->second << endl;
   }
   cout << "\n===================================================" << endl;
}

void PopulateINI(const string& iniName, vector<pair<string, string> >& data)
{
   ptree pt;
   read_ini(iniName.c_str(), pt);

   for(vector<pair<string, string> >::iterator it = data.begin(); it != data.end(); ++it)
   {
       pt.put(it->first.c_str(), it->second.c_str());
   }
   write_ini(iniName.c_str(), pt);
}

bool TestConnection(const string& connStringIn, string& connStringOut)
{
   bool fRC = false;
   SQLRETURN retcode;
   SQLHENV env=NULL;
   SQLHDBC dbc=NULL;
   SQLSMALLINT siOutConnStrLen;

   connStringOut.resize(2048);

   SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
   SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
   SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
   retcode = SQLDriverConnect(dbc, NULL, (SQLCHAR*)connStringIn.c_str(), SQL_NTS, (SQLCHAR*)&connStringOut.at(0), 2048, &siOutConnStrLen, SQL_DRIVER_NOPROMPT);                                                              

   if(SQL_SUCCEEDED(retcode))
   {
       connStringOut.resize(siOutConnStrLen);
       fRC = true;
       if(retcode == SQL_SUCCESS_WITH_INFO)
       {
           cout << "Driver reported the following diagnostics:" << endl;
           extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
       }
       SQLDisconnect(dbc);
   }
   else
   {
       cout << "Failed to connect:" << endl;
       extract_error("SQLDriverConnect", dbc, SQL_HANDLE_DBC);
   }

   SQLFreeHandle(SQL_HANDLE_DBC, dbc);
   SQLFreeHandle(SQL_HANDLE_ENV, env);

   return fRC;
}


void extract_error(char *fn, SQLHANDLE handle, SQLSMALLINT type)
{
   SQLINTEGER   i = 0;
   SQLINTEGER   native;
   SQLCHAR  state[ 7 ];
   SQLCHAR  text[256];
   SQLSMALLINT  len;
   SQLRETURN    ret;

   fprintf(stderr, "\nThe driver reported the following diagnostics whilst running %s\n\n", fn);

   do
   {
       ret = SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len );
       if (SQL_SUCCEEDED(ret))
           printf("%s:%ld:%ld:%s\n", state, i, native, text);
   }
   while( ret == SQL_SUCCESS );
}


void PrintIniFile(const string& iniName, const string& sDSN)
{
   ptree pt;

   read_ini(iniName.c_str(), pt);


   cout << "\n\n[" << sDSN << "]" << endl;

   cout << "Description : " << pt.get<string>((sDSN + "." + "Description").c_str()) <<endl;
   cout << "Driver : " << pt.get<string>((sDSN + "." + "Driver").c_str()) <<endl;
   cout << "Database : " << pt.get<string>((sDSN + "." + "Database").c_str()) <<endl;
   cout << "Servername : " << pt.get<string>((sDSN + "." + "Servername").c_str()) <<endl;
   cout << "Port : " << pt.get<string>((sDSN + "." + "Port").c_str()) <<endl;
   cout << "UserName : " << pt.get<string>((sDSN + "." + "UserName").c_str()) <<endl;
   cout << "Password : " << pt.get<string>((sDSN + "." + "Password").c_str()) <<endl;
   cout << "Trace : " << pt.get<string>((sDSN + "." + "Trace").c_str()) <<endl;
   cout << "TraceFile : " << pt.get<string>((sDSN + "." + "TraceFile").c_str()) <<endl;
   cout << "Protocol : " << pt.get<string>((sDSN + "." + "Protocol").c_str()) <<endl;
   cout << "ReadOnly : " << pt.get<string>((sDSN + "." + "ReadOnly").c_str()) <<endl;
   cout << "RowVersioning : " << pt.get<string>((sDSN + "." + "RowVersioning").c_str()) <<endl;
   cout << "ShowSystemTables : " << pt.get<string>((sDSN + "." + "ShowSystemTables").c_str()) <<endl;
   cout << "ShowOidColumn : " << pt.get<string>((sDSN + "." + "ShowOidColumn").c_str()) <<endl;
   cout << "FakeOidIndex : " << pt.get<string>((sDSN + "." + "FakeOidIndex").c_str()) <<endl;
   cout << "ConnSettings : " << pt.get<string>((sDSN + "." + "ConnSettings").c_str()) <<endl;
   cout << "\n\n" << endl;
}

. .

執行

程序採用單個參數“1”或“2”。

1 : Full run:-
       populate incorrect data
       attempt to connect
       populate CORRECT data
       wait 15 secs
       attempt to connect
2 : attempt to connect with existing ini data

例如

./程式碼 1

或者

./程式碼 2

輸出

對於完整執行./code 1,以下是輸出。請注意,在第二次嘗試連接之前,odbc.ini已修改並讀取以顯示正確的“埠號”。

[PostgresTest01]
Description : Description
Driver : PostgreSQL
Database : dvdrental
Servername : 192.168.45.217
Port : 1234
UserName : postgres
Password : postgres
Trace : Off
TraceFile : stderr
Protocol : 7.0
ReadOnly : No
RowVersioning : No
ShowSystemTables : No
ShowOidColumn : No
FakeOidIndex : No
ConnSettings : 



Failed to connect:

The driver reported the following diagnostics whilst running SQLDriverConnect

08001:1:101:[unixODBC]Could not connect to the server;
Connection refused [192.168.45.217:1234]
Test connection failed for sConnStrIn[DSN=PostgresTest01;]


====================================================================
Updating ini file with correct data...


Waiting for 15 secs


====================================================================


[PostgresTest01]
Description : Description
Driver : PostgreSQL
Database : dvdrental
Servername : 192.168.45.217
Port : 5432
UserName : postgres
Password : postgres
Trace : Off
TraceFile : stderr
Protocol : 7.0
ReadOnly : No
RowVersioning : No
ShowSystemTables : No
ShowOidColumn : No
FakeOidIndex : No
ConnSettings : 



Failed to connect:

The driver reported the following diagnostics whilst running SQLDriverConnect

08001:1:101:[unixODBC]Could not connect to the server;
Connection refused [192.168.45.217:1234]
Test connection failed for sConnStrIn[DSN=PostgresTest01;]

. . 請注意,在第二次嘗試中,儘管 ini 反映了在嘗試連接前 15 秒列印的正確數據,但錯誤消息顯示連接被拒絕到埠“1234”。

連接被拒絕

$$ 192.168.45.217:1234 $$ . .


. .

對於快速執行./code 2,在第一次執行之後立即執行,然後 ini 保存正確的數據,下面是輸出。它成功連接。

[PostgresTest01]
Description : Description
Driver : PostgreSQL
Database : dvdrental
Servername : 192.168.45.217
Port : 5432
UserName : postgres
Password : postgres
Trace : Off
TraceFile : stderr
Protocol : 7.0
ReadOnly : No
RowVersioning : No
ShowSystemTables : No
ShowOidColumn : No
FakeOidIndex : No
ConnSettings : 



Test connection succeeded.
Connection String is [DSN=PostgresTest01;DATABASE=dvdrental;SERVER=192.168.45.217;PORT=5432;UID=postgres;PWD=postgres;SSLmode=disable;ReadOnly=No;Protocol=7.0;FakeOidIndex=No;ShowOidColumn=No;RowVersioning=No;ShowSystemTables=No;ConnSettings=;Fetch=100;Socket=4096;UnknownSizes=0;MaxVarcharSize=255;MaxLongVarcharSize=8190;Debug=0;CommLog=0;Optimizer=0;Ksqo=1;UseDeclareFetch=0;TextAsLongVarchar=1;UnknownsAsLongVarchar=0;BoolsAsChar=1;Parse=0;CancelAsFreeStmt=0;ExtraSysTablePrefixes=dd_;;LFConversion=0;UpdatableCursors=1;DisallowPremature=0;TrueIsMinus1=0;BI=0;ByteaAsLongVarBinary=0;UseServerSidePrepare=0;LowerCaseIdentifier=0;]

. .

問題

在這裡重申問題。

  1. 為什麼./code 1導致兩個連接測試都失敗?
  2. SQLDriverConnect儘管在連接嘗試之間正確釋放了句柄,但是否以某種方式記憶體數據?
  3. 怎樣才能清除這個假定的記憶體,以便讓隨後的第二次嘗試成功?
  4. 如果它確實是一個錯誤,是否有一種解決方法可以在程序的同一執行中在後續測試中實現所需的結果(請記住,測試必須從無法重新啟動的伺服器觸發)?

. .

我會考慮嘗試驅動程序管理器的後一個版本,2.2.14 是 2008 年的。它可能無法解決嘗試更高版本的問題,但肯定會在記憶體程式碼中添加一些修復程序。

另外,在建構 2.3.x 時,我會在配置中添加 –enable-inicaching=no。這很可能是您看到的問題的原因。

引用自:https://unix.stackexchange.com/questions/329000