diff --git a/sql/query.py b/sql/query.py index c1a5f45196..29aa8d473b 100644 --- a/sql/query.py +++ b/sql/query.py @@ -6,6 +6,7 @@ import traceback import simplejson as json +import sqlparse from django.contrib.auth.decorators import permission_required from django.core import serializers from django.db import connection @@ -67,10 +68,10 @@ def query_priv_check(user, instance_name, db_name, sql_content, limit_num): # 检查用户是否有该数据库/表的查询权限 if user.is_superuser: - if SysConfig().sys_config.get('admin_query_limit'): + if SysConfig().sys_config.get('admin_query_limit', 5000): user_limit_num = int(SysConfig().sys_config.get('admin_query_limit')) else: - user_limit_num = 0 + user_limit_num = 5000 limit_num = int(user_limit_num) if int(limit_num) == 0 else min(int(limit_num), int(user_limit_num)) # 查看表结构和执行计划,inception会报错,故单独处理,explain直接跳过不做校验 @@ -484,8 +485,8 @@ def query(request): result['msg'] = '仅支持^select|^show|^explain语法,请联系管理员!' return HttpResponse(json.dumps(result), content_type='application/json') - # 按照分号截取第一条有效sql执行 - sql_content = sql_content.strip().split(';')[0] + # 执行第一条有效sql + sql_content = sqlparse.split(sql_content)[0].rstrip(';') try: # 查询权限校验 @@ -641,8 +642,8 @@ def explain(request): result['msg'] = '仅支持explain开头的语句,请检查' return HttpResponse(json.dumps(result), content_type='application/json') - # 按照分号截取第一条有效sql执行 - sql_content = sql_content.strip().split(';')[0] + # 执行第一条有效sql + sql_content = sqlparse.split(sql_content)[0].rstrip(';') # 执行获取执行计划语句 sql_result = Dao(instance_name=instance_name).mysql_query(str(db_name), sql_content) diff --git a/sql/sql_workflow.py b/sql/sql_workflow.py index ac0631d5fa..0eeeb0c95f 100644 --- a/sql/sql_workflow.py +++ b/sql/sql_workflow.py @@ -6,6 +6,7 @@ from threading import Thread import simplejson as json +import sqlparse from django.contrib.auth.decorators import permission_required from django.core.exceptions import PermissionDenied from django.db import transaction, connection @@ -263,8 +264,10 @@ def autoreview(request): # 判断SQL是否包含DDL语句,SQL语法 1、DDL,2、DML sql_syntax = 2 - for row in sql_content.strip(';').split(';'): - if re.match(r"^alter|^create|^drop|^truncate|^rename", row.strip().lower()): + for stmt in sqlparse.split(sql_content): + statement = sqlparse.parse(stmt)[0] + syntax_type = statement.token_first().ttype.__str__() + if syntax_type == 'Token.Keyword.DDL': sql_syntax = 1 break diff --git a/sql/utils/dao.py b/sql/utils/dao.py index 88ba15a830..0afb12825a 100644 --- a/sql/utils/dao.py +++ b/sql/utils/dao.py @@ -2,6 +2,7 @@ import MySQLdb import traceback +import sqlparse from common.utils.aes_decryptor import Prpcrypt from sql.models import Instance @@ -135,14 +136,13 @@ def mysql_execute(self, db_name, sql): conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.password, db=db_name, charset='utf8') cursor = conn.cursor() - for row in sql.strip(';').split(';'): - cursor.execute(row) + for statement in sqlparse.split(sql): + cursor.execute(statement) conn.commit() except Exception as e: logger.error(traceback.format_exc()) result['Error'] = str(e) else: - cursor.close() conn.close() return result diff --git a/sql/utils/execute_sql.py b/sql/utils/execute_sql.py index 2ba1e0282b..e20aa666aa 100644 --- a/sql/utils/execute_sql.py +++ b/sql/utils/execute_sql.py @@ -6,7 +6,7 @@ import time from threading import Thread - +import sqlparse from django.db import connection from django.utils import timezone @@ -197,26 +197,26 @@ def send_msg(workflow_detail, url): # 匹配DDL语句CREATE、ALTER(排除索引变更)、DROP、TRUNCATE、RENAME send = 0 - for row in sql_content.strip(';').split(';'): + for statement in sqlparse.split(sql_content): # alter语法 if re.match(r"^alter\s+table\s+\S+\s+(add|alter|change|drop|rename|modify)\s+(?!.*(index|key|unique))", - row.strip().lower()): + statement.strip().lower()): send = 1 break # create语法 - elif re.match(r"^create\s+(temporary\s+)?(database|schema|table)", row.strip().lower()): + elif re.match(r"^create\s+(temporary\s+)?(database|schema|table)", statement.strip().lower()): send = 1 break # drop语法 - elif re.match(r"^drop", row.strip().lower()): + elif re.match(r"^drop|^rename|^truncate", statement.strip().lower()): send = 1 break # rename语法 - elif re.match(r"^rename", row.strip().lower()): + elif re.match(r"", statement.strip().lower()): send = 1 break # truncate语法 - elif re.match(r"^truncate", row.strip().lower()): + elif re.match(r"", statement.strip().lower()): send = 1 break diff --git a/sql/utils/inception.py b/sql/utils/inception.py index 42fcb4ac7f..5f5ede8083 100644 --- a/sql/utils/inception.py +++ b/sql/utils/inception.py @@ -4,6 +4,7 @@ import simplejson as json import MySQLdb import traceback +import sqlparse from django.db import connection from sql.models import Instance, SqlWorkflow @@ -51,15 +52,15 @@ def critical_ddl(self, sql_content): map(lambda x: re.compile(r'(^--\s+.*|^/\*.*\*/;\s*$)').sub('', x, count=1), sql_content.splitlines(1))).strip() - for row in sql_content.rstrip(';').split(';'): - if p.match(row.strip().lower()): + for statement in sqlparse.split(sql_content): + if p.match(statement.strip().lower()): result = ( '', '', 2, '驳回高危SQL', '禁止提交匹配' + critical_ddl_regex + '条件的语句!', - row, + statement, '', '', '', '') critical_sql_found = 1 else: - result = ('', '', 0, '', 'None', row, '', '', '', '') + result = ('', '', 0, '', 'None', statement, '', '', '', '') result_list.append(result) if critical_sql_found == 1: return result_list @@ -70,15 +71,20 @@ def pre_check(self, sql_content): """ 在提交给inception之前,预先识别一些Inception不能正确审核的SQL,比如"alter table t1;"或"alter table test.t1;" 以免导致inception core dump """ + # 删除注释语句 + sql_content = ''.join( + map(lambda x: re.compile(r'(^--\s+.*|^/\*.*\*/;\s*$)').sub('', x, count=1), + sql_content.splitlines(1))).strip() result_list = [] syntax_error_sql_found = 0 - for row in sql_content.rstrip(';').split(';'): + for statement in sqlparse.split(sql_content): + # 注释不检测 if re.match(r"(\s*)alter(\s+)table(\s+)(\S+)(\s*);|(\s*)alter(\s+)table(\s+)(\S+)\.(\S+)(\s*);", - row.lower() + ";"): - result = ('', '', 2, 'SQL语法错误', 'ALTER TABLE 必须带有选项', row, '', '', '', '') + statement.lower() + ";"): + result = ('', '', 2, 'SQL语法错误', 'ALTER TABLE 必须带有选项', statement, '', '', '', '') syntax_error_sql_found = 1 else: - result = ('', '', 0, '', 'None', row, '', '', '', '') + result = ('', '', 0, '', 'None', statement, '', '', '', '') result_list.append(result) if syntax_error_sql_found == 1: return result_list diff --git a/sql/utils/sql_review.py b/sql/utils/sql_review.py index 551532d225..cda6c40a49 100644 --- a/sql/utils/sql_review.py +++ b/sql/utils/sql_review.py @@ -1,4 +1,5 @@ import re +import sqlparse from common.utils.const import Const from sql.models import SqlWorkflow @@ -36,8 +37,8 @@ def is_auto_review(workflow_id): # 判断是否匹配到需要手动审核的语句 is_autoreview = True - for row in sql_content.strip(';').split(';'): - if p.match(row.strip().lower()): + for statement in sqlparse.split(sql_content): + if p.match(statement.strip().lower()): is_autoreview = False break if is_autoreview: