diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 6b4bdd16..f7f3233c 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -16,3 +16,9 @@ updates:
- "๐ค Dependencies"
schedule:
interval: "daily"
+ - package-ecosystem: "gomod"
+ directory: "/clickhouse/" # Location of package manifests
+ labels:
+ - "๐ค Dependencies"
+ schedule:
+ interval: "daily"
diff --git a/.github/release-drafter-clickhouse.yml b/.github/release-drafter-clickhouse.yml
new file mode 100644
index 00000000..c08b7f25
--- /dev/null
+++ b/.github/release-drafter-clickhouse.yml
@@ -0,0 +1,50 @@
+name-template: 'ClickHouse - v$RESOLVED_VERSION'
+tag-template: 'clickhouse/v$RESOLVED_VERSION'
+tag-prefix: clickhouse/v
+include-paths:
+ - clickhouse
+categories:
+ - title: 'โ Breaking Changes'
+ labels:
+ - 'โ BreakingChange'
+ - title: '๐ New'
+ labels:
+ - 'โ๏ธ Feature'
+ - title: '๐งน Updates'
+ labels:
+ - '๐งน Updates'
+ - '๐ค Dependencies'
+ - title: '๐ Fixes'
+ labels:
+ - 'โข๏ธ Bug'
+ - title: '๐ Documentation'
+ labels:
+ - '๐ Documentation'
+change-template: '- $TITLE (#$NUMBER)'
+change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
+exclude-contributors:
+ - dependabot
+ - dependabot[bot]
+version-resolver:
+ major:
+ labels:
+ - 'major'
+ - 'โ BreakingChange'
+ minor:
+ labels:
+ - 'minor'
+ - 'โ๏ธ Feature'
+ patch:
+ labels:
+ - 'patch'
+ - '๐ Documentation'
+ - 'โข๏ธ Bug'
+ - '๐ค Dependencies'
+ - '๐งน Updates'
+ default: patch
+template: |
+ $CHANGES
+
+ **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...clickhouse/v$RESOLVED_VERSION
+
+ Thank you $CONTRIBUTORS for making this update possible.
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 20af27d9..c293e17d 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -134,6 +134,11 @@ jobs:
docker run --name scylladb -p 9042:9042 -p 19042:19042 -p 9160:9160 -p 7000:7000 -p 7001:7001 -p 7199:7199 -p 9180:9180 -d scylladb/scylla:latest --broadcast-address 127.0.0.1 --listen-address 0.0.0.0 --broadcast-rpc-address 127.0.0.1
sleep 15 # Wait for ScyllaDb to initialize
+ - name: Startup Clickhouse
+ run: |
+ docker run -d -p 9001:9000 --name clickhouse --ulimit nofile=262144:262144 clickhouse/clickhouse-server
+ sleep 10 # Wait for Clickhouse to initialize
+
- name: Setup Redis
uses: shogo82148/actions-setup-redis@v1
with:
diff --git a/.github/workflows/release-drafter-clickhouse.yml b/.github/workflows/release-drafter-clickhouse.yml
new file mode 100644
index 00000000..62043170
--- /dev/null
+++ b/.github/workflows/release-drafter-clickhouse.yml
@@ -0,0 +1,19 @@
+name: Release Drafter Clickhouse
+on:
+ push:
+ # branches to consider in the event; optional, defaults to all
+ branches:
+ - master
+ - main
+ paths:
+ - 'clickhouse/**'
+jobs:
+ draft_release_clickhouse:
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - uses: release-drafter/release-drafter@v6
+ with:
+ config-name: release-drafter-clickhouse.yml
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/test-clickhouse.yml b/.github/workflows/test-clickhouse.yml
new file mode 100644
index 00000000..a56a54de
--- /dev/null
+++ b/.github/workflows/test-clickhouse.yml
@@ -0,0 +1,32 @@
+on:
+ push:
+ branches:
+ - master
+ - main
+ paths:
+ - 'clickhouse/**'
+ pull_request:
+ paths:
+ - 'clickhouse/**'
+name: 'Tests Clickhouse'
+jobs:
+ Tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version:
+ - 1.21.x
+ - 1.22.x
+ steps:
+ - name: Fetch Repository
+ uses: actions/checkout@v4
+ - name: Startup Clickhouse
+ run: |
+ docker run -d -p 9001:9000 --name clickhouse --ulimit nofile=262144:262144 clickhouse/clickhouse-server
+ sleep 30
+ - name: Install Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '${{ matrix.go-version }}'
+ - name: Run Test
+ run: cd ./clickhouse && go clean -testcache && go test ./... -v -race
diff --git a/.github/workflows/test-cloudflarekv.yml b/.github/workflows/test-cloudflarekv.yml
index 4d819ba2..89d00e87 100644
--- a/.github/workflows/test-cloudflarekv.yml
+++ b/.github/workflows/test-cloudflarekv.yml
@@ -3,10 +3,13 @@ name: Tests CloudflareKV
on:
push:
branches:
+ - master
- main
+ paths:
+ - 'cloudflarekv/**'
pull_request:
- branches:
- - main
+ paths:
+ - 'cloudflarekv/**'
jobs:
Tests:
@@ -16,7 +19,6 @@ jobs:
go-version:
- 1.21.x
- 1.22.x
-
steps:
- name: Checkout Repository
uses: actions/checkout@v4
diff --git a/README.md b/README.md
index 532bebab..40f61d30 100644
--- a/README.md
+++ b/README.md
@@ -74,3 +74,4 @@ type Storage interface {
- [S3](./s3/README.md)
- [ScyllaDB](./scylladb/README.md)
- [SQLite3](./sqlite3/README.md)
+- [ClickHouse](./clickhouse/README.md)
diff --git a/clickhouse/README.md b/clickhouse/README.md
new file mode 100644
index 00000000..772f5527
--- /dev/null
+++ b/clickhouse/README.md
@@ -0,0 +1,117 @@
+# Clickhouse
+
+A Clickhouse storage driver using [https://github.com/ClickHouse/clickhouse-go](https://github.com/ClickHouse/clickhouse-go).
+
+### Table of Contents
+
+- [Signatures](#signatures)
+- [Installation](#installation)
+- [Examples](#examples)
+- [Config](#config)
+- [Default Config](#default-config)
+
+### Signatures
+
+```go
+func New(config ...Config) (*Storage, error)
+func (s *Storage) Get(key string) ([]byte, error)
+func (s *Storage) Set(key string, val []byte, exp time.Duration) error
+func (s *Storage) Delete(key string) error
+func (s *Storage) Reset() error
+func (s *Storage) Close() error
+func (s *Storage) Conn() *Session
+```
+
+### Installation
+
+Clickhouse is supported on the latest two versions of Go:
+
+Install the clickhouse implementation:
+```bash
+go get github.com/gofiber/storage/clickhouse
+```
+
+Before running or testing this implementation, you must ensure a Clickhouse cluster is available.
+For local development, we recommend using the Clickhouse Docker image; it contains everything
+necessary for the client to operate correctly.
+
+To start Clickhouse using Docker, issue the following:
+
+```bash
+docker run -d -p 9000:9000 --name some-clickhouse-server --ulimit nofile=262144:262144 clickhouse/clickhouse-server
+```
+
+After running this command you're ready to start using the storage and connecting to the database.
+
+### Examples
+
+You can use the following options to create a clickhouse storage driver:
+```go
+import "github.com/gofiber/storage/clickhouse"
+
+// Initialize default config, to connect to localhost:9000 using the memory engine and with a clean table.
+store, err := clickhouse.New(clickhouse.Config{
+ Host: "localhost",
+ Port: 9000,
+ Clean: true,
+})
+
+// Initialize custom config to connect to a different host/port and use custom engine and with clean table.
+store, err := clickhouse.New(clickhouse.Config{
+ Host: "some-ip-address",
+ Port: 9000,
+ Engine: clickhouse.MergeTree,
+ Clean: true,
+})
+
+// Initialize to connect with TLS enabled with your own tls.Config and with clean table.
+tlsConfig := config := &tls.Config{...}
+
+store, err := clickhouse.New(clickhouse.Config{
+ Host: "some-ip-address",
+ Port: 9000,
+ Clean: true,
+ TLSConfig: tlsConfig,
+})
+```
+
+### Config
+
+```go
+// Config defines configuration options for Clickhouse connection.
+type Config struct {
+ // The host of the database. Ex: 127.0.0.1
+ Host string
+ // The port where the database is supposed to listen to. Ex: 9000
+ Port int
+ // The database that the connection should authenticate from
+ Database string
+ // The username to be used in the authentication
+ Username string
+ // The password to be used in the authentication
+ Password string
+ // The name of the table that will store the data
+ Table string
+ // The engine that should be used in the table
+ Engine string
+ // Should start a clean table, default false
+ Clean bool
+ // TLS configuration, default nil
+ TLSConfig *tls.Config
+ // Should the connection be in debug mode, default false
+ Debug bool
+ // The function to use with the debug config, default print function. It only works when debug is true
+ Debugf func(format string, v ...any)
+}
+```
+
+### Default Config
+
+```go
+var DefaultConfig = Config{
+ Host: "localhost",
+ Port: 9000,
+ Engine: "Memory",
+ Clean: false,
+}
+```
diff --git a/clickhouse/clickhouse.go b/clickhouse/clickhouse.go
new file mode 100644
index 00000000..1056bc7b
--- /dev/null
+++ b/clickhouse/clickhouse.go
@@ -0,0 +1,126 @@
+package clickhouse
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "fmt"
+ "time"
+
+ driver "github.com/ClickHouse/clickhouse-go/v2"
+)
+
+type Storage struct {
+ session driver.Conn
+ context context.Context
+ table string
+}
+
+// New returns a new [*Storage] given a [Config].
+func New(configuration Config) (*Storage, error) {
+ cfg, engine, err := defaultConfig(configuration)
+ if err != nil {
+ return nil, err
+ }
+
+ conn, err := driver.Open(&cfg)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx := context.Background()
+
+ queryWithEngine := fmt.Sprintf(createTableString, engine)
+ if err := conn.Exec(ctx, queryWithEngine, driver.Named("table", configuration.Table)); err != nil {
+ return nil, err
+ }
+
+ if configuration.Clean {
+ if err := conn.Exec(ctx, resetDataString, driver.Named("table", configuration.Table)); err != nil {
+ return nil, err
+ }
+ }
+
+ if err := conn.Ping(ctx); err != nil {
+ return nil, err
+ }
+
+ return &Storage{
+ session: conn,
+ context: ctx,
+ table: configuration.Table,
+ }, nil
+}
+
+func (s *Storage) Set(key string, value []byte, expiration time.Duration) error {
+ if len(key) <= 0 || len(value) <= 0 {
+ return nil
+ }
+
+ exp := time.Time{}
+ if expiration != 0 {
+ exp = time.Now().Add(expiration).UTC()
+ }
+
+ return s.
+ session.
+ Exec(
+ s.context,
+ insertDataString,
+ driver.Named("table", s.table),
+ driver.Named("key", key),
+ driver.Named("value", string(value)),
+ driver.Named("expiration", exp.Format("2006-01-02 15:04:05")),
+ )
+}
+
+func (s *Storage) Get(key string) ([]byte, error) {
+ if len(key) == 0 {
+ return []byte{}, nil
+ }
+
+ var result schema
+
+ row := s.session.QueryRow(
+ s.context,
+ selectDataString,
+ driver.Named("table", s.table),
+ driver.Named("key", key),
+ )
+ if row.Err() != nil {
+ return []byte{}, row.Err()
+ }
+
+ if err := row.ScanStruct(&result); err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ return []byte{}, nil
+ }
+
+ return []byte{}, err
+ }
+
+ // The result.Expiration.IsZero() was returning a false value even when the time was
+ // set to be the zero value of the time.Time struct (Jan 1st 1970, 00:00:00 UTC)
+ // so we had to change the comparison
+ if !time.Unix(0, 0).Equal(result.Expiration) && result.Expiration.Before(time.Now().UTC()) {
+ return []byte{}, nil
+ }
+
+ return []byte(result.Value), nil
+}
+
+func (s *Storage) Delete(key string) error {
+ if len(key) == 0 {
+ return nil
+ }
+
+ return s.session.Exec(s.context, deleteDataString, driver.Named("table", s.table), driver.Named("key", key))
+}
+
+func (s *Storage) Reset() error {
+ return s.session.Exec(s.context, resetDataString, driver.Named("table", s.table))
+}
+
+func (s *Storage) Close() error {
+ return s.session.Close()
+}
diff --git a/clickhouse/clickhouse_test.go b/clickhouse/clickhouse_test.go
new file mode 100644
index 00000000..f1278ce2
--- /dev/null
+++ b/clickhouse/clickhouse_test.go
@@ -0,0 +1,229 @@
+package clickhouse
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+type TestOrBench interface {
+ Helper()
+}
+
+func getTestConnection(t TestOrBench, cfg Config) (*Storage, error) {
+ t.Helper()
+
+ client, err := New(cfg)
+
+ return client, err
+}
+
+func Test_Connection(t *testing.T) {
+ _, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+
+ require.NoError(t, err)
+}
+
+func Test_Set(t *testing.T) {
+ client, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(t, err)
+ defer client.Close()
+
+ err = client.Set("somekey", []byte("somevalue"), 0)
+ require.NoError(t, err)
+}
+
+func Test_Set_With_Exp(t *testing.T) {
+ client, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(t, err)
+ defer client.Close()
+
+ err = client.Set("setsomekeywithexp", []byte("somevalue"), time.Second*1)
+ require.NoError(t, err)
+}
+
+func Test_Get(t *testing.T) {
+ client, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(t, err)
+ defer client.Close()
+
+ err = client.Set("somekey", []byte("somevalue"), 0)
+ require.NoError(t, err)
+
+ value, err := client.Get("somekey")
+
+ require.NoError(t, err)
+ assert.NotNil(t, value)
+ assert.Equal(t, "somevalue", string(value))
+}
+
+func Test_Get_With_Exp(t *testing.T) {
+ client, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(t, err)
+ defer client.Close()
+
+ err = client.Set("getsomekeywithexp", []byte("somevalue"), time.Second*2)
+ require.NoError(t, err)
+
+ value, err := client.Get("getsomekeywithexp")
+
+ require.NoError(t, err)
+ assert.NotNil(t, value)
+ assert.Equal(t, "somevalue", string(value))
+
+ time.Sleep(time.Second * 5)
+
+ value, err = client.Get("getsomekeywithexp")
+
+ require.NoError(t, err)
+ assert.Equal(t, []byte{}, value)
+}
+
+func Test_Delete(t *testing.T) {
+ client, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(t, err)
+ defer client.Close()
+
+ err = client.Set("somekeytodelete", []byte("somevalue"), time.Second*5)
+ require.NoError(t, err)
+
+ err = client.Delete("somekeytodelete")
+
+ require.NoError(t, err)
+
+ value, err := client.Get("somekeytodelete")
+
+ require.NoError(t, err)
+ assert.Equal(t, []byte{}, value)
+}
+
+func Test_Reset(t *testing.T) {
+ client, err := getTestConnection(t, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(t, err)
+ defer client.Close()
+
+ err = client.Set("testkey", []byte("somevalue"), 0)
+ require.NoError(t, err)
+
+ err = client.Reset()
+
+ require.NoError(t, err)
+
+ value, err := client.Get("testkey")
+
+ require.NoError(t, err)
+ assert.Equal(t, []byte{}, value)
+}
+
+func Benchmark_Clickhouse_Set(b *testing.B) {
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ client, err := getTestConnection(b, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(b, err)
+
+ defer client.Close()
+
+ for i := 0; i < b.N; i++ {
+ err = client.Set("john", []byte("doe"), 0)
+ }
+
+ require.NoError(b, err)
+}
+
+func Benchmark_Clickhouse_Get(b *testing.B) {
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ client, err := getTestConnection(b, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+ require.NoError(b, err)
+
+ defer client.Close()
+
+ err = client.Set("john", []byte("doe"), 0)
+
+ for i := 0; i < b.N; i++ {
+ _, err = client.Get("john")
+ }
+
+ require.NoError(b, err)
+}
+
+func Benchmark_Clickhouse_Set_And_Delete(b *testing.B) {
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ client, err := getTestConnection(b, Config{
+ Host: "127.0.0.1",
+ Port: 9001,
+ Engine: Memory,
+ Table: "test_table",
+ Clean: true,
+ })
+
+ require.NoError(b, err)
+ defer client.Close()
+
+ for i := 0; i < b.N; i++ {
+ _ = client.Set("john", []byte("doe"), 0)
+ err = client.Delete("john")
+ }
+
+ require.NoError(b, err)
+}
diff --git a/clickhouse/config.go b/clickhouse/config.go
new file mode 100644
index 00000000..d9b820ce
--- /dev/null
+++ b/clickhouse/config.go
@@ -0,0 +1,116 @@
+package clickhouse
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "log"
+ "time"
+
+ driver "github.com/ClickHouse/clickhouse-go/v2"
+)
+
+type ClickhouseEngine string
+
+type schema struct {
+ Value string `ch:"value"`
+ Expiration time.Time `ch:"expiration"`
+}
+
+const (
+ Memory ClickhouseEngine = "Memory"
+ MergeTree ClickhouseEngine = "MergeTree"
+ StripeLog ClickhouseEngine = "StripeLog"
+ TinyLog ClickhouseEngine = "TinyLog"
+ Log ClickhouseEngine = "Log"
+)
+
+// Config defines configuration options for Clickhouse connection.
+type Config struct {
+ // The host of the database. Ex: 127.0.0.1
+ Host string
+ // The port where the database is supposed to listen to. Ex: 9000
+ Port int
+ // The database that the connection should authenticate from
+ Database string
+ // The username to be used in the authentication
+ Username string
+ // The password to be used in the authentication
+ Password string
+ // The name of the table that will store the data
+ Table string
+ // The engine that should be used in the table
+ Engine ClickhouseEngine
+ // Should start a clean table, default false
+ Clean bool
+ // TLS configuration, default nil
+ TLSConfig *tls.Config
+ // Should the connection be in debug mode, default false
+ Debug bool
+ // The function to use with the debug config, default print function. It only works when debug is true
+ Debugf func(format string, v ...any)
+}
+
+func defaultConfig(configuration Config) (driver.Options, ClickhouseEngine, error) {
+ if configuration.Table == "" {
+ return driver.Options{}, "", errors.New("table name not provided")
+ }
+
+ if configuration.Host == "" {
+ configuration.Host = "localhost"
+ }
+
+ if configuration.Port == 0 {
+ configuration.Port = 9000
+ }
+
+ if configuration.Engine == "" {
+ configuration.Engine = Memory
+ }
+
+ config := driver.Options{
+ Addr: []string{fmt.Sprintf("%s:%d", configuration.Host, configuration.Port)},
+ }
+
+ if configuration.Username != "" && configuration.Password != "" {
+ config.Auth = driver.Auth{
+ Database: configuration.Database,
+ Username: configuration.Username,
+ Password: configuration.Password,
+ }
+ }
+
+ if configuration.TLSConfig != nil {
+ config.TLS = configuration.TLSConfig
+ }
+
+ if configuration.Debug && config.Debugf == nil {
+ config.Debugf = log.Printf
+ }
+
+ return config, configuration.Engine, nil
+}
+
+const resetDataString = `
+ TRUNCATE TABLE {table:Identifier}
+`
+
+const deleteDataString = `
+ ALTER TABLE {table:Identifier} DELETE WHERE key = {key:String}
+`
+
+const selectDataString = `
+ SELECT value, expiration FROM {table:Identifier} WHERE key = {key:String}
+`
+
+const insertDataString = `
+ INSERT INTO {table:Identifier} (*) VALUES ({key:String}, {value:String}, {expiration:Datetime})
+`
+
+const createTableString = `
+ CREATE TABLE IF NOT EXISTS {table:Identifier} (
+ key String CODEC(ZSTD(1))
+ , value String CODEC(ZSTD(1))
+ , expiration Datetime CODEC(ZSTD(1))
+ ) ENGINE=%s
+`
diff --git a/clickhouse/go.mod b/clickhouse/go.mod
new file mode 100644
index 00000000..f40124ae
--- /dev/null
+++ b/clickhouse/go.mod
@@ -0,0 +1,28 @@
+module github.com/gofiber/storage/clickhouse
+
+go 1.21
+
+require (
+ github.com/ClickHouse/clickhouse-go/v2 v2.26.0
+ github.com/stretchr/testify v1.9.0
+)
+
+require (
+ github.com/ClickHouse/ch-go v0.61.5 // indirect
+ github.com/andybalholm/brotli v1.1.0 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/go-faster/city v1.0.1 // indirect
+ github.com/go-faster/errors v0.7.1 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/klauspost/compress v1.17.9 // indirect
+ github.com/paulmach/orb v0.11.1 // indirect
+ github.com/pierrec/lz4/v4 v4.1.21 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/segmentio/asm v1.2.0 // indirect
+ github.com/shopspring/decimal v1.4.0 // indirect
+ go.opentelemetry.io/otel v1.28.0 // indirect
+ go.opentelemetry.io/otel/trace v1.28.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/clickhouse/go.sum b/clickhouse/go.sum
new file mode 100644
index 00000000..46d90598
--- /dev/null
+++ b/clickhouse/go.sum
@@ -0,0 +1,110 @@
+github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
+github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg=
+github.com/ClickHouse/clickhouse-go/v2 v2.26.0 h1:j4/y6NYaCcFkJwN/TU700ebW+nmsIy34RmUAAcZKy9w=
+github.com/ClickHouse/clickhouse-go/v2 v2.26.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ=
+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
+github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
+github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
+github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
+github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
+github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
+github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
+github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
+github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
+go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
+go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
+go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
+go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=