• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
createStr(const char * path,short extra)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 
sqlLogger(void * databaseName,int iErrCode,const char * zMsg)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.
registerLoggingFunc(const char * path)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); */
dbopen(JNIEnv * env,jobject object,jstring pathString,jint flags)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 
getDatabaseName(JNIEnv * env,sqlite3 * handle,jstring databaseName,short connNum)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 
sqlTrace(void * databaseName,const char * sql)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(); */
enableSqlTracing(JNIEnv * env,jobject object,jstring databaseName,jshort connType)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 
sqlProfile(void * databaseName,const char * sql,sqlite3_uint64 tm)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(); */
enableSqlProfiling(JNIEnv * env,jobject object,jstring databaseName,jshort connType)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(); */
dbclose(JNIEnv * env,jobject object)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(); */
native_getDbLookaside(JNIEnv * env,jobject object)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 */
native_setLocale(JNIEnv * env,jobject object,jstring localeString,jint flags)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 
native_setSqliteSoftHeapLimit(JNIEnv * env,jobject clazz,jint limit)382 static void native_setSqliteSoftHeapLimit(JNIEnv* env, jobject clazz, jint limit) {
383     sSqliteSoftHeapLimit = limit;
384 }
385 
native_releaseMemory(JNIEnv * env,jobject clazz)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 
native_finalize(JNIEnv * env,jobject object,jint statementId)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 
custom_function_callback(sqlite3_context * context,int argc,sqlite3_value ** argv)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 
native_addCustomFunction(JNIEnv * env,jobject object,jstring name,jint numArgs,jobject function)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 
native_releaseCustomFunction(JNIEnv * env,jobject object,jint ref)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 
register_android_database_SQLiteDatabase(JNIEnv * env)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 */
throw_sqlite3_exception(JNIEnv * env,sqlite3 * 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 */
throw_sqlite3_exception(JNIEnv * env,const char * 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  */
throw_sqlite3_exception(JNIEnv * env,sqlite3 * handle,const char * message)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 */
throw_sqlite3_exception_errcode(JNIEnv * env,int errcode,const char * message)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  */
throw_sqlite3_exception(JNIEnv * env,int errcode,const char * sqlite3Message,const char * message)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