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 
23 namespace android {
24 
25 static 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 
sqlite3_error_code_to_msg(int errcode)114 static 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 */
throw_sqlite3_exception(JNIEnv * env,sqlite3 * handle)124 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
125     throw_sqlite3_exception(env, handle, NULL);
126 }
127 
128 /* throw a SQLiteException with the given message */
throw_sqlite3_exception(JNIEnv * env,const char * message)129 void 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  */
throw_sqlite3_exception(JNIEnv * env,sqlite3 * handle,const char * message)136 void 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 */
throw_sqlite3_exception_errcode(JNIEnv * env,int errcode,const char * message)154 void 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  */
throw_sqlite3_exception(JNIEnv * env,int errcode,const char * sqlite3Message,const char * message)161 void 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