Skip to content
Snippets Groups Projects
Commit 3adfeac6 authored by Steven Murray's avatar Steven Murray
Browse files

Added unit-tests for OcciRset::bind()

parent 5ad5eb78
No related branches found
No related tags found
No related merge requests found
......@@ -151,8 +151,8 @@ private:
/**
* Forward declaration of the nested class ColumnNameIdx that is intentionally
* hidden in the cpp file of the SqliteRset class. The class is hidden in
* order to enable the SqliteRset class to be used by code compiled against
* hidden in the cpp file of the OcciRset class. The class is hidden in
* order to enable the OcciRset class to be used by code compiled against
* the CXX11 ABI and used by code compiled against the pre-CXX11 ABI.
*/
class ColumnNameToIdx;
......@@ -168,8 +168,8 @@ private:
/**
* Forward declaration of the nest class TextColumnCache that is intentionally
* hidden in the cpp file of the SqliteRset class. The class is hidden in
* order to enable the SqliteRset class to be used by code compiled against
* hidden in the cpp file of the OcciRset class. The class is hidden in
* order to enable the OcciRset class to be used by code compiled against
* the CXX11 ABI and used by code compiled against the pre-CXX11 ABI.
*/
class TextColumnCache;
......
......@@ -95,9 +95,8 @@ TEST_F(cta_catalogue_OcciRsetTest, executeQuery_uint32_t) {
ASSERT_FALSE(rset->next());
}
// TODO - Implement 64-bit int test because the current code will fail
// TODO - Implement 64-bit int executeQuery test because the current code will fail
/*
TEST_F(cta_catalogue_OcciRsetTest, bind_c_string) {
using namespace cta;
using namespace cta::catalogue;
......@@ -110,12 +109,34 @@ TEST_F(cta_catalogue_OcciRsetTest, bind_c_string) {
dbLogin.database.c_str()));
const char *const sql = "SELECT DUMMY FROM DUAL WHERE DUMMY = :DUMMY";
std::unique_ptr<DbStmt> stmt(conn->createStmt(sql));
stmt->bind(":DUMMY", "X");
std::unique_ptr<DbRset> rset(stmt->executeQuery());
ASSERT_TRUE(rset->next());
const uint32_t i = rset->columnUint64("I");
ASSERT_EQ(1234, i);
std::string text(rset->columnText("DUMMY"));
ASSERT_EQ(std::string("X"), text);
ASSERT_FALSE(rset->next());
}
*/
TEST_F(cta_catalogue_OcciRsetTest, bind_uint32_t) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<OcciConn> conn(env.createConn(
dbLogin.username.c_str(),
dbLogin.password.c_str(),
dbLogin.database.c_str()));
const char *const sql = "SELECT :N AS AN_UNSIGNED_INT FROM DUAL";
std::unique_ptr<DbStmt> stmt(conn->createStmt(sql));
stmt->bind(":N", 1234);
std::unique_ptr<DbRset> rset(stmt->executeQuery());
ASSERT_TRUE(rset->next());
const uint32_t n = rset->columnUint64("AN_UNSIGNED_INT");
ASSERT_EQ(1234, n);
ASSERT_FALSE(rset->next());
}
// TODO - Implement 64-bit int bind test because the current code will fail
} // namespace unitTests
......@@ -24,11 +24,94 @@
#include "catalogue/OcciStmt.hpp"
#include <cstring>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
namespace cta {
namespace catalogue {
class OcciStmt::ParamNameToIdx {
public:
/**
* Constructor.
*
* Parses the specified SQL statement to populate an internal map from SQL
* parameter name to parameter index.
*
* @param sql The SQL statement to be parsed for SQL parameter names.
*/
ParamNameToIdx(const char *const sql) {
bool waitingForAParam = true;
std::ostringstream paramName;
unsigned int paramIdx = 1;
for(const char *ptr = sql; ; ptr++) {
if(waitingForAParam) {
if('\0' == *ptr) {
break;
} else if(':' == *ptr) {
waitingForAParam = false;
paramName << ":";
}
} else {
if(!isValidParamNameChar(*ptr)) {
if(paramName.str().empty()) {
throw std::runtime_error("Parse error: Empty SQL parameter name");
}
if(m_nameToIdx.find(paramName.str()) != m_nameToIdx.end()) {
throw std::runtime_error("Parse error: SQL parameter " + paramName.str() + " is a duplicate");
}
m_nameToIdx[paramName.str()] = paramIdx;
paramName.clear();
paramIdx++;
waitingForAParam = true;
}
if('\0' == *ptr) {
break;
}
if(':' == *ptr) {
throw std::runtime_error("Parse error: Consecutive SQL parameter names are not permitted");
} else {
paramName << *ptr;
}
}
}
}
/**
* Returns the index of teh specified SQL parameter.
*
* @param paramNAme The name of the SQL parameter.
* @return The index of the SQL parameter.
*/
unsigned int getIdx(const char *const paramName) const {
auto itor = m_nameToIdx.find(paramName);
if(itor == m_nameToIdx.end()) {
throw std::runtime_error(std::string(__FUNCTION__) + " failed: The SQL parameter " + paramName +
" does not exist");
}
return itor->second;
}
private:
/**
* Map from SQL parameter name to parameter index.
*/
std::map<std::string, unsigned int> m_nameToIdx;
bool isValidParamNameChar(const char c) {
return ('0' <= c && c <= '9') ||
('A' <= c && c <= 'Z') ||
('a' <= c && c <= 'z') ||
c == '_';
}
};
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
......@@ -48,6 +131,8 @@ OcciStmt::OcciStmt(const char *const sql, OcciConn &conn, oracle::occi::Statemen
m_sql.reset(new char[sqlLen + 1]);
std::memcpy(m_sql.get(), sql, sqlLen);
m_sql[sqlLen] = '\0';
m_paramNameToIdx.reset(new ParamNameToIdx(sql));
}
//------------------------------------------------------------------------------
......@@ -84,16 +169,24 @@ const char *OcciStmt::getSql() const {
// bind
//------------------------------------------------------------------------------
void OcciStmt::bind(const char *paramName, const uint64_t paramValue) {
std::runtime_error ex(std::string(__FUNCTION__) + " is not implemented");
throw ex;
try {
const unsigned paramIdx = m_paramNameToIdx->getIdx(paramName);
m_stmt->setUInt(paramIdx, paramValue);
} catch(std::exception &ne) {
throw std::runtime_error(std::string(__FUNCTION__) + " failed: " + ne.what());
}
}
//------------------------------------------------------------------------------
// bind
//------------------------------------------------------------------------------
void OcciStmt::bind(const char *paramName, const char *paramValue) {
std::runtime_error ex(std::string(__FUNCTION__) + " is not implemented");
throw ex;
try {
const unsigned paramIdx = m_paramNameToIdx->getIdx(paramName);
m_stmt->setString(paramIdx, paramValue);
} catch(std::exception &ne) {
throw std::runtime_error(std::string(__FUNCTION__) + " failed: " + ne.what());
}
}
//------------------------------------------------------------------------------
......@@ -105,8 +198,7 @@ DbRset *OcciStmt::executeQuery() {
try {
return new OcciRset(*this, m_stmt->executeQuery());
} catch(std::exception &ne) {
throw std::runtime_error(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() +
": " + ne.what());
throw std::runtime_error(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + ": " + ne.what());
}
}
......
......@@ -139,6 +139,23 @@ private:
*/
std::unique_ptr<char[]> m_sql;
/**
* Forward declaration of the nested class ParamNameToIdx that is intentionally
* hidden in the cpp file of the OcciStmt class. The class is hidden in
* order to enable the OcciStmt class to be used by code compiled against
* the CXX11 ABI and used by code compiled against the pre-CXX11 ABI.
*/
class ParamNameToIdx;
/**
* Map from SQL parameter name to parameter index.
*
* Please note that the type of the map is intentionally forward declared in
* order to avoid std::string being used. This is to aid with working with
* pre and post CXX11 ABIs.
*/
std::unique_ptr<ParamNameToIdx> m_paramNameToIdx;
/**
* The database connection.
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment