Commit 1015562f authored by Steven Murray's avatar Steven Murray
Browse files

Added unit-test insert_same_primary_twice

parent b22d8865
......@@ -84,6 +84,7 @@ set (COMMON_LIB_SRC_FILES
exception/DatabaseCheckConstraintError.cpp
exception/DatabaseConstraintError.cpp
exception/DatabasePrimaryKeyError.cpp
exception/DatabaseUniqueError.cpp
exception/DismountFailed.cpp
exception/ForceDismountFailed.cpp
exception/Errnum.cpp
......
/*
* 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/DatabaseUniqueError.hpp"
namespace cta {
namespace exception {
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
DatabaseUniqueError::DatabaseUniqueError(const std::string &context, const bool embedBacktrace):
DatabaseConstraintError(context, embedBacktrace) {
}
//------------------------------------------------------------------------------
// destructor
//------------------------------------------------------------------------------
DatabaseUniqueError::~DatabaseUniqueError() noexcept {
}
} // namespace exception
} // namespace cta
/*
* 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 "common/exception/DatabaseConstraintError.hpp"
#include <string>
namespace cta {
namespace exception {
/**
* A database constraint has been violated.
*/
class DatabaseUniqueError : public DatabaseConstraintError {
public:
/**
* Constructor.
*
* @param context optional context string added to the message
* at initialisation time.
* @param embedBacktrace whether to embed a backtrace of where the
* exception was throw in the message
*/
DatabaseUniqueError(const std::string &context = "", const bool embedBacktrace = true);
/**
* Empty Destructor, explicitely non-throwing (needed for std::exception
* inheritance)
*/
~DatabaseUniqueError() noexcept override;
}; // class DatabaseUniqueError
} // namespace exception
} // namespace cta
......@@ -18,6 +18,7 @@
#include "common/exception/DatabaseCheckConstraintError.hpp"
#include "common/exception/DatabasePrimaryKeyError.hpp"
#include "common/exception/DatabaseUniqueError.hpp"
#include "common/exception/Exception.hpp"
#include "common/make_unique.hpp"
#include "common/utils/utils.hpp"
......@@ -63,10 +64,12 @@ std::string cta_rdbms_StmtTest::getCreateStmtTestTableSql() {
try {
std::string sql =
"CREATE TABLE STMT_TEST(" "\n"
" ID UINT64TYPE CONSTRAINT STMT_TEST_ID_NN NOT NULL," "\n"
" DOUBLE_COL FLOAT," "\n"
" UINT64_COL UINT64TYPE," "\n"
" STRING_COL VARCHAR(100)," "\n"
" BOOL_COL CHAR(1) DEFAULT '0'," "\n"
" CONSTRAINT STMT_TEST_PK PRIMARY KEY(ID)," "\n"
" CONSTRAINT BOOL_COL_BOOL_CK CHECK(BOOL_COL IN ('0', '1'))" "\n"
")";
......@@ -127,10 +130,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindDouble) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"DOUBLE_COL) "
"VALUES("
":DOUBLE_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" DOUBLE_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :DOUBLE_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindDouble(":DOUBLE_COL", insertValue);
stmt.executeNonQuery();
......@@ -139,10 +144,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindDouble) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"DOUBLE_COL AS DOUBLE_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" DOUBLE_COL AS DOUBLE_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -166,10 +171,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"UINT64_COL) "
"VALUES("
":UINT64_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT64_COL) " "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT64_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindDouble(":UINT64_COL", insertValue);
stmt.executeNonQuery();
......@@ -178,10 +185,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"UINT64_COL AS UINT64_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" UINT64_COL AS UINT64_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -204,10 +211,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64_2_pow_64_minus_1) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"UINT64_COL) "
"VALUES("
":UINT64_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT64_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT64_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindUint64(":UINT64_COL", insertValue);
stmt.executeNonQuery();
......@@ -216,10 +225,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64_2_pow_64_minus_1) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"UINT64_COL AS UINT64_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" UINT64_COL AS UINT64_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -242,10 +251,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64_2_pow_64_minus_2) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"UINT64_COL) "
"VALUES("
":UINT64_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" UINT64_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :UINT64_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindUint64(":UINT64_COL", insertValue);
stmt.executeNonQuery();
......@@ -254,10 +265,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindUint64_2_pow_64_minus_2) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"UINT64_COL AS UINT64_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" UINT64_COL AS UINT64_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -280,10 +291,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindString) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"STRING_COL) "
"VALUES("
":STRING_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" STRING_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :STRING_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindString(":STRING_COL", insertValue);
stmt.executeNonQuery();
......@@ -292,10 +305,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindString) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"STRING_COL AS STRING_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" STRING_COL AS STRING_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -318,10 +331,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindBool_true) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"BOOL_COL) "
"VALUES("
":BOOL_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" BOOL_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :BOOL_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindBool(":BOOL_COL", insertValue);
stmt.executeNonQuery();
......@@ -330,10 +345,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindBool_true) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"BOOL_COL AS BOOL_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" BOOL_COL AS BOOL_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -356,10 +371,12 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindBool_false) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"BOOL_COL) "
"VALUES("
":BOOL_COL)";
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
" BOOL_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
" :BOOL_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindBool(":BOOL_COL", insertValue);
stmt.executeNonQuery();
......@@ -368,10 +385,10 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindBool_false) {
// Select the row back from the table
{
const char *const sql =
"SELECT "
"BOOL_COL AS BOOL_COL "
"FROM "
"STMT_TEST";
"SELECT" "\n"
" BOOL_COL AS BOOL_COL" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
......@@ -394,9 +411,11 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindString_invalid_bool_value) {
// Insert a row into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST("
"BOOL_COL) "
"VALUES("
"INSERT INTO STMT_TEST(" "\n"
" ID," "\n"
"BOOL_COL)" "\n"
"VALUES(" "\n"
" 1," "\n"
":BOOL_COL)";
auto stmt = m_conn.createStmt(sql);
stmt.bindString(":BOOL_COL", insertValue);
......@@ -405,4 +424,62 @@ TEST_P(cta_rdbms_StmtTest, insert_with_bindString_invalid_bool_value) {
}
}
TEST_P(cta_rdbms_StmtTest, insert_same_primary_twice) {
using namespace cta::rdbms;
const uint64_t insertValue = 1234;
// Insert an ID into the test table
{
const char *const sql =
"INSERT INTO STMT_TEST(" "\n"
" ID)" "\n"
"VALUES(" "\n"
" :ID)";
auto stmt = m_conn.createStmt(sql);
stmt.bindUint64(":ID", insertValue);
stmt.executeNonQuery();
}
// Select the ID back from the table
{
const char *const sql =
"SELECT" "\n"
" ID AS ID" "\n"
"FROM" "\n"
" STMT_TEST";
auto stmt = m_conn.createStmt(sql);
auto rset = stmt.executeQuery();
ASSERT_TRUE(rset.next());
const auto selectValue = rset.columnOptionalDouble("ID");
ASSERT_TRUE((bool)selectValue);
ASSERT_EQ(insertValue,selectValue.value());
ASSERT_FALSE(rset.next());
}
// Attempt to insert the same ID again
{
const char *const sql =
"INSERT INTO STMT_TEST(" "\n"
" ID) " "\n"
"VALUES(" "\n"
" :ID)";
auto stmt = m_conn.createStmt(sql);
stmt.bindUint64(":ID", insertValue);
switch(m_login.dbType) {
case Login::DBTYPE_IN_MEMORY:
case Login::DBTYPE_ORACLE:
case Login::DBTYPE_POSTGRESQL:
ASSERT_THROW(stmt.executeNonQuery(), cta::exception::DatabaseUniqueError);
break;
default:
ASSERT_THROW(stmt.executeNonQuery(), cta::exception::DatabasePrimaryKeyError);
}
}
}
} // namespace unitTests
......@@ -17,6 +17,8 @@
*/
#include "common/exception/DatabaseCheckConstraintError.hpp"
#include "common/exception/DatabasePrimaryKeyError.hpp"
#include "common/exception/DatabaseUniqueError.hpp"
#include "common/exception/Exception.hpp"
#include "common/exception/LostDatabaseConnection.hpp"
#include "common/make_unique.hpp"
......@@ -27,6 +29,7 @@
#include "rdbms/wrapper/OcciStmt.hpp"
#include <cstring>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
......@@ -268,9 +271,13 @@ void OcciStmt::executeNonQuery() {
}
throw exception::LostDatabaseConnection(msg.str());
}
if(2290 == ex.getErrorCode()) {
switch(ex.getErrorCode()) {
case 1:
throw exception::DatabaseUniqueError(msg.str());
case 2290:
throw exception::DatabaseCheckConstraintError(msg.str());
} else {
default:
throw exception::Exception(msg.str());
}
}
......
......@@ -19,11 +19,13 @@
#pragma once
#include "common/exception/DatabaseCheckConstraintError.hpp"
#include "common/exception/DatabaseUniqueError.hpp"
#include "common/exception/Exception.hpp"
#include "common/exception/LostDatabaseConnection.hpp"
#include <algorithm>
#include <cstring>
#include <iostream>
#include <libpq-fe.h>
#include <string>
......@@ -50,11 +52,13 @@ public:
pgstr.erase(std::remove(pgstr.begin(), pgstr.end(), '\n'), pgstr.end());
}
std::string resstr;
bool uniqueViolation = false;
bool checkViolation = false;
if (nullptr != res) {
resstr = "DB Result Status:" + std::to_string(PQresultStatus(res));
const char *const e = PQresultErrorField(res, PG_DIAG_SQLSTATE);
if (nullptr != e && '\0' != *e) {
uniqueViolation = 0 == std::strcmp("23505", e);
checkViolation = 0 == std::strcmp("23514", e);
resstr += " SQLState:" + std::string(e);
}
......@@ -90,6 +94,9 @@ public:
if (badconn) {
throw exception::LostDatabaseConnection(dbmsg);
}
if (uniqueViolation) {
throw exception::DatabaseUniqueError(dbmsg);
}
if (checkViolation) {
throw exception::DatabaseCheckConstraintError(dbmsg);
}
......
......@@ -19,6 +19,7 @@
#include "common/exception/DatabaseCheckConstraintError.hpp"
#include "common/exception/DatabaseConstraintError.hpp"
#include "common/exception/DatabasePrimaryKeyError.hpp"
#include "common/exception/DatabaseUniqueError.hpp"
#include "common/exception/Exception.hpp"
#include "common/make_unique.hpp"
#include "common/threading/MutexLocker.hpp"
......@@ -286,12 +287,14 @@ void SqliteStmt::executeNonQuery() {
msg << __FUNCTION__ << " failed for SQL statement " << getSqlForException() + ": " << Sqlite::rcToStr(stepRc);
switch(stepRc) {
case SQLITE_CONSTRAINT_PRIMARYKEY:
throw exception::DatabasePrimaryKeyError(msg.str());
case SQLITE_CONSTRAINT_CHECK:
throw exception::DatabaseCheckConstraintError(msg.str());
case SQLITE_CONSTRAINT:
throw exception::DatabaseConstraintError(msg.str());
case SQLITE_CONSTRAINT_CHECK:
throw exception::DatabaseCheckConstraintError(msg.str());
case SQLITE_CONSTRAINT_PRIMARYKEY:
throw exception::DatabasePrimaryKeyError(msg.str());
case SQLITE_CONSTRAINT_UNIQUE:
throw exception::DatabaseUniqueError(msg.str());
default:
if ((stepRc & 0xFF) == SQLITE_CONSTRAINT)
throw exception::DatabaseConstraintError(msg.str());
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment