Refactor project structure
This commit is contained in:
909
modules/ANSFR/FaceDatabase.cpp
Normal file
909
modules/ANSFR/FaceDatabase.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user