Skip to content

Support decimal or custom value bind #1177

Open

Description

@RelicOfTesla

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 golang/go#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?

Activity

  1. changed the title [-]Support decimal value query[/-] [+]Support decimal or custom value bind[/+] on May 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions