src/DriverManager.cpp
author František Kučera <franta-hg@frantovo.cz>
Tue, 02 Jun 2020 18:40:20 +0200
branchv_0
changeset 42 7f668a5a435b
parent 41 4b2d46989f44
child 47 428c278af4be
permissions -rw-r--r--
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);
}

}
}
}