1/* //device/libs/android_runtime/android_database_SQLiteCursor.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#undef LOG_TAG
19#define LOG_TAG "SQLiteStatementCpp"
20
21#include "android_util_Binder.h"
22
23#include <jni.h>
24#include <JNIHelp.h>
25#include <android_runtime/AndroidRuntime.h>
26
27#include <sqlite3.h>
28
29#include <cutils/ashmem.h>
30#include <utils/Log.h>
31
32#include <fcntl.h>
33#include <stdio.h>
34#include <string.h>
35#include <unistd.h>
36#include <sys/mman.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39
40#include "sqlite3_exception.h"
41
42namespace android {
43
44
45sqlite3_stmt * compile(JNIEnv* env, jobject object,
46                       sqlite3 * handle, jstring sqlString);
47
48static jfieldID gHandleField;
49static jfieldID gStatementField;
50
51
52#define GET_STATEMENT(env, object) \
53        (sqlite3_stmt *)env->GetIntField(object, gStatementField)
54#define GET_HANDLE(env, object) \
55        (sqlite3 *)env->GetIntField(object, gHandleField)
56
57
58static jint native_execute(JNIEnv* env, jobject object)
59{
60    int err;
61    sqlite3 * handle = GET_HANDLE(env, object);
62    sqlite3_stmt * statement = GET_STATEMENT(env, object);
63    int numChanges = -1;
64
65    // Execute the statement
66    err = sqlite3_step(statement);
67
68    // Throw an exception if an error occurred
69    if (err == SQLITE_ROW) {
70        throw_sqlite3_exception(env,
71                "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
72    } else if (err != SQLITE_DONE) {
73        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
74    } else {
75        numChanges = sqlite3_changes(handle);
76    }
77
78    // Reset the statement so it's ready to use again
79    sqlite3_reset(statement);
80    return numChanges;
81}
82
83static jlong native_executeInsert(JNIEnv* env, jobject object)
84{
85    sqlite3 * handle = GET_HANDLE(env, object);
86    jint numChanges = native_execute(env, object);
87    if (numChanges > 0) {
88        return sqlite3_last_insert_rowid(handle);
89    } else {
90        return -1;
91    }
92}
93
94static jlong native_1x1_long(JNIEnv* env, jobject object)
95{
96    int err;
97    sqlite3 * handle = GET_HANDLE(env, object);
98    sqlite3_stmt * statement = GET_STATEMENT(env, object);
99    jlong value = -1;
100
101    // Execute the statement
102    err = sqlite3_step(statement);
103
104    // Handle the result
105    if (err == SQLITE_ROW) {
106        // No errors, read the data and return it
107        value = sqlite3_column_int64(statement, 0);
108    } else {
109        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
110    }
111
112    // Reset the statment so it's ready to use again
113    sqlite3_reset(statement);
114
115    return value;
116}
117
118static jstring native_1x1_string(JNIEnv* env, jobject object)
119{
120    int err;
121    sqlite3 * handle = GET_HANDLE(env, object);
122    sqlite3_stmt * statement = GET_STATEMENT(env, object);
123    jstring value = NULL;
124
125    // Execute the statement
126    err = sqlite3_step(statement);
127
128    // Handle the result
129    if (err == SQLITE_ROW) {
130        // No errors, read the data and return it
131        char const * text = (char const *)sqlite3_column_text(statement, 0);
132        value = env->NewStringUTF(text);
133    } else {
134        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
135    }
136
137    // Reset the statment so it's ready to use again
138    sqlite3_reset(statement);
139
140    return value;
141}
142
143static jobject createParcelFileDescriptor(JNIEnv * env, int fd)
144{
145    // Create FileDescriptor object
146    jobject fileDesc = jniCreateFileDescriptor(env, fd);
147    if (fileDesc == NULL) {
148        // FileDescriptor constructor has thrown an exception
149        close(fd);
150        return NULL;
151    }
152
153    // Wrap it in a ParcelFileDescriptor
154    jobject parcelFileDesc = newParcelFileDescriptor(env, fileDesc);
155    if (parcelFileDesc == NULL) {
156        // ParcelFileDescriptor constructor has thrown an exception
157        close(fd);
158        return NULL;
159    }
160
161    return parcelFileDesc;
162}
163
164// Creates an ashmem area, copies some data into it, and returns
165// a ParcelFileDescriptor for the ashmem area.
166static jobject create_ashmem_region_with_data(JNIEnv * env,
167                                              const void * data, int length)
168{
169    // Create ashmem area
170    int fd = ashmem_create_region(NULL, length);
171    if (fd < 0) {
172        LOGE("ashmem_create_region failed: %s", strerror(errno));
173        jniThrowIOException(env, errno);
174        return NULL;
175    }
176
177    if (length > 0) {
178        // mmap the ashmem area
179        void * ashmem_ptr =
180                mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
181        if (ashmem_ptr == MAP_FAILED) {
182            LOGE("mmap failed: %s", strerror(errno));
183            jniThrowIOException(env, errno);
184            close(fd);
185            return NULL;
186        }
187
188        // Copy data to ashmem area
189        memcpy(ashmem_ptr, data, length);
190
191        // munmap ashmem area
192        if (munmap(ashmem_ptr, length) < 0) {
193            LOGE("munmap failed: %s", strerror(errno));
194            jniThrowIOException(env, errno);
195            close(fd);
196            return NULL;
197        }
198    }
199
200    // Make ashmem area read-only
201    if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
202        LOGE("ashmem_set_prot_region failed: %s", strerror(errno));
203        jniThrowIOException(env, errno);
204        close(fd);
205        return NULL;
206    }
207
208    // Wrap it in a ParcelFileDescriptor
209    return createParcelFileDescriptor(env, fd);
210}
211
212static jobject native_1x1_blob_ashmem(JNIEnv* env, jobject object)
213{
214    int err;
215    sqlite3 * handle = GET_HANDLE(env, object);
216    sqlite3_stmt * statement = GET_STATEMENT(env, object);
217    jobject value = NULL;
218
219    // Execute the statement
220    err = sqlite3_step(statement);
221
222    // Handle the result
223    if (err == SQLITE_ROW) {
224        // No errors, read the data and return it
225        const void * blob = sqlite3_column_blob(statement, 0);
226        if (blob != NULL) {
227            int len = sqlite3_column_bytes(statement, 0);
228            if (len >= 0) {
229                value = create_ashmem_region_with_data(env, blob, len);
230            }
231        }
232    } else {
233        throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle));
234    }
235
236    // Reset the statment so it's ready to use again
237    sqlite3_reset(statement);
238
239    return value;
240}
241
242static void native_executeSql(JNIEnv* env, jobject object, jstring sql)
243{
244    char const* sqlString = env->GetStringUTFChars(sql, NULL);
245    sqlite3 * handle = GET_HANDLE(env, object);
246    int err = sqlite3_exec(handle, sqlString, NULL, NULL, NULL);
247    if (err != SQLITE_OK) {
248        throw_sqlite3_exception(env, handle);
249    }
250    env->ReleaseStringUTFChars(sql, sqlString);
251}
252
253static JNINativeMethod sMethods[] =
254{
255     /* name, signature, funcPtr */
256    {"native_execute", "()I", (void *)native_execute},
257    {"native_executeInsert", "()J", (void *)native_executeInsert},
258    {"native_1x1_long", "()J", (void *)native_1x1_long},
259    {"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string},
260    {"native_1x1_blob_ashmem", "()Landroid/os/ParcelFileDescriptor;", (void *)native_1x1_blob_ashmem},
261    {"native_executeSql", "(Ljava/lang/String;)V", (void *)native_executeSql},
262};
263
264int register_android_database_SQLiteStatement(JNIEnv * env)
265{
266    jclass clazz;
267
268    clazz = env->FindClass("android/database/sqlite/SQLiteStatement");
269    if (clazz == NULL) {
270        LOGE("Can't find android/database/sqlite/SQLiteStatement");
271        return -1;
272    }
273
274    gHandleField = env->GetFieldID(clazz, "nHandle", "I");
275    gStatementField = env->GetFieldID(clazz, "nStatement", "I");
276
277    if (gHandleField == NULL || gStatementField == NULL) {
278        LOGE("Error locating fields");
279        return -1;
280    }
281
282    return AndroidRuntime::registerNativeMethods(env,
283        "android/database/sqlite/SQLiteStatement", sMethods, NELEM(sMethods));
284}
285
286} // namespace android
287