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

[QUESTION] Decoding a column of type Array(Tuple(String, String)) #402

Open
shteinitz opened this issue May 21, 2024 · 1 comment
Open

Comments

@shteinitz
Copy link

shteinitz commented May 21, 2024

What is the correct way to decode a column whose ClickHouse type is Array(Tuple(String, String)) and map it to a Go [][]string?

For example, given the following SQL query:

SELECT [('one', 'two'), ('three', 'four')] AS arr;

...how do I replace the TODO comment with something that will map each tuple to a [][]string?

func arrayOfTuples() {
	ctx := context.Background()
	c, err := ch.Dial(ctx, ch.Options{Address: "localhost:9000"})
	if err != nil {
		panic(err)
	}

	arr := new(proto.ColArr[proto.ColTuple])
	if err := c.Do(ctx, ch.Query{
		Body: "SELECT [('one', 'two'), ('three', 'four')] AS arr",
		Result: proto.Results{
			{Name: "arr", Data: arr},
		},
		OnResult: func(ctx context.Context, b proto.Block) error {
			// TODO: Decode b and map its values to `[][]string`
			return nil
		},
	}); err != nil {
		panic(err)
	}
}

The difficulty is in figuring out how to allocate a proto.ColumnOf[proto.ColTuple], where ColTuple is of type tuple(string, string), and how to properly decode a block into it.

The equivalent clickhouse-go code works smoothly:

func clickhouseGoArrayOfTuples() {
	conn, err := clickhousego.Open(&clickhousego.Options{
		Addr: []string{"127.0.0.1:9000"},
		Auth: clickhousego.Auth{
			Database: "default",
			Username: "default",
			Password: "",
		},
	})
	if err != nil {
		panic(err)
	}

	rows, err := conn.Query(context.Background(), "SELECT [('one', 'two'), ('three', 'four')] AS arr")
	if err != nil {
		panic(err)
	}

	for rows.Next() {
		var arr [][]string
		if err := rows.Scan(&arr); err != nil {
			panic(err)
		}
		fmt.Println(arr)
	}
}
@jkaflik
Copy link
Contributor

jkaflik commented May 28, 2024

Hi @shteinitz

We got the same ask at #227
ch-go doesn't provide an interface to interact with Array( Tuple() ). Tuple is implemented a bit differently compared to other column types.

When supported, interacting with the result would look like this:

package main

import (
	"context"
	"fmt"

	"github.com/ClickHouse/ch-go"
	"github.com/ClickHouse/ch-go/proto"
	"github.com/go-faster/errors"
)

func arrayOfTuples() ([][]string, error) {
	ctx := context.Background()
	c, err := ch.Dial(ctx, ch.Options{Address: "localhost:9000"})
	if err != nil {
		panic(err)
	}

	arr := &proto.ColArr[[]string]{
		Data: &proto.ColTuple{&proto.ColStr{}, &proto.ColStr{}},
	}
	if err := c.Do(ctx, ch.Query{
		Body: "SELECT [('one', 'two'), ('three', 'four')] AS arr",
		Result: proto.Results{
			{Name: "arr", Data: &arr},
		},
	}); err != nil {
		return nil, err
	}

	if arr.Rows() != 1 {
		return nil, errors.New("expected one row")
	}

	var result [][]string
	for _, row := range arr.Data.Row(0) {
		var tuple []string
		for _, col := range row.(proto.ColTuple) {
			switch col := col.(type) {
			case *proto.ColStr:
				tuple = append(tuple, col.First())
			default:
				return nil, errors.Errorf("unexpected column type in tuple: %T, expected proto.ColStr", col)
			}
		}
		result = append(result, tuple)
	}

	return result, nil
}

func main() {
	res, err := arrayOfTuples()
	if err != nil {
		panic(err)
	}
	fmt.Println(res)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants