/********************************************************
* 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;
}
}
}
}