Index: Doc/Extra/Provider/version.html ================================================================== --- Doc/Extra/Provider/version.html +++ Doc/Extra/Provider/version.html @@ -46,10 +46,11 @@

1.0.93.0 - June XX, 2014 (release scheduled)

Index: System.Data.SQLite/SQLiteBase.cs ================================================================== --- System.Data.SQLite/SQLiteBase.cs +++ System.Data.SQLite/SQLiteBase.cs @@ -1063,10 +1063,30 @@ /// Enable mapping of unsupported transaction isolation levels to the /// closest supported transaction isolation level. /// MapIsolationLevels = 0x1000000, + /// + /// When returning column values, attempt to detect the affinity of + /// textual values by checking if they fully conform to those of the + /// , + /// , + /// , + /// or types. + /// + DetectTextAffinity = 0x2000000, + + /// + /// When returning column values, attempt to detect the type of + /// string values by checking if they fully conform to those of + /// the , + /// , + /// , + /// or types. + /// + DetectStringType = 0x4000000, + /// /// When binding parameter values or returning column values, always /// treat them as though they were plain text (i.e. no numeric, /// date/time, or other conversions should be attempted). /// Index: System.Data.SQLite/SQLiteConvert.cs ================================================================== --- System.Data.SQLite/SQLiteConvert.cs +++ System.Data.SQLite/SQLiteConvert.cs @@ -1315,10 +1315,36 @@ new SQLiteDbTypeMapping("VARCHAR", DbType.AnsiString, true), new SQLiteDbTypeMapping("VARCHAR2", DbType.AnsiString, false), new SQLiteDbTypeMapping("YESNO", DbType.Boolean, false) }); } + + /// + /// Determines if a database type is considered to be a string. + /// + /// + /// The database type to check. + /// + /// + /// Non-zero if the database type is considered to be a string, zero + /// otherwise. + /// + internal static bool IsStringDbType( + DbType type + ) + { + switch (type) + { + case DbType.AnsiString: + case DbType.String: + case DbType.AnsiStringFixedLength: + case DbType.StringFixedLength: + return true; + default: + return false; + } + } /// /// Determines the default value to be used when a /// per-connection value is not available. /// @@ -1339,10 +1365,151 @@ if (!(enumValue is DbType)) return FallbackDefaultDbType; return (DbType)enumValue; } + + /// + /// Determines if the specified textual value appears to be a + /// value. + /// + /// + /// The textual value to inspect. + /// + /// + /// Non-zero if the text looks like a value, + /// zero otherwise. + /// + internal static bool LooksLikeNull( + string text + ) + { + return (text == null); + } + + /// + /// Determines if the specified textual value appears to be an + /// value. + /// + /// + /// The textual value to inspect. + /// + /// + /// Non-zero if the text looks like an value, + /// zero otherwise. + /// + internal static bool LooksLikeInt64( + string text + ) + { + long longValue; + +#if !PLATFORM_COMPACTFRAMEWORK + if (!long.TryParse( + text, NumberStyles.Integer, CultureInfo.InvariantCulture, + out longValue)) + { + return false; + } +#else + try + { + longValue = long.Parse( + text, NumberStyles.Integer, CultureInfo.InvariantCulture); + } + catch + { + return false; + } +#endif + + return String.Equals( + longValue.ToString(CultureInfo.InvariantCulture), text, + StringComparison.Ordinal); + } + + /// + /// Determines if the specified textual value appears to be a + /// value. + /// + /// + /// The textual value to inspect. + /// + /// + /// Non-zero if the text looks like a value, + /// zero otherwise. + /// + internal static bool LooksLikeDouble( + string text + ) + { + double doubleValue; + +#if !PLATFORM_COMPACTFRAMEWORK + if (!double.TryParse( + text, NumberStyles.Float | NumberStyles.AllowThousands, + CultureInfo.InvariantCulture, out doubleValue)) + { + return false; + } +#else + try + { + doubleValue = double.Parse(text, CultureInfo.InvariantCulture); + } + catch + { + return false; + } +#endif + + return String.Equals( + doubleValue.ToString(CultureInfo.InvariantCulture), text, + StringComparison.Ordinal); + } + + /// + /// Determines if the specified textual value appears to be a + /// value. + /// + /// + /// The object instance configured with + /// the chosen format. + /// + /// + /// The textual value to inspect. + /// + /// + /// Non-zero if the text looks like a in the + /// configured format, zero otherwise. + /// + internal static bool LooksLikeDateTime( + SQLiteConvert convert, + string text + ) + { + if (convert == null) + return false; + + try + { + DateTime dateTimeValue = convert.ToDateTime(text); + + if (String.Equals( + convert.ToString(dateTimeValue), + text, StringComparison.Ordinal)) + { + return true; + } + } + catch + { + // do nothing. + } + + return false; + } /// /// For a given type name, return a closest-match .NET type /// /// The connection context for custom type mappings, if any. Index: System.Data.SQLite/SQLiteDataReader.cs ================================================================== --- System.Data.SQLite/SQLiteDataReader.cs +++ System.Data.SQLite/SQLiteDataReader.cs @@ -989,14 +989,12 @@ { dataType = arSize[0]; arSize = arSize[1].Split(')'); if (arSize.Length > 1) { - arSize = arSize[0].Split(',', '.'); - if (sqlType.Type == DbType.AnsiString || sqlType.Type == DbType.Binary || - sqlType.Type == DbType.String || sqlType.Type == DbType.AnsiStringFixedLength || - sqlType.Type == DbType.StringFixedLength) + arSize = arSize[0].Split(',', '.'); + if (sqlType.Type == DbType.Binary || SQLiteConvert.IsStringDbType(sqlType.Type)) { row[SchemaTableColumn.ColumnSize] = Convert.ToInt32(arSize[0], CultureInfo.InvariantCulture); } else { @@ -1135,10 +1133,23 @@ if (i >= VisibleFieldCount && _keyInfo != null) return _keyInfo.GetValue(i - VisibleFieldCount); SQLiteConnectionFlags flags = SQLiteCommand.GetFlags(_command); SQLiteType typ = GetSQLiteType(flags, i); + + if (((flags & SQLiteConnectionFlags.DetectTextAffinity) == SQLiteConnectionFlags.DetectTextAffinity) && + ((typ == null) || (typ.Affinity == TypeAffinity.Text))) + { + typ = GetSQLiteType( + typ, _activeStatement._sql.GetText(_activeStatement, i)); + } + else if (((flags & SQLiteConnectionFlags.DetectStringType) == SQLiteConnectionFlags.DetectStringType) && + ((typ == null) || SQLiteConvert.IsStringDbType(typ.Type))) + { + typ = GetSQLiteType( + typ, _activeStatement._sql.GetText(_activeStatement, i)); + } return _activeStatement._sql.GetValue(_activeStatement, flags, i, typ); } /// @@ -1411,10 +1422,45 @@ // do nothing. } return SQLiteConnectionFlags.Default; } + + /// + /// Retrieves the SQLiteType for a given column and row value. + /// + /// + /// The original SQLiteType structure, based only on the column. + /// + /// + /// The textual value of the column for a given row. + /// + /// + /// The SQLiteType structure. + /// + private SQLiteType GetSQLiteType( + SQLiteType oldType, /* PASS-THROUGH */ + string text + ) + { + if (SQLiteConvert.LooksLikeNull(text)) + return new SQLiteType(TypeAffinity.Null, DbType.Object); + + if (SQLiteConvert.LooksLikeInt64(text)) + return new SQLiteType(TypeAffinity.Int64, DbType.Int64); + + if (SQLiteConvert.LooksLikeDouble(text)) + return new SQLiteType(TypeAffinity.Double, DbType.Double); + + if ((_activeStatement != null) && + SQLiteConvert.LooksLikeDateTime(_activeStatement._sql, text)) + { + return new SQLiteType(TypeAffinity.DateTime, DbType.DateTime); + } + + return oldType; + } /// /// Retrieves the SQLiteType for a given column, and caches it to avoid repetetive interop calls. /// /// The flags associated with the parent connection object. Index: readme.htm ================================================================== --- readme.htm +++ readme.htm @@ -213,10 +213,11 @@

  • Updated to SQLite 3.8.4.3.
  • Add support for mapping transaction isolation levels to their legacy default values. Pursuant to [56b42d99c1].
  • Add support for setting the default DbType and type name used for mappings on a per-connection basis. Pursuant to [3c00ec5b52].
  • +
  • Add DetectTextAffinity and DetectStringType connection flags to enable automatic detection of column types, when necessary. Pursuant to [3c00ec5b52].
  • Add SetChunkSize method to the SQLiteConnection class. Pursuant to [d1c008fa0a].
  • Make the ISQLiteSchemaExtensions interface public. ** Potentially Incompatible Change **
  • Have the SQLiteProviderFactory class (in the System.Data.SQLite.Linq assembly) implement the IServiceProvider interface.
  • Fix bug in documentation generator automation that prevented some internal documentation links from working.
Index: www/news.wiki ================================================================== --- www/news.wiki +++ www/news.wiki @@ -7,10 +7,11 @@

  • Updated to SQLite 3.8.4.3.
  • Add support for mapping transaction isolation levels to their legacy default values. Pursuant to [56b42d99c1].
  • Add support for setting the default DbType and type name used for mappings on a per-connection basis. Pursuant to [3c00ec5b52].
  • +
  • Add DetectTextAffinity and DetectStringType connection flags to enable automatic detection of column types, when necessary. Pursuant to [3c00ec5b52].
  • Add SetChunkSize method to the SQLiteConnection class. Pursuant to [d1c008fa0a].
  • Make the ISQLiteSchemaExtensions interface public. ** Potentially Incompatible Change **
  • Have the SQLiteProviderFactory class (in the System.Data.SQLite.Linq assembly) implement the IServiceProvider interface.
  • Fix bug in documentation generator automation that prevented some internal documentation links from working.