List:Commits« Previous MessageNext Message »
From:Reggie Burnett Date:April 20 2011 6:36pm
Subject:bzr commit into connector-net-6.0 branch (reggie.burnett:866) Bug#59616
Bug#11850286
View as plain text  
#At file:///C:/Users/Reggie/work/connector-net/EFWork/ based on revid:reggie.burnett@stripped

  866 Reggie Burnett	2011-04-20
      - added batching support for updates and deletes (bug #59616, Oracle bug #11850286)
      - small but important sql generation improvements in EF

    modified:
      CHANGES
      MySql.Data.Entity/Provider/Fragments/SqlFragment.cs
      MySql.Data.Entity/Provider/Generators/SelectGenerator.cs
      MySql.Data.Entity/Provider/Generators/SqlGenerator.cs
      MySql.Data.Entity/Provider/Statements/SelectStatement.cs
      MySql.Data.Entity/Tests/JoinTests.cs
      MySql.Data.Entity/Tests/Properties/SQLSyntax.Designer.cs
      MySql.Data.Entity/Tests/Properties/SQLSyntax.resx
      MySql.Data/Provider/Source/command.cs
      MySql.Data/Tests/Source/CommandTests.cs
=== modified file 'CHANGES'
=== modified file 'CHANGES'
--- a/CHANGES	2011-03-03 17:54:43 +0000
+++ b/CHANGES	2011-04-20 18:36:21 +0000
@@ -29,6 +29,7 @@
 - fixed calculation of precision and scale for decimal columns
   (MySQL bug #59989, Oracle bug #11776346)
 - small but important improvements in EF sql generation
+- added batching support for updates and deletes (bug #59616)
 
 Version 6.0.7
 - Fix authorization popup after modifying stored procedure in VS (Bug #44715)

=== modified file 'MySql.Data.Entity/Provider/Fragments/SqlFragment.cs'
--- a/MySql.Data.Entity/Provider/Fragments/SqlFragment.cs	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Provider/Fragments/SqlFragment.cs	2011-04-20 18:36:21 +0000
@@ -134,6 +134,7 @@
         public string TableName { get; set; }
         public string ColumnName { get; set; }
         public string ColumnAlias { get; set; }
+        public PropertyFragment PropertyFragment { get; set; }
 
         public override void WriteSql(StringBuilder sql)
         {
@@ -152,6 +153,14 @@
             if (ColumnAlias != null && ColumnAlias != ColumnName)
                 sql.AppendFormat(" AS {0}", QuoteIdentifier(ColumnAlias));
         }
+
+        public ColumnFragment Clone()
+        {
+            ColumnFragment cf = new ColumnFragment(TableName, ColumnName);
+            cf.ColumnAlias = ColumnAlias;
+            cf.Literal = Literal;
+            return cf;
+        }
     }
 
     internal class ExistsFragment : NegatableFragment

=== modified file 'MySql.Data.Entity/Provider/Generators/SelectGenerator.cs'
--- a/MySql.Data.Entity/Provider/Generators/SelectGenerator.cs	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Provider/Generators/SelectGenerator.cs	2011-04-20 18:36:21 +0000
@@ -151,9 +151,17 @@
             join.JoinType = Metadata.GetOperator(joinType);
 
             join.Left = VisitInputExpression(left.Expression, left.VariableName, left.VariableType);
-            WrapJoinInputIfNecessary(join.Left, false);
+            join.Left = WrapJoinInputIfNecessary(join.Left, false);
+
             join.Right = VisitInputExpression(right.Expression, right.VariableName, right.VariableType);
-            WrapJoinInputIfNecessary(join.Right, true);
+            join.Right = WrapJoinInputIfNecessary(join.Right, true);
+
+            if (join.Right is SelectStatement)
+            {
+                SelectStatement select = join.Right as SelectStatement;
+                if (select.IsWrapped)
+                    select.Name = right.VariableName;
+            }
 
             // now handle the ON case
             if (joinCondition != null)
@@ -171,7 +179,7 @@
             return newSelect;
         }
 
-        private void WrapJoinInputIfNecessary(InputFragment fragment, bool isRightPart)
+        private InputFragment WrapJoinInputIfNecessary(InputFragment fragment, bool isRightPart)
         {
             if (fragment is SelectStatement || fragment is UnionFragment)
             {
@@ -179,7 +187,14 @@
                 fragment.Scoped = true;
             }
             else if (fragment is JoinFragment && isRightPart)
-                fragment.Wrap(null);
+            {
+                SelectStatement select = new SelectStatement();
+                select.From = fragment;
+                select.Name = fragment.Name;
+                select.Wrap(scope);
+                return select;
+            }
+            return fragment;
         }
 
         public override SqlFragment Visit(DbNewInstanceExpression expression)

=== modified file 'MySql.Data.Entity/Provider/Generators/SqlGenerator.cs'
--- a/MySql.Data.Entity/Provider/Generators/SqlGenerator.cs	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Provider/Generators/SqlGenerator.cs	2011-04-20 18:36:21 +0000
@@ -80,6 +80,7 @@
             if (propertyLevel > 0) return fragment;
 
             ColumnFragment column = new ColumnFragment(null, fragment.LastProperty);
+            column.PropertyFragment = fragment;
             column.TableName = FindInputFromProperties(fragment);
 
             return column;
@@ -434,6 +435,7 @@
             Debug.Assert(f is InputFragment);
 
             InputFragment inputFragment = f as InputFragment;
+            inputFragment.Name = name;
 
             if (inputFragment is TableFragment && type != null)
                 (inputFragment as TableFragment).Type = type;

=== modified file 'MySql.Data.Entity/Provider/Statements/SelectStatement.cs'
--- a/MySql.Data.Entity/Provider/Statements/SelectStatement.cs	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Provider/Statements/SelectStatement.cs	2011-04-20 18:36:21 +0000
@@ -129,20 +129,38 @@
 
         void AddDefaultColumns()
         {
+//            List<PropertyFragment> properties = GetColumnPropertiesFromInput(From);
             AddDefaultColumnsForFragment(From);
         }
 
+        //private List<PropertyFragment> GetColumnPropertiesFromInput(InputFragment input)
+        //{
+        //    if (input is TableFragment)
+        //}
+
         void AddDefaultColumnsForFragment(InputFragment input)
         {
             if (input is TableFragment)
             {
                 AddDefaultColumnsForTable(input as TableFragment);
             }
-            else if (input is JoinFragment)
-            {
-                JoinFragment j = input as JoinFragment;
-                AddDefaultColumnsForFragment(j.Left);
-                AddDefaultColumnsForFragment(j.Right);
+            else if (input is JoinFragment || input is UnionFragment)
+            {
+                if (input.Left != null)
+                    AddDefaultColumnsForFragment(input.Left);
+                if (input.Right != null)
+                AddDefaultColumnsForFragment(input.Right);
+
+                // if this input is scoped, then it is the base tablename for the columns
+                if (input.Scoped)
+                    foreach (ColumnFragment col in Columns)
+                        col.TableName = input.Name;
+            }
+            else if (input is SelectStatement)
+            {
+                SelectStatement select = input as SelectStatement;
+                foreach (ColumnFragment cf in select.Columns)
+                    Columns.Add(cf.Clone());
             }
             else
                 throw new NotImplementedException();

=== modified file 'MySql.Data.Entity/Tests/JoinTests.cs'
--- a/MySql.Data.Entity/Tests/JoinTests.cs	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Tests/JoinTests.cs	2011-04-20 18:36:21 +0000
@@ -97,5 +97,41 @@
                 Assert.AreEqual(dt.Rows.Count, i);
             }
         }
+
+        [Test]
+        public void JoinOnRightSideAsDerivedTable()
+        {
+            using (testEntities context = new testEntities())
+            {
+                var q = from child in context.Children
+                        join emp in context.Employees
+                        on child.EmployeeID equals emp.Id
+                        where child.BirthWeight > 7
+                        select child;
+                string sql = q.ToTraceString();
+                CheckSql(sql, SQLSyntax.JoinOnRightSideAsDerivedTable);
+
+                foreach (Child c in q)
+                {
+                }
+            }
+        }
+
+        [Test]
+        public void JoinOnRightSideNameClash()
+        {
+
+            using (testEntities context = new testEntities())
+            {
+                var inner = from a in context.Authors join s in context.Stores on a.Id equals s.Id select a;
+                var outer = from o in context.Orders join i in inner on o.Id equals i.Id select o;
+                string sql = outer.ToTraceString();
+                CheckSql(sql, SQLSyntax.JoinOnRightSideNameClash);
+                foreach (Order o in outer)
+                {
+                    double d = o.Freight;
+                }
+            }
+        }
     }
 }
\ No newline at end of file

=== modified file 'MySql.Data.Entity/Tests/Properties/SQLSyntax.Designer.cs'
--- a/MySql.Data.Entity/Tests/Properties/SQLSyntax.Designer.cs	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Tests/Properties/SQLSyntax.Designer.cs	2011-04-20 18:36:21 +0000
@@ -195,6 +195,54 @@
         /// <summary>
         ///   Looks up a localized string similar to SELECT
         ///Extent1.Id, 
+        ///Extent1.EmployeeID, 
+        ///Extent1.LastName, 
+        ///Extent1.FirstName, 
+        ///Extent1.BirthTime, 
+        ///Extent1.Weight, 
+        ///Extent1.LastModified
+        ///FROM EmployeeChildren AS Extent1 INNER JOIN (SELECT
+        ///Extent2.Id, 
+        ///Extent2.LastName, 
+        ///Extent2.FirstName, 
+        ///Extent2.Age, 
+        ///Extent3.EmployeeId, 
+        ///Extent3.Salary
+        ///FROM Employees AS Extent2 LEFT OUTER JOIN SalariedEmployees AS Extent3 ON Extent2.Id = Extent3.EmployeeId) AS Join1 ON (Extent1.EmployeeID = Join1.Id) OR ((Extent1.EmployeeID IS  NULL) AND (Join1.Id IS  NU [rest of string was truncated]&quot;;.
+        /// </summary>
+        internal static string JoinOnRightSideAsDerivedTable {
+            get {
+                return ResourceManager.GetString("JoinOnRightSideAsDerivedTable", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to SELECT
+        ///1 AS C1, 
+        ///Extent1.Id, 
+        ///Extent1.Freight, 
+        ///Extent1.StoreId
+        ///FROM Orders AS Extent1 INNER JOIN (SELECT
+        ///Extent2.Id, 
+        ///Extent2.Name, 
+        ///Extent2.Age, 
+        ///Extent3.Id AS Id1, 
+        ///Extent3.Name AS Name1, 
+        ///Extent3.Address, 
+        ///Extent3.City, 
+        ///Extent3.State, 
+        ///Extent3.ZipCode
+        ///FROM Authors AS Extent2 INNER JOIN Stores AS Extent3 ON (Extent2.Id = Extent3.Id) OR ((Extent2.Id IS  NULL) AND (Extent3.Id IS  NULL))) AS Join1 ON (Extent1.Id = Join1.Id) OR ((Extent1.Id IS  NULL) AND (Join1.Id IS  NULL)).
+        /// </summary>
+        internal static string JoinOnRightSideNameClash {
+            get {
+                return ResourceManager.GetString("JoinOnRightSideNameClash", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to SELECT
+        ///Extent1.Id, 
         ///Extent1.Name, 
         ///Extent1.Address, 
         ///Extent1.City, 

=== modified file 'MySql.Data.Entity/Tests/Properties/SQLSyntax.resx'
--- a/MySql.Data.Entity/Tests/Properties/SQLSyntax.resx	2011-03-03 17:54:43 +0000
+++ b/MySql.Data.Entity/Tests/Properties/SQLSyntax.resx	2011-04-20 18:36:21 +0000
@@ -201,6 +201,43 @@
 FROM Toys AS Extent2
  WHERE (Project1.Id = Extent2.SupplierId) AND (Extent2.MinAge &lt; 4))</value>
   </data>
+  <data name="JoinOnRightSideAsDerivedTable" xml:space="preserve">
+    <value>SELECT
+Extent1.Id, 
+Extent1.EmployeeID, 
+Extent1.LastName, 
+Extent1.FirstName, 
+Extent1.BirthTime, 
+Extent1.Weight, 
+Extent1.LastModified
+FROM EmployeeChildren AS Extent1 INNER JOIN (SELECT
+Extent2.Id, 
+Extent2.LastName, 
+Extent2.FirstName, 
+Extent2.Age, 
+Extent3.EmployeeId, 
+Extent3.Salary
+FROM Employees AS Extent2 LEFT OUTER JOIN SalariedEmployees AS Extent3 ON Extent2.Id = Extent3.EmployeeId) AS Join1 ON (Extent1.EmployeeID = Join1.Id) OR ((Extent1.EmployeeID IS  NULL) AND (Join1.Id IS  NULL))
+ WHERE Extent1.Weight &gt; 7</value>
+  </data>
+  <data name="JoinOnRightSideNameClash" xml:space="preserve">
+    <value>SELECT
+1 AS C1, 
+Extent1.Id, 
+Extent1.Freight, 
+Extent1.StoreId
+FROM Orders AS Extent1 INNER JOIN (SELECT
+Extent2.Id, 
+Extent2.Name, 
+Extent2.Age, 
+Extent3.Id AS Id1, 
+Extent3.Name AS Name1, 
+Extent3.Address, 
+Extent3.City, 
+Extent3.State, 
+Extent3.ZipCode
+FROM Authors AS Extent2 INNER JOIN Stores AS Extent3 ON (Extent2.Id = Extent3.Id) OR ((Extent2.Id IS  NULL) AND (Extent3.Id IS  NULL))) AS Join1 ON (Extent1.Id = Join1.Id) OR ((Extent1.Id IS  NULL) AND (Join1.Id IS  NULL))</value>
+  </data>
   <data name="MaxInSubQuery1" xml:space="preserve">
     <value>SELECT
 Extent1.Id, 

=== modified file 'MySql.Data/Provider/Source/command.cs'
--- a/MySql.Data/Provider/Source/command.cs	2011-02-14 16:45:56 +0000
+++ b/MySql.Data/Provider/Source/command.cs	2011-04-20 18:36:21 +0000
@@ -825,6 +825,8 @@
                         token = tokenizer.NextToken();
                     }
                 }
+                // Otherwise use the command verbatim
+                else batchableCommandText = CommandText;
             }
 
             return batchableCommandText;

=== modified file 'MySql.Data/Tests/Source/CommandTests.cs'
--- a/MySql.Data/Tests/Source/CommandTests.cs	2011-03-03 17:55:08 +0000
+++ b/MySql.Data/Tests/Source/CommandTests.cs	2011-04-20 18:36:21 +0000
@@ -23,6 +23,7 @@
 using MySql.Data.MySqlClient;
 using NUnit.Framework;
 using System.Threading;
+using System.Diagnostics;
 
 namespace MySql.Data.MySqlClient.Tests
 {
@@ -490,8 +491,44 @@
             catch (MySqlException ex)
             {
             }
-
-        }
+        }
+
+#if !CF
+        /// <summary>
+        /// Bug #59616	Only INSERTs are batched
+        /// </summary>
+        [Test]
+        public void BatchUpdatesAndDeletes()
+        {
+            execSQL("CREATE TABLE test (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(20))");
+            execSQL("INSERT INTO test VALUES (1, 'boo'), (2, 'boo'), (3, 'boo')");
+
+            Trace.Listeners.Clear();
+            GenericListener listener = new GenericListener();
+            Trace.Listeners.Add(listener);
+
+            string connStr = GetConnectionString(true) + ";logging=true;allow batch=true";
+            using (MySqlConnection c = new MySqlConnection(connStr))
+            {
+                c.Open();
+                MySqlDataAdapter da = new MySqlDataAdapter("SELECT * FROM test", c);
+                MySqlCommandBuilder cb = new MySqlCommandBuilder(da);
+                da.UpdateCommand = cb.GetUpdateCommand();
+                da.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;
+                da.UpdateBatchSize = 100;
+
+                DataTable dt = new DataTable();
+                da.Fill(dt);
+
+                dt.Rows[0]["name"] = "boo2";
+                dt.Rows[1]["name"] = "boo2";
+                dt.Rows[2]["name"] = "boo2";
+                da.Update(dt);
+            }
+
+            Assert.AreEqual(1, listener.Find("UPDATE"));
+        }
+#endif
     }
 
 


Attachment: [text/bzr-bundle] bzr/reggie.burnett@oracle.com-20110420183621-97qoccellbpqwaf7.bundle
Thread
bzr commit into connector-net-6.0 branch (reggie.burnett:866) Bug#59616Bug#11850286Reggie Burnett20 Apr