github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/mysql/util.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package mysql 21 22 import ( 23 "database/sql" 24 "encoding/json" 25 "strconv" 26 "time" 27 ) 28 29 const DateTimeFormat = "2006-01-02 15:04:05.999999" 30 31 // RowMap represents one row in a result set. Its objective is to allow 32 // for easy, typed getters by column name. 33 type RowMap map[string]CellData 34 35 // CellData is the result of a single (atomic) column in a single row 36 type CellData sql.NullString 37 38 func (cd *CellData) MarshalJSON() ([]byte, error) { 39 if cd.Valid { 40 return json.Marshal(cd.String) 41 } else { 42 return json.Marshal(nil) 43 } 44 } 45 46 // UnmarshalJSON reds this object from JSON 47 func (cd *CellData) UnmarshalJSON(b []byte) error { 48 var s string 49 if err := json.Unmarshal(b, &s); err != nil { 50 return err 51 } 52 cd.String = s 53 cd.Valid = true 54 55 return nil 56 } 57 58 func (cd *CellData) NullString() *sql.NullString { 59 return (*sql.NullString)(cd) 60 } 61 62 // RowData is the result of a single row, in positioned array format 63 type RowData []CellData 64 65 // MarshalJSON will marshal this map as JSON 66 func (rd *RowData) MarshalJSON() ([]byte, error) { 67 cells := make([](*CellData), len(*rd)) 68 for i, val := range *rd { 69 d := val 70 cells[i] = &d 71 } 72 return json.Marshal(cells) 73 } 74 75 func (rd *RowData) Args() []interface{} { 76 result := make([]interface{}, len(*rd)) 77 for i := range *rd { 78 result[i] = (*(*rd)[i].NullString()) 79 } 80 return result 81 } 82 83 // ResultData is an ordered row set of RowData 84 type ResultData []RowData 85 type NamedResultData struct { 86 Columns []string 87 Data ResultData 88 } 89 90 var EmptyResultData = ResultData{} 91 92 func (rm *RowMap) GetString(key string) string { 93 return (*rm)[key].String 94 } 95 96 // GetStringD returns a string from the map, or a default value if the key does not exist 97 func (rm *RowMap) GetStringD(key string, def string) string { 98 if cell, ok := (*rm)[key]; ok { 99 return cell.String 100 } 101 return def 102 } 103 104 func (rm *RowMap) GetInt64(key string) int64 { 105 res, _ := strconv.ParseInt(rm.GetString(key), 10, 0) 106 return res 107 } 108 109 func (rm *RowMap) GetNullInt64(key string) sql.NullInt64 { 110 i, err := strconv.ParseInt(rm.GetString(key), 10, 0) 111 if err == nil { 112 return sql.NullInt64{Int64: i, Valid: true} 113 } else { 114 return sql.NullInt64{Valid: false} 115 } 116 } 117 118 func (rm *RowMap) GetInt(key string) int { 119 res, _ := strconv.Atoi(rm.GetString(key)) 120 return res 121 } 122 123 func (rm *RowMap) GetIntD(key string, def int) int { 124 res, err := strconv.Atoi(rm.GetString(key)) 125 if err != nil { 126 return def 127 } 128 return res 129 } 130 131 func (rm *RowMap) GetUint(key string) uint { 132 res, _ := strconv.ParseUint(rm.GetString(key), 10, 0) 133 return uint(res) 134 } 135 136 func (rm *RowMap) GetUintD(key string, def uint) uint { 137 res, err := strconv.Atoi(rm.GetString(key)) 138 if err != nil { 139 return def 140 } 141 return uint(res) 142 } 143 144 func (rm *RowMap) GetUint64(key string) uint64 { 145 res, _ := strconv.ParseUint(rm.GetString(key), 10, 0) 146 return res 147 } 148 149 func (rm *RowMap) GetUint64D(key string, def uint64) uint64 { 150 res, err := strconv.ParseUint(rm.GetString(key), 10, 0) 151 if err != nil { 152 return def 153 } 154 return res 155 } 156 157 func (rm *RowMap) GetBool(key string) bool { 158 return rm.GetInt(key) != 0 159 } 160 161 func (rm *RowMap) GetTime(key string) time.Time { 162 if t, err := time.Parse(DateTimeFormat, rm.GetString(key)); err == nil { 163 return t 164 } 165 return time.Time{} 166 } 167 168 func RowToArray(rows *sql.Rows, columns []string) []CellData { 169 buff := make([]interface{}, len(columns)) 170 data := make([]CellData, len(columns)) 171 for i := range buff { 172 buff[i] = data[i].NullString() 173 } 174 _ = rows.Scan(buff...) 175 return data 176 } 177 178 // ScanRowsToArrays is a convenience function, typically not called directly, which maps rows 179 // already read from the databse into arrays of NullString 180 func ScanRowsToArrays(rows *sql.Rows, onRow func([]CellData) error) error { 181 columns, _ := rows.Columns() 182 for rows.Next() { 183 arr := RowToArray(rows, columns) 184 err := onRow(arr) 185 if err != nil { 186 return err 187 } 188 } 189 return nil 190 } 191 192 func rowToMap(row []CellData, columns []string) map[string]CellData { 193 m := make(map[string]CellData) 194 for k, dataCol := range row { 195 m[columns[k]] = dataCol 196 } 197 return m 198 } 199 200 // ScanRowsToMaps is a convenience function, typically not called directly, which maps rows 201 // already read from the databse into RowMap entries. 202 func ScanRowsToMaps(rows *sql.Rows, onRow func(RowMap) error) error { 203 columns, _ := rows.Columns() 204 err := ScanRowsToArrays(rows, func(arr []CellData) error { 205 m := rowToMap(arr, columns) 206 err := onRow(m) 207 if err != nil { 208 return err 209 } 210 return nil 211 }) 212 return err 213 } 214 215 // QueryRowsMap is a convenience function allowing querying a result set while poviding a callback 216 // function activated per read row. 217 func QueryRowsMap(db *sql.DB, query string, onRow func(RowMap) error, args ...interface{}) (err error) { 218 var rows *sql.Rows 219 rows, err = db.Query(query, args...) 220 if rows != nil { 221 defer rows.Close() 222 } 223 if err != nil && err != sql.ErrNoRows { 224 return err 225 } 226 err = ScanRowsToMaps(rows, onRow) 227 return 228 }