blob: d0d53f6f451dd05372a468ab71dd8f4814833814 [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
32#include "binder/CursorWindow.h"
33
34#include <sqlite3.h>
35#include <sqlite3_android.h>
36
37#include "android_database_SQLiteCommon.h"
38
39#define UTF16_STORAGE 0
40#define ANDROID_TABLE "android_metadata"
41
42namespace android {
43
44static struct {
45 jfieldID name;
46 jfieldID numArgs;
47 jmethodID dispatchCallback;
48} gSQLiteCustomFunctionClassInfo;
49
50static struct {
51 jclass clazz;
52} gStringClassInfo;
53
54struct SQLiteConnection {
55 // Open flags.
56 // Must be kept in sync with the constants defined in SQLiteDatabase.java.
57 enum {
58 OPEN_READWRITE = 0x00000000,
59 OPEN_READONLY = 0x00000001,
60 OPEN_READ_MASK = 0x00000001,
61 NO_LOCALIZED_COLLATORS = 0x00000010,
62 CREATE_IF_NECESSARY = 0x10000000,
63 };
64
65 sqlite3* const db;
66 const int openFlags;
67 const String8 path;
68 const String8 label;
69
70 SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
71 db(db), openFlags(openFlags), path(path), label(label) { }
72};
73
74// Called each time a statement begins execution, when tracing is enabled.
75static void sqliteTraceCallback(void *data, const char *sql) {
76 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
77 ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n",
78 connection->label.string(), sql);
79}
80
81// Called each time a statement finishes execution, when profiling is enabled.
82static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
83 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
84 ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n",
85 connection->label.string(), sql, tm * 0.000001f);
86}
87
88
89static jint nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
90 jstring labelStr, jboolean enableTrace, jboolean enableProfile) {
91 int sqliteFlags;
92 if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
93 sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
94 } else if (openFlags & SQLiteConnection::OPEN_READONLY) {
95 sqliteFlags = SQLITE_OPEN_READONLY;
96 } else {
97 sqliteFlags = SQLITE_OPEN_READWRITE;
98 }
99
100 const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
101 String8 path(pathChars);
102 env->ReleaseStringUTFChars(pathStr, pathChars);
103
104 const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
105 String8 label(labelChars);
106 env->ReleaseStringUTFChars(labelStr, labelChars);
107
108 sqlite3* db;
109 int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL);
110 if (err != SQLITE_OK) {
111 throw_sqlite3_exception_errcode(env, err, "Could not open database");
112 return 0;
113 }
114
115 // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
116 err = sqlite3_busy_timeout(db, 1000 /* ms */);
117 if (err != SQLITE_OK) {
118 throw_sqlite3_exception(env, db, "Could not set busy timeout");
119 sqlite3_close(db);
120 return 0;
121 }
122
123 // Enable WAL auto-checkpointing after a commit whenever at least one frame is in the log.
124 // This ensures that a checkpoint will occur after each transaction if needed.
125 err = sqlite3_wal_autocheckpoint(db, 1);
126 if (err) {
127 throw_sqlite3_exception(env, db, "Could not enable auto-checkpointing.");
128 sqlite3_close(db);
129 return 0;
130 }
131
132 // Register custom Android functions.
133 err = register_android_functions(db, UTF16_STORAGE);
134 if (err) {
135 throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
136 sqlite3_close(db);
137 return 0;
138 }
139
140 // Create wrapper object.
141 SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
142
143 // Enable tracing and profiling if requested.
144 if (enableTrace) {
145 sqlite3_trace(db, &sqliteTraceCallback, connection);
146 }
147 if (enableProfile) {
148 sqlite3_profile(db, &sqliteProfileCallback, connection);
149 }
150
151 ALOGV("Opened connection %p with label '%s'", db, label.string());
152 return reinterpret_cast<jint>(connection);
153}
154
155static void nativeClose(JNIEnv* env, jclass clazz, jint connectionPtr) {
156 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
157
158 if (connection) {
159 ALOGV("Closing connection %p", connection->db);
160 int err = sqlite3_close(connection->db);
161 if (err != SQLITE_OK) {
162 // This can happen if sub-objects aren't closed first. Make sure the caller knows.
163 ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
164 throw_sqlite3_exception(env, connection->db, "Count not close db.");
165 return;
166 }
167
168 delete connection;
169 }
170}
171
172// Called each time a custom function is evaluated.
173static void sqliteCustomFunctionCallback(sqlite3_context *context,
174 int argc, sqlite3_value **argv) {
175 JNIEnv* env = AndroidRuntime::getJNIEnv();
176
177 // Get the callback function object.
178 // Create a new local reference to it in case the callback tries to do something
179 // dumb like unregister the function (thereby destroying the global ref) while it is running.
180 jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
181 jobject functionObj = env->NewLocalRef(functionObjGlobal);
182
183 jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL);
184 if (argsArray) {
185 for (int i = 0; i < argc; i++) {
186 const jchar* arg = static_cast<const jchar*>(sqlite3_value_text16(argv[i]));
187 if (!arg) {
188 ALOGW("NULL argument in custom_function_callback. This should not happen.");
189 } else {
190 size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar);
191 jstring argStr = env->NewString(arg, argLen);
192 if (!argStr) {
193 goto error; // out of memory error
194 }
195 env->SetObjectArrayElement(argsArray, i, argStr);
196 env->DeleteLocalRef(argStr);
197 }
198 }
199
200 // TODO: Support functions that return values.
201 env->CallVoidMethod(functionObj,
202 gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray);
203
204error:
205 env->DeleteLocalRef(argsArray);
206 }
207
208 env->DeleteLocalRef(functionObj);
209
210 if (env->ExceptionCheck()) {
211 ALOGE("An exception was thrown by custom SQLite function.");
212 LOGE_EX(env);
213 env->ExceptionClear();
214 }
215}
216
217// Called when a custom function is destroyed.
218static void sqliteCustomFunctionDestructor(void* data) {
219 jobject functionObjGlobal = reinterpret_cast<jobject>(data);
220
221 JNIEnv* env = AndroidRuntime::getJNIEnv();
222 env->DeleteGlobalRef(functionObjGlobal);
223}
224
225static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jint connectionPtr,
226 jobject functionObj) {
227 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
228
229 jstring nameStr = jstring(env->GetObjectField(
230 functionObj, gSQLiteCustomFunctionClassInfo.name));
231 jint numArgs = env->GetIntField(functionObj, gSQLiteCustomFunctionClassInfo.numArgs);
232
233 jobject functionObjGlobal = env->NewGlobalRef(functionObj);
234
235 const char* name = env->GetStringUTFChars(nameStr, NULL);
236 int err = sqlite3_create_function_v2(connection->db, name, numArgs, SQLITE_UTF16,
237 reinterpret_cast<void*>(functionObjGlobal),
238 &sqliteCustomFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor);
239 env->ReleaseStringUTFChars(nameStr, name);
240
241 if (err != SQLITE_OK) {
242 ALOGE("sqlite3_create_function returned %d", err);
243 env->DeleteGlobalRef(functionObjGlobal);
244 throw_sqlite3_exception(env, connection->db);
245 return;
246 }
247}
248
249// Set locale in the android_metadata table, install localized collators, and rebuild indexes
250static void nativeSetLocale(JNIEnv* env, jclass clazz, jint connectionPtr, jstring localeStr) {
251 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
252
253 if (connection->openFlags & SQLiteConnection::NO_LOCALIZED_COLLATORS) {
254 // We should probably throw IllegalStateException but the contract for
255 // setLocale says that we just do nothing. Oh well.
256 return;
257 }
258
259 int err;
260 char const* locale = env->GetStringUTFChars(localeStr, NULL);
261 sqlite3_stmt* stmt = NULL;
262 char** meta = NULL;
263 int rowCount, colCount;
264 char* dbLocale = NULL;
265
266 // create the table, if necessary and possible
267 if (!(connection->openFlags & SQLiteConnection::OPEN_READONLY)) {
268 err = sqlite3_exec(connection->db,
269 "CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)",
270 NULL, NULL, NULL);
271 if (err != SQLITE_OK) {
272 ALOGE("CREATE TABLE " ANDROID_TABLE " failed");
273 throw_sqlite3_exception(env, connection->db);
274 goto done;
275 }
276 }
277
278 // try to read from the table
279 err = sqlite3_get_table(connection->db,
280 "SELECT locale FROM " ANDROID_TABLE " LIMIT 1",
281 &meta, &rowCount, &colCount, NULL);
282 if (err != SQLITE_OK) {
283 ALOGE("SELECT locale FROM " ANDROID_TABLE " failed");
284 throw_sqlite3_exception(env, connection->db);
285 goto done;
286 }
287
288 dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
289
290 if (dbLocale != NULL && !strcmp(dbLocale, locale)) {
291 // database locale is the same as the desired locale; set up the collators and go
292 err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
293 if (err != SQLITE_OK) {
294 throw_sqlite3_exception(env, connection->db);
295 }
296 goto done; // no database changes needed
297 }
298
299 if (connection->openFlags & SQLiteConnection::OPEN_READONLY) {
300 // read-only database, so we're going to have to put up with whatever we got
301 // For registering new index. Not for modifing the read-only database.
302 err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
303 if (err != SQLITE_OK) {
304 throw_sqlite3_exception(env, connection->db);
305 }
306 goto done;
307 }
308
309 // need to update android_metadata and indexes atomically, so use a transaction...
310 err = sqlite3_exec(connection->db, "BEGIN TRANSACTION", NULL, NULL, NULL);
311 if (err != SQLITE_OK) {
312 ALOGE("BEGIN TRANSACTION failed setting locale");
313 throw_sqlite3_exception(env, connection->db);
314 goto done;
315 }
316
317 err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
318 if (err != SQLITE_OK) {
319 ALOGE("register_localized_collators() failed setting locale");
320 throw_sqlite3_exception(env, connection->db);
321 goto rollback;
322 }
323
324 err = sqlite3_exec(connection->db, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
325 if (err != SQLITE_OK) {
326 ALOGE("DELETE failed setting locale");
327 throw_sqlite3_exception(env, connection->db);
328 goto rollback;
329 }
330
331 static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
332 err = sqlite3_prepare_v2(connection->db, sql, -1, &stmt, NULL);
333 if (err != SQLITE_OK) {
334 ALOGE("sqlite3_prepare_v2(\"%s\") failed", sql);
335 throw_sqlite3_exception(env, connection->db);
336 goto rollback;
337 }
338
339 err = sqlite3_bind_text(stmt, 1, locale, -1, SQLITE_TRANSIENT);
340 if (err != SQLITE_OK) {
341 ALOGE("sqlite3_bind_text() failed setting locale");
342 throw_sqlite3_exception(env, connection->db);
343 goto rollback;
344 }
345
346 err = sqlite3_step(stmt);
347 if (err != SQLITE_OK && err != SQLITE_DONE) {
348 ALOGE("sqlite3_step(\"%s\") failed setting locale", sql);
349 throw_sqlite3_exception(env, connection->db);
350 goto rollback;
351 }
352
353 err = sqlite3_exec(connection->db, "REINDEX LOCALIZED", NULL, NULL, NULL);
354 if (err != SQLITE_OK) {
355 ALOGE("REINDEX LOCALIZED failed");
356 throw_sqlite3_exception(env, connection->db);
357 goto rollback;
358 }
359
360 // all done, yay!
361 err = sqlite3_exec(connection->db, "COMMIT TRANSACTION", NULL, NULL, NULL);
362 if (err != SQLITE_OK) {
363 ALOGE("COMMIT TRANSACTION failed setting locale");
364 throw_sqlite3_exception(env, connection->db);
365 goto done;
366 }
367
368rollback:
369 if (err != SQLITE_OK) {
370 sqlite3_exec(connection->db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
371 }
372
373done:
374 if (stmt) {
375 sqlite3_finalize(stmt);
376 }
377 if (meta) {
378 sqlite3_free_table(meta);
379 }
380 if (locale) {
381 env->ReleaseStringUTFChars(localeStr, locale);
382 }
383}
384
385static jint nativePrepareStatement(JNIEnv* env, jclass clazz, jint connectionPtr,
386 jstring sqlString) {
387 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
388
389 jsize sqlLength = env->GetStringLength(sqlString);
390 const jchar* sql = env->GetStringCritical(sqlString, NULL);
391 sqlite3_stmt* statement;
392 int err = sqlite3_prepare16_v2(connection->db,
393 sql, sqlLength * sizeof(jchar), &statement, NULL);
394 env->ReleaseStringCritical(sqlString, sql);
395
396 if (err != SQLITE_OK) {
397 // Error messages like 'near ")": syntax error' are not
398 // always helpful enough, so construct an error string that
399 // includes the query itself.
400 const char *query = env->GetStringUTFChars(sqlString, NULL);
401 char *message = (char*) malloc(strlen(query) + 50);
402 if (message) {
403 strcpy(message, ", while compiling: "); // less than 50 chars
404 strcat(message, query);
405 }
406 env->ReleaseStringUTFChars(sqlString, query);
407 throw_sqlite3_exception(env, connection->db, message);
408 free(message);
409 return 0;
410 }
411
412 ALOGV("Prepared statement %p on connection %p", statement, connection->db);
413 return reinterpret_cast<jint>(statement);
414}
415
416static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jint connectionPtr,
417 jint statementPtr) {
418 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
419 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
420
421 ALOGV("Finalized statement %p on connection %p", statement, connection->db);
422 int err = sqlite3_finalize(statement);
423 if (err != SQLITE_OK) {
424 throw_sqlite3_exception(env, connection->db, NULL);
425 }
426}
427
428static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jint connectionPtr,
429 jint statementPtr) {
430 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
431 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
432
433 return sqlite3_bind_parameter_count(statement);
434}
435
436static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jint connectionPtr,
437 jint statementPtr) {
438 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
439 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
440
441 return sqlite3_stmt_readonly(statement) != 0;
442}
443
444static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jint connectionPtr,
445 jint statementPtr) {
446 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
447 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
448
449 return sqlite3_column_count(statement);
450}
451
452static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jint connectionPtr,
453 jint statementPtr, jint index) {
454 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
455 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
456
457 const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(statement, index));
458 if (name) {
459 size_t length = 0;
460 while (name[length]) {
461 length += 1;
462 }
463 return env->NewString(name, length);
464 }
465 return NULL;
466}
467
468static void nativeBindNull(JNIEnv* env, jclass clazz, jint connectionPtr,
469 jint statementPtr, jint index) {
470 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
471 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
472
473 int err = sqlite3_bind_null(statement, index);
474 if (err != SQLITE_OK) {
475 throw_sqlite3_exception(env, connection->db, NULL);
476 }
477}
478
479static void nativeBindLong(JNIEnv* env, jclass clazz, jint connectionPtr,
480 jint statementPtr, jint index, jlong value) {
481 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
482 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
483
484 int err = sqlite3_bind_int64(statement, index, value);
485 if (err != SQLITE_OK) {
486 throw_sqlite3_exception(env, connection->db, NULL);
487 }
488}
489
490static void nativeBindDouble(JNIEnv* env, jclass clazz, jint connectionPtr,
491 jint statementPtr, jint index, jdouble value) {
492 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
493 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
494
495 int err = sqlite3_bind_double(statement, index, value);
496 if (err != SQLITE_OK) {
497 throw_sqlite3_exception(env, connection->db, NULL);
498 }
499}
500
501static void nativeBindString(JNIEnv* env, jclass clazz, jint connectionPtr,
502 jint statementPtr, jint index, jstring valueString) {
503 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
504 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
505
506 jsize valueLength = env->GetStringLength(valueString);
507 const jchar* value = env->GetStringCritical(valueString, NULL);
508 int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
509 SQLITE_TRANSIENT);
510 env->ReleaseStringCritical(valueString, value);
511 if (err != SQLITE_OK) {
512 throw_sqlite3_exception(env, connection->db, NULL);
513 }
514}
515
516static void nativeBindBlob(JNIEnv* env, jclass clazz, jint connectionPtr,
517 jint statementPtr, jint index, jbyteArray valueArray) {
518 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
519 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
520
521 jsize valueLength = env->GetArrayLength(valueArray);
522 jbyte* value = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(valueArray, NULL));
523 int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
524 env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
525 if (err != SQLITE_OK) {
526 throw_sqlite3_exception(env, connection->db, NULL);
527 }
528}
529
530static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jint connectionPtr,
531 jint statementPtr) {
532 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
533 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
534
535 int err = sqlite3_reset(statement);
536 if (err == SQLITE_OK) {
537 err = sqlite3_clear_bindings(statement);
538 }
539 if (err != SQLITE_OK) {
540 throw_sqlite3_exception(env, connection->db, NULL);
541 }
542}
543
544static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
545 int err = sqlite3_step(statement);
546 if (err == SQLITE_ROW) {
547 throw_sqlite3_exception(env,
548 "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
549 } else if (err != SQLITE_DONE) {
550 throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(connection->db));
551 }
552 return err;
553}
554
555static void nativeExecute(JNIEnv* env, jclass clazz, jint connectionPtr,
556 jint statementPtr) {
557 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
558 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
559
560 executeNonQuery(env, connection, statement);
561}
562
563static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
564 jint connectionPtr, jint statementPtr) {
565 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
566 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
567
568 int err = executeNonQuery(env, connection, statement);
569 return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
570}
571
572static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
573 jint connectionPtr, jint statementPtr) {
574 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
575 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
576
577 int err = executeNonQuery(env, connection, statement);
578 return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
579 ? sqlite3_last_insert_rowid(connection->db) : -1;
580}
581
582static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
583 int err = sqlite3_step(statement);
584 if (err != SQLITE_ROW) {
585 throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(connection->db));
586 }
587 return err;
588}
589
590static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
591 jint connectionPtr, jint statementPtr) {
592 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
593 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
594
595 int err = executeOneRowQuery(env, connection, statement);
596 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
597 return sqlite3_column_int64(statement, 0);
598 }
599 return -1;
600}
601
602static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
603 jint connectionPtr, jint statementPtr) {
604 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
605 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
606
607 int err = executeOneRowQuery(env, connection, statement);
608 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
609 const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(statement, 0));
610 if (text) {
611 size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
612 return env->NewString(text, length);
613 }
614 }
615 return NULL;
616}
617
618static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
619 int error = 0;
620 int fd = ashmem_create_region(NULL, length);
621 if (fd < 0) {
622 error = errno;
623 ALOGE("ashmem_create_region failed: %s", strerror(error));
624 } else {
625 if (length > 0) {
626 void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
627 if (ptr == MAP_FAILED) {
628 error = errno;
629 ALOGE("mmap failed: %s", strerror(error));
630 } else {
631 memcpy(ptr, data, length);
632 munmap(ptr, length);
633 }
634 }
635
636 if (!error) {
637 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
638 error = errno;
639 ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
640 } else {
641 return fd;
642 }
643 }
644
645 close(fd);
646 }
647
648 jniThrowIOException(env, error);
649 return -1;
650}
651
652static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
653 jint connectionPtr, jint statementPtr) {
654 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
655 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
656
657 int err = executeOneRowQuery(env, connection, statement);
658 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
659 const void* blob = sqlite3_column_blob(statement, 0);
660 if (blob) {
661 int length = sqlite3_column_bytes(statement, 0);
662 if (length >= 0) {
663 return createAshmemRegionWithData(env, blob, length);
664 }
665 }
666 }
667 return -1;
668}
669
670enum CopyRowResult {
671 CPR_OK,
672 CPR_FULL,
673 CPR_ERROR,
674};
675
676static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
677 sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
678 // Allocate a new field directory for the row.
679 status_t status = window->allocRow();
680 if (status) {
681 LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
682 startPos, addedRows, status);
683 return CPR_FULL;
684 }
685
686 // Pack the row into the window.
687 CopyRowResult result = CPR_OK;
688 for (int i = 0; i < numColumns; i++) {
689 int type = sqlite3_column_type(statement, i);
690 if (type == SQLITE_TEXT) {
691 // TEXT data
692 const char* text = reinterpret_cast<const char*>(
693 sqlite3_column_text(statement, i));
694 // SQLite does not include the NULL terminator in size, but does
695 // ensure all strings are NULL terminated, so increase size by
696 // one to make sure we store the terminator.
697 size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
698 status = window->putString(addedRows, i, text, sizeIncludingNull);
699 if (status) {
700 LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
701 sizeIncludingNull, startPos + addedRows, i, status);
702 result = CPR_FULL;
703 break;
704 }
705 LOG_WINDOW("%d,%d is TEXT with %u bytes",
706 startPos + addedRows, i, sizeIncludingNull);
707 } else if (type == SQLITE_INTEGER) {
708 // INTEGER data
709 int64_t value = sqlite3_column_int64(statement, i);
710 status = window->putLong(addedRows, i, value);
711 if (status) {
712 LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
713 i, status);
714 result = CPR_FULL;
715 break;
716 }
717 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
718 } else if (type == SQLITE_FLOAT) {
719 // FLOAT data
720 double value = sqlite3_column_double(statement, i);
721 status = window->putDouble(addedRows, i, value);
722 if (status) {
723 LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
724 i, status);
725 result = CPR_FULL;
726 break;
727 }
728 LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
729 } else if (type == SQLITE_BLOB) {
730 // BLOB data
731 const void* blob = sqlite3_column_blob(statement, i);
732 size_t size = sqlite3_column_bytes(statement, i);
733 status = window->putBlob(addedRows, i, blob, size);
734 if (status) {
735 LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
736 size, startPos + addedRows, i, status);
737 result = CPR_FULL;
738 break;
739 }
740 LOG_WINDOW("%d,%d is Blob with %u bytes",
741 startPos + addedRows, i, size);
742 } else if (type == SQLITE_NULL) {
743 // NULL field
744 status = window->putNull(addedRows, i);
745 if (status) {
746 LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
747 i, status);
748 result = CPR_FULL;
749 break;
750 }
751
752 LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
753 } else {
754 // Unknown data
755 ALOGE("Unknown column type when filling database window");
756 throw_sqlite3_exception(env, "Unknown column type when filling window");
757 result = CPR_ERROR;
758 break;
759 }
760 }
761
762 // Free the last row if if was not successfully copied.
763 if (result != CPR_OK) {
764 window->freeLastRow();
765 }
766 return result;
767}
768
769static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
770 jint connectionPtr, jint statementPtr, jint windowPtr,
771 jint startPos, jint requiredPos, jboolean countAllRows) {
772 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
773 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
774 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
775
776 status_t status = window->clear();
777 if (status) {
778 String8 msg;
779 msg.appendFormat("Failed to clear the cursor window, status=%d", status);
780 throw_sqlite3_exception(env, connection->db, msg.string());
781 return 0;
782 }
783
784 int numColumns = sqlite3_column_count(statement);
785 status = window->setNumColumns(numColumns);
786 if (status) {
787 String8 msg;
788 msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
789 numColumns, status);
790 throw_sqlite3_exception(env, connection->db, msg.string());
791 return 0;
792 }
793
794 int retryCount = 0;
795 int totalRows = 0;
796 int addedRows = 0;
797 bool windowFull = false;
798 bool gotException = false;
799 while (!gotException && (!windowFull || countAllRows)) {
800 int err = sqlite3_step(statement);
801 if (err == SQLITE_ROW) {
802 LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
803 retryCount = 0;
804 totalRows += 1;
805
806 // Skip the row if the window is full or we haven't reached the start position yet.
807 if (startPos >= totalRows || windowFull) {
808 continue;
809 }
810
811 CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
812 if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {
813 // We filled the window before we got to the one row that we really wanted.
814 // Clear the window and start filling it again from here.
815 // TODO: Would be nicer if we could progressively replace earlier rows.
816 window->clear();
817 window->setNumColumns(numColumns);
818 startPos += addedRows;
819 addedRows = 0;
820 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
821 }
822
823 if (cpr == CPR_OK) {
824 addedRows += 1;
825 } else if (cpr == CPR_FULL) {
826 windowFull = true;
827 } else {
828 gotException = true;
829 }
830 } else if (err == SQLITE_DONE) {
831 // All rows processed, bail
832 LOG_WINDOW("Processed all rows");
833 break;
834 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
835 // The table is locked, retry
836 LOG_WINDOW("Database locked, retrying");
837 if (retryCount > 50) {
838 ALOGE("Bailing on database busy retry");
839 throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
840 gotException = true;
841 } else {
842 // Sleep to give the thread holding the lock a chance to finish
843 usleep(1000);
844 retryCount++;
845 }
846 } else {
847 throw_sqlite3_exception(env, connection->db);
848 gotException = true;
849 }
850 }
851
852 LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
853 "to the window in %d bytes",
854 statement, totalRows, addedRows, window->size() - window->freeSpace());
855 sqlite3_reset(statement);
856
857 // Report the total number of rows on request.
858 if (startPos > totalRows) {
859 ALOGE("startPos %d > actual rows %d", startPos, totalRows);
860 }
861 jlong result = jlong(startPos) << 32 | jlong(totalRows);
862 return result;
863}
864
865static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jint connectionPtr) {
866 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
867
868 int cur = -1;
869 int unused;
870 sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
871 return cur;
872}
873
874
875static JNINativeMethod sMethods[] =
876{
877 /* name, signature, funcPtr */
878 { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
879 (void*)nativeOpen },
880 { "nativeClose", "(I)V",
881 (void*)nativeClose },
882 { "nativeRegisterCustomFunction", "(ILandroid/database/sqlite/SQLiteCustomFunction;)V",
883 (void*)nativeRegisterCustomFunction },
884 { "nativeSetLocale", "(ILjava/lang/String;)V",
885 (void*)nativeSetLocale },
886 { "nativePrepareStatement", "(ILjava/lang/String;)I",
887 (void*)nativePrepareStatement },
888 { "nativeFinalizeStatement", "(II)V",
889 (void*)nativeFinalizeStatement },
890 { "nativeGetParameterCount", "(II)I",
891 (void*)nativeGetParameterCount },
892 { "nativeIsReadOnly", "(II)Z",
893 (void*)nativeIsReadOnly },
894 { "nativeGetColumnCount", "(II)I",
895 (void*)nativeGetColumnCount },
896 { "nativeGetColumnName", "(III)Ljava/lang/String;",
897 (void*)nativeGetColumnName },
898 { "nativeBindNull", "(III)V",
899 (void*)nativeBindNull },
900 { "nativeBindLong", "(IIIJ)V",
901 (void*)nativeBindLong },
902 { "nativeBindDouble", "(IIID)V",
903 (void*)nativeBindDouble },
904 { "nativeBindString", "(IIILjava/lang/String;)V",
905 (void*)nativeBindString },
906 { "nativeBindBlob", "(III[B)V",
907 (void*)nativeBindBlob },
908 { "nativeResetStatementAndClearBindings", "(II)V",
909 (void*)nativeResetStatementAndClearBindings },
910 { "nativeExecute", "(II)V",
911 (void*)nativeExecute },
912 { "nativeExecuteForLong", "(II)J",
913 (void*)nativeExecuteForLong },
914 { "nativeExecuteForString", "(II)Ljava/lang/String;",
915 (void*)nativeExecuteForString },
916 { "nativeExecuteForBlobFileDescriptor", "(II)I",
917 (void*)nativeExecuteForBlobFileDescriptor },
918 { "nativeExecuteForChangedRowCount", "(II)I",
919 (void*)nativeExecuteForChangedRowCount },
920 { "nativeExecuteForLastInsertedRowId", "(II)J",
921 (void*)nativeExecuteForLastInsertedRowId },
922 { "nativeExecuteForCursorWindow", "(IIIIIZ)J",
923 (void*)nativeExecuteForCursorWindow },
924 { "nativeGetDbLookaside", "(I)I",
925 (void*)nativeGetDbLookaside },
926};
927
928#define FIND_CLASS(var, className) \
929 var = env->FindClass(className); \
930 LOG_FATAL_IF(! var, "Unable to find class " className);
931
932#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
933 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
934 LOG_FATAL_IF(! var, "Unable to find method" methodName);
935
936#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
937 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
938 LOG_FATAL_IF(! var, "Unable to find field " fieldName);
939
940int register_android_database_SQLiteConnection(JNIEnv *env)
941{
942 jclass clazz;
943 FIND_CLASS(clazz, "android/database/sqlite/SQLiteCustomFunction");
944
945 GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.name, clazz,
946 "name", "Ljava/lang/String;");
947 GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.numArgs, clazz,
948 "numArgs", "I");
949 GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback,
950 clazz, "dispatchCallback", "([Ljava/lang/String;)V");
951
952 FIND_CLASS(clazz, "java/lang/String");
953 gStringClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
954
955 return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteConnection",
956 sMethods, NELEM(sMethods));
957}
958
959} // namespace android