基于Python的数据库实现

FullHouse

发布日期: 2019-02-20 16:15:41 浏览量: 1360
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

1.需求分析

1.1 概述

从底层做起,实现数据库的组织、存储、检索、更新和索引等功能。

1.2 基本功能

  • 设计特定的数据结构,用于存储数据表、视图、索引三种数据库对象的元数据信息,建立数据库系统的数据字典

  • 设计特定的数据结构,用于存储数据表中的数据

  • 设计特定的数据结构,用于存储索引数据

  • 设计特定的数据结构,分别用于存储用户和访问权限的信息

  • 输入“help database”命令,输出所有数据表、视图和索引的信息,同时显示其对象类型;输入“help table 表名”命令,输出数据表中所有属性的详细信息;输入“help view 视图名”命令,输出视图的定义语句;输入“help index 索引名”命令,输出索引的详细信息

  • 解析CREATE、SELECT、INSERT、DELETE、UPDATE等SQL语句的内容;检查SQL语句中的语法错误和语义错误

  • 执行CREATE语句,创建数据表、视图、索引三种数据库对象;创建数据表时需要包含主码外码、唯一性约束、非空约束等完整性约束的定义

  • 执行SELECT语句,从自主设计的数据表中查询数据,并输出结果;在SELECT语句中需要支持GROUP BY、HAVING和ORDER BY子句,需要支持5种聚集函数

  • 执行INSERT、DELETE和UPDATE语句,更新数据表的内容;更新过程中需要检查更新后的数据表是否会违反参照完整性约束。如果是,则提示违反哪一条完整性约束,并拒绝执行更新操作;如果否,提示数据表更新成功,并说明插入、删除或修改了几个元组

  • 当数据表的内容更新后,根据索引的定义,自动更新索引表的内容

  • 在有索引的数据表上执行查询语句时,首先利用索引找到满足条件的元组指针,然后通过指针到数据表中取出相应的元组

  • 执行GRANT语句,为用户授予对某数据库对象的SELECT、INSERT、DELETE、UPDATE等权限;执行REVOKE语句,收回上述权限

  • 用户登录时,需要输入用户名;如果用户没有被授权,则拒绝执行用户查询或更新等操作,并给出提示信息

  • 将SELECT语句转化为关系代数表达式,再利用查询优化算法对关系代数表达式进行优化,输出优化后的关系代数表达式或SELECT语句

1.3 系统需求

  • 开发语言:Python

  • 开发工具:Pycharm

2.概要设计说明

2.1 数据文件存储

设置system库,table_information库,view库为系统初始化后建立的初始数据库。文件使用excel存储。

2.2 模块功能及描述

2.1.1 run函数模块

run函数模块是整个系统的入口,本模块的主要功能是初始化用户,调用各模块,处理用户输入,实现数据库管理功能。

2.1.2 用户验证及登录模块

本模块功能是进行用户的登录认证。

2.1.3 创建表模块

根据标准 SQL 语言将输入语句进行分割,获得表名,各个属性名,属性类型,约束条件等内容,再进行表的创建。

2.1.4 创建视图模块

本模块通过 CREATE VIEW VIEW_NAME AS SELECT …语句来创建视图,将存储视图和对应的语句。

2.1.5 插入数据模块

将用户输入的数据存入相应的表中并进行约束检查。

2.1.6 更新数据模块

根据用户的 WHERE 条件更新符合条件的元组并进行约束检查。

2.1.7 查询数据模块

根据用户的查询条件进行查询。

2.1.8 删除数据模块

根据用户的 WHERE 条件删除符合条件的元组。

2.1.9 帮助模块

  • HELP DATABASE 可查看当前数据库下的所有的表,视图信息

  • help table 表名可输出数据表中所有属性的详细信息

  • 输入“help view 视图名”命令,输出视图的定义语句

  • 输入“help index 索引名”命令,输出索引的详细信息

2.1.10 授权与权限收回模块

通过grant和revork实现用户对表和视图的 CREATE、DELETE、UPDATE、INSERT 四种操作的权限授予与收回。

2.1.11 索引建立和更新模块

使用create语句建立索引,在内容更新后更新索引。

2.3 模块调用图

3.详细设计说明

3.1 环境依赖

本程序用到的库:

``python
from openpyxl import
import os
import re
from index import

from prettytable import PrettyTable
import hashlib
import bisect
import Queue

  1. ##3.2 数据存储
  2. 数据库的数据使用xlsx格式存储,每一个文件对应一个数据库,工作簿对应库中的表,表结构和文件结构对应。
  3. 索引使用文本存储dict
  4. 使用openpyxl库进行文件内容的操作。
  5. 数据库示例:
  6. ![](/upload/image/125/a7c8b429ad2b2b6df89ecb2b52a7932d.jpg)
  7. ![](/upload/image/125/0a865f7e5408db187c4150f361de55f9.jpg)
  8. ##3.3 run函数的功能
  9. ```python
  10. * 程序初始化
  11. * 打印欢迎语句
  12. * 处理用户登录与认证
  13. * 处理帮助命令和退出命令
  14. def run():
  15. Initialization()
  16. welcome()
  17. login()
  18. while True:
  19. command = get_command()
  20. #print command
  21. if command == 'quit' or command == 'exit':
  22. print("[🍻] Thanks for using L-DBMS. Bye~~")
  23. exit(0)
  24. elif command == 'help':
  25. help()
  26. else:
  27. query(command)

3.3 初始化函数的功能

  1. * 创建数据存储目录
  2. * 创建系统数据库文件
  3. * 创建系统用户
  4. * 赋予系统用户权限
  5. def Initialization():
  6. if not os.path.exists(db_path):
  7. os.mkdir(db_path)
  8. if not os.path.exists("data/table_information.xlsx"):
  9. Workbook().save("data/table_information.xlsx")
  10. if os.path.exists("data/system.xlsx"):
  11. print "Initializating......"
  12. else:
  13. creat_db('system')
  14. db = load_workbook("data/system.xlsx")
  15. permission_tb_col = ['database char[50] pk unique','select char','insert char','delete char','update char']
  16. creat_table('permission', db, 'system',permission_tb_col)

目录结构

3.4 用户验证及登录模块

登陆处理

通过raw_input()函数获取用户输入的用户名和密码,交给check_login()函数来验证是否正确。如果check通过,输出欢迎界面并将全局变量user赋值。

  1. def login():
  2. global user
  3. print "Please Login:"
  4. username = raw_input("username: ")
  5. password = raw_input("password: ")
  6. if check_login(username,password):
  7. print "Login Success!Welcome {}! 😊".format(username)
  8. user = username
  9. else:
  10. print "user not exist or password is wrong!😣 Try again."
  11. login()

check_login函数通过输入的username查询数据库中对应的密码,将查询结果和用户输入的值的md5加密值进行比较,如果一致,认为成功登陆。
如果密码不正确或无此用户,都输出error并要求用户再一次输入

  1. def check_login(username,password):
  2. db = load_workbook("data/system.xlsx")
  3. #right_pswd = select(password,user,{'username':username})
  4. table = db['user']
  5. col_list = list(iter_cols(table))
  6. try:
  7. pos = col_list[0].index(username)
  8. except:
  9. return False
  10. right_pswd = col_list[1][pos]
  11. if hashlib.md5(password).hexdigest() == right_pswd:
  12. return True
  13. else:
  14. return False

登陆效果

3.5 创建表模块

语句

  1. create table tbname (id int PK null,user char[10] )

在创建时将约束写入table_information表中。

  1. def creat_table(table_name,current_database,current_dbname,columns_list):
  2. # create table
  3. if table_name not in current_database.sheetnames:
  4. table = current_database.create_sheet(table_name)
  5. else:
  6. print (u"数据表已存在,请重新输入.")
  7. return
  8. if current_database.worksheets[0].title == 'Sheet':
  9. del current_database['Sheet']
  10. #表创建完成,开始创建列
  11. length = len(columns_list)
  12. #print length
  13. tbinfo = load_workbook("data/table_information.xlsx")
  14. tbinfo_tb = tbinfo[current_dbname]
  15. tbinfo_rows = tbinfo_tb.max_row
  16. column_names = []
  17. for i in range(length): #将字段的属性写到table_information库中
  18. column = columns_list[i].split(' ')
  19. tbinfo_tb.cell(row=tbinfo_rows+1+i,column=1).value = table_name
  20. tbinfo_tb.cell(row=tbinfo_rows+1+i, column=2).value = column[0]
  21. tbinfo_tb.cell(row=tbinfo_rows+1+i, column=3).value = column[1]
  22. for key in column[2:]:
  23. if key == 'null':
  24. tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=4).value = '1'
  25. elif key == 'not_null':
  26. tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=4).value = '0'
  27. elif key == 'unique':
  28. tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=5).value = '1'
  29. elif key == 'pk':
  30. tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=6).value = '1'
  31. elif key == 'fk':
  32. tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=7).value = '1'
  33. column_names.append(column[0])
  34. for j in range(1, 8):
  35. if tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=j).value is None:
  36. tbinfo_tb.cell(row=tbinfo_rows + 1 + i, column=j).value = 'NULL'
  37. tbinfo.save("data/table_information.xlsx")
  38. for i in range(length):
  39. table.cell(row=1,column=i+1).value = column_names[i] #表第一行是列名
  40. current_dbname = db_path + current_dbname + '.xlsx'
  41. current_database.save(current_dbname)
  42. print (u"数据表创建完成。")

table_information表(测试数据)

3.6 创建视图模块

语句

  1. creat view view_name as select xx from xx

解析查询语句,将结果存在view库中,每一个视图已视图名作为表名写入view库中,在view库中设置一个sql表原来存储视图和对应的sql语句。

  1. def view(viewname,sql):
  2. """
  3. file = view_path + viewname
  4. view = query(sql,'view')
  5. f = open(file, "w")
  6. f.write(str(view))
  7. f.close()
  8. print "Success"
  9. """
  10. db = load_workbook("data/view.xlsx")
  11. if viewname not in db.sheetnames:
  12. table = db.create_sheet(viewname)
  13. else:
  14. print ("view is exist.")
  15. return
  16. if db.worksheets[0].title == 'Sheet':
  17. del db['Sheet']
  18. sql_table = db['sql']
  19. maxrow = sql_table.max_row #在sql表中存view名和对应的sql语句
  20. sql_table.cell(row=maxrow + 1, column = 1).value = viewname
  21. sql_table.cell(row=maxrow + 1, column = 2).value = sql
  22. table = db[viewname]
  23. views = query(sql, 'view')
  24. for i in range(len(views)):
  25. for j in range(len(views[i])):
  26. table.cell(row=i+1, column=j+1).value = views[i][j]
  27. db.save("data/view.xlsx")

3.7 插入模块

支持单组和多组的插入,单组的插入处理就是将sql语句中的要插入的数据处理成字典,再交给insert函数处理。

多组的话将每组字典加入数组再进行处理。在插入数据前通过check_Constraint函数进行约束检查,注释部分是用来处理嵌套语句的,逻辑是通过正则取出其中的子查询语句,带上tag参数交给query函数处理,将结果以数组形式返回。因为对表的操作也是先转化成数组,这样直接处理数组就ok。

  1. def insert(table_name, current_database, current_dbname, columns_list):
  2. if not check_Constraint(columns_list,table_name): #columns应为[dict]
  3. print "Constraint Error"
  4. return False
  5. table = current_database[table_name]
  6. for columns in columns_list:
  7. table_rows = table.max_row
  8. table_columns = table.max_column
  9. length = len(columns)
  10. # print length
  11. for i in range(length):
  12. column = re.search('\((.*?)\)', columns[i], re.S).group(1)
  13. column_list = column.split(',')
  14. chk_len = len(column_list)
  15. if chk_len != table_columns:
  16. print ('插入失败,请检查输入的数据数量是否与列数量对应。')
  17. return
  18. else:
  19. for j in range(chk_len):
  20. table.cell(row=table_rows + i + 1, column=j + 1).value = column_list[j]
  21. current_dbname = db_path + current_dbname + '.xlsx'
  22. current_database.save(current_dbname)
  23. print ("数据插入完成。")
  24. def check_Constraint(columns,tablename): #columns={'a':'xx'}
  25. db = load_workbook("system/table_information.xlsx")
  26. table = db[using_dbname]
  27. rows = []
  28. rows_list = list(iter_rows(table)) #所有行
  29. cols_list = list(iter_cols(table))
  30. for col in columns:
  31. value = columns[col]
  32. for i in range(len(cols_list[0])): #table对应的行
  33. if cols_list[0][i] == tablename:
  34. rows.append(i)
  35. for line in rows:
  36. if rows_list[line][1] == col:
  37. typee, is_null, unique, pk, fk = rows_list[line][2:]
  38. if is_null == '0':
  39. if value == '':
  40. return False
  41. if unique == '1':
  42. if not check_unique(tablename,col,value):
  43. return False
  44. if pk == '1':
  45. if not check_unique(tablename,col,value) or value == '':
  46. return False
  47. if '[' in typee:
  48. typee, maxlen = re.findall('(\w*)\[(\d*)\]',type) #int[10] => int,10
  49. else:
  50. maxlen = 1000
  51. if len(value) > maxlen:
  52. return False
  53. if typee == 'int':
  54. if type(value) != type(1):
  55. return False
  56. if typee == 'char':
  57. if type(value) != type('c'):
  58. return False

3.8 更新数据

支持单组和多组的更新,单组的更新处理就是将sql语句中的要插入的数据处理成字典,再交给insert函数处理。

多组的话将每组字典加入数组再进行处理。在更新数据前通过check_Constraint函数进行约束检查,其他功能和insert的逻辑一样。

语句

  1. UPDATE table_name SET column1=value1,column2=value2,... WHERE some_column=some_value;
  1. def update(table_name,current_database,current_dbname,columns_list,update_columns_list):
  2. if not check_Constraint(update_columns_list,table_name): #columns应为dict
  3. print "Constraint Error"
  4. return False
  5. table = current_database[table_name]
  6. table_rows = table.max_row # 行
  7. table_columns = table.max_column # 列
  8. length = len(columns_list)
  9. update_row_num = [x for x in range(2,table_rows+1)]
  10. columns_name = []
  11. for cell in list(table.rows)[0]:
  12. columns_name.append(cell.value)
  13. for key in columns_list:
  14. flag = 0
  15. for i in range(len(columns_name)): # 判断colmuns_list 是否有 not in colmus中的
  16. if columns_name[i] == key:
  17. flag = 1
  18. if flag == 0: # 输入的列名不存在
  19. print("Unknown column '{}' in 'where clause'".format(key))
  20. return
  21. for key in columns_list:
  22. column_num = columns_name.index(key)
  23. for i in update_row_num[::-1]: #倒着来
  24. if table.cell(row=i, column=column_num+1).value != columns_list[key]:
  25. update_row_num.remove(i)
  26. if len(update_row_num) > 0:
  27. for i in update_row_num[::-1]:
  28. for j in range(1,table_columns+1):
  29. clu_name = table.cell(row=1, column=j).value
  30. table.cell(row=i, column=j).value = update_columns_list[clu_name]
  31. else:
  32. print("find 0 to update.")
  33. current_database.save(db_path + current_dbname + '.xlsx')
  34. print("更新完成,影响{}行".format(len(update_row_num)))

3.9 语句处理函数

  1. * ql语句的处理与解析。
  2. * 通过切词和正则来转化数据和调用各函数。
  3. * 通过split将语句按照空格切成数组,先根据首词判断操作再细分。
  4. * tag参数是为insertview等函数需要用到查询结果但不输出是的标识,如果带着该参数调用select函数,不会打印结果而是将结果以数组返回。
  5. * select操作的谓词通过predicatesymbol参数来标识,带着这两个参数调用select函数,具体参见select模块
  6. * 如果都没匹配最后会输出错误
  7. def query(sql,tag=''):
  8. sql_word = sql.split(" ")
  9. if len(sql_word) < 2:
  10. print "[!] Wrong query!"
  11. return
  12. operate = sql_word[0].lower()
  13. if operate == 'use':
  14. if sql_word[1] == 'database':
  15. try:
  16. use_db(sql_word[2])
  17. except:
  18. print "[!]Error"
  19. else:
  20. print "[!]Syntax Error.\neg:>use database dbname"
  21. elif operate == 'create':
  22. if sql_word[1] == 'database':
  23. try:
  24. creat_db(sql_word[2])
  25. except:
  26. print "[!]Create Error"
  27. elif sql_word[1] == 'table':
  28. columns_list = re.findall('\((.*)\)', sql)[0].split(',')
  29. print columns_list, using_dbname
  30. try:
  31. creat_table(sql_word[2], using_db, using_dbname, columns_list)
  32. except:
  33. print "[!]Error"
  34. elif sql_word[1] == 'view': #creat view test1 as select * from user
  35. viewname = sql_word[2]
  36. sql = ' '.join(sql_word[4:])
  37. view(viewname,sql)
  38. elif sql_word[1] == 'index':
  39. return
  40. else:
  41. print "[!]Syntax Error."
  42. elif operate == 'select':
  43. pos = 0
  44. for i in range(len(sql_word)):
  45. if '(' in sql_word[i] and 'select' in sql_word[i]:
  46. pos = i
  47. if pos == 3:
  48. sql2 = sql_word[3][1:-1]
  49. query(sql2,tag='nesting')
  50. sql_word[3] = 'tmp'
  51. sql = ' '.join(sql_word)
  52. columns = sql_word[1]
  53. table_name = sql_word[3]
  54. if len(sql_word) > 4:
  55. #try:
  56. limit = sql_word[5].split()
  57. predicate = 'and'
  58. symbol = '='
  59. if ',' in sql_word[5]:
  60. limit = sql_word[5].split(',')
  61. predicate = 'and'
  62. elif '|' in sql_word[5]:
  63. limit = sql_word[5].split('|')
  64. predicate = 'or'
  65. elif '>' in sql_word[5]:
  66. #limit = sql_word[5].split()
  67. symbol = '>'
  68. elif '<' in sql_word[5]:
  69. #limit = sql_word[5].split()
  70. symbol = '<'
  71. elif len(sql_word) > 6:
  72. if sql_word[6] == 'in':
  73. limit = [sql_word[5] + '=' + sql_word[7]]
  74. predicate = 'in'
  75. if sql_word[6] == 'like':
  76. limit = [sql_word[5] + '=' + sql_word[7]]
  77. predicate = 'like'
  78. #except:
  79. #limit = [].append(sql_word[5])
  80. #print limit
  81. for i in range(len(limit)):
  82. limit[i] = limit[i].split(symbol)
  83. limit = dict(limit)
  84. return select(columns, table_name, limit, predicate=predicate, symbol=symbol, tag=tag)
  85. else: #没where的情况
  86. return select(columns, table_name, tag=tag)
  87. elif operate == 'grant':
  88. set_permission(sql_word[5], sql_word[3], sql_word[1])
  89. elif operate == 'revoke':
  90. del_permission(sql_word[5], sql_word[3], sql_word[1])
  91. elif operate == 'insert': #INSERT INTO table_name col1=val1,col2=val2&col3=val3,col4=val4
  92. table_name = sql_word[2]
  93. columns_list = []
  94. if '&' in sql:
  95. cols = sql_word[3].split('&') #[{xx},{xx}] 多组
  96. for p in range(len(cols)):
  97. col = cols[p]
  98. c = col.split(',')
  99. for i in range(len(c)):
  100. c[i] = c[i].split('=')
  101. cols[p] = dict(c)
  102. columns_list = cols
  103. else:
  104. cols = sql_word[3].split(',')
  105. for i in range(len(cols)):
  106. cols[i] = cols[i].split('=')
  107. columns_list.append(dict(cols))
  108. insert(table_name,using_db,using_dbname,columns_list)
  109. elif operate == 'update':
  110. return
  111. elif operate == 'help':
  112. if sql_word[1] == 'database':
  113. show_db()
  114. if sql_word[1] == 'table':
  115. usdbnm = using_dbname
  116. use_db('table_information')
  117. tbname = sql_word[2]
  118. select('*',usdbnm,{'table':tbname})
  119. if sql_word[1] == 'view':
  120. view_name = sql_word[2]
  121. use_db('view')
  122. select('sql','sql',{'viewnamw':view_name})
  123. if sql_word[1] == 'index':
  124. print "All Index:"
  125. indexs = os.listdir('data/index/') # 第二种方法,从保存数据库信息的库中查询
  126. for index in indexs:
  127. if '.DS' not in index:
  128. print "[*] " + index[:-5]
  129. else:
  130. print "[!]Syntax Error."

3.10 删除模块

会先check用户权限再进行操作。其他逻辑类似insert函数。

  1. def delect(table_name,current_database,current_dbname,columns_list): #columns_list={'name1':'value1','name2':'value2'}
  2. table = current_database[table_name]
  3. table_rows = table.max_row #行
  4. table_columns = table.max_column #列
  5. length = len(columns_list)
  6. delect_row_num = [x for x in range(2,table_rows+1)]
  7. columns_name=[]
  8. for cell in list(table.rows)[0]:
  9. columns_name.append(cell.value)
  10. for key in columns_list:
  11. flag = 0
  12. for i in range(len(columns_name)): #判断colmuns_list 是否有 not in colmus中的
  13. if columns_name[i] == key:
  14. flag = 1
  15. if flag == 0: #输入的列名不存在
  16. print("Unknown column '{}' in 'where clause'".format(key))
  17. return
  18. for key in columns_list:
  19. column_num = columns_name.index(key)
  20. for i in delect_row_num[::-1]: #倒着来
  21. if table.cell(row=i, column=column_num+1).value != columns_list[key]:
  22. delect_row_num.remove(i)
  23. if len(delect_row_num) > 0:
  24. for i in delect_row_num[::-1]:
  25. #print i,table_rows
  26. table.delete_rows(int(i))
  27. else:
  28. print("find 0 to delect.")
  29. current_database.save(db_path + current_dbname + '.xlsx')
  30. print("删除完成,影响{}行".format(len(delect_row_num)))

3.11 权限检查模块

在用户对某对象进行操作之前确定该用户有没有操作权限。用户对对象的操作权限存储在system库中的permission表,在用户进行相关操作时先去查询该用户有没有该操作的权限。

  1. def check_permission(user,database,action):
  2. table = load_workbook("data/system.xlsx")['permission']
  3. db_list = list(iter_cols(table))[0][1:]
  4. row = db_list.index(database)+2
  5. action_list = list(iter_rows(table))[0]
  6. col = action_list.index(action)+1
  7. allow_user = table.cell(row=row, column=col).value.split(',')
  8. if user in allow_user:
  9. return True
  10. else:
  11. print "Permission not allowed"
  12. return False

3.12 权限的赋予和回收模块

使用grant和revoke关键字赋予权限和回收权限,实质是对permision表中数据进行更新。

grant语句

  1. grant select on test_tb for testuser

这里第一列是具有权限的对象,不只是数据库,可以是table,view,index,在函数对应函数处理时将数据库名变量换成其他对象就OK。

  1. def set_permission(user,database,action):
  2. db = load_workbook("data/system.xlsx")
  3. table = db['permission']
  4. db_list = list(iter_cols(table))[0][1:]
  5. row = db_list.index(database) + 2
  6. action_list = list(iter_rows(table))[0]
  7. col = action_list.index(action) + 1
  8. allow_user = table.cell(row=row, column=col).value.split(',')
  9. if user in allow_user:
  10. print "user have this permission"
  11. else:
  12. table.cell(row=row, column=col).value = table.cell(row=row, column=col).value + ',' + user
  13. db.save("data/system.xlsx")

revoke语句

  1. revoke select on test_tb for testuser
  1. def del_permission(user,database,action):
  2. db = load_workbook("data/system.xlsx")
  3. table = db['permission']
  4. db_list = list(iter_cols(table))[0][1:]
  5. row = db_list.index(database) + 2
  6. action_list = list(iter_rows(table))[0]
  7. col = action_list.index(action) + 1
  8. allow_user = table.cell(row=row, column=col).value.split(',')
  9. if user in allow_user:
  10. if allow_user.index(user) == 0:
  11. table.cell(row=row, column=col).value = table.cell(row=row, column=col).value.replace(user, '')
  12. else:
  13. table.cell(row=row, column=col).value = table.cell(row=row, column=col).value.replace(',' + user, '')
  14. db.save("data/system.xlsx")
  15. else:
  16. print "user didn't have this permission"

3.13 约束检查模块

检查主码、外码、唯一性约束、非空约束等完整性约束。

单独的约束检查函数,在进行insert,update等操作时直接调用就OK。

  1. def check_Constraint(columns,tablename): #columns={'a':'xx'}
  2. db = load_workbook("system/table_information.xlsx")
  3. table = db[using_dbname]
  4. rows = []
  5. rows_list = list(iter_rows(table)) #所有行
  6. cols_list = list(iter_cols(table))
  7. for col in columns:
  8. value = columns[col]
  9. for i in range(len(cols_list[0])): #table对应的行
  10. if cols_list[0][i] == tablename:
  11. rows.append(i)
  12. for line in rows:
  13. if rows_list[line][1] == col:
  14. typee, is_null, unique, pk, fk = rows_list[line][2:]
  15. if is_null == '0':
  16. if value == '':
  17. return False
  18. if unique == '1':
  19. if not check_unique(tablename,col,value):
  20. return False
  21. if pk == '1':
  22. if not check_unique(tablename,col,value) or value == '':
  23. return False
  24. if '[' in typee:
  25. typee, maxlen = re.findall('(\w*)\[(\d*)\]',type) #int[10] => int,10
  26. else:
  27. maxlen = 1000
  28. if len(value) > maxlen:
  29. return False
  30. if typee == 'int':
  31. if type(value) != type(1):
  32. return False
  33. if typee == 'char':
  34. if type(value) != type('c'):
  35. return False
  36. def check_unique(tablename,column,value):
  37. table = using_db[tablename]
  38. col_pos = list(iter_rows(table))[0].index(column) #第几列
  39. cols_list = list(iter_cols(table))[col_pos][1:]
  40. if cols_list.count(value) > 1: #该列中该值数量
  41. return False
  42. else:
  43. return True

3.14 查询模块

  1. * 支持嵌套查询,and,or,in,like谓词。
  2. * 通过eval函数处理比较运算和数学运算
  3. * 通过select等关键字个数判断子查询,子查询通过tag参数调用query函数获得查询结果数组
  4. * query函数中语句的处理很关键,通过一些参数告诉select函数要返回什么样的值。提前讲语句处理成dictlist讲谓词等转换成符号方便select函数的处理。
  5. * 查询时转换成数组方便操作
  6. elif operate == 'select':
  7. pos = 0
  8. for i in range(len(sql_word)):
  9. if '(' in sql_word[i] and 'select' in sql_word[i]:
  10. pos = i
  11. if pos == 3:
  12. sql2 = sql_word[3][1:-1]
  13. query(sql2,tag='nesting')
  14. sql_word[3] = 'tmp'
  15. sql = ' '.join(sql_word)
  16. columns = sql_word[1]
  17. table_name = sql_word[3]
  18. if len(sql_word) > 4:
  19. #try:
  20. limit = sql_word[5].split()
  21. predicate = 'and'
  22. symbol = '='
  23. if ',' in sql_word[5]:
  24. limit = sql_word[5].split(',')
  25. predicate = 'and'
  26. elif '|' in sql_word[5]:
  27. limit = sql_word[5].split('|')
  28. predicate = 'or'
  29. elif '>' in sql_word[5]:
  30. #limit = sql_word[5].split()
  31. symbol = '>'
  32. elif '<' in sql_word[5]:
  33. #limit = sql_word[5].split()
  34. symbol = '<'
  35. elif len(sql_word) > 6:
  36. if sql_word[6] == 'in':
  37. limit = [sql_word[5] + '=' + sql_word[7]]
  38. predicate = 'in'
  39. if sql_word[6] == 'like':
  40. limit = [sql_word[5] + '=' + sql_word[7]]
  41. predicate = 'like'
  42. #except:
  43. #limit = [].append(sql_word[5])
  44. #print limit
  45. for i in range(len(limit)):
  46. limit[i] = limit[i].split(symbol)
  47. limit = dict(limit)
  48. return select(columns, table_name, limit, predicate=predicate, symbol=symbol, tag=tag)
  49. else: #没where的情况
  50. return select(columns, table_name, tag=tag)

select函数,不仅仅提供查询的功能,还用来处理view的一部分数据,通过tag参数来标识数据如何处理。

查询函数

  1. def select(columns,table_name,limit={},predicate='and', symbol='=', tag=''): #{'c':'x','d':'x'}
  2. if using_dbname == '':
  3. print "please choose databse!"
  4. return
  5. table = using_db[table_name]
  6. #print columns
  7. if columns == '*' and len(limit) == 0:
  8. columns_name = list(iter_rows(table))[0]
  9. table_print = PrettyTable(columns_name)
  10. for i in range(1,len(list(iter_rows(table)))):
  11. table_print.add_row(list(iter_rows(table))[i])
  12. table_print.reversesort = True
  13. if tag == 'view':
  14. print table_print
  15. return list(iter_rows(table)) #view
  16. else:
  17. print(table_print)
  18. else:
  19. sel_cols = columns.split(',') #*的情况
  20. rows_list = list(iter_rows(table)) #所有的行
  21. cols = rows_list[0]
  22. col_pos = []
  23. limit_pos = []
  24. print_row = []
  25. limit_cols = list(limit)
  26. symbol = '==' if symbol == '=' else symbol
  27. if columns[0] != '*':
  28. for i in range(len(sel_cols)):
  29. col_pos.append(cols.index(sel_cols[i])) #要查的列的列号
  30. else:
  31. sel_cols = list(iter_rows(table))[0]
  32. col_pos = range(len(cols))
  33. for i in range(len(limit)):
  34. limit_pos.append(cols.index(limit_cols[i])) #where的列
  35. for i in range(1, len(rows_list)):
  36. match = 0
  37. if predicate == 'in':
  38. match_list = limit[limit_cols[0]]
  39. for j in len(match_list):
  40. if rows_list[i][limit_pos[0]] == match_list[j]:
  41. print_row.append(i)
  42. if predicate == 'like':
  43. like_word = re.findall('(.*)\%',limit[limit_cols[0]])
  44. if like_word in rows_list[i][limit_pos[0]]:
  45. print_row.append(i)
  46. else:
  47. for j in range(len(limit_pos)): #通过eval实现比较运算
  48. if eval("'" + rows_list[i][limit_pos[j]] + "'" + symbol + "'" + limit[limit_cols[j]] + "'"):
  49. match += 1
  50. if predicate == None:
  51. print_row.append(i)
  52. if predicate == 'and' and match == len(limit_pos): #and时要全部匹配
  53. print_row.append(i) #符合条件的行号
  54. if predicate == 'or' and match > 0: #or时至少一个匹配
  55. print_row.append(i)
  56. table_print = PrettyTable(sel_cols)
  57. for i in range(len(print_row)):
  58. add_rows = []
  59. for x in col_pos:
  60. add_rows.append(rows_list[print_row[i]][x])
  61. table_print.add_row(add_rows)
  62. table_print.reversesort = True
  63. if tag == 'view':
  64. return table_print
  65. elif tag == 'nesting':
  66. tmpdb = using_db
  67. table = tmpdb['tmp']
  68. for i in range(len(sel_cols)):
  69. table.cell(row=0,column=i+1).value = sel_cols[i]
  70. for i in range(len(print_row)):
  71. add_rows = []
  72. for x in col_pos:
  73. add_rows.append(rows_list[print_row[i]][x])
  74. for j in range(len(add_rows)):
  75. table.cell(row=i+2,column=j+1).value = add_rows[j]
  76. tmpdb.save("data/" + using_dbname + ".xlsx")
  77. else:
  78. #table_print.reversesort = True
  79. print(table_print)

3.15 索引模块

B+树的索引,将数据先处理成数组,每一组数据包含数据值和数据在表中的位置(起到指针的作用)。B+树的处理单独写了一个类,方便调用处理。

  1. def index(current_database,table_name,column_name):
  2. table = current_database[table_name]
  3. table_columns = table.max_column
  4. table_rows = table.max_row
  5. column_num = 0
  6. column_value = []
  7. column_position = []
  8. for i in range(1,table_columns+1):
  9. if table.cell(row=1,column=i).value == column_name:
  10. column_num = i
  11. if column_num == 0:
  12. print "no this column"
  13. return
  14. else:
  15. for i in range(2,table_rows+1):
  16. column_value.append(str(table.cell(row=i,column=column_num).value))
  17. column_position.append('<{},{}>'.format(i,column_num))
  18. column_value.sort()
  19. for i in range(len(column_value)):
  20. tmp = [column_value[i],column_position[i]] #like [1,aaa|<2,1>]
  21. column_value[i] = tuple(tmp) #like [(1,aaa|<2,1>)]
  22. #print column_value[0]
  23. bt = test_BPTree(column_value)
  24. indexname = table_name + '|' +column_name
  25. save_index(str(bt), indexname)
  26. def save_index(bt,indexname):
  27. line = re.findall(r'\[.*?\]', bt)
  28. for i in range(len(line)):
  29. line[i] = line[i][2:-2].replace('|', '')
  30. file = open('data/index/' + indexname,'w')
  31. for i in range(len(line)):
  32. file.writelines(line[i] + '\n')
  33. file.close()

索引的存储,存储生成的b+树,每一行是B+树的一层。存储数据类型是字典。

  1. def select_index(a)
  2. Pos = BPTree_search(a)
  3. def update_index(table_name, column_name)
  4. index(using_db, table_name, column_name)

B+树的部分代码

  1. class BPTree(object):
  2. def search(self, node, key):
  3. i = bisect.bisect_left(node.keys, key)
  4. if i < len(node.keys) and key == node.keys[i]:
  5. if node.is_leaf():
  6. return (node, i)
  7. else:
  8. return self.search(node.children[i + 1], key)
  9. if node.is_leaf():
  10. return (None, None)
  11. else:
  12. # self.disk_read(node.children[i])
  13. return self.search(node.children[i], key)
  14. def insert(self, key, value):
  15. if len(self.root.keys) == self._maxkeys:
  16. oldroot = self.root
  17. self.root = BPNode()
  18. self.root.children.append(oldroot)
  19. self.split_child(self.root, 0, oldroot)
  20. self.insert_nonfull(self.root, key, value)
  21. else:
  22. self.insert_nonfull(self.root, key, value)
  23. def levels(self):
  24. leveldict = {}
  25. for level, node in self.bft(self.root):
  26. leveldict.setdefault(level, []).append(node)
  27. return leveldict
  28. def pprint(self, width=80):
  29. leveldict = self.levels()
  30. keys = leveldict.keys()
  31. for k in keys:
  32. print ' '.join(str(e) for e in leveldict[k]).center(width)
  33. return leveldict
  34. def min(self):
  35. node = self.root
  36. while node.children:
  37. node = node.children[0]
  38. return node.keys[0]
  39. def max(self):
  40. node = self.root
  41. while node.children:
  42. node = node.children[-1]
  43. return node.keys[-1]
  44. def bft(self, node, level=1):
  45. """Breadth first traversal."""
  46. q = Queue.Queue()
  47. level = level
  48. q.put((level, node))
  49. while not q.empty():
  50. level, node = q.get()
  51. yield (level, node)
  52. for e in node.children:
  53. q.put((level + 1, e))
  54. def ceiling(self, node, key):
  55. i = bisect.bisect(node.keys, key)
  56. if i < len(node.keys) and key == node.keys[i]:
  57. if node.is_leaf():
  58. return key
  59. else:
  60. return self.ceiling(node.children[i + 1], key)
  61. if node.is_leaf():
  62. if i == len(node.keys):
  63. kp = node.keys[-1]
  64. if node.keys[-1] < key:
  65. if len(node.next.keys) > 0:
  66. return node.next.keys[0]
  67. else:
  68. return kp
  69. return node.keys[i]
  70. else:
  71. return self.ceiling(node.children[i], key)
  72. def split_child(self, x, i, y):
  73. z = BPNode()
  74. z.keys = y.keys[self.degree:]
  75. z.values = y.values[self.degree:]
  76. if not y.is_leaf():
  77. z.children = y.children[self.degree:]
  78. y.next = None
  79. else:
  80. z.keys.insert(0, y.keys[self.degree - 1])
  81. z.values.insert(0, y.values[self.degree - 1])
  82. z.next = y.next
  83. y.next = z
  84. x.children.insert(i + 1, z)
  85. x.keys.insert(i, y.keys[self.degree - 1])
  86. # x.values.insert(i, y.values[self.degree-1])
  87. y.keys = y.keys[:self.degree - 1]
  88. y.values = y.values[:self.degree - 1]
  89. y.children = y.children[:self.degree]

3.16 help xxx模块

输入“help database”命令,输出所有数据表、视图和索引的信息,同时显示其对象类型;输入“help table 表名”命令,输出数据表中所有属性的详细信息;

输入“help view 视图名”命令,输出视图的定义语句;

输入“help index 索引名”命令,输出索引的详细信息;

  1. elif operate == 'help':
  2. if sql_word[1] == 'database':
  3. show_db()
  4. if sql_word[1] == 'table':
  5. usdbnm = using_dbname
  6. use_db('table_information')
  7. tbname = sql_word[2]
  8. select('*',usdbnm,{'table':tbname})
  9. if sql_word[1] == 'view':
  10. view_name = sql_word[2]
  11. use_db('view')
  12. select('sql','sql',{'viewnamw':view_name})
  13. if sql_word[1] == 'index':
  14. print "All Index:"
  15. indexs = os.listdir('data/index/') # 第二种方法,从保存数据库信息的库中查询
  16. for index in indexs:
  17. if '.DS' not in index:
  18. print "[*] " + index[:-5]

3.17 数据库选择和创建模块

定义来一些全局变量:

  1. using_dbname = ''
  2. using_db = Workbook()
  3. db_path = 'data/'
  4. #view_path = 'view/'
  5. user = ''
  1. def use_db(dbname):
  2. global using_dbname
  3. global using_db
  4. if check_permission(user, dbname, 'use'):
  5. using_dbname = dbname
  6. using_db = load_workbook(db_path+dbname+'.xlsx')
  7. print("Database changed.")
  8. def creat_db(dbname):
  9. dbpath = 'data/' + dbname + '.xlsx'
  10. database = Workbook()
  11. database.save(dbpath)
  12. create_tb_in_tbinfo(dbname)
  13. print(u"数据库创建操作执行成功")

4.用户使用说明

运行程序

  1. > python L-DBMS.py

进入用户登陆界面,输入用户名和密码登录

选择数据库

查询

Help xxx

Exit or quit退出

5.课程设计感想

对sql语句的处理可以说是最麻烦的一部分了,在处理sql语句方面感觉做的还不够优雅,而且功能没做完善。在写代码过程中也是随着遇到的问题学习到了许多。而且认识到了再开始要规划好代码逻辑和数据结构,不然写到一半再改很麻烦。

功能并不完善,奈何时间有限,回去看自己的代码感觉并不优美,一些功能的实现方式甚至用很麻烦的方法解决,还需要努力啊。

上传的附件 cloud_download 基于Python的数据库实现.7z ( 3.08mb, 12次下载 )
error_outline 下载需要11点积分

发送私信

人生百年,转眼成空。幸福靠的不是缘分,而是珍惜

7
文章数
8
评论数
最近文章
eject