From c5cc0d957b9213c9eb17b5c1c7ef551b5c363566 Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Mon, 28 Dec 2020 20:30:11 +0800 Subject: [PATCH] Suppport "type A map[string]interface{}" in named queries I'd like to use: type A map[string]interface{} db.NamedExec("...", A{ "named": "val", }) Because typing "map[string]interface{}" is a lot of work and looks kinda ugly. Previously this would panic, as the type assertion didn't work. So use reflection to convert it to map[string]interface{} if possible, and return a nicer error if not. --- named.go | 8 +++++++- sqlx_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/named.go b/named.go index 0c7f4b18..957ba3f8 100644 --- a/named.go +++ b/named.go @@ -383,7 +383,13 @@ func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Ma k := t.Kind() switch { case k == reflect.Map && t.Key().Kind() == reflect.String: - return bindMap(bindType, query, arg.(map[string]interface{})) + var m map[string]interface{} + if !t.ConvertibleTo(reflect.TypeOf(m)) { + return "", nil, fmt.Errorf("sqlx.bindNamedMapper: unsupported map type: %T", arg) + } + + m = reflect.ValueOf(arg).Convert(reflect.TypeOf(m)).Interface().(map[string]interface{}) + return bindMap(bindType, query, m) case k == reflect.Array || k == reflect.Slice: return bindArray(bindType, query, arg, m) default: diff --git a/sqlx_test.go b/sqlx_test.go index 4231c1ae..eb18bccc 100644 --- a/sqlx_test.go +++ b/sqlx_test.go @@ -1738,6 +1738,33 @@ func BenchmarkBindStruct(b *testing.B) { } } +func TestBindNamedMapper(t *testing.T) { + type A map[string]interface{} + m := reflectx.NewMapperFunc("db", NameMapper) + query, args, err := bindNamedMapper(DOLLAR, `select :x`, A{ + "x": "X!", + }, m) + if err != nil { + t.Fatal(err) + } + + got := fmt.Sprintf("%s %s", query, args) + want := `select $1 [X!]` + if got != want { + t.Errorf("\ngot: %q\nwant: %q", got, want) + } + + _, _, err = bindNamedMapper(DOLLAR, `select :x`, map[string]string{ + "x": "X!", + }, m) + if err == nil { + t.Fatal("err is nil") + } + if !strings.Contains(err.Error(), "unsupported map type") { + t.Errorf("wrong error: %s", err) + } +} + func BenchmarkBindMap(b *testing.B) { b.StopTimer() q1 := `INSERT INTO foo (a, b, c, d) VALUES (:name, :age, :first, :last)`