实现SQL基本语法

  1 #!/usr/bin/env python
  2 # writen by Lisp at 2017/5/15
  3 
  4 """
  5 对员工信息文件,实现增删改查操作
  6 
  7 1. 可进行模糊查询,语法至少支持下面3种:
  8 
  9     a. select user_name,user_age from db.emp where user_age > 22
 10     b. select * from db.emp where user_dept = "IT"
 11     c. select * from db.emp where user_date like "2013"
 12     d. select * from db.emp limit 3
 13     e. select * from db.emp where user_id > 12 and user_id < 15 or user_name like 李
 14     f. select * from db.emp where not user_id > 3
 15 
 16     查到的信息,打印后,最后面还要显示查到的条数
 17 
 18 2. 可创建新员工纪录,以phone做唯一键,staff_id需自增
 19 
 20     a. insert into db.emp values 张光棍,31,13111111111,CEO,2011-11-11
 21 
 22 3. 可删除指定员工信息纪录,输入员工id,即可删除
 23 
 24     a. delete from db.emp where id >25
 25 
 26 4. 可修改员工信息,语法如下:
 27 
 28     a. UPDATE db.emp SET dept="Market" WHERE user_dept = "IT"
 29 
 30 注意:以上需求,要充分使用函数,请尽你的最大限度来减少重复代码!
 31 
 32 
 33 思路:
 34     1. 先从文件读出所有数据到列表,每行数据按字典的格式存储
 35     2. 更新,插入,删除操作均操作列表,操作完以后,执行文件写入操作
 36 
 37 """
 38 # 记录 input 输入的历史命令, 可以使用上下左右箭头; 不同环境可能不一样.
 39 import readline
 40 import os
 41 
 42 
 43 def get_all_info(file_name):
 44     """
 45         通过db file 得到所有用户信息; 此处无需判断异常, 因为在调用此函数之前, 均已做好判断
 46     :param file_name: str db_filename
 47     :return:  list 返回所有用户信息的列表, 用户信息以dict形式保存
 48     """
 49     user_info_list = []
 50     with open(file_name, 'r', encoding='utf-8') as r_f:
 51         for each_line in r_f.readlines():
 52             each_list = each_line.strip().split(',')
 53             user_info_list.append(dict(zip(tk_temp, each_list)))
 54     return user_info_list
 55 
 56 
 57 def write_to_file(db_name, write_list):
 58     """
 59     将内容写入文件; 此处同样无需判断异常
 60     :param db_name get_filename()函数可得到
 61     :param write_list  要往文件中写入的数据 [{},{},{}...] 形式
 62     :return: 无返回,只写文件
 63     """
 64     # 将字段名字组成字符串   先列表生成,再join
 65     temp_str = ','.join(['{' + x + '}' for x in tk_temp])
 66     with open(db_name, 'w+', encoding='utf-8') as w_f:
 67         for each_dic in write_list:  # 遍历要写的数据列表
 68             if write_list[0] == each_dic:  # 判断首行
 69                 temp_data = '{0}'.format(temp_str).format_map(each_dic)
 70             else:
 71                 temp_data = '
{0}'.format(temp_str).format_map(each_dic)
 72             w_f.writelines(temp_data)
 73 
 74 
 75 def get_where_clause(para_str, para_item):
 76     """
 77         处理where后面的字符串
 78             id -> 每行记录的ID, user_name -> 每行记录的user_name ...
 79             and 和 or 和 not 可以不做处理
 80             like -> like前后值翻转, like变为in
 81             limit 最后处理
 82     :param para_item: dict 用户详细信息 -- 文件中的每行记录
 83     :param para_str: content -- where后的字符串
 84     :return: union
 85     """
 86     # 遍历字段名,把where后面的字段名全部替换为字段值
 87     # temp_str = para_str
 88     for ITEM in tk_temp:
 89         # 如: where user_id > 10 --> where 1 > 10
 90         para_str = para_str.replace(ITEM, para_item[ITEM].lower())
 91 
 92     if '"' in para_str:
 93         para_str = para_str.replace('"', '')
 94     if "'" in para_str:
 95         para_str = para_str.replace("'", '')
 96     # 处理like关键字
 97     temp_list = para_str.split()  # 定义临时列表, 用空格分开替换后的where子句
 98     if 'like' in para_str:  # 判断是否有like语句
 99         if ' like ' in para_str:  # 如果有like, 则判断是否被空格分开;因为上面的列表是按照空格分割的list
100             for temp in temp_list:  # 遍历temp_list
101                 if temp == 'like':  # 如果有like的列表元素
102                     like_flag = temp_list.index(temp)  # 找出like的下标
103                     temp_list[like_flag] = 'in'  # 把like替换为in
104                     if temp_list[like_flag - 1] != 'not':  # 找出like的下标后,判断like的前面是否有not关键字
105                         # 没有,则前后两个元素互换; 操作空间是temp_list
106                         temp_list[like_flag - 1], temp_list[like_flag + 1] = "'" + str(
107                             temp_list[like_flag + 1]) + "'", "'" + str(temp_list[like_flag - 1]) + "'"
108                     else:
109                         # 有,则not前一个元素与like后一个元素互换
110                         temp_list[like_flag - 2], temp_list[like_flag + 1] = "'" + str(
111                             temp_list[like_flag + 1]) + "'", "'" + str(temp_list[like_flag - 2]) + "'"
112         else:
113             return 901, 'like关键字附近'  # 返回语法错误和提示
114     para_str = ' '.join(temp_list)  # 再用空格将处理过like的列表组成字符串
115     # 处理 类似 user_dept=it 有一个等号的情况; 需要把一个等号变为两个等号 user_dept==it 才可以被eval
116     for temp in temp_list:  # 遍历临时列表
117         if '=' in temp and '==' not in temp:  # 如果列表元素中有=, 而且还没被替换; 匹配['user_id=2', '=', 'user_dept', '=it']
118             if '<=' not in temp and '>=' not in temp:  # 如果是 >= , <= 则不处理
119                 if temp != '=':  # 匹配user_id=2, user_id=, =2的情况
120                     temp_para = temp.split('=')  # 则,将item用=分割, 获取=两边的内容
121                     if temp.startswith('='):  # =2
122                         bf_temp_index = temp_list.index(temp) - 1
123                         bf_temp = temp_list[bf_temp_index]
124                         para_str = para_str.replace(temp, '=="' + temp_para[-1] + '"')
125                         para_str = para_str.replace(bf_temp + ' ', '"{}"'.format(bf_temp))
126                     elif temp.endswith('='):  # user_id=
127                         bh_temp_index = temp_list.index(temp) + 1
128                         bh_temp = temp_list[bh_temp_index]
129                         para_str = para_str.replace(temp, '"' + temp_para[0] + '"==')
130                         para_str = para_str.replace(bh_temp + ' ', '"{}"'.format(bh_temp))
131                     else:  # user_id=2
132                         # 替换原字符串中的temp, 替换为 'user_dept'=='IT'
133                         para_str = para_str.replace(temp, "'" + temp_para[0] + "'=='" + temp_para[1] + "'")
134                 else:  # 匹配'='的情况,则说明=两边是空格, 那直接替换原字符串 = 为 ==
135                     temp_index = temp_list.index(temp)
136                     old_str = temp_list[temp_index - 1] + ' = ' + temp_list[temp_index + 1]
137                     new_str = '"{}" == "{}"'.format(temp_list[temp_index - 1], temp_list[temp_index + 1])
138                     para_str = para_str.replace(old_str, new_str)
139     return para_str
140 
141 
142 def get_ok_dic_id(para_str, user_info_list):
143     """
144         遍历整个用户信息表, 返回符合where后面条件的dic在user_info_list中下标的列表, 使用与delete,select,update
145     :param user_info_list: 用户信息列表
146     :param para_str 传给get_where_clause函数, 返回能被eval的字符串
147     :return: union 
148     """
149     res_list = []
150     if not para_str:
151         return list(range(len(user_info_list)))
152     for dic_item in user_info_list:  # 遍历user_info_list,
153         # 调用函数,处理where子句,返回可以eval的字符串
154         eval_str = get_where_clause(para_str, dic_item)
155         if eval_str[0] == 901:
156             return eval_str
157         try:
158             if eval(eval_str):  # 判断eval的字符串是否成立; 成立则追加成立的字典下标到返回列表
159                 res_list.append(user_info_list.index(dic_item))
160         except (NameError, SyntaxError):
161             return 901, 'Where子句'
162     return res_list
163 
164 
165 def get_filename(para_str):
166     """
167         这个函数用来解决 sql 语句当中 db.emp 对应文件的问题
168     :param para_str: 小写字符串 db.emp
169     :return: union
170     """
171     # 获取当前文件的绝对路径(不包含文件名)
172     real_path = os.path.dirname(os.path.realpath(__file__))
173     # 替换参数中字符串的.为/
174     db_path = para_str.replace('.', '/')
175     # 字符串拼接,组成数据库文件的绝对路径
176     file_path = real_path + '/' + db_path
177     # 判断文件是否存在
178     if os.path.isfile(file_path):
179         return 950, db_path  # 返回数据文件的相对路径
180     else:
181         return 951, '{}不存在'.format(db_path)  # 数据文件不存在
182 
183 
184 def get_res_list(content, user_info_list, cl_name=''):
185     """
186     select函数专用; 简化 select * from ... 和 select column1, column2 from ... 的情况
187     :param cl_name:  查询字段,默认为空代表*
188     :param user_info_list: 用户信息列表
189     :param content: where子句
190     :return: res_list
191     """
192     t_flag = False
193     res_list = []
194     cont_list = []
195     # 获取字符串形式的字段名{user_name},{user_age}...
196     # 因查询的字段,显示不同
197     if not cl_name:
198         temp_str = ','.join(['{' + x + '}' for x in tk_temp])
199     else:
200         new_cl_list = cl_name.split(',')
201         for each_cl in new_cl_list:
202             if each_cl not in tk_temp:
203                 return 921, '字段名 {} 不存在'.format(each_cl)
204         temp_str = ','.join(['{' + x + '}' for x in new_cl_list])
205 
206     if content == '':
207         for item in user_info_list:
208             temp = '[{0}]'.format(temp_str).format_map(item)
209             res_list.append(temp)
210     # select * from db.emp [ where... | limit... ]
211     else:
212         # 如果有limit,把limit后面那个值取出来,content变为limit前的str;更改limit标志位
213         if 'limit' in content:
214             cont_list = content.split('limit')
215             if cont_list[0]:
216                 content = cont_list[0]
217             else:
218                 content = ''
219             t_flag = True
220         # 根据content 取出匹配到的dic的下标
221         se_id = get_ok_dic_id(content, user_info_list)
222         if se_id:
223             if se_id[0] == 901:
224                 return se_id
225             else:
226                 # 遍历下标,将内容先追加到一个列表
227                 for each_id in se_id:
228                     temp = '[{0}]'.format(temp_str).format_map(dict(user_info_list[each_id]))
229                     res_list.append(temp)
230         else:
231             res_list = se_id
232 
233     if t_flag:
234         return res_list[:int(cont_list[-1])]
235     else:
236         return res_list
237 
238 
239 def my_sql_select(para_str):
240     """
241     查询
242     :param: para_str: 接收用户输入的字符串 小写
243     :return: 
244     """
245     para_list = para_str.split()
246 
247     try:
248         # 找到from关键字的下标
249         from_id = para_list.index(ky_list[5])
250     except ValueError:
251         # 无关键字
252         return 901, '关键字{}缺失'.format(ky_list[5])
253 
254     # select 到 from 之间 判断长度
255     if len(para_list[1:from_id]) >= 1:
256         cl_name = ''.join(para_list[1:from_id])
257     else:
258         return 901, '{}与{}之间'.format(ky_list[0], ky_list[5])
259 
260     if len(para_list[from_id + 1:]) >= 2:
261         try:
262             # 查找where的下标
263             where_id = para_list.index(ky_list[6])
264             if len(para_list[from_id + 1:where_id]) != 1:
265                 return 901, '数据文件部分有语法错误'
266             if len(para_list[where_id + 1:]) > 0:
267                 content = para_str.split(ky_list[6])[-1].strip()
268             else:
269                 return 901, '关键字{}后缺少条件'.format(ky_list[6])
270 
271         except ValueError:
272             # 没有where
273             try:
274                 limit_id = para_list.index(ky_list[-2])
275                 if len(para_list[from_id + 1:limit_id]) != 1:
276                     return 901, '关键字{}位置不对'.format(ky_list[-2])
277                 if len(para_list[limit_id:]) == 2:
278                     content = ' '.join(para_list[limit_id:])
279                 else:
280                     return 901, '{} 后参数异常'.format(ky_list[-2])
281             except ValueError:
282                 return 901, '缺少 {} 或者 {} 关键字'.format(ky_list[-3], ky_list[-2])
283     elif len(para_list[from_id + 1:]) == 1:
284         content = ''
285     else:
286         return 901, '缺少数据文件'
287 
288     db_name = para_list[from_id + 1]
289     # 分割关键字之间的str结束, 得到:
290     #   select-->from = cl_name
291     #   from-->where = db_name
292     #   where--> = content(maybe include limit)
293 
294     # 通过get_filename函数将db.emp类型转文件路径db/emp
295     file_tup = get_filename(db_name)
296     file_name = file_tup[1]
297     # 将文件内容读取出来,保存到user_info_list列表
298     if file_tup[0] == 951:
299         return file_tup
300     user_info_list = get_all_info(file_name)
301 
302     # select * from ...
303     if cl_name == '*':
304         # select * from db.emp + content
305         # 这里这种情况直接调用函数, 因为就cl_name不一样,其他都基本一致.
306         # 把cl_name传进去,进行匹配判断.
307         res_list = get_res_list(content, user_info_list)
308         if res_list:
309             if res_list[0] == 901 or res_list[0] == 921:
310                 return res_list
311     else:
312         # select user_id ,name , xx from db.emp + content
313         res_list = get_res_list(content, user_info_list, cl_name)
314         if res_list:
315             if res_list[0] == 901 or res_list[0] == 921:
316                 return res_list
317 
318     for each_res in res_list:
319         print(each_res)
320     return 920, '总共{}条记录'.format(len(res_list))
321 
322 
323 def my_sql_insert(para_str):
324     """
325         往数据文件插入数据.
326     1. 可以直接将数据插入到文件; 
327     2. 先append到列表,然后再将列表覆盖写入文件的方式;
328     这里为了练习函数的使用,采用方法2。
329     :param para_str: 小写字符串 insert into [db_file] values xxx
330     :return:  
331     """
332     # 将接收的参数通过空格来分割,转为列表
333     para_list = para_str.split()
334 
335     try:
336         values_id = para_list.index(ky_list[4])  # 找到values关键字的位置;ky_list[4]=values
337     except ValueError:
338         return 901, '关键字{}缺失'.format(ky_list[4])  # 无values关键字
339 
340     if len(para_list[2:values_id]) != 1:  # 判断into之后到values关键字之间是否只有一个元素
341         return 901, '数据文件部分有语法错误'  # 否,则返回选择的数据库和表有误
342     else:
343         db_name = para_list[2]  # 是,数据文件暂定
344 
345     if len(para_list[values_id + 1:]) < 1:  # 判断values关键字之后是否再有无空格
346         return 901, '未发现要插入的数据'  # 如果有,插入的数据格式有误
347     else:
348         # 如果有values,则以values分割,取后面的字符串,以,分割得到一个列表
349         temp_list = para_str.split(ky_list[4])[1].split(',')
350         # 匹配列表长度,与用户信息dict长度是否一致
351         if len(temp_list) == 5:
352             for temp_item in temp_list:
353                 # 去掉空格
354                 temp_index = temp_list.index(temp_item)
355                 temp_list[temp_index] = temp_item.strip()
356         else:
357             return 921, '要插入的数据 格式有误'
358         content = ','.join(temp_list)  # 没有,则确定插入的数据
359 
360     # 通过上述判断,可得出要插入的内容content和要插入的文件名db_name
361     # 通过get_filename函数将db.emp类型转文件路径db/emp
362     file_tup = get_filename(db_name)
363     file_name = file_tup[1]
364     # 将文件内容读取出来,保存到user_info_list列表
365     if file_tup[0] == 951:
366         return file_tup
367     user_info_list = get_all_info(file_name)
368 
369     # 遍历user_info_list,判断手机号是否唯一
370     for item in user_info_list:
371         if content.split(',')[2] in item.values():
372             return 911, '手机号{}已经存在'.format(content.split(',')[2])  # 返回手机号已经存在
373 
374     # 上述判断通过,则这里表明输入的参数中,数据文件以及要插入的数据都是合法的
375     # 得到主键ID
376     insert_id = int(user_info_list[-1][tk_temp[0]]) + 1
377 
378     # 得到要插入的完整内容 id,name,age,phone,dept,date
379     insert_str = str(insert_id) + ',' + content
380 
381     # 先将得到的内容通过,分割得到列表;再与字段名的列表tk_temp组成dict,追加到user_info_list
382     user_info_list.append(dict(zip(tk_temp, insert_str.split(','))))
383 
384     # 将user_info_list写入文件
385     write_to_file(file_name, user_info_list)
386     # 打印插入成功提示
387     print('{2} func: insert(), db_file: {0}, values: {1} {3}'.format(file_name, content, '{', '}'))
388     return 910, '当前数据库中最大主键 ID = {0}'.format(str(insert_id))  # 返回插入成功
389 
390 
391 def my_sql_delete(para_str):
392     """
393     删除
394         delete from db.emp where id >25
395         delete * from db.emp
396         删除成功提示
397     :param: para_str: str
398     :return:
399     """
400     # 清空文件标识
401     tc_flag = False
402 
403     # 将接收的参数通过空格来分割,转为列表
404     para_list = para_str.split()
405 
406     try:
407         from_id = para_list.index(ky_list[5])  # 查找关键字from的下标
408     except ValueError:
409         return 901, '关键字{}缺失'.format(ky_list[5])  # 返回语法错误
410 
411     # 判断delete和from之间
412     if len(para_list[1:from_id]) == 1 and para_list[1] == '*':
413         # 判断是否是清空文件
414         tc_flag = True
415         # 判断from 后面是否只有数据文件
416         if len(para_list[from_id:]) < 2:
417             return 901, '缺失数据文件'
418         elif len(para_list[from_id:]) > 2:
419             return 901, '清空所有数据,数据文件后不能有多余的字符串'
420     elif para_list[1] == 'from':  # delete from xxx
421         # 判断from之后
422         if len(para_list[from_id:]) > 1:
423             if para_list[from_id + 1] == ky_list[6]:
424                 return 901, '数据文件缺失'  # delete from where
425             try:
426                 where_id = para_list.index(ky_list[6])  # from之后超过3个item,则判断where是否存在 ky_list[6]=where
427             except ValueError:
428                 return 901, '关键字{}缺失'.format(ky_list[6])  # where关键字缺失 delete from db.emp xxx
429             if len(para_list[from_id + 1:where_id]) != 1:
430                 return 901, '数据文件部分有语法错误'
431             if len(para_list[where_id + 1:]) > 0:
432                 content = para_str.split(ky_list[6])[-1].strip()
433             else:
434                 return 931, '关键字{}后无条件'.format(ky_list[6])
435         else:
436             return 901, '数据文件缺失'  # delete from where
437     else:
438         return 901, 'delete和from之间'  # 返回语法错误
439 
440     # 上述条件通过,则取到两个关键点db_name和content的值
441     db_name = para_list[from_id + 1]
442 
443     # 传db_name给get_filename函数,得到数据文件名
444     file_tup = get_filename(db_name)
445     file_name = file_tup[1]
446     if file_tup[0] == 951:
447         return file_tup
448     user_info_list = get_all_info(file_name)
449 
450     # 是否是清空文件
451     if tc_flag:
452         len_user_info_list = len(user_info_list)
453         with open(file_name, 'w') as t_f:
454             t_f.truncate()  # 直接清空文件
455         user_info_list.clear()  # 直接清空列表
456         return 940, '删除{}条数据'.format(len_user_info_list)  # 返回清空结果
457 
458     # 非*, 不清空文件, 处理where后面的语句
459     rm_id = get_ok_dic_id(content, user_info_list)
460     if rm_id:
461         if rm_id[0] == 901:
462             return rm_id
463     else:
464         return 941, '删除{}条记录'.format(len(rm_id))
465     write_id = list(set(range(len(user_info_list))).difference(set(rm_id)))
466     write_list = [user_info_list[i] for i in write_id]
467     write_to_file(file_name, write_list)
468     return 940, '删除{}条记录'.format(len(rm_id))  # 删除成功
469 
470 
471 def my_sql_update(para_str):
472     """
473         更新数据文件的数据, where后与select, delete类似
474     :param: para_str: 小写字符串 UPDATE staff_table SET dept="Market" WHERE where dept = "IT"
475     :return:
476     """
477     para_list = para_str.split()
478     try:
479         set_id = para_list.index(ky_list[-1])  # 查找set关键字下标
480     except ValueError:
481         return 901, '关键字{}缺失'.format(ky_list[-1])  # 返回语法错误
482 
483     if len(para_list[1:set_id]) != 1:  # 判断update和set之间长度
484         return 901, '数据文件部分有语法错误'
485 
486     try:
487         where_id = para_list.index(ky_list[6])  # 查找where关键字下标
488     except ValueError:
489         return 901, '关键字{}缺失'.format(ky_list[6])  # 返回语法错误
490 
491     # cl_list 确定是否有空格 user_dept='market' user_dept = 'market' user_dept= 'market'
492     temp_cl_str = para_str.split('set')[1].split('where')[0].strip()
493     if '=' not in temp_cl_str and len(temp_cl_str) < 2:
494         return 901, 'set和where之间'
495 
496     # 通过上述验证, 则获取到各关键字之间内容
497     db_name = para_list[set_id - 1]  #
498     cl_give = ''.join(para_list[set_id + 1: where_id])
499     if len(para_list[where_id + 1:]) > 0:
500         content = para_str.split(ky_list[6])[-1].strip()
501     else:
502         return 931, '关键字{}后无条件'.format(ky_list[6])
503 
504     # 获取文件名
505     file_tup = get_filename(db_name)
506     file_name = file_tup[1]
507     if file_tup[0] == 951:
508         return file_tup
509     # 获取user_info_list
510     user_info_list = get_all_info(file_name)
511     cl_list = cl_give.split('=')
512     cl_item = cl_list[0]
513     if cl_item not in tk_temp:
514         return 931, '{}是无效的字段名'.format(cl_item)
515     up_id = get_ok_dic_id(content, user_info_list)
516     if up_id:
517         if up_id[0] == 901:
518             return up_id
519     else:
520         return 931, '{}条记录被更新'.format(len(up_id))
521     for each_id in up_id:
522         user_info_list[each_id][cl_list[0]] = cl_list[1].strip("'")
523     write_to_file(file_name, user_info_list)
524     return 930, '{}条记录被更新'.format(len(up_id))  # 更新成功
525 
526 
527 def verify_keys(para_str):
528     # 验证是否出现多个关键字, 仅用在judge_begin函数当中
529     for k_w in ky_list:
530         if para_str.count(k_w) > 1:
531             return 901, '关键字{}重复'.format(k_w)  # 语法错误,未通过
532     else:
533         return 900, '关键字一次性验证OK'  # 通过
534 
535 
536 def judge_begin(para_str):
537     """
538     初始化验证
539     :param para_str: 接收用户输入的字符串,小写
540     :return:
541     """
542     para_str = para_str.lstrip()
543     is_res = verify_keys(para_str)
544     if is_res[0] == 900:
545         # 判断基础语法没问题
546         if para_str.startswith('select '):
547             return my_sql_select(para_str)
548         elif para_str.startswith('insert into '):
549             return my_sql_insert(para_str)
550         elif para_str.startswith('delete '):
551             return my_sql_delete(para_str)
552         elif para_str.startswith('update '):
553             return my_sql_update(para_str)
554         else:
555             return 901, '未发现开头关键字'
556     else:
557         return is_res
558 
559 
560 if __name__ == '__main__':
561     # 定义SQL语句当中的关键字
562     ky_list = ('select', 'delete', 'insert', 'update', 'values', 'from', 'where', 'limit', 'set')
563     # 定义字段名
564     tk_temp = ['user_id', 'user_name', 'user_age', 'user_phone', 'user_dept', 'user_date']
565     # 定义相关指标
566     flag_dic = {
567         # base
568         900: '语法正确',
569         901: '语法错误',
570         # select
571         910: '插入成功',
572         911: '插入失败',
573         # insert
574         920: '查询成功',
575         921: '查询失败',
576         # update
577         930: '更新成功',
578         931: '更新失败',
579         # delete
580         940: '删除成功',
581         941: '删除失败',
582         # db_file
583         950: '数据库正常',
584         951: '数据库异常',
585     }
586     # 定义主程序退出标志
587     flag = True
588     while flag:
589         try:
590             sql_line = input('Mysql (exit) > ').lower()
591             if sql_line.lower() == 'exit':
592                 flag = False
593                 print('已退出!')
594             elif sql_line.strip() == '':
595                 continue
596             else:
597                 # pass
598                 "执行相关函数"
599                 res_flag = judge_begin(sql_line)
600                 print(flag_dic.get(res_flag[0], ''), res_flag[1])
601         except KeyboardInterrupt:
602             flag = False
原文地址:https://www.cnblogs.com/alibner/p/6884909.html