From 093d4c05014e6f9210c7a898eaa815dd8a139c35 Mon Sep 17 00:00:00 2001 From: David Maisonave <47364845+David-Maisonave@users.noreply.github.com> Date: Tue, 30 Nov 2021 12:37:56 -0500 Subject: [PATCH] Added logic to verify global db open or close --- sqlite3pp.h | 4 +- sqlite3pp_ez.cpp | 133 ++++++++++++++++++++++++++++++++++++++++------- sqlite3pp_ez.h | 52 +++++++++++++----- 3 files changed, 155 insertions(+), 34 deletions(-) diff --git a/sqlite3pp.h b/sqlite3pp.h index fbc8e0a..d2838bd 100644 --- a/sqlite3pp.h +++ b/sqlite3pp.h @@ -177,6 +177,7 @@ namespace sqlite3pp { public: explicit database_error(char const* msg); + explicit database_error(const std::string& msg); explicit database_error(database& db); }; @@ -361,7 +362,8 @@ namespace sqlite3pp query_iterator& operator++(); - value_type operator*() const; +//#pragma warning(disable : 4996) + sqlite3pp::query::rows operator*() const; private: query* cmd_; diff --git a/sqlite3pp_ez.cpp b/sqlite3pp_ez.cpp index 0570f1a..019a6ba 100644 --- a/sqlite3pp_ez.cpp +++ b/sqlite3pp_ez.cpp @@ -138,6 +138,7 @@ namespace sqlite3pp // Added Global functions for global_db usage /////////////////////////////////////////////////////////////////////////// database sql_base::global_db; + bool sql_base::bIsGlblDbOpen = false; const char sql_base::TableArg_PreExecuteArg[] = "PreExecuteArg"; const char sql_base::TableArg_WhereClauseArg[] = "WhereClauseArg"; const char sql_base::TableArg_InsertArg[] = "InsertArg"; @@ -146,74 +147,150 @@ namespace sqlite3pp void setGlobalDB( const std::string& db_filename ) { - sql_base::global_db = database( db_filename.c_str() ); + if (!sql_base::bIsGlblDbOpen) + { + sql_base::global_db = database(db_filename.c_str()); + sql_base::bIsGlblDbOpen = true; + } + else + throw database_error("Trying to set Global DB with '" + db_filename + "' before detaching previous connection."); } void setGlobalDB( const std::wstring& db_filename ) { - sql_base::global_db = database(db_filename.c_str()); + if (!sql_base::bIsGlblDbOpen) + { + sql_base::global_db = database(db_filename.c_str()); + sql_base::bIsGlblDbOpen = true; + } + else + throw database_error("Trying to set Global DB with '" + to_string(db_filename.c_str()) + "' before detaching previous connection."); } - database& getGlobalDB( ) + database& getGlobalDB( ) { + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to get Global DB before it is opened."); return sql_base::global_db; } int Execute( const std::string& sql ) { - return sql_base::global_db.execute(sql); + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to Execute Global DB before it is opened. Query='" + sql + "'"); + int rc = sql_base::global_db.execute(sql); + // ToDo: Add logic here to repart error if rc!=SQLITE_OK + return rc; } int Execute( const std::wstring& sql ) { - return sql_base::global_db.execute( sql_base::to_string( sql).c_str() ); + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to Execute Global DB before it is opened. Query='" + to_string(sql.c_str()) + "'"); + int rc = sql_base::global_db.execute( sql_base::to_string( sql).c_str() ); + // ToDo: Add logic here to repart error if rc!=SQLITE_OK + return rc; } int Connect( char const * dbname, int flags, const char * vfs ) { - return sql_base::global_db.connect(dbname, flags, vfs); + if (sql_base::bIsGlblDbOpen) + throw database_error("Trying to connect to '" + std::string(dbname) + "' after Global DB is open."); + int rc = sql_base::global_db.connect(dbname, flags, vfs); + if (SQLITE_OK == rc) + sql_base::bIsGlblDbOpen = true; + else + throw database_error("Connect failed for '" + std::string(dbname) + "'."); + return rc; } int Connect( wchar_t const * dbname, int flags, const wchar_t * vfs ) { - return sql_base::global_db.connect( sql_base::to_string( dbname ).c_str(), flags, sql_base::to_string( vfs ).c_str() );; + if (sql_base::bIsGlblDbOpen) + throw database_error("Trying to connect to '" + to_string(dbname) + "' after Global DB is open."); + int rc = sql_base::global_db.connect( sql_base::to_string( dbname ).c_str(), flags, sql_base::to_string( vfs ).c_str() );; + if (SQLITE_OK == rc) + sql_base::bIsGlblDbOpen = true; + else + throw database_error("Connect failed for '" + to_string(dbname) + "'."); + return rc; } int Attach( char const * dbname, char const * name ) { - return sql_base::global_db.attach(dbname, name); + if (sql_base::bIsGlblDbOpen) + throw database_error("Trying to attach to '" + std::string(dbname) + "' after Global DB is open."); + int rc = sql_base::global_db.attach(dbname, name); + if (SQLITE_OK == rc) + sql_base::bIsGlblDbOpen = true; + else + throw database_error("Attach failed for '" + std::string(dbname) + "'."); + return rc; } int Attach( wchar_t const * dbname, wchar_t const * name ) { - return sql_base::global_db.attach( sql_base::to_string( dbname ).c_str(), sql_base::to_string( name ).c_str() ); + if (sql_base::bIsGlblDbOpen) + throw database_error("Trying to attach to '" + to_string(dbname) + "' after Global DB is open."); + int rc = sql_base::global_db.attach( sql_base::to_string( dbname ).c_str(), sql_base::to_string( name ).c_str() ); + if (SQLITE_OK == rc) + sql_base::bIsGlblDbOpen = true; + else + throw database_error("Attach failed for '" + to_string(dbname) + "'."); + return rc; } int Detach( char const * name ) { - return sql_base::global_db.detach(name); + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to detached before Global DB is open."); + int rc = sql_base::global_db.detach(name); + if (SQLITE_OK == rc) + sql_base::bIsGlblDbOpen = false; + else + throw database_error("Attach failed for '" + std::string(name) + "'."); + return rc; } int Detach( wchar_t const * name ) { - return sql_base::global_db.detach( sql_base::to_string(name).c_str() ); + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to detached before Global DB is open."); + int rc = sql_base::global_db.detach( sql_base::to_string(name).c_str() ); + if (SQLITE_OK == rc) + sql_base::bIsGlblDbOpen = false; + else + throw database_error("Attach failed for '" + to_string(name) + "'."); + return rc; } int Backup( char const * dbname, database & destdb, char const * destdbname, database::backup_handler h, int step_page ) { + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to use Global DB before it has been opened."); return sql_base::global_db.backup(dbname,destdb,destdbname,h,step_page); } int Backup( wchar_t const * dbname, database & destdb, wchar_t const * destdbname, database::backup_handler h, int step_page ) { + if (!sql_base::bIsGlblDbOpen) + throw database_error("Trying to use Global DB before it has been opened."); return sql_base::global_db.backup( sql_base::to_string( dbname).c_str(), destdb, sql_base::to_string( destdbname).c_str(), h, step_page ); } std::string GetDbErrMsg() { + if (!sql_base::bIsGlblDbOpen) + return "Error: Failed to open global database before using it"; return sql_base::global_db.error_msg(); } std::wstring GetDbErrMsgW() { + if (!sql_base::bIsGlblDbOpen) + return L"Error: Failed to open global database before using it"; return sql_base::to_wstring(sql_base::global_db.error_msg()); } int GetDbErrNo() { + if (!sql_base::bIsGlblDbOpen) + return -1; return sql_base::global_db.error_code(); } int GetDbExtErrNo() { + if (!sql_base::bIsGlblDbOpen) + return -1; return sql_base::global_db.extended_error_code(); } @@ -503,26 +580,40 @@ namespace sqlite3pp } return ProcessClassCreation(TableName, QueryStr); } + sqlite3pp::query* sql_base::CreateQuery(database& db, const std::string& QueryStr) + { + try + { + return new sqlite3pp::query(db, QueryStr.c_str()); + } + catch (...) + { + return NULL; + } + return NULL; + } bool SQLiteClassBuilder::ProcessClassCreation(const std::string& TableName, std::string QueryStr) { if (QueryStr.empty()) QueryStr = "SELECT * FROM \"" + TableName + "\""; - sqlite3pp::query qry(m_db, QueryStr.c_str()); + std::shared_ptr < sqlite3pp::query> qry(sql_base::CreateQuery(m_db, QueryStr)); + if (!qry) + return false; std::vector > columns; std::vector > columns_with_comma; std::string FirstColumnName; std::string LastColumnName = "get_MyColumnFoo()"; - for (int i = 0; i < qry.column_count(); ++i) + for (int i = 0; i < qry->column_count(); ++i) { - if (strstr(qry.column_name(i), ":") != NULL) continue; + if (strstr(qry->column_name(i), ":") != NULL) continue; - columns.push_back(std::pair(qry.column_name(i), GetType(qry.column_decltype(i)))); - columns_with_comma.push_back(std::pair(qry.column_name(i), i ? ", " : "")); + columns.push_back(std::pair(qry->column_name(i), GetType(qry->column_decltype(i)))); + columns_with_comma.push_back(std::pair(qry->column_name(i), i ? ", " : "")); if (FirstColumnName.empty()) - FirstColumnName = qry.column_name(i); + FirstColumnName = qry->column_name(i); else - LastColumnName = qry.column_name(i); + LastColumnName = qry->column_name(i); } std::ofstream myfile; std::string ClassName, HeaderUpper; @@ -559,7 +650,7 @@ namespace sqlite3pp // Miscellaneous functions if (!m_options.m.exclude_comments) myfile << "\n\t// Miscellaneous functions" << std::endl; - myfile << "\tstatic int getColumnCount() { return " << qry.column_count() << "; }" << std::endl; + myfile << "\tstatic int getColumnCount() { return " << qry->column_count() << "; }" << std::endl; } // Define get function for each data member variable. Always create these functions if member variables are protected. @@ -675,6 +766,10 @@ namespace sqlite3pp return value; } + database_error::database_error(const std::string& msg) : std::runtime_error(msg.c_str()) + { + } + #endif// !SQLITE3PP_NO_UNICODE Blob query::rows::get(int idx, const Blob&) const diff --git a/sqlite3pp_ez.h b/sqlite3pp_ez.h index fb449b6..b352739 100644 --- a/sqlite3pp_ez.h +++ b/sqlite3pp_ez.h @@ -90,10 +90,11 @@ namespace sqlite3pp friend std::wstring GetDbErrMsgW(); friend int GetDbErrNo(); friend int GetDbExtErrNo(); - + static sqlite3pp::query* CreateQuery(database& db, const std::string& QueryStr); protected: static sqlite3pp::database global_db; // To be used as global DB + static bool bIsGlblDbOpen; // To be used as global DB static const char TableArg_PreExecuteArg[]; static const char TableArg_WhereClauseArg[]; static const char TableArg_InsertArg[]; @@ -144,24 +145,43 @@ namespace sqlite3pp const T_STR m_ColumnNames; // Mainly here for debugging purposes const int m_ColumnCount; // Mainly here for debugging purposes public: - // Constructors - Table(PreExecuteArg preexecutearg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(global_db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, InsertArg(), DbFileNameArg()); } - Table(InsertArg insertarg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(global_db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), PreExecuteArg(), insertarg, DbFileNameArg()); } - Table(DbFileNameArg dbfilenamearg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(global_db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), PreExecuteArg(), InsertArg(), dbfilenamearg); } + // There are 2 constructor sets with each having 4 types of constructs. There are 4 types purely for the sake of convenience. Determine which constructors to use by which arguments are needed. + // Set of constructors needing a sqlite3pp::database instance in constructor argument. These constructors automatically populate the object using data from the database db instance. + Table(sqlite3pp::database &db, WhereClauseArg whereclausearg = WhereClauseArg(), PreExecuteArg preexecutearg = PreExecuteArg(), InsertArg insertarg = InsertArg(), DbFileNameArg dbfilenamearg = DbFileNameArg()) :m_db( db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, insertarg, dbfilenamearg); } Table(sqlite3pp::database &db, PreExecuteArg preexecutearg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, InsertArg(), DbFileNameArg()); } Table(sqlite3pp::database &db, InsertArg insertarg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), PreExecuteArg(), insertarg, DbFileNameArg()); } Table(sqlite3pp::database &db, DbFileNameArg dbfilenamearg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), PreExecuteArg(), InsertArg(), dbfilenamearg); } - Table( sqlite3pp::database &db, const VectType &VectTypes ) :m_db( db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { for ( auto v : VectTypes ) m_VectType.push_back( v ); } - Table( const VectType &VectTypes ) :m_db( global_db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { for ( auto v : VectTypes ) m_VectType.push_back( v ); } - Table(WhereClauseArg whereclausearg = WhereClauseArg(), PreExecuteArg preexecutearg = PreExecuteArg(), InsertArg insertarg = InsertArg(), DbFileNameArg dbfilenamearg = DbFileNameArg()) - :m_db( global_db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, insertarg, dbfilenamearg); } - Table( sqlite3pp::database &db, WhereClauseArg whereclausearg = WhereClauseArg(), PreExecuteArg preexecutearg = PreExecuteArg(), InsertArg insertarg = InsertArg(), DbFileNameArg dbfilenamearg = DbFileNameArg()) - :m_db( db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, insertarg, dbfilenamearg); } + // Same as above set, but this set uses the single global database instance, and so db does not need to be pass to the constructor. These constructors automatically populate the object using data from the global database instance. + Table(WhereClauseArg whereclausearg = WhereClauseArg(), PreExecuteArg preexecutearg = PreExecuteArg(), InsertArg insertarg = InsertArg(), DbFileNameArg dbfilenamearg = DbFileNameArg()):m_db( global_db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, insertarg, dbfilenamearg); } + Table(PreExecuteArg preexecutearg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(global_db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), preexecutearg, InsertArg(), DbFileNameArg()); } + Table(InsertArg insertarg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(global_db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), PreExecuteArg(), insertarg, DbFileNameArg()); } + Table(DbFileNameArg dbfilenamearg, WhereClauseArg whereclausearg = WhereClauseArg()) :m_db(global_db), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { PrepareQuery( m_db, CreateSelectQueryStr(whereclausearg, T_STR()), PreExecuteArg(), InsertArg(), dbfilenamearg); } + + // Set of constructors which do NOT populate itself using the database. Instead the constructors takes an argument which is used to automatically populate itself + Table(sqlite3pp::database &db, const VectType &VectTypes ) :m_db( db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { for ( auto v : VectTypes ) m_VectType.push_back( v ); } + Table( const VectType &VectTypes ) :m_db( global_db ), m_TableName(T::getTableName()), m_ColumnNames(T::getColumnNames()), m_ColumnCount(T::getColumnCount()) { for ( auto v : VectTypes ) m_VectType.push_back( v ); } + // Public methods const VectType& Get() const { return m_VectType; } - void Insert( const VectType& Data ) { for (auto d : Data) Insert(ValueArg(d.GetValues()), T_STR()); } + void Insert( bool DeleteAllBeforeInsert = false) + { + if (DeleteAllBeforeInsert) + DeleteAll(); // ToDo: Add logic to have delete use where clause if the constructor received one + for (auto d : m_VectType) + Insert(ValueArg(d.GetValues()), T_STR()); + } + void UpdateDb(bool DeleteAllBeforeUpdate = false) + { + if (DeleteAllBeforeUpdate) + DeleteAll(); // ToDo: Add logic to have delete use where clause if the constructor received one + for (auto d : m_VectType) + UpdateDb(ValueArg(d.GetValues()), T_STR()); + } int Execute(const T_STR& strExecute){return m_db.execute(strExecute);} + void Insert(const DataType &d) { push_back(d); Insert(ValueArg(d.GetValues()), T_STR()); } + void UpdateDb(const DataType &d) { UpdateDb(ValueArg(d.GetValues()), T_STR()); } + void DeleteAll(){ DeleteAll(T_STR()); } auto begin() { return m_VectType.begin(); } auto end() { return m_VectType.end(); } size_t size() const { return m_VectType.size(); } @@ -198,8 +218,12 @@ namespace sqlite3pp protected: // Protected methods - void Insert(const ValueArg& valuearg, std::string){ m_db.execute("INSERT INTO " + T::getTableName() + " (" + T::getColumnNames() + ") VALUES (" + valuearg.get_Str() + ")");} - void Insert(const ValueArg& valuearg, std::wstring) {m_db.execute(L"INSERT INTO " + T::getTableName() + L" (" + T::getColumnNames() + L") VALUES (" + valuearg.get_Str() + L")");} + void Insert(const ValueArg& valuearg, std::string) { m_db.execute("INSERT INTO " + T::getTableName() + " (" + T::getColumnNames() + ") VALUES (" + valuearg.get_Str() + ")"); } + void Insert(const ValueArg& valuearg, std::wstring) { m_db.execute(L"INSERT INTO " + T::getTableName() + L" (" + T::getColumnNames() + L") VALUES (" + valuearg.get_Str() + L")"); } + void UpdateDb(const ValueArg& valuearg, std::string) { m_db.execute("INSERT OR REPLACE INTO " + T::getTableName() + " (" + T::getColumnNames() + ") VALUES (" + valuearg.get_Str() + ")"); } + void UpdateDb(const ValueArg& valuearg, std::wstring) { m_db.execute(L"INSERT OR REPLACE INTO " + T::getTableName() + L" (" + T::getColumnNames() + L") VALUES (" + valuearg.get_Str() + L")"); } + void DeleteAll(std::string) { m_db.execute("DELETE FROM " + T::getTableName()); } + void DeleteAll(std::wstring) { m_db.execute(L"DELETE FROM " + T::getTableName()); } void PopulateVect(sqlite3pp::database &db, sqlite3pp::query &qry ) { for ( auto q : qry )