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  }