错误处理
The language of WCDB
Cpp
The version of WCDB
v2.0.4
The platform of WCDB
macOS
The installation of WCDB
Git clone
What's the issue?
- 通过
database.prepareInsert执行数据插入,发生错误时比如非空字段插入了空,通过database.getError()或者insert.getError()拿到的错误都是OK - 示例代码
// message_schema_hpp
#include <WCDBCpp/WCDBCpp.h>
#include <string>
struct MessageSchema {
MessageSchema() {}
~MessageSchema() {}
std::uint64_t messageId;
std::string remoteMessageId;
std::uint64_t timestamp;
std::uint64_t deliveredTimestamp;
std::uint64_t readTimestamp;
std::string conversationId;
std::int8_t conversationType;
std::string senderUid;
std::int8_t status;
std::int8_t isSelf;
std::int16_t contentType;
WCDB::Data contentData{WCDB::Data::null()};
WCDB::Data localCustomData{WCDB::Data::null()};
std::int32_t localCustomInt;
WCDB::Data cloudCustomData{WCDB::Data::null()};
std::string searchable;
constexpr static const char tableName[] = "t_message";
WCDB_CPP_ORM_DECLARATION(MessageSchema);
};
// message_schema.cpp
#include "message_schema.hpp"
WCDB_CPP_ORM_IMPLEMENTATION_BEGIN(MessageSchema)
WCDB_CPP_SYNTHESIZE_COLUMN(messageId, "message_id")
WCDB_CPP_SYNTHESIZE_COLUMN(remoteMessageId, "remote_message_id")
WCDB_CPP_SYNTHESIZE(timestamp)
WCDB_CPP_SYNTHESIZE_COLUMN(deliveredTimestamp, "delivered_timestamp")
WCDB_CPP_SYNTHESIZE_COLUMN(readTimestamp, "read_timestamp")
WCDB_CPP_SYNTHESIZE_COLUMN(conversationId, "conversation_id")
WCDB_CPP_SYNTHESIZE_COLUMN(conversationType, "conversation_type")
WCDB_CPP_SYNTHESIZE_COLUMN(senderUid, "sender_uid")
WCDB_CPP_SYNTHESIZE(status)
WCDB_CPP_SYNTHESIZE_COLUMN(isSelf, "is_self")
WCDB_CPP_SYNTHESIZE_COLUMN(contentType, "content_type")
WCDB_CPP_SYNTHESIZE_COLUMN(contentData, "content_data")
WCDB_CPP_SYNTHESIZE_COLUMN(localCustomData, "local_custom_data")
WCDB_CPP_SYNTHESIZE_COLUMN(localCustomInt, "local_custom_int")
WCDB_CPP_SYNTHESIZE_COLUMN(cloudCustomData, "cloud_custom_data")
WCDB_CPP_SYNTHESIZE(searchable)
WCDB_CPP_DEFAULT(deliveredTimestamp, 0);
WCDB_CPP_DEFAULT(readTimestamp, 0);
WCDB_CPP_PRIMARY_ASC_AUTO_INCREMENT(messageId)
WCDB_CPP_NOT_NULL(timestamp)
WCDB_CPP_NOT_NULL(conversationId)
WCDB_CPP_NOT_NULL(conversationType)
WCDB_CPP_NOT_NULL(senderUid)
WCDB_CPP_NOT_NULL(status)
// 组合唯一索引
WCDB_CPP_UNIQUE_INDEX("_uniqueIndex", conversationType)
WCDB_CPP_UNIQUE_INDEX("_uniqueIndex", conversationId)
WCDB_CPP_UNIQUE_INDEX("_uniqueIndex", timestamp)
WCDB_CPP_UNIQUE_INDEX("_uniqueIndex", senderUid)
WCDB_CPP_UNIQUE_INDEX("_uniqueIndex", remoteMessageId)
WCDB_CPP_INDEX("_timestampIndex", timestamp)
WCDB_CPP_INDEX("_remoteMessageIdIndex", remoteMessageId)
WCDB_CPP_INDEX("_senderUidIndex", senderUid)
WCDB_CPP_INDEX("_statusIndex", status)
WCDB_CPP_ORM_IMPLEMENTATION_END
// main.cpp
#include <chrono>
#include <iostream>
#include "message_schema.hpp"
std::uint64_t now_ms() {
auto duration = std::chrono::system_clock::now().time_since_epoch();
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(duration).count());
}
int main(int argc, const char *argv[]) {
std::string db_path = DB_FILE_PATH;
WCDB::Database database(db_path);
database.traceSQL([](long tag,
const WCDB::UnsafeStringView &path,
const WCDB::UnsafeStringView &sql,
const void *handleIdentifier) {
std::cerr << "sql: " << sql.data() << std::endl;
});
thread_local WCDB::Error lastError;
database.traceError([&](const WCDB::Error &error) {
lastError = error;
std::cerr << "error: " << error.getDescription().data() << std::endl;
});
database.createTable<MessageSchema>(MessageSchema::tableName);
MessageSchema object;
object.isAutoIncrement = true;
object.conversationType = 1;
object.conversationId = "1231313";
object.timestamp = 1697776099925; //now_ms();
object.senderUid = "41050";
object.remoteMessageId = "jfhafjakfjafk";
// 这种方式获取的Error 是OK
auto insert = database.prepareInsert<MessageSchema>()
.intoTable(MessageSchema::tableName)
.onFields({WCDB_FIELD(MessageSchema::conversationType), WCDB_FIELD(MessageSchema::conversationId), WCDB_FIELD(MessageSchema::timestamp), WCDB_FIELD(MessageSchema::senderUid), WCDB_FIELD(MessageSchema::remoteMessageId)})
.values(object);
bool res = insert.execute();
auto error = database.getError();
// 这种方式获取的Error 是符合预期的
// auto insert = WCDB::StatementInsert().insertIntoTable(MessageSchema::tableName).columns({WCDB_FIELD(MessageSchema::conversationType), WCDB_FIELD(MessageSchema::conversationId), WCDB_FIELD(MessageSchema::timestamp), WCDB_FIELD(MessageSchema::senderUid), WCDB_FIELD(MessageSchema::remoteMessageId)}).values({object.conversationType, object.conversationId, object.timestamp, object.senderUid, object.remoteMessageId});
// auto res = database.execute(insert);
// auto error = database.getError();
std::cerr << "error code: " << (int)error.code() << " ext_code: " << (int)error.getExtCode() << std::endl;
return 0;
}
Get
getError is actually quite cumbersome to use. We recommend using static Database::globalTraceError() or Database::traceError()
@Qiuwen-chen
I think the design of static Database::globalTraceError() or Database::traceError() is more geared towards error log statistics.
The actual business scenario requires different processing based on the current specific error type.
For example, when inserting non-null constraint data, the user will be clearly informed of a more specific error instead of simply telling the user that the insertion failed.
traceError is called back synchronously when an error occurs.
You can output the error returned by traceError to a log file or console, and then output data-related logs at the location where the API is called. Then you can easily associate the error with the operation.
This is how we use it now.
In this way, you won't miss any database errors.
@Qiuwen-chen
I understand what you mean, and this is really convenient for logging errors.
But as I said above, in some scenarios it is inconvenient to get the specific error information of the current execution. At this time, it is very convenient to use getError, and the corresponding code is also more readable.
I read the source code. The callback of Database::traceError() is executed before the current call returns. This way, the error can be captured in the callback and then used after the call returns. However, this code is very bad.
In most cases, you cannot handle errors at runtime. If you are expecting a not null constraint conflict, you can directly detect whether the value is null before the Insert operation, instead of handling it through a database error, which is less efficient.
The error mechanism is mainly used to deal with unexpected situations.
Fixed in the latest version.