github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/lightning/glue/glue.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package glue
    15  
    16  import (
    17  	"context"
    18  	"database/sql"
    19  	"errors"
    20  
    21  	"github.com/pingcap/parser"
    22  	"github.com/pingcap/parser/ast"
    23  	"github.com/pingcap/parser/model"
    24  	"github.com/pingcap/parser/mysql"
    25  	"github.com/pingcap/tidb/types"
    26  	"github.com/pingcap/tidb/util/sqlexec"
    27  
    28  	"github.com/pingcap/br/pkg/lightning/checkpoints"
    29  	"github.com/pingcap/br/pkg/lightning/common"
    30  	"github.com/pingcap/br/pkg/lightning/config"
    31  	"github.com/pingcap/br/pkg/lightning/log"
    32  )
    33  
    34  type Glue interface {
    35  	OwnsSQLExecutor() bool
    36  	GetSQLExecutor() SQLExecutor
    37  	GetDB() (*sql.DB, error)
    38  	GetParser() *parser.Parser
    39  	GetTables(context.Context, string) ([]*model.TableInfo, error)
    40  	GetSession(context.Context) (checkpoints.Session, error)
    41  	OpenCheckpointsDB(context.Context, *config.Config) (checkpoints.DB, error)
    42  	// Record is used to report some information (key, value) to host TiDB, including progress, stage currently
    43  	Record(string, uint64)
    44  }
    45  
    46  type SQLExecutor interface {
    47  	// ExecuteWithLog and ObtainStringWithLog should support concurrently call and can't assure different calls goes to
    48  	// same underlying connection
    49  	ExecuteWithLog(ctx context.Context, query string, purpose string, logger log.Logger) error
    50  	ObtainStringWithLog(ctx context.Context, query string, purpose string, logger log.Logger) (string, error)
    51  	QueryStringsWithLog(ctx context.Context, query string, purpose string, logger log.Logger) ([][]string, error)
    52  	Close()
    53  }
    54  
    55  // sqlConnSession implement checkpoints.Session used only for lighting itself
    56  type sqlConnSession struct {
    57  	checkpoints.Session
    58  	conn *sql.Conn
    59  }
    60  
    61  func (session *sqlConnSession) Close() {
    62  	session.conn.Close()
    63  }
    64  
    65  func (session *sqlConnSession) Execute(ctx context.Context, sql string) ([]sqlexec.RecordSet, error) {
    66  	_, err := session.conn.ExecContext(ctx, sql)
    67  	return nil, err
    68  }
    69  
    70  func (session *sqlConnSession) CommitTxn(context.Context) error {
    71  	return errors.New("sqlConnSession doesn't have a valid CommitTxn implementation")
    72  }
    73  
    74  func (session *sqlConnSession) RollbackTxn(context.Context) {}
    75  
    76  func (session *sqlConnSession) PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) {
    77  	return 0, 0, nil, errors.New("sqlConnSession doesn't have a valid PrepareStmt implementation")
    78  }
    79  
    80  func (session *sqlConnSession) ExecutePreparedStmt(ctx context.Context, stmtID uint32, param []types.Datum) (sqlexec.RecordSet, error) {
    81  	return nil, errors.New("sqlConnSession doesn't have a valid ExecutePreparedStmt implementation")
    82  }
    83  
    84  func (session *sqlConnSession) DropPreparedStmt(stmtID uint32) error {
    85  	return errors.New("sqlConnSession doesn't have a valid DropPreparedStmt implementation")
    86  }
    87  
    88  type ExternalTiDBGlue struct {
    89  	db     *sql.DB
    90  	parser *parser.Parser
    91  }
    92  
    93  func NewExternalTiDBGlue(db *sql.DB, sqlMode mysql.SQLMode) *ExternalTiDBGlue {
    94  	p := parser.New()
    95  	p.SetSQLMode(sqlMode)
    96  
    97  	return &ExternalTiDBGlue{db: db, parser: p}
    98  }
    99  
   100  func (e *ExternalTiDBGlue) GetSQLExecutor() SQLExecutor {
   101  	return e
   102  }
   103  
   104  func (e *ExternalTiDBGlue) ExecuteWithLog(ctx context.Context, query string, purpose string, logger log.Logger) error {
   105  	sql := common.SQLWithRetry{
   106  		DB:     e.db,
   107  		Logger: logger,
   108  	}
   109  	return sql.Exec(ctx, purpose, query)
   110  }
   111  
   112  func (e *ExternalTiDBGlue) ObtainStringWithLog(ctx context.Context, query string, purpose string, logger log.Logger) (string, error) {
   113  	var s string
   114  	err := common.SQLWithRetry{
   115  		DB:     e.db,
   116  		Logger: logger,
   117  	}.QueryRow(ctx, purpose, query, &s)
   118  	return s, err
   119  }
   120  
   121  func (e *ExternalTiDBGlue) QueryStringsWithLog(ctx context.Context, query string, purpose string, logger log.Logger) (result [][]string, finalErr error) {
   122  	finalErr = common.SQLWithRetry{
   123  		DB:     e.db,
   124  		Logger: logger,
   125  	}.Transact(ctx, purpose, func(c context.Context, tx *sql.Tx) (txErr error) {
   126  		rows, err := tx.QueryContext(c, query)
   127  		if err != nil {
   128  			return err
   129  		}
   130  		defer rows.Close()
   131  
   132  		colNames, err := rows.Columns()
   133  		if err != nil {
   134  			return err
   135  		}
   136  		for rows.Next() {
   137  			row := make([]string, len(colNames))
   138  			refs := make([]interface{}, 0, len(row))
   139  			for i := range row {
   140  				refs = append(refs, &row[i])
   141  			}
   142  			if err := rows.Scan(refs...); err != nil {
   143  				return err
   144  			}
   145  			result = append(result, row)
   146  		}
   147  
   148  		return rows.Err()
   149  	})
   150  	return
   151  }
   152  
   153  func (e *ExternalTiDBGlue) GetDB() (*sql.DB, error) {
   154  	return e.db, nil
   155  }
   156  
   157  func (e *ExternalTiDBGlue) GetParser() *parser.Parser {
   158  	return e.parser
   159  }
   160  
   161  func (e ExternalTiDBGlue) GetTables(context.Context, string) ([]*model.TableInfo, error) {
   162  	return nil, errors.New("ExternalTiDBGlue doesn't have a valid GetTables function")
   163  }
   164  
   165  func (e *ExternalTiDBGlue) GetSession(ctx context.Context) (checkpoints.Session, error) {
   166  	conn, err := e.db.Conn(ctx)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	return &sqlConnSession{conn: conn}, nil
   171  }
   172  
   173  func (e *ExternalTiDBGlue) OpenCheckpointsDB(ctx context.Context, cfg *config.Config) (checkpoints.DB, error) {
   174  	return checkpoints.OpenCheckpointsDB(ctx, cfg)
   175  }
   176  
   177  func (e *ExternalTiDBGlue) OwnsSQLExecutor() bool {
   178  	return true
   179  }
   180  
   181  func (e *ExternalTiDBGlue) Close() {
   182  	e.db.Close()
   183  }
   184  
   185  func (e *ExternalTiDBGlue) Record(string, uint64) {
   186  }
   187  
   188  const (
   189  	RecordEstimatedChunk = "EstimatedChunk"
   190  	RecordFinishedChunk  = "FinishedChunk"
   191  )