ch

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package ch models the ClickHouse type system.

Unlike Postgres, ClickHouse has no type catalog and no OIDs: a type is named structurally, and the name carries everything about it. Nullability is part of the type — Nullable(String) — rather than something to infer, and an enum's labels live in the type itself rather than in a named catalog object. So where internal/pg resolves OIDs against pg_catalog, this package parses type names into a tree.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsIdentifier added in v0.2.0

func IsIdentifier(t Type) bool

IsIdentifier reports whether t is ClickHouse's Identifier parameter type, which names a table or column rather than carrying a value.

It is the one parameter type pggen substitutes into the query text instead of binding: see SubstituteIdentifiers.

func IsNullable

func IsNullable(t Type) bool

IsNullable reports whether values of t can be null, looking through the LowCardinality encoding that does not affect nullability.

func RenameParams

func RenameParams(sql string, rename func(string) string) (string, error)

RenameParams rewrites each {name:Type} to {rename(name):Type}.

It exists because ClickHouse carries server-side query parameters in the same map as query settings, and `limit` and `offset` are both real settings. A parameter named after one of them makes the server reject the query with "Cannot parse quoted string" — and it does so even for a query that never mentions the parameter, since the collision happens while the settings are read, before the SQL is parsed.

Only inference sends parameters that way, so only inference needs this: it describes a copy of the query whose parameters are renamed out of the settings namespace. Generated code is unaffected, because it binds client-side through cast(@name AS Type); see RewriteParams.

func RewriteParams

func RewriteParams(sql string) (string, error)

RewriteParams turns each {name:Type} into cast(@name AS Type).

It exists because ClickHouse's server-side parameters travel as text, and clickhouse-go does not render every Go value into text the server will accept: a time.Time arrives as a Unix timestamp, and a uuid.UUID or a decimal.Decimal arrives quoted, all of which the server rejects. The @name form uses the driver's client-side binding instead, which renders every type correctly, and the cast keeps the declared type explicit.

The rewrite happens at generation time, so the query file itself stays a query you can paste into clickhouse-client.

func SingleStatement

func SingleStatement(sql string) (string, error)

SingleStatement returns sql as one statement, with comments and the statement terminator removed.

It exists because inference does not send the query as written: it splices it into DESCRIBE (...) or after EXPLAIN AST, where a trailing semicolon is a syntax error and a trailing -- comment swallows whatever follows it. Rather than have each wrapper defend itself, they all get a statement that has neither.

func SplitStatements

func SplitStatements(sql string) []string

SplitStatements splits sql on the semicolons that separate statements, ignoring any inside a string literal, a quoted identifier, or a comment.

ClickHouse runs one statement per call, so a schema file has to be taken apart before it can be loaded. Comments are dropped rather than carried along, so that a file ending in one does not produce a statement with no query in it.

func SubstituteIdentifiers added in v0.2.0

func SubstituteIdentifiers(sql string, value func(name string) string) (string, error)

SubstituteIdentifiers replaces each {name:Identifier} in sql with value(name), leaving every other parameter alone. A name value returns "" for is left as it was.

ClickHouse can bind an Identifier parameter itself, and does it safely — it quotes the value as a single identifier, so an injection attempt becomes an unknown table rather than SQL. pggen cannot use that: clickhouse-go switches a query to server-side parameters the moment its text contains any {…:…}, and server-side parameters travel as text that the driver renders wrongly for time.Time, uuid.UUID and decimal.Decimal — which is the whole reason RewriteParams exists. One Identifier parameter would therefore break every other parameter in the same query.

So the identifier is substituted into the text before the driver sees it, and the generated code checks the value is a plain identifier first.

func ZeroLiteral

func ZeroLiteral(t Type) (string, error)

ZeroLiteral renders a value of t as ClickHouse text.

It exists because of an asymmetry with Postgres. Postgres infers a prepared statement's parameter types server-side, so pggen never supplies values to learn a query's shape. ClickHouse has no PREPARE: DESCRIBE parses the query and fails with "Substitution `x` is not set" unless every parameter has a value, even though the values never affect the result columns. So inference binds a throwaway literal for each parameter, and this produces it.

An Identifier parameter never reaches here: it names a table or column rather than carrying a value, and inference substitutes a stand-in name into the query text instead of binding one. See ch.SubstituteIdentifiers.

Types

type Array

type Array struct{ Elem Type }

Array is Array(T).

func (Array) ElemType

func (t Array) ElemType() sqltype.Type

ElemType implements sqltype.ArrayType, which the Go code generator type asserts on to check that a --go-type slice override is backed by an array. The return type has to be sqltype.Type and not ch.Type: Go matches a method set exactly, so narrowing it here would silently fail the assertion.

func (Array) Key

func (t Array) Key() string

func (Array) String

func (t Array) String() string

type DateTime

type DateTime struct{ TZ string }

DateTime is DateTime, optionally carrying a timezone.

func (DateTime) Key

func (t DateTime) Key() string

func (DateTime) String

func (t DateTime) String() string

type DateTime64

type DateTime64 struct {
	Precision int
	TZ        string
}

DateTime64 is DateTime64(P), optionally carrying a timezone.

func (DateTime64) Key

func (t DateTime64) Key() string

func (DateTime64) String

func (t DateTime64) String() string

type Decimal

type Decimal struct{ Precision, Scale int }

Decimal is Decimal(P, S). The Decimal32/64/128/256(S) spellings parse to this with the precision their width implies.

func (Decimal) Key

func (t Decimal) Key() string

func (Decimal) String

func (t Decimal) String() string

type Enum

type Enum struct {
	Bits   int      // 8 or 16
	Labels []string // in declaration order
	Values []int16  // Values[i] is the stored value of Labels[i]
}

Enum is Enum8 or Enum16. ClickHouse enums are anonymous: the labels are part of the type rather than a named object, so two columns with the same labels have the same type.

func (Enum) Key

func (t Enum) Key() string

func (Enum) String

func (t Enum) String() string

type FixedString

type FixedString struct{ N int }

FixedString is FixedString(N), a fixed-width byte string.

func (FixedString) Key

func (t FixedString) Key() string

func (FixedString) String

func (t FixedString) String() string

type InsertTarget added in v0.1.2

type InsertTarget struct {
	// Table as the query spells it, qualified or not, with any quoting
	// removed: `source`.`raw` and source.raw both give "source.raw".
	Table string
	// Columns names in the order the query lists them. Empty for an INSERT
	// that gives no column list, which is legal and means every column.
	Columns []string
}

InsertTarget is where an INSERT writes: the table it names, and the columns it lists, if it lists any.

func ScanInsertTarget added in v0.1.2

func ScanInsertTarget(sql string) (InsertTarget, bool)

ScanInsertTarget reads the target off an INSERT statement.

It exists because an INSERT is the one statement ClickHouse will not analyse without running: DESCRIBE and EXPLAIN QUERY TREE are both SELECT-shaped, so an :exec query gets no more than a parse. The target is the half that can still be checked cheaply — DESCRIBE TABLE resolves it and reads no data — and it is where a schema change lands.

ok is false for anything this cannot read a plain table out of: a statement that is not an INSERT, or an INSERT INTO FUNCTION, which writes through a table function that has no schema to describe. A false ok means "nothing to check here", never "this query is wrong".

type LowCardinality

type LowCardinality struct{ Elem Type }

LowCardinality is LowCardinality(T), a storage encoding that does not change how the value is represented in Go.

func (LowCardinality) Key

func (t LowCardinality) Key() string

func (LowCardinality) String

func (t LowCardinality) String() string

type Map

type Map struct{ KeyType, ValType Type }

Map is Map(K, V).

func (Map) Key

func (t Map) Key() string

func (Map) String

func (t Map) String() string

type Nullable

type Nullable struct{ Elem Type }

Nullable is Nullable(T). ClickHouse reports this exactly, so pggen never has to guess whether a column can be null.

func (Nullable) Key

func (t Nullable) Key() string

func (Nullable) String

func (t Nullable) String() string

type Param

type Param struct {
	Name string
	Type Type
}

Param is a {name:Type} query parameter.

func ScanParams

func ScanParams(sql string) ([]Param, error)

ScanParams finds the ClickHouse query parameters in sql, in order of first appearance.

This is where ClickHouse differs most from Postgres. Postgres infers a parameter's type server-side from where it appears, so pggen never has to be told. ClickHouse spells a parameter {name:Type} with the type written by hand, which means the type is already in the query text and no round trip is needed to learn it.

A parameter may appear more than once; repeats must agree on the type and collapse to a single input.

type Scalar

type Scalar struct{ Name string }

Scalar is a type with no parameters, like String, Int64, or UUID.

func (Scalar) Key

func (t Scalar) Key() string

Key implements sqltype.Type. A ClickHouse type is identified by its canonical name, since that name fully describes it.

func (Scalar) String

func (t Scalar) String() string

type Tuple

type Tuple struct {
	Names []string // nil for a positional tuple
	Elems []Type
}

Tuple is Tuple(...), either named — Tuple(a UInt8, b String) — or positional.

func (Tuple) Key

func (t Tuple) Key() string

func (Tuple) String

func (t Tuple) String() string

type Type

type Type interface {
	sqltype.Type // String() is the canonical spelling; Key() namespaces it
	// contains filtered or unexported methods
}

Type is a ClickHouse type. String reports the canonical ClickHouse spelling, which is also the type's identity.

func MustParse

func MustParse(name string) Type

MustParse is Parse for type names known at compile time, like the entries in a known-type table.

func Parse

func Parse(name string) (Type, error)

Parse turns a ClickHouse type name into a Type. The names come from DESCRIBE, so they are the canonical spellings the server produces, like "LowCardinality(Nullable(String))" or "Enum8('MOC' = 1, 'SMO' = 2)".

A type pggen has no Go mapping for still parses, into Unsupported, so the caller can report which type it choked on rather than where it stopped lexing.

func Payload

func Payload(t Type) Type

Payload strips both LowCardinality and Nullable to reach the underlying value type.

func Unwrap

func Unwrap(t Type) Type

Unwrap strips the LowCardinality encoding, which says how a value is stored rather than what it is. It keeps Nullable, which does change the Go type.

type Unsupported

type Unsupported struct{ Raw string }

Unsupported is a type that parsed but that pggen has no Go mapping for, like Nested or AggregateFunction. Keeping it lets errors name the type instead of failing to lex.

func (Unsupported) Key

func (t Unsupported) Key() string

func (Unsupported) String

func (t Unsupported) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL