Merge pull request #3315 from Carbenium/db-misc

Misc. cleanup of DB related code
This commit is contained in:
Isaac Connor 2021-07-06 14:00:40 -04:00 committed by GitHub
commit 411a5181e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 136 additions and 200 deletions

View File

@ -75,7 +75,7 @@ void zmLoadDBConfig() {
std::string sql = stringtf("SELECT `Id` FROM `Servers` WHERE `Name`='%s'", std::string sql = stringtf("SELECT `Id` FROM `Servers` WHERE `Name`='%s'",
staticConfig.SERVER_NAME.c_str()); staticConfig.SERVER_NAME.c_str());
zmDbRow dbrow; zmDbRow dbrow;
if (dbrow.fetch(sql.c_str())) { if (dbrow.fetch(sql)) {
staticConfig.SERVER_ID = atoi(dbrow[0]); staticConfig.SERVER_ID = atoi(dbrow[0]);
} else { } else {
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str()); Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str());
@ -87,7 +87,7 @@ void zmLoadDBConfig() {
std::string sql = stringtf("SELECT `Name` FROM `Servers` WHERE `Id`='%d'", staticConfig.SERVER_ID); std::string sql = stringtf("SELECT `Name` FROM `Servers` WHERE `Id`='%d'", staticConfig.SERVER_ID);
zmDbRow dbrow; zmDbRow dbrow;
if (dbrow.fetch(sql.c_str())) { if (dbrow.fetch(sql)) {
staticConfig.SERVER_NAME = std::string(dbrow[0]); staticConfig.SERVER_NAME = std::string(dbrow[0]);
} else { } else {
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID); Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID);

View File

@ -38,11 +38,6 @@
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP #define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP #define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer #define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle #define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle

View File

@ -117,25 +117,25 @@ void zmDbClose() {
} }
} }
MYSQL_RES * zmDbFetch(const char * query) { MYSQL_RES *zmDbFetch(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex); std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) { if (!zmDbConnected) {
Error("Not connected."); Error("Not connected.");
return nullptr; return nullptr;
} }
if (mysql_query(&dbconn, query)) { if (mysql_query(&dbconn, query.c_str())) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
return nullptr; return nullptr;
} }
MYSQL_RES *result = mysql_store_result(&dbconn); MYSQL_RES *result = mysql_store_result(&dbconn);
if (!result) { if (!result) {
Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query); Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query.c_str());
} }
return result; return result;
} // end MYSQL_RES * zmDbFetch(const char * query); }
zmDbRow *zmDbFetchOne(const char *query) { zmDbRow *zmDbFetchOne(const std::string &query) {
zmDbRow *row = new zmDbRow(); zmDbRow *row = new zmDbRow();
if (row->fetch(query)) { if (row->fetch(query)) {
return row; return row;
@ -144,13 +144,13 @@ zmDbRow *zmDbFetchOne(const char *query) {
return nullptr; return nullptr;
} }
MYSQL_RES *zmDbRow::fetch(const char *query) { MYSQL_RES *zmDbRow::fetch(const std::string &query) {
result_set = zmDbFetch(query); result_set = zmDbFetch(query);
if (!result_set) return result_set; if (!result_set) return result_set;
int n_rows = mysql_num_rows(result_set); int n_rows = mysql_num_rows(result_set);
if (n_rows != 1) { if (n_rows != 1) {
Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query); Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query.c_str());
mysql_free_result(result_set); mysql_free_result(result_set);
result_set = nullptr; result_set = nullptr;
return result_set; return result_set;
@ -160,23 +160,23 @@ MYSQL_RES *zmDbRow::fetch(const char *query) {
if (!row) { if (!row) {
mysql_free_result(result_set); mysql_free_result(result_set);
result_set = nullptr; result_set = nullptr;
Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn)); Error("Error getting row from query %s. Error is %s", query.c_str(), mysql_error(&dbconn));
} else { } else {
Debug(5, "Success"); Debug(5, "Success");
} }
return result_set; return result_set;
} }
int zmDbDo(const char *query) { int zmDbDo(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex); std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) if (!zmDbConnected)
return 0; return 0;
int rc; int rc;
while ((rc = mysql_query(&dbconn, query)) and !zm_terminate) { while ((rc = mysql_query(&dbconn, query.c_str())) and !zm_terminate) {
Logger *logger = Logger::fetch(); Logger *logger = Logger::fetch();
Logger::Level oldLevel = logger->databaseLevel(); Logger::Level oldLevel = logger->databaseLevel();
logger->databaseLevel(Logger::NOLOG); logger->databaseLevel(Logger::NOLOG);
Error("Can't run query %s: %s", query, mysql_error(&dbconn)); Error("Can't run query %s: %s", query.c_str(), mysql_error(&dbconn));
logger->databaseLevel(oldLevel); logger->databaseLevel(oldLevel);
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) { if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) {
return rc; return rc;
@ -186,36 +186,36 @@ int zmDbDo(const char *query) {
Logger::Level oldLevel = logger->databaseLevel(); Logger::Level oldLevel = logger->databaseLevel();
logger->databaseLevel(Logger::NOLOG); logger->databaseLevel(Logger::NOLOG);
Debug(1, "Success running sql query %s", query); Debug(1, "Success running sql query %s", query.c_str());
logger->databaseLevel(oldLevel); logger->databaseLevel(oldLevel);
return 1; return 1;
} }
int zmDbDoInsert(const char *query) { int zmDbDoInsert(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex); std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) return 0; if (!zmDbConnected) return 0;
int rc; int rc;
while ( (rc = mysql_query(&dbconn, query)) and !zm_terminate) { while ( (rc = mysql_query(&dbconn, query.c_str())) and !zm_terminate) {
Error("Can't run query %s: %s", query, mysql_error(&dbconn)); Error("Can't run query %s: %s", query.c_str(), mysql_error(&dbconn));
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) )
return 0; return 0;
} }
int id = mysql_insert_id(&dbconn); int id = mysql_insert_id(&dbconn);
Debug(2, "Success running sql insert %s. Resulting id is %d", query, id); Debug(2, "Success running sql insert %s. Resulting id is %d", query.c_str(), id);
return id; return id;
} }
int zmDbDoUpdate(const char *query) { int zmDbDoUpdate(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex); std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) return 0; if (!zmDbConnected) return 0;
int rc; int rc;
while ( (rc = mysql_query(&dbconn, query)) and !zm_terminate) { while ( (rc = mysql_query(&dbconn, query.c_str())) and !zm_terminate) {
Error("Can't run query %s: %s", query, mysql_error(&dbconn)); Error("Can't run query %s: %s", query.c_str(), mysql_error(&dbconn));
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) )
return -rc; return -rc;
} }
int affected = mysql_affected_rows(&dbconn); int affected = mysql_affected_rows(&dbconn);
Debug(2, "Success running sql update %s. Rows modified %d", query, affected); Debug(2, "Success running sql update %s. Rows modified %d", query.c_str(), affected);
return affected; return affected;
} }
@ -255,7 +255,7 @@ void zmDbQueue::process() {
// My idea for leaving the locking around each sql statement is to allow // My idea for leaving the locking around each sql statement is to allow
// other db writers to get a chance // other db writers to get a chance
lock.unlock(); lock.unlock();
zmDbDo(sql.c_str()); zmDbDo(sql);
lock.lock(); lock.lock();
} }
} }
@ -267,3 +267,15 @@ void zmDbQueue::push(std::string &&sql) {
mQueue.push(std::move(sql)); mQueue.push(std::move(sql));
mCondition.notify_all(); mCondition.notify_all();
} }
std::string zmDbEscapeString(const std::string& to_escape) {
// According to docs, size of safer_whatever must be 2 * length + 1
// due to unicode conversions + null terminator.
std::string escaped((to_escape.length() * 2) + 1, '\0');
size_t escaped_len = mysql_real_escape_string(&dbconn, &escaped[0], to_escape.c_str(), to_escape.length());
escaped.resize(escaped_len);
return escaped;
}

View File

@ -38,7 +38,6 @@ class zmDbQueue {
public: public:
zmDbQueue(); zmDbQueue();
~zmDbQueue(); ~zmDbQueue();
void push(const char *sql) { return push(std::string(sql)); };
void push(std::string &&sql); void push(std::string &&sql);
void process(); void process();
void stop(); void stop();
@ -50,7 +49,7 @@ class zmDbRow {
MYSQL_ROW row; MYSQL_ROW row;
public: public:
zmDbRow() : result_set(nullptr), row(nullptr) { }; zmDbRow() : result_set(nullptr), row(nullptr) { };
MYSQL_RES *fetch(const char *query); MYSQL_RES *fetch(const std::string &query);
zmDbRow(MYSQL_RES *, MYSQL_ROW *row); zmDbRow(MYSQL_RES *, MYSQL_ROW *row);
~zmDbRow(); ~zmDbRow();
@ -67,15 +66,15 @@ extern zmDbQueue dbQueue;
extern bool zmDbConnected; extern bool zmDbConnected;
extern bool zmDbConnected;
bool zmDbConnect(); bool zmDbConnect();
void zmDbClose(); void zmDbClose();
int zmDbDo(const char *query); int zmDbDo(const std::string &query);
int zmDbDoInsert(const char *query); int zmDbDoInsert(const std::string &query);
int zmDbDoUpdate(const char *query); int zmDbDoUpdate(const std::string &query);
MYSQL_RES * zmDbFetch(const char *query); MYSQL_RES * zmDbFetch(const std::string &query);
zmDbRow *zmDbFetchOne(const char *query); zmDbRow *zmDbFetchOne(const std::string &query);
std::string zmDbEscapeString(const std::string& to_escape);
#endif // ZM_DB_H #endif // ZM_DB_H

View File

@ -32,8 +32,6 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
//#define USE_PREPARED_SQL 1
const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" }; const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" };
#define MAX_DB_FRAMES 100 #define MAX_DB_FRAMES 100
@ -127,7 +125,7 @@ Event::Event(
storage->SchemeString().c_str() storage->SchemeString().c_str()
); );
id = zmDbDoInsert(sql.c_str()); id = zmDbDoInsert(sql);
if ( !SetPath(storage) ) { if ( !SetPath(storage) ) {
// Try another // Try another
@ -140,7 +138,7 @@ Event::Event(
Debug(1, "%s", sql.c_str()); Debug(1, "%s", sql.c_str());
storage = nullptr; storage = nullptr;
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if ( result ) { if ( result ) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0])); storage = new Storage(atoi(dbrow[0]));
@ -159,7 +157,7 @@ Event::Event(
if ( monitor->ServerId() ) if ( monitor->ServerId() )
sql += stringtf(" OR ServerId != %u", monitor->ServerId()); sql += stringtf(" OR ServerId != %u", monitor->ServerId());
result = zmDbFetch(sql.c_str()); result = zmDbFetch(sql);
if ( result ) { if ( result ) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0])); storage = new Storage(atoi(dbrow[0]));
@ -177,7 +175,7 @@ Event::Event(
Warning("Failed to find a storage area to save events."); Warning("Failed to find a storage area to save events.");
} }
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id); sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
zmDbDo(sql.c_str()); zmDbDo(sql);
} // end if ! setPath(Storage) } // end if ! setPath(Storage)
Debug(1, "Using storage area at %s", path.c_str()); Debug(1, "Using storage area at %s", path.c_str());
@ -213,11 +211,11 @@ Event::Event(
if ( ! ( save_jpegs & 1 ) ) { if ( ! ( save_jpegs & 1 ) ) {
save_jpegs |= 1; // Turn on jpeg storage save_jpegs |= 1; // Turn on jpeg storage
sql = stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id); sql = stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id);
zmDbDo(sql.c_str()); zmDbDo(sql);
} }
} else { } else {
sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id); sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
zmDbDo(sql.c_str()); zmDbDo(sql);
} }
} // end if GetOptVideoWriter } // end if GetOptVideoWriter
} }
@ -255,7 +253,7 @@ Event::~Event() {
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score, tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
id); id);
if (!zmDbDoUpdate(sql.c_str())) { if (!zmDbDoUpdate(sql)) {
// Name might have been changed during recording, so just do the update without changing the name. // Name might have been changed during recording, so just do the update without changing the name.
sql = stringtf( sql = stringtf(
"UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, "UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
@ -264,7 +262,7 @@ Event::~Event() {
frames, alarm_frames, frames, alarm_frames,
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score, tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
id); id);
zmDbDoUpdate(sql.c_str()); zmDbDoUpdate(sql);
} // end if no changed rows due to Name change during recording } // end if no changed rows due to Name change during recording
} // Event::~Event() } // Event::~Event()
@ -351,65 +349,15 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
} // end if have old notes } // end if have old notes
} // end if have new notes } // end if have new notes
if ( update ) { if (update) {
std::string notes; std::string notes;
createNotes(notes); createNotes(notes);
Debug(2, "Updating notes for event %" PRIu64 ", '%s'", id, notes.c_str()); Debug(2, "Updating notes for event %" PRIu64 ", '%s'", id, notes.c_str());
#if USE_PREPARED_SQL
static MYSQL_STMT *stmt = 0;
char notesStr[ZM_SQL_MED_BUFSIZ] = ""; std::string sql = stringtf("UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64,
unsigned long notesLen = 0; zmDbEscapeString(notes).c_str(), id);
if ( !stmt ) {
const char *sql = "UPDATE `Events` SET `Notes` = ? WHERE `Id` = ?";
stmt = mysql_stmt_init(&dbconn);
if ( mysql_stmt_prepare(stmt, sql, strlen(sql)) ) {
Fatal("Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt));
}
/* Get the parameter count from the statement */
if ( mysql_stmt_param_count(stmt) != 2 ) {
Error("Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count(stmt), sql);
}
MYSQL_BIND bind[2];
memset(bind, 0, sizeof(bind));
/* STRING PARAM */
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (char *)notesStr;
bind[0].buffer_length = sizeof(notesStr);
bind[0].is_null = 0;
bind[0].length = &notesLen;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= (char *)&id;
bind[1].is_null= 0;
bind[1].length= 0;
/* Bind the buffers */
if ( mysql_stmt_bind_param(stmt, bind) ) {
Error("Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt));
}
} // end if ! stmt
strncpy(notesStr, notes.c_str(), sizeof(notesStr));
if ( mysql_stmt_execute(stmt) ) {
Error("Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt));
}
#else
char sql[ZM_SQL_LGE_BUFSIZ];
static char escapedNotes[ZM_SQL_MED_BUFSIZ];
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
snprintf(sql, sizeof(sql), "UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64, escapedNotes, id);
dbQueue.push(std::move(sql)); dbQueue.push(std::move(sql));
#endif
} // end if update } // end if update
} // void Event::updateNotes(const StringSetMap &newNoteSetMap) } // void Event::updateNotes(const StringSetMap &newNoteSetMap)
@ -477,7 +425,7 @@ void Event::WriteDbFrames() {
} // end while frames } // end while frames
// The -1 is for the extra , added for values above // The -1 is for the extra , added for values above
frame_insert_sql.erase(frame_insert_sql.size()-1); frame_insert_sql.erase(frame_insert_sql.size()-1);
//zmDbDo(frame_insert_sql.c_str()); //zmDbDo(frame_insert_sql);
dbQueue.push(std::move(frame_insert_sql)); dbQueue.push(std::move(frame_insert_sql));
if (stats_insert_sql.size() > 208) { if (stats_insert_sql.size() > 208) {
// The -1 is for the extra , added for values above // The -1 is for the extra , added for values above

View File

@ -46,7 +46,7 @@ bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_tim
"`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld " "`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld "
"ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time); "ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time);
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) if (!result)
exit(-1); exit(-1);
@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS FramesDuration, " "(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS FramesDuration, "
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id); "`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
exit(-1); exit(-1);
} }
@ -229,7 +229,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` " sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id); "FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
result = zmDbFetch(sql.c_str()); result = zmDbFetch(sql);
if (!result) { if (!result) {
exit(-1); exit(-1);
} }
@ -640,7 +640,7 @@ bool EventStream::checkEventLoaded() {
if ( forceEventChange || ( (mode != MODE_SINGLE) && (mode != MODE_NONE) ) ) { if ( forceEventChange || ( (mode != MODE_SINGLE) && (mode != MODE_NONE) ) ) {
Debug(1, "Checking for next event %s", sql.c_str()); Debug(1, "Checking for next event %s", sql.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
exit(-1); exit(-1);
} }

View File

@ -20,6 +20,7 @@
#include "zm_group.h" #include "zm_group.h"
#include "zm_logger.h" #include "zm_logger.h"
#include "zm_utils.h"
#include <cstring> #include <cstring>
Group::Group() { Group::Group() {
@ -39,27 +40,27 @@ Group::Group(const MYSQL_ROW &dbrow) {
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */ /* If a zero or invalid p_id is passed, then the old default path will be assumed. */
Group::Group(unsigned int p_id) { Group::Group(unsigned int p_id) {
id = 0; id = 0;
if ( p_id ) { if (p_id) {
char sql[ZM_SQL_SML_BUFSIZ]; std::string sql = stringtf("SELECT `Id`, `ParentId`, `Name` FROM `Group` WHERE `Id`=%u", p_id);
snprintf(sql, sizeof(sql), "SELECT `Id`, `ParentId`, `Name` FROM `Group` WHERE `Id`=%u", p_id); Debug(2, "Loading Group for %u using %s", p_id, sql.c_str());
Debug(2,"Loading Group for %u using %s", p_id, sql); zmDbRow dbrow;
zmDbRow dbrow; if (!dbrow.fetch(sql)) {
if ( !dbrow.fetch(sql) ) { Error("Unable to load group for id %u: %s", p_id, mysql_error(&dbconn));
Error("Unable to load group for id %u: %s", p_id, mysql_error(&dbconn)); } else {
} else { unsigned int index = 0;
unsigned int index = 0; id = atoi(dbrow[index++]);
id = atoi(dbrow[index++]); parent_id = dbrow[index] ? atoi(dbrow[index]) : 0;
parent_id = dbrow[index] ? atoi(dbrow[index]): 0; index++; index++;
strncpy(name, dbrow[index++], sizeof(name)-1); strncpy(name, dbrow[index++], sizeof(name) - 1);
Debug(1, "Loaded Group area %d '%s'", id, this->Name()); Debug(1, "Loaded Group area %d '%s'", id, this->Name());
} }
} }
if ( ! id ) { if (!id) {
Debug(1,"No id passed to Group constructor."); Debug(1, "No id passed to Group constructor.");
strcpy(name, "Default"); strcpy(name, "Default");
} }
} }
Group::~Group() { Group::~Group() {

View File

@ -518,11 +518,7 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const
if (level <= mDatabaseLevel) { if (level <= mDatabaseLevel) {
if (zmDbConnected) { if (zmDbConnected) {
int syslogSize = syslogEnd - syslogStart; std::string escapedString = zmDbEscapeString({syslogStart, syslogEnd});
std::string escapedString;
escapedString.resize((syslogSize * 2) + 1);
mysql_real_escape_string(&dbconn, &escapedString[0], syslogStart, syslogSize);
escapedString.resize(std::strlen(escapedString.c_str()));
std::string sql_string = stringtf( std::string sql_string = stringtf(
"INSERT INTO `Logs` " "INSERT INTO `Logs` "

View File

@ -880,7 +880,7 @@ std::shared_ptr<Monitor> Monitor::Load(unsigned int p_id, bool load_zones, Purpo
std::string sql = load_monitor_sql + stringtf(" WHERE Id=%d", p_id); std::string sql = load_monitor_sql + stringtf(" WHERE Id=%d", p_id);
zmDbRow dbrow; zmDbRow dbrow;
if ( !dbrow.fetch(sql.c_str()) ) { if (!dbrow.fetch(sql)) {
Error("Can't use query result: %s", mysql_error(&dbconn)); Error("Can't use query result: %s", mysql_error(&dbconn));
return nullptr; return nullptr;
} }
@ -1287,16 +1287,14 @@ void Monitor::actionReload() {
void Monitor::actionEnable() { void Monitor::actionEnable() {
shared_data->action |= RELOAD; shared_data->action |= RELOAD;
char sql[ZM_SQL_SML_BUFSIZ]; std::string sql = stringtf("UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %u", id);
snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %u", id);
zmDbDo(sql); zmDbDo(sql);
} }
void Monitor::actionDisable() { void Monitor::actionDisable() {
shared_data->action |= RELOAD; shared_data->action |= RELOAD;
char sql[ZM_SQL_SML_BUFSIZ]; std::string sql = stringtf("UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %u", id);
snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %u", id);
zmDbDo(sql); zmDbDo(sql);
} }
@ -1454,7 +1452,7 @@ void Monitor::DumpZoneImage(const char *zone_string) {
// Grab the most revent event image // Grab the most revent event image
std::string sql = stringtf("SELECT MAX(`Id`) FROM `Events` WHERE `MonitorId`=%d AND `Frames` > 0", id); std::string sql = stringtf("SELECT MAX(`Id`) FROM `Events` WHERE `MonitorId`=%d AND `Frames` > 0", id);
zmDbRow eventid_row; zmDbRow eventid_row;
if ( eventid_row.fetch(sql.c_str()) ) { if (eventid_row.fetch(sql)) {
uint64_t event_id = atoll(eventid_row[0]); uint64_t event_id = atoll(eventid_row[0]);
Debug(3, "Got event %" PRIu64, event_id); Debug(3, "Got event %" PRIu64, event_id);
@ -2285,7 +2283,7 @@ void Monitor::Reload() {
} }
std::string sql = load_monitor_sql + stringtf(" WHERE Id=%d", id); std::string sql = load_monitor_sql + stringtf(" WHERE Id=%d", id);
zmDbRow *row = zmDbFetchOne(sql.c_str()); zmDbRow *row = zmDbFetchOne(sql);
if (!row) { if (!row) {
Error("Can't run query: %s", mysql_error(&dbconn)); Error("Can't run query: %s", mysql_error(&dbconn));
} else if (MYSQL_ROW dbrow = row->mysql_row()) { } else if (MYSQL_ROW dbrow = row->mysql_row()) {
@ -2371,7 +2369,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
" AND `Enabled`=1", " AND `Enabled`=1",
link_ids[i]); link_ids[i]);
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
continue; continue;
} }
@ -2395,7 +2393,7 @@ std::vector<std::shared_ptr<Monitor>> Monitor::LoadMonitors(const std::string &w
std::string sql = load_monitor_sql + " WHERE " + where; std::string sql = load_monitor_sql + " WHERE " + where;
Debug(1, "Loading Monitors with %s", sql.c_str()); Debug(1, "Loading Monitors with %s", sql.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
Error("Can't load local monitors: %s", mysql_error(&dbconn)); Error("Can't load local monitors: %s", mysql_error(&dbconn));
return {}; return {};
@ -3141,7 +3139,7 @@ std::vector<Group *> Monitor::Groups() {
std::string sql = stringtf( std::string sql = stringtf(
"SELECT `Id`, `ParentId`, `Name` FROM `Groups` WHERE `Groups.Id` IN " "SELECT `Id`, `ParentId`, `Name` FROM `Groups` WHERE `Groups.Id` IN "
"(SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=%d)", id); "(SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=%d)", id);
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
Error("Can't load groups: %s", mysql_error(&dbconn)); Error("Can't load groups: %s", mysql_error(&dbconn));
return groups; return groups;

View File

@ -86,9 +86,8 @@ bool RtspThread::recvResponse(std::string &response) {
int RtspThread::requestPorts() { int RtspThread::requestPorts() {
if ( !smMinDataPort ) { if ( !smMinDataPort ) {
char sql[ZM_SQL_SML_BUFSIZ];
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors //FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
strncpy(sql, "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC", sizeof(sql)); std::string sql = "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC";
MYSQL_RES *result = zmDbFetch(sql); MYSQL_RES *result = zmDbFetch(sql);

View File

@ -21,32 +21,33 @@
#include "zm_db.h" #include "zm_db.h"
#include "zm_logger.h" #include "zm_logger.h"
#include "zm_utils.h"
#include <cstring> #include <cstring>
Storage::Storage() : id(0) { Storage::Storage() : id(0) {
Warning("Instantiating default Storage Object. Should not happen."); Warning("Instantiating default Storage Object. Should not happen.");
strcpy(name, "Default"); strcpy(name, "Default");
if ( staticConfig.DIR_EVENTS[0] != '/' ) { if (staticConfig.DIR_EVENTS[0] != '/') {
// not using an absolute path. Make it one by appending ZM_PATH_WEB // not using an absolute path. Make it one by appending ZM_PATH_WEB
snprintf(path, sizeof(path), "%s/%s", snprintf(path, sizeof(path), "%s/%s",
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str()); staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
} else { } else {
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1); strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path) - 1);
} }
scheme = MEDIUM; scheme = MEDIUM;
scheme_str = "Medium"; scheme_str = "Medium";
} }
Storage::Storage(MYSQL_ROW &dbrow) { Storage::Storage(MYSQL_ROW &dbrow) {
unsigned int index = 0; unsigned int index = 0;
id = atoi(dbrow[index++]); id = atoi(dbrow[index++]);
strncpy(name, dbrow[index++], sizeof(name)-1); strncpy(name, dbrow[index++], sizeof(name) - 1);
strncpy(path, dbrow[index++], sizeof(path)-1); strncpy(path, dbrow[index++], sizeof(path) - 1);
type_str = std::string(dbrow[index++]); type_str = std::string(dbrow[index++]);
scheme_str = std::string(dbrow[index++]); scheme_str = std::string(dbrow[index++]);
if ( scheme_str == "Deep" ) { if (scheme_str == "Deep") {
scheme = DEEP; scheme = DEEP;
} else if ( scheme_str == "Medium" ) { } else if (scheme_str == "Medium") {
scheme = MEDIUM; scheme = MEDIUM;
} else { } else {
scheme = SHALLOW; scheme = SHALLOW;
@ -55,44 +56,42 @@ Storage::Storage(MYSQL_ROW &dbrow) {
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */ /* If a zero or invalid p_id is passed, then the old default path will be assumed. */
Storage::Storage(unsigned int p_id) : id(p_id) { Storage::Storage(unsigned int p_id) : id(p_id) {
if (id) {
if ( id ) { std::string sql = stringtf("SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id);
char sql[ZM_SQL_SML_BUFSIZ]; Debug(2, "Loading Storage for %u using %s", id, sql.c_str());
snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id); zmDbRow dbrow;
Debug(2, "Loading Storage for %u using %s", id, sql); if (!dbrow.fetch(sql)) {
zmDbRow dbrow; Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn));
if ( !dbrow.fetch(sql) ) { } else {
Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn)); unsigned int index = 0;
} else { id = atoi(dbrow[index++]);
unsigned int index = 0; strncpy(name, dbrow[index++], sizeof(name) - 1);
id = atoi(dbrow[index++]); strncpy(path, dbrow[index++], sizeof(path) - 1);
strncpy(name, dbrow[index++], sizeof(name)-1);
strncpy(path, dbrow[index++], sizeof(path)-1);
type_str = std::string(dbrow[index++]); type_str = std::string(dbrow[index++]);
scheme_str = std::string(dbrow[index++]); scheme_str = std::string(dbrow[index++]);
if ( scheme_str == "Deep" ) { if (scheme_str == "Deep") {
scheme = DEEP; scheme = DEEP;
} else if ( scheme_str == "Medium" ) { } else if (scheme_str == "Medium") {
scheme = MEDIUM; scheme = MEDIUM;
} else { } else {
scheme = SHALLOW; scheme = SHALLOW;
} }
Debug(1, "Loaded Storage area %d '%s'", id, name); Debug(1, "Loaded Storage area %d '%s'", id, name);
} }
} }
if ( !id ) { if (!id) {
if ( staticConfig.DIR_EVENTS[0] != '/' ) { if (staticConfig.DIR_EVENTS[0] != '/') {
// not using an absolute path. Make it one by appending ZM_PATH_WEB // not using an absolute path. Make it one by appending ZM_PATH_WEB
snprintf(path, sizeof(path), "%s/%s", snprintf(path, sizeof(path), "%s/%s",
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str()); staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
} else { } else {
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1); strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path) - 1);
} }
Debug(1, "No id passed to Storage constructor. Using default path %s instead", path); Debug(1, "No id passed to Storage constructor. Using default path %s instead", path);
strcpy(name, "Default"); strcpy(name, "Default");
scheme = MEDIUM; scheme = MEDIUM;
scheme_str = "Medium"; scheme_str = "Medium";
} }
} }
Storage::~Storage() { Storage::~Storage() {

View File

@ -85,15 +85,7 @@ bool User::canAccess(int monitor_id) {
// Function to load a user from username and password // Function to load a user from username and password
// Please note that in auth relay mode = none, password is NULL // Please note that in auth relay mode = none, password is NULL
User *zmLoadUser(const char *username, const char *password) { User *zmLoadUser(const char *username, const char *password) {
int username_length = strlen(username); std::string escaped_username = zmDbEscapeString(username);
// According to docs, size of safer_whatever must be 2*length+1
// due to unicode conversions + null terminator.
std::string escaped_username((username_length * 2) + 1, '\0');
size_t escaped_len = mysql_real_escape_string(&dbconn, &escaped_username[0], username, username_length);
escaped_username.resize(escaped_len);
std::string sql = stringtf("SELECT `Id`, `Username`, `Password`, `Enabled`," std::string sql = stringtf("SELECT `Id`, `Username`, `Password`, `Enabled`,"
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0," " `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
@ -101,7 +93,7 @@ User *zmLoadUser(const char *username, const char *password) {
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1",
escaped_username.c_str()); escaped_username.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) if (!result)
return nullptr; return nullptr;
@ -152,7 +144,7 @@ User *zmLoadTokenUser(const std::string &jwt_token_str, bool use_remote_addr) {
" `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`" " `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`"
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str()); " FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) if (!result)
return nullptr; return nullptr;
@ -195,7 +187,7 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0," " `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
" `MonitorIds` FROM `Users` WHERE `Enabled` = 1"; " `MonitorIds` FROM `Users` WHERE `Enabled` = 1";
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) if (!result)
return nullptr; return nullptr;

View File

@ -116,8 +116,7 @@ Zone::~Zone() {
} }
void Zone::RecordStats(const Event *event) { void Zone::RecordStats(const Event *event) {
static char sql[ZM_SQL_MED_BUFSIZ]; std::string sql = stringtf(
snprintf(sql, sizeof(sql),
"INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, " "INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, "
"PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, " "PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, "
"Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, " "Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, "
@ -830,7 +829,7 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
"OverloadFrames,ExtendAlarmFrames" "OverloadFrames,ExtendAlarmFrames"
" FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id()); " FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id());
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
return {}; return {};
} }

View File

@ -232,7 +232,6 @@ int main(int argc, char *argv[]) {
while (!zm_terminate) { while (!zm_terminate) {
result = 0; result = 0;
static char sql[ZM_SQL_SML_BUFSIZ];
for (const std::shared_ptr<Monitor> &monitor : monitors) { for (const std::shared_ptr<Monitor> &monitor : monitors) {
monitor->LoadCamera(); monitor->LoadCamera();
@ -244,7 +243,7 @@ int main(int argc, char *argv[]) {
monitor->SetStartupTime(now); monitor->SetStartupTime(now);
monitor->SetHeartbeatTime(now); monitor->SetHeartbeatTime(now);
snprintf(sql, sizeof(sql), std::string sql = stringtf(
"INSERT INTO Monitor_Status (MonitorId,Status,CaptureFPS,AnalysisFPS)" "INSERT INTO Monitor_Status (MonitorId,Status,CaptureFPS,AnalysisFPS)"
" VALUES (%u, 'Running',0,0) ON DUPLICATE KEY UPDATE Status='Running',CaptureFPS=0,AnalysisFPS=0", " VALUES (%u, 'Running',0,0) ON DUPLICATE KEY UPDATE Status='Running',CaptureFPS=0,AnalysisFPS=0",
monitor->Id()); monitor->Id());
@ -273,9 +272,9 @@ int main(int argc, char *argv[]) {
break; break;
} }
snprintf(sql, sizeof(sql), sql = stringtf(
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'", "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'",
monitor->Id()); monitor->Id());
zmDbDo(sql); zmDbDo(sql);
} // end foreach monitor } // end foreach monitor
@ -360,8 +359,7 @@ int main(int argc, char *argv[]) {
} // end while ! zm_terminate outer connection loop } // end while ! zm_terminate outer connection loop
for (std::shared_ptr<Monitor> &monitor : monitors) { for (std::shared_ptr<Monitor> &monitor : monitors) {
static char sql[ZM_SQL_SML_BUFSIZ]; std::string sql = stringtf(
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'", "INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
monitor->Id()); monitor->Id());
zmDbDo(sql); zmDbDo(sql);

View File

@ -736,7 +736,7 @@ int main(int argc, char *argv[]) {
} }
sql += " ORDER BY Id ASC"; sql += " ORDER BY Id ASC";
MYSQL_RES *result = zmDbFetch(sql.c_str()); MYSQL_RES *result = zmDbFetch(sql);
if (!result) { if (!result) {
exit_zmu(-1); exit_zmu(-1);
} }