Skip to content

Commit

Permalink
Merge pull request #724 from hhyo/oracle
Browse files Browse the repository at this point in the history
Oracle功能增强和bugfix
  • Loading branch information
hhyo authored May 4, 2020
2 parents 01746a6 + bad1d07 commit afcc013
Show file tree
Hide file tree
Showing 7 changed files with 1,740 additions and 1,007 deletions.
663 changes: 591 additions & 72 deletions sql/engines/oracle.py

Large diffs are not rendered by default.

36 changes: 22 additions & 14 deletions sql/engines/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from datetime import timedelta, datetime
from unittest.mock import patch, Mock, ANY

import sqlparse
from django.contrib.auth import get_user_model
from django.test import TestCase

Expand Down Expand Up @@ -1369,10 +1370,11 @@ def test_query_check_disable_sql(self):
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.query_check(db_name='archery', sql=sql)
self.assertDictEqual(check_result,
{'msg': '仅支持^select语法!', 'bad_query': True, 'filtered_sql': sql.strip(';'),
{'msg': '不支持语法!', 'bad_query': True, 'filtered_sql': sql.strip(';'),
'has_star': False})

def test_query_check_star_sql(self):
@patch('sql.engines.oracle.OracleEngine.explain_check', return_value={'msg': '', 'rows': 0})
def test_query_check_star_sql(self, _explain_check):
sql = "select * from xx;"
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.query_check(db_name='archery', sql=sql)
Expand All @@ -1387,7 +1389,8 @@ def test_query_check_IndexError(self):
self.assertDictEqual(check_result,
{'msg': '没有有效的SQL语句', 'bad_query': True, 'filtered_sql': sql.strip(), 'has_star': False})

def test_query_check_plus(self):
@patch('sql.engines.oracle.OracleEngine.explain_check', return_value={'msg': '', 'rows': 0})
def test_query_check_plus(self, _explain_check):
sql = "select 100+1 from tb;"
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.query_check(db_name='archery', sql=sql)
Expand All @@ -1399,25 +1402,27 @@ def test_filter_sql_with_delimiter(self):
sql = "select * from xx;"
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.filter_sql(sql=sql, limit_num=100)
self.assertEqual(check_result, "select * from xx WHERE ROWNUM <= 100")
self.assertEqual(check_result, "select sql_audit.* from (select * from xx) sql_audit where rownum <= 100")

def test_filter_sql_with_delimiter_and_where(self):
sql = "select * from xx where id>1;"
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.filter_sql(sql=sql, limit_num=100)
self.assertEqual(check_result, "select * from xx where id>1 AND ROWNUM <= 100")
self.assertEqual(check_result,
"select sql_audit.* from (select * from xx where id>1) sql_audit where rownum <= 100")

def test_filter_sql_without_delimiter(self):
sql = "select * from xx;"
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.filter_sql(sql=sql, limit_num=100)
self.assertEqual(check_result, "select * from xx WHERE ROWNUM <= 100")
self.assertEqual(check_result, "select sql_audit.* from (select * from xx) sql_audit where rownum <= 100")

def test_filter_sql_with_limit(self):
sql = "select * from xx limit 10;"
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.filter_sql(sql=sql, limit_num=1)
self.assertEqual(check_result, "select * from xx limit 10 WHERE ROWNUM <= 1")
self.assertEqual(check_result,
"select sql_audit.* from (select * from xx limit 10) sql_audit where rownum <= 1")

def test_query_masking(self):
query_result = ResultSet()
Expand All @@ -1430,7 +1435,7 @@ def test_execute_check_select_sql(self):
row = ReviewResult(id=1, errlevel=2,
stagestatus='驳回不支持语句',
errormessage='仅支持DML和DDL语句,查询语句请使用SQL查询功能!',
sql=sql)
sql=sqlparse.format(sql, strip_comments=True, reindent=True, keyword_case='lower'))
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.execute_check(db_name='archery', sql=sql)
self.assertIsInstance(check_result, ReviewSet)
Expand All @@ -1443,20 +1448,23 @@ def test_execute_check_critical_sql(self):
row = ReviewResult(id=1, errlevel=2,
stagestatus='驳回高危SQL',
errormessage='禁止提交匹配' + '^|update' + '条件的语句!',
sql=sql)
sql=sqlparse.format(sql, strip_comments=True, reindent=True, keyword_case='lower'))
new_engine = OracleEngine(instance=self.ins)
check_result = new_engine.execute_check(db_name='archery', sql=sql)
self.assertIsInstance(check_result, ReviewSet)
self.assertEqual(check_result.rows[0].__dict__, row.__dict__)

def test_execute_check_normal_sql(self):
@patch('sql.engines.oracle.OracleEngine.explain_check', return_value={'msg': '', 'rows': 0})
@patch('sql.engines.oracle.OracleEngine.get_sql_first_object_name', return_value='tb')
@patch('sql.engines.oracle.OracleEngine.object_name_check', return_value=True)
def test_execute_check_normal_sql(self, _explain_check, _get_sql_first_object_name, _object_name_check):
self.sys_config.purge()
sql = 'alter table tb set id=1'
row = ReviewResult(id=1,
errlevel=0,
stagestatus='Audit completed',
errormessage='None',
sql=sql,
errlevel=1,
stagestatus='当前平台,此语法不支持审核!',
errormessage='当前平台,此语法不支持审核!',
sql=sqlparse.format(sql, strip_comments=True, reindent=True, keyword_case='lower'),
affected_rows=0,
execute_time=0,
stmt_type='SQL',
Expand Down
50 changes: 50 additions & 0 deletions sql/sql_optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,53 @@ def explain(request):
# 返回查询结果
return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True),
content_type='application/json')


def optimize_sqltuningadvisor(request):
"""
sqltuningadvisor工具获取优化报告
:param request:
:return:
"""
sql_content = request.POST.get('sql_content')
instance_name = request.POST.get('instance_name')
db_name = request.POST.get('schema_name')
result = {'status': 0, 'msg': 'ok', 'data': []}

# 服务器端参数验证
if sql_content is None or instance_name is None:
result['status'] = 1
result['msg'] = '页面提交参数可能为空'
return HttpResponse(json.dumps(result), content_type='application/json')

try:
instance = user_instances(request.user).get(instance_name=instance_name)
except Instance.DoesNotExist:
result = {'status': 1, 'msg': '实例不存在', 'data': []}
return HttpResponse(json.dumps(result), content_type='application/json')

# 不删除注释语句,已获取加hints的SQL优化建议,进行语法判断,执行第一条有效sql
sql_content = sqlparse.format(sql_content.strip(), strip_comments=False)
# 对单引号加转义符,支持plsql语法
sql_content = sql_content.replace("'", "''");
try:
sql_content = sqlparse.split(sql_content)[0]
except IndexError:
result['status'] = 1
result['msg'] = '没有有效的SQL语句'
return HttpResponse(json.dumps(result), content_type='application/json')
else:
# 过滤非Oracle语句
if not instance.db_type == 'oracle':
result['status'] = 1
result['msg'] = 'SQLTuningAdvisor仅支持oracle数据库的检查'
return HttpResponse(json.dumps(result), content_type='application/json')

# 执行获取优化报告
query_engine = get_engine(instance=instance)
sql_result = query_engine.sqltuningadvisor(str(db_name), sql_content).to_sep_dict()
result['data'] = sql_result

# 返回查询结果
return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True),
content_type='application/json')
Loading

0 comments on commit afcc013

Please sign in to comment.