github.com/matrixorigin/matrixone@v0.7.0/pkg/tests/txn/sql_client.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     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  
    15  package txn
    16  
    17  import (
    18  	"context"
    19  	"database/sql"
    20  	"fmt"
    21  	"sync"
    22  
    23  	_ "github.com/go-sql-driver/mysql"
    24  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    25  	"github.com/matrixorigin/matrixone/pkg/tests/service"
    26  	"github.com/matrixorigin/matrixone/pkg/txn/client"
    27  	"go.uber.org/multierr"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  var (
    32  	createDB  = `create database if not exists kv_test`
    33  	useDB     = `use kv_test;`
    34  	createSql = `create table if not exists txn_test_kv (kv_key varchar(20) primary key, kv_value varchar(10))`
    35  )
    36  
    37  // sqlClient use sql client to connect to CN node and use a table to simulate rr test KV operations
    38  type sqlClient struct {
    39  	cn service.CNService
    40  }
    41  
    42  func newSQLClient(logger *zap.Logger, env service.Cluster) (Client, error) {
    43  	cn, err := env.GetCNServiceIndexed(0)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	db, err := sql.Open("mysql", fmt.Sprintf("dump:111@tcp(%s)/", cn.SQLAddress()))
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	_, err = db.Exec(createDB)
    54  	if err != nil {
    55  		return nil, multierr.Append(err, db.Close())
    56  	}
    57  
    58  	_, err = db.Exec(useDB)
    59  	if err != nil {
    60  		return nil, multierr.Append(err, db.Close())
    61  	}
    62  
    63  	_, err = db.Exec(createSql)
    64  	if err != nil {
    65  		return nil, multierr.Append(err, db.Close())
    66  	}
    67  
    68  	return &sqlClient{
    69  		cn: cn,
    70  	}, multierr.Append(err, db.Close())
    71  }
    72  
    73  func (c *sqlClient) NewTxn(options ...client.TxnOption) (Txn, error) {
    74  	return newSQLTxn(c.cn)
    75  }
    76  
    77  type sqlTxn struct {
    78  	db  *sql.DB
    79  	txn *sql.Tx
    80  
    81  	mu struct {
    82  		sync.Mutex
    83  		closed bool
    84  	}
    85  }
    86  
    87  func newSQLTxn(cn service.CNService) (Txn, error) {
    88  	db, err := sql.Open("mysql", fmt.Sprintf("dump:111@tcp(%s)/kv_test", cn.SQLAddress()))
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	txn, err := db.Begin()
    94  	if err != nil {
    95  		return nil, multierr.Append(err, db.Close())
    96  	}
    97  	return &sqlTxn{
    98  		db:  db,
    99  		txn: txn,
   100  	}, nil
   101  }
   102  
   103  func (kop *sqlTxn) Commit() error {
   104  	kop.mu.Lock()
   105  	defer kop.mu.Unlock()
   106  	if kop.mu.closed {
   107  		return moerr.NewTxnClosed(context.Background(), nil)
   108  	}
   109  
   110  	kop.mu.closed = true
   111  	err := kop.txn.Commit()
   112  	if err != nil {
   113  		return multierr.Append(err, kop.db.Close())
   114  	}
   115  	return kop.db.Close()
   116  }
   117  
   118  func (kop *sqlTxn) Rollback() error {
   119  	kop.mu.Lock()
   120  	defer kop.mu.Unlock()
   121  	if kop.mu.closed {
   122  		return nil
   123  	}
   124  
   125  	kop.mu.closed = true
   126  	err := kop.txn.Rollback()
   127  	if err != nil {
   128  		return multierr.Append(err, kop.db.Close())
   129  	}
   130  	return kop.db.Close()
   131  }
   132  
   133  func (kop *sqlTxn) Read(key string) (string, error) {
   134  	rows, err := kop.txn.Query(fmt.Sprintf("select kv_value from txn_test_kv where kv_key = '%s'", key))
   135  	if err != nil {
   136  		return "", err
   137  	}
   138  
   139  	if !rows.Next() {
   140  		return "", rows.Close()
   141  	}
   142  	v := ""
   143  	if err := rows.Scan(&v); err != nil {
   144  		return "", multierr.Append(err, rows.Close())
   145  	}
   146  	return v, multierr.Append(err, rows.Close())
   147  }
   148  
   149  func (kop *sqlTxn) Write(key, value string) error {
   150  	v, err := kop.Read(key)
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	if v == "" {
   156  		return kop.insert(key, value)
   157  	}
   158  	return kop.update(key, value)
   159  }
   160  
   161  func (kop *sqlTxn) ExecSQL(sql string) (sql.Result, error) {
   162  	return kop.txn.Exec(sql)
   163  }
   164  
   165  func (kop *sqlTxn) ExecSQLQuery(sql string) (*sql.Rows, error) {
   166  	return kop.txn.Query(sql)
   167  }
   168  
   169  func (kop *sqlTxn) insert(key, value string) error {
   170  	res, err := kop.txn.Exec(fmt.Sprintf("insert into txn_test_kv(kv_key, kv_value) values('%s', '%s')", key, value))
   171  	if err != nil {
   172  		return err
   173  	}
   174  	n, err := res.RowsAffected()
   175  	if err != nil {
   176  		panic(err)
   177  	}
   178  	if n != 1 {
   179  		panic(n)
   180  	}
   181  	return err
   182  }
   183  
   184  func (kop *sqlTxn) update(key, value string) error {
   185  	_, err := kop.txn.Exec(fmt.Sprintf("update txn_test_kv set kv_value = '%s' where kv_key = '%s'", value, key))
   186  	return err
   187  }