mirror of
https://github.com/open-goal/jak-project
synced 2026-07-29 07:33:59 -04:00
d/jak2: cleanup more of editable and editable-player (#2029)
- Rough start of the SQLite integration to facilitate the SQL queries - Cleanup and disable a little bit of code so the game no longer crashes when entering the editor - Implement some of the mouse data stuff  https://user-images.githubusercontent.com/13153231/202881484-399747e7-dcdb-4e09-93e9-b561a45c8a18.mp4 This is a very old branch so best to get it merged now that it's at a decent point so it can be iterated on.
This commit is contained in:
+368
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* @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-2022 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(const Database& aDatabase, const char* apQuery) :
|
||||
mQuery(apQuery),
|
||||
mpSQLite(aDatabase.getHandle()),
|
||||
mpPreparedStatement(prepareStatement()) // prepare the SQL query (needs Database friendship)
|
||||
{
|
||||
mColumnCount = sqlite3_column_count(mpPreparedStatement.get());
|
||||
}
|
||||
|
||||
Statement::Statement(Statement&& aStatement) noexcept :
|
||||
mQuery(std::move(aStatement.mQuery)),
|
||||
mpSQLite(aStatement.mpSQLite),
|
||||
mpPreparedStatement(std::move(aStatement.mpPreparedStatement)),
|
||||
mColumnCount(aStatement.mColumnCount),
|
||||
mbHasRow(aStatement.mbHasRow),
|
||||
mbDone(aStatement.mbDone),
|
||||
mColumnNames(std::move(aStatement.mColumnNames))
|
||||
{
|
||||
aStatement.mpSQLite = nullptr;
|
||||
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(mpPreparedStatement.get());
|
||||
}
|
||||
|
||||
// Clears away all the bindings of a prepared statement (can be associated with #reset() above).
|
||||
void Statement::clearBindings()
|
||||
{
|
||||
const int ret = sqlite3_clear_bindings(getPreparedStatement());
|
||||
check(ret);
|
||||
}
|
||||
|
||||
int Statement::getIndex(const char * const apName) const
|
||||
{
|
||||
return sqlite3_bind_parameter_index(getPreparedStatement(), apName);
|
||||
}
|
||||
|
||||
// Bind an 32bits int value to a parameter "?", "?NNN", ":VVV", "@VVV" or "$VVV" in the SQL prepared statement
|
||||
void Statement::bind(const int aIndex, const int32_t aValue)
|
||||
{
|
||||
const int ret = sqlite3_bind_int(getPreparedStatement(), 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 uint32_t aValue)
|
||||
{
|
||||
const int ret = sqlite3_bind_int64(getPreparedStatement(), 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 int64_t aValue)
|
||||
{
|
||||
const int ret = sqlite3_bind_int64(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(mpSQLite))
|
||||
{
|
||||
throw SQLite::Exception(mpSQLite, 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(mpSQLite))
|
||||
{
|
||||
throw SQLite::Exception(mpSQLite, 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(mpSQLite);
|
||||
}
|
||||
|
||||
int Statement::tryExecuteStep() noexcept
|
||||
{
|
||||
if (mbDone)
|
||||
{
|
||||
return SQLITE_MISUSE; // Statement needs to be reseted !
|
||||
}
|
||||
|
||||
const int ret = sqlite3_step(mpPreparedStatement.get());
|
||||
if (SQLITE_ROW == ret) // one row is ready : call getColumn(N) to access it
|
||||
{
|
||||
mbHasRow = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mbHasRow = false;
|
||||
mbDone = SQLITE_DONE == ret; // check if the query has finished executing
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// 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) const
|
||||
{
|
||||
checkRow();
|
||||
checkIndex(aIndex);
|
||||
|
||||
// Share the Statement Object handle with the new Column created
|
||||
return Column(mpPreparedStatement, 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) const
|
||||
{
|
||||
checkRow();
|
||||
const int index = getColumnIndex(apName);
|
||||
|
||||
// Share the Statement Object handle with the new Column created
|
||||
return Column(mpPreparedStatement, index);
|
||||
}
|
||||
|
||||
// Test if the column is NULL
|
||||
bool Statement::isColumnNull(const int aIndex) const
|
||||
{
|
||||
checkRow();
|
||||
checkIndex(aIndex);
|
||||
return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), aIndex));
|
||||
}
|
||||
|
||||
bool Statement::isColumnNull(const char* apName) const
|
||||
{
|
||||
checkRow();
|
||||
const int index = getColumnIndex(apName);
|
||||
return (SQLITE_NULL == sqlite3_column_type(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), 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(getPreparedStatement(), i);
|
||||
mColumnNames[pName] = i;
|
||||
}
|
||||
}
|
||||
|
||||
const auto 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(getPreparedStatement(), 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(mpSQLite);
|
||||
}
|
||||
|
||||
int Statement::getBindParameterCount() const noexcept
|
||||
{
|
||||
return sqlite3_bind_parameter_count(mpPreparedStatement.get());
|
||||
}
|
||||
|
||||
// Return the numeric result code for the most recent failed API call (if any).
|
||||
int Statement::getErrorCode() const noexcept
|
||||
{
|
||||
return sqlite3_errcode(mpSQLite);
|
||||
}
|
||||
|
||||
// Return the extended numeric result code for the most recent failed API call (if any).
|
||||
int Statement::getExtendedErrorCode() const noexcept
|
||||
{
|
||||
return sqlite3_extended_errcode(mpSQLite);
|
||||
}
|
||||
|
||||
// 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(mpSQLite);
|
||||
}
|
||||
|
||||
// Return a UTF-8 string containing the SQL text of prepared statement with bound parameters expanded.
|
||||
std::string Statement::getExpandedSQL() const {
|
||||
char* expanded = sqlite3_expanded_sql(getPreparedStatement());
|
||||
std::string expandedString(expanded);
|
||||
sqlite3_free(expanded);
|
||||
return expandedString;
|
||||
}
|
||||
|
||||
// Prepare SQLite statement object and return shared pointer to this object
|
||||
Statement::TStatementPtr Statement::prepareStatement()
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
const int ret = sqlite3_prepare_v2(mpSQLite, mQuery.c_str(), static_cast<int>(mQuery.size()), &statement, nullptr);
|
||||
if (SQLITE_OK != ret)
|
||||
{
|
||||
throw SQLite::Exception(mpSQLite, ret);
|
||||
}
|
||||
return Statement::TStatementPtr(statement, [](sqlite3_stmt* stmt)
|
||||
{
|
||||
sqlite3_finalize(stmt);
|
||||
});
|
||||
}
|
||||
|
||||
// Return prepered statement object or throw
|
||||
sqlite3_stmt* Statement::getPreparedStatement() const
|
||||
{
|
||||
sqlite3_stmt* ret = mpPreparedStatement.get();
|
||||
if (ret)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
throw SQLite::Exception("Statement was not prepared.");
|
||||
}
|
||||
|
||||
} // namespace SQLite
|
||||
Reference in New Issue
Block a user