getDataSources() can be called multiple-times, use SQL_FETCH_FIRST and SQL_FETCH_NEXT
/**
* Relational pipes
* Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include "SqlException.h"
#include "DriverManager.h"
#include "OdbcCommon.h"
namespace relpipe {
namespace tr {
namespace sql {
DriverManager::DriverManager() {
environment = OdbcCommon::allocateHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE);
SQLRETURN result = SQLSetEnvAttr(environment, SQL_ATTR_ODBC_VERSION, (void*) SQL_OV_ODBC3, 0);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to set ODBC version", result, SQL_HANDLE_ENV, environment);
}
DriverManager::~DriverManager() {
OdbcCommon::freeHandle(SQL_HANDLE_ENV, environment);
}
std::vector<DriverManager::DataSource> DriverManager::getDataSources() {
std::vector<DriverManager::DataSource> list;
SQLCHAR name[SQL_MAX_DSN_LENGTH + 1];
SQLCHAR description[255];
memset(name, 0, sizeof (name));
memset(description, 0, sizeof (description));
SQLSMALLINT nameLength;
SQLSMALLINT descriptionLength;
for (int i = 0; true; i++) {
SQLRETURN result = SQLDataSources(environment, i == 0 ? SQL_FETCH_FIRST : SQL_FETCH_NEXT,
name, sizeof (name), &nameLength,
description, sizeof (description), &descriptionLength);
// TODO: description field actually contains the driver name – see comments in unixODBC file: DriverManager/SQLDataSources.c
// TODO: check nameLength and descriptionLength whether values were truncated?
if (OdbcCommon::isSuccessful(result)) list.push_back({convertor.from_bytes((char*) name), convertor.from_bytes((char*) description)});
else if (result == SQL_NO_DATA_FOUND) break;
else throw SqlException(L"Unable to list data sources: " + std::to_wstring(result), result, SQL_HANDLE_ENV, environment);
}
return list;
}
Connection* DriverManager::getConnectionByDSN(relpipe::reader::string_t dataSourceName, relpipe::reader::string_t userName, relpipe::reader::string_t password) {
SQLHDBC connection = OdbcCommon::allocateHandle(SQL_HANDLE_DBC, environment);
std::string dataSourceNameBytes = convertor.to_bytes(dataSourceName);
std::string userNameBytes = convertor.to_bytes(userName);
std::string passwordBytes = convertor.to_bytes(password);
SQLRETURN result = SQLConnect(connection,
(SQLCHAR*) dataSourceNameBytes.c_str(), SQL_NTS,
(SQLCHAR*) userNameBytes.c_str(), SQL_NTS,
(SQLCHAR*) passwordBytes.c_str(), SQL_NTS);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to connect to DSN: " + dataSourceName, result, SQL_HANDLE_DBC, connection, true);
return new Connection(connection);
}
Connection* DriverManager::getConnectionByURL(relpipe::reader::string_t connectionString) {
SQLHDBC connection = OdbcCommon::allocateHandle(SQL_HANDLE_DBC, environment);
char completeConnectionString[SQL_MAX_OPTION_STRING_LENGTH];
memset(completeConnectionString, 0, sizeof (completeConnectionString));
SQLSMALLINT completeConnectionStringLength = -1;
SQLRETURN result = SQLDriverConnect(connection, nullptr,
(SQLCHAR*) convertor.to_bytes(connectionString).c_str(), SQL_NTS,
(SQLCHAR*) completeConnectionString, sizeof (completeConnectionString), &completeConnectionStringLength,
SQL_DRIVER_NOPROMPT);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to connect to URL: " + connectionString, result, SQL_HANDLE_DBC, connection, true);
return new Connection(connection);
}
}
}
}