Observed
- Create a table with a single
Nullable(Decimal64(2))
- Insert
(NULL), (NULL), (NULL), (123.1), (NULL), (124.1), (NULL), (NULL), (NULL), (NULL)
- Read back the rows and observe the pointer not being reset when encountering a null value. The previous row's value is returned instead.
NULL, NULL, NULL, 123.1, 123.1, 124.1, 124.1, 124.1, 124.1, 124.1 returned
Expected behaviour
The pointer should be reset when encountering NULL and not return the previous row's value.
Code example
Reproduction example from the original issue modified for decimal.Decimal.
examples/clickhouse_api/dynamic_scan_types.go
package clickhouse_api
import (
"context"
"fmt"
"reflect"
"github.com/shopspring/decimal"
)
func DynamicScan() error {
conn, err := GetNativeConnection(nil, nil, nil)
if err != nil {
return err
}
if err := conn.Exec(context.Background(), "CREATE TABLE IF NOT EXISTS dynamic_scan_table (Col1 Nullable(Decimal64(2))) ENGINE = Memory"); err != nil {
return err
}
if err := conn.Exec(context.Background(), "INSERT INTO dynamic_scan_table VALUES (NULL), (NULL), (NULL), (123.1), (NULL), (124.1), (NULL), (NULL), (NULL), (NULL)"); err != nil {
return err
}
rows, err := conn.Query(context.Background(), "SELECT Col1 FROM dynamic_scan_table")
if err != nil {
return err
}
var (
columnTypes = rows.ColumnTypes()
vars = make([]interface{}, len(columnTypes))
)
for i := range columnTypes {
vars[i] = reflect.New(columnTypes[i].ScanType()).Interface()
}
for rows.Next() {
if err := rows.Scan(vars...); err != nil {
return err
}
for _, v := range vars {
switch v := v.(type) {
case **decimal.Decimal:
if *v == nil {
fmt.Println("NULL")
continue
}
fmt.Println(**v)
}
}
}
return nil
}
$ go test ./... -v -run DynamicScan
...
=== RUN TestDynamicScan
NULL
NULL
NULL
123.1
123.1
124.1
124.1
124.1
124.1
124.1
--- PASS: TestDynamicScan (0.06s)
Details
Note: seems to be working as expected if I implement a similar test for TestStdDynamicScan, in examples/std/dynamic_scan_types.go
=== RUN TestStdDynamicScan
NULL
NULL
NULL
123.1
NULL
124.1
NULL
NULL
NULL
NULL
--- PASS: TestStdDynamicScan (0.02s)
Environment
Observed
Nullable(Decimal64(2))(NULL), (NULL), (NULL), (123.1), (NULL), (124.1), (NULL), (NULL), (NULL), (NULL)NULL, NULL, NULL, 123.1, 123.1, 124.1, 124.1, 124.1, 124.1, 124.1returnedExpected behaviour
The pointer should be reset when encountering
NULLand not return the previous row's value.Code example
Reproduction example from the original issue modified for
decimal.Decimal.examples/clickhouse_api/dynamic_scan_types.goDetails
casefordecimal.Decimalhave been implemented in https://github.com/ClickHouse/clickhouse-go/blob/main/lib/column/nullable.go#L78-L103 ?Note: seems to be working as expected if I implement a similar test for
TestStdDynamicScan, inexamples/std/dynamic_scan_types.goEnvironment
clickhouse-goversion: 2.40.3CREATE TABLEstatements for tables involved: