New tests, updates
[mono.git] / mcs / class / System.Data / System.Data / XmlSchemaDataImporter.cs
old mode 100755 (executable)
new mode 100644 (file)
index 7b1018a..24950a7
@@ -245,6 +245,30 @@ namespace System.Data
                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;
@@ -266,17 +290,25 @@ namespace System.Data
                        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
                }
 
                #region Fields
 
                DataSet dataset;
+               bool forDataSet;
                XmlSchema schema;
 
                ArrayList relations = new ArrayList ();
+               Hashtable reservedConstraints = new Hashtable ();
 
                // such element that has an attribute msdata:IsDataSet="true"
                XmlSchemaElement datasetElement;
@@ -293,9 +325,10 @@ namespace System.Data
 
                // .ctor()
 
-               public XmlSchemaDataImporter (DataSet dataset, XmlReader reader)
+               public XmlSchemaDataImporter (DataSet dataset, XmlReader reader, bool forDataSet)
                {
                        this.dataset = dataset;
+                       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)
@@ -311,11 +344,46 @@ namespace System.Data
                                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);
                                }
                        }
@@ -336,23 +404,39 @@ el.ElementType != schemaAnyType)
                                if (obj is XmlSchemaAnnotation)
                                        HandleAnnotations ((XmlSchemaAnnotation) obj, false);
 
-                       foreach (RelationStructure rs in this.relations)
-                               dataset.Relations.Add (GenerateRelationship (rs));
-
                        if (datasetElement != null) {
                                // 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 bool IsDataSetElement (XmlSchemaElement el)
                {
+                       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
+                                                       return true;
+                                               case "false":
+                                                       break;
+                                               default:
+                                                       throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
+                                               }
+                                       }
+                               }
+                       }
+
                        if (schema.Elements.Count != 1)
                                return false;
                        if (!(el.SchemaType is XmlSchemaComplexType))
@@ -374,18 +458,20 @@ el.ElementType != schemaAnyType)
                {
                        XmlSchemaElement el = p as XmlSchemaElement;
                        if (el != null) {
-                               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
                                if (ct == null || ct == schemaAnyType)
                                        return true; // column element
                                if (ct.AttributeUses.Count > 0)
                                        return false; // table element
-                               switch (ct.ContentType) {
-                               case XmlSchemaContentType.Empty:
-                               case XmlSchemaContentType.TextOnly:
+                               if (ct.ContentType == XmlSchemaContentType.TextOnly)
                                        return true; // column element
-                               default:
+                               else
                                        return false; // table element
-                               }
                        }
                        XmlSchemaGroupBase gb = p as XmlSchemaGroupBase;
                        for (int i = 0; i < gb.Items.Count; i++) {
@@ -402,32 +488,20 @@ el.ElementType != schemaAnyType)
                        if (dataset.Tables.Contains (el.QualifiedName.Name))
                                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;
-                                               case "false":
-                                                       break;
-                                               default:
-                                                       throw new DataException (String.Format ("Value {0} is invalid for attribute 'IsDataSet'.", attr.Value));
-                                               }
-                                       }
-                               }
-                       }
-
                        // 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);
@@ -440,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);
@@ -450,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);
@@ -462,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) {
@@ -473,13 +566,14 @@ el.ElementType != schemaAnyType)
 
                private void ProcessDataTableElement (XmlSchemaElement el)
                {
-                       string tableName = XmlConvert.DecodeName (el.QualifiedName.Name);
+                       string tableName = XmlHelper.Decode (el.QualifiedName.Name);
                        // If it is already registered, just ignore.
                        if (dataset.Tables.Contains (tableName))
                                return;
 
                        DataTable table = new DataTable (tableName);
                        table.Namespace = el.QualifiedName.Namespace;
+                       TableStructure oldTable = currentTable;
                        currentTable = new TableStructure (table);
 
                        dataset.Tables.Add (table);
@@ -493,9 +587,14 @@ el.ElementType != schemaAnyType)
                                }
                        }
 
-                       // 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)
@@ -532,40 +631,66 @@ el.ElementType != schemaAnyType)
                                table.Columns.Add ((DataColumn) de.Value);
                        foreach (DataColumn dc in currentTable.NonOrdinalColumns)
                                table.Columns.Add (dc);
+
+                       currentTable = oldTable;
                }
 
                private DataRelation GenerateRelationship (RelationStructure rs)
                {
                        DataTable ptab = dataset.Tables [rs.ParentTableName];
                        DataTable ctab = dataset.Tables [rs.ChildTableName];
-                       DataColumn pcol = ptab.Columns [rs.ParentColumnName];
-                       DataColumn ccol = ctab.Columns [rs.ChildColumnName];
-
-                       if (ccol == null) {
-                               ccol = new DataColumn ();
-                               ccol.ColumnName = pcol.ColumnName;
-                               ccol.Namespace = String.Empty; // don't copy
-                               ccol.ColumnMapping = MappingType.Hidden;
-                               ccol.DataType = pcol.DataType;
-                               ctab.Columns.Add (ccol);
-                       }
 
-                       string name = rs.ExplicitName != null ? rs.ExplicitName : XmlConvert.DecodeName (ptab.TableName) + '_' + XmlConvert.DecodeName (ctab.TableName);
-                       DataRelation rel = new DataRelation (name, pcol, ccol, rs.CreateConstraint);
+                       DataRelation rel ;
+                       string name = rs.ExplicitName != null ? rs.ExplicitName : XmlHelper.Decode (ptab.TableName) + '_' + XmlHelper.Decode (ctab.TableName);
+
+                       // 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 DataColumn CreateChildColumn (DataColumn parentColumn, DataTable childTable)
+               {
+                       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)
                {
                        foreach (XmlSchemaParticle p in gb.Items) {
                                XmlSchemaElement el = p as XmlSchemaElement;
                                if (el != null)
                                        ImportColumnElement (parent, el);
-                               else
+                               else if (p is XmlSchemaGroupBase)
                                        ImportColumnGroupBase (parent, (XmlSchemaGroupBase) p);
+                               // otherwise p is xs:any
                        }
                }
 
@@ -585,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).
@@ -605,7 +735,11 @@ 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
                        ImportColumnMetaInfo (attr, attr.QualifiedName, col);
@@ -620,7 +754,11 @@ el.ElementType != schemaAnyType)
                        col.DefaultValue = GetElementDefaultValue (el);
                        col.AllowDBNull = (el.MinOccurs == 0);
 
+#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);
                        else if (el.MaxOccurs != 1)
                                FillDataColumnRepeatedSimpleElement (parent, el, col);
@@ -631,7 +769,6 @@ el.ElementType != schemaAnyType)
                // common process for element and attribute
                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)
@@ -649,11 +786,14 @@ 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;
                                        }
                                }
@@ -665,22 +805,23 @@ el.ElementType != schemaAnyType)
                        if (targetElements.Contains (el))
                                return; // do nothing
 
-                       string elName = XmlConvert.DecodeName (el.QualifiedName.Name);
+                       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);
-                       else {
+                       // If xsd:keyref xsd:key for this table exists, then don't add
+                       // relation here manually.
+                       else if (!DataSetDefinesKey (elName)) {
                                AddParentKeyColumn (parent, el, col);
-                               DataColumn pkey = currentTable.PrimaryKey;
 
                                RelationStructure rel = new RelationStructure ();
-                               rel.ParentTableName = XmlConvert.DecodeName (parent.QualifiedName.Name);
+                               rel.ParentTableName = XmlHelper.Decode (parent.QualifiedName.Name);
                                rel.ChildTableName = elName;
-                               rel.ParentColumnName = pkey.ColumnName;
-                               rel.ChildColumnName = pkey.ColumnName;
+                               rel.ParentColumnName = col.ColumnName;
+                               rel.ChildColumnName = col.ColumnName;
                                rel.CreateConstraint = true;
                                rel.IsNested = true;
                                relations.Add (rel);
@@ -688,19 +829,28 @@ el.ElementType != schemaAnyType)
 
                        // If the element is not referenced one, the element will be handled later.
                        if (el.RefName == XmlQualifiedName.Empty)
-                               targetElements.Add (el);
+                               ProcessDataTableElement (el);
 
                }
 
+               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 AddParentKeyColumn (XmlSchemaElement parent, XmlSchemaElement el, DataColumn col)
                {
                        if (currentTable.PrimaryKey != null)
                                return;
 
                        // check name identity
-                       string name = XmlConvert.DecodeName (parent.QualifiedName.Name) + "_Id";
-                       if (currentTable.ContainsColumn (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));
@@ -709,7 +859,6 @@ el.ElementType != schemaAnyType)
                        col.ColumnMapping = MappingType.Hidden;
                        col.Namespace = parent.QualifiedName.Namespace;
                        col.DataType = typeof (int);
-                       col.Unique = true;
                        col.AutoIncrement = true;
                        col.AllowDBNull = false;
 
@@ -726,8 +875,8 @@ el.ElementType != schemaAnyType)
                        AddParentKeyColumn (parent, el, col);
                        DataColumn pkey = currentTable.PrimaryKey;
 
-                       string elName = XmlConvert.DecodeName (el.QualifiedName.Name);
-                       string parentName = XmlConvert.DecodeName (parent.QualifiedName.Name);
+                       string elName = XmlHelper.Decode (el.QualifiedName.Name);
+                       string parentName = XmlHelper.Decode (parent.QualifiedName.Name);
 
                        DataTable dt = new DataTable ();
                        dt.TableName = elName;
@@ -745,7 +894,11 @@ el.ElementType != schemaAnyType)
                        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);
@@ -763,11 +916,16 @@ el.ElementType != schemaAnyType)
 
                private void FillDataColumnSimpleElement (XmlSchemaElement el, DataColumn col)
                {
-                       col.ColumnName = XmlConvert.DecodeName (el.QualifiedName.Name);
+                       col.ColumnName = XmlHelper.Decode (el.QualifiedName.Name);
                        col.Namespace = el.QualifiedName.Namespace;
                        col.ColumnMapping = MappingType.Element;
+#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);
 
@@ -836,20 +994,16 @@ el.ElementType != schemaAnyType)
                        if (index > 0)
                                tableName = tableName.Substring (index + 1);
 
-                       return XmlConvert.DecodeName (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 dt = dataset.Tables [tableName];
-                       if (dt == 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;
@@ -860,16 +1014,9 @@ el.ElementType != schemaAnyType)
                                else if (isAttr)
                                        colName = colName.Substring (1);
 
-                               colName = XmlConvert.DecodeName (colName);
-                               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];
+                               colName = XmlHelper.Decode (colName);
+                               cols [i] = colName;
+                               isAttrSpec [i] = isAttr;
                                i++;
                        }
                        
@@ -891,22 +1038,55 @@ el.ElementType != schemaAnyType)
                                        }
                                }
                        }
-                       UniqueConstraint c = new UniqueConstraint (constraintName, cols, isPK);
-                       dt.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);
-
-                       DataColumn [] cols;
+                       string tableName = c.TableName;
+                       
                        DataTable dt = dataset.Tables [tableName];
-                       if (dt == null)
-                               throw new DataException (String.Format ("Invalid XPath selection inside selector. Cannot find: {0}", 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 = 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));
+               }
+
+               private void ReserveRelationIdentity (XmlSchemaElement element, XmlSchemaKeyref keyref)
+               {
+                       // Basic concept came from XmlSchemaMapper.cs
 
-                       cols = new DataColumn [keyref.Fields.Count];
+                       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) {
                                string colName = Field.XPath;
@@ -917,32 +1097,77 @@ el.ElementType != schemaAnyType)
                                else if (isAttr)
                                        colName = colName.Substring (1);
 
-                               colName = XmlConvert.DecodeName (colName);
+                               colName = XmlHelper.Decode (colName);
+                               cols [i] = colName;
+                               isAttrSpec [i] = isAttr;
+                               i++;
+                       }
+                       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;
-                               i++;
                        }
-                       string name = keyref.Refer.Name;
+                       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);
+                       ForeignKeyConstraint fkc = new ForeignKeyConstraint(c.ConstraintName, uniq.Columns, cols);
                        dt.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);
 
-                       dataset.Relations.Add (rel);
+                       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);
+                       }
                }
 
                // get the unique constraint for the relation.
@@ -998,9 +1223,11 @@ el.ElementType != schemaAnyType)
                        string fkn = el.GetAttribute ("childkey", XmlConstants.MsdataNamespace);
 
                        RelationStructure rel = new RelationStructure ();
-                       rel.ExplicitName = name;
-                       rel.ParentTableName = ptn;
-                       rel.ChildTableName = ctn;
+                       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;