Bug #39815 | Using MySqlCommandBuilder with Batch Inserts causes | ||
---|---|---|---|
Submitted: | 2 Oct 2008 15:18 | Modified: | 22 Feb 2010 16:16 |
Reporter: | John Bayly (OCA) | Email Updates: | |
Status: | Unsupported | Impact on me: | |
Category: | Connector / NET | Severity: | S3 (Non-critical) |
Version: | 5.2.3.0 | OS: | Windows (XP Pro sp3 .Net v.2) |
Assigned to: | Vladislav Vaintroub | CPU Architecture: | Any |
[2 Oct 2008 15:18]
John Bayly
[2 Oct 2008 18:11]
Tonci Grgin
Hi John and thanks for your report. This might well be duplicate of Bug#38411. What do you think?
[3 Oct 2008 10:34]
John Bayly
Tonci, Comparing the stacktraces, it does seem to be a similar bug, however as the code in Bug#38411 is auto generated by VS2008 and I can't even start to compare the sequence of events that are being called. I added the MySql connector source to my solution so that I could step through the code when Update is called. As I suggested in the submission, it appears that the UpdatedRowSource is being reset, so I added added a breakpoint at: set_UpdatedRowSource in command.cs (line 836) and saw from the stacktrace that it's being reset by: RowUpdating event handler in CommandBuilder.cs (line 270) I can honestly say that I don't have an idea what UpdatedRowSource does (I'm looking through the code at the moment), however on a whim I commented out the line: args.Command.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord; and ran the batch insert with Wireshark running and could confirm that two insert commands were issued. This explains why disposing the MySqlCommandBuilder prevents this issue from occuring as the RowUpdating event is handled. Commenting out the line isn't a solution as it's clearly needed, as I found out when I tried running the code on a table with the Primary Key being an Auto Increment value; it only inserted the first value. Again, using Wireshark, I checked what command is being issued and this is it: INSERT INTO `tipstrade_2`.`tblXYZ` (`fldValue`) VALUES ('Index 000') ; SELECT last_insert_id() AS `fldID` , ('Index 001'), ('Index 002'), ('Index 003'), ('Index 004') # New lines added for readability So basically, the line: args.Command.CommandText += finalSelect; should only be issued on the last command of the batch. Of course, I'm still not sure why the UpdateRowSource is being explicitly set to UpdateRowSource.FirstReturnedRecord by the MySqlCommandBuilder
[13 Nov 2008 14:31]
John Bayly
Looking into this again, I've realised that batch inserts shouldn't be done when adding to a table with an Auto Increment field. However if there is no Auto Increment field, the CommandBuilder shouldn't be reseting the UpdatedRowSource property. Hence, I've submitted a diff for the MySqlCommandBuilder that does the following: 1). Create the Final Select statement before testing the UpdatedRowSource property 2). Test the Final Select statement, and if it's empty leave the RowUpdating handler 3). Modified the CreateFinalSelect method so that it can deal with a closed Connection as per bug #34657 4). Remove the StringBuilder, as it appeared to be a slight overkill as it was appended to only once As far as I can tell it shouldn't interfere with the existing functionality. @@ -275,16 +275,21 @@ if (ReturnGeneratedIdentifiers) { - if (args.Command.UpdatedRowSource != UpdateRowSource.None) - throw new InvalidOperationException( - Resources.MixingUpdatedRowSource); - args.Command.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord; - if (finalSelect == null) - CreateFinalSelect(); + // Get the Final Select if it's unset + if (finalSelect == null) CreateFinalSelect(); + + // Ignore if there is no FinalSelect set + // ie. There is no AutoIncrement column + if (finalSelect == "") return; + + if (args.Command.UpdatedRowSource != UpdateRowSource.None) + throw new InvalidOperationException( + Resources.MixingUpdatedRowSource); + args.Command.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord; } - if (finalSelect != null && finalSelect.Length > 0) - args.Command.CommandText += finalSelect; + if (!string.IsNullOrEmpty(finalSelect)) + args.Command.CommandText += finalSelect; } /// <summary> @@ -293,21 +298,43 @@ /// </summary> private void CreateFinalSelect() { - StringBuilder select = new StringBuilder(); - - DataTable dt = GetSchemaTable(DataAdapter.SelectCommand); - - foreach (DataRow row in dt.Rows) - { - if (!(bool)row["IsAutoIncrement"]) - continue; + // Check if the Insert command is being executed on a table + // with an AutoIncrement field + bool connOpened = false; + string autoIncrementField = null; + try { + // Ensure the Select command's connection is open + // As per bug #34657 + // http://bugs.mysql.com/bug.php?id=34657 + if (DataAdapter.SelectCommand.Connection.State != ConnectionState.Open) { + DataAdapter.SelectCommand.Connection.Open(); + connOpened = true; + } - select.AppendFormat(CultureInfo.InvariantCulture, - "; SELECT last_insert_id() AS `{0}`", row["ColumnName"]); + // Get the Select command's schema, and check each field for + // and AutoIncrement flag + DataTable schema = GetSchemaTable(DataAdapter.SelectCommand); + foreach (DataRow field in schema.Rows) { + if ((bool)field["IsAutoIncrement"]) { + autoIncrementField = (string)field["ColumnName"]; break; + } + } + schema.Dispose(); + } finally { + // Close the Select command's connection, so it's reset + // to it's original state + if (connOpened) { + DataAdapter.SelectCommand.Connection.Close(); } + } - finalSelect = select.ToString(); + if (autoIncrementField == null) { + finalSelect = ""; + }else{ + finalSelect = string.Format(CultureInfo.InvariantCulture, + "; SELECT last_insert_id() AS `{0}`", autoIncrementField); + } } } }
[19 Feb 2009 16:48]
Tonci Grgin
John, verified as described along with nice workaround (builder.Dispose()). Thanks for your interest in MySQL.
[19 Feb 2009 16:50]
Tonci Grgin
Test case is provided. I used bare minimum connection string and 5.2 branch from svn.
[22 Feb 2010 16:16]
Reggie Burnett
This behavior was changed several versions back and we are no longer supporting 5.x versions. We are currently on 6.2. Please download and try one of our 6.x products.
[1 Mar 2012 13:09]
Sylvain Petreolle
This issue still affects me using the 6.2.5 version of Connector/Net.
[2 Mar 2012 9:31]
Sylvain Petreolle
Please disregard my last comment.