1 /*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #undef LOG_TAG
18 #define LOG_TAG "SqliteCursor.cpp"
19
20 #include <jni.h>
21 #include <JNIHelp.h>
22 #include <android_runtime/AndroidRuntime.h>
23
24 #include <sqlite3.h>
25
26 #include <utils/Log.h>
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "binder/CursorWindow.h"
33 #include "sqlite3_exception.h"
34
35
36 namespace android {
37
nativeFillWindow(JNIEnv * env,jclass clazz,jint databasePtr,jint statementPtr,jint windowPtr,jint startPos,jint offsetParam)38 static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,
39 jint statementPtr, jint windowPtr, jint startPos, jint offsetParam) {
40 sqlite3* database = reinterpret_cast<sqlite3*>(databasePtr);
41 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
42 CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
43
44 // Only do the binding if there is a valid offsetParam. If no binding needs to be done
45 // offsetParam will be set to 0, an invalid value.
46 if (offsetParam > 0) {
47 // Bind the offset parameter, telling the program which row to start with
48 int err = sqlite3_bind_int(statement, offsetParam, startPos);
49 if (err != SQLITE_OK) {
50 LOGE("Unable to bind offset position, offsetParam = %d", offsetParam);
51 throw_sqlite3_exception(env, database);
52 return 0;
53 }
54 LOG_WINDOW("Bound to startPos %d", startPos);
55 } else {
56 LOG_WINDOW("Not binding to startPos %d", startPos);
57 }
58
59 // We assume numRows is initially 0.
60 LOG_WINDOW("Window: numRows = %d, size = %d, freeSpace = %d",
61 window->getNumRows(), window->size(), window->freeSpace());
62
63 int numColumns = sqlite3_column_count(statement);
64 status_t status = window->setNumColumns(numColumns);
65 if (status) {
66 LOGE("Failed to change column count from %d to %d", window->getNumColumns(), numColumns);
67 jniThrowException(env, "java/lang/IllegalStateException", "numColumns mismatch");
68 return 0;
69 }
70
71 int retryCount = 0;
72 int totalRows = 0;
73 int addedRows = 0;
74 bool windowFull = false;
75 bool gotException = false;
76 const bool countAllRows = (startPos == 0); // when startPos is 0, we count all rows
77 while (!gotException && (!windowFull || countAllRows)) {
78 int err = sqlite3_step(statement);
79 if (err == SQLITE_ROW) {
80 LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
81 retryCount = 0;
82 totalRows += 1;
83
84 // Skip the row if the window is full or we haven't reached the start position yet.
85 if (startPos >= totalRows || windowFull) {
86 continue;
87 }
88
89 // Allocate a new field directory for the row. This pointer is not reused
90 // since it may be possible for it to be relocated on a call to alloc() when
91 // the field data is being allocated.
92 status = window->allocRow();
93 if (status) {
94 LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
95 startPos, addedRows, status);
96 windowFull = true;
97 continue;
98 }
99
100 // Pack the row into the window.
101 for (int i = 0; i < numColumns; i++) {
102 int type = sqlite3_column_type(statement, i);
103 if (type == SQLITE_TEXT) {
104 // TEXT data
105 const char* text = reinterpret_cast<const char*>(
106 sqlite3_column_text(statement, i));
107 // SQLite does not include the NULL terminator in size, but does
108 // ensure all strings are NULL terminated, so increase size by
109 // one to make sure we store the terminator.
110 size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
111 status = window->putString(addedRows, i, text, sizeIncludingNull);
112 if (status) {
113 LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
114 sizeIncludingNull, startPos + addedRows, i, status);
115 windowFull = true;
116 break;
117 }
118 LOG_WINDOW("%d,%d is TEXT with %u bytes",
119 startPos + addedRows, i, sizeIncludingNull);
120 } else if (type == SQLITE_INTEGER) {
121 // INTEGER data
122 int64_t value = sqlite3_column_int64(statement, i);
123 status = window->putLong(addedRows, i, value);
124 if (status) {
125 LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
126 i, status);
127 windowFull = true;
128 break;
129 }
130 LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
131 } else if (type == SQLITE_FLOAT) {
132 // FLOAT data
133 double value = sqlite3_column_double(statement, i);
134 status = window->putDouble(addedRows, i, value);
135 if (status) {
136 LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
137 i, status);
138 windowFull = true;
139 break;
140 }
141 LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
142 } else if (type == SQLITE_BLOB) {
143 // BLOB data
144 const void* blob = sqlite3_column_blob(statement, i);
145 size_t size = sqlite3_column_bytes(statement, i);
146 status = window->putBlob(addedRows, i, blob, size);
147 if (status) {
148 LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
149 size, startPos + addedRows, i, status);
150 windowFull = true;
151 break;
152 }
153 LOG_WINDOW("%d,%d is Blob with %u bytes",
154 startPos + addedRows, i, size);
155 } else if (type == SQLITE_NULL) {
156 // NULL field
157 status = window->putNull(addedRows, i);
158 if (status) {
159 LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
160 i, status);
161 windowFull = true;
162 break;
163 }
164
165 LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
166 } else {
167 // Unknown data
168 LOGE("Unknown column type when filling database window");
169 throw_sqlite3_exception(env, "Unknown column type when filling window");
170 gotException = true;
171 break;
172 }
173 }
174
175 // Update the final row tally.
176 if (windowFull || gotException) {
177 window->freeLastRow();
178 } else {
179 addedRows += 1;
180 }
181 } else if (err == SQLITE_DONE) {
182 // All rows processed, bail
183 LOG_WINDOW("Processed all rows");
184 break;
185 } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
186 // The table is locked, retry
187 LOG_WINDOW("Database locked, retrying");
188 if (retryCount > 50) {
189 LOGE("Bailing on database busy retry");
190 throw_sqlite3_exception(env, database, "retrycount exceeded");
191 gotException = true;
192 } else {
193 // Sleep to give the thread holding the lock a chance to finish
194 usleep(1000);
195 retryCount++;
196 }
197 } else {
198 throw_sqlite3_exception(env, database);
199 gotException = true;
200 }
201 }
202
203 LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
204 "to the window in %d bytes",
205 statement, totalRows, addedRows, window->size() - window->freeSpace());
206 sqlite3_reset(statement);
207
208 // Report the total number of rows on request.
209 if (startPos > totalRows) {
210 LOGE("startPos %d > actual rows %d", startPos, totalRows);
211 }
212 return countAllRows ? totalRows : 0;
213 }
214
nativeColumnCount(JNIEnv * env,jclass clazz,jint statementPtr)215 static jint nativeColumnCount(JNIEnv* env, jclass clazz, jint statementPtr) {
216 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
217 return sqlite3_column_count(statement);
218 }
219
nativeColumnName(JNIEnv * env,jclass clazz,jint statementPtr,jint columnIndex)220 static jstring nativeColumnName(JNIEnv* env, jclass clazz, jint statementPtr,
221 jint columnIndex) {
222 sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
223 const char* name = sqlite3_column_name(statement, columnIndex);
224 return env->NewStringUTF(name);
225 }
226
227
228 static JNINativeMethod sMethods[] =
229 {
230 /* name, signature, funcPtr */
231 { "nativeFillWindow", "(IIIII)I",
232 (void*)nativeFillWindow },
233 { "nativeColumnCount", "(I)I",
234 (void*)nativeColumnCount},
235 { "nativeColumnName", "(II)Ljava/lang/String;",
236 (void*)nativeColumnName},
237 };
238
register_android_database_SQLiteQuery(JNIEnv * env)239 int register_android_database_SQLiteQuery(JNIEnv * env)
240 {
241 return AndroidRuntime::registerNativeMethods(env,
242 "android/database/sqlite/SQLiteQuery", sMethods, NELEM(sMethods));
243 }
244
245 } // namespace android
246