
Clickhouse Pydantic Config
- 119 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
Use clickhouse-pydantic-config for development tasks
About
clickhouse-pydantic-config: A skill for development. This provides functionality for development workflows.
- clickhouse-pydantic-config
Clickhouse Pydantic Config by the numbers
- 119 all-time installs (skills.sh)
- Ranked #2,847 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill clickhouse-pydantic-configAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 119 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
What it does
Use clickhouse-pydantic-config for development tasks
Files
ClickHouse Pydantic Config
<!-- ADR: 2025-12-09-clickhouse-pydantic-config-skill -->
Generate DBeaver database client configurations from Pydantic v2 models using mise [env] as Single Source of Truth (SSoT).
Schema documentation principle: ClickHouse table/column COMMENTs are the SSoT for what each column means and how it's computed. See quality-tools:clickhouse-architect for the full COMMENT policy.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
When to Use This Skill
Use this skill when:
- Setting up DBeaver connections for ClickHouse databases
- Generating database client configurations from environment variables
- Managing local vs cloud ClickHouse connection profiles
- Integrating ClickHouse with mise-based development workflows
- Automating DBeaver data-sources.json generation
Critical Design Principle: Semi-Prescriptive Adaptation
This skill is NOT a rigid template. It provides a SSoT pattern that MUST be adapted to each repository's structure and local database situation.
Why This Matters
Each repository has unique:
- Directory layouts (
.dbeaver/location may vary) - Environment variable naming conventions
- Existing connection management patterns
- Local vs cloud database mix
The SSoT principle is the constant; the implementation details are the variables.
Quick Start
# Generate local connection config
mise run db-client-generate
# Generate cloud connection config
mise run db-client:cloud
# Preview without writing
mise run db-client:dry-run
# Launch DBeaver
mise run dbeaverCredential Prerequisites (Cloud Mode)
<!-- ADR: 2025-12-10-clickhouse-skill-documentation-gaps -->
Before using cloud mode, obtain credentials via the skill chain:
1. Create/retrieve user: Use clickhouse-cloud-management skill to create read-only users or retrieve existing credentials from 1Password 2. Store in .env: Add to .env file (gitignored):
CLICKHOUSE_USER_READONLY=your_user
CLICKHOUSE_PASSWORD_READONLY=your_password1. Generate config: Run mise run db-client:cloud
Skill chain: clickhouse-cloud-management → .env → clickhouse-pydantic-config
mise [env] as Single Source of Truth
All configurable values live in .mise.toml:
[env]
CLICKHOUSE_NAME = "clickhouse-local"
CLICKHOUSE_MODE = "local" # "local" or "cloud"
CLICKHOUSE_HOST = "localhost"
CLICKHOUSE_PORT = "8123"
CLICKHOUSE_DATABASE = "default"Scripts read from os.environ.get() with backward-compatible defaults—works with or without mise installed.
Credential Handling by Mode
| Mode | Approach | Rationale |
|---|---|---|
| Local | Hardcode default user, empty password | Zero friction, no security concern |
| Cloud | Pre-populate from .env | Read from environment, write to gitignored JSON |
Key principle: The generated data-sources.json is gitignored anyway. Pre-populating credentials trades zero security risk for maximum developer convenience.
Cloud Credentials Setup
# .env (gitignored)
CLICKHOUSE_USER_READONLY=readonly_user
CLICKHOUSE_PASSWORD_READONLY=your-secret-passwordRepository Adaptation Workflow
Pre-Implementation Discovery (Phase 0)
Before writing any code, the executor MUST:
# 1. Discover existing configuration patterns
fd -t f ".mise.toml" .
fd -t f ".env*" .
fd -t d ".dbeaver" .
# 2. Test ClickHouse connectivity (local)
clickhouse-client --host localhost --port 9000 --query "SELECT 1"
# 3. Check for existing connection configs
fd -t f "data-sources.json" .
fd -t f "dataSources.xml" .Adaptation Decision Matrix
| Discovery Finding | Adaptation Action |
|---|---|
Existing .mise.toml at repo root | Extend existing [env] section, don't create new file |
Existing .dbeaver/ directory | Merge connections, preserve existing entries |
| Non-standard CLICKHOUSE\_\* vars | Map to repository's naming convention |
| Multiple databases (local + cloud) | Generate multiple connection entries |
| No ClickHouse available | Warn and generate placeholder config |
Validation Checklist (Post-Generation)
The executor MUST verify:
- [ ] Generated JSON is valid (
jq . .dbeaver/data-sources.json) - [ ] DBeaver can import the config (launch and verify connection appears)
- [ ] mise tasks execute without error (
mise run db-client-generate) - [ ]
.dbeaver/added to.gitignore
Pydantic Model
The ClickHouseConnection model provides:
- Type-safe configuration with Pydantic v2 validation
- Computed fields for JDBC URL and connection ID
- Mode-aware defaults (cloud auto-enables SSL on port 8443)
- Environment loading via
from_env()class method
See references/pydantic-model.md for complete model documentation.
DBeaver Format
DBeaver uses .dbeaver/data-sources.json with this structure:
{
"folders": {},
"connections": {
"clickhouse-jdbc-{random-hex}": {
"provider": "clickhouse",
"driver": "com_clickhouse",
"name": "Connection Name",
"configuration": { ... }
}
}
}Important: DBeaver does NOT support ${VAR} substitution—values must be pre-populated at generation time.
See references/dbeaver-format.md for complete format specification.
macOS Notes
1. DBeaver binary: Use /Applications/DBeaver.app/Contents/MacOS/dbeaver (NOT open -a) 2. Gitignore: Add .dbeaver/ to .gitignore
Related Skills
| Skill | Integration |
|---|---|
devops-tools:clickhouse-cloud-management | Credential retrieval for cloud mode |
quality-tools:clickhouse-architect | Schema design context |
itp:mise-configuration | SSoT environment variable patterns |
Python Driver Policy
For Python application code connecting to ClickHouse (not DBeaver), use clickhouse-connect (official HTTP driver). See `clickhouse-architect` for:
- Recommended code patterns
- Why NOT to use
clickhouse-driver(community) - Performance vs maintenance trade-offs
Additional Resources
| Reference | Content |
|---|---|
| references/pydantic-model.md | Complete model documentation |
| references/dbeaver-format.md | DBeaver JSON format spec |
---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| DBeaver can't connect | Port mismatch (8123 vs 9000) | HTTP uses 8123, native uses 9000 - check config |
| Credentials not loading | .env not sourced | Run mise trust or source .env manually |
| JSON validation fails | Invalid data-sources.json | Validate with jq . .dbeaver/data-sources.json |
| Cloud SSL error | Missing SSL on port 8443 | Cloud mode auto-enables SSL - verify port is 8443 |
| mise task not found | Missing task definition | Add task to mise.toml [tasks] section |
| .dbeaver/ in git | Missing gitignore entry | Add .dbeaver/ to .gitignore |
| Connection ID conflict | Duplicate connection names | Each connection needs unique ID (random hex) |
| Config not updating | DBeaver caching | Restart DBeaver to reload data-sources.json |
Post-Execution Reflection
After this skill completes, check before closing:
1. Did the command succeed? — If not, fix the instruction or error table that caused the failure. 2. Did parameters or output change? — If the underlying tool's interface drifted, update Usage examples and Parameters table to match. 3. Was a workaround needed? — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.
Only update if the issue is real and reproducible — not speculative.
Skill: ClickHouse Pydantic Config
DBeaver Format Reference
<!-- ADR: 2025-12-09-clickhouse-pydantic-config-skill -->
Complete specification of the DBeaver data-sources.json format for ClickHouse connections.
File Location
DBeaver stores connection configurations in:
.dbeaver/data-sources.jsonThis file should be gitignored as it may contain credentials.
JSON Structure
{
"folders": {},
"connections": {
"clickhouse-jdbc-{random-hex}": {
"provider": "clickhouse",
"driver": "com_clickhouse",
"name": "Connection Display Name",
"configuration": {
"host": "localhost",
"port": "8123",
"database": "default",
"url": "jdbc:clickhouse:http://localhost:8123/default",
"type": "dev",
"auth-model": "native",
"user": "default",
"password": ""
}
}
}
}Field Reference
Root Level
| Field | Type | Description |
|---|---|---|
folders | object | Connection folder organization |
connections | object | Map of connection ID to config |
Connection Entry
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | Yes | Always "clickhouse" |
driver | string | Yes | Always "com_clickhouse" |
name | string | Yes | Display name in DBeaver UI |
configuration | object | Yes | Connection parameters |
Configuration Block
| Field | Type | Required | Description |
|---|---|---|---|
host | string | Yes | ClickHouse hostname |
port | string | Yes | HTTP port (as string) |
database | string | Yes | Default database |
url | string | Yes | Full JDBC URL |
type | string | Yes | "dev", "test", or "prod" |
auth-model | string | Yes | Always "native" for ClickHouse |
user | string | No | Username (omit for prompt) |
password | string | No | Password (omit for prompt) |
handler-ssl | string | No | "openssl" when SSL enabled |
ssl-mode | string | No | SSL verification mode |
Connection ID Format
Connection IDs must be unique and follow this pattern:
clickhouse-jdbc-{16-char-hex}Example: clickhouse-jdbc-a1b2c3d4e5f67890
Generated using secrets.token_hex(8) in Python.
JDBC URL Format
Local (HTTP)
jdbc:clickhouse:http://{host}:{port}/{database}Example: jdbc:clickhouse:http://localhost:8123/default
Cloud (HTTPS)
jdbc:clickhouse:https://{host}:{port}/{database}Example: jdbc:clickhouse:https://xyz.clickhouse.cloud:8443/default
SSL Configuration
For cloud connections, add these fields to configuration:
{
"handler-ssl": "openssl",
"ssl-mode": "require"
}SSL Modes:
| Mode | Description |
|---|---|
disable | No SSL |
require | SSL required, no certificate validation |
verify-ca | Verify server certificate |
verify-full | Verify certificate and hostname |
Important Limitations
No Variable Substitution
DBeaver does NOT support environment variable substitution in data-sources.json:
// WRONG - will not work
{
"host": "${CLICKHOUSE_HOST}",
"password": "${CLICKHOUSE_PASSWORD}"
}
// CORRECT - pre-populate values at generation time
{
"host": "localhost",
"password": ""
}Port as String
The port field must be a string, not an integer:
// WRONG
{ "port": 8123 }
// CORRECT
{ "port": "8123" }Complete Examples
Local Development
{
"folders": {},
"connections": {
"clickhouse-jdbc-abc123def456": {
"provider": "clickhouse",
"driver": "com_clickhouse",
"name": "ClickHouse Local",
"configuration": {
"host": "localhost",
"port": "8123",
"database": "default",
"url": "jdbc:clickhouse:http://localhost:8123/default",
"type": "dev",
"auth-model": "native",
"user": "default",
"password": ""
}
}
}
}ClickHouse Cloud
{
"folders": {},
"connections": {
"clickhouse-jdbc-789xyz012abc": {
"provider": "clickhouse",
"driver": "com_clickhouse",
"name": "ClickHouse Cloud",
"configuration": {
"host": "xyz.clickhouse.cloud",
"port": "8443",
"database": "default",
"url": "jdbc:clickhouse:https://xyz.clickhouse.cloud:8443/default",
"type": "prod",
"auth-model": "native",
"handler-ssl": "openssl",
"ssl-mode": "require",
"user": "readonly_user",
"password": "secret-password"
}
}
}
}macOS Notes
DBeaver Binary Path
Use the full binary path, NOT open -a:
# CORRECT
/Applications/DBeaver.app/Contents/MacOS/dbeaver -data .dbeaver-workspace &
# WRONG - does not support -data flag
open -a DBeaverWorkspace Separation
Use -data flag to keep project-specific workspace:
dbeaver -data .dbeaver-workspace &This prevents mixing connections across projects.
Related
- Pydantic Model Reference
- Parent Skill
Evolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---
Skill: ClickHouse Pydantic Config
Pydantic Model Reference
<!-- ADR: 2025-12-09-clickhouse-pydantic-config-skill -->
Complete documentation for the ClickHouseConnection Pydantic v2 model.
Model Overview
The ClickHouseConnection model serves as the Single Source of Truth (SSoT) for ClickHouse connection configuration. It provides:
- Type-safe configuration with validation
- Computed fields for derived values
- Mode-aware defaults (local vs cloud)
- Environment variable loading
Fields
| Field | Type | Default | Description |
|---|---|---|---|
name | str | "clickhouse-local" | Connection display name |
mode | ConnectionMode | LOCAL | local or cloud |
host | str | "localhost" | ClickHouse hostname |
port | int | 8123 | HTTP port |
database | str | "default" | Default database |
ssl_enabled | bool | False | Enable SSL/TLS |
ssl_mode | Literal["disable", "require", "verify-ca", ...] | "disable" | SSL verification mode |
connection_type | Literal["dev", "test", "prod"] | "dev" | Environment type for DBeaver UI |
Computed Fields
jdbc_url
Generates the JDBC connection URL:
@computed_field
@property
def jdbc_url(self) -> str:
protocol = "https" if self.ssl_enabled else "http"
return f"jdbc:clickhouse:{protocol}://{self.host}:{self.port}/{self.database}"Examples:
- Local:
jdbc:clickhouse:http://localhost:8123/default - Cloud:
jdbc:clickhouse:https://xyz.clickhouse.cloud:8443/default
connection_id
Generates unique DBeaver connection ID:
@computed_field
@property
def connection_id(self) -> str:
return f"clickhouse-jdbc-{secrets.token_hex(8)}"Example: clickhouse-jdbc-a1b2c3d4e5f67890
Model Validator
The validate_mode_settings validator automatically applies cloud defaults:
@model_validator(mode='after')
def validate_mode_settings(self) -> 'ClickHouseConnection':
if self.mode == ConnectionMode.CLOUD:
self.port = 8443
self.ssl_enabled = True
self.ssl_mode = "require"
return selfFactory Methods
from_env()
Creates a connection from environment variables:
@classmethod
def from_env(cls, prefix: str = "CLICKHOUSE_") -> 'ClickHouseConnection':
return cls(
name=os.environ.get(f"{prefix}NAME", "clickhouse-local"),
mode=ConnectionMode(os.environ.get(f"{prefix}MODE", "local")),
host=os.environ.get(f"{prefix}HOST", "localhost"),
port=int(os.environ.get(f"{prefix}PORT", "8123")),
database=os.environ.get(f"{prefix}DATABASE", "default"),
connection_type=os.environ.get(f"{prefix}TYPE", "dev")
)Environment Variables:
| Variable | Default |
|---|---|
CLICKHOUSE_NAME | clickhouse-local |
CLICKHOUSE_MODE | local |
CLICKHOUSE_HOST | localhost |
CLICKHOUSE_PORT | 8123 |
CLICKHOUSE_DATABASE | default |
CLICKHOUSE_TYPE | dev |
Instance Methods
to_dbeaver_config()
Generates DBeaver connection entry with mode-aware credential handling:
def to_dbeaver_config(self) -> dict:
config = {
"provider": "clickhouse",
"driver": "com_clickhouse",
"name": self.name,
"configuration": {
"host": self.host,
"port": str(self.port),
"database": self.database,
"url": self.jdbc_url,
"type": self.connection_type,
"auth-model": "native"
}
}
# Credential handling by mode
if self.mode == ConnectionMode.LOCAL:
config["configuration"]["user"] = "default"
config["configuration"]["password"] = ""
elif self.mode == ConnectionMode.CLOUD:
config["configuration"]["user"] = os.environ.get("CLICKHOUSE_USER_READONLY", "default")
config["configuration"]["password"] = os.environ.get("CLICKHOUSE_PASSWORD_READONLY", "")
return configUsage Examples
Basic Local Connection
conn = ClickHouseConnection()
print(conn.jdbc_url) # jdbc:clickhouse:http://localhost:8123/defaultCloud Connection
conn = ClickHouseConnection(
mode=ConnectionMode.CLOUD,
host="xyz.clickhouse.cloud"
)
# Automatically sets port=8443, ssl_enabled=True, ssl_mode="require"
print(conn.jdbc_url) # jdbc:clickhouse:https://xyz.clickhouse.cloud:8443/defaultFrom Environment
# With mise [env] or exported variables
conn = ClickHouseConnection.from_env()
config = conn.to_dbeaver_config()ConnectionMode Enum
class ConnectionMode(str, Enum):
LOCAL = "local"
CLOUD = "cloud"Related
- DBeaver Format Reference
- Parent Skill
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pydantic>=2.0",
# ]
# ///
"""Generate DBeaver data-sources.json from Pydantic ClickHouse connection model.
ADR: 2025-12-09-clickhouse-pydantic-config-skill
This script uses Pydantic v2 as the Single Source of Truth (SSoT) for ClickHouse
connection configuration. All configurable values are read from environment
variables (mise `[env]` section) with sensible defaults.
Usage:
uv run scripts/generate_dbeaver_config.py --output .dbeaver/data-sources.json
uv run scripts/generate_dbeaver_config.py --mode cloud --output .dbeaver/data-sources.json
"""
from __future__ import annotations
import argparse
import json
import os
import secrets
import sys
from enum import Enum
from pathlib import Path
from typing import Literal
from pydantic import BaseModel, Field, computed_field, model_validator
class ConnectionMode(str, Enum):
"""Connection mode determines SSL and port defaults."""
LOCAL = "local"
CLOUD = "cloud"
class ClickHouseConnection(BaseModel):
"""ClickHouse connection configuration - Single Source of Truth.
ADR: 2025-12-09-clickhouse-pydantic-config-skill
This model defines all connection parameters. Values are read from
environment variables with the CLICKHOUSE_ prefix by default.
"""
name: str = Field(default="clickhouse-local", description="Connection display name")
mode: ConnectionMode = Field(
default=ConnectionMode.LOCAL, description="local or cloud"
)
host: str = Field(default="localhost", description="ClickHouse hostname")
port: int = Field(default=8123, description="HTTP port (8123 local, 8443 cloud)")
database: str = Field(default="default", description="Default database")
ssl_enabled: bool = Field(default=False, description="Enable SSL/TLS")
ssl_mode: Literal["disable", "require", "verify-ca", "verify-full"] = Field(
default="disable"
)
connection_type: Literal["dev", "test", "prod"] = Field(default="dev")
@model_validator(mode="after")
def validate_mode_settings(self) -> ClickHouseConnection:
"""Apply cloud-specific defaults when mode is CLOUD."""
if self.mode == ConnectionMode.CLOUD:
self.port = 8443
self.ssl_enabled = True
self.ssl_mode = "require"
return self
@computed_field
@property
def jdbc_url(self) -> str:
"""Generate JDBC URL for DBeaver."""
protocol = "https" if self.ssl_enabled else "http"
return f"jdbc:clickhouse:{protocol}://{self.host}:{self.port}/{self.database}"
@computed_field
@property
def connection_id(self) -> str:
"""Generate unique connection ID for DBeaver."""
return f"clickhouse-jdbc-{secrets.token_hex(8)}"
def to_dbeaver_config(self) -> dict:
"""Generate DBeaver data-sources.json connection entry.
Credential handling by mode:
- LOCAL: Hardcode `default` user, empty password (zero friction)
- CLOUD: Pre-populate from environment (gitignored output)
"""
config = {
"provider": "clickhouse",
"driver": "com_clickhouse",
"name": self.name,
"configuration": {
"host": self.host,
"port": str(self.port),
"database": self.database,
"url": self.jdbc_url,
"type": self.connection_type,
"auth-model": "native",
},
}
if self.ssl_enabled:
config["configuration"]["handler-ssl"] = "openssl"
config["configuration"]["ssl-mode"] = self.ssl_mode
# Credential handling by mode
if self.mode == ConnectionMode.LOCAL:
# Local: hardcode default credentials (no security concern)
config["configuration"]["user"] = "default"
config["configuration"]["password"] = ""
elif self.mode == ConnectionMode.CLOUD:
# Cloud: read from environment (gitignored output)
config["configuration"]["user"] = os.environ.get(
"CLICKHOUSE_USER_READONLY", "default"
)
config["configuration"]["password"] = os.environ.get(
"CLICKHOUSE_PASSWORD_READONLY", ""
)
return config
@classmethod
def from_env(cls, prefix: str = "CLICKHOUSE_") -> ClickHouseConnection:
"""Create connection from environment variables.
Reads from mise `[env]` section with backward-compatible defaults.
Works with or without mise installed.
"""
mode_str = os.environ.get(f"{prefix}MODE", "local")
return cls(
name=os.environ.get(f"{prefix}NAME", "clickhouse-local"),
mode=ConnectionMode(mode_str),
host=os.environ.get(f"{prefix}HOST", "localhost"),
port=int(os.environ.get(f"{prefix}PORT", "8123")),
database=os.environ.get(f"{prefix}DATABASE", "default"),
connection_type=os.environ.get(f"{prefix}TYPE", "dev"),
)
def generate_dbeaver_datasources(connection: ClickHouseConnection) -> dict:
"""Generate complete DBeaver data-sources.json structure."""
return {
"folders": {},
"connections": {connection.connection_id: connection.to_dbeaver_config()},
}
def main() -> int:
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Generate DBeaver config from Pydantic ClickHouse model"
)
parser.add_argument(
"--output",
"-o",
type=Path,
default=Path(".dbeaver/data-sources.json"),
help="Output path for data-sources.json",
)
parser.add_argument(
"--mode",
"-m",
choices=["local", "cloud"],
help="Override connection mode (default: from CLICKHOUSE_MODE env var)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print config to stdout without writing file",
)
args = parser.parse_args()
# Create connection from environment
connection = ClickHouseConnection.from_env()
# Override mode if specified
if args.mode:
connection = ClickHouseConnection(
name=connection.name,
mode=ConnectionMode(args.mode),
host=connection.host,
port=connection.port,
database=connection.database,
connection_type=connection.connection_type,
)
# Generate DBeaver config
config = generate_dbeaver_datasources(connection)
config_json = json.dumps(config, indent=2)
if args.dry_run:
print(config_json)
return 0
# Ensure output directory exists
args.output.parent.mkdir(parents=True, exist_ok=True)
# Write config
args.output.write_text(config_json)
print(f"Generated: {args.output}")
print(f" Connection: {connection.name}")
print(f" Mode: {connection.mode.value}")
print(f" Host: {connection.host}:{connection.port}")
print(f" Database: {connection.database}")
return 0
if __name__ == "__main__":
sys.exit(main())
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "pydantic>=2.0",
# ]
# ///
"""Validate DBeaver data-sources.json against expected schema.
ADR: 2025-12-09-clickhouse-pydantic-config-skill
This script validates generated DBeaver configurations to ensure they
conform to the expected structure before use.
Usage:
uv run scripts/validate_config.py --config .dbeaver/data-sources.json
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
from pydantic import BaseModel, Field, ValidationError
class DBeaverConnectionConfig(BaseModel):
"""DBeaver connection configuration block."""
host: str
port: str
database: str
url: str
type: str = Field(pattern=r"^(dev|test|prod)$")
class DBeaverConnection(BaseModel):
"""Single DBeaver connection entry."""
provider: str = Field(pattern=r"^clickhouse$")
driver: str = Field(pattern=r"^com_clickhouse$")
name: str
configuration: DBeaverConnectionConfig
class DBeaverDataSources(BaseModel):
"""Complete DBeaver data-sources.json structure."""
folders: dict[str, Any] = Field(default_factory=dict)
connections: dict[str, DBeaverConnection]
def validate_config(config_path: Path) -> tuple[bool, list[str]]:
"""Validate DBeaver config file.
Returns:
Tuple of (is_valid, list of error messages)
"""
errors: list[str] = []
if not config_path.exists():
return False, [f"Config file not found: {config_path}"]
try:
config_data = json.loads(config_path.read_text())
except json.JSONDecodeError as e:
return False, [f"Invalid JSON: {e}"]
try:
datasources = DBeaverDataSources.model_validate(config_data)
except ValidationError as e:
for error in e.errors():
loc = ".".join(str(x) for x in error["loc"])
errors.append(f"{loc}: {error['msg']}")
return False, errors
# Additional validations
if not datasources.connections:
errors.append("No connections defined")
return False, errors
for conn_id, conn in datasources.connections.items():
# Validate connection ID format
if not conn_id.startswith("clickhouse-jdbc-"):
errors.append(f"Connection ID should start with 'clickhouse-jdbc-': {conn_id}")
# Validate JDBC URL consistency
expected_protocol = "https" if conn.configuration.url.startswith("jdbc:clickhouse:https") else "http"
port = conn.configuration.port
if expected_protocol == "https" and port != "8443":
errors.append(f"HTTPS connection should use port 8443, got {port}")
if expected_protocol == "http" and port not in ("8123", "9000"):
errors.append(f"HTTP connection should use port 8123 or 9000, got {port}")
return len(errors) == 0, errors
def main() -> int:
"""Main entry point."""
parser = argparse.ArgumentParser(description="Validate DBeaver config")
parser.add_argument(
"--config",
"-c",
type=Path,
default=Path(".dbeaver/data-sources.json"),
help="Path to data-sources.json",
)
args = parser.parse_args()
is_valid, errors = validate_config(args.config)
if is_valid:
print(f"Valid: {args.config}")
return 0
print(f"Invalid: {args.config}")
for error in errors:
print(f" - {error}")
return 1
if __name__ == "__main__":
sys.exit(main())