Skip to content

Commit

Permalink
Invoke connection interceptor before tweaking connection string in Ex…
Browse files Browse the repository at this point in the history
…ists()

Fixes #2880
  • Loading branch information
roji committed Nov 5, 2024
1 parent 4078905 commit c9f7d96
Showing 1 changed file with 37 additions and 37 deletions.
74 changes: 37 additions & 37 deletions src/EFCore.PG/Storage/Internal/NpgsqlDatabaseCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,13 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class NpgsqlDatabaseCreator : RelationalDatabaseCreator
{
private readonly INpgsqlRelationalConnection _connection;
private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public NpgsqlDatabaseCreator(
public class NpgsqlDatabaseCreator(
RelationalDatabaseCreatorDependencies dependencies,
INpgsqlRelationalConnection connection,
IRawSqlCommandBuilder rawSqlCommandBuilder)
: base(dependencies)
{
_connection = connection;
_rawSqlCommandBuilder = rawSqlCommandBuilder;
}

IRawSqlCommandBuilder rawSqlCommandBuilder,
IRelationalConnectionDiagnosticsLogger connectionLogger)
: RelationalDatabaseCreator(dependencies)
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand All @@ -55,7 +41,7 @@ public NpgsqlDatabaseCreator(
/// </summary>
public override void Create()
{
using (var masterConnection = _connection.CreateAdminConnection())
using (var masterConnection = connection.CreateAdminConnection())
{
try
{
Expand All @@ -82,7 +68,7 @@ public override void Create()
/// </summary>
public override async Task CreateAsync(CancellationToken cancellationToken = default)
{
var masterConnection = _connection.CreateAdminConnection();
var masterConnection = connection.CreateAdminConnection();
await using (masterConnection.ConfigureAwait(false))
{
try
Expand Down Expand Up @@ -112,7 +98,7 @@ await Dependencies.MigrationCommandExecutor
public override bool HasTables()
=> Dependencies.ExecutionStrategy
.Execute(
_connection,
connection,
connection => (bool)CreateHasTablesCommand()
.ExecuteScalar(
new RelationalCommandParameterObject(
Expand All @@ -130,7 +116,7 @@ public override bool HasTables()
/// </summary>
public override Task<bool> HasTablesAsync(CancellationToken cancellationToken = default)
=> Dependencies.ExecutionStrategy.ExecuteAsync(
_connection,
connection,
async (connection, ct) => (bool)(await CreateHasTablesCommand()
.ExecuteScalarAsync(
new RelationalCommandParameterObject(
Expand All @@ -142,7 +128,7 @@ public override Task<bool> HasTablesAsync(CancellationToken cancellationToken =
cancellationToken: ct).ConfigureAwait(false))!, cancellationToken);

private IRelationalCommand CreateHasTablesCommand()
=> _rawSqlCommandBuilder
=> rawSqlCommandBuilder
.Build(
"""
SELECT CASE WHEN COUNT(*) = 0 THEN FALSE ELSE TRUE END
Expand Down Expand Up @@ -173,7 +159,7 @@ private IReadOnlyList<MigrationCommand> CreateCreateOperations()
[
new NpgsqlCreateDatabaseOperation
{
Name = _connection.DbConnection.Database,
Name = connection.DbConnection.Database,
Template = designTimeModel.GetDatabaseTemplate(),
Collation = designTimeModel.GetCollation(),
Tablespace = designTimeModel.GetTablespace()
Expand Down Expand Up @@ -201,15 +187,29 @@ public override Task<bool> ExistsAsync(CancellationToken cancellationToken = def

private async Task<bool> Exists(bool async, CancellationToken cancellationToken = default)
{
var logger = connectionLogger;
var startTime = DateTimeOffset.UtcNow;

var interceptionResult = async
? await logger.ConnectionOpeningAsync(connection, startTime, cancellationToken).ConfigureAwait(false)
: logger.ConnectionOpening(connection, startTime);

if (interceptionResult.IsSuppressed)
{
// If the connection attempt was suppressed by an interceptor, assume that the interceptor took care of all the opening
// details, and the database exists.
return true;
}

// When checking whether a database exists, pooling must be off, otherwise we may
// attempt to reuse a pooled connection, which may be broken (this happened in the tests).
// If Pooling is off, but Multiplexing is on - NpgsqlConnectionStringBuilder.Validate will throw,
// so we turn off Multiplexing as well.
var unpooledCsb = new NpgsqlConnectionStringBuilder(_connection.ConnectionString) { Pooling = false, Multiplexing = false };
var unpooledCsb = new NpgsqlConnectionStringBuilder(connection.ConnectionString) { Pooling = false, Multiplexing = false };

using var _ = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
var unpooledRelationalConnection =
await _connection.CloneWith(unpooledCsb.ToString(), async, cancellationToken).ConfigureAwait(false);
await connection.CloneWith(unpooledCsb.ToString(), async, cancellationToken).ConfigureAwait(false);

try
{
Expand Down Expand Up @@ -273,7 +273,7 @@ private static bool IsDoesNotExist(PostgresException exception)
/// </summary>
public override void Delete()
{
switch (_connection.DataSource)
switch (connection.DataSource)
{
case NpgsqlDataSource dataSource:
dataSource.Clear();
Expand All @@ -283,7 +283,7 @@ public override void Delete()
break;
}

using (var masterConnection = _connection.CreateAdminConnection())
using (var masterConnection = connection.CreateAdminConnection())
{
Dependencies.MigrationCommandExecutor
.ExecuteNonQuery(CreateDropCommands(), masterConnection);
Expand All @@ -298,7 +298,7 @@ public override void Delete()
/// </summary>
public override async Task DeleteAsync(CancellationToken cancellationToken = default)
{
switch (_connection.DataSource)
switch (connection.DataSource)
{
case NpgsqlDataSource dataSource:
// TODO: Do this asynchronously once https://github.com/npgsql/npgsql/issues/4499 is done
Expand All @@ -309,7 +309,7 @@ public override async Task DeleteAsync(CancellationToken cancellationToken = def
break;
}

var masterConnection = _connection.CreateAdminConnection();
var masterConnection = connection.CreateAdminConnection();
await using (masterConnection)
{
await Dependencies.MigrationCommandExecutor
Expand Down Expand Up @@ -339,15 +339,15 @@ public override void CreateTables()

try
{
Dependencies.MigrationCommandExecutor.ExecuteNonQuery(commands, _connection);
Dependencies.MigrationCommandExecutor.ExecuteNonQuery(commands, connection);
}
catch (PostgresException e) when (e is { SqlState: "23505", ConstraintName: "pg_type_typname_nsp_index" })
{
// This occurs when two connections are trying to create the same database concurrently
// (happens in the tests). Simply ignore the error.
}

if (reloadTypes && _connection.DbConnection is NpgsqlConnection npgsqlConnection)
if (reloadTypes && connection.DbConnection is NpgsqlConnection npgsqlConnection)
{
npgsqlConnection.Open();
try
Expand Down Expand Up @@ -375,7 +375,7 @@ public override async Task CreateTablesAsync(CancellationToken cancellationToken

try
{
await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(commands, _connection, cancellationToken)
await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(commands, connection, cancellationToken)
.ConfigureAwait(false);
}
catch (PostgresException e) when (e is { SqlState: "23505", ConstraintName: "pg_type_typname_nsp_index" })
Expand All @@ -389,7 +389,7 @@ await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(commands, _conn
.OfType<AlterDatabaseOperation>()
.Any(o => o.GetPostgresExtensions().Any() || o.GetPostgresEnums().Any() || o.GetPostgresRanges().Any());

if (reloadTypes && _connection.DbConnection is NpgsqlConnection npgsqlConnection)
if (reloadTypes && connection.DbConnection is NpgsqlConnection npgsqlConnection)
{
await npgsqlConnection.OpenAsync(cancellationToken).ConfigureAwait(false);
try
Expand All @@ -409,7 +409,7 @@ private IReadOnlyList<MigrationCommand> CreateDropCommands()
{
// TODO Check DbConnection.Database always gives us what we want
// Issue #775
new NpgsqlDropDatabaseOperation { Name = _connection.DbConnection.Database }
new NpgsqlDropDatabaseOperation { Name = connection.DbConnection.Database }
};

return Dependencies.MigrationsSqlGenerator.Generate(operations);
Expand All @@ -422,5 +422,5 @@ private static void ClearAllPools()
// Clear connection pool for the database connection since after the 'create database' call, a previously
// invalid connection may now be valid.
private void ClearPool()
=> NpgsqlConnection.ClearPool((NpgsqlConnection)_connection.DbConnection);
=> NpgsqlConnection.ClearPool((NpgsqlConnection)connection.DbConnection);
}

0 comments on commit c9f7d96

Please sign in to comment.