Skip to content

Commit 3c4bff0

Browse files
committed
Check for existing installations in children before installing.
1 parent fef1ee9 commit 3c4bff0

File tree

3 files changed

+60
-10
lines changed

3 files changed

+60
-10
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

postgresql_embedded/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ thiserror = { workspace = true }
3030
tokio = { workspace = true, features = ["full"], optional = true }
3131
tracing = { workspace = true, features = ["log"] }
3232
url = { workspace = true }
33+
walkdir = "2.5.0"
3334

3435
[dev-dependencies]
3536
anyhow = { workspace = true }

postgresql_embedded/src/postgresql.rs

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ use postgresql_commands::AsyncCommandExecutor;
1313
use postgresql_commands::CommandBuilder;
1414
#[cfg(not(feature = "tokio"))]
1515
use postgresql_commands::CommandExecutor;
16+
use semver::Version;
1617
use sqlx::{PgPool, Row};
18+
use std::ffi::OsStr;
1719
use std::fs::{remove_dir_all, remove_file};
1820
use std::io::prelude::*;
1921
use std::net::TcpListener;
22+
use std::path::PathBuf;
2023
use tracing::{debug, instrument};
24+
use walkdir::WalkDir;
2125

2226
use crate::Error::{CreateDatabaseError, DatabaseExistsError, DropDatabaseError};
2327

@@ -73,7 +77,7 @@ impl PostgreSQL {
7377
Status::Started
7478
} else if self.is_initialized() {
7579
Status::Stopped
76-
} else if self.is_installed() {
80+
} else if self.installed_dir().is_some() {
7781
Status::Installed
7882
} else {
7983
Status::NotInstalled
@@ -86,13 +90,53 @@ impl PostgreSQL {
8690
&self.settings
8791
}
8892

89-
/// Check if the `PostgreSQL` server is installed
90-
fn is_installed(&self) -> bool {
91-
let Some(version) = self.settings.version.exact_version() else {
92-
return false;
93-
};
93+
/// Find a directory where `PostgreSQL` server is installed.
94+
/// This first checks if the installation directory exists and matches the version requirement.
95+
/// If it doesn't, it will search all the child directories for the latest version that matches the requirement.
96+
/// If it returns None, we couldn't find a matching installation.
97+
fn installed_dir(&self) -> Option<PathBuf> {
98+
fn file_name_to_version(name: &OsStr) -> Option<Version> {
99+
Version::parse(name.to_string_lossy().as_ref()).ok()
100+
}
94101
let path = &self.settings.installation_dir;
95-
path.ends_with(version.to_string()) && path.exists()
102+
let maybe_path_version = path
103+
.file_name()
104+
.map(|name| file_name_to_version(name))
105+
.flatten();
106+
// If this directory matches the version requirement, we're done.
107+
if let Some(path_version) = maybe_path_version {
108+
if self.settings.version.matches(&path_version) && path.exists() {
109+
return Some(path.to_path_buf());
110+
}
111+
}
112+
// Otherwise we check the child directories.
113+
let mut max_version: Option<Version> = None;
114+
let mut max_path: Option<PathBuf> = None;
115+
for entry in WalkDir::new(path).min_depth(1).max_depth(1) {
116+
let Some(entry) = entry.ok() else {
117+
// We ignore filesystem errors.
118+
continue;
119+
};
120+
// Skip non-directories
121+
if !entry.file_type().is_dir() {
122+
continue;
123+
}
124+
// If it doesn't look like a version, we ignore it.
125+
let Some(version) = file_name_to_version(entry.file_name()) else {
126+
continue;
127+
};
128+
// If it doesn't match the version requirement, we ignore it.
129+
if !self.settings.version.matches(&version) {
130+
continue;
131+
}
132+
// If we already found a version that's greater or equal, we ignore it.
133+
if max_version.iter().any(|prev_max| *prev_max >= version) {
134+
continue;
135+
}
136+
max_version = Some(version.clone());
137+
max_path = Some(entry.path().to_path_buf());
138+
}
139+
max_path
96140
}
97141

98142
/// Check if the `PostgreSQL` server is initialized
@@ -111,10 +155,14 @@ impl PostgreSQL {
111155
/// If the data directory already exists, the database will not be initialized.
112156
#[instrument(skip(self))]
113157
pub async fn setup(&mut self) -> Result<()> {
114-
if !self.is_installed() {
115-
self.install().await?;
158+
match self.installed_dir() {
159+
Some(installed_dir) => {
160+
self.settings.installation_dir = installed_dir;
161+
}
162+
None => {
163+
self.install().await?;
164+
}
116165
}
117-
118166
if !self.is_initialized() {
119167
self.initialize().await?;
120168
}

0 commit comments

Comments
 (0)