/** * @file Statement.cpp * @ingroup SQLiteCpp * @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 #include #include #include #include #include namespace SQLite { // Compile and register the SQL query for the provided SQLite Database Connection Statement::Statement(Database& aDatabase, const char* apQuery) : mQuery(apQuery) , mStmtPtr(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 Statement::Statement(Database& aDatabase, const std::string& aQuery) : mQuery(aQuery) , mStmtPtr(aDatabase.mpSQLite, mQuery) , // prepare the SQL query, and ref count (needs Database friendship) mColumnCount(0) , mbHasRow(false) , mbDone(false) { mColumnCount = sqlite3_column_count(mStmtPtr); } #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600) Statement::Statement(Statement&& 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; } #endif // Finalize and unregister the SQL query from the SQLite Database Connection. Statement::~Statement() { // the finalization will be done by the destructor of the last shared pointer } // Reset the statement to make it ready for a new execution (see also #clearBindings() bellow) void Statement::reset() { const int ret = tryReset(); check(ret); } int Statement::tryReset() noexcept { mbHasRow = false; mbDone = false; return sqlite3_reset(mStmtPtr); } // Clears away all the bindings of a prepared statement (can be associated with #reset() above). void Statement::clearBindings() { const int ret = sqlite3_clear_bindings(mStmtPtr); check(ret); } // Bind an int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const int aValue) { const int ret = sqlite3_bind_int(mStmtPtr, aIndex, aValue); check(ret); } // Bind a 32bits unsigned int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const unsigned aValue) { const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue); check(ret); } // Bind a 64bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const long long aValue) { const int ret = sqlite3_bind_int64(mStmtPtr, aIndex, aValue); check(ret); } // Bind a double (64bits float) value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const double aValue) { const int ret = sqlite3_bind_double(mStmtPtr, aIndex, aValue); check(ret); } // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const std::string& aValue) { const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(), static_cast(aValue.size()), SQLITE_TRANSIENT); check(ret); } // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const char* apValue) { const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_TRANSIENT); check(ret); } // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex, const void* apValue, const int aSize) { const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_TRANSIENT); check(ret); } // Bind a string value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const int aIndex, const std::string& aValue) { const int ret = sqlite3_bind_text(mStmtPtr, aIndex, aValue.c_str(), static_cast(aValue.size()), SQLITE_STATIC); check(ret); } // Bind a text value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const int aIndex, const char* apValue) { const int ret = sqlite3_bind_text(mStmtPtr, aIndex, apValue, -1, SQLITE_STATIC); check(ret); } // Bind a binary blob value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const int aIndex, const void* apValue, const int aSize) { const int ret = sqlite3_bind_blob(mStmtPtr, aIndex, apValue, aSize, SQLITE_STATIC); check(ret); } // Bind a NULL value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const int aIndex) { const int ret = sqlite3_bind_null(mStmtPtr, aIndex); check(ret); } // Bind an int value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const int aValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_int(mStmtPtr, index, aValue); check(ret); } // Bind a 32bits unsigned int value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const unsigned aValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_int64(mStmtPtr, index, aValue); check(ret); } // Bind a 64bits int value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const long long aValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_int64(mStmtPtr, index, aValue); check(ret); } // Bind a double (64bits float) value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const double aValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_double(mStmtPtr, index, aValue); check(ret); } // Bind a string value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const std::string& aValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_text(mStmtPtr, index, aValue.c_str(), static_cast(aValue.size()), SQLITE_TRANSIENT); check(ret); } // Bind a text value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const char* apValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_text(mStmtPtr, index, apValue, -1, SQLITE_TRANSIENT); check(ret); } // Bind a binary blob value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName, const void* apValue, const int aSize) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_blob(mStmtPtr, index, apValue, aSize, SQLITE_TRANSIENT); check(ret); } // Bind a string value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const char* apName, const std::string& aValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_text(mStmtPtr, index, aValue.c_str(), static_cast(aValue.size()), SQLITE_STATIC); check(ret); } // Bind a text value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const char* apName, const char* apValue) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_text(mStmtPtr, index, apValue, -1, SQLITE_STATIC); check(ret); } // Bind a binary blob value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bindNoCopy(const char* apName, const void* apValue, const int aSize) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_blob(mStmtPtr, index, apValue, aSize, SQLITE_STATIC); check(ret); } // Bind a NULL value to a parameter "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement void Statement::bind(const char* apName) { const int index = sqlite3_bind_parameter_index(mStmtPtr, apName); const int ret = sqlite3_bind_null(mStmtPtr, index); check(ret); } // Execute a step of the query to fetch one row of results bool Statement::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 SQLite::Exception(mStmtPtr, ret); } else { throw SQLite::Exception("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 Statement::exec() { const int ret = tryExecuteStep(); if (SQLITE_DONE != ret) // the statement has finished executing successfully { if (SQLITE_ROW == ret) { throw SQLite::Exception("exec() does not expect results. Use executeStep."); } else if (ret == sqlite3_errcode(mStmtPtr)) { throw SQLite::Exception(mStmtPtr, ret); } else { throw SQLite::Exception("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 Statement::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 a copy of the column data specified by its index starting at 0 // (use the Column copy-constructor) Column Statement::getColumn(const int aIndex) { checkRow(); checkIndex(aIndex); // Share the Statement Object handle with the new Column created return Column(mStmtPtr, 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* apName) { checkRow(); const int index = getColumnIndex(apName); // Share the Statement Object handle with the new Column created return Column(mStmtPtr, index); } // Test if the column is NULL bool Statement::isColumnNull(const int aIndex) const { checkRow(); checkIndex(aIndex); return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, aIndex)); } bool Statement::isColumnNull(const char* apName) const { checkRow(); const int index = getColumnIndex(apName); return (SQLITE_NULL == sqlite3_column_type(mStmtPtr, index)); } // Return the named assigned to the specified result column (potentially aliased) const char* Statement::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* Statement::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 Statement::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()) { throw SQLite::Exception("Unknown column name."); } return (*iIndex).second; } int Statement::getBindParameterCount() const noexcept { return sqlite3_bind_parameter_count(mStmtPtr); } // Return the numeric result code for the most recent failed API call (if any). int Statement::getErrorCode() const noexcept // nothrow { return sqlite3_errcode(mStmtPtr); } // Return the extended numeric result code for the most recent failed API call (if any). int Statement::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* Statement::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 Statement::getExpandedSQL() { char* expanded = sqlite3_expanded_sql(mStmtPtr); std::string expandedString(expanded); sqlite3_free(expanded); return expandedString; } //////////////////////////////////////////////////////////////////////////////// // Internal class : shared pointer to the sqlite3_stmt SQLite Statement Object //////////////////////////////////////////////////////////////////////////////// /** * @brief Prepare the statement and initialize its reference counter * * @param[in] apSQLite The sqlite3 database connexion * @param[in] aQuery The SQL query string to prepare */ Statement::Ptr::Ptr(sqlite3* apSQLite, std::string& aQuery) : mpSQLite(apSQLite) , mpStmt(NULL) , mpRefCount(NULL) { const int ret = sqlite3_prepare_v2(apSQLite, aQuery.c_str(), static_cast(aQuery.size()), &mpStmt, NULL); if (SQLITE_OK != ret) { throw SQLite::Exception(apSQLite, ret); } // Initialize the reference counter of the sqlite3_stmt : // used to share the mStmtPtr between Statement and Column objects; // This is needed to enable Column objects to live longer than the Statement objet it refers to. mpRefCount = new unsigned int(1); // NOLINT(readability/casting) } /** * @brief Copy constructor increments the ref counter * * @param[in] aPtr Pointer to copy */ Statement::Ptr::Ptr(const Statement::Ptr& aPtr) : mpSQLite(aPtr.mpSQLite) , mpStmt(aPtr.mpStmt) , mpRefCount(aPtr.mpRefCount) { assert(NULL != mpRefCount); assert(0 != *mpRefCount); // Increment the reference counter of the sqlite3_stmt, // asking not to finalize the sqlite3_stmt during the lifetime of the new objet ++(*mpRefCount); } #if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600) Statement::Ptr::Ptr(Ptr&& aPtr) : mpSQLite(aPtr.mpSQLite) , mpStmt(aPtr.mpStmt) , mpRefCount(aPtr.mpRefCount) { aPtr.mpSQLite = NULL; aPtr.mpStmt = NULL; aPtr.mpRefCount = NULL; } #endif /** * @brief Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0 */ Statement::Ptr::~Ptr() { if (NULL != mpRefCount) { assert(0 != *mpRefCount); // Decrement and check the reference counter of the sqlite3_stmt --(*mpRefCount); if (0 == *mpRefCount) { // If count reaches zero, finalize the sqlite3_stmt, as no Statement nor Column objet use it anymore. // No need to check the return code, as it is the same as the last statement evaluation. sqlite3_finalize(mpStmt); // and delete the reference counter delete mpRefCount; mpRefCount = NULL; mpStmt = NULL; } // else, the finalization will be done later, by the last object } } } // namespace SQLite