From 2131f95e657b3d657ddd6d1349198f7490611a41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 06:38:12 +0000 Subject: [PATCH 1/7] Initial plan From d9b1860eccd935c776fd48d25b27eb94add447d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 06:43:40 +0000 Subject: [PATCH 2/7] Fix INSERT RETURNING clause to return actual values instead of just AffectedRows Co-authored-by: jefim <1387820+jefim@users.noreply.github.com> --- .../ExecuteQueryTests.cs | 76 ++++++++++++++++++- .../ExecuteQuery.cs | 3 +- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs index 89139c0..940cf46 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs @@ -36,7 +36,7 @@ public void TestSetup() { cmd.ExecuteNonQuery(); } - using (var cmd = new NpgsqlCommand(@"INSERT INTO ""lista"" (Id, Selite) VALUES (1, 'Ensimmäinen'), (2, 'foobar'), (3, ''), (4, null)", conn)) + using (var cmd = new NpgsqlCommand(@"INSERT INTO ""lista"" (Id, Selite) VALUES (1, 'Ensimm�inen'), (2, 'foobar'), (3, ''), (4, null)", conn)) { cmd.ExecuteNonQuery(); } @@ -113,4 +113,78 @@ public async Task TestInsertQuery() result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); Assert.AreEqual("Viides", (string)result.QueryResult[0]["selite"]); } + + /// + /// Test INSERT with RETURNING clause. + /// + [Test] + public async Task TestInsertWithReturning() + { + var input = new Input + { + Query = @"INSERT INTO ""lista"" (Id, Selite) VALUES (6, 'Kuudes') RETURNING Id, Selite", + Parameters = null, + ConnectionString = _connection + }; + + var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + + // Should return the inserted values, not AffectedRows + Assert.IsNotNull(result.QueryResult); + Assert.AreEqual(1, result.QueryResult.Count); + Assert.AreEqual(6, (int)result.QueryResult[0]["id"]); + Assert.AreEqual("Kuudes", (string)result.QueryResult[0]["selite"]); + } + + /// + /// Test UPDATE with RETURNING clause. + /// + [Test] + public async Task TestUpdateWithReturning() + { + var input = new Input + { + Query = @"UPDATE ""lista"" SET Selite = 'Updated' WHERE Id = 1 RETURNING Id, Selite", + Parameters = null, + ConnectionString = _connection + }; + + var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + + // Should return the updated values + Assert.IsNotNull(result.QueryResult); + Assert.AreEqual(1, result.QueryResult.Count); + Assert.AreEqual(1, (int)result.QueryResult[0]["id"]); + Assert.AreEqual("Updated", (string)result.QueryResult[0]["selite"]); + + // Restore original value + input.Query = @"UPDATE ""lista"" SET Selite = 'Ensimm�inen' WHERE Id = 1"; + await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + } + + /// + /// Test DELETE with RETURNING clause. + /// + [Test] + public async Task TestDeleteWithReturning() + { + // First, insert a row to delete + var input = new Input + { + Query = @"INSERT INTO ""lista"" (Id, Selite) VALUES (7, 'Seitsem�s')", + Parameters = null, + ConnectionString = _connection + }; + await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + + // Now delete it with RETURNING + input.Query = @"DELETE FROM ""lista"" WHERE Id = 7 RETURNING Id, Selite"; + var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + + // Should return the deleted values + Assert.IsNotNull(result.QueryResult); + Assert.AreEqual(1, result.QueryResult.Count); + Assert.AreEqual(7, (int)result.QueryResult[0]["id"]); + Assert.AreEqual("Seitsem�s", (string)result.QueryResult[0]["selite"]); + } } diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs index d7b537d..ff490f0 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs @@ -57,7 +57,8 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper // Execute command. - if (input.Query.ToLower().Contains("select")) + var queryLower = input.Query.ToLower(); + if (queryLower.Contains("select") || queryLower.Contains("returning")) { var reader = await cmd.ExecuteReaderAsync(cancellationToken); result = new Result(reader.ToJson(cancellationToken)); From bd84334fb9c40585fc1970ea73db7ff4d2d9b214 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 07:04:52 +0000 Subject: [PATCH 3/7] Add ExecuteType parameter for explicit control over query execution Co-authored-by: jefim <1387820+jefim@users.noreply.github.com> --- .../ExecuteQueryTests.cs | 43 +++++++++++++++ .../Definitions/ExecuteTypes.cs | 28 ++++++++++ .../Definitions/Input.cs | 10 ++++ .../ExecuteQuery.cs | 55 ++++++++++++++----- 4 files changed, 122 insertions(+), 14 deletions(-) create mode 100644 Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/ExecuteTypes.cs diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs index 940cf46..b8839ba 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs @@ -187,4 +187,47 @@ public async Task TestDeleteWithReturning() Assert.AreEqual(7, (int)result.QueryResult[0]["id"]); Assert.AreEqual("Seitsem�s", (string)result.QueryResult[0]["selite"]); } + + /// + /// Test INSERT with RETURNING clause using ExecuteType.ExecuteReader explicitly. + /// + [Test] + public async Task TestInsertWithReturningExplicit() + { + var input = new Input + { + Query = @"INSERT INTO ""lista"" (Id, Selite) VALUES (8, 'Kahdeksas') RETURNING Id, Selite", + Parameters = null, + ConnectionString = _connection, + ExecuteType = ExecuteTypes.ExecuteReader + }; + + var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + + // Should return the inserted values + Assert.IsNotNull(result.QueryResult); + Assert.AreEqual(1, result.QueryResult.Count); + Assert.AreEqual(8, (int)result.QueryResult[0]["id"]); + Assert.AreEqual("Kahdeksas", (string)result.QueryResult[0]["selite"]); + } + + /// + /// Test INSERT without RETURNING using ExecuteType.NonQuery explicitly. + /// + [Test] + public async Task TestInsertWithNonQueryExplicit() + { + var input = new Input + { + Query = @"INSERT INTO ""lista"" (Id, Selite) VALUES (9, 'Yhdeks�s')", + Parameters = null, + ConnectionString = _connection, + ExecuteType = ExecuteTypes.NonQuery + }; + + var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); + + // Should return affected rows + Assert.AreEqual(1, (int)result.QueryResult["AffectedRows"]); + } } diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/ExecuteTypes.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/ExecuteTypes.cs new file mode 100644 index 0000000..5261db7 --- /dev/null +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/ExecuteTypes.cs @@ -0,0 +1,28 @@ +namespace Frends.PostgreSQL.ExecuteQuery.Definitions; + +/// +/// Specifies how a command string is interpreted. +/// +public enum ExecuteTypes +{ + /// + /// Auto-detect based on query structure. + /// Uses ExecuteReader for queries that return data (SELECT or queries with RETURNING clause). + /// Uses NonQuery for INSERT, UPDATE, DELETE statements without RETURNING clause. + /// + Auto, + + /// + /// Execute the query without expecting any result set. + /// Use for INSERT, UPDATE, DELETE statements without RETURNING clause. + /// Returns the number of rows affected. + /// + NonQuery, + + /// + /// Execute the query and return the result set. + /// Use for SELECT queries or INSERT/UPDATE/DELETE with RETURNING clause. + /// Returns the data rows. + /// + ExecuteReader +} diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/Input.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/Input.cs index e6ca37d..85b165e 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/Input.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Definitions/Input.cs @@ -32,4 +32,14 @@ public class Input [PasswordPropertyText] public string ConnectionString { get; set; } + /// + /// Specifies how a command string is interpreted. + /// Auto: Automatically detects if the query returns data (SELECT or RETURNING clause) and uses ExecuteReader, otherwise uses NonQuery. + /// ExecuteReader: Use this to execute queries that return a result set (SELECT or INSERT/UPDATE/DELETE with RETURNING clause). + /// NonQuery: Use this to execute commands that don't return a result set (INSERT, UPDATE, DELETE without RETURNING). Returns the number of affected rows. + /// + /// ExecuteTypes.Auto + [DefaultValue(ExecuteTypes.Auto)] + public ExecuteTypes ExecuteType { get; set; } + } diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs index ff490f0..50d32ae 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs @@ -55,24 +55,51 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper } } - // Execute command. + // Execute command based on ExecuteType. + var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); + cmd.Transaction = transaction; - var queryLower = input.Query.ToLower(); - if (queryLower.Contains("select") || queryLower.Contains("returning")) + switch (input.ExecuteType) { - var reader = await cmd.ExecuteReaderAsync(cancellationToken); - result = new Result(reader.ToJson(cancellationToken)); - } - else - { - var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); - cmd.Transaction = transaction; - var rows = await cmd.ExecuteNonQueryAsync(cancellationToken); - await transaction.CommitAsync(cancellationToken); - transaction.Dispose(); - result = new Result(JToken.FromObject(new { AffectedRows = rows })); + case ExecuteTypes.Auto: + // Auto-detect: Use ExecuteReader and check if data is returned + using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) + { + // Check if the query returned any data (has columns) + if (reader.FieldCount > 0) + { + // Query returned data (SELECT or RETURNING clause) + result = new Result(reader.ToJson(cancellationToken)); + } + else + { + // Query did not return data, use RecordsAffected + result = new Result(JToken.FromObject(new { AffectedRows = reader.RecordsAffected })); + } + } + break; + + case ExecuteTypes.ExecuteReader: + // Explicitly return data + using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) + { + result = new Result(reader.ToJson(cancellationToken)); + } + break; + + case ExecuteTypes.NonQuery: + // Execute without returning data + var rows = await cmd.ExecuteNonQueryAsync(cancellationToken); + result = new Result(JToken.FromObject(new { AffectedRows = rows })); + break; + + default: + throw new ArgumentException($"Unsupported ExecuteType: {input.ExecuteType}"); } + await transaction.CommitAsync(cancellationToken); + transaction.Dispose(); + await conn.CloseAsync(); return result; From 9421ca644afd31d8e70bd73357778a9f6930613b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 07:07:36 +0000 Subject: [PATCH 4/7] Fix transaction handling and reader disposal issues Co-authored-by: jefim <1387820+jefim@users.noreply.github.com> --- .../ExecuteQuery.cs | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs index 50d32ae..e2801a0 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs @@ -56,31 +56,36 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper } // Execute command based on ExecuteType. - var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); - cmd.Transaction = transaction; - switch (input.ExecuteType) { case ExecuteTypes.Auto: - // Auto-detect: Use ExecuteReader and check if data is returned - using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) + // Auto-detect: Try ExecuteReader first to check if data is returned { - // Check if the query returned any data (has columns) - if (reader.FieldCount > 0) - { - // Query returned data (SELECT or RETURNING clause) - result = new Result(reader.ToJson(cancellationToken)); - } - else + var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); + cmd.Transaction = transaction; + + using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) { - // Query did not return data, use RecordsAffected - result = new Result(JToken.FromObject(new { AffectedRows = reader.RecordsAffected })); - } + // Check if the query returned any data (has columns) + if (reader.FieldCount > 0) + { + // Query returned data (SELECT or RETURNING clause) + result = new Result(reader.ToJson(cancellationToken)); + } + else + { + // Query did not return data, use RecordsAffected + result = new Result(JToken.FromObject(new { AffectedRows = reader.RecordsAffected })); + } + } // Reader is disposed here + + await transaction.CommitAsync(cancellationToken); + transaction.Dispose(); } break; case ExecuteTypes.ExecuteReader: - // Explicitly return data + // Explicitly return data - no transaction needed for read-only queries using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) { result = new Result(reader.ToJson(cancellationToken)); @@ -88,18 +93,21 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper break; case ExecuteTypes.NonQuery: - // Execute without returning data - var rows = await cmd.ExecuteNonQueryAsync(cancellationToken); - result = new Result(JToken.FromObject(new { AffectedRows = rows })); + // Execute without returning data - use transaction + { + var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); + cmd.Transaction = transaction; + var rows = await cmd.ExecuteNonQueryAsync(cancellationToken); + result = new Result(JToken.FromObject(new { AffectedRows = rows })); + await transaction.CommitAsync(cancellationToken); + transaction.Dispose(); + } break; default: throw new ArgumentException($"Unsupported ExecuteType: {input.ExecuteType}"); } - await transaction.CommitAsync(cancellationToken); - transaction.Dispose(); - await conn.CloseAsync(); return result; From 882992555a4de48383a45e5607aabfaecf301644 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 07:09:30 +0000 Subject: [PATCH 5/7] Improve transaction disposal with using statements Co-authored-by: jefim <1387820+jefim@users.noreply.github.com> --- .../Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs index e2801a0..cac610c 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs @@ -61,7 +61,7 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper case ExecuteTypes.Auto: // Auto-detect: Try ExecuteReader first to check if data is returned { - var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); + using var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); cmd.Transaction = transaction; using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) @@ -80,7 +80,6 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper } // Reader is disposed here await transaction.CommitAsync(cancellationToken); - transaction.Dispose(); } break; @@ -95,12 +94,11 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper case ExecuteTypes.NonQuery: // Execute without returning data - use transaction { - var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); + using var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); cmd.Transaction = transaction; var rows = await cmd.ExecuteNonQueryAsync(cancellationToken); result = new Result(JToken.FromObject(new { AffectedRows = rows })); await transaction.CommitAsync(cancellationToken); - transaction.Dispose(); } break; From 4f62ec1ac57b1c1fef88db12304dfe516d4fd62c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 26 Nov 2025 07:35:41 +0000 Subject: [PATCH 6/7] Fix whitespace formatting issues for superlinter Co-authored-by: jefim <1387820+jefim@users.noreply.github.com> --- .../ExecuteQueryTests.cs | 10 +++++----- .../Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs index b8839ba..8410109 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.Tests/ExecuteQueryTests.cs @@ -128,7 +128,7 @@ public async Task TestInsertWithReturning() }; var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); - + // Should return the inserted values, not AffectedRows Assert.IsNotNull(result.QueryResult); Assert.AreEqual(1, result.QueryResult.Count); @@ -150,7 +150,7 @@ public async Task TestUpdateWithReturning() }; var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); - + // Should return the updated values Assert.IsNotNull(result.QueryResult); Assert.AreEqual(1, result.QueryResult.Count); @@ -180,7 +180,7 @@ public async Task TestDeleteWithReturning() // Now delete it with RETURNING input.Query = @"DELETE FROM ""lista"" WHERE Id = 7 RETURNING Id, Selite"; var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); - + // Should return the deleted values Assert.IsNotNull(result.QueryResult); Assert.AreEqual(1, result.QueryResult.Count); @@ -203,7 +203,7 @@ public async Task TestInsertWithReturningExplicit() }; var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); - + // Should return the inserted values Assert.IsNotNull(result.QueryResult); Assert.AreEqual(1, result.QueryResult.Count); @@ -226,7 +226,7 @@ public async Task TestInsertWithNonQueryExplicit() }; var result = await PostgreSQL.ExecuteQuery(input, _options, new CancellationToken()); - + // Should return affected rows Assert.AreEqual(1, (int)result.QueryResult["AffectedRows"]); } diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs index cac610c..8b81501 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/ExecuteQuery.cs @@ -63,7 +63,7 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper { using var transaction = conn.BeginTransaction(GetIsolationLevel(options.SqlTransactionIsolationLevel)); cmd.Transaction = transaction; - + using (var reader = await cmd.ExecuteReaderAsync(cancellationToken)) { // Check if the query returned any data (has columns) @@ -78,7 +78,7 @@ public static async Task ExecuteQuery([PropertyTab] Input input, [Proper result = new Result(JToken.FromObject(new { AffectedRows = reader.RecordsAffected })); } } // Reader is disposed here - + await transaction.CommitAsync(cancellationToken); } break; From 0568b7bf41f6eb0cabb669d3722d224765262d9c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Dec 2025 07:12:43 +0000 Subject: [PATCH 7/7] Bump version to 2.0.0 and update CHANGELOG.md Co-authored-by: jefim <1387820+jefim@users.noreply.github.com> --- Frends.PostgreSQL.ExecuteQuery/CHANGELOG.md | 14 ++++++++++++++ .../Frends.PostgreSQL.ExecuteQuery.csproj | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Frends.PostgreSQL.ExecuteQuery/CHANGELOG.md b/Frends.PostgreSQL.ExecuteQuery/CHANGELOG.md index 552dff1..589618e 100644 --- a/Frends.PostgreSQL.ExecuteQuery/CHANGELOG.md +++ b/Frends.PostgreSQL.ExecuteQuery/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [2.0.0] - 2025-12-02 +### Added +- Added `ExecuteType` parameter to Input class with options: Auto (default), ExecuteReader, and NonQuery. +- Added support for INSERT/UPDATE/DELETE statements with RETURNING clause to return actual column values instead of just AffectedRows. +- Added `ExecuteTypes` enum to provide explicit control over query execution behavior. + +### Changed +- Modified query execution logic to use ExecuteType parameter instead of simple string parsing. +- Auto mode now checks reader.FieldCount to determine if data is returned, providing more reliable detection than keyword matching. +- Transaction handling now only applies to write operations (Auto and NonQuery modes), not read-only queries (ExecuteReader mode). + +### Fixed +- Fixed issue where INSERT/UPDATE/DELETE with RETURNING clause only returned AffectedRows instead of the actual returned column values. + ## [1.1.0] - 2024-08-23 ### Changed - Updated the Newtonsoft.Json package to version 13.0.3 and the Npgsql package to version 8.0.3. diff --git a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.csproj b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.csproj index 2af857f..2ab6208 100644 --- a/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.csproj +++ b/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery/Frends.PostgreSQL.ExecuteQuery.csproj @@ -7,7 +7,7 @@ true Frends.PostgreSQL.ExecuteQuery Frends.PostgreSQL.ExecuteQuery - 1.1.0 + 2.0.0 Frends Frends Frends