|
6 | 6 | package mysql |
7 | 7 |
|
8 | 8 | import ( |
| 9 | + "context" |
9 | 10 | gosql "database/sql" |
| 11 | + "database/sql/driver" |
10 | 12 | "fmt" |
11 | 13 | "strings" |
12 | 14 | "sync" |
13 | 15 | "time" |
14 | 16 |
|
15 | 17 | "github.com/github/gh-ost/go/sql" |
16 | 18 |
|
| 19 | + gomysql "github.com/go-sql-driver/mysql" |
17 | 20 | "github.com/openark/golib/log" |
18 | 21 | "github.com/openark/golib/sqlutils" |
19 | 22 | ) |
@@ -48,14 +51,68 @@ func (rlg *ReplicationLagResult) HasLag() bool { |
48 | 51 | var knownDBs map[string]*gosql.DB = make(map[string]*gosql.DB) |
49 | 52 | var knownDBsMutex = &sync.Mutex{} |
50 | 53 |
|
| 54 | +// initConnector wraps a driver.Connector to run a fixed set of statements on |
| 55 | +// every newly established connection (e.g. setting the transaction isolation |
| 56 | +// level), which the DSN-param mechanism can't express portably. |
| 57 | +type initConnector struct { |
| 58 | + driver.Connector |
| 59 | + statements []string |
| 60 | +} |
| 61 | + |
| 62 | +func (c *initConnector) Connect(ctx context.Context) (driver.Conn, error) { |
| 63 | + conn, err := c.Connector.Connect(ctx) |
| 64 | + if err != nil { |
| 65 | + return nil, err |
| 66 | + } |
| 67 | + execer, ok := conn.(driver.ExecerContext) |
| 68 | + if !ok { |
| 69 | + conn.Close() |
| 70 | + return nil, fmt.Errorf("mysql: driver connection does not implement driver.ExecerContext") |
| 71 | + } |
| 72 | + for _, stmt := range c.statements { |
| 73 | + if _, err := execer.ExecContext(ctx, stmt, nil); err != nil { |
| 74 | + conn.Close() |
| 75 | + return nil, err |
| 76 | + } |
| 77 | + } |
| 78 | + return conn, nil |
| 79 | +} |
| 80 | + |
| 81 | +// OpenDB opens a MySQL connection pool for the given DSN. A transaction_isolation |
| 82 | +// param is applied via the SQL-standard "SET SESSION TRANSACTION ISOLATION LEVEL" |
| 83 | +// statement on each new connection rather than being passed to the driver as a |
| 84 | +// system variable: |
| 85 | +// - "transaction_isolation" doesn't exist on MariaDB < 11.1, while |
| 86 | +// - "tx_isolation" doesn't exist on MySQL 8.0+ anymore, so no single variable name is portable. |
| 87 | +// The standard statement is accepted by every supported MySQL and MariaDB version. |
| 88 | +func OpenDB(mysql_uri string) (*gosql.DB, error) { |
| 89 | + cfg, err := gomysql.ParseDSN(mysql_uri) |
| 90 | + if err != nil { |
| 91 | + return nil, err |
| 92 | + } |
| 93 | + var statements []string |
| 94 | + if iso := strings.Trim(cfg.Params["transaction_isolation"], `"`); iso != "" { |
| 95 | + delete(cfg.Params, "transaction_isolation") |
| 96 | + statements = append(statements, "SET SESSION TRANSACTION ISOLATION LEVEL "+strings.ReplaceAll(iso, "-", " ")) |
| 97 | + } |
| 98 | + connector, err := gomysql.NewConnector(cfg) |
| 99 | + if err != nil { |
| 100 | + return nil, err |
| 101 | + } |
| 102 | + if len(statements) > 0 { |
| 103 | + connector = &initConnector{Connector: connector, statements: statements} |
| 104 | + } |
| 105 | + return gosql.OpenDB(connector), nil |
| 106 | +} |
| 107 | + |
51 | 108 | func GetDB(migrationUuid string, mysql_uri string) (db *gosql.DB, exists bool, err error) { |
52 | 109 | cacheKey := migrationUuid + ":" + mysql_uri |
53 | 110 |
|
54 | 111 | knownDBsMutex.Lock() |
55 | 112 | defer knownDBsMutex.Unlock() |
56 | 113 |
|
57 | 114 | if db, exists = knownDBs[cacheKey]; !exists { |
58 | | - db, err = gosql.Open("mysql", mysql_uri) |
| 115 | + db, err = OpenDB(mysql_uri) |
59 | 116 | if err != nil { |
60 | 117 | return nil, false, err |
61 | 118 | } |
@@ -88,7 +145,7 @@ func GetReplicationLagFromSlaveStatus(dbVersion string, informationSchemaDb *gos |
88 | 145 | func GetMasterKeyFromSlaveStatus(dbVersion string, connectionConfig *ConnectionConfig) (masterKey *InstanceKey, err error) { |
89 | 146 | currentUri := connectionConfig.GetDBUri("information_schema") |
90 | 147 | // This function is only called once, okay to not have a cached connection pool |
91 | | - db, err := gosql.Open("mysql", currentUri) |
| 148 | + db, err := OpenDB(currentUri) |
92 | 149 | if err != nil { |
93 | 150 | return nil, err |
94 | 151 | } |
|
0 commit comments