暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

openGauss新特性 | openGauss 7.0.0 RC1 用于词法语法分析的SQL审计工具libog_query实践

Gauss松鼠会 2025-05-13
126

一、SQL审计工具简介

通常使用数据库的商业公司对使用关键数据库执行SQL具有以下的管理流程:开发人员在业务平台上选择数据库,提交SQL,DBA收到工单以后进行审核,通过后在数据库中实际执行。这样的流程对于DBA审核工作量大,容易发生因人为疏忽的问题。借助SQL审计工具,DBA可以实现在数据库上SQL审核的自动化流程,提高DBA的工作效率,减少人为错误,同时利用工具记录日志也有助于后续的问题回溯定位和分析。

审计工具通常有在线审核和离线审核两种类型。在线审核会在线上机器实际部署运行一个数据库实例,sql提交给审计工具,由工具发送给线上机审核,并返回审记结果,通过审计的SQL语句可以提交给部署业务的数据库执行,特点是审核的准确率高,但对线上资源的要求也较高。离线审核则是本地模拟一个数据库的语法解析器,无需连接到数据库,直接调用工具提供的接口解析审核SQL语句,特点是资源消耗少,但由于只做语法层的解析,准确率相对没有在线审核高。

二、openGauss目前支持的审计工具

- 自openGauss 7.0.0-RC1版本 SQL语法审计工具libog_query支持通过第三方软件集成提供的so和接口,对SQL兼容性进行离线语法分析。

- 自openGauss 5.0.0-LTS版本 语法兼容性评估工具支持利用已有的openGauss节点在线评估数据SQL文本在openGauss的兼容性,包括语法兼容和完全兼容分析。

三、libog_query使用介绍

libog_query工具支持离线审计分析SQL语句在openGauss中的语法合法性,对外提供so和接口供第三方软件集成。目前支持对B兼容性数据库的语法进行审计,也可以自行编译得到A兼容性数据库的so库文件。

- 工具原理

libog_query利用ruby的FFI::Clang(FFI::Clang是libclang在ruby中的包装实现)分析openGauss语法解析器 raw_parser 相关的源文件得到函数声明、变量定义、宏定义等语言级别的信息,并通过分析这些信息的依赖关系来输出源文件中与语法解析器相关的函数和变量符号。之后通过编译这些符号得到一个动态库so文件,并通过对外提供so文件的函数接口,实现第三方软件集成so后调用接口对SQL语句进行词法语法分析,从而实现审计SQL语句在openGauss中的语法合法性。

- 工具使用

1. 确保从openGauss二进制分发包中得到libog_query.so库文件。

    openGauss打包二进制路径
    ├── bin
    ├── lib
    │   └── postgresql
    │       └── ***libog_query.so***
    └── share...

    2. 确保LD_LIBRARY_PATH中包含了openGauss安装的二进制lib库路径(依赖libcjson、libsecurec和libstdc++三个库)

    3. 编写python脚本调用库文件,使用`raw_parser_opengauss_dolphin`接口审计SQL语句,其中接口C函数原型定义如下:

      OgQueryParseResult raw_parser_opengauss_dolphin(const char* str);

      (1) 参数str:sql文本

      (2) 返回值:OgQueryParseResult 类型,有parse_tree_json、err_msg和 is_passed三个成员变量

      • parse_tree_json:字符串,表示简化语法树信息的JSON字符串(当前主要提取语句类型、字段类型、函数、表名、主键、索引这六类信息)

      • err_msg:字符串,表示对SQL进行词法语法分析时的报错信息

      • is_passed:布尔值,表示是否通过语法规则检查

      代码示例:

        python
        # coding:utf-8
        import ctypes
        # 定义返回数据结构
        class OgQueryParseResult(ctypes.Structure):
            _fields_ = [
                ("parse_tree_json", ctypes.c_char_p),
                ("err_msg", ctypes.c_char_p),
                ("is_passed", ctypes.c_bool)
            ]
        try:
            # 使用ctypes加载工具提供的so文件,并声明函数接口
            libpg_query = ctypes.CDLL('./libog_query.so')
            libpg_query.raw_parser_opengauss_dolphin.restype = OgQueryParseResult
            libpg_query.raw_parser_opengauss_dolphin.argtypes = [ctypes.c_char_p]
            sql = b"""
            create table t3(a pg_catalog.int4, b pg_catalog.int5 default 10, c varchar2(255) not null, d date, e varchar3(3), primary key(a,b));
            alter table t3 modify id varchar(20), alter column sdate type timestamptz(3);
            """
            # 调用接口对SQL语句进行审计
            result = libpg_query.raw_parser_opengauss_dolphin(sql)
            # 查看返回结果
            print(sql)
            print(result.parse_tree_json)
            print(result.err_msg)
            print(result.is_passed)
        except Exception as e:
            print("Exception", e)

        此时回显信息为:

          shell
              create table t3(a pg_catalog.int4b pg_catalog.int5 default 10, c varchar2(255) not null, d date, e varchar3(3), primary key(a,b));
              alter table t3 modify id varchar(20), alter column sdate type timestamptz(3);
          {
          "version": "7.0.0 RC1",
          "stmts": [{
          "stmtType": "create",
          "stmts": [],
          "fields": [{
          "fieldName": "a",
          "fieldType": "int4"
          }, {
          "fieldName": "b",
          "fieldType": "int5"
          }, {
          "fieldName": "c",
          "fieldType": "varchar"
          }, {
          "fieldName": "d",
          "fieldType": "date"
          }, {
          "fieldName": "e",
          "fieldType": "varchar3"
          }],
          "constraints": [{
          "contype": "DEFAULT",
          "keys": ["b"]
          }, {
          "contype": "NOTNULL",
          "keys": ["c"]
          }, {
          "contype": "PRIMARY_KEY",
          "keys": ["a""b"]
          }]
          }, {
          "stmtType": "alter table",
          "stmts": [],
          "fields": [{
          "fieldName": "id",
          "fieldType": "varchar"
          }, {
          "fieldName": "sdate",
          "fieldType": "timestamptz"
          }],
          "relations": [{
          "relName": "t3"
          }]
          }]
          }
          None # 编译没有词法语法错误,返回None
          True # 表示当前SQL语句通过语法审计

          4. 说明:

          (1) 返回的json字符串根据实际情况主要会包含以下字段,每个字段引用的也是一个json对象:

            stmtType:语句类型
            stmts:嵌套语句,同一层的嵌套语句会放在同一个stmts数组中
            relations:表名
            fields:字段,包含了字段名fieldName和字段类型fieldType
            constraints:主键约束
            funscs:函数名
            exprs:表达式名
            objects:删除/注释语句的对象名

            (2) 语句类型:即StmtType的取值范围,当前支持以下dml和ddl语句类型:

            • DML:insert、delete、update、merge、select

            • DDL:create [table]、create type、create index、create sequence、view、alter table、rename、drop、truncate、prepare、comment

            (3) 工具限制

            • 仅支持单行SQL文本输入,且SQL之间以`;`分割。

            • 支持A库和B库兼容性语法检查场景,默认为B库兼容性,A库需要单独编译得到一个独立的so文件,其他兼容性库的语法不兼容语句的报错信息可能不准确。

            • 不支持存储过程的语法兼容校验。

            • 仅支持审计在 openGauss 的语法解析层的错误。属于语义分析层或执行层等阶段处理的报错,本工具不支持审计:如字段类型不支持、函数不存在、表不存在,枚举类型值的匹配,Insert语句的目标列和值表达式的匹配等场景属于语义层,工具不会做检查。可以通过解析json提取字段或函数后在第三方软件中通过匹配进行审计。

            5. 重新编译工具

            可以参考代码仓库中的文档配置环境依赖后使用提供的ruby脚本重新编译生成so库文件。[src/bin/libog_query · openGauss/openGauss-server - 码云 - 开源中国 (gitee.com)](https://gitee.com/opengauss/openGauss-server/tree/master/src/bin/libog_query)

            四、总结

            libog_query工具可以离线审计分析SQL语句在openGauss中的语法合法性,第三方软件集成该工具so后,可利用提供的接口离线进行SQL兼容性语法分析,无需连接到openGauss,从而可以实现openGauss的SQL语法审计的自动化流程,提高SQL语句的审计效率,同时利用工具记录信息日志也有助于后续的问题回溯定位和分析。


            END



            Gauss松鼠会
            汇集数据库从业人员及爱好者
            互助解决问题 共建数据库技术交流圈


            文章转载自Gauss松鼠会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论