diff --git a/.travis.yml b/.travis.yml
index 68dd3110..4f2bb406 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,19 +1,21 @@
language: csharp
sudo: false
+env:
+ - MONO_THREADS_PER_CPU=2000 MONO_MANAGED_WATCHER=disabled
mono:
- beta
os:
- linux
+ - osx
addons:
apt:
- sources:
- - debian-sid
packages:
- libunwind8
- - sqlite3
-
+
before_script:
- sqlite3 -version
+before_install:
+ - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install icu4c; fi
script:
- ./build.sh --quiet verify
diff --git a/README.md b/README.md
index 190bad1d..9cf87891 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Contains SQLite implementations of the System.Data.Common interfaces.
This project is part of ASP.NET 5. You can find samples, documentation and getting started instructions for ASP.NET 5 at the [Home](https://github.com/aspnet/home) repo.
## Requirements
-Requires SQLite >= 3.7.15
+Requires SQLite >= 3.7.9
This library binds to the native SQLite library. On some systems, you must also install separately the SQLite library.
diff --git a/src/Microsoft.Data.Sqlite/Interop/MarshalEx.cs b/src/Microsoft.Data.Sqlite/Interop/MarshalEx.cs
index eadfc98a..9a0ad18f 100644
--- a/src/Microsoft.Data.Sqlite/Interop/MarshalEx.cs
+++ b/src/Microsoft.Data.Sqlite/Interop/MarshalEx.cs
@@ -60,11 +60,7 @@ public static void ThrowExceptionForRC(int rc, Sqlite3Handle db)
return;
}
- var message = db == null || db.IsInvalid
- ? NativeMethods.sqlite3_errstr(rc)
- : NativeMethods.sqlite3_errmsg(db);
-
- throw new SqliteException(Strings.FormatSqliteNativeError(rc, message), rc);
+ throw new SqliteException(VersionedMethods.SqliteErrorMessage(rc, db), rc);
}
}
}
diff --git a/src/Microsoft.Data.Sqlite/Interop/NativeMethods.cs b/src/Microsoft.Data.Sqlite/Interop/NativeMethods.cs
index bf68ff70..68b9cc92 100644
--- a/src/Microsoft.Data.Sqlite/Interop/NativeMethods.cs
+++ b/src/Microsoft.Data.Sqlite/Interop/NativeMethods.cs
@@ -92,6 +92,9 @@ public static int sqlite3_bind_text(Sqlite3StmtHandle pStmt, int i, string data,
[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_close_v2(IntPtr db);
+ [DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl)]
+ public static extern int sqlite3_close(IntPtr db);
+
[DllImport("sqlite3", EntryPoint = "sqlite3_column_blob", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr sqlite3_column_blob_raw(Sqlite3StmtHandle pStmt, int iCol);
diff --git a/src/Microsoft.Data.Sqlite/Interop/Sqlite3Handle.cs b/src/Microsoft.Data.Sqlite/Interop/Sqlite3Handle.cs
index 37ab6b6d..2319338f 100644
--- a/src/Microsoft.Data.Sqlite/Interop/Sqlite3Handle.cs
+++ b/src/Microsoft.Data.Sqlite/Interop/Sqlite3Handle.cs
@@ -17,7 +17,7 @@ private Sqlite3Handle()
protected override bool ReleaseHandle()
{
- var rc = NativeMethods.sqlite3_close_v2(handle);
+ var rc = VersionedMethods.SqliteClose(handle);
handle = IntPtr.Zero;
return rc == Constants.SQLITE_OK;
diff --git a/src/Microsoft.Data.Sqlite/Interop/VersionedMethods.cs b/src/Microsoft.Data.Sqlite/Interop/VersionedMethods.cs
new file mode 100644
index 00000000..86bdc50f
--- /dev/null
+++ b/src/Microsoft.Data.Sqlite/Interop/VersionedMethods.cs
@@ -0,0 +1,74 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.Data.Sqlite.Interop
+{
+ internal class VersionedMethods
+ {
+ public static string SqliteErrorMessage(int rc, Sqlite3Handle db)
+ {
+ var message = db == null || db.IsInvalid
+ ? _strategy.ErrorString(rc)
+ : NativeMethods.sqlite3_errmsg(db);
+
+ return Strings.FormatSqliteNativeError(rc, message);
+ }
+
+ public static int SqliteClose(IntPtr handle)
+ => _strategy.Close(handle);
+
+ public static string SqliteDbFilename(Sqlite3Handle db, string databaseName)
+ => _strategy.DbFilename(db, databaseName);
+
+ private static readonly StrategyBase _strategy = GetStrategy(new Version(NativeMethods.sqlite3_libversion()));
+
+ private static StrategyBase GetStrategy(Version current)
+ {
+ if (current >= new Version("3.7.15"))
+ {
+ return new Strategy3_7_15();
+ }
+ if (current >= new Version("3.7.14"))
+ {
+ return new Strategy3_7_14();
+ }
+ if (current >= new Version("3.7.10"))
+ {
+ return new Strategy3_7_10();
+ }
+ return new StrategyBase();
+ }
+
+ private class Strategy3_7_15 : Strategy3_7_14
+ {
+ public override string ErrorString(int rc)
+ => NativeMethods.sqlite3_errstr(rc) + " " + base.ErrorString(rc);
+ }
+
+ private class Strategy3_7_14 : Strategy3_7_10
+ {
+ public override int Close(IntPtr handle)
+ => NativeMethods.sqlite3_close_v2(handle);
+ }
+
+ private class Strategy3_7_10 : StrategyBase
+ {
+ public override string DbFilename(Sqlite3Handle db, string databaseName)
+ => NativeMethods.sqlite3_db_filename(db, databaseName);
+ }
+
+ private class StrategyBase
+ {
+ public virtual string ErrorString(int rc)
+ => Strings.DefaultNativeError;
+
+ public virtual int Close(IntPtr handle)
+ => NativeMethods.sqlite3_close(handle);
+
+ public virtual string DbFilename(Sqlite3Handle db, string databaseName)
+ => null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Data.Sqlite/Properties/Strings.Designer.cs b/src/Microsoft.Data.Sqlite/Properties/Strings.Designer.cs
index ecb21cc9..c0e15786 100644
--- a/src/Microsoft.Data.Sqlite/Properties/Strings.Designer.cs
+++ b/src/Microsoft.Data.Sqlite/Properties/Strings.Designer.cs
@@ -362,6 +362,22 @@ internal static string FormatSqliteNativeError(object errorCode, object message)
return string.Format(CultureInfo.CurrentCulture, GetString("SqliteNativeError", "errorCode", "message"), errorCode, message);
}
+ ///
+ /// For more information on this error code see https://www.sqlite.org/rescode.html
+ ///
+ internal static string DefaultNativeError
+ {
+ get { return GetString("DefaultNativeError"); }
+ }
+
+ ///
+ /// For more information on this error code see https://www.sqlite.org/rescode.html
+ ///
+ internal static string FormatDefaultNativeError()
+ {
+ return GetString("DefaultNativeError");
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.Data.Sqlite/SqliteConnection.cs b/src/Microsoft.Data.Sqlite/SqliteConnection.cs
index c6e00252..24f17d1e 100644
--- a/src/Microsoft.Data.Sqlite/SqliteConnection.cs
+++ b/src/Microsoft.Data.Sqlite/SqliteConnection.cs
@@ -62,7 +62,7 @@ public override string ConnectionString
public override string DataSource =>
State == ConnectionState.Open
- ? NativeMethods.sqlite3_db_filename(_db, MainDatabaseName)
+ ? VersionedMethods.SqliteDbFilename(_db, MainDatabaseName) ?? ConnectionStringBuilder.DataSource
: ConnectionStringBuilder.DataSource;
///
diff --git a/src/Microsoft.Data.Sqlite/Strings.resx b/src/Microsoft.Data.Sqlite/Strings.resx
index 301d752c..3b747e38 100644
--- a/src/Microsoft.Data.Sqlite/Strings.resx
+++ b/src/Microsoft.Data.Sqlite/Strings.resx
@@ -181,6 +181,9 @@
No mapping exists from object type {typeName} to a known managed provider native type.
- SQLite Error {errorCode}: '{message}'
+ SQLite Error {errorCode}: '{message}'.
+
+
+ For more information on this error code see https://www.sqlite.org/rescode.html
\ No newline at end of file
diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteConcurrencyTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteConcurrencyTest.cs
index 9f8d5205..d86202d3 100644
--- a/test/Microsoft.Data.Sqlite.Tests/SqliteConcurrencyTest.cs
+++ b/test/Microsoft.Data.Sqlite.Tests/SqliteConcurrencyTest.cs
@@ -116,8 +116,12 @@ public void It_throws_sqlite_busy_on_deadlock()
var ex = Assert.Throws(() => dropCommand.ExecuteNonQuery());
Assert.Equal(SQLITE_BUSY, ex.SqliteErrorCode);
- var message = NativeMethods.sqlite3_errstr(SQLITE_BUSY);
- Assert.Equal(Strings.FormatSqliteNativeError(SQLITE_BUSY, message), ex.Message);
+
+ if (CurrentVersion >= new Version("3.7.15"))
+ {
+ var message = NativeMethods.sqlite3_errstr(SQLITE_BUSY);
+ Assert.Equal(Strings.FormatSqliteNativeError(SQLITE_BUSY, message), ex.Message);
+ }
}
dropCommand.ExecuteNonQuery();
@@ -191,8 +195,12 @@ public void Command_times_out()
waitHandle.Release();
Assert.Equal(SQLITE_BUSY, ex.SqliteErrorCode);
- var message = NativeMethods.sqlite3_errstr(SQLITE_BUSY);
- Assert.Equal(Strings.FormatSqliteNativeError(SQLITE_BUSY, message), ex.Message);
+
+ if (CurrentVersion >= new Version("3.7.15"))
+ {
+ var message = NativeMethods.sqlite3_errstr(SQLITE_BUSY);
+ Assert.Equal(Strings.FormatSqliteNativeError(SQLITE_BUSY, message), ex.Message);
+ }
});
t1.Start();
@@ -202,6 +210,8 @@ public void Command_times_out()
}
}
}
+
+ private Version CurrentVersion => new Version(NativeMethods.sqlite3_libversion());
private const string FileName = "./concurrency.db";
diff --git a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs
index c360ec06..0fab3ae2 100644
--- a/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs
+++ b/test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs
@@ -4,6 +4,7 @@
using System;
using System.Data;
using System.IO;
+using Microsoft.AspNet.Testing.xunit;
using Xunit;
namespace Microsoft.Data.Sqlite
@@ -60,7 +61,8 @@ public void DataSource_returns_connection_string_data_source_when_closed()
Assert.Equal("test.db", connection.DataSource);
}
- [Fact]
+ [ConditionalFact]
+ [SqliteVersionCondition(Min = "3.7.10")]
public void DataSource_returns_actual_filename_when_open()
{
using (var connection = new SqliteConnection("Data Source=test.db"))
diff --git a/test/Microsoft.Data.Sqlite.Tests/TestUtilities/SqliteVersionConditionAttribute.cs b/test/Microsoft.Data.Sqlite.Tests/TestUtilities/SqliteVersionConditionAttribute.cs
new file mode 100644
index 00000000..a748c15a
--- /dev/null
+++ b/test/Microsoft.Data.Sqlite.Tests/TestUtilities/SqliteVersionConditionAttribute.cs
@@ -0,0 +1,63 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.Data.Sqlite.Interop;
+using Microsoft.AspNet.Testing.xunit;
+
+namespace Microsoft.Data.Sqlite
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = true)]
+ internal class SqliteVersionConditionAttribute : Attribute, ITestCondition
+ {
+ private Version _min;
+ private Version _max;
+ private Version _skip;
+ public string Min { get { return _min.ToString(); } set { _min = new Version(value); } }
+ public string Max { get { return _max.ToString(); } set { _max = new Version(value); } }
+ public string Skip { get { return _skip.ToString(); } set { _skip = new Version(value); } }
+
+ private Version Current = new Version(NativeMethods.sqlite3_libversion());
+
+ public bool IsMet
+ {
+ get
+ {
+ if (Current == _skip)
+ {
+ return false;
+ }
+
+ if (_min == null && _max == null)
+ {
+ return true;
+ }
+
+ if (_min == null)
+ {
+ return Current <= _max;
+ }
+
+ if (_max == null)
+ {
+ return Current >= _min;
+ }
+
+ return Current <= _max && Current >= _min;
+ }
+ }
+
+ private string _skipReason;
+
+ public string SkipReason
+ {
+ set { _skipReason = value; }
+ get
+ {
+ return _skipReason ??
+ $"Test only runs for SQLite versions >= { Min ?? "Any"} and <= { Max ?? "Any" }"
+ + (Skip == null ? "" : "and skipping on " + Skip);
+ }
+ }
+ }
+}
\ No newline at end of file