Skip to content

Commit 5cb8e38

Browse files
aashikgowdaAashik Nagadikeri Harishdroyad
authored
Add drop database support for PostgreSQL (#35)
* Added support for dropping database in postgresql extensions * Updated exception comment * Fix after merging other PR * Updated Approval --------- Co-authored-by: Aashik Nagadikeri Harish <[email protected]> Co-authored-by: Robert Wagner <[email protected]>
1 parent d4f258e commit 5cb8e38

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

src/Tests/ApprovalFiles/NoPublicApiChanges.Run.approved.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Bui
1313
public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
1414
public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { }
1515
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString) { }
16+
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForDropDatabase supported, string connectionString) { }
1617
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
1718
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { }
1819
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger) { }
20+
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForDropDatabase supported, string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
21+
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForDropDatabase supported, string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { }
22+
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForDropDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger) { }
1923
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
2024
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { }
25+
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForDropDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { }
26+
public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForDropDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { }
2127
}
2228
namespace DbUp.Postgresql
2329
{

src/dbup-postgresql/PostgresqlExtensions.cs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,164 @@ PostgresqlConnectionOptions connectionOptions
245245
logger.LogInformation(@"Created database {0}", databaseName);
246246
}
247247

248+
/// <summary>
249+
/// Drops the database specified in the connection string if it exists.
250+
/// </summary>
251+
/// <param name="supported">Fluent helper type.</param>
252+
/// <param name="connectionString">The connection string.</param>
253+
/// <returns></returns>
254+
public static void PostgresqlDatabase(this SupportedDatabasesForDropDatabase supported, string connectionString)
255+
{
256+
PostgresqlDatabase(supported, connectionString, new ConsoleUpgradeLog());
257+
}
258+
259+
/// <summary>
260+
/// Drops the database specified in the connection string if it exists using SSL for the connection.
261+
/// </summary>
262+
/// <param name="supported">Fluent helper type.</param>
263+
/// <param name="connectionString">The connection string.</param>
264+
/// <param name="certificate">Certificate for securing connection.</param>
265+
/// <returns></returns>
266+
public static void PostgresqlDatabase(this SupportedDatabasesForDropDatabase supported, string connectionString, X509Certificate2 certificate)
267+
{
268+
PostgresqlDatabase(supported, connectionString, new ConsoleUpgradeLog(), certificate);
269+
}
270+
271+
/// <summary>
272+
/// Drops the database specified in the connection string if it exists using SSL for the connection.
273+
/// </summary>
274+
/// <param name="supported">Fluent helper type.</param>
275+
/// <param name="connectionString">The connection string.</param>
276+
/// <param name="connectionOptions">Connection SSL to customize SSL behaviour</param>
277+
/// <returns></returns>
278+
public static void PostgresqlDatabase(this SupportedDatabasesForDropDatabase supported, string connectionString, PostgresqlConnectionOptions connectionOptions)
279+
{
280+
PostgresqlDatabase(supported, connectionString, new ConsoleUpgradeLog(), connectionOptions);
281+
}
282+
283+
/// <summary>
284+
/// Drops that the database specified in the connection string if it exists.
285+
/// </summary>
286+
/// <param name="supported">Fluent helper type.</param>
287+
/// <param name="connectionString">The connection string.</param>
288+
/// <param name="logger">The <see cref="DbUp.Engine.Output.IUpgradeLog"/> used to record actions.</param>
289+
/// <returns></returns>
290+
public static void PostgresqlDatabase(this SupportedDatabasesForDropDatabase supported, string connectionString, IUpgradeLog logger)
291+
{
292+
PostgresqlDatabase(supported, connectionString, logger, new PostgresqlConnectionOptions());
293+
}
294+
295+
public static void PostgresqlDatabase(this SupportedDatabasesForDropDatabase supported, string connectionString, IUpgradeLog logger, X509Certificate2 certificate)
296+
{
297+
var options = new PostgresqlConnectionOptions
298+
{
299+
ClientCertificate = certificate
300+
};
301+
PostgresqlDatabase(supported, connectionString, logger, options);
302+
}
303+
304+
public static void PostgresqlDatabase(
305+
this SupportedDatabasesForDropDatabase supported,
306+
string connectionString,
307+
IUpgradeLog logger,
308+
PostgresqlConnectionOptions connectionOptions
309+
)
310+
{
311+
if (supported == null) throw new ArgumentNullException("supported");
312+
313+
if (string.IsNullOrEmpty(connectionString) || connectionString.Trim() == string.Empty)
314+
{
315+
throw new ArgumentNullException("connectionString");
316+
}
317+
318+
if (logger == null) throw new ArgumentNullException("logger");
319+
320+
var masterConnectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString);
321+
322+
var databaseName = masterConnectionStringBuilder.Database;
323+
324+
if (string.IsNullOrEmpty(databaseName) || databaseName.Trim() == string.Empty)
325+
{
326+
throw new InvalidOperationException("The connection string does not specify a database name.");
327+
}
328+
329+
if (databaseName == connectionOptions.MasterDatabaseName)
330+
{
331+
throw new InvalidOperationException("Database in connection string needs to be different than the master database in PostgresqlConnectionOptions.");
332+
}
333+
334+
masterConnectionStringBuilder.Database = connectionOptions.MasterDatabaseName;
335+
masterConnectionStringBuilder.SearchPath = "public";
336+
337+
var logMasterConnectionStringBuilder = new NpgsqlConnectionStringBuilder(masterConnectionStringBuilder.ConnectionString);
338+
if (!string.IsNullOrEmpty(logMasterConnectionStringBuilder.Password))
339+
{
340+
logMasterConnectionStringBuilder.Password = "******";
341+
}
342+
343+
logger.LogDebug("Master ConnectionString => {0}", logMasterConnectionStringBuilder.ConnectionString);
344+
345+
var factory = new DataSourceConnectionFactory(masterConnectionStringBuilder.ConnectionString, connectionOptions);
346+
using var connection = factory.CreateConnection();
347+
connection.Open();
348+
349+
var sqlCommandText =
350+
$@"SELECT case WHEN oid IS NOT NULL THEN 1 ELSE 0 end FROM pg_database WHERE datname = '{databaseName}' limit 1;";
351+
352+
// check to see if the database already exists..
353+
using (var command = new NpgsqlCommand(sqlCommandText, connection)
354+
{
355+
CommandType = CommandType.Text
356+
})
357+
{
358+
var results = Convert.ToInt32(command.ExecuteScalar());
359+
360+
// if the database does not exist, we're done here...
361+
if (results == 0)
362+
{
363+
logger.LogInformation(@"Database {0} does not exist. Skipping delete operation.", databaseName);
364+
return;
365+
}
366+
}
367+
368+
// prevent new connections to the database
369+
sqlCommandText = $"alter database \"{databaseName}\" with ALLOW_CONNECTIONS false;";
370+
using (var command = new NpgsqlCommand(sqlCommandText, connection)
371+
{
372+
CommandType = CommandType.Text
373+
})
374+
{
375+
command.ExecuteNonQuery();
376+
}
377+
378+
logger.LogInformation(@"Stopped connections for database {0}.", databaseName);
379+
380+
// terminate all existing connections to the database
381+
sqlCommandText = $"select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where pg_stat_activity.datname = \'{databaseName}\';";
382+
using (var command = new NpgsqlCommand(sqlCommandText, connection)
383+
{
384+
CommandType = CommandType.Text
385+
})
386+
{
387+
command.ExecuteNonQuery();
388+
}
389+
390+
logger.LogInformation(@"Closed existing connections for database {0}.", databaseName);
391+
392+
sqlCommandText = $"drop database \"{databaseName}\";";
393+
394+
// drop the database
395+
using (var command = new NpgsqlCommand(sqlCommandText, connection)
396+
{
397+
CommandType = CommandType.Text
398+
})
399+
{
400+
command.ExecuteNonQuery();
401+
}
402+
403+
logger.LogInformation(@"Dropped database {0}.", databaseName);
404+
}
405+
248406
/// <summary>
249407
/// Tracks the list of executed scripts in a PostgreSQL table.
250408
/// </summary>

0 commit comments

Comments
 (0)