Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5d480f2
Added query params for cluster + tests
FatherCandle Apr 6, 2023
e9ecbfe
ran golangci
FatherCandle Apr 6, 2023
6183cd3
Added onClusterClause and used it on create and drop database
FatherCandle Apr 6, 2023
5b9f219
Refactored the cluster params to a new file
FatherCandle Apr 6, 2023
81e90d2
Refactored the cluster params to a new file
FatherCandle Apr 7, 2023
47d29ca
Refactored the cluster params to a new file
FatherCandle Apr 7, 2023
5148c9e
fix cluster params set + create separate test file for cluster set up
FatherCandle Apr 7, 2023
a9b6e6d
fix cluster params set + create separate test file for cluster set up
FatherCandle Apr 7, 2023
046adde
Added containers and configs for cluster
FatherCandle Apr 7, 2023
31754c2
reduced config files
FatherCandle Apr 8, 2023
0f5bb4f
fix cluster params and separate cluster tests
FatherCandle Apr 8, 2023
909287b
Set test to create and drop db on cluster
FatherCandle Apr 8, 2023
d9b8c4e
Added cluster logic to schema dump and migration table creation
FatherCandle Apr 8, 2023
b80fbe2
Created a utils file for common test functions
FatherCandle Apr 8, 2023
686f772
Created a utils file for common test functions
FatherCandle Apr 8, 2023
0e03285
Changed cluster config directory path
FatherCandle Apr 8, 2023
75d1441
Changed log size in cluster configs
FatherCandle Apr 8, 2023
606daae
rename function of getting cluster parameters
FatherCandle Apr 8, 2023
0a5519e
Fixed cluster configs to be able to drop and recreate tables
FatherCandle Apr 11, 2023
72487ed
Completed tests for cluster
FatherCandle Apr 11, 2023
801dc82
Ran golangci
FatherCandle Apr 11, 2023
7414365
Remove cluster query params on connection string
FatherCandle Apr 22, 2023
347c7aa
Fix test usage of connection link
FatherCandle Apr 22, 2023
70aab02
Added a test to see that driver creation is still atmoic
FatherCandle Apr 22, 2023
d928a27
Added indication of cluster to dump schema test
FatherCandle Apr 22, 2023
114b8b7
Ran golangchi
FatherCandle Apr 22, 2023
59ee572
increase wait time for replicas sync
FatherCandle Apr 22, 2023
78c024a
Replace sleep with insert_quorom settings
FatherCandle Apr 22, 2023
1e7a86c
Replace sleep with insert_quorom settings
FatherCandle Apr 22, 2023
acaa08e
ran golangchi
FatherCandle Apr 22, 2023
f7593db
Added docs for the cluster query parameters to the clickhouse section
FatherCandle May 13, 2023
88664b2
Added a escape string function to the clickhouse driver
FatherCandle May 13, 2023
1398b9f
Added usage of escaping to cluster settings on sql statements
FatherCandle May 13, 2023
73a9230
Ran golanci
FatherCandle May 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
/vendor
dist
node_modules
.vscode
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,33 @@ DATABASE_URL="sqlite:/tmp/database.sqlite3"
DATABASE_URL="clickhouse://username:password@127.0.0.1:9000/database_name"
```

To work with ClickHouse cluster, there are 4 connection query parameters that can be supplied:

- `on_cluster` - Indicataion to use cluster statements and replicated migration table. (default: `false`) If this parameter is not supplied, other cluster related query parameters are ignored.
```sh
DATABASE_URL="clickhouse://username:password@127.0.0.1:9000/database_name?on_cluster"

DATABASE_URL="clickhouse://username:password@127.0.0.1:9000/database_name?on_cluster=true"
```

- `cluster_macro` (Optional) - Macro value to be used for ON CLUSTER statements and for the replciated migration table engine zookeeper path. (default: `{cluster}`)

```sh
DATABASE_URL="clickhouse://username:password@127.0.0.1:9000/database_name?on_cluster&cluster_macro={my_cluster}"
```

- `replica_macro` (Optional) - Macro value to be used for the replica name in the replciated migration table engine. (default: `{replica}`)

```sh
DATABASE_URL="clickhouse://username:password@127.0.0.1:9000/database_name?on_cluster&replica_macro={my_replica}"
```

- `zoo_path` (Optional) - The path to the table migration in ClickHouse/Zoo Keeper. (default: `/clickhouse/tables/<cluster_macro>/{table}`)

```sh
DATABASE_URL="clickhouse://username:password@127.0.0.1:9000/database_name?on_cluster&zoo_path=/zk/path/tables"
```

[See other supported connection options](https://github.com/ClickHouse/clickhouse-go#dsn).

### Creating Migrations
Expand Down
28 changes: 28 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ services:
- mysql
- postgres
- clickhouse
- clickhouse-cluster-01
- clickhouse-cluster-02
environment:
CLICKHOUSE_TEST_URL: clickhouse://clickhouse:9000/dbmate_test
CLICKHOUSE_CLUSTER_01_TEST_URL: clickhouse://ch-cluster-01:9000/dbmate_test
CLICKHOUSE_CLUSTER_02_TEST_URL: clickhouse://ch-cluster-02:9000/dbmate_test
MYSQL_TEST_URL: mysql://root:root@mysql/dbmate_test
POSTGRES_TEST_URL: postgres://postgres:postgres@postgres/dbmate_test?sslmode=disable
SQLITE_TEST_URL: sqlite3:/tmp/dbmate_test.sqlite3
Expand All @@ -35,3 +39,27 @@ services:

clickhouse:
image: clickhouse/clickhouse-server:22.8

zookeeper:
image: zookeeper
hostname: zookeeper

clickhouse-cluster-01:
image: clickhouse/clickhouse-server:22.8
hostname: ch-cluster-01
environment:
- CLICKHOUSE_CONFIG=/etc/clickhouse-server/config.xml
depends_on:
- zookeeper
volumes:
- ./pkg/driver/clickhouse/testdata/cluster_config/ch-cluster-01:/etc/clickhouse-server

clickhouse-cluster-02:
image: clickhouse/clickhouse-server:22.8
hostname: ch-cluster-02
environment:
- CLICKHOUSE_CONFIG=/etc/clickhouse-server/config.xml
depends_on:
- zookeeper
volumes:
- ./pkg/driver/clickhouse/testdata/cluster_config/ch-cluster-02:/etc/clickhouse-server
42 changes: 36 additions & 6 deletions pkg/driver/clickhouse/clickhouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Driver struct {
migrationsTableName string
databaseURL *url.URL
log io.Writer
clusterParameters *ClusterParameters
}

// NewDriver initializes the driver
Expand All @@ -33,6 +34,7 @@ func NewDriver(config dbmate.DriverConfig) dbmate.Driver {
migrationsTableName: config.MigrationsTableName,
databaseURL: config.DatabaseURL,
log: config.Log,
clusterParameters: ExtractClusterParametersFromURL(config.DatabaseURL),
}
}

Expand Down Expand Up @@ -74,6 +76,8 @@ func connectionString(initialURL *url.URL) string {

u.RawQuery = query.Encode()

u = ClearClusterParametersFromURL(u)

return u.String()
}

Expand All @@ -95,6 +99,15 @@ func (drv *Driver) openClickHouseDB() (*sql.DB, error) {
return sql.Open("clickhouse", clickhouseURL.String())
}

func (drv *Driver) onClusterClause() string {
clusterClause := ""
if drv.clusterParameters.OnCluster {
escapedClusterMacro := drv.escapeString(drv.clusterParameters.ClusterMacro)
clusterClause = fmt.Sprintf(" ON CLUSTER '%s'", escapedClusterMacro)
}
return clusterClause
}

func (drv *Driver) databaseName() string {
name := strings.TrimLeft(dbutil.MustParseURL(connectionString(drv.databaseURL)).Path, "/")
if name == "" {
Expand All @@ -115,6 +128,12 @@ func (drv *Driver) quoteIdentifier(str string) string {
return fmt.Sprintf(`"%s"`, str)
}

func (drv *Driver) escapeString(str string) string {
quoteEscaper := strings.NewReplacer(`'`, `\'`, `\`, `\\`)
str = quoteEscaper.Replace(str)
return str
}

// CreateDatabase creates the specified database
func (drv *Driver) CreateDatabase() error {
name := drv.databaseName()
Expand All @@ -126,7 +145,9 @@ func (drv *Driver) CreateDatabase() error {
}
defer dbutil.MustClose(db)

_, err = db.Exec("create database " + drv.quoteIdentifier(name))
q := fmt.Sprintf("CREATE DATABASE %s%s", drv.quoteIdentifier(name), drv.onClusterClause())

_, err = db.Exec(q)

return err
}
Expand All @@ -142,14 +163,16 @@ func (drv *Driver) DropDatabase() error {
}
defer dbutil.MustClose(db)

_, err = db.Exec("drop database if exists " + drv.quoteIdentifier(name))
q := fmt.Sprintf("DROP DATABASE IF EXISTS %s%s", drv.quoteIdentifier(name), drv.onClusterClause())

_, err = db.Exec(q)

return err
}

func (drv *Driver) schemaDump(db *sql.DB, buf *bytes.Buffer, databaseName string) error {
buf.WriteString("\n--\n-- Database schema\n--\n\n")
buf.WriteString("CREATE DATABASE IF NOT EXISTS " + drv.quoteIdentifier(databaseName) + ";\n\n")
buf.WriteString(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s%s;\n\n", drv.quoteIdentifier(databaseName), drv.onClusterClause()))

tables, err := dbutil.QueryColumn(db, "show tables")
if err != nil {
Expand Down Expand Up @@ -250,15 +273,22 @@ func (drv *Driver) MigrationsTableExists(db *sql.DB) (bool, error) {

// CreateMigrationsTable creates the schema migrations table
func (drv *Driver) CreateMigrationsTable(db *sql.DB) error {
engineClause := "ReplacingMergeTree(ts)"
if drv.clusterParameters.OnCluster {
escapedZooPath := drv.escapeString(drv.clusterParameters.ZooPath)
escapedReplicaMacro := drv.escapeString(drv.clusterParameters.ReplicaMacro)
engineClause = fmt.Sprintf("ReplicatedReplacingMergeTree('%s', '%s', ts)", escapedZooPath, escapedReplicaMacro)
}

_, err := db.Exec(fmt.Sprintf(`
create table if not exists %s (
create table if not exists %s%s (
version String,
ts DateTime default now(),
applied UInt8 default 1
) engine = ReplacingMergeTree(ts)
) engine = %s
primary key version
order by version
`, drv.quotedMigrationsTableName()))
`, drv.quotedMigrationsTableName(), drv.onClusterClause(), engineClause))

return err
}
Expand Down
Loading