Replace dbeventid

This commit is contained in:
Mr-Dave
2022-06-26 20:13:25 -06:00
parent 062ebb5a1f
commit 0eea79f8ac
5 changed files with 32 additions and 165 deletions

View File

@@ -2159,8 +2159,8 @@
<td bgcolor="#edf4f9" >threshold</a> </td>
<td bgcolor="#edf4f9" >%Q</a> </td>
<td bgcolor="#edf4f9" >Number of labels from despeckle</a> </td>
<td bgcolor="#edf4f9" >%{dbeventid}</a> </td>
<td bgcolor="#edf4f9" >See <a href="#sql_query_start">sql_query_start</a> </a> </td>
<td bgcolor="#edf4f9" >%{eventid}</a> </td>
<td bgcolor="#edf4f9" >Unique identifier for event (cam id + date/time)</a> </td>
</tr>
<tr>
<td bgcolor="#edf4f9" >%$</a> </td>
@@ -5499,20 +5499,6 @@
The sql_log_* options do <em>not</em> impact <a href="#sql_query_start">sql_query_start</a>,
which, if specified, is always executed at event start.
<p></p>
Configuration option <a href="#sql_query_start">sql_query_start</a> allows motion-detected
events to be assigned unique event IDs and recorded in the database independently from
Motion's recording of images and movies.
When this SQL statement and the related database table schema are so configured, the
database assigns an event ID that is globally unique within its table and returns the ID
to the motion thread that recorded the event.
The conversion specifier %{dbeventid} may then be included in subsequently executed
<a href="#sql_query">sql_query</a> and <a href="#sql_query_stop">sql_query_stop</a>
statements to insert that event ID into database records created for associated files or
to control updates of event-related records.
For example, <a href="#sql_query_stop">sql_query_stop</a> may specify a SQL UPDATE statement
that inserts event end time into a recently-started event's record. (Note that Motion's "event"
data item, conversion specifier %v, is unique only within a given camera and Motion run).
<p></p>
Motion does not provide for deletion of the database records it creates nor for the
images and movies they catalog.
Other user-provided scripts or software may analyze, search, display,
@@ -5530,28 +5516,6 @@
time_stamp TIMESTAMP NOT NULL, frame INTEGER, motion_event INTEGER,
movie_end TIMESTAMP);</code>
<p></p>
Sample SQL defining a table for Motion-recognized events, each with a unique <code>%{dbeventid}</code> ID:
<p></p>
MySQL; MariaDB : <code>CREATE TABLE event (event_id INTEGER PRIMARY KEY AUTO_INCREMENT,
camera INTEGER NOT NULL, event_start DATETIME NOT NULL, event_stop DATETIME,
motion_event INTEGER NOT NULL, event_text VARCHAR(255));</code>
<p></p>
SQLite3 : <code>CREATE TABLE event (event_id INTEGER PRIMARY KEY AUTOINCREMENT,
camera INTEGER NOT NULL, event_start REAL NOT NULL, event_stop REAL,
motion_event INTEGER NOT NULL, event_text TEXT);</code>
(SQLite3 has no native timestamp data type; REAL supports timestamps down to microsecond precision.)
<p></p>
PostgreSQL : <code>
CREATE TABLE event (event_id SERIAL PRIMARY KEY, camera INTEGER NOT NULL,
event_start TIMESTAMP NOT NULL, event_stop TIMESTAMP, motion_event INTEGER NOT NULL,
motion_pixels INTEGER, motion_x INTEGER, motion_y INTEGER, event_text VARCHAR);</code><br/>
sql_query_start <code>INSERT INTO event values (DEFAULT, %t, '%Y-%m-%d %T', NULL, %v,
%D, %K, %L, %C) RETURNING event_id</code><br/>
sql_query_stop <code>UPDATE event SET event_stop='%Y-%m-%d %T' WHERE %n=8 AND
event_id=%{dbeventid} AND event_stop IS NULL</code>
(assumes <a href="#movie_output">movie_output</a> and
<a href="#sql_log_movie">sql_log_movie</a> are enabled)
<p></p>
<h3><a name="database_type"></a> database_type </h3>
<p></p>
@@ -5704,8 +5668,6 @@
VALUES(%t, '%f', %q, %n, '%Y-%m-%d %T', '%C')</code><br/>
<code>INSERT IGNORE INTO security(camera, file_name, year, month, day, hour, minute)
VALUES (8, '%f', %Y, %m,%d, %H, %M)</code><br/>
<code>INSERT INTO security(camera, event_id, filename, frame, file_type, time_stamp)
VALUES(%t, %{dbeventid}, '%f', %q, %n, '%Y-%m-%d %T')</code>
<p></p>
<p></p>
@@ -5723,26 +5685,6 @@
You can use <a href="#conversion_specifiers">Conversion Specifiers</a> in this statement.
<p></p>
<p></p>
Upon successful INSERTion of a record into a database table whose schema generates
a positive integer ID for each new record, the returned <code>mysql_insert_id()</code>,
<code>sqlite3_last_insert_rowid()</code>, or PostgreSQL <code>INSERT ... RETURNING</code>
value is saved until the next <code>sql_query_start</code> is executed
for the same camera.
<code>%{dbeventid}</code> can then be used as a value in <code>sql_query</code>
or <code>sql_query_stop</code> statements.
To obtain <code>%{dbeventid}</code> for PostgreSQL, an INSERT statement must include a
<code>RETURNING</code> clause that yields a single positive integer value.
Absent a valid ID value returned by the database, <code>%{dbeventid}</code> yields zero.
<p></p>
Sample Queries (presuming the schema of table <code>event</code> defines a column that generates unique IDs):
<p></p>
MySQL; MariaDB; SQLite3:
<code>INSERT INTO event(camera, event_start) VALUES(%t, '%Y-%m-%d %T')</code>
<p></p>
PostgreSQL:
<code>INSERT INTO event(camera, event_start) VALUES(%t, '%Y-%m-%d %T') RETURNING event_id</code><br/>
<p></p>
<p></p>
<h3><a name="sql_query_stop"></a> sql_query_stop </h3>
<p></p>
@@ -5766,9 +5708,6 @@
Sample Queries:
<p></p>
<code>UPDATE security SET movie_end='%Y-%m-%d %T' WHERE filename='%f'</code><br/>
<code>UPDATE event SET event_stop='%Y-%m-%d %T' WHERE %n=8 AND event_id=%{dbeventid}
AND event_stop IS NULL</code>
<p></p>
<p></p>
</ul>

View File

@@ -155,7 +155,6 @@ static int dbse_init_mysql(struct context *cnt)
#if defined(HAVE_MYSQL)
int dbport;
if ((mystreq(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) {
cnt->database_event_id = 0;
cnt->database_mysql = mymalloc(sizeof(MYSQL));
mysql_init(cnt->database_mysql);
if ((cnt->conf.database_port < 0) || (cnt->conf.database_port > 65535)) {
@@ -195,7 +194,6 @@ static int dbse_init_mariadb(struct context *cnt)
#if defined(HAVE_MARIADB)
int dbport;
if ((mystreq(cnt->conf.database_type, "mariadb")) && (cnt->conf.database_dbname)) {
cnt->database_event_id = 0;
cnt->database_mariadb = mymalloc(sizeof(MYSQL));
mysql_init(cnt->database_mariadb);
if ((cnt->conf.database_port < 0) || (cnt->conf.database_port > 65535)) {
@@ -273,11 +271,6 @@ static int dbse_init_pgsql(struct context *cnt)
#ifdef HAVE_PGSQL
if ((mystreq(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) {
char connstring[255];
/*
* Create the connection string.
* Quote the values so we can have null values (blank)
*/
snprintf(connstring, 255,
"dbname='%s' host='%s' user='%s' password='%s' port='%d'",
cnt->conf.database_dbname, /* dbname */
@@ -290,12 +283,11 @@ static int dbse_init_pgsql(struct context *cnt)
cnt->database_pgsql = PQconnectdb(connstring);
if (PQstatus(cnt->database_pgsql) == CONNECTION_BAD) {
MOTION_LOG(ERR, TYPE_DB, NO_ERRNO
,_("Connection to PostgreSQL database '%s' failed: %s")
,cnt->conf.database_dbname, PQerrorMessage(cnt->database_pgsql));
,_("Connection to PostgreSQL database '%s' failed: %s")
,cnt->conf.database_dbname, PQerrorMessage(cnt->database_pgsql));
return -2;
}
cnt->eid_db_format = dbeid_undetermined;
cnt->database_event_id = 0;
}
#else
(void)cnt; /* Avoid compiler warnings */
@@ -347,7 +339,6 @@ void dbse_deinit(struct context *cnt)
if ( (mystreq(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) {
mysql_thread_end();
mysql_close(cnt->database_mysql);
cnt->database_event_id = 0;
}
#endif /* HAVE_MYSQL */
@@ -355,7 +346,6 @@ void dbse_deinit(struct context *cnt)
if ( (mystreq(cnt->conf.database_type, "mariadb")) && (cnt->conf.database_dbname)) {
mysql_thread_end();
mysql_close(cnt->database_mariadb);
cnt->database_event_id = 0;
}
#endif /* HAVE_MARIADB */
@@ -363,7 +353,6 @@ void dbse_deinit(struct context *cnt)
if ((mystreq(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) {
PQfinish(cnt->database_pgsql);
cnt->database_pgsql = NULL;
cnt->database_event_id = 0;
}
#endif /* HAVE_PGSQL */
@@ -399,7 +388,7 @@ void dbse_sqlmask_update(struct context *cnt)
* dbse_exec_mysql
*
*/
static void dbse_exec_mysql(char *sqlquery, struct context *cnt, int save_id)
static void dbse_exec_mysql(char *sqlquery, struct context *cnt)
{
#if defined(HAVE_MYSQL)
if (mystreq(cnt->conf.database_type, "mysql")) {
@@ -440,14 +429,10 @@ static void dbse_exec_mysql(char *sqlquery, struct context *cnt, int save_id)
}
}
}
if (save_id) {
cnt->database_event_id = (unsigned long long) mysql_insert_id(cnt->database_mysql);
}
}
#else
(void)sqlquery;
(void)cnt;
(void)save_id;
#endif /* HAVE_MYSQL*/
}
@@ -455,7 +440,7 @@ static void dbse_exec_mysql(char *sqlquery, struct context *cnt, int save_id)
* dbse_exec_mariadb
*
*/
static void dbse_exec_mariadb(char *sqlquery, struct context *cnt, int save_id)
static void dbse_exec_mariadb(char *sqlquery, struct context *cnt)
{
#if defined(HAVE_MARIADB)
if (mystreq(cnt->conf.database_type, "mariadb")) {
@@ -496,14 +481,10 @@ static void dbse_exec_mariadb(char *sqlquery, struct context *cnt, int save_id)
}
}
}
if (save_id) {
cnt->database_event_id = (unsigned long long) mysql_insert_id(cnt->database_mariadb);
}
}
#else
(void)sqlquery;
(void)cnt;
(void)save_id;
#endif /* HAVE_MYSQL HAVE_MARIADB*/
}
@@ -512,7 +493,7 @@ static void dbse_exec_mariadb(char *sqlquery, struct context *cnt, int save_id)
* dbse_exec_pgsql
*
*/
static void dbse_exec_pgsql(char *sqlquery, struct context *cnt, int save_id)
static void dbse_exec_pgsql(char *sqlquery, struct context *cnt)
{
#ifdef HAVE_PGSQL
if (mystreq(cnt->conf.database_type, "postgresql") && cnt->database_pgsql) {
@@ -561,47 +542,6 @@ static void dbse_exec_pgsql(char *sqlquery, struct context *cnt, int save_id)
} else if (!(estat == PGRES_COMMAND_OK || estat == PGRES_TUPLES_OK)) {
MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO, _("PGSQL query failed: [%s] %s %s"),
sqlquery, PQresStatus(PQresultStatus(res)), PQresultErrorMessage(res));
} else if (save_id) {
/* sqlquery processing is complete unless it potentially returns an event ID value;
* save_id optionally allows SQL INSERT RETURNING a single positive integer value,
* e.g., an auto-incremented unique row ID. */
if (cnt->eid_db_format < dbeid_undetermined) {
/* A previous successful sqlquery returned nothing or an invalid value. This
* is either intended or a non-transient flaw in a table schema or sqlquery. */
} else {
if (estat == PGRES_TUPLES_OK) { /* returned DB record set (possibly empty) */
if (PQntuples(res) == 1 && PQnfields(res) == 1 && !PQgetisnull(res, 0, 0) && \
((cnt->eid_db_format=PQfformat(res, 0)) == 0)) { /* got one char string */
/* PQfformat()==0: null-term string, guaranteed unless DECLARE CURSOR BINARY
* or extended query protocol (PGSQL Doc.sec.51.2.2; PGSQL>v9, 52.2.2) */
char *eid_db_val;
char *endptr;
eid_db_val = PQgetvalue(res, 0, 0);
MOTION_LOG(DBG, TYPE_DB, NO_ERRNO, _("INSERT ... RETURNING VALUE=\"%s\""),
eid_db_val);
cnt->database_event_id = strtoll(eid_db_val, &endptr, 10);
/* "unsigned" dcl overriden by cast, else "<1" test fails if negative */
if (*endptr != '\0' || (long long)cnt->database_event_id < 1) {
/* not numeric or not positive or not integer */
cnt->eid_db_format = dbeid_not_valid; /* abandon event ID retrieval */
}
} else if (PQntuples(res) < 1) { /* returned empty record set */
cnt->eid_db_format = dbeid_no_return;
} else { /* returned NULL, BINARY value, multiple values, or muliple records */
cnt->eid_db_format = dbeid_not_valid; /* abandon event ID retrieval */
}
} else { /* nothing returned; OK if %{dbeventid} never used */
cnt->eid_db_format = dbeid_no_return;
}
if (cnt->eid_db_format == dbeid_not_valid) {
MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, _("Invalid event ID returned by SQL query \"%s\""),
sqlquery);
}
if (cnt->eid_db_format && cnt-> database_event_id) {
/* retrieval failed; nullify any earlier retrieved value */
cnt->database_event_id = 0;
}
}
}
if (res) {
PQclear(res);
@@ -610,7 +550,6 @@ static void dbse_exec_pgsql(char *sqlquery, struct context *cnt, int save_id)
#else
(void)sqlquery;
(void)cnt;
(void)save_id;
#endif /* HAVE_PGSQL */
}
@@ -619,7 +558,7 @@ static void dbse_exec_pgsql(char *sqlquery, struct context *cnt, int save_id)
* dbse_exec_sqlite3
*
*/
static void dbse_exec_sqlite3(char *sqlquery, struct context *cnt, int save_id)
static void dbse_exec_sqlite3(char *sqlquery, struct context *cnt)
{
#ifdef HAVE_SQLITE3
if ((mystreq(cnt->conf.database_type, "sqlite3")) && (cnt->conf.database_dbname)) {
@@ -630,18 +569,12 @@ static void dbse_exec_sqlite3(char *sqlquery, struct context *cnt, int save_id)
if (res != SQLITE_OK ) {
MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, _("SQLite3 error was %s"), errmsg);
sqlite3_free(errmsg);
if (save_id) {
cnt->database_event_id = 0;
}
} else if (save_id) {
cnt->database_event_id = sqlite3_last_insert_rowid(cnt->database_sqlite3);
}
}
#else
(void)sqlquery;
(void)cnt;
(void)save_id;
#endif /* HAVE_SQLITE3 */
}
@@ -663,13 +596,13 @@ void dbse_firstmotion(struct context *cnt)
}
if (mystreq(cnt->conf.database_type,"mysql")) {
dbse_exec_mysql(sqlquery, cnt, 1);
dbse_exec_mysql(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"mariadb")) {
dbse_exec_mariadb(sqlquery, cnt, 1);
dbse_exec_mariadb(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"postgresql")) {
dbse_exec_pgsql(sqlquery, cnt, 1);
dbse_exec_pgsql(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"sqlite3")) {
dbse_exec_sqlite3(sqlquery, cnt, 1);
dbse_exec_sqlite3(sqlquery, cnt);
}
}
@@ -691,13 +624,13 @@ void dbse_newfile(struct context *cnt, char *filename, int sqltype, struct timev
}
if (mystreq(cnt->conf.database_type,"mysql")) {
dbse_exec_mysql(sqlquery, cnt, 0);
dbse_exec_mysql(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"mariadb")) {
dbse_exec_mariadb(sqlquery, cnt, 0);
dbse_exec_mariadb(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"postgresql")) {
dbse_exec_pgsql(sqlquery, cnt, 0);
dbse_exec_pgsql(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"sqlite3")) {
dbse_exec_sqlite3(sqlquery, cnt, 0);
dbse_exec_sqlite3(sqlquery, cnt);
}
}
@@ -718,13 +651,13 @@ void dbse_fileclose(struct context *cnt, char *filename, int sqltype, struct tim
}
if (mystreq(cnt->conf.database_type,"mysql")) {
dbse_exec_mysql(sqlquery, cnt, 0);
dbse_exec_mysql(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"mariadb")) {
dbse_exec_mariadb(sqlquery, cnt, 0);
dbse_exec_mariadb(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"postgresql")) {
dbse_exec_pgsql(sqlquery, cnt, 0);
dbse_exec_pgsql(sqlquery, cnt);
} else if (mystreq(cnt->conf.database_type,"sqlite3")) {
dbse_exec_sqlite3(sqlquery, cnt, 0);
dbse_exec_sqlite3(sqlquery, cnt);
}
}

View File

@@ -554,7 +554,8 @@ static void motion_detected(struct context *cnt, int dev, struct image_data *img
cnt->prev_event = cnt->event_nr;
cnt->eventtime = img->timestamp_tv.tv_sec;
localtime_r(&cnt->eventtime, cnt->eventtime_tm);
sprintf(cnt->eventid,"%05d",cnt->camera_id);
strftime(cnt->eventid+5, 15,"%Y%m%d%H%M%S", cnt->eventtime_tm);
/*
* Since this is a new event we create the event_text_string used for
* the %C conversion specifier. We may already need it for
@@ -563,9 +564,9 @@ static void motion_detected(struct context *cnt, int dev, struct image_data *img
mystrftime(cnt, cnt->text_event_string, sizeof(cnt->text_event_string),
cnt->conf.text_event, &img->timestamp_tv, NULL, 0);
MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Motion detected - starting event %d"),
cnt->event_nr);
MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO
, _("Motion detected - starting event %d.")
, cnt->event_nr);
/* EVENT_FIRSTMOTION triggers on_event_start_command and event_ffmpeg_newfile */
indx = cnt->imgs.image_ring_out-1;

View File

@@ -372,10 +372,6 @@ struct rotdata {
};
/*
* These used to be global variables but now each thread will have its
* own context
*/
struct context {
FILE *extpipe;
int extpipe_open;
@@ -441,7 +437,7 @@ struct context {
int event_nr;
int prev_event;
unsigned long long database_event_id;
char eventid[20]; /* Cam ID + Date/Time 99999yyyymmddhhmmss */
unsigned int lightswitch_framecounter;
char text_event_string[PATH_MAX]; /* The text for conv. spec. %C - */
int text_scale;

View File

@@ -388,20 +388,18 @@ static void mystrftime_long (const struct context *cnt, int width, const char *w
return;
}
if (SPECIFIERWORD("dbeventid")) {
#ifdef HAVE_PGSQL
if (cnt->eid_db_format == dbeid_no_return) {
MOTION_LOG(ERR, TYPE_DB, NO_ERRNO,
_("Used %{dbeventid} but sql_query_start returned no valid event ID"));
((struct context *)cnt)->eid_db_format = dbeid_use_error;
}
#endif
sprintf(out, "%*llu", width, cnt->database_event_id);
MOTION_LOG(ERR, TYPE_DB, NO_ERRNO,
_("{dbeventid} is not supported. Use eventid."));
return;
}
if (SPECIFIERWORD("ver")) {
sprintf(out, "%*s", width, VERSION);
return;
}
if (SPECIFIERWORD("eventid")) {
sprintf(out, "%*s", width, cnt->eventid);
return;
}
// Not a valid modifier keyword. Log the error and ignore.
MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO,