Example #1
0
    def execute_check(self, db_name=None, sql=''):
        """上线单执行前的检查, 返回Review set"""
        config = SysConfig()
        check_result = ReviewSet(full_sql=sql)
        # 禁用/高危语句检查
        line = 1
        critical_ddl_regex = config.get('critical_ddl_regex', '')
        p = re.compile(critical_ddl_regex)
        check_result.syntax_type = 2  # TODO 工单类型 0、其他 1、DDL,2、DML

        # 把所有SQL转换成SqlItem List。 如有多行(内部有多个;)执行块,约定以delimiter $$作为开始, 以$$结束
        # 需要在函数里实现单条SQL做sqlparse.format(sql, strip_comments=True)
        sqlitemList = get_full_sqlitem_list(sql, db_name)

        for sqlitem in sqlitemList:
            # 禁用语句
            if re.match(r"^\s*select", sqlitem.statement.lower(), re.I):
                check_result.is_critical = True
                result = ReviewResult(id=line, errlevel=2,
                                      stagestatus='驳回不支持语句',
                                      errormessage='仅支持DML和DDL语句,查询语句请使用SQL查询功能!',
                                      sql=sqlitem.statement)
            # 高危语句
            elif critical_ddl_regex and p.match(sqlitem.statement.strip().lower()):
                check_result.is_critical = True
                result = ReviewResult(id=line, errlevel=2,
                                      stagestatus='驳回高危SQL',
                                      errormessage='禁止提交匹配' + critical_ddl_regex + '条件的语句!',
                                      sql=sqlitem.statement)

            # 正常语句
            else:
                result = ReviewResult(id=line, errlevel=0,
                                      stagestatus='Audit completed',
                                      errormessage='None',
                                      sql=sqlitem.statement,
                                      stmt_type=sqlitem.stmt_type,
                                      object_owner=sqlitem.object_owner,
                                      object_type=sqlitem.object_type,
                                      object_name=sqlitem.object_name,
                                      affected_rows=0,
                                      execute_time=0, )
            # 判断工单类型
            if get_syntax_type(sqlitem.statement) == 'DDL':
                check_result.syntax_type = 1
            check_result.rows += [result]

            # 遇到禁用和高危语句直接返回,提高效率
            if check_result.is_critical:
                check_result.error_count += 1
                return check_result
            line += 1
        return check_result
Example #2
0
    def execute_check(self, db_name=None, sql='', close_conn=True):
        """
        上线单执行前的检查, 返回Review set
        update by Jan.song 20200302
        使用explain对数据修改预计进行检测
        """
        config = SysConfig()
        check_result = ReviewSet(full_sql=sql)
        # explain支持的语法
        explain_re = r"^merge|^update|^delete|^insert|^create\s+table|^create\s+index|^create\s+unique\s+index"
        # 禁用/高危语句检查
        line = 1
        # 保存SQL中的新建对象
        object_name_list = set()
        critical_ddl_regex = config.get('critical_ddl_regex', '')
        p = re.compile(critical_ddl_regex)
        check_result.syntax_type = 2  # TODO 工单类型 0、其他 1、DDL,2、DML
        try:
            sqlitemList = get_full_sqlitem_list(sql, db_name)
            for sqlitem in sqlitemList:
                sql_lower = sqlitem.statement.lower().rstrip(';')
                # 禁用语句
                if re.match(r"^select|^with|^explain", sql_lower):
                    check_result.is_critical = True
                    result = ReviewResult(
                        id=line,
                        errlevel=2,
                        stagestatus='驳回不支持语句',
                        errormessage='仅支持DML和DDL语句,查询语句请使用SQL查询功能!',
                        sql=sqlitem.statement)
                # 高危语句
                elif critical_ddl_regex and p.match(sql_lower.strip()):
                    check_result.is_critical = True
                    result = ReviewResult(id=line,
                                          errlevel=2,
                                          stagestatus='驳回高危SQL',
                                          errormessage='禁止提交匹配' +
                                          critical_ddl_regex + '条件的语句!',
                                          sql=sqlitem.statement)
                # 驳回未带where数据修改语句,如确实需做全部删除或更新,显示的带上where 1=1
                elif re.match(r"^update((?!where).)*$|^delete((?!where).)*$",
                              sql_lower):
                    check_result.is_critical = True
                    result = ReviewResult(id=line,
                                          errlevel=2,
                                          stagestatus='驳回未带where数据修改',
                                          errormessage='数据修改需带where条件!',
                                          sql=sqlitem.statement)
                # 驳回事务控制,会话控制SQL
                elif re.match(r"^set|^rollback|^exit", sql_lower):
                    check_result.is_critical = True
                    result = ReviewResult(
                        id=line,
                        errlevel=2,
                        stagestatus='SQL中不能包含^set|^rollback|^exit',
                        errormessage='SQL中不能包含^set|^rollback|^exit',
                        sql=sqlitem.statement)

                # 通过explain对SQL做语法语义检查
                elif re.match(explain_re,
                              sql_lower) and sqlitem.stmt_type == 'SQL':
                    if self.check_create_index_table(
                            db_name=db_name,
                            sql=sql_lower,
                            object_name_list=object_name_list):
                        object_name = self.get_sql_first_object_name(
                            sql=sql_lower)
                        if '.' in object_name:
                            object_name = object_name
                        else:
                            object_name = f"""{db_name}.{object_name}"""
                        object_name_list.add(object_name)
                        result = ReviewResult(
                            id=line,
                            errlevel=1,
                            stagestatus='WARNING:新建表的新建索引语句暂无法检测!',
                            errormessage='WARNING:新建表的新建索引语句暂无法检测!',
                            stmt_type=sqlitem.stmt_type,
                            object_owner=sqlitem.object_owner,
                            object_type=sqlitem.object_type,
                            object_name=sqlitem.object_name,
                            sql=sqlitem.statement)
                    elif len(object_name_list) > 0 and self.get_dml_table(
                            db_name=db_name,
                            sql=sql_lower,
                            object_name_list=object_name_list):
                        result = ReviewResult(
                            id=line,
                            errlevel=1,
                            stagestatus='WARNING:新建表的数据修改暂无法检测!',
                            errormessage='WARNING:新建表的数据修改暂无法检测!',
                            stmt_type=sqlitem.stmt_type,
                            object_owner=sqlitem.object_owner,
                            object_type=sqlitem.object_type,
                            object_name=sqlitem.object_name,
                            sql=sqlitem.statement)
                    else:
                        result_set = self.explain_check(db_name=db_name,
                                                        sql=sqlitem.statement,
                                                        close_conn=False)
                        if result_set['msg']:
                            check_result.is_critical = True
                            result = ReviewResult(
                                id=line,
                                errlevel=2,
                                stagestatus='explain语法检查未通过!',
                                errormessage=result_set['msg'],
                                sql=sqlitem.statement)
                        else:
                            # 对create table\create index\create unique index语法做对象存在性检测
                            if re.match(
                                    r"^create\s+table|^create\s+index|^create\s+unique\s+index",
                                    sql_lower):
                                object_name = self.get_sql_first_object_name(
                                    sql=sql_lower)
                                # 保存create对象对后续SQL做存在性判断
                                if '.' in object_name:
                                    object_name = object_name
                                else:
                                    object_name = f"""{db_name}.{object_name}"""
                                if self.object_name_check(
                                        db_name=db_name,
                                        object_name=object_name
                                ) or object_name in object_name_list:
                                    check_result.is_critical = True
                                    result = ReviewResult(
                                        id=line,
                                        errlevel=2,
                                        stagestatus=f"""{object_name}对象已经存在!""",
                                        errormessage=
                                        f"""{object_name}对象已经存在!""",
                                        sql=sqlitem.statement)
                                else:
                                    object_name_list.add(object_name)
                                    if result_set['rows'] > 1000:
                                        result = ReviewResult(
                                            id=line,
                                            errlevel=1,
                                            stagestatus='影响行数大于1000,请关注',
                                            errormessage='影响行数大于1000,请关注',
                                            sql=sqlitem.statement,
                                            stmt_type=sqlitem.stmt_type,
                                            object_owner=sqlitem.object_owner,
                                            object_type=sqlitem.object_type,
                                            object_name=sqlitem.object_name,
                                            affected_rows=result_set['rows'],
                                            execute_time=0,
                                        )
                                    else:
                                        result = ReviewResult(
                                            id=line,
                                            errlevel=0,
                                            stagestatus='Audit completed',
                                            errormessage='None',
                                            sql=sqlitem.statement,
                                            stmt_type=sqlitem.stmt_type,
                                            object_owner=sqlitem.object_owner,
                                            object_type=sqlitem.object_type,
                                            object_name=sqlitem.object_name,
                                            affected_rows=result_set['rows'],
                                            execute_time=0,
                                        )
                            else:
                                if result_set['rows'] > 1000:
                                    result = ReviewResult(
                                        id=line,
                                        errlevel=1,
                                        stagestatus='影响行数大于1000,请关注',
                                        errormessage='影响行数大于1000,请关注',
                                        sql=sqlitem.statement,
                                        stmt_type=sqlitem.stmt_type,
                                        object_owner=sqlitem.object_owner,
                                        object_type=sqlitem.object_type,
                                        object_name=sqlitem.object_name,
                                        affected_rows=result_set['rows'],
                                        execute_time=0,
                                    )
                                else:
                                    result = ReviewResult(
                                        id=line,
                                        errlevel=0,
                                        stagestatus='Audit completed',
                                        errormessage='None',
                                        sql=sqlitem.statement,
                                        stmt_type=sqlitem.stmt_type,
                                        object_owner=sqlitem.object_owner,
                                        object_type=sqlitem.object_type,
                                        object_name=sqlitem.object_name,
                                        affected_rows=result_set['rows'],
                                        execute_time=0,
                                    )
                # 其它无法用explain判断的语句
                else:
                    # 对alter table做对象存在性检查
                    if re.match(r"^alter\s+table\s", sql_lower):
                        object_name = self.get_sql_first_object_name(
                            sql=sql_lower)
                        if '.' in object_name:
                            object_name = object_name
                        else:
                            object_name = f"""{db_name}.{object_name}"""
                        if not self.object_name_check(
                                db_name=db_name, object_name=object_name
                        ) and object_name not in object_name_list:
                            check_result.is_critical = True
                            result = ReviewResult(
                                id=line,
                                errlevel=2,
                                stagestatus=f"""{object_name}对象不存在!""",
                                errormessage=f"""{object_name}对象不存在!""",
                                sql=sqlitem.statement)
                        else:
                            result = ReviewResult(
                                id=line,
                                errlevel=1,
                                stagestatus='当前平台,此语法不支持审核!',
                                errormessage='当前平台,此语法不支持审核!',
                                sql=sqlitem.statement,
                                stmt_type=sqlitem.stmt_type,
                                object_owner=sqlitem.object_owner,
                                object_type=sqlitem.object_type,
                                object_name=sqlitem.object_name,
                                affected_rows=0,
                                execute_time=0,
                            )
                    # 对create做对象存在性检查
                    elif re.match(r"^create", sql_lower):
                        object_name = self.get_sql_first_object_name(
                            sql=sql_lower)
                        if '.' in object_name:
                            object_name = object_name
                        else:
                            object_name = f"""{db_name}.{object_name}"""
                        if self.object_name_check(
                                db_name=db_name, object_name=object_name
                        ) or object_name in object_name_list:
                            check_result.is_critical = True
                            result = ReviewResult(
                                id=line,
                                errlevel=2,
                                stagestatus=f"""{object_name}对象已经存在!""",
                                errormessage=f"""{object_name}对象已经存在!""",
                                sql=sqlitem.statement)
                        else:
                            object_name_list.add(object_name)
                            result = ReviewResult(
                                id=line,
                                errlevel=1,
                                stagestatus='当前平台,此语法不支持审核!',
                                errormessage='当前平台,此语法不支持审核!',
                                sql=sqlitem.statement,
                                stmt_type=sqlitem.stmt_type,
                                object_owner=sqlitem.object_owner,
                                object_type=sqlitem.object_type,
                                object_name=sqlitem.object_name,
                                affected_rows=0,
                                execute_time=0,
                            )
                    else:
                        result = ReviewResult(
                            id=line,
                            errlevel=1,
                            stagestatus='当前平台,此语法不支持审核!',
                            errormessage='当前平台,此语法不支持审核!',
                            sql=sqlitem.statement,
                            stmt_type=sqlitem.stmt_type,
                            object_owner=sqlitem.object_owner,
                            object_type=sqlitem.object_type,
                            object_name=sqlitem.object_name,
                            affected_rows=0,
                            execute_time=0,
                        )
                # 判断工单类型
                if get_syntax_type(sql=sqlitem.statement,
                                   db_type='oracle') == 'DDL':
                    check_result.syntax_type = 1
                check_result.rows += [result]
                # 遇到禁用和高危语句直接返回,提高效率
                if check_result.is_critical:
                    check_result.error_count += 1
                    return check_result
                line += 1
        except Exception as e:
            logger.warning(
                f"Oracle 语句执行报错,第{line}个SQL:{sqlitem.statement},错误信息{traceback.format_exc()}"
            )
            check_result.error = str(e)
        finally:
            if close_conn:
                self.close()
        return check_result