ClickHouse / clickhouse-go

Golang driver for ClickHouse
Apache License 2.0
2.92k stars 562 forks source link

wrong result for numeric and positional parameters check in query #935

Open dobergerator opened 1 year ago

dobergerator commented 1 year ago

When I build a request similar to this one

select my_other_field from my_other_table where toString((joinGet('my_db.my_table', 'my_field', cityHash64(lower(my_string_value))) as m_s_v) ? 'my_string_data' : '') <> $1

Driver returns "clickhouse [bind]: mixed named, numeric or positional parameters" and doesn't send query to Clickhouse, because of the check in bind() function

var bindNumericRe = regexp.MustCompile(\$[0-9]+) var bindPositionalRe = regexp.MustCompile([^\][?])

But if I send request with a CLI clickhouse-client, query works and returns data

Is it possible to fix this problem?

Environment

ClickHouse server

jkaflik commented 1 year ago

@dobergerator thanks for submitting this. Can you please try with a newest version? There was a fix released for bind mechanism some time ago.

jkaflik commented 1 year ago

I will check this case later. It seems it breaks on shorten if statement.

rogeryk commented 9 months ago

This problem is due to the conflict between positional parameter and ternary operation. e.g.

func main() {
    conn, err := connect()
    if err != nil {
        panic((err))
    }

    ctx := context.Background()
    rows, err := conn.Query(ctx, `select 1 > 2 ? 'hello': ? from test`, "world")
    if err != nil {
        panic(err)
    }

    for rows.Next() {
        var s string
        if err := rows.Scan(
            &s,
        ); err != nil {
            log.Fatal(err)
        }
        println(s)
    }

}

the above program will panic

panic: have no arg for param ? at last 1 positions

But you can add '\' to avoid

func main() {
    conn, err := connect()
    if err != nil {
        panic((err))
    }

    ctx := context.Background()
    rows, err := conn.Query(ctx, `select 1 > 2 \? 'hello': ? from test`, "world")
    if err != nil {
        panic(err)
    }

    for rows.Next() {
        var s string
        if err := rows.Scan(
            &s,
        ); err != nil {
            log.Fatal(err)
        }
        println(s)
    }

}

This program will output

world