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 ¶
- func IsIdentifier(t Type) bool
- func IsNullable(t Type) bool
- func RenameParams(sql string, rename func(string) string) (string, error)
- func RewriteParams(sql string) (string, error)
- func SingleStatement(sql string) (string, error)
- func SplitStatements(sql string) []string
- func SubstituteIdentifiers(sql string, value func(name string) string) (string, error)
- func ZeroLiteral(t Type) (string, error)
- type Array
- type DateTime
- type DateTime64
- type Decimal
- type Enum
- type FixedString
- type InsertTarget
- type LowCardinality
- type Map
- type Nullable
- type Param
- type Scalar
- type Tuple
- type Type
- type Unsupported
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsIdentifier ¶ added in v0.2.0
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 ¶
IsNullable reports whether values of t can be null, looking through the LowCardinality encoding that does not affect nullability.
func RenameParams ¶
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 ¶
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 ¶
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 ¶
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
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 ¶
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 ¶
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.
type DateTime ¶
type DateTime struct{ TZ string }
DateTime is DateTime, optionally carrying a timezone.
type DateTime64 ¶
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.
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.
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 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.
type Param ¶
Param is a {name:Type} query parameter.
func ScanParams ¶
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.
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 ¶
MustParse is Parse for type names known at compile time, like the entries in a known-type table.
func Parse ¶
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.
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