blob: fca5f20dddceccc564868e3712c76d78e0ee5a48 [file] [log] [blame]
Jeff Browne5360fb2011-10-31 17:48:13 -07001/*
2 * Copyright (C) 2011 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#define LOG_TAG "SQLiteConnection"
18
19#include <jni.h>
20#include <JNIHelp.h>
21#include <android_runtime/AndroidRuntime.h>
22
23#include <utils/Log.h>
24#include <utils/String8.h>
25#include <utils/String16.h>
26#include <cutils/ashmem.h>
27#include <sys/mman.h>
28
29#include <string.h>
30#include <unistd.h>
31
Mathias Agopian49d2b182012-02-27 18:11:20 -080032#include <androidfw/CursorWindow.h>
Jeff Browne5360fb2011-10-31 17:48:13 -070033
34#include <sqlite3.h>
35#include <sqlite3_android.h>
36
37#include "android_database_SQLiteCommon.h"
38
Jeff Brown1d9f7422012-03-15 14:32:32 -070039// Set to 1 to use UTF16 storage for localized indexes.
Jeff Browne5360fb2011-10-31 17:48:13 -070040#define UTF16_STORAGE 0
Jeff Browne5360fb2011-10-31 17:48:13 -070041
42namespace android {
43
44static struct {
45 jfieldID name;
46 jfieldID numArgs;
47 jmethodID dispatchCallback;
48} gSQLiteCustomFunctionClassInfo;
49
50static struct {
51 jclass clazz;
52} gStringClassInfo;
53
54struct SQLiteConnection {
55 // Open flags.
56 // Must be kept in sync with the constants defined in SQLiteDatabase.java.
57 enum {
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
65 sqlite3* const db;
66 const int openFlags;
67 const String8 path;
68 const String8 label;
69
Jeff Brown75ea64f2012-01-25 19:37:13 -080070 volatile bool canceled;
71
Jeff Browne5360fb2011-10-31 17:48:13 -070072 SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
Jeff Brown75ea64f2012-01-25 19:37:13 -080073 db(db), openFlags(openFlags), path(path), label(label), canceled(false) { }
Jeff Browne5360fb2011-10-31 17:48:13 -070074};
75
76// Called each time a statement begins execution, when tracing is enabled.
77static void sqliteTraceCallback(void *data, const char *sql) {
78 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
79 ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n",
80 connection->label.string(), sql);
81}
82
83// Called each time a statement finishes execution, when profiling is enabled.
84static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
85 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
86 ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n",
87 connection->label.string(), sql, tm * 0.000001f);
88}
89
Jeff Brown75ea64f2012-01-25 19:37:13 -080090// Called after each SQLite VM instruction when cancelation is enabled.
91static int sqliteProgressHandlerCallback(void* data) {
92 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
93 return connection->canceled;
94}
95
Jeff Browne5360fb2011-10-31 17:48:13 -070096
97static jint nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
98 jstring labelStr, jboolean enableTrace, jboolean enableProfile) {
99 int sqliteFlags;
100 if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
101 sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
102 } else if (openFlags & SQLiteConnection::OPEN_READONLY) {
103 sqliteFlags = SQLITE_OPEN_READONLY;
104 } else {
105 sqliteFlags = SQLITE_OPEN_READWRITE;
106 }
107
108 const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
109 String8 path(pathChars);
110 env->ReleaseStringUTFChars(pathStr, pathChars);
111
112 const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
113 String8 label(labelChars);
114 env->ReleaseStringUTFChars(labelStr, labelChars);
115
116 sqlite3* db;
117 int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL);
118 if (err != SQLITE_OK) {
119 throw_sqlite3_exception_errcode(env, err, "Could not open database");
120 return 0;
121 }
122
123 // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
124 err = sqlite3_busy_timeout(db, 1000 /* ms */);
125 if (err != SQLITE_OK) {
126 throw_sqlite3_exception(env, db, "Could not set busy timeout");
127 sqlite3_close(db);
128 return 0;
129 }
130
Jeff Browne5360fb2011-10-31 17:48:13 -0700131 // Register custom Android functions.
132 err = register_android_functions(db, UTF16_STORAGE);
133 if (err) {
134 throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
135 sqlite3_close(db);
136 return 0;
137 }
138
139 // Create wrapper object.
140 SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
141
142 // Enable tracing and profiling if requested.
143 if (enableTrace) {
144 sqlite3_trace(db, &sqliteTraceCallback, connection);
145 }
146 if (enableProfile) {
147 sqlite3_profile(db, &sqliteProfileCallback, connection);
148 }
149
150 ALOGV("Opened connection %p with label '%s'", db, label.string());
151 return reinterpret_cast<jint>(connection);
152}
153
154static void nativeClose(JNIEnv* env, jclass clazz, jint connectionPtr) {
155 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
156
157 if (connection) {
158 ALOGV("Closing connection %p", connection->db);
159 int err = sqlite3_close(connection->db);
160 if (err != SQLITE_OK) {
161 // This can happen if sub-objects aren't closed first. Make sure the caller knows.
162 ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
163 throw_sqlite3_exception(env, connection->db, "Count not close db.");
164 return;
165 }
166
167 delete connection;
168 }
169}
170
171// Called each time a custom function is evaluated.
172static void sqliteCustomFunctionCallback(sqlite3_context *context,
173 int argc, sqlite3_value **argv) {
174 JNIEnv* env = AndroidRuntime::getJNIEnv();
175
176 // Get the callback function object.
177 // Create a new local reference to it in case the callback tries to do something
178 // dumb like unregister the function (thereby destroying the global ref) while it is running.
179 jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
180 jobject functionObj = env->NewLocalRef(functionObjGlobal);
181
182 jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL);
183 if (argsArray) {
184 for (int i = 0; i < argc; i++) {
185 const jchar* arg = static_cast<const jchar*>(sqlite3_value_text16(argv[i]));
186 if (!arg) {
187 ALOGW("NULL argument in custom_function_callback. This should not happen.");
188 } else {
189 size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar);
190 jstring argStr = env->NewString(arg, argLen);
191 if (!argStr) {
192 goto error; // out of memory error
193 }
194 env->SetObjectArrayElement(argsArray, i, argStr);
195 env->DeleteLocalRef(argStr);
196 }
197 }
198
199 // TODO: Support functions that return values.
200 env->CallVoidMethod(functionObj,
201 gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray);
202
203error:
204 env->DeleteLocalRef(argsArray);
205 }
206
207 env->DeleteLocalRef(functionObj);
208
209 if (env->ExceptionCheck()) {
210 ALOGE("An exception was thrown by custom SQLite function.");
211 LOGE_EX(env);
212 env->ExceptionClear();
213 }
214}
215
216// Called when a custom function is destroyed.
217static void sqliteCustomFunctionDestructor(void* data) {
218 jobject functionObjGlobal = reinterpret_cast<jobject>(data);
219
220 JNIEnv* env = AndroidRuntime::getJNIEnv();
221 env->DeleteGlobalRef(functionObjGlobal);
222}
223
224static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jint connectionPtr,
225 jobject functionObj) {
226 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
227
228 jstring nameStr = jstring(env->GetObjectField(
229 functionObj, gSQLiteCustomFunctionClassInfo.name));
230 jint numArgs = env->GetIntField(functionObj, gSQLiteCustomFunctionClassInfo.numArgs);
231
232 jobject functionObjGlobal = env->NewGlobalRef(functionObj);
233
234 const char* name = env->GetStringUTFChars(nameStr, NULL);
235 int err = sqlite3_create_function_v2(connection->db, name, numArgs, SQLITE_UTF16,
236 reinterpret_cast<void*>(functionObjGlobal),
237 &sqliteCustomFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor);
238 env->ReleaseStringUTFChars(nameStr, name);
239
240 if (err != SQLITE_OK) {
241 ALOGE("sqlite3_create_function returned %d", err);
242 env->DeleteGlobalRef(functionObjGlobal);
243 throw_sqlite3_exception(env, connection->db);
244 return;
245 }
246}
247
Jeff Brown1d9f7422012-03-15 14:32:32 -0700248static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jint connectionPtr,
249 jstring localeStr) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700250 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
251
Jeff Brown1d9f7422012-03-15 14:32:32 -0700252 const char* locale = env->GetStringUTFChars(localeStr, NULL);
253 int err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
254 env->ReleaseStringUTFChars(localeStr, locale);
Jeff Browne5360fb2011-10-31 17:48:13 -0700255
Jeff Browne5360fb2011-10-31 17:48:13 -0700256 if (err != SQLITE_OK) {
Jeff Browne5360fb2011-10-31 17:48:13 -0700257 throw_sqlite3_exception(env, connection->db);
Jeff Browne5360fb2011-10-31 17:48:13 -0700258 }
259}
260
261static jint nativePrepareStatement(JNIEnv* env, jclass clazz, jint connectionPtr,
262 jstring sqlString) {
263 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
264
265 jsize sqlLength = env->GetStringLength(sqlString);
266 const jchar* sql = env->GetStringCritical(sqlString, NULL);
267 sqlite3_stmt* statement;
268 int err = sqlite3_prepare16_v2(connection->db,
269 sql, sqlLength * sizeof(jchar), &statement, NULL);
270 env->ReleaseStringCritical(sqlString, sql);
271
272 if (err != SQLITE_OK) {
273 // Error messages like 'near ")": syntax error' are not
274 // always helpful enough, so construct an error string that
275 // includes the query itself.
276 const char *query = env->GetStringUTFChars(sqlString, NULL);
277 char *message = (char*) malloc(strlen(query) + 50);
278 if (message) {
279 strcpy(message, ", while compiling: "); // less than 50 chars
280 strcat(message, query);
281 }
282 env->ReleaseStringUTFChars(sqlString, query);
283 throw_sqlite3_exception(env, connection->db, message);
284 free(message);
285 return 0;
286 }
287
288 ALOGV("Prepared statement %p on connection %p", statement, connection->db);
289 return reinterpret_cast<jint>(statement);
290}
291
292static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jint connectionPtr,
293 jint statementPtr) {
294 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
295 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
296
Jeff Brown958cbba2012-02-29 15:52:30 -0800297 // We ignore the result of sqlite3_finalize because it is really telling us about
298 // whether any errors occurred while executing the statement. The statement itself
299 // is always finalized regardless.
Jeff Browne5360fb2011-10-31 17:48:13 -0700300 ALOGV("Finalized statement %p on connection %p", statement, connection->db);
Jeff Brown958cbba2012-02-29 15:52:30 -0800301 sqlite3_finalize(statement);
Jeff Browne5360fb2011-10-31 17:48:13 -0700302}
303
304static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jint connectionPtr,
305 jint statementPtr) {
306 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
307 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
308
309 return sqlite3_bind_parameter_count(statement);
310}
311
312static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jint connectionPtr,
313 jint statementPtr) {
314 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
315 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
316
317 return sqlite3_stmt_readonly(statement) != 0;
318}
319
320static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jint connectionPtr,
321 jint statementPtr) {
322 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
323 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
324
325 return sqlite3_column_count(statement);
326}
327
328static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jint connectionPtr,
329 jint statementPtr, jint index) {
330 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
331 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
332
333 const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(statement, index));
334 if (name) {
335 size_t length = 0;
336 while (name[length]) {
337 length += 1;
338 }
339 return env->NewString(name, length);
340 }
341 return NULL;
342}
343
344static void nativeBindNull(JNIEnv* env, jclass clazz, jint connectionPtr,
345 jint statementPtr, jint index) {
346 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
347 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
348
349 int err = sqlite3_bind_null(statement, index);
350 if (err != SQLITE_OK) {
351 throw_sqlite3_exception(env, connection->db, NULL);
352 }
353}
354
355static void nativeBindLong(JNIEnv* env, jclass clazz, jint connectionPtr,
356 jint statementPtr, jint index, jlong value) {
357 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
358 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
359
360 int err = sqlite3_bind_int64(statement, index, value);
361 if (err != SQLITE_OK) {
362 throw_sqlite3_exception(env, connection->db, NULL);
363 }
364}
365
366static void nativeBindDouble(JNIEnv* env, jclass clazz, jint connectionPtr,
367 jint statementPtr, jint index, jdouble value) {
368 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
369 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
370
371 int err = sqlite3_bind_double(statement, index, value);
372 if (err != SQLITE_OK) {
373 throw_sqlite3_exception(env, connection->db, NULL);
374 }
375}
376
377static void nativeBindString(JNIEnv* env, jclass clazz, jint connectionPtr,
378 jint statementPtr, jint index, jstring valueString) {
379 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
380 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
381
382 jsize valueLength = env->GetStringLength(valueString);
383 const jchar* value = env->GetStringCritical(valueString, NULL);
384 int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
385 SQLITE_TRANSIENT);
386 env->ReleaseStringCritical(valueString, value);
387 if (err != SQLITE_OK) {
388 throw_sqlite3_exception(env, connection->db, NULL);
389 }
390}
391
392static void nativeBindBlob(JNIEnv* env, jclass clazz, jint connectionPtr,
393 jint statementPtr, jint index, jbyteArray valueArray) {
394 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
395 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
396
397 jsize valueLength = env->GetArrayLength(valueArray);
398 jbyte* value = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(valueArray, NULL));
399 int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
400 env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
401 if (err != SQLITE_OK) {
402 throw_sqlite3_exception(env, connection->db, NULL);
403 }
404}
405
406static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jint connectionPtr,
407 jint statementPtr) {
408 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
409 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
410
411 int err = sqlite3_reset(statement);
412 if (err == SQLITE_OK) {
413 err = sqlite3_clear_bindings(statement);
414 }
415 if (err != SQLITE_OK) {
416 throw_sqlite3_exception(env, connection->db, NULL);
417 }
418}
419
420static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
421 int err = sqlite3_step(statement);
422 if (err == SQLITE_ROW) {
423 throw_sqlite3_exception(env,
424 "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
425 } else if (err != SQLITE_DONE) {
426 throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(connection->db));
427 }
428 return err;
429}
430
431static void nativeExecute(JNIEnv* env, jclass clazz, jint connectionPtr,
432 jint statementPtr) {
433 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
434 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
435
436 executeNonQuery(env, connection, statement);
437}
438
439static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
440 jint connectionPtr, jint statementPtr) {
441 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
442 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
443
444 int err = executeNonQuery(env, connection, statement);
445 return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
446}
447
448static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
449 jint connectionPtr, jint statementPtr) {
450 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
451 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
452
453 int err = executeNonQuery(env, connection, statement);
454 return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
455 ? sqlite3_last_insert_rowid(connection->db) : -1;
456}
457
458static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
459 int err = sqlite3_step(statement);
460 if (err != SQLITE_ROW) {
461 throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(connection->db));
462 }
463 return err;
464}
465
466static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
467 jint connectionPtr, jint statementPtr) {
468 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
469 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
470
471 int err = executeOneRowQuery(env, connection, statement);
472 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
473 return sqlite3_column_int64(statement, 0);
474 }
475 return -1;
476}
477
478static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
479 jint connectionPtr, jint statementPtr) {
480 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
481 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
482
483 int err = executeOneRowQuery(env, connection, statement);
484 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
485 const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(statement, 0));
486 if (text) {
487 size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
488 return env->NewString(text, length);
489 }
490 }
491 return NULL;
492}
493
494static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
495 int error = 0;
496 int fd = ashmem_create_region(NULL, length);
497 if (fd < 0) {
498 error = errno;
499 ALOGE("ashmem_create_region failed: %s", strerror(error));
500 } else {
501 if (length > 0) {
502 void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
503 if (ptr == MAP_FAILED) {
504 error = errno;
505 ALOGE("mmap failed: %s", strerror(error));
506 } else {
507 memcpy(ptr, data, length);
508 munmap(ptr, length);
509 }
510 }
511
512 if (!error) {
513 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
514 error = errno;
515 ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
516 } else {
517 return fd;
518 }
519 }
520
521 close(fd);
522 }
523
524 jniThrowIOException(env, error);
525 return -1;
526}
527
528static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
529 jint connectionPtr, jint statementPtr) {
530 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
531 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
532
533 int err = executeOneRowQuery(env, connection, statement);
534 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
535 const void* blob = sqlite3_column_blob(statement, 0);
536 if (blob) {
537 int length = sqlite3_column_bytes(statement, 0);
538 if (length >= 0) {
539 return createAshmemRegionWithData(env, blob, length);
540 }
541 }
542 }
543 return -1;
544}
545
546enum CopyRowResult {
547 CPR_OK,
548 CPR_FULL,
549 CPR_ERROR,
550};
551
552static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
553 sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
554 // Allocate a new field directory for the row.
555 status_t status = window->allocRow();
556 if (status) {
557 LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
558 startPos, addedRows, status);
559 return CPR_FULL;
560 }
561
562 // Pack the row into the window.
563 CopyRowResult result = CPR_OK;
564 for (int i = 0; i < numColumns; i++) {
565 int type = sqlite3_column_type(statement, i);
566 if (type == SQLITE_TEXT) {
567 // TEXT data
568 const char* text = reinterpret_cast<const char*>(
569 sqlite3_column_text(statement, i));
570 // SQLite does not include the NULL terminator in size, but does
571 // ensure all strings are NULL terminated, so increase size by
572 // one to make sure we store the terminator.
573 size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
574 status = window->putString(addedRows, i, text, sizeIncludingNull);
575 if (status) {
576 LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
577 sizeIncludingNull, startPos + addedRows, i, status);
578 result = CPR_FULL;
579 break;
580 }
581 LOG_WINDOW("%d,%d is TEXT with %u bytes",
582 startPos + addedRows, i, sizeIncludingNull);
583 } else if (type == SQLITE_INTEGER) {
584 // INTEGER data
585 int64_t value = sqlite3_column_int64(statement, i);
586 status = window->putLong(addedRows, i, value);
587 if (status) {
588 LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
589 i, status);
590 result = CPR_FULL;
591 break;
592 }
593 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
594 } else if (type == SQLITE_FLOAT) {
595 // FLOAT data
596 double value = sqlite3_column_double(statement, i);
597 status = window->putDouble(addedRows, i, value);
598 if (status) {
599 LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
600 i, status);
601 result = CPR_FULL;
602 break;
603 }
604 LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
605 } else if (type == SQLITE_BLOB) {
606 // BLOB data
607 const void* blob = sqlite3_column_blob(statement, i);
608 size_t size = sqlite3_column_bytes(statement, i);
609 status = window->putBlob(addedRows, i, blob, size);
610 if (status) {
611 LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
612 size, startPos + addedRows, i, status);
613 result = CPR_FULL;
614 break;
615 }
616 LOG_WINDOW("%d,%d is Blob with %u bytes",
617 startPos + addedRows, i, size);
618 } else if (type == SQLITE_NULL) {
619 // NULL field
620 status = window->putNull(addedRows, i);
621 if (status) {
622 LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
623 i, status);
624 result = CPR_FULL;
625 break;
626 }
627
628 LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
629 } else {
630 // Unknown data
631 ALOGE("Unknown column type when filling database window");
632 throw_sqlite3_exception(env, "Unknown column type when filling window");
633 result = CPR_ERROR;
634 break;
635 }
636 }
637
638 // Free the last row if if was not successfully copied.
639 if (result != CPR_OK) {
640 window->freeLastRow();
641 }
642 return result;
643}
644
645static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
646 jint connectionPtr, jint statementPtr, jint windowPtr,
647 jint startPos, jint requiredPos, jboolean countAllRows) {
648 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
649 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
650 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
651
652 status_t status = window->clear();
653 if (status) {
654 String8 msg;
655 msg.appendFormat("Failed to clear the cursor window, status=%d", status);
656 throw_sqlite3_exception(env, connection->db, msg.string());
657 return 0;
658 }
659
660 int numColumns = sqlite3_column_count(statement);
661 status = window->setNumColumns(numColumns);
662 if (status) {
663 String8 msg;
664 msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
665 numColumns, status);
666 throw_sqlite3_exception(env, connection->db, msg.string());
667 return 0;
668 }
669
670 int retryCount = 0;
671 int totalRows = 0;
672 int addedRows = 0;
673 bool windowFull = false;
674 bool gotException = false;
675 while (!gotException && (!windowFull || countAllRows)) {
676 int err = sqlite3_step(statement);
677 if (err == SQLITE_ROW) {
678 LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
679 retryCount = 0;
680 totalRows += 1;
681
682 // Skip the row if the window is full or we haven't reached the start position yet.
683 if (startPos >= totalRows || windowFull) {
684 continue;
685 }
686
687 CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
688 if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {
689 // We filled the window before we got to the one row that we really wanted.
690 // Clear the window and start filling it again from here.
691 // TODO: Would be nicer if we could progressively replace earlier rows.
692 window->clear();
693 window->setNumColumns(numColumns);
694 startPos += addedRows;
695 addedRows = 0;
696 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
697 }
698
699 if (cpr == CPR_OK) {
700 addedRows += 1;
701 } else if (cpr == CPR_FULL) {
702 windowFull = true;
703 } else {
704 gotException = true;
705 }
706 } else if (err == SQLITE_DONE) {
707 // All rows processed, bail
708 LOG_WINDOW("Processed all rows");
709 break;
710 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
711 // The table is locked, retry
712 LOG_WINDOW("Database locked, retrying");
713 if (retryCount > 50) {
714 ALOGE("Bailing on database busy retry");
715 throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
716 gotException = true;
717 } else {
718 // Sleep to give the thread holding the lock a chance to finish
719 usleep(1000);
720 retryCount++;
721 }
722 } else {
723 throw_sqlite3_exception(env, connection->db);
724 gotException = true;
725 }
726 }
727
728 LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
729 "to the window in %d bytes",
730 statement, totalRows, addedRows, window->size() - window->freeSpace());
731 sqlite3_reset(statement);
732
733 // Report the total number of rows on request.
734 if (startPos > totalRows) {
735 ALOGE("startPos %d > actual rows %d", startPos, totalRows);
736 }
737 jlong result = jlong(startPos) << 32 | jlong(totalRows);
738 return result;
739}
740
741static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jint connectionPtr) {
742 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
743
744 int cur = -1;
745 int unused;
746 sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
747 return cur;
748}
749
Jeff Brown75ea64f2012-01-25 19:37:13 -0800750static void nativeCancel(JNIEnv* env, jobject clazz, jint connectionPtr) {
751 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
752 connection->canceled = true;
753}
754
755static void nativeResetCancel(JNIEnv* env, jobject clazz, jint connectionPtr,
756 jboolean cancelable) {
757 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
758 connection->canceled = false;
759
760 if (cancelable) {
761 sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
762 connection);
763 } else {
764 sqlite3_progress_handler(connection->db, 0, NULL, NULL);
765 }
766}
767
Jeff Browne5360fb2011-10-31 17:48:13 -0700768
769static JNINativeMethod sMethods[] =
770{
771 /* name, signature, funcPtr */
772 { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
773 (void*)nativeOpen },
774 { "nativeClose", "(I)V",
775 (void*)nativeClose },
776 { "nativeRegisterCustomFunction", "(ILandroid/database/sqlite/SQLiteCustomFunction;)V",
777 (void*)nativeRegisterCustomFunction },
Jeff Brown1d9f7422012-03-15 14:32:32 -0700778 { "nativeRegisterLocalizedCollators", "(ILjava/lang/String;)V",
779 (void*)nativeRegisterLocalizedCollators },
Jeff Browne5360fb2011-10-31 17:48:13 -0700780 { "nativePrepareStatement", "(ILjava/lang/String;)I",
781 (void*)nativePrepareStatement },
782 { "nativeFinalizeStatement", "(II)V",
783 (void*)nativeFinalizeStatement },
784 { "nativeGetParameterCount", "(II)I",
785 (void*)nativeGetParameterCount },
786 { "nativeIsReadOnly", "(II)Z",
787 (void*)nativeIsReadOnly },
788 { "nativeGetColumnCount", "(II)I",
789 (void*)nativeGetColumnCount },
790 { "nativeGetColumnName", "(III)Ljava/lang/String;",
791 (void*)nativeGetColumnName },
792 { "nativeBindNull", "(III)V",
793 (void*)nativeBindNull },
794 { "nativeBindLong", "(IIIJ)V",
795 (void*)nativeBindLong },
796 { "nativeBindDouble", "(IIID)V",
797 (void*)nativeBindDouble },
798 { "nativeBindString", "(IIILjava/lang/String;)V",
799 (void*)nativeBindString },
800 { "nativeBindBlob", "(III[B)V",
801 (void*)nativeBindBlob },
802 { "nativeResetStatementAndClearBindings", "(II)V",
803 (void*)nativeResetStatementAndClearBindings },
804 { "nativeExecute", "(II)V",
805 (void*)nativeExecute },
806 { "nativeExecuteForLong", "(II)J",
807 (void*)nativeExecuteForLong },
808 { "nativeExecuteForString", "(II)Ljava/lang/String;",
809 (void*)nativeExecuteForString },
810 { "nativeExecuteForBlobFileDescriptor", "(II)I",
811 (void*)nativeExecuteForBlobFileDescriptor },
812 { "nativeExecuteForChangedRowCount", "(II)I",
813 (void*)nativeExecuteForChangedRowCount },
814 { "nativeExecuteForLastInsertedRowId", "(II)J",
815 (void*)nativeExecuteForLastInsertedRowId },
816 { "nativeExecuteForCursorWindow", "(IIIIIZ)J",
817 (void*)nativeExecuteForCursorWindow },
818 { "nativeGetDbLookaside", "(I)I",
819 (void*)nativeGetDbLookaside },
Jeff Brown75ea64f2012-01-25 19:37:13 -0800820 { "nativeCancel", "(I)V",
821 (void*)nativeCancel },
822 { "nativeResetCancel", "(IZ)V",
823 (void*)nativeResetCancel },
Jeff Browne5360fb2011-10-31 17:48:13 -0700824};
825
826#define FIND_CLASS(var, className) \
827 var = env->FindClass(className); \
828 LOG_FATAL_IF(! var, "Unable to find class " className);
829
830#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
831 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
832 LOG_FATAL_IF(! var, "Unable to find method" methodName);
833
834#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
835 var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
836 LOG_FATAL_IF(! var, "Unable to find field " fieldName);
837
838int register_android_database_SQLiteConnection(JNIEnv *env)
839{
840 jclass clazz;
841 FIND_CLASS(clazz, "android/database/sqlite/SQLiteCustomFunction");
842
843 GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.name, clazz,
844 "name", "Ljava/lang/String;");
845 GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.numArgs, clazz,
846 "numArgs", "I");
847 GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback,
848 clazz, "dispatchCallback", "([Ljava/lang/String;)V");
849
850 FIND_CLASS(clazz, "java/lang/String");
851 gStringClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
852
853 return AndroidRuntime::registerNativeMethods(env, "android/database/sqlite/SQLiteConnection",
854 sMethods, NELEM(sMethods));
855}
856
857} // namespace android