Skip to content

Replacing autoprepared statement with an explicitly prepared leaves it in a bad state #5873

@vonzshik

Description

@vonzshik
System.InvalidOperationException : Invalid PreparedState state Unprepared encountered when scanning prepared statement slots
   at Npgsql.ThrowHelper.ThrowInvalidOperationException(String message) in C:\reps\npgsql\src\Npgsql\ThrowHelper.cs:line 33
   at Npgsql.PreparedStatementManager.<TryGetAutoPrepared>g__TryGetAutoPreparedSlow|21_0(NpgsqlBatchCommand batchCommand, PreparedStatement pStatement) in C:\reps\npgsql\src\Npgsql\PreparedStatementManager.cs:line 223
   at Npgsql.PreparedStatementManager.TryGetAutoPrepared(NpgsqlBatchCommand batchCommand) in C:\reps\npgsql\src\Npgsql\PreparedStatementManager.cs:line 105
   at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken) in C:\reps\npgsql\src\Npgsql\NpgsqlCommand.cs:line 1470
   at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken) in C:\reps\npgsql\src\Npgsql\NpgsqlCommand.cs:line 1621
   at Npgsql.NpgsqlCommand.ExecuteNonQuery(Boolean async, CancellationToken cancellationToken) in C:\reps\npgsql\src\Npgsql\NpgsqlCommand.cs:line 1249
   at Npgsql.NpgsqlCommand.ExecuteNonQuery() in C:\reps\npgsql\src\Npgsql\NpgsqlCommand.cs:line 1234
   at Npgsql.Tests.AutoPrepareTests.Promote_auto_to_explicit() in C:\reps\npgsql\test\Npgsql.Tests\AutoPrepareTests.cs:line 174
[Test]
public void Promote_auto_to_explicit()
{
	using var dataSource = CreateDataSource(csb =>
	{
		csb.MaxAutoPrepare = 10;
		csb.AutoPrepareMinUsages = 2;
	});
	using var conn = dataSource.OpenConnection();
	using var checkCmd = new NpgsqlCommand(CountPreparedStatements, conn);
	using var cmd1 = new NpgsqlCommand("SELECT 1", conn);
	using var cmd2 = new NpgsqlCommand("SELECT 1", conn);
	checkCmd.Prepare();

	cmd1.ExecuteNonQuery(); cmd1.ExecuteNonQuery();
	// cmd1 is now autoprepared
	Assert.That(checkCmd.ExecuteScalar(), Is.EqualTo(1));
	Assert.That(conn.Connector!.PreparedStatementManager.NumPrepared, Is.EqualTo(2));

	// Promote (replace) the autoprepared statement with an explicit one.
	cmd2.Prepare();
	Assert.That(checkCmd.ExecuteScalar(), Is.EqualTo(1));
	Assert.That(conn.Connector.PreparedStatementManager.NumPrepared, Is.EqualTo(2));

	// cmd1's statement is no longer valid (has been closed), make sure it still works (will run unprepared)
	cmd2.ExecuteScalar();

	// Trigger autoprepare on a different query to confirm we didn't leave replaced statement in a bad state
	using var cmd3 = new NpgsqlCommand("SELECT 2", conn);
	cmd3.ExecuteNonQuery(); cmd3.ExecuteNonQuery();
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions