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