Does SQLDriverConnect (from unix-odbc) cache the DSN data? If so, how can I clear/purge it?
2
votes
1
answer
1025
views
While using the UNIX-ODBC libraries from unixodbc site, I'm facing a problem with the
SQLDriverConnect
api.
If I try to connect to my database twice in a row, first with incorrect DSN data *(Data Source Name data, placed in the /etc/odbc.ini
generally)* & second with correct data, the second attempt to connect also fails. The reason for the failure seems to be that the SQLDriverConnect
seems to use the incorrect data fed to it in the first run.
After scouring the net for any mention of it caching the data, it seems no one else has come across this particular problem (or my search was inadequate).
The use-case for me is that I'm providing a GUI where the user can manually fill the form with all the parameters and then click on a 'Test Connection' button. This will write (or overwrite) the details into the /etc/odbc.ini
file, and then attempt to connect to the database using the unixodbc
apis. If the test succeeds, the connection string returned from the SQLDriverConnect
will be populated onto the GUI. If it fails the GUI will display the failure and allow the user to edit the data on the form and click 'Test Connection' button again.
Problem: If user enter some incorrect data (say port number), the test fails and the user rectifies the data. When the user now tries to test the connection it should pass, as all the data is correct and is also populating into the odbc.ini
file properly. Maddeningly, it fails for the second retest. However, sometimes a third or fourth retest onwards it is able to connect properly.
Note that the reconnects should ideally be in the single run of the program as a re-run of the program doesn't seem to surface the problem. This is important for me at the tests will be triggered from server-side and won't have the luxury of restarting.
----------
System Details
--------------
Below are the details of the systems used for development and execution of the sample later:
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
.
.
Sample code
-----------
**Build** the code with the libodbc, as below:
> g++ -g -o code code.cpp -lodbc
**Note** that you may have to ensure that the included files and the libraries are in place.
#include "../boost_1_52_0/boost/property_tree/ptree.hpp"
#include "../boost_1_52_0/boost/property_tree/ini_parser.hpp"
#include
#include
#include
#include
#include
#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 >& 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 > 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 >& data)
{
cout >::iterator it = data.begin(); it != data.end(); ++it)
{
cout first second >& data)
{
ptree pt;
read_ini(iniName.c_str(), pt);
for(vector >::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 ((sDSN + "." + "Description").c_str()) ((sDSN + "." + "Driver").c_str()) ((sDSN + "." + "Database").c_str()) ((sDSN + "." + "Servername").c_str()) ((sDSN + "." + "Port").c_str()) ((sDSN + "." + "UserName").c_str()) ((sDSN + "." + "Password").c_str()) ((sDSN + "." + "Trace").c_str()) ((sDSN + "." + "TraceFile").c_str()) ((sDSN + "." + "Protocol").c_str()) ((sDSN + "." + "ReadOnly").c_str()) ((sDSN + "." + "RowVersioning").c_str()) ((sDSN + "." + "ShowSystemTables").c_str()) ((sDSN + "." + "ShowOidColumn").c_str()) ((sDSN + "." + "FakeOidIndex").c_str()) ((sDSN + "." + "ConnSettings").c_str()) ./code 1
OR
> ./code 2
Outputs
-------
For the full run ./code 1
, below is the output.
Notice that before the second attempt to connect, the odbc.ini
has been modified and read to display the correct 'port number'.
[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;]
.
.
Notice that in the second attempt, although the ini reflects the correct data printed 15 secs before the connection is attempted, the error message shows that the connection is refused to port '1234'.
> Connection refused [192.168.45.217:1234]
.
.
----------
.
.
For the quick run ./code 2
, immediately following the first run, after which the ini holds the correct data, below is the output.
It succeeds in connecting.
[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;]
.
.
Questions
--------
Reiterating the questions here.
1. Why does ./code 1
result in both connection tests to fail?
2. Is SQLDriverConnect
somehow caching the data inspite of proper
deallocation of handles between the connect attempts?
3. How can this supposed cache be cleared, so as to let the second subsequent attempt succeed?
4. If it is indeed a bug, is there a workaround to achieve the desired result in subsequent test within the same run of the program (*remember that the tests have to be triggered from a server, which can't restart*)?
.
.
Asked by Ramakant
(23 rep)
Dec 8, 2016, 04:08 PM
Last activity: Dec 8, 2016, 05:20 PM
Last activity: Dec 8, 2016, 05:20 PM