mysql icon indicating copy to clipboard operation
mysql copied to clipboard

Support decimal or custom value bind

Open RelicOfTesla opened this issue 5 years ago • 0 comments

string stm query only support float 10^-16...not support full decimal value..

// ok
select (0.2676120186162537123456789+0.267612018616253712345678912312312312312312312312312123) as field22

// fail
// select (0.2676120186162537123456789+'0.267612018616253712345678912312312312312312312312312123') as field22
// tip: quote the number to string number, will lose accracy


// fail
select (0.2676120186162537123456789+?) as field22
arg1:="0.267612018616253712345678912312312312312312312312312123"
//

Same with update,insert and more sql command...

Because field type is string , not NewDecimal type or good type when send packet stm binary

so... maybe add a draft proposal https://github.com/golang/go/issues/30870 DecimalDecompose type to write custom field type value to mysql stm.

#985 #986

sample:

type Decimal struct {
	str string
}

func NewDecimal(str string) Decimal {
	return Decimal{str: str}
}

const fieldTypeNewDecimal = 246


func (x Decimal) Value() (driver.Value, error) {
	return sqlValueDecompose{Type: fieldTypeNewDecimal, Data: []byte(x.str)}, nil
}

/// 

type sqlValueDecompose struct {
	Type byte
	Data []byte
}

// implement database/sql convertAssignRows=>decimalDecompose
// var _ sql.decimal = (*sqlValueDecompose)(nil)
// var _ sql.decimalDecompose = (*sqlValueDecompose)(nil)
func (x sqlValueDecompose) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
	return x.Type, false, x.Data, 0
}

/// 


func main() {
	db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test1")
	require.NoError(t, err)

	
	verifyTest := func(want1, sqlCmd string, args ...any) {
		rows, err := db.Query(sqlCmd, args...)
		require.NoError(t, err)

		defer rows.Close()
		require.True(t, rows.Next())
		cb := sql.RawBytes{}
		err = rows.Scan(&cb)
		require.NoError(t, err)
		str := string(cb)
		require.Equal(t, str, want1)
	}

	//num1 := "0.11111222223333344444"
	//num2 := "0.111112222233333444445555566666" // mysql max decimal(65,30)
	//sum1 := "0.222224444466666888885555566666" // mysql max decimal(65,30)

	num1 := "0.2676120186162537123456789"
	num2 := "0.267612018616253712345678912312312312312312312312312123"
	sum1 := "0.535224037232507424691357812312312312312312312312312123"

	//verifyTest(sum1, "select ("+num1+"+?) as sum1", num2)               // bad
	//verifyTest(sum1, "select ("+num1+"+?) as sum1", string(num2))       // bad
	//verifyTest(sum1, "select ("+num1+"+?) as sum1", []byte(num2))       // bad
	//verifyTest(sum1, "select ("+num1+"+?) as sum1", sql.RawBytes(num2)) // bad

	//verifyTest(sum1, "select ("+num1+"+"+num2+") as sum1") // good
	verifyTest(sum1, "select ("+num1+"+?) as sum1", NewDecimal(num2)) // will good
}


/////////////
source : packets.go 

func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error 
   //.....
   for i, arg := range args {
     //.....
     switch v := arg.(type) {
     case int64: // ...
     case uint64: // ...
     case DecimalDecompose:
				tp, _, cb, _ := v.Decompose(nil)

				err = func() error {
					v := cb
					paramTypes[i+i] = byte(tp)
					paramTypes[i+i+1] = 0x00

					if len(v) < longDataSize {
						paramValues = appendLengthEncodedInteger(paramValues,
							uint64(len(v)),
						)
						paramValues = append(paramValues, v...)
					} else {
						if err := stmt.writeCommandLongData(i, []byte(v)); err != nil {
							return err
						}
					}
					return nil
				}()
				if err != nil {
					return err
				}
                // .......
          //......

//

// type DecimalDecompose = sql.DecimalDecompose // sql.decimalDecompose
type DecimalDecompose interface {
      // sql.decimalDecompose
      Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) 
  }

Or use some custom method?

RelicOfTesla avatar Dec 03 '20 11:12 RelicOfTesla

OR...

case interface {
      Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) 
  }:
  tp,_,cb,_ = v.Decompose(nil)
// ....

but im like CustomSqlValue....that are support BLOB and More Sql type..

RelicOfTesla avatar Dec 03 '20 11:12 RelicOfTesla