in driver/connect.cc [1243:1572]
SQLRETURN SQL_API MySQLDriverConnect(SQLHDBC hdbc, SQLHWND hwnd,
SQLWCHAR *szConnStrIn,
SQLSMALLINT cbConnStrIn,
SQLWCHAR *szConnStrOut,
SQLSMALLINT cbConnStrOutMax,
SQLSMALLINT *pcbConnStrOut,
SQLUSMALLINT fDriverCompletion)
{
SQLRETURN rc= SQL_SUCCESS;
DBC *dbc= (DBC *)hdbc;
DataSource* ds = new DataSource();
/* We may have to read driver info to find the setup library. */
Driver driver;
/* We never know how many new parameters might come out of the prompt */
SQLWCHAR prompt_outstr[4096];
BOOL bPrompt= FALSE;
HMODULE hModule= NULL;
SQLWSTRING conn_str_in, conn_str_out;
SQLWSTRING prompt_instr;
if (cbConnStrIn != SQL_NTS)
conn_str_in = SQLWSTRING(szConnStrIn, cbConnStrIn);
else
conn_str_in = szConnStrIn;
/* Parse the incoming string */
if (ds->from_kvpair(conn_str_in.c_str(), (SQLWCHAR)';'))
{
rc= dbc->set_error( "HY000",
"Failed to parse the incoming connect string.", 0);
goto error;
}
#ifndef NO_DRIVERMANAGER
/*
If the connection string contains the DSN keyword, the driver retrieves
the information for the specified data source (and merges it into the
connection info with the provided connection info having precedence).
This also allows us to get pszDRIVER (if not already given).
*/
if (ds->opt_DSN)
{
ds->lookup();
/*
If DSN is used:
1 - we want the connection string options to override DSN options
2 - no need to check for parsing erros as it was done before
*/
ds->from_kvpair(conn_str_in.c_str(), (SQLWCHAR)';');
}
#endif
/* If FLAG_NO_PROMPT is not set, force prompting off. */
if (ds->opt_NO_PROMPT)
fDriverCompletion= SQL_DRIVER_NOPROMPT;
/*
We only prompt if we need to.
A not-so-obvious gray area is when SQL_DRIVER_COMPLETE or
SQL_DRIVER_COMPLETE_REQUIRED was specified along without a server or
password - particularly password. These can work with defaults/blank but
many callers expect prompting when these are blank. So we compromise; we
try to connect and if it works we say its ok otherwise we go to
a prompt.
*/
switch (fDriverCompletion)
{
case SQL_DRIVER_PROMPT:
bPrompt= TRUE;
break;
case SQL_DRIVER_COMPLETE:
case SQL_DRIVER_COMPLETE_REQUIRED:
if (ds->opt_LOG_QUERY && !log_file)
log_file = init_log_file();
dbc->init_proxy_chain(ds);
dbc->connection_handler = std::make_shared<CONNECTION_HANDLER>(dbc);
dbc->fh = new FAILOVER_HANDLER(dbc, ds);
rc = dbc->fh->init_connection();
if (!SQL_SUCCEEDED(rc))
dbc->telemetry.set_error(dbc, dbc->error.message);
if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)
goto connected;
bPrompt= TRUE;
break;
case SQL_DRIVER_NOPROMPT:
bPrompt= FALSE;
break;
default:
rc= dbc->set_error("HY110", "Invalid driver completion.", 0);
goto error;
}
#ifdef __APPLE__
/*
We don't support prompting on Mac OS X.
*/
if (bPrompt)
{
rc= dbc->set_error("HY000",
"Prompting is not supported on this platform. "
"Please provide all required connect information.",
0);
goto error;
}
#endif
if (bPrompt)
{
PromptFunc pFunc;
/*
We can not present a prompt unless we can lookup the name of the setup
library file name. This means we need a DRIVER. We try to look it up
using a DSN (above) or, hopefully, get one in the connection string.
This will, when we need to prompt, trump the ODBC rule where we can
connect with a DSN which does not exist. A possible solution would be to
hard-code some fall-back value for ds->pszDRIVER.
*/
if (!ds->opt_DRIVER)
{
char szError[1024];
sprintf(szError,
"Could not determine the driver name; "
"could not lookup setup library. DSN=(%s)\n",
(const char*)ds->opt_DSN);
rc= dbc->set_error("HY000", szError, 0);
goto error;
}
#ifndef NO_DRIVERMANAGER
/* We can not present a prompt if we have a null window handle. */
if (!hwnd)
{
rc= dbc->set_error("IM008", "Invalid window handle", 0);
goto error;
}
/* if given a named DSN, we will have the path in the DRIVER field */
if (ds->opt_DSN)
driver.lib = ds->opt_DRIVER;
/* otherwise, it's the driver name */
else
driver.name = ds->opt_DRIVER;
if (driver.lookup())
{
char sz[1024];
sprintf(sz, "Could not find driver '%s' in system information.",
(const char*)ds->opt_DRIVER);
rc= dbc->set_error("IM003", sz, 0);
goto error;
}
if (!driver.setup_lib)
#endif
{
rc= dbc->set_error("HY000",
"Could not determine the file name of setup library.",
0);
goto error;
}
/*
We dynamically load the setup library so the driver itself does not
depend on GUI libraries.
*/
#ifndef WIN32
/*
lt_dlinit();
*/
#endif
if (!(hModule= LoadLibrary(driver.setup_lib)))
{
char sz[1024];
sprintf(sz, "Could not load the setup library '%s'.",
(const char *)driver.setup_lib);
rc= dbc->set_error("HY000", sz, 0);
goto error;
}
pFunc= (PromptFunc)GetProcAddress(hModule, "Driver_Prompt");
if (pFunc == NULL)
{
#ifdef WIN32
LPVOID pszMsg;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&pszMsg, 0, NULL);
rc= dbc->set_error("HY000", (char *)pszMsg, 0);
LocalFree(pszMsg);
#else
rc= dbc->set_error("HY000", dlerror(), 0);
#endif
goto error;
}
/* create a string for prompting, and add driver manually */
prompt_instr = ds->to_kvpair(';');
prompt_instr.append(W_DRIVER_PARAM);
SQLWSTRING drv = (const SQLWSTRING&)ds->opt_DRIVER;
prompt_instr.append(drv);
/*
In case the client app did not provide the out string we use our
inner buffer prompt_outstr
*/
if (!pFunc(hwnd, (SQLWCHAR*)prompt_instr.c_str(), fDriverCompletion,
prompt_outstr, sizeof(prompt_outstr), pcbConnStrOut))
{
dbc->set_error("HY000", "User cancelled.", 0);
rc= SQL_NO_DATA;
goto error;
}
/* refresh our DataSource */
ds->reset();
if (ds->from_kvpair(prompt_outstr, ';'))
{
rc= dbc->set_error( "HY000",
"Failed to parse the prompt output string.", 0);
goto error;
}
/*
We don't need prompt_outstr after the new DataSource is created.
Copy its contents into szConnStrOut if possible
*/
if (szConnStrOut)
{
*pcbConnStrOut= (SQLSMALLINT)myodbc_min(cbConnStrOutMax, *pcbConnStrOut);
memcpy(szConnStrOut, prompt_outstr, (size_t)*pcbConnStrOut*sizeof(SQLWCHAR));
/* term needed if possibly truncated */
szConnStrOut[*pcbConnStrOut - 1] = 0;
}
}
if (ds->opt_LOG_QUERY && !log_file)
log_file = init_log_file();
dbc->init_proxy_chain(ds);
dbc->connection_handler = std::make_shared<CONNECTION_HANDLER>(dbc);
dbc->fh = new FAILOVER_HANDLER(dbc, ds);
rc = dbc->fh->init_connection();
if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO)
{
goto error;
}
if (ds->opt_SAVEFILE)
{
/* We must disconnect if File DSN is created */
dbc->close();
}
connected:
/* copy input to output if connected without prompting */
if (!bPrompt)
{
size_t copylen;
conn_str_out = conn_str_in;
if (ds->opt_SAVEFILE)
{
SQLWSTRING pwd_temp = (const SQLWSTRING &)ds->opt_PWD;
/* make sure the password does not go into the output buffer */
ds->opt_PWD = nullptr;
conn_str_out = ds->to_kvpair(';');
/* restore old values */
ds->opt_PWD = pwd_temp;
}
size_t inlen = conn_str_out.length();
copylen = myodbc_min((size_t)cbConnStrOutMax, inlen + 1) * sizeof(SQLWCHAR);
if (szConnStrOut && copylen)
{
memcpy(szConnStrOut, conn_str_out.c_str(), copylen);
/* term needed if possibly truncated */
szConnStrOut[(copylen / sizeof(SQLWCHAR)) - 1] = 0;
}
/*
Even if the buffer for out connection string is NULL
it will still return the total number of characters
(excluding the null-termination character for character data)
available to return in the buffer pointed to by
OutConnectionString.
https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqldriverconnect-function?view=sql-server-ver16
*/
if (pcbConnStrOut)
*pcbConnStrOut = (SQLSMALLINT)inlen;
}
/* return SQL_SUCCESS_WITH_INFO if truncated output string */
if (pcbConnStrOut && cbConnStrOutMax &&
cbConnStrOutMax - sizeof(SQLWCHAR) <= *pcbConnStrOut * sizeof(SQLWCHAR))
{
dbc->set_error("01004", "String data, right truncated.", 0);
rc= SQL_SUCCESS_WITH_INFO;
}
error:
if (!SQL_SUCCEEDED(rc))
dbc->telemetry.set_error(dbc, dbc->error.message);
if (hModule)
FreeLibrary(hModule);
return rc;
}