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 )