From 0b67607383d6b518412addd1ac0bfd8891be1479 Mon Sep 17 00:00:00 2001 From: vseanreesermsft <78103370+vseanreesermsft@users.noreply.github.com> Date: Tue, 2 May 2023 12:29:23 -0700 Subject: [PATCH 1/7] Update branding to 6.0.18 (#30812) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 9abba288326..0c9578b30a6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,6 +1,6 @@ - 6.0.17 + 6.0.18 servicing False true From 21f912b704a0c78da0ffb7697370f1f2a426c831 Mon Sep 17 00:00:00 2001 From: vseanreesermsft <78103370+vseanreesermsft@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:59:12 -0700 Subject: [PATCH 2/7] Update branding to 6.0.19 (#31044) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 0c9578b30a6..f93bc472d63 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,6 +1,6 @@ - 6.0.18 + 6.0.19 servicing False true From baa7e451c83ce6a9fc928a5fd01b45c7da7a1ef8 Mon Sep 17 00:00:00 2001 From: William Godbe Date: Tue, 13 Jun 2023 10:33:57 -0700 Subject: [PATCH 3/7] Update global.json --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 9d6a9528b56..2913ed55701 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "6.0.116", + "dotnet": "6.0.118", "runtimes": { "dotnet": [ "3.1.32", From fd628562089c58932b0ffebe20917ea02793fd5f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 15 Jun 2023 22:03:44 +0000 Subject: [PATCH 4/7] Update dependencies from https://github.com/dotnet/arcade build 20230613.5 (#31091) [release/6.0] Update dependencies from dotnet/arcade --- eng/Version.Details.xml | 8 +++--- eng/common/templates/job/job.yml | 26 +++++++++++++------ .../templates/steps/component-governance.yml | 10 +++++++ global.json | 4 +-- 4 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 eng/common/templates/steps/component-governance.yml diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9217bf65eea..2cb742478b1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -47,13 +47,13 @@ - + https://github.com/dotnet/arcade - 7bca7a24dfc0eded1f3e364b4ff7bf1235b6eb26 + 91616785a1a6578c83f7e93d98c34a1eb83d6223 - + https://github.com/dotnet/arcade - 7bca7a24dfc0eded1f3e364b4ff7bf1235b6eb26 + 91616785a1a6578c83f7e93d98c34a1eb83d6223 diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 547d878da07..0e10e7db69c 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -24,7 +24,7 @@ parameters: enablePublishBuildAssets: false enablePublishTestResults: false enablePublishUsingPipelines: false - disableComponentGovernance: false + disableComponentGovernance: '' mergeTestResults: false testRunTitle: '' testResultsFormat: '' @@ -73,6 +73,10 @@ jobs: - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: - name: EnableRichCodeNavigation value: 'true' + # Retry signature validation up to three times, waiting 2 seconds between attempts. + # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures + - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY + value: 3,2000 - ${{ each variable in parameters.variables }}: # handle name-value variable syntax # example: @@ -81,7 +85,7 @@ jobs: - ${{ if ne(variable.name, '') }}: - name: ${{ variable.name }} value: ${{ variable.value }} - + # handle variable groups - ${{ if ne(variable.group, '') }}: - group: ${{ variable.group }} @@ -141,14 +145,20 @@ jobs: richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin continueOnError: true - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(parameters.disableComponentGovernance, 'true')) }}: - - task: ComponentGovernanceComponentDetection@0 - continueOnError: true + - template: /eng/common/templates/steps/component-governance.yml + parameters: + ${{ if eq(parameters.disableComponentGovernance, '') }}: + ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: + disableComponentGovernance: false + ${{ else }}: + disableComponentGovernance: true + ${{ else }}: + disableComponentGovernance: ${{ parameters.disableComponentGovernance }} - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - task: MicroBuildCleanup@1 - displayName: Execute Microbuild cleanup tasks + displayName: Execute Microbuild cleanup tasks condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) continueOnError: ${{ parameters.continueOnError }} env: @@ -216,7 +226,7 @@ jobs: displayName: Publish XUnit Test Results inputs: testResultsFormat: 'xUnit' - testResultsFiles: '*.xml' + testResultsFiles: '*.xml' searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit mergeTestResults: ${{ parameters.mergeTestResults }} @@ -227,7 +237,7 @@ jobs: displayName: Publish TRX Test Results inputs: testResultsFormat: 'VSTest' - testResultsFiles: '*.trx' + testResultsFiles: '*.trx' searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx mergeTestResults: ${{ parameters.mergeTestResults }} diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml new file mode 100644 index 00000000000..babc2757d8d --- /dev/null +++ b/eng/common/templates/steps/component-governance.yml @@ -0,0 +1,10 @@ +parameters: + disableComponentGovernance: false + +steps: +- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: + - script: "echo ##vso[task.setvariable variable=skipComponentGovernanceDetection]true" + displayName: Set skipComponentGovernanceDetection variable +- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: + - task: ComponentGovernanceComponentDetection@0 + continueOnError: true \ No newline at end of file diff --git a/global.json b/global.json index 2913ed55701..d55d0d8e5a1 100644 --- a/global.json +++ b/global.json @@ -18,7 +18,7 @@ "rollForward": "latestMajor" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.23211.7", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.23211.7" + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.23313.5", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.23313.5" } } From dffed7ee23a8772526cb1db883f4a73a12b6c06c Mon Sep 17 00:00:00 2001 From: vseanreesermsft <78103370+vseanreesermsft@users.noreply.github.com> Date: Mon, 19 Jun 2023 15:35:23 -0700 Subject: [PATCH 5/7] Update branding to 6.0.20 (#31102) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index f93bc472d63..cb21efcea31 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,6 +1,6 @@ - 6.0.19 + 6.0.20 servicing False true From 9e4930341171db4ae67daf033e2791988219dd09 Mon Sep 17 00:00:00 2001 From: vseanreesermsft <78103370+vseanreesermsft@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:59:06 -0700 Subject: [PATCH 6/7] Update branding to 6.0.21 (#31191) --- eng/Versions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index cb21efcea31..bfbc63b0699 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -1,6 +1,6 @@ - 6.0.20 + 6.0.21 servicing False true From 71fe707b0d40544e16ba548458d46487a978981b Mon Sep 17 00:00:00 2001 From: Brice Lambson Date: Thu, 6 Jul 2023 09:54:38 -0700 Subject: [PATCH 7/7] Microsoft.Data.Sqlite: Fix handling of queries with RETURNING clause (#31013) Fixes #30851 --- .../SqliteDataReader.cs | 25 ++-- .../SqliteDataRecord.cs | 54 ++++++++- .../SqliteCommandTest.cs | 114 ++++++++++++++++++ .../SqliteDataReaderTest.cs | 67 ++++++++++ 4 files changed, 245 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs b/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs index 95401f84ee0..f07af146e25 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs @@ -177,7 +177,7 @@ public override bool NextResult() // It's a SELECT statement if (sqlite3_column_count(stmt) != 0) { - _record = new SqliteDataRecord(stmt, rc != SQLITE_DONE, _command.Connection); + _record = new SqliteDataRecord(stmt, rc != SQLITE_DONE, _command.Connection, AddChanges); return true; } @@ -191,14 +191,7 @@ public override bool NextResult() sqlite3_reset(stmt); var changes = sqlite3_changes(_command.Connection.Handle); - if (_recordsAffected == -1) - { - _recordsAffected = changes; - } - else - { - _recordsAffected += changes; - } + AddChanges(changes); } catch { @@ -219,6 +212,18 @@ private static bool IsBusy(int rc) || rc == SQLITE_BUSY || rc == SQLITE_LOCKED_SHAREDCACHE; + private void AddChanges(int changes) + { + if (_recordsAffected == -1) + { + _recordsAffected = changes; + } + else + { + _recordsAffected += changes; + } + } + /// /// Closes the data reader. /// @@ -242,6 +247,7 @@ protected override void Dispose(bool disposing) _command.DataReader = null; _record?.Dispose(); + _record = null; if (_stmtEnumerator != null) { @@ -249,7 +255,6 @@ protected override void Dispose(bool disposing) { while (NextResult()) { - _record!.Dispose(); } } catch diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs b/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs index 936237497e5..6809af64854 100644 --- a/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs +++ b/src/Microsoft.Data.Sqlite.Core/SqliteDataRecord.cs @@ -16,18 +16,22 @@ namespace Microsoft.Data.Sqlite internal class SqliteDataRecord : SqliteValueReader, IDisposable { private readonly SqliteConnection _connection; + private readonly Action _addChanges; private byte[][]? _blobCache; private int?[]? _typeCache; private Dictionary? _columnNameOrdinalCache; private string[]? _columnNameCache; private bool _stepped; private int? _rowidOrdinal; + private bool _alreadyThrown; + private bool _alreadyAddedChanges; - public SqliteDataRecord(sqlite3_stmt stmt, bool hasRows, SqliteConnection connection) + public SqliteDataRecord(sqlite3_stmt stmt, bool hasRows, SqliteConnection connection, Action addChanges) { Handle = stmt; HasRows = hasRows; _connection = connection; + _addChanges = addChanges; } public virtual object this[string name] @@ -397,19 +401,59 @@ public bool Read() return false; } - var rc = sqlite3_step(Handle); - SqliteException.ThrowExceptionForRC(rc, _connection.Handle); + int rc; + try + { + rc = sqlite3_step(Handle); + SqliteException.ThrowExceptionForRC(rc, _connection.Handle); + } + catch + { + _alreadyThrown = true; + + throw; + } if (_blobCache != null) { Array.Clear(_blobCache, 0, _blobCache.Length); } - return rc != SQLITE_DONE; + if (rc != SQLITE_DONE) + { + return true; + } + + AddChanges(); + _alreadyAddedChanges = true; + + return false; } public void Dispose() - => sqlite3_reset(Handle); + { + var rc = sqlite3_reset(Handle); + if (!_alreadyThrown) + { + SqliteException.ThrowExceptionForRC(rc, _connection.Handle); + } + + if (!_alreadyAddedChanges) + { + AddChanges(); + } + } + + private void AddChanges() + { + if (sqlite3_stmt_readonly(Handle) != 0) + { + return; + } + + var changes = sqlite3_changes(_connection.Handle); + _addChanges(changes); + } private byte[] GetCachedBlob(int ordinal) { diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs index 01e6efc76e5..f5ccd66378f 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteCommandTest.cs @@ -931,6 +931,120 @@ public async Task ExecuteReader_retries_when_busy() } } + [Fact] + public Task ExecuteScalar_throws_when_busy_with_returning() + => Execute_throws_when_busy_with_returning(command => + { + var ex = Assert.Throws( + () => command.ExecuteScalar()); + + Assert.Equal(SQLITE_BUSY, ex.SqliteErrorCode); + }); + + [Fact] + public Task ExecuteNonQuery_throws_when_busy_with_returning() + => Execute_throws_when_busy_with_returning(command => + { + var ex = Assert.Throws( + () => command.ExecuteNonQuery()); + + Assert.Equal(SQLITE_BUSY, ex.SqliteErrorCode); + }); + + [Fact] + public Task ExecuteReader_throws_when_busy_with_returning() + => Execute_throws_when_busy_with_returning(command => + { + var reader = command.ExecuteReader(); + try + { + Assert.True(reader.Read()); + Assert.Equal(2L, reader.GetInt64(0)); + } + finally + { + var ex = Assert.Throws( + () => reader.Dispose()); + + Assert.Equal(SQLITE_BUSY, ex.SqliteErrorCode); + } + }); + + [Fact] + public Task ExecuteReader_throws_when_busy_with_returning_while_draining() + => Execute_throws_when_busy_with_returning(command => + { + using var reader = command.ExecuteReader(); + Assert.True(reader.Read()); + Assert.Equal(2L, reader.GetInt64(0)); + Assert.True(reader.Read()); + Assert.Equal(3L, reader.GetInt64(0)); + + var ex = Assert.Throws( + () => reader.Read()); + + Assert.Equal(SQLITE_BUSY, ex.SqliteErrorCode); + }); + + private static async Task Execute_throws_when_busy_with_returning(Action action) + { + const string connectionString = "Data Source=returning.db"; + + var selectedSignal = new AutoResetEvent(initialState: false); + + try + { + using var connection1 = new SqliteConnection(connectionString); + + if (new Version(connection1.ServerVersion) < new Version(3, 35, 0)) + { + // Skip. RETURNING clause not supported + return; + } + + connection1.Open(); + + connection1.ExecuteNonQuery( + "CREATE TABLE Data (Value); INSERT INTO Data VALUES (0);"); + + await Task.WhenAll( + Task.Run( + async () => + { + using var connection = new SqliteConnection(connectionString); + connection.Open(); + + using (connection.ExecuteReader("SELECT * FROM Data;")) + { + selectedSignal.Set(); + + await Task.Delay(1000); + } + }), + Task.Run( + () => + { + using var connection = new SqliteConnection(connectionString); + connection.Open(); + + selectedSignal.WaitOne(); + + var command = connection.CreateCommand(); + command.CommandText = "INSERT INTO Data VALUES (1),(2) RETURNING rowid;"; + + action(command); + })); + + var count = connection1.ExecuteScalar("SELECT COUNT(*) FROM Data;"); + Assert.Equal(1L, count); + } + finally + { + SqliteConnection.ClearPool(new SqliteConnection(connectionString)); + File.Delete("returning.db"); + } + } + [Fact] public void ExecuteReader_honors_CommandTimeout() { diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs index b9855f0e6da..ef108ea3817 100644 --- a/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs +++ b/test/Microsoft.Data.Sqlite.Tests/SqliteDataReaderTest.cs @@ -1851,6 +1851,73 @@ public void RecordsAffected_works_during_enumeration() } } + [Fact] + public void RecordsAffected_works_with_returning() + { + using (var connection = new SqliteConnection("Data Source=:memory:")) + { + if (new Version(connection.ServerVersion) < new Version(3, 35, 0)) + { + // Skip. RETURNING clause not supported + return; + } + + connection.Open(); + connection.ExecuteNonQuery("CREATE TABLE Test(Value);"); + + var reader = connection.ExecuteReader("INSERT INTO Test VALUES(1) RETURNING rowid;"); + ((IDisposable)reader).Dispose(); + + Assert.Equal(1, reader.RecordsAffected); + } + } + + [Fact] + public void RecordsAffected_works_with_returning_before_dispose_after_draining() + { + using (var connection = new SqliteConnection("Data Source=:memory:")) + { + if (new Version(connection.ServerVersion) < new Version(3, 35, 0)) + { + // Skip. RETURNING clause not supported + return; + } + + connection.Open(); + connection.ExecuteNonQuery("CREATE TABLE Test(Value);"); + + using (var reader = connection.ExecuteReader("INSERT INTO Test VALUES(1) RETURNING rowid;")) + { + while (reader.Read()) + { + } + + Assert.Equal(1, reader.RecordsAffected); + } + } + } + + [Fact] + public void RecordsAffected_works_with_returning_multiple() + { + using (var connection = new SqliteConnection("Data Source=:memory:")) + { + if (new Version(connection.ServerVersion) < new Version(3, 35, 0)) + { + // Skip. RETURNING clause not supported + return; + } + + connection.Open(); + connection.ExecuteNonQuery("CREATE TABLE Test(Value);"); + + var reader = connection.ExecuteReader("INSERT INTO Test VALUES(1),(2) RETURNING rowid;"); + ((IDisposable)reader).Dispose(); + + Assert.Equal(2, reader.RecordsAffected); + } + } + [Fact] public void GetSchemaTable_works() {