104 lines
2.4 KiB
Go
Raw Normal View History

package d1
import (
"database/sql/driver"
2023-01-09 23:10:30 +09:00
"errors"
"io"
"math"
2023-01-09 23:10:30 +09:00
"sync"
"syscall/js"
"github.com/syumai/workers/internal/jsutil"
)
2023-01-09 23:10:30 +09:00
type rows struct {
2024-04-27 15:34:02 +09:00
rowsArray js.Value
2023-01-09 23:10:30 +09:00
currentRow int
// _columns is cached value of Columns method.
2023-01-09 23:10:30 +09:00
// do not use this directly.
_columns []string
2023-01-09 23:10:30 +09:00
// _rowsLen is cached value of rowsLen method.
// do not use this directly.
_rowsLen int
onceRowsLen sync.Once
mu sync.Mutex
}
var _ driver.Rows = (*rows)(nil)
// Columns returns column names retrieved from query result.
2023-01-09 23:10:30 +09:00
// If rows are empty, this returns nil.
func (r *rows) Columns() []string {
return r._columns
}
func (r *rows) Close() error {
2023-01-09 23:10:30 +09:00
// do nothing
return nil
}
2023-02-26 18:40:18 +09:00
// isIntegralNumber returns if given float64 value is integral value or not.
func isIntegralNumber(f float64) bool {
// If the value is NaN or Inf, returns the value to avoid being mistakenly treated as an integral value.
if math.IsNaN(f) || math.IsInf(f, 0) {
return false
}
return f == math.Trunc(f)
}
2023-01-09 23:10:30 +09:00
// convertRowColumnValueToDriverValue converts row column's value in JS to Go's driver.Value.
// row column value is `null | Number | String | ArrayBuffer`.
// see: https://developers.cloudflare.com/d1/platform/client-api/#type-conversion
func convertRowColumnValueToAny(v js.Value) (driver.Value, error) {
switch v.Type() {
case js.TypeNull:
return nil, nil
case js.TypeNumber:
fv := v.Float()
2023-02-26 18:40:18 +09:00
// if the value can be treated as integral value, return as int64.
if isIntegralNumber(fv) {
return int64(fv), nil
}
return fv, nil
2023-01-09 23:10:30 +09:00
case js.TypeString:
return v.String(), nil
case js.TypeObject:
// handle BLOB type (ArrayBuffer).
src := jsutil.Uint8ArrayClass.New(v)
dst := make([]byte, src.Length())
n := js.CopyBytesToGo(dst, src)
if n != len(dst) {
return nil, errors.New("incomplete copy from Uint8Array")
}
return dst[:n], nil
2023-01-09 23:10:30 +09:00
}
return nil, errors.New("d1: unexpected row column value type")
}
func (r *rows) Next(dest []driver.Value) error {
2023-01-09 23:10:30 +09:00
r.mu.Lock()
defer r.mu.Unlock()
if r.currentRow == r.rowsLen() {
return io.EOF
}
2024-04-27 15:34:02 +09:00
// rowArray is Array of string.
rowArray := r.rowsArray.Index(r.currentRow)
rowArrayLen := rowArray.Length()
for i := 0; i < rowArrayLen; i++ {
v, err := convertRowColumnValueToAny(rowArray.Index(i))
2023-01-09 23:10:30 +09:00
if err != nil {
return err
}
dest[i] = v
}
r.currentRow++
return nil
}
func (r *rows) rowsLen() int {
r.onceRowsLen.Do(func() {
2024-04-27 15:34:02 +09:00
r._rowsLen = r.rowsArray.Length()
2023-01-09 23:10:30 +09:00
})
return r._rowsLen
}