Skip to content
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

Fix explicit batch preparation reset #5459

Merged
merged 2 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Npgsql/NpgsqlCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@ Task Prepare(bool async, CancellationToken cancellationToken = default)
ProcessRawQuery(connector.SqlQueryParser, connector.UseConformingStrings, batchCommand);

needToPrepare = batchCommand.ExplicitPrepare(connector) || needToPrepare;
batchCommand.ConnectorPreparedOn = connector;
}

if (logger.IsEnabled(LogLevel.Debug) && needToPrepare)
Expand Down
126 changes: 126 additions & 0 deletions test/Npgsql.Tests/PrepareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Npgsql.BackendMessages;
using Npgsql.Internal.Postgres;
using Npgsql.Tests.Support;
using NpgsqlTypes;
using NUnit.Framework;
using static Npgsql.Tests.TestUtil;
Expand All @@ -13,6 +16,8 @@ namespace Npgsql.Tests;

public class PrepareTests: TestBase
{
static uint Int4Oid => PostgresMinimalDatabaseInfo.DefaultTypeCatalog.GetOid(DataTypeNames.Int4).Value;

[Test]
public void Basic()
{
Expand Down Expand Up @@ -771,6 +776,127 @@ public async Task Explicit_prepare_unprepare_many_queries()
await cmd.UnprepareAsync();
}

[Test]
public async Task Explicitly_prepared_batch_sends_prepared_queries()
{
await using var postmaster = PgPostmasterMock.Start(ConnectionString);
await using var dataSource = CreateDataSource(postmaster.ConnectionString);

await using var conn = await dataSource.OpenConnectionAsync();
var server = await postmaster.WaitForServerConnection();

await using var batch = new NpgsqlBatch(conn)
{
BatchCommands = { new("SELECT 1"), new("SELECT 2") }
};

var prepareTask = batch.PrepareAsync();

await server.ExpectMessages(
FrontendMessageCode.Parse, FrontendMessageCode.Describe,
FrontendMessageCode.Parse, FrontendMessageCode.Describe,
FrontendMessageCode.Sync);

await server
.WriteParseComplete()
.WriteParameterDescription(new FieldDescription(Int4Oid))
.WriteRowDescription(new FieldDescription(Int4Oid))
.WriteParseComplete()
.WriteParameterDescription(new FieldDescription(Int4Oid))
.WriteRowDescription(new FieldDescription(Int4Oid))
.WriteReadyForQuery()
.FlushAsync();

await prepareTask;

for (var i = 0; i < 2; i++)
await ExecutePreparedBatch(batch, server);

async Task ExecutePreparedBatch(NpgsqlBatch batch, PgServerMock server)
{
var executeBatchTask = batch.ExecuteNonQueryAsync();

await server.ExpectMessages(
FrontendMessageCode.Bind, FrontendMessageCode.Execute,
FrontendMessageCode.Bind, FrontendMessageCode.Execute,
FrontendMessageCode.Sync);

await server
.WriteBindComplete()
.WriteCommandComplete()
.WriteBindComplete()
.WriteCommandComplete()
.WriteReadyForQuery()
.FlushAsync();

await executeBatchTask;
}
}

[Test]
public async Task Auto_prepared_batch_sends_prepared_queries()
{
var csb = new NpgsqlConnectionStringBuilder(ConnectionString)
{
AutoPrepareMinUsages = 1,
MaxAutoPrepare = 10
};
await using var postmaster = PgPostmasterMock.Start(csb.ConnectionString);
await using var dataSource = CreateDataSource(postmaster.ConnectionString);

await using var conn = await dataSource.OpenConnectionAsync();
var server = await postmaster.WaitForServerConnection();

await using var batch = new NpgsqlBatch(conn)
{
BatchCommands = { new("SELECT 1"), new("SELECT 2") }
};

var firstBatchExecuteTask = batch.ExecuteNonQueryAsync();

await server.ExpectMessages(
FrontendMessageCode.Parse, FrontendMessageCode.Bind, FrontendMessageCode.Describe, FrontendMessageCode.Execute,
FrontendMessageCode.Parse, FrontendMessageCode.Bind, FrontendMessageCode.Describe, FrontendMessageCode.Execute,
FrontendMessageCode.Sync);

await server
.WriteParseComplete()
.WriteBindComplete()
.WriteRowDescription(new FieldDescription(Int4Oid))
.WriteCommandComplete()
.WriteParseComplete()
.WriteBindComplete()
.WriteRowDescription(new FieldDescription(Int4Oid))
.WriteCommandComplete()
.WriteReadyForQuery()
.FlushAsync();

await firstBatchExecuteTask;

for (var i = 0; i < 2; i++)
await ExecutePreparedBatch(batch, server);

async Task ExecutePreparedBatch(NpgsqlBatch batch, PgServerMock server)
{
var executeBatchTask = batch.ExecuteNonQueryAsync();

await server.ExpectMessages(
FrontendMessageCode.Bind, FrontendMessageCode.Execute,
FrontendMessageCode.Bind, FrontendMessageCode.Execute,
FrontendMessageCode.Sync);

await server
.WriteBindComplete()
.WriteCommandComplete()
.WriteBindComplete()
.WriteCommandComplete()
.WriteReadyForQuery()
.FlushAsync();

await executeBatchTask;
}
}

NpgsqlConnection OpenConnectionAndUnprepare()
{
var conn = OpenConnection();
Expand Down
14 changes: 14 additions & 0 deletions test/Npgsql.Tests/Support/PgServerMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,20 @@ internal PgServerMock WriteRowDescription(params FieldDescription[] fields)
return this;
}

internal PgServerMock WriteParameterDescription(params FieldDescription[] fields)
{
CheckDisposed();

_writeBuffer.WriteByte((byte)BackendMessageCode.ParameterDescription);
_writeBuffer.WriteInt32(1 + 4 + 2 + fields.Length * 4);
_writeBuffer.WriteUInt16((ushort)fields.Length);

foreach (var field in fields)
_writeBuffer.WriteUInt32(field.TypeOID);

return this;
}

internal PgServerMock WriteNoData()
{
CheckDisposed();
Expand Down