Skip to content
This repository was archived by the owner on Nov 1, 2018. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
6 changes: 1 addition & 5 deletions src/Microsoft.Data.Sqlite/Interop/MarshalEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
3 changes: 3 additions & 0 deletions src/Microsoft.Data.Sqlite/Interop/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Data.Sqlite/Interop/Sqlite3Handle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
74 changes: 74 additions & 0 deletions src/Microsoft.Data.Sqlite/Interop/VersionedMethods.cs
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could take this one step further with the strategy pattern. You'd set the strategy once in the static constructor based on the version then each method wouldn't have to check each time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, strategy pattern is a lot cleaner. I updated the implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is beautiful now. Well done.

{
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;
}
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.Data.Sqlite/Properties/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Microsoft.Data.Sqlite/SqliteConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion src/Microsoft.Data.Sqlite/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@
<value>No mapping exists from object type {typeName} to a known managed provider native type.</value>
</data>
<data name="SqliteNativeError" xml:space="preserve">
<value>SQLite Error {errorCode}: '{message}'</value>
<value>SQLite Error {errorCode}: '{message}'.</value>
</data>
<data name="DefaultNativeError" xml:space="preserve">
<value>For more information on this error code see https://www.sqlite.org/rescode.html</value>
</data>
</root>
18 changes: 14 additions & 4 deletions test/Microsoft.Data.Sqlite.Tests/SqliteConcurrencyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,12 @@ public void It_throws_sqlite_busy_on_deadlock()
var ex = Assert.Throws<SqliteException>(() => 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();
Expand Down Expand Up @@ -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();
Expand All @@ -202,6 +210,8 @@ public void Command_times_out()
}
}
}

private Version CurrentVersion => new Version(NativeMethods.sqlite3_libversion());

private const string FileName = "./concurrency.db";

Expand Down
4 changes: 3 additions & 1 deletion test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Data;
using System.IO;
using Microsoft.AspNet.Testing.xunit;
using Xunit;

namespace Microsoft.Data.Sqlite
Expand Down Expand Up @@ -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"))
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}