1 /*
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 <nativehelper/JNIHelp.h>
21 #include <android_runtime/AndroidRuntime.h>
22 #include <android_runtime/Log.h>
23
24 #include <utils/Log.h>
25 #include <utils/String8.h>
26 #include <utils/String16.h>
27 #include <cutils/ashmem.h>
28 #include <sys/mman.h>
29
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <androidfw/CursorWindow.h>
34
35 #include <sqlite3.h>
36 #include <sqlite3_android.h>
37
38 #include "android_database_SQLiteCommon.h"
39
40 #include "core_jni_helpers.h"
41
42 // Set to 1 to use UTF16 storage for localized indexes.
43 #define UTF16_STORAGE 0
44
45 namespace android {
46
47 /* Busy timeout in milliseconds.
48 * If another connection (possibly in another process) has the database locked for
49 * longer than this amount of time then SQLite will generate a SQLITE_BUSY error.
50 * The SQLITE_BUSY error is then raised as a SQLiteDatabaseLockedException.
51 *
52 * In ordinary usage, busy timeouts are quite rare. Most databases only ever
53 * have a single open connection at a time unless they are using WAL. When using
54 * WAL, a timeout could occur if one connection is busy performing an auto-checkpoint
55 * operation. The busy timeout needs to be long enough to tolerate slow I/O write
56 * operations but not so long as to cause the application to hang indefinitely if
57 * there is a problem acquiring a database lock.
58 */
59 static const int BUSY_TIMEOUT_MS = 2500;
60
61 static struct {
62 jmethodID apply;
63 } gUnaryOperator;
64
65 static struct {
66 jmethodID apply;
67 } gBinaryOperator;
68
69 struct SQLiteConnection {
70 // Open flags.
71 // Must be kept in sync with the constants defined in SQLiteDatabase.java.
72 enum {
73 OPEN_READWRITE = 0x00000000,
74 OPEN_READONLY = 0x00000001,
75 OPEN_READ_MASK = 0x00000001,
76 NO_LOCALIZED_COLLATORS = 0x00000010,
77 CREATE_IF_NECESSARY = 0x10000000,
78 };
79
80 sqlite3* const db;
81 const int openFlags;
82 const String8 path;
83 const String8 label;
84
85 // The prepared statement used to determine which tables are updated by a statement. This
86 // is is initially null. It is set non-null on first use.
87 sqlite3_stmt* tableQuery;
88
89 volatile bool canceled;
90
SQLiteConnectionandroid::SQLiteConnection91 SQLiteConnection(sqlite3* db, int openFlags, const String8& path, const String8& label) :
92 db(db), openFlags(openFlags), path(path), label(label), tableQuery(nullptr),
93 canceled(false) { }
94
95 };
96
97 // Called each time a statement begins execution, when tracing is enabled.
sqliteTraceCallback(void * data,const char * sql)98 static void sqliteTraceCallback(void *data, const char *sql) {
99 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
100 ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n", connection->label.c_str(), sql);
101 }
102
103 // Called each time a statement finishes execution, when profiling is enabled.
sqliteProfileCallback(void * data,const char * sql,sqlite3_uint64 tm)104 static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
105 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
106 ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n", connection->label.c_str(),
107 sql, tm * 0.000001f);
108 }
109
110 // Called after each SQLite VM instruction when cancelation is enabled.
sqliteProgressHandlerCallback(void * data)111 static int sqliteProgressHandlerCallback(void* data) {
112 SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
113 return connection->canceled;
114 }
115
116
nativeOpen(JNIEnv * env,jclass clazz,jstring pathStr,jint openFlags,jstring labelStr,jboolean enableTrace,jboolean enableProfile,jint lookasideSz,jint lookasideCnt)117 static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
118 jstring labelStr, jboolean enableTrace, jboolean enableProfile, jint lookasideSz,
119 jint lookasideCnt) {
120 int sqliteFlags;
121 if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
122 sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
123 } else if (openFlags & SQLiteConnection::OPEN_READONLY) {
124 sqliteFlags = SQLITE_OPEN_READONLY;
125 } else {
126 sqliteFlags = SQLITE_OPEN_READWRITE;
127 }
128
129 const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
130 String8 path(pathChars);
131 env->ReleaseStringUTFChars(pathStr, pathChars);
132
133 const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
134 String8 label(labelChars);
135 env->ReleaseStringUTFChars(labelStr, labelChars);
136
137 sqlite3* db;
138 int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL);
139 if (err != SQLITE_OK) {
140 throw_sqlite3_exception_errcode(env, err, "Could not open database");
141 return 0;
142 }
143
144 if (lookasideSz >= 0 && lookasideCnt >= 0) {
145 int err = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, NULL, lookasideSz, lookasideCnt);
146 if (err != SQLITE_OK) {
147 ALOGE("sqlite3_db_config(..., %d, %d) failed: %d", lookasideSz, lookasideCnt, err);
148 throw_sqlite3_exception(env, db, "Cannot set lookaside");
149 sqlite3_close(db);
150 return 0;
151 }
152 }
153
154 // Check that the database is really read/write when that is what we asked for.
155 if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
156 throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
157 sqlite3_close(db);
158 return 0;
159 }
160
161 // Set the default busy handler to retry automatically before returning SQLITE_BUSY.
162 err = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
163 if (err != SQLITE_OK) {
164 throw_sqlite3_exception(env, db, "Could not set busy timeout");
165 sqlite3_close(db);
166 return 0;
167 }
168
169 // Register custom Android functions.
170 err = register_android_functions(db, UTF16_STORAGE);
171 if (err) {
172 throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
173 sqlite3_close(db);
174 return 0;
175 }
176
177 // Create wrapper object.
178 SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
179
180 // Enable tracing and profiling if requested.
181 if (enableTrace) {
182 sqlite3_trace(db, &sqliteTraceCallback, connection);
183 }
184 if (enableProfile) {
185 sqlite3_profile(db, &sqliteProfileCallback, connection);
186 }
187
188 ALOGV("Opened connection %p with label '%s'", db, label.c_str());
189 return reinterpret_cast<jlong>(connection);
190 }
191
nativeClose(JNIEnv * env,jclass clazz,jlong connectionPtr)192 static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) {
193 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
194
195 if (connection) {
196 ALOGV("Closing connection %p", connection->db);
197 if (connection->tableQuery != nullptr) {
198 sqlite3_finalize(connection->tableQuery);
199 }
200 int err = sqlite3_close(connection->db);
201 if (err != SQLITE_OK) {
202 // This can happen if sub-objects aren't closed first. Make sure the caller knows.
203 ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
204 throw_sqlite3_exception(env, connection->db, "Count not close db.");
205 return;
206 }
207
208 delete connection;
209 }
210 }
211
sqliteCustomScalarFunctionCallback(sqlite3_context * context,int argc,sqlite3_value ** argv)212 static void sqliteCustomScalarFunctionCallback(sqlite3_context *context,
213 int argc, sqlite3_value **argv) {
214 JNIEnv* env = AndroidRuntime::getJNIEnv();
215 jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
216 ScopedLocalRef<jobject> functionObj(env, env->NewLocalRef(functionObjGlobal));
217 ScopedLocalRef<jstring> argString(env,
218 env->NewStringUTF(reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))));
219 ScopedLocalRef<jstring> resString(env,
220 (jstring) env->CallObjectMethod(functionObj.get(), gUnaryOperator.apply, argString.get()));
221
222 if (env->ExceptionCheck()) {
223 ALOGE("Exception thrown by custom scalar function");
224 sqlite3_result_error(context, "Exception thrown by custom scalar function", -1);
225 env->ExceptionDescribe();
226 env->ExceptionClear();
227 return;
228 }
229
230 if (resString.get() == nullptr) {
231 sqlite3_result_null(context);
232 } else {
233 ScopedUtfChars res(env, resString.get());
234 sqlite3_result_text(context, res.c_str(), -1, SQLITE_TRANSIENT);
235 }
236 }
237
sqliteCustomScalarFunctionDestructor(void * data)238 static void sqliteCustomScalarFunctionDestructor(void* data) {
239 jobject functionObjGlobal = reinterpret_cast<jobject>(data);
240
241 JNIEnv* env = AndroidRuntime::getJNIEnv();
242 env->DeleteGlobalRef(functionObjGlobal);
243 }
244
nativeRegisterCustomScalarFunction(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring functionName,jobject functionObj)245 static void nativeRegisterCustomScalarFunction(JNIEnv* env, jclass clazz, jlong connectionPtr,
246 jstring functionName, jobject functionObj) {
247 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
248
249 jobject functionObjGlobal = env->NewGlobalRef(functionObj);
250 ScopedUtfChars functionNameChars(env, functionName);
251 int err = sqlite3_create_function_v2(connection->db,
252 functionNameChars.c_str(), 1, SQLITE_UTF8,
253 reinterpret_cast<void*>(functionObjGlobal),
254 &sqliteCustomScalarFunctionCallback,
255 nullptr,
256 nullptr,
257 &sqliteCustomScalarFunctionDestructor);
258
259 if (err != SQLITE_OK) {
260 ALOGE("sqlite3_create_function returned %d", err);
261 env->DeleteGlobalRef(functionObjGlobal);
262 throw_sqlite3_exception(env, connection->db);
263 return;
264 }
265 }
266
sqliteCustomAggregateFunctionStep(sqlite3_context * context,int argc,sqlite3_value ** argv)267 static void sqliteCustomAggregateFunctionStep(sqlite3_context *context,
268 int argc, sqlite3_value **argv) {
269 char** agg = reinterpret_cast<char**>(
270 sqlite3_aggregate_context(context, sizeof(const char**)));
271 if (agg == nullptr) {
272 return;
273 } else if (*agg == nullptr) {
274 // During our first call the best we can do is allocate our result
275 // holder and populate it with our first value; we'll reduce it
276 // against any additional values in future calls
277 const char* res = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
278 if (res == nullptr) {
279 *agg = nullptr;
280 } else {
281 *agg = strdup(res);
282 }
283 return;
284 }
285
286 JNIEnv* env = AndroidRuntime::getJNIEnv();
287 jobject functionObjGlobal = reinterpret_cast<jobject>(sqlite3_user_data(context));
288 ScopedLocalRef<jobject> functionObj(env, env->NewLocalRef(functionObjGlobal));
289 ScopedLocalRef<jstring> arg0String(env,
290 env->NewStringUTF(reinterpret_cast<const char*>(*agg)));
291 ScopedLocalRef<jstring> arg1String(env,
292 env->NewStringUTF(reinterpret_cast<const char*>(sqlite3_value_text(argv[0]))));
293 ScopedLocalRef<jstring> resString(env,
294 (jstring) env->CallObjectMethod(functionObj.get(), gBinaryOperator.apply,
295 arg0String.get(), arg1String.get()));
296
297 if (env->ExceptionCheck()) {
298 ALOGE("Exception thrown by custom aggregate function");
299 sqlite3_result_error(context, "Exception thrown by custom aggregate function", -1);
300 env->ExceptionDescribe();
301 env->ExceptionClear();
302 return;
303 }
304
305 // One way or another, we have a new value to collect, and we need to
306 // free our previous value
307 if (*agg != nullptr) {
308 free(*agg);
309 }
310 if (resString.get() == nullptr) {
311 *agg = nullptr;
312 } else {
313 ScopedUtfChars res(env, resString.get());
314 *agg = strdup(res.c_str());
315 }
316 }
317
sqliteCustomAggregateFunctionFinal(sqlite3_context * context)318 static void sqliteCustomAggregateFunctionFinal(sqlite3_context *context) {
319 // We pass zero size here to avoid allocating for empty sets
320 char** agg = reinterpret_cast<char**>(
321 sqlite3_aggregate_context(context, 0));
322 if (agg == nullptr) {
323 return;
324 } else if (*agg == nullptr) {
325 sqlite3_result_null(context);
326 } else {
327 sqlite3_result_text(context, *agg, -1, SQLITE_TRANSIENT);
328 free(*agg);
329 }
330 }
331
sqliteCustomAggregateFunctionDestructor(void * data)332 static void sqliteCustomAggregateFunctionDestructor(void* data) {
333 jobject functionObjGlobal = reinterpret_cast<jobject>(data);
334
335 JNIEnv* env = AndroidRuntime::getJNIEnv();
336 env->DeleteGlobalRef(functionObjGlobal);
337 }
338
nativeRegisterCustomAggregateFunction(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring functionName,jobject functionObj)339 static void nativeRegisterCustomAggregateFunction(JNIEnv* env, jclass clazz, jlong connectionPtr,
340 jstring functionName, jobject functionObj) {
341 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
342
343 jobject functionObjGlobal = env->NewGlobalRef(functionObj);
344 ScopedUtfChars functionNameChars(env, functionName);
345 int err = sqlite3_create_function_v2(connection->db,
346 functionNameChars.c_str(), 1, SQLITE_UTF8,
347 reinterpret_cast<void*>(functionObjGlobal),
348 nullptr,
349 &sqliteCustomAggregateFunctionStep,
350 &sqliteCustomAggregateFunctionFinal,
351 &sqliteCustomAggregateFunctionDestructor);
352
353 if (err != SQLITE_OK) {
354 ALOGE("sqlite3_create_function returned %d", err);
355 env->DeleteGlobalRef(functionObjGlobal);
356 throw_sqlite3_exception(env, connection->db);
357 return;
358 }
359 }
360
nativeRegisterLocalizedCollators(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring localeStr)361 static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jlong connectionPtr,
362 jstring localeStr) {
363 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
364
365 const char* locale = env->GetStringUTFChars(localeStr, NULL);
366 int err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
367 env->ReleaseStringUTFChars(localeStr, locale);
368
369 if (err != SQLITE_OK) {
370 throw_sqlite3_exception(env, connection->db);
371 }
372 }
373
nativePrepareStatement(JNIEnv * env,jclass clazz,jlong connectionPtr,jstring sqlString)374 static jlong nativePrepareStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
375 jstring sqlString) {
376 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
377
378 jsize sqlLength = env->GetStringLength(sqlString);
379 const jchar* sql = env->GetStringCritical(sqlString, NULL);
380 sqlite3_stmt* statement;
381 int err = sqlite3_prepare16_v2(connection->db,
382 sql, sqlLength * sizeof(jchar), &statement, NULL);
383 env->ReleaseStringCritical(sqlString, sql);
384
385 if (err != SQLITE_OK) {
386 // Error messages like 'near ")": syntax error' are not
387 // always helpful enough, so construct an error string that
388 // includes the query itself.
389 const char *query = env->GetStringUTFChars(sqlString, NULL);
390 char *message = (char*) malloc(strlen(query) + 50);
391 if (message) {
392 strcpy(message, ", while compiling: "); // less than 50 chars
393 strcat(message, query);
394 }
395 env->ReleaseStringUTFChars(sqlString, query);
396 throw_sqlite3_exception(env, connection->db, message);
397 free(message);
398 return 0;
399 }
400
401 ALOGV("Prepared statement %p on connection %p", statement, connection->db);
402 return reinterpret_cast<jlong>(statement);
403 }
404
nativeFinalizeStatement(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)405 static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
406 jlong statementPtr) {
407 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
408 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
409
410 // We ignore the result of sqlite3_finalize because it is really telling us about
411 // whether any errors occurred while executing the statement. The statement itself
412 // is always finalized regardless.
413 ALOGV("Finalized statement %p on connection %p", statement, connection->db);
414 sqlite3_finalize(statement);
415 }
416
nativeGetParameterCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)417 static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
418 jlong statementPtr) {
419 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
420
421 return sqlite3_bind_parameter_count(statement);
422 }
423
nativeIsReadOnly(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)424 static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jlong connectionPtr,
425 jlong statementPtr) {
426 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
427
428 return sqlite3_stmt_readonly(statement) != 0;
429 }
430
nativeUpdatesTempOnly(JNIEnv * env,jclass,jlong connectionPtr,jlong statementPtr)431 static jboolean nativeUpdatesTempOnly(JNIEnv* env, jclass,
432 jlong connectionPtr, jlong statementPtr) {
433 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
434 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
435
436 int result = SQLITE_OK;
437 if (connection->tableQuery == nullptr) {
438 static char const* sql =
439 "SELECT NULL FROM tables_used(?) WHERE schema != 'temp' AND wr != 0";
440 result = sqlite3_prepare_v2(connection->db, sql, -1, &connection->tableQuery, nullptr);
441 if (result != SQLITE_OK) {
442 ALOGE("failed to compile query table: %s",
443 sqlite3_errstr(sqlite3_extended_errcode(connection->db)));
444 return false;
445 }
446 }
447
448 // A temporary, to simplify the code.
449 sqlite3_stmt* query = connection->tableQuery;
450 result = sqlite3_bind_text(query, 1, sqlite3_sql(statement), -1, SQLITE_STATIC);
451 if (result != SQLITE_OK) {
452 ALOGE("tables bind pointer returns %s", sqlite3_errstr(result));
453 }
454 result = sqlite3_step(query);
455 // Make sure the query is no longer bound to the statement SQL string and
456 // that is no longer holding any table locks.
457 sqlite3_reset(query);
458 sqlite3_clear_bindings(query);
459
460 if (result != SQLITE_ROW && result != SQLITE_DONE) {
461 ALOGE("tables query error: %d/%s", result, sqlite3_errstr(result));
462 }
463 return result == SQLITE_DONE;
464 }
465
nativeGetColumnCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)466 static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
467 jlong statementPtr) {
468 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
469
470 return sqlite3_column_count(statement);
471 }
472
nativeGetColumnName(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index)473 static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jlong connectionPtr,
474 jlong statementPtr, jint index) {
475 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
476
477 const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(statement, index));
478 if (name) {
479 size_t length = 0;
480 while (name[length]) {
481 length += 1;
482 }
483 return env->NewString(name, length);
484 }
485 return NULL;
486 }
487
nativeBindNull(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index)488 static void nativeBindNull(JNIEnv* env, jclass clazz, jlong connectionPtr,
489 jlong statementPtr, jint index) {
490 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
491 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
492
493 int err = sqlite3_bind_null(statement, index);
494 if (err != SQLITE_OK) {
495 throw_sqlite3_exception(env, connection->db, NULL);
496 }
497 }
498
nativeBindLong(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jlong value)499 static void nativeBindLong(JNIEnv* env, jclass clazz, jlong connectionPtr,
500 jlong statementPtr, jint index, jlong value) {
501 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
502 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
503
504 int err = sqlite3_bind_int64(statement, index, value);
505 if (err != SQLITE_OK) {
506 throw_sqlite3_exception(env, connection->db, NULL);
507 }
508 }
509
nativeBindDouble(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jdouble value)510 static void nativeBindDouble(JNIEnv* env, jclass clazz, jlong connectionPtr,
511 jlong statementPtr, jint index, jdouble value) {
512 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
513 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
514
515 int err = sqlite3_bind_double(statement, index, value);
516 if (err != SQLITE_OK) {
517 throw_sqlite3_exception(env, connection->db, NULL);
518 }
519 }
520
nativeBindString(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jstring valueString)521 static void nativeBindString(JNIEnv* env, jclass clazz, jlong connectionPtr,
522 jlong statementPtr, jint index, jstring valueString) {
523 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
524 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
525
526 jsize valueLength = env->GetStringLength(valueString);
527 const jchar* value = env->GetStringCritical(valueString, NULL);
528 int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
529 SQLITE_TRANSIENT);
530 env->ReleaseStringCritical(valueString, value);
531 if (err != SQLITE_OK) {
532 throw_sqlite3_exception(env, connection->db, NULL);
533 }
534 }
535
nativeBindBlob(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jint index,jbyteArray valueArray)536 static void nativeBindBlob(JNIEnv* env, jclass clazz, jlong connectionPtr,
537 jlong statementPtr, jint index, jbyteArray valueArray) {
538 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
539 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
540
541 jsize valueLength = env->GetArrayLength(valueArray);
542 jbyte* value = static_cast<jbyte*>(env->GetPrimitiveArrayCritical(valueArray, NULL));
543 int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
544 env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
545 if (err != SQLITE_OK) {
546 throw_sqlite3_exception(env, connection->db, NULL);
547 }
548 }
549
nativeResetStatementAndClearBindings(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)550 static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jlong connectionPtr,
551 jlong statementPtr) {
552 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
553 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
554
555 int err = sqlite3_reset(statement);
556 if (err == SQLITE_OK) {
557 err = sqlite3_clear_bindings(statement);
558 }
559 if (err != SQLITE_OK) {
560 throw_sqlite3_exception(env, connection->db, NULL);
561 }
562 }
563
executeNonQuery(JNIEnv * env,SQLiteConnection * connection,sqlite3_stmt * statement,bool isPragmaStmt)564 static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement,
565 bool isPragmaStmt) {
566 int rc = sqlite3_step(statement);
567 if (isPragmaStmt) {
568 while (rc == SQLITE_ROW) {
569 rc = sqlite3_step(statement);
570 }
571 }
572 if (rc == SQLITE_ROW) {
573 throw_sqlite3_exception(env,
574 "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
575 } else if (rc != SQLITE_DONE) {
576 throw_sqlite3_exception(env, connection->db);
577 }
578 return rc;
579 }
580
nativeExecute(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jboolean isPragmaStmt)581 static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr,
582 jboolean isPragmaStmt) {
583 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
584 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
585
586 executeNonQuery(env, connection, statement, isPragmaStmt);
587 }
588
nativeExecuteForChangedRowCount(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)589 static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
590 jlong connectionPtr, jlong statementPtr) {
591 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
592 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
593
594 int err = executeNonQuery(env, connection, statement, false);
595 return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
596 }
597
nativeExecuteForLastInsertedRowId(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)598 static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
599 jlong connectionPtr, jlong statementPtr) {
600 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
601 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
602
603 int err = executeNonQuery(env, connection, statement, false);
604 return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
605 ? sqlite3_last_insert_rowid(connection->db) : -1;
606 }
607
executeOneRowQuery(JNIEnv * env,SQLiteConnection * connection,sqlite3_stmt * statement)608 static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
609 int err = sqlite3_step(statement);
610 if (err != SQLITE_ROW) {
611 throw_sqlite3_exception(env, connection->db);
612 }
613 return err;
614 }
615
nativeExecuteForLong(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)616 static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
617 jlong connectionPtr, jlong statementPtr) {
618 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
619 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
620
621 int err = executeOneRowQuery(env, connection, statement);
622 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
623 return sqlite3_column_int64(statement, 0);
624 }
625 return -1;
626 }
627
nativeExecuteForString(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)628 static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
629 jlong connectionPtr, jlong statementPtr) {
630 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
631 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
632
633 int err = executeOneRowQuery(env, connection, statement);
634 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
635 const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(statement, 0));
636 if (text) {
637 size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
638 return env->NewString(text, length);
639 }
640 }
641 return NULL;
642 }
643
createAshmemRegionWithData(JNIEnv * env,const void * data,size_t length)644 static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
645 int error = 0;
646 int fd = ashmem_create_region(NULL, length);
647 if (fd < 0) {
648 error = errno;
649 ALOGE("ashmem_create_region failed: %s", strerror(error));
650 } else {
651 if (length > 0) {
652 void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
653 if (ptr == MAP_FAILED) {
654 error = errno;
655 ALOGE("mmap failed: %s", strerror(error));
656 } else {
657 memcpy(ptr, data, length);
658 munmap(ptr, length);
659 }
660 }
661
662 if (!error) {
663 if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
664 error = errno;
665 ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
666 } else {
667 return fd;
668 }
669 }
670
671 close(fd);
672 }
673
674 jniThrowIOException(env, error);
675 return -1;
676 }
677
nativeExecuteForBlobFileDescriptor(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr)678 static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
679 jlong connectionPtr, jlong statementPtr) {
680 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
681 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
682
683 int err = executeOneRowQuery(env, connection, statement);
684 if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
685 const void* blob = sqlite3_column_blob(statement, 0);
686 if (blob) {
687 int length = sqlite3_column_bytes(statement, 0);
688 if (length >= 0) {
689 return createAshmemRegionWithData(env, blob, length);
690 }
691 }
692 }
693 return -1;
694 }
695
696 enum CopyRowResult {
697 CPR_OK,
698 CPR_FULL,
699 CPR_ERROR,
700 };
701
copyRow(JNIEnv * env,CursorWindow * window,sqlite3_stmt * statement,int numColumns,int startPos,int addedRows)702 static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
703 sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
704 // Allocate a new field directory for the row.
705 status_t status = window->allocRow();
706 if (status) {
707 LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
708 startPos, addedRows, status);
709 return CPR_FULL;
710 }
711
712 // Pack the row into the window.
713 CopyRowResult result = CPR_OK;
714 for (int i = 0; i < numColumns; i++) {
715 int type = sqlite3_column_type(statement, i);
716 if (type == SQLITE_TEXT) {
717 // TEXT data
718 const char* text = reinterpret_cast<const char*>(
719 sqlite3_column_text(statement, i));
720 // SQLite does not include the NULL terminator in size, but does
721 // ensure all strings are NULL terminated, so increase size by
722 // one to make sure we store the terminator.
723 size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
724 status = window->putString(addedRows, i, text, sizeIncludingNull);
725 if (status) {
726 LOG_WINDOW("Failed allocating %zu bytes for text at %d,%d, error=%d",
727 sizeIncludingNull, startPos + addedRows, i, status);
728 result = CPR_FULL;
729 break;
730 }
731 LOG_WINDOW("%d,%d is TEXT with %zu bytes",
732 startPos + addedRows, i, sizeIncludingNull);
733 } else if (type == SQLITE_INTEGER) {
734 // INTEGER data
735 int64_t value = sqlite3_column_int64(statement, i);
736 status = window->putLong(addedRows, i, value);
737 if (status) {
738 LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
739 i, status);
740 result = CPR_FULL;
741 break;
742 }
743 LOG_WINDOW("%d,%d is INTEGER %" PRId64, startPos + addedRows, i, value);
744 } else if (type == SQLITE_FLOAT) {
745 // FLOAT data
746 double value = sqlite3_column_double(statement, i);
747 status = window->putDouble(addedRows, i, value);
748 if (status) {
749 LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
750 i, status);
751 result = CPR_FULL;
752 break;
753 }
754 LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
755 } else if (type == SQLITE_BLOB) {
756 // BLOB data
757 const void* blob = sqlite3_column_blob(statement, i);
758 size_t size = sqlite3_column_bytes(statement, i);
759 status = window->putBlob(addedRows, i, blob, size);
760 if (status) {
761 LOG_WINDOW("Failed allocating %zu bytes for blob at %d,%d, error=%d",
762 size, startPos + addedRows, i, status);
763 result = CPR_FULL;
764 break;
765 }
766 LOG_WINDOW("%d,%d is Blob with %zu bytes",
767 startPos + addedRows, i, size);
768 } else if (type == SQLITE_NULL) {
769 // NULL field
770 status = window->putNull(addedRows, i);
771 if (status) {
772 LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
773 i, status);
774 result = CPR_FULL;
775 break;
776 }
777
778 LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
779 } else {
780 // Unknown data
781 ALOGE("Unknown column type when filling database window");
782 throw_sqlite3_exception(env, "Unknown column type when filling window");
783 result = CPR_ERROR;
784 break;
785 }
786 }
787
788 // Free the last row if if was not successfully copied.
789 if (result != CPR_OK) {
790 window->freeLastRow();
791 }
792 return result;
793 }
794
nativeExecuteForCursorWindow(JNIEnv * env,jclass clazz,jlong connectionPtr,jlong statementPtr,jlong windowPtr,jint startPos,jint requiredPos,jboolean countAllRows)795 static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
796 jlong connectionPtr, jlong statementPtr, jlong windowPtr,
797 jint startPos, jint requiredPos, jboolean countAllRows) {
798 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
799 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
800 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
801
802 status_t status = window->clear();
803 if (status) {
804 String8 msg;
805 msg.appendFormat("Failed to clear the cursor window, status=%d", status);
806 throw_sqlite3_exception(env, connection->db, msg.c_str());
807 return 0;
808 }
809
810 int numColumns = sqlite3_column_count(statement);
811 status = window->setNumColumns(numColumns);
812 if (status) {
813 String8 msg;
814 msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
815 numColumns, status);
816 throw_sqlite3_exception(env, connection->db, msg.c_str());
817 return 0;
818 }
819
820 int retryCount = 0;
821 int totalRows = 0;
822 int addedRows = 0;
823 bool windowFull = false;
824 bool gotException = false;
825 while (!gotException && (!windowFull || countAllRows)) {
826 int err = sqlite3_step(statement);
827 if (err == SQLITE_ROW) {
828 LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
829 retryCount = 0;
830 totalRows += 1;
831
832 // Skip the row if the window is full or we haven't reached the start position yet.
833 if (startPos >= totalRows || windowFull) {
834 continue;
835 }
836
837 CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
838 if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
839 // We filled the window before we got to the one row that we really wanted.
840 // Clear the window and start filling it again from here.
841 // TODO: Would be nicer if we could progressively replace earlier rows.
842 window->clear();
843 window->setNumColumns(numColumns);
844 startPos += addedRows;
845 addedRows = 0;
846 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
847 }
848
849 if (cpr == CPR_OK) {
850 addedRows += 1;
851 } else if (cpr == CPR_FULL) {
852 windowFull = true;
853 } else {
854 gotException = true;
855 }
856 } else if (err == SQLITE_DONE) {
857 // All rows processed, bail
858 LOG_WINDOW("Processed all rows");
859 break;
860 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
861 // The table is locked, retry
862 LOG_WINDOW("Database locked, retrying");
863 if (retryCount > 50) {
864 ALOGE("Bailing on database busy retry");
865 throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
866 gotException = true;
867 } else {
868 // Sleep to give the thread holding the lock a chance to finish
869 usleep(1000);
870 retryCount++;
871 }
872 } else {
873 throw_sqlite3_exception(env, connection->db);
874 gotException = true;
875 }
876 }
877
878 LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows "
879 "to the window in %zu bytes",
880 statement, totalRows, addedRows, window->size() - window->freeSpace());
881 sqlite3_reset(statement);
882
883 // Report the total number of rows on request.
884 if (startPos > totalRows) {
885 ALOGE("startPos %d > actual rows %d", startPos, totalRows);
886 }
887 if (totalRows > 0 && addedRows == 0) {
888 String8 msg;
889 msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
890 requiredPos, totalRows);
891 throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.c_str());
892 return 0;
893 }
894
895 jlong result = jlong(startPos) << 32 | jlong(totalRows);
896 return result;
897 }
898
nativeGetDbLookaside(JNIEnv * env,jobject clazz,jlong connectionPtr)899 static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jlong connectionPtr) {
900 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
901
902 int cur = -1;
903 int unused;
904 sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
905 return cur;
906 }
907
nativeCancel(JNIEnv * env,jobject clazz,jlong connectionPtr)908 static void nativeCancel(JNIEnv* env, jobject clazz, jlong connectionPtr) {
909 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
910 connection->canceled = true;
911 }
912
nativeResetCancel(JNIEnv * env,jobject clazz,jlong connectionPtr,jboolean cancelable)913 static void nativeResetCancel(JNIEnv* env, jobject clazz, jlong connectionPtr,
914 jboolean cancelable) {
915 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
916 connection->canceled = false;
917
918 if (cancelable) {
919 sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
920 connection);
921 } else {
922 sqlite3_progress_handler(connection->db, 0, NULL, NULL);
923 }
924 }
925
nativeLastInsertRowId(JNIEnv * env,jclass,jlong connectionPtr)926 static jint nativeLastInsertRowId(JNIEnv* env, jclass, jlong connectionPtr) {
927 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
928 return sqlite3_last_insert_rowid(connection->db);
929 }
930
nativeChanges(JNIEnv * env,jclass,jlong connectionPtr)931 static jlong nativeChanges(JNIEnv* env, jclass, jlong connectionPtr) {
932 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
933 return sqlite3_changes64(connection->db);
934 }
935
nativeTotalChanges(JNIEnv * env,jclass,jlong connectionPtr)936 static jlong nativeTotalChanges(JNIEnv* env, jclass, jlong connectionPtr) {
937 SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
938 return sqlite3_total_changes64(connection->db);
939 }
940
941 static const JNINativeMethod sMethods[] =
942 {
943 /* name, signature, funcPtr */
944 { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZII)J",
945 (void*)nativeOpen },
946 { "nativeClose", "(J)V",
947 (void*)nativeClose },
948 { "nativeRegisterCustomScalarFunction", "(JLjava/lang/String;Ljava/util/function/UnaryOperator;)V",
949 (void*)nativeRegisterCustomScalarFunction },
950 { "nativeRegisterCustomAggregateFunction", "(JLjava/lang/String;Ljava/util/function/BinaryOperator;)V",
951 (void*)nativeRegisterCustomAggregateFunction },
952 { "nativeRegisterLocalizedCollators", "(JLjava/lang/String;)V",
953 (void*)nativeRegisterLocalizedCollators },
954 { "nativePrepareStatement", "(JLjava/lang/String;)J",
955 (void*)nativePrepareStatement },
956 { "nativeFinalizeStatement", "(JJ)V",
957 (void*)nativeFinalizeStatement },
958 { "nativeGetParameterCount", "(JJ)I",
959 (void*)nativeGetParameterCount },
960 { "nativeIsReadOnly", "(JJ)Z",
961 (void*)nativeIsReadOnly },
962 { "nativeUpdatesTempOnly", "(JJ)Z",
963 (void*)nativeUpdatesTempOnly },
964 { "nativeGetColumnCount", "(JJ)I",
965 (void*)nativeGetColumnCount },
966 { "nativeGetColumnName", "(JJI)Ljava/lang/String;",
967 (void*)nativeGetColumnName },
968 { "nativeBindNull", "(JJI)V",
969 (void*)nativeBindNull },
970 { "nativeBindLong", "(JJIJ)V",
971 (void*)nativeBindLong },
972 { "nativeBindDouble", "(JJID)V",
973 (void*)nativeBindDouble },
974 { "nativeBindString", "(JJILjava/lang/String;)V",
975 (void*)nativeBindString },
976 { "nativeBindBlob", "(JJI[B)V",
977 (void*)nativeBindBlob },
978 { "nativeResetStatementAndClearBindings", "(JJ)V",
979 (void*)nativeResetStatementAndClearBindings },
980 { "nativeExecute", "(JJZ)V",
981 (void*)nativeExecute },
982 { "nativeExecuteForLong", "(JJ)J",
983 (void*)nativeExecuteForLong },
984 { "nativeExecuteForString", "(JJ)Ljava/lang/String;",
985 (void*)nativeExecuteForString },
986 { "nativeExecuteForBlobFileDescriptor", "(JJ)I",
987 (void*)nativeExecuteForBlobFileDescriptor },
988 { "nativeExecuteForChangedRowCount", "(JJ)I",
989 (void*)nativeExecuteForChangedRowCount },
990 { "nativeExecuteForLastInsertedRowId", "(JJ)J",
991 (void*)nativeExecuteForLastInsertedRowId },
992 { "nativeExecuteForCursorWindow", "(JJJIIZ)J",
993 (void*)nativeExecuteForCursorWindow },
994 { "nativeGetDbLookaside", "(J)I",
995 (void*)nativeGetDbLookaside },
996 { "nativeCancel", "(J)V",
997 (void*)nativeCancel },
998 { "nativeResetCancel", "(JZ)V",
999 (void*)nativeResetCancel },
1000
1001 { "nativeLastInsertRowId", "(J)I", (void*) nativeLastInsertRowId },
1002 { "nativeChanges", "(J)J", (void*) nativeChanges },
1003 { "nativeTotalChanges", "(J)J", (void*) nativeTotalChanges },
1004 };
1005
register_android_database_SQLiteConnection(JNIEnv * env)1006 int register_android_database_SQLiteConnection(JNIEnv *env)
1007 {
1008 jclass unaryClazz = FindClassOrDie(env, "java/util/function/UnaryOperator");
1009 gUnaryOperator.apply = GetMethodIDOrDie(env, unaryClazz,
1010 "apply", "(Ljava/lang/Object;)Ljava/lang/Object;");
1011
1012 jclass binaryClazz = FindClassOrDie(env, "java/util/function/BinaryOperator");
1013 gBinaryOperator.apply = GetMethodIDOrDie(env, binaryClazz,
1014 "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
1015
1016 return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteConnection", sMethods,
1017 NELEM(sMethods));
1018 }
1019
1020 } // namespace android
1021