Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bun cannot scan float32 into float32 for MySQL FLOAT columns #1087

Open
Re3os opened this issue Dec 17, 2024 · 1 comment · May be fixed by #1088
Open

Bun cannot scan float32 into float32 for MySQL FLOAT columns #1087

Re3os opened this issue Dec 17, 2024 · 1 comment · May be fixed by #1088
Assignees

Comments

@Re3os
Copy link

Re3os commented Dec 17, 2024

When using Bun with MySQL and a column of type FLOAT, the database/sql driver correctly returns a value of type float32. However, Bun fails to scan this value into a Go struct field of type float32 with the following error:
My code: https://github.com/WarhoopAll/Grimoire/blob/28c38ff89bdaa980ea1618c7e8802e3f991db6ca/app/model/character.go#L23

To Reproduce

Create a MySQL table with a FLOAT column:

CREATE TABLE characters (
    guid INT PRIMARY KEY,
    position_x FLOAT
);
INSERT INTO characters (guid, position_x) VALUES (1, -6279.93);
type DBCharacter struct {
 PositionX float32 `bun:"position_x"`
}
var character DBCharacter
err := bunDB.NewSelect().
    Model(&character).
    Where("guid = ?", 1).
    Scan(ctx)
if err != nil {
    log.Fatalf("Error: %v", err)
}

Fast sctipt for test

package main

import (
  "context"
  "database/sql"
  "fmt"
  "log"

  _ "github.com/go-sql-driver/mysql"
  "github.com/uptrace/bun"
  "github.com/uptrace/bun/dialect/mysqldialect"
)

func main() {
  dsn := "root:root@tcp(127.0.0.1:3306)/characters"


  sqlDB, err := sql.Open("mysql", dsn)
  if err != nil {
    log.Fatalf("Failed to open database: %v", err)
  }
  defer sqlDB.Close()

  // === go-sql-driver SQL-===
  var positionX interface{}
  err = sqlDB.QueryRow("SELECT position_x FROM characters WHERE guid = ?", 1).Scan(&positionX)
  if err != nil {
    log.Fatalf("Failed to execute query (database/sql): %v", err)
  }
  fmt.Printf("Raw value (database/sql): %v, Type: %T\n", positionX, positionX)

  // === Bun  mysqldialect ===
  bunDB := bun.NewDB(sqlDB, mysqldialect.New()) 
  defer bunDB.Close()

  var positionXWithBun float32
  err = bunDB.NewRaw("SELECT position_x FROM characters WHERE guid = ?", 1).
    Scan(context.Background(), &positionXWithBun)
  if err != nil {
    log.Fatalf("Failed to execute query (Bun): %v", err)
  }
  fmt.Printf("Value from Bun (mysqldialect): %f\n", positionXWithBun)
}
First go-sql-driver

Raw value (database/sql): -6279.927, Type: float32 - 32 type
Second mysqldialect

2024/12/17 23:07:05 Failed to execute query (Bun): sql: Scan error on column index 0, name "position_x": bun: can't scan -6279.93 (float32) into float32
Bun cannot scan float32 even in the float32 field
Change type 

var positionXWithBun float64

2024/12/17 23:40:32 Failed to execute query (Bun): sql: Scan error on column index 0, name "position_x": bun: can't scan -6279.93 (float32) into float64

okey check code

reflect.Float32: scanFloat64,

reflect.Float32: scanFloat64
there is no conversion of float32 to float64

add case

func scanFloat64(dest reflect.Value, src interface{}) error {
	switch src := src.(type) {
	case nil:
		dest.SetFloat(0)
		return nil
	case float32: // Safely convert float32 to float64
		dest.SetFloat(float64(src))
		return nil
	case float64:
		dest.SetFloat(src)
		return nil
	case []byte:
		f, err := strconv.ParseFloat(string(src), 64)
		if err != nil {
			return err
		}
		dest.SetFloat(f)
		return nil
	case string:
		f, err := strconv.ParseFloat(src, 64)
		if err != nil {
			return err
		}
		dest.SetFloat(f)
		return nil
	default:
		return scanError(dest.Type(), src)
	}
}

work fine:

Raw value (database/sql): -6279.927, Type: float32
Value from Bun (mysqldialect): -6279.930176

if now update 	var positionXWithBun float64

Raw value (database/sql): -6279.927, Type: float32
Value from Bun (mysqldialect): -6279.930176

work
mysql> SELECT CAST(position_x AS DOUBLE) AS precise_value FROM characters WHERE guid = 1;
+------------------+
| precise_value    |
+------------------+
| -6279.9267578125 |
+------------------+
1 row in set (0.02 sec)

mysql> SELECT position_x FROM characters WHERE guid = 1;
+------------+
| position_x |
+------------+
|   -6279.93 |
+------------+
1 row in set (0.01 sec)

mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 8.0.40    |
+-----------+
1 row in set (0.01 sec)

mysql> DESCRIBE characters position_x;
+------------+-------+------+-----+---------+-------+
| Field      | Type  | Null | Key | Default | Extra |
+------------+-------+------+-----+---------+-------+
| position_x | float | NO   |     | 0       |       |
+------------+-------+------+-----+---------+-------+
1 row in set (0.03 sec)

go list -m github.com/go-sql-driver/mysql
github.com/go-sql-driver/mysql v1.8.1

go list -m github.com/uptrace/bun/dialect/mysqldialect
github.com/uptrace/bun/dialect/mysqldialect v1.2.6

Old issue
#993

@j2gg0s
Copy link
Collaborator

j2gg0s commented Dec 18, 2024

Thank you for your detailed issue.

j2gg0s added a commit to j2gg0s/bun that referenced this issue Dec 18, 2024
@j2gg0s j2gg0s linked a pull request Dec 18, 2024 that will close this issue
@j2gg0s j2gg0s self-assigned this Dec 18, 2024
j2gg0s added a commit to j2gg0s/bun that referenced this issue Dec 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants