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  }