Commit 13f92cc7 authored by Christophe Gonzales's avatar Christophe Gonzales

added SQLite testunits + improved DBInitializerFromSQL to cope with SQLite databases

parent 50b8ac45
Pipeline #21802940 passed with stages
in 62 minutes and 31 seconds
......@@ -142,7 +142,7 @@ namespace gum {
// ##########################################################################
/// @{
/// default constructor
/// default constructor, especially for postgresql databases
/** This will read the result of query and load it in memory.
*
* @param dataSource A declared dataSource in your odbc configuration
......@@ -161,6 +161,21 @@ namespace gum {
long timeout = 0L,
const allocator_type& alloc = allocator_type () );
/// default constructor, especially for sqlite databases
/** This will read the result of query and load it in memory.
*
* @param connection_string a string specifying to nanODBC how to connect
* to a SQL database
* @param query The SQL query used as a database.
* @param timeout Defines a timeout for accessing the SQL database, if 0
* then no timeout is set.
* @param alloc the allocator used to allocate all the data structures
*/
DBInitializerFromSQL ( const std::string& connection_string,
const std::string& query,
long timeout = 0L,
const allocator_type& alloc = allocator_type () );
/// copy constructor
/** the new initializer points to the same SQL query as from, but
* it reparses the result it from scratch. */
......@@ -229,14 +244,8 @@ namespace gum {
#ifndef DOXYGEN_SHOULD_SKIP_THIS
private:
// the name of the database
std::string __data_source;
// the login to connect to the database
std::string __login;
// the password to connect to the database
std::string __password;
// the string specifying how to connect to the database
std::string __connection_string;
// the current query
std::string __query;
......@@ -252,7 +261,12 @@ namespace gum {
// the parser used for parsing the query results
NanodbcParser<ALLOC> __parser;
/// perform a connection from a connection string
void __connect(const std::string& connection_string,
long timeout);
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
};
......
......@@ -35,19 +35,69 @@ namespace gum {
namespace learning {
/// perform a connection from a connection string
template <template<typename> class ALLOC>
void DBInitializerFromSQL<ALLOC>::__connect(const std::string& connection_string,
long timeout) {
// analyze the connection string: either this is a user-defined connection
// string or this is an aGrUM-constructed one derived from a datasource,
// a login and a password
bool agrum_connection = ( connection_string.size () > 4 ) &&
( connection_string.compare( 0, 4, "gum " ) == 0 );
// perform the connection to the database
if ( ! agrum_connection ) {
__connection.connect ( connection_string, timeout );
}
else {
std::size_t deb_index, end_index;
const std::string delimiter = "|";
deb_index = connection_string.find ( delimiter, 0 );
if ( deb_index == std::string::npos )
GUM_ERROR ( DatabaseError, "could not determine the datasource" );
deb_index += std::size_t(1);
end_index = connection_string.find ( delimiter, deb_index );
if ( end_index == std::string::npos )
GUM_ERROR ( DatabaseError, "could not determine the datasource" );
std::string dataSource =
connection_string.substr ( deb_index, end_index - deb_index );
deb_index = connection_string.find ( delimiter, end_index + std::size_t(1) );
if ( deb_index == std::string::npos )
GUM_ERROR ( DatabaseError, "could not determine the database login" );
deb_index += std::size_t(1);
end_index = connection_string.find ( delimiter, deb_index );
if ( end_index == std::string::npos )
GUM_ERROR ( DatabaseError, "could not determine the database login" );
std::string login =
connection_string.substr ( deb_index, end_index - deb_index );
deb_index = connection_string.find ( delimiter, end_index + std::size_t(1) );
if ( deb_index == std::string::npos )
GUM_ERROR ( DatabaseError, "could not determine the database password" );
deb_index += std::size_t(1);
end_index = connection_string.find ( delimiter, deb_index );
if ( end_index == std::string::npos )
GUM_ERROR ( DatabaseError, "could not determine the database password" );
std::string password =
connection_string.substr ( deb_index, end_index - deb_index );
__connection.connect ( dataSource, login, password, timeout );
}
}
/// default constructor
template <template<typename> class ALLOC>
DBInitializerFromSQL<ALLOC>::DBInitializerFromSQL (
const std::string& dataSource,
const std::string& login,
const std::string& password,
const std::string& connection_string,
const std::string& query,
long timeout,
const typename DBInitializerFromSQL<ALLOC>::allocator_type& alloc )
: IDBInitializer<ALLOC> ( IDBInitializer<ALLOC>::InputType::STRING, alloc )
, __data_source ( dataSource )
, __login ( login )
, __password ( password )
, __connection_string ( connection_string )
, __query ( query )
, __timeout ( timeout )
, __var_names ( alloc )
......@@ -56,9 +106,9 @@ namespace gum {
// will change it
const std::string current_locale = std::setlocale(LC_NUMERIC, NULL );
// perform the connection to the database
__connection.connect ( dataSource, login, password, timeout );
// perform the connection
__connect ( connection_string, timeout );
// restore the locale
std::setlocale(LC_NUMERIC, current_locale.c_str () );
......@@ -75,14 +125,26 @@ namespace gum {
}
/// default constructor
template <template<typename> class ALLOC>
DBInitializerFromSQL<ALLOC>::DBInitializerFromSQL (
const std::string& dataSource,
const std::string& login,
const std::string& password,
const std::string& query,
long timeout,
const typename DBInitializerFromSQL<ALLOC>::allocator_type& alloc )
: DBInitializerFromSQL<ALLOC> ( "gum datasource=|" + dataSource + "|; login=|" +
login + "|; password=|" + password + "|",
query, timeout, alloc ) {}
/// copy constructor with a given allocator
template <template<typename> class ALLOC>
DBInitializerFromSQL<ALLOC>::DBInitializerFromSQL (
const DBInitializerFromSQL<ALLOC>& from,
const allocator_type& alloc )
: DBInitializerFromSQL<ALLOC> ( from.__data_source,
from.__login,
from.__password,
: DBInitializerFromSQL<ALLOC> ( from.__connection_string,
from.__query,
from.__timeout,
alloc ) {}
......@@ -100,9 +162,7 @@ namespace gum {
DBInitializerFromSQL<ALLOC>::DBInitializerFromSQL (
DBInitializerFromSQL<ALLOC>&& from,
const allocator_type& alloc )
: DBInitializerFromSQL<ALLOC> ( from.__data_source,
from.__login,
from.__password,
: DBInitializerFromSQL<ALLOC> ( from.__connection_string,
from.__query,
from.__timeout,
alloc ) {}
......@@ -156,23 +216,18 @@ namespace gum {
IDBInitializer<ALLOC>::operator= ( from );
// check if the connection parameters have changed
const bool connexion_changed =
( __data_source != from.__data_source ) ||
( __login != from.__login ) ||
( __password != from.__password ) ||
( __timeout != from.__timeout );
( __connection_string != from.__connection_string );
// save the new connection parameters
__data_source = from.__data_source;
__login = from.__login;
__password = from.__password;
__query = from.__query;
__timeout = from.__timeout;
__connection_string = from.__connection_string;
__query = from.__query;
__timeout = from.__timeout;
// recreate the connection if needed
if ( connexion_changed ) {
if ( __connection.connected () )
__connection.disconnect ();
__connection.connect ( __data_source, __login, __password, __timeout );
__connect ( __connection_string, __timeout );
}
// initiate the SQL parser
......
......@@ -57,30 +57,30 @@ namespace gum {
/// type for the allocators passed in arguments of methods
using allocator_type = ALLOC<std::string>;
// ##########################################################################
/// @name Constructors / Destructors
// ##########################################################################
/// @{
/// Default constructor: create a parser without being connected
NanodbcParser( const allocator_type& alloc = allocator_type () );
NanodbcParser( const ALLOC<std::string>& alloc = ALLOC<std::string>() );
/// constructor that executes an SQL query if the connection is active
/** @param connection a nanODBC connection to a SQL database
* @param query a string containing an SQL query
* @param alloc The allocator that will be used by all methods
*/
NanodbcParser( nanodbc::connection& connection,
const std::string& query,
const allocator_type& alloc = allocator_type () );
NanodbcParser( nanodbc::connection& connection,
const std::string& query,
const ALLOC<std::string>& alloc = ALLOC<std::string> () );
/// destructor
virtual ~NanodbcParser();
/// @}
// ########################################################################
/// @name Accessors / Modifiers
// ########################################################################
......@@ -109,19 +109,22 @@ namespace gum {
/// @}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
private:
// the result of the last SQL query performed
nanodbc::result __result;
// the line number within the current query
std::size_t __nb_line { std::size_t(0) };
// a vector that will contain the content of the current line of result
std::vector<std::string,ALLOC<std::string>> __data;
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
};
} // namespace learning
......
......@@ -107,6 +107,7 @@ namespace gum {
__data[i] = "NULL";
}
}
++__nb_line;
return true;
}
} catch ( std::runtime_error& e ) {
......@@ -119,13 +120,8 @@ namespace gum {
// return the current number line
template <template<typename> class ALLOC>
INLINE
std::size_t NanodbcParser<ALLOC>::nbLine() const {
try {
return std::size_t ( __result.position() );
} catch ( std::runtime_error& e ) {
return std::size_t(0);
}
INLINE std::size_t NanodbcParser<ALLOC>::nbLine() const {
return __nb_line >= 1 ? __nb_line - 1 : std::size_t(0);
}
......@@ -148,6 +144,7 @@ namespace gum {
const std::string& query ) {
__result = nanodbc::execute( connexion, query );
__data.resize ( std::size_t ( __result.columns() ) );
__nb_line = std::size_t(0);
}
......
This diff is collapsed.
/***************************************************************************
* Copyright (C) 2005 by Pierre-Henri WUILLEMIN et Christophe GONZALES *
* {prenom.nom}_at_lip6.fr *
* *
* 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 2 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <iostream>
#include <string>
#include <ressources/myalloc.h>
#include <cxxtest/AgrumTestSuite.h>
#include <cxxtest/testsuite_utils.h>
#include <agrum/learning/database/nanodbcParser.h>
namespace gum_tests {
class NanodbcParserTestSuite : public CxxTest::TestSuite {
public:
void testSimpleSQL() {
#ifdef _ODBC
const std::string dataSource = "PostgreSQL";
const std::string login = "gonzales";
const std::string password = "agrum";
const std::string query = "SELECT * FROM testunit.asia";
// the database contains the following rows:
// smoking? lung_cancer? bronchitis? visit_to_Asia? tuberculosis?
// tuberculos_or_cancer? dyspnoea? positive_XraY?
//
// Their types are:
// boolean boolean boolean boolean boolean character varying
// text numeric
//
// Their content:
// FALSE FALSE FALSE TRUE TRUE "false" "toto titi" -1.2
// TRUE TRUE FALSE TRUE TRUE "true" "toto titi" 2.45
// TRUE TRUE TRUE TRUE TRUE "true" "toto titi" 4
const std::string current_locale = std::setlocale(LC_NUMERIC, NULL );
nanodbc::connection connection ( dataSource, login, password, 0 );
std::setlocale(LC_NUMERIC, current_locale.c_str () );
gum::learning::NanodbcParser<> parser ( connection, query );
TS_ASSERT ( parser.nbColumns () == std::size_t(8) );
parser.next();
TS_ASSERT ( parser.nbLine() == std::size_t(1) );
const auto& row1 = parser.current ();
TS_ASSERT ( row1[0] == "0" );
TS_ASSERT ( row1[1] == "0" );
TS_ASSERT ( row1[2] == "0" );
TS_ASSERT ( row1[3] == "1" );
TS_ASSERT ( row1[4] == "1" );
TS_ASSERT ( row1[5] == "false" );
TS_ASSERT ( row1[6] == "toto titi" );
TS_ASSERT ( row1[7] == "-1.2" );
parser.next();
TS_ASSERT ( parser.nbLine() == std::size_t(2) );
const auto& row2 = parser.current ();
TS_ASSERT ( row2[0] == "1" );
TS_ASSERT ( row2[1] == "1" );
TS_ASSERT ( row2[2] == "0" );
TS_ASSERT ( row2[3] == "1" );
TS_ASSERT ( row2[4] == "1" );
TS_ASSERT ( row2[5] == "true" );
TS_ASSERT ( row2[6] == "toto titi" );
TS_ASSERT ( row2[7] == "2.45" );
parser.next();
TS_ASSERT ( parser.nbLine() == std::size_t(3) );
const auto& row3 = parser.current ();
TS_ASSERT ( row3[0] == "1" );
TS_ASSERT ( row3[1] == "1" );
TS_ASSERT ( row3[2] == "1" );
TS_ASSERT ( row3[3] == "1" );
TS_ASSERT ( row3[4] == "1" );
TS_ASSERT ( row3[5] == "true" );
TS_ASSERT ( row3[6] == "toto titi" );
TS_ASSERT ( row3[7] == "4" );
TS_ASSERT ( parser.columnName ( std::size_t(0) ) == "smoking?" );
TS_ASSERT ( parser.columnName ( std::size_t(1) ) == "lung_cancer?" );
TS_ASSERT ( parser.columnName ( std::size_t(2) ) == "bronchitis?" );
TS_ASSERT ( parser.columnName ( std::size_t(3) ) == "visit_to_Asia?" );
TS_ASSERT ( parser.columnName ( std::size_t(4) ) == "tuberculosis?" );
TS_ASSERT ( parser.columnName ( std::size_t(5) ) == "tuberculos_or_cancer?");
TS_ASSERT ( parser.columnName ( std::size_t(6) ) == "dyspnoea?" );
TS_ASSERT ( parser.columnName ( std::size_t(7) ) == "positive_XraY?" );
TS_ASSERT ( parser.next() == false );
parser.useNewQuery ( connection, query );
parser.next();
TS_ASSERT ( parser.nbLine() == std::size_t(1) );
const auto& xrow1 = parser.current ();
TS_ASSERT ( xrow1[0] == "0" );
TS_ASSERT ( xrow1[1] == "0" );
TS_ASSERT ( xrow1[2] == "0" );
TS_ASSERT ( xrow1[3] == "1" );
TS_ASSERT ( xrow1[4] == "1" );
TS_ASSERT ( xrow1[5] == "false" );
TS_ASSERT ( xrow1[6] == "toto titi" );
TS_ASSERT ( xrow1[7] == "-1.2" );
parser.next();
TS_ASSERT ( parser.nbLine() == std::size_t(2) );
const auto& xrow2 = parser.current ();
TS_ASSERT ( xrow2[0] == "1" );
TS_ASSERT ( xrow2[1] == "1" );
TS_ASSERT ( xrow2[2] == "0" );
TS_ASSERT ( xrow2[3] == "1" );
TS_ASSERT ( xrow2[4] == "1" );
TS_ASSERT ( xrow2[5] == "true" );
TS_ASSERT ( xrow2[6] == "toto titi" );
TS_ASSERT ( xrow2[7] == "2.45" );
parser.next();
TS_ASSERT ( parser.nbLine() == std::size_t(3) );
const auto& xrow3 = parser.current ();
TS_ASSERT ( xrow3[0] == "1" );
TS_ASSERT ( xrow3[1] == "1" );
TS_ASSERT ( xrow3[2] == "1" );
TS_ASSERT ( xrow3[3] == "1" );
TS_ASSERT ( xrow3[4] == "1" );
TS_ASSERT ( xrow3[5] == "true" );
TS_ASSERT ( xrow3[6] == "toto titi" );
TS_ASSERT ( xrow3[7] == "4" );
TS_ASSERT ( parser.columnName ( std::size_t(0) ) == "smoking?" );
TS_ASSERT ( parser.columnName ( std::size_t(1) ) == "lung_cancer?" );
TS_ASSERT ( parser.columnName ( std::size_t(2) ) == "bronchitis?" );
TS_ASSERT ( parser.columnName ( std::size_t(3) ) == "visit_to_Asia?" );
TS_ASSERT ( parser.columnName ( std::size_t(4) ) == "tuberculosis?" );
TS_ASSERT ( parser.columnName ( std::size_t(5) ) == "tuberculos_or_cancer?");
TS_ASSERT ( parser.columnName ( std::size_t(6) ) == "dyspnoea?" );
TS_ASSERT ( parser.columnName ( std::size_t(7) ) == "positive_XraY?" );
TS_ASSERT ( parser.next() == false );
#endif // _ODBC
}
};
} /* namespace gum_tests */
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