Initial commit
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
#include "database_manager.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "execute_sql_file.h"
|
||||
#include <QFile>
|
||||
#include <QSqlError>
|
||||
#include <qsqldatabase.h>
|
||||
|
||||
DatabaseManager::~DatabaseManager() {
|
||||
disconnectDefaultDatabase();
|
||||
|
||||
QString defaultConnectionName = QSqlDatabase::database().connectionName();
|
||||
QSqlDatabase::removeDatabase(QSqlDatabase::database().connectionName());
|
||||
|
||||
qInfo() << "Database with default connection name:" << defaultConnectionName
|
||||
<< "was removed";
|
||||
}
|
||||
|
||||
DatabaseManager::CreateDatabaseResult
|
||||
DatabaseManager::createDefaultDatabaseIfMissing(
|
||||
const QString &databaseFileName) {
|
||||
if (QFile::exists(databaseFileName)) {
|
||||
qInfo()
|
||||
<< "Database file name already exists and will not be created again:"
|
||||
<< databaseFileName;
|
||||
return CreateDatabaseResult::AlreadyExists;
|
||||
}
|
||||
|
||||
if (!connectToDefaultDatabase(databaseFileName)) {
|
||||
qWarning() << "Failed to create and connect to the database";
|
||||
return CreateDatabaseResult::Error;
|
||||
}
|
||||
|
||||
// Execute SQL files to initialize the database
|
||||
QStringList sqlFiles = {":/sql/create_tables.sql", ":/sql/create_indexes.sql",
|
||||
":/sql/create_views.sql", ":/sql/create_triggers.sql",
|
||||
":/sql/insert_role.sql"};
|
||||
|
||||
#ifndef NDEBUG
|
||||
QStringList additionalSqlFiles = {
|
||||
":/sql/insert_genre.sql", ":/sql/insert_hall.sql",
|
||||
":/sql/insert_movie.sql", ":/sql/insert_user.sql"};
|
||||
sqlFiles.append(additionalSqlFiles);
|
||||
#endif
|
||||
|
||||
QSqlDatabase defaultDatabase = QSqlDatabase::database();
|
||||
bool success = executeSqlFiles(sqlFiles, defaultDatabase);
|
||||
disconnectDefaultDatabase();
|
||||
|
||||
if (success) {
|
||||
qInfo() << "Database created and initialized successfully";
|
||||
return CreateDatabaseResult::Success;
|
||||
}
|
||||
|
||||
qWarning() << "Failed to execute initialization SQL files";
|
||||
|
||||
if (QFile::remove(databaseFileName)) {
|
||||
qInfo() << "Database file deleted due to initialization failure:"
|
||||
<< databaseFileName;
|
||||
} else {
|
||||
qWarning() << "Failed to delete database file:" << databaseFileName;
|
||||
}
|
||||
|
||||
return CreateDatabaseResult::Error;
|
||||
}
|
||||
|
||||
bool DatabaseManager::connectToDefaultDatabase(
|
||||
const QString &databaseFileName) {
|
||||
QSqlDatabase defaultDatabase = QSqlDatabase::database();
|
||||
|
||||
defaultDatabase.setDatabaseName(databaseFileName);
|
||||
if (defaultDatabase.isValid() && defaultDatabase.isOpen()) {
|
||||
qInfo() << "Database is already connected";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Constructor does not open/close the connection
|
||||
defaultDatabase = QSqlDatabase::addDatabase(DATABASE_DRIVER);
|
||||
defaultDatabase.setDatabaseName(databaseFileName);
|
||||
|
||||
if (!defaultDatabase.open()) {
|
||||
qWarning() << "Failed to connect to the database:" << databaseFileName;
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Database was connected successfully: Connection name:"
|
||||
<< defaultDatabase.connectionName();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DatabaseManager::disconnectDefaultDatabase() {
|
||||
QSqlDatabase defaultDatabase = QSqlDatabase::database();
|
||||
if (defaultDatabase.isOpen()) {
|
||||
defaultDatabase.close();
|
||||
qInfo() << "Database disconnected";
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseManager::executeSqlFiles(const QStringList &filePaths,
|
||||
QSqlDatabase &database) {
|
||||
ExecuteSqlFile executor(database);
|
||||
for (const QString &filePath : filePaths) {
|
||||
qInfo() << "Executing SQL file:" << filePath;
|
||||
if (!executor(filePath)) {
|
||||
qWarning() << "Failed to execute SQL file:" << filePath;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef DATABASE_MANAGER_H
|
||||
#define DATABASE_MANAGER_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QSqlDatabase>
|
||||
#include <QString>
|
||||
|
||||
class DatabaseManager {
|
||||
public:
|
||||
enum class CreateDatabaseResult { Success, AlreadyExists, Error };
|
||||
|
||||
private:
|
||||
~DatabaseManager();
|
||||
|
||||
DatabaseManager(const DatabaseManager &) = delete;
|
||||
DatabaseManager &operator=(const DatabaseManager &) = delete;
|
||||
|
||||
public:
|
||||
static CreateDatabaseResult
|
||||
createDefaultDatabaseIfMissing(const QString &databaseFileName);
|
||||
|
||||
static bool connectToDefaultDatabase(const QString &databaseFileName);
|
||||
|
||||
static void disconnectDefaultDatabase();
|
||||
|
||||
private:
|
||||
static bool databaseFileExists(const QString &databaseName, QSqlDatabase &db);
|
||||
static bool executeSqlFiles(const QStringList &filePaths, QSqlDatabase &db);
|
||||
};
|
||||
|
||||
#endif // DATABASE_MANAGER_H
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "execute_sql_file.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
ExecuteSqlFile::ExecuteSqlFile(QSqlDatabase &db)
|
||||
: db_(db), delimiter_(";") { // Default delimiter is ";"
|
||||
// Constructor does not open/close the connection
|
||||
}
|
||||
|
||||
bool ExecuteSqlFile::operator()(const QString &filePath) {
|
||||
// Ensure the database connection is open
|
||||
if (!db_.isOpen()) {
|
||||
qWarning() << "Database connection is not open";
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qWarning() << "Failed to open file:" << filePath;
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream in(&file);
|
||||
QString sqlContent = in.readAll();
|
||||
file.close();
|
||||
|
||||
QStringList statements = sqlContent.split("\n", Qt::SkipEmptyParts);
|
||||
|
||||
qInfo() << "Executing SQL file:" << filePath;
|
||||
|
||||
return processStatements(statements);
|
||||
}
|
||||
|
||||
bool ExecuteSqlFile::processStatements(QStringList &statements) {
|
||||
QString currentStatement;
|
||||
|
||||
for (QString &line : statements) {
|
||||
qInfo() << "Processing line:" << line;
|
||||
if (!processLine(line, currentStatement)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute any remaining statement
|
||||
if (!currentStatement.isEmpty() && !executeStatement(currentStatement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExecuteSqlFile::processLine(QString &line, QString ¤tStatement) {
|
||||
if (!filterLine(line)) {
|
||||
qInfo() << "Line is empty after filtering";
|
||||
return true; // Line was empty after filtering
|
||||
}
|
||||
|
||||
if (processDelimiterChange(line)) {
|
||||
qInfo() << "Delimiter changed to:" << delimiter_;
|
||||
return true;
|
||||
}
|
||||
|
||||
currentStatement += line + " ";
|
||||
|
||||
auto delimiterIndex = currentStatement.lastIndexOf(delimiter_);
|
||||
if (delimiterIndex == -1) {
|
||||
qInfo() << "Delimiter not found, current statement is not finished";
|
||||
return true;
|
||||
}
|
||||
qInfo() << "Statement finished, delimiter found at index:" << delimiterIndex;
|
||||
|
||||
// Execute the current statement
|
||||
QString completedStatement =
|
||||
currentStatement.replace(delimiter_, ";").trimmed();
|
||||
if (!executeStatement(completedStatement)) {
|
||||
qWarning() << "Failed to execute statement:" << completedStatement;
|
||||
return false; // Execution failed
|
||||
}
|
||||
qInfo() << "Successfully executed statement:" << completedStatement;
|
||||
|
||||
currentStatement.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExecuteSqlFile::processDelimiterChange(QString &line) {
|
||||
QRegularExpression regex(R"(^DELIMITER\s+(\S+))");
|
||||
QRegularExpressionMatch match = regex.match(line);
|
||||
if (!match.hasMatch()) {
|
||||
return false;
|
||||
}
|
||||
delimiter_ = match.captured(1); // Update the delimiter_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExecuteSqlFile::filterLine(QString &line) {
|
||||
line = line.section("--", 0, 0).trimmed(); // Remove comments and trim
|
||||
return !line.isEmpty(); // Return true if line is not empty
|
||||
}
|
||||
|
||||
bool ExecuteSqlFile::executeStatement(const QString &statement) const {
|
||||
QSqlQuery query(db_);
|
||||
if (!query.exec(statement)) {
|
||||
qWarning() << "Error:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef EXECUTE_SQL_FILE_H
|
||||
#define EXECUTE_SQL_FILE_H
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
#include <QTextStream>
|
||||
|
||||
class ExecuteSqlFile {
|
||||
private:
|
||||
QSqlDatabase &db_; // Reference to the database handler
|
||||
|
||||
// Tracks if the parser is inside a trigger block
|
||||
bool insideTrigger_ = false;
|
||||
|
||||
// Delimiter for SQL statements
|
||||
QString delimiter_;
|
||||
|
||||
public:
|
||||
// Constructor to initialize the database handler
|
||||
explicit ExecuteSqlFile(QSqlDatabase &db);
|
||||
|
||||
// Executes the SQL commands from the given file
|
||||
bool operator()(const QString &filePath);
|
||||
|
||||
private:
|
||||
// Executes a single SQL statement
|
||||
bool executeStatement(const QString &statement) const;
|
||||
|
||||
// Filter out comments and empty lines
|
||||
bool filterLine(QString &line);
|
||||
|
||||
// Processes the DELIMITER command
|
||||
bool processDelimiterChange(QString &line);
|
||||
|
||||
// Processes a single line of SQL and updates the current state
|
||||
bool processLine(QString &line, QString ¤tStatement);
|
||||
|
||||
// Processes a block of SQL statements, handling triggers separately
|
||||
bool processStatements(QStringList &statements);
|
||||
};
|
||||
|
||||
#endif // EXECUTE_SQL_FILE_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef TABLE_INFO_INTERFACE_H
|
||||
#define TABLE_INFO_INTERFACE_H
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
|
||||
class TableInfoInterface {
|
||||
public:
|
||||
virtual ~TableInfoInterface() {}
|
||||
|
||||
virtual QMetaType::Type
|
||||
columnMetaType(const QString &columnName) const noexcept = 0;
|
||||
|
||||
virtual QString columnName(qint32 columnIndex) const noexcept = 0;
|
||||
|
||||
virtual QString databaseTableName() const noexcept = 0;
|
||||
|
||||
virtual QStringList columnNames() const noexcept = 0;
|
||||
|
||||
virtual qint32 columnIndex(const QString &columnName) const noexcept = 0;
|
||||
|
||||
virtual void
|
||||
setDatabaseTableName(const QString &databaseTableName) noexcept = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user