Как отлаживать sqlite3_prepare_v2 и sqlite3_step, не давая ожидаемых значений, когда не генерируются коды ошибок? - PullRequest
0 голосов
/ 05 августа 2020

У меня проблемы с использованием объединения sqlite3 C / C ++ в контексте Unreal Engine 4 ...

Я могу открыть соединение с базой данных и записать в нее данные, проверяя это с помощью DB Browser, но выполнение операторов SELECT с использованием sqlite3_prepare_v2 и sqlite3_step приводит к неожиданному поведению. Я проверяю возвращаемые значения и не вижу никаких кодов ошибок, но я вижу SQLITE_DONE, когда я ожидал бы увидеть SQLITE_ROW, возвращенный sqlite3_step. Если я выхожу из системы с результатом sqlite3_expanded_sql, я получаю ожидаемый оператор со всеми правильно вставленными параметрами, и если я копирую и вставляю его в браузер БД, я получаю ожидаемые результаты.

Вытягиваемые данные указаны c при каждом запуске программы, поэтому я знаю, что не вижу старых или ненужных данных в браузере БД, и я знаю, что мои функции «записи» также работают должным образом.

Что особенно странно заключается в том, что после того, как закомментировал все ссылки на SQLPreparedStatementString (который я использовал в попытке отладить проблему), код работает по-другому: теперь я возвращаю одну строку (вместо ожидаемых 4).

Это почти заставляет меня подозревать, что происходит проблема с памятью, возможно, sqlite3 и Unreal записывают в одни и те же разделы памяти, не сообщая об этом друг другу? Я склонен полагаться на управление памятью UE4 и не имею большого опыта работы с C / C ++ за пределами UE4, поэтому мои знания о таких вещах, как malloc, в значительной степени ограничены общим ощущением, что «вот драконы» и т. Д. 1033 *. Я также понимаю, что sqlite3_prepare_v2 и sqlite3_step - это функции, которые, как правило, выделяют много памяти, но если это вызывает проблему, я удивлен, что это происходит так редко, поскольку я успешно вызываю их несколько тысячу раз (для добавления данных) при каждом запуске игры.

Я понимаю, что можно установить параметры времени компиляции для sqlite, которые предварительно выделяют ему блок памяти, чтобы он не использовал mallo c вообще во время выполнения, но я еще не совсем понял, как это сделать в контексте включения объединения в UE4.

Соответствующий код ниже:

TArray<FName> UOzyBrainLogger::GetLoggedCivsForTurn(const int32 TurnNumber)
{
    TArray<FName> rVal;
    
        sqlite3_stmt *SQLStatement;
        FString SQLCommand = "SELECT DISTINCT CivName FROM Decision WHERE (GameID = ? AND TurnNumber = ?) ;";
        int rc;
        
        rc = sqlite3_prepare_v2(DB, TCHAR_TO_UTF8(*SQLCommand), -1, &SQLStatement, NULL);
        if (rc == SQLITE_OK)
        {
            //FString SQLPreparedStatementString;
            UE_LOG(OzyBrainLogger, Log, TEXT("Getting Civs logged for turn %d in game %s"), TurnNumber, *GameID);
            if(sqlite3_bind_text(  SQLStatement, 1, TCHAR_TO_UTF8(*GameID), -1, SQLITE_STATIC) == SQLITE_OK &&
               sqlite3_bind_int(   SQLStatement, 2, TurnNumber)                                == SQLITE_OK)
            {
                //SQLPreparedStatementString = UTF8_TO_TCHAR(sqlite3_expanded_sql(SQLStatement));
                //UE_LOG(OzyBrainLogger, Log, TEXT("Successfully created statement: %s"), *SQLPreparedStatementString);
                rc = sqlite3_step(SQLStatement);
                if (rc == SQLITE_DONE)
                {
                    UE_LOG(OzyBrainLogger, Log, TEXT("No results found?"));
                }
                else if (rc != SQLITE_ROW)
                {
                    UE_LOG(OzyBrainLogger, Log, TEXT("Return code was: %d"), rc);
                }
                while (rc == SQLITE_ROW)
                {
                    FName CivName = FName(*FString(UTF8_TO_TCHAR((const char*) sqlite3_column_text(SQLStatement, 0))));
                    //UE_LOG(OzyBrainLogger, Log, TEXT("Returning %s for turn %d via statement: %s"), *CivName.ToString(), TurnNumber, *SQLPreparedStatementString);
                    UE_LOG(OzyBrainLogger, Log, TEXT("Returning %s for turn %d"), *CivName.ToString(), TurnNumber);
                    
                    rVal.Add(CivName);
                    rc = sqlite3_step(SQLStatement);
                }
            }
            else
            {
                UE_LOG(OzyBrainLogger, Warning, TEXT("%s"), UTF8_TO_TCHAR(sqlite3_errmsg(DB)));
            }
        }
        else
        {
            UE_LOG(OzyBrainLogger, Warning, TEXT("%s"), UTF8_TO_TCHAR(sqlite3_errmsg(DB)));
        }
        sqlite3_finalize(SQLStatement);
    
    return rVal;
}

Я мог бы упускать что-то действительно очевидное, но я не уверен, как исследовать это дальше, чтобы добиться прогресса, поэтому любые советы о путях исследования были бы очень полезны.

Я чувствую себя немного потерянным!

1 Ответ

1 голос
/ 06 августа 2020

Кажется, мне удалось это исправить! Проблема, по-видимому, связана с комбинацией:

  1. Вызов sqlite3_open в конструкторе моего объекта, ожидая одноэлементного поведения, когда UE4 время от времени молча вызывает конструкторы (например, при компиляции).
  2. Использование SQLITE_STATIC вместо SQLITE_TRANSIENT
  3. Добавление кода возврата (rc), абсолютная проверка везде

Устранение обеих этих проблем похоже, решила основную проблему.

Последней проблемой (возвращение только 2 строк вместо 4) была ошибка более пешеходной ладьи ie (я бы переставил вызов sqlite3_step на do ... while l oop и случайно закончил тем, что вызвал sqlite3_step дважды за итерацию!

Вещи, которые не устранили основную проблему, включали вызов sqlite3_config с SQLITE_CONFIG_MEMSTATUS, SQLITE_CONFIG_LOOKASIDE и SQLITE_CONFIG_HEAP, однако : когда я начал правильно проверять коды возврата для этих вызовов и обнаруживать ошибки, это было основным ключом к пониманию того, что что-то уже запустило соединение с базой данных без моего понимания g it (т.е. тихий вызов конструктора UE4 при компиляции)

...