Commit c1ab4b55 authored by Steven Murray's avatar Steven Murray
Browse files

Extended the syntax of the CTA database configuration file to support both in-memory and oracle

parent 5aca316b
......@@ -66,6 +66,7 @@ add_custom_command(OUTPUT RdbmsCatalogueSchema.cpp
DEPENDS catalogue_schema.cpp)
set(CATALOGUE_UNIT_TESTS_LIB_SRC_FILES
DbLoginTest.cpp
RdbmsCatalogueTest.cpp
SqliteStmtTest.cpp)
......@@ -90,3 +91,7 @@ target_link_libraries (ctacatalogueoraunittests
ctacatalogue)
install(TARGETS ctacatalogueoraunittests DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
install (FILES cta_catalogue_db.conf.example
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cta
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
......@@ -20,44 +20,30 @@
#include "common/exception/Exception.hpp"
#include "common/utils/utils.hpp"
#include <errno.h>
#include <fstream>
#include <iostream>
#include <list>
#include <memory>
#include <stdint.h>
#include <sstream>
#include <stdio.h>
#include <stdexcept>
#include <string.h>
namespace {
namespace cta {
namespace catalogue {
using namespace cta;
namespace {
/**
* Reads the entire contents of the specified files and returns a list of the
* Reads the entire contents of the specified stream and returns a list of the
* non-empty lines.
*
* A line is considered not empty if it contains characters that are not white
* space and are not part of a comment.
*
* @param is The input stream.
* @return A list of the non-empty lines.
*/
std::list<std::string> readNonEmptyLines(
const std::string &filename) {
std::ifstream file(filename);
if(!file) {
exception::Exception ex;
ex.getMessage() << "Failed to open " << filename;
throw ex;
}
std::list<std::string> readNonEmptyLines(std::istream &inputStream) {
std::list<std::string> lines;
std::string line;
while(std::getline(file, line)) {
while(std::getline(inputStream, line)) {
// Remove the newline character if there is one
{
const std::string::size_type newlinePos = line.find("\n");
......@@ -88,51 +74,80 @@ std::list<std::string> readNonEmptyLines(
} // anonymous namespace
namespace cta {
namespace catalogue {
//------------------------------------------------------------------------------
// constructor
//------------------------------------------------------------------------------
DbLogin::DbLogin(
const DbType dbType,
const std::string &username,
const std::string &password,
const std::string &database):
dbType(dbType),
username(username),
password(password),
database(database) {
}
//------------------------------------------------------------------------------
// readFromFile
// parseFile
//------------------------------------------------------------------------------
DbLogin DbLogin::readFromFile(const std::string &filename) {
const std::string fileFormat = "username/password@database";
const std::list<std::string> lines = readNonEmptyLines(filename);
DbLogin DbLogin::parseFile(const std::string &filename) {
try {
std::ifstream file(filename);
if (!file) {
throw exception::Exception("Failed to open file");
}
return parseStream(file);
} catch(exception::Exception &ex) {
throw exception::Exception(std::string("Failed to parse database login file " + filename + ": " +
ex.getMessage().str()));
}
}
//------------------------------------------------------------------------------
// parseStream
//------------------------------------------------------------------------------
DbLogin DbLogin::parseStream(std::istream &inputStream) {
const std::string fileFormat = "either in_memory or oracle:username/password@database";
const std::list<std::string> lines = readNonEmptyLines(inputStream);
if(1 != lines.size()) {
std::ostringstream msg;
msg << filename << " should contain one and only one connection string";
throw exception::Exception(msg.str());
throw exception::Exception("There should only be one and only one line containing a connection string");
}
const std::string connectionString = lines.front();
if(connectionString == "in_memory") {
const std::string username = "";
const std::string password = "";
const std::string database = "";
return DbLogin(DBTYPE_IN_MEMORY, username, password, database);
}
std::vector<std::string> typeAndDetails; // Where details are username, password and database
utils::splitString(connectionString, ':', typeAndDetails);
if(2 != typeAndDetails.size()) {
throw exception::Exception(std::string("Invalid connection string: Correct format is ") + fileFormat);
}
if(typeAndDetails[0] != "oracle") {
throw exception::Exception(std::string("Invalid connection string: Correct format is ") + fileFormat);
}
std::vector<std::string> userPassAndDb;
utils::splitString(connectionString, '@', userPassAndDb);
utils::splitString(typeAndDetails[1], '@', userPassAndDb);
if(2 != userPassAndDb.size()) {
throw exception::Exception(std::string("Invalid connection string"
": Correct format is ") + fileFormat);
throw exception::Exception(std::string("Invalid connection string: Correct format is ") + fileFormat);
}
std::vector<std::string> userAndPass;
utils::splitString(userPassAndDb[0], '/', userAndPass);
if(2 != userAndPass.size()) {
throw exception::Exception(std::string("Invalid connection string"
": Correct format is ") + fileFormat);
throw exception::Exception(std::string("Invalid connection string: Correct format is ") + fileFormat);
}
return DbLogin(userAndPass[0], userAndPass[1], userPassAndDb[1]);
return DbLogin(DBTYPE_ORACLE, userAndPass[0], userAndPass[1], userPassAndDb[1]);
}
} // namesapce catalogue
......
......@@ -18,6 +18,7 @@
#pragma once
#include <istream>
#include <string>
namespace cta {
......@@ -28,18 +29,34 @@ namespace catalogue {
*/
struct DbLogin {
/**
* Enumeration of the supported database types.
*/
enum DbType {
DBTYPE_IN_MEMORY,
DBTYPE_ORACLE,
DBTYPE_NONE
};
/**
* Constructor.
*
* @param dbType The type of the database.
* @param username The username.
* @param password The password.
* @param database The database name.
*/
DbLogin(
const DbType dbType,
const std::string &username,
const std::string &password,
const std::string &database);
/**
* The type of the database.
*/
DbType dbType;
/**
* The user name.
*/
......@@ -56,15 +73,44 @@ struct DbLogin {
std::string database;
/**
* Reads the database login information from the specified file. The format
* of the file is:
* Reads and parses the database login information from the specified file.
*
* The input stream must contain one and only one connection string.
*
* The format of the connection string is:
*
* in_memory or oracle:username/password@database
*
* The file can contain multiple empty lines.
*
* The file can contain multiple comment lines where a comment
* line starts with optional whitespace and a hash character '#'.
*
* @param filename The name of the file containing the database login
* information.
* @return The database login information.
*/
static DbLogin parseFile(const std::string &filename);
/**
* Reads and parses the database login information from the specified input
* stream.
*
* The input stream must contain one and only one connection string.
*
* The format of the connection string is:
*
* in_memory or oracle:username/password@database
*
* The input stream can contain multiple empty lines.
*
* username/password@database
* The input stream can contain multiple comment lines where a comment
* line starts with optional whitespace and a hash character '#'.
*
* @param The name of the file containing the database login information.
* @param inputStream The input stream to be read from.
* @return The database login information.
*/
static DbLogin readFromFile(const std::string &filename);
static DbLogin parseStream(std::istream &inputStream);
}; // class DbLogin
......
/*
* 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 "catalogue/DbLogin.hpp"
#include "common/exception/Exception.hpp"
#include <gtest/gtest.h>
#include <sstream>
namespace unitTests {
class cta_catalogue_DbLoginTest : public ::testing::Test {
protected:
virtual void SetUp() {
}
virtual void TearDown() {
}
};
TEST_F(cta_catalogue_DbLoginTest, constructor) {
using namespace cta::catalogue;
const DbLogin inMemoryLogin(DbLogin::DBTYPE_IN_MEMORY, "", "", "");
ASSERT_EQ(DbLogin::DBTYPE_IN_MEMORY, inMemoryLogin.dbType);
ASSERT_TRUE(inMemoryLogin.username.empty());
ASSERT_TRUE(inMemoryLogin.password.empty());
ASSERT_TRUE(inMemoryLogin.database.empty());
const DbLogin oracleLogin(DbLogin::DBTYPE_ORACLE, "username", "password", "database");
ASSERT_EQ(DbLogin::DBTYPE_ORACLE, oracleLogin.dbType);
ASSERT_EQ(std::string("username"), oracleLogin.username);
ASSERT_EQ(std::string("password"), oracleLogin.password);
ASSERT_EQ(std::string("database"), oracleLogin.database);
}
TEST_F(cta_catalogue_DbLoginTest, parseStream_in_memory) {
using namespace cta::catalogue;
std::stringstream inputStream;
inputStream << "# A comment" << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << "# Another comment" << std::endl;
inputStream << "in_memory";
inputStream << std::endl;
inputStream << std::endl;
inputStream << std::endl;
const DbLogin dbLogin = DbLogin::parseStream(inputStream);
ASSERT_EQ(DbLogin::DBTYPE_IN_MEMORY, dbLogin.dbType);
ASSERT_TRUE(dbLogin.username.empty());
ASSERT_TRUE(dbLogin.password.empty());
ASSERT_TRUE(dbLogin.database.empty());
}
TEST_F(cta_catalogue_DbLoginTest, parseStream_oracle) {
using namespace cta::catalogue;
std::stringstream inputStream;
inputStream << "# A comment" << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << "# Another comment" << std::endl;
inputStream << "oracle:username/password@database" << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << std::endl;
const DbLogin dbLogin = DbLogin::parseStream(inputStream);
ASSERT_EQ(DbLogin::DBTYPE_ORACLE, dbLogin.dbType);
ASSERT_EQ(std::string("username"), dbLogin.username);
ASSERT_EQ(std::string("password"), dbLogin.password);
ASSERT_EQ(std::string("database"), dbLogin.database);
}
TEST_F(cta_catalogue_DbLoginTest, parseStream_invalid) {
using namespace cta;
using namespace cta::catalogue;
std::stringstream inputStream;
inputStream << "# A comment" << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << std::endl;
inputStream << "# Another comment" << std::endl;
inputStream << "invalid_connection_string";
inputStream << std::endl;
inputStream << std::endl;
inputStream << std::endl;
ASSERT_THROW(DbLogin::parseStream(inputStream), exception::Exception);
}
} // namespace unitTests
......@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
namespace unitTests {
......@@ -52,7 +53,7 @@ TEST_F(cta_catalogue_OcciConnTest, constructor_real_connection) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......@@ -64,7 +65,7 @@ TEST_F(cta_catalogue_OcciConnTest, createStmt_null_sql) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......@@ -78,7 +79,7 @@ TEST_F(cta_catalogue_OcciConnTest, createStmt) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......
......@@ -43,7 +43,7 @@ TEST_F(cta_catalogue_OcciRsetTest, executeQuery) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......@@ -62,7 +62,7 @@ TEST_F(cta_catalogue_OcciRsetTest, executeQueryRelyOnRsetDestructorForCacheDelet
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......@@ -80,7 +80,7 @@ TEST_F(cta_catalogue_OcciRsetTest, executeQuery_uint32_t) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......@@ -101,7 +101,7 @@ TEST_F(cta_catalogue_OcciRsetTest, bind_c_string) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......@@ -121,7 +121,7 @@ TEST_F(cta_catalogue_OcciRsetTest, bind_uint32_t) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......
......@@ -42,7 +42,7 @@ TEST_F(cta_catalogue_DbStmtTest, executeQuery) {
using namespace cta;
using namespace cta::catalogue;
const DbLogin dbLogin = DbLogin::readFromFile(g_cmdLineArgs.oraDbConnFile);
const DbLogin dbLogin = DbLogin::parseFile(g_cmdLineArgs.oraDbConnFile);
OcciEnv env;
std::unique_ptr<DbConn> conn(env.createConn(
dbLogin.username.c_str(),
......
#include "catalogue/SqliteConn.hpp"
#include "common/exception/Exception.hpp"
#include <gtest/gtest.h>
#include <memory>
namespace unitTests {
class cta_catalogue_SqliteConnTest : public ::testing::Test {
protected:
virtual void SetUp() {
}
virtual void TearDown() {
}
};
TEST_F(cta_catalogue_SqliteConnTest, constructor) {
using namespace cta::catalogue;
std::unique_ptr<SqliteConn> db;
ASSERT_NO_THROW(db.reset(new SqliteConn(":memory:")));
}
TEST_F(cta_catalogue_SqliteConnTest, enableForeignKeys) {
using namespace cta::catalogue;
std::unique_ptr<SqliteConn> db;
ASSERT_NO_THROW(db.reset(new SqliteConn(":memory:")));
ASSERT_NO_THROW(db->enableForeignKeys());
}
TEST_F(cta_catalogue_SqliteConnTest, createTable) {
using namespace cta::catalogue;
std::unique_ptr<SqliteConn> db;
ASSERT_NO_THROW(db.reset(new SqliteConn(":memory:")));
ASSERT_NO_THROW(db->execNonQuery("CREATE TABLE TEST(COL1 INTEGER);"));
}
TEST_F(cta_catalogue_SqliteConnTest, createSameTableTwice) {
using namespace cta::catalogue;
std::unique_ptr<SqliteConn> db;
ASSERT_NO_THROW(db.reset(new SqliteConn(":memory:")));
ASSERT_NO_THROW(db->execNonQuery("CREATE TABLE TEST(COL1 INTEGER);"));
ASSERT_THROW(db->execNonQuery("CREATE TABLE TEST(COL1 INTEGER);"),
cta::exception::Exception);
}
} // 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/>.
# This file should contain one and only one connection string. Blank lines and
# comments are ignored. The format of the connection string is either:
#
# in_memory
#
# or:
#
# oracle:username/password@database
......@@ -133,6 +133,7 @@ The shared libraries
%attr(0755,root,root) %{_libdir}/libctamessagesutils.so
%attr(0755,root,root) %{_libdir}/libctatapereactorutils.so
%attr(0755,root,root) %{_libdir}/libctatapeserverdaemonutils.so
%attr(0644,root,root) %{_sysconfdir}/cta/cta_catalogue_db.conf.example
%package -n cta-systemtests
Summary: CERN Tape Archive: unit and system tests with virtual tape drives
......
Markdown is supported
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