1 // Licensed to the .NET Foundation under one or more agreements.
2 // See the LICENSE file in the project root for more information.
3 
4 // (C) Franklin Wise
5 // (C) 2003 Martin Willemoes Hansen
6 
7 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 
28 using Xunit;
29 
30 namespace System.Data.Tests
31 {
32     public class ForeignKeyConstraintTest
33     {
34         private DataSet _ds;
35 
36         //NOTE: fk constraints only work when the table is part of a DataSet
37 
ForeignKeyConstraintTest()38         public ForeignKeyConstraintTest()
39         {
40             _ds = new DataSet();
41 
42             //Setup DataTable
43             DataTable table;
44             table = new DataTable("TestTable");
45             table.Columns.Add("Col1", typeof(int));
46             table.Columns.Add("Col2", typeof(int));
47             table.Columns.Add("Col3", typeof(int));
48 
49             _ds.Tables.Add(table);
50 
51             table = new DataTable("TestTable2");
52             table.Columns.Add("Col1", typeof(int));
53             table.Columns.Add("Col2", typeof(int));
54             table.Columns.Add("Col3", typeof(int));
55 
56             _ds.Tables.Add(table);
57         }
58 
59         // Tests ctor (string, DataColumn, DataColumn)
60         [Fact]
Ctor1()61         public void Ctor1()
62         {
63             DataTable Table = _ds.Tables[0];
64 
65             Assert.Equal(0, Table.Constraints.Count);
66             Table = _ds.Tables[1];
67             Assert.Equal(0, Table.Constraints.Count);
68 
69             // ctor (string, DataColumn, DataColumn
70             ForeignKeyConstraint Constraint = new ForeignKeyConstraint("test", _ds.Tables[0].Columns[2], _ds.Tables[1].Columns[0]);
71             Table = _ds.Tables[1];
72             Table.Constraints.Add(Constraint);
73 
74             Assert.Equal(1, Table.Constraints.Count);
75             Assert.Equal("test", Table.Constraints[0].ConstraintName);
76             Assert.Equal(typeof(ForeignKeyConstraint), Table.Constraints[0].GetType());
77 
78             Table = _ds.Tables[0];
79             Assert.Equal(1, Table.Constraints.Count);
80             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
81             Assert.Equal(typeof(UniqueConstraint), Table.Constraints[0].GetType());
82         }
83 
84         // Tests ctor (DataColumn, DataColumn)
85         [Fact]
Ctor2()86         public void Ctor2()
87         {
88             DataTable Table = _ds.Tables[0];
89 
90             Assert.Equal(0, Table.Constraints.Count);
91             Table = _ds.Tables[1];
92             Assert.Equal(0, Table.Constraints.Count);
93 
94             // ctor (string, DataColumn, DataColumn
95             ForeignKeyConstraint Constraint = new ForeignKeyConstraint(_ds.Tables[0].Columns[2], _ds.Tables[1].Columns[0]);
96             Table = _ds.Tables[1];
97             Table.Constraints.Add(Constraint);
98 
99             Assert.Equal(1, Table.Constraints.Count);
100             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
101             Assert.Equal(typeof(ForeignKeyConstraint), Table.Constraints[0].GetType());
102 
103             Table = _ds.Tables[0];
104             Assert.Equal(1, Table.Constraints.Count);
105             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
106             Assert.Equal(typeof(UniqueConstraint), Table.Constraints[0].GetType());
107         }
108 
109         // Test ctor (DataColumn [], DataColumn [])
110         [Fact]
Ctor3()111         public void Ctor3()
112         {
113             DataTable Table = _ds.Tables[0];
114 
115             Assert.Equal(0, Table.Constraints.Count);
116             Table = _ds.Tables[1];
117             Assert.Equal(0, Table.Constraints.Count);
118 
119             DataColumn[] Cols1 = new DataColumn[2];
120             Cols1[0] = _ds.Tables[0].Columns[1];
121             Cols1[1] = _ds.Tables[0].Columns[2];
122 
123             DataColumn[] Cols2 = new DataColumn[2];
124             Cols2[0] = _ds.Tables[1].Columns[0];
125             Cols2[1] = _ds.Tables[1].Columns[1];
126 
127             ForeignKeyConstraint Constraint = new ForeignKeyConstraint(Cols1, Cols2);
128             Table = _ds.Tables[1];
129             Table.Constraints.Add(Constraint);
130 
131             Assert.Equal(1, Table.Constraints.Count);
132             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
133             Assert.Equal(typeof(ForeignKeyConstraint), Table.Constraints[0].GetType());
134 
135             Table = _ds.Tables[0];
136             Assert.Equal(1, Table.Constraints.Count);
137             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
138             Assert.Equal(typeof(UniqueConstraint), Table.Constraints[0].GetType());
139         }
140 
141         // Tests ctor (string, DataColumn [], DataColumn [])
142         [Fact]
Ctor4()143         public void Ctor4()
144         {
145             DataTable Table = _ds.Tables[0];
146 
147             Assert.Equal(0, Table.Constraints.Count);
148             Table = _ds.Tables[1];
149             Assert.Equal(0, Table.Constraints.Count);
150 
151             DataColumn[] Cols1 = new DataColumn[2];
152             Cols1[0] = _ds.Tables[0].Columns[1];
153             Cols1[1] = _ds.Tables[0].Columns[2];
154 
155             DataColumn[] Cols2 = new DataColumn[2];
156             Cols2[0] = _ds.Tables[1].Columns[0];
157             Cols2[1] = _ds.Tables[1].Columns[1];
158 
159             ForeignKeyConstraint Constraint = new ForeignKeyConstraint("Test", Cols1, Cols2);
160             Table = _ds.Tables[1];
161             Table.Constraints.Add(Constraint);
162 
163             Assert.Equal(1, Table.Constraints.Count);
164             Assert.Equal("Test", Table.Constraints[0].ConstraintName);
165             Assert.Equal(typeof(ForeignKeyConstraint), Table.Constraints[0].GetType());
166 
167             Table = _ds.Tables[0];
168             Assert.Equal(1, Table.Constraints.Count);
169             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
170             Assert.Equal(typeof(UniqueConstraint), Table.Constraints[0].GetType());
171         }
172 
173         [Fact]
TestCtor5()174         public void TestCtor5()
175         {
176             DataTable table1 = new DataTable("Table1");
177             DataTable table2 = new DataTable("Table2");
178             DataSet dataSet = new DataSet();
179             dataSet.Tables.Add(table1);
180             dataSet.Tables.Add(table2);
181             DataColumn column1 = new DataColumn("col1");
182             DataColumn column2 = new DataColumn("col2");
183             DataColumn column3 = new DataColumn("col3");
184             table1.Columns.Add(column1);
185             table1.Columns.Add(column2);
186             table1.Columns.Add(column3);
187             DataColumn column4 = new DataColumn("col4");
188             DataColumn column5 = new DataColumn("col5");
189             DataColumn column6 = new DataColumn("col6");
190             table2.Columns.Add(column4);
191             table2.Columns.Add(column5);
192             table2.Columns.Add(column6);
193             string[] parentColumnNames = { "col1", "col2", "col3" };
194             string[] childColumnNames = { "col4", "col5", "col6" };
195             string parentTableName = "table1";
196 
197             // Create a ForeingKeyConstraint Object using the constructor
198             // ForeignKeyConstraint (string, string, string[], string[], AcceptRejectRule, Rule, Rule);
199             ForeignKeyConstraint fkc = new ForeignKeyConstraint("hello world", parentTableName, parentColumnNames, childColumnNames, AcceptRejectRule.Cascade, Rule.Cascade, Rule.Cascade);                                                                                                                            // Assert that the Constraint object does not belong to any table yet
200             try
201             {
202                 DataTable tmp = fkc.Table;
203                 Assert.False(true);
204             }
205             catch (NullReferenceException)
206             { // actually .NET throws this (bug)
207             }
208             catch (InvalidOperationException)
209             {
210             }
211 
212             Constraint[] constraints = new Constraint[3];
213             constraints[0] = new UniqueConstraint(column1);
214             constraints[1] = new UniqueConstraint(column2);
215             constraints[2] = fkc;
216 
217             // Try to add the constraint to ConstraintCollection of the DataTable through Add()
218             try
219             {
220                 table2.Constraints.Add(fkc);
221                 throw new ApplicationException("An Exception was expected");
222             }
223             // LAMESPEC : spec says InvalidConstraintException but throws this
224             catch (NullReferenceException)
225             {
226             }
227 
228 #if false // FIXME: Here this test crashes under MS.NET.
229                         // OK - So AddRange() is the only way!
230                         table2.Constraints.AddRange (constraints);
231 			   // After AddRange(), Check the properties of ForeignKeyConstraint object
232                         Assert.True(fkc.RelatedColumns [0].ColumnName.Equals ("col1"));
233                         Assert.True(fkc.RelatedColumns [1].ColumnName.Equals ("col2"));
234                         Assert.True(fkc.RelatedColumns [2].ColumnName.Equals ("col3"));
235                         Assert.True(fkc.Columns [0].ColumnName.Equals ("col4"));
236                         Assert.True(fkc.Columns [1].ColumnName.Equals ("col5"));
237                         Assert.True(fkc.Columns [2].ColumnName.Equals ("col6"));
238 #endif
239             // Try to add columns with names which do not exist in the table
240             parentColumnNames[2] = "noColumn";
241             ForeignKeyConstraint foreignKeyConstraint = new ForeignKeyConstraint("hello world", parentTableName, parentColumnNames, childColumnNames, AcceptRejectRule.Cascade, Rule.Cascade, Rule.Cascade);
242             constraints[0] = new UniqueConstraint(column1);
243             constraints[1] = new UniqueConstraint(column2);
244             constraints[2] = foreignKeyConstraint;
245             try
246             {
247                 table2.Constraints.AddRange(constraints);
248                 throw new ApplicationException("An Exception was expected");
249             }
250             catch (ArgumentException e)
251             {
252             }
253             catch (InvalidConstraintException e)
254             { // Could not test on ms.net, as ms.net does not reach here so far.
255             }
256 
257 #if false // FIXME: Here this test crashes under MS.NET.
258                         // Check whether the child table really contains the foreign key constraint named "hello world"
259                         Assert.True(table2.Constraints.Contains ("hello world"));
260 #endif
261         }
262 
263 
264 
265         //  If Childs and parents are in same table
266         [Fact]
KeyBetweenColumns()267         public void KeyBetweenColumns()
268         {
269             DataTable Table = _ds.Tables[0];
270 
271             Assert.Equal(0, Table.Constraints.Count);
272             Table = _ds.Tables[1];
273             Assert.Equal(0, Table.Constraints.Count);
274 
275 
276             ForeignKeyConstraint Constraint = new ForeignKeyConstraint("Test", _ds.Tables[0].Columns[0], _ds.Tables[0].Columns[2]);
277             Table = _ds.Tables[0];
278             Table.Constraints.Add(Constraint);
279 
280             Assert.Equal(2, Table.Constraints.Count);
281             Assert.Equal("Constraint1", Table.Constraints[0].ConstraintName);
282             Assert.Equal(typeof(UniqueConstraint), Table.Constraints[0].GetType());
283             Assert.Equal("Test", Table.Constraints[1].ConstraintName);
284             Assert.Equal(typeof(ForeignKeyConstraint), Table.Constraints[1].GetType());
285         }
286 
287         [Fact]
CtorExceptions()288         public void CtorExceptions()
289         {
290             ForeignKeyConstraint fkc;
291 
292             DataTable localTable = new DataTable();
293             localTable.Columns.Add("Col1", typeof(int));
294             localTable.Columns.Add("Col2", typeof(bool));
295 
296             //Null
297             Assert.Throws<NullReferenceException>(() =>
298             {
299                 fkc = new ForeignKeyConstraint(null, (DataColumn)null);
300             });
301 
302             //zero length collection
303             AssertExtensions.Throws<ArgumentException>(null, () =>
304             {
305                 fkc = new ForeignKeyConstraint(new DataColumn[] { }, new DataColumn[] { });
306             });
307 
308             //different datasets
309             Assert.Throws<InvalidOperationException>(() =>
310             {
311                 fkc = new ForeignKeyConstraint(_ds.Tables[0].Columns[0], localTable.Columns[0]);
312             });
313 
314             Assert.Throws<InvalidOperationException>(() =>
315             {
316                 fkc = new ForeignKeyConstraint(_ds.Tables[0].Columns[0], localTable.Columns[1]);
317             });
318 
319             // Cannot create a Key from Columns that belong to
320             // different tables.
321             Assert.Throws<InvalidConstraintException>(() =>
322             {
323                 fkc = new ForeignKeyConstraint(new DataColumn[] { _ds.Tables[0].Columns[0], _ds.Tables[0].Columns[1] }, new DataColumn[] { localTable.Columns[1], _ds.Tables[1].Columns[0] });
324             });
325         }
326 
327         [Fact]
CtorExceptions2()328         public void CtorExceptions2()
329         {
330             DataColumn col = new DataColumn("MyCol1", typeof(int));
331 
332             ForeignKeyConstraint fkc;
333 
334             //Columns must belong to a Table
335             AssertExtensions.Throws<ArgumentException>(null, () =>
336             {
337                 fkc = new ForeignKeyConstraint(col, _ds.Tables[0].Columns[0]);
338             });
339 
340             //Columns must belong to the same table
341             //InvalidConstraintException
342 
343             DataColumn[] difTable = new DataColumn[] {_ds.Tables[0].Columns[2],
344                                        _ds.Tables[1].Columns[0]};
345             Assert.Throws<InvalidConstraintException>(() =>
346             {
347                 fkc = new ForeignKeyConstraint(difTable, new DataColumn[] {
348                                  _ds.Tables[0].Columns[1],
349                                 _ds.Tables[0].Columns[0]});
350             });
351 
352             //parent columns and child columns should be the same length
353             //ArgumentException
354             DataColumn[] twoCol =
355                 new DataColumn[] { _ds.Tables[0].Columns[0], _ds.Tables[0].Columns[1] };
356 
357 
358             AssertExtensions.Throws<ArgumentException>(null, () =>
359             {
360                 fkc = new ForeignKeyConstraint(twoCol,
361                     new DataColumn[] { _ds.Tables[0].Columns[0] });
362             });
363 
364             //InvalidOperation: Parent and child are the same column.
365             Assert.Throws<InvalidOperationException>(() =>
366             {
367                 fkc = new ForeignKeyConstraint(_ds.Tables[0].Columns[0],
368                     _ds.Tables[0].Columns[0]);
369             });
370         }
371 
372         [Fact]
EqualsAndHashCode()373         public void EqualsAndHashCode()
374         {
375             DataTable tbl = _ds.Tables[0];
376             DataTable tbl2 = _ds.Tables[1];
377 
378             ForeignKeyConstraint fkc = new ForeignKeyConstraint(
379                 new DataColumn[] { tbl.Columns[0], tbl.Columns[1] },
380                 new DataColumn[] { tbl2.Columns[0], tbl2.Columns[1] });
381 
382             ForeignKeyConstraint fkc2 = new ForeignKeyConstraint(
383                 new DataColumn[] { tbl.Columns[0], tbl.Columns[1] },
384                 new DataColumn[] { tbl2.Columns[0], tbl2.Columns[1] });
385 
386             ForeignKeyConstraint fkcDiff =
387                 new ForeignKeyConstraint(tbl.Columns[1], tbl.Columns[2]);
388 
389             Assert.True(fkc.Equals(fkc2));
390             Assert.True(fkc2.Equals(fkc));
391             Assert.True(fkc.Equals(fkc));
392 
393             Assert.True(fkc.Equals(fkcDiff) == false);
394 
395             //Assert.True( "Hash Code Assert.True.Failed. 1");
396             Assert.True(fkc.GetHashCode() != fkcDiff.GetHashCode());
397         }
398 
399         [Fact]
ViolationTest()400         public void ViolationTest()
401         {
402             AssertExtensions.Throws<ArgumentException>(null, () =>
403             {
404                 DataTable parent = _ds.Tables[0];
405                 DataTable child = _ds.Tables[1];
406 
407                 parent.Rows.Add(new object[] { 1, 1, 1 });
408                 child.Rows.Add(new object[] { 2, 2, 2 });
409 
410                 try
411                 {
412                     child.Constraints.Add(new ForeignKeyConstraint(parent.Columns[0],
413                                               child.Columns[0])
414                                    );
415                 }
416                 finally
417                 {
418                     // clear the rows for further testing
419                     _ds.Clear();
420                 }
421             });
422         }
423 
424         [Fact]
NoViolationTest()425         public void NoViolationTest()
426         {
427             DataTable parent = _ds.Tables[0];
428             DataTable child = _ds.Tables[1];
429 
430             parent.Rows.Add(new object[] { 1, 1, 1 });
431             child.Rows.Add(new object[] { 2, 2, 2 });
432 
433             try
434             {
435                 _ds.EnforceConstraints = false;
436                 child.Constraints.Add(new ForeignKeyConstraint(parent.Columns[0],
437                                           child.Columns[0])
438                                );
439             }
440             finally
441             {
442                 // clear the rows for further testing
443                 _ds.Clear();
444                 _ds.EnforceConstraints = true;
445             }
446         }
447 
448         [Fact]
ModifyParentKeyBeforeAcceptChanges()449         public void ModifyParentKeyBeforeAcceptChanges()
450         {
451             DataSet ds1 = new DataSet();
452             DataTable t1 = ds1.Tables.Add("t1");
453             DataTable t2 = ds1.Tables.Add("t2");
454             t1.Columns.Add("col1", typeof(int));
455             t2.Columns.Add("col2", typeof(int));
456             ds1.Relations.Add("fk", t1.Columns[0], t2.Columns[0]);
457 
458             t1.Rows.Add(new object[] { 10 });
459             t2.Rows.Add(new object[] { 10 });
460 
461             t1.Rows[0][0] = 20;
462             Assert.True((int)t2.Rows[0][0] == 20);
463         }
464 
465         [Fact]
466         // https://bugzilla.novell.com/show_bug.cgi?id=650402
ForeignKey_650402()467         public void ForeignKey_650402()
468         {
469             DataSet data = new DataSet();
470             DataTable parent = new DataTable("parent");
471             DataColumn pk = parent.Columns.Add("PK");
472             DataTable child = new DataTable("child");
473             DataColumn fk = child.Columns.Add("FK");
474 
475             data.Tables.Add(parent);
476             data.Tables.Add(child);
477             data.Relations.Add(pk, fk);
478 
479             parent.Rows.Add("value");
480             child.Rows.Add("value");
481             data.AcceptChanges();
482             child.Rows[0].Delete();
483             parent.Rows[0][0] = "value2";
484 
485             data.EnforceConstraints = false;
486             data.EnforceConstraints = true;
487         }
488     }
489 }
490