Skip to content

Commit 1ac0d45

Browse files
committed
Merge master into net7.
2 parents ad4b79c + 000f586 commit 1ac0d45

File tree

5 files changed

+95
-13
lines changed

5 files changed

+95
-13
lines changed

docs/content/overview/version-history.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
lastmod: 2022-03-06
2+
lastmod: 2022-06-04
33
date: 2017-03-27
44
menu:
55
main:
@@ -10,6 +10,20 @@ weight: 30
1010

1111
# Version History
1212

13+
### 2.1.10
14+
15+
* Add `MySqlBulkCopy.ConflictOption` setting: [#1176](https://github.com/mysql-net/MySqlConnector/pull/1176).
16+
* Thanks to [Mykola Klymyuk](https://github.com/klym1) for contributions to this release.
17+
18+
### 2.1.9
19+
20+
* Set `Activity` status to ERROR if an exception occurs during `ExecuteReader`: [#1171](https://github.com/mysql-net/MySqlConnector/issues/1171).
21+
* `SSL Mode = Disabled` is supported as a synonym for `SSL Mode = None`: [#1168](https://github.com/mysql-net/MySqlConnector/issues/1168).
22+
* Added `MySqlSslMode.Disabled` enum value.
23+
* `MySqlAttribute` implements `ICloneable`: [#1169](https://github.com/mysql-net/MySqlConnector/issues/1169).
24+
* (Internal) Unix domain socket connection uses `UnixDomainSocketEndPoint` class: [#1160](https://github.com/mysql-net/MySqlConnector/issues/1160).
25+
* Thanks to [qq362220083](https://github.com/qq362220083) for contributions to this release.
26+
1327
### 2.1.8
1428

1529
* Fix bug that reset `MySqlCommand.LastInsertedId` to `-1` between commands: [#1147](https://github.com/mysql-net/MySqlConnector/issues/1147).

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
<ItemGroup>
3737
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
38-
<PackageReference Include="MinVer" Version="3.1.0">
38+
<PackageReference Include="MinVer" Version="4.0.0">
3939
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4040
<PrivateAssets>all</PrivateAssets>
4141
</PackageReference>

src/MySqlConnector/MySqlBulkCopy.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction =
5656
ColumnMappings = new();
5757
}
5858

59+
/// <summary>
60+
/// A <see cref="MySqlBulkLoaderConflictOption"/> value that specifies how conflicts are resolved (default <see cref="MySqlBulkLoaderConflictOption.None"/>).
61+
/// </summary>
62+
public MySqlBulkLoaderConflictOption ConflictOption { get; set; }
63+
5964
/// <summary>
6065
/// The number of seconds for the operation to complete before it times out, or <c>0</c> for no timeout.
6166
/// </summary>
@@ -247,6 +252,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
247252
Source = this,
248253
TableName = tableName,
249254
Timeout = BulkCopyTimeout,
255+
ConflictOption = ConflictOption,
250256
};
251257

252258
var closeConnection = false;
@@ -338,10 +344,10 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
338344

339345
Log.Debug("Finished bulk copy to {0}", tableName);
340346

341-
if (!m_wasAborted && rowsInserted != m_rowsCopied)
347+
if (!m_wasAborted && rowsInserted != m_rowsCopied && ConflictOption is MySqlBulkLoaderConflictOption.None)
342348
{
343349
Log.Error("Bulk copy to DestinationTableName={0} failed; RowsCopied={1}; RowsInserted={2}", tableName, m_rowsCopied, rowsInserted);
344-
throw new MySqlException(MySqlErrorCode.BulkCopyFailed, "{0} rows were copied to {1} but only {2} were inserted.".FormatInvariant(m_rowsCopied, tableName, rowsInserted));
350+
throw new MySqlException(MySqlErrorCode.BulkCopyFailed, "{0} rows {1} copied to {2} but only {3} {4} inserted.".FormatInvariant(m_rowsCopied, m_rowsCopied == 1 ? "was" : "were", tableName, rowsInserted, rowsInserted == 1 ? "was" : "were"));
345351
}
346352

347353
return new(errors, rowsInserted);

src/MySqlConnector/MySqlConnection.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -746,13 +746,13 @@ internal ServerSession Session
746746

747747
internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
748748
{
749-
if (m_session is null || State != ConnectionState.Open || !m_session.TryStartCancel(command))
749+
if (m_session?.Id is not string sessionId || State != ConnectionState.Open || m_session?.TryStartCancel(command) is not true)
750750
{
751751
Log.Trace("Ignoring cancellation for closed connection or invalid CommandId {0}", commandId);
752752
return;
753753
}
754754

755-
Log.Debug("CommandId {0} for Session{1} has been canceled via {2}.", commandId, m_session.Id, isCancel ? "Cancel()" : "command timeout");
755+
Log.Debug("CommandId {0} for Session{1} has been canceled via {2}.", commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
756756

757757
try
758758
{
@@ -762,29 +762,29 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
762762
AutoEnlist = false,
763763
Pooling = false,
764764
};
765-
if (m_session.IPAddress is not null)
766-
csb.Server = m_session.IPAddress.ToString();
765+
if (m_session?.IPAddress is { } ipAddress)
766+
csb.Server = ipAddress.ToString();
767767
var cancellationTimeout = GetConnectionSettings().CancellationTimeout;
768768
csb.ConnectionTimeout = cancellationTimeout < 1 ? 3u : (uint) cancellationTimeout;
769769

770770
using var connection = new MySqlConnection(csb.ConnectionString);
771771
connection.Open();
772772
using var killCommand = new MySqlCommand("KILL QUERY {0}".FormatInvariant(command.Connection!.ServerThread), connection);
773773
killCommand.CommandTimeout = cancellationTimeout < 1 ? 3 : cancellationTimeout;
774-
m_session.DoCancel(command, killCommand);
774+
m_session?.DoCancel(command, killCommand);
775775
}
776776
catch (InvalidOperationException ex)
777777
{
778778
// ignore a rare race condition where the connection is open at the beginning of the method, but closed by the time
779779
// KILL QUERY is executed: https://github.com/mysql-net/MySqlConnector/issues/1002
780-
Log.Info(ex, "Session{0} ignoring cancellation for closed connection.", m_session!.Id);
781-
m_session.AbortCancel(command);
780+
Log.Info(ex, "Session{0} ignoring cancellation for closed connection.", sessionId);
781+
m_session?.AbortCancel(command);
782782
}
783783
catch (MySqlException ex)
784784
{
785785
// cancelling the query failed; setting the state back to 'Querying' will allow another call to 'Cancel' to try again
786-
Log.Info(ex, "Session{0} cancelling CommandId {1} failed", m_session!.Id, command.CommandId);
787-
m_session.AbortCancel(command);
786+
Log.Info(ex, "Session{0} cancelling CommandId {1} failed", sessionId, command.CommandId);
787+
m_session?.AbortCancel(command);
788788
}
789789
}
790790

tests/SideBySide/BulkLoaderSync.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,68 @@ public void BulkCopyNullDataReader()
10941094
var bulkCopy = new MySqlBulkCopy(connection);
10951095
Assert.Throws<ArgumentNullException>(() => bulkCopy.WriteToServer(default(DbDataReader)));
10961096
}
1097+
1098+
[Theory]
1099+
[InlineData(MySqlBulkLoaderConflictOption.None, 1, "one")]
1100+
[InlineData(MySqlBulkLoaderConflictOption.Ignore, 1, "one")]
1101+
[InlineData(MySqlBulkLoaderConflictOption.Replace, 3, "two")]
1102+
public void BulkCopyDataTableConflictOption(MySqlBulkLoaderConflictOption conflictOption, int expectedRowsInserted, string expected)
1103+
{
1104+
var dataTable = new DataTable()
1105+
{
1106+
Columns =
1107+
{
1108+
new DataColumn("id", typeof(int)),
1109+
new DataColumn("data", typeof(string)),
1110+
},
1111+
Rows =
1112+
{
1113+
new object[] { 1, "one" },
1114+
new object[] { 1, "two" },
1115+
},
1116+
};
1117+
1118+
using var connection = new MySqlConnection(GetLocalConnectionString());
1119+
connection.Open();
1120+
using (var cmd = new MySqlCommand(@"drop table if exists bulk_load_data_table;
1121+
create table bulk_load_data_table(a int not null primary key auto_increment, b text);", connection))
1122+
{
1123+
cmd.ExecuteNonQuery();
1124+
}
1125+
1126+
var bulkCopy = new MySqlBulkCopy(connection)
1127+
{
1128+
ConflictOption = conflictOption,
1129+
DestinationTableName = "bulk_load_data_table",
1130+
};
1131+
1132+
switch (conflictOption)
1133+
{
1134+
case MySqlBulkLoaderConflictOption.None:
1135+
var exception = Assert.Throws<MySqlException>(() => bulkCopy.WriteToServer(dataTable));
1136+
Assert.Equal(MySqlErrorCode.BulkCopyFailed, exception.ErrorCode);
1137+
break;
1138+
1139+
case MySqlBulkLoaderConflictOption.Replace:
1140+
var replaceResult = bulkCopy.WriteToServer(dataTable);
1141+
Assert.Equal(expectedRowsInserted, replaceResult.RowsInserted);
1142+
Assert.Empty(replaceResult.Warnings);
1143+
break;
1144+
1145+
case MySqlBulkLoaderConflictOption.Ignore:
1146+
var ignoreResult = bulkCopy.WriteToServer(dataTable);
1147+
Assert.Equal(expectedRowsInserted, ignoreResult.RowsInserted);
1148+
if (!connection.ServerVersion.StartsWith("5.6.", StringComparison.Ordinal))
1149+
{
1150+
var error = Assert.Single(ignoreResult.Warnings);
1151+
Assert.Equal(MySqlErrorCode.DuplicateKeyEntry, error.ErrorCode);
1152+
}
1153+
break;
1154+
}
1155+
1156+
using (var cmd = new MySqlCommand("select b from bulk_load_data_table;", connection))
1157+
Assert.Equal(expected, cmd.ExecuteScalar());
1158+
}
10971159
#endif
10981160

10991161
internal static string GetConnectionString() => AppConfig.ConnectionString;

0 commit comments

Comments
 (0)