Skip to content

Named()/DateNamed() serialize server-side {name:Type} parameter values incorrectly for several types #1927

Description

@nm-yegor-parfenenka

Observed

When using server-side query parameters ({name:Type} syntax with clickhouse.Named / clickhouse.DateNamed), the driver serializes several value types into text that ClickHouse cannot parse back, causing the query to fail:

  1. Named("p", time.Time{}) for a {p:Date} parameter is sent as toDateTime('2026-05-15 00:00:00'), which the server rejects as a Date (code 38). The same reuse of the client-side formatter also affects Array(Date) / Array(DateTime) and Array(Bool) (codes 27 / 130).
  2. String values inside array parameters are escaped with \', which the parameter parser rejects (code 25). Plain string parameters containing a newline or tab fail under server-side TSV parsing (code 457).
  3. DateNamed formats the timestamp without a timezone, so the server interprets it in its own timezone — the bound instant shifts on any non-UTC server.

Expected behaviour

Values passed via Named / DateNamed for {name:Type} parameters should be serialized in the plain text format ClickHouse's parameter parser expects (e.g. 2026-05-15 for Date, true/false for Bool, unescaped/TSV-correct strings, timezone-anchored timestamps), so that any value valid for the declared type round-trips successfully.

Code example

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/ClickHouse/clickhouse-go/v2"
)

func main() {
	conn, err := clickhouse.Open(&clickhouse.Options{
		Addr: []string{"127.0.0.1:9000"},
		Auth: clickhouse.Auth{Database: "default", Username: "default", Password: "pass"},
	})
	if err != nil {
		panic(err)
	}
	ctx := context.Background()

	run := func(name, sql string, args ...any) {
		rows, err := conn.Query(ctx, sql, args...)
		if err != nil {
			fmt.Printf("%s\n    ERROR: %v\n", name, err)
			return
		}
		rows.Close()
		fmt.Printf("%s\n    OK\n", name)
	}

	d := time.Date(2026, 5, 15, 0, 0, 0, 0, time.UTC)

	// 1. Named(time.Time) sends toDateTime('...'), invalid as a Date literal.
	run("1. {p:Date} bound with Named(time.Time)",
		"SELECT {p:Date}", clickhouse.Named("p", d))

	// 1b/1c. Array element formatting reuses the same formatter.
	run("1b. {p:Array(Bool)} bound with []bool",
		"SELECT {p:Array(Bool)}", clickhouse.Named("p", []bool{true}))
	run("1c. {p:Array(Date)} bound with []time.Time",
		"SELECT {p:Array(Date)}", clickhouse.Named("p", []time.Time{d}))

	// 2a. Single quote in an array element is escaped as \', rejected by the parser.
	run("2a. {p:Array(String)} element containing a single quote",
		"SELECT {p:Array(String)}", clickhouse.Named("p", []string{"it's"}))

	// 2b. String parameters are re-parsed with TSV escaping; newline fails.
	run("2b. {p:String} with embedded newline",
		"SELECT {p:String}", clickhouse.Named("p", "a\nb"))

	// 3. DateNamed formats without a timezone; result shifts on non-UTC servers.
	var got time.Time
	if err := conn.QueryRow(ctx, "SELECT {p:DateTime64(9)}",
		clickhouse.DateNamed("p", d, clickhouse.NanoSeconds)).Scan(&got); err != nil {
		fmt.Printf("3. {p:DateTime64(9)} bound with DateNamed\n    ERROR: %v\n", err)
	} else {
		fmt.Printf("3. {p:DateTime64(9)} bound with DateNamed\n    bound unix=%d, got unix=%d (differs when server TZ is not UTC)\n",
			d.Unix(), got.Unix())
	}
}

Error log

1. {p:Date} bound with Named(time.Time)
    ERROR: code: 38, message: Cannot parse date here: toDateTime: value toDateTime('2026-05-15 00:00:00') cannot be parsed as Date for query parameter 'p'
1b. {p:Array(Bool)} bound with []bool
    ERROR: code: 130, message: Cannot read array from text, expected comma or end of array, found '1': value [1] cannot be parsed as Array(Bool) for query parameter 'p'
1c. {p:Array(Date)} bound with []time.Time
    ERROR: code: 27, message: Cannot parse input: expected '\'' before: 'toDateTime(\'2026-05-15 00:00:00\')]': value [toDateTime('2026-05-15 00:00:00')] cannot be parsed as Array(Date) for query parameter 'p'
2a. {p:Array(String)} element containing a single quote
    ERROR: code: 25, message: Cannot parse escape sequence: value ['it\ cannot be parsed as Array(String) for query parameter 'p'
2b. {p:String} with embedded newline
    ERROR: code: 457, message: Value a
b cannot be parsed as String for query parameter 'p' because it isn't parsed completely: only 1 of 3 bytes was parsed: a

Details

Environment

  • clickhouse-go version: v2.45.0
  • Interface: ClickHouse API
  • Go version: 1.26
  • Operating system: macOS (arm64) / Linux
  • ClickHouse version: 24.8
  • Is it a ClickHouse Cloud? No (local clickhouse/clickhouse-server:24.8)
  • ClickHouse Server non-default settings, if any: none (for case 3, set server TZ=Australia/Sydney to observe the shift)
  • CREATE TABLE statements for tables involved: n/a — reproduces with SELECT {p:Type}, no tables
  • Sample data for all these tables: n/a

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions