Repository: LadybirdBrowser/ladybird
File: Libraries/LibDatabase/Database.cpp (C++)

Code before:
    __ENUMERATE_TYPE(UnixDateTime)       \
    __ENUMERATE_TYPE(i8)                 \
    __ENUMERATE_TYPE(i16)                \
    __ENUMERATE_TYPE(i32)                \
    __ENUMERATE_TYPE(long)               \
    __ENUMERATE_TYPE(long long)          \
    __ENUMERATE_TYPE(u8)                 \
    __ENUMERATE_TYPE(u16)                \
    __ENUMERATE_TYPE(u32)                \
    __ENUMERATE_TYPE(unsigned long)      \
    __ENUMERATE_TYPE(unsigned long long) \
    __ENUMERATE_TYPE(bool)

ErrorOr<NonnullRefPtr<Database>> Database::create_memory_backed()
{
    return create(LexicalPath { ":memory:" });
}

ErrorOr<NonnullRefPtr<Database>> Database::create(ByteString const& directory, StringView name)
{
    TRY(Core::Directory::create(directory, Core::Directory::CreateDirectories::Yes));
    LexicalPath database_path { ByteString::formatted("{}/{}.db", directory, name) };
    return create(move(database_path));
}

ErrorOr<NonnullRefPtr<Database>> Database::create(LexicalPath database_path)
{
    sqlite3* m_database { nullptr };
    SQL_TRY(sqlite3_open(database_path.string().characters(), &m_database));

    auto database = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Database(move(database_path), m_database)));

    // Enable the WAL and set the synchronous pragma to normal by default for performance.
    TRY(database->set_journal_mode_pragma(JournalMode::WriteAheadLog));
    TRY(database->set_synchronous_pragma(Synchronous::Normal));

    return database;
}

Database::Database(LexicalPath database_path, sqlite3* database)
    : m_database_path(move(database_path))
    , m_database(database)
{
    VERIFY(m_database);
}

Database::~Database()
{
    for (auto* prepared_statement : m_prepared_statements)
        sqlite3_finalize(prepared_statement);


Review comment (refactor):
Feels like a bit of a weird API difference that the API that accepts a directory will create that directory if it doesn't exist, but the API that accepts a file will not create the parent directory.

Intuitively, I think it makes more sense for the stored path to be `Optional<LexicalPath>`, which is null for in-memory DBs, because getting a path of ":memory:" doesn't seem right. You could have a (private) `create(sqlite3*)` helper to facilitate that, and just make both the file and memory factories call `sqlite3_open`.

Code after:
    __ENUMERATE_TYPE(UnixDateTime)       \
    __ENUMERATE_TYPE(i8)                 \
    __ENUMERATE_TYPE(i16)                \
    __ENUMERATE_TYPE(i32)                \
    __ENUMERATE_TYPE(long)               \
    __ENUMERATE_TYPE(long long)          \
    __ENUMERATE_TYPE(u8)                 \
    __ENUMERATE_TYPE(u16)                \
    __ENUMERATE_TYPE(u32)                \
    __ENUMERATE_TYPE(unsigned long)      \
    __ENUMERATE_TYPE(unsigned long long) \
    __ENUMERATE_TYPE(bool)

ErrorOr<NonnullRefPtr<Database>> Database::create_memory_backed()
{
    sqlite3* sql_database { nullptr };
    SQL_TRY(sqlite3_open(":memory:", &sql_database));
    return create(sql_database);
}

ErrorOr<NonnullRefPtr<Database>> Database::create(ByteString const& directory, StringView name)
{
    TRY(Core::Directory::create(directory, Core::Directory::CreateDirectories::Yes));
    LexicalPath database_path { ByteString::formatted("{}/{}.db", directory, name) };
    sqlite3* sql_database { nullptr };
    SQL_TRY(sqlite3_open(database_path.string().characters(), &sql_database));
    return create(sql_database, database_path);
}

ErrorOr<NonnullRefPtr<Database>> Database::create(sqlite3* sql_database, Optional<LexicalPath> database_path)
{
    auto database = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Database(sql_database, move(database_path))));

    // Enable the WAL and set the synchronous pragma to normal by default for performance.
    TRY(database->set_journal_mode_pragma(JournalMode::WriteAheadLog));
    TRY(database->set_synchronous_pragma(Synchronous::Normal));

    return database;
}

Database::Database(sqlite3* database, Optional<LexicalPath> database_path)
    : m_database_path(move(database_path))
    , m_database(database)
{
    VERIFY(m_database);
}

Database::~Database()
{
    for (auto* prepared_statement : m_prepared_statements)
        sqlite3_finalize(prepared_statement);