Documentation
¶
Overview ¶
Package utils provides common utility functions used throughout the Housekeeper codebase.
This package contains shared utilities that are used by multiple packages to avoid code duplication and ensure consistent behavior across the application.
Identifier Utilities (identifier.go) ¶
The identifier utilities provide consistent handling of ClickHouse SQL identifiers, including proper backtick quoting for names that may contain special characters or reserved keywords.
Value Type Utilities (validation.go) ¶
The value type utilities provide proper validation for different data types commonly used in ClickHouse SQL generation, particularly for named collection parameters and other configuration values.
## BacktickIdentifier
The primary function for adding backticks to identifiers, handling both simple and qualified names:
// Simple identifier
name := utils.BacktickIdentifier("users")
// Result: `users`
// Qualified identifier
qualified := utils.BacktickIdentifier("analytics.events")
// Result: `analytics`.`events`
// Already backticked (not double-backticked)
existing := utils.BacktickIdentifier("`users`")
// Result: `users`
## BacktickQualifiedName
Specialized function for database-qualified names, commonly used for tables, views, and dictionaries:
db := "analytics" table := "events" qualified := utils.BacktickQualifiedName(&db, table) // Result: `analytics`.`events` // Without database prefix simple := utils.BacktickQualifiedName(nil, "users") // Result: `users`
## Helper Functions
Additional utilities for working with backticked identifiers:
// Check if already backticked
if utils.IsBackticked(name) {
// Already has backticks
}
// Remove backticks
clean := utils.StripBackticks("`database`.`table`")
// Result: database.table
## IsNumericValue and IsBooleanValue
Value type validation functions for properly formatting SQL values:
// Validate numeric values (uses strconv.ParseFloat)
if utils.IsNumericValue("123.45") {
// Can be used without quotes in SQL
}
// Validate boolean values (case-insensitive)
if utils.IsBooleanValue("TRUE") {
// Can be used without quotes in SQL
}
// Example usage for SQL value formatting
value := "123.45"
if utils.IsNumericValue(value) || utils.IsBooleanValue(value) {
sql += value // No quotes needed
} else {
sql += "'" + value + "'" // Add quotes for string values
}
Usage Guidelines ¶
These utilities should be used whenever generating or manipulating SQL identifiers to ensure consistent formatting across all generated DDL statements. They handle edge cases like:
- Identifiers that are already backticked
- Qualified names with multiple parts (database.schema.table)
- Empty strings and nil pointers
- Special characters in identifiers
The utilities are designed to be safe and idempotent - calling BacktickIdentifier on an already backticked identifier will not double-backtick it.
Index ¶
- func BacktickColumnName(name string) string
- func BacktickIdentifier(name string) string
- func BacktickQualifiedName(database *string, name string) string
- func IsBackticked(s string) bool
- func IsBooleanValue(value string) bool
- func IsNumericValue(value string) bool
- func Ptr[T any](v T) *T
- func StripBackticks(s string) string
- type SQLBuilder
- func (b *SQLBuilder) Alter(objectType string) *SQLBuilder
- func (b *SQLBuilder) As(expression string) *SQLBuilder
- func (b *SQLBuilder) Comment(comment string) *SQLBuilder
- func (b *SQLBuilder) Create(objectType string) *SQLBuilder
- func (b *SQLBuilder) CreateOrReplace(objectType string) *SQLBuilder
- func (b *SQLBuilder) Drop(objectType string) *SQLBuilder
- func (b *SQLBuilder) Engine(engine string) *SQLBuilder
- func (b *SQLBuilder) Escaped(value string) *SQLBuilder
- func (b *SQLBuilder) IfExists() *SQLBuilder
- func (b *SQLBuilder) IfNotExists() *SQLBuilder
- func (b *SQLBuilder) Modify(clause string) *SQLBuilder
- func (b *SQLBuilder) Name(name string) *SQLBuilder
- func (b *SQLBuilder) OnCluster(cluster string) *SQLBuilder
- func (b *SQLBuilder) QualifiedName(database *string, name string) *SQLBuilder
- func (b *SQLBuilder) QualifiedTo(database *string, name string) *SQLBuilder
- func (b *SQLBuilder) Raw(sql string) *SQLBuilder
- func (b *SQLBuilder) Rename(objectType string) *SQLBuilder
- func (b *SQLBuilder) String() string
- func (b *SQLBuilder) StringWithoutSemicolon() string
- func (b *SQLBuilder) To(name string) *SQLBuilder
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BacktickColumnName ¶ added in v0.1.8
BacktickColumnName adds backticks around a column name without splitting on dots. This is specifically for column names that may contain dots (like flattened nested columns).
Examples:
- "column" -> "`column`"
- "metadata.source" -> "`metadata.source`" (NOT split)
- "`column`" -> "`column`" (already backticked)
- "" -> ""
This function treats the entire input as a single identifier, unlike BacktickIdentifier which splits on dots for qualified names.
func BacktickIdentifier ¶
BacktickIdentifier adds backticks around an identifier, handling nested identifiers. It properly handles database.table.column style identifiers by backticking each part.
Examples:
- "table" -> "`table`"
- "database.table" -> "`database`.`table`"
- "db.schema.table" -> "`db`.`schema`.`table`"
- "`table`" -> "`table`" (already backticked, not double-backticked)
- "" -> ""
This function is used throughout the codebase for consistent identifier formatting in generated DDL statements.
func BacktickQualifiedName ¶
BacktickQualifiedName formats a qualified name (database.name) with proper backticks. If database is nil or empty, only the name is backticked.
Examples:
- ("analytics", "events") -> "`analytics`.`events`"
- (nil, "events") -> "`events`"
- ("", "events") -> "`events`"
This is commonly used for formatting table, view, and dictionary names that may include a database prefix.
func IsBackticked ¶
IsBackticked checks if a string is already wrapped in backticks.
Examples:
- "`table`" -> true
- "table" -> false
- "`db`.`table`" -> false (qualified name, not a single backticked identifier)
- "" -> false
func IsBooleanValue ¶
IsBooleanValue checks if a string represents a boolean value. This is case-insensitive and supports various boolean representations.
Examples:
- "true" -> true
- "TRUE" -> true
- "True" -> true
- "false" -> true
- "FALSE" -> true
- "1" -> false (use IsNumericValue for numeric booleans)
- "yes" -> false
- "" -> false
func IsNumericValue ¶
IsNumericValue checks if a string represents a valid numeric value. This uses strconv.ParseFloat to properly validate numeric formats, including integers, floats, and scientific notation.
Examples:
- "123" -> true
- "123.45" -> true
- "-123.45" -> true
- "1.23e-4" -> true (scientific notation)
- "1.23E+5" -> true (scientific notation)
- "abc" -> false
- "1.2.3" -> false (multiple decimal points)
- "" -> false
- "." -> false
- "-" -> false
func Ptr ¶ added in v0.1.9
func Ptr[T any](v T) *T
Ptr returns a pointer to the provided value v. This is useful for creating pointers to literals or temporary values.
func StripBackticks ¶
StripBackticks removes backticks from an identifier if present.
Examples:
- "`table`" -> "table"
- "table" -> "table"
- "`db`.`table`" -> "db.table"
- "" -> ""
Types ¶
type SQLBuilder ¶ added in v0.1.7
type SQLBuilder struct {
// contains filtered or unexported fields
}
SQLBuilder provides a fluent interface for building ClickHouse DDL statements. It handles common patterns like cluster injection, identifier backticking, and conditional clause building to reduce code duplication across the schema package.
Example usage:
sql := New().
CREATE("DATABASE").
Name("analytics").
OnCluster("production").
Engine("Atomic").
Comment("Analytics database").
String()
// Output: CREATE DATABASE `analytics` ON CLUSTER `production` ENGINE = Atomic COMMENT 'Analytics database';
func NewSQLBuilder ¶ added in v0.1.7
func NewSQLBuilder() *SQLBuilder
NewSQLBuilder creates a new SQLBuilder instance.
Example:
builder := sqlbuilder.NewSQLBuilder()
func (*SQLBuilder) Alter ¶ added in v0.1.7
func (b *SQLBuilder) Alter(objectType string) *SQLBuilder
Alter adds an Alter clause with the specified object type.
Example:
builder.Alter("DATABASE") // Alter DATABASE
builder.Alter("TABLE") // Alter TABLE
func (*SQLBuilder) As ¶ added in v0.1.7
func (b *SQLBuilder) As(expression string) *SQLBuilder
As adds an As clause for CREATE FUNCTION operations.
Example:
builder.As("(x) -> x * 2") // As (x) -> x * 2
func (*SQLBuilder) Comment ¶ added in v0.1.7
func (b *SQLBuilder) Comment(comment string) *SQLBuilder
Comment adds a COMMENT clause with the specified comment text. The comment is automatically quoted and SQL-escaped.
Example:
builder.Comment("Analytics database") // COMMENT 'Analytics database'
builder.Comment("") // (nothing added)
func (*SQLBuilder) Create ¶ added in v0.1.7
func (b *SQLBuilder) Create(objectType string) *SQLBuilder
Create adds a Create clause with the specified object type.
Example:
builder.Create("DATABASE") // Create DATABASE
builder.Create("TABLE") // Create TABLE
func (*SQLBuilder) CreateOrReplace ¶ added in v0.1.7
func (b *SQLBuilder) CreateOrReplace(objectType string) *SQLBuilder
CreateOrReplace adds a CREATE OR REPLACE clause with the specified object type.
Example:
builder.CreateOrReplace("DICTIONARY") // CREATE OR REPLACE DICTIONARY
func (*SQLBuilder) Drop ¶ added in v0.1.7
func (b *SQLBuilder) Drop(objectType string) *SQLBuilder
Drop adds a Drop clause with the specified object type.
Example:
builder.Drop("DATABASE") // Drop DATABASE
builder.Drop("TABLE") // Drop TABLE
func (*SQLBuilder) Engine ¶ added in v0.1.7
func (b *SQLBuilder) Engine(engine string) *SQLBuilder
Engine adds an ENGINE clause with the specified engine name.
Example:
builder.Engine("Atomic") // ENGINE = Atomic
builder.Engine("MergeTree()") // ENGINE = MergeTree()
func (*SQLBuilder) Escaped ¶ added in v0.1.7
func (b *SQLBuilder) Escaped(value string) *SQLBuilder
Escaped adds an escaped SQL string value with single quotes. This is useful for cases where you need just the quoted value without a keyword.
Example:
builder.Modify("COMMENT").Escaped("User's database") // MODIFY COMMENT 'User\'s database'
builder.Raw("DEFAULT").Escaped("hello") // DEFAULT 'hello'
func (*SQLBuilder) IfExists ¶ added in v0.1.7
func (b *SQLBuilder) IfExists() *SQLBuilder
IfExists adds an IF EXISTS clause. This should be called after DROP operations.
Example:
builder.DROP("DATABASE").IfExists() // DROP DATABASE IF EXISTS
func (*SQLBuilder) IfNotExists ¶ added in v0.1.7
func (b *SQLBuilder) IfNotExists() *SQLBuilder
IfNotExists adds an IF NOT EXISTS clause. This should be called after CREATE operations.
Example:
builder.CREATE("DATABASE").IfNotExists() // CREATE DATABASE IF NOT EXISTS
func (*SQLBuilder) Modify ¶ added in v0.1.7
func (b *SQLBuilder) Modify(clause string) *SQLBuilder
Modify adds a Modify clause for ALTER operations.
Example:
builder.Modify("COMMENT") // Modify COMMENT
func (*SQLBuilder) Name ¶ added in v0.1.7
func (b *SQLBuilder) Name(name string) *SQLBuilder
Name adds a backticked object name.
Example:
builder.Name("analytics") // `analytics`
builder.Name("db.table") // `db`.`table`
func (*SQLBuilder) OnCluster ¶ added in v0.1.7
func (b *SQLBuilder) OnCluster(cluster string) *SQLBuilder
OnCluster adds an ON CLUSTER clause if cluster is not empty.
Example:
builder.OnCluster("production") // ON CLUSTER `production`
builder.OnCluster("") // (nothing added)
func (*SQLBuilder) QualifiedName ¶ added in v0.1.7
func (b *SQLBuilder) QualifiedName(database *string, name string) *SQLBuilder
QualifiedName adds a qualified name with optional database prefix. If database is nil or empty, only the name is added with backticks.
Example:
builder.QualifiedName(nil, "events") // `events` builder.QualifiedName(&"analytics", "events") // `analytics`.`events`
func (*SQLBuilder) QualifiedTo ¶ added in v0.1.7
func (b *SQLBuilder) QualifiedTo(database *string, name string) *SQLBuilder
QualifiedTo adds a TO clause with qualified name for rename operations.
Example:
builder.QualifiedTo(nil, "new_table") // TO `new_table` builder.QualifiedTo(&"newdb", "new_table") // TO `newdb`.`new_table`
func (*SQLBuilder) Raw ¶ added in v0.1.7
func (b *SQLBuilder) Raw(sql string) *SQLBuilder
Raw adds raw SQL text to the builder. Use sparingly for complex constructs that don't fit the fluent pattern.
Example:
builder.Raw("SYNC") // SYNC
func (*SQLBuilder) Rename ¶ added in v0.1.7
func (b *SQLBuilder) Rename(objectType string) *SQLBuilder
Rename adds a Rename clause with the specified object type.
Example:
builder.Rename("DATABASE") // Rename DATABASE
builder.Rename("TABLE") // Rename TABLE
func (*SQLBuilder) String ¶ added in v0.1.7
func (b *SQLBuilder) String() string
String builds and returns the final SQL statement with a semicolon.
Example:
sql := builder.Create("DATABASE").Name("test").String()
// Returns: "CREATE DATABASE `test`;"
func (*SQLBuilder) StringWithoutSemicolon ¶ added in v0.1.7
func (b *SQLBuilder) StringWithoutSemicolon() string
StringWithoutSemicolon builds and returns the final SQL statement without a semicolon. Useful for building parts of larger statements.
Example:
clause := builder.OnCluster("prod").StringWithoutSemicolon()
// Returns: "ON CLUSTER `prod`"
func (*SQLBuilder) To ¶ added in v0.1.7
func (b *SQLBuilder) To(name string) *SQLBuilder
To adds a To clause for rename operations.
Example:
builder.To("new_name") // To `new_name`