跳转至

5.2 数据库管理

1. 创建数据库

大作业需要实现一个单用户多数据库的数据库管理系统,因此必然会涉及到不同数据库的创建、删除、切换等操作。简便起见,这里我们推荐采用 “文件夹即数据库” 管理方法,创建一个数据库时我们在数据文件夹下创建一个同名文件夹表示该数据库,当需要查询有哪些数据库时只需要扫描文件夹即可。

例如,假设用户的所有数据均存放在 ./data 下,那么当用户使用 CREATE DATABASE mydb 时,会试图创建一个文件夹 ./data/mydb,如果文件夹创建成功则数据库 mydb 创建成功。

对于大作业的需求而言采用“文件夹即数据库”的思路已经够用,但这里也介绍一下采用引入一个 meta 文件的更严谨的方法,例如创建一个 ./data/db 文件来存储所有数据库的元信息,它主要包含了被创建的所有数据库的名字(以及其他可能的元信息,例如对多用户数据库可能会包含权限记录等),这样可以保证即便用户手动创建了一个 ./data/test 文件夹也不会导致莫名多出来一个 test 数据库。这样做的好处是降低了数据库和文件系统本身的耦合性,另外如果你创建数据库时除了创建文件夹外还做了其他操作(例如生成一个 ./data/mydb/meta 的元信息文件),那么可以避免上述手动创建文件夹导致的异常数据——我们通常认为删改文件(夹)导致异常是合理的,但是创建一个看上去无害的文件(夹)不应该影响系统的正常使用。

文件系统操作

在 C 语言的标准库中只有文件级别的操作,没有文件系统级别的操作,例如遍历一个文件夹的所有文件、创建或删除一个文件夹、移动一个文件等,这导致我们需要用操作系统的接口来实现这些操作,比如 Windows 的 <Windows.h>,Linux 的 <dirent.h><sys/stat.h> 等。

C++ 在 C++17 中终于引入了 <filesystem> 库,可以跨系统地完成相关操作;常用高级语言通常都支持这些操作,如 Java 的 java.io.File、Python 的os.pathpathlib 等。

如果你想使用 C 语言的系统耦合库来处理文件系统,请务必保证你的程序能在 Linux 上正常运行。

此外也有数据库采用“文件即数据库”的做法,例如 SQLite,它将所有数据全部写入一个文件中,这样做理论上效果与文件夹相差不大,只是(后文会提到)用文件夹的方法借用了文件系统来区分不同表格、索引等,而单文件法需要自行组织这些结构。如果你有兴趣也可以采用单文件法,它可以降低对文件系统的依赖,但这可能会导致你无法采用 数据表管理 中介绍的方法。

2. 切换数据库

执行 DML 需要已经指定了某个数据库后在该数据库中执行,因此我们通常需要用 USE <database> 语句来显式地指出当前使用的数据库。

一种最简单的实现模式是仅在前端记录当前所使用的数据库,每次执行查询时将数据库名一并发送给后端;但这不是最佳实践,通常我们在切换数据库时还可能会关闭原来数据库的文件、刷新缓存、预加载新数据库的一些文件等,所以最好在前端与后端都记录当前正在使用的数据库:后端记录状态以处理查询,前端可以在交互模式下为用户展示当前正在使用的数据库。

如果启动 DBMS 时没有使用 -d 参数指明数据库,则当前没有使用任何数据库,此时 DBMS 应当不允许用户执行基于具体数据库的 DML 。

3. 删除数据库

如果创建数据库采用了创建文件夹的方法,那么删除数据库自然也应该删除文件夹,但注意这里应该是递归删除,数据库内的数据表等都要删除。删除数据库的操作可能和已经打开的文件等冲突,你应该妥善处理这一问题。

Authors: Congyuan Rao