* System.Data/DataTableReader.cs: Added event handlers to make reader stable
authorSureshkumar T <suresh@mono-cvs.ximian.com>
Wed, 4 May 2005 11:55:27 +0000 (11:55 -0000)
committerSureshkumar T <suresh@mono-cvs.ximian.com>
Wed, 4 May 2005 11:55:27 +0000 (11:55 -0000)
when the datasource is modified.

* Test/System.Data/DataTableReaderTest.cs:
- reader.close in finally in all test cases.
- Added tests to check scenarios when the datasource is modified/deleted/added.
- Added tests to check when the datatable is cleared.

svn path=/trunk/mcs/; revision=44021

mcs/class/System.Data/System.Data/ChangeLog
mcs/class/System.Data/System.Data/DataTableReader.cs
mcs/class/System.Data/Test/System.Data/ChangeLog
mcs/class/System.Data/Test/System.Data/DataTableReaderTest.cs

index 9d263f7b6fda7624f5f6f68119353f269b5871f7..ca95c452b4927c04b5bbb23c5f0f885b4f271edc 100644 (file)
@@ -1,5 +1,8 @@
 2005-05-04  Sureshkumar T  <tsureshkumar@novell.com>
 
+       * DataTableReader.cs: Added event handlers to make reader stable
+       when the datasource is modified.
+
        * DataTable.cs: Clear (): Raise TableCleared event.
 
        * DataTableClearEventArgs.cs: Args for DataTableClearEventHandler.
index 49b1c4347bc6020bd4a7a36362a4bc9cad7acdab..efd659a1288b9b3eaadb1e6ac95245e9f463938f 100644 (file)
@@ -41,10 +41,11 @@ namespace System.Data {
         {
                 bool            _closed;
                 DataTable []    _tables;
-                IEnumerator     _enumerator;
                 int             _current = -1;
                 int             _index;
                 DataTable       _schemaTable;
+                bool            _tableCleared = false;
+                bool            _subscribed = false;
 
                 #region Constructors
 
@@ -68,6 +69,9 @@ namespace System.Data {
                         _closed = false;
                         _index = 0;
                         _current = -1;
+                        _tableCleared = false;
+
+                        SubscribeEvents ();
                 }
 
                 #endregion // Constructors
@@ -137,9 +141,31 @@ namespace System.Data {
                 #endregion // Properties
 
                 #region Methods
+
+                private void SubscribeEvents ()
+                {
+                        if (_subscribed)        // avoid subscribing multiple times
+                                return;
+                        CurrentTable.TableCleared += new DataTableClearEventHandler (OnTableCleared);
+                        CurrentTable.RowChanged += new DataRowChangeEventHandler (OnRowChanged);
+                        _subscribed = true;
+                }
+
+                private void UnsubscribeEvents ()
+                {
+                        if (!_subscribed)       // avoid un-subscribing multiple times
+                                return;
+                        CurrentTable.TableCleared -= new DataTableClearEventHandler (OnTableCleared);
+                        CurrentTable.RowChanged -= new DataRowChangeEventHandler (OnRowChanged);
+                        _subscribed = false;
+                }
                 
                 public override void Close ()
                 {
+                        if (IsClosed)
+                                return;
+                        
+                        UnsubscribeEvents ();
                         _closed = true;
                 }
                 
@@ -321,9 +347,18 @@ namespace System.Data {
                 private void Validate ()
                 {
                        ValidateClosed ();
+
+                        if (_index >= _tables.Length)
+                                throw new InvalidOperationException ("Invalid attempt to read when " + 
+                                                                     "no data is present");
+
+                        if (_tableCleared)
+                                throw new RowNotInTableException ("The table is cleared, no rows are " +
+                                                                  "accessible");
+                        
                        if (_current == -1)
-                                throw new InvalidOperationException ("Invalid attempt to read before " + 
-                                                                     "Read is called");
+                                throw new InvalidOperationException ("DataReader is invalid " + 
+                                                                     "for the DataTable");
                 }
 
                 private void ValidateClosed ()
@@ -336,19 +371,33 @@ namespace System.Data {
                 
                 private bool MoveNext ()
                 {
+                        if (_index >= _tables.Length || _tableCleared)
+                                return false;
+                        
                         do {
                                 _current++;
                         } while (_current < CurrentTable.Rows.Count 
                                  && CurrentRow.RowState == DataRowState.Deleted);
+                        
                         return _current < CurrentTable.Rows.Count;
+
                 }
 
                 public override bool NextResult ()
                 {
-                        _current = -1;
+                        if ((_index + 1) >= _tables.Length) {
+                                UnsubscribeEvents ();
+                                _index = _tables.Length;     // to make any attempt invalid
+                                return false; // end of tables.
+                        }
+                        
+                        UnsubscribeEvents ();
                         _index++;
+                        _current = -1;
                         _schemaTable = null;            // force to create fresh
-                        return _index < _tables.Length;
+                        _tableCleared = false;
+                        SubscribeEvents ();
+                        return true;
                 }
 
                 public override bool Read ()
@@ -358,6 +407,46 @@ namespace System.Data {
                 }
 
                 #endregion // Methods
+
+                #region // Event Handlers
+
+                private void OnRowChanged (object src, DataRowChangeEventArgs args)
+                {
+                        DataRowAction action = args.Action;
+                        DataRow row = args.Row;
+                        if (action == DataRowAction.Add) {
+                                if (_tableCleared && _current != -1)
+                                        return;
+                                
+                                if (_current == -1 // yet to read
+                                    || (_current >= 0 && row.RowID > CurrentRow.RowID) // row added above
+                                    ) {
+                                        _tableCleared = false;
+                                        return; // no. movement required, if row added after current.
+                                }
+
+                                _current++;
+
+                        }
+                        
+                        if (action == DataRowAction.Commit 
+                            && row.RowState == DataRowState.Detached) {
+                                // FIXME : How to find whether the row deleted falls below 
+                                //  current row or above?.
+                                if (_current >= CurrentTable.Rows.Count) {
+                                        _current--;
+                                }
+                                
+                                
+                        }
+                }
+
+                private void OnTableCleared (object src, DataTableClearEventArgs args)
+                {
+                        _tableCleared = true;
+                }
+                
+                #endregion // Event Handlers
         }
 }
 
index db6cd83d040f5c36f9e4b39df7063dd33b920b6c..b32a60f7c2e041ccb90074d0a78fad504e7a2887 100644 (file)
@@ -1,5 +1,11 @@
 2005-05-04  Sureshkumar T  <tsureshkumar@novell.com>
 
+       * DataTableReaderTest.cs:
+       - reader.close in finally in all test cases.
+       - Added tests to check scenarios when the datasource is
+       modified/deleted/added.
+       - Added tests to check when the datatable is cleared.
+
        * DataTableTest.cs: 
        - Added a test for DataTable. Should clear rows from indexes as
        well. Simplified table creation for ClearReset test.
index c3532c002ce73ffbf4c1d700993792a5d55e6cc8..69252f624a4be921e3defeb9154e692d01635bc1 100755 (executable)
@@ -63,17 +63,20 @@ namespace MonoTests.System.Data.SqlClient
                 [Test]
                 public void CtorTest ()
                 {
-
                         dt.Rows [1].Delete ();
-
-                        int i = 0;
-
                         DataTableReader reader = new DataTableReader (dt);
-                        while (reader.Read ())
-                                i++;
-                        reader.Close ();
+                        try {
+                                
+                                int i = 0;
+                                while (reader.Read ())
+                                        i++;
+                                reader.Close ();
 
-                        Assert.AreEqual (2, i, "no. of rows iterated is wrong");
+                                Assert.AreEqual (2, i, "no. of rows iterated is wrong");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
@@ -82,10 +85,15 @@ namespace MonoTests.System.Data.SqlClient
                 {
 
                         DataTableReader reader = new DataTableReader (dt);
-                        reader.Read ();
-                        reader.Read (); // 2nd row
-                        dt.Rows [1].Delete ();
-                        string value = reader [1].ToString ();
+                        try {
+                                reader.Read ();
+                                reader.Read (); // 2nd row
+                                dt.Rows [1].Delete ();
+                                string value = reader [1].ToString ();
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
@@ -93,22 +101,32 @@ namespace MonoTests.System.Data.SqlClient
                 {
 
                         DataTableReader reader = new DataTableReader (dt);
-                        reader.Read (); // first row
-                        dt.Rows [1].Delete ();
-                        reader.Read (); // it should be 3rd row
-                        string value = reader [0].ToString ();
-                        Assert.AreEqual ("3", value, "#1 reader should have moved to 3rd row");
+                        try {
+                                reader.Read (); // first row
+                                dt.Rows [1].Delete ();
+                                reader.Read (); // it should be 3rd row
+                                string value = reader [0].ToString ();
+                                Assert.AreEqual ("3", value, "#1 reader should have moved to 3rd row");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
                 public void SeeTheModifiedTest ()
                 {
                         DataTableReader reader = new DataTableReader (dt);
-                        reader.Read (); // first row
-                        dt.Rows [1] ["name"] = "mono changed";
-                        reader.Read ();
-                        string value = reader [1].ToString ();
-                        Assert.AreEqual ("mono changed", value, "#2 reader should have moved to 3rd row");
+                        try {
+                                reader.Read (); // first row
+                                dt.Rows [1] ["name"] = "mono changed";
+                                reader.Read ();
+                                string value = reader [1].ToString ();
+                                Assert.AreEqual ("mono changed", value, "#2 reader should have moved to 3rd row");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
@@ -122,17 +140,22 @@ namespace MonoTests.System.Data.SqlClient
                         another.Rows.Add (new object [] {"test 3" });
 
                         DataTableReader reader = new DataTableReader (new DataTable [] { dt, another });
-                        DataTable schema = reader.GetSchemaTable ();
+                        try {
+                                DataTable schema = reader.GetSchemaTable ();
 
-                        Assert.AreEqual (dt.Columns.Count, schema.Rows.Count, "#1 should be same");
-                        Assert.AreEqual (dt.Columns [1].DataType.ToString (), schema.Rows [1] ["DataType"].ToString (), "#2 data type should match");
+                                Assert.AreEqual (dt.Columns.Count, schema.Rows.Count, "#1 should be same");
+                                Assert.AreEqual (dt.Columns [1].DataType.ToString (), schema.Rows [1] ["DataType"].ToString (), "#2 data type should match");
 
-                        reader.NextResult (); //schema should change here
-                        schema = reader.GetSchemaTable ();
+                                reader.NextResult (); //schema should change here
+                                schema = reader.GetSchemaTable ();
 
-                        Assert.AreEqual (another.Columns.Count, schema.Rows.Count, "#3 should be same");
-                        Assert.AreEqual (another.Columns [0].DataType.ToString (), schema.Rows [0] ["DataType"].ToString (), "#4 data type should match");
+                                Assert.AreEqual (another.Columns.Count, schema.Rows.Count, "#3 should be same");
+                                Assert.AreEqual (another.Columns [0].DataType.ToString (), schema.Rows [0] ["DataType"].ToString (), "#4 data type should match");
                         
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
@@ -147,13 +170,18 @@ namespace MonoTests.System.Data.SqlClient
                         DataTable [] collection = new DataTable [] { dt, dt1 } ; 
                         
                         DataTableReader reader = new DataTableReader (collection);
-                        int i = 0;
-                        do {
-                                while (reader.Read ())
-                                        i++;
-                        } while (reader.NextResult ());
+                        try {
+                                int i = 0;
+                                do {
+                                        while (reader.Read ())
+                                                i++;
+                                } while (reader.NextResult ());
                                                 
-                        Assert.AreEqual (5, i, "#1 rows should be of both the tables");
+                                Assert.AreEqual (5, i, "#1 rows should be of both the tables");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
@@ -163,13 +191,18 @@ namespace MonoTests.System.Data.SqlClient
                         dt.Rows [0] ["nullint"] = 333;
 
                         DataTableReader reader = new DataTableReader (dt);
-                        reader.Read ();
+                        try {
+                                reader.Read ();
                         
-                        int ordinal = reader.GetOrdinal ("nullint");
-                        // Get by name
-                        Assert.AreEqual (1, (int) reader ["id"], "#1 should be able to get by name");
-                        Assert.AreEqual (333, reader.GetInt32 (ordinal), "#2 should get int32");
-                        Assert.AreEqual ("System.Int32", reader.GetDataTypeName (ordinal), "#3 data type should match");
+                                int ordinal = reader.GetOrdinal ("nullint");
+                                // Get by name
+                                Assert.AreEqual (1, (int) reader ["id"], "#1 should be able to get by name");
+                                Assert.AreEqual (333, reader.GetInt32 (ordinal), "#2 should get int32");
+                                Assert.AreEqual ("System.Int32", reader.GetDataTypeName (ordinal), "#3 data type should match");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
@@ -177,21 +210,32 @@ namespace MonoTests.System.Data.SqlClient
                 public void CloseTest ()
                 {
                         DataTableReader reader = new DataTableReader (dt);
-                        int i = 0;
-                        while (reader.Read () && i < 1)
-                                i++;
-                        reader.Close ();
-                        reader.Read ();
+                        try {
+                                int i = 0;
+                                while (reader.Read () && i < 1)
+                                        i++;
+                                reader.Close ();
+                                reader.Read ();
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                 [Test]
                 public void GetOrdinalTest ()
                 {
                         DataTableReader reader = new DataTableReader (dt);
-                        Assert.AreEqual (1, reader.GetOrdinal ("name"), "#1 get ordinal should work even" +
-                                         " without calling Read");
+                        try {
+                                Assert.AreEqual (1, reader.GetOrdinal ("name"), "#1 get ordinal should work even" +
+                                                 " without calling Read");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
                 #endregion // Positive Tests
+
                 
                 #region Negative Tests
                 [Test]
@@ -201,9 +245,14 @@ namespace MonoTests.System.Data.SqlClient
                         dt.AcceptChanges ();
                         
                         DataTableReader reader = new DataTableReader (dt);
+                        try {
                         
-                        Assert.AreEqual (false, reader.Read (), "#1 there are no rows");
-                        Assert.AreEqual (false, reader.NextResult (), "#2 there are no further resultsets");
+                                Assert.AreEqual (false, reader.Read (), "#1 there are no rows");
+                                Assert.AreEqual (false, reader.NextResult (), "#2 there are no further resultsets");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
                 
                 [Test]
@@ -211,7 +260,12 @@ namespace MonoTests.System.Data.SqlClient
                 public void NoTablesTest ()
                 {
                         DataTableReader reader = new DataTableReader (new DataTable [] {});
-                        reader.Read ();
+                        try {
+                                reader.Read ();
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                 }
 
                [Test]
@@ -219,9 +273,14 @@ namespace MonoTests.System.Data.SqlClient
                public void ReadAfterClosedTest ()
                {
                         DataTableReader reader = new DataTableReader (dt);
-                       reader.Read ();
-                       reader.Close ();
-                        reader.Read ();
+                        try {
+                                reader.Read ();
+                                reader.Close ();
+                                reader.Read ();
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                }       
 
                [Test]
@@ -229,10 +288,15 @@ namespace MonoTests.System.Data.SqlClient
                public void AccessAfterClosedTest ()
                {
                         DataTableReader reader = new DataTableReader (dt);
-                       reader.Read ();
-                       reader.Close ();
-                       int i = (int) reader [0];
-                       i++; // to supress warning
+                        try {
+                                reader.Read ();
+                                reader.Close ();
+                                int i = (int) reader [0];
+                                i++; // to supress warning
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                }
 
                 [Test]
@@ -240,8 +304,13 @@ namespace MonoTests.System.Data.SqlClient
                public void AccessBeforeReadTest ()
                {
                         DataTableReader reader = new DataTableReader (dt);
-                       int i = (int) reader [0];
-                       i++; // to supress warning
+                        try {
+                                int i = (int) reader [0];
+                                i++; // to supress warning
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                }
 
                 [Test]
@@ -249,11 +318,197 @@ namespace MonoTests.System.Data.SqlClient
                public void InvalidIndexTest ()
                {
                         DataTableReader reader = new DataTableReader (dt);
-                        reader.Read ();
-                       int i = (int) reader [90]; // kidding, ;-)
-                       i++; // to supress warning
+                        try {
+                                reader.Read ();
+                                int i = (int) reader [90]; // kidding, ;-)
+                                i++; // to supress warning
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
                }
 
+                [Test]
+               public void DontSeeTheEarlierRowsTest ()
+               {
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+
+                                // insert a row at position 0
+                                DataRow r = dt.NewRow ();
+                                r [0] = 0;
+                                r [1] = "adhi bagavan";
+                                dt.Rows.InsertAt (r, 0);
+                        
+                                Assert.AreEqual (2, (int) reader.GetInt32 (0), "#1 should not alter the position");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+               }
+
+                [Test]
+                public void AddBeforePointTest ()
+                {
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                DataRow r = dt.NewRow ();
+                                r [0] = 0;
+                                r [1] = "adhi bagavan";
+                                dt.Rows.InsertAt (r, 0);
+                                dt.Rows.Add (new object [] { 4, "mono 4"}); // should not affect the counter
+                                Assert.AreEqual (2, (int) reader [0], "#1 should not affect the current position");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+                }
+
+                [Test]
+                public void AddAtPointTest ()
+                {
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                DataRow r = dt.NewRow ();
+                                r [0] = 0;
+                                r [1] = "same point";
+                                dt.Rows.InsertAt (r, 1);
+                                dt.Rows.Add (new object [] { 4, "mono 4"}); // should not affect the counter
+                                Assert.AreEqual (2, (int) reader [0], "#1 should not affect the current position");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+                }
+
+                [Test]
+                public void DeletePreviousAndAcceptChangesTest ()
+                {
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                dt.Rows [0].Delete ();
+                                dt.AcceptChanges ();
+                                Assert.AreEqual (2, (int) reader [0], "#1 should not affect the current position");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+
+                }
+
+                [Test]
+                public void DeleteCurrentAndAcceptChangesTest2 ()
+                {
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                dt.Rows [1].Delete (); // delete row, where reader points to
+                                dt.AcceptChanges (); // accept the action
+                                Assert.AreEqual (1, (int) reader [0], "#1 should point to the first row");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+                }
+
+                [Test]
+                public void DeleteLastAndAcceptChangesTest2 ()
+                {
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                reader.Read (); // third row
+                                dt.Rows [2].Delete (); // delete row, where reader points to
+                                dt.AcceptChanges (); // accept the action
+                                Assert.AreEqual (2, (int) reader [0], "#1 should point to the first row");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+                }
+
+                [Test]
+                public void ClearTest ()
+                {
+                        DataTableReader reader = null;
+                        try {
+                                reader = new DataTableReader (dt);
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                dt.Clear ();
+                                try {
+                                        int i = (int) reader [0];
+                                        i++; // supress warning
+                                        Assert.Fail("#1 should have thrown RowNotInTableException");
+                                } catch (RowNotInTableException) {}
+
+                                // clear and add test
+                                reader.Close ();
+                                reader = new DataTableReader (dt);
+                                reader.Read (); // first row
+                                reader.Read (); // second row
+                                dt.Clear ();
+                                dt.Rows.Add (new object [] {8, "mono 8"});
+                                dt.AcceptChanges ();
+                                bool success = reader.Read ();
+                                Assert.AreEqual (false, success, "#2 is always invalid");
+
+                                // clear when reader is not read yet
+                                reader.Close ();
+                                reader = new DataTableReader (dt);
+                                dt.Clear ();
+                                dt.Rows.Add (new object [] {8, "mono 8"});
+                                dt.AcceptChanges ();
+                                success = reader.Read ();
+                                Assert.AreEqual (true, success, "#3 should add");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+                        
+                }
+
+                [Test]
+                public void MultipleDeleteTest ()
+                {
+                        dt.Rows.Add (new object [] {4, "mono 4"});
+                        dt.Rows.Add (new object [] {5, "mono 5"});
+                        dt.Rows.Add (new object [] {6, "mono 6"});
+                        dt.Rows.Add (new object [] {7, "mono 7"});
+                        dt.Rows.Add (new object [] {8, "mono 8"});
+                        dt.AcceptChanges ();
+                        
+                        DataTableReader reader = new DataTableReader (dt);
+                        try {
+                                reader.Read (); // first row
+                                reader.Read ();
+                                reader.Read ();
+                                reader.Read ();
+                                reader.Read ();
+
+                                dt.Rows [3].Delete ();
+                                dt.Rows [1].Delete ();
+                                dt.Rows [2].Delete ();
+                                dt.Rows [0].Delete ();
+                                dt.Rows [6].Delete ();
+                                dt.AcceptChanges ();
+
+                                Assert.AreEqual (5, (int) reader [0], "#1 should keep pointing to 5");
+                        } finally {
+                                if (reader != null && !reader.IsClosed)
+                                        reader.Close ();
+                        }
+                }
                 #endregion // Negative Tests
 
         }