1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "SQLiteConnection"
18
19 #include <jni.h>
20 #include <JNIHelp.h>
21 #include <android_runtime/AndroidRuntime.h>
22 #include <android_runtime/Log.h>
23
24 #include <utils/Log.h>
25 #include <utils/String8.h>
26 #include <utils/String16.h>
27 #include <cutils/ashmem.h>
28 #include <sys/mman.h>
29
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <androidfw/CursorWindow.h>
34
35 #include <sqlite3.h>
36 #include <sqlite3_android.h>
37
38 #include "android_database_SQLiteCommon.h"
39
40 #include "core_jni_helpers.h"
41
42 // Set to 1 to use UTF16 storage for localized indexes.
43 #define UTF16_STORAGE 0
44
45 namespace android {
46
47 /* Busy timeout in milliseconds.
48 * If another connection (possibly in another process) has the database locked for
49 * longer than this amount of time then SQLite will generate a SQLITE_BUSY error.
50 * The SQLITE_BUSY error is then raised as a SQLiteDatabaseLockedException.
51 *
52 * In ordinary usage, busy timeouts are quite rare. Most databases only ever
53 * have a single open connection at a time unless they are using WAL. When using
54 * WAL, a timeout could occur if one connection is busy performing an auto-checkpoint
55 * operation. The busy timeout needs to be long enough to tolerate slow I/O write
56 * operations but not so long as to cause the application to hang indefinitely if
57 * there is a problem acquiring a database lock.
58 */
59 static const int BUSY_TIMEOUT_MS = 2500;
60
61 static struct {
62 jfieldID name;
63 jfieldID numArgs;
64 jmethodID dispatchCallback;
65 } gSQLiteCustomFunctionClassInfo;
66
67 static struct {
68 jclass clazz;
69 } gStringClassInfo;
70
71 struct SQLiteConnection {
72 // Open flags.
73 // Must be kept in sync with the constants defined in SQLiteDatabase.java.
74 enum {
75 OPEN_READWRITE = 0x00000000,
76 OPEN_READONLY = 0x00000001,
77 OPEN_READ_MASK = 0x00000001,
78 NO_LOCALIZED_COLLATORS = 0x00000010,
79 CREATE_IF_NECESSARY = 0x10000000,
80 };
81
82 sqlite3* const db;
83 const int openFlags;
84 const String8 path;
85 const String8 label;
86
87 volatile bool canceled;
88
SQLiteConnectionandroid::SQLiteConnection89 SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
90 db(db), openFlags(openFlags), path(path), label(label), canceled(false) { }
91 };
92
93 // Called each time a statement begins execution, when tracing is enabled.
sqliteTraceCallback(void * data,const char * sql)94 static void sqliteTraceCallback(void *data, const char *sql) {
95 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
96 ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n",
97 connection->label.string(), sql);
98 }
99
100 // Called each time a statement finishes execution, when profiling is enabled.
sqliteProfileCallback(void * data,const char * sql,sqlite3_uint64 tm)101 static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
102 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
103 ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n",
104 connection->label.string(), sql, tm * 0.000001f);
105 }
106
107 // Called after each SQLite VM instruction when cancelation is enabled.
sqliteProgressHandlerCallback(void * data)108 static int sqliteProgressHandlerCallback(void* data) {
109 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
110 return connection->canceled;
111 }
112
113
nativeOpen(JNIEnv * env,jclass clazz,jstring pathStr,jint openFlags,jstring labelStr,jboolean enableTrace,jboolean enableProfile)114 static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
115 jstring labelStr, jboolean enableTrace, jboolean enableProfile) {
116 int sqliteFlags;
117 if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
118 sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
119 } else if (openFlags & SQLiteConnection::OPEN_READONLY) {
120 sqliteFlags = SQLITE_OPEN_READONLY;
121 } else {
122 sqliteFlags = SQLITE_OPEN_READWRITE;
123 }
124
125 const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
126 String8 path(pathChars);
127 env->ReleaseStringUTFChars(pathStr, pathChars);
128
129 const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
130 String8 label(labelChars);
131 env->ReleaseStringUTFChars(labelStr, labelChars);
132
133 sqlite3* db;
134 int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL);
135 if (err != SQLITE_OK) {
136 throw_sqlite3_exception_errcode(env, err, "Could not open database");
137 return 0;
138 }
139
140 // Check that the database is really read/write when that is what we asked for.
141 if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
142 throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
143 sqlite3_close(db);
144 return 0;
145 }
146
147 // Set the default busy handler to retry automatically before returning SQLITE_BUSY.
148 err = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
149 if (err != SQLITE_OK) {
150 throw_sqlite3_exception(env, db, "Could not set busy timeout");
151 sqlite3_close(db);
152 return 0;
153 }
154
155 // Register custom Android functions.
156 err = register_android_functions(db, UTF16_STORAGE);
157 if (err) {
158 throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
159 sqlite3_close(db);
160 return 0;
161 }
162
163 // Create wrapper object.
164 SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
165
166 // Enable tracing and profiling if requested.
167 if (enableTrace) {
168 sqlite3_trace(db, &sqliteTraceCallback, connection);
169 }
170 if (enableProfile) {
171 sqlite3_profile(db, &sqliteProfileCallback, connection);
172 }
173
174 ALOGV("Opened connection %p with label '%s'", db, label.string());
175 return reinterpret_cast<jlong>(connection);
176 }
177
nativeClose(JNIEnv * env,jclass clazz,jlong connectionPtr)178 static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) {
179 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
180
181 if (connection) {
182 ALOGV("Closing connection %p", connection->db);
183 int err = sqlite3_close(connection->db);
184 if (err != SQLITE_OK) {
185 // This can happen if sub-objects aren't closed first. Make sure the caller knows.
186 ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
187 throw_sqlite3_exception(env, connection->db, "Count not close db.");
188 return;
189 }
190
191 delete connection;
192 }
193 }
194
195 // Called each time a custom function is evaluated.
sqliteCustomFunctionCallback(sqlite3_context * context,int argc,sqlite3_value ** argv)196 static void sqliteCustomFunctionCallback(sqlite3_context *context,
197 int argc, sqlite3_value **argv) {
198 JNIEnv* env = AndroidRuntime::getJNIEnv();
199
200 // Get the callback function object.
201 // Create a new local reference to it in case the callback tries to do something
202 // dumb like unregister the function (thereby destroying the global ref) while it is running.
203 jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
204 jobject functionObj = env->NewLocalRef(functionObjGlobal);
205
206 jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL);
207 if (argsArray) {
208 for (int i = 0; i < argc; i++) {
209 const jchar* arg = static_cast<const jchar*>(sqlite3_value_text16(argv[i]));
210 if (!arg) {
211 ALOGW("NULL argument in custom_function_callback. This should not happen.");
212 } else {
213 size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar);
214 jstring argStr = env->NewString(arg, argLen);
215 if (!argStr) {
216 goto error; // out of memory error
217 }
218 env->SetObjectArrayElement(argsArray, i, argStr);
219 env->DeleteLocalRef(argStr);
220 }
221 }
222
223 // TODO: Support functions that return values.
224 env->CallVoidMethod(functionObj,
225 gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray);
226
227 error:
228 env->DeleteLocalRef(argsArray);
229 }
230
231 env->DeleteLocalRef(functionObj);
232
233 if (env->ExceptionCheck()) {
234 ALOGE("An exception was thrown by custom SQLite function.");
235 LOGE_EX(env);
236 env->ExceptionClear();
237 }
238 }
239
240 // Called when a custom function is destroyed.
sqliteCustomFunctionDestructor(void * data)241 static void sqliteCustomFunctionDestructor(void* data) {
242 jobject functionObjGlobal = reinterpret_cast<jobject>(data);
243
244 JNIEnv* env = AndroidRuntime::getJNIEnv();
245 env->DeleteGlobalRef(functionObjGlobal);
246 }
247
nativeRegisterCustomFunction(JNIEnv * env,jclass clazz,jlong connectionPtr,jobject functionObj)248 static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jlong connectionPtr,
249 jobject functionObj) {
250 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
251
252 jstring nameStr = jstring(env->GetObjectField(
253 functionObj, gSQLiteCustomFunctionClassInfo.name));
254 jint numArgs = env->GetIntField(functionObj, gSQLiteCustomFunctionClassInfo.numArgs);
255
256 jobject functionObjGlobal = env->NewGlobalRef(functionObj);
257
258 const char* name = env->GetStringUTFChars(nameStr, NULL);
259 int err = sqlite3_create_function_v2(connection->db, name, numArgs, SQLITE_UTF16,
260 reinterpret_cast<void*>(functionObjGlobal),
261 &sqliteCustomFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor);
262 env->ReleaseStringUTFChars(nameStr, name);
263
264 if (err != SQLITE_OK) {
265 ALOGE("sqlite3_create_function returned %d", err);
266 env->DeleteGlobalRef(functionObjGlobal);
267 throw_sqlite3_exception(env, connection->db);
268 return;
269 }
270 }
271
nativeRegisterLocalizedCollators(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring localeStr)272 static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jlong connectionPtr,
273 jstring localeStr) {
274 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
275
276 const char* locale = env->GetStringUTFChars(localeStr, NULL);
277 int err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
278 env->ReleaseStringUTFChars(localeStr, locale);
279
280 if (err != SQLITE_OK) {
281 throw_sqlite3_exception(env, connection->db);
282 }
283 }
284
nativePrepareStatement(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring sqlString)285 static jlong nativePrepareStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
286 jstring sqlString) {
287 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
288
289 jsize sqlLength = env->GetStringLength(sqlString);
290 const jchar* sql = env->GetStringCritical(sqlString, NULL);
291 sqlite3_stmt* statement;
292 int err = sqlite3_prepare16_v2(connection->db,
293 sql, sqlLength * sizeof(jchar), &statement, NULL);
294 env->ReleaseStringCritical(sqlString, sql);
295
296 if (err != SQLITE_OK) {
297 // Error messages like 'near ")": syntax error' are not
298 // always helpful enough, so construct an error string that
299 // includes the query itself.
300 const char *query = env->GetStringUTFChars(sqlString, NULL);
301 char *message = (char*) malloc(strlen(query) + 50);
302 if (message) {
303 strcpy(message, ", while compiling: "); // less than 50 chars
304 strcat(message, query);
305 }
306 env->ReleaseStringUTFChars(sqlString, query);
307 throw_sqlite3_exception(env, connection->db, message);
308 free(message);
309 return 0;
310 }
311
312 ALOGV("Prepared statement %p on connection %p", statement, connection->db);
313 return reinterpret_cast<jlong>(statement);
314 }
315
nativeFinalizeStatement(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)316 static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
317 jlong statementPtr) {
318 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
319 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
320
321 // We ignore the result of sqlite3_finalize because it is really telling us about
322 // whether any errors occurred while executing the statement. The statement itself
323 // is always finalized regardless.
324 ALOGV("Finalized statement %p on connection %p", statement, connection->db);
325 sqlite3_finalize(statement);
326 }
327
nativeGetParameterCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)328 static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
329 jlong statementPtr) {
330 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
331
332 return sqlite3_bind_parameter_count(statement);
333 }
334
nativeIsReadOnly(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)335 static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jlong connectionPtr,
336 jlong statementPtr) {
337 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
338
339 return sqlite3_stmt_readonly(statement) != 0;
340 }
341
nativeGetColumnCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)342 static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
343 jlong statementPtr) {
344 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
345
346 return sqlite3_column_count(statement);
347 }
348
nativeGetColumnName(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index)349 static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jlong connectionPtr,
350 jlong statementPtr, jint index) {
351 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
352
353 const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(statement, index));
354 if (name) {
355 size_t length = 0;
356 while (name[length]) {
357 length += 1;
358 }
359 return env->NewString(name, length);
360 }
361 return NULL;
362 }
363
nativeBindNull(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index)364 static void nativeBindNull(JNIEnv* env, jclass clazz, jlong connectionPtr,
365 jlong statementPtr, jint index) {
366 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
367 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
368
369 int err = sqlite3_bind_null(statement, index);
370 if (err != SQLITE_OK) {
371 throw_sqlite3_exception(env, connection->db, NULL);
372 }
373 }
374
nativeBindLong(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jlong value)375 static void nativeBindLong(JNIEnv* env, jclass clazz, jlong connectionPtr,
376 jlong statementPtr, jint index, jlong value) {
377 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
378 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
379
380 int err = sqlite3_bind_int64(statement, index, value);
381 if (err != SQLITE_OK) {
382 throw_sqlite3_exception(env, connection->db, NULL);
383 }
384 }
385
nativeBindDouble(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jdouble value)386 static void nativeBindDouble(JNIEnv* env, jclass clazz, jlong connectionPtr,
387 jlong statementPtr, jint index, jdouble value) {
388 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
389 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
390
391 int err = sqlite3_bind_double(statement, index, value);
392 if (err != SQLITE_OK) {
393 throw_sqlite3_exception(env, connection->db, NULL);
394 }
395 }
396
nativeBindString(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jstring valueString)397 static void nativeBindString(JNIEnv* env, jclass clazz, jlong connectionPtr,
398 jlong statementPtr, jint index, jstring valueString) {
399 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
400 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
401
402 jsize valueLength = env->GetStringLength(valueString);
403 const jchar* value = env->GetStringCritical(valueString, NULL);
404 int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
405 SQLITE_TRANSIENT);
406 env->ReleaseStringCritical(valueString, value);
407 if (err != SQLITE_OK) {
408 throw_sqlite3_exception(env, connection->db, NULL);
409 }
410 }
411
nativeBindBlob(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jbyteArray valueArray)412 static void nativeBindBlob(JNIEnv* env, jclass clazz, jlong connectionPtr,
413 jlong statementPtr, jint index, jbyteArray valueArray) {
414 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
415 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
416
417 jsize valueLength = env->GetArrayLength(valueArray);
418 jbyte* value = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(valueArray, NULL));
419 int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
420 env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
421 if (err != SQLITE_OK) {
422 throw_sqlite3_exception(env, connection->db, NULL);
423 }
424 }
425
nativeResetStatementAndClearBindings(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)426 static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jlong connectionPtr,
427 jlong statementPtr) {
428 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
429 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
430
431 int err = sqlite3_reset(statement);
432 if (err == SQLITE_OK) {
433 err = sqlite3_clear_bindings(statement);
434 }
435 if (err != SQLITE_OK) {
436 throw_sqlite3_exception(env, connection->db, NULL);
437 }
438 }
439
executeNonQuery(JNIEnv * env,SQLiteConnection * connection,sqlite3_stmt * statement)440 static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
441 int err = sqlite3_step(statement);
442 if (err == SQLITE_ROW) {
443 throw_sqlite3_exception(env,
444 "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
445 } else if (err != SQLITE_DONE) {
446 throw_sqlite3_exception(env, connection->db);
447 }
448 return err;
449 }
450
nativeExecute(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)451 static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr,
452 jlong statementPtr) {
453 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
454 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
455
456 executeNonQuery(env, connection, statement);
457 }
458
nativeExecuteForChangedRowCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)459 static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
460 jlong connectionPtr, jlong statementPtr) {
461 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
462 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
463
464 int err = executeNonQuery(env, connection, statement);
465 return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
466 }
467
nativeExecuteForLastInsertedRowId(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)468 static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
469 jlong connectionPtr, jlong statementPtr) {
470 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
471 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
472
473 int err = executeNonQuery(env, connection, statement);
474 return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
475 ? sqlite3_last_insert_rowid(connection->db) : -1;
476 }
477
executeOneRowQuery(JNIEnv * env,SQLiteConnection * connection,sqlite3_stmt * statement)478 static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
479 int err = sqlite3_step(statement);
480 if (err != SQLITE_ROW) {
481 throw_sqlite3_exception(env, connection->db);
482 }
483 return err;
484 }
485
nativeExecuteForLong(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)486 static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
487 jlong connectionPtr, jlong statementPtr) {
488 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
489 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
490
491 int err = executeOneRowQuery(env, connection, statement);
492 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
493 return sqlite3_column_int64(statement, 0);
494 }
495 return -1;
496 }
497
nativeExecuteForString(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)498 static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
499 jlong connectionPtr, jlong statementPtr) {
500 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
501 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
502
503 int err = executeOneRowQuery(env, connection, statement);
504 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
505 const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(statement, 0));
506 if (text) {
507 size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
508 return env->NewString(text, length);
509 }
510 }
511 return NULL;
512 }
513
createAshmemRegionWithData(JNIEnv * env,const void * data,size_t length)514 static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
515 int error = 0;
516 int fd = ashmem_create_region(NULL, length);
517 if (fd < 0) {
518 error = errno;
519 ALOGE("ashmem_create_region failed: %s", strerror(error));
520 } else {
521 if (length > 0) {
522 void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
523 if (ptr == MAP_FAILED) {
524 error = errno;
525 ALOGE("mmap failed: %s", strerror(error));
526 } else {
527 memcpy(ptr, data, length);
528 munmap(ptr, length);
529 }
530 }
531
532 if (!error) {
533 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
534 error = errno;
535 ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
536 } else {
537 return fd;
538 }
539 }
540
541 close(fd);
542 }
543
544 jniThrowIOException(env, error);
545 return -1;
546 }
547
nativeExecuteForBlobFileDescriptor(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)548 static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
549 jlong connectionPtr, jlong statementPtr) {
550 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
551 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
552
553 int err = executeOneRowQuery(env, connection, statement);
554 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
555 const void* blob = sqlite3_column_blob(statement, 0);
556 if (blob) {
557 int length = sqlite3_column_bytes(statement, 0);
558 if (length >= 0) {
559 return createAshmemRegionWithData(env, blob, length);
560 }
561 }
562 }
563 return -1;
564 }
565
566 enum CopyRowResult {
567 CPR_OK,
568 CPR_FULL,
569 CPR_ERROR,
570 };
571
copyRow(JNIEnv * env,CursorWindow * window,sqlite3_stmt * statement,int numColumns,int startPos,int addedRows)572 static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
573 sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
574 // Allocate a new field directory for the row.
575 status_t status = window->allocRow();
576 if (status) {
577 LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
578 startPos, addedRows, status);
579 return CPR_FULL;
580 }
581
582 // Pack the row into the window.
583 CopyRowResult result = CPR_OK;
584 for (int i = 0; i < numColumns; i++) {
585 int type = sqlite3_column_type(statement, i);
586 if (type == SQLITE_TEXT) {
587 // TEXT data
588 const char* text = reinterpret_cast<const char*>(
589 sqlite3_column_text(statement, i));
590 // SQLite does not include the NULL terminator in size, but does
591 // ensure all strings are NULL terminated, so increase size by
592 // one to make sure we store the terminator.
593 size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
594 status = window->putString(addedRows, i, text, sizeIncludingNull);
595 if (status) {
596 LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
597 sizeIncludingNull, startPos + addedRows, i, status);
598 result = CPR_FULL;
599 break;
600 }
601 LOG_WINDOW("%d,%d is TEXT with %u bytes",
602 startPos + addedRows, i, sizeIncludingNull);
603 } else if (type == SQLITE_INTEGER) {
604 // INTEGER data
605 int64_t value = sqlite3_column_int64(statement, i);
606 status = window->putLong(addedRows, i, value);
607 if (status) {
608 LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
609 i, status);
610 result = CPR_FULL;
611 break;
612 }
613 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
614 } else if (type == SQLITE_FLOAT) {
615 // FLOAT data
616 double value = sqlite3_column_double(statement, i);
617 status = window->putDouble(addedRows, i, value);
618 if (status) {
619 LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
620 i, status);
621 result = CPR_FULL;
622 break;
623 }
624 LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
625 } else if (type == SQLITE_BLOB) {
626 // BLOB data
627 const void* blob = sqlite3_column_blob(statement, i);
628 size_t size = sqlite3_column_bytes(statement, i);
629 status = window->putBlob(addedRows, i, blob, size);
630 if (status) {
631 LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
632 size, startPos + addedRows, i, status);
633 result = CPR_FULL;
634 break;
635 }
636 LOG_WINDOW("%d,%d is Blob with %u bytes",
637 startPos + addedRows, i, size);
638 } else if (type == SQLITE_NULL) {
639 // NULL field
640 status = window->putNull(addedRows, i);
641 if (status) {
642 LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
643 i, status);
644 result = CPR_FULL;
645 break;
646 }
647
648 LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
649 } else {
650 // Unknown data
651 ALOGE("Unknown column type when filling database window");
652 throw_sqlite3_exception(env, "Unknown column type when filling window");
653 result = CPR_ERROR;
654 break;
655 }
656 }
657
658 // Free the last row if if was not successfully copied.
659 if (result != CPR_OK) {
660 window->freeLastRow();
661 }
662 return result;
663 }
664
nativeExecuteForCursorWindow(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jlong windowPtr,jint startPos,jint requiredPos,jboolean countAllRows)665 static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
666 jlong connectionPtr, jlong statementPtr, jlong windowPtr,
667 jint startPos, jint requiredPos, jboolean countAllRows) {
668 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
669 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
670 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
671
672 status_t status = window->clear();
673 if (status) {
674 String8 msg;
675 msg.appendFormat("Failed to clear the cursor window, status=%d", status);
676 throw_sqlite3_exception(env, connection->db, msg.string());
677 return 0;
678 }
679
680 int numColumns = sqlite3_column_count(statement);
681 status = window->setNumColumns(numColumns);
682 if (status) {
683 String8 msg;
684 msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
685 numColumns, status);
686 throw_sqlite3_exception(env, connection->db, msg.string());
687 return 0;
688 }
689
690 int retryCount = 0;
691 int totalRows = 0;
692 int addedRows = 0;
693 bool windowFull = false;
694 bool gotException = false;
695 while (!gotException && (!windowFull || countAllRows)) {
696 int err = sqlite3_step(statement);
697 if (err == SQLITE_ROW) {
698 LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
699 retryCount = 0;
700 totalRows += 1;
701
702 // Skip the row if the window is full or we haven't reached the start position yet.
703 if (startPos >= totalRows || windowFull) {
704 continue;
705 }
706
707 CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
708 if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
709 // We filled the window before we got to the one row that we really wanted.
710 // Clear the window and start filling it again from here.
711 // TODO: Would be nicer if we could progressively replace earlier rows.
712 window->clear();
713 window->setNumColumns(numColumns);
714 startPos += addedRows;
715 addedRows = 0;
716 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
717 }
718
719 if (cpr == CPR_OK) {
720 addedRows += 1;
721 } else if (cpr == CPR_FULL) {
722 windowFull = true;
723 } else {
724 gotException = true;
725 }
726 } else if (err == SQLITE_DONE) {
727 // All rows processed, bail
728 LOG_WINDOW("Processed all rows");
729 break;
730 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
731 // The table is locked, retry
732 LOG_WINDOW("Database locked, retrying");
733 if (retryCount > 50) {
734 ALOGE("Bailing on database busy retry");
735 throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
736 gotException = true;
737 } else {
738 // Sleep to give the thread holding the lock a chance to finish
739 usleep(1000);
740 retryCount++;
741 }
742 } else {
743 throw_sqlite3_exception(env, connection->db);
744 gotException = true;
745 }
746 }
747
748 LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
749 "to the window in %d bytes",
750 statement, totalRows, addedRows, window->size() - window->freeSpace());
751 sqlite3_reset(statement);
752
753 // Report the total number of rows on request.
754 if (startPos > totalRows) {
755 ALOGE("startPos %d > actual rows %d", startPos, totalRows);
756 }
757 jlong result = jlong(startPos) << 32 | jlong(totalRows);
758 return result;
759 }
760
nativeGetDbLookaside(JNIEnv * env,jobject clazz,jlong connectionPtr)761 static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jlong connectionPtr) {
762 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
763
764 int cur = -1;
765 int unused;
766 sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
767 return cur;
768 }
769
nativeCancel(JNIEnv * env,jobject clazz,jlong connectionPtr)770 static void nativeCancel(JNIEnv* env, jobject clazz, jlong connectionPtr) {
771 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
772 connection->canceled = true;
773 }
774
nativeResetCancel(JNIEnv * env,jobject clazz,jlong connectionPtr,jboolean cancelable)775 static void nativeResetCancel(JNIEnv* env, jobject clazz, jlong connectionPtr,
776 jboolean cancelable) {
777 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
778 connection->canceled = false;
779
780 if (cancelable) {
781 sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
782 connection);
783 } else {
784 sqlite3_progress_handler(connection->db, 0, NULL, NULL);
785 }
786 }
787
788
789 static JNINativeMethod sMethods[] =
790 {
791 /* name, signature, funcPtr */
792 { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)J",
793 (void*)nativeOpen },
794 { "nativeClose", "(J)V",
795 (void*)nativeClose },
796 { "nativeRegisterCustomFunction", "(JLandroid/database/sqlite/SQLiteCustomFunction;)V",
797 (void*)nativeRegisterCustomFunction },
798 { "nativeRegisterLocalizedCollators", "(JLjava/lang/String;)V",
799 (void*)nativeRegisterLocalizedCollators },
800 { "nativePrepareStatement", "(JLjava/lang/String;)J",
801 (void*)nativePrepareStatement },
802 { "nativeFinalizeStatement", "(JJ)V",
803 (void*)nativeFinalizeStatement },
804 { "nativeGetParameterCount", "(JJ)I",
805 (void*)nativeGetParameterCount },
806 { "nativeIsReadOnly", "(JJ)Z",
807 (void*)nativeIsReadOnly },
808 { "nativeGetColumnCount", "(JJ)I",
809 (void*)nativeGetColumnCount },
810 { "nativeGetColumnName", "(JJI)Ljava/lang/String;",
811 (void*)nativeGetColumnName },
812 { "nativeBindNull", "(JJI)V",
813 (void*)nativeBindNull },
814 { "nativeBindLong", "(JJIJ)V",
815 (void*)nativeBindLong },
816 { "nativeBindDouble", "(JJID)V",
817 (void*)nativeBindDouble },
818 { "nativeBindString", "(JJILjava/lang/String;)V",
819 (void*)nativeBindString },
820 { "nativeBindBlob", "(JJI[B)V",
821 (void*)nativeBindBlob },
822 { "nativeResetStatementAndClearBindings", "(JJ)V",
823 (void*)nativeResetStatementAndClearBindings },
824 { "nativeExecute", "(JJ)V",
825 (void*)nativeExecute },
826 { "nativeExecuteForLong", "(JJ)J",
827 (void*)nativeExecuteForLong },
828 { "nativeExecuteForString", "(JJ)Ljava/lang/String;",
829 (void*)nativeExecuteForString },
830 { "nativeExecuteForBlobFileDescriptor", "(JJ)I",
831 (void*)nativeExecuteForBlobFileDescriptor },
832 { "nativeExecuteForChangedRowCount", "(JJ)I",
833 (void*)nativeExecuteForChangedRowCount },
834 { "nativeExecuteForLastInsertedRowId", "(JJ)J",
835 (void*)nativeExecuteForLastInsertedRowId },
836 { "nativeExecuteForCursorWindow", "(JJJIIZ)J",
837 (void*)nativeExecuteForCursorWindow },
838 { "nativeGetDbLookaside", "(J)I",
839 (void*)nativeGetDbLookaside },
840 { "nativeCancel", "(J)V",
841 (void*)nativeCancel },
842 { "nativeResetCancel", "(JZ)V",
843 (void*)nativeResetCancel },
844 };
845
register_android_database_SQLiteConnection(JNIEnv * env)846 int register_android_database_SQLiteConnection(JNIEnv *env)
847 {
848 jclass clazz = FindClassOrDie(env, "android/database/sqlite/SQLiteCustomFunction");
849
850 gSQLiteCustomFunctionClassInfo.name = GetFieldIDOrDie(env, clazz, "name", "Ljava/lang/String;");
851 gSQLiteCustomFunctionClassInfo.numArgs = GetFieldIDOrDie(env, clazz, "numArgs", "I");
852 gSQLiteCustomFunctionClassInfo.dispatchCallback = GetMethodIDOrDie(env, clazz,
853 "dispatchCallback", "([Ljava/lang/String;)V");
854
855 clazz = FindClassOrDie(env, "java/lang/String");
856 gStringClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
857
858 return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteConnection", sMethods,
859 NELEM(sMethods));
860 }
861
862 } // namespace android
863