New tests.
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Vendor / Implementation / SchemaLoader.ForeignKey.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System.Data;\r
28 using System.Linq;\r
29 using DbLinq.Schema;\r
30 using DbLinq.Schema.Dbml;\r
31 using System.Text;\r
32 using System.Collections.Generic;\r
33 \r
34 namespace DbLinq.Vendor.Implementation\r
35 {\r
36     partial class SchemaLoader\r
37     {\r
38         /// <summary>\r
39         /// Loads the constraints.\r
40         /// </summary>\r
41         /// <param name="schema">The schema.</param>\r
42         /// <param name="schemaName">Name of the schema.</param>\r
43         /// <param name="conn">The conn.</param>\r
44         /// <param name="nameFormat">The name format.</param>\r
45         /// <param name="names">The names.</param>\r
46         protected abstract void LoadConstraints(Database schema, SchemaName schemaName, IDbConnection conn, NameFormat nameFormat, Names names);\r
47 \r
48         protected string BuildForeignKey(IDictionary<string, ColumnName> table, string key)\r
49         {\r
50             string[] keys = key.Split(',');\r
51             StringBuilder result = new StringBuilder();\r
52             foreach (string lookupKey in keys)\r
53             {\r
54                 if (result.Length != 0)\r
55                     result.Append(',');\r
56                 result.Append(table[lookupKey].PropertyName);\r
57             }\r
58 \r
59             return result.ToString();\r
60         }\r
61 \r
62         /// <summary>\r
63         /// Loads the foreign key.\r
64         /// </summary>\r
65         /// <param name="schema">The schema.</param>\r
66         /// <param name="table">The table.</param>\r
67         /// <param name="columnName">Name of the column.</param>\r
68         /// <param name="tableName">Name of the table.</param>\r
69         /// <param name="tableSchema">The table schema.</param>\r
70         /// <param name="referencedColumnName">Name of the referenced column.</param>\r
71         /// <param name="referencedTableName">Name of the referenced table.</param>\r
72         /// <param name="referencedTableSchema">The referenced table schema.</param>\r
73         /// <param name="constraintName">Name of the constraint.</param>\r
74         /// <param name="nameFormat">The name format.</param>\r
75         /// <param name="names">The names.</param>\r
76         protected virtual void LoadForeignKey(Database schema, Table table, string columnName, string tableName, string tableSchema,\r
77             string referencedColumnName, string referencedTableName, string referencedTableSchema,\r
78             string constraintName,\r
79             NameFormat nameFormat, Names names)\r
80         {\r
81             var foreignKey = BuildForeignKey(names.ColumnsNames[tableName], columnName);\r
82             var reverseForeignKey = BuildForeignKey(names.ColumnsNames[referencedTableName], referencedColumnName);\r
83 \r
84             var associationName = CreateAssociationName(tableName, tableSchema,\r
85                 referencedTableName, referencedTableSchema, constraintName, foreignKey, nameFormat);\r
86 \r
87             //both parent and child table get an [Association]\r
88             var assoc = new Association();\r
89             assoc.IsForeignKey = true;\r
90             assoc.Name = constraintName;\r
91             assoc.Type = null;\r
92             assoc.ThisKey = foreignKey;\r
93             assoc.OtherKey = reverseForeignKey;\r
94             assoc.Member = associationName.ManyToOneMemberName;\r
95             assoc.CardinalitySpecified = false;\r
96             // TODO: generate assoc.Cardinality?\r
97             table.Type.Associations.Add(assoc);\r
98 \r
99             //and insert the reverse association:\r
100             var reverseAssociation = new Association();\r
101             reverseAssociation.Name = constraintName;\r
102             reverseAssociation.Type = table.Type.Name;\r
103             reverseAssociation.Member = associationName.OneToManyMemberName;\r
104             reverseAssociation.CardinalitySpecified = false;\r
105             // TODO: generate reverseAssociation.Cardinality?\r
106             reverseAssociation.ThisKey = reverseForeignKey;\r
107             reverseAssociation.OtherKey = foreignKey;\r
108             reverseAssociation.DeleteRule = "NO ACTION";\r
109 \r
110             string referencedFullDbName = GetFullDbName(referencedTableName, referencedTableSchema);\r
111             var referencedTable = schema.Tables.FirstOrDefault(t => referencedFullDbName == t.Name);\r
112             if (referencedTable == null)\r
113             {\r
114                 //try case-insensitive match \r
115                 //reason: MySql's Key_Column_Usage table contains both 'Northwind' and 'northwind'\r
116                 referencedTable = schema.Tables.FirstOrDefault(t => referencedFullDbName.ToLower() == t.Name.ToLower());\r
117             }\r
118 \r
119             if (referencedTable == null)\r
120             {\r
121                 ReportForeignKeyError(schema, referencedFullDbName);\r
122             }\r
123             else\r
124             {\r
125                 referencedTable.Type.Associations.Add(reverseAssociation);\r
126                 assoc.Type = referencedTable.Type.Name;\r
127             }\r
128         }\r
129 \r
130         /// <summary>\r
131         /// Reports a foreign key error.\r
132         /// </summary>\r
133         /// <param name="schema">The schema.</param>\r
134         /// <param name="referencedTableFull">The referenced table full.</param>\r
135         private void ReportForeignKeyError(Database schema, string referencedTableFull)\r
136         {\r
137             var tablesMap = schema.Tables.ToDictionary(t => t.Name.ToLower());\r
138             var referencedTableFullL = referencedTableFull.ToLower();\r
139 \r
140             string msg = "ERROR L91: parent table not found: " + referencedTableFull;\r
141             Table matchedTable;\r
142             if (tablesMap.TryGetValue(referencedTableFullL, out matchedTable))\r
143             {\r
144                 //case problems arise from various reasons,\r
145                 //e.g. different capitalization on WIndows vs Linux,\r
146                 //bugs in DbLinq etc\r
147                 msg += " - however, schema lists a table spelled as " + matchedTable.Name;\r
148             }\r
149             WriteErrorLine(msg);\r
150         }\r
151     }\r
152 }\r