Commit 00d6de65 authored by Steven Murray's avatar Steven Murray
Browse files

Added rdbms::Stmt::bindDouble() and rdbms::Rset::columnDouble()

parent c27bff0d
......@@ -279,9 +279,11 @@ Unit tests and system tests with virtual tape drives
%{_libdir}/libctacommonunittests.so*
%{_libdir}/libctadbconfigcatalogueunittests.so*
%{_libdir}/libctadbconfigconnunittests.so*
%{_libdir}/libctadbconfigstmtunittests.so*
%{_libdir}/libctaexceptionunittests.so*
%{_libdir}/libctainmemorycatalogueunittests.so*
%{_libdir}/libctainmemoryconnunittests.so*
%{_libdir}/libctainmemorystmtunittests.so*
%{_libdir}/libctaobjectstoreunittests.so*
%{_libdir}/libctardbmsunittests.so*
%{_libdir}/libctardbmswrapperunittests.so*
......
......@@ -43,7 +43,6 @@ install (TARGETS ctardbms DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
set(RDBMS_UNIT_TESTS_LIB_SRC_FILES
ConnPoolTest.cpp
ConnTest.cpp
LoginTest.cpp
RdbmsTest.cpp
StmtPoolTest.cpp)
......@@ -72,6 +71,20 @@ target_link_libraries (ctainmemoryconnunittests
install (TARGETS ctainmemoryconnunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
set(IN_MEMORY_STMT_UNIT_TESTS_LIB_SRC_FILES
StmtTest.cpp
InMemoryVersionOfStmtTest.cpp)
add_library (ctainmemorystmtunittests SHARED
${IN_MEMORY_STMT_UNIT_TESTS_LIB_SRC_FILES})
set_property(TARGET ctainmemorystmtunittests PROPERTY SOVERSION "${CTA_SOVERSION}")
set_property(TARGET ctainmemorystmtunittests PROPERTY VERSION "${CTA_LIBVERSION}")
target_link_libraries (ctainmemorystmtunittests
ctardbms)
install (TARGETS ctainmemorystmtunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
set (DBCONFIG_CONN_UNIT_TESTS_LIB_SRC_FILES
ConnTest.cpp
DbConfigVersionOfConnTest.cpp)
......@@ -85,3 +98,17 @@ target_link_libraries (ctadbconfigconnunittests
ctardbms)
install (TARGETS ctadbconfigconnunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
set (DBCONFIG_STMT_UNIT_TESTS_LIB_SRC_FILES
StmtTest.cpp
DbConfigVersionOfStmtTest.cpp)
add_library (ctadbconfigstmtunittests SHARED
${DBCONFIG_STMT_UNIT_TESTS_LIB_SRC_FILES})
set_property(TARGET ctadbconfigstmtunittests PROPERTY SOVERSION "${CTA_SOVERSION}")
set_property(TARGET ctadbconfigstmtunittests PROPERTY VERSION "${CTA_LIBVERSION}")
target_link_libraries (ctadbconfigstmtunittests
ctardbms)
install (TARGETS ctadbconfigstmtunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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 "rdbms/StmtTest.hpp"
#include "tests/RdbmsUnitTestsCmdLineArgs.hpp"
namespace {
/**
* Creates Login objects from a database configuration file passed on the
* command-line to the unit-tests program.
*/
class DbConfigFileVersionOfStmtTestLoginFactory: public cta::rdbms::LoginFactory {
public:
/**
* Destructor.
*/
virtual ~DbConfigFileVersionOfStmtTestLoginFactory() {
}
/**
* Returns a newly created Login object.
*
* @return A newly created Login object.
*/
virtual cta::rdbms::Login create() {
return cta::rdbms::Login::parseFile(g_cmdLineArgs.dbConfigPath);
}
}; // class OracleLoginFactory
DbConfigFileVersionOfStmtTestLoginFactory g_dbConfigFileVersionOfStmtTestLoginFactory;
} // anonymous namespace
namespace unitTests {
INSTANTIATE_TEST_CASE_P(DbConfigFile, cta_rdbms_StmtTest,
::testing::Values(dynamic_cast<cta::rdbms::LoginFactory*>(&g_dbConfigFileVersionOfStmtTestLoginFactory)));
} // namespace unitTests
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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 "rdbms/StmtTest.hpp"
namespace unitTests {
namespace {
/**
* Creates Login objects for in-memory catalogue databases.
*/
class RdbmsInMemoryLoginFactory: public cta::rdbms::LoginFactory {
public:
/**
* Destructor.
*/
virtual ~RdbmsInMemoryLoginFactory() {
}
/**
* Returns a newly created Login object.
*
* @return A newly created Login object.
*/
virtual cta::rdbms::Login create() {
return cta::rdbms::Login(cta::rdbms::Login::DBTYPE_IN_MEMORY, "", "", "", "", 0);
}
}; // class RdbmsInMemoryLoginFactory
RdbmsInMemoryLoginFactory g_inMemoryLoginFactory;
} // anonymous namespace
INSTANTIATE_TEST_CASE_P(InMemory, cta_rdbms_StmtTest,
::testing::Values(dynamic_cast<cta::rdbms::LoginFactory*>(&g_inMemoryLoginFactory)));
} // namespace unitTests
......@@ -192,5 +192,36 @@ optional<uint64_t> Rset::columnOptionalUint64(const std::string &colName) const
return m_impl->columnOptionalUint64(colName);
}
//------------------------------------------------------------------------------
// columnDouble
//------------------------------------------------------------------------------
double Rset::columnDouble(const std::string &colName) const {
try {
if(nullptr == m_impl) {
throw exception::Exception("This result set is invalid");
}
const optional<double> col = columnOptionalDouble(colName);
if(col) {
return col.value();
} else {
throw NullDbValue(std::string("Database column ") + colName + " contains a null value");
}
} catch(exception::Exception &ex) {
throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
}
}
//------------------------------------------------------------------------------
// columnOptionalDouble
//------------------------------------------------------------------------------
optional<double> Rset::columnOptionalDouble(const std::string &colName) const {
if(nullptr == m_impl) {
throw exception::Exception(std::string(__FUNCTION__) + " failed: "
"This result set is invalid");
}
return m_impl->columnOptionalDouble(colName);
}
} // namespace rdbms
} // namespace cta
......@@ -166,6 +166,27 @@ public:
*/
optional<bool> columnOptionalBool(const std::string &colName) const;
/**
* Returns the value of the specified column as a double.
*
* This method will throw an exception if the value of the specified column
* is nullptr.
*
* @param colName The name of the column.
* @return The value of the specified column.
*/
double columnDouble(const std::string &colName) const;
/**
* Returns the value of the specified column as a double.
*
* This method will return a null column value as an optional with no value.
*
* @param colName The name of the column.
* @return The value of the specified column.
*/
optional<double> columnOptionalDouble(const std::string &colName) const;
private:
/**
......
......@@ -128,6 +128,28 @@ void Stmt::bindOptionalUint64(const std::string &paramName, const optional<uint6
}
}
//-----------------------------------------------------------------------------
// bindDouble
//-----------------------------------------------------------------------------
void Stmt::bindDouble(const std::string &paramName, const double paramValue) {
if(nullptr != m_stmt) {
return m_stmt->bindDouble(paramName, paramValue);
} else {
throw exception::Exception(std::string(__FUNCTION__) + " failed: Stmt does not contain a cached statement");
}
}
//-----------------------------------------------------------------------------
// bindOptionalDouble
//-----------------------------------------------------------------------------
void Stmt::bindOptionalDouble(const std::string &paramName, const optional<double> &paramValue) {
if(nullptr != m_stmt) {
return m_stmt->bindOptionalDouble(paramName, paramValue);
} else {
throw exception::Exception(std::string(__FUNCTION__) + " failed: Stmt does not contain a cached statement");
}
}
//-----------------------------------------------------------------------------
// bindBool
//-----------------------------------------------------------------------------
......
......@@ -119,6 +119,22 @@ public:
*/
void bindOptionalUint64(const std::string &paramName, const optional<uint64_t> &paramValue);
/**
* Binds an SQL parameter.
*
* @param paramName The name of the parameter.
* @param paramValue The value to be bound.
*/
void bindDouble(const std::string &paramName, const double paramValue);
/**
* Binds an SQL parameter.
*
* @param paramName The name of the parameter.
* @param paramValue The value to be bound.
*/
void bindOptionalDouble(const std::string &paramName, const optional<double> &paramValue);
/**
* Binds an SQL parameter.
*
......
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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 "common/exception/Exception.hpp"
#include "common/make_unique.hpp"
#include "rdbms/ConnPool.hpp"
#include "rdbms/StmtTest.hpp"
#include <gtest/gtest.h>
namespace unitTests {
//------------------------------------------------------------------------------
// Setup
//------------------------------------------------------------------------------
void cta_rdbms_StmtTest::SetUp() {
using namespace cta::rdbms;
if(!m_connPool) {
const cta::rdbms::Login login = GetParam()->create();
const uint64_t maxNbConns = 1;
m_connPool = cta::make_unique<ConnPool>(login, maxNbConns);
}
m_conn = m_connPool->getConn();
ASSERT_EQ(AutocommitMode::AUTOCOMMIT_ON, m_conn.getAutocommitMode());
try {
m_conn.executeNonQuery("DROP TABLE STMT_TEST");
} catch(...) {
// Do nothing
}
m_conn.executeNonQuery("CREATE TABLE STMT_TEST(DOUBLE_COL FLOAT)");
}
//------------------------------------------------------------------------------
// TearDown
//------------------------------------------------------------------------------
void cta_rdbms_StmtTest::TearDown() {
using namespace cta::rdbms;
try {
m_conn.executeNonQuery("DROP TABLE STMT_TEST");
} catch(...) {
// Do nothing
}
}
TEST_P(cta_rdbms_StmtTest, insert_with_bindDouble) {
using namespace cta::rdbms;
const double insertValue = 1.234;
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"DOUBLE_COL) "
"VALUES("
":DOUBLE_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindDouble(":DOUBLE_COL", insertValue);
stmt.executeNonQuery();
}
// Select the row back from the table
{
const char *const sql =
"SELECT "
"DOUBLE_COL AS DOUBLE_COL "
"FROM "
"STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
const auto selectValue = rset.columnOptionalDouble("DOUBLE_COL");
ASSERT_TRUE((bool)selectValue);
const double diff = insertValue - selectValue.value();
ASSERT_TRUE(0.000001 > diff);
ASSERT_FALSE(rset.next());
}
}
} // namespace unitTests
/*
* The CERN Tape Archive (CTA) project
* Copyright (C) 2015 CERN
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
#pragma once
#include "rdbms/LoginFactory.hpp"
#include "rdbms/Conn.hpp"
#include <gtest/gtest.h>
#include <memory>
namespace unitTests {
class cta_rdbms_StmtTest : public ::testing::TestWithParam<cta::rdbms::LoginFactory*> {
protected:
/**
* The database connection pool.
*/
std::unique_ptr<cta::rdbms::ConnPool> m_connPool;
/**
* The database connection.
*/
cta::rdbms::Conn m_conn;
virtual void SetUp();
virtual void TearDown();
}; // cta_rdbms_StmtTest
} // namespace unitTests
......@@ -99,6 +99,7 @@ class Mysql {
enum buffer_types {
placeholder_uint64,
placeholder_string,
placeholder_double,
};
virtual std::string show() = 0;
......@@ -113,6 +114,7 @@ class Mysql {
// following is to access data
virtual uint64_t get_uint64() = 0;
virtual std::string get_string() = 0;
virtual double get_double() = 0;
// helper
virtual bool reset() = 0;
};
......@@ -155,6 +157,10 @@ class Mysql {
return std::to_string(val);
}
double get_double() {
return val;
}
bool reset() {
return false;
}
......@@ -210,6 +216,12 @@ class Mysql {
return std::string(val, val+*get_length());
}
// note: allow users try to convert from string to int,
// but users need to catch the exception.
double get_double() {
return std::stod(val);
}
bool reset() {
memset(val, 0, buf_sz);
......@@ -218,6 +230,54 @@ class Mysql {
};
struct Placeholder_Double: Placeholder {
double val;
Placeholder_Double()
: Placeholder(), val(0) {
}
std::string show() override {
std::stringstream ss;
ss << "['" << idx << "' '" << is_null << "' '" << length << "' '" << val << "' '" << get_buffer_length() << "']";
return ss.str();
}
buffer_types get_buffer_type() override {
return placeholder_double;
}
void* get_buffer() override {
return &val;
}
unsigned long get_buffer_length() override {
return sizeof(double);
}
bool get_is_unsigned() override {
return true;
}
uint64_t get_uint64() override {
return val;
}
std::string get_string() override {
return std::to_string(val);
}
double get_double() override {
return val;
}
bool reset() {
return false;
}
};
struct FieldsInfo {
FieldsInfo(MYSQL_RES* result_metadata);
......
......@@ -134,6 +134,25 @@ optional<uint64_t> MysqlRset::columnOptionalUint64(const std::string &colName) c
return optional<uint64_t>(holder->get_uint64());
}
//------------------------------------------------------------------------------
// columnOptionalDouble
//------------------------------------------------------------------------------
optional<double> MysqlRset::columnOptionalDouble(const std::string &colName) const {
if (not m_fields.exists(colName)) {
throw exception::Exception(std::string(__FUNCTION__) + " column does not exist: " + colName);
return nullopt;
}
Mysql::Placeholder* holder = m_stmt.columnHolder(colName); // m_holders[idx];
// the value can be null
if (holder->get_is_null() and *holder->get_is_null()) {
return nullopt;
}
return optional<double>(holder->get_double());
}
} // namespace wrapper
} // namespace rdbms
} // namespace cta
......@@ -96,6 +96,16 @@ public:
*/
optional<uint64_t> columnOptionalUint64(const std::string &colName) const override;
/**
* Returns the value of the specified column as a double.
*
* This method will return a null column value as an optional with no value.
*
* @param colName The name of the column.
* @return The value of the specified column.
*/
optional<double> columnOptionalDouble(const std::string &colName) const override;
private:
/**
......
......@@ -189,14 +189,59 @@ void MysqlStmt::bindOptionalUint64(const std::string &paramName, const optional<
const unsigned int paramIdx = getParamIdx(paramName); // starts from 1.
const unsigned int idx = paramIdx - 1;
Mysql::Placeholder_Uint64* holder = dynamic_cast<Mysql::Placeholder_Uint64*>(m_placeholder[idx]);
Mysql::Placeholder_Double* holder = dynamic_cast<Mysql::Placeholder_Double*>(m_placeholder[idx]);
// if already exists, try to reuse
if (!holder and m_placeholder[idx]) {
throw exception::Exception(std::string(__FUNCTION__) + " can't cast from Placeholder to Placeholder_Uint64. " );
}
if (!holder) {
holder = new Mysql::Placeholder_Uint64();
holder = new Mysql::Placeholder_Double();
holder->idx = idx;
holder->length = sizeof(uint64_t);
}
if (paramValue) {
holder->val = paramValue.value();
} else {
holder->is_null = true;
}
// delete m_placeholder[idx]; // remove the previous placeholder
m_placeholder[idx] = holder;
} catch(exception::Exception &ex) {
throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " +
getSqlForException() + ": " + ex.getMessage().str());
}
}
//------------------------------------------------------------------------------
// bindDouble
//------------------------------------------------------------------------------
void MysqlStmt::bindDouble(const std::string &paramName, const double paramValue) {
try {
bindOptionalDouble(paramName, paramValue);
} catch(exception::Exception &ex) {
throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
}
}
//------------------------------------------------------------------------------