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