Initial commit
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
# NOTE: VARIABLES
|
||||
|
||||
# Define the path to the directory with SQL scripts
|
||||
set(SQL_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/core/sql")
|
||||
set(SQL_DEST_DIR "${CMAKE_CURRENT_BINARY_DIR}/core/sql")
|
||||
|
||||
# Define directories for tests
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests")
|
||||
set(TESTS_SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests_sources")
|
||||
|
||||
# Specify paths to test files
|
||||
set(TEST_FILES
|
||||
${TESTS_DIR}/main_test.cpp
|
||||
${TESTS_DIR}/sqlite_database_crud_test.cpp
|
||||
${TESTS_DIR}/sqlite_database_foreign_key_test.cpp
|
||||
${TESTS_DIR}/sqlite_database_trigger_test.cpp
|
||||
)
|
||||
|
||||
# Specify paths to test sources
|
||||
set(TEST_SOURCES
|
||||
${TESTS_SOURCES_DIR}/sqlite_database.cpp
|
||||
${TESTS_SOURCES_DIR}/sqlite_file_executor.cpp
|
||||
${TESTS_SOURCES_DIR}/sqlite_statement.cpp
|
||||
${TESTS_SOURCES_DIR}/file_reader.cpp
|
||||
)
|
||||
|
||||
# NOTE: OUTPUT
|
||||
|
||||
# Output test file paths to console
|
||||
message(STATUS "Test files: ${TEST_FILES}")
|
||||
message(STATUS "Test sources: ${TEST_SOURCES}")
|
||||
|
||||
# Output variable values to console
|
||||
message(STATUS "Tests directory: ${TESTS_DIR}")
|
||||
message(STATUS "Test sources directory: ${TESTS_SOURCES_DIR}")
|
||||
message(STATUS "SQL scripts source directory: ${SQL_SCRIPTS_DIR}")
|
||||
message(STATUS "SQL scripts destination directory: ${SQL_DEST_DIR}")
|
||||
|
||||
# NOTE: BUILD
|
||||
|
||||
# Create the test executable
|
||||
add_executable(tests_schema ${TEST_FILES} ${TEST_SOURCES})
|
||||
|
||||
# Include directories for headers
|
||||
include_directories(${TESTS_SOURCES_DIR})
|
||||
|
||||
# Link the test executable with required libraries
|
||||
target_link_libraries(tests_schema PRIVATE
|
||||
GTest::GTest
|
||||
GTest::Main
|
||||
SQLite::SQLite3
|
||||
)
|
||||
|
||||
# NOTE: BUILD END
|
||||
|
||||
# Copy SQL scripts to the build directory
|
||||
add_custom_target(copy_sql_files ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${SQL_DEST_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SQL_SCRIPTS_DIR}/*.sql ${SQL_DEST_DIR}
|
||||
COMMENT "Copying SQL scripts to ${SQL_DEST_DIR}"
|
||||
)
|
||||
|
||||
# Ensure SQL scripts are copied before running tests
|
||||
add_dependencies(tests_schema copy_sql_files)
|
||||
|
||||
# Pass the SQL scripts path to the tests via a macro
|
||||
target_compile_definitions(tests_schema PRIVATE SQL_SCRIPTS_PATH="${SQL_DEST_DIR}")
|
||||
|
||||
# Register the tests
|
||||
include(GoogleTest)
|
||||
gtest_discover_tests(tests_schema)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
#include "sqlite_database.h"
|
||||
#include "sqlite_file_executor.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// WARNING: Foreign keys are off
|
||||
// WARNING: Triggers are off
|
||||
|
||||
const std::string TEST_DB = "test.db";
|
||||
|
||||
// Test fixture for SQLiteDatabase
|
||||
class SQLiteDatabaseCRUDTest : public ::testing::Test {
|
||||
protected:
|
||||
SQLiteDatabase *db_;
|
||||
|
||||
void SetUp() override {
|
||||
db_ = new SQLiteDatabase(TEST_DB);
|
||||
SQLiteFileExecutor executor(TEST_DB);
|
||||
executor.executeSQLFile("sql/cinema_db_drop_table.sql");
|
||||
executor.executeSQLFile("sql/cinema_db_create_table.sql");
|
||||
|
||||
// Not necessarily, since the default setting
|
||||
std::string sql;
|
||||
FileReaderInterface *reader = &executor;
|
||||
reader->read("sql/cinema_db_foreign_key_off.sql", sql);
|
||||
db_->executeNonQuery(sql);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
delete db_;
|
||||
remove(TEST_DB.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
// INFO: ----- GENERAL -----
|
||||
|
||||
// Check that the test fixture is working
|
||||
class SimpleClass : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {}
|
||||
void TearDown() override {}
|
||||
};
|
||||
// Simle test
|
||||
TEST_F(SimpleClass, SimpleTest) { EXPECT_EQ(1, 1); }
|
||||
|
||||
// INFO: ----- GENERAL -----
|
||||
|
||||
// Test checking if the database is available
|
||||
TEST_F(SQLiteDatabaseCRUDTest, IsDatabaseAvailable) {
|
||||
EXPECT_TRUE(db_->isDatabaseAvailable());
|
||||
}
|
||||
|
||||
// INFO: ----- GENRE -----
|
||||
|
||||
// Test adding a genre
|
||||
TEST_F(SQLiteDatabaseCRUDTest, AddGenre) {
|
||||
NewGenre genre{"Action"};
|
||||
EXPECT_TRUE(db_->addGenre(genre));
|
||||
|
||||
auto genres = db_->getAllGenres();
|
||||
ASSERT_EQ(genres.size(), 1);
|
||||
EXPECT_EQ(genres[0].genre_name, "Action");
|
||||
}
|
||||
|
||||
// Test getting a genre by ID
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetGenreById) {
|
||||
NewGenre genre{"Drama"};
|
||||
db_->addGenre(genre);
|
||||
|
||||
Genre retrieved_genre = db_->getGenre(1);
|
||||
EXPECT_EQ(retrieved_genre.genre_id, 1);
|
||||
EXPECT_EQ(retrieved_genre.genre_name, "Drama");
|
||||
}
|
||||
|
||||
// Test updating a genre
|
||||
TEST_F(SQLiteDatabaseCRUDTest, UpdateGenre) {
|
||||
NewGenre genre{"Drama"};
|
||||
db_->addGenre(genre);
|
||||
|
||||
Genre updated_genre = db_->getGenre(1);
|
||||
updated_genre.genre_name = "Horror";
|
||||
EXPECT_TRUE(db_->updateGenre(updated_genre));
|
||||
|
||||
Genre retrieved_genre = db_->getGenre(1);
|
||||
EXPECT_EQ(retrieved_genre.genre_id, 1);
|
||||
EXPECT_EQ(retrieved_genre.genre_name, "Horror");
|
||||
}
|
||||
|
||||
// Test removing a genre
|
||||
TEST_F(SQLiteDatabaseCRUDTest, RemoveGenre) {
|
||||
NewGenre genre{"Horror"};
|
||||
db_->addGenre(genre);
|
||||
EXPECT_TRUE(db_->removeGenre(1));
|
||||
|
||||
auto genres = db_->getAllGenres();
|
||||
EXPECT_TRUE(genres.empty());
|
||||
}
|
||||
|
||||
// Test getting all genres
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetAllGenres) {
|
||||
db_->addGenre({"Comedy"});
|
||||
db_->addGenre({"Thriller"});
|
||||
|
||||
auto genres = db_->getAllGenres();
|
||||
ASSERT_EQ(genres.size(), 2);
|
||||
EXPECT_EQ(genres[0].genre_name, "Comedy");
|
||||
EXPECT_EQ(genres[1].genre_name, "Thriller");
|
||||
}
|
||||
|
||||
// INFO: ----- MOVIE -----
|
||||
|
||||
// Test adding a movie
|
||||
TEST_F(SQLiteDatabaseCRUDTest, AddMovie) {
|
||||
NewMovie movie{"The Shawshank Redemption", 1, 142};
|
||||
EXPECT_TRUE(db_->addMovie(movie));
|
||||
|
||||
auto movies = db_->getAllMovies();
|
||||
ASSERT_EQ(movies.size(), 1);
|
||||
EXPECT_EQ(movies[0].title, "The Shawshank Redemption");
|
||||
EXPECT_EQ(movies[0].genre_id, 1);
|
||||
EXPECT_EQ(movies[0].duration, 142);
|
||||
}
|
||||
|
||||
// Test getting a movie by ID
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetMovieById) {
|
||||
NewMovie movie{"The Godfather", 2, 175};
|
||||
db_->addMovie(movie);
|
||||
|
||||
Movie retrieved_movie = db_->getMovie(1);
|
||||
EXPECT_EQ(retrieved_movie.movie_id, 1);
|
||||
EXPECT_EQ(retrieved_movie.title, "The Godfather");
|
||||
EXPECT_EQ(retrieved_movie.genre_id, 2);
|
||||
EXPECT_EQ(retrieved_movie.duration, 175);
|
||||
}
|
||||
|
||||
// Test removing a movie
|
||||
TEST_F(SQLiteDatabaseCRUDTest, RemoveMovie) {
|
||||
NewMovie movie{"The Dark Knight", 3, 152};
|
||||
db_->addMovie(movie);
|
||||
EXPECT_TRUE(db_->removeMovie(1));
|
||||
|
||||
auto movies = db_->getAllMovies();
|
||||
EXPECT_TRUE(movies.empty());
|
||||
}
|
||||
|
||||
// Test updating a movie
|
||||
TEST_F(SQLiteDatabaseCRUDTest, UpdateMovie) {
|
||||
NewMovie movie{"The Shawshank Redemption", 1, 142};
|
||||
db_->addMovie(movie);
|
||||
|
||||
Movie updated_movie = db_->getMovie(1);
|
||||
updated_movie.title = "The Godfather";
|
||||
updated_movie.duration = 155;
|
||||
EXPECT_TRUE(db_->updateMovie(updated_movie));
|
||||
|
||||
Movie retrieved_movie = db_->getMovie(1);
|
||||
EXPECT_EQ(retrieved_movie.movie_id, 1);
|
||||
EXPECT_EQ(retrieved_movie.title, "The Godfather");
|
||||
EXPECT_EQ(retrieved_movie.genre_id, 1);
|
||||
EXPECT_EQ(retrieved_movie.duration, 155);
|
||||
}
|
||||
|
||||
// Test getting all movies
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetAllMovies) {
|
||||
db_->addMovie({"The Shawshank Redemption", 1, 142});
|
||||
db_->addMovie({"The Godfather", 2, 175});
|
||||
|
||||
auto movies = db_->getAllMovies();
|
||||
ASSERT_EQ(movies.size(), 2);
|
||||
EXPECT_EQ(movies[0].title, "The Shawshank Redemption");
|
||||
EXPECT_EQ(movies[0].genre_id, 1);
|
||||
EXPECT_EQ(movies[0].duration, 142);
|
||||
EXPECT_EQ(movies[1].title, "The Godfather");
|
||||
EXPECT_EQ(movies[1].genre_id, 2);
|
||||
EXPECT_EQ(movies[1].duration, 175);
|
||||
}
|
||||
|
||||
// INFO: ----- HALL -----
|
||||
|
||||
// Test adding a hall
|
||||
TEST_F(SQLiteDatabaseCRUDTest, AddHall) {
|
||||
NewHall hall{"Hall 1", 132};
|
||||
EXPECT_TRUE(db_->addHall(hall));
|
||||
|
||||
auto halls = db_->getAllHalls();
|
||||
ASSERT_EQ(halls.size(), 1);
|
||||
EXPECT_EQ(halls[0].hall_id, 1);
|
||||
EXPECT_EQ(halls[0].hall_name, "Hall 1");
|
||||
EXPECT_EQ(halls[0].capacity, 132);
|
||||
}
|
||||
|
||||
// Test getting a hall by ID
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetHallById) {
|
||||
NewHall hall{"Hall 2", 142};
|
||||
db_->addHall(hall);
|
||||
|
||||
Hall retrieved_hall = db_->getHall(1);
|
||||
EXPECT_EQ(retrieved_hall.hall_id, 1);
|
||||
EXPECT_EQ(retrieved_hall.hall_name, "Hall 2");
|
||||
EXPECT_EQ(retrieved_hall.capacity, 142);
|
||||
}
|
||||
|
||||
// Test removing a hall
|
||||
TEST_F(SQLiteDatabaseCRUDTest, RemoveHall) {
|
||||
NewHall hall{"Hall 3", 152};
|
||||
db_->addHall(hall);
|
||||
EXPECT_TRUE(db_->removeHall(1));
|
||||
|
||||
auto halls = db_->getAllHalls();
|
||||
EXPECT_TRUE(halls.empty());
|
||||
}
|
||||
|
||||
// Test updating a hall
|
||||
TEST_F(SQLiteDatabaseCRUDTest, UpdateHall) {
|
||||
NewHall hall{"Hall 4", 162};
|
||||
db_->addHall(hall);
|
||||
|
||||
Hall updated_hall = db_->getHall(1);
|
||||
updated_hall.hall_name = "Hall 5";
|
||||
updated_hall.capacity = 172;
|
||||
EXPECT_TRUE(db_->updateHall(updated_hall));
|
||||
|
||||
Hall retrieved_hall = db_->getHall(1);
|
||||
EXPECT_EQ(retrieved_hall.hall_id, 1);
|
||||
EXPECT_EQ(retrieved_hall.hall_name, "Hall 5");
|
||||
EXPECT_EQ(retrieved_hall.capacity, 172);
|
||||
}
|
||||
|
||||
// Test getting all halls
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetAllHalls) {
|
||||
db_->addHall({"Hall 6", 172});
|
||||
db_->addHall({"Hall 7", 182});
|
||||
|
||||
auto halls = db_->getAllHalls();
|
||||
ASSERT_EQ(halls.size(), 2);
|
||||
EXPECT_EQ(halls[0].hall_id, 1);
|
||||
EXPECT_EQ(halls[0].hall_name, "Hall 6");
|
||||
EXPECT_EQ(halls[0].capacity, 172);
|
||||
EXPECT_EQ(halls[1].hall_id, 2);
|
||||
EXPECT_EQ(halls[1].hall_name, "Hall 7");
|
||||
EXPECT_EQ(halls[1].capacity, 182);
|
||||
}
|
||||
|
||||
// INFO: ----- SESSION -----
|
||||
|
||||
// Test adding a session
|
||||
TEST_F(SQLiteDatabaseCRUDTest, AddSession) {
|
||||
NewSession session{1, 1, "2025-01-01 12:00:00", 10};
|
||||
EXPECT_TRUE(db_->addSession(session));
|
||||
|
||||
auto sessions = db_->getAllSessions();
|
||||
ASSERT_EQ(sessions.size(), 1);
|
||||
EXPECT_EQ(sessions[0].movie_id, 1);
|
||||
EXPECT_EQ(sessions[0].hall_id, 1);
|
||||
EXPECT_EQ(sessions[0].begin_at, "2025-01-01 12:00:00");
|
||||
EXPECT_EQ(sessions[0].ticket_price, 10);
|
||||
}
|
||||
|
||||
// Test getting a session by ID
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetSessionById) {
|
||||
NewSession session{2, 2, "2025-01-01 12:00:00", 10};
|
||||
db_->addSession(session);
|
||||
|
||||
Session retrieved_session = db_->getSession(1);
|
||||
EXPECT_EQ(retrieved_session.session_id, 1);
|
||||
EXPECT_EQ(retrieved_session.movie_id, 2);
|
||||
EXPECT_EQ(retrieved_session.hall_id, 2);
|
||||
EXPECT_EQ(retrieved_session.begin_at, "2025-01-01 12:00:00");
|
||||
EXPECT_EQ(retrieved_session.ticket_price, 10);
|
||||
}
|
||||
|
||||
// Test removing a session
|
||||
TEST_F(SQLiteDatabaseCRUDTest, RemoveSession) {
|
||||
NewSession session{3, 3, "2025-01-01 12:00:00", 10};
|
||||
db_->addSession(session);
|
||||
EXPECT_TRUE(db_->removeSession(1));
|
||||
|
||||
auto sessions = db_->getAllSessions();
|
||||
EXPECT_TRUE(sessions.empty());
|
||||
}
|
||||
|
||||
// Test updating a session
|
||||
TEST_F(SQLiteDatabaseCRUDTest, UpdateSession) {
|
||||
NewSession session{4, 4, "2025-01-01 12:00:00", 10};
|
||||
db_->addSession(session);
|
||||
|
||||
Session updated_session = db_->getSession(1);
|
||||
updated_session.begin_at = "2025-01-01 13:00:00";
|
||||
EXPECT_TRUE(db_->updateSession(updated_session));
|
||||
|
||||
Session retrieved_session = db_->getSession(1);
|
||||
EXPECT_EQ(retrieved_session.session_id, 1);
|
||||
EXPECT_EQ(retrieved_session.movie_id, 4);
|
||||
EXPECT_EQ(retrieved_session.hall_id, 4);
|
||||
EXPECT_EQ(retrieved_session.begin_at, "2025-01-01 13:00:00");
|
||||
EXPECT_EQ(retrieved_session.ticket_price, 10);
|
||||
}
|
||||
|
||||
// Test getting all sessions
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetAllSessions) {
|
||||
db_->addSession({5, 5, "2025-01-01 12:00:00", 10});
|
||||
db_->addSession({6, 6, "2025-01-01 13:00:00", 10});
|
||||
|
||||
auto sessions = db_->getAllSessions();
|
||||
ASSERT_EQ(sessions.size(), 2);
|
||||
EXPECT_EQ(sessions[0].movie_id, 5);
|
||||
EXPECT_EQ(sessions[0].hall_id, 5);
|
||||
EXPECT_EQ(sessions[0].begin_at, "2025-01-01 12:00:00");
|
||||
EXPECT_EQ(sessions[0].ticket_price, 10);
|
||||
EXPECT_EQ(sessions[1].movie_id, 6);
|
||||
EXPECT_EQ(sessions[1].hall_id, 6);
|
||||
EXPECT_EQ(sessions[1].begin_at, "2025-01-01 13:00:00");
|
||||
EXPECT_EQ(sessions[1].ticket_price, 10);
|
||||
}
|
||||
|
||||
// INFO: ----- TICKET -----
|
||||
|
||||
// Test adding a ticket
|
||||
TEST_F(SQLiteDatabaseCRUDTest, AddTicket) {
|
||||
NewTicket ticket{1, 1, "2025-01-01 12:00:00"};
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
|
||||
auto tickets = db_->getAllTickets();
|
||||
ASSERT_EQ(tickets.size(), 1);
|
||||
EXPECT_EQ(tickets[0].session_id, 1);
|
||||
EXPECT_EQ(tickets[0].seat_number, 1);
|
||||
EXPECT_EQ(tickets[0].sold_at, "2025-01-01 12:00:00");
|
||||
}
|
||||
|
||||
// Test getting a ticket by ID
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetTicketById) {
|
||||
NewTicket ticket{2, 2, "2025-01-01 12:00:00"};
|
||||
db_->addTicket(ticket);
|
||||
|
||||
Ticket retrieved_ticket = db_->getTicket(1);
|
||||
EXPECT_EQ(retrieved_ticket.ticket_id, 1);
|
||||
EXPECT_EQ(retrieved_ticket.session_id, 2);
|
||||
EXPECT_EQ(retrieved_ticket.seat_number, 2);
|
||||
EXPECT_EQ(retrieved_ticket.sold_at, "2025-01-01 12:00:00");
|
||||
}
|
||||
|
||||
// Test removing a ticket
|
||||
TEST_F(SQLiteDatabaseCRUDTest, RemoveTicket) {
|
||||
NewTicket ticket{3, 3, "2025-01-01 12:00:00"};
|
||||
db_->addTicket(ticket);
|
||||
EXPECT_TRUE(db_->removeTicket(1));
|
||||
|
||||
auto tickets = db_->getAllTickets();
|
||||
EXPECT_TRUE(tickets.empty());
|
||||
}
|
||||
|
||||
// Test updating a ticket
|
||||
TEST_F(SQLiteDatabaseCRUDTest, UpdateTicket) {
|
||||
NewTicket ticket{4, 4, "2025-01-01 12:00:00"};
|
||||
db_->addTicket(ticket);
|
||||
|
||||
Ticket updated_ticket = db_->getTicket(1);
|
||||
updated_ticket.seat_number = 5;
|
||||
EXPECT_TRUE(db_->updateTicket(updated_ticket));
|
||||
|
||||
Ticket retrieved_ticket = db_->getTicket(1);
|
||||
EXPECT_EQ(retrieved_ticket.ticket_id, 1);
|
||||
EXPECT_EQ(retrieved_ticket.session_id, 4);
|
||||
EXPECT_EQ(retrieved_ticket.seat_number, 5);
|
||||
EXPECT_EQ(retrieved_ticket.sold_at, "2025-01-01 12:00:00");
|
||||
}
|
||||
|
||||
// Test getting all tickets
|
||||
TEST_F(SQLiteDatabaseCRUDTest, GetAllTickets) {
|
||||
db_->addTicket({5, 5, "2025-01-01 12:00:00"});
|
||||
db_->addTicket({6, 6, "2025-01-01 13:00:00"});
|
||||
|
||||
auto tickets = db_->getAllTickets();
|
||||
ASSERT_EQ(tickets.size(), 2);
|
||||
EXPECT_EQ(tickets[0].session_id, 5);
|
||||
EXPECT_EQ(tickets[0].seat_number, 5);
|
||||
EXPECT_EQ(tickets[0].sold_at, "2025-01-01 12:00:00");
|
||||
EXPECT_EQ(tickets[1].session_id, 6);
|
||||
EXPECT_EQ(tickets[1].seat_number, 6);
|
||||
EXPECT_EQ(tickets[1].sold_at, "2025-01-01 13:00:00");
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "sqlite_database.h"
|
||||
#include "sqlite_file_executor.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// WARNING: Triggers are off
|
||||
|
||||
const std::string TEST_DB = "test.db";
|
||||
|
||||
// Test fixture for SQLiteDatabase
|
||||
class SQLiteDatabaseForeignKeyTest : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
SQLiteDatabase *db_;
|
||||
|
||||
public:
|
||||
// void pragmaForeignKeysOn() {
|
||||
// const std::string query = "PRAGMA foreign_keys = ON;";
|
||||
// db_->executeNonQuery(query);
|
||||
// }
|
||||
|
||||
std::vector<std::vector<std::string>> pragmaForeignKeys() {
|
||||
const std::string query = "PRAGMA foreign_keys;";
|
||||
auto results = db_->executeSelectQuery(query);
|
||||
return results;
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
db_ = new SQLiteDatabase(TEST_DB);
|
||||
SQLiteFileExecutor executor(TEST_DB);
|
||||
executor.executeSQLFile("sql/cinema_db_drop_table.sql");
|
||||
executor.executeSQLFile("sql/cinema_db_create_table.sql");
|
||||
|
||||
// pragmaForeignKeysOn();
|
||||
|
||||
std::string sql;
|
||||
FileReaderInterface *reader = &executor;
|
||||
reader->read("sql/cinema_db_foreign_key_on.sql", sql);
|
||||
db_->executeNonQuery(sql);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
delete db_;
|
||||
remove(TEST_DB.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
// INFO: ----- GENERAL -----
|
||||
|
||||
// Check pragma foreign_keys is true
|
||||
TEST_F(SQLiteDatabaseForeignKeyTest, PragmaForeignKeys) {
|
||||
auto results = pragmaForeignKeys();
|
||||
ASSERT_FALSE(results.empty());
|
||||
ASSERT_EQ(results[0][0], "1");
|
||||
}
|
||||
|
||||
// Test adding a movie
|
||||
TEST_F(SQLiteDatabaseForeignKeyTest, AddMovie) {
|
||||
NewMovie movie{"The Shawshank Redemption", 1, 142};
|
||||
EXPECT_FALSE(db_->addMovie(movie));
|
||||
|
||||
auto movies = db_->getAllMovies();
|
||||
ASSERT_TRUE(movies.empty());
|
||||
|
||||
NewGenre genre{"Drama"};
|
||||
EXPECT_TRUE(db_->addGenre(genre));
|
||||
EXPECT_TRUE(db_->addMovie(movie));
|
||||
movies = db_->getAllMovies();
|
||||
ASSERT_FALSE(movies.empty());
|
||||
}
|
||||
|
||||
// Test adding session
|
||||
TEST_F(SQLiteDatabaseForeignKeyTest, AddSession) {
|
||||
NewSession session{1, 1, "2025-01-01 12:00:00", 10};
|
||||
EXPECT_FALSE(db_->addSession(session));
|
||||
|
||||
NewHall hall{"Hall 1", 100};
|
||||
db_->addHall(hall);
|
||||
EXPECT_FALSE(db_->addSession(session));
|
||||
|
||||
NewGenre genre{"Drama"};
|
||||
db_->addGenre(genre);
|
||||
NewMovie movie{"The Shawshank Redemption", 1, 142};
|
||||
db_->addMovie(movie);
|
||||
EXPECT_TRUE(db_->addSession(session));
|
||||
}
|
||||
|
||||
// Test adding ticket
|
||||
TEST_F(SQLiteDatabaseForeignKeyTest, AddTicket) {
|
||||
NewTicket ticket{1, 1, "2025-01-01 12:00:00"};
|
||||
EXPECT_FALSE(db_->addTicket(ticket));
|
||||
|
||||
NewHall hall{"Hall 1", 100};
|
||||
db_->addHall(hall);
|
||||
NewGenre genre{"Drama"};
|
||||
db_->addGenre(genre);
|
||||
NewMovie movie{"The Shawshank Redemption", 1, 142};
|
||||
db_->addMovie(movie);
|
||||
NewSession session{1, 1, "2025-01-01 12:00:00", 10};
|
||||
db_->addSession(session);
|
||||
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "sqlite_database.h"
|
||||
#include "sqlite_file_executor.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// WARNING: Foreign keys are off
|
||||
|
||||
const std::string TEST_DB = "test.db";
|
||||
|
||||
// Test fixture for SQLiteDatabase
|
||||
class SQLiteDatabaseTriggerTest : public ::testing::Test {
|
||||
protected:
|
||||
SQLiteDatabase *db_;
|
||||
|
||||
void SetUp() override {
|
||||
db_ = new SQLiteDatabase(TEST_DB);
|
||||
SQLiteFileExecutor executor(TEST_DB);
|
||||
executor.executeSQLFile("sql/cinema_db_drop_table.sql");
|
||||
executor.executeSQLFile("sql/cinema_db_create_table.sql");
|
||||
executor.executeSQLFile("sql/cinema_db_drop_trigger.sql");
|
||||
executor.executeSQLFile("sql/cinema_db_create_trigger.sql");
|
||||
|
||||
// Not necessarily, since the default setting
|
||||
std::string sql;
|
||||
FileReaderInterface *reader = &executor;
|
||||
reader->read("sql/cinema_db_foreign_key_off.sql", sql);
|
||||
db_->executeNonQuery(sql);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
delete db_;
|
||||
remove(TEST_DB.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
// INFO: ----- TICKET -----
|
||||
|
||||
// Test trigger check_seat_number
|
||||
TEST_F(SQLiteDatabaseTriggerTest, CheckSeatNumber) {
|
||||
db_->addHall({"Hall 1", 100});
|
||||
db_->addSession({1, 1, "2025-01-01 12:00:00", 10});
|
||||
|
||||
NewTicket ticket;
|
||||
ticket.session_id = 1;
|
||||
ticket.sold_at = "2025-01-01 12:00:00";
|
||||
|
||||
ticket.seat_number = 101;
|
||||
EXPECT_FALSE(db_->addTicket(ticket));
|
||||
|
||||
ticket.seat_number = 100;
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
|
||||
ticket.seat_number = 1;
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
}
|
||||
|
||||
// Test trigger check_session_not_started
|
||||
TEST_F(SQLiteDatabaseTriggerTest, CheckSessionNotStarted) {
|
||||
db_->addSession({1, 1, "2025-01-01 12:00:00", 10});
|
||||
|
||||
NewTicket ticket;
|
||||
ticket.session_id = 1;
|
||||
ticket.seat_number = 1;
|
||||
|
||||
ticket.sold_at = "2025-01-01 13:00:00";
|
||||
EXPECT_FALSE(db_->addTicket(ticket));
|
||||
|
||||
ticket.sold_at = "2025-01-01 11:00:00";
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
|
||||
ticket.seat_number = 2;
|
||||
ticket.sold_at = "2025-01-01 12:00:00";
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
}
|
||||
|
||||
// Test trigger prevent_late_ticket_purchase
|
||||
TEST_F(SQLiteDatabaseTriggerTest, PreventLateTicketPurchase) {
|
||||
db_->addHall({"Hall 1", 100});
|
||||
db_->addSession({1, 1, "2025-01-01 12:00:00", 10});
|
||||
|
||||
NewTicket ticket;
|
||||
ticket.session_id = 1;
|
||||
ticket.seat_number = 1;
|
||||
|
||||
ticket.sold_at = "2025-01-01 13:00:00";
|
||||
EXPECT_FALSE(db_->addTicket(ticket));
|
||||
|
||||
ticket.sold_at = "2025-01-01 12:00:00";
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
|
||||
ticket.seat_number = 2;
|
||||
ticket.sold_at = "2025-01-01 11:00:00";
|
||||
EXPECT_TRUE(db_->addTicket(ticket));
|
||||
}
|
||||
|
||||
// INFO: ----- REFUND -----
|
||||
|
||||
// Test trigger validate_refund_amount
|
||||
TEST_F(SQLiteDatabaseTriggerTest, ValidateRefundAmount) {
|
||||
db_->addHall({"Hall 1", 100});
|
||||
db_->addSession({1, 1, "2025-01-01 12:00:00", 10});
|
||||
|
||||
// ID 1
|
||||
db_->addTicket({1, 1, "2025-01-01 10:00:00"});
|
||||
// ID 2
|
||||
db_->addTicket({1, 2, "2025-01-01 10:00:00"});
|
||||
|
||||
NewRefund refund;
|
||||
refund.ticket_id = 1;
|
||||
refund.refund_at = "2025-01-01 11:00:00";
|
||||
|
||||
refund.refund_amount = 11;
|
||||
EXPECT_FALSE(db_->addRefund(refund));
|
||||
|
||||
refund.refund_amount = 10;
|
||||
EXPECT_TRUE(db_->addRefund(refund));
|
||||
|
||||
refund.ticket_id = 2;
|
||||
refund.refund_amount = 9;
|
||||
EXPECT_TRUE(db_->addRefund(refund));
|
||||
}
|
||||
|
||||
// Test trigger validate_refund_date
|
||||
TEST_F(SQLiteDatabaseTriggerTest, ValidateRefundDate) {
|
||||
db_->addHall({"Hall 1", 100});
|
||||
db_->addSession({1, 1, "2025-01-01 12:00:00", 10});
|
||||
|
||||
// ID 1
|
||||
db_->addTicket({1, 1, "2025-01-01 10:00:00"});
|
||||
// ID 2
|
||||
db_->addTicket({1, 2, "2025-01-01 10:00:00"});
|
||||
|
||||
NewRefund refund;
|
||||
refund.ticket_id = 1;
|
||||
refund.refund_amount = 10;
|
||||
|
||||
refund.refund_at = "2025-01-01 09:00:00";
|
||||
EXPECT_FALSE(db_->addRefund(refund));
|
||||
|
||||
refund.refund_at = "2025-01-01 11:00:00";
|
||||
EXPECT_TRUE(db_->addRefund(refund));
|
||||
|
||||
refund.refund_at = "2025-01-01 12:00:00";
|
||||
EXPECT_FALSE(db_->addRefund(refund));
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
#ifndef DATABASE_INTERFACE_H
|
||||
#define DATABASE_INTERFACE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Data structures representing the entities
|
||||
|
||||
struct Genre {
|
||||
int genre_id;
|
||||
std::string genre_name;
|
||||
|
||||
Genre() = default;
|
||||
Genre(int id, const std::string &name) : genre_id(id), genre_name(name) {}
|
||||
};
|
||||
|
||||
struct Movie {
|
||||
int movie_id;
|
||||
std::string title;
|
||||
int genre_id;
|
||||
int duration; // Duration in minutes
|
||||
|
||||
Movie() = default;
|
||||
Movie(int id, const std::string &title, int genre_id, int duration)
|
||||
: movie_id(id), title(title), genre_id(genre_id), duration(duration) {}
|
||||
};
|
||||
|
||||
struct Hall {
|
||||
int hall_id;
|
||||
std::string hall_name;
|
||||
int capacity;
|
||||
|
||||
Hall() = default;
|
||||
Hall(int id, const std::string &name, int capacity)
|
||||
: hall_id(id), hall_name(name), capacity(capacity) {}
|
||||
};
|
||||
|
||||
struct Session {
|
||||
int session_id;
|
||||
int movie_id;
|
||||
int hall_id;
|
||||
std::string begin_at; // Use appropriate date-time type if needed
|
||||
double ticket_price;
|
||||
|
||||
Session() = default;
|
||||
Session(int id, int movie_id, int hall_id, const std::string &begin_at,
|
||||
double price)
|
||||
: session_id(id), movie_id(movie_id), hall_id(hall_id),
|
||||
begin_at(begin_at), ticket_price(price) {}
|
||||
};
|
||||
|
||||
struct Ticket {
|
||||
int ticket_id;
|
||||
int session_id;
|
||||
int seat_number;
|
||||
std::string sold_at; // Use appropriate date-time type if needed
|
||||
|
||||
Ticket() = default;
|
||||
Ticket(int id, int session_id, int seat_number, const std::string &time)
|
||||
: ticket_id(id), session_id(session_id), seat_number(seat_number),
|
||||
sold_at(time) {}
|
||||
};
|
||||
|
||||
struct Refund {
|
||||
int refund_id;
|
||||
int ticket_id;
|
||||
std::string refund_at; // Use appropriate date-time type if needed
|
||||
double refund_amount;
|
||||
|
||||
Refund() = default;
|
||||
Refund(int id, int ticket_id, const std::string &time, double amount)
|
||||
: refund_id(id), ticket_id(ticket_id), refund_at(time),
|
||||
refund_amount(amount) {}
|
||||
};
|
||||
|
||||
// Structures for adding new records (without IDs)
|
||||
|
||||
struct NewGenre {
|
||||
std::string genre_name;
|
||||
|
||||
NewGenre() = default;
|
||||
NewGenre(const std::string &name) : genre_name(name) {}
|
||||
};
|
||||
|
||||
struct NewMovie {
|
||||
std::string title;
|
||||
int genre_id;
|
||||
int duration; // Duration in minutes
|
||||
|
||||
NewMovie() = default;
|
||||
NewMovie(const std::string &title, int genre_id, int duration)
|
||||
: title(title), genre_id(genre_id), duration(duration) {}
|
||||
};
|
||||
|
||||
struct NewHall {
|
||||
std::string hall_name;
|
||||
int capacity;
|
||||
|
||||
NewHall() = default;
|
||||
NewHall(const std::string &name, int capacity)
|
||||
: hall_name(name), capacity(capacity) {}
|
||||
};
|
||||
|
||||
struct NewSession {
|
||||
int movie_id;
|
||||
int hall_id;
|
||||
std::string begin_at; // Use appropriate date-time type if needed
|
||||
double ticket_price;
|
||||
|
||||
NewSession() = default;
|
||||
NewSession(int movie_id, int hall_id, const std::string &begin_at,
|
||||
double price)
|
||||
: movie_id(movie_id), hall_id(hall_id), begin_at(begin_at),
|
||||
ticket_price(price) {}
|
||||
};
|
||||
|
||||
struct NewTicket {
|
||||
int session_id;
|
||||
int seat_number;
|
||||
std::string sold_at; // Optional, can default to CURRENT_TIMESTAMP
|
||||
|
||||
NewTicket() = default;
|
||||
NewTicket(int session_id, int seat_number, const std::string &time)
|
||||
: session_id(session_id), seat_number(seat_number), sold_at(time) {}
|
||||
};
|
||||
|
||||
struct NewRefund {
|
||||
int ticket_id;
|
||||
std::string refund_at; // Optional, can default to CURRENT_TIMESTAMP
|
||||
double refund_amount;
|
||||
|
||||
NewRefund() = default;
|
||||
NewRefund(int ticket_id, const std::string &time, double amount)
|
||||
: ticket_id(ticket_id), refund_at(time), refund_amount(amount) {}
|
||||
};
|
||||
|
||||
// Abstract interface for database operations
|
||||
class DatabaseInterface {
|
||||
public:
|
||||
virtual ~DatabaseInterface() {}
|
||||
|
||||
// Verification of the database availability
|
||||
virtual bool isDatabaseAvailable() = 0;
|
||||
|
||||
// Genre operations
|
||||
virtual bool addGenre(const NewGenre &genre) = 0;
|
||||
virtual bool removeGenre(int genre_id) = 0;
|
||||
virtual bool updateGenre(const Genre &genre) = 0;
|
||||
virtual Genre getGenre(int genre_id) = 0;
|
||||
virtual std::vector<Genre> getAllGenres() = 0;
|
||||
|
||||
// Movie operations
|
||||
virtual bool addMovie(const NewMovie &movie) = 0;
|
||||
virtual bool removeMovie(int movie_id) = 0;
|
||||
virtual bool updateMovie(const Movie &movie) = 0;
|
||||
virtual Movie getMovie(int movie_id) = 0;
|
||||
virtual std::vector<Movie> getAllMovies() = 0;
|
||||
|
||||
// Hall operations
|
||||
virtual bool addHall(const NewHall &hall) = 0;
|
||||
virtual bool removeHall(int hall_id) = 0;
|
||||
virtual bool updateHall(const Hall &hall) = 0;
|
||||
virtual Hall getHall(int hall_id) = 0;
|
||||
virtual std::vector<Hall> getAllHalls() = 0;
|
||||
|
||||
// Session operations
|
||||
virtual bool addSession(const NewSession &session) = 0;
|
||||
virtual bool removeSession(int session_id) = 0;
|
||||
virtual bool updateSession(const Session &session) = 0;
|
||||
virtual Session getSession(int session_id) = 0;
|
||||
virtual std::vector<Session> getAllSessions() = 0;
|
||||
|
||||
// Ticket operations
|
||||
virtual bool addTicket(const NewTicket &ticket) = 0;
|
||||
virtual bool removeTicket(int ticket_id) = 0;
|
||||
virtual bool updateTicket(const Ticket &ticket) = 0;
|
||||
virtual Ticket getTicket(int ticket_id) = 0;
|
||||
virtual std::vector<Ticket> getAllTickets() = 0;
|
||||
|
||||
// Refund operations
|
||||
virtual bool addRefund(const NewRefund &refund) = 0;
|
||||
virtual bool removeRefund(int refund_id) = 0;
|
||||
virtual bool updateRefund(const Refund &refund) = 0;
|
||||
virtual Refund getRefund(int refund_id) = 0;
|
||||
virtual std::vector<Refund> getAllRefunds() = 0;
|
||||
};
|
||||
|
||||
#endif // DATABASE_INTERFACE_H
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "file_reader.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
// Read a SQL file
|
||||
bool FileReader::read(const std::string &sqlFilePath, std::string &sql) {
|
||||
// Try read the SQL file
|
||||
std::ifstream file(sqlFilePath);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error opening file:" << sqlFilePath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
|
||||
sql = buffer.str();
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef FILE_READER_H
|
||||
#define FILE_READER_H
|
||||
|
||||
#include "file_reader_interface.h"
|
||||
#include <string>
|
||||
|
||||
class FileReader : virtual public FileReaderInterface {
|
||||
public:
|
||||
bool read(const std::string &sqlFilePath, std::string &sql) override;
|
||||
};
|
||||
|
||||
#endif // FILE_READER_H
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef FILE_READER_INTERFACE_H
|
||||
#define FILE_READER_INTERFACE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class FileReaderInterface {
|
||||
public:
|
||||
virtual ~FileReaderInterface() = default;
|
||||
virtual bool read(const std::string &sqlFilePath, std::string &data) = 0;
|
||||
};
|
||||
|
||||
#endif // !FILE_READER_INTERFACE
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef SQL_FILE_EXECUTOR_INTERFASE_H
|
||||
#define SQL_FILE_EXECUTOR_INTERFASE_H
|
||||
|
||||
#include "file_reader_interface.h"
|
||||
#include <string>
|
||||
|
||||
class SQLFileExecutorInterface : virtual public FileReaderInterface {
|
||||
public:
|
||||
virtual ~SQLFileExecutorInterface() {}
|
||||
virtual void executeSQLFile(const std::string &sqlFilePath) = 0;
|
||||
};
|
||||
|
||||
#endif // !SQL_FILE_EXECUTOR_INTERF
|
||||
@@ -0,0 +1,487 @@
|
||||
#include "sqlite_database.h"
|
||||
|
||||
#include "sqlite_statement.h"
|
||||
#include <iostream>
|
||||
#include <utility> // std::exchange
|
||||
|
||||
// Constructor: Opens the SQLite database
|
||||
SQLiteDatabase::SQLiteDatabase(const std::string &db_file) {
|
||||
// WARNING: The database is not blocked
|
||||
if (sqlite3_open(db_file.c_str(), &db_) != SQLITE_OK) {
|
||||
std::cerr << "Failed to open database:" << sqlite3_errmsg(db_) << std::endl;
|
||||
db_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor: Closes the database connection
|
||||
SQLiteDatabase::~SQLiteDatabase() {
|
||||
if (db_) {
|
||||
sqlite3_close(db_);
|
||||
}
|
||||
}
|
||||
|
||||
SQLiteDatabase::SQLiteDatabase(SQLiteDatabase &&other) noexcept
|
||||
: db_(std::exchange(other.db_, nullptr)) {}
|
||||
|
||||
SQLiteDatabase &SQLiteDatabase::operator=(SQLiteDatabase &&other) noexcept {
|
||||
if (this != &other) {
|
||||
if (db_) {
|
||||
sqlite3_close(db_);
|
||||
}
|
||||
db_ = std::exchange(other.db_, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Helper function for executing non-SELECT queries
|
||||
int SQLiteDatabase::executeNonQuery(
|
||||
const std::string &query,
|
||||
const std::function<void(SQLiteStatement &)> &bindFunc) {
|
||||
SQLiteStatement stmt(db_, query);
|
||||
|
||||
if (db_ == nullptr) {
|
||||
throw std::runtime_error("Database is not open");
|
||||
}
|
||||
|
||||
if (bindFunc) {
|
||||
bindFunc(stmt);
|
||||
}
|
||||
|
||||
if (!stmt.execute()) {
|
||||
std::cerr << "Failed to execute query:" << sqlite3_errmsg(db_);
|
||||
}
|
||||
|
||||
return stmt.changes();
|
||||
}
|
||||
|
||||
// Helper function for executing SELECT queries
|
||||
std::vector<std::vector<std::string>> SQLiteDatabase::executeSelectQuery(
|
||||
const std::string &query,
|
||||
const std::function<void(SQLiteStatement &)> &bindFunc) {
|
||||
std::vector<std::vector<std::string>> results;
|
||||
SQLiteStatement stmt(db_, query);
|
||||
|
||||
if (db_ == nullptr) {
|
||||
throw std::runtime_error("Database is not open");
|
||||
}
|
||||
|
||||
if (bindFunc) {
|
||||
bindFunc(stmt);
|
||||
}
|
||||
|
||||
while (stmt.step()) {
|
||||
std::vector<std::string> row;
|
||||
for (int i = 0, count = stmt.getColumnCount(); i < count; ++i) {
|
||||
row.emplace_back(stmt.getColumnText(i));
|
||||
}
|
||||
results.push_back(row);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// INFO: ----- GENRE -----
|
||||
|
||||
// Add a new genre
|
||||
bool SQLiteDatabase::addGenre(const NewGenre &genre) {
|
||||
const std::string query = "INSERT INTO genres (genre_name) VALUES (?);";
|
||||
int changes = executeNonQuery(query, [&genre](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, genre.genre_name);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Remove a genre by ID
|
||||
bool SQLiteDatabase::removeGenre(int genre_id) {
|
||||
const std::string query = "DELETE FROM genres WHERE genre_id = ?;";
|
||||
int changes = executeNonQuery(
|
||||
query, [&genre_id](SQLiteStatement &stmt) { stmt.bind(1, genre_id); });
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Update an existing genre
|
||||
bool SQLiteDatabase::updateGenre(const Genre &genre) {
|
||||
const std::string query =
|
||||
"UPDATE genres SET genre_name = ? WHERE genre_id = ?;";
|
||||
int changes = executeNonQuery(query, [&genre](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, genre.genre_name);
|
||||
stmt.bind(2, genre.genre_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Get a genre by ID
|
||||
Genre SQLiteDatabase::getGenre(int genre_id) {
|
||||
const std::string query = "SELECT * FROM genres WHERE genre_id = ?;";
|
||||
auto result = executeSelectQuery(
|
||||
query, [&genre_id](SQLiteStatement &stmt) { stmt.bind(1, genre_id); });
|
||||
|
||||
if (!result.empty()) {
|
||||
auto genre_id = std::stoi(result[0][0]);
|
||||
auto genre_name = result[0][1];
|
||||
Genre genre(genre_id, genre_name);
|
||||
return genre;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get all genres
|
||||
std::vector<Genre> SQLiteDatabase::getAllGenres() {
|
||||
const std::string query = "SELECT * FROM genres;";
|
||||
auto result = executeSelectQuery(query);
|
||||
|
||||
std::vector<Genre> genres;
|
||||
for (const auto &row : result) {
|
||||
genres.push_back({std::stoi(row[0]), row[1]});
|
||||
}
|
||||
|
||||
return genres;
|
||||
}
|
||||
|
||||
// INFO: ----- MOVIE -----
|
||||
|
||||
// Add a new movie
|
||||
bool SQLiteDatabase::addMovie(const NewMovie &movie) {
|
||||
const std::string query =
|
||||
"INSERT INTO movies (title, genre_id, duration) VALUES (?, ?, ?);";
|
||||
int changes = executeNonQuery(query, [&movie](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, movie.title);
|
||||
stmt.bind(2, movie.genre_id);
|
||||
stmt.bind(3, movie.duration);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Remove a movie by ID
|
||||
bool SQLiteDatabase::removeMovie(int movie_id) {
|
||||
const std::string query = "DELETE FROM movies WHERE movie_id = ?;";
|
||||
int changes = executeNonQuery(
|
||||
query, [&movie_id](SQLiteStatement &stmt) { stmt.bind(1, movie_id); });
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Update an existing movie
|
||||
bool SQLiteDatabase::updateMovie(const Movie &movie) {
|
||||
const std::string query =
|
||||
"UPDATE movies SET title = ?, genre_id = ?, duration = ? WHERE movie_id "
|
||||
"= ?;";
|
||||
int changes = executeNonQuery(query, [&movie](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, movie.title);
|
||||
stmt.bind(2, movie.genre_id);
|
||||
stmt.bind(3, movie.duration);
|
||||
stmt.bind(4, movie.movie_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Get a movie by ID
|
||||
Movie SQLiteDatabase::getMovie(int movie_id) {
|
||||
const std::string query = "SELECT * FROM movies WHERE movie_id = ?;";
|
||||
auto result = executeSelectQuery(
|
||||
query, [&movie_id](SQLiteStatement &stmt) { stmt.bind(1, movie_id); });
|
||||
|
||||
if (!result.empty()) {
|
||||
auto movie_id = std::stoi(result[0][0]);
|
||||
auto title = result[0][1];
|
||||
auto genre_id = std::stoi(result[0][2]);
|
||||
auto duration = std::stoi(result[0][3]);
|
||||
Movie movie(movie_id, title, genre_id, duration);
|
||||
return movie;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get all movies
|
||||
std::vector<Movie> SQLiteDatabase::getAllMovies() {
|
||||
const std::string query = "SELECT * FROM movies;";
|
||||
auto result = executeSelectQuery(query);
|
||||
std::vector<Movie> movies;
|
||||
|
||||
for (const auto &row : result) {
|
||||
movies.push_back(
|
||||
{std::stoi(row[0]), row[1], std::stoi(row[2]), std::stoi(row[3])});
|
||||
}
|
||||
return movies;
|
||||
}
|
||||
|
||||
// INFO: ----- HALL -----
|
||||
|
||||
// Add a new hall
|
||||
bool SQLiteDatabase::addHall(const NewHall &hall) {
|
||||
const std::string query =
|
||||
"INSERT INTO halls (hall_name, capacity) VALUES (?, ?);";
|
||||
int changes = executeNonQuery(query, [&hall](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, hall.hall_name);
|
||||
stmt.bind(2, hall.capacity);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Remove a hall by ID
|
||||
bool SQLiteDatabase::removeHall(int hall_id) {
|
||||
const std::string query = "DELETE FROM halls WHERE hall_id = ?;";
|
||||
int changes = executeNonQuery(
|
||||
query, [&hall_id](SQLiteStatement &stmt) { stmt.bind(1, hall_id); });
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Update an existing hall
|
||||
bool SQLiteDatabase::updateHall(const Hall &hall) {
|
||||
const std::string query =
|
||||
"UPDATE halls SET hall_name = ?, capacity = ? WHERE hall_id = ?;";
|
||||
int changes = executeNonQuery(query, [&hall](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, hall.hall_name);
|
||||
stmt.bind(2, hall.capacity);
|
||||
stmt.bind(3, hall.hall_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Get a hall by ID
|
||||
Hall SQLiteDatabase::getHall(int hall_id) {
|
||||
const std::string query = "SELECT * FROM halls WHERE hall_id = ?;";
|
||||
auto result = executeSelectQuery(
|
||||
query, [&hall_id](SQLiteStatement &stmt) { stmt.bind(1, hall_id); });
|
||||
|
||||
if (!result.empty()) {
|
||||
auto hall_id = std::stoi(result[0][0]);
|
||||
auto hall_name = result[0][1];
|
||||
auto capacity = std::stoi(result[0][2]);
|
||||
Hall hall(hall_id, hall_name, capacity);
|
||||
return hall;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get all halls
|
||||
std::vector<Hall> SQLiteDatabase::getAllHalls() {
|
||||
const std::string query = "SELECT * FROM halls;";
|
||||
auto result = executeSelectQuery(query);
|
||||
std::vector<Hall> halls;
|
||||
|
||||
for (const auto &row : result) {
|
||||
halls.push_back({std::stoi(row[0]), row[1], std::stoi(row[2])});
|
||||
}
|
||||
return halls;
|
||||
}
|
||||
|
||||
// INFO: ----- SESSION -----
|
||||
|
||||
// Add a new session
|
||||
bool SQLiteDatabase::addSession(const NewSession &session) {
|
||||
const std::string query = "INSERT INTO sessions (movie_id, hall_id, "
|
||||
"begin_at, ticket_price) VALUES (?, ?, ?, ?);";
|
||||
int changes = executeNonQuery(query, [&session](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, session.movie_id);
|
||||
stmt.bind(2, session.hall_id);
|
||||
stmt.bind(3, session.begin_at);
|
||||
stmt.bind(4, session.ticket_price);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Remove a session by ID
|
||||
bool SQLiteDatabase::removeSession(int session_id) {
|
||||
const std::string query = "DELETE FROM sessions WHERE session_id = ?;";
|
||||
int changes = executeNonQuery(query, [&session_id](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, session_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Update an existing session
|
||||
bool SQLiteDatabase::updateSession(const Session &session) {
|
||||
const std::string query = "UPDATE sessions SET movie_id = ?, hall_id = ?, "
|
||||
"begin_at = ?, ticket_price = ? WHERE "
|
||||
"session_id = ?;";
|
||||
int changes = executeNonQuery(query, [&session](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, session.movie_id);
|
||||
stmt.bind(2, session.hall_id);
|
||||
stmt.bind(3, session.begin_at);
|
||||
stmt.bind(4, session.ticket_price);
|
||||
stmt.bind(5, session.session_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Get a session by ID
|
||||
Session SQLiteDatabase::getSession(int session_id) {
|
||||
const std::string query = "SELECT * FROM sessions WHERE session_id = ?;";
|
||||
auto result = executeSelectQuery(query, [&session_id](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, session_id);
|
||||
});
|
||||
|
||||
if (!result.empty()) {
|
||||
auto session_id = std::stoi(result[0][0]);
|
||||
auto movie_id = std::stoi(result[0][1]);
|
||||
auto hall_id = std::stoi(result[0][2]);
|
||||
auto begin_at = result[0][3];
|
||||
auto ticket_price = std::stod(result[0][4]);
|
||||
Session session(session_id, movie_id, hall_id, begin_at, ticket_price);
|
||||
return session;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get all sessions
|
||||
std::vector<Session> SQLiteDatabase::getAllSessions() {
|
||||
const std::string query = "SELECT * FROM sessions;";
|
||||
auto result = executeSelectQuery(query);
|
||||
std::vector<Session> sessions;
|
||||
|
||||
for (const auto &row : result) {
|
||||
sessions.push_back({std::stoi(row[0]), std::stoi(row[1]), std::stoi(row[2]),
|
||||
row[3], std::stod(row[4])});
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
// INFO: ----- TICKET -----
|
||||
|
||||
// Add a new ticket
|
||||
bool SQLiteDatabase::addTicket(const NewTicket &ticket) {
|
||||
const std::string query = "INSERT INTO tickets (session_id, seat_number, "
|
||||
"sold_at) VALUES (?, ?, ?);";
|
||||
int changes = executeNonQuery(query, [&ticket](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, ticket.session_id);
|
||||
stmt.bind(2, ticket.seat_number);
|
||||
stmt.bind(3, ticket.sold_at);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Remove a ticket by ID
|
||||
bool SQLiteDatabase::removeTicket(int ticket_id) {
|
||||
const std::string query = "DELETE FROM tickets WHERE ticket_id = ?;";
|
||||
int changes = executeNonQuery(
|
||||
query, [&ticket_id](SQLiteStatement &stmt) { stmt.bind(1, ticket_id); });
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Update an existing ticket
|
||||
bool SQLiteDatabase::updateTicket(const Ticket &ticket) {
|
||||
const std::string query = "UPDATE tickets SET session_id = ?, seat_number = "
|
||||
"?, sold_at = ? WHERE ticket_id = ?;";
|
||||
int changes = executeNonQuery(query, [&ticket](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, ticket.session_id);
|
||||
stmt.bind(2, ticket.seat_number);
|
||||
stmt.bind(3, ticket.sold_at);
|
||||
stmt.bind(4, ticket.ticket_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Get a ticket by ID
|
||||
Ticket SQLiteDatabase::getTicket(int ticket_id) {
|
||||
const std::string query = "SELECT * FROM tickets WHERE ticket_id = ?;";
|
||||
auto result = executeSelectQuery(
|
||||
query, [&ticket_id](SQLiteStatement &stmt) { stmt.bind(1, ticket_id); });
|
||||
|
||||
if (!result.empty()) {
|
||||
auto ticket_id = std::stoi(result[0][0]);
|
||||
auto session_id = std::stoi(result[0][1]);
|
||||
auto seat_number = std::stoi(result[0][2]);
|
||||
auto sold_at = result[0][3];
|
||||
Ticket ticket(ticket_id, session_id, seat_number, sold_at);
|
||||
return ticket;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get all tickets
|
||||
std::vector<Ticket> SQLiteDatabase::getAllTickets() {
|
||||
const std::string query = "SELECT * FROM tickets;";
|
||||
auto result = executeSelectQuery(query);
|
||||
std::vector<Ticket> tickets;
|
||||
|
||||
for (const auto &row : result) {
|
||||
tickets.push_back(
|
||||
{std::stoi(row[0]), std::stoi(row[1]), std::stoi(row[2]), row[3]});
|
||||
}
|
||||
return tickets;
|
||||
}
|
||||
|
||||
// INFO: ----- REFUND -----
|
||||
|
||||
// Add a new refund
|
||||
bool SQLiteDatabase::addRefund(const NewRefund &refund) {
|
||||
const std::string query =
|
||||
"INSERT INTO refunds (ticket_id, refund_at, refund_amount) "
|
||||
"VALUES (?, ?, ?);";
|
||||
int changes = executeNonQuery(query, [&refund](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, refund.ticket_id);
|
||||
stmt.bind(2, refund.refund_at);
|
||||
stmt.bind(3, refund.refund_amount);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Remove a refund by ID
|
||||
bool SQLiteDatabase::removeRefund(int refund_id) {
|
||||
const std::string query = "DELETE FROM refunds WHERE refund_id = ?;";
|
||||
int changes = executeNonQuery(
|
||||
query, [&refund_id](SQLiteStatement &stmt) { stmt.bind(1, refund_id); });
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Update an existing refund
|
||||
bool SQLiteDatabase::updateRefund(const Refund &refund) {
|
||||
const std::string query =
|
||||
"UPDATE refunds SET ticket_id = ?, refund_at = ?, refund_amount = ? "
|
||||
"WHERE refund_id = ?;";
|
||||
int changes = executeNonQuery(query, [&refund](SQLiteStatement &stmt) {
|
||||
stmt.bind(1, refund.ticket_id);
|
||||
stmt.bind(2, refund.refund_at);
|
||||
stmt.bind(3, refund.refund_amount);
|
||||
stmt.bind(4, refund.refund_id);
|
||||
});
|
||||
|
||||
return changes > 0;
|
||||
}
|
||||
|
||||
// Get a refund by ID
|
||||
Refund SQLiteDatabase::getRefund(int refund_id) {
|
||||
const std::string query = "SELECT * FROM refunds WHERE refund_id = ?;";
|
||||
auto result = executeSelectQuery(
|
||||
query, [&refund_id](SQLiteStatement &stmt) { stmt.bind(1, refund_id); });
|
||||
|
||||
if (!result.empty()) {
|
||||
auto refund_id = std::stoi(result[0][0]);
|
||||
auto ticket_id = std::stoi(result[0][1]);
|
||||
auto refund_at = result[0][2];
|
||||
auto refund_amount = std::stod(result[0][3]);
|
||||
Refund refund(refund_id, ticket_id, refund_at, refund_amount);
|
||||
return refund;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Get all refunds
|
||||
std::vector<Refund> SQLiteDatabase::getAllRefunds() {
|
||||
const std::string query = "SELECT * FROM refunds;";
|
||||
auto result = executeSelectQuery(query);
|
||||
std::vector<Refund> refunds;
|
||||
|
||||
for (const auto &row : result) {
|
||||
refunds.push_back(
|
||||
{std::stoi(row[0]), std::stoi(row[1]), row[2], std::stod(row[3])});
|
||||
}
|
||||
return refunds;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef SQLITE_DATABASE_H
|
||||
#define SQLITE_DATABASE_H
|
||||
|
||||
#include "database_interface.h"
|
||||
#include <functional>
|
||||
#include <sqlite3.h>
|
||||
|
||||
// Forward declaration
|
||||
class SQLiteStatement;
|
||||
|
||||
// SQLite implementation of the DatabaseInterface
|
||||
class SQLiteDatabase : public DatabaseInterface {
|
||||
private:
|
||||
sqlite3 *db_; // Pointer to SQLite database connection
|
||||
|
||||
// Helper functions
|
||||
int executeNonQuery(
|
||||
const std::string &query,
|
||||
const std::function<void(SQLiteStatement &)> &bindFunc = nullptr);
|
||||
std::vector<std::vector<std::string>> executeSelectQuery(
|
||||
const std::string &query,
|
||||
const std::function<void(SQLiteStatement &)> &bindFunc = nullptr);
|
||||
|
||||
friend class SQLiteDatabaseForeignKeyTest;
|
||||
friend class SQLiteDatabaseCRUDTest;
|
||||
friend class SQLiteDatabaseTriggerTest;
|
||||
|
||||
public:
|
||||
// Verification of the database availability
|
||||
bool isDatabaseAvailable() override { return db_ != nullptr; }
|
||||
|
||||
public:
|
||||
SQLiteDatabase(const std::string &db_file);
|
||||
~SQLiteDatabase();
|
||||
|
||||
SQLiteDatabase(const SQLiteDatabase &) = delete;
|
||||
SQLiteDatabase &operator=(const SQLiteDatabase &) = delete;
|
||||
|
||||
SQLiteDatabase(SQLiteDatabase &&) noexcept;
|
||||
SQLiteDatabase &operator=(SQLiteDatabase &&) noexcept;
|
||||
|
||||
// Genre operations
|
||||
bool addGenre(const NewGenre &genre) override;
|
||||
bool removeGenre(int genre_id) override;
|
||||
bool updateGenre(const Genre &genre) override;
|
||||
Genre getGenre(int genre_id) override;
|
||||
std::vector<Genre> getAllGenres() override;
|
||||
|
||||
// Movie operations
|
||||
bool addMovie(const NewMovie &movie) override;
|
||||
bool removeMovie(int movie_id) override;
|
||||
bool updateMovie(const Movie &movie) override;
|
||||
Movie getMovie(int movie_id) override;
|
||||
std::vector<Movie> getAllMovies() override;
|
||||
|
||||
// Hall operations
|
||||
bool addHall(const NewHall &hall) override;
|
||||
bool removeHall(int hall_id) override;
|
||||
bool updateHall(const Hall &hall) override;
|
||||
Hall getHall(int hall_id) override;
|
||||
std::vector<Hall> getAllHalls() override;
|
||||
|
||||
// Session operations
|
||||
bool addSession(const NewSession &session) override;
|
||||
bool removeSession(int session_id) override;
|
||||
bool updateSession(const Session &session) override;
|
||||
Session getSession(int session_id) override;
|
||||
std::vector<Session> getAllSessions() override;
|
||||
|
||||
// Ticket operations
|
||||
bool addTicket(const NewTicket &ticket) override;
|
||||
bool removeTicket(int ticket_id) override;
|
||||
bool updateTicket(const Ticket &ticket) override;
|
||||
Ticket getTicket(int ticket_id) override;
|
||||
std::vector<Ticket> getAllTickets() override;
|
||||
|
||||
// Refund operations
|
||||
bool addRefund(const NewRefund &refund) override;
|
||||
bool removeRefund(int refund_id) override;
|
||||
bool updateRefund(const Refund &refund) override;
|
||||
Refund getRefund(int refund_id) override;
|
||||
std::vector<Refund> getAllRefunds() override;
|
||||
};
|
||||
|
||||
#endif // SQLITE_DATABASE_H
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "sqlite_file_executor.h"
|
||||
|
||||
#include "sqlite_statement.h"
|
||||
#include <iostream>
|
||||
|
||||
SQLiteFileExecutor::SQLiteFileExecutor(const std::string &dbPath) {
|
||||
// WARNING: The database is not blocked
|
||||
if (sqlite3_open(dbPath.c_str(), &db_) != SQLITE_OK) {
|
||||
std::cerr << "Failed to open database:" << sqlite3_errmsg(db_) << std::endl;
|
||||
db_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SQLiteFileExecutor::~SQLiteFileExecutor() {
|
||||
if (db_) {
|
||||
sqlite3_close(db_);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute a SQL file
|
||||
void SQLiteFileExecutor::executeSQLFile(const std::string &sqlFilePath) {
|
||||
if (db_ == nullptr) {
|
||||
throw std::runtime_error("Database is not open");
|
||||
}
|
||||
|
||||
std::string sqlContent;
|
||||
if (!read(sqlFilePath, sqlContent)) {
|
||||
throw std::runtime_error("Failed to read SQL file: " + sqlFilePath);
|
||||
}
|
||||
|
||||
char *errorMessage = nullptr;
|
||||
if (sqlite3_exec(db_, sqlContent.c_str(), nullptr, nullptr, &errorMessage) !=
|
||||
SQLITE_OK) {
|
||||
std::cerr << "Error executing SQL:" << errorMessage << std::endl;
|
||||
sqlite3_free(errorMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef SQLITE_FILE_EXECUTOR_INTERFASE_H
|
||||
#define SQLITE_FILE_EXECUTOR_INTERFASE_H
|
||||
|
||||
#include "sql_file_executor_interfase.h"
|
||||
|
||||
#include "file_reader.h"
|
||||
#include "sqlite_statement.h"
|
||||
#include <string>
|
||||
|
||||
class SQLiteFileExecutor : public SQLFileExecutorInterface, public FileReader {
|
||||
private:
|
||||
sqlite3 *db_;
|
||||
|
||||
public:
|
||||
SQLiteFileExecutor(const std::string &dbPath);
|
||||
~SQLiteFileExecutor() override;
|
||||
|
||||
public:
|
||||
void executeSQLFile(const std::string &sqlFilePath) override;
|
||||
};
|
||||
|
||||
#endif // !SQLITE_FILE_EXECUTOR_INTERFASE_H
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "sqlite_statement.h"
|
||||
#include <iostream>
|
||||
|
||||
// Constructor: Prepares the SQL statement
|
||||
SQLiteStatement::SQLiteStatement(sqlite3 *db, const std::string &query)
|
||||
: db_(db), stmt_(nullptr) {
|
||||
if (db == nullptr) {
|
||||
throw std::invalid_argument("Database connection is null");
|
||||
}
|
||||
|
||||
if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt_, nullptr) != SQLITE_OK) {
|
||||
std::cerr << "Error preparing SQL statement:" << "\"" + query + "\""
|
||||
<< std::endl;
|
||||
std::cerr << "Error message:" << sqlite3_errmsg(db) << std::endl;
|
||||
|
||||
if (stmt_) {
|
||||
sqlite3_finalize(stmt_);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Failed to prepare SQLite statement: " + query);
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor: Finalizes the statement
|
||||
SQLiteStatement::~SQLiteStatement() { finalize(); }
|
||||
|
||||
// Bind integer value
|
||||
bool SQLiteStatement::bind(int index, int value) {
|
||||
return sqlite3_bind_int(stmt_, index, value) == SQLITE_OK;
|
||||
}
|
||||
|
||||
// Bind double value
|
||||
bool SQLiteStatement::bind(int index, double value) {
|
||||
return sqlite3_bind_double(stmt_, index, value) == SQLITE_OK;
|
||||
}
|
||||
|
||||
// Bind text value
|
||||
bool SQLiteStatement::bind(int index, const std::string &value) {
|
||||
return sqlite3_bind_text(stmt_, index, value.c_str(), -1, SQLITE_TRANSIENT) ==
|
||||
SQLITE_OK;
|
||||
}
|
||||
|
||||
// Bind NULL value
|
||||
bool SQLiteStatement::bindNull(int index) {
|
||||
return sqlite3_bind_null(stmt_, index) == SQLITE_OK;
|
||||
}
|
||||
|
||||
// Run the statement
|
||||
// step() returns true if there are more rows
|
||||
bool SQLiteStatement::execute() { return step() == false; }
|
||||
|
||||
// Execute the statement
|
||||
bool SQLiteStatement::step() {
|
||||
int rc = sqlite3_step(stmt_);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
return true;
|
||||
}
|
||||
if (rc == SQLITE_DONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cerr << "Failed to step:" << sqlite3_errmsg(db_) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the number of rows affected
|
||||
int SQLiteStatement::changes() { return sqlite3_changes(db_); }
|
||||
|
||||
// Reset the statement for re-use
|
||||
void SQLiteStatement::reset() { sqlite3_reset(stmt_); }
|
||||
|
||||
// Finalize and clean up the statement
|
||||
void SQLiteStatement::finalize() {
|
||||
if (stmt_) {
|
||||
sqlite3_finalize(stmt_);
|
||||
stmt_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Adapter for sqlite3_column_count
|
||||
int SQLiteStatement::getColumnCount() const {
|
||||
return sqlite3_column_count(stmt_);
|
||||
}
|
||||
|
||||
// Adapter for sqlite3_column_text
|
||||
std::string SQLiteStatement::getColumnText(int column_index) const {
|
||||
const unsigned char *text = sqlite3_column_text(stmt_, column_index);
|
||||
return text ? reinterpret_cast<const char *>(text) : "";
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef SQLITE_STATEMENT_H
|
||||
#define SQLITE_STATEMENT_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <string>
|
||||
|
||||
class SQLiteStatement {
|
||||
private:
|
||||
sqlite3 *db_;
|
||||
sqlite3_stmt *stmt_; // Prepared statement
|
||||
|
||||
public:
|
||||
SQLiteStatement(sqlite3 *db, const std::string &query);
|
||||
~SQLiteStatement();
|
||||
|
||||
bool bind(int index, int value);
|
||||
bool bind(int index, double value);
|
||||
bool bind(int index, const std::string &value);
|
||||
bool bindNull(int index);
|
||||
|
||||
bool execute();
|
||||
bool step();
|
||||
|
||||
int changes();
|
||||
|
||||
void finalize();
|
||||
void reset();
|
||||
|
||||
int getColumnCount() const;
|
||||
std::string getColumnText(int column_index) const;
|
||||
};
|
||||
|
||||
#endif // SQLITE_STATEMENT_H
|
||||
Reference in New Issue
Block a user