Skip to content
This repository was archived by the owner on Nov 1, 2018. It is now read-only.

Add SqliteConnection.CreateCollation method #340

Merged
merged 8 commits into from
Apr 20, 2017
24 changes: 24 additions & 0 deletions src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,30 @@ protected override void Dispose(bool disposing)
protected override DbCommand CreateDbCommand()
=> CreateCommand();

/// <summary>
/// Create custom collation.
/// </summary>
/// <param name="name">Name of the collation.</param>
/// <param name="comparison">Method that compares two strings.</param>
public virtual void CreateCollation(string name, Comparison<string> comparison)
Copy link
Contributor

Choose a reason for hiding this comment

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

Passing a null comparison will currently result in a NullReferenceException. We should either:

  1. Throw ArgumentNullException
  2. Support it by flowing null into sqlite3_create_collation (which removes the collation)

Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably validate name too if the native API gives its "misuse" error for null or empty values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opted for option 2 and added test.

=> CreateSqlite3Collation(name, null, (_, s1, s2) => comparison(s1, s2));
Copy link
Contributor

Choose a reason for hiding this comment

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

Personally, I'd just call CreateCollation<object>(name, null,... and inline CreateSqlite3Collation into the other overload.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.


/// <summary>
/// Create custom collation.
/// </summary>
/// <typeparam name="T">The type of the state object.</typeparam>
/// <param name="name">Name of the collation.</param>
/// <param name="state">State object passed to each invokation of the collation.</param>
/// <param name="comparison">Method that compares two strings, using additional state.</param>
public virtual void CreateCollation<T>(string name, T state, Func<T, string, string, int> comparison)
=> CreateSqlite3Collation(name, state, (v, s1, s2) => comparison((T)v, s1, s2));

private void CreateSqlite3Collation(string name, object state, delegate_collation collation)
{
var rc = raw.sqlite3_create_collation(_db, name, state, collation);
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to validate name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added validation.

SqliteException.ThrowExceptionForRC(rc, _db);
}

/// <summary>
/// Begins a transaction on the connection.
/// </summary>
Expand Down
37 changes: 36 additions & 1 deletion test/Microsoft.Data.Sqlite.Tests/SqliteConnectionTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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 System.Collections.Generic;
using System.Data;
using System.IO;
using Microsoft.Data.Sqlite.Properties;
Expand Down Expand Up @@ -310,6 +311,40 @@ public void CreateCommand_returns_command()
}
}

[Fact]
public void CreateCollation_works()
{
using (var connection = new SqliteConnection("Data Source=:memory:"))
{
connection.Open();
connection.CreateCollation("MY_NOCASE", (s1, s2) => string.Compare(s1, s2, StringComparison.OrdinalIgnoreCase));

Assert.Equal(1L, connection.ExecuteScalar<long>("SELECT 'Νικοσ' = 'ΝΙΚΟΣ' COLLATE MY_NOCASE;"));
}
}

[Fact]
public void CreateCollation_works_with_state()
{
using (var connection = new SqliteConnection("Data Source=:memory:"))
{
connection.Open();
var list = new List<string>();
connection.CreateCollation<List<string>>(
"MY_NOCASE",
list,
(l, s1, s2) =>
{
l.Add("Invoked");
return string.Compare(s1, s2, StringComparison.OrdinalIgnoreCase);
});

Assert.Equal(1L, connection.ExecuteScalar<long>("SELECT 'Νικοσ' = 'ΝΙΚΟΣ' COLLATE MY_NOCASE;"));
Assert.Equal(1, list.Count);
Assert.Equal("Invoked", list[0]);
}
}

[Fact]
public void BeginTransaction_throws_when_closed()
{
Expand Down