2

Windows ランタイムの SQLite でカスタム照合を実装しようとしています。

create_collat​​ion メソッドは次のように実装されます。

SQLITE_API int sqlite3_create_collation(
  sqlite3*, 
  const char *zName, 
  int eTextRep, 
  void *pArg,
  int(*xCompare)(void*,int,const void*,int,const void*)
);

これまでのところ、次の C# 署名があります。

[DllImport("sqlite3", EntryPoint = "sqlite3_create_collation", CallingConvention = CallingConvention.Cdecl)]
public static extern int CreateCollation(IntPtr db, [MarshalAs(UnmanagedType.LPStr)] string name, int textRep, object state, Compare callback);

public delegate int Compare(object pCompareArg, int size1, IntPtr Key1, int size2, IntPtr Key2);

これは実装です:

int i = CreateCollation(db, "unicode_nocase", SQLITE_UTF8, null, CompareMethod);

/* ... */

public static int CompareMethod(object o, int i1, IntPtr s1, int i2, IntPtr s2)
{
    return string.Compare(Marshal.PtrToStringUni(s1), Marshal.PtrToStringUni(s2));
}

アプリケーションはエラーなしでコンパイルされます。create_collat​​ion の呼び出しはゼロ (SQLITE_OK) を返しますが、ステートメントで照合を使用すると、次のエラー メッセージが返されます。

no such collation sequence: unicode_nocase

ソース参照: https://github.com/doo/SQLite3-WinRT/tree/master/SQLite3Component

誰か助けてくれませんか?

ありがとうございました!

4

1 に答える 1

2

SQLite の C 実装も使用する Mono.Android.SQLite 内をしばらく調べた後、解決策を見つけました。

問題は、sqlite3_create_collat​​ion の呼び出しに void* パラメーターが含まれていることでした。これは、IntPtr である必要がある C# のオブジェクトとして誤って定義しました。

現在の実装を以下に掲載しました。私は Mono 実装からソリューションを部分的にリバース エンジニアリングしました。これは、すべての照合順序を登録するために sqlite3_create_collat​​ion を 2 回呼び出します。これは、SQLite コアが、文字列値が格納されるさまざまな形式の高速な実装を見つけるのに役立つかもしれないと想像することしかできませんでした。ただし、これらを C# 文字列に変換するときは、別のデコードが必要です。

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    private delegate int CompareCallback(IntPtr pvUser, int len1, IntPtr pv1, int len2, IntPtr pv2);

    [DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl)]
    private static extern int sqlite3_create_collation(IntPtr db, byte[] strName, int nType, IntPtr pvUser, CompareCallback func);

    private const int SQLITE_UTF8 = 1;
    private const int SQLITE_UTF16LE = 2;
    private const int SQLITE_UTF16BE = 3;
    private const int SQLITE_UTF16 = 4;    /* Use native byte order */
    private const int SQLITE_ANY = 5;    /* sqlite3_create_function only */
    private const int SQLITE_UTF16_ALIGNED = 8;    /* sqlite3_create_collation only */

    public void Register(IntPtr db)
    {
        if (db == IntPtr.Zero)
            throw new ArgumentNullException("db");

        //create null-terminated UTF8 byte array
        string name = Name;
        var nameLength = System.Text.Encoding.UTF8.GetByteCount(name);
        var nameBytes = new byte[nameLength + 1];
        System.Text.Encoding.UTF8.GetBytes(name, 0, name.Length, nameBytes, 0);

        //register UTF16 comparison
        int result = sqlite3_create_collation(db, nameBytes, SQLITE_UTF16LE, IntPtr.Zero, CompareUTF16);
        if (result != 0)
        {
            string msg = SQLite3.GetErrmsg(db);
            throw SQLiteException.New((SQLite3.Result)result, msg);
        }

        //register UTF8 comparison
        result = sqlite3_create_collation(db, nameBytes, SQLITE_UTF8, IntPtr.Zero, CompareUTF8);
        if (result != 0)
        {
            string msg = SQLite3.GetErrmsg(db);
            throw SQLiteException.New((SQLite3.Result)result, msg);
        }
    }

    private string GetUTF8String(IntPtr ptr, int len)
    {
        if (len == 0 || ptr == IntPtr.Zero)
            return string.Empty;

        if (len == -1)
        {
            do
            {
                len++;
            }
            while (Marshal.ReadByte(ptr, len) != 0);
        }

        byte[] array = new byte[len];
        Marshal.Copy(ptr, array, 0, len);

        return Encoding.UTF8.GetString(array, 0, len);
    }

    private string GetUTF16String(IntPtr ptr, int len)
    {
        if (len == 0 || ptr == IntPtr.Zero)
            return string.Empty;

        if (len == -1)
        {
            return Marshal.PtrToStringUni(ptr);
        }

        return Marshal.PtrToStringUni(ptr, len / 2);
    }

    internal int CompareUTF8(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
    {
        return Compare(GetUTF8String(ptr1, len1), GetUTF8String(ptr2, len2));
    }

    internal int CompareUTF16(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
    {
        return Compare(GetUTF16String(ptr1, len1), GetUTF16String(ptr2, len2));
    }
于 2013-03-01T14:50:12.577 に答える