Database.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/ByteString.h>
  7. #include <AK/String.h>
  8. #include <AK/Time.h>
  9. #include <LibCore/Directory.h>
  10. #include <LibCore/StandardPaths.h>
  11. #include <LibWebView/Database.h>
  12. #include <sqlite3.h>
  13. namespace WebView {
  14. static constexpr StringView sql_error(int error_code)
  15. {
  16. char const* _sql_error = sqlite3_errstr(error_code);
  17. return { _sql_error, __builtin_strlen(_sql_error) };
  18. }
  19. #define SQL_TRY(expression) \
  20. ({ \
  21. /* Ignore -Wshadow to allow nesting the macro. */ \
  22. AK_IGNORE_DIAGNOSTIC("-Wshadow", auto _sql_result = (expression)); \
  23. if (_sql_result != SQLITE_OK) [[unlikely]] \
  24. return Error::from_string_view(sql_error(_sql_result)); \
  25. })
  26. #define SQL_MUST(expression) \
  27. ({ \
  28. /* Ignore -Wshadow to allow nesting the macro. */ \
  29. AK_IGNORE_DIAGNOSTIC("-Wshadow", auto _sql_result = (expression)); \
  30. if (_sql_result != SQLITE_OK) [[unlikely]] { \
  31. warnln("\033[31;1mDatabase error\033[0m: {}: {}", sql_error(_sql_result), sqlite3_errmsg(m_database)); \
  32. VERIFY_NOT_REACHED(); \
  33. } \
  34. })
  35. ErrorOr<NonnullRefPtr<Database>> Database::create()
  36. {
  37. // FIXME: Move this to a generic "Ladybird data directory" helper.
  38. auto database_path = ByteString::formatted("{}/Ladybird", Core::StandardPaths::data_directory());
  39. TRY(Core::Directory::create(database_path, Core::Directory::CreateDirectories::Yes));
  40. auto database_file = ByteString::formatted("{}/Ladybird.db", database_path);
  41. sqlite3* m_database { nullptr };
  42. SQL_TRY(sqlite3_open(database_file.characters(), &m_database));
  43. return adopt_nonnull_ref_or_enomem(new (nothrow) Database(m_database));
  44. }
  45. Database::Database(sqlite3* database)
  46. : m_database(database)
  47. {
  48. VERIFY(m_database);
  49. }
  50. Database::~Database()
  51. {
  52. for (auto* prepared_statement : m_prepared_statements)
  53. sqlite3_finalize(prepared_statement);
  54. sqlite3_close(m_database);
  55. }
  56. ErrorOr<Database::StatementID> Database::prepare_statement(StringView statement)
  57. {
  58. sqlite3_stmt* prepared_statement { nullptr };
  59. SQL_TRY(sqlite3_prepare_v2(m_database, statement.characters_without_null_termination(), static_cast<int>(statement.length()), &prepared_statement, nullptr));
  60. auto statement_id = m_prepared_statements.size();
  61. m_prepared_statements.append(prepared_statement);
  62. return statement_id;
  63. }
  64. void Database::execute_statement(StatementID statement_id, OnResult on_result)
  65. {
  66. auto* statement = prepared_statement(statement_id);
  67. while (true) {
  68. auto result = sqlite3_step(statement);
  69. switch (result) {
  70. case SQLITE_DONE:
  71. SQL_MUST(sqlite3_reset(statement));
  72. return;
  73. case SQLITE_ROW:
  74. if (on_result)
  75. on_result(statement_id);
  76. continue;
  77. default:
  78. SQL_MUST(result);
  79. return;
  80. }
  81. }
  82. }
  83. template<typename ValueType>
  84. void Database::apply_placeholder(StatementID statement_id, int index, ValueType const& value)
  85. {
  86. auto* statement = prepared_statement(statement_id);
  87. if constexpr (IsSame<ValueType, String>) {
  88. StringView string { value };
  89. SQL_MUST(sqlite3_bind_text(statement, index, string.characters_without_null_termination(), static_cast<int>(string.length()), SQLITE_TRANSIENT));
  90. } else if constexpr (IsSame<ValueType, UnixDateTime>) {
  91. SQL_MUST(sqlite3_bind_int64(statement, index, value.offset_to_epoch().to_milliseconds()));
  92. } else if constexpr (IsSame<ValueType, int>) {
  93. SQL_MUST(sqlite3_bind_int(statement, index, value));
  94. } else if constexpr (IsSame<ValueType, bool>) {
  95. SQL_MUST(sqlite3_bind_int(statement, index, static_cast<int>(value)));
  96. }
  97. }
  98. template void Database::apply_placeholder(StatementID, int, String const&);
  99. template void Database::apply_placeholder(StatementID, int, UnixDateTime const&);
  100. template void Database::apply_placeholder(StatementID, int, int const&);
  101. template void Database::apply_placeholder(StatementID, int, bool const&);
  102. template<typename ValueType>
  103. ValueType Database::result_column(StatementID statement_id, int column)
  104. {
  105. auto* statement = prepared_statement(statement_id);
  106. if constexpr (IsSame<ValueType, String>) {
  107. auto const* text = reinterpret_cast<char const*>(sqlite3_column_text(statement, column));
  108. return MUST(String::from_utf8(StringView { text, strlen(text) }));
  109. } else if constexpr (IsSame<ValueType, UnixDateTime>) {
  110. auto milliseconds = sqlite3_column_int64(statement, column);
  111. return UnixDateTime::from_milliseconds_since_epoch(milliseconds);
  112. } else if constexpr (IsSame<ValueType, int>) {
  113. return sqlite3_column_int(statement, column);
  114. } else if constexpr (IsSame<ValueType, bool>) {
  115. return static_cast<bool>(sqlite3_column_int(statement, column));
  116. }
  117. VERIFY_NOT_REACHED();
  118. }
  119. template String Database::result_column(StatementID, int);
  120. template UnixDateTime Database::result_column(StatementID, int);
  121. template int Database::result_column(StatementID, int);
  122. template bool Database::result_column(StatementID, int);
  123. }