utils

package
v0.1.9 Latest Latest
Warning

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

Go to latest
Published: Dec 12, 2025 License: GPL-3.0 Imports: 3 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func BacktickColumnName added in v0.1.8

func BacktickColumnName(name string) string

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

func BacktickIdentifier(name string) string

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

func BacktickQualifiedName(database *string, name string) string

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

func IsBackticked(s string) bool

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

func IsBooleanValue(value string) bool

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

func IsNumericValue(value string) bool

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

func StripBackticks(s string) string

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`

Jump to

Keyboard shortcuts

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