Bumping manifests a=b2g-bump
[gecko.git] / mozglue / android / SQLiteBridge.cpp
blob2d444749d0d299310b042baf47552cb29658d1fd
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <jni.h>
8 #include <android/log.h>
9 #include "dlfcn.h"
10 #include "APKOpen.h"
11 #include "ElfLoader.h"
12 #include "SQLiteBridge.h"
14 #ifdef DEBUG
15 #define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x)
16 #else
17 #define LOG(x...)
18 #endif
20 #define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name;
22 SQLITE_WRAPPER_INT(sqlite3_open)
23 SQLITE_WRAPPER_INT(sqlite3_errmsg)
24 SQLITE_WRAPPER_INT(sqlite3_prepare_v2)
25 SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count)
26 SQLITE_WRAPPER_INT(sqlite3_bind_text)
27 SQLITE_WRAPPER_INT(sqlite3_step)
28 SQLITE_WRAPPER_INT(sqlite3_column_count)
29 SQLITE_WRAPPER_INT(sqlite3_finalize)
30 SQLITE_WRAPPER_INT(sqlite3_close)
31 SQLITE_WRAPPER_INT(sqlite3_column_name)
32 SQLITE_WRAPPER_INT(sqlite3_column_type)
33 SQLITE_WRAPPER_INT(sqlite3_column_blob)
34 SQLITE_WRAPPER_INT(sqlite3_column_bytes)
35 SQLITE_WRAPPER_INT(sqlite3_column_text)
36 SQLITE_WRAPPER_INT(sqlite3_changes)
37 SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid)
39 void setup_sqlite_functions(void *sqlite_handle)
41 #define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name)
42 GETFUNC(sqlite3_open);
43 GETFUNC(sqlite3_errmsg);
44 GETFUNC(sqlite3_prepare_v2);
45 GETFUNC(sqlite3_bind_parameter_count);
46 GETFUNC(sqlite3_bind_text);
47 GETFUNC(sqlite3_step);
48 GETFUNC(sqlite3_column_count);
49 GETFUNC(sqlite3_finalize);
50 GETFUNC(sqlite3_close);
51 GETFUNC(sqlite3_column_name);
52 GETFUNC(sqlite3_column_type);
53 GETFUNC(sqlite3_column_blob);
54 GETFUNC(sqlite3_column_bytes);
55 GETFUNC(sqlite3_column_text);
56 GETFUNC(sqlite3_changes);
57 GETFUNC(sqlite3_last_insert_rowid);
58 #undef GETFUNC
61 static bool initialized = false;
62 static jclass stringClass;
63 static jclass objectClass;
64 static jclass byteBufferClass;
65 static jclass cursorClass;
66 static jmethodID jByteBufferAllocateDirect;
67 static jmethodID jCursorConstructor;
68 static jmethodID jCursorAddRow;
70 static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery,
71 jobjectArray jParams, jlongArray jQueryRes);
73 static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...)
75 va_list ap;
76 va_start(ap, aFormat);
77 char* msg = nullptr;
78 vasprintf(&msg, aFormat, ap);
79 LOG("Error in SQLiteBridge: %s\n", msg);
80 JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg);
81 free(msg);
82 va_end(ap);
85 static void
86 JNI_Setup(JNIEnv* jenv)
88 if (initialized) return;
90 jclass lObjectClass = jenv->FindClass("java/lang/Object");
91 jclass lStringClass = jenv->FindClass("java/lang/String");
92 jclass lByteBufferClass = jenv->FindClass("java/nio/ByteBuffer");
93 jclass lCursorClass = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor");
95 if (lStringClass == nullptr
96 || lObjectClass == nullptr
97 || lByteBufferClass == nullptr
98 || lCursorClass == nullptr) {
99 throwSqliteException(jenv, "FindClass error");
100 return;
103 // Those are only local references. Make them global so they work
104 // across calls and threads.
105 objectClass = (jclass)jenv->NewGlobalRef(lObjectClass);
106 stringClass = (jclass)jenv->NewGlobalRef(lStringClass);
107 byteBufferClass = (jclass)jenv->NewGlobalRef(lByteBufferClass);
108 cursorClass = (jclass)jenv->NewGlobalRef(lCursorClass);
110 if (stringClass == nullptr || objectClass == nullptr
111 || byteBufferClass == nullptr
112 || cursorClass == nullptr) {
113 throwSqliteException(jenv, "NewGlobalRef error");
114 return;
117 // public static ByteBuffer allocateDirect(int capacity)
118 jByteBufferAllocateDirect =
119 jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;");
120 // new MatrixBlobCursor(String [])
121 jCursorConstructor =
122 jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V");
123 // public void addRow (Object[] columnValues)
124 jCursorAddRow =
125 jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V");
127 if (jByteBufferAllocateDirect == nullptr
128 || jCursorConstructor == nullptr
129 || jCursorAddRow == nullptr) {
130 throwSqliteException(jenv, "GetMethodId error");
131 return;
134 initialized = true;
137 extern "C" NS_EXPORT jobject JNICALL
138 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass,
139 jstring jDb,
140 jstring jQuery,
141 jobjectArray jParams,
142 jlongArray jQueryRes)
144 JNI_Setup(jenv);
146 int rc;
147 jobject jCursor = nullptr;
148 const char* dbPath;
149 sqlite3 *db;
151 dbPath = jenv->GetStringUTFChars(jDb, nullptr);
152 rc = f_sqlite3_open(dbPath, &db);
153 jenv->ReleaseStringUTFChars(jDb, dbPath);
154 if (rc != SQLITE_OK) {
155 throwSqliteException(jenv,
156 "Can't open database: %s", f_sqlite3_errmsg(db));
157 f_sqlite3_close(db); // close db even if open failed
158 return nullptr;
160 jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
161 f_sqlite3_close(db);
162 return jCursor;
165 extern "C" NS_EXPORT jobject JNICALL
166 Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass,
167 jlong jDb,
168 jstring jQuery,
169 jobjectArray jParams,
170 jlongArray jQueryRes)
172 JNI_Setup(jenv);
174 jobject jCursor = nullptr;
175 sqlite3 *db = (sqlite3*)jDb;
176 jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes);
177 return jCursor;
180 extern "C" NS_EXPORT jlong JNICALL
181 Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass,
182 jstring jDb)
184 JNI_Setup(jenv);
186 int rc;
187 const char* dbPath;
188 sqlite3 *db;
190 dbPath = jenv->GetStringUTFChars(jDb, nullptr);
191 rc = f_sqlite3_open(dbPath, &db);
192 jenv->ReleaseStringUTFChars(jDb, dbPath);
193 if (rc != SQLITE_OK) {
194 throwSqliteException(jenv,
195 "Can't open database: %s", f_sqlite3_errmsg(db));
196 f_sqlite3_close(db); // close db even if open failed
197 return 0;
199 return (jlong)db;
202 extern "C" NS_EXPORT void JNICALL
203 Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass,
204 jlong jDb)
206 JNI_Setup(jenv);
208 sqlite3 *db = (sqlite3*)jDb;
209 f_sqlite3_close(db);
212 static jobject
213 sqliteInternalCall(JNIEnv* jenv,
214 sqlite3 *db,
215 jstring jQuery,
216 jobjectArray jParams,
217 jlongArray jQueryRes)
219 JNI_Setup(jenv);
221 jobject jCursor = nullptr;
222 jsize numPars = 0;
224 const char *pzTail;
225 sqlite3_stmt *ppStmt;
226 int rc;
228 const char* queryStr;
229 queryStr = jenv->GetStringUTFChars(jQuery, nullptr);
231 rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail);
232 if (rc != SQLITE_OK || ppStmt == nullptr) {
233 throwSqliteException(jenv,
234 "Can't prepare statement: %s", f_sqlite3_errmsg(db));
235 return nullptr;
237 jenv->ReleaseStringUTFChars(jQuery, queryStr);
239 // Check if number of parameters matches
240 if (jParams != nullptr) {
241 numPars = jenv->GetArrayLength(jParams);
243 int sqlNumPars;
244 sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt);
245 if (numPars != sqlNumPars) {
246 throwSqliteException(jenv,
247 "Passed parameter count (%d) "
248 "doesn't match SQL parameter count (%d)",
249 numPars, sqlNumPars);
250 return nullptr;
253 if (jParams != nullptr) {
254 // Bind parameters, if any
255 if (numPars > 0) {
256 for (int i = 0; i < numPars; i++) {
257 jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i);
258 // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf
259 // should be OK.
260 jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass);
261 if (isString != JNI_TRUE) {
262 throwSqliteException(jenv,
263 "Parameter is not of String type");
264 return nullptr;
266 jstring jStringParam = (jstring)jObjectParam;
267 const char* paramStr = jenv->GetStringUTFChars(jStringParam, nullptr);
268 // SQLite parameters index from 1.
269 rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT);
270 jenv->ReleaseStringUTFChars(jStringParam, paramStr);
271 if (rc != SQLITE_OK) {
272 throwSqliteException(jenv, "Error binding query parameter");
273 return nullptr;
279 // Execute the query and step through the results
280 rc = f_sqlite3_step(ppStmt);
281 if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
282 throwSqliteException(jenv,
283 "Can't step statement: (%d) %s", rc, f_sqlite3_errmsg(db));
284 return nullptr;
287 // Get the column count and names
288 int cols;
289 cols = f_sqlite3_column_count(ppStmt);
292 // Allocate a String[cols]
293 jobjectArray jStringArray = jenv->NewObjectArray(cols,
294 stringClass,
295 nullptr);
296 if (jStringArray == nullptr) {
297 throwSqliteException(jenv, "Can't allocate String[]");
298 return nullptr;
301 // Assign column names to the String[]
302 for (int i = 0; i < cols; i++) {
303 const char* colName = f_sqlite3_column_name(ppStmt, i);
304 jstring jStr = jenv->NewStringUTF(colName);
305 jenv->SetObjectArrayElement(jStringArray, i, jStr);
308 // Construct the MatrixCursor(String[]) with given column names
309 jCursor = jenv->NewObject(cursorClass,
310 jCursorConstructor,
311 jStringArray);
312 if (jCursor == nullptr) {
313 throwSqliteException(jenv, "Can't allocate MatrixBlobCursor");
314 return nullptr;
318 // Return the id and number of changed rows in jQueryRes
320 jlong id = f_sqlite3_last_insert_rowid(db);
321 jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id);
323 jlong changed = f_sqlite3_changes(db);
324 jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed);
327 // For each row, add an Object[] to the passed ArrayList,
328 // with that containing either String or ByteArray objects
329 // containing the columns
330 while (rc != SQLITE_DONE) {
331 // Process row
332 // Construct Object[]
333 jobjectArray jRow = jenv->NewObjectArray(cols,
334 objectClass,
335 nullptr);
336 if (jRow == nullptr) {
337 throwSqliteException(jenv, "Can't allocate jRow Object[]");
338 return nullptr;
341 for (int i = 0; i < cols; i++) {
342 int colType = f_sqlite3_column_type(ppStmt, i);
343 if (colType == SQLITE_BLOB) {
344 // Treat as blob
345 const void* blob = f_sqlite3_column_blob(ppStmt, i);
346 int colLen = f_sqlite3_column_bytes(ppStmt, i);
348 // Construct ByteBuffer of correct size
349 jobject jByteBuffer =
350 jenv->CallStaticObjectMethod(byteBufferClass,
351 jByteBufferAllocateDirect,
352 colLen);
353 if (jByteBuffer == nullptr) {
354 throwSqliteException(jenv,
355 "Failure calling ByteBuffer.allocateDirect");
356 return nullptr;
359 // Get its backing array
360 void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer);
361 if (bufferArray == nullptr) {
362 throwSqliteException(jenv,
363 "Failure calling GetDirectBufferAddress");
364 return nullptr;
366 memcpy(bufferArray, blob, colLen);
368 jenv->SetObjectArrayElement(jRow, i, jByteBuffer);
369 jenv->DeleteLocalRef(jByteBuffer);
370 } else if (colType == SQLITE_NULL) {
371 jenv->SetObjectArrayElement(jRow, i, nullptr);
372 } else {
373 // Treat everything else as text
374 const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i);
375 jstring jStr = jenv->NewStringUTF(txt);
376 jenv->SetObjectArrayElement(jRow, i, jStr);
377 jenv->DeleteLocalRef(jStr);
381 // Append Object[] to Cursor
382 jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow);
384 // Clean up
385 jenv->DeleteLocalRef(jRow);
387 // Get next row
388 rc = f_sqlite3_step(ppStmt);
389 // Real error?
390 if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
391 throwSqliteException(jenv,
392 "Can't re-step statement:(%d) %s", rc, f_sqlite3_errmsg(db));
393 return nullptr;
397 rc = f_sqlite3_finalize(ppStmt);
398 if (rc != SQLITE_OK) {
399 throwSqliteException(jenv,
400 "Can't finalize statement: %s", f_sqlite3_errmsg(db));
401 return nullptr;
404 return jCursor;