add implicit --copy '.*' only if no CLI arguments were specified (original behavior)
/**
* 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) {
setAutoCommit(false);
}
Connection::~Connection() {
// If an exception was thrown somewhere, there might be a transaction in progress, so we will cancel this transaction.
// If there was no exception, commit() has already been called and this rollback() will not affect any (desired) transaction.
// Without this rollback(), SQLDisconnect() will fail and then will fail also freeHandle() which will throw exception.
// And throwing exception while throwing exception will lead to a catastrophic failure: SIGABRT, core dump.
rollback();
SQLRETURN result = SQLDisconnect(connection);
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)) throw SqlException(L"Unable to prepare statement", result, SQL_HANDLE_STMT, statement, true);
return new PreparedStatement(statement);
}
bool Connection::getAutoCommit() {
SQLULEN autoCommit = SQL_AUTOCOMMIT_DEFAULT;
SQLRETURN result = SQLGetConnectOption(connection, SQL_AUTOCOMMIT, &autoCommit);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to get auto-commit status", result, SQL_HANDLE_DBC, connection);
if (autoCommit == SQL_AUTOCOMMIT_ON) return true;
else if (autoCommit == SQL_AUTOCOMMIT_OFF) return false;
else throw SqlException(L"Unexpected value of auto-commit status");
}
void Connection::setAutoCommit(bool autoCommit) {
SQLRETURN result = SQLSetConnectOption(connection, SQL_AUTOCOMMIT, autoCommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(std::wstring(L"Unable to set auto-commit to ") + (autoCommit ? L"enabled" : L"disabled"), result, SQL_HANDLE_DBC, connection);
}
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::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)) throw SqlException(L"Unable get tables (prepare)", result, SQL_HANDLE_STMT, statementHandle, true);
ResultSet resultSet(statementHandle);
std::shared_ptr<ResultSet::MetaData> metaData(resultSet.getMetaData());
SQLUSMALLINT catalogColumn = metaData->getColumnNumber(L"TABLE_CAT");
SQLUSMALLINT schemaColumn = metaData->getColumnNumber(L"TABLE_SCHEM");
SQLUSMALLINT nameColumn = metaData->getColumnNumber(L"TABLE_NAME");
SQLUSMALLINT grantorColumn = metaData->getColumnNumber(L"GRANTOR");
SQLUSMALLINT granteeColumn = metaData->getColumnNumber(L"GRANTEE");
SQLUSMALLINT privilegeColumn = metaData->getColumnNumber(L"PRIVILEGE");
SQLUSMALLINT isGrantableColumn = metaData->getColumnNumber(L"IS_GRANTABLE");
while (resultSet.next()) {
TablePrivilege tp;
tp.catalog = resultSet.getString(catalogColumn);
tp.schema = resultSet.getString(schemaColumn);
tp.name = resultSet.getString(nameColumn);
tp.grantor = resultSet.getString(grantorColumn);
tp.grantee = resultSet.getString(granteeColumn);
tp.privilege = resultSet.getString(privilegeColumn);
tp.isGrantable = resultSet.getString(isGrantableColumn) == L"YES";
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);
}
}
}
}