summaryrefslogtreecommitdiff
path: root/platform/android/src/sqlite3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/src/sqlite3.cpp')
-rw-r--r--platform/android/src/sqlite3.cpp121
1 files changed, 121 insertions, 0 deletions
diff --git a/platform/android/src/sqlite3.cpp b/platform/android/src/sqlite3.cpp
new file mode 100644
index 0000000000..e316ea9890
--- /dev/null
+++ b/platform/android/src/sqlite3.cpp
@@ -0,0 +1,121 @@
+#include "../default/sqlite3.hpp"
+
+#include "android/database/sqlite/sqlite_database.hpp"
+#include "android/database/sqlite/sqlite_exception.hpp"
+#include "java/lang/throwable.hpp"
+
+#include "attach_env.hpp"
+
+using namespace mbgl::android;
+
+using android::database::sqlite::SQLiteDatabase;
+using android::database::sqlite::SQLiteCantOpenDatabaseException;
+using android::database::sqlite::SQLiteDatabaseCorruptException;
+using java::lang::Throwable;
+
+namespace mapbox {
+namespace sqlite {
+
+template <typename Lambda>
+void checkException(jni::JNIEnv& env, Lambda&& lambda) {
+ try {
+ lambda();
+ } catch (const jni::PendingJavaException&) {
+ auto throwable = jni::Object<Throwable>(jni::ExceptionOccurred(env));
+ jni::ExceptionClear(env);
+ const std::string message =
+ jni::Make<std::string>(env, Throwable::GetMessage(env, throwable));
+ if (throwable.IsInstanceOf(env, SQLiteCantOpenDatabaseException::Class(env))) {
+ throw Exception{ Exception::Code::CANTOPEN, message };
+ } else if (throwable.IsInstanceOf(env, SQLiteDatabaseCorruptException::Class(env))) {
+ throw Exception{ Exception::Code::NOTADB, message };
+ } else {
+ throw Exception{ 1 /* SQLITE_ERROR */, message };
+ }
+ }
+}
+
+class DatabaseImpl {
+public:
+ DatabaseImpl(const std::string& filename, OpenFlag openFlags) {
+ int flags = 0;
+ if (openFlags & OpenFlag::ReadOnly) {
+ flags |= SQLiteDatabase::OPEN_READONLY;
+ }
+ if (openFlags & OpenFlag::ReadWrite) {
+ flags |= SQLiteDatabase::OPEN_READWRITE;
+ }
+ if (openFlags & OpenFlag::Create) {
+ flags |= SQLiteDatabase::CREATE_IF_NECESSARY;
+ }
+
+ checkException(*env, [&] {
+ db = SQLiteDatabase::OpenDatabase(*env, jni::Make<jni::String>(*env, filename), flags).NewGlobalRef(*env);
+ });
+ }
+
+ UniqueEnv env{ AttachEnv() };
+ jni::UniqueObject<android::database::sqlite::SQLiteDatabase> db;
+};
+
+Database::Database(const std::string& filename, int flags)
+ : impl(std::make_unique<DatabaseImpl>(filename, OpenFlag(flags))) {
+}
+
+Database::Database(Database&& other) : impl(std::move(other.impl)) {
+}
+
+Database& Database::operator=(Database&& other) {
+ std::swap(impl, other.impl);
+ return *this;
+}
+
+Database::~Database() {
+}
+
+Database::operator bool() const {
+ return impl.operator bool();
+}
+
+void Database::setBusyTimeout(std::chrono::milliseconds timeout) {
+ // Java bindings don't support setting a direct busy_timeout.
+ exec(std::string{ "PRAGMA busy_timeout = " } +
+ std::to_string(std::min<std::chrono::milliseconds::rep>(timeout.count(),
+ std::numeric_limits<int>::max())));
+}
+
+void Database::exec(const std::string &sql) {
+ assert(impl);
+ checkException(*impl->env, [&] {
+ SQLiteDatabase::ExecSQL(*impl->env, *impl->db, jni::Make<jni::String>(*impl->env, sql));
+ });
+}
+
+Statement Database::prepare(const char *query) {
+ assert(impl);
+ return Statement(this, query);
+}
+
+Statement::Statement(Database *db, const char *sql)
+ : impl(std::make_unique<StatementImpl>(db->impl->db, sql))
+{
+}
+
+Statement::Statement(Statement &&other) {
+ *this = std::move(other);
+}
+
+Statement &Statement::operator=(Statement &&other) {
+ std::swap(impl, other.impl);
+ return *this;
+}
+
+Statement::~Statement() {
+}
+
+Statement::operator bool() const {
+ return impl.operator bool();
+}
+
+} // namespace sqlite
+} // namespace mapbox