Skip to content

Commit fb3ac37

Browse files
Merge pull request #162 from jsdt/jsdt/reuse-children
Check for existing installations in children before installing.
2 parents 3dd9541 + 4746b48 commit fb3ac37

File tree

1 file changed

+52
-11
lines changed

1 file changed

+52
-11
lines changed

postgresql_embedded/src/postgresql.rs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ use postgresql_commands::initdb::InitDbBuilder;
1313
use postgresql_commands::pg_ctl::Mode::{Start, Stop};
1414
use postgresql_commands::pg_ctl::PgCtlBuilder;
1515
use postgresql_commands::pg_ctl::ShutdownMode::Fast;
16+
use semver::Version;
1617
use sqlx::{PgPool, Row};
17-
use std::fs::{remove_dir_all, remove_file};
18+
use std::fs::{read_dir, remove_dir_all, remove_file};
1819
use std::io::prelude::*;
1920
use std::net::TcpListener;
21+
use std::path::PathBuf;
2022
use tracing::{debug, instrument};
2123

2224
use crate::Error::{CreateDatabaseError, DatabaseExistsError, DropDatabaseError};
@@ -73,7 +75,7 @@ impl PostgreSQL {
7375
Status::Started
7476
} else if self.is_initialized() {
7577
Status::Stopped
76-
} else if self.is_installed() {
78+
} else if self.installed_dir().is_some() {
7779
Status::Installed
7880
} else {
7981
Status::NotInstalled
@@ -86,13 +88,48 @@ impl PostgreSQL {
8688
&self.settings
8789
}
8890

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-
};
91+
/// Find a directory where `PostgreSQL` server is installed.
92+
/// This first checks if the installation directory exists and matches the version requirement.
93+
/// If it doesn't, it will search all the child directories for the latest version that matches the requirement.
94+
/// If it returns None, we couldn't find a matching installation.
95+
fn installed_dir(&self) -> Option<PathBuf> {
9496
let path = &self.settings.installation_dir;
95-
path.ends_with(version.to_string()) && path.exists()
97+
let maybe_path_version = path
98+
.file_name()
99+
.and_then(|file_name| Version::parse(&file_name.to_string_lossy()).ok());
100+
// If this directory matches the version requirement, we're done.
101+
if let Some(path_version) = maybe_path_version {
102+
if self.settings.version.matches(&path_version) && path.exists() {
103+
return Some(path.clone());
104+
}
105+
}
106+
107+
// Get all directories in the path as versions.
108+
let mut versions = read_dir(path)
109+
.ok()?
110+
.filter_map(|entry| {
111+
let Some(entry) = entry.ok() else {
112+
// We ignore filesystem errors.
113+
return None;
114+
};
115+
// Skip non-directories
116+
if !entry.file_type().ok()?.is_dir() {
117+
return None;
118+
}
119+
let file_name = entry.file_name();
120+
let version = Version::parse(&file_name.to_string_lossy()).ok()?;
121+
if self.settings.version.matches(&version) {
122+
Some((version, entry.path()))
123+
} else {
124+
None
125+
}
126+
})
127+
.collect::<Vec<_>>();
128+
// Sort the versions in descending order i.e. latest version first
129+
versions.sort_by(|(a, _), (b, _)| b.cmp(a));
130+
// Get the first matching version as the best match
131+
let version_path = versions.first().map(|(_, path)| path.clone());
132+
version_path
96133
}
97134

98135
/// Check if the `PostgreSQL` server is initialized
@@ -111,10 +148,14 @@ impl PostgreSQL {
111148
/// If the data directory already exists, the database will not be initialized.
112149
#[instrument(skip(self))]
113150
pub async fn setup(&mut self) -> Result<()> {
114-
if !self.is_installed() {
115-
self.install().await?;
151+
match self.installed_dir() {
152+
Some(installed_dir) => {
153+
self.settings.installation_dir = installed_dir;
154+
}
155+
None => {
156+
self.install().await?;
157+
}
116158
}
117-
118159
if !self.is_initialized() {
119160
self.initialize().await?;
120161
}

0 commit comments

Comments
 (0)