vitess.io/vitess@v0.16.2/go/vt/vtorc/db/db.go (about) 1 /* 2 Copyright 2014 Outbrain Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package db 18 19 import ( 20 "database/sql" 21 "strings" 22 23 "vitess.io/vitess/go/vt/external/golib/sqlutils" 24 "vitess.io/vitess/go/vt/log" 25 "vitess.io/vitess/go/vt/vtorc/config" 26 ) 27 28 var ( 29 Db DB = (*vtorcDB)(nil) 30 ) 31 32 type DB interface { 33 QueryVTOrc(query string, argsArray []any, onRow func(sqlutils.RowMap) error) error 34 } 35 36 type vtorcDB struct { 37 } 38 39 var _ DB = (*vtorcDB)(nil) 40 41 func (m *vtorcDB) QueryVTOrc(query string, argsArray []any, onRow func(sqlutils.RowMap) error) error { 42 return QueryVTOrc(query, argsArray, onRow) 43 } 44 45 type DummySQLResult struct { 46 } 47 48 func (dummyRes DummySQLResult) LastInsertId() (int64, error) { 49 return 0, nil 50 } 51 52 func (dummyRes DummySQLResult) RowsAffected() (int64, error) { 53 return 1, nil 54 } 55 56 // OpenTopology returns the DB instance for the vtorc backed database 57 func OpenVTOrc() (db *sql.DB, err error) { 58 var fromCache bool 59 db, fromCache, err = sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile) 60 if err == nil && !fromCache { 61 log.Infof("Connected to vtorc backend: sqlite on %v", config.Config.SQLite3DataFile) 62 _ = initVTOrcDB(db) 63 } 64 if db != nil { 65 db.SetMaxOpenConns(1) 66 db.SetMaxIdleConns(1) 67 } 68 return db, err 69 } 70 71 func translateStatement(statement string) string { 72 return sqlutils.ToSqlite3Dialect(statement) 73 } 74 75 // registerVTOrcDeployment updates the vtorc_metadata table upon successful deployment 76 func registerVTOrcDeployment(db *sql.DB) error { 77 query := ` 78 replace into vtorc_db_deployments ( 79 deployed_version, deployed_timestamp 80 ) values ( 81 ?, NOW() 82 ) 83 ` 84 if _, err := execInternal(db, query, ""); err != nil { 85 log.Fatalf("Unable to write to vtorc_metadata: %+v", err) 86 } 87 return nil 88 } 89 90 // deployStatements will issue given sql queries that are not already known to be deployed. 91 // This iterates both lists (to-run and already-deployed) and also verifies no contraditions. 92 func deployStatements(db *sql.DB, queries []string) error { 93 tx, err := db.Begin() 94 if err != nil { 95 log.Fatal(err.Error()) 96 } 97 for _, query := range queries { 98 query = translateStatement(query) 99 if _, err := tx.Exec(query); err != nil { 100 if strings.Contains(err.Error(), "syntax error") { 101 log.Fatalf("Cannot initiate vtorc: %+v; query=%+v", err, query) 102 return err 103 } 104 if !sqlutils.IsAlterTable(query) && !sqlutils.IsCreateIndex(query) && !sqlutils.IsDropIndex(query) { 105 log.Fatalf("Cannot initiate vtorc: %+v; query=%+v", err, query) 106 return err 107 } 108 if !strings.Contains(err.Error(), "duplicate column name") && 109 !strings.Contains(err.Error(), "Duplicate column name") && 110 !strings.Contains(err.Error(), "check that column/key exists") && 111 !strings.Contains(err.Error(), "already exists") && 112 !strings.Contains(err.Error(), "Duplicate key name") { 113 log.Errorf("Error initiating vtorc: %+v; query=%+v", err, query) 114 } 115 } 116 } 117 if err := tx.Commit(); err != nil { 118 log.Fatal(err.Error()) 119 } 120 return nil 121 } 122 123 // ClearVTOrcDatabase is used to clear the VTOrc database. This function is meant to be used by tests to clear the 124 // database to get a clean slate without starting a new one. 125 func ClearVTOrcDatabase() { 126 db, _, _ := sqlutils.GetSQLiteDB(config.Config.SQLite3DataFile) 127 if db != nil { 128 _ = initVTOrcDB(db) 129 } 130 } 131 132 // initVTOrcDB attempts to create/upgrade the vtorc backend database. It is created once in the 133 // application's lifetime. 134 func initVTOrcDB(db *sql.DB) error { 135 log.Info("Initializing vtorc") 136 log.Info("Migrating database schema") 137 _ = deployStatements(db, vtorcBackend) 138 _ = registerVTOrcDeployment(db) 139 140 _, _ = ExecVTOrc(`PRAGMA journal_mode = WAL`) 141 _, _ = ExecVTOrc(`PRAGMA synchronous = NORMAL`) 142 143 return nil 144 } 145 146 // execInternal 147 func execInternal(db *sql.DB, query string, args ...any) (sql.Result, error) { 148 var err error 149 query = translateStatement(query) 150 res, err := sqlutils.ExecNoPrepare(db, query, args...) 151 return res, err 152 } 153 154 // ExecVTOrc will execute given query on the vtorc backend database. 155 func ExecVTOrc(query string, args ...any) (sql.Result, error) { 156 var err error 157 query = translateStatement(query) 158 db, err := OpenVTOrc() 159 if err != nil { 160 return nil, err 161 } 162 res, err := sqlutils.ExecNoPrepare(db, query, args...) 163 return res, err 164 } 165 166 // QueryVTOrcRowsMap 167 func QueryVTOrcRowsMap(query string, onRow func(sqlutils.RowMap) error) error { 168 query = translateStatement(query) 169 db, err := OpenVTOrc() 170 if err != nil { 171 return err 172 } 173 174 return sqlutils.QueryRowsMap(db, query, onRow) 175 } 176 177 // QueryVTOrc 178 func QueryVTOrc(query string, argsArray []any, onRow func(sqlutils.RowMap) error) error { 179 query = translateStatement(query) 180 db, err := OpenVTOrc() 181 if err != nil { 182 return err 183 } 184 185 if err = sqlutils.QueryRowsMap(db, query, onRow, argsArray...); err != nil { 186 log.Warning(err.Error()) 187 } 188 189 return err 190 } 191 192 // ReadTimeNow reads and returns the current timestamp as string. This is an unfortunate workaround 193 // to support both MySQL and SQLite in all possible timezones. SQLite only speaks UTC where MySQL has 194 // timezone support. By reading the time as string we get the database's de-facto notion of the time, 195 // which we can then feed back to it. 196 func ReadTimeNow() (timeNow string, err error) { 197 err = QueryVTOrc(`select now() as time_now`, nil, func(m sqlutils.RowMap) error { 198 timeNow = m.GetString("time_now") 199 return nil 200 }) 201 return timeNow, err 202 }