github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/lorry/engines/mysql/json.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  	"database/sql/driver"
    25  	"encoding/json"
    26  	"reflect"
    27  )
    28  
    29  func jsonify(rows *sql.Rows) ([]byte, error) {
    30  	columnTypes, err := rows.ColumnTypes()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	var ret []interface{}
    35  	for rows.Next() {
    36  		values := prepareValues(columnTypes)
    37  		err := rows.Scan(values...)
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  		r, err := convert(columnTypes, values)
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  		ret = append(ret, r)
    46  	}
    47  	return json.Marshal(ret)
    48  }
    49  
    50  func prepareValues(columnTypes []*sql.ColumnType) []interface{} {
    51  	types := make([]reflect.Type, len(columnTypes))
    52  	for i, tp := range columnTypes {
    53  		types[i] = tp.ScanType()
    54  	}
    55  	values := make([]interface{}, len(columnTypes))
    56  	for i := range values {
    57  		switch types[i].Kind() {
    58  		case reflect.String, reflect.Interface:
    59  			values[i] = &sql.NullString{}
    60  		case reflect.Bool:
    61  			values[i] = &sql.NullBool{}
    62  		case reflect.Float64:
    63  			values[i] = &sql.NullFloat64{}
    64  		case reflect.Int16, reflect.Uint16:
    65  			values[i] = &sql.NullInt16{}
    66  		case reflect.Int32, reflect.Uint32:
    67  			values[i] = &sql.NullInt32{}
    68  		case reflect.Int64, reflect.Uint64:
    69  			values[i] = &sql.NullInt64{}
    70  		default:
    71  			values[i] = reflect.New(types[i]).Interface()
    72  		}
    73  	}
    74  	return values
    75  }
    76  
    77  func convert(columnTypes []*sql.ColumnType, values []interface{}) (map[string]interface{}, error) {
    78  	r := map[string]interface{}{}
    79  	for i, ct := range columnTypes {
    80  		value := values[i]
    81  		switch v := values[i].(type) {
    82  		case driver.Valuer:
    83  			if vv, err := v.Value(); err != nil {
    84  				return nil, err
    85  			} else {
    86  				value = interface{}(vv)
    87  			}
    88  		case *sql.RawBytes:
    89  			// special case for sql.RawBytes, see https://github.com/go-sql-driver/mysql/blob/master/fields.go#L178
    90  			switch ct.DatabaseTypeName() {
    91  			case "VARCHAR", "CHAR", "TEXT", "LONGTEXT":
    92  				value = string(*v)
    93  			}
    94  		}
    95  		if value != nil {
    96  			r[ct.Name()] = value
    97  		}
    98  	}
    99  	return r, nil
   100  }