——命令解析器、数据存储的设计与实现
摘 要
当今时代,“数据”已经成为一种资源。随着各种数据获取技术和数据库技术的迅速发展,人们积累的数据越来越多,如何更加合理的管理数据显得更加重要。小型数据库就是模拟目前比较流行的一些大型数据库,实现通过在命令行输入相应命令来对数据进行存储,管理和查询。
该小型数据库MyDB包括两大模块:SQL命令解析器及数据存储模块。SQL命令解析器负责解析用户命令并完成用户对表的创建、删除、插入、更新等操作;数据存储模块的主要功能是保存和管理用户的数据。整个系统是用C语言、采用模块化的程序设计思想实现的。
关键词:MyDB;命令解析;数据存储;C语言
Minidatabase
---- Design and Implementation of Command Interpreter and
Data Storage
Abstract
In this information era, data has been a kind of resource. With the fast development of data getting technology and database technology, people accumulate more and more data. How to manage these data more rational become more and more important. Minidatabase is to simulate popular database at present and implement data storage, management and querying by inputting commands from command line.
This Minidatabase ——MyDB includes two modules: SQL command parser and data storage. SQL command parser takes in change of parsing user commands and operating tables, such as creating a table, deleting a table, inserting elements into table and updating table. The primary function of data storage module is to save and manage user data. The whole system is designed with the idea of modularized programmer and developed with C program language.
Key words: MyDB ; command parse ; data storage ; C program language
目 录
论文总页数:24页
1 引言 .................................................................... 1 1.1 数据库课程教学的现状 ................................................... 1 1.2 研制DBMS的重要性 ...................................................... 1 1.3 MyDB的设计目标 ........................................................ 2 2 数据库理论 .............................................................. 2 2.1 数据元素的表示 ........................................................ 2 2.1.1字段 ............................................................... 2 2.1.2记录 ............................................................... 3 2.1.3块 ................................................................. 3 2.2 查询编译器 ............................................................ 3 3 MyDB的实现 .............................................................. 5 3.1 记录的定义 ............................................................ 5 3.2 命令解析模块 .......................................................... 6 3.2.1 词法分析器 ......................................................... 7 3.2.2 语法分析器 ........................................................ 11 3.2.3 SQL语句的实现 .................................................... 13 3.3 基本表模块 ........................................................... 18 3.3.1数据组织 .......................................................... 18 3.3.2基本表的实现 ...................................................... 19 3.4 数据存储模块 ......................................... 错误!未定义书签。 结 论 ..................................................... 错误!未定义书签。 参考文献 ................................................... 错误!未定义书签。 致 谢 ..................................................... 错误!未定义书签。 声 明 ..................................................... 错误!未定义书签。
1 引言
1.1 数据库课程教学的现状
现在数据库教学的不足突出地表现在以下几点:
1.普遍只强调理论,不重视实践,在学习过程中难以对概念深刻领悟,课程结束后就很快把其中许多内容给淡忘掉了。
2.现有对数据库的实践也是流于形式,内容肤浅与真实的数据库管理系统相去甚远。比如用SQL语言对数据库进行一定的创建查询操作。这些实践都不过是对数据库管理系统的使用,根本谈不上了解数据库本身的运行机理。而且这些实践都太过理想化,完全把底层原理透明化了,这些实践充其量只不过是对SQL语言熟悉而已。
3.用真实的数据库管理系统来实践显然要好得多。但现实中的数据库管理系统都太过庞大,比如开源的数据库管理系统MYSQL,仅源代码就达数十万行之多。专业人员阅读起来都不会很容易,更不要说刚读本科的学生对其进行修改了,所以收效甚微。
以上三点明显地说明了:“实践”在数据库原理教学中的重要性。需要一个能够真正对数据库所学理论进行有效的实践的数据库管理系统。但缺少一个好的教学用数据库管理系统,现有的教学用数据库管理系统并不那么适合中国的实际情况。
因此,无论是从应用的角度还是学习数据库的理论教学的角度来看,设计与实现一个小型的数据库管理系统都是很有必要的。
1.2 研制DBMS的重要性
数据库技术产生于1970年前后。它的出现使得计算机的应用进入了新的时期,社会的每个领域都与计算机发生了联系。数据库技术聚集了数据处理最精华的思想,是管理信息最先进的工具。信息社会的紧迫需求使数据库技术成为计算机园地中一支最有生命力的新秀。而与人工智能的结合又使它获得了新的血液。20世纪80年代中期数据库技术进入一个新的层次,智能数据库、演绎数据库、专家数据库、面向对象数据库、工程数据库、多介质数据库、并行数据库、实时数据库等就是当代数据库研究的前沿。
数据库管理系统(DBMS)的研制是一件非常复杂的软件工程。它涉及的面非常广泛,需要软件、硬件及设备方面的知识;需要一定的物质条件;需要研制人员的丰富的编程经验和精深的软件技术;需要科学的管理方法和科学先进的测试技术。当然由于系统的功能和规模不一样,其复杂程度也相差很大。大一点的数据库如IMS花费几千人年,系统R的研制花费了120个专家人年以及几千程序员
第 1 页 共 24 页
人年,SYSTEM2000花费400人年。数据库的设计与数据库的设计不同,前者属于系统软件设计,与机器世界比较接近,它的基础是OS;后者属于应用软件设计,与现实世界更为接近,它的基础是数据库。所以数据库是介于用户程序和OS之间的一个中间媒介,是使得物理数据库与用户程序相互的软件系统。
研制数据库对于从事数据库开发的科研人员和教学人员是一件十分有价值的工作。通过参加研制数据库的工作,可以加深对数据库技术的理解,弄清其来龙去脉,提高技术水平,从而改进数据库教学质量。更重要的是满足社会需求。同时数据库的研制是一件很基础的工作,是研究面向对象数据库、分布式数据库、知识库以及智能数据库的基础。因此,数据库原理一般都作为计算机专业的基础课程学习。
1.3 MyDB的设计目标
先看看国际上有关数据库发展的情况。随着计算机广泛而深入地应用与社会各行各业,作为其中重要支柱的系统软件——数据库变的越来越庞大,功能越来越强,而且这种发展势头丝毫不见有放慢的迹象。特别是随着网络通信技术的发展,现代主流的数据库厂商纷纷把网络特性集成系统之中,甚至还出现了专门应用于网络环境的分布式数据库管理系统。所有这些数据库的复杂性给数据库的设计带来很大的挑战。
如果个人想设计并实现一个商用的数据库基本上是不可能的,复杂的技术细节会把数据库的最基本的理论完全掩盖。因此,学生很难通过商用的数据库学习数据库的原理。MyDB是一个面向教学用的DBMS,这是必须首先坚持的,同时亦对DBMS的存储管理、SQL语言感兴趣。
总之,MyDB是一个基于关系代数的、用C实现的、面向教学的关系型数据库管理系统。
2 数据库理论
2.1 数据元素的表示
首先,研究一下最基本的数据元素的表示,即关系数据库系统中的属性值的表示。这是用“字段”来表示的。然后,考察字段如何组装成存储系统中更大的元素:记录、块和文件
2.1.1 字段
属性需要用定长或变长的字节序列表示,称作“字段”;
可以用下式所示的CREATE TABLE 语句在SQL系统中声明一个关系。数据库负责表示和存储由这个定义描述的关系。既然关系是元组的集合,元组与记录或
第 2 页 共 24 页
“结构”(C或C++术语)相似,可以设想每一个元组在磁盘中作为一条记录来存储。记录会占据某个磁盘块(或一部分),在记录内部,对应于关系的每一个属性有一个字段。
CREATE TABLE StudentInfor
(
number int ,
name CHAR(6) , age int ,
address CHAR(20) , „„ )
2.1.2 记录
字段被组装成定长或变长的集合,成为“记录”; 定长记录的构造
元组由记录表示,而记录由上述所讨论的各种字段组成。最简单的情况是记录的所有字段均为定长,则可以将字段连接成记录。
记录首部
当设计记录的格式的时候,必然会引出另一个问题:通常在记录中需要保存一些信息,而这些信息不是任何字段的值。因此有必要在记录的首部添加相关信息,如:记录长度、字段数等。
2.1.3 块
构成一个关系或类的外延的记录集存储为块的集合,成为文件。
2.2 查询编译器
查询处理器需采取三个主要步骤:
1)对使用诸如SQL的某种语言书写的查询进行语法分析,亦即将查询语句转换成按某种有用方式表示查询语句结构的语法树;
2)把语法分析树转换成代数关系表达式树(或某种类似标记),称之为逻辑查询计划;
3)逻辑查询计划需转换成物理查询计划,物理查询计划不仅指名了要执行的操作,而且也找出了这些操作执行的顺序、执行每步所用的算法、获得所存储数据的方式以及数据从一个操作传递给另一个操作的方式。
查询编译的开始几个阶段如图所示:
第 3 页 共 24 页
图1 查询编译的阶段图
1. 语法分析与语法分析树
语法分析器的工作是接收用类似SQL这样的语言编写的文本并将之转换成语法分析树,结点对应于以下两者之一:
1)原子:它们是词法成分,如关键字(如SELECT等)、关系或属性的名字、常数、括号、操作符(如+等),以及其它成分;
2)语法类:即在一个查询中起相似作用的查询子成分所形成族的名字。可以用尖括号将描述性的名称括起来表示语法类。例如, 如果结点是一个原子,则该结点没有子女。然而,若该结点是一个语法类,则其子女通过该语言的语法规则之一进行描述。 1. SQL的一个简单子集的语法 通过给出可用于SQL子集的语言的某些规则,我们借此说明语法分析树的过程。 1)查询 语法 这两条规则说明一个选择列表可以为任何由逗号分隔的属性列表:要么是单个属性,要么是一个属性、一个逗号以及一个或多个属性的任意列表。 3)From列表 第 4 页 共 24 页 这里的from列表可由任意用逗号分隔的关系列表组成。 这里只列出了部分规则,其他的规则请参考其他的书籍。 我们来看这样一个查询语句: SELECT title FROM StarsIn WHERE starName IN ( SELECT name FROM MovieStar WHERE birthdate LIKE ‘%1960’ ); 按照我们所描绘的语法,此查询语句的语法分析树如下所示 图2 语法分析树 根是语法类 3 MyDB的实现 3.1 记录的定义 在这里,段是解析用户命令的最小单位,若干段的集合就构成了一条记录。 第 5 页 共 24 页 因此段和记录的定义是进行命令解析器及数据存储的基础。 在MyDB中,段与记录的关系可表示如下: 字段名1 记录字段数 长度 字段长度 字段长度 字段长度 字段类型 字段类型 „„ 字段类型 文件指针 字段名2 字段名n 图3 段与记录的关系 段和记录的定义在hrecord.h中实现: 1.段的定义: typedef struct FldInforStruct { /* 字段名 */ char fldname[10] ; /* 字段类型 */ char fldclass[6] ; /* 字段长度 */ int fldlen ; /* 小数长度 */ int dicimllen ; } FieldInfor_T ; 2. 记录的定义: typedef struct WorkAreaStruct { /* 记录长度 */ int reclen ; /* 字段数 */ int fldnum ; /* 记录总数 */ int rectotal ; /* 字段结构信息指针数组,0号单元未用 */ FieldInfor_T fldinforptr[MAX_FIELD_NUM + 1]; /* 文件指针 */ FILE *DBFile_ptr ; } RecordWorkArea ; 3.2 命令解析模块 SQL语言是一种面向集合的结构化查询语言。用SQL语言编写的命令一般都是解释执行的。所谓解释执行,就是解释和执行同步,不是像编译那样先把程序全部扫描一遍翻译成机器指令再直接运行机器指令。一般SQL语言可以嵌套在C等其他的高级语言中。因此,现在的商业用数据库都有自己的一套编译器系统,实现SQL解释(或编译)和高级语言编译器的无缝集成。由于MyDB的各个模块 第 6 页 共 24 页 接口是采用C语言函数调用,因此SQL语言也不可避免地要和底层模块的接口函数打交道。MyDB命令解析器具体的原理图如下: 图4 命令解析器具体工作流程原理图 3.2.1 词法分析器 词法分析器将SQL源程序解释成一个个的记号,然后将记号的类型以及记号所对应的值返回给语法分析器。 MyDB的词法分析过程如图所示: 图5 词法分析 1) 首先删除用户命令中的空格、制表符、逗号等,提取相关的字符,这个 功能由函数CutBlankTab_Head_Tail完成; 2) 然后扫描提取的字符串,分步将关键字提取出。第一步,连分隔符(如 括号等)一起提取,这个由函数GetSubstr_Delimitor完成;第二步,提取分隔符内的字符串,这步由函数GetSubstr_BetweenDelim完成;第三步,提取合法字符集,这一步由函数GetSubstr_ValidChar完成; 3) 将字段信息存入数组,包括所提取出的字段名,字段类型,字段长度等, 这个主要由函数*Change完成; 4) 将记录值存入结构中,将所提取出来的记录值插入相应的结构之中。 实现各流程的函数会在下面分别叙述。 1. MyDB的词法分析器的关键字表为: enum SysKeyWordSet 第 7 页 共 24 页 { } ; CREATE,DROP,INSERT,DELETE, SELECT,UPDATE,EXIT,HELP 由于MyDB只是一个面向教学的微型数据库,所以它所包含的关键字仅仅是标准SQL的一个很小的子集。 2. 词法分析器的接口参数: 用户给出的命令结构是SQL编译器进行词法分析的基础。词法分析的任务在于提取关键字、省略多余空格等等。所以用户命令结构信息的定义也是必要的。 /* 用户命令结构信息 */ typedef struct { /* 命令内部代码,如\"INSERT\" , \"CREATE\"等 */ SysKeyWord_Type Cmd ; /* 表名 */ char userstr[10] ; /* 字段信息指针 */ FieldInfor_T *fld ; /* 记录值指针数组 */ char *recvalptr[MAX_FIELD_NUM + 1] ; /* 查找范围 */ char range[10] ; /* 介词 */ char parse[20] ; /* 表达式 */ char expression[30] ; char token[100] ; int count ; }CmdRec_Type ; 此接口参数定义了用户命令的结构信息,以便后来词法分析中关键字及有效值的提取。 3. 词法分析器的接口函数 词法分析器的接口函数定义在头文件lexical_tool.h中。主要是与词法分析相关的函数体,包括: void CutBlankTab_Head_Tail (char *S); 此函数用于删除词头和词尾的空格以及控制符; void CutComma (char *S) ; 此函数用于删除词头逗号; int CountSymbol (char *S , char c) ; 此函数用于计算字符个数; 第 8 页 共 24 页 int GetSubstr_Delimitor (char *S , int *ScanPos , char * Delimitor , char *Substr) ; 此函数根据分隔符集合Delimitor,在串S中扫描起点ScanPos,取出子串Substr,并且移动扫描起点到新的位置,用于词法分析。伪码如下: int GetSubstr_Delimitor(char *S , int *ScanPos , char * Delimitor , char *Substr) { InStr = FALSE; /*跳过分隔符(双引号的分隔符除外)*/ while((*ScanPos < len) && (strchr(Delimitor , *(S + (*ScanPos))) != NULL)) { if(*(S + (*ScanPos)) == '\\\"') InStr = !InStr ; (*ScanPos) ++ ; } SubstrBegin = *ScanPos ; /*扫描子串,包括双引号内的分隔符*/ while((*ScanPos <= len) && (InStr || (strchr (Delimitor , *(S + *ScanPos)) == NULL))) { if(*(S + (*ScanPos)) == '\\\"') InStr = !InStr ; (*ScanPos) ++ ; } /*复制子串*/ strncpy(Substr , S + SubstrBegin , *ScanPos - SubstrBegin) ; /*串尾加0*/ (Substr + *ScanPos - SubstrBegin) = '\\0' ; ok = ((strcmp(Substr , \"\") != 0) && (*ScanPos) <= len) ; } int GetSubstr_BetweenDelim(char *S , int *ScanPos , char Delim1 , char Delim2 , char *Substr); 此函数在串S中扫描起点,取出在分隔符Delim1和Delim2之间的子串Substr,并且移动扫描起点到新的位置,用于词法分析。伪码如下: int GetSubstr_BetweenDelim(char *S , int *ScanPos , char Delim1 , char Delim2 , char *Substr) { left = *ScanPos ; len = strlen(S) ; if(len == 0) { 第 9 页 共 24 页 } *Substr = '\\0' ; return (TURE) ; } /*验明左边的分隔符*/ while((left < len) && (Delim1 != S[left])) ++left ; if(S[left] != Delim1) return 0 ; right = left + 1 ; /*验明右边的分隔符*/ while((right < len) && S[right] != Delim2) ++right ; if(S[right] != Delim2) return 0 ; /*复制子串*/ strncpy(Sunstr , &S[left + 1] , right - left + 1) ; Substr[right - left + 1] = '\\0' ; /*移动扫描点*/ *ScanPos = right + 1 ; int GetSunstr_ValidChar(char *S , int *ScanPos , char *ValidCh , char *Substr); 此函数在串S中扫描起点后,取出由合法字符集合ValidCh组成的子串Substr,并且移动扫描点到新的位置,用于词法分析。伪码如下: int GetSunstr_ValidChar(char *S , int *ScanPos , char *ValidCh , char *Substr) { len = strlen(S) ; *Substr = '\\0' ; while((*ScanPos < len) && (strchr(ValidCh , S[ScanPos])) == NULL) ++ (*ScanPos) ; left = *ScanPos ; while((*ScanPos < len) && (strchr(ValidCh , S[ScanPos])) != NULL) ++ (*ScanPos) ; /*复制子串*/ strncpy(Substr , &S[left] , *ScanPos - left) ; Substr[*ScanPos - left] = '\\0' ; return (*ScanPos - left) ; } void CloseWA(); 此函数用于关闭工作区间; void ClearCmdRec (CmdRec_Type CmdRec); 此函数用于把命令行记录,即分析结果清0; 第 10 页 共 24 页 void CallbackCmdRec (CmdRec_Type CmdRec); 此函数用于回收分配的指针空间并清0; FieldInfor_T ToFldStr(char *Token , int i); 此函数通过字符串string,分别对各个参数进行赋值; FieldInfor_T StoreToStr(char *Token , int i); 此函数将第i个字段信息数组Token的字段名、字段类型,整数和小数长度存入结构中,供建表时使用; FieldInfor_T *Change(char *Token1); 此函数用于将字段信息放入CmdRec的字段结构数组中; char (*Value(char *Token))[15][30]; 将用户插入的记录值分别存入CmdRec结构的recvalptr中,供插入时使用; 以上函数均在lexical_too.c中进行了实现。SQL命令编译器对用户命令进行扫描,调用以上函数实现基本的词法分析。先根据第一个字符判断该记号可能是什么类型,然后根据好的规则继续分析,直到遇到一个新的记号类型。 3.2.2 语法分析器 语法分析器依据SQL语言的相关规则,对由词法分析器返回的记号序列进行分析。语法分析器和词法分析类似,不过语法分析器是在词法分析的基础上,对一系列记号进行分析。 MyDB的语法分析过程图如下: 图6 语法分析 语法分析过程如下: 1) 删除用户命令中的词头尾的空格、制表符等无关元素; 第 11 页 共 24 页 2) 扫描用户命令,判断其词性并做相应的处理,直到扫描到用户命令的结 尾; 实现语法分析器相应的函数体CmdRec_Type CmdLine_To_CmdRec (char *CmdLine)的伪码表示为: CmdRec_Type CmdLine_To_CmdRec (char *CmdLine) { /*删除命令行头尾空格及制表符,并取得括号之间的字符串;*/ CutBlankTab_Head_Tail(CmdLine) ; GetSubstr_BetweenDelim (CmdLine , &TokenPos , '(' , ')' , CmdRec.token) ; while(strlen(CmdLine) >= 1) { for(i = 0 ; i < strlen(Cmdline) ; i ++) { if(判断是否为行为动词) /*存储此关键字*/ CmdRec.Cmd = SysKeyWordArray[i].SysW_N ; else if(判断是否为介词) /*存储此介词*/ strcpy(CmdRec.parse , Token) ; else if(判断是否为表达式) ptr = strchr(Token , '=') ; if (ptr) { strcpy(CmdRec.expression , Token) ; } else { /*判断是否为全部范围*/ ptr = strchr(Token , '*') ; if(ptr) { strcpy(CmdRec.range , '*') ; } else { ptr = strchr(Token , ',') ; if(ptr) { strcpy(CmdRec.range , Token) ; } else { strcpy(CmdRec.userstr , Token) ; 第 12 页 共 24 页 } } } } } return ; } 对具体SQL语句的语法的判断在Mydb.c中进行处理。相应的伪码表示如下: int main(void) { swich(CmdRec.Cmd) { case CREATE: case DROP: case INSERT: case SELECT: case UPDATE: case DELETE: case EXIT: case HELP: default: } return 0; } Do_Create(CmdRec) ;break Do_Remove(CmdRec.userstr) ;break; Do_Insert(CmdRec) ;break; Do_Select(CmdRec,CmdRec.token) ;break; Do_Update(CmdRec,CmdRec.token);break; Do_Delete(CmdRec,CmdRec.token);break; exit(0);break; Do_Help;break; break; 3.2.3 SQL语句的实现 MyDB所支持的SQL语句大体遵从标准的SQL语句的规范。在其规范上有稍许变动。MyDB支持标准SQL语句的一个子集,具体所支持的SQL语句的行为动词是:create、select、drop、delete、update、insert等。 SQL语句的接口函数均包含在mysql.h头文件中,在mysql.c中进行具体的实现。接口函数如下: int Do_Remove(char *file) ; 删除文件,相当于Drop语句; void Do_Insert(CmdRec_Type CmdRec) ; 插入语句,将要插入的值放入表内,插入语句的实现过程如下图: 第 13 页 共 24 页 图7 Insert语句实现过程 伪码实现如下: void Do_Insert(CmdRec_Type CmdRec) { 定位头文件; 读取一级数据元; 读取二级数据元,即各字段信息; for(i = 0 ; i < 插入字段数 ; i ++) { /*根据不同情况将要出入的记录值写入文件;*/ if(插入的属性值为float) 将此float类型字段值插入表中; else if(插入的属性值为int) 将此int类型字段值插入表中; else (插入的属性值为char) 将此char类型字段值插入表中; } 每插入一条记录,记录总数加1; 关闭数据文件; } void Do_Select (CmdRec_Type CmdRec , char *Token); 查询语句,根据不同的情况实现对数据的简单查询,SELECT函数的分析过程如下图: 第 14 页 共 24 页 图8 Select语句实现过程 伪码如下: void Do_Select(CmdRec_Type CmdRec , char *Token) { 取得运算符在查询条件数组中的位置; 把数据文件一二级原数据载入工作区; 取得查询条件中字段相对应的本记录首位置的长度以便定位; for(m = 0 ; m < reordtotal ; m ++) { 逐一从文件读取每一条记录; 根据字段类型读取记录值; swich(ch) { /**根据记录类型对取出的记录值进行处理即满足条件的记录输出到屏幕**/ case 整形: 该整型数据满足条件,定位文件至该记录头位置; 逐一输出该记录字段值; break; case 浮点形: 该浮点型数据满足条件,定位文件至该记录头位置; 逐一输出该记录字段值; break; case 字符型: 该字符型数据满足条件,定位文件至该记录头位置; 逐一输出该记录字段值; break; 第 15 页 共 24 页 } default:break; } 关闭数据文件; } void Do_Delete(CmdRec_Type CmdRec , char *Token); 对基本表的数据的删除。MyDB的删除函数的分析过程如下图: 图9 Delete语句实现过程 伪码如下: void Do_Delete(CmdRec_Type CmdRec , char *Token) { 打开数据文件; for(m = 0 ; m < recordtotal ; m ++) { swich(ch) { /**根据删除条件字段的类型的不同,分流处理**/ case 整形: 满足删除条件显示的屏幕; 不满足删除条件的写入临时新文件; break; case 浮点形: 第 16 页 共 24 页 满足删除条件显示的屏幕; 不满足删除条件的写入临时新文件; break; case 字符型: 满足删除条件显示的屏幕; 不满足删除条件的写入临时新文件; break; default:break; } } 关闭数据文件; } void Do_Update(CmdRec_Type CmdRec,char *Token); 更新数据库中的数据。MyDB中的Update函数分析过程如下图: 图10 Update语句实现过程 伪码如下: void Do_Update(CmdRec_Type CmdRec , char *Token) { 打开数据文件 ; for(m = 0 ; m < recordtotal ; m ++) { /**根据字段的不同进行分流处理**/ swich(ch) { case 整形: 将满足更新条件的进行更新并现实在屏幕; 不满足条件的写入临时文件; break; 第 17 页 共 24 页 } case 浮点形: 将满足更新条件的进行更新并现实在屏幕; 不满足条件的写入临时文件; break; case 字符型: 将满足更新条件的进行更新并现实在屏幕; 不满足条件的写入临时文件; break; default:break; } 关闭数据文件; } void Do_Help(); 帮助文档,在这里就不再赘述。 void Do_Create(CmdRec_Type CmdRec) ; 创建基本表。参见基本表模块设计。 3.3 基本表模块 基本表是关系数据库的基础,没有基本表提供的路径无关的存储支持,所有基于基本表的操作都将难以实现。基本表模块本身就可以看做是一个表式的系统。 基本表模块负责把整个数据以表的方式来管理,用户可以通过关系名、主键的值以及属性名的关联地址唯一地操控表中的任何数据。基本表模块在提供关联地址支持的同时,也提供基本表的其他操作。基本表的常用操作有:创建、删除一个表,添加、删除一个记录,添加 、删除一个列等。关于数据库的操作本来不应该是属于基本表模块的。但是数据库由基本表组成,基本表和数据库的关系太紧密了,如果要将数据库的操作单独分离成一个模块,那它不可避免地要访问基本表的内部数据,那样反而造成不安全。因此,在MyDB中将数据库的操作和基本表的操作放在一个模块实现。 在MyDB中,每一个表即对应一个文件,表中的数据按照顺序表的数据结构进行存储、删除、更新等等。 3.3.1 数据组织 在MyDB中,将多个表实现在一起,对应一个数据库。每个表的基本属性信息,包括表的名称、列的名称、列的数据类型等都统一存放在一个二进制文件中。将表属性放在一个文件中的目的是为了方便查找表的信息。 MyDB的表的数据组织方式如下: 第 18 页 共 24 页 记录长度 记录长度 字段总数 字段总数 字段1 字段1 字段2 字段2 „„ „„ 字段n 字段n „„ „„ „„ „„ „„ „„ „„ „„ „„ „„ „„ „„ 图11 MyDB表的组织方式图 3.3.2 基本表的实现 基本表的接口函数定义在table.h头文件中,具体实现是在table.c文件中。至于删除一个表、在表中对数据进行删除、插入、更新等操作,在命令行解析模块中进行了具体的讲解,这里就不再赘述。 MyDB的基本表的实现过程如下图所示: 图12 基本表的实现过程 1) 打开文件; 2) 若打开文件失败则输出文件不存在;若成功则计算记录长度,直到扫描 所有字段结束; 3) 将数据写入文件; 4) 关闭文件指针。 创建基本表的接口函数为: void Do_Create (CmdRec_Type CmdRec); 伪码如下: void Do_Create(CmdRec_Type CmdRec) { FILE fp ; 第 19 页 共 24 页 } /*记录长度*/ string[0] = reclen ; /*字段数*/ string[1] = CmdRec.count ; /*记录总数*/ string[2] = total ; 将一级数据写入文件; 将二级数据即各字段结构信息写入文件; fclose(fp) ; } int string[3] = {0,0,0} ; /*打开文件*/ fp = fopen(TemName , \"w + b\") ; if(fp == NULL) { printf(\"文件不存在\\n\") ; } for(i = 0 ; i <= CmdRec.count ; i ++) { 计算记录的长度; 第 20 页 共 24 页 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- dfix.cn 版权所有 湘ICP备2024080961号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务