From af3c78c7827176704b142800aba48cbd07814e0f Mon Sep 17 00:00:00 2001 From: sdairs Date: Fri, 5 Jun 2026 13:43:38 +0100 Subject: [PATCH 1/2] Create postgres project scaffold on local init `local init` now creates a `postgres/` scaffold (tables/, materialized_views/, queries/, seed/) alongside the existing `clickhouse/` scaffold, so Postgres project files have a home too. Generalized `create_project_scaffold` to take the target dir and subdirs, added unit tests covering creation and idempotency, and updated the agent help text and README. Closes #221 Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 5 +++ crates/clickhousectl/src/init.rs | 64 +++++++++++++++++++++++---- crates/clickhousectl/src/local/cli.rs | 7 +-- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 119c701..13f6d86 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,11 @@ clickhouse/ ├── materialized_views/ # Materialized view definitions ├── queries/ # Saved queries └── seed/ # Seed data / INSERT statements +postgres/ +├── tables/ # Table definitions (CREATE TABLE ...) +├── materialized_views/ # Materialized view definitions +├── queries/ # Saved queries +└── seed/ # Seed data / INSERT statements ``` ### Running queries diff --git a/crates/clickhousectl/src/init.rs b/crates/clickhousectl/src/init.rs index fef12fa..0956f93 100644 --- a/crates/clickhousectl/src/init.rs +++ b/crates/clickhousectl/src/init.rs @@ -13,6 +13,12 @@ pub fn project_dir() -> PathBuf { .join("clickhouse") } +pub fn postgres_project_dir() -> PathBuf { + std::env::current_dir() + .expect("failed to get current directory") + .join("postgres") +} + pub fn is_initialized() -> bool { local_dir().exists() } @@ -28,17 +34,21 @@ pub fn init() -> Result<()> { eprintln!("Initialized ClickHouse project in {}", dir.display()); } - create_project_scaffold()?; + create_project_scaffold( + project_dir(), + &["tables", "materialized_views", "queries", "seed"], + )?; + create_project_scaffold( + postgres_project_dir(), + &["tables", "materialized_views", "queries", "seed"], + )?; Ok(()) } -fn create_project_scaffold() -> Result<()> { - let dir = project_dir(); - let subdirs = ["tables", "materialized_views", "queries", "seed"]; - +fn create_project_scaffold(dir: PathBuf, subdirs: &[&str]) -> Result<()> { let mut created = false; - for subdir in &subdirs { + for subdir in subdirs { let path = dir.join(subdir); if !path.exists() { std::fs::create_dir_all(&path)?; @@ -49,8 +59,9 @@ fn create_project_scaffold() -> Result<()> { if created { eprintln!( - "Created project scaffold in {}/ (tables, materialized_views, queries, seed)", - dir.display() + "Created project scaffold in {}/ ({})", + dir.display(), + subdirs.join(", ") ); } @@ -61,3 +72,40 @@ fn create_project_scaffold() -> Result<()> { pub fn server_flags() -> Vec { vec!["--".into(), "--path=./".into()] } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn scaffold_creates_subdirs_with_gitkeep() { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path().join("postgres"); + let subdirs = ["tables", "materialized_views", "queries", "seed"]; + + create_project_scaffold(dir.clone(), &subdirs).unwrap(); + + for subdir in &subdirs { + let path = dir.join(subdir); + assert!(path.is_dir(), "{} should be a directory", path.display()); + assert!( + path.join(".gitkeep").is_file(), + "{}/.gitkeep should exist", + path.display() + ); + } + } + + #[test] + fn scaffold_is_idempotent() { + let tmp = tempfile::tempdir().unwrap(); + let dir = tmp.path().join("clickhouse"); + let subdirs = ["tables", "queries"]; + + create_project_scaffold(dir.clone(), &subdirs).unwrap(); + // Running again over an existing scaffold must not error. + create_project_scaffold(dir.clone(), &subdirs).unwrap(); + + assert!(dir.join("tables").join(".gitkeep").is_file()); + } +} diff --git a/crates/clickhousectl/src/local/cli.rs b/crates/clickhousectl/src/local/cli.rs index e681583..78612ed 100644 --- a/crates/clickhousectl/src/local/cli.rs +++ b/crates/clickhousectl/src/local/cli.rs @@ -78,9 +78,10 @@ CONTEXT FOR AGENTS: /// Initialize a project-local ClickHouse configuration #[command(after_help = "\ CONTEXT FOR AGENTS: - Creates a .clickhouse/ directory (runtime data, git-ignored) and a clickhouse/ project - scaffold with subdirs: tables/, materialized_views/, queries/, seed/ (each with .gitkeep). - The clickhouse/ directory is meant to be committed — organize your SQL files there. + Creates a .clickhouse/ directory (runtime data, git-ignored) plus clickhouse/ and postgres/ + project scaffolds, each with subdirs: tables/, materialized_views/, queries/, seed/ (each + with .gitkeep). The clickhouse/ and postgres/ directories are meant to be committed — + organize your SQL files there. Related: `clickhousectl local server start` to start a server with project-local data.")] Init, From 54d974b26dba80e00d241577c94680d22f7c7bae Mon Sep 17 00:00:00 2001 From: sdairs Date: Fri, 5 Jun 2026 13:59:28 +0100 Subject: [PATCH 2/2] Use PG-native subdirs for postgres scaffold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Postgres scaffold now uses tables/, views/, functions/, queries/, seed/ — plain views and functions/procedures are far more central to a Postgres project than ClickHouse-style materialized views, which in PG are just manually-refreshed query snapshots. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 3 ++- crates/clickhousectl/src/init.rs | 2 +- crates/clickhousectl/src/local/cli.rs | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 13f6d86..b27c568 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,8 @@ clickhouse/ └── seed/ # Seed data / INSERT statements postgres/ ├── tables/ # Table definitions (CREATE TABLE ...) -├── materialized_views/ # Materialized view definitions +├── views/ # View definitions (CREATE VIEW ...) +├── functions/ # Function / procedure definitions (CREATE FUNCTION ...) ├── queries/ # Saved queries └── seed/ # Seed data / INSERT statements ``` diff --git a/crates/clickhousectl/src/init.rs b/crates/clickhousectl/src/init.rs index 0956f93..248de5e 100644 --- a/crates/clickhousectl/src/init.rs +++ b/crates/clickhousectl/src/init.rs @@ -40,7 +40,7 @@ pub fn init() -> Result<()> { )?; create_project_scaffold( postgres_project_dir(), - &["tables", "materialized_views", "queries", "seed"], + &["tables", "views", "functions", "queries", "seed"], )?; Ok(()) diff --git a/crates/clickhousectl/src/local/cli.rs b/crates/clickhousectl/src/local/cli.rs index 78612ed..e4b6900 100644 --- a/crates/clickhousectl/src/local/cli.rs +++ b/crates/clickhousectl/src/local/cli.rs @@ -79,9 +79,9 @@ CONTEXT FOR AGENTS: #[command(after_help = "\ CONTEXT FOR AGENTS: Creates a .clickhouse/ directory (runtime data, git-ignored) plus clickhouse/ and postgres/ - project scaffolds, each with subdirs: tables/, materialized_views/, queries/, seed/ (each - with .gitkeep). The clickhouse/ and postgres/ directories are meant to be committed — - organize your SQL files there. + project scaffolds (each subdir has a .gitkeep). clickhouse/: tables/, materialized_views/, + queries/, seed/. postgres/: tables/, views/, functions/, queries/, seed/. The clickhouse/ and + postgres/ directories are meant to be committed — organize your SQL files there. Related: `clickhousectl local server start` to start a server with project-local data.")] Init,