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