You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
456 lines
14 KiB
C
456 lines
14 KiB
C
![]()
9 years ago
|
/*
|
||
|
* log_storage_sqlite.c
|
||
|
*
|
||
|
* Copyright 2016 Michael Zillgith
|
||
|
*
|
||
|
* This file is part of libIEC61850.
|
||
|
*
|
||
|
* libIEC61850 is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* libIEC61850 is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
* See COPYING file for the complete license text.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "logging_api.h"
|
||
|
#include "libiec61850_platform_includes.h"
|
||
|
|
||
|
#include <sqlite3.h>
|
||
|
|
||
|
#ifndef DEBUG_LOG_STORAGE_DRIVER
|
||
|
#define DEBUG_LOG_STORAGE_DRIVER 0
|
||
|
#endif
|
||
|
|
||
|
static uint64_t
|
||
|
SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp);
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode);
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
|
||
|
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
|
||
|
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter);
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
|
||
|
uint64_t* oldEntry, uint64_t* oldEntryTime);
|
||
|
|
||
|
static void
|
||
|
SqliteLogStorage_destroy(LogStorage self);
|
||
|
|
||
|
|
||
|
typedef struct sSqliteLogStorage {
|
||
|
char* filename;
|
||
|
sqlite3* db;
|
||
|
sqlite3_stmt* insertEntryStmt;
|
||
|
sqlite3_stmt* insertEntryDataStmt;
|
||
|
sqlite3_stmt* getEntriesWithRange;
|
||
|
sqlite3_stmt* getEntriesAfter;
|
||
|
sqlite3_stmt* getEntryData;
|
||
|
sqlite3_stmt* getOldEntry;
|
||
|
sqlite3_stmt* getNewEntry;
|
||
|
} SqliteLogStorage;
|
||
|
|
||
|
static const char* CREATE_TABLE_ENTRYS = "create table if not exists Entries (entryID integer primary key, timeOfEntry integer)";
|
||
|
static const char* CREATE_TABLE_ENTRY_DATA = "create table if not exists EntryData (entryID integer, dataRef text, value blob, reasonCode integer)";
|
||
|
static const char* INSERT_ENTRY = "insert into Entries (timeOfEntry) values (?)";
|
||
|
static const char* INSERT_ENTRY_DATA = "insert into EntryData (entryID, dataRef, value, reasonCode) values (?,?,?,?)";
|
||
|
static const char* GET_ENTRIES_WITH_RANGE = "select entryID, timeOfEntry from Entries where timeOfEntry >= ? and timeOfEntry <= ?";
|
||
|
static const char* GET_ENTRIES_AFTER = "select entryID, timeOfEntry from Entries where entryID > ?";
|
||
|
static const char* GET_ENTRY_DATA = "select dataRef, value, reasonCode from EntryData where entryID = ?";
|
||
|
|
||
|
static const char* GET_OLD_ENTRY = "select * from Entries where entryID = (select min(entryID) from Entries where timeOfEntry = (select min(timeOfEntry) from Entries))";
|
||
|
static const char* GET_NEW_ENTRY = "select * from Entries where entryID = (select max(entryID) from Entries where timeOfEntry = (select max(timeOfEntry) from Entries))";
|
||
|
|
||
|
LogStorage
|
||
|
SqliteLogStorage_createInstance(const char* filename)
|
||
|
{
|
||
|
|
||
|
sqlite3* db = NULL;
|
||
|
sqlite3_stmt* insertEntryStmt = NULL;
|
||
|
sqlite3_stmt* insertEntryDataStmt = NULL;
|
||
|
sqlite3_stmt* getEntriesWithRange = NULL;
|
||
|
sqlite3_stmt* getEntriesAfter = NULL;
|
||
|
sqlite3_stmt* getEntryData = NULL;
|
||
|
sqlite3_stmt* getOldEntry = NULL;
|
||
|
sqlite3_stmt* getNewEntry = NULL;
|
||
|
char *zErrMsg = 0;
|
||
|
|
||
|
int rc = sqlite3_open(filename, &db);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_exec(db, CREATE_TABLE_ENTRYS, NULL, 0, &zErrMsg);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_exec(db, CREATE_TABLE_ENTRY_DATA, NULL, 0, &zErrMsg);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare(db, INSERT_ENTRY, -1, &insertEntryStmt, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare(db, INSERT_ENTRY_DATA, -1, &insertEntryDataStmt, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare_v2(db, GET_ENTRIES_WITH_RANGE, -1, &getEntriesWithRange, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare_v2(db, GET_ENTRIES_AFTER, -1, &getEntriesAfter, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare_v2(db, GET_ENTRY_DATA, -1, &getEntryData, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare_v2(db, GET_OLD_ENTRY, -1, &getOldEntry, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_prepare_v2(db, GET_NEW_ENTRY, -1, &getNewEntry, NULL);
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
LogStorage self = (LogStorage) GLOBAL_CALLOC(1, sizeof(struct sLogStorage));
|
||
|
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) GLOBAL_CALLOC(1, sizeof(struct sSqliteLogStorage));
|
||
|
instanceData->filename = copyString(filename);
|
||
|
instanceData->db = db;
|
||
|
instanceData->insertEntryStmt = insertEntryStmt;
|
||
|
instanceData->insertEntryDataStmt = insertEntryDataStmt;
|
||
|
instanceData->getEntriesWithRange = getEntriesWithRange;
|
||
|
instanceData->getEntriesAfter = getEntriesAfter;
|
||
|
instanceData->getEntryData = getEntryData;
|
||
|
instanceData->getOldEntry = getOldEntry;
|
||
|
instanceData->getNewEntry = getNewEntry;
|
||
|
|
||
|
self->instanceData = (void*) instanceData;
|
||
|
|
||
|
self->addEntry = SqliteLogStorage_addEntry;
|
||
|
self->addEntryData = SqliteLogStorage_addEntryData;
|
||
|
self->getEntries = SqliteLogStorage_getEntries;
|
||
|
self->getEntriesAfter = SqliteLogStorage_getEntriesAfter;
|
||
|
self->getOldestAndNewestEntries = SqliteLogStorage_getOldestAndNewestEntries;
|
||
|
self->destroy = SqliteLogStorage_destroy;
|
||
|
|
||
|
return self;
|
||
|
|
||
|
exit_with_error:
|
||
|
if (DEBUG_LOG_STORAGE_DRIVER)
|
||
|
printf("LOG_STORAGE_DRIVER: sqlite - failed to create LogStorage instance!\n");
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
static uint64_t
|
||
|
SqliteLogStorage_addEntry(LogStorage self, uint64_t timestamp)
|
||
|
{
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||
|
|
||
|
sqlite3* db = instanceData->db;
|
||
|
int rc;
|
||
|
char *zErrMsg = 0;
|
||
|
|
||
|
rc = sqlite3_bind_int64(instanceData->insertEntryStmt, 1, (sqlite_int64) timestamp);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_step(instanceData->insertEntryStmt);
|
||
|
|
||
|
if (rc != SQLITE_DONE)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
uint64_t id = sqlite3_last_insert_rowid(db);
|
||
|
|
||
|
if (DEBUG_LOG_STORAGE_DRIVER)
|
||
|
printf("LOG_STORAGE_DRIVER: sqlite - new entry with ID = %lu\n", id);
|
||
|
|
||
|
rc = sqlite3_reset(instanceData->insertEntryStmt);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
return id;
|
||
|
|
||
|
exit_with_error:
|
||
|
if (DEBUG_LOG_STORAGE_DRIVER)
|
||
|
printf("LOG_STORAGE_DRIVER: sqlite - failed to add entry to log!\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_addEntryData(LogStorage self, uint64_t entryID, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode)
|
||
|
{
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||
|
|
||
|
sqlite3* db = instanceData->db;
|
||
|
|
||
|
int rc;
|
||
|
|
||
|
char *zErrMsg = 0;
|
||
|
|
||
|
rc = sqlite3_bind_int64(instanceData->insertEntryDataStmt, 1, (sqlite_int64) entryID);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_bind_text(instanceData->insertEntryDataStmt, 2, dataRef, -1, SQLITE_STATIC);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_bind_blob(instanceData->insertEntryDataStmt, 3, data, dataSize, SQLITE_STATIC);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_bind_int(instanceData->insertEntryDataStmt, 4, reasonCode);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_step(instanceData->insertEntryDataStmt);
|
||
|
|
||
|
if (rc != SQLITE_DONE)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
rc = sqlite3_reset(instanceData->insertEntryDataStmt);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
goto exit_with_error;
|
||
|
|
||
|
return true;
|
||
|
|
||
|
exit_with_error:
|
||
|
if (DEBUG_LOG_STORAGE_DRIVER)
|
||
|
printf("LOG_STORAGE_DRIVER: sqlite - failed to add entry data!\n");
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
getEntryData(LogStorage self, uint64_t entryID, LogEntryDataCallback entryDataCallback, void* parameter)
|
||
|
{
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||
|
|
||
|
int rc;
|
||
|
|
||
|
rc = sqlite3_bind_int64(instanceData->getEntryData, 1, entryID);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("getEntryData 1 rc:%i\n", rc);
|
||
|
|
||
|
bool sendFinalEvent = true;
|
||
|
|
||
|
while ((rc = sqlite3_step(instanceData->getEntryData)) == SQLITE_ROW) {
|
||
|
|
||
|
const char* dataRef = sqlite3_column_text(instanceData->getEntryData, 0);
|
||
|
const uint8_t* data = sqlite3_column_blob(instanceData->getEntryData, 1);
|
||
|
int dataSize = sqlite3_column_bytes(instanceData->getEntryData, 1);
|
||
|
int reasonCode = sqlite3_column_int(instanceData->getEntryData, 2);
|
||
|
|
||
|
if (entryDataCallback != NULL) {
|
||
|
if (entryDataCallback(parameter, dataRef, data, dataSize, (uint8_t) reasonCode, true) == false) {
|
||
|
sendFinalEvent = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sendFinalEvent) {
|
||
|
if (entryDataCallback != NULL)
|
||
|
entryDataCallback(parameter, NULL, NULL, 0, (uint8_t) 0, false);
|
||
|
}
|
||
|
|
||
|
rc = sqlite3_reset(instanceData->getEntryData);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("getEntryData reset rc:%i\n", rc);
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_getEntries(LogStorage self, uint64_t startingTime, uint64_t endingTime,
|
||
|
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter)
|
||
|
{
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||
|
|
||
|
sqlite3* db = instanceData->db;
|
||
|
|
||
|
int rc;
|
||
|
|
||
|
rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 1, startingTime);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("SqliteLogStorage_getEntries 1 rc:%i\n", rc);
|
||
|
|
||
|
rc = sqlite3_bind_int64(instanceData->getEntriesWithRange, 2, endingTime);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("SqliteLogStorage_getEntries 2 rc:%i\n", rc);
|
||
|
|
||
|
bool sendFinalEvent = true;
|
||
|
|
||
|
while((rc = sqlite3_step(instanceData->getEntriesWithRange)) == SQLITE_ROW) {
|
||
|
int col;
|
||
|
|
||
|
uint64_t entryID = sqlite3_column_int64(instanceData->getEntriesWithRange, 0);
|
||
|
uint64_t timestamp = sqlite3_column_int64(instanceData->getEntriesWithRange, 1);
|
||
|
|
||
|
if (entryCallback != NULL) {
|
||
|
if (entryCallback(parameter, timestamp, entryID, true) == false) {
|
||
|
sendFinalEvent = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
getEntryData(self, entryID, entryDataCallback, parameter);
|
||
|
}
|
||
|
|
||
|
if (sendFinalEvent)
|
||
|
if (entryCallback != NULL)
|
||
|
entryCallback(parameter, 0, 0, false);
|
||
|
|
||
|
|
||
|
rc = sqlite3_reset(instanceData->getEntriesWithRange);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("SqliteLogStorage_getEntries reset rc:%i\n", rc);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_getOldestAndNewestEntries(LogStorage self, uint64_t* newEntry, uint64_t* newEntryTime,
|
||
|
uint64_t* oldEntry, uint64_t* oldEntryTime)
|
||
|
{
|
||
|
bool validOldEntry = false;
|
||
|
bool validNewEntry = false;
|
||
|
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||
|
|
||
|
sqlite3* db = instanceData->db;
|
||
|
|
||
|
int rc;
|
||
|
|
||
|
/* Get oldest entry */
|
||
|
|
||
|
rc = sqlite3_step(instanceData->getOldEntry);
|
||
|
|
||
|
if (rc == SQLITE_ROW) {
|
||
|
*oldEntry = sqlite3_column_int64(instanceData->getOldEntry, 0);
|
||
|
*oldEntryTime = sqlite3_column_int64(instanceData->getOldEntry, 1);
|
||
|
validNewEntry = true;
|
||
|
}
|
||
|
|
||
|
sqlite3_reset(instanceData->getOldEntry);
|
||
|
|
||
|
/* Get newest entry */
|
||
|
|
||
|
rc = sqlite3_step(instanceData->getNewEntry);
|
||
|
|
||
|
if (rc == SQLITE_ROW) {
|
||
|
*newEntry = sqlite3_column_int64(instanceData->getNewEntry, 0);
|
||
|
*newEntryTime = sqlite3_column_int64(instanceData->getNewEntry, 1);
|
||
|
validOldEntry = true;
|
||
|
}
|
||
|
|
||
|
sqlite3_reset(instanceData->getNewEntry);
|
||
|
|
||
|
return (validOldEntry && validNewEntry);
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
SqliteLogStorage_getEntriesAfter(LogStorage self, uint64_t startingTime, uint64_t entryID,
|
||
|
LogEntryCallback entryCallback, LogEntryDataCallback entryDataCallback, void* parameter)
|
||
|
{
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) (self->instanceData);
|
||
|
|
||
|
sqlite3* db = instanceData->db;
|
||
|
|
||
|
int rc;
|
||
|
|
||
|
rc = sqlite3_bind_int64(instanceData->getEntriesAfter, 1, entryID);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("SqliteLogStorage_getEntriesAfter 1 rc:%i\n", rc);
|
||
|
|
||
|
bool sendFinalEvent = true;
|
||
|
|
||
|
while ((rc = sqlite3_step(instanceData->getEntriesAfter)) == SQLITE_ROW) {
|
||
|
int col;
|
||
|
|
||
|
uint64_t entryID = sqlite3_column_int64(instanceData->getEntriesAfter, 0);
|
||
|
uint64_t timestamp = sqlite3_column_int64(instanceData->getEntriesAfter, 1);
|
||
|
|
||
|
if (entryCallback != NULL) {
|
||
|
if (entryCallback(parameter, timestamp, entryID, true) == false) {
|
||
|
sendFinalEvent = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
getEntryData(self, entryID, entryDataCallback, parameter);
|
||
|
}
|
||
|
|
||
|
if (sendFinalEvent)
|
||
|
if (entryCallback != NULL)
|
||
|
entryCallback(parameter, 0, 0, false);
|
||
|
|
||
|
rc = sqlite3_reset(instanceData->getEntriesAfter);
|
||
|
|
||
|
if (rc != SQLITE_OK)
|
||
|
printf("SqliteLogStorage_getEntriesAfter reset rc:%i\n", rc);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
SqliteLogStorage_destroy(LogStorage self)
|
||
|
{
|
||
|
SqliteLogStorage* instanceData = (SqliteLogStorage*) self->instanceData;
|
||
|
|
||
|
sqlite3_finalize(instanceData->insertEntryStmt);
|
||
|
sqlite3_finalize(instanceData->insertEntryDataStmt);
|
||
|
sqlite3_finalize(instanceData->getEntriesWithRange);
|
||
|
sqlite3_finalize(instanceData->getEntriesAfter);
|
||
|
sqlite3_finalize(instanceData->getEntryData);
|
||
|
sqlite3_finalize(instanceData->getOldEntry);
|
||
|
sqlite3_finalize(instanceData->getNewEntry);
|
||
|
|
||
|
if (sqlite3_close(instanceData->db) != SQLITE_OK)
|
||
|
printf("SqliteLogStorage: failed to close database %s!\n", instanceData->filename);
|
||
|
|
||
|
GLOBAL_FREEMEM(instanceData->filename);
|
||
|
GLOBAL_FREEMEM(instanceData);
|
||
|
GLOBAL_FREEMEM(self);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|