@@ -99,3 +99,109 @@ func TestSelectScanStruct(t *testing.T) {
9999 }
100100 })
101101}
102+
103+ func TestArrayTupleNullableFieldPanic (t * testing.T ) {
104+ TestProtocols (t , func (t * testing.T , protocol clickhouse.Protocol ) {
105+ conn , err := GetNativeConnection (t , protocol , nil , nil , & clickhouse.Compression {
106+ Method : clickhouse .CompressionLZ4 ,
107+ })
108+ require .NoError (t , err )
109+
110+ ctx := context .Background ()
111+ require .NoError (t , conn .Exec (ctx , `
112+ CREATE TABLE test_table (
113+ c1 Int32,
114+ c2 Array(Tuple(c3 String, c4 Nullable(Int32)))
115+ ) Engine MergeTree() ORDER BY tuple()
116+ ` ))
117+ defer func () {
118+ conn .Exec (ctx , "DROP TABLE IF EXISTS test_table" )
119+ }()
120+ type subRowType struct {
121+ Col3 string `ch:"c3"`
122+ // THIS FIELD causes PANIC IF IT'S NULLABLE
123+ Col4 * int32 `ch:"c4"`
124+ }
125+ type rowType struct {
126+ Col1 int32 `ch:"c1"`
127+ Col2 []subRowType `ch:"c2"`
128+ }
129+ nonNull := int32 (42 )
130+ element1 := rowType {
131+ Col1 : 1 ,
132+ Col2 : []subRowType {
133+ {Col3 : "a" , Col4 : & nonNull },
134+ {Col3 : "b" , Col4 : nil },
135+ },
136+ }
137+
138+ batch , err := conn .PrepareBatch (ctx , "INSERT INTO test_table" )
139+ require .NoError (t , err )
140+ require .NoError (t , batch .AppendStruct (& element1 ))
141+ require .NoError (t , batch .Send ())
142+
143+ rows , err := conn .Query (ctx , "SELECT c1, c2 FROM test_table;" )
144+ require .NoError (t , err )
145+ var results []rowType
146+ for rows .Next () {
147+ var result rowType
148+ assert .NoError (t , rows .ScanStruct (& result ))
149+ results = append (results , result )
150+ }
151+ require .ElementsMatch (t , results , []rowType {element1 })
152+ require .NoError (t , rows .Close ())
153+ require .NoError (t , rows .Err ())
154+ })
155+ }
156+
157+ func TestArrayTupleNonNull (t * testing.T ) {
158+ TestProtocols (t , func (t * testing.T , protocol clickhouse.Protocol ) {
159+ conn , err := GetNativeConnection (t , protocol , nil , nil , & clickhouse.Compression {
160+ Method : clickhouse .CompressionLZ4 ,
161+ })
162+ require .NoError (t , err )
163+
164+ ctx := context .Background ()
165+ require .NoError (t , conn .Exec (ctx , `
166+ CREATE TABLE test_table (
167+ c1 Int32,
168+ c2 Array(Tuple(c3 String, c4 Int32))
169+ ) Engine MergeTree() ORDER BY tuple()
170+ ` ))
171+ defer func () {
172+ conn .Exec (ctx , "DROP TABLE IF EXISTS test_table" )
173+ }()
174+ type subRowType struct {
175+ Col3 string `ch:"c3"`
176+ Col4 int32 `ch:"c4"`
177+ }
178+ type rowType struct {
179+ Col1 int32 `ch:"c1"`
180+ Col2 []subRowType `ch:"c2"`
181+ }
182+ element1 := rowType {
183+ Col1 : 1 ,
184+ Col2 : []subRowType {
185+ {Col3 : "a" , Col4 : 42 },
186+ {Col3 : "b" , Col4 : 52 },
187+ },
188+ }
189+
190+ batch , err := conn .PrepareBatch (ctx , "INSERT INTO test_table" )
191+ require .NoError (t , err )
192+ require .NoError (t , batch .AppendStruct (& element1 ))
193+ require .NoError (t , batch .Send ())
194+
195+ rows , err := conn .Query (ctx , "SELECT c1, c2 FROM test_table;" )
196+ require .NoError (t , err )
197+ var results []rowType
198+ for rows .Next () {
199+ var result rowType
200+ assert .NoError (t , rows .ScanStruct (& result ))
201+ results = append (results , result )
202+ }
203+ require .ElementsMatch (t , results , []rowType {element1 })
204+ require .NoError (t , rows .Close ())
205+ require .NoError (t , rows .Err ())
206+ })
207+ }
0 commit comments