From 7fbfc2967779d0baff53ac09603b4c053ce6473d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Rombauts?= Date: Thu, 30 Apr 2015 18:39:46 +0200 Subject: [PATCH] Fix #23 optimized Statement::getColumn() by name - fix Statement::getColumn(apName) provided by #45 but was not working instead of using #46 that conflicts with current master - rework it by using a map of columns name as a cache populated the first time the method is called - add corresponding Unit Test --- include/SQLiteCpp/Statement.h | 21 +++++++++++------- src/Statement.cpp | 24 ++++++++++++++------ tests/Statement_test.cpp | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/include/SQLiteCpp/Statement.h b/include/SQLiteCpp/Statement.h index 7dab620..5b620b3 100644 --- a/include/SQLiteCpp/Statement.h +++ b/include/SQLiteCpp/Statement.h @@ -12,6 +12,7 @@ #include #include +#include #include @@ -274,7 +275,8 @@ public: * Can be used to access the data of the current row of result when applicable, * while the executeStep() method returns true. * - * Throw an exception if there is no row to return a Column from : + * Throw an exception if there is no row to return a Column from: + * - if provided index is out of bound * - before any executeStep() call * - after the last executeStep() returned false * - after a reset() call @@ -283,8 +285,7 @@ public: * * @param[in] aIndex Index of the column, starting at 0 * - * @note This method is no more const, starting in v0.5, - * which reflects the fact that the returned Column object will + * @note This method is not const, reflecting the fact that the returned Column object will * share the ownership of the underlying sqlite3_stmt. * * @warning The resulting Column object must not be memorized "as-is". @@ -302,16 +303,16 @@ public: * while the executeStep() method returns true. * * Throw an exception if there is no row to return a Column from : + * - if provided name is not one of the aliased column names * - before any executeStep() call * - after the last executeStep() returned false * - after a reset() call * * Throw an exception if the specified index is out of the [0, getColumnCount()) range. * - * @param[in] aName Name of the column, starting at index 0 + * @param[in] apName Name of the column, starting at index 0 * - * @note This method is no more const, starting in v0.5, - * which reflects the fact that the returned Column object will + * @note This method is not const, reflecting the fact that the returned Column object will * share the ownership of the underlying sqlite3_stmt. * * @warning The resulting Column object must not be memorized "as-is". @@ -320,8 +321,8 @@ public: * Thus, you should instead extract immediately its data (getInt(), getText()...) * and use or copy this data for any later usage. */ - Column getColumn(const char* aName); - + Column getColumn(const char* apName); + /** * @brief Test if the column value is NULL * @@ -424,10 +425,14 @@ private: */ void check(const int aRet); +private: + typedef std::map TColumnNames; + private: std::string mQuery; //!< UTF-8 SQL Query Ptr mStmtPtr; //!< Shared Pointer to the prepared SQLite Statement Object int mColumnCount; //!< Number of columns in the result of the prepared statement + TColumnNames mColumnNames; //!< Map of columns index by name bool mbOk; //!< true when a row has been fetched with executeStep() bool mbDone; //!< true when the last executeStep() had no more row to fetch }; diff --git a/src/Statement.cpp b/src/Statement.cpp index 4b5a73e..597fcae 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -258,9 +258,9 @@ Column Statement::getColumn(const int aIndex) // Return a copy of the column data specified by its column name starting at 0 // (use the Column copy-constructor) -Column Statement::getColumn(const char* aName) +Column Statement::getColumn(const char* apName) { - int aIndex = -1; + int Index = -1; if (false == mbOk) { @@ -268,18 +268,28 @@ Column Statement::getColumn(const char* aName) } else { - for (int i = 0; i < mColumnCount; ++i) { - if (sqlite3_column_name(mStmtPtr, i) == aName) - break; + if (mColumnNames.empty()) + { + for (int i = 0; i < mColumnCount; ++i) + { + const char* pName = sqlite3_column_name(mStmtPtr, i); + mColumnNames[pName] = i; + } } - if ((aIndex < 0) || (aIndex >= mColumnCount)) { + const TColumnNames::const_iterator iIndex = mColumnNames.find(apName); + if (iIndex != mColumnNames.end()) + { + Index = (*iIndex).second; + } + else + { throw SQLite::Exception("Column index out of range"); } } // Share the Statement Object handle with the new Column created - return Column(mStmtPtr, aIndex); + return Column(mStmtPtr, Index); } // Test if the column is NULL diff --git a/tests/Statement_test.cpp b/tests/Statement_test.cpp index 4a16dc1..55898be 100644 --- a/tests/Statement_test.cpp +++ b/tests/Statement_test.cpp @@ -77,3 +77,45 @@ TEST(Statement, invalid) { } // Close DB test.db3 remove("test.db3"); } + + +TEST(Statement, getColumnByName) { + remove("test.db3"); + { + // Create a new database + SQLite::Database db("test.db3", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + EXPECT_EQ(SQLITE_OK, db.getErrorCode()); + EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode()); + + // Create a new table + EXPECT_EQ(0, db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, msg TEXT, int INTEGER, double REAL)")); + EXPECT_EQ(SQLITE_OK, db.getErrorCode()); + EXPECT_EQ(SQLITE_OK, db.getExtendedErrorCode()); + + // Create a first row + EXPECT_EQ(1, db.exec("INSERT INTO test VALUES (NULL, \"first\", 123, 0.123)")); + EXPECT_EQ(1, db.getLastInsertRowid()); + EXPECT_EQ(1, db.getTotalChanges()); + + // Compile a SQL query + SQLite::Statement query(db, "SELECT * FROM test"); + EXPECT_STREQ("SELECT * FROM test", query.getQuery().c_str()); + EXPECT_EQ(4, query.getColumnCount()); + query.executeStep(); + EXPECT_TRUE (query.isOk()); + EXPECT_FALSE(query.isDone()); + + // Look for unexisting columns + EXPECT_THROW(query.getColumn("unknown"), SQLite::Exception); + EXPECT_THROW(query.getColumn(""), SQLite::Exception); + + const std::string msg = query.getColumn("msg"); + const int integer = query.getColumn("int"); + const double real = query.getColumn("double"); + EXPECT_EQ("first", msg); + EXPECT_EQ(123, integer); + EXPECT_EQ(0.123, real); + + } // Close DB test.db3 + remove("test.db3"); +}