Files
cdp/data-layer/api/internal/repo/chconn.go
2026-05-25 08:38:26 +07:00

59 lines
1.7 KiB
Go

package repo
import (
"context"
"fmt"
"github.com/ClickHouse/clickhouse-go/v2"
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
)
// NewClickHouse opens a native-protocol ClickHouse connection. The returned
// driver.Conn is safe for concurrent use. Caller owns Close().
func NewClickHouse(ctx context.Context, addr, db, user, password string) (driver.Conn, error) {
conn, err := clickhouse.Open(&clickhouse.Options{
Addr: []string{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 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) (driver.Conn, error) {
conn, err := clickhouse.Open(&clickhouse.Options{
Addr: []string{addr},
Auth: clickhouse.Auth{
Database: db,
Username: user,
Password: password,
},
Settings: clickhouse.Settings{
"readonly": 2, // belt-and-braces: server-side enforce read-only
},
})
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
}