1 /* 2 * Copyright (C) 2006-2007 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 #undef LOG_TAG 18 #define LOG_TAG "SqliteDatabaseCpp" 19 20 #include <utils/Log.h> 21 #include <utils/String8.h> 22 #include <utils/String16.h> 23 24 #include <jni.h> 25 #include <JNIHelp.h> 26 #include <android_runtime/AndroidRuntime.h> 27 28 #include <sqlite3.h> 29 #include <sqlite3_android.h> 30 #include <string.h> 31 #include <utils/Log.h> 32 #include <utils/threads.h> 33 #include <utils/List.h> 34 #include <utils/Errors.h> 35 #include <ctype.h> 36 37 #include <stdio.h> 38 #include <sys/types.h> 39 #include <sys/socket.h> 40 #include <netinet/in.h> 41 #include <string.h> 42 #include <netdb.h> 43 #include <sys/ioctl.h> 44 45 #include "sqlite3_exception.h" 46 47 #define UTF16_STORAGE 0 48 #define INVALID_VERSION -1 49 #define ANDROID_TABLE "android_metadata" 50 /* uncomment the next line to force-enable logging of all statements */ 51 // #define DB_LOG_STATEMENTS 52 53 #define DEBUG_JNI 0 54 55 namespace android { 56 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 static jfieldID offset_db_handle; 66 static jmethodID method_custom_function_callback; 67 static jclass string_class; 68 static jint sSqliteSoftHeapLimit = 0; 69 70 static char *createStr(const char *path, short extra) { 71 int len = strlen(path) + extra; 72 char *str = (char *)malloc(len + 1); 73 strncpy(str, path, len); 74 str[len] = NULL; 75 return str; 76 } 77 78 static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) { 79 // skip printing this message if it is due to certain types of errors 80 if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT) return; 81 // print databasename, errorcode and msg 82 LOGI("sqlite returned: error code = %d, msg = %s, db=%s\n", iErrCode, zMsg, databaseName); 83 } 84 85 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called. 86 static void registerLoggingFunc(const char *path) { 87 static bool loggingFuncSet = false; 88 if (loggingFuncSet) { 89 return; 90 } 91 92 LOGV("Registering sqlite logging func \n"); 93 int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0)); 94 if (err != SQLITE_OK) { 95 LOGW("sqlite returned error = %d when trying to register logging func.\n", err); 96 return; 97 } 98 loggingFuncSet = true; 99 } 100 101 /* public native void dbopen(String path, int flags, String locale); */ 102 static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags) 103 { 104 int err; 105 sqlite3 * handle = NULL; 106 sqlite3_stmt * statement = NULL; 107 char const * path8 = env->GetStringUTFChars(pathString, NULL); 108 int sqliteFlags; 109 110 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called. 111 registerLoggingFunc(path8); 112 113 // convert our flags into the sqlite flags 114 if (flags & CREATE_IF_NECESSARY) { 115 sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; 116 } else if (flags & OPEN_READONLY) { 117 sqliteFlags = SQLITE_OPEN_READONLY; 118 } else { 119 sqliteFlags = SQLITE_OPEN_READWRITE; 120 } 121 122 err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL); 123 if (err != SQLITE_OK) { 124 LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags); 125 throw_sqlite3_exception(env, handle); 126 goto done; 127 } 128 129 // The soft heap limit prevents the page cache allocations from growing 130 // beyond the given limit, no matter what the max page cache sizes are 131 // set to. The limit does not, as of 3.5.0, affect any other allocations. 132 sqlite3_soft_heap_limit(sSqliteSoftHeapLimit); 133 134 // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY 135 err = sqlite3_busy_timeout(handle, 1000 /* ms */); 136 if (err != SQLITE_OK) { 137 LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8); 138 throw_sqlite3_exception(env, handle); 139 goto done; 140 } 141 142 #ifdef DB_INTEGRITY_CHECK 143 static const char* integritySql = "pragma integrity_check(1);"; 144 err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL); 145 if (err != SQLITE_OK) { 146 LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8); 147 throw_sqlite3_exception(env, handle); 148 goto done; 149 } 150 151 // first is OK or error message 152 err = sqlite3_step(statement); 153 if (err != SQLITE_ROW) { 154 LOGE("integrity check failed for \"%s\"\n", integritySql, path8); 155 throw_sqlite3_exception(env, handle); 156 goto done; 157 } else { 158 const char *text = (const char*)sqlite3_column_text(statement, 0); 159 if (strcmp(text, "ok") != 0) { 160 LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text); 161 jniThrowException(env, "android/database/sqlite/SQLiteDatabaseCorruptException", text); 162 goto done; 163 } 164 } 165 #endif 166 167 err = register_android_functions(handle, UTF16_STORAGE); 168 if (err) { 169 throw_sqlite3_exception(env, handle); 170 goto done; 171 } 172 173 LOGV("Opened '%s' - %p\n", path8, handle); 174 env->SetIntField(object, offset_db_handle, (int) handle); 175 handle = NULL; // The caller owns the handle now. 176 177 done: 178 // Release allocated resources 179 if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8); 180 if (statement != NULL) sqlite3_finalize(statement); 181 if (handle != NULL) sqlite3_close(handle); 182 } 183 184 static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName, short connNum) { 185 char const *path = env->GetStringUTFChars(databaseName, NULL); 186 if (path == NULL) { 187 LOGE("Failure in getDatabaseName(). VM ran out of memory?\n"); 188 return NULL; // VM would have thrown OutOfMemoryError 189 } 190 char *dbNameStr = createStr(path, 4); 191 if (connNum > 999) { // TODO: if number of pooled connections > 999, fix this line. 192 connNum = -1; 193 } 194 sprintf(dbNameStr + strlen(path), "|%03d", connNum); 195 env->ReleaseStringUTFChars(databaseName, path); 196 return dbNameStr; 197 } 198 199 static void sqlTrace(void *databaseName, const char *sql) { 200 LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql); 201 } 202 203 /* public native void enableSqlTracing(); */ 204 static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName, jshort connType) 205 { 206 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); 207 sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName, connType)); 208 } 209 210 static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) { 211 double d = tm/1000000.0; 212 LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql); 213 } 214 215 /* public native void enableSqlProfiling(); */ 216 static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName, jshort connType) 217 { 218 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); 219 sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName, 220 connType)); 221 } 222 223 /* public native void close(); */ 224 static void dbclose(JNIEnv* env, jobject object) 225 { 226 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); 227 228 if (handle != NULL) { 229 // release the memory associated with the traceFuncArg in enableSqlTracing function 230 void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL); 231 if (traceFuncArg != NULL) { 232 free(traceFuncArg); 233 } 234 // release the memory associated with the traceFuncArg in enableSqlProfiling function 235 traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL); 236 if (traceFuncArg != NULL) { 237 free(traceFuncArg); 238 } 239 LOGV("Closing database: handle=%p\n", handle); 240 int result = sqlite3_close(handle); 241 if (result == SQLITE_OK) { 242 LOGV("Closed %p\n", handle); 243 env->SetIntField(object, offset_db_handle, 0); 244 } else { 245 // This can happen if sub-objects aren't closed first. Make sure the caller knows. 246 throw_sqlite3_exception(env, handle); 247 LOGE("sqlite3_close(%p) failed: %d\n", handle, result); 248 } 249 } 250 } 251 252 /* native int native_getDbLookaside(); */ 253 static jint native_getDbLookaside(JNIEnv* env, jobject object) 254 { 255 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); 256 int pCur = -1; 257 int unused; 258 sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0); 259 return pCur; 260 } 261 262 /* set locale in the android_metadata table, install localized collators, and rebuild indexes */ 263 static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags) 264 { 265 if ((flags & NO_LOCALIZED_COLLATORS)) return; 266 267 int err; 268 char const* locale8 = env->GetStringUTFChars(localeString, NULL); 269 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); 270 sqlite3_stmt* stmt = NULL; 271 char** meta = NULL; 272 int rowCount, colCount; 273 char* dbLocale = NULL; 274 275 // create the table, if necessary and possible 276 if (!(flags & OPEN_READONLY)) { 277 static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)"; 278 err = sqlite3_exec(handle, createSql, NULL, NULL, NULL); 279 if (err != SQLITE_OK) { 280 LOGE("CREATE TABLE " ANDROID_TABLE " failed\n"); 281 throw_sqlite3_exception(env, handle); 282 goto done; 283 } 284 } 285 286 // try to read from the table 287 static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1"; 288 err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL); 289 if (err != SQLITE_OK) { 290 LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n"); 291 throw_sqlite3_exception(env, handle); 292 goto done; 293 } 294 295 dbLocale = (rowCount >= 1) ? meta[colCount] : NULL; 296 297 if (dbLocale != NULL && !strcmp(dbLocale, locale8)) { 298 // database locale is the same as the desired locale; set up the collators and go 299 err = register_localized_collators(handle, locale8, UTF16_STORAGE); 300 if (err != SQLITE_OK) throw_sqlite3_exception(env, handle); 301 goto done; // no database changes needed 302 } 303 304 if ((flags & OPEN_READONLY)) { 305 // read-only database, so we're going to have to put up with whatever we got 306 // For registering new index. Not for modifing the read-only database. 307 err = register_localized_collators(handle, locale8, UTF16_STORAGE); 308 if (err != SQLITE_OK) throw_sqlite3_exception(env, handle); 309 goto done; 310 } 311 312 // need to update android_metadata and indexes atomically, so use a transaction... 313 err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL); 314 if (err != SQLITE_OK) { 315 LOGE("BEGIN TRANSACTION failed setting locale\n"); 316 throw_sqlite3_exception(env, handle); 317 goto done; 318 } 319 320 err = register_localized_collators(handle, locale8, UTF16_STORAGE); 321 if (err != SQLITE_OK) { 322 LOGE("register_localized_collators() failed setting locale\n"); 323 throw_sqlite3_exception(env, handle); 324 goto rollback; 325 } 326 327 err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL); 328 if (err != SQLITE_OK) { 329 LOGE("DELETE failed setting locale\n"); 330 throw_sqlite3_exception(env, handle); 331 goto rollback; 332 } 333 334 static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);"; 335 err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL); 336 if (err != SQLITE_OK) { 337 LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql); 338 throw_sqlite3_exception(env, handle); 339 goto rollback; 340 } 341 342 err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT); 343 if (err != SQLITE_OK) { 344 LOGE("sqlite3_bind_text() failed setting locale\n"); 345 throw_sqlite3_exception(env, handle); 346 goto rollback; 347 } 348 349 err = sqlite3_step(stmt); 350 if (err != SQLITE_OK && err != SQLITE_DONE) { 351 LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql); 352 throw_sqlite3_exception(env, handle); 353 goto rollback; 354 } 355 356 err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL); 357 if (err != SQLITE_OK) { 358 LOGE("REINDEX LOCALIZED failed\n"); 359 throw_sqlite3_exception(env, handle); 360 goto rollback; 361 } 362 363 // all done, yay! 364 err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL); 365 if (err != SQLITE_OK) { 366 LOGE("COMMIT TRANSACTION failed setting locale\n"); 367 throw_sqlite3_exception(env, handle); 368 goto done; 369 } 370 371 rollback: 372 if (err != SQLITE_OK) { 373 sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL); 374 } 375 376 done: 377 if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8); 378 if (stmt != NULL) sqlite3_finalize(stmt); 379 if (meta != NULL) sqlite3_free_table(meta); 380 } 381 382 static void native_setSqliteSoftHeapLimit(JNIEnv* env, jobject clazz, jint limit) { 383 sSqliteSoftHeapLimit = limit; 384 } 385 386 static jint native_releaseMemory(JNIEnv *env, jobject clazz) 387 { 388 // Attempt to release as much memory from the 389 return sqlite3_release_memory(sSqliteSoftHeapLimit); 390 } 391 392 static void native_finalize(JNIEnv* env, jobject object, jint statementId) 393 { 394 if (statementId > 0) { 395 sqlite3_finalize((sqlite3_stmt *)statementId); 396 } 397 } 398 399 static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) { 400 JNIEnv* env = AndroidRuntime::getJNIEnv(); 401 if (!env) { 402 LOGE("custom_function_callback cannot call into Java on this thread"); 403 return; 404 } 405 // get global ref to CustomFunction object from our user data 406 jobject function = (jobject)sqlite3_user_data(context); 407 408 // pack up the arguments into a string array 409 jobjectArray strArray = env->NewObjectArray(argc, string_class, NULL); 410 if (!strArray) 411 goto done; 412 for (int i = 0; i < argc; i++) { 413 char* arg = (char *)sqlite3_value_text(argv[i]); 414 if (!arg) { 415 LOGE("NULL argument in custom_function_callback. This should not happen."); 416 return; 417 } 418 jobject obj = env->NewStringUTF(arg); 419 if (!obj) 420 goto done; 421 env->SetObjectArrayElement(strArray, i, obj); 422 env->DeleteLocalRef(obj); 423 } 424 425 env->CallVoidMethod(function, method_custom_function_callback, strArray); 426 env->DeleteLocalRef(strArray); 427 428 done: 429 if (env->ExceptionCheck()) { 430 LOGE("An exception was thrown by custom sqlite3 function."); 431 LOGE_EX(env); 432 env->ExceptionClear(); 433 } 434 } 435 436 static jint native_addCustomFunction(JNIEnv* env, jobject object, 437 jstring name, jint numArgs, jobject function) 438 { 439 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle); 440 char const *nameStr = env->GetStringUTFChars(name, NULL); 441 jobject ref = env->NewGlobalRef(function); 442 LOGD_IF(DEBUG_JNI, "native_addCustomFunction %s ref: %p", nameStr, ref); 443 int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8, 444 (void *)ref, custom_function_callback, NULL, NULL); 445 env->ReleaseStringUTFChars(name, nameStr); 446 447 if (err == SQLITE_OK) 448 return (int)ref; 449 else { 450 LOGE("sqlite3_create_function returned %d", err); 451 env->DeleteGlobalRef(ref); 452 throw_sqlite3_exception(env, handle); 453 return 0; 454 } 455 } 456 457 static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref) 458 { 459 LOGD_IF(DEBUG_JNI, "native_releaseCustomFunction %d", ref); 460 env->DeleteGlobalRef((jobject)ref); 461 } 462 463 static JNINativeMethod sMethods[] = 464 { 465 /* name, signature, funcPtr */ 466 {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen}, 467 {"dbclose", "()V", (void *)dbclose}, 468 {"enableSqlTracing", "(Ljava/lang/String;S)V", (void *)enableSqlTracing}, 469 {"enableSqlProfiling", "(Ljava/lang/String;S)V", (void *)enableSqlProfiling}, 470 {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale}, 471 {"native_getDbLookaside", "()I", (void *)native_getDbLookaside}, 472 {"native_setSqliteSoftHeapLimit", "(I)V", (void *)native_setSqliteSoftHeapLimit}, 473 {"releaseMemory", "()I", (void *)native_releaseMemory}, 474 {"native_finalize", "(I)V", (void *)native_finalize}, 475 {"native_addCustomFunction", 476 "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I", 477 (void *)native_addCustomFunction}, 478 {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction}, 479 }; 480 481 int register_android_database_SQLiteDatabase(JNIEnv *env) 482 { 483 jclass clazz; 484 485 clazz = env->FindClass("android/database/sqlite/SQLiteDatabase"); 486 if (clazz == NULL) { 487 LOGE("Can't find android/database/sqlite/SQLiteDatabase\n"); 488 return -1; 489 } 490 491 string_class = (jclass)env->NewGlobalRef(env->FindClass("java/lang/String")); 492 if (string_class == NULL) { 493 LOGE("Can't find java/lang/String\n"); 494 return -1; 495 } 496 497 offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I"); 498 if (offset_db_handle == NULL) { 499 LOGE("Can't find SQLiteDatabase.mNativeHandle\n"); 500 return -1; 501 } 502 503 clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction"); 504 if (clazz == NULL) { 505 LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n"); 506 return -1; 507 } 508 method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V"); 509 if (method_custom_function_callback == NULL) { 510 LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n"); 511 return -1; 512 } 513 514 return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", 515 sMethods, NELEM(sMethods)); 516 } 517 518 /* throw a SQLiteException with a message appropriate for the error in handle */ 519 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { 520 throw_sqlite3_exception(env, handle, NULL); 521 } 522 523 /* throw a SQLiteException with the given message */ 524 void throw_sqlite3_exception(JNIEnv* env, const char* message) { 525 throw_sqlite3_exception(env, NULL, message); 526 } 527 528 /* throw a SQLiteException with a message appropriate for the error in handle 529 concatenated with the given message 530 */ 531 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) { 532 if (handle) { 533 throw_sqlite3_exception(env, sqlite3_errcode(handle), 534 sqlite3_errmsg(handle), message); 535 } else { 536 // we use SQLITE_OK so that a generic SQLiteException is thrown; 537 // any code not specified in the switch statement below would do. 538 throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message); 539 } 540 } 541 542 /* throw a SQLiteException for a given error code */ 543 void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) { 544 if (errcode == SQLITE_DONE) { 545 throw_sqlite3_exception(env, errcode, NULL, message); 546 } else { 547 char temp[21]; 548 sprintf(temp, "error code %d", errcode); 549 throw_sqlite3_exception(env, errcode, temp, message); 550 } 551 } 552 553 /* throw a SQLiteException for a given error code, sqlite3message, and 554 user message 555 */ 556 void throw_sqlite3_exception(JNIEnv* env, int errcode, 557 const char* sqlite3Message, const char* message) { 558 const char* exceptionClass; 559 switch (errcode) { 560 case SQLITE_IOERR: 561 exceptionClass = "android/database/sqlite/SQLiteDiskIOException"; 562 break; 563 case SQLITE_CORRUPT: 564 case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also 565 exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException"; 566 break; 567 case SQLITE_CONSTRAINT: 568 exceptionClass = "android/database/sqlite/SQLiteConstraintException"; 569 break; 570 case SQLITE_ABORT: 571 exceptionClass = "android/database/sqlite/SQLiteAbortException"; 572 break; 573 case SQLITE_DONE: 574 exceptionClass = "android/database/sqlite/SQLiteDoneException"; 575 break; 576 case SQLITE_FULL: 577 exceptionClass = "android/database/sqlite/SQLiteFullException"; 578 break; 579 case SQLITE_MISUSE: 580 exceptionClass = "android/database/sqlite/SQLiteMisuseException"; 581 break; 582 case SQLITE_PERM: 583 exceptionClass = "android/database/sqlite/SQLiteAccessPermException"; 584 break; 585 case SQLITE_BUSY: 586 exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException"; 587 break; 588 case SQLITE_LOCKED: 589 exceptionClass = "android/database/sqlite/SQLiteTableLockedException"; 590 break; 591 case SQLITE_READONLY: 592 exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException"; 593 break; 594 case SQLITE_CANTOPEN: 595 exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException"; 596 break; 597 case SQLITE_TOOBIG: 598 exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException"; 599 break; 600 case SQLITE_RANGE: 601 exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; 602 break; 603 case SQLITE_NOMEM: 604 exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException"; 605 break; 606 case SQLITE_MISMATCH: 607 exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException"; 608 break; 609 case SQLITE_UNCLOSED: 610 exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException"; 611 break; 612 default: 613 exceptionClass = "android/database/sqlite/SQLiteException"; 614 break; 615 } 616 617 if (sqlite3Message != NULL && message != NULL) { 618 char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3); 619 if (fullMessage != NULL) { 620 strcpy(fullMessage, sqlite3Message); 621 strcat(fullMessage, ": "); 622 strcat(fullMessage, message); 623 jniThrowException(env, exceptionClass, fullMessage); 624 free(fullMessage); 625 } else { 626 jniThrowException(env, exceptionClass, sqlite3Message); 627 } 628 } else if (sqlite3Message != NULL) { 629 jniThrowException(env, exceptionClass, sqlite3Message); 630 } else { 631 jniThrowException(env, exceptionClass, message); 632 } 633 } 634 635 636 } // namespace android 637