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