/**
* 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 <cstring>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include "PreparedStatement.h"
#include "ResultSet.h"
#include "OdbcCommon.h"
namespace relpipe {
namespace tr {
namespace sql {
PreparedStatement::PreparedStatement(void* stmt) : statement(stmt) {
}
PreparedStatement::~PreparedStatement() {
OdbcCommon::freeHandle(SQL_HANDLE_STMT, statement);
}
ResultSet* PreparedStatement::executeQuery() {
SQLRETURN result = SQLExecute(statement);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to execute (query) prepared statement", result, SQL_HANDLE_STMT, statement);
return new ResultSet(statement);
}
long PreparedStatement::executeUpdate() {
SQLRETURN result;
SQLLEN count = 0; // returned e.g. on empty statement: ""
result = SQLExecute(statement);
// PostgreSQL returns SUCCESS but SQLite returns SQL_NO_DATA_FOUND:
if (OdbcCommon::isNotSuccessful(result) && result != SQL_NO_DATA_FOUND) throw SqlException(L"Unable to execute (update) prepared statement", result, SQL_HANDLE_STMT, statement);
result = SQLRowCount(statement, &count);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to get updated record count", result, SQL_HANDLE_STMT, statement);
return count;
}
void PreparedStatement::setBoolean(int parameterNumber, relpipe::reader::boolean_t value) {
booleanParameters.emplace_back(value);
// TODO: review SQL_C_TINYINT
SQLRETURN result = SQLBindParameter(statement, parameterNumber, SQL_PARAM_INPUT, SQL_C_TINYINT, SQL_INTEGER, 0, 0, &booleanParameters.back(), 0, nullptr);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to set boolean parameter in prepared statement", result, SQL_HANDLE_STMT, statement);
}
void PreparedStatement::setInteger(int parameterNumber, relpipe::reader::integer_t value) {
integerParameters.emplace_back(value);
SQLRETURN result = SQLBindParameter(statement, parameterNumber, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &integerParameters.back(), 0, nullptr);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to set integer parameter in prepared statement", result, SQL_HANDLE_STMT, statement);
}
void PreparedStatement::setString(int parameterNumber, relpipe::reader::string_t value) {
std::string valueBytes = convertor.to_bytes(value);
stringParameters.emplace_back(std::make_pair(valueBytes, valueBytes.size()));
SQLRETURN result = SQLBindParameter(statement, parameterNumber, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, (void *) stringParameters.back().first.c_str(), 0, &stringParameters.back().second);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to set string parameter in prepared statement", result, SQL_HANDLE_STMT, statement);
}
void PreparedStatement::setNull(int parameterNumber) {
throw SqlException(L"Use ODBC: setNull()");
}
void PreparedStatement::reset() {
// TODO: do also SQL_UNBIND if column binding is used
SQLRETURN result = SQLFreeStmt(statement, SQL_RESET_PARAMS);
if (OdbcCommon::isNotSuccessful(result)) throw SqlException(L"Unable to reset prepared statement", result, SQL_HANDLE_STMT, statement);
booleanParameters.clear();
integerParameters.clear();
stringParameters.clear();
}
}
}
}