From dfe6fba5027dec4db145242d1047bb3f6b399037 Mon Sep 17 00:00:00 2001 From: zhangliang032 Date: Wed, 23 Feb 2022 16:47:13 +0800 Subject: [PATCH] rewrite support regexp replace add reg2select, for oracle, sql server and other dbms --- Makefile | 18 --------- ast/rewrite.go | 47 ++++++++++++++++++++++++ ast/rewrite_test.go | 25 +++++++++++++ ast/testdata/TestListRewriteRules.golden | 20 ++++++++++ deps.sh | 2 +- 5 files changed, 93 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index dcd1e6f..ad46c8d 100644 --- a/Makefile +++ b/Makefile @@ -143,24 +143,6 @@ heuristic: doc go test github.com/XiaoMi/soar/advisor -v -update -run TestMergeConflictHeuristicRules docker stop soar-mysql 2>/dev/null || true -# Update vitess vendor -.PHONY: vitess -vitess: - @echo "$(CGREEN)Update vitess deps ...$(CEND)" - govendor fetch -v vitess.io/vitess/... - -# Update tidb vendor -.PHONY: tidb -tidb: - @echo "$(CGREEN)Update tidb deps ...$(CEND)" - govendor fetch -v github.com/pingcap/tidb/... - -# make pingcap parser -.PHONY: pingcap-parser -pingcap-parser: tidb - @echo "$(CGREEN)Update pingcap parser deps ...$(CEND)" - govendor fetch -v github.com/pingcap/parser/... - # Update all vendor .PHONY: vendor vendor: vitess pingcap-parser diff --git a/ast/rewrite.go b/ast/rewrite.go index 779b20e..f1c49e7 100644 --- a/ast/rewrite.go +++ b/ast/rewrite.go @@ -52,6 +52,13 @@ func init() { Suggest: "select * from film where length > 100", Func: (*Rewrite).RewriteDML2Select, }, + { + Name: "reg2select", + Description: "使用正则的方式将数据库更新请求转换为只读查询请求,便于执行EXPLAIN", + Original: "DELETE FROM film WHERE length > 100", + Suggest: "select * from film where length > 100", + Func: (*Rewrite).RewriteReg2Select, + }, { Name: "star2columns", Description: "为SELECT *补全表的列信息", @@ -1619,6 +1626,24 @@ func (rw *Rewrite) RewriteDML2Select() *Rewrite { return rw } +func (rw *Rewrite) RewriteReg2Select() *Rewrite { + var pre = 9 + if len(rw.SQL) < pre { + // SQL to short no need convert + return rw + } + if strings.HasPrefix(strings.ToLower(rw.SQL[:pre]), "select") { + rw.NewSQL = rw.SQL + } + if strings.HasPrefix(strings.ToLower(rw.SQL[:pre]), "update") { + rw.NewSQL = regUpdate2Select(rw.SQL) + } + if strings.HasPrefix(strings.ToLower(rw.SQL[:pre]), "delete") { + rw.NewSQL = regDelete2Select(rw.SQL) + } + return rw +} + // delete2Select 将 Delete 语句改写成 Select func delete2Select(stmt *sqlparser.Delete) string { newSQL := &sqlparser.Select{ @@ -1632,6 +1657,17 @@ func delete2Select(stmt *sqlparser.Delete) string { return sqlparser.String(newSQL) } +// regDelete2Select convert delete to select by regexp +func regDelete2Select(sql string) string { + sql = strings.TrimSpace(sql) + sqlRegexp := regexp.MustCompile(`^(?i)delete\s+from\s+(.*)$`) + params := sqlRegexp.FindStringSubmatch(sql) + if len(params) > 1 { + return fmt.Sprintf(`select * from %s`, params[1]) + } + return sql +} + // update2Select 将 Update 语句改写成 Select func update2Select(stmt *sqlparser.Update) string { newSQL := &sqlparser.Select{ @@ -1646,6 +1682,17 @@ func update2Select(stmt *sqlparser.Update) string { return sqlparser.String(newSQL) } +// regUpdate2Select convert update to select by regexp +func regUpdate2Select(sql string) string { + sql = strings.TrimSpace(sql) + sqlRegexp := regexp.MustCompile(`^(?i)update\s+(.*)\s+set\s+(.*)\s+(where\s+.*)$`) + params := sqlRegexp.FindStringSubmatch(sql) + if len(params) > 2 { + return fmt.Sprintf(`select * from %s %s`, params[1], params[3]) + } + return sql +} + // insert2Select 将 Insert 语句改写成 Select func insert2Select(stmt *sqlparser.Insert) string { switch row := stmt.Rows.(type) { diff --git a/ast/rewrite_test.go b/ast/rewrite_test.go index 0e742df..cf072fb 100644 --- a/ast/rewrite_test.go +++ b/ast/rewrite_test.go @@ -587,6 +587,31 @@ func TestRewriteDML2Select(t *testing.T) { common.Log.Debug("Exiting function: %s", common.GetFunctionName()) } +func TestRewriteReg2Select(t *testing.T) { + common.Log.Debug("Entering function: %s", common.GetFunctionName()) + testSQL := []map[string]string{ + { + "input": "select 1 from dual", + "output": "select 1 from dual", + }, + { + "input": "delete from dual", + "output": "select * from dual", + }, + { + "input": "update tb set col = 1 where col = 2", + "output": "select * from tb where col = 2", + }, + } + for _, sql := range testSQL { + rw := NewRewrite(sql["input"]).RewriteReg2Select() + if rw.NewSQL != sql["output"] { + t.Errorf("want: %s\ngot: %s", sql["output"], rw.NewSQL) + } + } + common.Log.Debug("Exiting function: %s", common.GetFunctionName()) +} + func TestRewriteDistinctStar(t *testing.T) { common.Log.Debug("Entering function: %s", common.GetFunctionName()) testSQL := []map[string]string{ diff --git a/ast/testdata/TestListRewriteRules.golden b/ast/testdata/TestListRewriteRules.golden index e855f91..0ac7cdc 100644 --- a/ast/testdata/TestListRewriteRules.golden +++ b/ast/testdata/TestListRewriteRules.golden @@ -13,6 +13,20 @@ DELETE FROM film WHERE length > 100 * **Suggest**: +```sql +select * from film where length > 100 +``` +## reg2select +* **Description**:使用正则的方式将数据库更新请求转换为只读查询请求,便于执行EXPLAIN + +* **Original**: + +```sql +DELETE FROM film WHERE length > 100 +``` + +* **Suggest**: + ```sql select * from film where length > 100 ``` @@ -277,6 +291,12 @@ use sakila; "Original": "DELETE FROM film WHERE length \u003e 100", "Suggest": "select * from film where length \u003e 100" }, + { + "Name": "reg2select", + "Description": "使用正则的方式将数据库更新请求转换为只读查询请求,便于执行EXPLAIN", + "Original": "DELETE FROM film WHERE length \u003e 100", + "Suggest": "select * from film where length \u003e 100" + }, { "Name": "star2columns", "Description": "为SELECT *补全表的列信息", diff --git a/deps.sh b/deps.sh index 21a3e94..6303580 100755 --- a/deps.sh +++ b/deps.sh @@ -1,6 +1,6 @@ #!/bin/bash -NEEDED_COMMANDS="docker git go govendor retool bats" +NEEDED_COMMANDS="docker git go retool bats" for cmd in ${NEEDED_COMMANDS} ; do if ! command -v "${cmd}" &> /dev/null ; then -- GitLab