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

ScanStruct with a Nullable tuple value results in pointer value as text #1446

Open
5 of 9 tasks
Viktor-Nordell opened this issue Dec 5, 2024 · 0 comments
Open
5 of 9 tasks

Comments

@Viktor-Nordell
Copy link

Observed

When using ScanStruct to scan a tuple containing a nullable string the driver will insert the pointer address as a string into the struct.

Or with other words:

  1. Row containing tuple with a Nullable string.
  2. ScanStruct the row into a struct with a string field.
  3. A pointer is stored into the field as text, e.g. '0xc00052f790'

(The behavior is correct when not using nullable values)

Expected behaviour

Either the ScanStruct operation should fail, or null values are mapped to "".

Code example

The code does the following:

  1. Creates a new table with a tuple.
  2. Inserts test data.
  3. Reads the row into Row using ScanStruct.
  4. The tuple values in the struct (myRow.MyTuple.TupleOne) now contains a pointer as string, instead of the actual value.
package code

type Tuple struct {
	TupleOne string `ch:"tuple_one"`
	TupleTwo string `ch:"tuple_two"`
}

type Row struct {
	MyId    uint64 `ch:"my_id"`
	MyTuple Tuple  `ch:"my_tuple"`
}

func TestBug(t *testing.T) {
	ctx := context.Background()

        db := // Db setup code

	createdDb := `
create table if not exists my_table(
    my_id    UInt64,
    my_tuple Tuple(tuple_one Nullable(String), tuple_two Nullable(String))
) engine = MergeTree primary key (my_id) order by (my_id)
`

	if err := db.Exec(ctx, createdDb); err != nil {
		t.Fatal(err)
	}

	if err := db.Exec(ctx, "insert into my_table(my_id, my_tuple) values (1, tuple('one', 'two'))"); err != nil {
		t.Fatal(err)
	}

	myRow := Row{}

	if err := db.QueryRow(ctx, "select * from my_table limit 1").ScanStruct(&myRow); err != nil {
		t.Fatal(err)
	}

	if myRow.MyTuple.TupleOne != "one" {
		t.Errorf("Expected 'one' but got '%s'", myRow.MyTuple.TupleOne)
	}

	if myRow.MyTuple.TupleTwo != "two" {
		t.Errorf("Expected 'two' but got '%s'", myRow.MyTuple.TupleTwo)
	}
}

The result will be:

    bug_test.go:48: Expected 'one' but got '0xc00052f790'
    bug_test.go:52: Expected 'two' but got '0xc00052f7c0'

Details

Environment

  • clickhouse-go version: v2.30.0
  • Interface: ClickHouse API / database/sql compatible driver
  • Go version: 1.23.4
  • Operating system: linux/amd64
  • ClickHouse version: 24.5.4
  • Is it a ClickHouse Cloud? No
  • ClickHouse Server non-default settings, if any: None
  • CREATE TABLE statements for tables involved: See code example
  • Sample data for all these tables, use [clickhouse-obfuscator]: (https://github.com/ClickHouse/ClickHouse/blob/master/programs/obfuscator/Obfuscator.cpp#L42-L80) if necessary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant