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#include "android_database_SQLiteCommon.h"
18
19#include <utils/String8.h>
20
21#include <map>
22
23namespace android {
24
25static const std::map<int, std::string> sErrorCodesMap = {
26    // Primary Result Code List
27    {4,     "SQLITE_ABORT"},
28    {23,    "SQLITE_AUTH"},
29    {5,     "SQLITE_BUSY"},
30    {14,    "SQLITE_CANTOPEN"},
31    {19,    "SQLITE_CONSTRAINT"},
32    {11,    "SQLITE_CORRUPT"},
33    {101,   "SQLITE_DONE"},
34    {16,    "SQLITE_EMPTY"},
35    {1,     "SQLITE_ERROR"},
36    {24,    "SQLITE_FORMAT"},
37    {13,    "SQLITE_FULL"},
38    {2,     "SQLITE_INTERNAL"},
39    {9,     "SQLITE_INTERRUPT"},
40    {10,    "SQLITE_IOERR"},
41    {6,     "SQLITE_LOCKED"},
42    {20,    "SQLITE_MISMATCH"},
43    {21,    "SQLITE_MISUSE"},
44    {22,    "SQLITE_NOLFS"},
45    {7,     "SQLITE_NOMEM"},
46    {26,    "SQLITE_NOTADB"},
47    {12,    "SQLITE_NOTFOUND"},
48    {27,    "SQLITE_NOTICE"},
49    {0,     "SQLITE_OK"},
50    {3,     "SQLITE_PERM"},
51    {15,    "SQLITE_PROTOCOL"},
52    {25,    "SQLITE_RANGE"},
53    {8,     "SQLITE_READONLY"},
54    {100,   "SQLITE_ROW"},
55    {17,    "SQLITE_SCHEMA"},
56    {18,    "SQLITE_TOOBIG"},
57    {28,    "SQLITE_WARNING"},
58    // Extended Result Code List
59    {516,   "SQLITE_ABORT_ROLLBACK"},
60    {261,   "SQLITE_BUSY_RECOVERY"},
61    {517,   "SQLITE_BUSY_SNAPSHOT"},
62    {1038,  "SQLITE_CANTOPEN_CONVPATH"},
63    {782,   "SQLITE_CANTOPEN_FULLPATH"},
64    {526,   "SQLITE_CANTOPEN_ISDIR"},
65    {270,   "SQLITE_CANTOPEN_NOTEMPDIR"},
66    {275,   "SQLITE_CONSTRAINT_CHECK"},
67    {531,   "SQLITE_CONSTRAINT_COMMITHOOK"},
68    {787,   "SQLITE_CONSTRAINT_FOREIGNKEY"},
69    {1043,  "SQLITE_CONSTRAINT_FUNCTION"},
70    {1299,  "SQLITE_CONSTRAINT_NOTNULL"},
71    {1555,  "SQLITE_CONSTRAINT_PRIMARYKEY"},
72    {2579,  "SQLITE_CONSTRAINT_ROWID"},
73    {1811,  "SQLITE_CONSTRAINT_TRIGGER"},
74    {2067,  "SQLITE_CONSTRAINT_UNIQUE"},
75    {2323,  "SQLITE_CONSTRAINT_VTAB"},
76    {267,   "SQLITE_CORRUPT_VTAB"},
77    {3338,  "SQLITE_IOERR_ACCESS"},
78    {2826,  "SQLITE_IOERR_BLOCKED"},
79    {3594,  "SQLITE_IOERR_CHECKRESERVEDLOCK"},
80    {4106,  "SQLITE_IOERR_CLOSE"},
81    {6666,  "SQLITE_IOERR_CONVPATH"},
82    {2570,  "SQLITE_IOERR_DELETE"},
83    {5898,  "SQLITE_IOERR_DELETE_NOENT"},
84    {4362,  "SQLITE_IOERR_DIR_CLOSE"},
85    {1290,  "SQLITE_IOERR_DIR_FSYNC"},
86    {1802,  "SQLITE_IOERR_FSTAT"},
87    {1034,  "SQLITE_IOERR_FSYNC"},
88    {6410,  "SQLITE_IOERR_GETTEMPPATH"},
89    {3850,  "SQLITE_IOERR_LOCK"},
90    {6154,  "SQLITE_IOERR_MMAP"},
91    {3082,  "SQLITE_IOERR_NOMEM"},
92    {2314,  "SQLITE_IOERR_RDLOCK"},
93    {266,   "SQLITE_IOERR_READ"},
94    {5642,  "SQLITE_IOERR_SEEK"},
95    {5130,  "SQLITE_IOERR_SHMLOCK"},
96    {5386,  "SQLITE_IOERR_SHMMAP"},
97    {4618,  "SQLITE_IOERR_SHMOPEN"},
98    {4874,  "SQLITE_IOERR_SHMSIZE"},
99    {522,   "SQLITE_IOERR_SHORT_READ"},
100    {1546,  "SQLITE_IOERR_TRUNCATE"},
101    {2058,  "SQLITE_IOERR_UNLOCK"},
102    {778,   "SQLITE_IOERR_WRITE"},
103    {262,   "SQLITE_LOCKED_SHAREDCACHE"},
104    {539,   "SQLITE_NOTICE_RECOVER_ROLLBACK"},
105    {283,   "SQLITE_NOTICE_RECOVER_WAL"},
106    {256,   "SQLITE_OK_LOAD_PERMANENTLY"},
107    {520,   "SQLITE_READONLY_CANTLOCK"},
108    {1032,  "SQLITE_READONLY_DBMOVED"},
109    {264,   "SQLITE_READONLY_RECOVERY"},
110    {776,   "SQLITE_READONLY_ROLLBACK"},
111    {284,   "SQLITE_WARNING_AUTOINDEX"},
112};
113
114static std::string sqlite3_error_code_to_msg(int errcode) {
115    auto it = sErrorCodesMap.find(errcode);
116    if (it != sErrorCodesMap.end()) {
117        return std::to_string(errcode) + " " + it->second;
118    } else {
119        return std::to_string(errcode);
120    }
121}
122
123/* throw a SQLiteException with a message appropriate for the error in handle */
124void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
125    throw_sqlite3_exception(env, handle, NULL);
126}
127
128/* throw a SQLiteException with the given message */
129void throw_sqlite3_exception(JNIEnv* env, const char* message) {
130    throw_sqlite3_exception(env, NULL, message);
131}
132
133/* throw a SQLiteException with a message appropriate for the error in handle
134   concatenated with the given message
135 */
136void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
137    if (handle) {
138        // get the error code and message from the SQLite connection
139        // the error message may contain more information than the error code
140        // because it is based on the extended error code rather than the simplified
141        // error code that SQLite normally returns.
142        throw_sqlite3_exception(env, sqlite3_extended_errcode(handle),
143                sqlite3_errmsg(handle), message);
144    } else {
145        // we use SQLITE_OK so that a generic SQLiteException is thrown;
146        // any code not specified in the switch statement below would do.
147        throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
148    }
149}
150
151/* throw a SQLiteException for a given error code
152 * should only be used when the database connection is not available because the
153 * error information will not be quite as rich */
154void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
155    throw_sqlite3_exception(env, errcode, "unknown error", message);
156}
157
158/* throw a SQLiteException for a given error code, sqlite3message, and
159   user message
160 */
161void throw_sqlite3_exception(JNIEnv* env, int errcode,
162                             const char* sqlite3Message, const char* message) {
163    const char* exceptionClass;
164    switch (errcode & 0xff) { /* mask off extended error code */
165        case SQLITE_IOERR:
166            exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
167            break;
168        case SQLITE_CORRUPT:
169        case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
170            exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
171            break;
172        case SQLITE_CONSTRAINT:
173            exceptionClass = "android/database/sqlite/SQLiteConstraintException";
174            break;
175        case SQLITE_ABORT:
176            exceptionClass = "android/database/sqlite/SQLiteAbortException";
177            break;
178        case SQLITE_DONE:
179            exceptionClass = "android/database/sqlite/SQLiteDoneException";
180            sqlite3Message = NULL; // SQLite error message is irrelevant in this case
181            break;
182        case SQLITE_FULL:
183            exceptionClass = "android/database/sqlite/SQLiteFullException";
184            break;
185        case SQLITE_MISUSE:
186            exceptionClass = "android/database/sqlite/SQLiteMisuseException";
187            break;
188        case SQLITE_PERM:
189            exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
190            break;
191        case SQLITE_BUSY:
192            exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
193            break;
194        case SQLITE_LOCKED:
195            exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
196            break;
197        case SQLITE_READONLY:
198            exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
199            break;
200        case SQLITE_CANTOPEN:
201            exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
202            break;
203        case SQLITE_TOOBIG:
204            exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
205            break;
206        case SQLITE_RANGE:
207            exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
208            break;
209        case SQLITE_NOMEM:
210            exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
211            break;
212        case SQLITE_MISMATCH:
213            exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
214            break;
215        case SQLITE_INTERRUPT:
216            exceptionClass = "android/os/OperationCanceledException";
217            break;
218        default:
219            exceptionClass = "android/database/sqlite/SQLiteException";
220            break;
221    }
222
223    if (sqlite3Message) {
224        String8 fullMessage;
225        fullMessage.append(sqlite3Message);
226        std::string errcode_msg = sqlite3_error_code_to_msg(errcode);
227        fullMessage.appendFormat(" (code %s)", errcode_msg.c_str()); // print extended error code
228        if (message) {
229            fullMessage.append(": ");
230            fullMessage.append(message);
231        }
232        jniThrowException(env, exceptionClass, fullMessage.string());
233    } else {
234        jniThrowException(env, exceptionClass, message);
235    }
236}
237
238
239} // namespace android
240