Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first implementation of sql_auditing #1367

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev/error_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace sqlite_orm {
index_is_out_of_bounds,
value_is_null,
no_tables_specified,
failure_to_init_logfile,
};
}

Expand Down
88 changes: 88 additions & 0 deletions dev/sql_auditing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once
#include <fstream>
#include "error_code.h"
#include <chrono>
#include <ctime> // For std::localtime and std::tm
#include <iomanip> // For std::put_time
#include <string>

#include "sql_auditing.h"

enum class auditing_behavior : signed char { OFF = 0, ON = 1 };

class sql_auditor_settings;

class sql_auditor {

std::ofstream log_file;
friend class sql_auditor_settings;
sql_auditor();
void open();
inline static sql_auditor& auditor() {
static sql_auditor auditor{};
return auditor;
}

public:
static void log(const std::string& message);
};

class sql_auditor_settings {
std::string destination_file = "sql_auditor.txt";
friend class sql_auditor;
sql_auditor_settings() {}
inline static sql_auditor_settings& settings() {
static sql_auditor_settings sql_settings;
return sql_settings;
}
auditing_behavior behavior = auditing_behavior::ON;
std::string format_str = "%Y-%m-%d %H:%M:%S";

public:
static void set_destination_file(const std::string& filename);
static void set_behavior(auditing_behavior behavior) {
settings().behavior = behavior;
}
static void set_format(const std::string& format_str) {
settings().format_str = format_str;
}
};

inline sql_auditor::sql_auditor() {
open();
}

inline void sql_auditor_settings::set_destination_file(const std::string& filename) {
settings().destination_file = filename;
sql_auditor::auditor().open();
}

inline void sql_auditor::open() {
using namespace std;
if(!log_file.is_open()) {
log_file.open(sql_auditor_settings::settings().destination_file, ios::trunc | ios::out);
if(!log_file.good()) {
throw std::system_error{sqlite_orm::orm_error_code::failure_to_init_logfile};
}
}
}

inline void sql_auditor::log(const std::string& message) {
// guard and exit if off
if(sql_auditor_settings::settings().behavior == auditing_behavior::OFF)
return;

// would use format if C++ 20
auto now = std::chrono::system_clock::now();

std::time_t now_time = std::chrono::system_clock::to_time_t(now);

// Convert to local time (std::tm structure)
// WARNING: localtime is not thread safe!
std::tm local_time = *std::localtime(&now_time);

// Print the local time in a human-readable format
auditor().log_file << "@: " << std::put_time(&local_time, sql_auditor_settings::settings().format_str.c_str())
<< " = ";
auditor().log_file << message << std::endl;
}
1 change: 1 addition & 0 deletions dev/statement_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "table_type_of.h"
#include "util.h"
#include "error_code.h"
#include "sql_auditing.h"
#include "schema/triggers.h"
#include "schema/column.h"
#include "schema/index.h"
Expand Down
4 changes: 4 additions & 0 deletions dev/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <utility> // std::move

#include "error_code.h"
#include "sql_auditing.h"

namespace sqlite_orm {

Expand Down Expand Up @@ -61,6 +62,7 @@ namespace sqlite_orm {
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logs are enabled always even though devs don't need them?

return stmt;
}

Expand All @@ -69,6 +71,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand All @@ -79,6 +82,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand Down
97 changes: 97 additions & 0 deletions include/sqlite_orm/sqlite_orm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2863,6 +2863,7 @@ namespace sqlite_orm {
index_is_out_of_bounds,
value_is_null,
no_tables_specified,
failure_to_init_logfile,
};
}

Expand Down Expand Up @@ -13447,6 +13448,97 @@ namespace sqlite_orm {

// #include "error_code.h"

// #include "sql_auditing.h"

#include <fstream>
// #include "error_code.h"

#include <chrono>
#include <ctime> // For std::localtime and std::tm
#include <iomanip> // For std::put_time
#include <string>

// #include "sql_auditing.h"

enum class auditing_behavior : signed char { OFF = 0, ON = 1 };

class sql_auditor_settings;

class sql_auditor {

std::ofstream log_file;
friend class sql_auditor_settings;
sql_auditor();
void open();
inline static sql_auditor& auditor() {
static sql_auditor auditor{};
return auditor;
}

public:
static void log(const std::string& message);
};

class sql_auditor_settings {
std::string destination_file = "sql_auditor.txt";
friend class sql_auditor;
sql_auditor_settings() {}
inline static sql_auditor_settings& settings() {
static sql_auditor_settings sql_settings;
return sql_settings;
}
auditing_behavior behavior = auditing_behavior::ON;
std::string format_str = "%Y-%m-%d %H:%M:%S";

public:
static void set_destination_file(const std::string& filename);
static void set_behavior(auditing_behavior behavior) {
settings().behavior = behavior;
}
static void set_format(const std::string& format_str) {
settings().format_str = format_str;
}
};

inline sql_auditor::sql_auditor() {
open();
}

inline void sql_auditor_settings::set_destination_file(const std::string& filename) {
settings().destination_file = filename;
sql_auditor::auditor().open();
}

inline void sql_auditor::open() {
using namespace std;
if(!log_file.is_open()) {
log_file.open(sql_auditor_settings::settings().destination_file, ios::trunc | ios::out);
if(!log_file.good()) {
throw std::system_error{sqlite_orm::orm_error_code::failure_to_init_logfile};
}
}
}

inline void sql_auditor::log(const std::string& message) {
// guard and exit if off
if(sql_auditor_settings::settings().behavior == auditing_behavior::OFF)
return;

// would use format if C++ 20
auto now = std::chrono::system_clock::now();

std::time_t now_time = std::chrono::system_clock::to_time_t(now);

// Convert to local time (std::tm structure)
// WARNING: localtime is not thread safe!
std::tm local_time = *std::localtime(&now_time);

// Print the local time in a human-readable format
auditor().log_file << "@: " << std::put_time(&local_time, sql_auditor_settings::settings().format_str.c_str())
<< " = ";
auditor().log_file << message << std::endl;
}

namespace sqlite_orm {

/**
Expand Down Expand Up @@ -13502,6 +13594,7 @@ namespace sqlite_orm {
if(sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
return stmt;
}

Expand All @@ -13510,6 +13603,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand All @@ -13520,6 +13614,7 @@ namespace sqlite_orm {
if(rc != SQLITE_OK) {
throw_translated_sqlite_error(db);
}
sql_auditor::log(query);
}

inline void perform_exec(sqlite3* db,
Expand Down Expand Up @@ -19119,6 +19214,8 @@ namespace sqlite_orm {

// #include "error_code.h"

// #include "sql_auditing.h"

// #include "schema/triggers.h"

#include <memory>
Expand Down
7 changes: 7 additions & 0 deletions tests/prepared_statement_tests/select.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ TEST_CASE("Prepared select") {

auto filename = "prepared.sqlite";
remove(filename);

#define JD_AUDITING_SETTINGS
#ifdef JD_AUDITING_SETTINGS
sql_auditor_settings::set_destination_file("log.txt");
sql_auditor_settings::set_behavior(auditing_behavior::ON);
sql_auditor_settings::set_format("%H:%M:%S");
#endif
auto storage = make_storage(filename,
make_index("user_id_index", &User::id),
make_table("users",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ TEST_CASE("statement_serializer insert/replace") {
expected =
R"(REPLACE INTO "users" SELECT "users_backup"."id", "users_backup"."name" FROM "users_backup")";
}
#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
SECTION("With clause") {
constexpr orm_cte_moniker auto data = "data"_cte;
Expand Down Expand Up @@ -374,7 +374,7 @@ TEST_CASE("statement_serializer insert/replace") {
}
}
SECTION("With clause") {
#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
constexpr orm_cte_moniker auto data = "data"_cte;
constexpr auto cteExpression = cte<data>().as(select(asterisk<UserBackup>()));
Expand Down
2 changes: 1 addition & 1 deletion tests/statement_serializer_tests/statements/remove_all.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ TEST_CASE("statement_serializer remove_all") {
value = serialize(expression, context);
expected = R"(DELETE FROM "users" WHERE ("id" = 1) LIMIT 1)";
}
#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
SECTION("With clause") {
constexpr orm_cte_moniker auto data = "data"_cte;
Expand Down
2 changes: 1 addition & 1 deletion tests/statement_serializer_tests/statements/update_all.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ TEST_CASE("statement_serializer update_all") {
expected =
R"(UPDATE "contacts" SET "phone" = (SELECT "customers"."Phone" FROM "customers" WHERE ("customers"."CustomerId" = 1)) WHERE ("contact_id" = 1))";
}
#if(SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#if (SQLITE_VERSION_NUMBER >= 3008003) && defined(SQLITE_ORM_WITH_CTE)
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
SECTION("With clause") {
constexpr orm_cte_moniker auto data = "data"_cte;
Expand Down
Loading