2013-02-18 37 views
2

我試圖在SQLite for Windows Runtime中實現自定義排序規則。在SQLite中爲WinRT實現自定義排序規則

的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

有人可以幫我嗎?

謝謝!

回答

2

一段時間後,環顧四周內Mono.Android.SQLite,後者也使用C語言實現的SQLite,我找到了解決辦法:

的問題是調用sqlite3_create_collat​​ion有一個void *參數,我不當定義爲C#中的對象,它應該是IntPtr。

我已經發布了我現在的實現。我部分反向設計了Mono實現的解決方案,該解決方案針對要註冊的每個排序規則調用sqlite3_create_collat​​ion兩次 - 一次使用參數eTextRep設置爲SQLITE_UTF16LE,第二次使用SQLITE_UTF8。我只能想象這可能有助於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)); 
    } 
+0

你應該只能使用'SQLITE_UTF8';這是SQLite字符串的本地格式。 – 2013-03-01 15:15:03

+0

謝謝!我會嘗試;) – 2013-05-15 21:26:09

+0

因此,只是爲了確認我明白,這種自定義整理會相對較慢,因爲實現是C#,從C調用?或者,如果使用C#自定義collat​​or執行表掃描,那麼可以期望合理的性能? – hyperspasm 2017-01-03 17:17:29