blob: cf4cbb843dcadac42194f8ac54080ba706a91bc1 [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049#define ANDROID_TABLE "android_metadata"
50/* uncomment the next line to force-enable logging of all statements */
51// #define DB_LOG_STATEMENTS
52
Vasu Nori77267fc2010-09-14 16:03:35 -070053#define DEBUG_JNI 0
54
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055namespace android {
56
57enum {
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
65static jfieldID offset_db_handle;
Mike Lockwood9d9c1be2010-07-13 19:27:52 -040066static jmethodID method_custom_function_callback;
Mike Lockwoodb0f72de2011-03-15 14:04:09 -040067static jclass string_class;
Vasu Nori34ad57f02010-12-21 09:32:36 -080068static jint sSqliteSoftHeapLimit = 0;
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
Vasu Nori060e25b2010-10-04 18:10:51 -070080 if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT) return;
Vasu Nori5be894e2010-11-02 21:55:30 -070081 // print databasename, errorcode and msg
Steve Block6215d3f2012-01-04 20:05:49 +000082 ALOGI("sqlite returned: error code = %d, msg = %s, db=%s\n", iErrCode, zMsg, databaseName);
Vasu Nori54dd0f22010-03-01 11:14:53 -080083}
84
85// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
Vasu Noribd29b7c2010-03-04 16:16:03 -080086static void registerLoggingFunc(const char *path) {
Vasu Nori54dd0f22010-03-01 11:14:53 -080087 static bool loggingFuncSet = false;
88 if (loggingFuncSet) {
89 return;
90 }
91
Steve Block71f2cf12011-10-20 11:56:00 +010092 ALOGV("Registering sqlite logging func \n");
Vasu Nori6c354da2010-04-26 23:33:39 -070093 int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path, 0));
Vasu Nori91d0e3c2010-03-02 14:12:19 -080094 if (err != SQLITE_OK) {
Vasu Norif7ebe8e2010-09-27 11:37:54 -070095 LOGW("sqlite returned error = %d when trying to register logging func.\n", err);
Vasu Nori91d0e3c2010-03-02 14:12:19 -080096 return;
97 }
Vasu Nori54dd0f22010-03-01 11:14:53 -080098 loggingFuncSet = true;
99}
100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101/* public native void dbopen(String path, int flags, String locale); */
102static 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
Vasu Nori54dd0f22010-03-01 11:14:53 -0800110 // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
Vasu Noribd29b7c2010-03-04 16:16:03 -0800111 registerLoggingFunc(path8);
Vasu Nori54dd0f22010-03-01 11:14:53 -0800112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 // 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.
Vasu Nori34ad57f02010-12-21 09:32:36 -0800132 sqlite3_soft_heap_limit(sSqliteSoftHeapLimit);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
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
Steve Block71f2cf12011-10-20 11:56:00 +0100173 ALOGV("Opened '%s' - %p\n", path8, handle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 env->SetIntField(object, offset_db_handle, (int) handle);
175 handle = NULL; // The caller owns the handle now.
176
177done:
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
Vasu Nori6c354da2010-04-26 23:33:39 -0700184static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName, short connNum) {
Vasu Nori54dd0f22010-03-01 11:14:53 -0800185 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 }
Vasu Nori6c354da2010-04-26 23:33:39 -0700190 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);
Vasu Nori54dd0f22010-03-01 11:14:53 -0800195 env->ReleaseStringUTFChars(databaseName, path);
196 return dbNameStr;
197}
198
199static void sqlTrace(void *databaseName, const char *sql) {
Steve Block6215d3f2012-01-04 20:05:49 +0000200 ALOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
Vasu Nori3ef94e22010-02-05 14:49:04 -0800201}
202
203/* public native void enableSqlTracing(); */
Vasu Nori6c354da2010-04-26 23:33:39 -0700204static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
Vasu Nori3ef94e22010-02-05 14:49:04 -0800205{
206 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
Vasu Nori6c354da2010-04-26 23:33:39 -0700207 sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName, connType));
Vasu Nori3ef94e22010-02-05 14:49:04 -0800208}
209
Vasu Nori54dd0f22010-03-01 11:14:53 -0800210static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
Vasu Nori3ef94e22010-02-05 14:49:04 -0800211 double d = tm/1000000.0;
Steve Block6215d3f2012-01-04 20:05:49 +0000212 ALOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
Vasu Nori3ef94e22010-02-05 14:49:04 -0800213}
214
215/* public native void enableSqlProfiling(); */
Vasu Nori6c354da2010-04-26 23:33:39 -0700216static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName, jshort connType)
Vasu Nori3ef94e22010-02-05 14:49:04 -0800217{
218 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
Vasu Nori6c354da2010-04-26 23:33:39 -0700219 sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName,
220 connType));
Vasu Nori3ef94e22010-02-05 14:49:04 -0800221}
222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223/* public native void close(); */
224static void dbclose(JNIEnv* env, jobject object)
225{
226 sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
227
228 if (handle != NULL) {
Vasu Nori3ef94e22010-02-05 14:49:04 -0800229 // 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 }
Steve Block71f2cf12011-10-20 11:56:00 +0100239 ALOGV("Closing database: handle=%p\n", handle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 int result = sqlite3_close(handle);
241 if (result == SQLITE_OK) {
Steve Block71f2cf12011-10-20 11:56:00 +0100242 ALOGV("Closed %p\n", handle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 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
Vasu Noric3849202010-03-09 10:47:25 -0800252/* native int native_getDbLookaside(); */
253static 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262/* set locale in the android_metadata table, install localized collators, and rebuild indexes */
263static 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
Daisuke Miyakawab9456392010-03-15 11:48:39 +0900295 dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296
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
Daisuke Miyakawab9456392010-03-15 11:48:39 +0900306 // For registering new index. Not for modifing the read-only database.
307 err = register_localized_collators(handle, locale8, UTF16_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 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
Daisuke Miyakawab9456392010-03-15 11:48:39 +0900320 err = register_localized_collators(handle, locale8, UTF16_STORAGE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 if (err != SQLITE_OK) {
322 LOGE("register_localized_collators() failed setting locale\n");
323 throw_sqlite3_exception(env, handle);
Vasu Nori78f307d2010-03-05 22:12:51 -0800324 goto rollback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 }
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
371rollback:
Vasu Nori78f307d2010-03-05 22:12:51 -0800372 if (err != SQLITE_OK) {
373 sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375
376done:
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
Vasu Nori34ad57f02010-12-21 09:32:36 -0800382static void native_setSqliteSoftHeapLimit(JNIEnv* env, jobject clazz, jint limit) {
383 sSqliteSoftHeapLimit = limit;
384}
385
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386static jint native_releaseMemory(JNIEnv *env, jobject clazz)
387{
388 // Attempt to release as much memory from the
Vasu Nori34ad57f02010-12-21 09:32:36 -0800389 return sqlite3_release_memory(sSqliteSoftHeapLimit);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390}
391
Vasu Nori6f37f832010-05-19 11:53:25 -0700392static void native_finalize(JNIEnv* env, jobject object, jint statementId)
393{
394 if (statementId > 0) {
395 sqlite3_finalize((sqlite3_stmt *)statementId);
396 }
397}
398
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400399static 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 }
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400405 // get global ref to CustomFunction object from our user data
406 jobject function = (jobject)sqlite3_user_data(context);
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400407
408 // pack up the arguments into a string array
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400409 jobjectArray strArray = env->NewObjectArray(argc, string_class, NULL);
410 if (!strArray)
411 goto done;
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400412 for (int i = 0; i < argc; i++) {
413 char* arg = (char *)sqlite3_value_text(argv[i]);
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400414 if (!arg) {
415 LOGE("NULL argument in custom_function_callback. This should not happen.");
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400416 return;
417 }
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400418 jobject obj = env->NewStringUTF(arg);
419 if (!obj)
420 goto done;
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400421 env->SetObjectArrayElement(strArray, i, obj);
422 env->DeleteLocalRef(obj);
423 }
424
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400425 env->CallVoidMethod(function, method_custom_function_callback, strArray);
Mike Lockwoodb0f72de2011-03-15 14:04:09 -0400426 env->DeleteLocalRef(strArray);
Mike Lockwoodc5bb8d82010-08-05 11:01:03 -0400427
428done:
429 if (env->ExceptionCheck()) {
430 LOGE("An exception was thrown by custom sqlite3 function.");
431 LOGE_EX(env);
432 env->ExceptionClear();
433 }
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400434}
435
436static 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);
Steve Block5baa3a62011-12-20 16:23:08 +0000442 ALOGD_IF(DEBUG_JNI, "native_addCustomFunction %s ref: %p", nameStr, ref);
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400443 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
457static void native_releaseCustomFunction(JNIEnv* env, jobject object, jint ref)
458{
Steve Block5baa3a62011-12-20 16:23:08 +0000459 ALOGD_IF(DEBUG_JNI, "native_releaseCustomFunction %d", ref);
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400460 env->DeleteGlobalRef((jobject)ref);
461}
462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463static JNINativeMethod sMethods[] =
464{
465 /* name, signature, funcPtr */
466 {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
467 {"dbclose", "()V", (void *)dbclose},
Vasu Nori6c354da2010-04-26 23:33:39 -0700468 {"enableSqlTracing", "(Ljava/lang/String;S)V", (void *)enableSqlTracing},
469 {"enableSqlProfiling", "(Ljava/lang/String;S)V", (void *)enableSqlProfiling},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
Vasu Noric3849202010-03-09 10:47:25 -0800471 {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
Vasu Nori34ad57f02010-12-21 09:32:36 -0800472 {"native_setSqliteSoftHeapLimit", "(I)V", (void *)native_setSqliteSoftHeapLimit},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 {"releaseMemory", "()I", (void *)native_releaseMemory},
Vasu Nori6f37f832010-05-19 11:53:25 -0700474 {"native_finalize", "(I)V", (void *)native_finalize},
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400475 {"native_addCustomFunction",
476 "(Ljava/lang/String;ILandroid/database/sqlite/SQLiteDatabase$CustomFunction;)I",
477 (void *)native_addCustomFunction},
478 {"native_releaseCustomFunction", "(I)V", (void *)native_releaseCustomFunction},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479};
480
481int 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
Mike Lockwoodb0f72de2011-03-15 14:04:09 -0400491 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 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
Mike Lockwood9d9c1be2010-07-13 19:27:52 -0400503 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
Vasu Nori6c354da2010-04-26 23:33:39 -0700514 return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteDatabase",
515 sMethods, NELEM(sMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516}
517
518/* throw a SQLiteException with a message appropriate for the error in handle */
519void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
520 throw_sqlite3_exception(env, handle, NULL);
521}
522
523/* throw a SQLiteException with the given message */
524void 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 */
531void 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 */
543void 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 {
Kenny Rootf80efdf2010-02-18 10:13:11 -0800547 char temp[21];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 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 */
556void 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:
Vasu Norib3395092010-06-10 17:47:25 -0700564 case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 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;
Vasu Noridea97132010-07-28 18:23:33 -0700582 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;
Vasu Nori422dad02010-09-03 16:03:08 -0700609 case SQLITE_UNCLOSED:
610 exceptionClass = "android/database/sqlite/SQLiteUnfinalizedObjectsException";
611 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 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