/*
|
* Copyright (C) 2011 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
#include "android_database_SQLiteCommon.h"
|
|
#include <utils/String8.h>
|
|
#include <map>
|
|
namespace android {
|
|
static const std::map<int, std::string> sErrorCodesMap = {
|
// Primary Result Code List
|
{4, "SQLITE_ABORT"},
|
{23, "SQLITE_AUTH"},
|
{5, "SQLITE_BUSY"},
|
{14, "SQLITE_CANTOPEN"},
|
{19, "SQLITE_CONSTRAINT"},
|
{11, "SQLITE_CORRUPT"},
|
{101, "SQLITE_DONE"},
|
{16, "SQLITE_EMPTY"},
|
{1, "SQLITE_ERROR"},
|
{24, "SQLITE_FORMAT"},
|
{13, "SQLITE_FULL"},
|
{2, "SQLITE_INTERNAL"},
|
{9, "SQLITE_INTERRUPT"},
|
{10, "SQLITE_IOERR"},
|
{6, "SQLITE_LOCKED"},
|
{20, "SQLITE_MISMATCH"},
|
{21, "SQLITE_MISUSE"},
|
{22, "SQLITE_NOLFS"},
|
{7, "SQLITE_NOMEM"},
|
{26, "SQLITE_NOTADB"},
|
{12, "SQLITE_NOTFOUND"},
|
{27, "SQLITE_NOTICE"},
|
{0, "SQLITE_OK"},
|
{3, "SQLITE_PERM"},
|
{15, "SQLITE_PROTOCOL"},
|
{25, "SQLITE_RANGE"},
|
{8, "SQLITE_READONLY"},
|
{100, "SQLITE_ROW"},
|
{17, "SQLITE_SCHEMA"},
|
{18, "SQLITE_TOOBIG"},
|
{28, "SQLITE_WARNING"},
|
// Extended Result Code List
|
{516, "SQLITE_ABORT_ROLLBACK"},
|
{261, "SQLITE_BUSY_RECOVERY"},
|
{517, "SQLITE_BUSY_SNAPSHOT"},
|
{1038, "SQLITE_CANTOPEN_CONVPATH"},
|
{782, "SQLITE_CANTOPEN_FULLPATH"},
|
{526, "SQLITE_CANTOPEN_ISDIR"},
|
{270, "SQLITE_CANTOPEN_NOTEMPDIR"},
|
{275, "SQLITE_CONSTRAINT_CHECK"},
|
{531, "SQLITE_CONSTRAINT_COMMITHOOK"},
|
{787, "SQLITE_CONSTRAINT_FOREIGNKEY"},
|
{1043, "SQLITE_CONSTRAINT_FUNCTION"},
|
{1299, "SQLITE_CONSTRAINT_NOTNULL"},
|
{1555, "SQLITE_CONSTRAINT_PRIMARYKEY"},
|
{2579, "SQLITE_CONSTRAINT_ROWID"},
|
{1811, "SQLITE_CONSTRAINT_TRIGGER"},
|
{2067, "SQLITE_CONSTRAINT_UNIQUE"},
|
{2323, "SQLITE_CONSTRAINT_VTAB"},
|
{267, "SQLITE_CORRUPT_VTAB"},
|
{3338, "SQLITE_IOERR_ACCESS"},
|
{2826, "SQLITE_IOERR_BLOCKED"},
|
{3594, "SQLITE_IOERR_CHECKRESERVEDLOCK"},
|
{4106, "SQLITE_IOERR_CLOSE"},
|
{6666, "SQLITE_IOERR_CONVPATH"},
|
{2570, "SQLITE_IOERR_DELETE"},
|
{5898, "SQLITE_IOERR_DELETE_NOENT"},
|
{4362, "SQLITE_IOERR_DIR_CLOSE"},
|
{1290, "SQLITE_IOERR_DIR_FSYNC"},
|
{1802, "SQLITE_IOERR_FSTAT"},
|
{1034, "SQLITE_IOERR_FSYNC"},
|
{6410, "SQLITE_IOERR_GETTEMPPATH"},
|
{3850, "SQLITE_IOERR_LOCK"},
|
{6154, "SQLITE_IOERR_MMAP"},
|
{3082, "SQLITE_IOERR_NOMEM"},
|
{2314, "SQLITE_IOERR_RDLOCK"},
|
{266, "SQLITE_IOERR_READ"},
|
{5642, "SQLITE_IOERR_SEEK"},
|
{5130, "SQLITE_IOERR_SHMLOCK"},
|
{5386, "SQLITE_IOERR_SHMMAP"},
|
{4618, "SQLITE_IOERR_SHMOPEN"},
|
{4874, "SQLITE_IOERR_SHMSIZE"},
|
{522, "SQLITE_IOERR_SHORT_READ"},
|
{1546, "SQLITE_IOERR_TRUNCATE"},
|
{2058, "SQLITE_IOERR_UNLOCK"},
|
{778, "SQLITE_IOERR_WRITE"},
|
{262, "SQLITE_LOCKED_SHAREDCACHE"},
|
{539, "SQLITE_NOTICE_RECOVER_ROLLBACK"},
|
{283, "SQLITE_NOTICE_RECOVER_WAL"},
|
{256, "SQLITE_OK_LOAD_PERMANENTLY"},
|
{520, "SQLITE_READONLY_CANTLOCK"},
|
{1032, "SQLITE_READONLY_DBMOVED"},
|
{264, "SQLITE_READONLY_RECOVERY"},
|
{776, "SQLITE_READONLY_ROLLBACK"},
|
{284, "SQLITE_WARNING_AUTOINDEX"},
|
};
|
|
static std::string sqlite3_error_code_to_msg(int errcode) {
|
auto it = sErrorCodesMap.find(errcode);
|
if (it != sErrorCodesMap.end()) {
|
return std::to_string(errcode) + " " + it->second;
|
} else {
|
return std::to_string(errcode);
|
}
|
}
|
|
/* throw a SQLiteException with a message appropriate for the error in handle */
|
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
|
throw_sqlite3_exception(env, handle, NULL);
|
}
|
|
/* throw a SQLiteException with the given message */
|
void throw_sqlite3_exception(JNIEnv* env, const char* message) {
|
throw_sqlite3_exception(env, NULL, message);
|
}
|
|
/* throw a SQLiteException with a message appropriate for the error in handle
|
concatenated with the given message
|
*/
|
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
|
if (handle) {
|
// get the error code and message from the SQLite connection
|
// the error message may contain more information than the error code
|
// because it is based on the extended error code rather than the simplified
|
// error code that SQLite normally returns.
|
throw_sqlite3_exception(env, sqlite3_extended_errcode(handle),
|
sqlite3_errmsg(handle), message);
|
} else {
|
// we use SQLITE_OK so that a generic SQLiteException is thrown;
|
// any code not specified in the switch statement below would do.
|
throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
|
}
|
}
|
|
/* throw a SQLiteException for a given error code
|
* should only be used when the database connection is not available because the
|
* error information will not be quite as rich */
|
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
|
throw_sqlite3_exception(env, errcode, "unknown error", message);
|
}
|
|
/* throw a SQLiteException for a given error code, sqlite3message, and
|
user message
|
*/
|
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
const char* sqlite3Message, const char* message) {
|
const char* exceptionClass;
|
switch (errcode & 0xff) { /* mask off extended error code */
|
case SQLITE_IOERR:
|
exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
|
break;
|
case SQLITE_CORRUPT:
|
case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
|
exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
|
break;
|
case SQLITE_CONSTRAINT:
|
exceptionClass = "android/database/sqlite/SQLiteConstraintException";
|
break;
|
case SQLITE_ABORT:
|
exceptionClass = "android/database/sqlite/SQLiteAbortException";
|
break;
|
case SQLITE_DONE:
|
exceptionClass = "android/database/sqlite/SQLiteDoneException";
|
sqlite3Message = NULL; // SQLite error message is irrelevant in this case
|
break;
|
case SQLITE_FULL:
|
exceptionClass = "android/database/sqlite/SQLiteFullException";
|
break;
|
case SQLITE_MISUSE:
|
exceptionClass = "android/database/sqlite/SQLiteMisuseException";
|
break;
|
case SQLITE_PERM:
|
exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
|
break;
|
case SQLITE_BUSY:
|
exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
|
break;
|
case SQLITE_LOCKED:
|
exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
|
break;
|
case SQLITE_READONLY:
|
exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
|
break;
|
case SQLITE_CANTOPEN:
|
exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
|
break;
|
case SQLITE_TOOBIG:
|
exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
|
break;
|
case SQLITE_RANGE:
|
exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
|
break;
|
case SQLITE_NOMEM:
|
exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
|
break;
|
case SQLITE_MISMATCH:
|
exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
|
break;
|
case SQLITE_INTERRUPT:
|
exceptionClass = "android/os/OperationCanceledException";
|
break;
|
default:
|
exceptionClass = "android/database/sqlite/SQLiteException";
|
break;
|
}
|
|
if (sqlite3Message) {
|
String8 fullMessage;
|
fullMessage.append(sqlite3Message);
|
std::string errcode_msg = sqlite3_error_code_to_msg(errcode);
|
fullMessage.appendFormat(" (code %s)", errcode_msg.c_str()); // print extended error code
|
if (message) {
|
fullMessage.append(": ");
|
fullMessage.append(message);
|
}
|
jniThrowException(env, exceptionClass, fullMessage.string());
|
} else {
|
jniThrowException(env, exceptionClass, message);
|
}
|
}
|
|
|
} // namespace android
|