src/Connection.cpp
author František Kučera <franta-hg@frantovo.cz>
Sun, 31 May 2020 21:20:24 +0200
branchv_0
changeset 37 3de41719d7eb
parent 36 91cb012d779a
child 38 a871779a4e3c
permissions -rw-r--r--
add options --data-source-name and --data-source-url for custom datasource; drop options --file and --file-keep

/**
 * Relational pipes
 * Copyright © 2019 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 <memory>
#include <iostream>

#include <sql.h>
#include <sqlext.h>

#include "Connection.h"
#include "OdbcCommon.h"

namespace relpipe {
namespace tr {
namespace sql {

Connection::Connection(void* db) : connection(db) {
	// TODO: transaction control
}

Connection::~Connection() {
	SQLRETURN result = SQLDisconnect(connection);
	// FIXME: do not throw exception from destructor
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to disconnect: " + std::to_wstring(result));
	OdbcCommon::freeHandle(SQL_HANDLE_DBC, connection);
}

PreparedStatement* Connection::prepareStatement(relpipe::reader::string_t sql) {
	SQLHSTMT statement = OdbcCommon::allocateHandle(SQL_HANDLE_STMT, connection);
	SQLRETURN result = SQLPrepare(statement, (SQLCHAR*) convertor.to_bytes(sql).c_str(), SQL_NTS);
	if (OdbcCommon::isNotSuccessful(result)) {
		OdbcCommon::freeHandle(SQL_HANDLE_STMT, statement);
		throw SqlException(L"Unable to prepare statement", result, SQL_HANDLE_DBC, connection); // TODO: SQL_HANDLE_STMT?
	}
	return new PreparedStatement(statement);
}

bool Connection::getAutoCommit() {
	// TODO: transaction control
	return false;
}

void Connection::setAutoCommit(bool autoCommit) {
	// TODO: transaction control
}

void Connection::commit() {
	SQLRETURN result = SQLEndTran(SQL_HANDLE_DBC, connection, SQL_COMMIT);
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to COMMIT: " + std::to_wstring(result));
}

void Connection::rollback() {
	SQLRETURN result = SQLEndTran(SQL_HANDLE_DBC, connection, SQL_ROLLBACK);
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to ROLLBACK: " + std::to_wstring(result));
}

std::vector<Connection::TableMetaData> Connection::getTables() {
	std::vector<TableMetaData> tables;
	SQLHSTMT statementHandle = OdbcCommon::allocateHandle(SQL_HANDLE_STMT, connection);
	SQLRETURN result = SQLTables(statementHandle, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0);
	if (OdbcCommon::isNotSuccessful(result)) {
		OdbcCommon::freeHandle(SQL_HANDLE_STMT, statementHandle);
		throw SqlException(L"Unable get tables (prepare)", result, SQL_HANDLE_DBC, connection); // TODO: SQL_HANDLE_STMT?
	}
	ResultSet resultSet(statementHandle);
	while (resultSet.next()) {
		TableMetaData tm;
		tm.catalog = resultSet.getString(1); // FIXME: column name: table_cat
		tm.schema = resultSet.getString(2); // FIXME: column name: table_schem
		tm.name = resultSet.getString(3); // FIXME: column name: table_name
		tm.type = resultSet.getString(4); // FIXME: column name: table_type
		tables.emplace_back(tm);
	}

	return tables;
}

std::vector<Connection::TablePrivilege> Connection::getTablePrivileges() {
	std::vector<TablePrivilege> tables;
	SQLHSTMT statementHandle = OdbcCommon::allocateHandle(SQL_HANDLE_STMT, connection);
	SQLRETURN result = SQLTablePrivileges(statementHandle, nullptr, 0, nullptr, 0, nullptr, 0);
	if (OdbcCommon::isNotSuccessful(result)) {
		OdbcCommon::freeHandle(SQL_HANDLE_STMT, statementHandle);
		throw SqlException(L"Unable get tables (prepare)", result, SQL_HANDLE_DBC, connection); // TODO: SQL_HANDLE_STMT?
	}
	ResultSet resultSet(statementHandle);
	while (resultSet.next()) {
		TablePrivilege tp;
		tp.catalog = resultSet.getString(1); // FIXME: column name: table_cat
		tp.schema = resultSet.getString(2); // FIXME: column name: table_schem
		tp.name = resultSet.getString(3); // FIXME: column name: table_name
		tp.grantor = resultSet.getString(4); // FIXME: column name: grantor
		tp.grantee = resultSet.getString(5); // FIXME: column name: grantee
		tp.privilege = resultSet.getString(6); // FIXME: column name: privilege
		tp.isGrantable = resultSet.getString(7) == L"YES"; // FIXME: column name: is_grantable
		tables.emplace_back(tp);
	}

	return tables;
}

relpipe::reader::string_t Connection::getUserName() {
	std::vector<char> buffer(100);
	SQLSMALLINT stringLength;
	SQLRETURN result = SQLGetInfo(connection, SQL_USER_NAME, buffer.data(), buffer.size(), &stringLength);
	if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to get user name: " + std::to_wstring(result));
	if (stringLength >= buffer.size()) throw SqlException(L"Unable to get user name: too long" + std::to_wstring(stringLength));
	return convertor.from_bytes(buffer.data(), buffer.data() + stringLength);
}


}
}
}