作者:孫健
孫健,愛可生研發工程師,負責 SQLE 相關開發;
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
前言
近些年來,資料庫產業發展迅猛,各種新興資料庫如雨後春筍般出現,各個公司的技術棧也不再侷限於某一種資料庫。對於SQL質量管理平臺來說僅支援某一個型別的資料庫(例如MySQL),那麼是會有一定的侷限性,SQLE在設計之初考慮支援多種資料庫,因此產品設計時,將稽核流程(業務)的程式碼和具體SQL稽核上線的程式碼進行分離,SQL稽核上線通過外掛的形式實現。SQLE對外提供外掛開發所需的介面和庫,可以快速建立開啟一個稽核外掛,無需升級軟體,匯入稽核外掛即可獲對應資料庫型別的稽核上線能力,使用平臺所有功能。
外掛的開發參考文件:https://actiontech.github.io/... ,
外掛的使用參考文件:https://actiontech.github.io/...。
本文將演示如何從零開始建立一個簡單可用的稽核外掛,作為案例。
目標
首先將建立一個 Postgres 資料庫稽核外掛,並新增兩條規則,“禁止使用 SELECT *”和“建立的表欄位過多”,並在開發過程中結合SQLE對Postgres資料庫進行SQL稽核上線工單的測試演示。以下過程中的演示程式碼可從此處https://github.com/actiontech...下載。
實操
提示:SQLE和外掛為GO語言開發,如果要進行外掛開發,需要對GO有一丟丟了解即可。
1. 建立外掛專案
首先使用go mod初始化一個go專案,然後
mkdir sqle-pg-plugin
cd sqle-pg-plugin
touch main.go
go mod init sqle-pg-plugin # 初始化go mod
export GOPROXY=goproxy.cn,goproxy.io,direct # 設定 GoProxy,解決SQLE庫下載問題,通過IDEA開發的可以在IDEA軟體上設定;
go get github.com/actiontech/sqle@v1.2204.0 # 此版本為該文章編輯時的最新版本。
2.編寫最小化外掛程式碼
在專案main.go檔案內編寫如下程式碼,即可最快的新增一個Postgres資料庫稽核外掛,此時外掛沒有稽核規則。
package main
import (
adaptor "github.com/actiontech/sqle/sqle/pkg/driver"
)
func main() {
plugin := adaptor.NewAdaptor(&adaptor.PostgresDialector{})
plugin.Serve()
}
使用‘go build‘編譯後得到二進位制檔案 sqle-pg-plugin,按前言中的外掛的使用參考文件,我們部署到SQLE服務裡。可以正常新增資料來源,如下圖所示:
此時正常進行SQL稽核上線工單建立並上線,如下圖所示:
3.給外掛新增一條規則
在剛剛程式碼的基礎上,我們在main函式內新增如下程式碼來新增一條規則“禁止使用 SELECT *”,完整程式碼如下所示。
package main
import (
"context"
"strings"
"github.com/actiontech/sqle/sqle/driver"
adaptor "github.com/actiontech/sqle/sqle/pkg/driver"
)
func main() {
plugin := adaptor.NewAdaptor(&adaptor.PostgresDialector{})
rule1 := &driver.Rule{
Name: "pg_rule_1", // 規則ID,該值會與外掛型別一起作為這條規則在 SQLE 的唯一標識
Desc: "禁止使用 SELECT *", // 規則描述
Category: "DQL規範", // 規則分類,用於分組,相同型別的規則會在 SQLE 的頁面上展示在一起
Level: driver.RuleLevelError, // 規則等級,表示該規則的嚴重程度
}
//
rule1Handler := func(ctx context.Context, rule *driver.Rule, sql string) (string, error) {
if strings.Contains(sql, "select *") {
return rule.Desc, nil
}
return "", nil
}
plugin.AddRule(rule1, rule1Handler)
plugin.Serve()
}
我們按之前的方式編譯外掛二進位制檔案,並部署到SQLE server內,可以看到新增了一條規則,如下圖所示:
此時我們提交一個工單驗證一下,可以看到觸發了我們剛新增的規則
4.給外掛新增一條可配置的複雜規則
基於上面的程式碼,我們再新增一條規則“建立的表欄位過多”,具備如下特性:
- 上面新增的規則基於字串匹配進行的,準確性不高,無法匹配到不同的書寫格式,比如大小寫,換行等。因此我們在這裡會基於SQL解析器開發一條規則,測試使用的解析庫 https://github.com/pganalyze/...;
- 為了增加規則的適用性,我們準備給規則加一個動態配置給使用者提供可選項。
程式碼如下:
package main
import (
"context"
"fmt"
"strings"
"github.com/actiontech/sqle/sqle/driver"
adaptor "github.com/actiontech/sqle/sqle/pkg/driver"
"github.com/actiontech/sqle/sqle/pkg/params"
parser "github.com/pganalyze/pg_query_go/v2"
)
func main() {
plugin := adaptor.NewAdaptor(&adaptor.PostgresDialector{})
rule1 := &driver.Rule{
Name: "pg_rule_1", // 規則ID,該值會與外掛型別一起作為這條規則在 SQLE 的唯一標識
Desc: "避免查詢所有的列", // 規則描述
Category: "DQL規範", // 規則分類,用於分組,相同型別的規則會在 SQLE 的頁面上展示在一起
Level: driver.RuleLevelError, // 規則等級,表示該規則的嚴重程度
}
rule1Handler := func(ctx context.Context, rule *driver.Rule, sql string) (string, error) {
if strings.Contains(sql, "select *") {
return rule.Desc, nil
}
return "", nil
}
// 定義第二條規則
rule2 := &driver.Rule{
Name: "pg_rule_2",
Desc: "表欄位不建議過多",
Level: driver.RuleLevelWarn,
Category: "DDL規範",
Params: []*params.Param{ // 自定義引數列表
¶ms.Param{
Key: "max_column_count", // 自定義引數的ID
Value: "50", // 自定義引數的預設值
Desc: "最大欄位個數", // 自定義引數在頁面上的描述
Type: params.ParamTypeInt, // 自定義引數的值型別
},
},
}
// 這時處理函式的引數是 interface{} 型別,需要將其斷言成 AST 語法樹。
rule2Handler := func(ctx context.Context, rule *driver.Rule, ast interface{}) (string, error) {
node, ok := ast.(*parser.RawStmt)
if !ok {
return "", nil
}
switch stmt := node.GetStmt().GetNode().(type) {
case *parser.Node_CreateStmt:
columnCounter := 0
for _, elt := range stmt.CreateStmt.TableElts {
switch elt.GetNode().(type) {
case *parser.Node_ColumnDef:
columnCounter++
}
}
// 讀取 SQLE 傳遞過來的該引數配置的值
count := rule.Params.GetParam("max_column_count").Int()
if count > 0 && columnCounter > count {
return fmt.Sprintf("表欄位不建議超過%d個,目前有%d個", count, columnCounter), nil
}
}
return "", nil
}
plugin.AddRule(rule1, rule1Handler)
plugin.AddRuleWithSQLParser(rule2, rule2Handler)
// 需要將 SQL 解析的方法註冊到外掛中。
plugin.Serve(adaptor.WithSQLParser(func(sql string) (ast interface{}, err error) {
// parser.Parse 使用 PostgreSQL 的解析器,將 sql 解析成 AST 語法樹。
result, err := parser.Parse(sql)
if err != nil {
return nil, fmt.Errorf("parse sql error")
}
if len(result.Stmts) != 1 {
return nil, fmt.Errorf("unexpected statement count: %d", len(result.Stmts))
}
// 將 SQL 的語法樹返回。
return result.Stmts[0], nil
}))
plugin.Serve()
}
開啟SQLE規則介面,可以看到該規則已經新增到SQLE了,如圖:
我們將規則模板內該規則的值調小點然後進行測試一下
首先我們提交一條超過5個欄位的建表語句,此時SQLE會觸發該規則並給出預期的提示資訊,如下圖所示:
然後我們提交一條不超過5個欄位的建表語句,此時SQLE不會觸發該規則,如下圖所示:
總結
通過上面的演示,大概介紹了SQLE資料庫稽核外掛的簡單開發測試過程。大家可根據類似步驟開發出一套符合自己公司需求的規則集,結合SQLE平臺來滿足日常使用。我們也提供了一些常見資料庫的稽核外掛,大家也可以在此基礎上進行開發,參考文件:https://actiontech.github.io/...