UNIX-ODBCライブラリを使用する場合unixodbcウェブサイト、APIに問題がありますSQLDriverConnect
。連続して2回データベースに接続しようとすると、最初は無効なDSNデータが使用されます。(データソース名データ、/etc/odbc.ini
通常は配置)& 2番目のデータが正しい場合、2番目の接続試行も失敗します。失敗の原因は、SQLDriverConnect
最初の実行時に誤ったデータが使用されたことが示されたために発生したようです。
データキャッシュに関するコンテンツをWebで検索した結果、この特定の問題を経験した人は誰もいないようです(または私の検索だけでは不十分です)。
私のユースケースは、ユーザーがフォームのすべてのパラメータを手動で入力し、[接続テスト]ボタンをクリックできるGUIを提供することです。詳細をファイルに書き込む(または上書き)/etc/odbc.ini
次のunixodbc
APIを使用してデータベースに接続しようとします。テストが成功すると、SQLDriverConnect
GUIから返された接続文字列が入力されます。失敗すると、GUIに失敗が表示され、ユーザーがフォームのデータを編集して[接続テスト]ボタンをもう一度クリックできます。
問題:ユーザーが誤ったデータ(ポート番号など)を入力すると、テストは失敗し、ユーザーはデータを修正します。ユーザーが接続をテストしようとすると、すべてのデータが正確でodbc.ini
ファイルにも正しく入力されたため、接続テストに合格する必要があります。驚いたことに、2回目の再テストは失敗しました。ただし、3回目または4回目の再テスト後に接続が正しい場合もあります。
プログラムを再実行しても問題は発生しないようです。理想的には、プログラムを一度実行している間は再接続する必要があります。テストはサーバー側で実行され、再起動する必要がないため、これは私にとって重要です。
システムの詳細
以下は、後でサンプルを開発して実行するために使用されるシステムの詳細です。
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
出力は次のようになります。 2回目の接続を試みる前に、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;]
。 。 2回目の試行では、接続試行の15秒前に印刷された正しいデータがiniに反映されていますが、ポート「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;]
。 。
質問
ここで質問をもう一度申し上げます。
./code 1
両方の接続テストが失敗するのはなぜですか?- 接続試行の間にハンドルが正しく解放されたにもかかわらず、
SQLDriverConnect
データは何とかキャッシュされていますか? - 2番目の試みが成功するように、この仮定キャッシュをどのように消去しますか?
- 実際にバグの場合、同じプログラム実行内の後続のテストで目的の結果を得るための回避策はありますか(テストは再起動できないサーバーでトリガーする必要があることに注意してください。)?
。 。
答え1
最新バージョンのドライバマネージャ(2.2.14は2008バージョン)をお試しください。更新されたバージョンを使用しても問題は解決しない可能性がありますが、確かにキャッシュコードに修正が追加されます。
また、2.3.xをビルドするときは、設定に--enable-inicaching = noを追加します。これが現在見ている問題の原因である可能性が高いです。