• 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 "Database"
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 SQLITE_SOFT_HEAP_LIMIT (4 * 1024 * 1024)
50 #define ANDROID_TABLE "android_metadata"
51 /* uncomment the next line to force-enable logging of all statements */
52 // #define DB_LOG_STATEMENTS
53 
54 namespace android {
55 
56 enum {
57     OPEN_READWRITE          = 0x00000000,
58     OPEN_READONLY           = 0x00000001,
59     OPEN_READ_MASK          = 0x00000001,
60     NO_LOCALIZED_COLLATORS  = 0x00000010,
61     CREATE_IF_NECESSARY     = 0x10000000
62 };
63 
64 static jfieldID offset_db_handle;
65 
66 /* public native void dbopen(String path, int flags, String locale); */
dbopen(JNIEnv * env,jobject object,jstring pathString,jint flags)67 static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags)
68 {
69     int err;
70     sqlite3 * handle = NULL;
71     sqlite3_stmt * statement = NULL;
72     char const * path8 = env->GetStringUTFChars(pathString, NULL);
73     int sqliteFlags;
74 
75     // convert our flags into the sqlite flags
76     if (flags & CREATE_IF_NECESSARY) {
77         sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
78     } else if (flags & OPEN_READONLY) {
79         sqliteFlags = SQLITE_OPEN_READONLY;
80     } else {
81         sqliteFlags = SQLITE_OPEN_READWRITE;
82     }
83 
84     err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
85     if (err != SQLITE_OK) {
86         LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
87         throw_sqlite3_exception(env, handle);
88         goto done;
89     }
90 
91     // The soft heap limit prevents the page cache allocations from growing
92     // beyond the given limit, no matter what the max page cache sizes are
93     // set to. The limit does not, as of 3.5.0, affect any other allocations.
94     sqlite3_soft_heap_limit(SQLITE_SOFT_HEAP_LIMIT);
95 
96     // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
97     err = sqlite3_busy_timeout(handle, 1000 /* ms */);
98     if (err != SQLITE_OK) {
99         LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8);
100         throw_sqlite3_exception(env, handle);
101         goto done;
102     }
103 
104 #ifdef DB_INTEGRITY_CHECK
105     static const char* integritySql = "pragma integrity_check(1);";
106     err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL);
107     if (err != SQLITE_OK) {
108         LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8);
109         throw_sqlite3_exception(env, handle);
110         goto done;
111     }
112 
113     // first is OK or error message
114     err = sqlite3_step(statement);
115     if (err != SQLITE_ROW) {
116         LOGE("integrity check failed for \"%s\"\n", integritySql, path8);
117         throw_sqlite3_exception(env, handle);
118         goto done;
119     } else {
120         const char *text = (const char*)sqlite3_column_text(statement, 0);
121         if (strcmp(text, "ok") != 0) {
122             LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text);
123             jniThrowException(env, "android/database/sqlite/SQLiteDatabaseCorruptException", text);
124             goto done;
125         }
126     }
127 #endif
128 
129     err = register_android_functions(handle, UTF16_STORAGE);
130     if (err) {
131         throw_sqlite3_exception(env, handle);
132         goto done;
133     }
134 
135     LOGV("Opened '%s' - %p\n", path8, handle);
136     env->SetIntField(object, offset_db_handle, (int) handle);
137     handle = NULL;  // The caller owns the handle now.
138 
139 done:
140     // Release allocated resources
141     if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
142     if (statement != NULL) sqlite3_finalize(statement);
143     if (handle != NULL) sqlite3_close(handle);
144 }
145 
146 /* public native void close(); */
dbclose(JNIEnv * env,jobject object)147 static void dbclose(JNIEnv* env, jobject object)
148 {
149     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
150 
151     if (handle != NULL) {
152         LOGV("Closing database: handle=%p\n", handle);
153         int result = sqlite3_close(handle);
154         if (result == SQLITE_OK) {
155             LOGV("Closed %p\n", handle);
156             env->SetIntField(object, offset_db_handle, 0);
157         } else {
158             // This can happen if sub-objects aren't closed first.  Make sure the caller knows.
159             throw_sqlite3_exception(env, handle);
160             LOGE("sqlite3_close(%p) failed: %d\n", handle, result);
161         }
162     }
163 }
164 
165 /* public native void native_execSQL(String sql); */
native_execSQL(JNIEnv * env,jobject object,jstring sqlString)166 static void native_execSQL(JNIEnv* env, jobject object, jstring sqlString)
167 {
168     int err;
169     int stepErr;
170     sqlite3_stmt * statement = NULL;
171     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
172     jchar const * sql = env->GetStringChars(sqlString, NULL);
173     jsize sqlLen = env->GetStringLength(sqlString);
174 
175     if (sql == NULL || sqlLen == 0) {
176         jniThrowException(env, "java/lang/IllegalArgumentException", "You must supply an SQL string");
177         return;
178     }
179 
180     err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
181 
182     env->ReleaseStringChars(sqlString, sql);
183 
184     if (err != SQLITE_OK) {
185         char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
186         LOGE("Failure %d (%s) on %p when preparing '%s'.\n", err, sqlite3_errmsg(handle), handle, sql8);
187         throw_sqlite3_exception(env, handle, sql8);
188         env->ReleaseStringUTFChars(sqlString, sql8);
189         return;
190     }
191 
192     stepErr = sqlite3_step(statement);
193     err = sqlite3_finalize(statement);
194 
195     if (stepErr != SQLITE_DONE) {
196         if (stepErr == SQLITE_ROW) {
197             throw_sqlite3_exception(env, "Queries cannot be performed using execSQL(), use query() instead.");
198         } else {
199             char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
200             LOGE("Failure %d (%s) on %p when executing '%s'\n", err, sqlite3_errmsg(handle), handle, sql8);
201             throw_sqlite3_exception(env, handle, sql8);
202             env->ReleaseStringUTFChars(sqlString, sql8);
203 
204         }
205     } else
206 #ifndef DB_LOG_STATEMENTS
207     IF_LOGV()
208 #endif
209     {
210         char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
211         LOGV("Success on %p when executing '%s'\n", handle, sql8);
212         env->ReleaseStringUTFChars(sqlString, sql8);
213     }
214 }
215 
216 /* native long lastInsertRow(); */
lastInsertRow(JNIEnv * env,jobject object)217 static jlong lastInsertRow(JNIEnv* env, jobject object)
218 {
219     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
220 
221     return sqlite3_last_insert_rowid(handle);
222 }
223 
224 /* native int lastChangeCount(); */
lastChangeCount(JNIEnv * env,jobject object)225 static jint lastChangeCount(JNIEnv* env, jobject object)
226 {
227     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
228 
229     return sqlite3_changes(handle);
230 }
231 
232 /* set locale in the android_metadata table, install localized collators, and rebuild indexes */
native_setLocale(JNIEnv * env,jobject object,jstring localeString,jint flags)233 static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
234 {
235     if ((flags & NO_LOCALIZED_COLLATORS)) return;
236 
237     int err;
238     char const* locale8 = env->GetStringUTFChars(localeString, NULL);
239     sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
240     sqlite3_stmt* stmt = NULL;
241     char** meta = NULL;
242     int rowCount, colCount;
243     char* dbLocale = NULL;
244 
245     // create the table, if necessary and possible
246     if (!(flags & OPEN_READONLY)) {
247         static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)";
248         err = sqlite3_exec(handle, createSql, NULL, NULL, NULL);
249         if (err != SQLITE_OK) {
250             LOGE("CREATE TABLE " ANDROID_TABLE " failed\n");
251             throw_sqlite3_exception(env, handle);
252             goto done;
253         }
254     }
255 
256     // try to read from the table
257     static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1";
258     err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL);
259     if (err != SQLITE_OK) {
260         LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n");
261         throw_sqlite3_exception(env, handle);
262         goto done;
263     }
264 
265     dbLocale = (rowCount >= 1) ? meta[1 * colCount + 0] : NULL;
266 
267     if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
268         // database locale is the same as the desired locale; set up the collators and go
269         err = register_localized_collators(handle, locale8, UTF16_STORAGE);
270         if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
271         goto done;   // no database changes needed
272     }
273 
274     if ((flags & OPEN_READONLY)) {
275         // read-only database, so we're going to have to put up with whatever we got
276         err = register_localized_collators(handle, dbLocale ? dbLocale : locale8, UTF16_STORAGE);
277         if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
278         goto done;
279     }
280 
281     // need to update android_metadata and indexes atomically, so use a transaction...
282     err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
283     if (err != SQLITE_OK) {
284         LOGE("BEGIN TRANSACTION failed setting locale\n");
285         throw_sqlite3_exception(env, handle);
286         goto done;
287     }
288 
289     err = register_localized_collators(handle, dbLocale ? dbLocale : locale8, UTF16_STORAGE);
290     if (err != SQLITE_OK) {
291         LOGE("register_localized_collators() failed setting locale\n");
292         throw_sqlite3_exception(env, handle);
293         goto done;
294     }
295 
296     err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
297     if (err != SQLITE_OK) {
298         LOGE("DELETE failed setting locale\n");
299         throw_sqlite3_exception(env, handle);
300         goto rollback;
301     }
302 
303     static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
304     err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL);
305     if (err != SQLITE_OK) {
306         LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql);
307         throw_sqlite3_exception(env, handle);
308         goto rollback;
309     }
310 
311     err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT);
312     if (err != SQLITE_OK) {
313         LOGE("sqlite3_bind_text() failed setting locale\n");
314         throw_sqlite3_exception(env, handle);
315         goto rollback;
316     }
317 
318     err = sqlite3_step(stmt);
319     if (err != SQLITE_OK && err != SQLITE_DONE) {
320         LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql);
321         throw_sqlite3_exception(env, handle);
322         goto rollback;
323     }
324 
325     err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL);
326     if (err != SQLITE_OK) {
327         LOGE("REINDEX LOCALIZED failed\n");
328         throw_sqlite3_exception(env, handle);
329         goto rollback;
330     }
331 
332     // all done, yay!
333     err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL);
334     if (err != SQLITE_OK) {
335         LOGE("COMMIT TRANSACTION failed setting locale\n");
336         throw_sqlite3_exception(env, handle);
337         goto done;
338     }
339 
340 rollback:
341     sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
342 
343 done:
344     if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
345     if (stmt != NULL) sqlite3_finalize(stmt);
346     if (meta != NULL) sqlite3_free_table(meta);
347 }
348 
native_releaseMemory(JNIEnv * env,jobject clazz)349 static jint native_releaseMemory(JNIEnv *env, jobject clazz)
350 {
351     // Attempt to release as much memory from the
352     return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT);
353 }
354 
355 static JNINativeMethod sMethods[] =
356 {
357     /* name, signature, funcPtr */
358     {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
359     {"dbclose", "()V", (void *)dbclose},
360     {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL},
361     {"lastInsertRow", "()J", (void *)lastInsertRow},
362     {"lastChangeCount", "()I", (void *)lastChangeCount},
363     {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
364     {"releaseMemory", "()I", (void *)native_releaseMemory},
365 };
366 
register_android_database_SQLiteDatabase(JNIEnv * env)367 int register_android_database_SQLiteDatabase(JNIEnv *env)
368 {
369     jclass clazz;
370 
371     clazz = env->FindClass("android/database/sqlite/SQLiteDatabase");
372     if (clazz == NULL) {
373         LOGE("Can't find android/database/sqlite/SQLiteDatabase\n");
374         return -1;
375     }
376 
377     offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I");
378     if (offset_db_handle == NULL) {
379         LOGE("Can't find SQLiteDatabase.mNativeHandle\n");
380         return -1;
381     }
382 
383     return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase", sMethods, NELEM(sMethods));
384 }
385 
386 /* throw a SQLiteException with a message appropriate for the error in handle */
throw_sqlite3_exception(JNIEnv * env,sqlite3 * handle)387 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
388     throw_sqlite3_exception(env, handle, NULL);
389 }
390 
391 /* throw a SQLiteException with the given message */
throw_sqlite3_exception(JNIEnv * env,const char * message)392 void throw_sqlite3_exception(JNIEnv* env, const char* message) {
393     throw_sqlite3_exception(env, NULL, message);
394 }
395 
396 /* throw a SQLiteException with a message appropriate for the error in handle
397    concatenated with the given message
398  */
throw_sqlite3_exception(JNIEnv * env,sqlite3 * handle,const char * message)399 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
400     if (handle) {
401         throw_sqlite3_exception(env, sqlite3_errcode(handle),
402                                 sqlite3_errmsg(handle), message);
403     } else {
404         // we use SQLITE_OK so that a generic SQLiteException is thrown;
405         // any code not specified in the switch statement below would do.
406         throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
407     }
408 }
409 
410 /* throw a SQLiteException for a given error code */
throw_sqlite3_exception_errcode(JNIEnv * env,int errcode,const char * message)411 void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
412     if (errcode == SQLITE_DONE) {
413         throw_sqlite3_exception(env, errcode, NULL, message);
414     } else {
415         char temp[20];
416         sprintf(temp, "error code %d", errcode);
417         throw_sqlite3_exception(env, errcode, temp, message);
418     }
419 }
420 
421 /* throw a SQLiteException for a given error code, sqlite3message, and
422    user message
423  */
throw_sqlite3_exception(JNIEnv * env,int errcode,const char * sqlite3Message,const char * message)424 void throw_sqlite3_exception(JNIEnv* env, int errcode,
425                              const char* sqlite3Message, const char* message) {
426     const char* exceptionClass;
427     switch (errcode) {
428         case SQLITE_IOERR:
429             exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
430             break;
431         case SQLITE_CORRUPT:
432             exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
433             break;
434         case SQLITE_CONSTRAINT:
435            exceptionClass = "android/database/sqlite/SQLiteConstraintException";
436            break;
437         case SQLITE_ABORT:
438            exceptionClass = "android/database/sqlite/SQLiteAbortException";
439            break;
440         case SQLITE_DONE:
441            exceptionClass = "android/database/sqlite/SQLiteDoneException";
442            break;
443         case SQLITE_FULL:
444            exceptionClass = "android/database/sqlite/SQLiteFullException";
445            break;
446         case SQLITE_MISUSE:
447            exceptionClass = "android/database/sqlite/SQLiteMisuseException";
448            break;
449         default:
450            exceptionClass = "android/database/sqlite/SQLiteException";
451            break;
452     }
453 
454     if (sqlite3Message != NULL && message != NULL) {
455         char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
456         if (fullMessage != NULL) {
457             strcpy(fullMessage, sqlite3Message);
458             strcat(fullMessage, ": ");
459             strcat(fullMessage, message);
460             jniThrowException(env, exceptionClass, fullMessage);
461             free(fullMessage);
462         } else {
463             jniThrowException(env, exceptionClass, sqlite3Message);
464         }
465     } else if (sqlite3Message != NULL) {
466         jniThrowException(env, exceptionClass, sqlite3Message);
467     } else {
468         jniThrowException(env, exceptionClass, message);
469     }
470 }
471 
472 
473 } // namespace android
474