github.com/XiaoMi/Gaea@v1.2.5/proxy/server/executor_test.go (about) 1 // Copyright 2019 The Gaea Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package server 15 16 import ( 17 "context" 18 "encoding/json" 19 "fmt" 20 "testing" 21 22 "github.com/XiaoMi/Gaea/backend" 23 "github.com/XiaoMi/Gaea/backend/mocks" 24 "github.com/XiaoMi/Gaea/log" 25 "github.com/XiaoMi/Gaea/models" 26 "github.com/XiaoMi/Gaea/mysql" 27 "github.com/XiaoMi/Gaea/parser" 28 "github.com/XiaoMi/Gaea/parser/ast" 29 "github.com/XiaoMi/Gaea/util" 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/assert" 32 "gopkg.in/ini.v1" 33 ) 34 35 func TestGetVariableExprResult(t *testing.T) { 36 tests := []struct { 37 variable []string 38 expect string 39 }{ 40 {[]string{"ON", "on", "'on'", "`on`"}, "on"}, 41 {[]string{"OFF", "off", "'off'", "`off`"}, "off"}, 42 {[]string{"1", "'1'", "`1`"}, "1"}, 43 {[]string{"0", "'0'", "`0`"}, "0"}, 44 } 45 for _, test := range tests { 46 t.Run(test.expect, func(t *testing.T) { 47 for _, v := range test.variable { 48 sql := fmt.Sprintf("set autocommit = %s", v) 49 s, err := parser.ParseSQL(sql) 50 if err != nil { 51 t.Fatal(err) 52 } 53 stmt := s.(*ast.SetStmt) 54 for _, v := range stmt.Variables { 55 actual := getVariableExprResult(v.Value) 56 if actual != test.expect { 57 t.Errorf("not equal, expect: %v, actual: %v", test.expect, actual) 58 } 59 } 60 } 61 }) 62 } 63 } 64 65 func TestExecute(t *testing.T) { 66 se, err := prepareSessionExecutor() 67 if err != nil { 68 t.Fatal("prepare session executer error:", err) 69 return 70 } 71 mockCtrl := gomock.NewController(t) 72 defer mockCtrl.Finish() 73 74 slice0MasterPool := new(mocks.ConnectionPool) 75 slice0SlavePool := new(mocks.ConnectionPool) 76 slice1MasterPool := new(mocks.ConnectionPool) 77 slice1SlavePool := new(mocks.ConnectionPool) 78 se.manager.GetNamespace("test_executor_namespace").slices["slice-0"].Master = slice0MasterPool 79 se.manager.GetNamespace("test_executor_namespace").slices["slice-0"].Slave = []backend.ConnectionPool{slice0SlavePool} 80 se.manager.GetNamespace("test_executor_namespace").slices["slice-1"].Master = slice1MasterPool 81 se.manager.GetNamespace("test_executor_namespace").slices["slice-1"].Slave = []backend.ConnectionPool{slice1SlavePool} 82 83 expectResult1 := &mysql.Result{} 84 expectResult2 := &mysql.Result{} 85 //slice-0 86 ctx := context.Background() 87 slice0MasterConn := new(mocks.PooledConnect) 88 slice0MasterConn.On("GetConnectionID").Return(int64(1)) 89 slice0MasterPool.On("Get", ctx).Return(slice0MasterConn, nil).Once() 90 slice0MasterConn.On("UseDB", "db_mycat_0").Return(nil) 91 slice0MasterConn.On("SetCharset", "utf8", mysql.CharsetIds["utf8"]).Return(false, nil) 92 slice0MasterConn.On("SetSessionVariables", mysql.NewSessionVariables()).Return(false, nil) 93 slice0MasterConn.On("GetAddr").Return("127.0.0.1:3306") 94 slice0MasterConn.On("Execute", "SELECT * FROM `tbl_mycat` WHERE `k`=0", defaultMaxSqlResultSize).Return(expectResult1, nil) 95 slice0MasterConn.On("Recycle").Return(nil) 96 97 //slice-1 98 slice1MasterConn := new(mocks.PooledConnect) 99 slice1MasterConn.On("GetConnectionID").Return(int64(2)) 100 slice1MasterPool.On("Get", ctx).Return(slice1MasterConn, nil).Once() 101 slice1MasterConn.On("UseDB", "db_mycat_2").Return(nil) 102 slice1MasterConn.On("SetCharset", "utf8", mysql.CharsetIds["utf8"]).Return(false, nil) 103 slice1MasterConn.On("SetSessionVariables", mysql.NewSessionVariables()).Return(false, nil) 104 slice1MasterConn.On("GetAddr").Return("127.0.0.1:3306") 105 slice1MasterConn.On("Execute", "SELECT * FROM `tbl_mycat` WHERE `k`=0", defaultMaxSqlResultSize).Return(expectResult2, nil) 106 slice1MasterConn.On("Recycle").Return(nil) 107 108 sqls := map[string]map[string][]string{ 109 "slice-0": { 110 "db_mycat_0": {"SELECT * FROM `tbl_mycat` WHERE `k`=0"}, 111 }, 112 "slice-1": { 113 "db_mycat_2": {"SELECT * FROM `tbl_mycat` WHERE `k`=0"}, 114 }, 115 } 116 117 ret := make([]*mysql.Result, 0) 118 ret = append(ret, expectResult1, expectResult2) 119 120 reqCtx := util.NewRequestContext() 121 reqCtx.Set(util.StmtType, parser.StmtInsert) 122 123 rs, err := se.ExecuteSQLs(reqCtx, sqls) 124 assert.Equal(t, nil, err) 125 assert.Equal(t, rs, ret) 126 } 127 128 func prepareSessionExecutor() (*SessionExecutor, error) { 129 var userName = "test_executor" 130 var namespaceName = "test_executor_namespace" 131 var database = "db_ks" 132 133 m, err := prepareNamespaceManager() 134 if err != nil { 135 return nil, err 136 } 137 executor := newSessionExecutor(m) 138 executor.user = userName 139 140 collationID := 33 // "utf8" 141 executor.SetCollationID(mysql.CollationID(collationID)) 142 executor.SetCharset("utf8") 143 // set database 144 executor.SetDatabase(database) 145 executor.namespace = namespaceName 146 return executor, nil 147 } 148 149 func prepareNamespaceManager() (*Manager, error) { 150 proxyCfg := ` 151 ; config type, etcd/file, you can test gaea with file type, you shoud use etcd in production 152 config_type=etcd 153 ;file config path, 具体配置放到file_config_path的namespace目录下,该下级目录为固定目录 154 file_config_path=./etc/file 155 ;coordinator addr 156 coordinator_addr=http://127.0.0.1:2379 157 ;远程配置(当前为etcd)根目录 158 ;将会废弃该配置项,通过cluster name识别root 159 coordinator_root=/gaea 160 ;etcd user config 161 username=root 162 password=root 163 ;environ 164 environ=local 165 ;service name 166 service_name=gaea_proxy 167 ;gaea_proxy cluster name 168 cluster_name=gaea 169 ;log config 170 log_path=./logs 171 log_level=Notice 172 log_filename=gaea 173 log_output=file 174 175 ;admin addr 176 admin_addr=0.0.0.0:13307 177 ; basic auth 178 admin_user=admin 179 admin_password=admin 180 181 ;proxy addr 182 proto_type=tcp4 183 proxy_addr=0.0.0.0:13306 184 proxy_charset=utf8 185 186 ;slow sql time, when execute time is higher than this, log it, unit: ms 187 slow_sql_time=100 188 189 ;close session after session timeout, unit: seconds 190 session_timeout=3600 191 192 ;stats conf 193 stats_enabled=true 194 195 ;encrypt key 196 encrypt_key=1234abcd5678efg* 197 ` 198 199 nsCfg := ` 200 { 201 "name": "test_executor_namespace", 202 "online": true, 203 "read_only": true, 204 "allowed_dbs": { 205 "db_ks": true, 206 "db_mycat": true 207 }, 208 "default_phy_dbs": { 209 "db_ks": "db_ks", 210 "db_mycat": "db_mycat_0" 211 }, 212 "slices": [ 213 { 214 "name": "slice-0", 215 "user_name": "root", 216 "password": "root", 217 "master": "127.0.0.1:3306", 218 "slave":[ 219 "127.0.0.1:3307" 220 ], 221 "capacity": 64, 222 "max_capacity": 128, 223 "idle_timeout": 3600 224 }, 225 { 226 "name": "slice-1", 227 "user_name": "root", 228 "password": "root", 229 "master": "127.0.0.1:13306", 230 "slave":[ 231 "127.0.0.1:13307" 232 ], 233 "capacity": 64, 234 "max_capacity": 128, 235 "idle_timeout": 3600 236 } 237 ], 238 "shard_rules": [ 239 { 240 "db": "db_ks", 241 "table": "tbl_ks", 242 "type": "mod", 243 "key": "id", 244 "locations": [ 245 2, 246 2 247 ], 248 "slices": [ 249 "slice-0", 250 "slice-1" 251 ] 252 } 253 ], 254 "users": [ 255 { 256 "user_name": "test_executor", 257 "password": "test_executor", 258 "namespace": "test_executor_namespace", 259 "rw_flag": 2, 260 "rw_split": 1 261 } 262 ], 263 "default_slice": "slice-0", 264 "max_sql_execute_time": 0 265 }` 266 267 //加载proxy配置 268 var proxy = &models.Proxy{} 269 cfg, err := ini.Load([]byte(proxyCfg)) 270 if err != nil { 271 return nil, err 272 } 273 if err = cfg.MapTo(proxy); err != nil { 274 return nil, err 275 } 276 277 //加载namespace配置 278 namespaceName := "test_executor_namespace" 279 namespaceConfig := &models.Namespace{} 280 if err := json.Unmarshal([]byte(nsCfg), namespaceConfig); err != nil { 281 return nil, err 282 } 283 284 m := NewManager() 285 // init statistics 286 statisticManager, err := CreateStatisticManager(proxy, m) 287 if err != nil { 288 log.Warn("init stats manager failed, %v", err) 289 return nil, err 290 } 291 m.statistics = statisticManager 292 293 // init namespace 294 current, _, _ := m.switchIndex.Get() 295 namespaceConfigs := map[string]*models.Namespace{namespaceName: namespaceConfig} 296 m.namespaces[current] = CreateNamespaceManager(namespaceConfigs) 297 user, err := CreateUserManager(namespaceConfigs) 298 if err != nil { 299 return nil, err 300 } 301 m.users[current] = user 302 return m, nil 303 }