Commit 3a431e67 authored by Steven Murray's avatar Steven Murray
Browse files

Added Stmt::bindUint32()

parent be65b928
......@@ -102,6 +102,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_trailer.sql
| sed 's/UINT16TYPE/INTEGER/g'
| sed 's/UINT32TYPE/INTEGER/g'
| sed 's/UINT64TYPE/INTEGER/g'
| sed 's/NUMERIC\([^\)]*\)/INTEGER/g'
| sed 's/CHECKSUM_BLOB_TYPE/BLOB\(200\)/g'
......@@ -111,6 +112,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
${CMAKE_CURRENT_SOURCE_DIR}/mysql_catalogue_schema_trailer.sql
| sed 's/UINT16TYPE/SMALLINT_UNSIGNED/g'
| sed 's/UINT32TYPE/INT_UNSIGNED/g'
| sed 's/UINT64TYPE/BIGINT UNSIGNED/g'
| sed 's/NUMERIC\([^\)]*\)/BIGINT UNSIGNED/g'
| sed 's/CHECKSUM_BLOB_TYPE/VARBINARY\(200\)/g'
......@@ -120,6 +122,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
${CMAKE_CURRENT_SOURCE_DIR}/oracle_catalogue_schema_trailer.sql
| sed 's/UINT16TYPE/NUMERIC(5, 0)/g'
| sed 's/UINT32TYPE/NUMERIC(10, 0)/g'
| sed 's/UINT64TYPE/NUMERIC(20, 0)/g'
| sed 's/VARCHAR/VARCHAR2/g'
| sed 's/CHECKSUM_BLOB_TYPE/RAW\(200\)/g'
......@@ -129,6 +132,7 @@ add_custom_command (OUTPUT sqlite_catalogue_schema.sql mysql_catalogue_schema.sq
${CMAKE_CURRENT_SOURCE_DIR}/common_catalogue_schema.sql
${CMAKE_CURRENT_SOURCE_DIR}/postgres_catalogue_schema_trailer.sql
| sed 's/UINT16TYPE/NUMERIC(5, 0)/g'
| sed 's/UINT32TYPE/NUMERIC(10, 0)/g'
| sed 's/UINT64TYPE/NUMERIC(20, 0)/g'
| sed 's/CHECKSUM_BLOB_TYPE/BYTEA/g'
> postgres_catalogue_schema.sql
......
......@@ -269,6 +269,21 @@ optional<uint16_t> Rset::columnOptionalUint16(const std::string &colName) const
}
}
//------------------------------------------------------------------------------
// columnOptionalUint32
//------------------------------------------------------------------------------
optional<uint32_t> Rset::columnOptionalUint32(const std::string &colName) const {
try {
if(nullptr == m_impl) {
throw InvalidResultSet("This result set is invalid");
}
return m_impl->columnOptionalUint32(colName);
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// columnOptionalUint64
//------------------------------------------------------------------------------
......
......@@ -191,6 +191,17 @@ public:
*/
optional<uint16_t> columnOptionalUint16(const std::string &colName) const;
/**
* Returns the value of the specified column as an integer.
*
* 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.
* @throw InvalidResultSet if the result is invalid.
*/
optional<uint32_t> columnOptionalUint32(const std::string &colName) const;
/**
* Returns the value of the specified column as an integer.
*
......
......@@ -148,6 +148,38 @@ void Stmt::bindOptionalUint16(const std::string &paramName, const optional<uint1
}
}
//-----------------------------------------------------------------------------
// bindUint32
//-----------------------------------------------------------------------------
void Stmt::bindUint32(const std::string &paramName, const uint32_t paramValue) {
try {
if(nullptr != m_stmt) {
return m_stmt->bindUint32(paramName, paramValue);
} else {
throw exception::Exception("Stmt does not contain a cached statement");
}
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//-----------------------------------------------------------------------------
// bindOptionalUint32
//-----------------------------------------------------------------------------
void Stmt::bindOptionalUint32(const std::string &paramName, const optional<uint32_t> &paramValue) {
try {
if(nullptr != m_stmt) {
return m_stmt->bindOptionalUint32(paramName, paramValue);
} else {
throw exception::Exception("Stmt does not contain a cached statement");
}
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//-----------------------------------------------------------------------------
// bindUint64
//-----------------------------------------------------------------------------
......
......@@ -119,6 +119,22 @@ public:
*/
void bindOptionalUint16(const std::string &paramName, const optional<uint16_t> &paramValue);
/**
* Binds an SQL parameter.
*
* @param paramName The name of the parameter.
* @param paramValue The value to be bound.
*/
void bindUint32(const std::string &paramName, const uint32_t paramValue);
/**
* Binds an SQL parameter.
*
* @param paramName The name of the parameter.
* @param paramValue The value to be bound.
*/
void bindOptionalUint32(const std::string &paramName, const optional<uint32_t> &paramValue);
/**
* Binds an SQL parameter.
*
......
......@@ -67,6 +67,7 @@ std::string cta_rdbms_StmtTest::getCreateStmtTestTableSql() {
" ID UINT64TYPE CONSTRAINT STMT_TEST_ID_NN NOT NULL," "\n"
" DOUBLE_COL FLOAT," "\n"
" UINT16_COL UINT16TYPE," "\n"
" UINT32_COL UINT32TYPE," "\n"
" UINT64_COL UINT64TYPE," "\n"
" STRING_COL VARCHAR(100)," "\n"
" BOOL_COL CHAR(1) DEFAULT '0'," "\n"
......@@ -79,19 +80,23 @@ std::string cta_rdbms_StmtTest::getCreateStmtTestTableSql() {
break;
case Login::DBTYPE_ORACLE:
utils::searchAndReplace(sql, "UINT16TYPE", "NUMERIC(5, 0)");
utils::searchAndReplace(sql, "UINT32TYPE", "NUMERIC(10, 0)");
utils::searchAndReplace(sql, "UINT64TYPE", "NUMERIC(20, 0)");
utils::searchAndReplace(sql, "VARCHAR", "VARCHAR2");
break;
case Login::DBTYPE_SQLITE:
utils::searchAndReplace(sql, "UINT16TYPE", "INTEGER");
utils::searchAndReplace(sql, "UINT32TYPE", "INTEGER");
utils::searchAndReplace(sql, "UINT64TYPE", "INTEGER");
break;
case Login::DBTYPE_MYSQL:
utils::searchAndReplace(sql, "UINT16TYPE", "SMALLINT UNSIGNED");
utils::searchAndReplace(sql, "UINT32TYPE", "INT UNSIGNED");
utils::searchAndReplace(sql, "UINT64TYPE", "BIGINT UNSIGNED");
break;
case Login::DBTYPE_POSTGRESQL:
utils::searchAndReplace(sql, "UINT16TYPE", "NUMERIC(5, 0)");
utils::searchAndReplace(sql, "UINT32TYPE", "NUMERIC(10, 0)");
utils::searchAndReplace(sql, "UINT64TYPE", "NUMERIC(20, 0)");
break;
case Login::DBTYPE_NONE:
......@@ -326,6 +331,164 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindOptionalUint16) {
}
}
TEST_P(cta_rdbms_StmtTest, insert_with_bindUint32) {
using namespace cta::rdbms;
const uint32_t insertValue = 1234;
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT32_COL) " "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT32_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindUint32(":UINT32_COL", insertValue);
stmt.executeNonQuery();
}
// Select the row back from the table
{
const char *const sql =
"SELECT" "\n"
" UINT32_COL AS UINT32_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
const auto selectValue = rset.columnOptionalUint32("UINT32_COL");
ASSERT_TRUE((bool)selectValue);
ASSERT_EQ(insertValue,selectValue.value());
ASSERT_FALSE(rset.next());
}
}
TEST_P(cta_rdbms_StmtTest, insert_with_bindUint32_2_pow_32_minus_1) {
using namespace cta::rdbms;
const uint32_t insertValue = 4294967295U;
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT32_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT32_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindUint32(":UINT32_COL", insertValue);
stmt.executeNonQuery();
}
// Select the row back from the table
{
const char *const sql =
"SELECT" "\n"
" UINT32_COL AS UINT32_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
const auto selectValue = rset.columnOptionalUint32("UINT32_COL");
ASSERT_TRUE((bool)selectValue);
ASSERT_EQ(insertValue,selectValue.value());
ASSERT_FALSE(rset.next());
}
}
TEST_P(cta_rdbms_StmtTest, insert_with_bindOptionalUint32_null) {
using namespace cta::rdbms;
const cta::optional<uint32_t> insertValue; // Null value
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT32_COL) " "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT32_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindOptionalUint32(":UINT32_COL", insertValue);
stmt.executeNonQuery();
}
// Select the row back from the table
{
const char *const sql =
"SELECT" "\n"
" UINT32_COL AS UINT32_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
const auto selectValue = rset.columnOptionalUint32("UINT32_COL");
ASSERT_FALSE((bool)selectValue);
ASSERT_FALSE(rset.next());
}
}
TEST_P(cta_rdbms_StmtTest, insert_with_bindOptionalUint32) {
using namespace cta::rdbms;
const cta::optional<uint32_t> insertValue = 1234;
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT32_COL) " "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT32_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindOptionalUint32(":UINT32_COL", insertValue);
stmt.executeNonQuery();
}
// Select the row back from the table
{
const char *const sql =
"SELECT" "\n"
" UINT32_COL AS UINT32_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
const auto selectValue = rset.columnOptionalUint32("UINT32_COL");
ASSERT_TRUE((bool)selectValue);
ASSERT_EQ(insertValue,selectValue.value());
ASSERT_FALSE(rset.next());
}
}
TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64) {
using namespace cta::rdbms;
......
......@@ -99,6 +99,7 @@ class Mysql {
enum buffer_types {
placeholder_uint16,
placeholder_uint32,
placeholder_uint64,
placeholder_string,
placeholder_double,
......@@ -115,6 +116,7 @@ class Mysql {
virtual my_bool* get_error() { return &error; }
// following is to access data
virtual uint16_t get_uint16() = 0;
virtual uint32_t get_uint32() = 0;
virtual uint64_t get_uint64() = 0;
virtual std::string get_string() = 0;
virtual double get_double() = 0;
......@@ -156,6 +158,72 @@ class Mysql {
return val;
}
uint32_t get_uint32() override {
return val;
}
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() override {
return false;
}
};
struct Placeholder_Uint32: Placeholder {
uint32_t val;
Placeholder_Uint32()
: 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_uint32;
}
void* get_buffer() override {
return &val;
}
unsigned long get_buffer_length() override {
return sizeof(uint32_t);
}
bool get_is_unsigned() override {
return true;
}
uint16_t get_uint16() override {
if(std::numeric_limits<uint16_t>::max() < val) {
std::ostringstream msg;
msg << "Cannot convert uint32 to uint16: Overflow: uint32=" << val <<
" maxUint16=" << std::numeric_limits<uint16_t>::max();
throw exception::Exception(msg.str());
}
return val;
}
uint32_t get_uint32() override {
return val;
}
uint64_t get_uint64() override {
return val;
}
......@@ -214,6 +282,16 @@ class Mysql {
return val;
}
uint32_t get_uint32() override {
if(std::numeric_limits<uint32_t>::max() < val) {
std::ostringstream msg;
msg << "Cannot convert uint64 to uint32: Overflow: uint64=" << val <<
" maxUint32=" << std::numeric_limits<uint32_t>::max();
throw exception::Exception(msg.str());
}
return val;
}
uint64_t get_uint64() override {
return val;
}
......@@ -282,6 +360,17 @@ class Mysql {
return i;
}
uint32_t get_uint32() override {
const uint64_t i = std::stoll(val);
if(std::numeric_limits<uint32_t>::max() > i) {
std::ostringstream msg;
msg << "Failed to convert string to uint32: Overflow: string=" << val << " maxUint32=" <<
std::numeric_limits<uint32_t>::max();
throw exception::Exception(msg.str());
}
return i;
}
// note: allow users try to convert from string to int,
// but users need to catch the exception.
uint64_t get_uint64() override {
......@@ -340,6 +429,10 @@ class Mysql {
throw val;
}
uint32_t get_uint32() override {
throw val;
}
uint64_t get_uint64() override {
return val;
}
......
......@@ -138,6 +138,25 @@ optional<uint16_t> MysqlRset::columnOptionalUint16(const std::string &colName) c
return optional<uint16_t>(holder->get_uint16());
}
//------------------------------------------------------------------------------
// columnOptionalUint32
//------------------------------------------------------------------------------
optional<uint32_t> MysqlRset::columnOptionalUint32(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<uint32_t>(holder->get_uint32());
}
//------------------------------------------------------------------------------
// columnOptionalUint64
//------------------------------------------------------------------------------
......
......@@ -104,6 +104,16 @@ public:
*/
optional<uint16_t> columnOptionalUint16(const std::string &colName) const override;
/**
* Returns the value of the specified column as an integer.
*
* 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<uint32_t> columnOptionalUint32(const std::string &colName) const override;
/**
* Returns the value of the specified column as an integer.
*
......
......@@ -209,6 +209,52 @@ void MysqlStmt::bindOptionalUint16(const std::string &paramName, const optional<
}
// 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());
}
}
//------------------------------------------------------------------------------
// bindUint32
//------------------------------------------------------------------------------
void MysqlStmt::bindUint32(const std::string &paramName, const uint32_t paramValue) {
try {
bindOptionalUint32(paramName, paramValue);
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// bindOptionalUint32
//------------------------------------------------------------------------------
void MysqlStmt::bindOptionalUint32(const std::string &paramName, const optional<uint32_t> &paramValue) {
try {
const unsigned int paramIdx = getParamIdx(paramName); // starts from 1.
const unsigned int idx = paramIdx - 1;
Mysql::Placeholder_Uint32* holder = dynamic_cast<Mysql::Placeholder_Uint32*>(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_Uint16. " );
}
if (!holder) {
holder = new Mysql::Placeholder_Uint32();
holder->idx = idx;
holder->length = sizeof(uint32_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 " +
......@@ -493,6 +539,9 @@ bool MysqlStmt::do_bind() {
case Mysql::Placeholder::placeholder_uint16:
bind[idx].buffer_type = MYSQL_TYPE_SHORT;
break;
case Mysql::Placeholder::placeholder_uint32:
bind[idx].buffer_type = MYSQL_TYPE_LONG;
break;
case Mysql::Placeholder::placeholder_uint64:
bind[idx].buffer_type = MYSQL_TYPE_LONGLONG;
break;
......
......@@ -100,6 +100,22 @@ public:
*/
void bindOptionalUint16(const std::string &paramName, const optional<uint16_t> &paramValue) override;
/**
* Binds an SQL parameter.
*
* @param paramName The name of the parameter.
* @param paramValue The value to be bound.
*/
void bindUint32(const std::string &paramName, const uint32_t paramValue) override;
/**
* Binds an SQL parameter.
*
* @param paramName The name of the parameter.
* @param paramValue The value to be bound.
*/
void bindOptionalUint32(const std::string &paramName, const optional<uint32_t> &paramValue) override;
/**
* Binds an SQL parameter.
*
......
......@@ -188,6 +188,32 @@ optional<uint16_t> OcciRset::columnOptionalUint16(const std::string &colName) co
}
}
//------------------------------------------------------------------------------
// columnOptionalUint32
//------------------------------------------------------------------------------
optional<uint32_t> OcciRset::columnOptionalUint32(const std::string &colName) const {
try {
threading::Mutex locker(m_mutex);
const int colIdx = m_colNameToIdx.getIdx(colName);
const std::string stringValue = m_rset->getString(colIdx);
if(stringValue.empty()) {
return nullopt;
}
if(!utils::isValidUInt(stringValue)) {
throw exception::Exception(std::string("Column ") + colName + " contains the value " + stringValue +
" which is not a valid unsigned integer");
}