/******************************************************** * ADO.NET 2.0 Data Provider for SQLite Version 3.X * Written by Robert Simpson (robert@blackcastlesoft.com) * * Released to the public domain, use at your own risk! ********************************************************/ namespace System.Data.SQLite { using System; using System.Data; using System.Reflection; using System.IO; using System.Xml; using System.Data.Common; using System.Data.Metadata.Edm; /// /// The Provider Manifest for SQL Server /// internal class SQLiteProviderManifest : DbXmlEnabledProviderManifest { internal SQLiteDateFormats _dateFormat; /// /// Constructs the provider manifest. /// /// /// We pass the token as a DateTimeFormat enum text, because all the datetime functions /// are vastly different depending on how the user is opening the connection /// /// A token used to infer the capabilities of the store public SQLiteProviderManifest(string manifestToken) : base(SQLiteProviderManifest.GetProviderManifest()) { _dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), manifestToken, true); } internal static XmlReader GetProviderManifest() { return GetXmlResource("System.Data.SQLite.Linq.Resources.SQLiteProviderServices.ProviderManifest.xml"); } /// /// Returns manifest information for the provider /// /// The name of the information to be retrieved. /// An XmlReader at the begining of the information requested. protected override XmlReader GetDbInformation(string informationType) { if (informationType == DbProviderManifest.StoreSchemaDefinition) return GetStoreSchemaDescription(); else if (informationType == DbProviderManifest.StoreSchemaMapping) return GetStoreSchemaMapping(); else if (informationType == DbProviderManifest.ConceptualSchemaDefinition) return null; throw new ProviderIncompatibleException(String.Format("SQLite does not support this information type '{0}'.", informationType)); } /// /// This method takes a type and a set of facets and returns the best mapped equivalent type /// in EDM. /// /// A TypeUsage encapsulating a store type and a set of facets /// A TypeUsage encapsulating an EDM type and a set of facets public override TypeUsage GetEdmType(TypeUsage storeType) { if (storeType == null) { throw new ArgumentNullException("storeType"); } string storeTypeName = storeType.EdmType.Name.ToLowerInvariant(); //if (!base.StoreTypeNameToEdmPrimitiveType.ContainsKey(storeTypeName)) //{ // switch (storeTypeName) // { // case "integer": // return TypeUsage.CreateDefaultTypeUsage(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int64)); // default: // throw new ArgumentException(String.Format("SQLite does not support the type '{0}'.", storeTypeName)); // } //} PrimitiveType edmPrimitiveType; if (base.StoreTypeNameToEdmPrimitiveType.TryGetValue(storeTypeName, out edmPrimitiveType) == false) throw new ArgumentException(String.Format("SQLite does not support the type '{0}'.", storeTypeName)); int maxLength = 0; bool isUnicode = true; bool isFixedLen = false; bool isUnbounded = true; PrimitiveTypeKind newPrimitiveTypeKind; switch (storeTypeName) { case "tinyint": case "smallint": case "integer": case "bit": case "uniqueidentifier": case "int": case "float": case "real": return TypeUsage.CreateDefaultTypeUsage(edmPrimitiveType); case "varchar": newPrimitiveTypeKind = PrimitiveTypeKind.String; isUnbounded = !TypeHelpers.TryGetMaxLength(storeType, out maxLength); isUnicode = false; isFixedLen = false; break; case "char": newPrimitiveTypeKind = PrimitiveTypeKind.String; isUnbounded = !TypeHelpers.TryGetMaxLength(storeType, out maxLength); isUnicode = false; isFixedLen = true; break; case "nvarchar": newPrimitiveTypeKind = PrimitiveTypeKind.String; isUnbounded = !TypeHelpers.TryGetMaxLength(storeType, out maxLength); isUnicode = true; isFixedLen = false; break; case "nchar": newPrimitiveTypeKind = PrimitiveTypeKind.String; isUnbounded = !TypeHelpers.TryGetMaxLength(storeType, out maxLength); isUnicode = true; isFixedLen = true; break; case "blob": newPrimitiveTypeKind = PrimitiveTypeKind.Binary; isUnbounded = !TypeHelpers.TryGetMaxLength(storeType, out maxLength); isFixedLen = false; break; case "decimal": { byte precision; byte scale; if (TypeHelpers.TryGetPrecision(storeType, out precision) && TypeHelpers.TryGetScale(storeType, out scale)) { return TypeUsage.CreateDecimalTypeUsage(edmPrimitiveType, precision, scale); } else { return TypeUsage.CreateDecimalTypeUsage(edmPrimitiveType); } } case "datetime": return TypeUsage.CreateDateTimeTypeUsage(edmPrimitiveType, null); default: throw new NotSupportedException(String.Format("SQLite does not support the type '{0}'.", storeTypeName)); } switch (newPrimitiveTypeKind) { case PrimitiveTypeKind.String: if (!isUnbounded) { return TypeUsage.CreateStringTypeUsage(edmPrimitiveType, isUnicode, isFixedLen, maxLength); } else { return TypeUsage.CreateStringTypeUsage(edmPrimitiveType, isUnicode, isFixedLen); } case PrimitiveTypeKind.Binary: if (!isUnbounded) { return TypeUsage.CreateBinaryTypeUsage(edmPrimitiveType, isFixedLen, maxLength); } else { return TypeUsage.CreateBinaryTypeUsage(edmPrimitiveType, isFixedLen); } default: throw new NotSupportedException(String.Format("SQLite does not support the type '{0}'.", storeTypeName)); } } /// /// This method takes a type and a set of facets and returns the best mapped equivalent type /// /// A TypeUsage encapsulating an EDM type and a set of facets /// A TypeUsage encapsulating a store type and a set of facets public override TypeUsage GetStoreType(TypeUsage edmType) { if (edmType == null) throw new ArgumentNullException("edmType"); PrimitiveType primitiveType = edmType.EdmType as PrimitiveType; if (primitiveType == null) throw new ArgumentException(String.Format("SQLite does not support the type '{0}'.", edmType)); ReadOnlyMetadataCollection facets = edmType.Facets; switch (primitiveType.PrimitiveTypeKind) { case PrimitiveTypeKind.Boolean: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["bit"]); case PrimitiveTypeKind.Byte: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["tinyint"]); case PrimitiveTypeKind.Int16: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["smallint"]); case PrimitiveTypeKind.Int32: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["int"]); case PrimitiveTypeKind.Int64: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["integer"]); case PrimitiveTypeKind.Guid: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["uniqueidentifier"]); case PrimitiveTypeKind.Double: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["float"]); case PrimitiveTypeKind.Single: return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["real"]); case PrimitiveTypeKind.Decimal: // decimal, numeric, smallmoney, money { byte precision; if (!TypeHelpers.TryGetPrecision(edmType, out precision)) { precision = 18; } byte scale; if (!TypeHelpers.TryGetScale(edmType, out scale)) { scale = 0; } return TypeUsage.CreateDecimalTypeUsage(StoreTypeNameToStorePrimitiveType["decimal"], precision, scale); } case PrimitiveTypeKind.Binary: // binary, varbinary, varbinary(max), image, timestamp, rowversion { bool isFixedLength = null != facets["FixedLength"].Value && (bool)facets["FixedLength"].Value; Facet f = facets["MaxLength"]; bool isMaxLength = f.IsUnbounded || null == f.Value || (int)f.Value > Int32.MaxValue; int maxLength = !isMaxLength ? (int)f.Value : Int32.MinValue; TypeUsage tu; if (isFixedLength) tu = TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["blob"], true, maxLength); else { if (isMaxLength) tu = TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["blob"], false); else tu = TypeUsage.CreateBinaryTypeUsage(StoreTypeNameToStorePrimitiveType["blob"], false, maxLength); } return tu; } case PrimitiveTypeKind.String: // char, nchar, varchar, nvarchar, varchar(max), nvarchar(max), ntext, text { bool isUnicode = null == facets["Unicode"].Value || (bool)facets["Unicode"].Value; bool isFixedLength = null != facets["FixedLength"].Value && (bool)facets["FixedLength"].Value; Facet f = facets["MaxLength"]; // maxlen is true if facet value is unbounded, the value is bigger than the limited string sizes *or* the facet // value is null. this is needed since functions still have maxlength facet value as null bool isMaxLength = f.IsUnbounded || null == f.Value || (int)f.Value > (isUnicode ? Int32.MaxValue : Int32.MaxValue); int maxLength = !isMaxLength ? (int)f.Value : Int32.MinValue; TypeUsage tu; if (isUnicode) { if (isFixedLength) tu = TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["nchar"], true, true, maxLength); else { if (isMaxLength) tu = TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["nvarchar"], true, false); else tu = TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["nvarchar"], true, false, maxLength); } } else { if (isFixedLength) tu = TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["char"], false, true, maxLength); else { if (isMaxLength) tu = TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["varchar"], false, false); else tu = TypeUsage.CreateStringTypeUsage(StoreTypeNameToStorePrimitiveType["varchar"], false, false, maxLength); } } return tu; } case PrimitiveTypeKind.DateTime: // datetime, smalldatetime return TypeUsage.CreateDefaultTypeUsage(StoreTypeNameToStorePrimitiveType["datetime"]); default: throw new NotSupportedException(String.Format("There is no store type corresponding to the EDM type '{0}' of primitive type '{1}'.", edmType, primitiveType.PrimitiveTypeKind)); } } private XmlReader GetStoreSchemaMapping() { return GetXmlResource("System.Data.SQLite.Linq.Resources.SQLiteProviderServices.StoreSchemaMapping.msl"); } private XmlReader GetStoreSchemaDescription() { return GetXmlResource("System.Data.SQLite.Linq.Resources.SQLiteProviderServices.StoreSchemaDefinition.ssdl"); } internal static XmlReader GetXmlResource(string resourceName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); Stream stream = executingAssembly.GetManifestResourceStream(resourceName); return XmlReader.Create(stream); } private static class TypeHelpers { public static bool TryGetPrecision(TypeUsage tu, out byte precision) { Facet f; precision = 0; if (tu.Facets.TryGetValue("Precision", false, out f)) { if (!f.IsUnbounded && f.Value != null) { precision = (byte)f.Value; return true; } } return false; } public static bool TryGetMaxLength(TypeUsage tu, out int maxLength) { Facet f; maxLength = 0; if (tu.Facets.TryGetValue("MaxLength", false, out f)) { if (!f.IsUnbounded && f.Value != null) { maxLength = (int)f.Value; return true; } } return false; } public static bool TryGetScale(TypeUsage tu, out byte scale) { Facet f; scale = 0; if (tu.Facets.TryGetValue("Scale", false, out f)) { if (!f.IsUnbounded && f.Value != null) { scale = (byte)f.Value; return true; } } return false; } } } }