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
This commit is contained in:
Sébastien Rombauts 2015-04-30 18:39:46 +02:00
parent d45ec996a3
commit 7fbfc29677
3 changed files with 72 additions and 15 deletions

View File

@ -12,6 +12,7 @@
#include <sqlite3.h>
#include <string>
#include <map>
#include <SQLiteCpp/Exception.h>
@ -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<std::string, int> 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
};

View File

@ -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

View File

@ -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");
}