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 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 */ 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 */ 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 */ 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 */ 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 */ 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