Reflection and more complex type variables

It’s pretty easy to get an underlying value in reflections if it’s a simple type like string or int etc. but what if it’s more complex like sql.NullInt64 where there are calls that produce different values. I’m not sure how to get to those calls (like .Valid or .Int64) using reflections.

The closest I’ve got is returning something like “{install false}” which is still a compound value instead of just the “install”.

Perhaps someone could show me the last piece that needs to be done to get that last bit of data?

Here’s the code I was playing with: code.

On a side note, is it possible in the online go playground to import external libraries to use like:
github.com/davecgh/go-spew/spew

I find that spew.Dump useful over fmt.Print… sometimes. It’s not a big deal, just curious, I played around and couldn’t get it to work.

I’m not sure if there’s a convention to put code here to link to the playground so I’ll put it below as well. If anyone has thoughts on that please let me know.

package main

import (
	"database/sql"
	"fmt"
	"reflect"
)

type TableInstallRow struct {
	Module        sql.NullString
	ModuleVersion sql.NullString
	Status        sql.NullInt64
	Weight        sql.NullInt64
}

func main() {
	var curRow TableInstallRow
	curRow.Module.String = `install`
	curRow.ModuleVersion.String = `0.1.0`
	curRow.Status.Valid = true
	curRow.Weight.Int64 = int64(-100)

	var newRow TableInstallRow
	newRow.Module.String = `install`
	newRow.ModuleVersion.String = `0.2.0`
	newRow.Status.Valid = true
	newRow.Weight.Int64 = int64(-1000)

	fmt.Println(buildUpdateSQL(curRow, newRow, `install`, "`module` = 'install'"))
}

func buildUpdateSQL(curRow, newRow interface{}, table, where string) string {
	sqlStr := "UPDATE `install` SET "

	cur := reflect.ValueOf(curRow)
	new := reflect.ValueOf(newRow)

	// fmt.Println(`a`, reflect.StructOf(cur))   // Type
	t := reflect.TypeOf(curRow)
	v := reflect.ValueOf(curRow)

	fmt.Println()
	fmt.Print(`Type: `)
	fmt.Println(t)
	fmt.Println()
	fmt.Print(`Value: `)
	fmt.Println(v)
	fmt.Print(`Type:Kind: `)
	fmt.Println(t.Kind())
	fmt.Println()

	for i := 0; i < cur.NumField(); i++ {
		fmt.Print(`1: `)
		fmt.Println(t.Field(i).Name)

		fmt.Print(`2: `)
		fmt.Println(cur.Field(i).String())

		fmt.Print(`3: `)
		fmt.Println(cur.Field(i).Type().Name())

		fmt.Print(`4: `)
		fmt.Println(new.Field(i))

		fmt.Print(`5: `)
		fmt.Println(new.Field(i).Interface())

		fmt.Print(`5: `)
		fmt.Println(new.Field(i).Interface())

		// switch cur.Field(i).Type().Name() {
		// case `NullString`:
		//
		// case `NullInt64`:
		// }

		// fmt.Print(`6: `)
		// fmt.Println(new.Field(i).Interface().Name)

		fmt.Println()

	}

	return sqlStr + ` WHERE ` + where
}

After much playing around I came up with something that seems to work so in case anyone else comes upon this question, here’s a solution.

package main

import (
	"bytes"
	"database/sql"
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/shomali11/util/xconditions"
)

type TableInstallRow struct {
	Module        sql.NullString
	ModuleVersion sql.NullString
	Status        sql.NullInt64
	Weight        sql.NullInt64
}

func main() {

	var curRow TableInstallRow
	curRow.Module.Scan(`install`)
	curRow.ModuleVersion.Scan(`0.1.0`)
	curRow.Status.Scan(true)
	curRow.Weight.Scan(int64(-100))

	var newRow TableInstallRow
	newRow.Module.Scan(`install`)
	newRow.ModuleVersion.Scan(`0.2.0`)
	newRow.Status.Scan(true)
	newRow.Weight.Scan(int64(-1000))

	fmt.Println(buildUpdateSQL(curRow, newRow, `install`, "`module` = 'install'"))
}

func convReflectValueToString(i interface{}) string {
	var buf bytes.Buffer
	fmt.Fprint(&buf, reflect.ValueOf(i))
	return buf.String()
}

func buildUpdateSQL(curRow, newRow interface{}, table, where string) string {
	var strVal string
	var sqlStr string
	curValues := reflect.ValueOf(curRow)
	newValues := reflect.ValueOf(newRow)
	newTypes := reflect.TypeOf(newRow)

	for i := 0; i < newValues.NumField(); i++ {
		newValue := newValues.Field(i).Interface()
		if newValue == curValues.Field(i).Interface() {
			continue
		}
		
		newType := newTypes.Field(i)
		subValues := reflect.ValueOf(newValue)

		switch newValue.(type) {
		case sql.NullString:
			var sqlVar sql.NullString
			for i := 0; i < subValues.NumField(); i++ {
				subValue := subValues.Field(i).Interface()
				fieldValue := convReflectValueToString(subValue)
				switch subValue.(type) {
				case string:
					sqlVar.String = fieldValue
				case bool:
					sqlVar.Valid, _ = strconv.ParseBool(fieldValue)
				}
			}
			strVal = xconditions.IfThenElse(sqlVar.Valid, sqlVar.String, `NULL`).(string)

		case sql.NullInt64:
			var sqlVar sql.NullInt64
			for i := 0; i < subValues.NumField(); i++ {
				subValue := subValues.Field(i).Interface()
				fieldValue := convReflectValueToString(subValue)
				switch subValue.(type) {
				case int64:
					sqlVar.Int64, _ = strconv.ParseInt(fieldValue, 10, 64)
				case bool:
					sqlVar.Valid, _ = strconv.ParseBool(fieldValue)
				}
			}
			strVal = xconditions.IfThenElse(sqlVar.Valid, strconv.FormatInt(sqlVar.Int64, 10), `NULL`).(string)
		}
		sqlStr = sqlStr + "`" + newType.Name + "` = '" + strVal + `, `
	}
	if sqlStr > `` {
		sqlStr = "UPDATE `install` SET " + strings.TrimRight(sqlStr, `, `) + ` WHERE ` + where
	}

	return sqlStr
}

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.