Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An error occurs when querying fields of type Tuple(Tuple(UInt16, UInt16), Tuple(UInt16, UInt16)) #1245

Closed
monchickey opened this issue Mar 22, 2024 · 1 comment · Fixed by #1249
Assignees
Labels

Comments

@monchickey
Copy link

Describe the bug

When the field type in the data table is Tuple(Tuple(UInt16, UInt16), Tuple(UInt16, UInt16)), an error will be reported when querying using the database/sql interface. An error will also be reported when using the native interface to query without specifying a specific type.

Steps to reproduce

  1. CREATE TABLE
    CREATE TABLE test_tuple
    (
        `id` Int32,
        `segment` Tuple(Tuple(UInt16, UInt16), Tuple(UInt16, UInt16))
    )
    ENGINE = Memory
  2. INSERT samples data
    insert into test_tuple values (1, ((1,3),(8,9)))
    insert into test_tuple values (2, ((2,6),(10,10)))
  3. Verify write
    select * from test_tuple
    ┌─id─┬─segment─────────┐
    │  1 │ ((1,3),(8,9))   │
    │  2 │ ((2,6),(10,10)) │
    └────┴─────────────────┘
    

Expected behaviour

The following code runs the query and it should execute normally.

Code example

1. Query using database/sql interface

func TestSqlNestedTuple(t *testing.T) {
	conn := clickhouse.OpenDB(&clickhouse.Options{
		Addr: []string{"127.0.0.1:9000"},
		Auth: clickhouse.Auth{
			Database: "default",
			Username: "default",
			Password: "",
		},
		Debug: true,
		Debugf: func(format string, v ...any) {
			fmt.Printf(format+"\n", v...)
		},
	})
	defer conn.Close()

	rows, err := conn.Query("SELECT * FROM test_tuple")
	assert.Nil(t, err)
	defer rows.Close()
	for rows.Next() {
	}
}

The error message:

--- FAIL: TestSqlNestedTuple (0.01s)
panic: reflect: call of reflect.Value.Set on zero Value [recovered]
        panic: reflect: call of reflect.Value.Set on zero Value

goroutine 6 [running]:
testing.tRunner.func1.2({0x9f5720, 0xc000012240})
        /usr/local/go/src/testing/testing.go:1545 +0x238
testing.tRunner.func1()
        /usr/local/go/src/testing/testing.go:1548 +0x397
panic({0x9f5720?, 0xc000012240?})
        /usr/local/go/src/runtime/panic.go:914 +0x21f
reflect.flag.mustBeExportedSlow(0xc00009d350?)
        /usr/local/go/src/reflect/value.go:247 +0xb6
reflect.flag.mustBeExported(...)
        /usr/local/go/src/reflect/value.go:241
reflect.Value.Set({0x9c2160?, 0xc000012210?, 0x9c2160?}, {0x0?, 0x0?, 0xc0000e7b50?})
        /usr/local/go/src/reflect/value.go:2255 +0x9a
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*Tuple).ScanRow(0xc00003f140?, {0xc00003f140?, 0xc000012210?}, 0xa06f60?)
        /opt/gopath/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.22.2/lib/column/tuple.go:481 +0x11f
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*Tuple).Row(0xc000092690, 0x9ccb40?, 0x0)
        /opt/gopath/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.22.2/lib/column/tuple.go:139 +0xce
github.com/ClickHouse/clickhouse-go/v2.(*stdRows).Next(0xc00021e170, {0xc00021c2a0, 0x2, 0x0?})
        /opt/gopath/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.22.2/clickhouse_std.go:396 +0x2e8
database/sql.(*Rows).nextLocked(0xc000234090)
        /usr/local/go/src/database/sql/sql.go:3019 +0x107
database/sql.(*Rows).Next.func1()
        /usr/local/go/src/database/sql/sql.go:2994 +0x29
database/sql.withLock({0xbdc6a8, 0xc0002340c8}, 0xc0000e7d90)
        /usr/local/go/src/database/sql/sql.go:3502 +0x82
database/sql.(*Rows).Next(0xc000234090)
        /usr/local/go/src/database/sql/sql.go:2993 +0x85
ch-go-tuple_test.TestSqlNestedTuple(0x0?)
        /opt/programming/ch-go-tuple/main_test.go:92 +0x1a5
testing.tRunner(0xc0000ec4e0, 0xb43920)
        /usr/local/go/src/testing/testing.go:1595 +0xff
created by testing.(*T).Run in goroutine 1
        /usr/local/go/src/testing/testing.go:1648 +0x3ad

panic at rows.Next

2. Query using native interface (Use reflection type to receive)

func TestNativeNestedTuple(t *testing.T) {
	conn, err := clickhouse.Open(&clickhouse.Options{
		Addr: []string{"127.0.0.1:9000"},
		Auth: clickhouse.Auth{
			Database: "default",
			Username: "default",
			Password: "",
		},
		Debug: true,
		Debugf: func(format string, v ...any) {
			fmt.Printf(format+"\n", v...)
		},
	})
	assert.Nil(t, err)
	defer conn.Close()

	rows, err := conn.Query(context.Background(), "SELECT * FROM test_tuple")
	assert.Nil(t, err)
	defer rows.Close()
	for rows.Next() {
		columns := rows.Columns()
		columnTypes := rows.ColumnTypes()
		results := make([]interface{}, len(columns))
		for i, columnType := range columnTypes {
			results[i] = reflect.New(columnType.ScanType()).Interface()

			fmt.Printf("column: %s, type: %s, value type: %v\n",
				columnType.Name(), columnType.ScanType(), columnType.DatabaseTypeName())
		}

		err := rows.Scan(results...)
		assert.Nil(t, err)
	}
}

The error message:

column: id, type: int32, value type: Int32
column: segment, type: []interface {}, value type: Tuple(Tuple(UInt16, UInt16), Tuple(UInt16, UInt16))
--- FAIL: TestNativeNestedTuple (0.01s)
panic: reflect: call of reflect.Value.Set on zero Value [recovered]
        panic: reflect: call of reflect.Value.Set on zero Value

goroutine 6 [running]:
testing.tRunner.func1.2({0x9f56e0, 0xc000012258})
        /usr/local/go/src/testing/testing.go:1545 +0x238
testing.tRunner.func1()
        /usr/local/go/src/testing/testing.go:1548 +0x397
panic({0x9f56e0?, 0xc000012258?})
        /usr/local/go/src/runtime/panic.go:914 +0x21f
reflect.flag.mustBeExportedSlow(0x67?)
        /usr/local/go/src/reflect/value.go:247 +0xb6
reflect.flag.mustBeExported(...)
        /usr/local/go/src/reflect/value.go:241
reflect.Value.Set({0x9c20e0?, 0xc000012228?, 0x9c20e0?}, {0x0?, 0x0?, 0x0?})
        /usr/local/go/src/reflect/value.go:2255 +0x9a
github.com/ClickHouse/clickhouse-go/v2/lib/column.(*Tuple).ScanRow(0xc000024120?, {0xc00003f1c0?, 0xc000012228?}, 0x4b6ef1?)
        /opt/gopath/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.22.2/lib/column/tuple.go:481 +0x11f
github.com/ClickHouse/clickhouse-go/v2.scan(0xc00003f040, 0x1, {0xc000080b40?, 0x2, 0x0?})
        /opt/gopath/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.22.2/scan.go:82 +0x19d
github.com/ClickHouse/clickhouse-go/v2.(*rows).Scan(0xbda700?, {0xc000080b40?, 0xab2c6f?, 0x25?})
        /opt/gopath/pkg/mod/github.com/!click!house/clickhouse-go/v2@v2.22.2/clickhouse_rows.go:78 +0xa5
ch-go-tuple_test.TestNativeNestedTuple(0x0?)
        /opt/programming/ch-go-tuple/main_test.go:66 +0x309
testing.tRunner(0xc0000ec820, 0xb438f0)
        /usr/local/go/src/testing/testing.go:1595 +0xff
created by testing.(*T).Run in goroutine 1
        /usr/local/go/src/testing/testing.go:1648 +0x3ad

panic at rows.Scan, The ScanType of column segment is returned as []interface{}, Using the [][]interface{} type receives normally

	for rows.Next() {
		columns := rows.Columns()
		columnTypes := rows.ColumnTypes()
		results := make([]interface{}, len(columns))
		for i, columnType := range columnTypes {
			if columnType.Name() == "segment" {
				var seg [][]interface{}
				results[i] = reflect.New(reflect.TypeOf(seg)).Interface()
			} else {
				results[i] = reflect.New(columnType.ScanType()).Interface()
			}

			fmt.Printf("column: %s, type: %s, value type: %v\n",
				columnType.Name(), columnType.ScanType(), columnType.DatabaseTypeName())
		}

		err := rows.Scan(results...)
		assert.Nil(t, err)
	}

This code can run successfully.

Configuration

Environment

  • Client version: v2.22.2
  • Language version: Go 1.21.2
  • OS: Client Debian 11, Server Ubuntu 22.04.3 LTS
  • Interface: ClickHouse API / database/sql compatible driver

ClickHouse server

  • ClickHouse Server version: 23.9.1.1
  • ClickHouse Server non-default settings, if any: none
  • CREATE TABLE statements for tables involved: exist in previous steps
@jkaflik
Copy link
Contributor

jkaflik commented Mar 25, 2024

Hi @monchickey

I've addressed panic in this issue: #1249

Unnamed tuple scan is supported only against struct, map or slice. If you want to use any/interface{} use named tuple.

@jkaflik jkaflik self-assigned this Mar 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants