SQLDriverConnect(unix-odbc에서)가 DSN 데이터를 캐시합니까? 그렇다면 어떻게 지우거나 지울 수 있나요?

SQLDriverConnect(unix-odbc에서)가 DSN 데이터를 캐시합니까? 그렇다면 어떻게 지우거나 지울 수 있나요?

UNIX-ODBC 라이브러리를 사용하는 경우unixodbc웹사이트, API에 문제가 있습니다 SQLDriverConnect. 연속으로 두 번 데이터베이스에 연결하려고 하면 처음에는 잘못된 DSN 데이터가 사용됩니다.(데이터 소스 이름 데이터, /etc/odbc.ini일반적으로 배치됨)& 두 번째로 데이터가 정확하면 두 번째 연결 시도도 실패합니다. 실패 원인은 SQLDriverConnect첫 실행 시 잘못된 데이터가 사용된 것으로 보여서 발생한 것으로 보인다.

데이터 캐싱에 관한 내용을 웹에서 검색한 결과, 이 특정한 문제를 겪은 사람은 아무도 없는 것 같습니다(또는 내 검색만으로는 충분하지 않았습니다).

나의 사용 사례는 사용자가 양식의 모든 매개변수를 수동으로 입력하고 "연결 테스트" 버튼을 클릭할 수 있는 GUI를 제공하는 것입니다. 세부 정보를 파일에 쓴(또는 덮어쓴) /etc/odbc.ini다음 unixodbcAPI를 사용하여 데이터베이스에 연결을 시도합니다. 테스트가 성공하면 SQLDriverConnectGUI에서 반환된 연결 문자열이 채워집니다. 실패하면 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;]

. . 두 번째 시도에서는 연결 시도 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;]

. .

질문

여기서 질문을 다시 한번 말씀드리겠습니다.

  1. ./code 1두 연결 테스트가 모두 실패하는 이유는 무엇 입니까?
  2. 연결 시도 사이에 핸들이 올바르게 해제되었음에도 불구하고 SQLDriverConnect데이터가 어떻게든 캐시되고 있습니까?
  3. 두 번째 시도가 성공하도록 이 가정된 캐시를 어떻게 지우나요?
  4. 실제로 버그인 경우 동일한 프로그램 실행 내 후속 테스트에서 원하는 결과를 얻을 수 있는 해결 방법이 있습니까(테스트는 다시 시작할 수 없는 서버에서 트리거되어야 한다는 점을 기억하세요.)?

. .

답변1

최신 버전의 드라이버 관리자(2.2.14는 2008 버전)를 사용해 보는 것이 좋습니다. 업데이트된 버전을 사용해도 문제가 해결되지 않을 수 있지만 확실히 캐시 코드에 수정 사항이 추가됩니다.

또한 2.3.x를 빌드할 때 구성에 --enable-inicaching=no를 추가합니다. 이것이 현재 보고 있는 문제의 원인일 가능성이 높습니다.

관련 정보