blob: 05e1ff3711ce77d76b9a9c70892f190b977fa205 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Vasu Norifb16cbd2010-07-25 16:38:48 -070018#define LOG_TAG "SqliteDatabaseCpp"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019
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>
Mathias Agopian3b4062e2009-05-31 19:13:00 -070031#include <utils/Log.h>
32#include <utils/threads.h>
33#include <utils/List.h>
34#include <utils/Errors.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035#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
Vasu Nori77267fc2010-09-14 16:03:35 -070054#define DEBUG_JNI 0
55
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056namespace android {
57
58enum {
59 OPEN_READWRITE = 0x00000000,
60 OPEN_READONLY = 0x00000001,
61 OPEN_READ_MASK = 0x00000001,
62 NO_LOCALIZED_COLLATORS = 0x00000010,
63 CREATE_IF_NECESSARY = 0x10000000
64};
65
66static jfieldID offset_db_handle;
Mike Lockwood9d9c1be2010-07-13 19:27:52 -040067static jmethodID method_custom_function_callback;
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -040068static jclass string_class = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
Vasu Nori6c354da2010-04-26 23:33:39 -070070static char *createStr(const char *path, short extra) {
71 int len = strlen(path) + extra;
Vasu Noribd29b7c2010-03-04 16:16:03 -080072 char *str = (char *)malloc(len + 1);
73 strncpy(str, path, len);
74 str[len] = NULL;
75 return str;
76}
77
Vasu Nori54dd0f22010-03-01 11:14:53 -080078static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
Vasu Noricc17ed22010-03-18 11:26:19 -070079 // skip printing this message if it is due to certain types of errors
80 if (iErrCode == SQLITE_CONSTRAINT) return;
81 LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
Vasu Nori54dd0f22010-03-01 11:14:53 -080082}
83
84// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
Vasu Noribd29b7c2010-03-04 16:16:03 -080085static void registerLoggingFunc(const char *path) {
Vasu Nori54dd0f22010-03-01 11:14:53 -080086 static bool loggingFuncSet = false;
87 if (loggingFuncSet) {
88 return;
89 }
90
91 LOGV("Registering sqlite logging func \n");
Vasu Nori6c354da2010-04-26 23:33:39 -070092 int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0));
Vasu Nori91d0e3c2010-03-02 14:12:19 -080093 if (err != SQLITE_OK) {
Vasu Norif7ebe8e2010-09-27 11:37:54 -070094 LOGW("sqlite returned error = %d when trying to register logging func.\n", err);
Vasu Nori91d0e3c2010-03-02 14:12:19 -080095 return;
96 }
Vasu Nori54dd0f22010-03-01 11:14:53 -080097 loggingFuncSet = true;
98}
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100/* public native void dbopen(String path, int flags, String locale); */
101static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags)
102{
103 int err;
104 sqlite3 * handle = NULL;
105 sqlite3_stmt * statement = NULL;
106 char const * path8 = env->GetStringUTFChars(pathString, NULL);
107 int sqliteFlags;
108
Vasu Nori54dd0f22010-03-01 11:14:53 -0800109 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
Vasu Noribd29b7c2010-03-04 16:16:03 -0800110 registerLoggingFunc(path8);
Vasu Nori54dd0f22010-03-01 11:14:53 -0800111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 // convert our flags into the sqlite flags
113 if (flags & CREATE_IF_NECESSARY) {
114 sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
115 } else if (flags & OPEN_READONLY) {
116 sqliteFlags = SQLITE_OPEN_READONLY;
117 } else {
118 sqliteFlags = SQLITE_OPEN_READWRITE;
119 }
120
121 err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
122 if (err != SQLITE_OK) {
123 LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
124 throw_sqlite3_exception(env, handle);
125 goto done;
126 }
127
128 // The soft heap limit prevents the page cache allocations from growing
129 // beyond the given limit, no matter what the max page cache sizes are
130 // set to. The limit does not, as of 3.5.0, affect any other allocations.
131 sqlite3_soft_heap_limit(SQLITE_SOFT_HEAP_LIMIT);
132
133 // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
134 err = sqlite3_busy_timeout(handle, 1000 /* ms */);
135 if (err != SQLITE_OK) {
136 LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8);
137 throw_sqlite3_exception(env, handle);
138 goto done;
139 }
140
141#ifdef DB_INTEGRITY_CHECK
142 static const char* integritySql = "pragma integrity_check(1);";
143 err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL);
144 if (err != SQLITE_OK) {
145 LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8);
146 throw_sqlite3_exception(env, handle);
147 goto done;
148 }
149
150 // first is OK or error message
151 err = sqlite3_step(statement);
152 if (err != SQLITE_ROW) {
153 LOGE("integrity check failed for \"%s\"\n", integritySql, path8);
154 throw_sqlite3_exception(env, handle);
155 goto done;
156 } else {
157 const char *text = (const char*)sqlite3_column_text(statement, 0);
158 if (strcmp(text, "ok") != 0) {
159 LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text);
160 jniThrowException(env, "android/database/sqlite/SQLiteDatabaseCorruptException", text);
161 goto done;
162 }
163 }
164#endif
165
166 err = register_android_functions(handle, UTF16_STORAGE);
167 if (err) {
168 throw_sqlite3_exception(env, handle);
169 goto done;
170 }
171
172 LOGV("Opened '%s' - %p\n", path8, handle);
173 env->SetIntField(object, offset_db_handle, (int) handle);
174 handle = NULL; // The caller owns the handle now.
175
176done:
177 // Release allocated resources
178 if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
179 if (statement != NULL) sqlite3_finalize(statement);
180 if (handle != NULL) sqlite3_close(handle);
181}
182
Vasu Nori6c354da2010-04-26 23:33:39 -0700183static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName, short connNum) {
Vasu Nori54dd0f22010-03-01 11:14:53 -0800184 char const *path = env->GetStringUTFChars(databaseName, NULL);
185 if (path == NULL) {
186 LOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
187 return NULL; // VM would have thrown OutOfMemoryError
188 }
Vasu Nori6c354da2010-04-26 23:33:39 -0700189 char *dbNameStr = createStr(path, 4);
190 if (connNum > 999) { // TODO: if number of pooled connections > 999, fix this line.
191 connNum = -1;
192 }
193 sprintf(dbNameStr + strlen(path), "|%03d", connNum);
Vasu Nori54dd0f22010-03-01 11:14:53 -0800194 env->ReleaseStringUTFChars(databaseName, path);
195 return dbNameStr;
196}
197
198static void sqlTrace(void *databaseName, const char *sql) {
Vasu Nori3ef94e22010-02-05 14:49:04 -0800199 LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
200}
201
202/* public native void enableSqlTracing(); */
Vasu Nori6c354da2010-04-26 23:33:39 -0700203static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
Vasu Nori3ef94e22010-02-05 14:49:04 -0800204{
205 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
Vasu Nori6c354da2010-04-26 23:33:39 -0700206 sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName, connType));
Vasu Nori3ef94e22010-02-05 14:49:04 -0800207}
208
Vasu Nori54dd0f22010-03-01 11:14:53 -0800209static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
Vasu Nori3ef94e22010-02-05 14:49:04 -0800210 double d = tm/1000000.0;
211 LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
212}
213
214/* public native void enableSqlProfiling(); */
Vasu Nori6c354da2010-04-26 23:33:39 -0700215static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
Vasu Nori3ef94e22010-02-05 14:49:04 -0800216{
217 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
Vasu Nori6c354da2010-04-26 23:33:39 -0700218 sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName,
219 connType));
Vasu Nori3ef94e22010-02-05 14:49:04 -0800220}
221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222/* public native void close(); */
223static void dbclose(JNIEnv* env, jobject object)
224{
225 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
226
227 if (handle != NULL) {
Vasu Nori3ef94e22010-02-05 14:49:04 -0800228 // release the memory associated with the traceFuncArg in enableSqlTracing function
229 void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL);
230 if (traceFuncArg != NULL) {
231 free(traceFuncArg);
232 }
233 // release the memory associated with the traceFuncArg in enableSqlProfiling function
234 traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL);
235 if (traceFuncArg != NULL) {
236 free(traceFuncArg);
237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 LOGV("Closing database: handle=%p\n", handle);
239 int result = sqlite3_close(handle);
240 if (result == SQLITE_OK) {
241 LOGV("Closed %p\n", handle);
242 env->SetIntField(object, offset_db_handle, 0);
243 } else {
244 // This can happen if sub-objects aren't closed first. Make sure the caller knows.
245 throw_sqlite3_exception(env, handle);
246 LOGE("sqlite3_close(%p) failed: %d\n", handle, result);
247 }
248 }
249}
250
Vasu Noric3849202010-03-09 10:47:25 -0800251/* native int native_getDbLookaside(); */
252static jint native_getDbLookaside(JNIEnv* env, jobject object)
253{
254 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
255 int pCur = -1;
256 int unused;
257 sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0);
258 return pCur;
259}
260
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261/* set locale in the android_metadata table, install localized collators, and rebuild indexes */
262static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
263{
264 if ((flags & NO_LOCALIZED_COLLATORS)) return;
265
266 int err;
267 char const* locale8 = env->GetStringUTFChars(localeString, NULL);
268 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
269 sqlite3_stmt* stmt = NULL;
270 char** meta = NULL;
271 int rowCount, colCount;
272 char* dbLocale = NULL;
273
274 // create the table, if necessary and possible
275 if (!(flags & OPEN_READONLY)) {
276 static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)";
277 err = sqlite3_exec(handle, createSql, NULL, NULL, NULL);
278 if (err != SQLITE_OK) {
279 LOGE("CREATE TABLE " ANDROID_TABLE " failed\n");
280 throw_sqlite3_exception(env, handle);
281 goto done;
282 }
283 }
284
285 // try to read from the table
286 static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1";
287 err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL);
288 if (err != SQLITE_OK) {
289 LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n");
290 throw_sqlite3_exception(env, handle);
291 goto done;
292 }
293
Daisuke Miyakawab9456392010-03-15 11:48:39 +0900294 dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295
296 if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
297 // database locale is the same as the desired locale; set up the collators and go
298 err = register_localized_collators(handle, locale8, UTF16_STORAGE);
299 if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
300 goto done; // no database changes needed
301 }
302
303 if ((flags & OPEN_READONLY)) {
304 // read-only database, so we're going to have to put up with whatever we got
Daisuke Miyakawab9456392010-03-15 11:48:39 +0900305 // For registering new index. Not for modifing the read-only database.
306 err = register_localized_collators(handle, locale8, UTF16_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
308 goto done;
309 }
310
311 // need to update android_metadata and indexes atomically, so use a transaction...
312 err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
313 if (err != SQLITE_OK) {
314 LOGE("BEGIN TRANSACTION failed setting locale\n");
315 throw_sqlite3_exception(env, handle);
316 goto done;
317 }
318
Daisuke Miyakawab9456392010-03-15 11:48:39 +0900319 err = register_localized_collators(handle, locale8, UTF16_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 if (err != SQLITE_OK) {
321 LOGE("register_localized_collators() failed setting locale\n");
322 throw_sqlite3_exception(env, handle);
Vasu Nori78f307d2010-03-05 22:12:51 -0800323 goto rollback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 }
325
326 err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
327 if (err != SQLITE_OK) {
328 LOGE("DELETE failed setting locale\n");
329 throw_sqlite3_exception(env, handle);
330 goto rollback;
331 }
332
333 static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
334 err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL);
335 if (err != SQLITE_OK) {
336 LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql);
337 throw_sqlite3_exception(env, handle);
338 goto rollback;
339 }
340
341 err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT);
342 if (err != SQLITE_OK) {
343 LOGE("sqlite3_bind_text() failed setting locale\n");
344 throw_sqlite3_exception(env, handle);
345 goto rollback;
346 }
347
348 err = sqlite3_step(stmt);
349 if (err != SQLITE_OK && err != SQLITE_DONE) {
350 LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql);
351 throw_sqlite3_exception(env, handle);
352 goto rollback;
353 }
354
355 err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL);
356 if (err != SQLITE_OK) {
357 LOGE("REINDEX LOCALIZED failed\n");
358 throw_sqlite3_exception(env, handle);
359 goto rollback;
360 }
361
362 // all done, yay!
363 err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL);
364 if (err != SQLITE_OK) {
365 LOGE("COMMIT TRANSACTION failed setting locale\n");
366 throw_sqlite3_exception(env, handle);
367 goto done;
368 }
369
370rollback:
Vasu Nori78f307d2010-03-05 22:12:51 -0800371 if (err != SQLITE_OK) {
372 sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
373 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374
375done:
376 if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
377 if (stmt != NULL) sqlite3_finalize(stmt);
378 if (meta != NULL) sqlite3_free_table(meta);
379}
380
381static jint native_releaseMemory(JNIEnv *env, jobject clazz)
382{
383 // Attempt to release as much memory from the
384 return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT);
385}
386
Vasu Nori6f37f832010-05-19 11:53:25 -0700387static void native_finalize(JNIEnv* env, jobject object, jint statementId)
388{
389 if (statementId > 0) {
390 sqlite3_finalize((sqlite3_stmt *)statementId);
391 }
392}
393
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400394static void custom_function_callback(sqlite3_context * context, int argc, sqlite3_value ** argv) {
395 JNIEnv* env = AndroidRuntime::getJNIEnv();
396 if (!env) {
397 LOGE("custom_function_callback cannot call into Java on this thread");
398 return;
399 }
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400400 // get global ref to CustomFunction object from our user data
401 jobject function = (jobject)sqlite3_user_data(context);
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400402
403 // pack up the arguments into a string array
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400404 if (!string_class)
405 string_class = (jclass)env->NewGlobalRef(env->FindClass("java/lang/String"));
406 jobjectArray strArray = env->NewObjectArray(argc, string_class, NULL);
407 if (!strArray)
408 goto done;
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400409 for (int i = 0; i < argc; i++) {
410 char* arg = (char *)sqlite3_value_text(argv[i]);
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400411 if (!arg) {
412 LOGE("NULL argument in custom_function_callback. This should not happen.");
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400413 return;
414 }
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400415 jobject obj = env->NewStringUTF(arg);
416 if (!obj)
417 goto done;
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400418 env->SetObjectArrayElement(strArray, i, obj);
419 env->DeleteLocalRef(obj);
420 }
421
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400422 env->CallVoidMethod(function, method_custom_function_callback, strArray);
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400423
424done:
425 if (env->ExceptionCheck()) {
426 LOGE("An exception was thrown by custom sqlite3 function.");
427 LOGE_EX(env);
428 env->ExceptionClear();
429 }
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400430}
431
432static jint native_addCustomFunction(JNIEnv* env, jobject object,
433 jstring name, jint numArgs, jobject function)
434{
435 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
436 char const *nameStr = env->GetStringUTFChars(name, NULL);
437 jobject ref = env->NewGlobalRef(function);
Vasu Nori77267fc2010-09-14 16:03:35 -0700438 LOGD_IF(DEBUG_JNI, "native_addCustomFunction %s ref: %p", nameStr, ref);
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400439 int err = sqlite3_create_function(handle, nameStr, numArgs, SQLITE_UTF8,
440 (void *)ref, custom_function_callback, NULL, NULL);
441 env->ReleaseStringUTFChars(name, nameStr);
442
443 if (err == SQLITE_OK)
444 return (int)ref;
445 else {
446 LOGE("sqlite3_create_function returned %d", err);
447 env->DeleteGlobalRef(ref);
448 throw_sqlite3_exception(env, handle);
449 return 0;
450 }
451}
452
453static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref)
454{
Vasu Nori77267fc2010-09-14 16:03:35 -0700455 LOGD_IF(DEBUG_JNI, "native_releaseCustomFunction %d", ref);
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400456 env->DeleteGlobalRef((jobject)ref);
457}
458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459static JNINativeMethod sMethods[] =
460{
461 /* name, signature, funcPtr */
462 {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
463 {"dbclose", "()V", (void *)dbclose},
Vasu Nori6c354da2010-04-26 23:33:39 -0700464 {"enableSqlTracing", "(Ljava/lang/String;S)V", (void *)enableSqlTracing},
465 {"enableSqlProfiling", "(Ljava/lang/String;S)V", (void *)enableSqlProfiling},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
Vasu Noric3849202010-03-09 10:47:25 -0800467 {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 {"releaseMemory", "()I", (void *)native_releaseMemory},
Vasu Nori6f37f832010-05-19 11:53:25 -0700469 {"native_finalize", "(I)V", (void *)native_finalize},
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400470 {"native_addCustomFunction",
471 "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I",
472 (void *)native_addCustomFunction},
473 {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474};
475
476int register_android_database_SQLiteDatabase(JNIEnv *env)
477{
478 jclass clazz;
479
480 clazz = env->FindClass("android/database/sqlite/SQLiteDatabase");
481 if (clazz == NULL) {
482 LOGE("Can't find android/database/sqlite/SQLiteDatabase\n");
483 return -1;
484 }
485
486 offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I");
487 if (offset_db_handle == NULL) {
488 LOGE("Can't find SQLiteDatabase.mNativeHandle\n");
489 return -1;
490 }
491
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400492 clazz = env->FindClass("android/database/sqlite/SQLiteDatabase$CustomFunction");
493 if (clazz == NULL) {
494 LOGE("Can't find android/database/sqlite/SQLiteDatabase$CustomFunction\n");
495 return -1;
496 }
497 method_custom_function_callback = env->GetMethodID(clazz, "callback", "([Ljava/lang/String;)V");
498 if (method_custom_function_callback == NULL) {
499 LOGE("Can't find method SQLiteDatabase.CustomFunction.callback\n");
500 return -1;
501 }
502
Vasu Nori6c354da2010-04-26 23:33:39 -0700503 return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase",
504 sMethods, NELEM(sMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505}
506
507/* throw a SQLiteException with a message appropriate for the error in handle */
508void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
509 throw_sqlite3_exception(env, handle, NULL);
510}
511
512/* throw a SQLiteException with the given message */
513void throw_sqlite3_exception(JNIEnv* env, const char* message) {
514 throw_sqlite3_exception(env, NULL, message);
515}
516
517/* throw a SQLiteException with a message appropriate for the error in handle
518 concatenated with the given message
519 */
520void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
521 if (handle) {
522 throw_sqlite3_exception(env, sqlite3_errcode(handle),
523 sqlite3_errmsg(handle), message);
524 } else {
525 // we use SQLITE_OK so that a generic SQLiteException is thrown;
526 // any code not specified in the switch statement below would do.
527 throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
528 }
529}
530
531/* throw a SQLiteException for a given error code */
532void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
533 if (errcode == SQLITE_DONE) {
534 throw_sqlite3_exception(env, errcode, NULL, message);
535 } else {
Kenny Rootf80efdf2010-02-18 10:13:11 -0800536 char temp[21];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 sprintf(temp, "error code %d", errcode);
538 throw_sqlite3_exception(env, errcode, temp, message);
539 }
540}
541
542/* throw a SQLiteException for a given error code, sqlite3message, and
543 user message
544 */
545void throw_sqlite3_exception(JNIEnv* env, int errcode,
546 const char* sqlite3Message, const char* message) {
547 const char* exceptionClass;
548 switch (errcode) {
549 case SQLITE_IOERR:
550 exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
551 break;
552 case SQLITE_CORRUPT:
Vasu Norib3395092010-06-10 17:47:25 -0700553 case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
555 break;
556 case SQLITE_CONSTRAINT:
557 exceptionClass = "android/database/sqlite/SQLiteConstraintException";
558 break;
559 case SQLITE_ABORT:
560 exceptionClass = "android/database/sqlite/SQLiteAbortException";
561 break;
562 case SQLITE_DONE:
563 exceptionClass = "android/database/sqlite/SQLiteDoneException";
564 break;
565 case SQLITE_FULL:
566 exceptionClass = "android/database/sqlite/SQLiteFullException";
567 break;
568 case SQLITE_MISUSE:
569 exceptionClass = "android/database/sqlite/SQLiteMisuseException";
570 break;
Vasu Noridea97132010-07-28 18:23:33 -0700571 case SQLITE_PERM:
572 exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
573 break;
574 case SQLITE_BUSY:
575 exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
576 break;
577 case SQLITE_LOCKED:
578 exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
579 break;
580 case SQLITE_READONLY:
581 exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
582 break;
583 case SQLITE_CANTOPEN:
584 exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
585 break;
586 case SQLITE_TOOBIG:
587 exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
588 break;
589 case SQLITE_RANGE:
590 exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
591 break;
592 case SQLITE_NOMEM:
593 exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
594 break;
595 case SQLITE_MISMATCH:
596 exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
597 break;
Vasu Nori422dad02010-09-03 16:03:08 -0700598 case SQLITE_UNCLOSED:
599 exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
600 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 default:
602 exceptionClass = "android/database/sqlite/SQLiteException";
603 break;
604 }
605
606 if (sqlite3Message != NULL && message != NULL) {
607 char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
608 if (fullMessage != NULL) {
609 strcpy(fullMessage, sqlite3Message);
610 strcat(fullMessage, ": ");
611 strcat(fullMessage, message);
612 jniThrowException(env, exceptionClass, fullMessage);
613 free(fullMessage);
614 } else {
615 jniThrowException(env, exceptionClass, sqlite3Message);
616 }
617 } else if (sqlite3Message != NULL) {
618 jniThrowException(env, exceptionClass, sqlite3Message);
619 } else {
620 jniThrowException(env, exceptionClass, message);
621 }
622}
623
624
625} // namespace android