Skip to content

Commit f96b394

Browse files
authored
Fix panic on format nil *fmt.Stringer type value (#1200)
1 parent 51cea28 commit f96b394

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

bind.go

+5
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,11 @@ func format(tz *time.Location, scale TimeUnit, v any) (string, error) {
306306
}
307307
return fmt.Sprintf("[%s]", val), nil
308308
case fmt.Stringer:
309+
if v := reflect.ValueOf(v); v.Kind() == reflect.Pointer &&
310+
v.IsNil() &&
311+
v.Type().Elem().Implements(reflect.TypeOf((*fmt.Stringer)(nil)).Elem()) {
312+
return "NULL", nil
313+
}
309314
return quote(v.String()), nil
310315
case column.OrderedMap:
311316
values := make([]string, 0)

tests/issues/1200_pr_test.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package issues
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/ClickHouse/clickhouse-go/v2"
9+
clickhouse_tests "github.com/ClickHouse/clickhouse-go/v2/tests"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
func Test1200(t *testing.T) {
14+
var (
15+
conn, err = clickhouse_tests.GetConnection("issues", clickhouse.Settings{
16+
"max_execution_time": 60,
17+
"allow_experimental_object_type": true,
18+
}, nil, &clickhouse.Compression{
19+
Method: clickhouse.CompressionLZ4,
20+
})
21+
)
22+
ctx := context.Background()
23+
require.NoError(t, err)
24+
const ddl = "CREATE TABLE test_1200 (id UInt32, null_str Nullable(FixedString(5))) Engine MergeTree() ORDER BY tuple()"
25+
require.NoError(t, conn.Exec(ctx, ddl))
26+
defer func() {
27+
conn.Exec(ctx, "DROP TABLE IF EXISTS test_1200")
28+
}()
29+
30+
v := "value"
31+
32+
tests := []struct {
33+
name string
34+
value fmt.Stringer
35+
want *string
36+
}{
37+
{
38+
name: "fmt.Stringer implemented struct value",
39+
value: Test1200NullStr{underlying: v},
40+
want: &v,
41+
},
42+
{
43+
name: "nil value",
44+
value: nil,
45+
want: nil,
46+
},
47+
{
48+
name: "fmt.Stringer implemented struct pointer value",
49+
value: &Test1200NullStr{underlying: v},
50+
want: &v,
51+
},
52+
{
53+
name: "fmt.Stringer implemented struct typed-nil value",
54+
value: (*Test1200NullStr)(nil),
55+
want: nil,
56+
},
57+
}
58+
for i, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
id := i + 1
61+
err = conn.Exec(ctx, "INSERT INTO test_1200 (id, null_str) VALUES (?, ?)", id, tt.value)
62+
require.NoError(t, err)
63+
64+
var got *string
65+
err = conn.QueryRow(ctx, "SELECT null_str FROM test_1200 WHERE id = ?", id).Scan(&got)
66+
require.NoError(t, err)
67+
68+
if tt.want == nil {
69+
require.Nil(t, got)
70+
} else {
71+
require.NotNil(t, got)
72+
require.Equal(t, *tt.want, *got)
73+
}
74+
})
75+
}
76+
}
77+
78+
type Test1200NullStr struct {
79+
underlying string
80+
}
81+
82+
func (nc Test1200NullStr) String() string {
83+
return nc.underlying
84+
}

0 commit comments

Comments
 (0)