Skip to content

Commit b278269

Browse files
fix: structured config with create_if_missing for non-existent database (#50)
When using structured database config, the `name` field was included in the initial connection string. If the target database did not exist yet (and a phase had `create_if_missing: true`), the connection failed before the create phase could run. Now initium detects this scenario and first connects to the driver's default admin database (`postgres` / `mysql`), creates the missing database, then reconnects to the target for full execution. Also adds integration tests for structured config edge cases: special-character passwords, PostgreSQL options field, and the create_if_missing regression. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 06794c4 commit b278269

3 files changed

Lines changed: 633 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- Structured database config now works with `create_if_missing: true` when the target database does not yet exist. Previously, the initial connection included the non-existent database name, causing an immediate connection error. Now initium connects to the driver's default admin database (`postgres` / `mysql`) first, creates the target, then reconnects. Fixes [#50](https://github.com/KitStream/initium/issues/50).
13+
14+
### Chores
15+
16+
- Added integration tests for structured database connectivity: special-character passwords (URL-reserved chars like `@`, `:`, `/`, `?`, `#`, `%`), PostgreSQL `options` field (`connect_timeout`), and `create_if_missing` with non-existent database ([#50](https://github.com/KitStream/initium/issues/50)).
17+
1018
## [2.0.1] - 2026-03-14
1119

1220
### Fixed

src/seed/mod.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ pub mod schema;
55

66
use crate::logging::Logger;
77

8+
fn default_admin_database(driver: &str) -> String {
9+
match driver {
10+
"postgres" | "postgresql" => "postgres".into(),
11+
"mysql" => "mysql".into(),
12+
_ => String::new(),
13+
}
14+
}
15+
816
fn render_template(content: &str) -> Result<String, String> {
917
let env_map: std::collections::HashMap<String, String> = std::env::vars().collect();
1018
let mut jinja_env = minijinja::Environment::new();
@@ -41,6 +49,44 @@ pub fn run(
4149
let tracking_table = plan.database.tracking_table.clone();
4250
let driver = plan.database.driver.clone();
4351

52+
// When using structured config and a phase needs to create a database that
53+
// matches the configured name, we must first connect without the database
54+
// name (to the driver's default database), create the target, then
55+
// reconnect. See https://github.com/KitStream/initium/issues/50
56+
let needs_bootstrap = plan.database.has_structured_config()
57+
&& plan.phases.iter().any(|p| {
58+
p.create_if_missing && !p.database.is_empty() && p.database == plan.database.name
59+
});
60+
61+
if needs_bootstrap {
62+
let mut admin_config = plan.database.clone();
63+
admin_config.name = default_admin_database(&admin_config.driver);
64+
65+
log.info(
66+
"connecting to default database for bootstrap",
67+
&[("driver", driver.as_str())],
68+
);
69+
let mut admin_db = db::connect(&admin_config)?;
70+
71+
for phase in &plan.phases {
72+
if phase.create_if_missing && !phase.database.is_empty() {
73+
log.info(
74+
"creating database if missing",
75+
&[("database", phase.database.as_str())],
76+
);
77+
admin_db.create_database(&phase.database)?;
78+
}
79+
if phase.create_if_missing && !phase.schema.is_empty() {
80+
log.info(
81+
"creating schema if missing",
82+
&[("schema", phase.schema.as_str())],
83+
);
84+
admin_db.create_schema(&phase.schema)?;
85+
}
86+
}
87+
drop(admin_db);
88+
}
89+
4490
log.info("connecting to database", &[("driver", driver.as_str())]);
4591

4692
let db = db::connect(&plan.database)?;

0 commit comments

Comments
 (0)