/******************************************************** * 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! ********************************************************/ #if USE_ENTITY_FRAMEWORK_6 namespace System.Data.SQLite.EF6 #else namespace System.Data.SQLite.Linq #endif { using System; using System.Collections.Generic; using System.IO; using System.Reflection; #if !PLATFORM_COMPACTFRAMEWORK using System.Text; #endif using System.Xml; #if USE_ENTITY_FRAMEWORK_6 using System.Data.Entity.Core; using System.Data.Entity.Core.Common; using System.Data.Entity.Core.Metadata.Edm; #else using System.Data.Common; using System.Data.Metadata.Edm; #endif /// /// The Provider Manifest for SQL Server /// internal sealed class SQLiteProviderManifest : DbXmlEnabledProviderManifest { internal SQLiteDateFormats _dateTimeFormat; internal DateTimeKind _dateTimeKind; internal string _dateTimeFormatString; internal bool _binaryGuid; /// /// Constructs the provider manifest. /// /// /// Previously, the manifest token was interpreted as a , /// because the functions are vastly different depending on the /// connection was opened. However, the manifest token may specify a connection string /// instead. WARNING: Only the "DateTimeFormat", "DateTimeKind", "DateTimeFormatString", /// and "BinaryGUID" connection parameters are extracted from it. All other connection /// parameters, if any are present, are silently ignored. /// /// /// A token used to infer the capabilities of the store. /// public SQLiteProviderManifest(string manifestToken) : base(GetProviderManifest()) { SetFromOptions(ParseProviderManifestToken(GetProviderManifestToken(manifestToken))); } private static XmlReader GetProviderManifest() { return GetXmlResource("System.Data.SQLite.SQLiteProviderServices.ProviderManifest.xml"); } /// /// Determines and returns the effective provider manifest token to use, /// based on the specified provider manifest token and the environment, /// if applicable. /// /// /// The original provider manifest token passed to the constructor for this /// class. /// /// /// The effective provider manifest token. /// private static string GetProviderManifestToken( string manifestToken ) { #if !PLATFORM_COMPACTFRAMEWORK string value = UnsafeNativeMethods.GetSettingValue( "AppendManifestToken_SQLiteProviderManifest", null); if (String.IsNullOrEmpty(value)) return manifestToken; int capacity = value.Length; if (manifestToken != null) capacity += manifestToken.Length; StringBuilder builder = new StringBuilder(capacity); builder.Append(manifestToken); builder.Append(value); return builder.ToString(); #else // // NOTE: The .NET Compact Framework lacks environment variable support. // Therefore, just return the original provider manifest token // verbatim in this case. // return manifestToken; #endif } /// /// Attempts to parse a provider manifest token. It must contain either a /// legacy string that specifies the value /// -OR- string that uses the standard connection string syntax; otherwise, /// the results are undefined. /// /// /// The manifest token to parse. /// /// /// The dictionary containing the connection string parameters. /// private static SortedList ParseProviderManifestToken( string manifestToken ) { return SQLiteConnection.ParseConnectionString(manifestToken, false, true); } /// /// Attempts to set the provider manifest options from the specified /// connection string parameters. An exception may be thrown if one /// or more of the connection string parameter values do not conform /// to the expected type. /// /// /// The dictionary containing the connection string parameters. /// internal void SetFromOptions( SortedList opts ) { _dateTimeFormat = SQLiteConnection.DefaultDateTimeFormat; _dateTimeKind = SQLiteConnection.DefaultDateTimeKind; _dateTimeFormatString = SQLiteConnection.DefaultDateTimeFormatString; _binaryGuid = /* SQLiteConnection.DefaultBinaryGUID; */ false; /* COMPAT: Legacy. */ if (opts == null) return; #if !PLATFORM_COMPACTFRAMEWORK string[] names = Enum.GetNames(typeof(SQLiteDateFormats)); #else string[] names = { SQLiteDateFormats.Ticks.ToString(), SQLiteDateFormats.ISO8601.ToString(), SQLiteDateFormats.JulianDay.ToString(), SQLiteDateFormats.UnixEpoch.ToString(), SQLiteDateFormats.InvariantCulture.ToString(), SQLiteDateFormats.CurrentCulture.ToString(), "Default" }; #endif foreach (string name in names) { if (String.IsNullOrEmpty(name)) continue; object value = SQLiteConnection.FindKey(opts, name, null); if (value == null) continue; _dateTimeFormat = (SQLiteDateFormats)Enum.Parse( typeof(SQLiteDateFormats), name, true); } object enumValue; enumValue = SQLiteConnection.TryParseEnum( typeof(SQLiteDateFormats), SQLiteConnection.FindKey( opts, "DateTimeFormat", null), true); if (enumValue is SQLiteDateFormats) _dateTimeFormat = (SQLiteDateFormats)enumValue; enumValue = SQLiteConnection.TryParseEnum( typeof(DateTimeKind), SQLiteConnection.FindKey( opts, "DateTimeKind", null), true); if (enumValue is DateTimeKind) _dateTimeKind = (DateTimeKind)enumValue; string stringValue = SQLiteConnection.FindKey( opts, "DateTimeFormatString", null); if (stringValue != null) _dateTimeFormatString = stringValue; stringValue = SQLiteConnection.FindKey( opts, "BinaryGUID", null); if (stringValue != null) _binaryGuid = SQLiteConvert.ToBoolean(stringValue); } /// /// 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.SQLiteProviderServices.StoreSchemaMapping.msl"); } private XmlReader GetStoreSchemaDescription() { return GetXmlResource("System.Data.SQLite.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; } } } }