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