blob: f1f83acf8f2278776f7e2da345bf08345a3eb273 [file] [log] [blame]
// Copyright 2024 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <jni.h>
#include <string>
#include "src/main/cpp/util/logging.h"
#include "src/main/native/latin1_jni_path.h"
#include "sqlite3.h"
namespace blaze_jni {
namespace {
// A RAII wrapper around a null-terminated UTF string obtained from a jstring.
class ScopedUTFString {
public:
ScopedUTFString(JNIEnv *env, jstring jstr)
: env_(env),
jstr_(jstr),
str_(env->GetStringUTFChars(jstr, nullptr)),
len_(env->GetStringUTFLength(jstr)) {}
~ScopedUTFString() { env_->ReleaseStringUTFChars(jstr_, str_); }
const char *c_str() const { return str_; }
int length() const { return len_; }
private:
JNIEnv *env_;
jstring jstr_;
const char *str_;
int len_;
};
// Throws an exception for the given JNI function name and SQLite error code.
void PostException(JNIEnv *env, const std::string &fn, int err) {
std::string message =
fn + ": [" + std::to_string(err) + "] " + sqlite3_errstr(err);
bool success = false;
jclass exc_class = env->FindClass("java/io/IOException");
if (exc_class != nullptr) {
success = env->ThrowNew(exc_class, message.c_str()) == 0;
}
if (!success) {
BAZEL_LOG(FATAL) << "Failed to throw Java exception from JNI: "
<< message.c_str();
}
}
} // namespace
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_openConn(
JNIEnv *env, jclass cls, jstring path_str) {
const char *path = GetStringLatin1Chars(env, path_str);
sqlite3 *conn = nullptr;
int err = sqlite3_open(path, &conn);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_open", err);
}
ReleaseStringLatin1Chars(path);
return reinterpret_cast<jlong>(conn);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_closeConn(
JNIEnv *env, jclass cls, jlong conn_ptr) {
sqlite3 *conn = reinterpret_cast<sqlite3 *>(conn_ptr);
int err = sqlite3_close(conn);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_close", err);
}
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_prepareStmt(
JNIEnv *env, jclass cls, jlong conn_ptr, jstring sql_jstr) {
sqlite3 *conn = reinterpret_cast<sqlite3 *>(conn_ptr);
ScopedUTFString sql(env, sql_jstr);
sqlite3_stmt *stmt;
const char *sql_tail;
int err = sqlite3_prepare_v3(conn, sql.c_str(), sql.length(),
SQLITE_PREPARE_PERSISTENT, &stmt, &sql_tail);
if (err == SQLITE_OK && *sql_tail != '\0') {
// Return special value to signal an unsupported multi-statement string.
sqlite3_finalize(stmt);
return -1L;
}
if (err != SQLITE_OK) {
PostException(env, "sqlite3_prepare_v3", err);
}
return reinterpret_cast<jlong>(stmt);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_bindStmtLong(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i, jlong val) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_bind_int64(stmt, i, val);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_bind_int64", err);
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_bindStmtDouble(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i, jdouble val) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_bind_double(stmt, i, val);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_bind_double", err);
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_bindStmtString(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i, jstring val_jstr) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
ScopedUTFString val(env, val_jstr);
int err =
sqlite3_bind_text(stmt, i, val.c_str(), val.length(), SQLITE_TRANSIENT);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_bind_text", err);
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_clearStmtBinding(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_bind_null(stmt, i);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_bind_null", err);
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_clearStmtBindings(
JNIEnv *env, jclass cls, jlong stmt_ptr) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_clear_bindings(stmt);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_clear_bindings", err);
}
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_stepStmt(JNIEnv *env,
jclass cls,
jlong stmt_ptr) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_step(stmt);
// Deviation from the C API: SQLITE_ROW and SQLITE_DONE are returned as true
// or false, respectively. Everything else causes an exception to be thrown.
if (err == SQLITE_ROW) {
return true;
} else if (err == SQLITE_DONE) {
return false;
}
PostException(env, "sqlite3_step", err);
return false;
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_columnIsNull(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
return sqlite3_column_type(stmt, i) == SQLITE_NULL;
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_columnLong(JNIEnv *env,
jclass cls,
jlong stmt_ptr,
jint i) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
return sqlite3_column_int64(stmt, i);
}
extern "C" JNIEXPORT jdouble JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_columnDouble(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
return sqlite3_column_double(stmt, i);
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_columnString(
JNIEnv *env, jclass cls, jlong stmt_ptr, jint i) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
const char *val =
reinterpret_cast<const char *>(sqlite3_column_text(stmt, i));
return val != nullptr ? env->NewStringUTF(val) : env->NewStringUTF("");
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_resetStmt(
JNIEnv *env, jclass cls, jlong stmt_ptr) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_reset(stmt);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_reset", err);
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_devtools_build_lib_remote_disk_Sqlite_finalizeStmt(
JNIEnv *env, jclass cls, jlong stmt_ptr) {
sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt *>(stmt_ptr);
int err = sqlite3_finalize(stmt);
if (err != SQLITE_OK) {
PostException(env, "sqlite3_finalize", err);
}
}
} // namespace blaze_jni