引言
这个项目的来源是由于在电科参加了一门CPP的选修课,老师的期末大作业要求是设计一个mini数据库,我作为小组成员,主要任务是负责DDL部分的代码书写,详细任务如下:
对数据库的操作:
- 创建库(createDatabase)
- 删除库(dropDatabase)
- 切换库(useDatabase)
- 显示库(showDatabases)
对表的操作:
- 创建表(createTable)
- 删除表(dropTable)
- 显示表头信息(descTable)
- 显示表(showTables)
故写此博客以做学习记录,由于cpp和数据库相关知识都是本学期才开始了解,如有谬误或不足欢迎批评指正。
学习历程
了解数据库
由于答主此时(2023.11)正处大二上学期,还未进行数据库相关知识的学习,故我先是简单了解了一下数据库的相关知识,掌握了我需要编写的几个基本指令的使用效果:
关于库的操作
create database;
mysql> create database test;
Query OK, 1 row affected (0.00 sec)
drop database;
mysql> drop database db1;
Query OK, 1 row affected (0.05 sec)
show databases;
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| db1 |
| db2 |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.02 sec)
use database;
mysql> use test;
Database changed
关于表的操作
create table;
mysql> create table tb1(
-> id int,
-> name varchar(10)
-> );
Query OK, 0 rows affected (0.02 sec)
desc table;
mysql> desc tb1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
show tables;
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| tb1 |
+----------------+
1 row in set (0.01 sec)
drop table
mysql> drop table tb1;
Query OK, 0 rows affected (0.01 sec)
下面将该期间查阅过的资料分享给大家:
MySQL 有这一篇就够(呕心狂敲37k字,只为博君一点赞!!!)_mysql 有这一篇就够(呕心狂敲37k字,只为博君一点赞!!!)-CSDN博客
Mac安装MySQL详细教程_mac mysql安装_普通网友的博客-CSDN博客
代码书写
大致了解数据库的基本使用后,我开始了代码书写
分析
总的来说,我的任务不算复杂,只需要将相关的函数用cpp实现并封装好,交给负责前端的同学调用即可。故我将所有函数的返回类型都设置成了bool型。
代码实现
关于库的操作
头文件
// Database.h
#ifndef MINISQL_DATABASE_H
#define MINISQL_DATABASE_H
#include <iostream>
#include <sys/stat.h>
#include <cerrno>
#include <cstring> // 用于 strerror 函数
#include <unistd.h> // 用于 rmdir 函数
#include <string>
#include <filesystem>
#include <vector>
using namespace std;
class Database {
public:
Database();
~Database();
static string currentDbFilename;//标明当前所在的databaseFile名,建议初始化时将其置为空
static string currentDbname;//标明当前所在的database名,建议初始化时将其置为空
static vector<pair<string,vector<pair<string,vector<pair<string, string>>>>>> miniSqlVector;
bool createDatabase(const string& dbName);
bool dropDatabase(const string& dbName);
bool useDatabase(const string& dbName);
bool isDirectoryExists(const string& path);
bool showDatabases(vector<string> &databaseVector);
};
#endif // MINISQL_DATABASE_H
创建库
/**
* @brief 创建数据库
* @param 数据库的名称
* @return 成功则为true,失败则为false
*/
bool Database:: createDatabase(const string& dbName)
{
if(dbName.empty())
{
return false;
}
// 拼接数据库目录路径
string dbPath = fatherPath + dbName; // 注意配置说明 这里的路径一定要正确
// 创建数据库目录
int status = mkdir(dbPath.c_str(), 0755);//后面的参数表示所有人都有读、写、执行权限
if (status == 0) {
vector<pair<string,vector<pair<string, string>>>> dbVector = {};
miniSqlVector.push_back(make_pair(dbName, dbVector));
return true;
} else {
cerr << "Error creating directory: " << strerror(errno) << endl;//查看错误码
return false;
}
}
删除库
/**
* @brief 删除数据库
* @param 数据库的名称
* @return 成功则为true,失败则为false
*/
bool Database:: dropDatabase(const string& dbName)
{
if(dbName.empty())
{
return false;
}
string dbPath = fatherPath + dbName; // 注意配置说明 这里的路径一定要正确
int status = rmdir(dbPath.c_str());
if (status == 0) {
return true;
} else {
cerr << "Error removing directory: " << strerror(errno) << endl;
return false;
}
}
切换库
/**
* @brief 切换数据库
* @param 数据库的名称
* @return 成功则为true,失败则为false
*/
bool Database:: useDatabase(const string& dbName)
{
if(dbName.empty())
{
return false;
}
string dbPath = fatherPath + dbName + '/'; // 注意配置说明 这里的路径一定要正确
if(!isDirectoryExists(dbPath))
{
return false;
}
Database::currentDbname = dbName;
Database::currentDbFilename = dbPath;
return true;
}
/**
* @brief 查找数据库
* @param 数据库的路径
* @return 成功则为true,失败则为false
*/
namespace fs = filesystem;
bool Database:: isDirectoryExists(const string& path) {
return fs::is_directory(path);
}
显示库
/**
* @brief 显示数据库
* @param 存放所有库名称的vector
* @return 成功则为true,失败则为false
*/
bool Database::showDatabases(vector<string>&databaseVector)
{
for (int i = 0; i < Database::miniSqlVector.size(); ++i) {
databaseVector.push_back(Database::miniSqlVector[i].first);
}
return true;
}
database类全部代码
#include "Database.h"
Database::Database() = default;
Database::~Database() = default;
string fatherPath = "../src/data/";//此处声明要存放database的父文件夹地址
string Database::currentDbname; // 静态变量的实际的定义,用于标记当前所在的库名
string Database::currentDbFilename; // 静态变量的实际的定义,用于标记当前所在库的文件路径
vector<pair<string,vector<pair<string,vector<pair<string, string>>>>>> Database:: miniSqlVector;//静态变量的实际定义
/**
* @brief 创建数据库
* @param 数据库的名称
* @return 成功则为true,失败则为false
*/
bool Database:: createDatabase(const string& dbName)
{
if(dbName.empty())
{
return false;
}
// 拼接数据库目录路径
string dbPath = fatherPath + dbName; // 注意配置说明 这里的路径一定要正确
// 创建数据库目录
int status = mkdir(dbPath.c_str(), 0755);//后面的参数表示所有人都有读、写、执行权限
if (status == 0) {
vector<pair<string,vector<pair<string, string>>>> dbVector = {};
miniSqlVector.push_back(make_pair(dbName, dbVector));
return true;
} else {
cerr << "Error creating directory: " << strerror(errno) << endl;//查看错误码
return false;
}
}
/**
* @brief 删除数据库
* @param 数据库的名称
* @return 成功则为true,失败则为false
*/
bool Database:: dropDatabase(const string& dbName)
{
if(dbName.empty())
{
return false;
}
string dbPath = fatherPath + dbName; // 注意配置说明 这里的路径一定要正确
int status = rmdir(dbPath.c_str());
if (status == 0) {
return true;
} else {
cerr << "Error removing directory: " << strerror(errno) << endl;
return false;
}
}
/**
* @brief 切换数据库
* @param 数据库的名称
* @return 成功则为true,失败则为false
*/
bool Database:: useDatabase(const string& dbName)
{
if(dbName.empty())
{
return false;
}
string dbPath = fatherPath + dbName + '/'; // 注意配置说明 这里的路径一定要正确
if(!isDirectoryExists(dbPath))
{
return false;
}
Database::currentDbname = dbName;
Database::currentDbFilename = dbPath;
return true;
}
/**
* @brief 查找数据库
* @param 数据库的路径
* @return 成功则为true,失败则为false
*/
namespace fs = filesystem;
bool Database:: isDirectoryExists(const string& path) {
return fs::is_directory(path);
}
/**
* @brief 显示数据库
* @param 存放所有库名称的vector
* @return 成功则为true,失败则为false
*/
bool Database::showDatabases(vector<string>&databaseVector)
{
for (int i = 0; i < Database::miniSqlVector.size(); ++i) {
databaseVector.push_back(Database::miniSqlVector[i].first);
}
return true;
}
关于表的操作
头文件
#ifndef MINISQL_TABLE_H
#define MINISQL_TABLE_H
#include <vector>
#include <iostream>
#include <sys/stat.h>
#include <cerrno>
#include <cstring> // 用于 strerror 函数
#include <unistd.h> // 用于 rmdir 函数
#include <string>
#include <filesystem>
#include <iostream>
#include <fstream>
#include <map>
using namespace std;
using InnerPair = pair<string, vector<pair<string, string>>>;
using OuterPair = pair<string, vector<InnerPair>>;
using DatabaseVector = vector<OuterPair>;
class Table {
public:
Table();
~Table();
bool createTable(const std::string& tableName, const std::vector<std::pair<std::string, std::string>>& columns);
bool dropTable(const std::string &tableName);
int creatFile_txt(const std::string& fileName);
int dropTxt(const std::string filename);
OuterPair findMatchingPair(const DatabaseVector& miniSqlVector, const string& dbName);
vector<pair<string, string>> findMatchingInnerVector(const string &inputString);
bool descTable(multimap<string, string> &tableHeadMap, const string &tbNeme);
bool showTables(vector<string> &tableVector);
};
#endif //MINISQL_TABLE_H
创建表
/**
* @brief 创建表
* @param 表的名称
* @param 一个vector数组的引用,数组单元内容为两个string的pair,这里需要前端将类似于
* create table tb1(
-> id int,
-> name char,
-> age int
-> );
这样的语句解析为{{"id","int"},{"name","char"},{"age","int"}}这样的数组
* @return 成功则为true,失败则为false
*/
bool Table::createTable(const string& tableName, const vector<pair<string, string>>& columns)
{
string currentDbFilename = Database::currentDbFilename;
string currentDbname = Database::currentDbname;
vector<pair<string, vector<pair<string, vector<pair<string, string>>>>>>& miniSqlVector = Database::miniSqlVector;
if(currentDbFilename == "")
{
return false;
}
if(tableName.empty() || columns.empty())
{
return false;
} else {
string tbPath = currentDbFilename + tableName; // 注意配置说明 这里的路径一定要正确
// 创建表目录
int status = creatFile_txt(tbPath);//后面的参数表示所有人都有读、写、执行权限
if (status == 0) {
// 要查找的数据库名称
string dbNameToFind = currentDbname;
// 查找匹配的最外层的 pair
OuterPair matchingPair = findMatchingPair(miniSqlVector, dbNameToFind);
matchingPair.second.push_back(make_pair(tableName,columns));
miniSqlVector[miniSqlVector.size()-1].second = matchingPair.second;
return true;
} else {
cerr << "Error creating table: " << strerror(errno) << endl;//查看错误码
return false;
}
}
}
/**
* @brief 根据database的name找到minisql中当前的database
* @param minisql
* @param database名
* @return 成功则为true,失败则为false
*/
OuterPair Table::findMatchingPair(const DatabaseVector& miniSqlVector, const string& dbName) {
auto it = find_if(miniSqlVector.begin(), miniSqlVector.end(),
[&dbName](const OuterPair& entry) { return entry.first == dbName; });
if (it != miniSqlVector.end()) {
return *it;
} else {
// 如果没有找到匹配项,可以返回一个默认构造的 pair
return make_pair("", vector<InnerPair>());
}
}
/**
* @brief 封装了一个创建.txt文件的函数
* @param 文件名称
* @return 成功则为true,失败则为false
*/
int Table::creatFile_txt(const string& fileName){
// 创建 ofstream 对象并打开文件
ofstream outFile(fileName);
// 检查文件是否成功打开
if (!outFile.is_open()) {
std::cerr << "无法打开文件: " << fileName << std::endl;
return -1; // 返回非零表示出现错误
}
// 向文件写入内容
// outFile << "Hello, World!" << std::endl;
// 关闭文件
outFile.close();
// std::cout << "文件创建成功: " << fileName << std::endl;
return 0;
}
删除表
/**
* @brief 删除表
* @param 表的名称
* @return 成功则为true,失败则为false
*/
bool Table::dropTable(const string& tableName)
{
if(tableName.empty())
{
return false;
}
string currentDbFilename = Database::currentDbFilename;
string tbPath = currentDbFilename + tableName; // 注意配置说明 这里的路径一定要正确
int status = dropTxt(tbPath);
if (status == 0) {
return true;
} else {
cerr << "Error removing table: " << strerror(errno) << endl;
return false;
}
}
/**
* @brief 删除txt文件的函数
* @param txt文件的名称
* @return 成功则为true,失败则为false
*/
int Table::dropTxt(const string filename) {
// 删除文件
if (std::remove(filename.c_str()) != 0) {
std::cerr << "无法删除文件: " << filename << std::endl;
return 1; // 返回非零表示出现错误
}
return 0;
}
显示表头
/**
* @brief 根据table名显示表头及其数据类型
* @param 存放表头信息的mutimap(一定注意是muti),后续操作针对该map即可
* @param table名
* @return 成功则为true,失败则为false
*/
bool Table::descTable( multimap<string,string> &tableHeadMap,const string& tbNeme)//必须用mutimap,map的键不允许重复
{
vector<pair<string, string>> vectorFound = findMatchingInnerVector(tbNeme);
if(vectorFound.size() == 0)
{
return false;
} else{
for (int i = 0; i < vectorFound.size(); ++i) {
tableHeadMap.insert(make_pair(vectorFound[i].first,vectorFound[i].second));
}
return true;
}
}
/**
* @brief 根据table的name找到存放表头信息的vector
* @param table名
* @return 存放表头信息的vector
*/
vector<pair<string, string>>Table:: findMatchingInnerVector(const string& inputString) {
string currentDbname = Database::currentDbname;
string dbNameToFind = currentDbname;
// 查找匹配的最外层的 pair
OuterPair matchingPair = findMatchingPair(Database::miniSqlVector, dbNameToFind);
for (const auto& outerPair : matchingPair.second) {
if (outerPair.first == inputString) {
return outerPair.second;
}
}
// 如果没有找到匹配项,返回一个空的 vector
return vector<pair<string, string>>();
}
显示所有表
/**
* @brief 显示表
* @param 存放所有表名称的vector
* @return 成功则为true,失败则为false
*/
bool Table::showTables(vector<string>&tableVector)
{
OuterPair outerPair = findMatchingPair(Database::miniSqlVector,Database::currentDbname);
for (int i = 0; i < outerPair.second.size(); ++i) {
tableVector.push_back(outerPair.second[i].first);
}
return true;
}
table类全部代码
#include "Table.h"
#include "../database/Database.h"
Table::Table() = default;
Table::~Table() = default;
// 假设这是你的数据库结构
/**
* @brief 创建表
* @param 表的名称
* @param 一个vector数组的引用,数组单元内容为两个string的pair,这里需要前端将类似于
* create table tb1(
-> id int,
-> name char,
-> age int
-> );
这样的语句解析为{{"id","int"},{"name","char"},{"age","int"}}这样的数组
* @return 成功则为true,失败则为false
*/
bool Table::createTable(const string& tableName, const vector<pair<string, string>>& columns)
{
string currentDbFilename = Database::currentDbFilename;
string currentDbname = Database::currentDbname;
vector<pair<string, vector<pair<string, vector<pair<string, string>>>>>>& miniSqlVector = Database::miniSqlVector;
if(currentDbFilename == "")
{
return false;
}
if(tableName.empty() || columns.empty())
{
return false;
} else {
string tbPath = currentDbFilename + tableName; // 注意配置说明 这里的路径一定要正确
// 创建表目录
int status = creatFile_txt(tbPath);//后面的参数表示所有人都有读、写、执行权限
if (status == 0) {
// 要查找的数据库名称
string dbNameToFind = currentDbname;
// 查找匹配的最外层的 pair
OuterPair matchingPair = findMatchingPair(miniSqlVector, dbNameToFind);
matchingPair.second.push_back(make_pair(tableName,columns));
miniSqlVector[miniSqlVector.size()-1].second = matchingPair.second;
return true;
} else {
cerr << "Error creating table: " << strerror(errno) << endl;//查看错误码
return false;
}
}
}
//抄的gpt
/**
* @brief 根据database的name找到minisql中当前的database
* @param minisql
* @param database名
* @return 成功则为true,失败则为false
*/
// 查找并返回匹配的最外层的 pair
OuterPair Table::findMatchingPair(const DatabaseVector& miniSqlVector, const string& dbName) {
auto it = find_if(miniSqlVector.begin(), miniSqlVector.end(),
[&dbName](const OuterPair& entry) { return entry.first == dbName; });
if (it != miniSqlVector.end()) {
return *it;
} else {
// 如果没有找到匹配项,可以返回一个默认构造的 pair
return make_pair("", vector<InnerPair>());
}
}
/**
* @brief 封装了一个创建.txt文件的函数
* @param 文件名称
* @return 成功则为true,失败则为false
*/
int Table::creatFile_txt(const string& fileName){
// 创建 ofstream 对象并打开文件
ofstream outFile(fileName);
// 检查文件是否成功打开
if (!outFile.is_open()) {
std::cerr << "无法打开文件: " << fileName << std::endl;
return -1; // 返回非零表示出现错误
}
return 0;
}
/**
* @brief 删除表
* @param 表的名称
* @return 成功则为true,失败则为false
*/
bool Table::dropTable(const string& tableName)
{
if(tableName.empty())
{
return false;
}
string currentDbFilename = Database::currentDbFilename;
string tbPath = currentDbFilename + tableName; // 注意配置说明 这里的路径一定要正确
int status = dropTxt(tbPath);
if (status == 0) {
return true;
} else {
cerr << "Error removing table: " << strerror(errno) << endl;
return false;
}
}
/**
* @brief 删除txt文件的函数
* @param txt文件的名称
* @return 成功则为true,失败则为false
*/
int Table::dropTxt(const string filename) {
// 删除文件
if (std::remove(filename.c_str()) != 0) {
std::cerr << "无法删除文件: " << filename << std::endl;
return 1; // 返回非零表示出现错误
}
return 0;
}
/**
* @brief 根据table名显示表头及其数据类型
* @param 存放表头信息的mutimap(一定注意是muti),后续操作针对该map即可
* @param table名
* @return 成功则为true,失败则为false
*/
bool Table::descTable( multimap<string,string> &tableHeadMap,const string& tbNeme)//必须用mutimap,map的键不允许重复
{
vector<pair<string, string>> vectorFound = findMatchingInnerVector(tbNeme);
if(vectorFound.size() == 0)
{
return false;
} else{
for (int i = 0; i < vectorFound.size(); ++i) {
tableHeadMap.insert(make_pair(vectorFound[i].first,vectorFound[i].second));
}
return true;
}
}
/**
* @brief 根据table的name找到存放表头信息的vector
* @param table名
* @return 存放表头信息的vector
*/
vector<pair<string, string>>Table:: findMatchingInnerVector(const string& inputString) {
string currentDbname = Database::currentDbname;
string dbNameToFind = currentDbname;
// 查找匹配的最外层的 pair
OuterPair matchingPair = findMatchingPair(Database::miniSqlVector, dbNameToFind);
for (const auto& outerPair : matchingPair.second) {
if (outerPair.first == inputString) {
return outerPair.second;
}
}
// 如果没有找到匹配项,返回一个空的 vector
return vector<pair<string, string>>();
}
/**
* @brief 显示表
* @param 存放所有表名称的vector
* @return 成功则为true,失败则为false
*/
bool Table::showTables(vector<string>&tableVector)
{
OuterPair outerPair = findMatchingPair(Database::miniSqlVector,Database::currentDbname);
for (int i = 0; i < outerPair.second.size(); ++i) {
tableVector.push_back(outerPair.second[i].first);
}
return true;
}
附:
附上文件目录结构,方便各位对路径上的理解