Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 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 | |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 19 | #include <utils/String8.h> |
| 20 | |
Fyodor Kupolov | 6e98f46 | 2018-01-19 12:45:11 -0800 | [diff] [blame] | 21 | #include <map> |
| 22 | |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 23 | namespace android { |
| 24 | |
Fyodor Kupolov | 6e98f46 | 2018-01-19 12:45:11 -0800 | [diff] [blame] | 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 | |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 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) { |
Jeff Brown | 9d25fa6 | 2012-06-05 14:29:02 -0700 | [diff] [blame] | 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. |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 142 | throw_sqlite3_exception(env, sqlite3_extended_errcode(handle), |
| 143 | sqlite3_errmsg(handle), message); |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 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 | |
Jeff Brown | 9d25fa6 | 2012-06-05 14:29:02 -0700 | [diff] [blame] | 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 */ |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 154 | void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) { |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 155 | throw_sqlite3_exception(env, errcode, "unknown error", message); |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 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; |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 164 | switch (errcode & 0xff) { /* mask off extended error code */ |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 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: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 173 | exceptionClass = "android/database/sqlite/SQLiteConstraintException"; |
| 174 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 175 | case SQLITE_ABORT: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 176 | exceptionClass = "android/database/sqlite/SQLiteAbortException"; |
| 177 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 178 | case SQLITE_DONE: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 179 | exceptionClass = "android/database/sqlite/SQLiteDoneException"; |
Jeff Brown | 9d25fa6 | 2012-06-05 14:29:02 -0700 | [diff] [blame] | 180 | sqlite3Message = NULL; // SQLite error message is irrelevant in this case |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 181 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 182 | case SQLITE_FULL: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 183 | exceptionClass = "android/database/sqlite/SQLiteFullException"; |
| 184 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 185 | case SQLITE_MISUSE: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 186 | exceptionClass = "android/database/sqlite/SQLiteMisuseException"; |
| 187 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 188 | case SQLITE_PERM: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 189 | exceptionClass = "android/database/sqlite/SQLiteAccessPermException"; |
| 190 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 191 | case SQLITE_BUSY: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 192 | exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException"; |
| 193 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 194 | case SQLITE_LOCKED: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 195 | exceptionClass = "android/database/sqlite/SQLiteTableLockedException"; |
| 196 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 197 | case SQLITE_READONLY: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 198 | exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException"; |
| 199 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 200 | case SQLITE_CANTOPEN: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 201 | exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException"; |
| 202 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 203 | case SQLITE_TOOBIG: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 204 | exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException"; |
| 205 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 206 | case SQLITE_RANGE: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 207 | exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; |
| 208 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 209 | case SQLITE_NOMEM: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 210 | exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException"; |
| 211 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 212 | case SQLITE_MISMATCH: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 213 | exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException"; |
| 214 | break; |
| 215 | case SQLITE_INTERRUPT: |
Jeff Brown | 25c9345 | 2012-05-09 18:04:34 -0700 | [diff] [blame] | 216 | exceptionClass = "android/os/OperationCanceledException"; |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 217 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 218 | default: |
Jeff Brown | 75ea64f | 2012-01-25 19:37:13 -0800 | [diff] [blame] | 219 | exceptionClass = "android/database/sqlite/SQLiteException"; |
| 220 | break; |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 221 | } |
| 222 | |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 223 | if (sqlite3Message) { |
| 224 | String8 fullMessage; |
| 225 | fullMessage.append(sqlite3Message); |
Andreas Gampe | f02798b | 2018-01-22 17:55:17 -0800 | [diff] [blame] | 226 | std::string errcode_msg = sqlite3_error_code_to_msg(errcode); |
| 227 | fullMessage.appendFormat(" (code %s)", errcode_msg.c_str()); // print extended error code |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 228 | if (message) { |
| 229 | fullMessage.append(": "); |
| 230 | fullMessage.append(message); |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 231 | } |
Jeff Brown | ca309f2 | 2012-06-12 15:39:09 -0700 | [diff] [blame] | 232 | jniThrowException(env, exceptionClass, fullMessage.string()); |
Jeff Brown | e5360fb | 2011-10-31 17:48:13 -0700 | [diff] [blame] | 233 | } else { |
| 234 | jniThrowException(env, exceptionClass, message); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | |
| 239 | } // namespace android |