Refactor project structure

This commit is contained in:
2026-03-28 19:56:39 +11:00
parent 1d267378b2
commit 8a2e721058
511 changed files with 59 additions and 48 deletions

View File

@@ -0,0 +1,909 @@
#include "FaceDatabase.h"
namespace ANSCENTER {
FaceDatabase::FaceDatabase() {
m_db = nullptr;
m_embedding_dim = 0;
}
bool FaceDatabase::Initialize(std::string path, int embedding_dim, std::string dataFolder) {
std::lock_guard<std::mutex> lock(_mutex);
try {
sqlite3_stmt* stmt;
_dataFolder = dataFolder;
m_embedding_dim = embedding_dim;
std::string sql;
int rc;
int isNotEmpty = 0;
//this->_logger.LogDebug("FaceDatabase::Initialize. Database path: ", path, __FILE__, __LINE__);
std::string faceDatabaseFolder = GetParentFolder(path);
if (!FolderExist(faceDatabaseFolder)) fs::create_directory(faceDatabaseFolder);
std::string imageDataFolder = CreateFilePath(faceDatabaseFolder, "data");
if (!FolderExist(imageDataFolder)) fs::create_directory(imageDataFolder);
rc = sqlite3_open(path.c_str(), &m_db);
if (rc) {
_logger.LogError("FaceDatabase::Initialize", std::string("Can't open database: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
else {
_logger.LogDebug("FaceDatabase::Initialize", "Opened database successfully", __FILE__, __LINE__);
}
// Fix #2: Enable WAL mode for concurrent read/write from separate processes
sqlite3_exec(m_db, "PRAGMA journal_mode=WAL;", nullptr, nullptr, nullptr);
// Fix #2: 5-second busy timeout for multi-process contention
sqlite3_exec(m_db, "PRAGMA busy_timeout=5000;", nullptr, nullptr, nullptr);
// Fix #2: Enable foreign key enforcement
sqlite3_exec(m_db, "PRAGMA foreign_keys=ON;", nullptr, nullptr, nullptr);
// Prepare
sql = "SELECT name FROM sqlite_master;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::Initialize. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
// Step
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
isNotEmpty = 1;
break;
}
sqlite3_finalize(stmt);
if (isNotEmpty) {
//this->_logger.LogDebug("FaceDatabase::Initialize.", "Database is not empty", __FILE__, __LINE__);
}
else {
//this->_logger.LogDebug("FaceDatabase::Initialize.", "Empty Database, initiating...", __FILE__, __LINE__);
// Prepare
sql = "CREATE TABLE IF NOT EXISTS USER ( \
ID INTEGER PRIMARY KEY AUTOINCREMENT, \
CODE TEXT, \
NAME TEXT, \
UNIQUE(ID, CODE)\
)";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::Initialize. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
// this->_logger.LogError("FaceDatabase::Initialize. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
else {
// this->_logger.LogDebug("FaceDatabase::Initialize.", "Table `USER` created successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
// Prepare
sql = "CREATE TABLE IF NOT EXISTS FACE ( \
ID INTEGER PRIMARY KEY AUTOINCREMENT, \
USER INTEGER, \
IMG_PATH TEXT, \
EMBEDDING BLOB, \
UNIQUE(ID, USER), \
FOREIGN KEY(USER) REFERENCES USER(ID) \
)";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::Initialize. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
// this->_logger.LogError("FaceDatabase::Initialize. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
else {
// this->_logger.LogDebug("FaceDatabase::Initialize", "Table `FACE` created successfully.", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
}
return true;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::Initialize", e.what(), __FILE__, __LINE__);
//sqlite3_finalize(stmt);
return false;
}
}
int FaceDatabase::InsertUser(std::string userId, std::string userName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
// Prepare
sql = "INSERT INTO USER (CODE, NAME) VALUES (?, ?);";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
return -2;
}
// Binding
rc = sqlite3_bind_text(stmt, 1, userId.c_str(), int(userId.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
rc = sqlite3_bind_text(stmt, 2, userName.c_str(), int(userName.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -4;
}
if (rc == SQLITE_DONE) {
// this->_logger.LogDebug("FaceDatabase::InsertUser:", "Record successfully created", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
int id = (int)sqlite3_last_insert_rowid(m_db);
resultCode = id;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::InsertUser", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::UpdateUser(int userId, std::string newUserCode, std::string newUserName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int resultCode = 0;
int rc;
// Prepare
// UPDATE USER SET CODE ="0003", NAME= "Biden" WHERE ID =1
sql = "UPDATE USER SET CODE=?, NAME=? WHERE ID=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
return -1;
}
// Binding
rc = sqlite3_bind_text(stmt, 1, newUserCode.c_str(), int(newUserCode.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
rc = sqlite3_bind_text(stmt, 2, newUserName.c_str(), int(newUserName.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
rc = sqlite3_bind_int(stmt, 3, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::InsertUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_DONE) {
// this->_logger.LogDebug("FaceDatabase::InsertUser:", "Record successfully created", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::InsertUser", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
int FaceDatabase::InsertFace(int userId, std::string imgPath, float embedding[]) {
std::lock_guard<std::mutex> lock(_mutex);
if (!FileExist(imgPath)) return 0;
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
// Prepare
sql = "INSERT INTO FACE (USER, IMG_PATH, EMBEDDING) VALUES (?, ?, ?);";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//std::cout << "SQL error: " << sqlite3_errmsg(m_db);
return -2;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertFace. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
rc = sqlite3_bind_text(stmt, 2, imgPath.c_str(), int(imgPath.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::InsertFace. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
rc = sqlite3_bind_blob(stmt, 3, embedding, sizeof(float) * m_embedding_dim, SQLITE_STATIC);
if (rc != SQLITE_OK) {
//::cout << "SQL bind error: " << sqlite3_errmsg(m_db);
resultCode = -3;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::InsertFace. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -4;
}
if (rc == SQLITE_DONE) {
//this->_logger.LogDebug("FaceDatabase::InsertFace.", "Embedding for user created successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
int id = (int)sqlite3_last_insert_rowid(m_db);
resultCode = id;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::InsertFace", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::DeleteFace(int id) {
try {
// Fix #4: Single lock scope — GetFaceById + DELETE in one atomic section
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int userId;
int resultCode = 0;
std::string faceImagePath;
// Look up face image path (now inside lock — no TOCTOU race)
{
sqlite3_stmt* lookupStmt;
rc = sqlite3_prepare_v2(m_db, "SELECT USER, IMG_PATH FROM FACE WHERE ID=?;", -1, &lookupStmt, NULL);
if (rc == SQLITE_OK) {
sqlite3_bind_int(lookupStmt, 1, id);
if (sqlite3_step(lookupStmt) == SQLITE_ROW) {
userId = sqlite3_column_int(lookupStmt, 0);
const char* pathText = reinterpret_cast<const char*>(sqlite3_column_text(lookupStmt, 1));
faceImagePath = pathText ? std::string(pathText) : "";
}
}
sqlite3_finalize(lookupStmt);
}
// Delete image file
if (!faceImagePath.empty() && FileExist(faceImagePath)) {
if (!fs::remove(faceImagePath)) {
_logger.LogError("FaceDatabase::DeleteFace", "Failed to delete face image: " + faceImagePath, __FILE__, __LINE__);
}
}
//_mutex.lock();
// Prepare
sql = "DELETE FROM FACE WHERE ID=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteFace. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, id);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteFace. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::DeleteFace. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_DONE) {
// this->_logger.LogDebug("FaceDatabase::DeleteFace.", "Embedding for user deleted successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::DeleteFace", e.what(), __FILE__, __LINE__);
//_mutex.unlock();
return 0;
}
}
int FaceDatabase::DeleteUser(int userId) {
try {
std::string sql;
sqlite3_stmt* stmt;
int resultCode = 0;
int rc;
std::vector<int> faceIds;
faceIds.clear();
GetFaceIdsByUser(userId, faceIds);
int numOfFaces = (int)faceIds.size();
//this->_logger.LogDebug("FaceDatabase::DeleteUser. Number of Face for this user", std::to_string(numOfFaces), __FILE__, __LINE__);
if (numOfFaces > 0) {
for (int i = 0; i < numOfFaces; i++) {
if (!DeleteFace(faceIds[i])) {
//this->_logger.LogError("FaceDatabase::DeleteUser. Failed to delete face id", std::to_string(numOfFaces), __FILE__, __LINE__);
}
}
}
std::string userImageFolderName = "User" + std::to_string(userId);
std::string userImageFolder = CreateFilePath(_dataFolder, userImageFolderName);
if (FolderExist(userImageFolder)) {
if (!DeleteFolder(userImageFolder)) {
//this->_logger.LogError("FaceDatabase::DeleteUser. Failed to delete user data folder", userImageFolder, __FILE__, __LINE__);
}
}
// Delete person
std::lock_guard<std::mutex> lock(_mutex);
//_mutex.lock();
// Prepare
sql = "DELETE FROM USER WHERE ID=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::DeleteUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::DeleteUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_DONE) {
//this->_logger.LogDebug("FaceDatabase::DeleteUser.", "Deleted person successfully", __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::DeleteUser", e.what(), __FILE__, __LINE__);
return 0;
}
}
int FaceDatabase::GetUserId(std::string code) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
// Prepare
sql = "SELECT ID FROM USER WHERE CODE =?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
int resultCode = 0;
int userId = -1;
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_text(stmt, 1, code.c_str(), int(code.length()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_ROW) {
//this->_logger.LogDebug("FaceDatabase::GetUser.", "Get User information successfully", __FILE__, __LINE__);
userId = sqlite3_column_int(stmt, 0);
}
sqlite3_finalize(stmt);
resultCode = userId;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUserId", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::GetUser(int userId, std::string& code, std::string& userName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int resultCode = 0;
userName = "";
code = "";
int rc;
// Prepare
sql = "SELECT * FROM USER WHERE ID =?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL bind error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUser. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -3;
}
if (rc == SQLITE_ROW) {
//this->_logger.LogDebug("FaceDatabase::GetUser.", "Get User information successfully", __FILE__, __LINE__);
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
code = codeText ? std::string(codeText) : "";
userName = nameText ? std::string(nameText) : "";
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUserId", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return -1;
}
}
int FaceDatabase::GetFaceIdsByUser(int userId, std::vector<int>& faceIds)
{
std::lock_guard<std::mutex> lock(_mutex);
faceIds.clear();
std::string sql;
sqlite3_stmt* stmt;
int rc;
sql = "SELECT ID FROM FACE WHERE USER=?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
int resultCode = 0;
int faceId;
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceIdsByUser", std::string("SQL error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, userId);
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceIdsByUser", std::string("SQL bind error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
faceId = sqlite3_column_int(stmt, 0);
faceIds.push_back(faceId);
}
if (rc != SQLITE_DONE) {
_logger.LogError("FaceDatabase::GetFaceIdsByUser", std::string("SQL step error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetFaceIdsByUser", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
int FaceDatabase::GetUsers(std::vector<int>& ids, std::vector<std::string>& codes, std::vector<std::string>& userNames) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int userId;
std::string userCode;
std::string userName;
int resutCode = 0;
ids.clear();
codes.clear();
userNames.clear();
sql = "SELECT * FROM USER;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUsers. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resutCode = -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
userId = sqlite3_column_int(stmt, 0);
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
userCode = codeText ? std::string(codeText) : "";
userName = nameText ? std::string(nameText) : "";
ids.push_back(userId);
codes.push_back(userCode);
userNames.push_back(userName);
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUsers. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resutCode = 1;
return resutCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUsers", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
int FaceDatabase::GetFaceById(int faceId, int& userId, std::string& imagePath) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
userId = -1;
imagePath = "";
// SELECT *FROM FACE WHERE ID =1
sql = "SELECT * FROM FACE WHERE ID =?;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceById", std::string("SQL error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Binding
rc = sqlite3_bind_int(stmt, 1, faceId);
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetFaceById", std::string("SQL bind error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
userId = sqlite3_column_int(stmt, 1);
const char* pathText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
imagePath = pathText ? std::string(pathText) : "";
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetFaceIds. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 1;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetFaceIds", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return 0;
}
}
std::map<std::string, std::string> FaceDatabase::GetUserDict() {
std::lock_guard<std::mutex> lock(_mutex);
std::map<std::string, std::string> userDict;
userDict.clear();
std::string sql;
sqlite3_stmt* stmt;
int rc;
std::string userId;
std::string userCode;
std::string userName;
// get user id and name
sql = "SELECT * FROM USER;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
try {
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetUserDict. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* idText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
userId = idText ? std::string(idText) : "";
userCode = codeText ? std::string(codeText) : "";
userName = nameText ? std::string(nameText) : "";
if (!userCode.empty()) {
userDict[userId] = userName+"(" + userCode + ")";
}
else {
userDict[userId] = userName;
}
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetUserDict. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
return userDict;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetUserDict", e.what(), __FILE__, __LINE__);
sqlite3_finalize(stmt);
return userDict;
}
}
int FaceDatabase::GetNumEmbeddings() {
std::lock_guard<std::mutex> lock(_mutex);
try {
int numEmbeds = 0;
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
// Prepare
sql = "SELECT COUNT(*) FROM FACE;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetNumEmbeddings. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
// Step
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetNumEmbeddings. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -2;
}
numEmbeds = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
resultCode = numEmbeds;
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetNumEmbeddings", e.what(), __FILE__, __LINE__);
return 0;
}
}
int FaceDatabase::GetEmbeddings(std::unique_ptr<ANSFRBase>& recognizer) {
try {
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
std::string userId;
int numEmbeds = GetNumEmbeddings();
_logger.LogDebug("FaceDatabase::GetEmbeddings", "There are " + std::to_string(numEmbeds) + " embeddings in database", __FILE__, __LINE__);
// get embedding
//_mutex.lock();
std::lock_guard<std::mutex> lock(_mutex);
sql = "SELECT * FROM FACE;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* userIdText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
if (!userIdText) continue;
userId = std::string(userIdText);
float* embedding = (float*)sqlite3_column_blob(stmt, 3);
if (!embedding) continue;
recognizer->AddEmbedding(userId, embedding);
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 0;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetEmbeddings", e.what(), __FILE__, __LINE__);
//_mutex.unlock();
return 0;
}
}
int FaceDatabase::GetEmbeddings(ANSFRBase& recognizer) {
try {
std::string sql;
sqlite3_stmt* stmt;
int rc;
int resultCode = 0;
std::string userId;
int numEmbeds = GetNumEmbeddings();
_logger.LogDebug("FaceDatabase::GetEmbeddings", "There are " + std::to_string(numEmbeds) + " embeddings in database", __FILE__, __LINE__);
// get embedding
//_mutex.lock();
std::lock_guard<std::mutex> lock(_mutex);
sql = "SELECT * FROM FACE;";
rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, NULL);
if (rc != SQLITE_OK) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL step error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
resultCode = -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char* userIdText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
if (!userIdText) continue;
userId = std::string(userIdText);
float* embedding = (float*)sqlite3_column_blob(stmt, 3);
if (!embedding) continue;
recognizer.AddEmbedding(userId, embedding);
}
if (rc != SQLITE_DONE) {
//this->_logger.LogError("FaceDatabase::GetEmbeddings. SQL error:", sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
resultCode = 0;
//_mutex.unlock();
return resultCode;
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::GetEmbeddings", e.what(), __FILE__, __LINE__);
//_mutex.unlock();
return 0;
}
}
int FaceDatabase::GetAllEmbeddingRecords(std::vector<FaceEmbeddingRecord>& records) {
try {
std::lock_guard<std::mutex> lock(_mutex);
records.clear();
int numEmbeds = 0;
{
// Count for pre-allocation (inline, mutex already held)
sqlite3_stmt* countStmt;
int rc = sqlite3_prepare_v2(m_db, "SELECT COUNT(*) FROM FACE;", -1, &countStmt, NULL);
if (rc == SQLITE_OK && sqlite3_step(countStmt) == SQLITE_ROW) {
numEmbeds = sqlite3_column_int(countStmt, 0);
}
sqlite3_finalize(countStmt);
}
records.reserve(numEmbeds);
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(m_db,
"SELECT ID, USER, EMBEDDING FROM FACE;", -1, &stmt, NULL);
if (rc != SQLITE_OK) {
return -1;
}
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int faceId = sqlite3_column_int(stmt, 0);
const char* userIdText = reinterpret_cast<const char*>(
sqlite3_column_text(stmt, 1));
if (!userIdText) continue;
const float* embData = reinterpret_cast<const float*>(
sqlite3_column_blob(stmt, 2));
int embBytes = sqlite3_column_bytes(stmt, 2);
int embSize = embBytes / static_cast<int>(sizeof(float));
if (!embData || embSize <= 0) continue;
FaceEmbeddingRecord record;
record.faceId = faceId;
record.userId = std::string(userIdText);
record.embedding.assign(embData, embData + embSize);
records.push_back(std::move(record));
}
sqlite3_finalize(stmt);
return 0;
}
catch (const std::exception& e) {
return -1;
}
}
int FaceDatabase::GetUsersWithFaceIds(std::vector<int>& ids, std::vector<std::string>& codes,
std::vector<std::string>& userNames, std::vector<std::vector<int>>& faceIdsByUser) {
// Fix #12: Single JOIN query replaces N+1 GetUser + GetFaceIdsByUser calls
std::lock_guard<std::mutex> lock(_mutex);
ids.clear();
codes.clear();
userNames.clear();
faceIdsByUser.clear();
try {
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(m_db,
"SELECT U.ID, U.CODE, U.NAME, F.ID AS FACE_ID "
"FROM USER U LEFT JOIN FACE F ON U.ID = F.USER "
"ORDER BY U.ID;",
-1, &stmt, NULL);
if (rc != SQLITE_OK) {
_logger.LogError("FaceDatabase::GetUsersWithFaceIds",
std::string("SQL error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
return -1;
}
int lastUserId = -1;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
int userId = sqlite3_column_int(stmt, 0);
if (userId != lastUserId) {
// New user row
const char* codeText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
const char* nameText = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
ids.push_back(userId);
codes.push_back(codeText ? std::string(codeText) : "");
userNames.push_back(nameText ? std::string(nameText) : "");
faceIdsByUser.emplace_back();
lastUserId = userId;
}
// Append face ID (may be NULL if user has no faces — LEFT JOIN)
if (sqlite3_column_type(stmt, 3) != SQLITE_NULL) {
faceIdsByUser.back().push_back(sqlite3_column_int(stmt, 3));
}
}
if (rc != SQLITE_DONE) {
_logger.LogError("FaceDatabase::GetUsersWithFaceIds",
std::string("SQL step error: ") + sqlite3_errmsg(m_db), __FILE__, __LINE__);
}
sqlite3_finalize(stmt);
return 1;
}
catch (const std::exception& e) {
_logger.LogError("FaceDatabase::GetUsersWithFaceIds", e.what(), __FILE__, __LINE__);
return 0;
}
}
FaceDatabase::~FaceDatabase() {
try {
Destroy();
}
catch (std::exception& e) {
_logger.LogError("FaceDatabase::~FaceDatabase", e.what(), __FILE__, __LINE__);
}
}
bool FaceDatabase::Destroy() {
std::lock_guard<std::mutex> lock(_mutex);
try {
if (!m_db) return true;
int rc = sqlite3_close(m_db);
if (rc == SQLITE_OK) {
//this->_logger.LogDebug("Database::Destroy", "Success!", __FILE__, __LINE__);
m_db = nullptr;
return true;
}
else {
std::string errorCode = "Cannot close database connection.Error code:" + std::to_string(rc);
//this->_logger.LogError("Database::Destroy", errorCode, __FILE__, __LINE__);
return false;
}
}
catch (std::exception& e) {
//this->_logger.LogFatal("FaceDatabase::Destroy", e.what(), __FILE__, __LINE__);
return false;
}
}
}