text: /**
   * @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-2021 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 "SQLiteCpp/Statement.h"
  
  #include "SQLiteCpp/Database.h"
  #include "SQLiteCpp/Column.h"
  #include "SQLiteCpp/Assertion.h"
  #include "SQLiteCpp/Exception.h"
  
  #include <sqlite3.h>
  
  namespace SQLite
  {
  
  Statement::Statement(Database &aDatabase, const char* apQuery) :
      mQuery(apQuery),
      mStmtPtr(aDatabase.getHandle(), mQuery), // prepare the SQL query, and ref count (needs Database friendship)
      mColumnCount(0),
      mbHasRow(false),
      mbDone(false)
  {
      mColumnCount = sqlite3_column_count(mStmtPtr);
  }
  
  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;
  }
  
  // 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);
  }
  
  int Statement::getIndex(const char * const apName)
  {
      return sqlite3_bind_parameter_index(mStmtPtr, apName);
  }
  
  // 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<int>(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<int>(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);
  }
  
  
  // 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, and return the number of changes.
  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;
  }
  
  const char * Statement::getColumnDeclaredType(const int aIndex) const
  {
      checkIndex(aIndex);
      const char * result = sqlite3_column_decltype(mStmtPtr, aIndex);
      if (!result)
      {
          throw SQLite::Exception("Could not determine declared column type.");
      }
      else
      {
          return result;
      }
  }
  
  // Get number of rows modified by last INSERT, UPDATE or DELETE statement (not DROP table).
  int Statement::getChanges() const noexcept
  {
      return sqlite3_changes(mStmtPtr);
  }
  
  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
  {
      return sqlite3_errcode(mStmtPtr);
  }
  
  // Return the extended numeric result code for the most recent failed API call (if any).
  int Statement::getExtendedErrorCode() const noexcept
  {
      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
  {
      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<int>(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(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);
  }
  
  Statement::Ptr::Ptr(Ptr&& aPtr) :
      mpSQLite(aPtr.mpSQLite),
      mpStmt(aPtr.mpStmt),
      mpRefCount(aPtr.mpRefCount)
  {
      aPtr.mpSQLite = nullptr;
      aPtr.mpStmt = nullptr;
      aPtr.mpRefCount = nullptr;
  }
  
  /**
   * @brief Decrement the ref counter and finalize the sqlite3_stmt when it reaches 0
   */
  Statement::Ptr::~Ptr()
  {
      if (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 = nullptr;
              mpStmt = nullptr;
          }
          // else, the finalization will be done later, by the last object
      }
  }
  
  
  }  // namespace SQLite
  