/** * @file Statement.cpp * @ingroup CSQLite * @brief A prepared SQLite Statement is a compiled SQL query ready to be executed, pointing to a row of result. * * Copyright (c) 2012-2019 Sebastien Rombauts (sebastien.rombauts@gmail.com) * * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt * or copy at http://opensource.org/licenses/MIT) */ #include "pch.h" #include "CSQLite/CSQLite.h" #include // Compile and register the SQL query for the provided SQLite Database Connection CSQLiteStatement::CSQLiteStatement(CSQLiteDB &aDatabase, const char* apQuery) : mQuery(apQuery), mStmtPtr(std::make_shared<_Statement>(aDatabase.mpSQLite, mQuery)), // prepare the SQL query, and ref count (needs Database friendship) mColumnCount(0), mbHasRow(false), mbDone(false) { mColumnCount = sqlite3_column_count(*mStmtPtr); } // Compile and register the SQL query for the provided SQLite Database Connection CSQLiteStatement::CSQLiteStatement(CSQLiteDB &aDatabase, const std::string& aQuery) : mQuery(aQuery), mStmtPtr(std::make_shared<_Statement>(aDatabase.mpSQLite, mQuery)), // prepare the SQL query, and ref count (needs Database friendship) mColumnCount(0), mbHasRow(false), mbDone(false) { mColumnCount = sqlite3_column_count(*mStmtPtr); } CSQLiteStatement::CSQLiteStatement(CSQLiteStatement&& aStatement) noexcept : mQuery(std::move(aStatement.mQuery)), mStmtPtr(std::move(aStatement.mStmtPtr)), mColumnCount(aStatement.mColumnCount), mbHasRow(aStatement.mbHasRow), mbDone(aStatement.mbDone) { aStatement.mColumnCount = 0; aStatement.mbHasRow = false; aStatement.mbDone = false; } // Finalize and unregister the SQL query from the SQLite Database Connection. CSQLiteStatement::~CSQLiteStatement() { // the finalization will be done by the destructor of the last shared pointer } // Execute a step of the query to fetch one row of results bool CSQLiteStatement::executeStep() { const int ret = tryExecuteStep(); if ((SQLITE_ROW != ret) && (SQLITE_DONE != ret)) // on row or no (more) row ready, else it's a problem { if (ret == sqlite3_errcode(*mStmtPtr)) { throw CSQLiteException(*mStmtPtr, ret); } else { throw CSQLiteException("Statement needs to be reseted", ret); } } return mbHasRow; // true only if one row is accessible by getColumn(N) } // Execute a one-step query with no expected result int CSQLiteStatement::exec() { const int ret = tryExecuteStep(); if (SQLITE_DONE != ret) // the statement has finished executing successfully { if (SQLITE_ROW == ret) { throw CSQLiteException("exec() does not expect results. Use executeStep."); } else if (ret == sqlite3_errcode(*mStmtPtr)) { throw CSQLiteException(*mStmtPtr, ret); } else { throw CSQLiteException("Statement needs to be reseted", ret); } } // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE) return sqlite3_changes(*mStmtPtr); } int CSQLiteStatement::tryExecuteStep() noexcept { if (false == mbDone) { const int ret = sqlite3_step(*mStmtPtr); if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it { mbHasRow = true; } else if (SQLITE_DONE == ret) // no (more) row ready : the query has finished executing { mbHasRow = false; mbDone = true; } else { mbHasRow = false; mbDone = false; } return ret; } else { // Statement needs to be reseted ! return SQLITE_MISUSE; } } // Return the named assigned to the specified result column (potentially aliased) const char* CSQLiteStatement::getColumnName(const int aIndex) const { checkIndex(aIndex); return sqlite3_column_name(*mStmtPtr, aIndex); } #ifdef SQLITE_ENABLE_COLUMN_METADATA // Return the named assigned to the specified result column (potentially aliased) const char* CSQLiteStatement::getColumnOriginName(const int aIndex) const { checkIndex(aIndex); return sqlite3_column_origin_name(*mStmtPtr, aIndex); } #endif // Return the index of the specified (potentially aliased) column name int CSQLiteStatement::getColumnIndex(const char* apName) const { // Build the map of column index by name on first call if (mColumnNames.empty()) { for (int i = 0; i < mColumnCount; ++i) { const char* pName = sqlite3_column_name(*mStmtPtr, i); mColumnNames[pName] = i; } } const TColumnNames::const_iterator iIndex = mColumnNames.find(apName); if (iIndex == mColumnNames.end()) { std::string errStr = "Unknown column name:"; errStr += apName; throw CSQLiteException(errStr); } return (*iIndex).second; } int CSQLiteStatement::getBindParameterCount() const noexcept { return sqlite3_bind_parameter_count(*mStmtPtr); } // Return the numeric result code for the most recent failed API call (if any). int CSQLiteStatement::getErrorCode() const noexcept // nothrow { return sqlite3_errcode(*mStmtPtr); } // Return the extended numeric result code for the most recent failed API call (if any). int CSQLiteStatement::getExtendedErrorCode() const noexcept // nothrow { return sqlite3_extended_errcode(*mStmtPtr); } // Return UTF-8 encoded English language explanation of the most recent failed API call (if any). const char* CSQLiteStatement::getErrorMsg() const noexcept // nothrow { return sqlite3_errmsg(*mStmtPtr); } // Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded. std::string CSQLiteStatement::getExpandedSQL() { char* expanded = sqlite3_expanded_sql(*mStmtPtr); std::string expandedString(expanded); sqlite3_free(expanded); return expandedString; }