From adfe33a8782a092461cb870fb3316b551529a860 Mon Sep 17 00:00:00 2001 From: syumai Date: Sun, 26 Feb 2023 18:15:59 +0900 Subject: [PATCH 1/3] treat D1 driver row's integral float64 value as int64 --- cloudflare/d1/rows.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cloudflare/d1/rows.go b/cloudflare/d1/rows.go index 66dc4d1..3166ad7 100644 --- a/cloudflare/d1/rows.go +++ b/cloudflare/d1/rows.go @@ -4,6 +4,7 @@ import ( "database/sql/driver" "errors" "io" + "math" "sync" "syscall/js" @@ -58,8 +59,12 @@ func convertRowColumnValueToAny(v js.Value) (driver.Value, error) { case js.TypeNull: return nil, nil case js.TypeNumber: - // TODO: handle INTEGER type. - return v.Float(), nil + fv := v.Float() + // if the value can be treated as integer, return as int64. + if fv == math.Trunc(fv) { + return int64(fv), nil + } + return fv, nil case js.TypeString: return v.String(), nil case js.TypeObject: From 53627e79e8c7b4c491b186f5c573ce0daf6f3c2b Mon Sep 17 00:00:00 2001 From: syumai Date: Sun, 26 Feb 2023 18:16:16 +0900 Subject: [PATCH 2/3] fix d1-blog-server example to use normal integer type --- examples/d1-blog-server/app/handler.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/examples/d1-blog-server/app/handler.go b/examples/d1-blog-server/app/handler.go index 1e352eb..2e79145 100644 --- a/examples/d1-blog-server/app/handler.go +++ b/examples/d1-blog-server/app/handler.go @@ -108,20 +108,15 @@ ORDER BY created_at DESC; articles := []model.Article{} for rows.Next() { - var ( - title, body string - id, createdAt float64 // number value is always retrieved as float64. - ) - err = rows.Scan(&id, &title, &body, &createdAt) + var a model.Article + err = rows.Scan(&a.ID, &a.Title, &a.Body, &a.CreatedAt) if err != nil { - break + log.Println(err) + h.handleErr(w, http.StatusInternalServerError, + "failed to scan article") + return } - articles = append(articles, model.Article{ - ID: uint64(id), - Title: title, - Body: body, - CreatedAt: uint64(createdAt), - }) + articles = append(articles, a) } res := model.ListArticlesResponse{ Articles: articles, From a496d2d714b059a874bb08fe0b071355c47b9248 Mon Sep 17 00:00:00 2001 From: syumai Date: Sun, 26 Feb 2023 18:40:18 +0900 Subject: [PATCH 3/3] split isIntegralNumber func --- cloudflare/d1/rows.go | 13 +++++++++-- cloudflare/d1/rows_test.go | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 cloudflare/d1/rows_test.go diff --git a/cloudflare/d1/rows.go b/cloudflare/d1/rows.go index 3166ad7..47079e8 100644 --- a/cloudflare/d1/rows.go +++ b/cloudflare/d1/rows.go @@ -51,6 +51,15 @@ func (r *rows) Close() error { return nil } +// 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) +} + // 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 @@ -60,8 +69,8 @@ func convertRowColumnValueToAny(v js.Value) (driver.Value, error) { return nil, nil case js.TypeNumber: fv := v.Float() - // if the value can be treated as integer, return as int64. - if fv == math.Trunc(fv) { + // if the value can be treated as integral value, return as int64. + if isIntegralNumber(fv) { return int64(fv), nil } return fv, nil diff --git a/cloudflare/d1/rows_test.go b/cloudflare/d1/rows_test.go new file mode 100644 index 0000000..b218a62 --- /dev/null +++ b/cloudflare/d1/rows_test.go @@ -0,0 +1,44 @@ +package d1 + +import ( + "math" + "testing" +) + +func Test_isIntegralNumber(t *testing.T) { + tests := map[string]struct { + f float64 + want bool + }{ + "valid integral value": { + f: 1, + want: true, + }, + "invalid float value": { + f: 1.1, + want: false, + }, + "invalid NaN": { + f: math.NaN(), + want: false, + }, + "invalid +Inf": { + f: math.Inf(+1), + want: false, + }, + "invalid -Inf": { + f: math.Inf(-1), + want: false, + }, + } + for name, tc := range tests { + name := name + tc := tc + t.Run(name, func(t *testing.T) { + t.Parallel() + if got := isIntegralNumber(tc.f); got != tc.want { + t.Errorf("isIntegralNumber() = %v, want %v", got, tc.want) + } + }) + } +}