1 #include <jni.h>
2 #include "sqlite3.h"
3 #include <sstream>
4 #include <stdlib.h>
5
6 /**
7 * Throws SQLiteException with the given error code and message.
8 *
9 * @return true if the exception was thrown, otherwise false.
10 */
throwSQLiteException(JNIEnv * env,int errorCode,const char * errorMsg)11 static bool throwSQLiteException(JNIEnv *env, int errorCode, const char *errorMsg) {
12 jclass exceptionClass = env->FindClass("androidx/sqlite/SQLiteException");
13 if (exceptionClass == nullptr) {
14 // If androidx's exception isn't found we are likely in Android's native where the
15 // actual exception is type aliased. Clear the ClassNotFoundException and instead find
16 // and throw Android's exception.
17 env->ExceptionClear();
18 exceptionClass = env->FindClass("android/database/SQLException");
19 }
20 std::stringstream message;
21 message << "Error code: " << errorCode;
22 if (errorMsg != nullptr) {
23 message << ", message: " << errorMsg;
24 }
25 int throwResult = env->ThrowNew(exceptionClass, message.str().c_str());
26 return throwResult == 0;
27 }
28
throwIfNoRow(JNIEnv * env,sqlite3_stmt * stmt)29 static bool throwIfNoRow(JNIEnv *env, sqlite3_stmt* stmt) {
30 if (sqlite3_stmt_busy(stmt) == 0) {
31 return throwSQLiteException(env, SQLITE_MISUSE, "no row");
32 }
33 return false;
34 }
35
throwIfInvalidColumn(JNIEnv * env,sqlite3_stmt * stmt,int index)36 static bool throwIfInvalidColumn(JNIEnv *env, sqlite3_stmt *stmt, int index) {
37 if (index < 0 || index >= sqlite3_column_count(stmt)) {
38 return throwSQLiteException(env, SQLITE_RANGE, "column index out of range");
39 }
40 return false;
41 }
42
throwOutOfMemoryError(JNIEnv * env)43 static bool throwOutOfMemoryError(JNIEnv *env) {
44 jclass exceptionClass = env->FindClass("java/lang/OutOfMemoryError");
45 int throwResult = env->ThrowNew(exceptionClass, nullptr);
46 return throwResult == 0;
47 }
48
throwIfOutOfMemory(JNIEnv * env,sqlite3_stmt * stmt)49 static bool throwIfOutOfMemory(JNIEnv *env, sqlite3_stmt *stmt) {
50 int lastRc = sqlite3_errcode(sqlite3_db_handle(stmt));
51 if (lastRc == SQLITE_NOMEM) {
52 return throwOutOfMemoryError(env);
53 }
54 return false;
55 }
56
57 extern "C" JNIEXPORT jint JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteDriverKt_nativeThreadSafeMode(JNIEnv * env,jclass clazz)58 Java_androidx_sqlite_driver_bundled_BundledSQLiteDriverKt_nativeThreadSafeMode(
59 JNIEnv* env,
60 jclass clazz) {
61 return sqlite3_threadsafe();
62 }
63
64 extern "C" JNIEXPORT jlong JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteDriverKt_nativeOpen(JNIEnv * env,jclass clazz,jstring name,int openFlags)65 Java_androidx_sqlite_driver_bundled_BundledSQLiteDriverKt_nativeOpen(
66 JNIEnv* env,
67 jclass clazz,
68 jstring name,
69 int openFlags) {
70 const char *path = env->GetStringUTFChars(name, nullptr);
71 sqlite3 *db;
72 int rc = sqlite3_open_v2(path, &db, openFlags, nullptr);
73 env->ReleaseStringUTFChars(name, path);
74 if (rc != SQLITE_OK) {
75 throwSQLiteException(env, rc, nullptr);
76 return 0;
77 }
78 return reinterpret_cast<jlong>(db);
79 }
80
81 extern "C" JNIEXPORT jlong JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteConnectionKt_nativePrepare(JNIEnv * env,jclass clazz,jlong dbPointer,jstring sqlString)82 Java_androidx_sqlite_driver_bundled_BundledSQLiteConnectionKt_nativePrepare(
83 JNIEnv* env,
84 jclass clazz,
85 jlong dbPointer,
86 jstring sqlString) {
87 sqlite3* db = reinterpret_cast<sqlite3*>(dbPointer);
88 sqlite3_stmt* stmt;
89 jsize sqlLength = env->GetStringLength(sqlString);
90 // Java / jstring represents a string in UTF-16 encoding.
91 const jchar* sql = env->GetStringCritical(sqlString, nullptr);
92 int rc = sqlite3_prepare16_v2(db, sql, sqlLength * sizeof(jchar), &stmt, nullptr);
93 env->ReleaseStringCritical(sqlString, sql);
94 if (rc != SQLITE_OK) {
95 throwSQLiteException(env, rc, sqlite3_errmsg(db));
96 return 0;
97 }
98 return reinterpret_cast<jlong>(stmt);
99 }
100
101 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteConnectionKt_nativeClose(JNIEnv * env,jclass clazz,jlong dbPointer)102 Java_androidx_sqlite_driver_bundled_BundledSQLiteConnectionKt_nativeClose(
103 JNIEnv* env,
104 jclass clazz,
105 jlong dbPointer) {
106 sqlite3 *db = reinterpret_cast<sqlite3*>(dbPointer);
107 sqlite3_close_v2(db);
108 }
109
110 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindBlob(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index,jbyteArray value)111 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindBlob(
112 JNIEnv* env,
113 jclass clazz,
114 jlong stmtPointer,
115 jint index,
116 jbyteArray value) {
117 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
118 jsize valueLength = env->GetArrayLength(value);
119 jbyte* blob = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(value, nullptr));
120 int rc = sqlite3_bind_blob(stmt, index, blob, valueLength, SQLITE_TRANSIENT);
121 env->ReleasePrimitiveArrayCritical(value, blob, JNI_ABORT);
122 if (rc != SQLITE_OK) {
123 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
124 }
125 }
126
127 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindDouble(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index,jdouble value)128 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindDouble(
129 JNIEnv* env,
130 jclass clazz,
131 jlong stmtPointer,
132 jint index,
133 jdouble value) {
134 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
135 int rc = sqlite3_bind_double(stmt, index, value);
136 if (rc != SQLITE_OK) {
137 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
138 }
139 }
140
141 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindLong(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index,jlong value)142 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindLong(
143 JNIEnv* env,
144 jclass clazz,
145 jlong stmtPointer,
146 jint index,
147 jlong value) {
148 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
149 int rc = sqlite3_bind_int64(stmt, index, value);
150 if (rc != SQLITE_OK) {
151 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
152 }
153 }
154
155 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindText(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index,jstring value)156 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindText(
157 JNIEnv* env,
158 jclass clazz,
159 jlong stmtPointer,
160 jint index,
161 jstring value) {
162 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
163 jsize valueLength = env->GetStringLength(value);
164 const jchar* text = env->GetStringCritical(value, NULL);
165 int rc = sqlite3_bind_text16(stmt, index, text, valueLength * sizeof(jchar), SQLITE_TRANSIENT);
166 env->ReleaseStringCritical(value, text);
167 if (rc != SQLITE_OK) {
168 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
169 }
170 }
171
172 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindNull(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)173 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeBindNull(
174 JNIEnv* env,
175 jclass clazz,
176 jlong stmtPointer,
177 jint index) {
178 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
179 int rc = sqlite3_bind_null(stmt, index);
180 if (rc != SQLITE_OK) {
181 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
182 }
183 }
184
185 extern "C" JNIEXPORT jboolean JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeStep(JNIEnv * env,jclass clazz,jlong stmtPointer)186 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeStep(
187 JNIEnv *env,
188 jclass clazz,
189 jlong stmtPointer) {
190 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
191 int rc = sqlite3_step(stmt);
192 if (rc == SQLITE_ROW) {
193 return JNI_TRUE;
194 }
195 if (rc == SQLITE_DONE) {
196 return JNI_FALSE;
197 }
198 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
199 return JNI_FALSE;
200 }
201
202 extern "C" JNIEXPORT jbyteArray JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetBlob(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)203 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetBlob(
204 JNIEnv *env,
205 jclass clazz,
206 jlong stmtPointer,
207 jint index) {
208 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
209 if (throwIfNoRow(env, stmt)) return nullptr;
210 if (throwIfInvalidColumn(env, stmt, index)) return nullptr;
211 const void *blob = sqlite3_column_blob(stmt, index);
212 if (blob == nullptr && throwIfOutOfMemory(env, stmt)) return nullptr;
213 int size = sqlite3_column_bytes(stmt, index);
214 if (size == 0 && throwIfOutOfMemory(env, stmt)) return nullptr;
215 jbyteArray byteArray = env->NewByteArray(size);
216 if (size > 0) {
217 env->SetByteArrayRegion(byteArray, 0, size, static_cast<const jbyte*>(blob));
218 }
219 return byteArray;
220 }
221
222 extern "C" JNIEXPORT jdouble JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetDouble(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)223 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetDouble(
224 JNIEnv *env,
225 jclass clazz,
226 jlong stmtPointer,
227 jint index) {
228 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
229 if (throwIfNoRow(env, stmt)) return 0.0;
230 if (throwIfInvalidColumn(env, stmt, index)) return 0.0;
231 return sqlite3_column_double(stmt, index);
232 }
233
234 extern "C" JNIEXPORT jlong JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetLong(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)235 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetLong(
236 JNIEnv *env,
237 jclass clazz,
238 jlong stmtPointer,
239 jint index) {
240 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
241 if (throwIfNoRow(env, stmt)) return 0;
242 if (throwIfInvalidColumn(env, stmt, index)) return 0;
243 return sqlite3_column_int64(stmt, index);
244 }
245
246 extern "C" JNIEXPORT jstring JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetText(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)247 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetText(
248 JNIEnv *env,
249 jclass clazz,
250 jlong stmtPointer,
251 jint index) {
252 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
253 if (throwIfNoRow(env, stmt)) return nullptr;
254 if (throwIfInvalidColumn(env, stmt, index)) return nullptr;
255 // Java / jstring represents a string in UTF-16 encoding.
256 const jchar *text = static_cast<const jchar*>(sqlite3_column_text16(stmt, index));
257 if (text == nullptr && throwIfOutOfMemory(env, stmt)) return nullptr;
258 size_t length = sqlite3_column_bytes16(stmt, index) / sizeof(jchar);
259 if (length == 0 && throwIfOutOfMemory(env, stmt)) return nullptr;
260 return env->NewString(text, length);
261 }
262
263 extern "C" JNIEXPORT jint JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetColumnCount(JNIEnv * env,jclass clazz,jlong stmtPointer)264 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetColumnCount(
265 JNIEnv *env,
266 jclass clazz,
267 jlong stmtPointer) {
268 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
269 return sqlite3_column_count(stmt);
270 }
271
272 extern "C" JNIEXPORT jstring JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetColumnName(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)273 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetColumnName(
274 JNIEnv *env,
275 jclass clazz,
276 jlong stmtPointer,
277 jint index) {
278 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
279 if (throwIfInvalidColumn(env, stmt, index)) return nullptr;
280 const char *name = sqlite3_column_name(stmt, index);
281 if (name == nullptr) {
282 throwOutOfMemoryError(env);
283 return nullptr;
284 }
285 return env->NewStringUTF(name);
286 }
287
288 extern "C" JNIEXPORT jint JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetColumnType(JNIEnv * env,jclass clazz,jlong stmtPointer,jint index)289 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeGetColumnType(
290 JNIEnv *env,
291 jclass clazz,
292 jlong stmtPointer,
293 jint index) {
294 sqlite3_stmt *stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
295 if (throwIfNoRow(env, stmt)) return 0;
296 if (throwIfInvalidColumn(env, stmt, index)) return 0;
297 return sqlite3_column_type(stmt, index);
298 }
299
300 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeReset(JNIEnv * env,jclass clazz,jlong stmtPointer)301 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeReset(
302 JNIEnv* env,
303 jclass clazz,
304 jlong stmtPointer) {
305 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
306 int rc = sqlite3_reset(stmt);
307 if (rc != SQLITE_OK) {
308 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
309 }
310 }
311
312 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeClearBindings(JNIEnv * env,jclass clazz,jlong stmtPointer)313 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeClearBindings(
314 JNIEnv* env,
315 jclass clazz,
316 jlong stmtPointer) {
317 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
318 int rc = sqlite3_clear_bindings(stmt);
319 if (rc != SQLITE_OK) {
320 throwSQLiteException(env, rc, sqlite3_errmsg(sqlite3_db_handle(stmt)));
321 }
322 }
323
324 extern "C" JNIEXPORT void JNICALL
Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeClose(JNIEnv * env,jclass clazz,jlong stmtPointer)325 Java_androidx_sqlite_driver_bundled_BundledSQLiteStatementKt_nativeClose(
326 JNIEnv* env,
327 jclass clazz,
328 jlong stmtPointer) {
329 sqlite3_stmt* stmt = reinterpret_cast<sqlite3_stmt*>(stmtPointer);
330 sqlite3_finalize(stmt);
331 }
332