Conversation
This adds a new generic ArrayOf[T any] type:
var a pq.ArrayOf[int]
db.QueryRow("...").Scan(&a)
This replaces all the existing Array* types, which are now implemented
with ArrayOf type aliases and marked as deprecated. The existing tests
are unchanged except for some error texts, and I believe ths should be
fully compatible.
Only GenericArray is left as-is, as re-implementing that with ArrayOf in
a way that's fully compatible is kind of tricky. I'd rather just leave
it as-is instead of spending the effort and risk breakage.
The main issue is that we rely on:
//go:linkname convertAssign database/sql.convertAssign
func convertAssign(dest, src any) error
Which does not exactly spark joy, however there is already a comment on
convertAssign():
// convertAssign should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
// - ariga.io/entcache
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
So if compatibility is already guaranteed because of other packages,
then I guess it's okay to join this "hall of shame". Also, it looks like
it'll probably just get exported in the future:
golang/go#62146 (comment)
Fixes #1103
There was a problem hiding this comment.
Tried this on a project and everything still works, replacing the deprecated types with ArrayOf is also easy 👍
Edit: Pushed suggestions below to this commit.
| case *[]bool: | ||
| return (*BoolArray)(a) | ||
| case *[]float64: | ||
| return (*Float64Array)(a) | ||
| case *[]float32: | ||
| return (*Float32Array)(a) | ||
| case *[]int64: | ||
| return (*Int64Array)(a) | ||
| case *[]int32: | ||
| return (*Int32Array)(a) | ||
| case *[]string: | ||
| return (*StringArray)(a) | ||
| case *[][]byte: | ||
| return (*ByteaArray)(a) |
There was a problem hiding this comment.
| case *[]bool: | |
| return (*BoolArray)(a) | |
| case *[]float64: | |
| return (*Float64Array)(a) | |
| case *[]float32: | |
| return (*Float32Array)(a) | |
| case *[]int64: | |
| return (*Int64Array)(a) | |
| case *[]int32: | |
| return (*Int32Array)(a) | |
| case *[]string: | |
| return (*StringArray)(a) | |
| case *[][]byte: | |
| return (*ByteaArray)(a) | |
| case *[]bool: | |
| return (*ArrayOf[bool])(a) | |
| case *[]float64: | |
| return (*ArrayOf[float64])(a) | |
| case *[]float32: | |
| return (*ArrayOf[float32])(a) | |
| case *[]int64: | |
| return (*ArrayOf[int64])(a) | |
| case *[]int32: | |
| return (*ArrayOf[int32])(a) | |
| case *[]string: | |
| return (*ArrayOf[string])(a) | |
| case *[][]byte: | |
| return (*ArrayOf[[]byte])(a) |
Unless there was a reason to use the deprecated names here?
| case float64: | ||
| b = strconv.AppendFloat(b, v, 'f', -1, 64) | ||
| case bool: | ||
| if any(aa).(bool) { |
There was a problem hiding this comment.
| if any(aa).(bool) { | |
| if v { |
| b = strconv.AppendUint(b, rv.Uint(), 10) | ||
| case reflect.Float32, reflect.Float64: | ||
| b = strconv.AppendFloat(b, rv.Float(), 'f', -1, 32) | ||
| case reflect.Ptr: |
There was a problem hiding this comment.
| case reflect.Ptr: | |
| case reflect.Bool: | |
| if rv.Bool() { | |
| b = append(b, 't') | |
| } else { | |
| b = append(b, 'f') | |
| } | |
| case reflect.Ptr: |
Probably just missing the boolean case?
| b = append(b, del...) | ||
| if b, del, err = appendArrayElement(b, rv.Index(i)); err != nil { | ||
| return b, del, err | ||
| for i, aa := range a { |
There was a problem hiding this comment.
Given that the element type is already known, wouldn't it be faster to move the type assertion out of the loop for the common cases?
That is, specialise this block for the common slice types via something like:
switch xx := any([]T(a)).(type) {
case []bool:
...
case []uint8:
b = appendAllUint(xx, b, del)
case []uint16:
b = appendAllUint(xx, b, del)
...
default:
// only use reflection on individual elements here
for i, aa := range a {
...
}
}The duplication for the signed and unsigned types could also be minimised with something like:
func appendAllUint[T uint8 | uint16 | uint32 | uint64](xx []T, b []byte, del []byte) []byte {
for i, aa := range xx {
if i > 0 {
b = append(b, del...)
}
b = strconv.AppendUint(b, uint64(aa), 10)
}
return b
}
// similar for the other types
Add generic ArrayOf[T any] type
This adds a new generic ArrayOf[T any] type:
This replaces all the existing Array* types, which are now implemented
with ArrayOf type aliases and marked as deprecated. The existing tests
are unchanged except for some error texts, and I believe ths should be
fully compatible.
Only GenericArray is left as-is, as re-implementing that with ArrayOf in
a way that's fully compatible is kind of tricky. I'd rather just leave
it as-is instead of spending the effort and risk breakage.
The main issue is that we rely on:
Which does not exactly spark joy, however there is already a comment on
convertAssign():
So if compatibility is already guaranteed because of other packages,
then I guess it's okay to join this "hall of shame". Also, it looks like
it'll probably just get exported in the future:
golang/go#62146 (comment)
Fixes #1103