2013-02-18 102 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