92 lines
2.5 KiB
Go
92 lines
2.5 KiB
Go
package repo
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
|
|
"github.com/ClickHouse/clickhouse-go/v2"
|
|
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
|
)
|
|
|
|
// NewClickHouse opens a ClickHouse connection. `secure` enables TLS. The
|
|
// wire protocol is auto-selected from the port: 8123/8443 (HTTP interface)
|
|
// use HTTP, the native default otherwise.
|
|
func NewClickHouse(ctx context.Context, addr, db, user, password string, secure bool) (driver.Conn, error) {
|
|
opts := &clickhouse.Options{
|
|
Addr: []string{addr},
|
|
Protocol: protocolFromAddr(addr),
|
|
Auth: clickhouse.Auth{
|
|
Database: db,
|
|
Username: user,
|
|
Password: password,
|
|
},
|
|
Settings: clickhouse.Settings{
|
|
"readonly": 0, // analytics queries; per-user read-only enforced for /query/sql separately
|
|
},
|
|
}
|
|
if secure {
|
|
opts.TLS = &tls.Config{MinVersion: tls.VersionTLS12}
|
|
}
|
|
conn, err := clickhouse.Open(opts)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open clickhouse: %w", err)
|
|
}
|
|
if err := conn.Ping(ctx); err != nil {
|
|
_ = conn.Close()
|
|
return nil, fmt.Errorf("ping clickhouse: %w", err)
|
|
}
|
|
return conn, nil
|
|
}
|
|
|
|
// NewClickHouseReadOnly opens a ClickHouse connection using a SELECT-only
|
|
// account. Used to back the /query/sql sandbox: DDL/DML are rejected at the DB
|
|
// level even if the app-level keyword guard is bypassed.
|
|
func NewClickHouseReadOnly(ctx context.Context, addr, db, user, password string, secure bool) (driver.Conn, error) {
|
|
opts := &clickhouse.Options{
|
|
Addr: []string{addr},
|
|
Protocol: protocolFromAddr(addr),
|
|
Auth: clickhouse.Auth{
|
|
Database: db,
|
|
Username: user,
|
|
Password: password,
|
|
},
|
|
Settings: clickhouse.Settings{
|
|
"readonly": 2, // belt-and-braces: server-side enforce read-only
|
|
},
|
|
}
|
|
if secure {
|
|
opts.TLS = &tls.Config{MinVersion: tls.VersionTLS12}
|
|
}
|
|
conn, err := clickhouse.Open(opts)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open clickhouse (ro): %w", err)
|
|
}
|
|
if err := conn.Ping(ctx); err != nil {
|
|
_ = conn.Close()
|
|
return nil, fmt.Errorf("ping clickhouse (ro): %w", err)
|
|
}
|
|
return conn, nil
|
|
}
|
|
|
|
// protocolFromAddr selects HTTP for the well-known ClickHouse HTTP-interface
|
|
// ports (8123/8443) and Native otherwise. Lets CLICKHOUSE_ADDR target either
|
|
// kind of endpoint without an extra env var.
|
|
func protocolFromAddr(addr string) clickhouse.Protocol {
|
|
switch port := portOf(addr); port {
|
|
case "8123", "8443":
|
|
return clickhouse.HTTP
|
|
default:
|
|
return clickhouse.Native
|
|
}
|
|
}
|
|
|
|
func portOf(addr string) string {
|
|
for i := len(addr) - 1; i >= 0; i-- {
|
|
if addr[i] == ':' {
|
|
return addr[i+1:]
|
|
}
|
|
}
|
|
return ""
|
|
}
|