• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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