src/PreparedStatement.cpp
author František Kučera <franta-hg@frantovo.cz>
Mon, 26 Oct 2020 00:00:35 +0100
branchv_0
changeset 53 cc6ffeba0fe5
parent 48 c83119110c7b
permissions -rw-r--r--
fix: support bigger numbers, SQL_BIGINT there was an overflow and some values resulted into negative ones. TODO: there is still a problem with big negative numbers – this needs to be addressed in relpipe-lib-writer/relpipe-lib-reader

/**
 * 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);
	SQLRETURN result = SQLBindParameter(statement, parameterNumber, SQL_PARAM_INPUT, SQL_C_BIT, 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(std::to_string(value));
	SQLRETURN result = SQLBindParameter(statement, parameterNumber, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_BIGINT, 0, 0, (void *) integerParameters.back().c_str(), 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();
}

}
}
}