1 /*
2 * Copyright (C) 2023 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 "SQLiteRawStatement"
18
19 #include <string.h>
20 #include <algorithm>
21
22 #include <jni.h>
23 #include <nativehelper/JNIHelp.h>
24 #include <nativehelper/scoped_primitive_array.h>
25 #include <nativehelper/scoped_string_chars.h>
26 #include <android_runtime/AndroidRuntime.h>
27 #include <android_runtime/Log.h>
28 #include <android-base/stringprintf.h>
29 #include <core_jni_helpers.h>
30
31 #include <utils/Log.h>
32 #include <utils/Unicode.h>
33
34 #include <sqlite3.h>
35 #include <sqlite3_android.h>
36
37 #include "android_database_SQLiteCommon.h"
38
39 /**
40 * JNI functions supporting the android.database.sqlite.SQLiteRawStatement class.
41 */
42 namespace android {
43
44 // A zero-length byte array that can be returned by getColumnBlob(). The theory is that
45 // zero-length blobs are common enough that it is worth having a single, global instance. The
46 // object is created in the jni registration function. It is never destroyed.
47 static jbyteArray emptyArray = nullptr;
48
49 // Helper functions.
db(long statementPtr)50 static sqlite3 *db(long statementPtr) {
51 return sqlite3_db_handle(reinterpret_cast<sqlite3_stmt*>(statementPtr));
52 }
53
stmt(long statementPtr)54 static sqlite3_stmt* stmt(long statementPtr) {
55 return reinterpret_cast<sqlite3_stmt*>(statementPtr);
56 }
57
58 // This throws a SQLiteBindOrColumnIndexOutOfRangeException if the parameter index is out
59 // of bounds. The function exists to construct an error message that includes
60 // the bounds.
throwInvalidParameter(JNIEnv * env,jlong stmtPtr,jint index)61 static void throwInvalidParameter(JNIEnv *env, jlong stmtPtr, jint index) {
62 if (sqlite3_extended_errcode(db(stmtPtr)) == SQLITE_RANGE) {
63 int count = sqlite3_bind_parameter_count(stmt(stmtPtr));
64 std::string message = android::base::StringPrintf(
65 "parameter index %d out of bounds [1,%d]", index, count);
66 char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
67 throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
68 } else {
69 throw_sqlite3_exception(env, db(stmtPtr), nullptr);
70 }
71 }
72
73
74 // This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out
75 // of bounds.
throwIfInvalidColumn(JNIEnv * env,jlong stmtPtr,jint col)76 static void throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
77 if (col < 0 || col >= sqlite3_data_count(stmt(stmtPtr))) {
78 int count = sqlite3_data_count(stmt(stmtPtr));
79 std::string message = android::base::StringPrintf(
80 "column index %d out of bounds [0,%d]", col, count - 1);
81 char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
82 throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
83 }
84 }
85
86 // If the last operation failed, throw an exception and return true. Otherwise return false.
throwIfError(JNIEnv * env,jlong stmtPtr)87 static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
88 switch (sqlite3_errcode(db(stmtPtr))) {
89 case SQLITE_OK:
90 case SQLITE_DONE:
91 case SQLITE_ROW: return false;
92 }
93 throw_sqlite3_exception(env, db(stmtPtr), nullptr);
94 return true;
95 }
96
bindParameterCount(JNIEnv * env,jclass,jlong stmtPtr)97 static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
98 return sqlite3_bind_parameter_count(stmt(stmtPtr));
99 }
100
101 // jname must be in standard UTF-8. This throws an NPE if jname is null.
bindParameterIndex(JNIEnv * env,jclass,jlong stmtPtr,jstring jname)102 static jint bindParameterIndex(JNIEnv *env, jclass, jlong stmtPtr, jstring jname) {
103 ScopedStringChars name(env, jname);
104 if (name.get() == nullptr) {
105 return 0;
106 }
107 size_t len16 = env->GetStringLength(jname);
108 size_t len8 = utf16_to_utf8_length(reinterpret_cast<const char16_t*>(name.get()), len16);
109 // The extra byte is for the terminating null.
110 char *utf8Name = new char[len8 + 1];
111 utf16_to_utf8(reinterpret_cast<const char16_t*>(name.get()), len16, utf8Name, len8 + 1);
112 int r = sqlite3_bind_parameter_index(stmt(stmtPtr), utf8Name);
113 delete [] utf8Name;
114 return r;
115 }
116
117 // The name returned from the database is UTF-8. If there is no matching name,
118 // null is returned.
bindParameterName(JNIEnv * env,jclass,jlong stmtPtr,jint param)119 static jstring bindParameterName(JNIEnv *env, jclass, jlong stmtPtr, jint param) {
120 char const *src = sqlite3_bind_parameter_name(stmt(stmtPtr), param);
121 if (src == nullptr) {
122 return NULL;
123 }
124 return env->NewStringUTF(src);
125 }
126
columnCount(JNIEnv * env,jclass,jlong stmtPtr)127 static jint columnCount(JNIEnv* env, jclass, jlong stmtPtr) {
128 return sqlite3_column_count(stmt(stmtPtr));
129 }
130
131 // Step the prepared statement. If the result is other than ROW, DONE, BUSY, or LOCKED, throw an
132 // exception if throwOnError is true. The advantage of throwing from the native latyer is that
133 // all the error codes and error strings are easily visible.
step(JNIEnv * env,jclass,jlong stmtPtr,jboolean throwOnError)134 static jint step(JNIEnv* env, jclass, jlong stmtPtr, jboolean throwOnError) {
135 sqlite3_stmt* statement = stmt(stmtPtr);
136 int err = sqlite3_step(statement);
137 switch (err) {
138 case SQLITE_ROW:
139 case SQLITE_DONE:
140 case SQLITE_BUSY:
141 case SQLITE_LOCKED:
142 return err;
143 }
144 if (throwOnError) {
145 throw_sqlite3_exception(env, db(stmtPtr), "failure in step()");
146 }
147 return err;
148 }
149
reset(JNIEnv *,jclass,jlong stmtPtr,jboolean clear)150 static void reset(JNIEnv*, jclass, jlong stmtPtr, jboolean clear) {
151 if (clear) sqlite3_clear_bindings(stmt(stmtPtr));
152 // The return value is ignored.
153 sqlite3_reset(stmt(stmtPtr));
154 }
155
clearBindings(JNIEnv *,jclass,jlong stmtPtr)156 static void clearBindings(JNIEnv*, jclass, jlong stmtPtr) {
157 sqlite3_clear_bindings(stmt(stmtPtr));
158 }
159
160
161 // This binds null to the parameter if the incoming array is null.
bindBlob(JNIEnv * env,jclass obj,jlong stmtPtr,jint index,jbyteArray val,jint offset,jint length)162 static void bindBlob(JNIEnv* env, jclass obj, jlong stmtPtr, jint index, jbyteArray val,
163 jint offset, jint length) {
164 ScopedByteArrayRO value(env, val);
165 int err;
166 if (value.get() == nullptr) {
167 err = sqlite3_bind_null(stmt(stmtPtr), index);
168 } else {
169 err = sqlite3_bind_blob(stmt(stmtPtr), index, value.get() + offset,
170 length, SQLITE_TRANSIENT);
171 }
172 if (err != SQLITE_OK) {
173 throwInvalidParameter(env, stmtPtr, index);
174 }
175 }
176
bindDouble(JNIEnv * env,jclass,jlong stmtPtr,jint index,jdouble val)177 static void bindDouble(JNIEnv* env, jclass, jlong stmtPtr, jint index, jdouble val) {
178 if (sqlite3_bind_double(stmt(stmtPtr), index, val) != SQLITE_OK) {
179 throwInvalidParameter(env, stmtPtr, index);
180 }
181 }
182
bindInt(JNIEnv * env,jclass,jlong stmtPtr,jint index,jint val)183 static void bindInt(JNIEnv* env, jclass, jlong stmtPtr, jint index, jint val) {
184 if (sqlite3_bind_int(stmt(stmtPtr), index, val) != SQLITE_OK) {
185 throwInvalidParameter(env, stmtPtr, index);
186 }
187 }
188
bindLong(JNIEnv * env,jclass,jlong stmtPtr,jint index,jlong val)189 static void bindLong(JNIEnv* env, jclass, jlong stmtPtr, jint index, jlong val) {
190 if (sqlite3_bind_int64(stmt(stmtPtr), index, val) != SQLITE_OK) {
191 throwInvalidParameter(env, stmtPtr, index);
192 }
193 }
194
bindNull(JNIEnv * env,jclass,jlong stmtPtr,jint index)195 static void bindNull(JNIEnv* env, jclass, jlong stmtPtr, jint index) {
196 if (sqlite3_bind_null(stmt(stmtPtr), index) != SQLITE_OK) {
197 throwInvalidParameter(env, stmtPtr, index);
198 }
199 }
200
201 // This binds null to the parameter if the string is null.
bindText(JNIEnv * env,jclass,jlong stmtPtr,jint index,jstring val)202 static void bindText(JNIEnv* env, jclass, jlong stmtPtr, jint index, jstring val) {
203 ScopedStringChars value(env, val);
204 int err;
205 if (value.get() == nullptr) {
206 err = sqlite3_bind_null(stmt(stmtPtr), index);
207 } else {
208 jsize valueLength = env->GetStringLength(val);
209 err = sqlite3_bind_text16(stmt(stmtPtr), index, value.get(),
210 valueLength * sizeof(jchar), SQLITE_TRANSIENT);
211 }
212 if (err != SQLITE_OK) {
213 throwInvalidParameter(env, stmtPtr, index);
214 }
215 }
216
217
columnType(JNIEnv * env,jclass,jlong stmtPtr,jint col)218 static jint columnType(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
219 throwIfInvalidColumn(env, stmtPtr, col);
220 return sqlite3_column_type(stmt(stmtPtr), col);
221 }
222
columnName(JNIEnv * env,jclass,jlong stmtPtr,jint col)223 static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
224 throwIfInvalidColumn(env, stmtPtr, col);
225 const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(stmt(stmtPtr), col));
226 if (name == nullptr) {
227 throw_sqlite3_exception(env, db(stmtPtr), "error fetching columnName()");
228 return NULL;
229 }
230 size_t length = strlen16(reinterpret_cast<const char16_t*>(name));
231 return env->NewString(name, length);
232 }
233
columnBytes(JNIEnv * env,jclass,jlong stmtPtr,jint col)234 static jint columnBytes(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
235 throwIfInvalidColumn(env, stmtPtr, col);
236 int r = sqlite3_column_bytes16(stmt(stmtPtr), col);
237 throwIfError(env, stmtPtr);
238 return r;
239 }
240
columnBlob(JNIEnv * env,jclass,jlong stmtPtr,jint col)241 static jbyteArray columnBlob(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
242 throwIfInvalidColumn(env, stmtPtr, col);
243 const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
244 if (blob == nullptr) {
245 if (throwIfError(env, stmtPtr)) {
246 return NULL;
247 }
248 return (sqlite3_column_type(stmt(stmtPtr), col) == SQLITE_NULL) ? NULL : emptyArray;
249 }
250 size_t size = sqlite3_column_bytes(stmt(stmtPtr), col);
251 if (throwIfError(env, stmtPtr)) {
252 return NULL;
253 }
254 jbyteArray result = env->NewByteArray(size);
255 if (result == nullptr) {
256 // An OutOfMemory exception will have been thrown.
257 return NULL;
258 }
259 env->SetByteArrayRegion(result, 0, size, reinterpret_cast<const jbyte*>(blob));
260 return result;
261 }
262
columnBuffer(JNIEnv * env,jclass,jlong stmtPtr,jint col,jbyteArray buffer,jint offset,jint length,jint srcOffset)263 static int columnBuffer(JNIEnv* env, jclass, jlong stmtPtr, jint col,
264 jbyteArray buffer, jint offset, jint length, jint srcOffset) {
265 throwIfInvalidColumn(env, stmtPtr, col);
266 const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
267 if (blob == nullptr) {
268 throwIfError(env, stmtPtr);
269 return 0;
270 }
271 jsize bsize = sqlite3_column_bytes(stmt(stmtPtr), col);
272 if (throwIfError(env, stmtPtr)) {
273 return 0;
274 }
275 if (bsize == 0 || bsize <= srcOffset) {
276 return 0;
277 }
278 jsize want = std::min(bsize - srcOffset, length);
279 env->SetByteArrayRegion(buffer, offset, want, reinterpret_cast<const jbyte*>(blob) + srcOffset);
280 return want;
281 }
282
columnDouble(JNIEnv * env,jclass,jlong stmtPtr,jint col)283 static jdouble columnDouble(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
284 throwIfInvalidColumn(env, stmtPtr, col);
285 return sqlite3_column_double(stmt(stmtPtr), col);
286 }
287
columnInt(JNIEnv * env,jclass,jlong stmtPtr,jint col)288 static jint columnInt(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
289 throwIfInvalidColumn(env, stmtPtr, col);
290 return sqlite3_column_int(stmt(stmtPtr), col);
291 }
292
columnLong(JNIEnv * env,jclass,jlong stmtPtr,jint col)293 static jlong columnLong(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
294 throwIfInvalidColumn(env, stmtPtr, col);
295 return sqlite3_column_int64(stmt(stmtPtr), col);
296 }
297
columnText(JNIEnv * env,jclass,jlong stmtPtr,jint col)298 static jstring columnText(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
299 throwIfInvalidColumn(env, stmtPtr, col);
300 const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(stmt(stmtPtr), col));
301 if (text == nullptr) {
302 throwIfError(env, stmtPtr);
303 return NULL;
304 }
305 size_t length = sqlite3_column_bytes16(stmt(stmtPtr), col) / sizeof(jchar);
306 if (throwIfError(env, stmtPtr)) {
307 return NULL;
308 }
309 return env->NewString(text, length);
310 }
311
312 static const JNINativeMethod sStatementMethods[] =
313 {
314 // Metadata
315 { "nativeBindParameterCount", "(J)I", (void*) bindParameterCount },
316 { "nativeBindParameterIndex", "(JLjava/lang/String;)I", (void*) bindParameterIndex },
317 { "nativeBindParameterName", "(JI)Ljava/lang/String;", (void*) bindParameterName },
318
319 // Operations on a statement
320 { "nativeStep", "(JZ)I", (void*) step },
321 { "nativeReset", "(JZ)V", (void*) reset },
322 { "nativeClearBindings", "(J)V", (void*) clearBindings },
323
324 // Methods that bind values to parameters
325 { "nativeBindBlob", "(JI[BII)V", (void*) bindBlob },
326 { "nativeBindDouble", "(JID)V", (void*) bindDouble },
327 { "nativeBindInt", "(JII)V", (void*) bindInt },
328 { "nativeBindLong", "(JIJ)V", (void*) bindLong },
329 { "nativeBindNull", "(JI)V", (void*) bindNull },
330 { "nativeBindText", "(JILjava/lang/String;)V", (void*) bindText },
331
332 // Methods that return information about columns in a result row.
333 { "nativeColumnCount", "(J)I", (void*) columnCount },
334 { "nativeColumnType", "(JI)I", (void*) columnType },
335 { "nativeColumnName", "(JI)Ljava/lang/String;", (void*) columnName },
336
337 { "nativeColumnBytes", "(JI)I", (void*) columnBytes },
338
339 { "nativeColumnBlob", "(JI)[B", (void*) columnBlob },
340 { "nativeColumnBuffer", "(JI[BIII)I", (void*) columnBuffer },
341 { "nativeColumnDouble", "(JI)D", (void*) columnDouble },
342 { "nativeColumnInt", "(JI)I", (void*) columnInt },
343 { "nativeColumnLong", "(JI)J", (void*) columnLong },
344 { "nativeColumnText", "(JI)Ljava/lang/String;", (void*) columnText },
345 };
346
register_android_database_SQLiteRawStatement(JNIEnv * env)347 int register_android_database_SQLiteRawStatement(JNIEnv *env)
348 {
349 RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteRawStatement",
350 sStatementMethods, NELEM(sStatementMethods));
351 emptyArray = MakeGlobalRefOrDie(env, env->NewByteArray(0));
352 return 0;
353 }
354
355 } // namespace android
356