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.
libiec61850/src/logging/drivers/sqlite/log_storage_sqlite.c

466 lines
14 KiB
C

/*
* 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)
{
printf("SQLITE-DRIVER: add entry\n");
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;
}
else {
*oldEntry = 0;
*oldEntryTime = 0;
}
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;
}
else {
*newEntry = 0;
*newEntryTime = 0;
}
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);
}