New tests, updates
[mono.git] / mcs / class / System.Data / System.Data / XmlSchemaDataImporter.cs
old mode 100755 (executable)
new mode 100644 (file)
index 7ceab7d..24950a7
@@ -7,6 +7,7 @@
 // (C)2004 Novell Inc.
 //
 //
+// ***** The design note became somewhat obsolete. Should be rewritten. *****
 //
 // * Design Notes
 //
@@ -40,7 +41,6 @@
 //     Local elements are converted into either a table or a column in
 //     the "context DataTable". Simple-typed element is not always converted
 //     into a DataColumn; if maxOccurs > 1, it will be converted as a table.
-//     (FIXME: Not implemented yet)
 //
 // ** Name Convention
 //
 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/_generating_dataset_relational_structure_from_xsd.asp
 //
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
 using System.Data;
@@ -143,35 +166,149 @@ using System.Xml.Schema;
 
 namespace System.Data
 {
+       internal class TableStructureCollection : CollectionBase
+       {
+               public void Add (TableStructure table)
+               {
+                       List.Add (table);
+               }
+
+               public TableStructure this [int i] {
+                       get { return List [i] as TableStructure; }
+               }
+
+               public TableStructure this [string name] {
+                       get {
+                               foreach (TableStructure ts in List)
+                                       if (ts.Table.TableName == name)
+                                               return ts;
+                               return null;
+                       }
+               }
+       }
+
+       internal class RelationStructureCollection : CollectionBase
+       {
+               public void Add (RelationStructure rel)
+               {
+                       List.Add (rel);
+               }
+
+               public RelationStructure this [int i] {
+                       get { return List [i] as RelationStructure; }
+               }
+
+               public RelationStructure this [string parent, string child] {
+                       get {
+                               foreach (RelationStructure rel in List)
+                                       if (rel.ParentTableName == parent && rel.ChildTableName == child)
+                                               return rel;
+                               return null;
+                       }
+               }
+       }
+
+       internal class TableStructure
+       {
+               public TableStructure (DataTable table)
+               {
+                       this.Table = table;
+               }
+
+               // The columns and orders which will be added to the context
+               // table (See design notes; Because of the ordinal problem)
+               public DataTable Table;
+               public Hashtable OrdinalColumns = new Hashtable ();
+               public ArrayList NonOrdinalColumns = new ArrayList ();
+               public DataColumn PrimaryKey;
+
+               public bool ContainsColumn (string name)
+               {
+                       foreach (DataColumn col in NonOrdinalColumns)
+                               if (col.ColumnName == name)
+                                       return true;
+                       foreach (DataColumn col in OrdinalColumns.Keys)
+                               if (col.ColumnName == name)
+                                       return true;
+                       return false;
+               }
+       }
+
+       internal class RelationStructure
+       {
+               public string ExplicitName;
+               public string ParentTableName;
+               public string ChildTableName;
+               public string ParentColumnName;
+               public string ChildColumnName;
+               public bool IsNested;
+               public bool CreateConstraint;
+       }
+
+       internal class ConstraintStructure
+       {
+               public readonly string TableName;
+               public readonly string [] Columns;
+               public readonly bool [] IsAttribute;
+               public readonly string ConstraintName;
+               public readonly bool IsPrimaryKey;
+               public readonly string ReferName;
+               public readonly bool IsNested;
+               public readonly bool IsConstraintOnly;
+
+               public ConstraintStructure (string tname, string [] cols, bool [] isAttr, string cname, bool isPK, string refName, bool isNested, bool isConstraintOnly)
+               {
+                       TableName = tname;
+                       Columns = cols;
+                       IsAttribute = isAttr;
+                       ConstraintName = XmlHelper.Decode (cname);
+                       IsPrimaryKey = isPK;
+                       ReferName = refName;
+                       IsNested = isNested;
+                       IsConstraintOnly = isConstraintOnly;
+               }
+       }
+
        internal class XmlSchemaDataImporter
        {
+               static readonly XmlSchemaDatatype schemaIntegerType;
+               static readonly XmlSchemaDatatype schemaDecimalType;
+               static readonly XmlSchemaComplexType schemaAnyType;
+
                static XmlSchemaDataImporter ()
                {
                        XmlSchema s = new XmlSchema ();
                        XmlSchemaAttribute a = new XmlSchemaAttribute ();
                        a.Name = "foo";
-                       // FIXME: mcs looks to have a bug around static 
-                       // reference resolution. XmlSchema.Namespace should work.
-                       a.SchemaTypeName = new XmlQualifiedName ("integer", System.Xml.Schema.XmlSchema.Namespace);
+                       a.SchemaTypeName = new XmlQualifiedName ("integer", XmlSchema.Namespace);
                        s.Items.Add (a);
+                       XmlSchemaAttribute b = new XmlSchemaAttribute ();
+                       b.Name = "bar";
+                       b.SchemaTypeName = new XmlQualifiedName ("decimal", XmlSchema.Namespace);
+                       s.Items.Add (b);
                        XmlSchemaElement e = new XmlSchemaElement ();
                        e.Name = "bar";
                        s.Items.Add (e);
                        s.Compile (null);
+#if NET_2_0
+                       schemaIntegerType = ((XmlSchemaSimpleType) a.AttributeSchemaType).Datatype;
+                       schemaDecimalType = ((XmlSchemaSimpleType) b.AttributeSchemaType).Datatype;
+                       schemaAnyType = e.ElementSchemaType as XmlSchemaComplexType;
+#else
                        schemaIntegerType = a.AttributeType as XmlSchemaDatatype;
+                       schemaDecimalType = b.AttributeType as XmlSchemaDatatype;
                        schemaAnyType = e.ElementType as XmlSchemaComplexType;
+#endif
                }
 
-               static readonly XmlSchemaDatatype schemaIntegerType;
-               static readonly XmlSchemaComplexType schemaAnyType;
+               #region Fields
 
                DataSet dataset;
-               DataTable table;
+               bool forDataSet;
                XmlSchema schema;
 
-               Hashtable nameToComponentMap = new Hashtable ();
-               Hashtable relationParentColumns = new Hashtable ();
-               Hashtable nestedRelationColumns = new Hashtable ();
+               ArrayList relations = new ArrayList ();
+               Hashtable reservedConstraints = new Hashtable ();
 
                // such element that has an attribute msdata:IsDataSet="true"
                XmlSchemaElement datasetElement;
@@ -182,86 +319,77 @@ namespace System.Data
                // import target elements
                ArrayList targetElements = new ArrayList ();
 
-               // The DataTable currently processing
-               DataTable contextTable;
+               TableStructure currentTable;
 
-               // The columns and orders which will be added to the context
-               // table (See design notes; Because of the ordinal problem)
-               // [DataColumn] -> int ordinal
-               Hashtable currentOrdinalColumns = new Hashtable ();
-               ArrayList currentNonOrdinalColumns = new ArrayList ();
-               ArrayList currentColumnNames = new ArrayList ();
-
-               // used to determine if it handles the only one element as DataSet
-               int globalElementCount;
+               #endregion
 
                // .ctor()
 
-               public XmlSchemaDataImporter (DataSet dataset, XmlReader reader)
+               public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
                {
                        this.dataset = dataset;
-                       ProcessMain (reader);
-               }
-
-               public XmlSchemaDataImporter (DataTable table, XmlReader reader)
-               {
-                       this.table = table;
-                       ProcessMain (reader);
-               }
-
-               // types
-               struct RelationMapping
-               {
-                       public RelationMapping (XmlSchemaElement el, DataTable table, DataColumn col)
-                       {
-                               Element = el;
-                               Table = table;
-                               Column = col;
-                       }
-
-                       public XmlSchemaElement Element;
-                       public DataTable Table;
-                       public DataColumn Column;
-               }
-
-               // properties
-
-               public DataSet DataSet {
-                       get { return dataset; }
-               }
-
-               public DataTable DataTable {
-                       get { return table; }
-               }
-
-               public XmlSchema XmlSchema {
-                       get { return schema; }
+                       this.forDataSet = forDataSet;
+                       dataset.DataSetName = "NewDataSet"; // Initialize always
+                       schema = XmlSchema.Read (reader, null);
+                       if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == XmlSchema.Namespace)
+                               reader.ReadEndElement ();
+                       schema.Compile (null);
                }
 
                // methods
 
-               private void ProcessMain (XmlReader reader)
+               public void Process ()
                {
-                       schema = XmlSchema.Read (reader, null);
-                       // FIXME: Just XmlSchema.Namespace should work (mcs bug)
-                       if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "schema" && reader.NamespaceURI == System.Xml.Schema.XmlSchema.Namespace)
-                               reader.ReadEndElement ();
-                       schema.Compile (null);
                        if (schema.Id != null)
-                               dataset.DataSetName = schema.Id; // default
+                               dataset.DataSetName = schema.Id; // default. Overridable by "DataSet element"
                        dataset.Namespace = schema.TargetNamespace;
 
+                       // Find dataset element
+                       foreach (XmlSchemaObject obj in schema.Items) {
+                               XmlSchemaElement el = obj as XmlSchemaElement;
+                               if (el != null) {
+                                       if (datasetElement == null &&
+                                               IsDataSetElement (el))
+                                               datasetElement = el;
+#if NET_2_0
+                                       if (el.ElementSchemaType is XmlSchemaComplexType &&
+                                           el.ElementSchemaType != schemaAnyType)
+#else
+                                       if (el.ElementType is XmlSchemaComplexType &&
+                                           el.ElementType != schemaAnyType)
+#endif
+                                               targetElements.Add (obj);
+                               }
+                       }
+
+                       // make reservation of identity constraints
+                       if (datasetElement != null) {
+                               // keys/uniques.
+                               foreach (XmlSchemaObject obj in datasetElement.Constraints)
+                                       if (! (obj is XmlSchemaKeyref))
+                                               ReserveSelfIdentity ((XmlSchemaIdentityConstraint) obj);
+                               // keyrefs.
+                               foreach (XmlSchemaObject obj in datasetElement.Constraints)
+                                       if (obj is XmlSchemaKeyref)
+                                               ReserveRelationIdentity (datasetElement, (XmlSchemaKeyref) obj);
+                       }
+
                        foreach (XmlSchemaObject obj in schema.Items) {
                                if (obj is XmlSchemaElement) {
                                        XmlSchemaElement el = obj as XmlSchemaElement;
+#if NET_2_0
+                                       if (el.ElementSchemaType is XmlSchemaComplexType &&
+                                           el.ElementSchemaType != schemaAnyType)
+#else
                                        if (el.ElementType is XmlSchemaComplexType &&
-el.ElementType != schemaAnyType)
+                                           el.ElementType != schemaAnyType)
+#endif
                                                targetElements.Add (obj);
                                }
                        }
 
                        // This collection will grow up while processing elements.
-                       globalElementCount = targetElements.Count;
+                       int globalElementCount = targetElements.Count;
 
                        for (int i = 0; i < globalElementCount; i++)
                                ProcessGlobalElement ((XmlSchemaElement) targetElements [i]);
@@ -280,30 +408,26 @@ el.ElementType != schemaAnyType)
                                // Handle constraints in the DataSet element. First keys.
                                foreach (XmlSchemaObject obj in datasetElement.Constraints)
                                        if (! (obj is XmlSchemaKeyref))
-                                               ProcessParentKey ((XmlSchemaIdentityConstraint) obj);
+                                               ProcessSelfIdentity (reservedConstraints [obj] as ConstraintStructure);
                                // Then keyrefs.
                                foreach (XmlSchemaObject obj in datasetElement.Constraints)
                                        if (obj is XmlSchemaKeyref)
-                                               ProcessReferenceKey (datasetElement, (XmlSchemaKeyref) obj);
+                                               ProcessRelationIdentity (datasetElement, reservedConstraints [obj] as ConstraintStructure);
                        }
+
+                       foreach (RelationStructure rs in this.relations)
+                               dataset.Relations.Add (GenerateRelationship (rs));
                }
 
-               private void ProcessGlobalElement (XmlSchemaElement el)
+               private bool IsDataSetElement (XmlSchemaElement el)
                {
-                       // If it is already registered (by resolving reference
-                       // in previously-imported elements), just ignore.
-                       if (nameToComponentMap.Contains (el))
-                               return;
-
-                       // Check if element is DataSet element
                        if (el.UnhandledAttributes != null) {
                                foreach (XmlAttribute attr in el.UnhandledAttributes) {
                                        if (attr.LocalName == "IsDataSet" &&
                                                attr.NamespaceURI == XmlConstants.MsdataNamespace) {
                                                switch (attr.Value) {
                                                case "true": // case sensitive
-                                                       ProcessDataSetElement (el);
-                                                       return;
+                                                       return true;
                                                case "false":
                                                        break;
                                                default:
@@ -313,26 +437,71 @@ el.ElementType != schemaAnyType)
                                }
                        }
 
-                       // FIXME: It should be more simple. It is likely to occur when the type was not resulted in a DataTable
-
-                       // Read the design notes for detail... it is very complicated.
+                       if (schema.Elements.Count != 1)
+                               return false;
+                       if (!(el.SchemaType is XmlSchemaComplexType))
+                               return false;
+                       XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
+                       if (ct.AttributeUses.Count > 0)
+                               return false;
+                       XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
+                       if (gb == null || gb.Items.Count == 0)
+                               return false;
+                       foreach (XmlSchemaParticle p in gb.Items) {
+                               if (ContainsColumn (p))
+                                       return false;
+                       }
+                       return true;
+               }
 
-                       if (globalElementCount == 1 && el.SchemaType is XmlSchemaComplexType) {
-                               XmlSchemaComplexType ct = (XmlSchemaComplexType) el.SchemaType;
-                               XmlSchemaGroupBase gb = ct.ContentTypeParticle as XmlSchemaGroupBase;
-                               XmlSchemaElement cpElem = gb != null && gb.Items.Count > 0 ? gb.Items [0] as XmlSchemaElement : null;
-                               XmlSchemaComplexType ct2 = cpElem != null ? cpElem.ElementType as XmlSchemaComplexType : null;
-                               // What a complex condition!!!!!!!!!
-                               if (ct2 != null && ct != schemaAnyType && ct.AttributeUses.Count == 0) {
-                                       ProcessDataSetElement (el);
-                                       return;
-                               }
+               private bool ContainsColumn (XmlSchemaParticle p)
+               {
+                       XmlSchemaElement el = p as XmlSchemaElement;
+                       if (el != null) {
+                               XmlSchemaComplexType ct = null;
+#if NET_2_0
+                               ct = el.ElementSchemaType as XmlSchemaComplexType;
+#else
+                               ct = el.ElementType as XmlSchemaComplexType;
+#endif
+                               if (ct == null || ct == schemaAnyType)
+                                       return true; // column element
+                               if (ct.AttributeUses.Count > 0)
+                                       return false; // table element
+                               if (ct.ContentType == XmlSchemaContentType.TextOnly)
+                                       return true; // column element
+                               else
+                                       return false; // table element
+                       }
+                       XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
+                       for (int i = 0; i < gb.Items.Count; i++) {
+                               if (ContainsColumn ((XmlSchemaParticle) gb.Items [i]))
+                                       return true;
                        }
+                       return false;
+               }
 
+               private void ProcessGlobalElement (XmlSchemaElement el)
+               {
+                       // If it is already registered (by resolving reference
+                       // in previously-imported elements), just ignore.
+                       if (dataset.Tables.Contains (el.QualifiedName.Name))
+                               return;
 
                        // If type is not complex, just skip this element
+#if NET_2_0
+                       if (! (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType))
+#else
                        if (! (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType))
+#endif
+                               return;
+
+                       if (IsDataSetElement (el)) {
+                               ProcessDataSetElement (el);
                                return;
+                       }
+                       else
+                               dataset.Locale = CultureInfo.CurrentCulture;
 
                        // Register as a top-level element
                        topLevelElements.Add (el);
@@ -345,9 +514,15 @@ el.ElementType != schemaAnyType)
                        dataset.DataSetName = el.Name;
                        this.datasetElement = el;
 
-                       // Search for msdata:Locale attribute
+                       // Search for locale attributes
+                       bool useCurrent = false;
                        if (el.UnhandledAttributes != null) {
                                foreach (XmlAttribute attr in el.UnhandledAttributes) {
+#if NET_2_0
+                                       if (attr.LocalName == "UseCurrentLocale" &&
+                                               attr.NamespaceURI == XmlConstants.MsdataNamespace)
+                                               useCurrent = true;
+#endif
                                        if (attr.LocalName == "Locale" &&
                                                attr.NamespaceURI == XmlConstants.MsdataNamespace) {
                                                CultureInfo ci = new CultureInfo (attr.Value);
@@ -355,9 +530,18 @@ el.ElementType != schemaAnyType)
                                        }
                                }
                        }
+#if NET_2_0
+                       if (!useCurrent && !dataset.LocaleSpecified) // then set current culture instance _explicitly_
+                               dataset.Locale = CultureInfo.CurrentCulture;
+#endif
 
                        // Process content type particle (and create DataTable)
-                       XmlSchemaComplexType ct = el.ElementType as XmlSchemaComplexType;
+                       XmlSchemaComplexType ct = null;
+#if NET_2_0
+                       ct = el.ElementSchemaType as XmlSchemaComplexType;
+#else
+                       ct = el.ElementType as XmlSchemaComplexType;
+#endif
                        XmlSchemaParticle p = ct != null ? ct.ContentTypeParticle : null;
                        if (p != null)
                                HandleDataSetContentTypeParticle (p);
@@ -367,7 +551,11 @@ el.ElementType != schemaAnyType)
                {
                        XmlSchemaElement el = p as XmlSchemaElement;
                        if (el != null) {
+#if NET_2_0
+                               if (el.ElementSchemaType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
+#else
                                if (el.ElementType is XmlSchemaComplexType && el.RefName != el.QualifiedName)
+#endif
                                        ProcessDataTableElement (el);
                        }
                        else if (p is XmlSchemaGroupBase) {
@@ -378,40 +566,35 @@ el.ElementType != schemaAnyType)
 
                private void ProcessDataTableElement (XmlSchemaElement el)
                {
+                       string tableName = XmlHelper.Decode (el.QualifiedName.Name);
                        // If it is already registered, just ignore.
-                       if (nameToComponentMap.Contains (el))
+                       if (dataset.Tables.Contains (tableName))
                                return;
 
-                       // On reading schema into DataTable (not into DataSet)
-                       // This operation should be an error.
-                       if (table != null)
-                               throw new InvalidOperationException ("More than one table is defined in this schema");
-                       else if (contextTable != null)
-                               throw new InvalidOperationException ("Should not occur. Request for data table definition was found on defining another data table.");
-
-                       string name = el.QualifiedName.Name;
+                       DataTable table = new DataTable (tableName);
+                       table.Namespace = el.QualifiedName.Namespace;
+                       TableStructure oldTable = currentTable;
+                       currentTable = new TableStructure (table);
 
-                       currentOrdinalColumns.Clear ();
-                       currentNonOrdinalColumns.Clear ();
-                       currentColumnNames.Clear ();
-                       contextTable = new DataTable ();
-                       contextTable.TableName = name;
-
-                       dataset.Tables.Add (contextTable);
-                       nameToComponentMap.Add (name, el);
+                       dataset.Tables.Add (table);
 
                        // Find Locale
                        if (el.UnhandledAttributes != null) {
                                foreach (XmlAttribute attr in el.UnhandledAttributes) {
                                        if (attr.LocalName == "Locale" &&
                                                attr.NamespaceURI == XmlConstants.MsdataNamespace)
-                                               contextTable.Locale = new CultureInfo (attr.Value);
+                                               table.Locale = new CultureInfo (attr.Value);
                                }
                        }
 
-                       // Handle complex type (NOTE: It is or should be 
+                       // Handle complex type (NOTE: It is (or should be)
                        // impossible the type is other than complex type).
-                       XmlSchemaComplexType ct = (XmlSchemaComplexType) el.ElementType;
+                       XmlSchemaComplexType ct = null;
+#if NET_2_0
+                       ct = (XmlSchemaComplexType) el.ElementSchemaType;
+#else
+                       ct = (XmlSchemaComplexType) el.ElementType;
+#endif
 
                        // Handle attributes
                        foreach (DictionaryEntry de in ct.AttributeUses)
@@ -427,68 +610,77 @@ el.ElementType != schemaAnyType)
                        // Handle simple content
                        switch (ct.ContentType) {
                        case XmlSchemaContentType.TextOnly:
-                       case XmlSchemaContentType.Mixed:
+//                     case XmlSchemaContentType.Mixed:
                                // LAMESPEC: When reading from XML Schema, it maps to "_text", while on the data inference, it is mapped to "_Text" (case ignorant).
                                string simpleName = el.QualifiedName.Name + "_text";
                                DataColumn simple = new DataColumn (simpleName);
+                               simple.Namespace = el.QualifiedName.Namespace;
                                simple.AllowDBNull = (el.MinOccurs == 0);
                                simple.ColumnMapping = MappingType.SimpleContent;
                                simple.DataType = ConvertDatatype (ct.Datatype);
-                               currentNonOrdinalColumns.Add (simple);
-                               currentColumnNames.Add (simpleName);
+                               currentTable.NonOrdinalColumns.Add (simple);
                                break;
                        }
 
                        // add columns to the table in specified order 
                        // (by msdata:Ordinal attributes)
                        SortedList sd = new SortedList ();
-                       foreach (DictionaryEntry de in currentOrdinalColumns)
+                       foreach (DictionaryEntry de in currentTable.OrdinalColumns)
                                sd.Add (de.Value, de.Key);
                        foreach (DictionaryEntry de in sd)
-                               contextTable.Columns.Add ((DataColumn) de.Value);
-                       foreach (DataColumn dc in currentNonOrdinalColumns)
-                               contextTable.Columns.Add (dc);
-
-                       HandlePopulatedRelationship (el);
+                               table.Columns.Add ((DataColumn) de.Value);
+                       foreach (DataColumn dc in currentTable.NonOrdinalColumns)
+                               table.Columns.Add (dc);
 
-                       contextTable = null;
+                       currentTable = oldTable;
                }
 
-               private void HandlePopulatedRelationship (XmlSchemaElement el)
+               private DataRelation GenerateRelationship (RelationStructure rs)
                {
-                       // If there is a parent column, create DataRelation and pk
-                       DataColumn col = relationParentColumns [el] as DataColumn;
-                       if (col != null) {
-                               if (col.Table.PrimaryKey.Length > 0)
-                                       throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", contextTable.TableName));
-                               col.Table.PrimaryKey = new DataColumn [] {col};
+                       DataTable ptab = dataset.Tables [rs.ParentTableName];
+                       DataTable ctab = dataset.Tables [rs.ChildTableName];
 
-                               DataColumn child = new DataColumn ();
-                               child.ColumnName = col.ColumnName;
-                               child.Namespace = String.Empty; // don't copy
-                               child.ColumnMapping = MappingType.Hidden;
-                               child.DataType = col.DataType;
-                               contextTable.Columns.Add (child);
+                       DataRelation rel ;
+                       string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
 
-                               DataRelation rel = new DataRelation (col.Table.TableName + '_' + child.Table.TableName, col, child);
-                               if (nestedRelationColumns.ContainsValue (col))
-                                       rel.Nested = true;
-                               dataset.Relations.Add (rel);
+                       // Annotation Relations belonging to a DataSet can contain multiple colnames
+                       // in parentkey and childkey.
+                       if (datasetElement != null) {
+                               String[] pcolnames = rs.ParentColumnName.Split (null);
+                               String[] ccolnames = rs.ChildColumnName.Split (null);
+
+                               DataColumn[] pcol = new DataColumn [pcolnames.Length];
+                               for (int i=0; i<pcol.Length; ++i)
+                                       pcol [i] = ptab.Columns [XmlHelper.Decode (pcolnames [i])];
+
+                               DataColumn[] ccol = new DataColumn [ccolnames.Length];
+                               for (int i=0; i < ccol.Length; ++i) {
+                                       ccol [i] = ctab.Columns [XmlHelper.Decode (ccolnames [i])];
+                                       if (ccol [i] == null)
+                                               ccol [i] = CreateChildColumn (pcol [i], ctab);
+                               }
+                               rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
+                       } else {
+                               DataColumn pcol = ptab.Columns [XmlHelper.Decode (rs.ParentColumnName)];
+                               DataColumn ccol = ctab.Columns [XmlHelper.Decode (rs.ChildColumnName)];
+                               if (ccol == null) 
+                                       ccol = CreateChildColumn (pcol, ctab);
+                               rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
                        }
+                       rel.Nested = rs.IsNested;
+                       if (rs.CreateConstraint)
+                               rel.ParentTable.PrimaryKey = rel.ParentColumns;
+                       return rel;
                }
 
-               /*
-               private void HandleAttributes (XmlSchemaObjectCollection atts)
+               private DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
                {
-                       foreach (XmlSchemaObject attrObj in atts) {
-                               XmlSchemaAttribute attr = attrObj as XmlSchemaAttribute;
-                               if (attr != null)
-                                       ImportColumnAttribute (attr);
-                               else
-                                       HandleAttributes (((XmlSchemaAttributeGroup) attrObj).Attributes);
-                       }
+                       DataColumn col = childTable.Columns.Add (parentColumn.ColumnName, 
+                                                               parentColumn.DataType);
+                       col.Namespace = String.Empty;
+                       col.ColumnMapping = MappingType.Hidden;
+                       return col;
                }
-               */
 
                private void ImportColumnGroupBase (XmlSchemaElement parent, XmlSchemaGroupBase gb)
                {
@@ -496,13 +688,16 @@ el.ElementType != schemaAnyType)
                                XmlSchemaElement el = p as XmlSchemaElement;
                                if (el != null)
                                        ImportColumnElement (parent, el);
-                               else
-                                       ImportColumnGroupBase (parent, (XmlSchemaGroupBase) gb);
+                               else if (p is XmlSchemaGroupBase)
+                                       ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
+                               // otherwise p is xs:any
                        }
                }
 
                private XmlSchemaDatatype GetSchemaPrimitiveType (object type)
                {
+                       if (type is XmlSchemaComplexType)
+                               return null; // It came here, so that maybe it is xs:anyType
                        XmlSchemaDatatype dt = type as XmlSchemaDatatype;
                        if (dt == null && type != null)
                                dt = ((XmlSchemaSimpleType) type).Datatype;
@@ -515,7 +710,12 @@ el.ElementType != schemaAnyType)
                        DataColumn col = new DataColumn ();
                        col.ColumnName = attr.QualifiedName.Name;
                        col.Namespace = attr.QualifiedName.Namespace;
-                       XmlSchemaDatatype dt = GetSchemaPrimitiveType (attr.AttributeType);
+                       XmlSchemaDatatype dt = null;
+#if NET_2_0
+                       dt = GetSchemaPrimitiveType (((XmlSchemaSimpleType) attr.AttributeSchemaType).Datatype);
+#else
+                       dt = GetSchemaPrimitiveType (attr.AttributeType);
+#endif
                        // This complicated check comes from the fact that
                        // MS.NET fails to map System.Object to anyType (that
                        // will cause ReadTypedObject() fail on XmlValidatingReader).
@@ -535,10 +735,15 @@ el.ElementType != schemaAnyType)
                        if (attr.Use == XmlSchemaUse.Required)
                                col.AllowDBNull = false;
 
+#if NET_2_0
+                       FillFacet (col, attr.AttributeSchemaType as XmlSchemaSimpleType);
+#else
                        FillFacet (col, attr.AttributeType as XmlSchemaSimpleType);
+#endif
 
                        // Call this method after filling the name
-                       ImportColumnCommon (attr, attr.QualifiedName, col);
+                       ImportColumnMetaInfo (attr, attr.QualifiedName, col);
+                       AddColumn (col);
                }
 
                private void ImportColumnElement (XmlSchemaElement parent, XmlSchemaElement el)
@@ -549,25 +754,21 @@ el.ElementType != schemaAnyType)
                        col.DefaultValue = GetElementDefaultValue (el);
                        col.AllowDBNull = (el.MinOccurs == 0);
 
-                       // FIXME: need to handle array item for maxOccurs > 1
-                       if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType) {
-                               // import new table and set this column as reference.
+#if NET_2_0
+                       if (el.ElementSchemaType is XmlSchemaComplexType && el.ElementSchemaType != schemaAnyType)
+#else
+                       if (el.ElementType is XmlSchemaComplexType && el.ElementType != schemaAnyType)
+#endif
                                FillDataColumnComplexElement (parent, el, col);
-                               // If the element is not referenced one, the element will be handled later.
-                               if (el.RefName == XmlQualifiedName.Empty)
-                                       targetElements.Add (el);
-                       }
+                       else if (el.MaxOccurs != 1)
+                               FillDataColumnRepeatedSimpleElement (parent, el, col);
                        else
                                FillDataColumnSimpleElement (el, col);
-
-                       // Call this method after filling the name
-                       ImportColumnCommon (el, el.QualifiedName, col);
                }
 
                // common process for element and attribute
-               private void ImportColumnCommon (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
+               private void ImportColumnMetaInfo (XmlSchemaAnnotated obj, XmlQualifiedName name, DataColumn col)
                {
-                       int ordinal = -1;
                        if (obj.UnhandledAttributes != null) {
                                foreach (XmlAttribute attr in obj.UnhandledAttributes) {
                                        if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
@@ -585,52 +786,85 @@ el.ElementType != schemaAnyType)
                                        case XmlConstants.AutoIncrementSeed:
                                                col.AutoIncrementSeed = int.Parse (attr.Value);
                                                break;
+                                       case XmlConstants.AutoIncrementStep:
+                                               col.AutoIncrementStep = int.Parse (attr.Value);
+                                               break;
+                                       case XmlConstants.ReadOnly:
+                                               col.ReadOnly = XmlConvert.ToBoolean (attr.Value);
+                                               break;
                                        case XmlConstants.Ordinal:
-                                               ordinal = int.Parse (attr.Value);
+                                               int ordinal = int.Parse (attr.Value);
                                                break;
                                        }
                                }
                        }
-                       if (ordinal < 0)
-                               currentNonOrdinalColumns.Add (col);
-                       else
-                               currentOrdinalColumns.Add (col, ordinal);
-                       currentColumnNames.Add (col.ColumnName);
                }
 
-               [MonoTODO]
                private void FillDataColumnComplexElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
                {
                        if (targetElements.Contains (el))
                                return; // do nothing
 
-                       FillUniqueKeyColumn (parent, el, col);
+                       string elName = XmlHelper.Decode (el.QualifiedName.Name);
+                       if (elName == dataset.DataSetName)
+                               // Well, why it is ArgumentException :-?
+                               throw new ArgumentException ("Nested element must not have the same name as DataSet's name.");
+
+                       if (el.Annotation != null)
+                               HandleAnnotations (el.Annotation, true);
+                       // If xsd:keyref xsd:key for this table exists, then don't add
+                       // relation here manually.
+                       else if (!DataSetDefinesKey (elName)) {
+                               AddParentKeyColumn (parent, el, col);
+
+                               RelationStructure rel = new RelationStructure ();
+                               rel.ParentTableName = XmlHelper.Decode (parent.QualifiedName.Name);
+                               rel.ChildTableName = elName;
+                               rel.ParentColumnName = col.ColumnName;
+                               rel.ChildColumnName = col.ColumnName;
+                               rel.CreateConstraint = true;
+                               rel.IsNested = true;
+                               relations.Add (rel);
+                       }
 
-                       // For nested relations, DataRelation.Nested will be filled later
-                       if (el.SchemaType != el.ElementType)
-                               nestedRelationColumns [el] = col;
+                       // If the element is not referenced one, the element will be handled later.
+                       if (el.RefName == XmlQualifiedName.Empty)
+                               ProcessDataTableElement (el);
 
-                       // FIXME: Handle "relationship" annotations
-//                     if (el.Annotation != null)
-//                             HandleAnnotations (el.Annotation, true);
+               }
 
-                       relationParentColumns.Add (el, col);
+               private bool DataSetDefinesKey (string name)
+               {
+                       foreach (ConstraintStructure c in reservedConstraints.Values)
+                               if (c.TableName == name && (c.IsPrimaryKey || c.IsNested))
+                                       return true;
+                       return false;
                }
 
-               private void FillUniqueKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
+               private void AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
                {
+                       if (currentTable.PrimaryKey != null)
+                               return;
+
                        // check name identity
-                       string name = parent.QualifiedName.Name + "_Id";
-                       if (currentColumnNames.Contains (name))
-                               throw new DataException (String.Format ("There is already a column that has the same name: {0}", name));
+                       string name = XmlHelper.Decode (parent.QualifiedName.Name) + "_Id";
+                       int count = 0;
+                       while (currentTable.ContainsColumn (name))
+                               name = String.Format ("{0}_{1}", name, count++);
+                       // check existing primary key
+                       if (currentTable.Table.PrimaryKey.Length > 0)
+                               throw new DataException (String.Format ("There is already primary key columns in the table \"{0}\".", currentTable.Table.TableName));
 
                        col.ColumnName = name;
                        col.ColumnMapping = MappingType.Hidden;
                        col.Namespace = parent.QualifiedName.Namespace;
                        col.DataType = typeof (int);
-                       col.Unique = true;
                        col.AutoIncrement = true;
                        col.AllowDBNull = false;
+
+                       ImportColumnMetaInfo (el, el.QualifiedName, col);
+                       AddColumn (col);
+                       currentTable.PrimaryKey = col;
                }
 
                private void FillDataColumnRepeatedSimpleElement (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
@@ -638,32 +872,72 @@ el.ElementType != schemaAnyType)
                        if (targetElements.Contains (el))
                                return; // do nothing
 
-                       FillUniqueKeyColumn (parent, el, col);
-                       // FIXME: confirm.
-                       // Those components will be handled later.
-                       targetElements.Add (el);
+                       AddParentKeyColumn (parent, el, col);
+                       DataColumn pkey = currentTable.PrimaryKey;
+
+                       string elName = XmlHelper.Decode (el.QualifiedName.Name);
+                       string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
 
                        DataTable dt = new DataTable ();
-                       dt.TableName = parent.QualifiedName.Name + "_" + el.QualifiedName.Name;
+                       dt.TableName = elName;
+                       dt.Namespace = el.QualifiedName.Namespace;
+                       // reference key column to parent
                        DataColumn cc = new DataColumn ();
-                       cc.ColumnName = el.QualifiedName.Name;
-                       cc.Namespace = dataset.Namespace;
+                       cc.ColumnName = parentName + "_Id";
+                       cc.Namespace = parent.QualifiedName.Namespace;
                        cc.ColumnMapping = MappingType.Hidden;
-                       cc.DataType = this.ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
+                       cc.DataType = typeof (int);
+
+                       // repeatable content simple element
+                       DataColumn cc2 = new DataColumn ();
+                       cc2.ColumnName = elName + "_Column";
+                       cc2.Namespace = el.QualifiedName.Namespace;
+                       cc2.ColumnMapping = MappingType.SimpleContent;
+                       cc2.AllowDBNull = false;
+#if NET_2_0
+                       cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
+#else
+                       cc2.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
+#endif
+
+                       dt.Columns.Add (cc2);
+                       dt.Columns.Add (cc);
+                       dataset.Tables.Add (dt);
+
+                       RelationStructure rel = new RelationStructure ();
+                       rel.ParentTableName = parentName;
+                       rel.ChildTableName = dt.TableName;
+                       rel.ParentColumnName = pkey.ColumnName;
+                       rel.ChildColumnName = cc.ColumnName;
+                       rel.IsNested = true;
+                       rel.CreateConstraint = true;
+                       relations.Add (rel);
                }
 
                private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
                {
-                       col.ColumnName = el.QualifiedName.Name;
+                       col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
                        col.Namespace = el.QualifiedName.Namespace;
                        col.ColumnMapping = MappingType.Element;
-                       XmlSchemaDatatype dt = el.ElementType as XmlSchemaDatatype;
-                       if (dt == null && el.ElementType != null) {
-                               XmlSchemaSimpleType st = el.ElementType as XmlSchemaSimpleType;
-                               dt = st != null ? st.Datatype : null;
-                       }
-                       col.DataType = ConvertDatatype (dt);
+#if NET_2_0
+                       col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementSchemaType));
+                       FillFacet (col, el.ElementSchemaType as XmlSchemaSimpleType);
+#else
+                       col.DataType = ConvertDatatype (GetSchemaPrimitiveType (el.ElementType));
                        FillFacet (col, el.ElementType as XmlSchemaSimpleType);
+#endif
+
+                       ImportColumnMetaInfo (el, el.QualifiedName, col);
+
+                       AddColumn (col);
+               }
+
+               private void AddColumn (DataColumn col)
+               {
+                       if (col.Ordinal < 0)
+                               currentTable.NonOrdinalColumns.Add (col);
+                       else
+                               currentTable.OrdinalColumns.Add (col, col.Ordinal);
                }
 
                private void FillFacet (DataColumn col, XmlSchemaSimpleType st)
@@ -672,7 +946,6 @@ el.ElementType != schemaAnyType)
                                return;
 
                        // Handle restriction facets
-                       // FIXME: how to handle list and union??
 
                        XmlSchemaSimpleTypeRestriction restriction = st == null ? null : st.Content as XmlSchemaSimpleTypeRestriction;
                        if (restriction == null)
@@ -689,12 +962,18 @@ el.ElementType != schemaAnyType)
                {
                        if (dt == null)
                                return typeof (string);
-                       else if (dt == schemaIntegerType)
+                       else if (dt.ValueType == typeof (decimal)) {
                                // LAMESPEC: MSDN documentation says it is based 
                                // on ValueType. However, in the System.Xml.Schema
                                // context, xs:integer is mapped to Decimal, while
                                // in DataSet context it is mapped to Int64.
-                               return typeof (long);
+                               if (dt == schemaDecimalType)
+                                       return typeof (decimal);
+                               else if (dt == schemaIntegerType)
+                                       return typeof (long);
+                               else
+                                       return typeof (ulong);
+                       }
                        else
                                return dt.ValueType;
                }
@@ -715,33 +994,29 @@ el.ElementType != schemaAnyType)
                        if (index > 0)
                                tableName = tableName.Substring (index + 1);
 
-                       return tableName;
+                       return XmlHelper.Decode (tableName);
                }
 
-               private void ProcessParentKey (XmlSchemaIdentityConstraint ic)
+               private void ReserveSelfIdentity (XmlSchemaIdentityConstraint ic)
                {
-                       // Basic concept came from XmlSchemaMapper.cs
-
                        string tableName = GetSelectorTarget (ic.Selector.XPath);
-                       
-                       DataTable table = dataset.Tables [tableName];
-                       if (table == null)
-                               throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
 
-                       DataColumn [] cols = new DataColumn [ic.Fields.Count];
+                       string [] cols = new string [ic.Fields.Count];
+                       bool [] isAttrSpec = new bool [cols.Length];
+
                        int i = 0;
                        foreach (XmlSchemaXPath Field in ic.Fields) {
                                string colName = Field.XPath;
-                               // FIXME: attribute XPath
+                               bool isAttr = colName.Length > 0 && colName [0] == '@';
                                int index = colName.LastIndexOf (':');
                                if (index > 0)
                                        colName = colName.Substring (index + 1);
+                               else if (isAttr)
+                                       colName = colName.Substring (1);
 
-                               DataColumn col = table.Columns [colName];
-                               if (col == null)
-                                       throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
-
-                               cols [i] = table.Columns [colName];
+                               colName = XmlHelper.Decode (colName);
+                               cols [i] = colName;
+                               isAttrSpec [i] = isAttr;
                                i++;
                        }
                        
@@ -763,50 +1038,136 @@ el.ElementType != schemaAnyType)
                                        }
                                }
                        }
-                       UniqueConstraint c = new UniqueConstraint (constraintName, cols, isPK);
-                       table.Constraints.Add (c);
+                       reservedConstraints.Add (ic,
+                               new ConstraintStructure (tableName, cols,
+                                       isAttrSpec, constraintName, isPK, null, false, false));
                }
 
-               private void ProcessReferenceKey (XmlSchemaElement element, XmlSchemaKeyref keyref)
+               private void ProcessSelfIdentity (ConstraintStructure c)
                {
                        // Basic concept came from XmlSchemaMapper.cs
 
-                       string tableName = GetSelectorTarget (keyref.Selector.XPath);
+                       string tableName = c.TableName;
+                       
+                       DataTable dt = dataset.Tables [tableName];
+                       if (dt == null) {
+                               if (forDataSet)
+                                       throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+                               else
+                                       // nonexistent table name. .NET ignores it for DataTable.ReadXmlSchema().
+                                       return;
+                       }
 
-                       DataColumn [] cols;
-                       DataTable table = dataset.Tables [tableName];
-                       if (table == null)
-                               throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+                       DataColumn [] cols = new DataColumn [c.Columns.Length];
+                       for (int i = 0; i < cols.Length; i++) {
+                               string colName = c.Columns [i];
+                               bool isAttr = c.IsAttribute [i];
+                               DataColumn col = dt.Columns [colName];
+                               if (col == null)
+                                       throw new DataException (String.Format ("Invalid XPath selection inside field. Cannot find: {0}", tableName));
+                               if (isAttr && col.ColumnMapping != MappingType.Attribute)
+                                       throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
+                               if (!isAttr && col.ColumnMapping != MappingType.Element)
+                                       throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
+
+                               cols [i] = dt.Columns [colName];
+                       }
+                       
+                       bool isPK = c.IsPrimaryKey;
+                       string constraintName = c.ConstraintName;
+                       dt.Constraints.Add (new UniqueConstraint (
+                               constraintName, cols, isPK));
+               }
 
-                       cols = new DataColumn [keyref.Fields.Count];
+               private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
+               {
+                       // Basic concept came from XmlSchemaMapper.cs
+
+                       string tableName = GetSelectorTarget (keyref.Selector.XPath);
+
+                       string [] cols = new string [keyref.Fields.Count];
+                       bool [] isAttrSpec = new bool [cols.Length];
                        int i = 0;
                        foreach (XmlSchemaXPath Field in keyref.Fields) {
-                               // FIXME: attribute XPath
                                string colName = Field.XPath;
+                               bool isAttr = colName.Length > 0 && colName [0] == '@';
                                int index = colName.LastIndexOf (':');
-                               if (index != -1)
+                               if (index > 0)
                                        colName = colName.Substring (index + 1);
+                               else if (isAttr)
+                                       colName = colName.Substring (1);
 
-                               cols [i] = table.Columns [colName];
+                               colName = XmlHelper.Decode (colName);
+                               cols [i] = colName;
+                               isAttrSpec [i] = isAttr;
                                i++;
                        }
-                       string name = keyref.Refer.Name;
+                       string constraintName = keyref.Name;
+                       bool isNested = false;
+                       bool isConstraintOnly = false;
+                       if (keyref.UnhandledAttributes != null) {
+                               foreach (XmlAttribute attr in keyref.UnhandledAttributes) {
+                                       if (attr.NamespaceURI != XmlConstants.MsdataNamespace)
+                                               continue;
+                                       switch (attr.LocalName) {
+                                       case XmlConstants.ConstraintName:
+                                               constraintName = attr.Value;
+                                               break;
+                                       case XmlConstants.IsNested:
+                                               if (attr.Value == "true")
+                                                       isNested = true;
+                                               break;
+                                       case XmlConstants.ConstraintOnly:
+                                               if (attr.Value == "true")
+                                                       isConstraintOnly = true;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       reservedConstraints.Add (keyref, new ConstraintStructure (
+                               tableName, cols, isAttrSpec, constraintName,
+                               false, keyref.Refer.Name, isNested, isConstraintOnly));
+               }
+
+               private void ProcessRelationIdentity (XmlSchemaElement element, ConstraintStructure c)
+               {
+                       // Basic concept came from XmlSchemaMapper.cs
+
+                       string tableName = c.TableName;
+
+                       DataColumn [] cols;
+                       DataTable dt = dataset.Tables [tableName];
+                       if (dt == null)
+                               throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", tableName));
+
+                       cols = new DataColumn [c.Columns.Length];
+                       for (int i = 0; i < cols.Length; i++) {
+                               string colName = c.Columns [i];
+                               bool isAttr = c.IsAttribute [i];
+                               DataColumn col = dt.Columns [colName];
+                               if (isAttr && col.ColumnMapping != MappingType.Attribute)
+                                       throw new DataException ("The XPath specified attribute field, but mapping type is not attribute.");
+                               if (!isAttr && col.ColumnMapping != MappingType.Element)
+                                       throw new DataException ("The XPath specified simple element field, but mapping type is not simple element.");
+                               cols [i] = col;
+                       }
+                       string name = c.ReferName;
                        // get the unique constraint for the releation
                        UniqueConstraint uniq = FindConstraint (name, element);
                        // generate the FK.
-                       ForeignKeyConstraint fkc = new ForeignKeyConstraint(keyref.Name, uniq.Columns, cols);
-                       table.Constraints.Add (fkc);
-                       // generate the relation.
-                       DataRelation rel = new DataRelation (keyref.Name, uniq.Columns, cols, false);
-                       if (keyref.UnhandledAttributes != null) {
-                               foreach (XmlAttribute attr in keyref.UnhandledAttributes)
-                                       if (attr.LocalName == "IsNested" && attr.Value == "true" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
-                                               rel.Nested = true;
-                       }
-                       rel.SetParentKeyConstraint (uniq);
-                       rel.SetChildKeyConstraint (fkc);
+                       ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
+                       dt.Constraints.Add (fkc);
+
+                       if (!c.IsConstraintOnly) {
+                               // generate the relation.
+                               DataRelation rel = new DataRelation (c.ConstraintName, uniq.Columns, cols, true);
+                               rel.Nested = c.IsNested;
+                               rel.SetParentKeyConstraint (uniq);
+                               rel.SetChildKeyConstraint (fkc);
 
-                       dataset.Relations.Add (rel);
+                               dataset.Relations.Add (rel);
+                       }
                }
 
                // get the unique constraint for the relation.
@@ -824,7 +1185,7 @@ el.ElementType != schemaAnyType)
                                        string tableName = GetSelectorTarget (c.Selector.XPath);
 
                                        // find the table in the dataset.
-                                       DataTable table = dataset.Tables [tableName];
+                                       DataTable dt = dataset.Tables [tableName];
 
                                        string constraintName = c.Name;
                                        // find if there is an attribute with the constraint name
@@ -833,7 +1194,7 @@ el.ElementType != schemaAnyType)
                                                foreach (XmlAttribute attr in c.UnhandledAttributes)
                                                        if (attr.LocalName == "ConstraintName" && attr.NamespaceURI == XmlConstants.MsdataNamespace)
                                                                constraintName = attr.Value;
-                                       return (UniqueConstraint) table.Constraints [constraintName];
+                                       return (UniqueConstraint) dt.Constraints [constraintName];
                                }
                        }
                        throw new DataException ("Target identity constraint was not found: " + name);
@@ -855,19 +1216,23 @@ el.ElementType != schemaAnyType)
 
                private void HandleRelationshipAnnotation (XmlElement el, bool nested)
                {
+                       string name = el.GetAttribute ("name");
                        string ptn = el.GetAttribute ("parent", XmlConstants.MsdataNamespace);
                        string ctn = el.GetAttribute ("child", XmlConstants.MsdataNamespace);
                        string pkn = el.GetAttribute ("parentkey", XmlConstants.MsdataNamespace);
                        string fkn = el.GetAttribute ("childkey", XmlConstants.MsdataNamespace);
 
-                       DataTable pt = dataset.Tables [ptn];
-                       DataTable ct = dataset.Tables [ctn];
-                       DataColumn pc = pt.Columns [pkn];
-                       DataColumn cc = ct.Columns [fkn];
-
-                       DataRelation rel = new DataRelation (el.GetAttribute ("name"), pc, cc, false);
-                       rel.Nested = nested;
-                       dataset.Relations.Add (rel);
+                       RelationStructure rel = new RelationStructure ();
+                       rel.ExplicitName = XmlHelper.Decode (name);
+                       rel.ParentTableName = XmlHelper.Decode (ptn);
+                       rel.ChildTableName = XmlHelper.Decode (ctn);
+                       // ColumnNames will be decoded wherever they are used as they can
+                       // contain 'space' separated list of column-names.
+                       rel.ParentColumnName = pkn;
+                       rel.ChildColumnName = fkn;
+                       rel.IsNested = nested;
+                       rel.CreateConstraint = false; // by default?
+                       relations.Add (rel);
                }
 
                private object GetElementDefaultValue (XmlSchemaElement elem)