-
Notifications
You must be signed in to change notification settings - Fork 14
Added a function to allow creation of database with owner #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
c824d96
a686ef3
3375b7e
31d7eac
8a002b2
daea170
a9f9ae3
7e17862
0a58605
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,7 +16,7 @@ | |||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||
| public static class PostgresqlExtensions | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| private static readonly string pattern= @"(?i)Search\s?Path=([^;]+)"; | ||||||||||||||||||||||||||||||||
| private static readonly string pattern = @"(?i)Search\s?Path=([^;]+)"; | ||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||
| /// Creates an upgrader for PostgreSQL databases. | ||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||
|
|
@@ -172,10 +172,11 @@ public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase s | |||||||||||||||||||||||||||||||
| /// <param name="connectionString">The connection string.</param> | ||||||||||||||||||||||||||||||||
| /// <param name="logger">The <see cref="DbUp.Engine.Output.IUpgradeLog"/> used to record actions.</param> | ||||||||||||||||||||||||||||||||
| /// <param name="certificate">Certificate for securing connection.</param> | ||||||||||||||||||||||||||||||||
| /// <returns></returns> | ||||||||||||||||||||||||||||||||
| public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase supported, string connectionString, IUpgradeLog logger, X509Certificate2 certificate) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var options = new PostgresqlConnectionOptions | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| ClientCertificate = certificate | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
| PostgresqlDatabase(supported, connectionString, logger, options); | ||||||||||||||||||||||||||||||||
|
|
@@ -189,11 +190,30 @@ public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase s | |||||||||||||||||||||||||||||||
| /// <param name="logger">The <see cref="DbUp.Engine.Output.IUpgradeLog"/> used to record actions.</param> | ||||||||||||||||||||||||||||||||
| /// <param name="connectionOptions">Connection options to set SSL parameters</param> | ||||||||||||||||||||||||||||||||
| public static void PostgresqlDatabase( | ||||||||||||||||||||||||||||||||
| this SupportedDatabasesForEnsureDatabase supported, | ||||||||||||||||||||||||||||||||
| string connectionString, | ||||||||||||||||||||||||||||||||
| IUpgradeLog logger, | ||||||||||||||||||||||||||||||||
| this SupportedDatabasesForEnsureDatabase supported, | ||||||||||||||||||||||||||||||||
| string connectionString, | ||||||||||||||||||||||||||||||||
| IUpgradeLog logger, | ||||||||||||||||||||||||||||||||
| PostgresqlConnectionOptions connectionOptions | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| PostgresqlDatabase(supported, connectionString, logger, connectionOptions, null); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||
| /// Ensures that the database specified in the connection string exists, assigning an owner at creation time. | ||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||
| /// <param name="supported">Fluent helper type.</param> | ||||||||||||||||||||||||||||||||
| /// <param name="connectionString">The connection string.</param> | ||||||||||||||||||||||||||||||||
| /// <param name="logger">The <see cref="DbUp.Engine.Output.IUpgradeLog"/> used to record actions.</param> | ||||||||||||||||||||||||||||||||
| /// <param name="connectionOptions">Connection SSL to customize SSL behaviour</param> | ||||||||||||||||||||||||||||||||
droyad marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||
| /// <param name="owner">Role to own the new database during creation (adds 'WITH OWNER = "role"').</param> | ||||||||||||||||||||||||||||||||
| public static void PostgresqlDatabase( | ||||||||||||||||||||||||||||||||
| this SupportedDatabasesForEnsureDatabase supported, | ||||||||||||||||||||||||||||||||
| string connectionString, | ||||||||||||||||||||||||||||||||
| IUpgradeLog logger, | ||||||||||||||||||||||||||||||||
| PostgresqlConnectionOptions connectionOptions, | ||||||||||||||||||||||||||||||||
| string owner | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (supported == null) throw new ArgumentNullException("supported"); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -205,14 +225,16 @@ PostgresqlConnectionOptions connectionOptions | |||||||||||||||||||||||||||||||
| if (logger == null) throw new ArgumentNullException("logger"); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var masterConnectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var databaseName = masterConnectionStringBuilder.Database; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (string.IsNullOrEmpty(databaseName) || databaseName.Trim() == string.Empty) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| throw new InvalidOperationException("The connection string does not specify a database name."); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| owner = string.IsNullOrWhiteSpace(owner) ? masterConnectionStringBuilder.Username : owner; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| masterConnectionStringBuilder.Database = connectionOptions.MasterDatabaseName; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| var logMasterConnectionStringBuilder = new NpgsqlConnectionStringBuilder(masterConnectionStringBuilder.ConnectionString); | ||||||||||||||||||||||||||||||||
|
|
@@ -232,9 +254,9 @@ PostgresqlConnectionOptions connectionOptions | |||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // check to see if the database already exists.. | ||||||||||||||||||||||||||||||||
| using (var command = new NpgsqlCommand(sqlCommandText, connection) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| CommandType = CommandType.Text | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| CommandType = CommandType.Text | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| var results = Convert.ToInt32(command.ExecuteScalar()); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -245,13 +267,29 @@ PostgresqlConnectionOptions connectionOptions | |||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| sqlCommandText = $"create database \"{databaseName}\";"; | ||||||||||||||||||||||||||||||||
| sqlCommandText = $"select 1 from pg_roles where rolname = \'{owner}\' limit 1;"; | ||||||||||||||||||||||||||||||||
| // check to see if the owner exists.. | ||||||||||||||||||||||||||||||||
| using (var command = new NpgsqlCommand(sqlCommandText, connection) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| CommandType = CommandType.Text | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| sqlCommandText = $"select 1 from pg_roles where rolname = \'{owner}\' limit 1;"; | |
| // check to see if the owner exists.. | |
| using (var command = new NpgsqlCommand(sqlCommandText, connection) | |
| { | |
| CommandType = CommandType.Text | |
| }) | |
| { | |
| sqlCommandText = "select 1 from pg_roles where rolname = @owner limit 1;"; | |
| // check to see if the owner exists.. | |
| using (var command = new NpgsqlCommand(sqlCommandText, connection) | |
| { | |
| CommandType = CommandType.Text | |
| }) | |
| { | |
| command.Parameters.AddWithValue("@owner", owner); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well... it's unlikely SQLi would happen this way, but I don't think it hurts to do it this way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the SQL command to take in parameters instead.
Outdated
Copilot
AI
Nov 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null reference exception: ExecuteScalar() can return null when no rows are found. The code should handle the null case explicitly before converting to Int32, e.g., var results = command.ExecuteScalar() as int? ?? 0;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds right based on my SQL Server knowledge. If owner does not exist, no rows are returned (since it doesn't use Count(*).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convert.ToInt32 can take in a null object and return a zero for null objects.
However, to keep it cleaner, I updated the SQL to return a boolean instead which makes it more readable.
Outdated
Copilot
AI
Nov 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SQL injection vulnerability: both databaseName and owner are directly interpolated into the CREATE DATABASE statement. While PostgreSQL doesn't support parameterized DDL, consider validating these identifiers (e.g., checking they only contain alphanumeric characters and underscores) or using identifier quoting functions to prevent injection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SQLi via connection string...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed it by formatting using helper functions.
Uh oh!
There was an error while loading. Please reload this page.