11use std:: convert:: TryInto ;
2- use std:: path:: Path ;
2+ use std:: path:: { Path , PathBuf } ;
33use std:: process:: Stdio ;
44use std:: sync:: { Arc , OnceLock } ;
55
@@ -115,6 +115,7 @@ pub(crate) async fn exec_create_db(
115115 port : u32 ,
116116 owner : & str ,
117117 dbname : & str ,
118+ template_db : Option < & str > ,
118119) -> TmpPostgrustResult < ( ) > {
119120 let mut command = Command :: new ( createdb_bin) ;
120121 command
@@ -126,10 +127,35 @@ pub(crate) async fn exec_create_db(
126127 . arg ( "postgres" )
127128 . arg ( "-O" )
128129 . arg ( owner)
130+ . arg ( "--echo" ) ;
131+ if let Some ( template_db) = template_db {
132+ command. arg ( "-T" ) . arg ( template_db) ;
133+ }
134+ command. arg ( dbname) ;
135+ cmd_as_non_root ( & mut command) ;
136+ exec_process ( & mut command, TmpPostgrustError :: CreateDBFailed ) . await
137+ }
138+
139+ #[ instrument]
140+ pub ( crate ) async fn exec_drop_db (
141+ dropdb_bin : & Path ,
142+ socket : & Path ,
143+ port : u32 ,
144+ dbname : & str ,
145+ ) -> TmpPostgrustResult < ( ) > {
146+ let mut command = Command :: new ( dropdb_bin) ;
147+ command
148+ . arg ( "-h" )
149+ . arg ( socket)
150+ . arg ( "-p" )
151+ . arg ( port. to_string ( ) )
152+ . arg ( "-U" )
153+ . arg ( "postgres" )
154+ . arg ( "--if-exists" )
129155 . arg ( "--echo" )
130156 . arg ( dbname) ;
131157 cmd_as_non_root ( & mut command) ;
132- exec_process ( & mut command, TmpPostgrustError :: CreateDBFailed ) . await
158+ exec_process ( & mut command, TmpPostgrustError :: DropDBFailed ) . await
133159}
134160
135161#[ instrument]
@@ -199,6 +225,7 @@ pub struct ProcessGuard {
199225 pub ( crate ) _cache_directory : Arc < TempDir > ,
200226 /// Socket directory for connection to the running process.
201227 pub ( crate ) socket_dir : Arc < TempDir > ,
228+ pub ( crate ) createdb_bin : PathBuf ,
202229 pub ( crate ) postgres_process : Child ,
203230 // Limit the total concurrent processes.
204231 pub ( crate ) _process_permit : SemaphorePermit < ' static > ,
@@ -224,17 +251,47 @@ impl ProcessGuard {
224251 /// Panics if a string file path cannot be obtained from the socket directory.
225252 #[ must_use]
226253 pub fn connection_string ( & self ) -> String {
254+ self . connection_string_for_db ( & self . db_name )
255+ }
256+
257+ /// Get a Postgresql format connection string for a database on this running instance.
258+ ///
259+ /// # Panics
260+ ///
261+ /// Panics if a string file path cannot be obtained from the socket directory.
262+ #[ must_use]
263+ pub fn connection_string_for_db ( & self , db_name : & str ) -> String {
227264 format ! (
228265 "postgresql:///?host={}&port={}&dbname={}&user={}" ,
229266 self . socket_dir
230267 . path( )
231268 . to_str( )
232269 . expect( "Failed to convert socket directory to a path" ) ,
233270 self . port,
234- self . db_name,
271+ db_name,
235272 self . user_name,
236273 )
237274 }
275+
276+ /// Create a new database in this running instance from the preserved template database.
277+ ///
278+ /// The returned connection string points at the newly-created database.
279+ ///
280+ /// # Errors
281+ ///
282+ /// Returns an error if `createdb` fails to clone the template database.
283+ pub async fn create_database ( & self , db_name : & str ) -> TmpPostgrustResult < String > {
284+ exec_create_db (
285+ & self . createdb_bin ,
286+ self . socket_dir . path ( ) ,
287+ self . port ,
288+ & self . user_name ,
289+ db_name,
290+ Some ( crate :: TMP_POSTGRUST_TEMPLATE_DB_NAME ) ,
291+ )
292+ . await ?;
293+ Ok ( self . connection_string_for_db ( db_name) )
294+ }
238295}
239296
240297fn cmd_as_non_root ( command : & mut Command ) {
0 commit comments