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

Error while scanning column of type array<decimal(10,2)> into Go type []float64 #213

Open
esdrasbeleza opened this issue Apr 11, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@esdrasbeleza
Copy link
Contributor

I have a table that has a column AMOUNTS with a type array<decimal(10,2)>, which I expect to scan into a field type []float64.
This is the test I wrote to reproduce it:

func (s *DatabricksRepositorySuite) Test_Float64_Conversion() {
	query := `SELECT AMOUNTS FROM components WHERE AMOUNTS IS NOT NULL AND array_size(AMOUNTS) > 0 LIMIT 1`
	var result []float64
	err := s.repository.db.QueryRow(query).Scan(&result)
	s.NoError(err)
}

When I run my query, I get this error:

sql: Scan error on column index 0, name "AMOUNTS": unsupported Scan, storing driver.Value type string into type *[]float64

Source

This error comes from the standard library in Go: the value for that field is provided by the SDK as a string, but the destination is []float64, so it fails. I also dove into Databricks' SDK source code and found the place [2] where the string is generated.

[1] Golang source - convertAssignRows
[2] Databricks SDK - listValueContainer.Value

Workaround

I created a custom type for the fields I have that use arrays:

import (
	"database/sql/driver"
	"encoding/json"
	"errors"
)

type databricksArray[T any] struct {
	parsedValue []T
}

func (f *databricksArray[T]) Scan(value interface{}) error {
	switch typedValue := value.(type) {
	case []T:
		f.parsedValue = typedValue
		return nil
	case string:
		return f.parseString(typedValue)
	default:
		return errors.New("error while handling databricks type")
	}
}

func (f *databricksArray[T]) parseString(value string) error {
	// This is a workaround for Databricks returning float arrays as strings
	// Example: `[1.0, 2.0, 3.0]`
	// We need to convert it to an array
	// This should be removed if/when Databricks fixes this issue
	valueBytes := []byte(value)
	var parsedValue []T
	if err := json.Unmarshal(valueBytes, &parsedValue); err != nil {
		return err
	}
	f.parsedValue = parsedValue
	return nil
}

func (f *databricksArray[T]) Value() (driver.Value, error) {
	return f.parsedValue, nil
}

func (f *databricksArray[T]) Slice() []T {
	if f == nil {
		return nil
	}
	return f.parsedValue
}

So my field

Amount []float64 `db:"AMOUNTS"`

is now

Amount databricksArray[float64] `db:"AMOUNTS"`

and I call Amount.Slice() when I want to read its value.

@kravets-levko kravets-levko added the bug Something isn't working label Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants