-
Notifications
You must be signed in to change notification settings - Fork 1.7k
数据库管理页面-添加mysql/pgsql/mongo实例选择下拉菜单-并添加pgsql/mongo创建数据库功能 #1401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
from . import EngineBase | ||
from .models import ResultSet, ReviewSet, ReviewResult | ||
from sql.utils.data_masking import simple_column_mask | ||
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT | ||
|
||
__author__ = 'hhyo、yyukai' | ||
|
||
|
@@ -299,6 +300,34 @@ def execute_workflow(self, workflow, close_conn=True): | |
self.close() | ||
return execute_result | ||
|
||
def execute(self, db_name=None, sql='', ddl='', close_conn=True): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. execute方法就是用于执行有影响的语句,可以去除ddl入参和判断,统一设置autocommit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pgsql执行create database需要设置conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)自动提交,和dml操作默认放到一个block事务提交有冲突,dml保证事务一致性还是比较合理。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 是否是ddl直接在方法内判断比较好,可以优化一下 |
||
execute_result = ResultSet(full_sql=sql) | ||
# 删除注释语句,切分语句,将切换CURRENT_SCHEMA语句增加到切分结果中 | ||
sql = sqlparse.format(sql, strip_comments=True) | ||
split_sql = sqlparse.split(sql) | ||
statement = None | ||
try: | ||
conn = self.get_connection(db_name=db_name) | ||
# 设置事务隔离级别 | ||
if ddl.lower() == 'true': | ||
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) | ||
else: | ||
pass | ||
cursor = conn.cursor() | ||
# 逐条执行切分语句,追加到执行结果中 | ||
for statement in split_sql: | ||
statement = statement.rstrip(';') | ||
with FuncTimer() as t: | ||
cursor.execute(statement) | ||
conn.commit() | ||
except Exception as e: | ||
logger.warning(f"PGSQL命令执行报错,语句:{statement or sql}, 错误信息:{traceback.format_exc()}") | ||
execute_result.error = str(e) | ||
finally: | ||
if close_conn: | ||
self.close() | ||
return execute_result | ||
|
||
def close(self): | ||
if self.conn: | ||
self.conn.close() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,14 +23,15 @@ | |
@permission_required('sql.menu_database', raise_exception=True) | ||
def databases(request): | ||
"""获取实例数据库列表""" | ||
db_type1 = request.POST.get('db_type1') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 通过instance_id获取实例对象后就可以判断db类型了,不需要外部再传入这个参数 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 嗯看了源代码instance_id获取实例可以得到db_type,不过前端传的db_type1是为了在选择数据类型后在自动过滤该db_type1的实例列表,和返回db_type没有关系吧。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个方法是获取实例的数据库列表,看了下不需要这个 |
||
instance_id = request.POST.get('instance_id') | ||
saved = True if request.POST.get('saved') == 'true' else False # 平台是否保存 | ||
|
||
if not instance_id: | ||
if not instance_id or not db_type1: | ||
return JsonResponse({'status': 0, 'msg': '', 'data': []}) | ||
|
||
try: | ||
instance = user_instances(request.user, db_type=['mysql']).get(id=instance_id) | ||
instance = user_instances(request.user, db_type=['mysql','pgsql','mongo']).get(id=instance_id) | ||
except Instance.DoesNotExist: | ||
return JsonResponse({'status': 1, 'msg': '你所在组未关联该实例', 'data': []}) | ||
|
||
|
@@ -41,46 +42,117 @@ def databases(request): | |
db['saved'] = True | ||
cnf_dbs[f"{db['db_name']}"] = db | ||
|
||
# 获取所有数据库 | ||
sql_get_db = """SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME | ||
FROM information_schema.SCHEMATA | ||
WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'test', 'sys');""" | ||
query_engine = get_engine(instance=instance) | ||
query_result = query_engine.query('information_schema', sql_get_db, close_conn=False) | ||
if not query_result.error: | ||
dbs = query_result.rows | ||
# 获取数据库关联用户信息 | ||
rows = [] | ||
for db in dbs: | ||
db_name = db[0] | ||
sql_get_bind_users = f"""select group_concat(distinct(GRANTEE)),TABLE_SCHEMA | ||
from information_schema.SCHEMA_PRIVILEGES | ||
where TABLE_SCHEMA='{db_name}' | ||
group by TABLE_SCHEMA;""" | ||
bind_users = query_engine.query('information_schema', sql_get_bind_users, close_conn=False).rows | ||
row = { | ||
'db_name': db_name, | ||
'charset': db[1], | ||
'collation': db[2], | ||
'grantees': bind_users[0][0].split(',') if bind_users else [], | ||
'saved': False | ||
} | ||
# 合并数据 | ||
if db_name in cnf_dbs.keys(): | ||
row = dict(row, **cnf_dbs[db_name]) | ||
rows.append(row) | ||
# 过滤参数 | ||
if saved: | ||
rows = [row for row in rows if row['saved']] | ||
|
||
result = {'status': 0, 'msg': 'ok', 'rows': rows} | ||
if db_type1 == "pgsql": | ||
# 获取所有数据库 | ||
sql_get_db = """SELECT datname,datctype,datcollate,datacl | ||
FROM pg_database | ||
where datname not in ('postgres','template1','template0');""" | ||
query_engine = get_engine(instance=instance) | ||
query_result = query_engine.query('postgres', sql_get_db, close_conn=False) | ||
if not query_result.error: | ||
dbs = query_result.rows | ||
# 获取数据库关联用户信息 | ||
rows = [] | ||
for db in dbs: | ||
db_name = db[0] | ||
row = { | ||
'db_name': db_name, | ||
'charset': db[1], | ||
'collation': db[2], | ||
'grantees': db[3], | ||
'saved': False | ||
} | ||
# 合并数据 | ||
if db_name in cnf_dbs.keys(): | ||
row = dict(row, **cnf_dbs[db_name]) | ||
rows.append(row) | ||
# 过滤参数 | ||
if saved: | ||
rows = [row for row in rows if row['saved']] | ||
|
||
result = {'status': 0, 'msg': 'ok', 'rows': rows} | ||
else: | ||
result = {'status': 1, 'msg': query_result.error} | ||
|
||
# 关闭连接 | ||
query_engine.close() | ||
return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True), | ||
content_type='application/json') | ||
elif db_type1 == "mongo": | ||
# 获取所有数据库 | ||
query_engine = get_engine(instance=instance) | ||
query_result = query_engine.get_all_databases() | ||
if not query_result.error: | ||
dbs = query_result.rows | ||
# 获取数据库关联用户信息 | ||
rows = [] | ||
for db in dbs: | ||
db_name = db | ||
row = { | ||
'db_name': db_name, | ||
'charset': '', | ||
'collation': '', | ||
'grantees': '', | ||
'saved': False | ||
} | ||
# 合并数据 | ||
if db_name in cnf_dbs.keys(): | ||
row = dict(row, **cnf_dbs[db_name]) | ||
rows.append(row) | ||
# 过滤参数 | ||
if saved: | ||
rows = [row for row in rows if row['saved']] | ||
|
||
result = {'status': 0, 'msg': 'ok', 'rows': rows} | ||
else: | ||
result = {'status': 1, 'msg': query_result.error} | ||
|
||
# 关闭连接 | ||
query_engine.close() | ||
return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True), | ||
content_type='application/json') | ||
else: | ||
result = {'status': 1, 'msg': query_result.error} | ||
# 获取所有数据库 | ||
sql_get_db = """SELECT SCHEMA_NAME,DEFAULT_CHARACTER_SET_NAME,DEFAULT_COLLATION_NAME | ||
FROM information_schema.SCHEMATA | ||
WHERE SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'test', 'sys');""" | ||
query_engine = get_engine(instance=instance) | ||
query_result = query_engine.query('information_schema', sql_get_db, close_conn=False) | ||
if not query_result.error: | ||
dbs = query_result.rows | ||
# 获取数据库关联用户信息 | ||
rows = [] | ||
for db in dbs: | ||
db_name = db[0] | ||
sql_get_bind_users = f"""select group_concat(distinct(GRANTEE)),TABLE_SCHEMA | ||
from information_schema.SCHEMA_PRIVILEGES | ||
where TABLE_SCHEMA='{db_name}' | ||
group by TABLE_SCHEMA;""" | ||
bind_users = query_engine.query('information_schema', sql_get_bind_users, close_conn=False).rows | ||
row = { | ||
'db_name': db_name, | ||
'charset': db[1], | ||
'collation': db[2], | ||
'grantees': bind_users[0][0].split(',') if bind_users else [], | ||
'saved': False | ||
} | ||
# 合并数据 | ||
if db_name in cnf_dbs.keys(): | ||
row = dict(row, **cnf_dbs[db_name]) | ||
rows.append(row) | ||
# 过滤参数 | ||
if saved: | ||
rows = [row for row in rows if row['saved']] | ||
|
||
result = {'status': 0, 'msg': 'ok', 'rows': rows} | ||
else: | ||
result = {'status': 1, 'msg': query_result.error} | ||
|
||
# 关闭连接 | ||
query_engine.close() | ||
return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True), | ||
content_type='application/json') | ||
|
||
# 关闭连接 | ||
query_engine.close() | ||
return HttpResponse(json.dumps(result, cls=ExtendJSONEncoder, bigint_as_string=True), | ||
content_type='application/json') | ||
|
||
|
||
@permission_required('sql.menu_database', raise_exception=True) | ||
|
@@ -90,12 +162,13 @@ def create(request): | |
db_name = request.POST.get('db_name') | ||
owner = request.POST.get('owner', '') | ||
remark = request.POST.get('remark', '') | ||
db_type1 = request.POST.get('db_type1') | ||
|
||
if not all([db_name]): | ||
return JsonResponse({'status': 1, 'msg': '参数不完整,请确认后提交', 'data': []}) | ||
|
||
try: | ||
instance = user_instances(request.user, db_type=['mysql']).get(id=instance_id) | ||
instance = user_instances(request.user, db_type=['mysql','pgsql','mongo']).get(id=instance_id) | ||
except Instance.DoesNotExist: | ||
return JsonResponse({'status': 1, 'msg': '你所在组未关联该实例', 'data': []}) | ||
|
||
|
@@ -108,7 +181,16 @@ def create(request): | |
db_name = MySQLdb.escape_string(db_name).decode('utf-8') | ||
|
||
engine = get_engine(instance=instance) | ||
exec_result = engine.execute(db_name='information_schema', sql=f"create database {db_name};") | ||
|
||
if db_type1 == "pgsql": | ||
exec_result = engine.execute(db_name='postgres', sql=f"create database {db_name};", ddl='true') | ||
elif db_type1 == "mongo": | ||
password = remark.strip() | ||
exec_sql=f"db.createUser("+"{user:"+f"'{db_name}',pwd:"+f"'{password}',roles:["+"{role:'dbAdmin',db:"+f"'{db_name}'"+"}],mechanisms:['SCRAM-SHA-1']});db.testcoll.insert({testid:'test01'});" | ||
exec_result = engine.execute(db_name=db_name, sql=exec_sql, ddl='true') | ||
else: | ||
exec_result = engine.execute(db_name='information_schema', sql=f"create database {db_name};") | ||
|
||
if exec_result.error: | ||
return JsonResponse({'status': 1, 'msg': exec_result.error}) | ||
# 保存到数据库 | ||
|
@@ -134,7 +216,7 @@ def edit(request): | |
return JsonResponse({'status': 1, 'msg': '参数不完整,请确认后提交', 'data': []}) | ||
|
||
try: | ||
instance = user_instances(request.user, db_type=['mysql']).get(id=instance_id) | ||
instance = user_instances(request.user, db_type=['mysql','pgsql','mongo']).get(id=instance_id) | ||
except Instance.DoesNotExist: | ||
return JsonResponse({'status': 1, 'msg': '你所在组未关联该实例', 'data': []}) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,15 @@ | |
{% block content %} | ||
<!-- 自定义操作按钮--> | ||
<div id="toolbar" class="form-inline pull-left"> | ||
<div class="form-group"> | ||
<select id="db_type1" class="form-control selectpicker" name="db_type" | ||
taochao2000 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. db_type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 嗯,这个修改成db_type_f,表示数据库类型过滤 |
||
title="请选择数据库类型" | ||
data-live-search="true"> | ||
<option value="mysql">mysql</option> | ||
<option value="pgsql">pgsql</option> | ||
<option value="mongo">mongo</option> | ||
</select> | ||
</div> | ||
<div class="form-group "> | ||
<select id=instance class="form-control selectpicker" name="instance_list" | ||
title="请选择实例" | ||
|
@@ -68,11 +77,11 @@ <h4 class="modal-title">创建数据库</h4> | |
</div> | ||
</div> | ||
<div class="form-group row"> | ||
<label for="remark" class="col-sm-3 col-form-label">备注</label> | ||
<label for="remark" class="col-sm-3 col-form-label">备注/mongo密码</label> | ||
<div class="col-sm-9"> | ||
<input type="text" id="remark" class="form-control" | ||
autocomplete="off" | ||
placeholder="请输入备注"> | ||
placeholder="请输入备注/mongo密码"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不特意描述为密码比较好,这里就是填写描述信息的地方 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 嗯,这个可以修改回来。 |
||
</div> | ||
</div> | ||
</div> | ||
|
@@ -178,11 +187,12 @@ <h4 class="modal-title">编辑/录入数据库信息</h4> | |
//请求服务数据时所传参数 | ||
queryParams: | ||
function (params) { | ||
if ($("#instance").val()) { | ||
if ($("#instance").val() && $("#db_type1").val()) { | ||
return { | ||
search: params.search, | ||
instance_id: $("#instance").val(), | ||
saved: $("#saved").val() | ||
saved: $("#saved").val(), | ||
db_type1: $("#db_type1").val() | ||
} | ||
} | ||
}, | ||
|
@@ -200,11 +210,18 @@ <h4 class="modal-title">编辑/录入数据库信息</h4> | |
title: '授权账号', | ||
field: 'grantees', | ||
formatter: function (value, row, index) { | ||
if ( $("#db_type1").val() == 'mysql' ) { | ||
let grantee = ''; | ||
for (let i = 0; i < value.length; i++) { | ||
grantee = grantee + value[i] + '</br>' | ||
} | ||
return grantee | ||
} | ||
else { | ||
let grantee = ''; | ||
grantee = value | ||
return grantee | ||
} | ||
} | ||
}, { | ||
title: '负责人', | ||
|
@@ -214,7 +231,7 @@ <h4 class="modal-title">编辑/录入数据库信息</h4> | |
title: '负责人', | ||
field: 'owner_display' | ||
}, { | ||
title: '备注', | ||
title: '备注/mongo密码', | ||
field: 'remark' | ||
}, { | ||
title: '操作', | ||
|
@@ -261,7 +278,7 @@ <h4 class="modal-title">编辑/录入数据库信息</h4> | |
|
||
//创建数据库 | ||
function create_database() { | ||
if (!$("#instance").val()) { | ||
if (!$("#instance").val() || !$("#db_type1").val()) { | ||
alert("请选择实例!"); | ||
} else { | ||
$.ajax({ | ||
|
@@ -272,7 +289,8 @@ <h4 class="modal-title">编辑/录入数据库信息</h4> | |
instance_id: $("#instance").val(), | ||
db_name: $("#db_name").val(), | ||
owner: $("#owner").val(), | ||
remark: $("#remark").val() | ||
remark: $("#remark").val(), | ||
db_type1: $("#db_type1").val() | ||
}, | ||
complete: function () { | ||
}, | ||
|
@@ -339,13 +357,13 @@ <h4 class="modal-title">编辑/录入数据库信息</h4> | |
$(document).ready(function () { | ||
database_list(); | ||
//获取用户实例列表 | ||
$(function () { | ||
$("#db_type1").change(function () { | ||
$.ajax({ | ||
type: "get", | ||
url: "/group/user_all_instances/", | ||
dataType: "json", | ||
data: { | ||
db_type: ['mysql'] | ||
db_type: [$("#db_type1").val()] | ||
}, | ||
complete: function () { | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
没有看出来ddl和非ddl的区别,可以去除ddl入参和下面的判断
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mongo.py ddl执行用的还是之前的方法,确实没有区别可以去掉,是为了打印ddl字符串加了一个判断。