diff --git a/Native.Csharp.Sdk/Cqp/CqApi.cs b/Native.Csharp.Sdk/Cqp/CqApi.cs
index a63172b6..8829bffb 100644
--- a/Native.Csharp.Sdk/Cqp/CqApi.cs
+++ b/Native.Csharp.Sdk/Cqp/CqApi.cs
@@ -1,13 +1,13 @@
-using Native.Csharp.Sdk.Cqp.Core;
-using Native.Csharp.Sdk.Cqp.Enum;
-using Native.Csharp.Sdk.Cqp.Model;
-using Native.Csharp.Sdk.Cqp.Other;
-using Native.Csharp.Tool;
-using System;
+using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
+using Native.Csharp.Sdk.Cqp.Core;
+using Native.Csharp.Sdk.Cqp.Enum;
+using Native.Csharp.Sdk.Cqp.Model;
+using Native.Csharp.Sdk.Cqp.Other;
namespace Native.Csharp.Sdk.Cqp
{
@@ -16,6 +16,7 @@ public class CqApi
#region --字段--
private int _authCode = 0;
private string _appDirCache = null;
+ private Encoding _defaultEncoding = null;
#endregion
#region --属性--
@@ -27,12 +28,13 @@ public class CqApi
#region --构造函数--
///
- /// 初始化一个 CqApi 类的新实例, 该实例将由酷Q授权
+ /// 初始化一个 类的新实例, 该实例将由 Initialize ()
函数授权
///
/// 插件验证码
public CqApi (int authCode)
{
this._authCode = authCode;
+ this._defaultEncoding = Encoding.GetEncoding ("GB18030");
}
#endregion
@@ -237,8 +239,9 @@ public string CqCode_Record (string filePath)
/// 消息内容
public int SendGroupMessage (long groupId, string message)
{
- return CQP.CQ_sendGroupMsg (_authCode, groupId, NativeConvert.ToStringPtr (message, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_sendGroupMsg (_authCode, groupId, message.ToIntPtr (_defaultEncoding));
}
+
///
/// 发送私聊消息
///
@@ -247,8 +250,9 @@ public int SendGroupMessage (long groupId, string message)
///
public int SendPrivateMessage (long qqId, string message)
{
- return CQP.CQ_sendPrivateMsg (_authCode, qqId, NativeConvert.ToStringPtr (message, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_sendPrivateMsg (_authCode, qqId, message.ToIntPtr (_defaultEncoding));
}
+
///
/// 发送讨论组消息
///
@@ -257,8 +261,9 @@ public int SendPrivateMessage (long qqId, string message)
///
public int SendDiscussMessage (long discussId, string message)
{
- return CQP.CQ_sendDiscussMsg (_authCode, discussId, NativeConvert.ToStringPtr (message, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_sendDiscussMsg (_authCode, discussId, message.ToIntPtr (_defaultEncoding));
}
+
///
/// 发送赞
///
@@ -269,6 +274,7 @@ public int SendPraise (long qqId, int count = 1)
{
return CQP.CQ_sendLikeV2 (_authCode, qqId, (count <= 0 || count > 10) ? 1 : count);
}
+
///
/// 接收消息中的语音(record),返回语音文件绝对路径
///
@@ -280,6 +286,7 @@ public string ReceiveRecord (string fileName, AudioOutFormat formatType)
//return CQP.CQ_getRecord (_authCode, fileName, formatType.ToString ());
return CQP.CQ_getRecordV2 (_authCode, fileName, formatType.ToString ());
}
+
///
/// 接收消息中的图片(image),返回图片文件绝对路径
///
@@ -289,6 +296,7 @@ public string ReceiveImage (string fileName)
{
return CQP.CQ_getImage (_authCode, fileName);
}
+
///
/// 撤回消息
///
@@ -309,14 +317,16 @@ public long GetLoginQQ ()
{
return CQP.CQ_getLoginQQ (_authCode);
}
+
///
/// 获取当前登录QQ的昵称
///
///
public string GetLoginNick ()
{
- return NativeConvert.ToPtrString (CQP.CQ_getLoginNick (_authCode));
+ return CQP.CQ_getLoginNick (_authCode).ToString (_defaultEncoding);
}
+
///
/// 取应用目录
///
@@ -329,6 +339,7 @@ public string GetAppDirectory ()
}
return _appDirCache;
}
+
///
/// 获取Cookies 慎用,此接口需要严格授权
///
@@ -337,6 +348,7 @@ public string GetCookies ()
{
return CQP.CQ_getCookies (_authCode);
}
+
///
/// 即QQ网页用到的bkn/g_tk等 慎用,此接口需要严格授权
///
@@ -345,6 +357,7 @@ public int GetCsrfToken ()
{
return CQP.CQ_getCsrfToken (_authCode);
}
+
///
/// 获取QQ信息
///
@@ -360,14 +373,15 @@ public int GetQQInfo (long qqId, out QQ qqInfo, bool notCache = false)
qqInfo = null;
return -1000;
}
- UnPack unpack = new UnPack (Convert.FromBase64String (result));
+ BinaryReader binary = new BinaryReader (new MemoryStream (Convert.FromBase64String (result)));
qqInfo = new QQ ();
- qqInfo.Id = unpack.GetInt64 ();
- qqInfo.Nick = unpack.GetString (Encoding.GetEncoding ("GB18030"));
- qqInfo.Sex = (Sex)unpack.GetInt32 ();
- qqInfo.Age = unpack.GetInt32 ();
+ qqInfo.Id = binary.ReadInt64_Ex ();
+ qqInfo.Nick = binary.ReadString_Ex (_defaultEncoding);
+ qqInfo.Sex = (Sex)binary.ReadInt32_Ex ();
+ qqInfo.Age = binary.ReadInt32_Ex ();
return 0;
}
+
///
/// 获取群成员信息
///
@@ -385,27 +399,28 @@ public int GetMemberInfo (long groupId, long qqId, out GroupMember member, bool
return -1000;
}
#region --其它_转换_文本到群成员信息--
+ BinaryReader binary = new BinaryReader (new MemoryStream (Convert.FromBase64String (result)));
member = new GroupMember ();
- UnPack unpack = new UnPack (Convert.FromBase64String (result));
- member.GroupId = unpack.GetInt64 ();
- member.QQId = unpack.GetInt64 ();
- member.Nick = unpack.GetString (Encoding.GetEncoding ("GB18030"));
- member.Card = unpack.GetString (Encoding.GetEncoding ("GB18030"));
- member.Sex = (Sex)unpack.GetInt32 ();
- member.Age = unpack.GetInt32 ();
- member.Area = unpack.GetString (Encoding.GetEncoding ("GB18030"));
- member.JoiningTime = NativeConvert.FotmatUnixTime (unpack.GetInt32 ().ToString ());
- member.LastDateTime = NativeConvert.FotmatUnixTime (unpack.GetInt32 ().ToString ());
- member.Level = unpack.GetString (Encoding.GetEncoding ("GB18030"));
- member.PermitType = (PermitType)unpack.GetInt32 ();
- member.BadRecord = unpack.GetInt32 () == 1;
- member.SpecialTitle = unpack.GetString (Encoding.GetEncoding ("GB18030"));
- member.SpecialTitleDurationTime = NativeConvert.FotmatUnixTime (unpack.GetInt32 ().ToString ());
- member.CanModifiedCard = unpack.GetInt32 () == 1;
+ member.GroupId = binary.ReadInt64_Ex ();
+ member.QQId = binary.ReadInt64_Ex ();
+ member.Nick = binary.ReadString_Ex (_defaultEncoding);
+ member.Card = binary.ReadString_Ex (_defaultEncoding);
+ member.Sex = (Sex)binary.ReadInt32_Ex ();
+ member.Age = binary.ReadInt32_Ex ();
+ member.Area = binary.ReadString_Ex (_defaultEncoding);
+ member.JoiningTime = binary.ReadInt32_Ex ().ToDateTime ();
+ member.LastDateTime = binary.ReadInt32_Ex ().ToDateTime ();
+ member.Level = binary.ReadString_Ex (_defaultEncoding);
+ member.PermitType = (PermitType)binary.ReadInt32_Ex ();
+ member.BadRecord = binary.ReadInt32_Ex () == 1;
+ member.SpecialTitle = binary.ReadString_Ex (_defaultEncoding);
+ member.SpecialTitleDurationTime = binary.ReadInt32_Ex ().ToDateTime ();
+ member.CanModifiedCard = binary.ReadInt32_Ex () == 1;
#endregion
return 0;
}
+
///
/// 获取群成员列表
///
@@ -421,43 +436,44 @@ public int GetMemberList (long groupId, out List memberInfos)
return -1000;
}
#region --其他_转换_文本到群成员列表信息a--
- UnPack unpack = new UnPack (Convert.FromBase64String (result));
+ BinaryReader binary = new BinaryReader (new MemoryStream (Convert.FromBase64String (result)));
memberInfos = new List ();
- for (int i = 0, len = unpack.GetInt32 (); i < len; i++)
+ for (int i = 0, len = binary.ReadInt32_Ex (); i < len; i++)
{
- if (unpack.OverLength <= 0)
+ if (binary.Length () <= 0)
{
memberInfos = null;
return -1000;
}
#region --其它_转换_ansihex到群成员信息--
- UnPack temp = new UnPack (unpack.GetToken ()); //解析群成员信息
+ BinaryReader tempBinary = new BinaryReader (new MemoryStream (binary.ReadToken_Ex ())); //解析群成员信息
GroupMember member = new GroupMember ();
- member.GroupId = temp.GetInt64 ();
- member.QQId = temp.GetInt64 ();
- member.Nick = temp.GetString (Encoding.GetEncoding ("GB18030"));
- member.Card = temp.GetString (Encoding.GetEncoding ("GB18030"));
- member.Sex = (Sex)temp.GetInt32 ();
- member.Age = temp.GetInt32 ();
- member.Area = temp.GetString (Encoding.GetEncoding ("GB18030"));
- member.JoiningTime = NativeConvert.FotmatUnixTime (temp.GetInt32 ().ToString ());
- member.LastDateTime = NativeConvert.FotmatUnixTime (temp.GetInt32 ().ToString ());
- member.Level = temp.GetString (Encoding.GetEncoding ("GB18030"));
- member.PermitType = (PermitType)temp.GetInt32 ();
- member.BadRecord = temp.GetInt32 () == 1;
- member.SpecialTitle = temp.GetString (Encoding.GetEncoding ("GB18030"));
- member.SpecialTitleDurationTime = NativeConvert.FotmatUnixTime (temp.GetInt32 ().ToString ());
- member.CanModifiedCard = temp.GetInt32 () == 1;
+ member.GroupId = tempBinary.ReadInt64_Ex ();
+ member.QQId = tempBinary.ReadInt64_Ex ();
+ member.Nick = tempBinary.ReadString_Ex (_defaultEncoding);
+ member.Card = tempBinary.ReadString_Ex (_defaultEncoding);
+ member.Sex = (Sex)tempBinary.ReadInt32_Ex ();
+ member.Age = tempBinary.ReadInt32_Ex ();
+ member.Area = tempBinary.ReadString_Ex (_defaultEncoding);
+ member.JoiningTime = tempBinary.ReadInt32_Ex ().ToDateTime ();
+ member.LastDateTime = tempBinary.ReadInt32_Ex ().ToDateTime ();
+ member.Level = tempBinary.ReadString_Ex (_defaultEncoding);
+ member.PermitType = (PermitType)tempBinary.ReadInt32_Ex ();
+ member.BadRecord = tempBinary.ReadInt32_Ex () == 1;
+ member.SpecialTitle = tempBinary.ReadString_Ex (_defaultEncoding);
+ member.SpecialTitleDurationTime = binary.ReadInt32_Ex ().ToDateTime ();
+ member.CanModifiedCard = tempBinary.ReadInt32_Ex () == 1;
#endregion
memberInfos.Add (member);
}
#endregion
return 0;
}
+
///
/// 获取群列表
///
- ///
+ /// 返回的群列表
///
public int GetGroupList (out List groups)
{
@@ -469,25 +485,26 @@ public int GetGroupList (out List groups)
}
groups = new List ();
#region --其他_转换_文本到群列表信息a--
- UnPack unpack = new UnPack (Convert.FromBase64String (result));
- for (int i = 0, len = unpack.GetInt32 (); i < len; i++)
+ BinaryReader binary = new BinaryReader (new MemoryStream (Convert.FromBase64String (result)));
+ for (int i = 0, len = binary.ReadInt32_Ex (); i < len; i++)
{
- if (unpack.OverLength <= 0)
+ if (binary.Length () <= 0)
{
groups = null;
return -1000;
}
#region --其他_转换_ansihex到群信息--
- UnPack temp = new UnPack (unpack.GetToken ());
+ BinaryReader tempBinary = new BinaryReader (new MemoryStream (binary.ReadToken_Ex ()));
Group group = new Group ();
- group.Id = temp.GetInt64 ();
- group.Name = temp.GetString (Encoding.GetEncoding ("GB18030"));
+ group.Id = tempBinary.ReadInt64_Ex ();
+ group.Name = tempBinary.ReadString_Ex (_defaultEncoding);
groups.Add (group);
#endregion
}
#endregion
return 0;
}
+
///
/// 获取发送语音支持
///
@@ -496,6 +513,7 @@ public bool GetSendRecordSupport ()
{
return CQP.CQ_canSendRecord (_authCode) > 0;
}
+
///
/// 获取发送图片支持
///
@@ -516,8 +534,9 @@ public bool GetSendImageSupport ()
///
public int AddLoger (LogerLevel level, string type, string content)
{
- return CQP.CQ_addLog (_authCode, (int)level, type, NativeConvert.ToStringPtr (content, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_addLog (_authCode, (int)level, type, content.ToIntPtr (_defaultEncoding));
}
+
///
/// 添加致命错误提示
///
@@ -543,8 +562,9 @@ public int SetFriendAddRequest (string tag, ResponseType response, string append
{
appendMsg = string.Empty;
}
- return CQP.CQ_setFriendAddRequest (_authCode, tag, (int)response, NativeConvert.ToStringPtr (appendMsg, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_setFriendAddRequest (_authCode, tag, (int)response, appendMsg.ToIntPtr (_defaultEncoding));
}
+
///
/// 置群添加请求
///
@@ -559,7 +579,7 @@ public int SetGroupAddRequest (string tag, RequestType request, ResponseType res
{
appendMsg = string.Empty;
}
- return CQP.CQ_setGroupAddRequestV2 (_authCode, tag, (int)request, (int)response, NativeConvert.ToStringPtr (appendMsg, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_setGroupAddRequestV2 (_authCode, tag, (int)request, (int)response, appendMsg.ToIntPtr (_defaultEncoding));
}
#endregion
@@ -578,8 +598,9 @@ public int SetGroupAnonymousBanSpeak (long groupId, string anonymous, TimeSpan t
time = TimeSpan.Zero;
}
- return CQP.CQ_setGroupAnonymousBan (_authCode, groupId, NativeConvert.ToStringPtr (anonymous, Encoding.GetEncoding ("GB18030")), (long)time.TotalSeconds);
+ return CQP.CQ_setGroupAnonymousBan (_authCode, groupId, anonymous.ToIntPtr (_defaultEncoding), (long)time.TotalSeconds);
}
+
///
/// 置群员禁言
///
@@ -595,6 +616,7 @@ public int SetGroupBanSpeak (long groupId, long qqId, TimeSpan time)
}
return CQP.CQ_setGroupBan (_authCode, groupId, qqId, (long)time.TotalSeconds);
}
+
///
/// 置全群禁言
///
@@ -605,6 +627,7 @@ public int SetGroupWholeBanSpeak (long groupId, bool isOpen)
{
return CQP.CQ_setGroupWholeBan (_authCode, groupId, isOpen);
}
+
///
/// 置群成员名片
///
@@ -614,8 +637,9 @@ public int SetGroupWholeBanSpeak (long groupId, bool isOpen)
///
public int SetGroupMemberNewCard (long groupId, long qqId, string newNick)
{
- return CQP.CQ_setGroupCard (_authCode, groupId, qqId, NativeConvert.ToStringPtr (newNick, Encoding.GetEncoding ("GB18030")));
+ return CQP.CQ_setGroupCard (_authCode, groupId, qqId, newNick.ToIntPtr (_defaultEncoding));
}
+
///
/// 置群成员专属头衔
///
@@ -630,8 +654,9 @@ public int SetGroupSpecialTitle (long groupId, long qqId, string specialTitle, T
{
time = new TimeSpan (-10000000); //-1秒
}
- return CQP.CQ_setGroupSpecialTitle (_authCode, groupId, qqId, NativeConvert.ToStringPtr (specialTitle, Encoding.GetEncoding ("GB18030")), (long)time.TotalSeconds);
+ return CQP.CQ_setGroupSpecialTitle (_authCode, groupId, qqId, specialTitle.ToIntPtr (_defaultEncoding), (long)time.TotalSeconds);
}
+
///
/// 置群管理员
///
@@ -643,6 +668,7 @@ public int SetGroupManager (long groupId, long qqId, bool isCalcel)
{
return CQP.CQ_setGroupAdmin (_authCode, groupId, qqId, isCalcel);
}
+
///
/// 置群匿名设置
///
@@ -653,6 +679,7 @@ public int SetAnonymousStatus (long groupId, bool isOpen)
{
return CQP.CQ_setGroupAnonymous (_authCode, groupId, isOpen);
}
+
///
/// 置群退出 慎用,此接口需要严格授权
///
@@ -663,6 +690,7 @@ public int SetGroupExit (long groupId, bool dissolve = false)
{
return CQP.CQ_setGroupLeave (_authCode, groupId, dissolve);
}
+
///
/// 置群员移除
///
@@ -674,6 +702,7 @@ public int SetGroupMemberRemove (long groupId, long qqId, bool notAccept = false
{
return CQP.CQ_setGroupKick (_authCode, groupId, qqId, notAccept);
}
+
///
/// 置讨论组退出
///
@@ -695,6 +724,7 @@ public void SetAuthCode (int authCode)
{
_authCode = authCode;
}
+
///
/// 获取App验证码
///
@@ -704,6 +734,7 @@ public int GetAuthCode ()
{
return _authCode;
}
+
///
/// 获取匿名信息
///
@@ -711,13 +742,14 @@ public int GetAuthCode ()
///
public GroupAnonymous GetAnonymous (string source)
{
- UnPack unPack = new UnPack (Convert.FromBase64String (source));
+ BinaryReader binary = new BinaryReader (new MemoryStream (Convert.FromBase64String (source)));
GroupAnonymous anonymous = new GroupAnonymous ();
- anonymous.Id = unPack.GetInt64 ();
- anonymous.CodeName = unPack.GetString (Encoding.GetEncoding ("GB18030"));
- anonymous.Token = unPack.GetToken ();
+ anonymous.Id = binary.ReadInt64_Ex ();
+ anonymous.CodeName = binary.ReadString_Ex ();
+ anonymous.Token = binary.ReadToken_Ex ();
return anonymous;
}
+
///
/// 获取群文件
///
@@ -725,14 +757,15 @@ public GroupAnonymous GetAnonymous (string source)
///
public GroupFile GetFile (string source)
{
- UnPack unPack = new UnPack (Convert.FromBase64String (source));
+ BinaryReader binary = new BinaryReader (new MemoryStream (Convert.FromBase64String (source)));
GroupFile file = new GroupFile ();
- file.Id = unPack.GetString (Encoding.GetEncoding ("GB18030"));
- file.Name = unPack.GetString (Encoding.GetEncoding ("GB18030"));
- file.Size = unPack.GetInt64 ();
- file.Busid = Convert.ToInt32 (unPack.GetInt64 ());
+ file.Id = binary.ReadString_Ex (); // 参照官方SDK, 编码为 ASCII
+ file.Name = binary.ReadString_Ex (); // 参照官方SDK, 编码为 ASCII
+ file.Size = binary.ReadInt64_Ex ();
+ file.Busid = Convert.ToInt32 (binary.ReadInt64_Ex ());
return file;
}
+
///
/// 编码悬浮窗数据置文本
///
@@ -740,11 +773,12 @@ public GroupFile GetFile (string source)
///
public string FormatStringFloatWindow (FloatWindow floatWindow)
{
- Pack pack = new Pack ();
- pack.SetLenString (floatWindow.Data);
- pack.SetLenString (floatWindow.Unit);
- pack.SetInt32 ((int)floatWindow.Color);
- return Convert.ToBase64String (pack.GetAll ());
+ BinaryWriter binary = new BinaryWriter (new MemoryStream ());
+ binary.Write_Ex (floatWindow.Data);
+ binary.Write_Ex (floatWindow.Unit);
+ binary.Write_Ex ((int)floatWindow.Color);
+
+ return Convert.ToBase64String (binary.ToArray ());
}
#endregion
}
diff --git a/Native.Csharp.Sdk/Cqp/Other/BinaryReaderExpand.cs b/Native.Csharp.Sdk/Cqp/Other/BinaryReaderExpand.cs
new file mode 100644
index 00000000..ca50d6e7
--- /dev/null
+++ b/Native.Csharp.Sdk/Cqp/Other/BinaryReaderExpand.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace Native.Csharp.Sdk.Cqp.Other
+{
+ ///
+ /// 类的扩展方法集
+ ///
+ public static class BinaryReaderExpand
+ {
+ #region --公开方法--
+ ///
+ /// 获取基础流的剩余长度
+ ///
+ ///
+ ///
+ public static long Length (this BinaryReader binary)
+ {
+ return binary.BaseStream.Length - binary.BaseStream.Position;
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取所有字节。
+ ///
+ /// 基础 对象
+ /// 读入 buffer 的字节数。 如果可用的字节没有请求的那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static byte[] ReadAll_Ex (this BinaryReader binary)
+ {
+ return GetBinary (binary, binary.BaseStream.Length, false);
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取指定字节长度。
+ ///
+ /// 基础 对象
+ /// 要读取的字节数。
+ /// 读入 buffer 的字节数。 如果可用的字节没有请求的那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// len 为负数。
+ /// 已解码要读取的字符数超过了边界。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static byte[] ReadBin_Ex (this BinaryReader binary, long len)
+ {
+ return GetBinary (binary, len);
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取 2 字节长度并反序为 值。
+ ///
+ /// 基础 对象
+ /// 读入 2 字节的结果值,如果可用的字节没有那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// 已解码要读取的字符数超过了边界。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static short ReadInt16_Ex (this BinaryReader binary)
+ {
+ return BitConverter.ToInt16 (GetBinary (binary, 2, true), 0);
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取 4 字节长度并反序为 值。
+ ///
+ /// 基础 对象
+ /// 读入 4 字节的结果值,如果可用的字节没有那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// 已解码要读取的字符数超过了边界。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static int ReadInt32_Ex (this BinaryReader binary)
+ {
+ return BitConverter.ToInt32 (GetBinary (binary, 4, true), 0);
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取 8 字节长度并反序为 值。
+ ///
+ /// 基础 对象
+ /// 读入 8 字节的结果值,如果可用的字节没有那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// 已解码要读取的字符数超过了边界。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static long ReadInt64_Ex (this BinaryReader binary)
+ {
+ return BitConverter.ToInt64 (GetBinary (binary, 8, true), 0);
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取指定字节长度。
+ ///
+ /// 基础 对象
+ /// 读入 buffer 的字节数。 如果可用的字节没有请求的那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// len 为负数。
+ /// 已解码要读取的字符数超过了边界。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static byte[] ReadToken_Ex (this BinaryReader binary)
+ {
+ short len = ReadInt16_Ex (binary);
+ return GetBinary (binary, len);
+ }
+
+ ///
+ /// 从字节数组中的指定点开始,从流中读取指定编码的字符串。
+ ///
+ /// 基础 对象
+ ///
+ /// 读入 buffer 的字节数。 如果可用的字节没有请求的那么多,此数可能小于所请求的字节数;如果到达了流的末尾,此数可能为零。
+ /// len 为负数。
+ /// 已解码要读取的字符数超过了边界。
+ /// 流已关闭。
+ /// 出现 I/O 错误。
+ public static string ReadString_Ex (this BinaryReader binary, Encoding encoding = null)
+ {
+ if (encoding == null)
+ {
+ encoding = Encoding.ASCII;
+ }
+
+ return encoding.GetString (ReadToken_Ex (binary));
+ }
+ #endregion
+
+ #region --私有方法--
+ private static byte[] GetBinary (BinaryReader binary, long len, bool isReverse = false)
+ {
+ byte[] buffer = new byte[len];
+ binary.Read (buffer, 0, buffer.Length);
+ if (isReverse)
+ {
+ buffer = buffer.Reverse ().ToArray ();
+ }
+ return buffer;
+ }
+ #endregion
+ }
+}
diff --git a/Native.Csharp.Sdk/Cqp/Other/BinaryWriterExpand.cs b/Native.Csharp.Sdk/Cqp/Other/BinaryWriterExpand.cs
new file mode 100644
index 00000000..bdc378eb
--- /dev/null
+++ b/Native.Csharp.Sdk/Cqp/Other/BinaryWriterExpand.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace Native.Csharp.Sdk.Cqp.Other
+{
+ ///
+ /// 类的扩展方法集
+ ///
+ public static class BinaryWriterExpand
+ {
+ #region --公开方法--
+ ///
+ /// 将写入基础流的 数值
+ ///
+ /// 基础 对象
+ /// 要写入的值
+ /// 出现 I/O 错误。
+ /// 流已关闭。
+ /// buffer 为 null。
+ public static void Write_Ex (this BinaryWriter binary, short value)
+ {
+ SetBinary (binary, BitConverter.GetBytes (value), true);
+ }
+
+ ///
+ /// 将写入基础流的 数值
+ ///
+ /// 基础 对象
+ /// 要写入的值
+ /// 出现 I/O 错误。
+ /// 流已关闭。
+ /// buffer 为 null。
+ public static void Write_Ex (this BinaryWriter binary, int value)
+ {
+ SetBinary (binary, BitConverter.GetBytes (value), true);
+ }
+
+ ///
+ /// 将写入基础流的 数值
+ ///
+ /// 基础 对象
+ /// 要写入的值
+ /// 出现 I/O 错误。
+ /// 流已关闭。
+ /// buffer 为 null。
+ public static void Write_Ex (this BinaryWriter binary, long value)
+ {
+ SetBinary (binary, BitConverter.GetBytes (value), true);
+ }
+
+ ///
+ /// 将写入基础流的 数值
+ ///
+ /// 基础 对象
+ /// 要写入的值
+ /// 出现 I/O 错误。
+ /// 流已关闭。
+ /// buffer 为 null。
+ public static void Write_Ex (this BinaryWriter binary, string value)
+ {
+ byte[] buffer = Encoding.Default.GetBytes (value);
+ Write_Ex (binary, (short)buffer.Length);
+ SetBinary (binary, buffer, false);
+ }
+
+ ///
+ /// 将基础流转换为相同的字节数组
+ ///
+ /// 基础 对象
+ public static byte[] ToArray (this BinaryWriter binary)
+ {
+ long position = binary.BaseStream.Position; // 记录原指针位置
+
+ byte[] buffer = new byte[binary.BaseStream.Length];
+ binary.BaseStream.Position = 0; // 设置读取位置为 0
+ binary.BaseStream.Read (buffer, 0, buffer.Length);
+
+ binary.BaseStream.Position = position; // 还原原指针位置
+ return buffer;
+ }
+ #endregion
+
+ #region --私有方法--
+ private static void SetBinary (BinaryWriter binary, byte[] buffer, bool isReverse)
+ {
+ if (isReverse)
+ {
+ buffer = buffer.Reverse ().ToArray ();
+ }
+ binary.Write (buffer);
+ }
+ #endregion
+ }
+}
diff --git a/Native.Csharp.Sdk/Cqp/Other/OtherExpand.cs b/Native.Csharp.Sdk/Cqp/Other/OtherExpand.cs
new file mode 100644
index 00000000..f51fe2fd
--- /dev/null
+++ b/Native.Csharp.Sdk/Cqp/Other/OtherExpand.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Native.Csharp.Sdk.Cqp.Other
+{
+ ///
+ /// 其它类扩展方法集
+ ///
+ public static class OtherExpand
+ {
+ #region --Kernel32--
+ [DllImport ("kernel32.dll", EntryPoint = "lstrlenA", CharSet = CharSet.Ansi)]
+ internal extern static int LstrlenA (IntPtr ptr);
+ #endregion
+
+ ///
+ /// 获取 Unix 时间戳的 表示形式
+ ///
+ /// unix 时间戳
+ ///
+ public static DateTime ToDateTime (this long unixTime)
+ {
+ DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime (new DateTime (1970, 1, 1));
+ TimeSpan toNow = new TimeSpan (unixTime);
+ DateTime daTime = dtStart.Add (toNow);
+ return daTime;
+ }
+
+ ///
+ /// 获取 Unix 时间戳的 表示形式
+ ///
+ /// unix 时间戳
+ ///
+ public static DateTime ToDateTime (this int unixTime)
+ {
+ DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime (new DateTime (1970, 1, 1));
+ TimeSpan toNow = new TimeSpan (unixTime);
+ DateTime daTime = dtStart.Add (toNow);
+ return daTime;
+ }
+
+ ///
+ /// 转换字符串的 实例对象
+ ///
+ /// 将转换的字符串
+ /// 目标编码格式
+ ///
+ public static IntPtr ToIntPtr (this string source, Encoding encoding = null)
+ {
+ if (encoding == null)
+ {
+ encoding = Encoding.ASCII;
+ }
+ byte[] buffer = encoding.GetBytes (source);
+ GCHandle hobj = GCHandle.Alloc (buffer, GCHandleType.Pinned);
+ return hobj.AddrOfPinnedObject ();
+ }
+
+ ///
+ /// 读取指针内所有的字节数组并编码为指定字符串
+ ///
+ /// 字符串的 对象
+ /// 目标编码格式
+ ///
+ public static string ToString (this IntPtr strPtr, Encoding encoding = null)
+ {
+ if (encoding == null)
+ {
+ encoding = Encoding.Default;
+ }
+
+ int len = LstrlenA (strPtr); //获取指针中数据的长度
+ if (len == 0)
+ {
+ return string.Empty;
+ }
+
+ byte[] buffer = new byte[len];
+ Marshal.Copy (strPtr, buffer, 0, len);
+ return encoding.GetString (buffer);
+ }
+ }
+}
diff --git a/Native.Csharp.Sdk/Cqp/Other/Pack.cs b/Native.Csharp.Sdk/Cqp/Other/Pack.cs
deleted file mode 100644
index 175ab0e6..00000000
--- a/Native.Csharp.Sdk/Cqp/Other/Pack.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Native.Csharp.Sdk.Cqp.Other
-{
- ///
- /// 装包类
- ///
- public class Pack
- {
- #region --字段--
- private readonly List _bytes = null;
- #endregion
-
- #region --属性--
- ///
- /// 获取当前字节集长度
- ///
- public int Length
- {
- get { return this._bytes.Count; }
- }
- #endregion
-
- #region --构造函数--
- public Pack()
- {
- _bytes = new List();
- }
- #endregion
-
- #region --公开方法--
- ///
- /// 取全部数据
- ///
- ///
- public byte[] GetAll()
- { return this._bytes.ToArray(); }
- ///
- /// 置byte[]
- ///
- ///
- public void SetBin(byte[] arr)
- {
- this._bytes.AddRange(arr);
- }
- ///
- /// 置字节
- ///
- ///
- public void SetByte(byte value)
- {
- this._bytes.Add(value);
- }
- ///
- /// 置类数据
- ///
- ///
- public void SetData(byte[] data)
- {
- this._bytes.Clear();
- this._bytes.AddRange(data);
-
- }
- ///
- /// 置Int16值
- ///
- ///
- public void SetInt16(short value)
- {
- this._bytes.AddRange(BitConverter.GetBytes(value));
- }
- ///
- /// 置Int32值
- ///
- ///
- public void SetInt32(int value)
- {
- this._bytes.AddRange(BitConverter.GetBytes(value));
- }
- ///
- /// 置Int64值
- ///
- ///
- public void SetInt64(long value)
- {
- this._bytes.AddRange(BitConverter.GetBytes(value));
- }
- ///
- /// 置字符串
- ///
- /// 欲设置字符串
- /// 解码格式
- public void SetString(string str, Encoding decode = null)
- {
- if (decode == null)
- {
- decode = Encoding.Default;
- }
- this._bytes.AddRange(decode.GetBytes(str));
- }
- ///
- /// 置令牌字符串
- ///
- ///
- ///
- public void SetLenString(string str, Encoding decode = null)
- {
- if (decode == null)
- {
- decode = Encoding.Default;
- }
- SetToken(decode.GetBytes(str));
- }
- ///
- /// 置令牌
- ///
- ///
- public void SetToken(byte[] buf)
- {
- SetInt16(Convert.ToInt16(buf.Length));
- SetBin(buf);
- }
- #endregion
- }
-}
diff --git a/Native.Csharp.Sdk/Cqp/Other/UnPack.cs b/Native.Csharp.Sdk/Cqp/Other/UnPack.cs
deleted file mode 100644
index 417702ea..00000000
--- a/Native.Csharp.Sdk/Cqp/Other/UnPack.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Native.Csharp.Sdk.Cqp.Other
-{
- ///
- /// 拆包类
- ///
- public class UnPack
- {
- #region --字段--
- private readonly byte[] _bytes = null;
- private int _index = 0;
- #endregion
-
- #region --属性--
- ///
- /// 获取当前解包数据的剩余长度
- ///
- public int OverLength
- {
- get
- {
- return this._bytes.Length - this._index + 1;
- }
- }
- #endregion
-
- #region --构造函数--
- ///
- /// 初始化 Native.Sdk.Cqp.Tool.Unpack 实例对象
- ///
- /// 预处理的 byte[]
- public UnPack(byte[] data)
- {
- this._bytes = data;
- }
- #endregion
-
- #region --公开方法--
- ///
- /// 获取剩余所有数据
- ///
- ///
- public byte[] GetAll()
- {
- return GetData(this._bytes.Length - this._index);
- }
- ///
- /// 获取指定长度 byte[]
- ///
- /// 欲获取数据的长度
- ///
- public byte[] GetBin(int len)
- {
- return this.GetData(len);
- }
- ///
- /// 获取一个 byte
- ///
- ///
- public byte GetByte()
- {
- return this.GetData(1)[0];
- }
- ///
- /// 获取一个 Int16 数据
- ///
- ///
- public short GetInt16()
- {
- return BitConverter.ToInt16(this.GetData(2, true), 0);
- }
- ///
- /// 获取一个 Int32 数据
- ///
- ///
- public int GetInt32()
- {
- return BitConverter.ToInt32(this.GetData(4, true), 0);
- }
- ///
- /// 获取一个 Int64 数据
- ///
- ///
- public long GetInt64()
- {
- return BitConverter.ToInt64(this.GetData(8, true), 0);
- }
- ///
- /// 获取一个 String 数据
- ///
- /// 编码格式, 默认: Default
- ///
- public string GetString(Encoding code = null)
- {
- if (code == null)
- {
- code = Encoding.Default;
- }
- short len = this.GetInt16();
- return code.GetString(this.GetData(len));
- }
- ///
- /// 获取令牌
- ///
- ///
- public byte[] GetToken()
- {
- short len = this.GetInt16();
- return GetBin(len);
- }
- #endregion
-
- #region --私有方法--
- ///
- /// 获取指定位数的 byte[], 并把游标向后移动指定长度
- ///
- /// 长度
- /// 是否反转, 默认: False
- ///
- private byte[] GetData(int len, bool isReverse = false)
- {
- byte[] temp = new byte[len];
- Buffer.BlockCopy(_bytes, _index, temp, 0, len);
- _index += len;
- return isReverse == true ? temp.Reverse().ToArray() : temp;
- }
- #endregion
- }
-}
diff --git a/Native.Csharp.Sdk/Native.Csharp.Sdk.csproj b/Native.Csharp.Sdk/Native.Csharp.Sdk.csproj
index 110c4502..26d51524 100644
--- a/Native.Csharp.Sdk/Native.Csharp.Sdk.csproj
+++ b/Native.Csharp.Sdk/Native.Csharp.Sdk.csproj
@@ -60,15 +60,10 @@
-
-
+
+
+
-
-
- {9db94cbf-6843-4ea3-9241-769124416fe9}
- Native.Csharp.Tool
-
-
\ No newline at end of file
diff --git a/Native.Csharp.Sdk/Properties/AssemblyInfo.cs b/Native.Csharp.Sdk/Properties/AssemblyInfo.cs
index c046c9a5..dc42f9f7 100644
--- a/Native.Csharp.Sdk/Properties/AssemblyInfo.cs
+++ b/Native.Csharp.Sdk/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion ("3.0.6.0525")]
-[assembly: AssemblyFileVersion ("3.0.6.0525")]
\ No newline at end of file
+[assembly: AssemblyVersion ("3.0.7.0607")]
+[assembly: AssemblyFileVersion ("3.0.7.0607")]
\ No newline at end of file
diff --git a/Native.Csharp.Tool/Core/Kernel32.cs b/Native.Csharp.Tool/Core/Kernel32.cs
deleted file mode 100644
index 75967d49..00000000
--- a/Native.Csharp.Tool/Core/Kernel32.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace Native.Csharp.Tool.Core
-{
- internal static class Kernel32
- {
- #region --常量--
- private const string DllName = "kernel32.dll";
- #endregion
-
- [DllImport (DllName)]
- internal extern static int GetPrivateProfileIntA (string segName, string keyName, int iDefault, string fileName);
-
- [DllImport (DllName)]
- internal extern static int GetPrivateProfileStringA (string segName, string keyName, string sDefault, StringBuilder buffer, int nSize, string fileName);
-
- [DllImport (DllName)]
- internal extern static int GetPrivateProfileSectionA (string segName, StringBuilder buffer, int nSize, string fileName);
-
- [DllImport (DllName)]
- internal extern static int GetPrivateProfileSectionNamesA (byte[] buffer, int iLen, string fileName);
-
- [DllImport (DllName)]
- internal extern static int WritePrivateProfileSectionA (string segName, string sValue, string fileName);
-
- [DllImport (DllName)]
- internal extern static int WritePrivateProfileStringA (string segName, string keyName, string sValue, string fileName);
-
- [DllImport (DllName, EntryPoint = "lstrlenA", CharSet = CharSet.Ansi)]
- internal extern static int LstrlenA (IntPtr ptr);
- }
-}
diff --git a/Native.Csharp.Tool/Http/HttpHelper.cs b/Native.Csharp.Tool/Http/HttpHelper.cs
deleted file mode 100644
index 65e760ba..00000000
--- a/Native.Csharp.Tool/Http/HttpHelper.cs
+++ /dev/null
@@ -1,424 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net;
-using System.Net.Security;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
-using System.Web;
-
-namespace Native.Csharp.Tool.Http
-{
- /**
- * Http访问的操作类来自 Flexlive.CQP.Framework 框架. 若有侵权请联系我删除重写
- */
- ///
- /// Http访问的操作类
- ///
- public static class HttpHelper
- {
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 参照页
- /// User-Agent HTTP标头
- /// Accept HTTP标头
- /// 超时时间
- /// HTTP 标头
- /// Cookies
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, string referer, string userAgent, string accept, int timeout, WebHeaderCollection header, CookieCollection cookies, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- string result = string.Empty;
- try
- {
- HttpWebRequest httpWebRequest = null;
- if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
- {
- ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
- httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
- httpWebRequest.ProtocolVersion = HttpVersion.Version10;
- }
- else
- {
- httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
- }
- httpWebRequest.UserAgent = userAgent;
- httpWebRequest.Referer = referer;
- httpWebRequest.Method = "GET";
- httpWebRequest.Timeout = timeout;
- httpWebRequest.Accept = accept;
- if (cookies != null)
- {
- httpWebRequest.CookieContainer = new CookieContainer();
- httpWebRequest.CookieContainer.Add(cookies);
- }
- if (header != null)
- {
- httpWebRequest.Headers = header;
- }
- httpWebRequest.AutomaticDecompression = decompression;
- HttpWebResponse webResponse = (HttpWebResponse)httpWebRequest.GetResponse();
- result = GetResponseString(webResponse, encoding);
- return result;
- }
- catch
- {
- return result;
- }
- }
-
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 参照页
- /// HTTP 标头
- /// Cookies
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, string referer, WebHeaderCollection header, CookieCollection cookies, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Get(url, referer, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 30000, header, cookies, encoding, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 参照页
- /// Cookies
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, string referer, CookieCollection cookies, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Get(url, referer, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 30000, null, cookies, encoding, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 参照页
- /// Cookies
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, string referer, CookieCollection cookies, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Get(url, referer, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 30000, null, cookies, Encoding.UTF8, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 参照页
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, string referer, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Get(url, referer, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 30000, null, null, encoding, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 参照页
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, string referer, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Get(url, referer, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 30000, null, null, Encoding.UTF8, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Get请求
- ///
- /// 请求地址
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Get(string url, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Get(url, "", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", 30000, null, null, Encoding.UTF8, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 参照页
- /// User-Agent HTTP标头
- /// Accept HTTP标头
- /// 超时时间
- /// HTTP 标头
- /// Cookies
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, string referer, string userAgent, string accept, int timeout, WebHeaderCollection header, CookieCollection cookies, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- string result = string.Empty;
- try
- {
- HttpWebRequest httpWebRequest = null;
- if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
- {
- ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
- httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
- httpWebRequest.ProtocolVersion = HttpVersion.Version10;
- }
- else
- {
- httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
- }
- httpWebRequest.UserAgent = userAgent;
- httpWebRequest.Referer = referer;
- httpWebRequest.Method = "POST";
- httpWebRequest.Timeout = timeout;
- httpWebRequest.Accept = accept;
- if (cookies != null)
- {
- httpWebRequest.CookieContainer = new CookieContainer();
- httpWebRequest.CookieContainer.Add(cookies);
- }
- if (header != null)
- {
- httpWebRequest.Headers = header;
- }
- httpWebRequest.AutomaticDecompression = decompression;
- httpWebRequest.ContentType = "application/x-www-form-urlencoded";
- if (parameters != null && parameters.Count > 0)
- {
- StringBuilder stringBuilder = new StringBuilder();
- int num = 0;
- foreach (string key in parameters.Keys)
- {
- stringBuilder.AppendFormat("{0}={1}", key, parameters[key]);
- if (num != parameters.Keys.Count - 1)
- {
- stringBuilder.Append("&");
- }
- num++;
- }
- httpWebRequest.ContentLength = stringBuilder.ToString().Length;
- byte[] bytes = Encoding.ASCII.GetBytes(stringBuilder.ToString());
- using (Stream stream = httpWebRequest.GetRequestStream())
- {
- stream.Write(bytes, 0, bytes.Length);
- }
- }
- HttpWebResponse webResponse = (HttpWebResponse)httpWebRequest.GetResponse();
- result = GetResponseString(webResponse, encoding);
- return result;
- }
- catch
- {
- return result;
- }
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 参照页
- /// HTTP 标头
- /// Cookies
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, string referer, WebHeaderCollection header, CookieCollection cookies, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Post(url, parameters, referer, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 30000, header, cookies, encoding, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 参照页
- /// Cookies
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, string referer, CookieCollection cookies, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Post(url, parameters, referer, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 30000, null, cookies, encoding, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 参照页
- /// Cookies
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, string referer, CookieCollection cookies, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Post(url, parameters, referer, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 30000, null, cookies, Encoding.UTF8, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 参照页
- /// 文本编码
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, string referer, Encoding encoding, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Post(url, parameters, referer, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 30000, null, null, encoding, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 参照页
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, string referer, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Post(url, parameters, referer, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 30000, null, null, Encoding.UTF8, decompression);
- }
-
- ///
- /// 向HTTP服务器发送Post请求
- ///
- /// 请求地址
- /// 请求参数
- /// 加密方式
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static string Post(string url, Dictionary parameters, DecompressionMethods decompression = DecompressionMethods.None)
- {
- return Post(url, parameters, "", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 30000, null, null, Encoding.UTF8, decompression);
- }
-
- ///
- /// 向HTTP服务器发送请求, 获取 byte[]
- ///
- /// Url地址
- ///
- [Obsolete("请使用 HttpWebClient")]
- public static byte[] GetData(string url)
- {
- byte[] result = null;
- try
- {
- WebClient webClient = new WebClient();
- webClient.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
- Stream stream = webClient.OpenRead(url);
- result = new byte[stream.Length];
- stream.Read(result, 0, result.Length);
- stream.Close();
- webClient = null;
- return result;
- }
- catch
- {
- return result;
- }
- }
-
- ///
- /// Url编码数据
- ///
- /// 要编码的数据
- /// 编码后的数据
- [Obsolete("请使用 HttpWebClient")]
- public static string UrlEncode(string data)
- {
- return HttpUtility.UrlEncode(data);
- }
-
- ///
- /// Url解码
- ///
- /// 要解码的数据
- /// 解码后的数据
- [Obsolete("请使用 HttpWebClient")]
- public static string UrlDecode(string data)
- {
- return HttpUtility.UrlDecode(data);
- }
-
- ///
- /// 获取请求的数据
- ///
- ///
- ///
- ///
- private static string GetResponseString(HttpWebResponse webResponse, Encoding encoding)
- {
- if (webResponse.ContentEncoding.ToLower().Contains("gzip"))
- {
- using (GZipStream stream = new GZipStream(webResponse.GetResponseStream(), CompressionMode.Decompress))
- {
- using (StreamReader streamReader = new StreamReader(stream, encoding))
- {
- return streamReader.ReadToEnd();
- }
- }
- }
- if (webResponse.ContentEncoding.ToLower().Contains("deflate"))
- {
- using (DeflateStream stream2 = new DeflateStream(webResponse.GetResponseStream(), CompressionMode.Decompress))
- {
- using (StreamReader streamReader2 = new StreamReader(stream2, encoding))
- {
- return streamReader2.ReadToEnd();
- }
- }
- }
- using (Stream stream3 = webResponse.GetResponseStream())
- {
- StreamReader streamReader3 = new StreamReader(stream3, encoding);
- return streamReader3.ReadToEnd();
- }
- }
-
- ///
- /// 验证证书
- ///
- ///
- ///
- ///
- ///
- ///
- private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
- {
- return true;
- }
- }
-}
diff --git a/Native.Csharp.Tool/Http/HttpTool.cs b/Native.Csharp.Tool/Http/HttpTool.cs
new file mode 100644
index 00000000..74f63085
--- /dev/null
+++ b/Native.Csharp.Tool/Http/HttpTool.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web;
+
+namespace Native.Csharp.Tool.Http
+{
+ ///
+ /// Http 工具类
+ ///
+ public static class HttpTool
+ {
+ ///
+ /// 使用默认编码对 URL 进行编码
+ ///
+ /// 要编码的地址
+ /// 编码后的地址
+ public static string UrlEncode (string url)
+ {
+ return HttpUtility.UrlEncode (url);
+ }
+
+ ///
+ /// 使用指定的编码 对 URL 进行编码
+ ///
+ /// 要编码的地址
+ /// 编码类型
+ /// 编码后的地址
+ public static string UrlEncode (string url, Encoding encoding)
+ {
+ return HttpUtility.UrlEncode (url, encoding);
+ }
+
+ ///
+ /// 使用默认编码对 URL 进行解码
+ ///
+ /// 要解码的地址
+ /// 编码后的地址
+ public static string UrlDecode (string url)
+ {
+ return HttpUtility.UrlDecode (url);
+ }
+
+ ///
+ /// 使用指定的编码 对 URL 进行解码
+ ///
+ /// 要解码的地址
+ /// 编码类型
+ /// 编码后的地址
+ public static string UrlDecode (string url, Encoding encoding)
+ {
+ return HttpUtility.UrlDecode (url, encoding);
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/Http/HttpWebClient.cs b/Native.Csharp.Tool/Http/HttpWebClient.cs
index f3750f34..00d34ab6 100644
--- a/Native.Csharp.Tool/Http/HttpWebClient.cs
+++ b/Native.Csharp.Tool/Http/HttpWebClient.cs
@@ -9,740 +9,692 @@
namespace Native.Csharp.Tool.Http
{
- ///
- /// 提供用于将数据发送到和接收来自通过 URI 确认的资源数据丰富的常用方法。
- ///
- public class HttpWebClient : WebClient
- {
- #region --属性--
- ///
- /// 获取或设置请求的方法
- ///
- /// 未提供任何方法。 - 或 - 方法字符串包含无效字符。
- public string Method { get; set; }
- ///
- /// 获取或设置 User-Agent HTTP 标头的值
- ///
- public string UserAgent { get; set; }
- ///
- /// 获取或设置 Referer HTTP 标头的值
- ///
- public string Referer { get; set; }
- ///
- /// 获取或设置获取 Intelnet 资源过程的超时值
- ///
- /// 指定的值是小于零,且不是 System.Threading.Timeout.Infinite。
- public int TimeOut { get; set; }
- ///
- /// 获取或设置 Accept HTTP 标头的值
- ///
- public string Accept { get; set; }
- ///
- /// 获取或设置与此请求关联的
- ///
- public CookieCollection CookieCollection { get; set; }
- ///
- /// 获取或设置 Content-Type HTTP 标头的值
- ///
- public string ContentType { get; set; }
- ///
- /// 获取或设置一个值, 该值指示请求是否跟应跟随重定向响应
- ///
- public bool AllowAutoRedirect { get; set; }
- ///
- /// 获取或设置请求将跟随的重定向的最大数目。
- ///
- /// 值设置为 0 或更小。
- public int MaximumAutomaticRedirections { get; set; }
- ///
- /// 获取或设置一个值, 该值指示是否与 Internal 建立持续型的连接
- ///
- public bool KeepAlive { get; set; }
- ///
- /// 获取或设置一个值, 该值指示是否获取 Internet 资源后自动合并关联的
- ///
- public bool AutoCookieMerge { get; set; }
- #endregion
+ ///
+ /// 提供用于将数据发送到和接收来自通过 URI 确认的资源数据丰富的常用方法。
+ ///
+ public class HttpWebClient : WebClient
+ {
+ #region --属性--
+ ///
+ /// 获取或设置请求的方法
+ ///
+ /// 未提供任何方法。 - 或 - 方法字符串包含无效字符。
+ public string Method { get; set; }
+ ///
+ /// 获取或设置 User-Agent HTTP 标头的值
+ ///
+ public string UserAgent { get; set; }
+ ///
+ /// 获取或设置 Referer HTTP 标头的值
+ ///
+ public string Referer { get; set; }
+ ///
+ /// 获取或设置获取 Intelnet 资源过程的超时值
+ ///
+ /// 指定的值是小于零,且不是 System.Threading.Timeout.Infinite。
+ public int TimeOut { get; set; }
+ ///
+ /// 获取或设置 Accept HTTP 标头的值
+ ///
+ public string Accept { get; set; }
+ ///
+ /// 获取或设置与此请求关联的
+ ///
+ public CookieCollection CookieCollection { get; set; }
+ ///
+ /// 获取或设置 Content-Type HTTP 标头的值
+ ///
+ public string ContentType { get; set; }
+ ///
+ /// 获取或设置一个值, 该值指示请求是否跟应跟随重定向响应
+ ///
+ public bool AllowAutoRedirect { get; set; }
+ ///
+ /// 获取或设置请求将跟随的重定向的最大数目。
+ ///
+ /// 值设置为 0 或更小。
+ public int MaximumAutomaticRedirections { get; set; }
+ ///
+ /// 获取或设置一个值, 该值指示是否与 Internal 建立持续型的连接
+ ///
+ public bool KeepAlive { get; set; }
+ ///
+ /// 获取或设置一个值, 该值指示是否获取 Internet 资源后自动合并关联的
+ ///
+ public bool AutoCookieMerge { get; set; }
+ #endregion
- #region --构造函数--
- ///
- /// 初始化 类的一个实例对象
- ///
- public HttpWebClient ()
- { }
- #endregion
+ #region --构造函数--
+ ///
+ /// 初始化 类的一个实例对象
+ ///
+ public HttpWebClient()
+ { }
+ #endregion
- #region --公开方法--
+ #region --公开方法--
- #region --Get--
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// User-Agent HTTP 标头
- /// Accept HTTP 标头
- /// 超时时间
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 代理 的 实例
- /// 文本编码
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, string userAgent, string accept, int timeout, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- HttpWebClient httpWebClient = new HttpWebClient ();
- httpWebClient.CookieCollection = cookies;
- httpWebClient.Headers = headers;
- httpWebClient.Referer = referer;
- httpWebClient.UserAgent = userAgent;
- httpWebClient.Accept = accept;
- httpWebClient.TimeOut = timeout;
- httpWebClient.Encoding = encoding;
- httpWebClient.Proxy = proxy;
- httpWebClient.AllowAutoRedirect = allowAutoRedirect;
- httpWebClient.AutoCookieMerge = autoCookieMerge;
- byte[] result = httpWebClient.DownloadData (new Uri (url));
- headers = httpWebClient.ResponseHeaders;
- cookies = httpWebClient.CookieCollection;
- return result;
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 代理 的 实例
- /// 文本编码
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Get (url, referer, string.Empty, string.Empty, 0, ref cookies, ref headers, proxy, encoding, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 文本编码
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Get (url, referer, ref cookies, ref headers, null, encoding, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Get (url, referer, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- WebHeaderCollection headers = new WebHeaderCollection ();
- return Get (url, referer, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
- {
- CookieCollection cookies = new CookieCollection ();
- return Get (url, referer, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, false);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, string referer, bool allowAutoRedirect = true)
- {
- WebHeaderCollection headers = new WebHeaderCollection ();
- return Get (url, referer, ref headers, allowAutoRedirect);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- WebHeaderCollection headers = new WebHeaderCollection ();
- return Get (url, string.Empty, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
- {
- CookieCollection cookies = new CookieCollection ();
- return Get (url, string.Empty, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, false);
- }
- ///
- /// 向服务器发送 HTTP GET 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Get (string url, bool allowAutoRedirect = true)
- {
- return Get (url, string.Empty, allowAutoRedirect);
- }
- #endregion
+ #region --Get--
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// User-Agent HTTP 标头
+ /// Accept HTTP 标头
+ /// 超时时间
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 代理 的 实例
+ /// 文本编码
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, string userAgent, string accept, int timeout, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ HttpWebClient httpWebClient = new HttpWebClient();
+ httpWebClient.CookieCollection = cookies;
+ httpWebClient.Headers = headers;
+ httpWebClient.Referer = referer;
+ httpWebClient.UserAgent = userAgent;
+ httpWebClient.Accept = accept;
+ httpWebClient.TimeOut = timeout;
+ httpWebClient.Encoding = encoding;
+ httpWebClient.Proxy = proxy;
+ httpWebClient.AllowAutoRedirect = allowAutoRedirect;
+ httpWebClient.AutoCookieMerge = autoCookieMerge;
+ byte[] result = httpWebClient.DownloadData(new Uri(url));
+ headers = httpWebClient.ResponseHeaders;
+ cookies = httpWebClient.CookieCollection;
+ return result;
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 代理 的 实例
+ /// 文本编码
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Get(url, referer, string.Empty, string.Empty, 0, ref cookies, ref headers, proxy, encoding, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 文本编码
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Get(url, referer, ref cookies, ref headers, null, encoding, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Get(url, referer, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ return Get(url, referer, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
+ {
+ CookieCollection cookies = new CookieCollection();
+ return Get(url, referer, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, false);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, string referer, bool allowAutoRedirect = true)
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ return Get(url, referer, ref headers, allowAutoRedirect);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ return Get(url, string.Empty, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
+ {
+ CookieCollection cookies = new CookieCollection();
+ return Get(url, string.Empty, ref cookies, ref headers, null, Encoding.UTF8, allowAutoRedirect, false);
+ }
+ ///
+ /// 向服务器发送 HTTP GET 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Get(string url, bool allowAutoRedirect = true)
+ {
+ return Get(url, string.Empty, allowAutoRedirect);
+ }
+ #endregion
- #region --Post--
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// User-Agent HTTP 标头
- /// Accept HTTP 标头
- /// 超时时间
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 代理 的 实例
- /// 文本编码
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, string userAgent, string accept, int timeout, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- HttpWebClient httpWebClient = new HttpWebClient ();
- httpWebClient.ContentType = contentType;
- httpWebClient.Referer = referer;
- httpWebClient.UserAgent = userAgent;
- httpWebClient.Accept = accept;
- httpWebClient.TimeOut = timeout;
- httpWebClient.CookieCollection = cookies;
- httpWebClient.Headers = headers;
- httpWebClient.Proxy = proxy;
- httpWebClient.AutoCookieMerge = autoCookieMerge;
- httpWebClient.AllowAutoRedirect = allowAutoRedirect;
- byte[] result = httpWebClient.UploadData (new Uri (url), data);
- headers = httpWebClient.ResponseHeaders;
- cookies = httpWebClient.CookieCollection;
- return result;
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 代理 的 实例
- /// 文本编码
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Post (url, data, contentType, referer, string.Empty, string.Empty, 0, ref cookies, ref headers, proxy, encoding, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 文本编码
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Post (url, data, contentType, referer, ref cookies, ref headers, null, encoding, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Post (url, data, contentType, referer, ref cookies, ref headers, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- WebHeaderCollection headers = new WebHeaderCollection ();
- return Post (url, data, contentType, referer, ref cookies, ref headers, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
- {
- CookieCollection cookies = new CookieCollection ();
- return Post (url, data, contentType, referer, ref cookies, ref headers, allowAutoRedirect, false);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 请求附带的 Cookies
- /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
- ///
- /// 跟随重定向响应
- /// 指定自动 合并
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
- {
- return Post (url, data, contentType, string.Empty, ref cookies, allowAutoRedirect, autoCookieMerge);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 请求附带的 Headers
- /// 此参数支持自动更新
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
- {
- return Post (url, data, contentType, string.Empty, ref headers, allowAutoRedirect);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 参考页链接
- /// 告知服务器, 访问时的来源地址
- ///
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, string referer, bool allowAutoRedirect = true)
- {
- WebHeaderCollection headers = new WebHeaderCollection ();
- return Post (url, data, contentType, referer, ref headers, allowAutoRedirect);
- }
- ///
- /// 向服务器发送 HTTP POST 请求
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, string contentType, bool allowAutoRedirect = true)
- {
- return Post (url, data, contentType, string.Empty, allowAutoRedirect);
- }
- ///
- /// 向服务器发送 HTTP POST 请求 : application/x-www-form-urlencoded
- ///
- /// 完整的网页地址
- /// 必须包含 "http://" 或 "https://"
- ///
- /// 请求所需的上传数据
- /// Content-Type HTTP 标头
- /// 跟随重定向响应
- /// 返回从 Internal 读取的 数组
- public static byte[] Post (string url, byte[] data, bool allowAutoRedirect = true)
- {
- return Post (url, data, string.Empty, string.Empty, allowAutoRedirect);
- }
- #endregion
+ #region --Post--
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// User-Agent HTTP 标头
+ /// Accept HTTP 标头
+ /// 超时时间
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 代理 的 实例
+ /// 文本编码
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, string userAgent, string accept, int timeout, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ HttpWebClient httpWebClient = new HttpWebClient();
+ httpWebClient.ContentType = contentType;
+ httpWebClient.Referer = referer;
+ httpWebClient.UserAgent = userAgent;
+ httpWebClient.Accept = accept;
+ httpWebClient.TimeOut = timeout;
+ httpWebClient.CookieCollection = cookies;
+ httpWebClient.Headers = headers;
+ httpWebClient.Proxy = proxy;
+ httpWebClient.AutoCookieMerge = autoCookieMerge;
+ httpWebClient.AllowAutoRedirect = allowAutoRedirect;
+ byte[] result = httpWebClient.UploadData(new Uri(url), data);
+ headers = httpWebClient.ResponseHeaders;
+ cookies = httpWebClient.CookieCollection;
+ return result;
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 代理 的 实例
+ /// 文本编码
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, WebProxy proxy, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Post(url, data, contentType, referer, string.Empty, string.Empty, 0, ref cookies, ref headers, proxy, encoding, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 文本编码
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, Encoding encoding, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Post(url, data, contentType, referer, ref cookies, ref headers, null, encoding, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, ref WebHeaderCollection headers, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Post(url, data, contentType, referer, ref cookies, ref headers, Encoding.UTF8, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ return Post(url, data, contentType, referer, ref cookies, ref headers, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
+ {
+ CookieCollection cookies = new CookieCollection();
+ return Post(url, data, contentType, referer, ref cookies, ref headers, allowAutoRedirect, false);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 请求附带的 Cookies
+ /// 此参数支持自动更新 , 若 参数为 True, 将合并新旧 Cookie
+ ///
+ /// 跟随重定向响应
+ /// 指定自动 合并
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, ref CookieCollection cookies, bool allowAutoRedirect = true, bool autoCookieMerge = true)
+ {
+ return Post(url, data, contentType, string.Empty, ref cookies, allowAutoRedirect, autoCookieMerge);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 请求附带的 Headers
+ /// 此参数支持自动更新
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, ref WebHeaderCollection headers, bool allowAutoRedirect = true)
+ {
+ return Post(url, data, contentType, string.Empty, ref headers, allowAutoRedirect);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 参考页链接
+ /// 告知服务器, 访问时的来源地址
+ ///
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, string referer, bool allowAutoRedirect = true)
+ {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ return Post(url, data, contentType, referer, ref headers, allowAutoRedirect);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// Content-Type HTTP 标头
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, string contentType, bool allowAutoRedirect = true)
+ {
+ return Post(url, data, contentType, string.Empty, allowAutoRedirect);
+ }
+ ///
+ /// 向服务器发送 HTTP POST 请求 : application/x-www-form-urlencoded
+ ///
+ /// 完整的网页地址
+ /// 必须包含 "http://" 或 "https://"
+ ///
+ /// 请求所需的上传数据
+ /// 跟随重定向响应
+ /// 返回从 Internal 读取的 数组
+ public static byte[] Post(string url, byte[] data, bool allowAutoRedirect = true)
+ {
+ return Post(url, data, string.Empty, string.Empty, allowAutoRedirect);
+ }
+ #endregion
- #region --Cookie--
- ///
- /// 合并更新
- ///
- /// 原始的Cookis
- /// 欲合并Cookies
- /// 返回处理过的
- public static CookieCollection UpdateCookie (CookieCollection oldCookies, CookieCollection newCookies)
- {
- if (oldCookies == null)
- {
- throw new ArgumentNullException ("oldCookies");
- }
- if (newCookies == null)
- {
- throw new ArgumentNullException ("newCookies");
- }
+ #region --Cookie--
+ ///
+ /// 合并更新
+ ///
+ /// 原始的Cookis
+ /// 欲合并Cookies
+ /// 返回处理过的
+ public static CookieCollection UpdateCookie(CookieCollection oldCookies, CookieCollection newCookies)
+ {
+ if (oldCookies == null)
+ {
+ throw new ArgumentNullException("oldCookies");
+ }
+ if (newCookies == null)
+ {
+ throw new ArgumentNullException("newCookies");
+ }
- for (int i = 0; i < newCookies.Count; i++)
- {
- int index = CheckCookie (oldCookies, newCookies[i].Name);
- if (index >= 0)
- {
- oldCookies[index].Value = newCookies[i].Value;
- }
- else
- {
- oldCookies.Add (newCookies[i]);
- }
- }
- return oldCookies;
- }
- #endregion
+ for (int i = 0; i < newCookies.Count; i++)
+ {
+ int index = CheckCookie(oldCookies, newCookies[i].Name);
+ if (index >= 0)
+ {
+ oldCookies[index].Value = newCookies[i].Value;
+ }
+ else
+ {
+ oldCookies.Add(newCookies[i]);
+ }
+ }
+ return oldCookies;
+ }
+ #endregion
- #region --URL--
- ///
- /// 使用默认编码对 URL 进行编码
- ///
- /// 要编码的地址
- /// 编码后的地址
- public static string UrlEncode (string url)
- {
- return HttpUtility.UrlEncode (url);
- }
- ///
- /// 使用指定的编码 对 URL 进行编码
- ///
- /// 要编码的地址
- /// 编码类型
- /// 编码后的地址
- public static string UrlEncode (string url, Encoding encoding)
- {
- return HttpUtility.UrlEncode (url, encoding);
- }
- ///
- /// 使用默认编码对 URL 进行解码
- ///
- /// 要解码的地址
- /// 编码后的地址
- public static string UrlDecode (string url)
- {
- return HttpUtility.UrlDecode (url);
- }
- ///
- /// 使用指定的编码 对 URL 进行解码
- ///
- /// 要解码的地址
- /// 编码类型
- /// 编码后的地址
- public static string UrlDecode (string url, Encoding encoding)
- {
- return HttpUtility.UrlDecode (url, encoding);
- }
- #endregion
+ #endregion
- #endregion
+ #region --私有方法--
+ ///
+ /// 验证HTTPS证书
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ {
+ return true;
+ }
+ ///
+ /// 确认Cookie是否存在
+ ///
+ /// Cookie对象
+ /// cookie名称
+ ///
+ private static int CheckCookie(CookieCollection cookie, string name)
+ {
+ for (int i = 0; i < cookie.Count; i++)
+ {
+ if (cookie[i].Name == name)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+ #endregion
- #region --私有方法--
- ///
- /// 验证HTTPS证书
- ///
- ///
- ///
- ///
- ///
- ///
- private bool CheckValidationResult (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
- {
- return true;
- }
- ///
- /// 确认Cookie是否存在
- ///
- /// Cookie对象
- /// cookie名称
- ///
- private static int CheckCookie (CookieCollection cookie, string name)
- {
- for (int i = 0; i < cookie.Count; i++)
- {
- if (cookie[i].Name == name)
- {
- return i;
- }
- }
- return -1;
- }
- #endregion
+ #region --重写方法--
+ ///
+ /// 返回带有 Cookies 的 HttpWebRequest
+ ///
+ /// 一个 System.Uri,它标识要请求的资源
+ ///
+ protected override WebRequest GetWebRequest(Uri address)
+ {
+ if (address.OriginalString.StartsWith("https", StringComparison.OrdinalIgnoreCase))
+ {
+ ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult; // 强行验证HTTPS通过
+ ServicePointManager.SecurityProtocol = (SecurityProtocolType)(48 | 192 | 768 | 3072); // 通过验证的协议类型, 来源 .Net Framework 4.5
+ }
+ HttpWebRequest httpWebRequest = (HttpWebRequest)base.GetWebRequest(address);
+ httpWebRequest.ProtocolVersion = HttpVersion.Version11;
+ httpWebRequest.KeepAlive = KeepAlive; // 默认: False, 不建立持续型连接
+ if (CookieCollection != null)
+ {
+ httpWebRequest.CookieContainer = new CookieContainer();
+ httpWebRequest.CookieContainer.Add(address, CookieCollection);
+ }
+ else
+ {
+ httpWebRequest.CookieContainer = new CookieContainer();
+ }
+ if (!string.IsNullOrEmpty(this.UserAgent))
+ {
+ httpWebRequest.UserAgent = UserAgent;
+ }
+ else
+ {
+ httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36";
+ }
+ if (TimeOut > 0)
+ {
+ httpWebRequest.Timeout = this.TimeOut;
+ }
+ if (!string.IsNullOrEmpty(this.Accept))
+ {
+ httpWebRequest.Accept = this.Accept;
+ }
+ else
+ {
+ httpWebRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
+ }
+ httpWebRequest.AllowAutoRedirect = this.AllowAutoRedirect;
+ if (this.AllowAutoRedirect)
+ {
+ if (this.MaximumAutomaticRedirections <= 0)
+ {
+ httpWebRequest.MaximumAutomaticRedirections = 5;
+ }
+ else
+ {
+ httpWebRequest.MaximumAutomaticRedirections = this.MaximumAutomaticRedirections;
+ }
+ }
+ if (!string.IsNullOrEmpty(this.Referer))
+ {
+ httpWebRequest.Referer = this.Referer;
+ }
+ if (httpWebRequest.Method.ToUpper() != "GET") //GET不需要包体参数
+ {
+ if (!string.IsNullOrEmpty(this.ContentType))
+ {
+ httpWebRequest.ContentType = this.ContentType;
+ }
+ else
+ {
+ httpWebRequest.ContentType = "application/x-www-form-urlencoded";
+ }
+ }
- #region --重写方法--
- ///
- /// 返回带有 Cookies 的 HttpWebRequest
- ///
- /// 一个 System.Uri,它标识要请求的资源
- ///
- protected override WebRequest GetWebRequest (Uri address)
- {
- if (address.OriginalString.StartsWith ("https", StringComparison.OrdinalIgnoreCase))
- {
- ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult; // 强行验证HTTPS通过
- ServicePointManager.SecurityProtocol = (SecurityProtocolType)(48 | 192 | 768 | 3072); // 通过验证的协议类型, 来源 .Net Framework 4.5
- }
- HttpWebRequest httpWebRequest = (HttpWebRequest)base.GetWebRequest (address);
- httpWebRequest.ProtocolVersion = HttpVersion.Version11;
- httpWebRequest.KeepAlive = KeepAlive; // 默认: False, 不建立持续型连接
- if (CookieCollection != null)
- {
- httpWebRequest.CookieContainer = new CookieContainer ();
- httpWebRequest.CookieContainer.Add (address, CookieCollection);
- }
- else
- {
- httpWebRequest.CookieContainer = new CookieContainer ();
- }
- if (!string.IsNullOrEmpty (this.UserAgent))
- {
- httpWebRequest.UserAgent = UserAgent;
- }
- else
- {
- httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36";
- }
- if (TimeOut > 0)
- {
- httpWebRequest.Timeout = this.TimeOut;
- }
- if (!string.IsNullOrEmpty (this.Accept))
- {
- httpWebRequest.Accept = this.Accept;
- }
- else
- {
- httpWebRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
- }
- httpWebRequest.AllowAutoRedirect = this.AllowAutoRedirect;
- if (this.AllowAutoRedirect)
- {
- if (this.MaximumAutomaticRedirections <= 0)
- {
- httpWebRequest.MaximumAutomaticRedirections = 5;
- }
- else
- {
- httpWebRequest.MaximumAutomaticRedirections = this.MaximumAutomaticRedirections;
- }
- }
- if (!string.IsNullOrEmpty (this.Referer))
- {
- httpWebRequest.Referer = this.Referer;
- }
- if (httpWebRequest.Method.ToUpper () != "GET") //GET不需要包体参数
- {
- if (!string.IsNullOrEmpty (this.ContentType))
- {
- httpWebRequest.ContentType = this.ContentType;
- }
- else
- {
- httpWebRequest.ContentType = "application/x-www-form-urlencoded";
- }
- }
-
- return httpWebRequest;
- }
- ///
- /// 返回指定 System.Net.WebResponse 的 System.Net.WebRequest。
- ///
- /// 一个 System.Net.WebRequest 用于获得响应。
- /// 一个 System.Net.WebResponse 包含指定的响应 System.Net.WebRequest。
- protected override WebResponse GetWebResponse (WebRequest request)
- {
- HttpWebResponse httpWebResponse = (HttpWebResponse)base.GetWebResponse (request);
- this.Method = httpWebResponse.Method;
- this.ContentType = httpWebResponse.ContentType;
- this.Headers = httpWebResponse.Headers;
- if (this.AutoCookieMerge)
- {
- UpdateCookie (this.CookieCollection, httpWebResponse.Cookies);
- }
- else
- {
- this.CookieCollection = httpWebResponse.Cookies;
- }
- return httpWebResponse;
- }
- #endregion
- }
+ return httpWebRequest;
+ }
+ ///
+ /// 返回指定 System.Net.WebResponse 的 System.Net.WebRequest。
+ ///
+ /// 一个 System.Net.WebRequest 用于获得响应。
+ /// 一个 System.Net.WebResponse 包含指定的响应 System.Net.WebRequest。
+ protected override WebResponse GetWebResponse(WebRequest request)
+ {
+ HttpWebResponse httpWebResponse = (HttpWebResponse)base.GetWebResponse(request);
+ this.Method = httpWebResponse.Method;
+ this.ContentType = httpWebResponse.ContentType;
+ this.Headers = httpWebResponse.Headers;
+ if (this.AutoCookieMerge)
+ {
+ UpdateCookie(this.CookieCollection, httpWebResponse.Cookies);
+ }
+ else
+ {
+ this.CookieCollection = httpWebResponse.Cookies;
+ }
+ return httpWebResponse;
+ }
+ #endregion
+ }
}
diff --git a/Native.Csharp.Tool/IniConfig/Linq/IniObject.cs b/Native.Csharp.Tool/IniConfig/Linq/IniObject.cs
index dd6475b8..13b08b8c 100644
--- a/Native.Csharp.Tool/IniConfig/Linq/IniObject.cs
+++ b/Native.Csharp.Tool/IniConfig/Linq/IniObject.cs
@@ -98,9 +98,9 @@ public IniObject (int capacity)
: base (capacity)
{ }
///
- /// 初始化 类的新实例, 该实例从包含指定的 赋值的元素并为键类型使用默认的相等比较器
+ /// 初始化 类的新实例, 该实例从包含指定的 赋值的元素并为键类型使用默认的相等比较器
///
- /// , 它的元素被复制到新
+ /// , 它的元素被复制到新
public IniObject (IDictionary dictionary)
: base (dictionary)
{ }
@@ -140,7 +140,7 @@ public void Save ()
///
/// 将 Ini 配置项保存到指定的文件。 如果存在指定文件,则此方法会覆盖它。
///
- /// 要将文档保存到其中的文件的位置。
+ /// 要将文档保存到其中的文件的位置。
public void Save (string filePath)
{
Save (new Uri (filePath));
diff --git a/Native.Csharp.Tool/IniConfig/Linq/IniValue.cs b/Native.Csharp.Tool/IniConfig/Linq/IniValue.cs
index 4ac65e74..f0f451d1 100644
--- a/Native.Csharp.Tool/IniConfig/Linq/IniValue.cs
+++ b/Native.Csharp.Tool/IniConfig/Linq/IniValue.cs
@@ -195,7 +195,6 @@ public bool Equals (IniValue other)
///
/// 指示当前对象是否等于另一个对象。
///
- /// 与此实例比较的另一个对象
/// 如果当前对象等于 obj 参数,则为 true;否则为 false。
public TypeCode GetTypeCode ()
{
diff --git a/Native.Csharp.Tool/IniConfig/README.md b/Native.Csharp.Tool/IniConfig/README.md
index 0f37b196..9313ae0d 100644
--- a/Native.Csharp.Tool/IniConfig/README.md
+++ b/Native.Csharp.Tool/IniConfig/README.md
@@ -69,7 +69,7 @@ value1.ToByte ();
Convert.ToDateTime (value1); // 当然也可以使用 Convert
// 快速拿取 Value
-IniValue value2 = section2["节点"]["键1"];
+IniValue value2 = iObject["节点"]["键1"];
```
>4. 修改 Ini 配置文件
@@ -81,4 +81,5 @@ IniObject iObject = IniObject.Load ("1.ini");
iObject["节点1"]["键1"] = new IniValue ("更新值"); // 因为无法重载 = 运算符, 所以没办法只能 new 对象
iObject["节点1"]["键1"] = new IniValue (10);
iObject["节点1"]["键1"].Value = "更新值"; // 适用于字符串的时候
+iObject.Save ();
```
diff --git a/Native.Csharp.Tool/IniFile.cs b/Native.Csharp.Tool/IniFile.cs
deleted file mode 100644
index 864b3618..00000000
--- a/Native.Csharp.Tool/IniFile.cs
+++ /dev/null
@@ -1,331 +0,0 @@
-using Native.Csharp.Tool.Core;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-
-namespace Native.Csharp.Tool
-{
- ///
- /// Ini配置文件
- ///
- [Obsolete("请改用 IniConfig ")]
- public class IniFile
- {
- #region --字段--
- private readonly string _fileName;
- #endregion
-
- #region --属性--
- ///
- /// 获取当前Ini文件的绝对路径
- ///
- public string FileName
- {
- get { return _fileName; }
- }
- ///
- /// 获取文件是否存在
- ///
- public bool IsExists
- {
- get { return File.Exists(this.FileName); }
- }
- #endregion
-
- #region --构造函数--
- ///
- /// 初始化 Native.Csharp.Sdk.Cqp.Tool.IniFile 实例对象
- ///
- /// 文件路径
- public IniFile(string filePath)
- {
- this._fileName = filePath;
- if (!IsExists)
- {
- File.Create(this.FileName);
- }
- }
- #endregion
-
- #region --公开方法--
-
- #region --Write--
- ///
- /// 写入一个keyValuePair, 若 key 已存在, 则替换Value
- ///
- /// 该键所在的节名称
- /// 该键的名称
- /// 该键的值
- public void Write(string section, string key, object Value)
- {
- Kernel32.WritePrivateProfileStringA(section, key, Value.ToString(), this.FileName);
- }
- #endregion
-
- #region --Read--
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public string Read(string section, string key, string value = null)
- {
- StringBuilder buffer = new StringBuilder(65535);
- Kernel32.GetPrivateProfileSectionA(section, buffer, buffer.Capacity, this.FileName);
- string str = buffer.ToString();
- if (string.IsNullOrEmpty(str))
- {
- return value;
- }
- return str;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual int Read(string section, string key, int value = 0)
- {
- int result;
- try
- {
- result = int.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual long Read(string section, string key, long value = 0)
- {
- long result;
- try
- {
- result = long.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual byte Read(string section, string key, byte value = 0x00)
- {
- byte result;
- try
- {
- result = byte.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual float Read(string section, string key, float value = 0)
- {
- float result;
- try
- {
- result = float.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual double Read(string section, string key, double value = 0)
- {
- double result;
- try
- {
- result = double.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual bool Read(string section, string key, bool value = false)
- {
- bool result;
- try
- {
- result = bool.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual DateTime Read(string section, string key, DateTime value = new DateTime())
- {
- DateTime result;
- try
- {
- result = DateTime.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- ///
- /// 读取Ini文件指定节, 指定键中的值
- ///
- /// 节
- /// 键
- /// 值, 当读取失败时, 返回该值
- ///
- public virtual TimeSpan Read(string section, string key, TimeSpan value = new TimeSpan())
- {
- TimeSpan result;
- try
- {
- result = TimeSpan.Parse(this.Read(section, key, string.Empty));
- }
- catch
- {
- result = value;
- }
- return result;
- }
- #endregion
-
- ///
- /// 读取Ini文件的所有节集合
- ///
- ///
- public List ReadSections()
- {
- byte[] buffer = new byte[65535];
- int rel = Kernel32.GetPrivateProfileSectionNamesA(buffer, buffer.GetUpperBound(0), this.FileName);
- int iCnt, iPos;
- List arrayList = new List();
- string tmp;
- if (rel > 0)
- {
- iCnt = 0; iPos = 0;
- for (iCnt = 0; iCnt < rel; iCnt++)
- {
- if (buffer[iCnt] == 0x00)
- {
- tmp = ASCIIEncoding.Default.GetString(buffer, iPos, iCnt).Trim();
- iPos = iCnt + 1;
- if (tmp != "")
- arrayList.Add(tmp);
- }
- }
- }
- return arrayList;
- }
- ///
- /// 判断指定的节是否存在
- ///
- /// 节名称
- ///
- public bool SectionExists(string section)
- {
- StringBuilder buffer = new StringBuilder(65535);
- Kernel32.GetPrivateProfileSectionA(section, buffer, buffer.Capacity, this.FileName);
- return buffer.ToString().Trim() == "";
- }
- ///
- /// 判断指定的节中指定的键是否存在
- ///
- /// 节名称
- /// 键名称
- ///
- public bool ValueExits(string section, string key)
- {
- return Read(section, key, string.Empty).Trim() == "";
- }
- ///
- /// 删除指定的节中的指定键
- ///
- /// 该键所在的节的名称
- /// 该键的名称
- public void DeleteKey(string section, string key)
- {
- Write(section, key, null);
- }
- ///
- /// 删除指定的节的所有内容
- ///
- /// 要删除的节的名字
- public void DeleteSection(string section)
- {
- Kernel32.WritePrivateProfileSectionA(section, null, this.FileName);
- }
- ///
- /// 添加一个节
- ///
- /// 要添加的节名称
- public void AddSection(string section)
- {
- Kernel32.WritePrivateProfileSectionA(section, "", this.FileName);
- }
- ///
- /// 清空Ini配置文件
- ///
- public void Clear()
- {
- File.Delete(this.FileName);
- File.Create(this.FileName);
- }
- #endregion
- }
-}
diff --git a/Native.Csharp.Tool/Native.Csharp.Tool.csproj b/Native.Csharp.Tool/Native.Csharp.Tool.csproj
index 56a97c98..642aa573 100644
--- a/Native.Csharp.Tool/Native.Csharp.Tool.csproj
+++ b/Native.Csharp.Tool/Native.Csharp.Tool.csproj
@@ -16,11 +16,14 @@
true
bin\x86\Debug\
- DEBUG;TRACE
+ TRACE;DEBUG;NET_40;SQLITE_STANDARD;INTEROP_VIRTUAL_TABLE;INTEROP_SESSION_EXTENSION;TRACE_SHARED
full
x86
prompt
MinimumRecommendedRules.ruleset
+ 618,1591;3001
+ NU1605
+ bin\x86\Debug\Native.Csharp.Tool.xml
bin\x86\Release\
@@ -34,6 +37,7 @@
+
@@ -42,23 +46,80 @@
-
-
+
Component
-
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+ Component
+
+
+ Component
+
+
+
+
+
+
+ Component
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
\ No newline at end of file
diff --git a/Native.Csharp.Tool/NativeConvert.cs b/Native.Csharp.Tool/NativeConvert.cs
index aa09c9fe..6fb187e3 100644
--- a/Native.Csharp.Tool/NativeConvert.cs
+++ b/Native.Csharp.Tool/NativeConvert.cs
@@ -3,76 +3,84 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
-using Native.Csharp.Tool.Core;
namespace Native.Csharp.Tool
{
///
- /// Native 用于数据转换的工具类
+ /// 转换工具类
///
public static class NativeConvert
{
+ #region --Kernel32--
+ [DllImport ("kernel32.dll", EntryPoint = "lstrlenA", CharSet = CharSet.Ansi)]
+ internal extern static int LstrlenA (IntPtr ptr);
+ #endregion
+
///
- /// 获取10或13位时间戳的 System.DateTime 表示形式
+ /// 获取 Unix 时间戳的 表示形式
///
- /// 10 or 13 位时间戳
+ /// unix 时间戳
///
- public static DateTime FotmatUnixTime (string timeStamp)
+ public static DateTime ToDateTime (long unixTime)
{
DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime (new DateTime (1970, 1, 1));
- long lTime;
- if (timeStamp.Length.Equals (10))//判断是10位
- {
- lTime = long.Parse (timeStamp + "0000000");
- }
- else
- {
- lTime = long.Parse (timeStamp + "0000");//13位
- }
- TimeSpan toNow = new TimeSpan (lTime);
+ TimeSpan toNow = new TimeSpan (unixTime);
DateTime daTime = dtStart.Add (toNow);
return daTime;
}
///
- /// 获取指定 IntPtr 实例中的字符串
+ /// 获取 Unix 时间戳的 表示形式
///
- /// 字符串的 IntPtr 对象
+ /// unix 时间戳
+ ///
+ public static DateTime ToDateTime (int unixTime)
+ {
+ DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime (new DateTime (1970, 1, 1));
+ TimeSpan toNow = new TimeSpan (unixTime);
+ DateTime daTime = dtStart.Add (toNow);
+ return daTime;
+ }
+
+ ///
+ /// 转换字符串的 实例对象
+ ///
+ /// 将转换的字符串
/// 目标编码格式
///
- public static string ToPtrString (IntPtr strPtr, Encoding encoding = null)
+ public static IntPtr ToIntPtr (string source, Encoding encoding = null)
{
if (encoding == null)
{
- encoding = Encoding.Default;
+ encoding = Encoding.ASCII;
}
-
- int len = Kernel32.LstrlenA (strPtr); //获取指针中数据的长度
- if (len == 0)
- {
- return string.Empty;
- }
-
- byte[] buffer = new byte[len];
- Marshal.Copy (strPtr, buffer, 0, len);
- return encoding.GetString (buffer);
+ byte[] buffer = encoding.GetBytes (source);
+ GCHandle hobj = GCHandle.Alloc (buffer, GCHandleType.Pinned);
+ return hobj.AddrOfPinnedObject ();
}
///
- /// 获取字符串的 IntPtr 实例对象
+ /// 读取指针内所有的字节数组并编码为指定字符串
///
- /// 将转换的字符串
+ /// 字符串的 对象
/// 目标编码格式
///
- public static IntPtr ToStringPtr (string value, Encoding encoding = null)
+ public static string ToString (IntPtr strPtr, Encoding encoding = null)
{
if (encoding == null)
{
encoding = Encoding.Default;
}
- byte[] buffer = encoding.GetBytes (value);
- GCHandle hobj = GCHandle.Alloc (buffer, GCHandleType.Pinned);
- return hobj.AddrOfPinnedObject ();
+
+ int len = LstrlenA (strPtr); //获取指针中数据的长度
+ if (len == 0)
+ {
+ return string.Empty;
+ }
+
+ byte[] buffer = new byte[len];
+ Marshal.Copy (strPtr, buffer, 0, len);
+ return encoding.GetString (buffer);
}
}
}
diff --git a/Native.Csharp.Tool/Properties/AssemblyInfo.cs b/Native.Csharp.Tool/Properties/AssemblyInfo.cs
index 48046569..7d81e91e 100644
--- a/Native.Csharp.Tool/Properties/AssemblyInfo.cs
+++ b/Native.Csharp.Tool/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion ("3.0.6.0525")]
-[assembly: AssemblyFileVersion ("3.0.6.0525")]
+[assembly: AssemblyVersion ("3.0.7.0607")]
+[assembly: AssemblyFileVersion ("3.0.7.0607")]
diff --git a/Native.Csharp.Tool/Properties/AssemblySourceIdAttribute.cs b/Native.Csharp.Tool/Properties/AssemblySourceIdAttribute.cs
new file mode 100644
index 00000000..3666fe19
--- /dev/null
+++ b/Native.Csharp.Tool/Properties/AssemblySourceIdAttribute.cs
@@ -0,0 +1,42 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System;
+
+namespace System.Data.SQLite
+{
+ ///
+ /// Defines a source code identifier custom attribute for an assembly
+ /// manifest.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblySourceIdAttribute : Attribute
+ {
+ ///
+ /// Constructs an instance of this attribute class using the specified
+ /// source code identifier value.
+ ///
+ ///
+ /// The source code identifier value to use.
+ ///
+ public AssemblySourceIdAttribute(string value)
+ {
+ sourceId = value;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private string sourceId;
+ ///
+ /// Gets the source code identifier value.
+ ///
+ public string SourceId
+ {
+ get { return sourceId; }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/Properties/AssemblySourceTimeStampAttribute.cs b/Native.Csharp.Tool/Properties/AssemblySourceTimeStampAttribute.cs
new file mode 100644
index 00000000..828aca66
--- /dev/null
+++ b/Native.Csharp.Tool/Properties/AssemblySourceTimeStampAttribute.cs
@@ -0,0 +1,42 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System;
+
+namespace System.Data.SQLite
+{
+ ///
+ /// Defines a source code time-stamp custom attribute for an assembly
+ /// manifest.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblySourceTimeStampAttribute : Attribute
+ {
+ ///
+ /// Constructs an instance of this attribute class using the specified
+ /// source code time-stamp value.
+ ///
+ ///
+ /// The source code time-stamp value to use.
+ ///
+ public AssemblySourceTimeStampAttribute(string value)
+ {
+ sourceTimeStamp = value;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private string sourceTimeStamp;
+ ///
+ /// Gets the source code time-stamp value.
+ ///
+ public string SourceTimeStamp
+ {
+ get { return sourceTimeStamp; }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/Configurations/System.Data.SQLite.dll.config b/Native.Csharp.Tool/SQLite/Configurations/System.Data.SQLite.dll.config
new file mode 100644
index 00000000..96afee22
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/Configurations/System.Data.SQLite.dll.config
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Native.Csharp.Tool/SQLite/Generated/SR.resources b/Native.Csharp.Tool/SQLite/Generated/SR.resources
new file mode 100644
index 00000000..1c26a86d
Binary files /dev/null and b/Native.Csharp.Tool/SQLite/Generated/SR.resources differ
diff --git a/Native.Csharp.Tool/SQLite/ISQLiteNativeModule.cs b/Native.Csharp.Tool/SQLite/ISQLiteNativeModule.cs
new file mode 100644
index 00000000..3f340334
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/ISQLiteNativeModule.cs
@@ -0,0 +1,1642 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ #region ISQLiteNativeModule Interface
+ ///
+ /// This interface represents a virtual table implementation written in
+ /// native code.
+ ///
+ public interface ISQLiteNativeModule
+ {
+ ///
+ ///
+ /// int (*xCreate)(sqlite3 *db, void *pAux,
+ /// int argc, char *const*argv,
+ /// sqlite3_vtab **ppVTab,
+ /// char **pzErr);
+ ///
+ ///
+ /// The xCreate method is called to create a new instance of a virtual table
+ /// in response to a CREATE VIRTUAL TABLE statement.
+ /// If the xCreate method is the same pointer as the xConnect method, then the
+ /// virtual table is an eponymous virtual table.
+ /// If the xCreate method is omitted (if it is a NULL pointer) then the virtual
+ /// table is an eponymous-only virtual table.
+ ///
+ ///
+ /// The db parameter is a pointer to the SQLite database connection that
+ /// is executing the CREATE VIRTUAL TABLE statement.
+ /// The pAux argument is the copy of the client data pointer that was the
+ /// fourth argument to the sqlite3_create_module() or
+ /// sqlite3_create_module_v2() call that registered the
+ /// virtual table module.
+ /// The argv parameter is an array of argc pointers to null terminated strings.
+ /// The first string, argv[0], is the name of the module being invoked. The
+ /// module name is the name provided as the second argument to
+ /// sqlite3_create_module() and as the argument to the USING clause of the
+ /// CREATE VIRTUAL TABLE statement that is running.
+ /// The second, argv[1], is the name of the database in which the new virtual table is being created. The database name is "main" for the primary database, or
+ /// "temp" for TEMP database, or the name given at the end of the ATTACH
+ /// statement for attached databases. The third element of the array, argv[2],
+ /// is the name of the new virtual table, as specified following the TABLE
+ /// keyword in the CREATE VIRTUAL TABLE statement.
+ /// If present, the fourth and subsequent strings in the argv[] array report
+ /// the arguments to the module name in the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// The job of this method is to construct the new virtual table object
+ /// (an sqlite3_vtab object) and return a pointer to it in *ppVTab.
+ ///
+ ///
+ /// As part of the task of creating a new sqlite3_vtab structure, this
+ /// method must invoke sqlite3_declare_vtab() to tell the SQLite
+ /// core about the columns and datatypes in the virtual table.
+ /// The sqlite3_declare_vtab() API has the following prototype:
+ ///
+ ///
+ /// int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable)
+ ///
+ ///
+ /// The first argument to sqlite3_declare_vtab() must be the same
+ /// database connection pointer as the first parameter to this method.
+ /// The second argument to sqlite3_declare_vtab() must a zero-terminated
+ /// UTF-8 string that contains a well-formed CREATE TABLE statement that
+ /// defines the columns in the virtual table and their data types.
+ /// The name of the table in this CREATE TABLE statement is ignored,
+ /// as are all constraints. Only the column names and datatypes matter.
+ /// The CREATE TABLE statement string need not to be
+ /// held in persistent memory. The string can be
+ /// deallocated and/or reused as soon as the sqlite3_declare_vtab()
+ /// routine returns.
+ ///
+ ///
+ /// The xCreate method need not initialize the pModule, nRef, and zErrMsg
+ /// fields of the sqlite3_vtab object. The SQLite core will take care of
+ /// that chore.
+ ///
+ ///
+ /// The xCreate should return SQLITE_OK if it is successful in
+ /// creating the new virtual table, or SQLITE_ERROR if it is not successful.
+ /// If not successful, the sqlite3_vtab structure must not be allocated.
+ /// An error message may optionally be returned in *pzErr if unsuccessful.
+ /// Space to hold the error message string must be allocated using
+ /// an SQLite memory allocation function like
+ /// sqlite3_malloc() or sqlite3_mprintf() as the SQLite core will
+ /// attempt to free the space using sqlite3_free() after the error has
+ /// been reported up to the application.
+ ///
+ ///
+ /// If the xCreate method is omitted (left as a NULL pointer) then the
+ /// virtual table is an eponymous-only virtual table. New instances of
+ /// the virtual table cannot be created using CREATE VIRTUAL TABLE and the
+ /// virtual table can only be used via its module name.
+ /// Note that SQLite versions prior to 3.9.0 (2015-10-14) do not understand
+ /// eponymous-only virtual tables and will segfault if an attempt is made
+ /// to CREATE VIRTUAL TABLE on an eponymous-only virtual table because
+ /// the xCreate method was not checked for null.
+ ///
+ ///
+ /// If the xCreate method is the exact same pointer as the xConnect method,
+ /// that indicates that the virtual table does not need to initialize backing
+ /// store. Such a virtual table can be used as an eponymous virtual table
+ /// or as a named virtual table using CREATE VIRTUAL TABLE or both.
+ ///
+ ///
+ /// If a column datatype contains the special keyword "HIDDEN"
+ /// (in any combination of upper and lower case letters) then that keyword
+ /// it is omitted from the column datatype name and the column is marked
+ /// as a hidden column internally.
+ /// A hidden column differs from a normal column in three respects:
+ ///
+ ///
+ /// ]]>
+ /// ]]> Hidden columns are not listed in the dataset returned by
+ /// "PRAGMA table_info",
+ /// ]]>]]> Hidden columns are not included in the expansion of a "*"
+ /// expression in the result set of a SELECT, and
+ /// ]]>]]> Hidden columns are not included in the implicit column-list
+ /// used by an INSERT statement that lacks an explicit column-list.
+ /// ]]>]]>
+ ///
+ ///
+ /// For example, if the following SQL is passed to sqlite3_declare_vtab():
+ ///
+ ///
+ /// CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);
+ ///
+ ///
+ /// Then the virtual table would be created with two hidden columns,
+ /// and with datatypes of "VARCHAR(12)" and "INTEGER".
+ ///
+ ///
+ /// An example use of hidden columns can be seen in the FTS3 virtual
+ /// table implementation, where every FTS virtual table
+ /// contains an FTS hidden column that is used to pass information from the
+ /// virtual table into FTS auxiliary functions and to the FTS MATCH operator.
+ ///
+ ///
+ /// A virtual table that contains hidden columns can be used like
+ /// a table-valued function in the FROM clause of a SELECT statement.
+ /// The arguments to the table-valued function become constraints on
+ /// the HIDDEN columns of the virtual table.
+ ///
+ ///
+ /// For example, the "generate_series" extension (located in the
+ /// ext/misc/series.c
+ /// file in the source tree)
+ /// implements an eponymous virtual table with the following schema:
+ ///
+ ///
+ /// CREATE TABLE generate_series(
+ /// value,
+ /// start HIDDEN,
+ /// stop HIDDEN,
+ /// step HIDDEN
+ /// );
+ ///
+ ///
+ /// The sqlite3_module.xBestIndex method in the implementation of this
+ /// table checks for equality constraints against the HIDDEN columns, and uses
+ /// those as input parameters to determine the range of integer "value" outputs
+ /// to generate. Reasonable defaults are used for any unconstrained columns.
+ /// For example, to list all integers between 5 and 50:
+ ///
+ ///
+ /// SELECT value FROM generate_series(5,50);
+ ///
+ ///
+ /// The previous query is equivalent to the following:
+ ///
+ ///
+ /// SELECT value FROM generate_series WHERE start=5 AND stop=50;
+ ///
+ ///
+ /// Arguments on the virtual table name are matched to hidden columns
+ /// in order. The number of arguments can be less than the
+ /// number of hidden columns, in which case the latter hidden columns are
+ /// unconstrained. However, an error results if there are more arguments
+ /// than there are hidden columns in the virtual table.
+ ///
+ ///
+ /// Beginning with SQLite version 3.14.0 (2016-08-08),
+ /// the CREATE TABLE statement that
+ /// is passed into sqlite3_declare_vtab() may contain a WITHOUT ROWID clause.
+ /// This is useful for cases where the virtual table rows
+ /// cannot easily be mapped into unique integers. A CREATE TABLE
+ /// statement that includes WITHOUT ROWID must define one or more columns as
+ /// the PRIMARY KEY. Every column of the PRIMARY KEY must individually be
+ /// NOT NULL and all columns for each row must be collectively unique.
+ ///
+ ///
+ /// Note that SQLite does not enforce the PRIMARY KEY for a WITHOUT ROWID
+ /// virtual table. Enforcement is the responsibility of the underlying
+ /// virtual table implementation. But SQLite does assume that the PRIMARY KEY
+ /// constraint is valid - that the identified columns really are UNIQUE and
+ /// NOT NULL - and it uses that assumption to optimize queries against the
+ /// virtual table.
+ ///
+ ///
+ /// The rowid column is not accessible on a
+ /// WITHOUT ROWID virtual table (of course).
+ ///
+ ///
+ /// The xUpdate method was originally designed around having a
+ /// ROWID as a single value. The xUpdate method has been expanded to
+ /// accommodate an arbitrary PRIMARY KEY in place of the ROWID, but the
+ /// PRIMARY KEY must still be only one column. For this reason, SQLite
+ /// will reject any WITHOUT ROWID virtual table that has more than one
+ /// PRIMARY KEY column and a non-NULL xUpdate method.
+ ///
+ ///
+ ///
+ /// The native database connection handle.
+ ///
+ ///
+ /// The original native pointer value that was provided to the
+ /// sqlite3_create_module(), sqlite3_create_module_v2() or
+ /// sqlite3_create_disposable_module() functions.
+ ///
+ ///
+ /// The number of arguments from the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// The array of string arguments from the CREATE VIRTUAL TABLE
+ /// statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to point to the newly
+ /// created native sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to point to the error
+ /// message, with the underlying memory having been obtained from the
+ /// sqlite3_malloc() function.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xCreate(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xConnect)(sqlite3*, void *pAux,
+ /// int argc, char *const*argv,
+ /// sqlite3_vtab **ppVTab,
+ /// char **pzErr);
+ ///
+ ///
+ /// The xConnect method is very similar to xCreate.
+ /// It has the same parameters and constructs a new sqlite3_vtab structure
+ /// just like xCreate.
+ /// And it must also call sqlite3_declare_vtab() like xCreate.
+ ///
+ ///
+ /// The difference is that xConnect is called to establish a new
+ /// connection to an existing virtual table whereas xCreate is called
+ /// to create a new virtual table from scratch.
+ ///
+ ///
+ /// The xCreate and xConnect methods are only different when the
+ /// virtual table has some kind of backing store that must be initialized
+ /// the first time the virtual table is created. The xCreate method creates
+ /// and initializes the backing store. The xConnect method just connects
+ /// to an existing backing store. When xCreate and xConnect are the same,
+ /// the table is an eponymous virtual table.
+ ///
+ ///
+ /// As an example, consider a virtual table implementation that
+ /// provides read-only access to existing comma-separated-value (CSV)
+ /// files on disk. There is no backing store that needs to be created
+ /// or initialized for such a virtual table (since the CSV files already
+ /// exist on disk) so the xCreate and xConnect methods will be identical
+ /// for that module.
+ ///
+ ///
+ /// Another example is a virtual table that implements a full-text index.
+ /// The xCreate method must create and initialize data structures to hold
+ /// the dictionary and posting lists for that index. The xConnect method,
+ /// on the other hand, only has to locate and use an existing dictionary
+ /// and posting lists that were created by a prior xCreate call.
+ ///
+ ///
+ /// The xConnect method must return SQLITE_OK if it is successful
+ /// in creating the new virtual table, or SQLITE_ERROR if it is not
+ /// successful. If not successful, the sqlite3_vtab structure must not be
+ /// allocated. An error message may optionally be returned in *pzErr if
+ /// unsuccessful.
+ /// Space to hold the error message string must be allocated using
+ /// an SQLite memory allocation function like
+ /// sqlite3_malloc() or sqlite3_mprintf() as the SQLite core will
+ /// attempt to free the space using sqlite3_free() after the error has
+ /// been reported up to the application.
+ ///
+ ///
+ /// The xConnect method is required for every virtual table implementation,
+ /// though the xCreate and xConnect pointers of the sqlite3_module object
+ /// may point to the same function if the virtual table does not need to
+ /// initialize backing store.
+ ///
+ ///
+ ///
+ /// The native database connection handle.
+ ///
+ ///
+ /// The original native pointer value that was provided to the
+ /// sqlite3_create_module(), sqlite3_create_module_v2() or
+ /// sqlite3_create_disposable_module() functions.
+ ///
+ ///
+ /// The number of arguments from the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// The array of string arguments from the CREATE VIRTUAL TABLE
+ /// statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to point to the newly
+ /// created native sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to point to the error
+ /// message, with the underlying memory having been obtained from the
+ /// sqlite3_malloc() function.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xConnect(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// SQLite uses the xBestIndex method of a virtual table module to determine
+ /// the best way to access the virtual table.
+ /// The xBestIndex method has a prototype like this:
+ ///
+ ///
+ /// int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*);
+ ///
+ ///
+ /// The SQLite core communicates with the xBestIndex method by filling
+ /// in certain fields of the sqlite3_index_info structure and passing a
+ /// pointer to that structure into xBestIndex as the second parameter.
+ /// The xBestIndex method fills out other fields of this structure which
+ /// forms the reply. The sqlite3_index_info structure looks like this:
+ ///
+ ///
+ /// struct sqlite3_index_info {
+ /// /* Inputs */
+ /// const int nConstraint; /* Number of entries in aConstraint */
+ /// const struct sqlite3_index_constraint {
+ /// int iColumn; /* Column constrained. -1 for ROWID */
+ /// unsigned char op; /* Constraint operator */
+ /// unsigned char usable; /* True if this constraint is usable */
+ /// int iTermOffset; /* Used internally - xBestIndex should ignore */
+ /// } *const aConstraint; /* Table of WHERE clause constraints */
+ /// const int nOrderBy; /* Number of terms in the ORDER BY clause */
+ /// const struct sqlite3_index_orderby {
+ /// int iColumn; /* Column number */
+ /// unsigned char desc; /* True for DESC. False for ASC. */
+ /// } *const aOrderBy; /* The ORDER BY clause */
+ /// /* Outputs */
+ /// struct sqlite3_index_constraint_usage {
+ /// int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ /// unsigned char omit; /* Do not code a test for this constraint */
+ /// } *const aConstraintUsage;
+ /// int idxNum; /* Number used to identify the index */
+ /// char *idxStr; /* String, possibly obtained from sqlite3_malloc */
+ /// int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ /// int orderByConsumed; /* True if output is already ordered */
+ /// double estimatedCost; /* Estimated cost of using this index */
+ /// ]]>/* Fields below are only available in SQLite 3.8.2 and later */]]>
+ /// sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ /// ]]>/* Fields below are only available in SQLite 3.9.0 and later */]]>
+ /// int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ /// ]]>/* Fields below are only available in SQLite 3.10.0 and later */]]>
+ /// sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
+ /// };
+ ///
+ ///
+ /// Note the warnings on the "estimatedRows", "idxFlags", and colUsed fields.
+ /// These fields were added with SQLite versions 3.8.2, 3.9.0, and 3.10.0, respectively.
+ /// Any extension that reads or writes these fields must first check that the
+ /// version of the SQLite library in use is greater than or equal to appropriate
+ /// version - perhaps comparing the value returned from sqlite3_libversion_number()
+ /// against constants 3008002, 3009000, and/or 3010000. The result of attempting
+ /// to access these fields in an sqlite3_index_info structure created by an
+ /// older version of SQLite are undefined.
+ ///
+ ///
+ /// In addition, there are some defined constants:
+ ///
+ ///
+ /// #define SQLITE_INDEX_CONSTRAINT_EQ 2
+ /// #define SQLITE_INDEX_CONSTRAINT_GT 4
+ /// #define SQLITE_INDEX_CONSTRAINT_LE 8
+ /// #define SQLITE_INDEX_CONSTRAINT_LT 16
+ /// #define SQLITE_INDEX_CONSTRAINT_GE 32
+ /// #define SQLITE_INDEX_CONSTRAINT_MATCH 64
+ /// #define SQLITE_INDEX_CONSTRAINT_LIKE 65 /* 3.10.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_GLOB 66 /* 3.10.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_REGEXP 67 /* 3.10.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_NE 68 /* 3.21.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_ISNOT 69 /* 3.21.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 /* 3.21.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_ISNULL 71 /* 3.21.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_IS 72 /* 3.21.0 and later */
+ /// #define SQLITE_INDEX_CONSTRAINT_FUNCTION 150 /* 3.25.0 and later */
+ /// #define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+ ///
+ ///
+ /// The SQLite core calls the xBestIndex method when it is compiling a query
+ /// that involves a virtual table. In other words, SQLite calls this method
+ /// when it is running sqlite3_prepare() or the equivalent.
+ /// By calling this method, the
+ /// SQLite core is saying to the virtual table that it needs to access
+ /// some subset of the rows in the virtual table and it wants to know the
+ /// most efficient way to do that access. The xBestIndex method replies
+ /// with information that the SQLite core can then use to conduct an
+ /// efficient search of the virtual table.
+ ///
+ ///
+ /// While compiling a single SQL query, the SQLite core might call
+ /// xBestIndex multiple times with different settings in sqlite3_index_info.
+ /// The SQLite core will then select the combination that appears to
+ /// give the best performance.
+ ///
+ ///
+ /// Before calling this method, the SQLite core initializes an instance
+ /// of the sqlite3_index_info structure with information about the
+ /// query that it is currently trying to process. This information
+ /// derives mainly from the WHERE clause and ORDER BY or GROUP BY clauses
+ /// of the query, but also from any ON or USING clauses if the query is a
+ /// join. The information that the SQLite core provides to the xBestIndex
+ /// method is held in the part of the structure that is marked as "Inputs".
+ /// The "Outputs" section is initialized to zero.
+ ///
+ ///
+ /// The information in the sqlite3_index_info structure is ephemeral
+ /// and may be overwritten or deallocated as soon as the xBestIndex method
+ /// returns. If the xBestIndex method needs to remember any part of the
+ /// sqlite3_index_info structure, it should make a copy. Care must be
+ /// take to store the copy in a place where it will be deallocated, such
+ /// as in the idxStr field with needToFreeIdxStr set to 1.
+ ///
+ ///
+ /// Note that xBestIndex will always be called before xFilter, since
+ /// the idxNum and idxStr outputs from xBestIndex are required inputs to
+ /// xFilter. However, there is no guarantee that xFilter will be called
+ /// following a successful xBestIndex.
+ ///
+ ///
+ /// The xBestIndex method is required for every virtual table implementation.
+ ///
+ ///
+ /// The main thing that the SQLite core is trying to communicate to
+ /// the virtual table is the constraints that are available to limit
+ /// the number of rows that need to be searched. The aConstraint[] array
+ /// contains one entry for each constraint. There will be exactly
+ /// nConstraint entries in that array.
+ ///
+ ///
+ /// Each constraint will usually correspond to a term in the WHERE clause
+ /// or in a USING or ON clause that is of the form
+ ///
+ ///
+ /// column OP EXPR
+ ///
+ ///
+ /// Where "column" is a column in the virtual table, OP is an operator
+ /// like "=" or "<", and EXPR is an arbitrary expression. So, for example,
+ /// if the WHERE clause contained a term like this:
+ ///
+ ///
+ /// a = 5
+ ///
+ ///
+ /// Then one of the constraints would be on the "a" column with
+ /// operator "=" and an expression of "5". Constraints need not have a
+ /// literal representation of the WHERE clause. The query optimizer might
+ /// make transformations to the
+ /// WHERE clause in order to extract as many constraints
+ /// as it can. So, for example, if the WHERE clause contained something
+ /// like this:
+ ///
+ ///
+ /// x BETWEEN 10 AND 100 AND 999>y
+ ///
+ ///
+ /// The query optimizer might translate this into three separate constraints:
+ ///
+ ///
+ /// x >= 10
+ /// x <= 100
+ /// y < 999
+ ///
+ ///
+ /// For each such constraint, the aConstraint[].iColumn field indicates which
+ /// column appears on the left-hand side of the constraint.
+ /// The first column of the virtual table is column 0.
+ /// The rowid of the virtual table is column -1.
+ /// The aConstraint[].op field indicates which operator is used.
+ /// The SQLITE_INDEX_CONSTRAINT_* constants map integer constants
+ /// into operator values.
+ /// Columns occur in the order they were defined by the call to
+ /// sqlite3_declare_vtab() in the xCreate or xConnect method.
+ /// Hidden columns are counted when determining the column index.
+ ///
+ ///
+ /// If the xFindFunction() method for the virtual table is defined, and
+ /// if xFindFunction() sometimes returns SQLITE_INDEX_CONSTRAINT_FUNCTION or
+ /// larger, then the constraints might also be of the form:
+ ///
+ ///
+ /// FUNCTION( column, EXPR)
+ ///
+ ///
+ /// In this case the aConstraint[].op value is the same as the value
+ /// returned by xFindFunction() for FUNCTION.
+ ///
+ ///
+ /// The aConstraint[] array contains information about all constraints
+ /// that apply to the virtual table. But some of the constraints might
+ /// not be usable because of the way tables are ordered in a join.
+ /// The xBestIndex method must therefore only consider constraints
+ /// that have an aConstraint[].usable flag which is true.
+ ///
+ ///
+ /// In addition to WHERE clause constraints, the SQLite core also
+ /// tells the xBestIndex method about the ORDER BY clause.
+ /// (In an aggregate query, the SQLite core might put in GROUP BY clause
+ /// information in place of the ORDER BY clause information, but this fact
+ /// should not make any difference to the xBestIndex method.)
+ /// If all terms of the ORDER BY clause are columns in the virtual table,
+ /// then nOrderBy will be the number of terms in the ORDER BY clause
+ /// and the aOrderBy[] array will identify the column for each term
+ /// in the order by clause and whether or not that column is ASC or DESC.
+ ///
+ ///
+ /// In SQLite version 3.10.0 (2016-01-06) and later,
+ /// the colUsed field is available
+ /// to indicate which fields of the virtual table are actually used by the
+ /// statement being prepared. If the lowest bit of colUsed is set, that
+ /// means that the first column is used. The second lowest bit corresponds
+ /// to the second column. And so forth. If the most significant bit of
+ /// colUsed is set, that means that one or more columns other than the
+ /// first 63 columns are used. If column usage information is needed by the
+ /// xFilter method, then the required bits must be encoded into either
+ /// the idxNum or idxStr output fields.
+ ///
+ ///
+ /// Given all of the information above, the job of the xBestIndex
+ /// method it to figure out the best way to search the virtual table.
+ ///
+ ///
+ /// The xBestIndex method fills the idxNum and idxStr fields with
+ /// information that communicates an indexing strategy to the xFilter
+ /// method. The information in idxNum and idxStr is arbitrary as far
+ /// as the SQLite core is concerned. The SQLite core just copies the
+ /// information through to the xFilter method. Any desired meaning can
+ /// be assigned to idxNum and idxStr as long as xBestIndex and xFilter
+ /// agree on what that meaning is.
+ ///
+ ///
+ /// The idxStr value may be a string obtained from an SQLite
+ /// memory allocation function such as sqlite3_mprintf().
+ /// If this is the case, then the needToFreeIdxStr flag must be set to
+ /// true so that the SQLite core will know to call sqlite3_free() on
+ /// that string when it has finished with it, and thus avoid a memory leak.
+ /// The idxStr value may also be a static constant string, in which case
+ /// the needToFreeIdxStr boolean should remain false.
+ ///
+ ///
+ /// If the virtual table will output rows in the order specified by
+ /// the ORDER BY clause, then the orderByConsumed flag may be set to
+ /// true. If the output is not automatically in the correct order
+ /// then orderByConsumed must be left in its default false setting.
+ /// This will indicate to the SQLite core that it will need to do a
+ /// separate sorting pass over the data after it comes out of the virtual table.
+ ///
+ ///
+ /// The estimatedCost field should be set to the estimated number
+ /// of disk access operations required to execute this query against
+ /// the virtual table. The SQLite core will often call xBestIndex
+ /// multiple times with different constraints, obtain multiple cost
+ /// estimates, then choose the query plan that gives the lowest estimate.
+ /// The SQLite core initializes estimatedCost to a very large value
+ /// prior to invoking xBestIndex, so if xBestIndex determines that the
+ /// current combination of parameters is undesirable, it can leave the
+ /// estimatedCost field unchanged to discourage its use.
+ ///
+ ///
+ /// If the current version of SQLite is 3.8.2 or greater, the estimatedRows
+ /// field may be set to an estimate of the number of rows returned by the
+ /// proposed query plan. If this value is not explicitly set, the default
+ /// estimate of 25 rows is used.
+ ///
+ ///
+ /// If the current version of SQLite is 3.9.0 or greater, the idxFlags field
+ /// may be set to SQLITE_INDEX_SCAN_UNIQUE to indicate that the virtual table
+ /// will return only zero or one rows given the input constraints. Additional
+ /// bits of the idxFlags field might be understood in later versions of SQLite.
+ ///
+ ///
+ /// The aConstraintUsage[] array contains one element for each of
+ /// the nConstraint constraints in the inputs section of the
+ /// sqlite3_index_info structure.
+ /// The aConstraintUsage[] array is used by xBestIndex to tell the
+ /// core how it is using the constraints.
+ ///
+ ///
+ /// The xBestIndex method may set aConstraintUsage[].argvIndex
+ /// entries to values greater than zero.
+ /// Exactly one entry should be set to 1, another to 2, another to 3,
+ /// and so forth up to as many or as few as the xBestIndex method wants.
+ /// The EXPR of the corresponding constraints will then be passed
+ /// in as the argv[] parameters to xFilter.
+ ///
+ ///
+ /// For example, if the aConstraint[3].argvIndex is set to 1, then
+ /// when xFilter is called, the argv[0] passed to xFilter will have
+ /// the EXPR value of the aConstraint[3] constraint.
+ ///
+ ///
+ /// By default, the SQLite core double checks all constraints on
+ /// each row of the virtual table that it receives. If such a check
+ /// is redundant, the xBestFilter method can suppress that double-check by
+ /// setting aConstraintUsage[].omit.
+ ///
+ ///
+ /// The xBestIndex method should return SQLITE_OK on success. If any
+ /// kind of fatal error occurs, an appropriate error code (ex: SQLITE_NOMEM)
+ /// should be returned instead.
+ ///
+ ///
+ /// If xBestIndex returns SQLITE_CONSTRAINT, that does not indicate an
+ /// error. Rather, SQLITE_CONSTRAINT indicates that the particular combination
+ /// of input parameters specified should not be used in the query plan.
+ /// The SQLITE_CONSTRAINT return is useful for table-valued functions that
+ /// have required parameters. If the aConstraint[].usable field is false
+ /// for one of the required parameter, then the xBestIndex method should
+ /// return SQLITE_CONSTRAINT.
+ ///
+ ///
+ /// The following example will better illustrate the use of SQLITE_CONSTRAINT
+ /// as a return value from xBestIndex:
+ ///
+ ///
+ /// SELECT * FROM realtab, tablevaluedfunc(realtab.x);
+ ///
+ ///
+ /// Assuming that the first hidden column of "tablevaluedfunc" is "param1",
+ /// the query above is semantically equivalent to this:
+ ///
+ ///
+ /// SELECT * FROM realtab, tablevaluedfunc
+ /// WHERE tablevaluedfunc.param1 = realtab.x;
+ ///
+ ///
+ /// The query planner must decide between many possible implementations
+ /// of this query, but two plans in particular are of note:
+ ///
+ /// ]]>
+ /// ]]>Scan all
+ /// rows of realtab and for each row, find rows in tablevaluedfunc where
+ /// param1 is equal to realtab.x
+ /// ]]>]]>Scan all rows of tablevalued func and for each row find rows
+ /// in realtab where x is equal to tablevaluedfunc.param1.
+ /// ]]>]]>
+ ///
+ /// The xBestIndex method will be invoked once for each of the potential
+ /// plans above. For plan 1, the aConstraint[].usable flag for for the
+ /// SQLITE_CONSTRAINT_EQ constraint on the param1 column will be true because
+ /// the right-hand side value for the "param1 = ?" constraint will be known,
+ /// since it is determined by the outer realtab loop.
+ /// But for plan 2, the aConstraint[].usable flag for "param1 = ?" will be false
+ /// because the right-hand side value is determined by an inner loop and is thus
+ /// an unknown quantity. Because param1 is a required input to the table-valued
+ /// functions, the xBestIndex method should return SQLITE_CONSTRAINT when presented
+ /// with plan 2, indicating that a required input is missing. This forces the
+ /// query planner to select plan 1.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_index_info structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xBestIndex(
+ IntPtr pVtab,
+ IntPtr pIndex
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xDisconnect)(sqlite3_vtab *pVTab);
+ ///
+ ///
+ /// This method releases a connection to a virtual table.
+ /// Only the sqlite3_vtab object is destroyed.
+ /// The virtual table is not destroyed and any backing store
+ /// associated with the virtual table persists.
+ ///
+ /// This method undoes the work of xConnect.
+ ///
+ /// This method is a destructor for a connection to the virtual table.
+ /// Contrast this method with xDestroy. The xDestroy is a destructor
+ /// for the entire virtual table.
+ ///
+ ///
+ /// The xDisconnect method is required for every virtual table implementation,
+ /// though it is acceptable for the xDisconnect and xDestroy methods to be
+ /// the same function if that makes sense for the particular virtual table.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xDisconnect(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xDestroy)(sqlite3_vtab *pVTab);
+ ///
+ ///
+ /// This method releases a connection to a virtual table, just like
+ /// the xDisconnect method, and it also destroys the underlying
+ /// table implementation. This method undoes the work of xCreate.
+ ///
+ ///
+ /// The xDisconnect method is called whenever a database connection
+ /// that uses a virtual table is closed. The xDestroy method is only
+ /// called when a DROP TABLE statement is executed against the virtual table.
+ ///
+ ///
+ /// The xDestroy method is required for every virtual table implementation,
+ /// though it is acceptable for the xDisconnect and xDestroy methods to be
+ /// the same function if that makes sense for the particular virtual table.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xDestroy(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor);
+ ///
+ ///
+ /// The xOpen method creates a new cursor used for accessing (read and/or
+ /// writing) a virtual table. A successful invocation of this method
+ /// will allocate the memory for the sqlite3_vtab_cursor (or a subclass),
+ /// initialize the new object, and make *ppCursor point to the new object.
+ /// The successful call then returns SQLITE_OK.
+ ///
+ ///
+ /// For every successful call to this method, the SQLite core will
+ /// later invoke the xClose method to destroy
+ /// the allocated cursor.
+ ///
+ ///
+ /// The xOpen method need not initialize the pVtab field of the
+ /// sqlite3_vtab_cursor structure. The SQLite core will take care
+ /// of that chore automatically.
+ ///
+ ///
+ /// A virtual table implementation must be able to support an arbitrary
+ /// number of simultaneously open cursors.
+ ///
+ ///
+ /// When initially opened, the cursor is in an undefined state.
+ /// The SQLite core will invoke the xFilter method
+ /// on the cursor prior to any attempt to position or read from the cursor.
+ ///
+ ///
+ /// The xOpen method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to point to the newly
+ /// created native sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xOpen(
+ IntPtr pVtab,
+ ref IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xClose)(sqlite3_vtab_cursor*);
+ ///
+ ///
+ /// The xClose method closes a cursor previously opened by
+ /// xOpen.
+ /// The SQLite core will always call xClose once for each cursor opened
+ /// using xOpen.
+ ///
+ ///
+ /// This method must release all resources allocated by the
+ /// corresponding xOpen call. The routine will not be called again even if it
+ /// returns an error. The SQLite core will not use the
+ /// sqlite3_vtab_cursor again after it has been closed.
+ ///
+ ///
+ /// The xClose method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xClose(
+ IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr,
+ /// int argc, sqlite3_value **argv);
+ ///
+ ///
+ /// This method begins a search of a virtual table.
+ /// The first argument is a cursor opened by xOpen.
+ /// The next two arguments define a particular search index previously
+ /// chosen by xBestIndex. The specific meanings of idxNum and idxStr
+ /// are unimportant as long as xFilter and xBestIndex agree on what
+ /// that meaning is.
+ ///
+ ///
+ /// The xBestIndex function may have requested the values of
+ /// certain expressions using the aConstraintUsage[].argvIndex values
+ /// of the sqlite3_index_info structure.
+ /// Those values are passed to xFilter using the argc and argv parameters.
+ ///
+ ///
+ /// If the virtual table contains one or more rows that match the
+ /// search criteria, then the cursor must be left point at the first row.
+ /// Subsequent calls to xEof must return false (zero).
+ /// If there are no rows match, then the cursor must be left in a state
+ /// that will cause the xEof to return true (non-zero).
+ /// The SQLite engine will use
+ /// the xColumn and xRowid methods to access that row content.
+ /// The xNext method will be used to advance to the next row.
+ ///
+ ///
+ /// This method must return SQLITE_OK if successful, or an sqlite
+ /// error code if an error occurs.
+ ///
+ ///
+ /// The xFilter method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// Number used to help identify the selected index.
+ ///
+ ///
+ /// The native pointer to the UTF-8 encoded string containing the
+ /// string used to help identify the selected index.
+ ///
+ ///
+ /// The number of native pointers to sqlite3_value structures specified
+ /// in .
+ ///
+ ///
+ /// An array of native pointers to sqlite3_value structures containing
+ /// filtering criteria for the selected index.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xFilter(
+ IntPtr pCursor,
+ int idxNum,
+ IntPtr idxStr,
+ int argc,
+ IntPtr argv
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xNext)(sqlite3_vtab_cursor*);
+ ///
+ ///
+ /// The xNext method advances a virtual table cursor
+ /// to the next row of a result set initiated by xFilter.
+ /// If the cursor is already pointing at the last row when this
+ /// routine is called, then the cursor no longer points to valid
+ /// data and a subsequent call to the xEof method must return true (non-zero).
+ /// If the cursor is successfully advanced to another row of content, then
+ /// subsequent calls to xEof must return false (zero).
+ ///
+ ///
+ /// This method must return SQLITE_OK if successful, or an sqlite
+ /// error code if an error occurs.
+ ///
+ ///
+ /// The xNext method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xNext(
+ IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xEof)(sqlite3_vtab_cursor*);
+ ///
+ ///
+ /// The xEof method must return false (zero) if the specified cursor
+ /// currently points to a valid row of data, or true (non-zero) otherwise.
+ /// This method is called by the SQL engine immediately after each
+ /// xFilter and xNext invocation.
+ ///
+ ///
+ /// The xEof method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// Non-zero if no more rows are available; zero otherwise.
+ ///
+ int xEof(
+ IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int N);
+ ///
+ ///
+ /// The SQLite core invokes this method in order to find the value for
+ /// the N-th column of the current row. N is zero-based so the first column
+ /// is numbered 0.
+ /// The xColumn method may return its result back to SQLite using one of the
+ /// following interface:
+ ///
+ ///
+ /// ]]>
+ /// ]]> sqlite3_result_blob()
+ /// ]]>]]> sqlite3_result_double()
+ /// ]]>]]> sqlite3_result_int()
+ /// ]]>]]> sqlite3_result_int64()
+ /// ]]>]]> sqlite3_result_null()
+ /// ]]>]]> sqlite3_result_text()
+ /// ]]>]]> sqlite3_result_text16()
+ /// ]]>]]> sqlite3_result_text16le()
+ /// ]]>]]> sqlite3_result_text16be()
+ /// ]]>]]> sqlite3_result_zeroblob()
+ /// ]]>]]>
+ ///
+ ///
+ /// If the xColumn method implementation calls none of the functions above,
+ /// then the value of the column defaults to an SQL NULL.
+ ///
+ ///
+ /// To raise an error, the xColumn method should use one of the result_text()
+ /// methods to set the error message text, then return an appropriate
+ /// error code. The xColumn method must return SQLITE_OK on success.
+ ///
+ ///
+ /// The xColumn method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_context structure to be used
+ /// for returning the specified column value to the SQLite core
+ /// library.
+ ///
+ ///
+ /// The zero-based index corresponding to the column containing the
+ /// value to be returned.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xColumn(
+ IntPtr pCursor,
+ IntPtr pContext,
+ int index
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xRowid)(sqlite3_vtab_cursor *pCur, sqlite_int64 *pRowid);
+ ///
+ ///
+ /// A successful invocation of this method will cause *pRowid to be
+ /// filled with the rowid of row that the
+ /// virtual table cursor pCur is currently pointing at.
+ /// This method returns SQLITE_OK on success.
+ /// It returns an appropriate error code on failure.
+ ///
+ ///
+ /// The xRowid method is required for every virtual table implementation.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the unique
+ /// integer row identifier for the current row for the specified cursor.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xRowId(
+ IntPtr pCursor,
+ ref long rowId
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xUpdate)(
+ /// sqlite3_vtab *pVTab,
+ /// int argc,
+ /// sqlite3_value **argv,
+ /// sqlite_int64 *pRowid
+ /// );
+ ///
+ ///
+ /// All changes to a virtual table are made using the xUpdate method.
+ /// This one method can be used to insert, delete, or update.
+ ///
+ ///
+ /// The argc parameter specifies the number of entries in the argv array.
+ /// The value of argc will be 1 for a pure delete operation or N+2 for an insert
+ /// or replace or update where N is the number of columns in the table.
+ /// In the previous sentence, N includes any hidden columns.
+ ///
+ ///
+ /// Every argv entry will have a non-NULL value in C but may contain the
+ /// SQL value NULL. In other words, it is always true that
+ /// ]]>argv[i]!=0]]> for ]]>i]]> between 0 and ]]>argc-1]]>.
+ /// However, it might be the case that
+ /// ]]>sqlite3_value_type(argv[i])==SQLITE_NULL]]>.
+ ///
+ ///
+ /// The argv[0] parameter is the rowid of a row in the virtual table
+ /// to be deleted. If argv[0] is an SQL NULL, then no deletion occurs.
+ ///
+ ///
+ /// The argv[1] parameter is the rowid of a new row to be inserted
+ /// into the virtual table. If argv[1] is an SQL NULL, then the implementation
+ /// must choose a rowid for the newly inserted row. Subsequent argv[]
+ /// entries contain values of the columns of the virtual table, in the
+ /// order that the columns were declared. The number of columns will
+ /// match the table declaration that the xConnect or xCreate method made
+ /// using the sqlite3_declare_vtab() call. All hidden columns are included.
+ ///
+ ///
+ /// When doing an insert without a rowid (argc>1, argv[1] is an SQL NULL),
+ /// on a virtual table that uses ROWID (but not on a WITHOUT ROWID virtual table),
+ /// the implementation must set *pRowid to the rowid of the newly inserted row;
+ /// this will become the value returned by the sqlite3_last_insert_rowid()
+ /// function. Setting this value in all the other cases is a harmless no-op;
+ /// the SQLite engine ignores the *pRowid return value if argc==1 or
+ /// argv[1] is not an SQL NULL.
+ ///
+ ///
+ /// Each call to xUpdate will fall into one of cases shown below.
+ /// Not that references to ]]>argv[i]]]> mean the SQL value
+ /// held within the argv[i] object, not the argv[i]
+ /// object itself.
+ ///
+ ///
+ /// ]]>
+ /// ]]>]]>argc = 1 ]]> argv[0] ≠ NULL]]>
+ /// ]]>]]>
+ /// DELETE: The single row with rowid or PRIMARY KEY equal to argv[0] is deleted.
+ /// No insert occurs.
+ /// ]]>]]>]]>argc > 1 ]]> argv[0] = NULL]]>
+ /// ]]>]]>
+ /// INSERT: A new row is inserted with column values taken from
+ /// argv[2] and following. In a rowid virtual table, if argv[1] is an SQL NULL,
+ /// then a new unique rowid is generated automatically. The argv[1] will be NULL
+ /// for a WITHOUT ROWID virtual table, in which case the implementation should
+ /// take the PRIMARY KEY value from the appropriate column in argv[2] and following.
+ /// ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] = argv[1]]]>
+ /// ]]>]]>
+ /// UPDATE:
+ /// The row with rowid or PRIMARY KEY argv[0] is updated with new values
+ /// in argv[2] and following parameters.
+ /// ]]>]]>]]>argc > 1 ]]> argv[0] ≠ NULL ]]> argv[0] ≠ argv[1]]]>
+ /// ]]>]]>
+ /// UPDATE with rowid or PRIMARY KEY change:
+ /// The row with rowid or PRIMARY KEY argv[0] is updated with
+ /// the rowid or PRIMARY KEY in argv[1]
+ /// and new values in argv[2] and following parameters. This will occur
+ /// when an SQL statement updates a rowid, as in the statement:
+ ///
+ /// UPDATE table SET rowid=rowid+1 WHERE ...;
+ ///
+ /// ]]>]]>
+ ///
+ ///
+ /// The xUpdate method must return SQLITE_OK if and only if it is
+ /// successful. If a failure occurs, the xUpdate must return an appropriate
+ /// error code. On a failure, the pVTab->zErrMsg element may optionally
+ /// be replaced with error message text stored in memory allocated from SQLite
+ /// using functions such as sqlite3_mprintf() or sqlite3_malloc().
+ ///
+ ///
+ /// If the xUpdate method violates some constraint of the virtual table
+ /// (including, but not limited to, attempting to store a value of the wrong
+ /// datatype, attempting to store a value that is too
+ /// large or too small, or attempting to change a read-only value) then the
+ /// xUpdate must fail with an appropriate error code.
+ ///
+ ///
+ /// If the xUpdate method is performing an UPDATE, then
+ /// sqlite3_value_nochange(X) can be used to discover which columns
+ /// of the virtual table were actually modified by the UPDATE
+ /// statement. The sqlite3_value_nochange(X) interface returns
+ /// true for columns that do not change.
+ /// On every UPDATE, SQLite will first invoke
+ /// xColumn separately for each unchanging column in the table to
+ /// obtain the value for that column. The xColumn method can
+ /// check to see if the column is unchanged at the SQL level
+ /// by invoking sqlite3_vtab_nochange(). If xColumn sees that
+ /// the column is not being modified, it should return without setting
+ /// a result using one of the sqlite3_result_xxxxx()
+ /// interfaces. Only in that case sqlite3_value_nochange() will be
+ /// true within the xUpdate method. If xColumn does
+ /// invoke one or more sqlite3_result_xxxxx()
+ /// interfaces, then SQLite understands that as a change in the value
+ /// of the column and the sqlite3_value_nochange() call for that
+ /// column within xUpdate will return false.
+ ///
+ ///
+ /// There might be one or more sqlite3_vtab_cursor objects open and in use
+ /// on the virtual table instance and perhaps even on the row of the virtual
+ /// table when the xUpdate method is invoked. The implementation of
+ /// xUpdate must be prepared for attempts to delete or modify rows of the table
+ /// out from other existing cursors. If the virtual table cannot accommodate
+ /// such changes, the xUpdate method must return an error code.
+ ///
+ ///
+ /// The xUpdate method is optional.
+ /// If the xUpdate pointer in the sqlite3_module for a virtual table
+ /// is a NULL pointer, then the virtual table is read-only.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The number of new or modified column values contained in
+ /// .
+ ///
+ ///
+ /// The array of native pointers to sqlite3_value structures containing
+ /// the new or modified column values, if any.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the unique
+ /// integer row identifier for the row that was inserted, if any.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xUpdate(
+ IntPtr pVtab,
+ int argc,
+ IntPtr argv,
+ ref long rowId
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xBegin)(sqlite3_vtab *pVTab);
+ ///
+ ///
+ /// This method begins a transaction on a virtual table.
+ /// This is method is optional. The xBegin pointer of sqlite3_module
+ /// may be NULL.
+ ///
+ ///
+ /// This method is always followed by one call to either the
+ /// xCommit or xRollback method. Virtual table transactions do
+ /// not nest, so the xBegin method will not be invoked more than once
+ /// on a single virtual table
+ /// without an intervening call to either xCommit or xRollback.
+ /// Multiple calls to other methods can and likely will occur in between
+ /// the xBegin and the corresponding xCommit or xRollback.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xBegin(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xSync)(sqlite3_vtab *pVTab);
+ ///
+ ///
+ /// This method signals the start of a two-phase commit on a virtual
+ /// table.
+ /// This is method is optional. The xSync pointer of sqlite3_module
+ /// may be NULL.
+ ///
+ ///
+ /// This method is only invoked after call to the xBegin method and
+ /// prior to an xCommit or xRollback. In order to implement two-phase
+ /// commit, the xSync method on all virtual tables is invoked prior to
+ /// invoking the xCommit method on any virtual table. If any of the
+ /// xSync methods fail, the entire transaction is rolled back.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xSync(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xCommit)(sqlite3_vtab *pVTab);
+ ///
+ ///
+ /// This method causes a virtual table transaction to commit.
+ /// This is method is optional. The xCommit pointer of sqlite3_module
+ /// may be NULL.
+ ///
+ ///
+ /// A call to this method always follows a prior call to xBegin and
+ /// xSync.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xCommit(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xRollback)(sqlite3_vtab *pVTab);
+ ///
+ ///
+ /// This method causes a virtual table transaction to rollback.
+ /// This is method is optional. The xRollback pointer of sqlite3_module
+ /// may be NULL.
+ ///
+ ///
+ /// A call to this method always follows a prior call to xBegin.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xRollback(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xFindFunction)(
+ /// sqlite3_vtab *pVtab,
+ /// int nArg,
+ /// const char *zName,
+ /// void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ /// void **ppArg
+ /// );
+ ///
+ ///
+ /// This method is called during sqlite3_prepare() to give the virtual
+ /// table implementation an opportunity to overload functions.
+ /// This method may be set to NULL in which case no overloading occurs.
+ ///
+ ///
+ /// When a function uses a column from a virtual table as its first
+ /// argument, this method is called to see if the virtual table would
+ /// like to overload the function. The first three parameters are inputs:
+ /// the virtual table, the number of arguments to the function, and the
+ /// name of the function. If no overloading is desired, this method
+ /// returns 0. To overload the function, this method writes the new
+ /// function implementation into *pxFunc and writes user data into *ppArg
+ /// and returns either 1 or a number between
+ /// SQLITE_INDEX_CONSTRAINT_FUNCTION and 255.
+ ///
+ ///
+ /// Historically, the return value from xFindFunction() was either zero
+ /// or one. Zero means that the function is not overloaded and one means that
+ /// it is overload. The ability to return values of
+ /// SQLITE_INDEX_CONSTRAINT_FUNCTION or greater was added in
+ /// version 3.25.0 (2018-09-15). If xFindFunction returns
+ /// SQLITE_INDEX_CONSTRAINT_FUNCTION or greater, than means that the function
+ /// takes two arguments and the function
+ /// can be used as a boolean in the WHERE clause of a query and that
+ /// the virtual table is able to exploit that function to speed up the query
+ /// result. When xFindFunction returns SQLITE_INDEX_CONSTRAINT_FUNCTION or
+ /// larger, the value returned becomes the sqlite3_index_info.aConstraint.op
+ /// value for one of the constraints passed into xBestIndex() and the second
+ /// argument becomes the value corresponding to that constraint that is passed
+ /// to xFilter(). This enables the
+ /// xBestIndex()/xFilter implementations to use the function to speed
+ /// its search.
+ ///
+ ///
+ /// The technique of having xFindFunction() return values of
+ /// SQLITE_INDEX_CONSTRAINT_FUNCTION was initially used in the implementation
+ /// of the Geopoly module. The xFindFunction() method of that module returns
+ /// SQLITE_INDEX_CONSTRAINT_FUNCTION for the geopoly_overlap() SQL function
+ /// and it returns
+ /// SQLITE_INDEX_CONSTRAINT_FUNCTION+1 for the geopoly_within() SQL function.
+ /// This permits search optimizations for queries such as:
+ ///
+ ///
+ /// SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
+ ///
+ ///
+ /// Note that infix functions (LIKE, GLOB, REGEXP, and MATCH) reverse
+ /// the order of their arguments. So "like(A,B)" is equivalent to "B like A".
+ /// For the form "B like A" the B term is considered the first argument
+ /// to the function. But for "like(A,B)" the A term is considered the
+ /// first argument.
+ ///
+ ///
+ /// The function pointer returned by this routine must be valid for
+ /// the lifetime of the sqlite3_vtab object given in the first parameter.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The number of arguments to the function being sought.
+ ///
+ ///
+ /// The name of the function being sought.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// delegate responsible for implementing the specified function.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// native user-data pointer associated with
+ /// .
+ ///
+ ///
+ /// Non-zero if the specified function was found; zero otherwise.
+ ///
+ int xFindFunction(
+ IntPtr pVtab,
+ int nArg,
+ IntPtr zName,
+ ref SQLiteCallback callback,
+ ref IntPtr pClientData
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
+ ///
+ ///
+ /// This method provides notification that the virtual table implementation
+ /// that the virtual table will be given a new name.
+ /// If this method returns SQLITE_OK then SQLite renames the table.
+ /// If this method returns an error code then the renaming is prevented.
+ ///
+ ///
+ /// The xRename method is optional. If omitted, then the virtual
+ /// table may not be renamed using the ALTER TABLE RENAME command.
+ ///
+ ///
+ /// The PRAGMA legacy_alter_table setting is enabled prior to invoking this
+ /// method, and the value for legacy_alter_table is restored after this
+ /// method finishes. This is necessary for the correct operation of virtual
+ /// tables that make use of shadow tables where the shadow tables must be
+ /// renamed to match the new virtual table name. If the legacy_alter_format is
+ /// off, then the xConnect method will be invoked for the virtual table every
+ /// time the xRename method tries to change the name of the shadow table.
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The native pointer to the UTF-8 encoded string containing the new
+ /// name for the virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xRename(
+ IntPtr pVtab,
+ IntPtr zNew
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ /// int (*xRelease)(sqlite3_vtab *pVtab, int);
+ /// int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+ ///
+ ///
+ /// These methods provide the virtual table implementation an opportunity to
+ /// implement nested transactions. They are always optional and will only be
+ /// called in SQLite version 3.7.7 (2011-06-23) and later.
+ ///
+ ///
+ /// When xSavepoint(X,N) is invoked, that is a signal to the virtual table X
+ /// that it should save its current state as savepoint N.
+ /// A subsequent call
+ /// to xRollbackTo(X,R) means that the state of the virtual table should return
+ /// to what it was when xSavepoint(X,R) was last called.
+ /// The call
+ /// to xRollbackTo(X,R) will invalidate all savepoints with N>R; none of the
+ /// invalided savepoints will be rolled back or released without first
+ /// being reinitialized by a call to xSavepoint().
+ /// A call to xRelease(X,M) invalidates all savepoints where N>=M.
+ ///
+ ///
+ /// None of the xSavepoint(), xRelease(), or xRollbackTo() methods will ever
+ /// be called except in between calls to xBegin() and
+ /// either xCommit() or xRollback().
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// This is an integer identifier under which the the current state of
+ /// the virtual table should be saved.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xSavepoint(
+ IntPtr pVtab,
+ int iSavepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ /// int (*xRelease)(sqlite3_vtab *pVtab, int);
+ /// int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+ ///
+ ///
+ /// These methods provide the virtual table implementation an opportunity to
+ /// implement nested transactions. They are always optional and will only be
+ /// called in SQLite version 3.7.7 (2011-06-23) and later.
+ ///
+ ///
+ /// When xSavepoint(X,N) is invoked, that is a signal to the virtual table X
+ /// that it should save its current state as savepoint N.
+ /// A subsequent call
+ /// to xRollbackTo(X,R) means that the state of the virtual table should return
+ /// to what it was when xSavepoint(X,R) was last called.
+ /// The call
+ /// to xRollbackTo(X,R) will invalidate all savepoints with N>R; none of the
+ /// invalided savepoints will be rolled back or released without first
+ /// being reinitialized by a call to xSavepoint().
+ /// A call to xRelease(X,M) invalidates all savepoints where N>=M.
+ ///
+ ///
+ /// None of the xSavepoint(), xRelease(), or xRollbackTo() methods will ever
+ /// be called except in between calls to xBegin() and
+ /// either xCommit() or xRollback().
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// This is an integer used to indicate that any saved states with an
+ /// identifier greater than or equal to this should be deleted by the
+ /// virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xRelease(
+ IntPtr pVtab,
+ int iSavepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ ///
+ /// int (*xSavepoint)(sqlite3_vtab *pVtab, int);
+ /// int (*xRelease)(sqlite3_vtab *pVtab, int);
+ /// int (*xRollbackTo)(sqlite3_vtab *pVtab, int);
+ ///
+ ///
+ /// These methods provide the virtual table implementation an opportunity to
+ /// implement nested transactions. They are always optional and will only be
+ /// called in SQLite version 3.7.7 (2011-06-23) and later.
+ ///
+ ///
+ /// When xSavepoint(X,N) is invoked, that is a signal to the virtual table X
+ /// that it should save its current state as savepoint N.
+ /// A subsequent call
+ /// to xRollbackTo(X,R) means that the state of the virtual table should return
+ /// to what it was when xSavepoint(X,R) was last called.
+ /// The call
+ /// to xRollbackTo(X,R) will invalidate all savepoints with N>R; none of the
+ /// invalided savepoints will be rolled back or released without first
+ /// being reinitialized by a call to xSavepoint().
+ /// A call to xRelease(X,M) invalidates all savepoints where N>=M.
+ ///
+ ///
+ /// None of the xSavepoint(), xRelease(), or xRollbackTo() methods will ever
+ /// be called except in between calls to xBegin() and
+ /// either xCommit() or xRollback().
+ ///
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// This is an integer identifier used to specify a specific saved
+ /// state for the virtual table for it to restore itself back to, which
+ /// should also have the effect of deleting all saved states with an
+ /// integer identifier greater than this one.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode xRollbackTo(
+ IntPtr pVtab,
+ int iSavepoint
+ );
+ }
+ #endregion
+}
diff --git a/Native.Csharp.Tool/SQLite/LINQ/SQLiteConnection_Linq.cs b/Native.Csharp.Tool/SQLite/LINQ/SQLiteConnection_Linq.cs
new file mode 100644
index 00000000..6f16dfc6
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/LINQ/SQLiteConnection_Linq.cs
@@ -0,0 +1,22 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System.Data.Common;
+
+ public sealed partial class SQLiteConnection
+ {
+ ///
+ /// Returns the instance.
+ ///
+ protected override DbProviderFactory DbProviderFactory
+ {
+ get { return SQLiteFactory.Instance; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Native.Csharp.Tool/SQLite/LINQ/SQLiteFactory_Linq.cs b/Native.Csharp.Tool/SQLite/LINQ/SQLiteFactory_Linq.cs
new file mode 100644
index 00000000..ac95d5cf
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/LINQ/SQLiteFactory_Linq.cs
@@ -0,0 +1,110 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Globalization;
+ using System.Reflection;
+ using System.Security.Permissions;
+
+ ///
+ /// SQLite implementation of .
+ ///
+ public sealed partial class SQLiteFactory : IServiceProvider
+ {
+ //
+ // TODO: This points to the legacy "System.Data.SQLite.Linq" assembly
+ // (i.e. the one that does not support Entity Framework 6).
+ // Currently, this class and its containing assembly (i.e.
+ // "System.Data.SQLite") know nothing about the Entity Framework
+ // 6 compatible assembly (i.e. "System.Data.SQLite.EF6"). This
+ // situation may need to change.
+ //
+ private static readonly string DefaultTypeName =
+ "System.Data.SQLite.Linq.SQLiteProviderServices, System.Data.SQLite.Linq, " +
+ "Version={0}, Culture=neutral, PublicKeyToken=db937bc2d44ff139";
+
+ private static readonly BindingFlags DefaultBindingFlags =
+ BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static Type _dbProviderServicesType;
+ private static object _sqliteServices;
+
+ static SQLiteFactory()
+ {
+#if (SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK) && PRELOAD_NATIVE_LIBRARY
+ UnsafeNativeMethods.Initialize();
+#endif
+
+ SQLiteLog.Initialize(typeof(SQLiteFactory).Name);
+
+ string version =
+#if NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47 || NET_471 || NET_472
+ "4.0.0.0";
+#else
+ "3.5.0.0";
+#endif
+
+ _dbProviderServicesType = Type.GetType(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "System.Data.Common.DbProviderServices, System.Data.Entity, Version={0}, Culture=neutral, PublicKeyToken=b77a5c561934e089", version), false);
+ }
+
+ ///
+ /// Will provide a object in .NET 3.5.
+ ///
+ /// The class or interface type to query for.
+ ///
+ object IServiceProvider.GetService(Type serviceType)
+ {
+ if (serviceType == typeof(ISQLiteSchemaExtensions) ||
+ (_dbProviderServicesType != null && serviceType == _dbProviderServicesType))
+ {
+ return GetSQLiteProviderServicesInstance();
+ }
+ return null;
+ }
+
+#if !NET_STANDARD_20
+ [ReflectionPermission(SecurityAction.Assert, MemberAccess = true)]
+#endif
+ private object GetSQLiteProviderServicesInstance()
+ {
+ if (_sqliteServices == null)
+ {
+ string typeName = UnsafeNativeMethods.GetSettingValue(
+ "TypeName_SQLiteProviderServices", null);
+
+ Version version = this.GetType().Assembly.GetName().Version;
+
+ if (typeName != null)
+ {
+ typeName = HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture, typeName, version);
+ }
+ else
+ {
+ typeName = HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture, DefaultTypeName, version);
+ }
+
+ Type type = Type.GetType(typeName, false);
+
+ if (type != null)
+ {
+ FieldInfo field = type.GetField(
+ "Instance", DefaultBindingFlags);
+
+ if (field != null)
+ _sqliteServices = field.GetValue(null);
+ }
+ }
+ return _sqliteServices;
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/Resources/DataTypes.xml b/Native.Csharp.Tool/SQLite/Resources/DataTypes.xml
new file mode 100644
index 00000000..03cd3f3e
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/Resources/DataTypes.xml
@@ -0,0 +1,806 @@
+
+
+
+
+
+
+ smallint
+ 10
+ 5
+ System.Int16
+ smallint
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ int
+ 11
+ 10
+ System.Int32
+ int
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ real
+ 8
+ 6
+ System.Double
+ real
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ single
+ 15
+ 7
+ System.Single
+ single
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ float
+ 8
+ 6
+ System.Double
+ float
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ double
+ 8
+ 6
+ System.Double
+ double
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ money
+ 7
+ 19
+ System.Decimal
+ money
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ currency
+ 7
+ 19
+ System.Decimal
+ currency
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ decimal
+ 7
+ 19
+ System.Decimal
+ decimal
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ numeric
+ 7
+ 19
+ System.Decimal
+ numeric
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ bit
+ 3
+ 1
+ System.Boolean
+ bit
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ true
+
+
+ yesno
+ 3
+ 1
+ System.Boolean
+ yesno
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+
+
+ logical
+ 3
+ 1
+ System.Boolean
+ logical
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+
+
+ bool
+ 3
+ 1
+ System.Boolean
+ bool
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+
+
+ boolean
+ 3
+ 1
+ System.Boolean
+ boolean
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+
+
+ tinyint
+ 2
+ 3
+ System.Byte
+ tinyint
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+
+
+ integer
+ 12
+ 19
+ System.Int64
+ integer
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+
+
+ counter
+ 12
+ 19
+ System.Int64
+ counter
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ autoincrement
+ 12
+ 19
+ System.Int64
+ autoincrement
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ identity
+ 12
+ 19
+ System.Int64
+ identity
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ long
+ 12
+ 19
+ System.Int64
+ long
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ bigint
+ 12
+ 19
+ System.Int64
+ bigint
+ true
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+
+
+ binary
+ 1
+ 2147483647
+ System.Byte[]
+ binary
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ X'
+ '
+ true
+
+
+ varbinary
+ 1
+ 2147483647
+ System.Byte[]
+ varbinary
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ X'
+ '
+ false
+
+
+ blob
+ 1
+ 2147483647
+ System.Byte[]
+ blob
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ X'
+ '
+ false
+
+
+ image
+ 1
+ 2147483647
+ System.Byte[]
+ image
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ X'
+ '
+ false
+
+
+ general
+ 1
+ 2147483647
+ System.Byte[]
+ general
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ X'
+ '
+ false
+
+
+ oleobject
+ 1
+ 2147483647
+ System.Byte[]
+ oleobject
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ X'
+ '
+ false
+
+
+ varchar
+ 16
+ 2147483647
+ max length
+ System.String
+ varchar({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ true
+
+
+ nvarchar
+ 16
+ 2147483647
+ max length
+ System.String
+ nvarchar({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ true
+
+
+ memo
+ 16
+ 2147483647
+ max length
+ System.String
+ memo({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ longtext
+ 16
+ 2147483647
+ max length
+ System.String
+ longtext({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ note
+ 16
+ 2147483647
+ max length
+ System.String
+ note({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ text
+ 16
+ 2147483647
+ max length
+ System.String
+ text({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ ntext
+ 16
+ 2147483647
+ max length
+ System.String
+ ntext({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ string
+ 16
+ 2147483647
+ max length
+ System.String
+ string({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ char
+ 16
+ 2147483647
+ max length
+ System.String
+ char({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ nchar
+ 16
+ 2147483647
+ max length
+ System.String
+ char({0})
+ false
+ false
+ false
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ datetime
+ 6
+ 23
+ System.DateTime
+ datetime
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ true
+
+
+ smalldate
+ 6
+ 23
+ System.DateTime
+ smalldate
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ timestamp
+ 6
+ 23
+ System.DateTime
+ timestamp
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ date
+ 6
+ 23
+ System.DateTime
+ date
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ time
+ 6
+ 23
+ System.DateTime
+ time
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ true
+ '
+ '
+ false
+
+
+ uniqueidentifier
+ 9
+ 16
+ System.Guid
+ uniqueidentifier
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ '
+ '
+ true
+
+
+ guid
+ 9
+ 16
+ System.Guid
+ guid
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ '
+ '
+ false
+
+
diff --git a/Native.Csharp.Tool/SQLite/Resources/MetaDataCollections.xml b/Native.Csharp.Tool/SQLite/Resources/MetaDataCollections.xml
new file mode 100644
index 00000000..fa57aab6
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/Resources/MetaDataCollections.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+ MetaDataCollections
+ 0
+ 0
+
+
+ DataSourceInformation
+ 0
+ 0
+
+
+ DataTypes
+ 0
+ 0
+
+
+ ReservedWords
+ 0
+ 0
+
+
+ Catalogs
+ 1
+ 1
+
+
+ Columns
+ 4
+ 4
+
+
+ Indexes
+ 4
+ 3
+
+
+ IndexColumns
+ 5
+ 4
+
+
+ Tables
+ 4
+ 3
+
+
+ Views
+ 3
+ 3
+
+
+ ViewColumns
+ 4
+ 4
+
+
+ ForeignKeys
+ 4
+ 3
+
+
+ Triggers
+ 4
+ 3
+
+
diff --git a/Native.Csharp.Tool/SQLite/Resources/SQLiteCommand.bmp b/Native.Csharp.Tool/SQLite/Resources/SQLiteCommand.bmp
new file mode 100644
index 00000000..13dbda04
Binary files /dev/null and b/Native.Csharp.Tool/SQLite/Resources/SQLiteCommand.bmp differ
diff --git a/Native.Csharp.Tool/SQLite/Resources/SQLiteConnection.bmp b/Native.Csharp.Tool/SQLite/Resources/SQLiteConnection.bmp
new file mode 100644
index 00000000..f787a2db
Binary files /dev/null and b/Native.Csharp.Tool/SQLite/Resources/SQLiteConnection.bmp differ
diff --git a/Native.Csharp.Tool/SQLite/Resources/SQLiteDataAdapter.bmp b/Native.Csharp.Tool/SQLite/Resources/SQLiteDataAdapter.bmp
new file mode 100644
index 00000000..27186a09
Binary files /dev/null and b/Native.Csharp.Tool/SQLite/Resources/SQLiteDataAdapter.bmp differ
diff --git a/Native.Csharp.Tool/SQLite/Resources/SR.Designer.cs b/Native.Csharp.Tool/SQLite/Resources/SR.Designer.cs
new file mode 100644
index 00000000..f33df398
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/Resources/SR.Designer.cs
@@ -0,0 +1,121 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.1
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace System.Data.SQLite {
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+#if !NET_COMPACT_20
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+#endif
+ internal sealed class SR {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal SR() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Data.SQLite.SR", typeof(SR).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <?xml version="1.0" standalone="yes"?>
+ ///<DocumentElement>
+ /// <DataTypes>
+ /// <TypeName>smallint</TypeName>
+ /// <ProviderDbType>10</ProviderDbType>
+ /// <ColumnSize>5</ColumnSize>
+ /// <DataType>System.Int16</DataType>
+ /// <CreateFormat>smallint</CreateFormat>
+ /// <IsAutoIncrementable>false</IsAutoIncrementable>
+ /// <IsCaseSensitive>false</IsCaseSensitive>
+ /// <IsFixedLength>true</IsFixedLength>
+ /// <IsFixedPrecisionScale>true</IsFixedPrecisionScale>
+ /// <IsLong>false</IsLong>
+ /// <IsNullable>true</ [rest of string was truncated]";.
+ ///
+ internal static string DataTypes {
+ get {
+ return ResourceManager.GetString("DataTypes", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to ALL,ALTER,AND,AS,AUTOINCREMENT,BETWEEN,BY,CASE,CHECK,COLLATE,COMMIT,CONSTRAINT,CREATE,CROSS,DEFAULT,DEFERRABLE,DELETE,DISTINCT,DROP,ELSE,ESCAPE,EXCEPT,FOREIGN,FROM,FULL,GROUP,HAVING,IN,INDEX,INNER,INSERT,INTERSECT,INTO,IS,ISNULL,JOIN,LEFT,LIMIT,NATURAL,NOT,NOTNULL,NULL,ON,OR,ORDER,OUTER,PRIMARY,REFERENCES,RIGHT,ROLLBACK,SELECT,SET,TABLE,THEN,TO,TRANSACTION,UNION,UNIQUE,UPDATE,USING,VALUES,WHEN,WHERE.
+ ///
+ internal static string Keywords {
+ get {
+ return ResourceManager.GetString("Keywords", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?>
+ ///<DocumentElement>
+ /// <MetaDataCollections>
+ /// <CollectionName>MetaDataCollections</CollectionName>
+ /// <NumberOfRestrictions>0</NumberOfRestrictions>
+ /// <NumberOfIdentifierParts>0</NumberOfIdentifierParts>
+ /// </MetaDataCollections>
+ /// <MetaDataCollections>
+ /// <CollectionName>DataSourceInformation</CollectionName>
+ /// <NumberOfRestrictions>0</NumberOfRestrictions>
+ /// <NumberOfIdentifierParts>0</NumberOfIdentifierParts>
+ /// </MetaDataCollections>
+ /// <MetaDataC [rest of string was truncated]";.
+ ///
+ internal static string MetaDataCollections {
+ get {
+ return ResourceManager.GetString("MetaDataCollections", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/Resources/SR.resx b/Native.Csharp.Tool/SQLite/Resources/SR.resx
new file mode 100644
index 00000000..5c6589d1
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/Resources/SR.resx
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ DataTypes.xml;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
+ ABORT,ACTION,ADD,AFTER,ALL,ALTER,ANALYZE,AND,AS,ASC,ATTACH,AUTOINCREMENT,BEFORE,BEGIN,BETWEEN,BY,CASCADE,CASE,CAST,CHECK,COLLATE,COLUMN,COMMIT,CONFLICT,CONSTRAINT,CREATE,CROSS,CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,DATABASE,DEFAULT,DEFERRABLE,DEFERRED,DELETE,DESC,DETACH,DISTINCT,DO,DROP,EACH,ELSE,END,ESCAPE,EXCEPT,EXCLUSIVE,EXISTS,EXPLAIN,FAIL,FOR,FOREIGN,FROM,FULL,GLOB,GROUP,HAVING,IF,IGNORE,IMMEDIATE,IN,INDEX,INDEXED,INITIALLY,INNER,INSERT,INSTEAD,INTERSECT,INTO,IS,ISNULL,JOIN,KEY,LEFT,LIKE,LIMIT,MATCH,NATURAL,NO,NOT,NOTHING,NOTNULL,NULL,OF,OFFSET,ON,OR,ORDER,OUTER,PLAN,PRAGMA,PRIMARY,QUERY,RAISE,RECURSIVE,REFERENCES,REGEXP,REINDEX,RELEASE,RENAME,REPLACE,RESTRICT,RIGHT,ROLLBACK,ROW,SAVEPOINT,SELECT,SET,TABLE,TEMP,TEMPORARY,THEN,TO,TRANSACTION,TRIGGER,UNION,UNIQUE,UPDATE,USING,VACUUM,VALUES,VIEW,VIRTUAL,WHEN,WHERE,WITH,WITHOUT
+
+
+ MetaDataCollections.xml;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+
\ No newline at end of file
diff --git a/Native.Csharp.Tool/SQLite/SQLite3.cs b/Native.Csharp.Tool/SQLite/SQLite3.cs
new file mode 100644
index 00000000..d7817c3b
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLite3.cs
@@ -0,0 +1,4143 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Collections.Generic;
+
+#if !NET_COMPACT_20 && (TRACE_CONNECTION || TRACE_STATEMENT)
+ using System.Diagnostics;
+#endif
+
+ using System.Globalization;
+ using System.Runtime.InteropServices;
+ using System.Text;
+ using System.Threading;
+
+ ///
+ /// This is the method signature for the SQLite core library logging callback
+ /// function for use with sqlite3_log() and the SQLITE_CONFIG_LOG.
+ ///
+ /// WARNING: This delegate is used more-or-less directly by native code, do
+ /// not modify its type signature.
+ ///
+ ///
+ /// The extra data associated with this message, if any.
+ ///
+ ///
+ /// The error code associated with this message.
+ ///
+ ///
+ /// The message string to be logged.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteLogCallback(IntPtr pUserData, int errorCode, IntPtr pMessage);
+
+ ///
+ /// This class implements SQLiteBase completely, and is the guts of the code that interop's SQLite with .NET
+ ///
+ internal class SQLite3 : SQLiteBase
+ {
+ private static object syncRoot = new object();
+
+ ///
+ /// This field is used to refer to memory allocated for the
+ /// SQLITE_DBCONFIG_MAINDBNAME value used with the native
+ /// "sqlite3_db_config" API. If allocated, the associated
+ /// memeory will be freed when the underlying connection is
+ /// closed.
+ ///
+ private IntPtr dbName = IntPtr.Zero;
+
+ //
+ // NOTE: This is the public key for the System.Data.SQLite assembly. If you change the
+ // SNK file, you will need to change this as well.
+ //
+ internal const string PublicKey =
+ "002400000480000094000000060200000024000052534131000400000100010005a288de5687c4e1" +
+ "b621ddff5d844727418956997f475eb829429e411aff3e93f97b70de698b972640925bdd44280df0" +
+ "a25a843266973704137cbb0e7441c1fe7cae4e2440ae91ab8cde3933febcb1ac48dd33b40e13c421" +
+ "d8215c18a4349a436dd499e3c385cc683015f886f6c10bd90115eb2bd61b67750839e3a19941dc9c";
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ internal const string DesignerVersion = "1.0.110.0";
+#endif
+
+ ///
+ /// The opaque pointer returned to us by the sqlite provider
+ ///
+ protected internal SQLiteConnectionHandle _sql;
+ protected string _fileName;
+ protected SQLiteConnectionFlags _flags;
+ private bool _setLogCallback;
+ protected bool _usePool;
+ protected int _poolVersion;
+ private int _cancelCount;
+
+#if (NET_35 || NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47 || NET_471 || NET_472) && !PLATFORM_COMPACTFRAMEWORK
+ private bool _buildingSchema;
+#endif
+
+ ///
+ /// The user-defined functions registered on this connection
+ ///
+ protected Dictionary _functions;
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// This is the name of the native library file that contains the
+ /// "vtshim" extension [wrapper].
+ ///
+ protected string _shimExtensionFileName = null;
+
+ ///
+ /// This is the flag indicate whether the native library file that
+ /// contains the "vtshim" extension must be dynamically loaded by
+ /// this class prior to use.
+ ///
+ protected bool? _shimIsLoadNeeded = null;
+
+ ///
+ /// This is the name of the native entry point for the "vtshim"
+ /// extension [wrapper].
+ ///
+ protected string _shimExtensionProcName = "sqlite3_vtshim_init";
+
+ ///
+ /// The modules created using this connection.
+ ///
+ protected Dictionary _modules;
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs the object used to interact with the SQLite core library
+ /// using the UTF-8 text encoding.
+ ///
+ ///
+ /// The DateTime format to be used when converting string values to a
+ /// DateTime and binding DateTime parameters.
+ ///
+ ///
+ /// The to be used when creating DateTime
+ /// values.
+ ///
+ ///
+ /// The format string to be used when parsing and formatting DateTime
+ /// values.
+ ///
+ ///
+ /// The native handle to be associated with the database connection.
+ ///
+ ///
+ /// The fully qualified file name associated with .
+ ///
+ ///
+ /// Non-zero if the newly created object instance will need to dispose
+ /// of when it is no longer needed.
+ ///
+ internal SQLite3(
+ SQLiteDateFormats fmt,
+ DateTimeKind kind,
+ string fmtString,
+ IntPtr db,
+ string fileName,
+ bool ownHandle
+ )
+ : base(fmt, kind, fmtString)
+ {
+ if (db != IntPtr.Zero)
+ {
+ _sql = new SQLiteConnectionHandle(db, ownHandle);
+ _fileName = fileName;
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null,
+ null, null, null, _sql, fileName, new object[] {
+ typeof(SQLite3), fmt, kind, fmtString, db, fileName,
+ ownHandle }));
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLite3).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+#if INTEROP_VIRTUAL_TABLE
+ DisposeModules();
+#endif
+
+ Close(true); /* Disposing, cannot throw. */
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+#if DEBUG
+ public override string ToString()
+ {
+ return HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture, "fileName = {0}, flags = {1}",
+ _fileName, _flags);
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// This method attempts to dispose of all the derived
+ /// object instances currently associated with the native database connection.
+ ///
+ private void DisposeModules()
+ {
+ //
+ // NOTE: If any modules were created, attempt to dispose of
+ // them now. This code is designed to avoid throwing
+ // exceptions unless the Dispose method of the module
+ // itself throws an exception.
+ //
+ if (_modules != null)
+ {
+ foreach (KeyValuePair pair in _modules)
+ {
+ SQLiteModule module = pair.Value;
+
+ if (module == null)
+ continue;
+
+ module.Dispose();
+ }
+
+ _modules.Clear();
+ }
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ // It isn't necessary to cleanup any functions we've registered. If the connection
+ // goes to the pool and is resurrected later, re-registered functions will overwrite the
+ // previous functions. The SQLiteFunctionCookieHandle will take care of freeing unmanaged
+ // resources belonging to the previously-registered functions.
+ internal override void Close(bool disposing)
+ {
+ if (_sql != null)
+ {
+ if (!_sql.OwnHandle)
+ {
+ _sql = null;
+ return;
+ }
+
+ bool unbindFunctions = HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UnbindFunctionsOnClose);
+
+ retry:
+
+ if (_usePool)
+ {
+ if (SQLiteBase.ResetConnection(_sql, _sql, !disposing) &&
+ UnhookNativeCallbacks(true, !disposing))
+ {
+ if (unbindFunctions)
+ {
+ if (SQLiteFunction.UnbindAllFunctions(this, _flags, false))
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "UnbindFunctions (Pool) Success: {0}",
+ HandleToString()));
+#endif
+ }
+ else
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "UnbindFunctions (Pool) Failure: {0}",
+ HandleToString()));
+#endif
+ }
+ }
+
+#if INTEROP_VIRTUAL_TABLE
+ DisposeModules();
+#endif
+
+ SQLiteConnectionPool.Add(_fileName, _sql, _poolVersion);
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.ClosedToPool, null, null,
+ null, null, _sql, _fileName, new object[] {
+ typeof(SQLite3), !disposing, _fileName, _poolVersion }));
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Close (Pool) Success: {0}",
+ HandleToString()));
+#endif
+ }
+ else
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Close (Pool) Failure: {0}",
+ HandleToString()));
+#endif
+
+ //
+ // NOTE: This connection cannot be added to the pool;
+ // therefore, just use the normal disposal
+ // procedure on it.
+ //
+ _usePool = false;
+ goto retry;
+ }
+ }
+ else
+ {
+ /* IGNORED */
+ UnhookNativeCallbacks(disposing, !disposing);
+
+ if (unbindFunctions)
+ {
+ if (SQLiteFunction.UnbindAllFunctions(this, _flags, false))
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "UnbindFunctions Success: {0}",
+ HandleToString()));
+#endif
+ }
+ else
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "UnbindFunctions Failure: {0}",
+ HandleToString()));
+#endif
+ }
+ }
+
+ _sql.Dispose();
+
+ FreeDbName(!disposing);
+ }
+ _sql = null;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ protected string HandleToString()
+ {
+ if (_sql == null)
+ return "";
+
+ return _sql.ToString();
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the number of times the method has been
+ /// called.
+ ///
+ private int GetCancelCount()
+ {
+ return Interlocked.CompareExchange(ref _cancelCount, 0, 0);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method determines whether or not a
+ /// with a return code of should
+ /// be thrown after making a call into the SQLite core library.
+ ///
+ ///
+ /// Non-zero if a to be thrown. This method
+ /// will only return non-zero if the method was called
+ /// one or more times during a call into the SQLite core library (e.g. when
+ /// the sqlite3_prepare*() or sqlite3_step() APIs are used).
+ ///
+ private bool ShouldThrowForCancel()
+ {
+ return GetCancelCount() > 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Resets the value of the field.
+ ///
+ private int ResetCancelCount()
+ {
+ return Interlocked.CompareExchange(ref _cancelCount, 0, _cancelCount);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to interrupt the query currently executing on the associated
+ /// native database connection.
+ ///
+ internal override void Cancel()
+ {
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ Interlocked.Increment(ref _cancelCount);
+ UnsafeNativeMethods.sqlite3_interrupt(_sql);
+ }
+ }
+
+ ///
+ /// This function binds a user-defined function to the connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// The object instance that implements the
+ /// function to be bound.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ internal override void BindFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ SQLiteFunction function,
+ SQLiteConnectionFlags flags
+ )
+ {
+ if (functionAttribute == null)
+ throw new ArgumentNullException("functionAttribute");
+
+ if (function == null)
+ throw new ArgumentNullException("function");
+
+ SQLiteFunction.BindFunction(this, functionAttribute, function, flags);
+
+ if (_functions == null)
+ _functions = new Dictionary();
+
+ _functions[functionAttribute] = function;
+ }
+
+ ///
+ /// This function binds a user-defined function to the connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be unbound.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ /// Non-zero if the function was unbound and removed.
+ internal override bool UnbindFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ SQLiteConnectionFlags flags
+ )
+ {
+ if (functionAttribute == null)
+ throw new ArgumentNullException("functionAttribute");
+
+ if (_functions == null)
+ return false;
+
+ SQLiteFunction function;
+
+ if (_functions.TryGetValue(functionAttribute, out function))
+ {
+ if (SQLiteFunction.UnbindFunction(
+ this, functionAttribute, function, flags) &&
+ _functions.Remove(functionAttribute))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ internal override string Version
+ {
+ get
+ {
+ return SQLiteVersion;
+ }
+ }
+
+ internal override int VersionNumber
+ {
+ get
+ {
+ return SQLiteVersionNumber;
+ }
+ }
+
+ internal static string DefineConstants
+ {
+ get
+ {
+ StringBuilder result = new StringBuilder();
+ IList list = SQLiteDefineConstants.OptionList;
+
+ if (list != null)
+ {
+ foreach (string element in list)
+ {
+ if (element == null)
+ continue;
+
+ if (result.Length > 0)
+ result.Append(' ');
+
+ result.Append(element);
+ }
+ }
+
+ return result.ToString();
+ }
+ }
+
+ internal static string SQLiteVersion
+ {
+ get
+ {
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_libversion(), -1);
+ }
+ }
+
+ internal static int SQLiteVersionNumber
+ {
+ get
+ {
+ return UnsafeNativeMethods.sqlite3_libversion_number();
+ }
+ }
+
+ internal static string SQLiteSourceId
+ {
+ get
+ {
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_sourceid(), -1);
+ }
+ }
+
+ internal static string SQLiteCompileOptions
+ {
+ get
+ {
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+ IntPtr zValue = UnsafeNativeMethods.sqlite3_compileoption_get(index++);
+
+ while (zValue != IntPtr.Zero)
+ {
+ if (result.Length > 0)
+ result.Append(' ');
+
+ result.Append(UTF8ToString(zValue, -1));
+ zValue = UnsafeNativeMethods.sqlite3_compileoption_get(index++);
+ }
+
+ return result.ToString();
+ }
+ }
+
+ internal static string InteropVersion
+ {
+ get
+ {
+#if !SQLITE_STANDARD
+ return UTF8ToString(UnsafeNativeMethods.interop_libversion(), -1);
+#else
+ return null;
+#endif
+ }
+ }
+
+ internal static string InteropSourceId
+ {
+ get
+ {
+#if !SQLITE_STANDARD
+ return UTF8ToString(UnsafeNativeMethods.interop_sourceid(), -1);
+#else
+ return null;
+#endif
+ }
+ }
+
+ internal static string InteropCompileOptions
+ {
+ get
+ {
+#if !SQLITE_STANDARD
+ StringBuilder result = new StringBuilder();
+ int index = 0;
+ IntPtr zValue = UnsafeNativeMethods.interop_compileoption_get(index++);
+
+ while (zValue != IntPtr.Zero)
+ {
+ if (result.Length > 0)
+ result.Append(' ');
+
+ result.Append(UTF8ToString(zValue, -1));
+ zValue = UnsafeNativeMethods.interop_compileoption_get(index++);
+ }
+
+ return result.ToString();
+#else
+ return null;
+#endif
+ }
+ }
+
+ internal override bool AutoCommit
+ {
+ get
+ {
+ return IsAutocommit(_sql, _sql);
+ }
+ }
+
+ internal override bool IsReadOnly(
+ string name
+ )
+ {
+ IntPtr pDbName = IntPtr.Zero;
+
+ try
+ {
+ if (name != null)
+ pDbName = SQLiteString.Utf8IntPtrFromString(name);
+
+ int result = UnsafeNativeMethods.sqlite3_db_readonly(
+ _sql, pDbName);
+
+ if (result == -1) /* database not found */
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "database \"{0}\" not found", name));
+ }
+
+ return result == 0 ? false : true;
+ }
+ finally
+ {
+ if (pDbName != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pDbName);
+ pDbName = IntPtr.Zero;
+ }
+ }
+ }
+
+ internal override long LastInsertRowId
+ {
+ get
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_last_insert_rowid(_sql);
+#elif !SQLITE_STANDARD
+ long rowId = 0;
+ UnsafeNativeMethods.sqlite3_last_insert_rowid_interop(_sql, ref rowId);
+ return rowId;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+
+ internal override int Changes
+ {
+ get
+ {
+#if !SQLITE_STANDARD
+ return UnsafeNativeMethods.sqlite3_changes_interop(_sql);
+#else
+ return UnsafeNativeMethods.sqlite3_changes(_sql);
+#endif
+ }
+ }
+
+ internal override long MemoryUsed
+ {
+ get
+ {
+ return StaticMemoryUsed;
+ }
+ }
+
+ internal static long StaticMemoryUsed
+ {
+ get
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_memory_used();
+#elif !SQLITE_STANDARD
+ long bytes = 0;
+ UnsafeNativeMethods.sqlite3_memory_used_interop(ref bytes);
+ return bytes;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+
+ internal override long MemoryHighwater
+ {
+ get
+ {
+ return StaticMemoryHighwater;
+ }
+ }
+
+ internal static long StaticMemoryHighwater
+ {
+ get
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_memory_highwater(0);
+#elif !SQLITE_STANDARD
+ long bytes = 0;
+ UnsafeNativeMethods.sqlite3_memory_highwater_interop(0, ref bytes);
+ return bytes;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+
+ ///
+ /// Returns non-zero if the underlying native connection handle is owned
+ /// by this instance.
+ ///
+ internal override bool OwnHandle
+ {
+ get
+ {
+ if (_sql == null)
+ throw new SQLiteException("no connection handle available");
+
+ return _sql.OwnHandle;
+ }
+ }
+
+ ///
+ /// Returns the logical list of functions associated with this connection.
+ ///
+ internal override IDictionary Functions
+ {
+ get { return _functions; }
+ }
+
+ internal override SQLiteErrorCode SetMemoryStatus(bool value)
+ {
+ return StaticSetMemoryStatus(value);
+ }
+
+ internal static SQLiteErrorCode StaticSetMemoryStatus(bool value)
+ {
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_config_int(
+ SQLiteConfigOpsEnum.SQLITE_CONFIG_MEMSTATUS, value ? 1 : 0);
+
+ return rc;
+ }
+
+ ///
+ /// Attempts to free as much heap memory as possible for the database connection.
+ ///
+ /// A standard SQLite return code (i.e. zero for success and non-zero for failure).
+ internal override SQLiteErrorCode ReleaseMemory()
+ {
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_db_release_memory(_sql);
+ return rc;
+ }
+
+ ///
+ /// Attempts to free N bytes of heap memory by deallocating non-essential memory
+ /// allocations held by the database library. Memory used to cache database pages
+ /// to improve performance is an example of non-essential memory. This is a no-op
+ /// returning zero if the SQLite core library was not compiled with the compile-time
+ /// option SQLITE_ENABLE_MEMORY_MANAGEMENT. Optionally, attempts to reset and/or
+ /// compact the Win32 native heap, if applicable.
+ ///
+ ///
+ /// The requested number of bytes to free.
+ ///
+ ///
+ /// Non-zero to attempt a heap reset.
+ ///
+ ///
+ /// Non-zero to attempt heap compaction.
+ ///
+ ///
+ /// The number of bytes actually freed. This value may be zero.
+ ///
+ ///
+ /// This value will be non-zero if the heap reset was successful.
+ ///
+ ///
+ /// The size of the largest committed free block in the heap, in bytes.
+ /// This value will be zero unless heap compaction is enabled.
+ ///
+ ///
+ /// A standard SQLite return code (i.e. zero for success and non-zero
+ /// for failure).
+ ///
+ internal static SQLiteErrorCode StaticReleaseMemory(
+ int nBytes,
+ bool reset,
+ bool compact,
+ ref int nFree,
+ ref bool resetOk,
+ ref uint nLargest
+ )
+ {
+ SQLiteErrorCode rc = SQLiteErrorCode.Ok;
+
+ int nFreeLocal = UnsafeNativeMethods.sqlite3_release_memory(nBytes);
+ uint nLargestLocal = 0;
+ bool resetOkLocal = false;
+
+#if !DEBUG && WINDOWS // NOTE: Should be "WIN32HEAP && !MEMDEBUG && WINDOWS"
+ if (HelperMethods.IsWindows())
+ {
+ if ((rc == SQLiteErrorCode.Ok) && reset)
+ {
+ rc = UnsafeNativeMethods.sqlite3_win32_reset_heap();
+
+ if (rc == SQLiteErrorCode.Ok)
+ resetOkLocal = true;
+ }
+
+ if ((rc == SQLiteErrorCode.Ok) && compact)
+ rc = UnsafeNativeMethods.sqlite3_win32_compact_heap(ref nLargestLocal);
+ }
+ else
+#endif
+ if (reset || compact)
+ {
+ rc = SQLiteErrorCode.NotFound;
+ }
+
+ nFree = nFreeLocal;
+ nLargest = nLargestLocal;
+ resetOk = resetOkLocal;
+
+ return rc;
+ }
+
+ ///
+ /// Shutdown the SQLite engine so that it can be restarted with different
+ /// configuration options. We depend on auto initialization to recover.
+ ///
+ /// Returns a standard SQLite result code.
+ internal override SQLiteErrorCode Shutdown()
+ {
+ return StaticShutdown(false);
+ }
+
+ ///
+ /// Shutdown the SQLite engine so that it can be restarted with different
+ /// configuration options. We depend on auto initialization to recover.
+ ///
+ ///
+ /// Non-zero to reset the database and temporary directories to their
+ /// default values, which should be null for both. This parameter has no
+ /// effect on non-Windows operating systems.
+ ///
+ /// Returns a standard SQLite result code.
+ internal static SQLiteErrorCode StaticShutdown(
+ bool directories
+ )
+ {
+ SQLiteErrorCode rc = SQLiteErrorCode.Ok;
+
+ if (directories)
+ {
+#if WINDOWS
+ if (HelperMethods.IsWindows())
+ {
+ if (rc == SQLiteErrorCode.Ok)
+ rc = UnsafeNativeMethods.sqlite3_win32_set_directory(1, null);
+
+ if (rc == SQLiteErrorCode.Ok)
+ rc = UnsafeNativeMethods.sqlite3_win32_set_directory(2, null);
+ }
+ else
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(
+ "Shutdown: Cannot reset directories on this platform.");
+#endif
+ }
+ }
+
+ if (rc == SQLiteErrorCode.Ok)
+ rc = UnsafeNativeMethods.sqlite3_shutdown();
+
+ return rc;
+ }
+
+ ///
+ /// Determines if the associated native connection handle is open.
+ ///
+ ///
+ /// Non-zero if the associated native connection handle is open.
+ ///
+ internal override bool IsOpen()
+ {
+ return (_sql != null) && !_sql.IsInvalid && !_sql.IsClosed;
+ }
+
+ ///
+ /// Returns the fully qualified path and file name for the currently open
+ /// database, if any.
+ ///
+ ///
+ /// The name of the attached database to query.
+ ///
+ ///
+ /// The fully qualified path and file name for the currently open database,
+ /// if any.
+ ///
+ internal override string GetFileName(string dbName)
+ {
+ if (_sql == null)
+ return null;
+
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_db_filename_bytes(
+ _sql, ToUTF8(dbName)), -1);
+ }
+
+ ///
+ /// This method attempts to determine if a database connection opened
+ /// with the specified should be
+ /// allowed into the connection pool.
+ ///
+ ///
+ /// The that were specified when the
+ /// connection was opened.
+ ///
+ ///
+ /// Non-zero if the connection should (eventually) be allowed into the
+ /// connection pool; otherwise, zero.
+ ///
+ private static bool IsAllowedToUsePool(
+ SQLiteOpenFlagsEnum openFlags
+ )
+ {
+ return openFlags == SQLiteOpenFlagsEnum.Default;
+ }
+
+ internal override void Open(string strFilename, string vfsName, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool)
+ {
+ //
+ // NOTE: If the database connection is currently open, attempt to
+ // close it now. This must be done because the file name or
+ // other parameters that may impact the underlying database
+ // connection may have changed.
+ //
+ if (_sql != null) Close(false);
+
+ //
+ // NOTE: If the connection was not closed successfully, throw an
+ // exception now.
+ //
+ if (_sql != null)
+ throw new SQLiteException("connection handle is still active");
+
+ _usePool = usePool;
+
+ //
+ // BUGFIX: Do not allow a connection into the pool if it was opened
+ // with flags that are incompatible with the default flags
+ // (e.g. read-only).
+ //
+ if (_usePool && !IsAllowedToUsePool(openFlags))
+ _usePool = false;
+
+ _fileName = strFilename;
+ _flags = connectionFlags;
+
+ if (usePool)
+ {
+ _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.OpenedFromPool, null, null,
+ null, null, _sql, strFilename, new object[] {
+ typeof(SQLite3), strFilename, vfsName, connectionFlags,
+ openFlags, maxPoolSize, usePool, _poolVersion }));
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Open (Pool): {0}", HandleToString()));
+#endif
+ }
+
+ if (_sql == null)
+ {
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ IntPtr db = IntPtr.Zero;
+ SQLiteErrorCode n;
+
+#if !SQLITE_STANDARD
+ int extFuncs = HelperMethods.HasFlags(connectionFlags, SQLiteConnectionFlags.NoExtensionFunctions) ? 0 : 1;
+
+ if (extFuncs != 0)
+ {
+ n = UnsafeNativeMethods.sqlite3_open_interop(ToUTF8(strFilename), ToUTF8(vfsName), openFlags, extFuncs, ref db);
+ }
+ else
+#endif
+ {
+ n = UnsafeNativeMethods.sqlite3_open_v2(ToUTF8(strFilename), ref db, openFlags, ToUTF8(vfsName));
+ }
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Open: {0}", db));
+#endif
+
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
+ _sql = new SQLiteConnectionHandle(db, true);
+ }
+ lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null,
+ null, null, null, _sql, strFilename, new object[] {
+ typeof(SQLite3), strFilename, vfsName, connectionFlags,
+ openFlags, maxPoolSize, usePool }));
+ }
+
+ // Bind functions to this connection. If any previous functions of the same name
+ // were already bound, then the new bindings replace the old.
+ if (!HelperMethods.HasFlags(connectionFlags, SQLiteConnectionFlags.NoBindFunctions))
+ {
+ if (_functions == null)
+ _functions = new Dictionary();
+
+ foreach (KeyValuePair pair
+ in SQLiteFunction.BindFunctions(this, connectionFlags))
+ {
+ _functions[pair.Key] = pair.Value;
+ }
+ }
+
+ SetTimeout(0);
+ GC.KeepAlive(_sql);
+ }
+
+ internal override void ClearPool()
+ {
+ SQLiteConnectionPool.ClearPool(_fileName);
+ }
+
+ internal override int CountPool()
+ {
+ Dictionary counts = null;
+ int openCount = 0;
+ int closeCount = 0;
+ int totalCount = 0;
+
+ SQLiteConnectionPool.GetCounts(_fileName,
+ ref counts, ref openCount, ref closeCount,
+ ref totalCount);
+
+ return totalCount;
+ }
+
+ internal override void SetTimeout(int nTimeoutMS)
+ {
+ IntPtr db = _sql;
+ if (db == IntPtr.Zero) throw new SQLiteException("no connection handle available");
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_busy_timeout(db, nTimeoutMS);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override bool Step(SQLiteStatement stmt)
+ {
+ SQLiteErrorCode n;
+ Random rnd = null;
+ uint starttick = (uint)Environment.TickCount;
+ uint timeout = (uint)(stmt._command._commandTimeout * 1000);
+
+ ResetCancelCount();
+
+ while (true)
+ {
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ n = UnsafeNativeMethods.sqlite3_step(stmt._sqlite_stmt);
+ }
+
+ if (ShouldThrowForCancel())
+ {
+ if ((n == SQLiteErrorCode.Ok) ||
+ (n == SQLiteErrorCode.Row) ||
+ (n == SQLiteErrorCode.Done))
+ {
+ n = SQLiteErrorCode.Interrupt;
+ }
+
+ throw new SQLiteException(n, null);
+ }
+
+ if (n == SQLiteErrorCode.Interrupt) return false;
+ if (n == SQLiteErrorCode.Row) return true;
+ if (n == SQLiteErrorCode.Done) return false;
+
+ if (n != SQLiteErrorCode.Ok)
+ {
+ SQLiteErrorCode r;
+
+ // An error occurred, attempt to reset the statement. If the reset worked because the
+ // schema has changed, re-try the step again. If it errored our because the database
+ // is locked, then keep retrying until the command timeout occurs.
+ r = Reset(stmt);
+
+ if (r == SQLiteErrorCode.Ok)
+ throw new SQLiteException(n, GetLastError());
+
+ else if ((r == SQLiteErrorCode.Locked || r == SQLiteErrorCode.Busy) && stmt._command != null)
+ {
+ // Keep trying
+ if (rnd == null) // First time we've encountered the lock
+ rnd = new Random();
+
+ // If we've exceeded the command's timeout, give up and throw an error
+ if ((uint)Environment.TickCount - starttick > timeout)
+ {
+ throw new SQLiteException(r, GetLastError());
+ }
+ else
+ {
+ // Otherwise sleep for a random amount of time up to 150ms
+ System.Threading.Thread.Sleep(rnd.Next(1, 150));
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Has the sqlite3_errstr() core library API been checked for yet?
+ /// If so, is it present?
+ ///
+ private static bool? have_errstr = null;
+
+ ///
+ /// Returns the error message for the specified SQLite return code using
+ /// the sqlite3_errstr() function, falling back to the internal lookup
+ /// table if necessary.
+ ///
+ /// WARNING: Do not remove this method, it is used via reflection.
+ ///
+ /// The SQLite return code.
+ /// The error message or null if it cannot be found.
+ internal static string GetErrorString(SQLiteErrorCode rc)
+ {
+ try
+ {
+ if (have_errstr == null)
+ {
+ int versionNumber = SQLiteVersionNumber;
+ have_errstr = (versionNumber >= 3007015);
+ }
+
+ if ((bool)have_errstr)
+ {
+ IntPtr ptr = UnsafeNativeMethods.sqlite3_errstr(rc);
+
+ if (ptr != IntPtr.Zero)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return Marshal.PtrToStringAnsi(ptr);
+#else
+ return UTF8ToString(ptr, -1);
+#endif
+ }
+ }
+ }
+ catch (EntryPointNotFoundException)
+ {
+ // do nothing.
+ }
+
+ return FallbackGetErrorString(rc);
+ }
+
+ ///
+ /// Has the sqlite3_stmt_readonly() core library API been checked for yet?
+ /// If so, is it present?
+ ///
+ private static bool? have_stmt_readonly = null;
+
+ ///
+ /// Returns non-zero if the specified statement is read-only in nature.
+ ///
+ /// The statement to check.
+ /// True if the outer query is read-only.
+ internal override bool IsReadOnly(
+ SQLiteStatement stmt
+ )
+ {
+ try
+ {
+ if (have_stmt_readonly == null)
+ {
+ int versionNumber = SQLiteVersionNumber;
+ have_stmt_readonly = (versionNumber >= 3007004);
+ }
+
+ if ((bool)have_stmt_readonly)
+ {
+ return UnsafeNativeMethods.sqlite3_stmt_readonly(
+ stmt._sqlite_stmt) != 0;
+ }
+ }
+ catch (EntryPointNotFoundException)
+ {
+ // do nothing.
+ }
+
+ return false; /* NOTE: Unknown, assume false. */
+ }
+
+ internal override SQLiteErrorCode Reset(SQLiteStatement stmt)
+ {
+ SQLiteErrorCode n;
+
+#if !SQLITE_STANDARD
+ n = UnsafeNativeMethods.sqlite3_reset_interop(stmt._sqlite_stmt);
+#else
+ n = UnsafeNativeMethods.sqlite3_reset(stmt._sqlite_stmt);
+#endif
+
+ // If the schema changed, try and re-prepare it
+ if (n == SQLiteErrorCode.Schema)
+ {
+ // Recreate a dummy statement
+ string str = null;
+ using (SQLiteStatement tmp = Prepare(null, stmt._sqlStatement, null, (uint)(stmt._command._commandTimeout * 1000), ref str))
+ {
+ // Finalize the existing statement
+ stmt._sqlite_stmt.Dispose();
+ // Reassign a new statement pointer to the old statement and clear the temporary one
+ if (tmp != null)
+ {
+ stmt._sqlite_stmt = tmp._sqlite_stmt;
+ tmp._sqlite_stmt = null;
+ }
+
+ // Reapply parameters
+ stmt.BindParameters();
+ }
+ return SQLiteErrorCode.Unknown; // Reset was OK, with schema change
+ }
+ else if (n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy)
+ return n;
+
+ if (n != SQLiteErrorCode.Ok)
+ throw new SQLiteException(n, GetLastError());
+
+ return n; // We reset OK, no schema changes
+ }
+
+ internal override string GetLastError()
+ {
+ return GetLastError(null);
+ }
+
+ internal override string GetLastError(string defValue)
+ {
+ string result = SQLiteBase.GetLastError(_sql, _sql);
+ if (String.IsNullOrEmpty(result)) result = defValue;
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Query Diagnostics Support
+ ///
+ /// This field is used to keep track of whether or not the
+ /// "SQLite_ForceLogPrepare" environment variable has been queried. If so,
+ /// it will only be non-zero if the environment variable was present.
+ ///
+ private static bool? forceLogPrepare = null;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if all calls to prepare a SQL query will be logged,
+ /// regardless of the flags for the associated connection.
+ ///
+ ///
+ /// Non-zero to log all calls to prepare a SQL query.
+ ///
+ internal static bool ForceLogPrepare()
+ {
+ lock (syncRoot)
+ {
+ if (forceLogPrepare == null)
+ {
+ if (UnsafeNativeMethods.GetSettingValue(
+ "SQLite_ForceLogPrepare", null) != null)
+ {
+ forceLogPrepare = true;
+ }
+ else
+ {
+ forceLogPrepare = false;
+ }
+ }
+
+ return (bool)forceLogPrepare;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ internal override SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, ref string strRemain)
+ {
+ if (!String.IsNullOrEmpty(strSql)) strSql = strSql.Trim();
+ if (!String.IsNullOrEmpty(strSql))
+ {
+ //
+ // NOTE: SQLite does not support the concept of separate schemas
+ // in one database; therefore, remove the base schema name
+ // used to smooth integration with the base .NET Framework
+ // data classes.
+ //
+ string baseSchemaName = (cnn != null) ? cnn._baseSchemaName : null;
+
+ if (!String.IsNullOrEmpty(baseSchemaName))
+ {
+ strSql = strSql.Replace(
+ HelperMethods.StringFormat(CultureInfo.InvariantCulture,
+ "[{0}].", baseSchemaName), String.Empty);
+
+ strSql = strSql.Replace(
+ HelperMethods.StringFormat(CultureInfo.InvariantCulture,
+ "{0}.", baseSchemaName), String.Empty);
+ }
+ }
+
+ SQLiteConnectionFlags flags =
+ (cnn != null) ? cnn.Flags : SQLiteConnectionFlags.Default;
+
+ if (ForceLogPrepare() ||
+ HelperMethods.LogPrepare(flags))
+ {
+ if ((strSql == null) || (strSql.Length == 0) || (strSql.Trim().Length == 0))
+ SQLiteLog.LogMessage("Preparing {}...");
+ else
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Preparing {{{0}}}...", strSql));
+ }
+
+ IntPtr stmt = IntPtr.Zero;
+ IntPtr ptr = IntPtr.Zero;
+ int len = 0;
+ SQLiteErrorCode n = SQLiteErrorCode.Schema;
+ int retries = 0;
+ int maximumRetries = (cnn != null) ? cnn._prepareRetries : SQLiteConnection.DefaultPrepareRetries;
+ byte[] b = ToUTF8(strSql);
+ string typedefs = null;
+ SQLiteStatement cmd = null;
+ Random rnd = null;
+ uint starttick = (uint)Environment.TickCount;
+
+ ResetCancelCount();
+
+ GCHandle handle = GCHandle.Alloc(b, GCHandleType.Pinned);
+ IntPtr psql = handle.AddrOfPinnedObject();
+ SQLiteStatementHandle statementHandle = null;
+ try
+ {
+ while ((n == SQLiteErrorCode.Schema || n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) && retries < maximumRetries)
+ {
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ stmt = IntPtr.Zero;
+ ptr = IntPtr.Zero;
+
+#if !SQLITE_STANDARD
+ len = 0;
+ n = UnsafeNativeMethods.sqlite3_prepare_interop(_sql, psql, b.Length - 1, ref stmt, ref ptr, ref len);
+#else
+#if USE_PREPARE_V2
+ n = UnsafeNativeMethods.sqlite3_prepare_v2(_sql, psql, b.Length - 1, ref stmt, ref ptr);
+#else
+ n = UnsafeNativeMethods.sqlite3_prepare(_sql, psql, b.Length - 1, ref stmt, ref ptr);
+#endif
+ len = -1;
+#endif
+
+#if !NET_COMPACT_20 && TRACE_STATEMENT
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Prepare ({0}): {1}", n, stmt));
+#endif
+
+ if ((n == SQLiteErrorCode.Ok) && (stmt != IntPtr.Zero))
+ {
+ if (statementHandle != null) statementHandle.Dispose();
+ statementHandle = new SQLiteStatementHandle(_sql, stmt);
+ }
+ }
+
+ if (statementHandle != null)
+ {
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null, null,
+ null, null, statementHandle, strSql, new object[] {
+ typeof(SQLite3), cnn, strSql, previous, timeoutMS }));
+ }
+
+ if (ShouldThrowForCancel())
+ {
+ if ((n == SQLiteErrorCode.Ok) ||
+ (n == SQLiteErrorCode.Row) ||
+ (n == SQLiteErrorCode.Done))
+ {
+ n = SQLiteErrorCode.Interrupt;
+ }
+
+ throw new SQLiteException(n, null);
+ }
+
+ if (n == SQLiteErrorCode.Interrupt)
+ break;
+ else if (n == SQLiteErrorCode.Schema)
+ retries++;
+ else if (n == SQLiteErrorCode.Error)
+ {
+ if (String.Compare(GetLastError(), "near \"TYPES\": syntax error", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ int pos = strSql.IndexOf(';');
+ if (pos == -1) pos = strSql.Length - 1;
+
+ typedefs = strSql.Substring(0, pos + 1);
+ strSql = strSql.Substring(pos + 1);
+
+ strRemain = String.Empty;
+
+ while (cmd == null && strSql.Length > 0)
+ {
+ cmd = Prepare(cnn, strSql, previous, timeoutMS, ref strRemain);
+ strSql = strRemain;
+ }
+
+ if (cmd != null)
+ cmd.SetTypes(typedefs);
+
+ return cmd;
+ }
+#if (NET_35 || NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47 || NET_471 || NET_472) && !PLATFORM_COMPACTFRAMEWORK
+ else if (_buildingSchema == false && String.Compare(GetLastError(), 0, "no such table: TEMP.SCHEMA", 0, 26, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ strRemain = String.Empty;
+ _buildingSchema = true;
+ try
+ {
+ ISQLiteSchemaExtensions ext = ((IServiceProvider)SQLiteFactory.Instance).GetService(typeof(ISQLiteSchemaExtensions)) as ISQLiteSchemaExtensions;
+
+ if (ext != null)
+ ext.BuildTempSchema(cnn);
+
+ while (cmd == null && strSql.Length > 0)
+ {
+ cmd = Prepare(cnn, strSql, previous, timeoutMS, ref strRemain);
+ strSql = strRemain;
+ }
+
+ return cmd;
+ }
+ finally
+ {
+ _buildingSchema = false;
+ }
+ }
+#endif
+ }
+ else if (n == SQLiteErrorCode.Locked || n == SQLiteErrorCode.Busy) // Locked -- delay a small amount before retrying
+ {
+ // Keep trying
+ if (rnd == null) // First time we've encountered the lock
+ rnd = new Random();
+
+ // If we've exceeded the command's timeout, give up and throw an error
+ if ((uint)Environment.TickCount - starttick > timeoutMS)
+ {
+ throw new SQLiteException(n, GetLastError());
+ }
+ else
+ {
+ // Otherwise sleep for a random amount of time up to 150ms
+ System.Threading.Thread.Sleep(rnd.Next(1, 150));
+ }
+ }
+ }
+
+ if (ShouldThrowForCancel())
+ {
+ if ((n == SQLiteErrorCode.Ok) ||
+ (n == SQLiteErrorCode.Row) ||
+ (n == SQLiteErrorCode.Done))
+ {
+ n = SQLiteErrorCode.Interrupt;
+ }
+
+ throw new SQLiteException(n, null);
+ }
+
+ if (n == SQLiteErrorCode.Interrupt) return null;
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+
+ strRemain = UTF8ToString(ptr, len);
+
+ if (statementHandle != null) cmd = new SQLiteStatement(this, flags, statementHandle, strSql.Substring(0, strSql.Length - strRemain.Length), previous);
+
+ return cmd;
+ }
+ finally
+ {
+ handle.Free();
+ }
+ }
+
+ protected static void LogBind(SQLiteStatementHandle handle, int index)
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Binding statement {0} paramter #{1} as NULL...",
+ handleIntPtr, index));
+ }
+
+ protected static void LogBind(SQLiteStatementHandle handle, int index, ValueType value)
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Binding statement {0} paramter #{1} as type {2} with value {{{3}}}...",
+ handleIntPtr, index, value.GetType(), value));
+ }
+
+ private static string FormatDateTime(DateTime value)
+ {
+ StringBuilder result = new StringBuilder();
+
+ result.Append(value.ToString("yyyy-MM-ddTHH:mm:ss.FFFFFFFK"));
+ result.Append(' ');
+ result.Append(value.Kind);
+ result.Append(' ');
+ result.Append(value.Ticks);
+
+ return result.ToString();
+ }
+
+ protected static void LogBind(SQLiteStatementHandle handle, int index, DateTime value)
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Binding statement {0} paramter #{1} as type {2} with value {{{3}}}...",
+ handleIntPtr, index, typeof(DateTime), FormatDateTime(value)));
+ }
+
+ protected static void LogBind(SQLiteStatementHandle handle, int index, string value)
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Binding statement {0} paramter #{1} as type {2} with value {{{3}}}...",
+ handleIntPtr, index, typeof(String), (value != null) ? value : ""));
+ }
+
+ private static string ToHexadecimalString(
+ byte[] array
+ )
+ {
+ if (array == null)
+ return null;
+
+ StringBuilder result = new StringBuilder(array.Length * 2);
+
+ int length = array.Length;
+
+ for (int index = 0; index < length; index++)
+ result.Append(array[index].ToString("x2"));
+
+ return result.ToString();
+ }
+
+ protected static void LogBind(SQLiteStatementHandle handle, int index, byte[] value)
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Binding statement {0} paramter #{1} as type {2} with value {{{3}}}...",
+ handleIntPtr, index, typeof(Byte[]), (value != null) ? ToHexadecimalString(value) : ""));
+ }
+
+ internal override void Bind_Double(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, double value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_double(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+#elif !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_double_interop(handle, index, ref value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override void Bind_Int32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, int value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override void Bind_UInt32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, uint value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+ SQLiteErrorCode n;
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.BindUInt32AsInt64))
+ {
+ long value2 = value;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ n = UnsafeNativeMethods.sqlite3_bind_int64(handle, index, value2);
+#elif !SQLITE_STANDARD
+ n = UnsafeNativeMethods.sqlite3_bind_int64_interop(handle, index, ref value2);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ else
+ {
+ n = UnsafeNativeMethods.sqlite3_bind_uint(handle, index, value);
+ }
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override void Bind_Int64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, long value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int64(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+#elif !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int64_interop(handle, index, ref value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override void Bind_UInt64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, ulong value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_uint64(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+#elif !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_uint64_interop(handle, index, ref value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override void Bind_Boolean(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, bool value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+ int value2 = value ? 1 : 0;
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int(handle, index, value2);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+ byte[] b = ToUTF8(value);
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, b);
+ }
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text(handle, index, b, b.Length - 1, (IntPtr)(-1));
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, dt);
+ }
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.BindDateTimeWithKind))
+ {
+ if ((_datetimeKind != DateTimeKind.Unspecified) &&
+ (dt.Kind != DateTimeKind.Unspecified) &&
+ (dt.Kind != _datetimeKind))
+ {
+ if (_datetimeKind == DateTimeKind.Utc)
+ dt = dt.ToUniversalTime();
+ else if (_datetimeKind == DateTimeKind.Local)
+ dt = dt.ToLocalTime();
+ }
+ }
+
+ switch (_datetimeFormat)
+ {
+ case SQLiteDateFormats.Ticks:
+ {
+ long value = dt.Ticks;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int64(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+#elif !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int64_interop(handle, index, ref value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ case SQLiteDateFormats.JulianDay:
+ {
+ double value = ToJulianDay(dt);
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_double(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+#elif !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_double_interop(handle, index, ref value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ case SQLiteDateFormats.UnixEpoch:
+ {
+ long value = Convert.ToInt64(dt.Subtract(UnixEpoch).TotalSeconds);
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int64(handle, index, value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+#elif !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_int64_interop(handle, index, ref value);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ default:
+ {
+ byte[] b = ToUTF8(dt);
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, b);
+ }
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text(handle, index, b, b.Length - 1, (IntPtr)(-1));
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ break;
+ }
+ }
+ }
+
+ internal override void Bind_Blob(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, byte[] blobData)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, blobData);
+ }
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_blob(handle, index, blobData, blobData.Length, (IntPtr)(-1));
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override void Bind_Null(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index);
+ }
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_null(handle, index);
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override int Bind_ParamCount(SQLiteStatement stmt, SQLiteConnectionFlags flags)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+ int value = UnsafeNativeMethods.sqlite3_bind_parameter_count(handle);
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Statement {0} paramter count is {1}.",
+ handleIntPtr, value));
+ }
+
+ return value;
+ }
+
+ internal override string Bind_ParamName(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+ string name;
+
+#if !SQLITE_STANDARD
+ int len = 0;
+ name = UTF8ToString(UnsafeNativeMethods.sqlite3_bind_parameter_name_interop(handle, index, ref len), len);
+#else
+ name = UTF8ToString(UnsafeNativeMethods.sqlite3_bind_parameter_name(handle, index), -1);
+#endif
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Statement {0} paramter #{1} name is {{{2}}}.",
+ handleIntPtr, index, name));
+ }
+
+ return name;
+ }
+
+ internal override int Bind_ParamIndex(SQLiteStatement stmt, SQLiteConnectionFlags flags, string paramName)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+ int index = UnsafeNativeMethods.sqlite3_bind_parameter_index(handle, ToUTF8(paramName));
+
+ if (ForceLogPrepare() || HelperMethods.LogBind(flags))
+ {
+ IntPtr handleIntPtr = handle;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Statement {0} paramter index of name {{{1}}} is #{2}.",
+ handleIntPtr, paramName, index));
+ }
+
+ return index;
+ }
+
+ internal override int ColumnCount(SQLiteStatement stmt)
+ {
+ return UnsafeNativeMethods.sqlite3_column_count(stmt._sqlite_stmt);
+ }
+
+ internal override string ColumnName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ IntPtr p = UnsafeNativeMethods.sqlite3_column_name_interop(stmt._sqlite_stmt, index, ref len);
+#else
+ IntPtr p = UnsafeNativeMethods.sqlite3_column_name(stmt._sqlite_stmt, index);
+#endif
+ if (p == IntPtr.Zero)
+ throw new SQLiteException(SQLiteErrorCode.NoMem, GetLastError());
+#if !SQLITE_STANDARD
+ return UTF8ToString(p, len);
+#else
+ return UTF8ToString(p, -1);
+#endif
+ }
+
+ internal override TypeAffinity ColumnAffinity(SQLiteStatement stmt, int index)
+ {
+ return UnsafeNativeMethods.sqlite3_column_type(stmt._sqlite_stmt, index);
+ }
+
+ internal override string ColumnType(SQLiteStatement stmt, int index, ref TypeAffinity nAffinity)
+ {
+ int len;
+#if !SQLITE_STANDARD
+ len = 0;
+ IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype_interop(stmt._sqlite_stmt, index, ref len);
+#else
+ len = -1;
+ IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype(stmt._sqlite_stmt, index);
+#endif
+ nAffinity = ColumnAffinity(stmt, index);
+
+ if ((p != IntPtr.Zero) && ((len > 0) || (len == -1)))
+ {
+ string declType = UTF8ToString(p, len);
+
+ if (!String.IsNullOrEmpty(declType))
+ return declType;
+ }
+
+ string[] ar = stmt.TypeDefinitions;
+
+ if (ar != null)
+ {
+ if (index < ar.Length && ar[index] != null)
+ return ar[index];
+ }
+
+ return String.Empty;
+ }
+
+ internal override int ColumnIndex(SQLiteStatement stmt, string columnName)
+ {
+ int x = ColumnCount(stmt);
+
+ for (int n = 0; n < x; n++)
+ {
+ if (String.Compare(columnName, ColumnName(stmt, n), StringComparison.OrdinalIgnoreCase) == 0)
+ return n;
+ }
+ return -1;
+ }
+
+ internal override string ColumnOriginalName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_origin_name_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_origin_name(stmt._sqlite_stmt, index), -1);
+#endif
+ }
+
+ internal override string ColumnDatabaseName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_database_name_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_database_name(stmt._sqlite_stmt, index), -1);
+#endif
+ }
+
+ internal override string ColumnTableName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_table_name_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_table_name(stmt._sqlite_stmt, index), -1);
+#endif
+ }
+
+ internal override bool DoesTableExist(
+ string dataBase,
+ string table
+ )
+ {
+ string dataType = null; /* NOT USED */
+ string collateSequence = null; /* NOT USED */
+ bool notNull = false; /* NOT USED */
+ bool primaryKey = false; /* NOT USED */
+ bool autoIncrement = false; /* NOT USED */
+
+ return ColumnMetaData(
+ dataBase, table, null, false, ref dataType,
+ ref collateSequence, ref notNull, ref primaryKey,
+ ref autoIncrement);
+ }
+
+ internal override bool ColumnMetaData(string dataBase, string table, string column, bool canThrow, ref string dataType, ref string collateSequence, ref bool notNull, ref bool primaryKey, ref bool autoIncrement)
+ {
+ IntPtr dataTypePtr = IntPtr.Zero;
+ IntPtr collSeqPtr = IntPtr.Zero;
+ int nnotNull = 0;
+ int nprimaryKey = 0;
+ int nautoInc = 0;
+ SQLiteErrorCode n;
+ int dtLen;
+ int csLen;
+
+#if !SQLITE_STANDARD
+ dtLen = 0;
+ csLen = 0;
+ n = UnsafeNativeMethods.sqlite3_table_column_metadata_interop(_sql, ToUTF8(dataBase), ToUTF8(table), ToUTF8(column), ref dataTypePtr, ref collSeqPtr, ref nnotNull, ref nprimaryKey, ref nautoInc, ref dtLen, ref csLen);
+#else
+ dtLen = -1;
+ csLen = -1;
+
+ n = UnsafeNativeMethods.sqlite3_table_column_metadata(_sql, ToUTF8(dataBase), ToUTF8(table), ToUTF8(column), ref dataTypePtr, ref collSeqPtr, ref nnotNull, ref nprimaryKey, ref nautoInc);
+#endif
+ if (canThrow && (n != SQLiteErrorCode.Ok)) throw new SQLiteException(n, GetLastError());
+
+ dataType = UTF8ToString(dataTypePtr, dtLen);
+ collateSequence = UTF8ToString(collSeqPtr, csLen);
+
+ notNull = (nnotNull == 1);
+ primaryKey = (nprimaryKey == 1);
+ autoIncrement = (nautoInc == 1);
+
+ return (n == SQLiteErrorCode.Ok);
+ }
+
+ internal override object GetObject(SQLiteStatement stmt, int index)
+ {
+ switch (ColumnAffinity(stmt, index))
+ {
+ case TypeAffinity.Int64:
+ {
+ return GetInt64(stmt, index);
+ }
+ case TypeAffinity.Double:
+ {
+ return GetDouble(stmt, index);
+ }
+ case TypeAffinity.Text:
+ {
+ return GetText(stmt, index);
+ }
+ case TypeAffinity.Blob:
+ {
+ long size = GetBytes(stmt, index, 0, null, 0, 0);
+
+ if ((size > 0) && (size <= int.MaxValue))
+ {
+ byte[] bytes = new byte[(int)size];
+
+ GetBytes(stmt, index, 0, bytes, 0, (int)size);
+
+ return bytes;
+ }
+ break;
+ }
+ case TypeAffinity.Null:
+ {
+ return DBNull.Value;
+ }
+ }
+
+ throw new NotImplementedException();
+ }
+
+ internal override double GetDouble(SQLiteStatement stmt, int index)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_column_double(stmt._sqlite_stmt, index);
+#elif !SQLITE_STANDARD
+ double value = 0.0;
+ UnsafeNativeMethods.sqlite3_column_double_interop(stmt._sqlite_stmt, index, ref value);
+ return value;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override bool GetBoolean(SQLiteStatement stmt, int index)
+ {
+ return ToBoolean(GetObject(stmt, index), CultureInfo.InvariantCulture, false);
+ }
+
+ internal override sbyte GetSByte(SQLiteStatement stmt, int index)
+ {
+ return unchecked((sbyte)(GetInt32(stmt, index) & byte.MaxValue));
+ }
+
+ internal override byte GetByte(SQLiteStatement stmt, int index)
+ {
+ return unchecked((byte)(GetInt32(stmt, index) & byte.MaxValue));
+ }
+
+ internal override short GetInt16(SQLiteStatement stmt, int index)
+ {
+ return unchecked((short)(GetInt32(stmt, index) & ushort.MaxValue));
+ }
+
+ internal override ushort GetUInt16(SQLiteStatement stmt, int index)
+ {
+ return unchecked((ushort)(GetInt32(stmt, index) & ushort.MaxValue));
+ }
+
+ internal override int GetInt32(SQLiteStatement stmt, int index)
+ {
+ return UnsafeNativeMethods.sqlite3_column_int(stmt._sqlite_stmt, index);
+ }
+
+ internal override uint GetUInt32(SQLiteStatement stmt, int index)
+ {
+ return unchecked((uint)GetInt32(stmt, index));
+ }
+
+ internal override long GetInt64(SQLiteStatement stmt, int index)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_column_int64(stmt._sqlite_stmt, index);
+#elif !SQLITE_STANDARD
+ long value = 0;
+ UnsafeNativeMethods.sqlite3_column_int64_interop(stmt._sqlite_stmt, index, ref value);
+ return value;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override ulong GetUInt64(SQLiteStatement stmt, int index)
+ {
+ return unchecked((ulong)GetInt64(stmt, index));
+ }
+
+ internal override string GetText(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index),
+ UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index));
+#endif
+ }
+
+ internal override DateTime GetDateTime(SQLiteStatement stmt, int index)
+ {
+ if (_datetimeFormat == SQLiteDateFormats.Ticks)
+ return TicksToDateTime(GetInt64(stmt, index), _datetimeKind);
+ else if (_datetimeFormat == SQLiteDateFormats.JulianDay)
+ return ToDateTime(GetDouble(stmt, index), _datetimeKind);
+ else if (_datetimeFormat == SQLiteDateFormats.UnixEpoch)
+ return UnixEpochToDateTime(GetInt64(stmt, index), _datetimeKind);
+
+#if !SQLITE_STANDARD
+ int len = 0;
+ return ToDateTime(UnsafeNativeMethods.sqlite3_column_text_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return ToDateTime(UnsafeNativeMethods.sqlite3_column_text(stmt._sqlite_stmt, index),
+ UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index));
+#endif
+ }
+
+ internal override long GetBytes(SQLiteStatement stmt, int index, int nDataOffset, byte[] bDest, int nStart, int nLength)
+ {
+ int nlen = UnsafeNativeMethods.sqlite3_column_bytes(stmt._sqlite_stmt, index);
+
+ // If no destination buffer, return the size needed.
+ if (bDest == null) return nlen;
+
+ int nCopied = nLength;
+
+ if (nCopied + nStart > bDest.Length) nCopied = bDest.Length - nStart;
+ if (nCopied + nDataOffset > nlen) nCopied = nlen - nDataOffset;
+
+ if (nCopied > 0)
+ {
+ IntPtr ptr = UnsafeNativeMethods.sqlite3_column_blob(stmt._sqlite_stmt, index);
+
+ Marshal.Copy((IntPtr)(ptr.ToInt64() + nDataOffset), bDest, nStart, nCopied);
+ }
+ else
+ {
+ nCopied = 0;
+ }
+
+ return nCopied;
+ }
+
+ internal override char GetChar(SQLiteStatement stmt, int index)
+ {
+ return Convert.ToChar(GetUInt16(stmt, index));
+ }
+
+ internal override long GetChars(SQLiteStatement stmt, int index, int nDataOffset, char[] bDest, int nStart, int nLength)
+ {
+ int nlen;
+ int nCopied = nLength;
+
+ string str = GetText(stmt, index);
+ nlen = str.Length;
+
+ if (bDest == null) return nlen;
+
+ if (nCopied + nStart > bDest.Length) nCopied = bDest.Length - nStart;
+ if (nCopied + nDataOffset > nlen) nCopied = nlen - nDataOffset;
+
+ if (nCopied > 0)
+ str.CopyTo(nDataOffset, bDest, nStart, nCopied);
+ else nCopied = 0;
+
+ return nCopied;
+ }
+
+ internal override bool IsNull(SQLiteStatement stmt, int index)
+ {
+ return (ColumnAffinity(stmt, index) == TypeAffinity.Null);
+ }
+
+ internal override int AggregateCount(IntPtr context)
+ {
+ return UnsafeNativeMethods.sqlite3_aggregate_count(context);
+ }
+
+ internal override SQLiteErrorCode CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal, bool canThrow)
+ {
+ SQLiteErrorCode n;
+
+#if !SQLITE_STANDARD
+ n = UnsafeNativeMethods.sqlite3_create_function_interop(_sql, ToUTF8(strFunction), nArgs, 4, IntPtr.Zero, func, funcstep, funcfinal, (needCollSeq == true) ? 1 : 0);
+ if (n == SQLiteErrorCode.Ok) n = UnsafeNativeMethods.sqlite3_create_function_interop(_sql, ToUTF8(strFunction), nArgs, 1, IntPtr.Zero, func, funcstep, funcfinal, (needCollSeq == true) ? 1 : 0);
+#else
+ n = UnsafeNativeMethods.sqlite3_create_function(_sql, ToUTF8(strFunction), nArgs, 4, IntPtr.Zero, func, funcstep, funcfinal);
+ if (n == SQLiteErrorCode.Ok) n = UnsafeNativeMethods.sqlite3_create_function(_sql, ToUTF8(strFunction), nArgs, 1, IntPtr.Zero, func, funcstep, funcfinal);
+#endif
+ if (canThrow && (n != SQLiteErrorCode.Ok)) throw new SQLiteException(n, GetLastError());
+ return n;
+ }
+
+ internal override SQLiteErrorCode CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16, bool canThrow)
+ {
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_create_collation(_sql, ToUTF8(strCollation), 2, IntPtr.Zero, func16);
+ if (n == SQLiteErrorCode.Ok) n = UnsafeNativeMethods.sqlite3_create_collation(_sql, ToUTF8(strCollation), 1, IntPtr.Zero, func);
+ if (canThrow && (n != SQLiteErrorCode.Ok)) throw new SQLiteException(n, GetLastError());
+ return n;
+ }
+
+ internal override int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, string s1, string s2)
+ {
+#if !SQLITE_STANDARD
+ byte[] b1;
+ byte[] b2;
+ System.Text.Encoding converter = null;
+
+ switch (enc)
+ {
+ case CollationEncodingEnum.UTF8:
+ converter = System.Text.Encoding.UTF8;
+ break;
+ case CollationEncodingEnum.UTF16LE:
+ converter = System.Text.Encoding.Unicode;
+ break;
+ case CollationEncodingEnum.UTF16BE:
+ converter = System.Text.Encoding.BigEndianUnicode;
+ break;
+ }
+
+ b1 = converter.GetBytes(s1);
+ b2 = converter.GetBytes(s2);
+
+ return UnsafeNativeMethods.sqlite3_context_collcompare_interop(context, b1, b1.Length, b2, b2.Length);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, char[] c1, char[] c2)
+ {
+#if !SQLITE_STANDARD
+ byte[] b1;
+ byte[] b2;
+ System.Text.Encoding converter = null;
+
+ switch (enc)
+ {
+ case CollationEncodingEnum.UTF8:
+ converter = System.Text.Encoding.UTF8;
+ break;
+ case CollationEncodingEnum.UTF16LE:
+ converter = System.Text.Encoding.Unicode;
+ break;
+ case CollationEncodingEnum.UTF16BE:
+ converter = System.Text.Encoding.BigEndianUnicode;
+ break;
+ }
+
+ b1 = converter.GetBytes(c1);
+ b2 = converter.GetBytes(c2);
+
+ return UnsafeNativeMethods.sqlite3_context_collcompare_interop(context, b1, b1.Length, b2, b2.Length);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override CollationSequence GetCollationSequence(SQLiteFunction func, IntPtr context)
+ {
+#if !SQLITE_STANDARD
+ CollationSequence seq = new CollationSequence();
+ int len = 0;
+ int type = 0;
+ int enc = 0;
+ IntPtr p = UnsafeNativeMethods.sqlite3_context_collseq_interop(context, ref type, ref enc, ref len);
+
+ if (p != null) seq.Name = UTF8ToString(p, len);
+ seq.Type = (CollationTypeEnum)type;
+ seq._func = func;
+ seq.Encoding = (CollationEncodingEnum)enc;
+
+ return seq;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override long GetParamValueBytes(IntPtr p, int nDataOffset, byte[] bDest, int nStart, int nLength)
+ {
+ int nlen = UnsafeNativeMethods.sqlite3_value_bytes(p);
+
+ // If no destination buffer, return the size needed.
+ if (bDest == null) return nlen;
+
+ int nCopied = nLength;
+
+ if (nCopied + nStart > bDest.Length) nCopied = bDest.Length - nStart;
+ if (nCopied + nDataOffset > nlen) nCopied = nlen - nDataOffset;
+
+ if (nCopied > 0)
+ {
+ IntPtr ptr = UnsafeNativeMethods.sqlite3_value_blob(p);
+
+ Marshal.Copy((IntPtr)(ptr.ToInt64() + nDataOffset), bDest, nStart, nCopied);
+ }
+ else
+ {
+ nCopied = 0;
+ }
+
+ return nCopied;
+ }
+
+ internal override double GetParamValueDouble(IntPtr ptr)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_value_double(ptr);
+#elif !SQLITE_STANDARD
+ double value = 0.0;
+ UnsafeNativeMethods.sqlite3_value_double_interop(ptr, ref value);
+ return value;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override int GetParamValueInt32(IntPtr ptr)
+ {
+ return UnsafeNativeMethods.sqlite3_value_int(ptr);
+ }
+
+ internal override long GetParamValueInt64(IntPtr ptr)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_value_int64(ptr);
+#elif !SQLITE_STANDARD
+ Int64 value = 0;
+ UnsafeNativeMethods.sqlite3_value_int64_interop(ptr, ref value);
+ return value;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override string GetParamValueText(IntPtr ptr)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text_interop(ptr, ref len), len);
+#else
+ return UTF8ToString(UnsafeNativeMethods.sqlite3_value_text(ptr),
+ UnsafeNativeMethods.sqlite3_value_bytes(ptr));
+#endif
+ }
+
+ internal override TypeAffinity GetParamValueType(IntPtr ptr)
+ {
+ return UnsafeNativeMethods.sqlite3_value_type(ptr);
+ }
+
+ internal override void ReturnBlob(IntPtr context, byte[] value)
+ {
+ UnsafeNativeMethods.sqlite3_result_blob(context, value, value.Length, (IntPtr)(-1));
+ }
+
+ internal override void ReturnDouble(IntPtr context, double value)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ UnsafeNativeMethods.sqlite3_result_double(context, value);
+#elif !SQLITE_STANDARD
+ UnsafeNativeMethods.sqlite3_result_double_interop(context, ref value);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override void ReturnError(IntPtr context, string value)
+ {
+ UnsafeNativeMethods.sqlite3_result_error(context, ToUTF8(value), value.Length);
+ }
+
+ internal override void ReturnInt32(IntPtr context, int value)
+ {
+ UnsafeNativeMethods.sqlite3_result_int(context, value);
+ }
+
+ internal override void ReturnInt64(IntPtr context, long value)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ UnsafeNativeMethods.sqlite3_result_int64(context, value);
+#elif !SQLITE_STANDARD
+ UnsafeNativeMethods.sqlite3_result_int64_interop(context, ref value);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ internal override void ReturnNull(IntPtr context)
+ {
+ UnsafeNativeMethods.sqlite3_result_null(context);
+ }
+
+ internal override void ReturnText(IntPtr context, string value)
+ {
+ byte[] b = ToUTF8(value);
+ UnsafeNativeMethods.sqlite3_result_text(context, ToUTF8(value), b.Length - 1, (IntPtr)(-1));
+ }
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Determines the file name of the native library containing the native
+ /// "vtshim" extension -AND- whether it should be dynamically loaded by
+ /// this class.
+ ///
+ ///
+ /// This output parameter will be set to non-zero if the returned native
+ /// library file name should be dynamically loaded prior to attempting
+ /// the creation of native disposable extension modules.
+ ///
+ ///
+ /// The file name of the native library containing the native "vtshim"
+ /// extension -OR- null if it cannot be determined.
+ ///
+ private string GetShimExtensionFileName(
+ ref bool isLoadNeeded /* out */
+ )
+ {
+ if (_shimIsLoadNeeded != null)
+ isLoadNeeded = (bool)_shimIsLoadNeeded;
+ else
+#if SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK
+ isLoadNeeded = HelperMethods.IsWindows(); /* COMPAT */
+#else
+ isLoadNeeded = false; /* mixed-mode assembly */
+#endif
+
+ string fileName = _shimExtensionFileName;
+
+ if (fileName != null)
+ return fileName;
+
+#if (SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK) && PRELOAD_NATIVE_LIBRARY
+ return UnsafeNativeMethods.GetNativeLibraryFileNameOnly(); /* COMPAT */
+#else
+ return null;
+#endif
+ }
+
+ ///
+ /// Calls the native SQLite core library in order to create a disposable
+ /// module containing the implementation of a virtual table.
+ ///
+ ///
+ /// The module object to be used when creating the native disposable module.
+ ///
+ ///
+ /// The flags for the associated object instance.
+ ///
+ internal override void CreateModule(SQLiteModule module, SQLiteConnectionFlags flags)
+ {
+ if (module == null)
+ throw new ArgumentNullException("module");
+
+ if (HelperMethods.NoLogModule(flags))
+ {
+ module.LogErrors = HelperMethods.LogModuleError(flags);
+ module.LogExceptions = HelperMethods.LogModuleException(flags);
+ }
+
+ if (_sql == null)
+ throw new SQLiteException("connection has an invalid handle");
+
+ bool isLoadNeeded = false;
+ string fileName = GetShimExtensionFileName(ref isLoadNeeded);
+
+ if (isLoadNeeded)
+ {
+ if (fileName == null)
+ throw new SQLiteException("the file name for the \"vtshim\" extension is unknown");
+
+ if (_shimExtensionProcName == null)
+ throw new SQLiteException("the entry point for the \"vtshim\" extension is unknown");
+
+ SetLoadExtension(true);
+ LoadExtension(fileName, _shimExtensionProcName);
+ }
+
+ if (module.CreateDisposableModule(_sql))
+ {
+ if (_modules == null)
+ _modules = new Dictionary();
+
+ _modules.Add(module.Name, module);
+
+ if (_usePool)
+ {
+ _usePool = false;
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "CreateModule (Pool) Disabled: {0}",
+ HandleToString()));
+#endif
+ }
+ }
+ else
+ {
+ throw new SQLiteException(GetLastError());
+ }
+ }
+
+ ///
+ /// Calls the native SQLite core library in order to cleanup the resources
+ /// associated with a module containing the implementation of a virtual table.
+ ///
+ ///
+ /// The module object previously passed to the
+ /// method.
+ ///
+ ///
+ /// The flags for the associated object instance.
+ ///
+ internal override void DisposeModule(SQLiteModule module, SQLiteConnectionFlags flags)
+ {
+ if (module == null)
+ throw new ArgumentNullException("module");
+
+ module.Dispose();
+ }
+#endif
+
+ internal override IntPtr AggregateContext(IntPtr context)
+ {
+ return UnsafeNativeMethods.sqlite3_aggregate_context(context, 1);
+ }
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Calls the native SQLite core library in order to declare a virtual table
+ /// in response to a call into the
+ /// or virtual table methods.
+ ///
+ ///
+ /// The virtual table module that is to be responsible for the virtual table
+ /// being declared.
+ ///
+ ///
+ /// The string containing the SQL statement describing the virtual table to
+ /// be declared.
+ ///
+ ///
+ /// Upon success, the contents of this parameter are undefined. Upon failure,
+ /// it should contain an appropriate error message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal override SQLiteErrorCode DeclareVirtualTable(
+ SQLiteModule module,
+ string strSql,
+ ref string error
+ )
+ {
+ if (_sql == null)
+ {
+ error = "connection has an invalid handle";
+ return SQLiteErrorCode.Error;
+ }
+
+ IntPtr pSql = IntPtr.Zero;
+
+ try
+ {
+ pSql = SQLiteString.Utf8IntPtrFromString(strSql);
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_declare_vtab(
+ _sql, pSql);
+
+ if ((n == SQLiteErrorCode.Ok) && (module != null))
+ module.Declared = true;
+
+ if (n != SQLiteErrorCode.Ok) error = GetLastError();
+
+ return n;
+ }
+ finally
+ {
+ if (pSql != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pSql);
+ pSql = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///
+ /// Calls the native SQLite core library in order to declare a virtual table
+ /// function in response to a call into the
+ /// or virtual table methods.
+ ///
+ ///
+ /// The virtual table module that is to be responsible for the virtual table
+ /// function being declared.
+ ///
+ ///
+ /// The number of arguments to the function being declared.
+ ///
+ ///
+ /// The name of the function being declared.
+ ///
+ ///
+ /// Upon success, the contents of this parameter are undefined. Upon failure,
+ /// it should contain an appropriate error message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal override SQLiteErrorCode DeclareVirtualFunction(
+ SQLiteModule module,
+ int argumentCount,
+ string name,
+ ref string error
+ )
+ {
+ if (_sql == null)
+ {
+ error = "connection has an invalid handle";
+ return SQLiteErrorCode.Error;
+ }
+
+ IntPtr pName = IntPtr.Zero;
+
+ try
+ {
+ pName = SQLiteString.Utf8IntPtrFromString(name);
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_overload_function(
+ _sql, pName, argumentCount);
+
+ if (n != SQLiteErrorCode.Ok) error = GetLastError();
+
+ return n;
+ }
+ finally
+ {
+ if (pName != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pName);
+ pName = IntPtr.Zero;
+ }
+ }
+ }
+#endif
+
+ ///
+ /// Builds an error message string fragment containing the
+ /// defined values of the
+ /// enumeration.
+ ///
+ ///
+ /// The built string fragment.
+ ///
+ private static string GetStatusDbOpsNames()
+ {
+ StringBuilder builder = new StringBuilder();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ foreach (string name in Enum.GetNames(
+ typeof(SQLiteStatusOpsEnum)))
+ {
+ if (String.IsNullOrEmpty(name))
+ continue;
+
+ if (builder.Length > 0)
+ builder.Append(", ");
+
+ builder.Append(name);
+ }
+#else
+ //
+ // TODO: Update this list if the available values in the
+ // "SQLiteConfigDbOpsEnum" enumeration change.
+ //
+ builder.AppendFormat(CultureInfo.InvariantCulture,
+ "{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}, {11}",
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_LOOKASIDE_USED,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_CACHE_USED,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_SCHEMA_USED,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_STMT_USED,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_LOOKASIDE_HIT,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_CACHE_HIT,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_CACHE_MISS,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_CACHE_WRITE,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_DEFERRED_FKS,
+ SQLiteStatusOpsEnum.SQLITE_DBSTATUS_CACHE_USED_SHARED);
+#endif
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Builds an error message string fragment containing the
+ /// defined values of the
+ /// enumeration.
+ ///
+ ///
+ /// The built string fragment.
+ ///
+ private static string GetConfigDbOpsNames()
+ {
+ StringBuilder builder = new StringBuilder();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ foreach (string name in Enum.GetNames(
+ typeof(SQLiteConfigDbOpsEnum)))
+ {
+ if (String.IsNullOrEmpty(name))
+ continue;
+
+ if (builder.Length > 0)
+ builder.Append(", ");
+
+ builder.Append(name);
+ }
+#else
+ //
+ // TODO: Update this list if the available values in the
+ // "SQLiteConfigDbOpsEnum" enumeration change.
+ //
+ builder.AppendFormat(CultureInfo.InvariantCulture,
+ "{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}",
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NONE,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_MAINDBNAME,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_LOOKASIDE,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FKEY,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_TRIGGER,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_QPSG,
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_TRIGGER_EQP);
+#endif
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Returns the current and/or highwater values for the specified
+ /// database status parameter.
+ ///
+ ///
+ /// The database status parameter to query.
+ ///
+ ///
+ /// Non-zero to reset the highwater value to the current value.
+ ///
+ ///
+ /// If applicable, receives the current value.
+ ///
+ ///
+ /// If applicable, receives the highwater value.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal override SQLiteErrorCode GetStatusParameter(
+ SQLiteStatusOpsEnum option,
+ bool reset,
+ ref int current,
+ ref int highwater
+ )
+ {
+ if (!Enum.IsDefined(typeof(SQLiteStatusOpsEnum), option))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "unrecognized status option, must be: {0}",
+ GetStatusDbOpsNames()));
+ }
+
+ return UnsafeNativeMethods.sqlite3_db_status(
+ _sql, option, ref current, ref highwater, reset ? 1 : 0);
+ }
+
+ ///
+ /// Change a configuration option value for the database.
+ /// connection.
+ ///
+ ///
+ /// The database configuration option to change.
+ ///
+ ///
+ /// The new value for the specified configuration option.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal override SQLiteErrorCode SetConfigurationOption(
+ SQLiteConfigDbOpsEnum option,
+ object value
+ )
+ {
+ if (!Enum.IsDefined(typeof(SQLiteConfigDbOpsEnum), option))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "unrecognized configuration option, must be: {0}",
+ GetConfigDbOpsNames()));
+ }
+
+ switch (option)
+ {
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NONE: // nil
+ {
+ //
+ // NOTE: Do nothing, return success.
+ //
+ return SQLiteErrorCode.Ok;
+ }
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_MAINDBNAME: // char*
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (!(value is string))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "configuration value type mismatch, must be of type {0}",
+ typeof(string)));
+ }
+
+ SQLiteErrorCode rc = SQLiteErrorCode.Error;
+ IntPtr pDbName = IntPtr.Zero;
+
+ try
+ {
+ pDbName = SQLiteString.Utf8IntPtrFromString(
+ (string)value);
+
+ if (pDbName == IntPtr.Zero)
+ {
+ throw new SQLiteException(
+ SQLiteErrorCode.NoMem,
+ "cannot allocate database name");
+ }
+
+ rc = UnsafeNativeMethods.sqlite3_db_config_charptr(
+ _sql, option, pDbName);
+
+ if (rc == SQLiteErrorCode.Ok)
+ {
+ FreeDbName(true);
+
+ dbName = pDbName;
+ pDbName = IntPtr.Zero;
+ }
+ }
+ finally
+ {
+ if ((rc != SQLiteErrorCode.Ok) &&
+ (pDbName != IntPtr.Zero))
+ {
+ SQLiteMemory.Free(pDbName);
+ pDbName = IntPtr.Zero;
+ }
+ }
+
+ return rc;
+ }
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_LOOKASIDE: // void* int int
+ {
+ object[] array = value as object[];
+
+ if (array == null)
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "configuration value type mismatch, must be of type {0}",
+ typeof(object[])));
+ }
+
+ if (!(array[0] is IntPtr))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "configuration element zero (0) type mismatch, must be of type {0}",
+ typeof(IntPtr)));
+ }
+
+ if (!(array[1] is int))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "configuration element one (1) type mismatch, must be of type {0}",
+ typeof(int)));
+ }
+
+ if (!(array[2] is int))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "configuration element two (2) type mismatch, must be of type {0}",
+ typeof(int)));
+ }
+
+ return UnsafeNativeMethods.sqlite3_db_config_intptr_two_ints(
+ _sql, option, (IntPtr)array[0], (int)array[1], (int)array[2]);
+ }
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FKEY: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_TRIGGER: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_QPSG: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_TRIGGER_EQP: // int int*
+ case SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_RESET_DATABASE: // int int*
+ {
+ if (!(value is bool))
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "configuration value type mismatch, must be of type {0}",
+ typeof(bool)));
+ }
+
+ int result = 0; /* NOT USED */
+
+ return UnsafeNativeMethods.sqlite3_db_config_int_refint(
+ _sql, option, ((bool)value ? 1 : 0), ref result);
+ }
+ default:
+ {
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "unsupported configuration option {0}", option));
+ }
+ }
+ }
+
+ ///
+ /// Enables or disables extension loading by SQLite.
+ ///
+ ///
+ /// True to enable loading of extensions, false to disable.
+ ///
+ internal override void SetLoadExtension(bool bOnOff)
+ {
+ SQLiteErrorCode n;
+
+ if (SQLiteVersionNumber >= 3013000)
+ {
+ n = SetConfigurationOption(
+ SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
+ bOnOff);
+ }
+ else
+ {
+ n = UnsafeNativeMethods.sqlite3_enable_load_extension(
+ _sql, (bOnOff ? -1 : 0));
+ }
+
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ ///
+ /// Loads a SQLite extension library from the named file.
+ ///
+ ///
+ /// The name of the dynamic link library file containing the extension.
+ ///
+ ///
+ /// The name of the exported function used to initialize the extension.
+ /// If null, the default "sqlite3_extension_init" will be used.
+ ///
+ internal override void LoadExtension(string fileName, string procName)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException("fileName");
+
+ IntPtr pError = IntPtr.Zero;
+
+ try
+ {
+ byte[] utf8FileName = UTF8Encoding.UTF8.GetBytes(fileName + '\0');
+ byte[] utf8ProcName = null;
+
+ if (procName != null)
+ utf8ProcName = UTF8Encoding.UTF8.GetBytes(procName + '\0');
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_load_extension(
+ _sql, utf8FileName, utf8ProcName, ref pError);
+
+ if (n != SQLiteErrorCode.Ok)
+ throw new SQLiteException(n, UTF8ToString(pError, -1));
+ }
+ finally
+ {
+ if (pError != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3_free(pError);
+ pError = IntPtr.Zero;
+ }
+ }
+ }
+
+ /// Enables or disabled extended result codes returned by SQLite
+ internal override void SetExtendedResultCodes(bool bOnOff)
+ {
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_extended_result_codes(
+ _sql, (bOnOff ? -1 : 0));
+
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+ /// Gets the last SQLite error code
+ internal override SQLiteErrorCode ResultCode()
+ {
+ return UnsafeNativeMethods.sqlite3_errcode(_sql);
+ }
+ /// Gets the last SQLite extended error code
+ internal override SQLiteErrorCode ExtendedResultCode()
+ {
+ return UnsafeNativeMethods.sqlite3_extended_errcode(_sql);
+ }
+
+ /// Add a log message via the SQLite sqlite3_log interface.
+ internal override void LogMessage(SQLiteErrorCode iErrCode, string zMessage)
+ {
+ StaticLogMessage(iErrCode, zMessage);
+ }
+
+ /// Add a log message via the SQLite sqlite3_log interface.
+ internal static void StaticLogMessage(SQLiteErrorCode iErrCode, string zMessage)
+ {
+ UnsafeNativeMethods.sqlite3_log(iErrCode, ToUTF8(zMessage));
+ }
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ private static void ZeroPassword(byte[] passwordBytes)
+ {
+ if (passwordBytes == null) return;
+
+ for (int index = 0; index < passwordBytes.Length; index++)
+ {
+ byte value = (byte)((index + 1) % byte.MaxValue);
+
+ passwordBytes[index] = value;
+ passwordBytes[index] ^= value;
+ }
+ }
+
+ internal override void SetPassword(byte[] passwordBytes)
+ {
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_key(_sql, passwordBytes, passwordBytes.Length);
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword))
+ ZeroPassword(passwordBytes);
+
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+
+ if (_usePool)
+ {
+ _usePool = false;
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "SetPassword (Pool) Disabled: {0}",
+ HandleToString()));
+#endif
+ }
+ }
+
+ internal override void ChangePassword(byte[] newPasswordBytes)
+ {
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_rekey(_sql, newPasswordBytes, (newPasswordBytes == null) ? 0 : newPasswordBytes.Length);
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword))
+ ZeroPassword(newPasswordBytes);
+
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+
+ if (_usePool)
+ {
+ _usePool = false;
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "ChangePassword (Pool) Disabled: {0}",
+ HandleToString()));
+#endif
+ }
+ }
+#endif
+
+ internal override void SetProgressHook(int nOps, SQLiteProgressCallback func)
+ {
+ UnsafeNativeMethods.sqlite3_progress_handler(_sql, nOps, func, IntPtr.Zero);
+ }
+
+ internal override void SetAuthorizerHook(SQLiteAuthorizerCallback func)
+ {
+ UnsafeNativeMethods.sqlite3_set_authorizer(_sql, func, IntPtr.Zero);
+ }
+
+ internal override void SetUpdateHook(SQLiteUpdateCallback func)
+ {
+ UnsafeNativeMethods.sqlite3_update_hook(_sql, func, IntPtr.Zero);
+ }
+
+ internal override void SetCommitHook(SQLiteCommitCallback func)
+ {
+ UnsafeNativeMethods.sqlite3_commit_hook(_sql, func, IntPtr.Zero);
+ }
+
+ internal override void SetTraceCallback(SQLiteTraceCallback func)
+ {
+ UnsafeNativeMethods.sqlite3_trace(_sql, func, IntPtr.Zero);
+ }
+
+ internal override void SetTraceCallback2(SQLiteTraceFlags mask, SQLiteTraceCallback2 func)
+ {
+ UnsafeNativeMethods.sqlite3_trace_v2(_sql, mask, func, IntPtr.Zero);
+ }
+
+ internal override void SetRollbackHook(SQLiteRollbackCallback func)
+ {
+ UnsafeNativeMethods.sqlite3_rollback_hook(_sql, func, IntPtr.Zero);
+ }
+
+ ///
+ /// Allows the setting of a logging callback invoked by SQLite when a
+ /// log event occurs. Only one callback may be set. If NULL is passed,
+ /// the logging callback is unregistered.
+ ///
+ /// The callback function to invoke.
+ /// Returns a result code
+ internal override SQLiteErrorCode SetLogCallback(SQLiteLogCallback func)
+ {
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_config_log(
+ SQLiteConfigOpsEnum.SQLITE_CONFIG_LOG, func, IntPtr.Zero);
+
+ if (rc == SQLiteErrorCode.Ok)
+ _setLogCallback = (func != null);
+
+ return rc;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Appends an error message and an appropriate line-ending to a
+ /// instance. This is useful because the .NET Compact Framework has a slightly different set
+ /// of supported methods for the class.
+ ///
+ ///
+ /// The instance to append to.
+ ///
+ ///
+ /// The message to append. It will be followed by an appropriate line-ending.
+ ///
+ private static void AppendError(
+ StringBuilder builder,
+ string message
+ )
+ {
+ if (builder == null)
+ return;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ builder.AppendLine(message);
+#else
+ builder.Append(message);
+ builder.Append("\r\n");
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method attempts to cause the SQLite native library to invalidate
+ /// its function pointers that refer to this instance. This is necessary
+ /// to prevent calls from native code into delegates that may have been
+ /// garbage collected. Normally, these types of issues can only arise for
+ /// connections that are added to the pool; howver, it is good practice to
+ /// unconditionally invalidate function pointers that may refer to objects
+ /// being disposed.
+ ///
+ ///
+ /// Non-zero to also invalidate global function pointers (i.e. those that
+ /// are not directly associated with this connection on the native side).
+ ///
+ ///
+ /// Non-zero if this method is being executed within a context where it can
+ /// throw an exception in the event of failure; otherwise, zero.
+ ///
+ ///
+ /// Non-zero if this method was successful; otherwise, zero.
+ ///
+ private bool UnhookNativeCallbacks(
+ bool includeGlobal,
+ bool canThrow
+ )
+ {
+ //
+ // NOTE: Initially, this method assumes success. Then, if any attempt
+ // to invalidate a function pointer fails, the overall result is
+ // set to failure. However, this will not prevent further
+ // attempts, if any, to invalidate subsequent function pointers.
+ //
+ bool result = true;
+ SQLiteErrorCode rc = SQLiteErrorCode.Ok;
+ StringBuilder builder = new StringBuilder();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Rollback Hook (Per-Connection)
+ try
+ {
+ SetRollbackHook(null); /* throw */
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset rollback hook: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset rollback hook");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Trace Callback (Per-Connection)
+ try
+ {
+ //
+ // NOTE: When using version 3.14 (or later) of the SQLite core
+ // library, use the newer sqlite3_trace_v2() API in order
+ // to unhook the trace callback, just in case the older
+ // API is not available (e.g. SQLITE_OMIT_DEPRECATED).
+ //
+ if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3014000)
+ SetTraceCallback2(SQLiteTraceFlags.SQLITE_TRACE_NONE, null); /* throw */
+ else
+ SetTraceCallback(null); /* throw */
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset trace callback: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset trace callback");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Commit Hook (Per-Connection)
+ try
+ {
+ SetCommitHook(null); /* throw */
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset commit hook: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset commit hook");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Update Hook (Per-Connection)
+ try
+ {
+ SetUpdateHook(null); /* throw */
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset update hook: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset update hook");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Authorizer Hook (Per-Connection)
+ try
+ {
+ SetAuthorizerHook(null); /* throw */
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset authorizer hook: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset authorizer hook");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Progress Hook (Per-Connection)
+ try
+ {
+ SetProgressHook(0, null); /* throw */
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset progress hook: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset progress hook");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Log Callback (Global)
+ //
+ // NOTE: We have to be careful here because the log callback
+ // is not per-connection on the native side. It should
+ // only be unset by this method if this instance was
+ // responsible for setting it.
+ //
+ if (includeGlobal && _setLogCallback)
+ {
+ try
+ {
+ SQLiteErrorCode rc2 = SetLogCallback(null); /* throw */
+
+ if (rc2 != SQLiteErrorCode.Ok)
+ {
+ AppendError(builder, "could not unset log callback");
+ rc = rc2;
+
+ result = false;
+ }
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to unset log callback: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ AppendError(builder, "failed to unset log callback");
+ rc = SQLiteErrorCode.Error;
+
+ result = false;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ if (!result && canThrow)
+ throw new SQLiteException(rc, builder.ToString());
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method attempts to free the cached database name used with the
+ /// method.
+ ///
+ ///
+ /// Non-zero if this method is being executed within a context where it can
+ /// throw an exception in the event of failure; otherwise, zero.
+ ///
+ ///
+ /// Non-zero if this method was successful; otherwise, zero.
+ ///
+ private bool FreeDbName(
+ bool canThrow
+ )
+ {
+ try
+ {
+ if (dbName != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(dbName);
+ dbName = IntPtr.Zero;
+ }
+
+ return true;
+ }
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to free database name: {0}",
+ e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ if (canThrow)
+ throw;
+ }
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates a new SQLite backup object based on the provided destination
+ /// database connection. The source database connection is the one
+ /// associated with this object. The source and destination database
+ /// connections cannot be the same.
+ ///
+ /// The destination database connection.
+ /// The destination database name.
+ /// The source database name.
+ /// The newly created backup object.
+ internal override SQLiteBackup InitializeBackup(
+ SQLiteConnection destCnn,
+ string destName,
+ string sourceName
+ )
+ {
+ if (destCnn == null)
+ throw new ArgumentNullException("destCnn");
+
+ if (destName == null)
+ throw new ArgumentNullException("destName");
+
+ if (sourceName == null)
+ throw new ArgumentNullException("sourceName");
+
+ SQLite3 destSqlite3 = destCnn._sql as SQLite3;
+
+ if (destSqlite3 == null)
+ throw new ArgumentException(
+ "Destination connection has no wrapper.",
+ "destCnn");
+
+ SQLiteConnectionHandle destHandle = destSqlite3._sql;
+
+ if (destHandle == null)
+ throw new ArgumentException(
+ "Destination connection has an invalid handle.",
+ "destCnn");
+
+ SQLiteConnectionHandle sourceHandle = _sql;
+
+ if (sourceHandle == null)
+ throw new InvalidOperationException(
+ "Source connection has an invalid handle.");
+
+ byte[] zDestName = ToUTF8(destName);
+ byte[] zSourceName = ToUTF8(sourceName);
+
+ SQLiteBackupHandle backupHandle = null;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ IntPtr backup = UnsafeNativeMethods.sqlite3_backup_init(
+ destHandle, zDestName, sourceHandle, zSourceName);
+
+ if (backup == IntPtr.Zero)
+ {
+ SQLiteErrorCode resultCode = ResultCode();
+
+ if (resultCode != SQLiteErrorCode.Ok)
+ throw new SQLiteException(resultCode, GetLastError());
+ else
+ throw new SQLiteException("failed to initialize backup");
+ }
+
+ backupHandle = new SQLiteBackupHandle(destHandle, backup);
+ }
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null,
+ null, null, null, backupHandle, null, new object[] {
+ typeof(SQLite3), destCnn, destName, sourceName }));
+
+ return new SQLiteBackup(
+ this, backupHandle, destHandle, zDestName, sourceHandle,
+ zSourceName);
+ }
+
+ ///
+ /// Copies up to N pages from the source database to the destination
+ /// database associated with the specified backup object.
+ ///
+ /// The backup object to use.
+ ///
+ /// The number of pages to copy, negative to copy all remaining pages.
+ ///
+ ///
+ /// Set to true if the operation needs to be retried due to database
+ /// locking issues; otherwise, set to false.
+ ///
+ ///
+ /// True if there are more pages to be copied, false otherwise.
+ ///
+ internal override bool StepBackup(
+ SQLiteBackup backup,
+ int nPage,
+ ref bool retry
+ )
+ {
+ retry = false;
+
+ if (backup == null)
+ throw new ArgumentNullException("backup");
+
+ SQLiteBackupHandle handle = backup._sqlite_backup;
+
+ if (handle == null)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle.");
+
+ IntPtr handlePtr = handle;
+
+ if (handlePtr == IntPtr.Zero)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle pointer.");
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_backup_step(handlePtr, nPage);
+ backup._stepResult = n; /* NOTE: Save for use by FinishBackup. */
+
+ if (n == SQLiteErrorCode.Ok)
+ {
+ return true;
+ }
+ else if (n == SQLiteErrorCode.Busy)
+ {
+ retry = true;
+ return true;
+ }
+ else if (n == SQLiteErrorCode.Locked)
+ {
+ retry = true;
+ return true;
+ }
+ else if (n == SQLiteErrorCode.Done)
+ {
+ return false;
+ }
+ else
+ {
+ throw new SQLiteException(n, GetLastError());
+ }
+ }
+
+ ///
+ /// Returns the number of pages remaining to be copied from the source
+ /// database to the destination database associated with the specified
+ /// backup object.
+ ///
+ /// The backup object to check.
+ /// The number of pages remaining to be copied.
+ internal override int RemainingBackup(
+ SQLiteBackup backup
+ )
+ {
+ if (backup == null)
+ throw new ArgumentNullException("backup");
+
+ SQLiteBackupHandle handle = backup._sqlite_backup;
+
+ if (handle == null)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle.");
+
+ IntPtr handlePtr = handle;
+
+ if (handlePtr == IntPtr.Zero)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle pointer.");
+
+ return UnsafeNativeMethods.sqlite3_backup_remaining(handlePtr);
+ }
+
+ ///
+ /// Returns the total number of pages in the source database associated
+ /// with the specified backup object.
+ ///
+ /// The backup object to check.
+ /// The total number of pages in the source database.
+ internal override int PageCountBackup(
+ SQLiteBackup backup
+ )
+ {
+ if (backup == null)
+ throw new ArgumentNullException("backup");
+
+ SQLiteBackupHandle handle = backup._sqlite_backup;
+
+ if (handle == null)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle.");
+
+ IntPtr handlePtr = handle;
+
+ if (handlePtr == IntPtr.Zero)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle pointer.");
+
+ return UnsafeNativeMethods.sqlite3_backup_pagecount(handlePtr);
+ }
+
+ ///
+ /// Destroys the backup object, rolling back any backup that may be in
+ /// progess.
+ ///
+ /// The backup object to destroy.
+ internal override void FinishBackup(
+ SQLiteBackup backup
+ )
+ {
+ if (backup == null)
+ throw new ArgumentNullException("backup");
+
+ SQLiteBackupHandle handle = backup._sqlite_backup;
+
+ if (handle == null)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle.");
+
+ IntPtr handlePtr = handle;
+
+ if (handlePtr == IntPtr.Zero)
+ throw new InvalidOperationException(
+ "Backup object has an invalid handle pointer.");
+
+#if !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_backup_finish_interop(handlePtr);
+#else
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_backup_finish(handlePtr);
+#endif
+ handle.SetHandleAsInvalid();
+
+#if COUNT_HANDLE
+ if ((n == SQLiteErrorCode.Ok) || (n == backup._stepResult)) handle.WasReleasedOk();
+#endif
+
+ if ((n != SQLiteErrorCode.Ok) && (n != backup._stepResult))
+ throw new SQLiteException(n, GetLastError());
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if the SQLite core library has been initialized for the
+ /// current process.
+ ///
+ ///
+ /// A boolean indicating whether or not the SQLite core library has been
+ /// initialized for the current process.
+ ///
+ internal override bool IsInitialized()
+ {
+ return StaticIsInitialized();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if the SQLite core library has been initialized for the
+ /// current process.
+ ///
+ ///
+ /// A boolean indicating whether or not the SQLite core library has been
+ /// initialized for the current process.
+ ///
+ internal static bool StaticIsInitialized()
+ {
+ //
+ // BUGFIX: Prevent races with other threads for this entire block, due
+ // to the try/finally semantics. See ticket [72905c9a77].
+ //
+ lock (syncRoot)
+ {
+ //
+ // NOTE: Save the state of the logging class and then restore it
+ // after we are done to avoid logging too many false errors.
+ //
+ bool savedEnabled = SQLiteLog.Enabled;
+ SQLiteLog.Enabled = false;
+
+ try
+ {
+ //
+ // NOTE: This method [ab]uses the fact that SQLite will always
+ // return SQLITE_ERROR for any unknown configuration option
+ // *unless* the SQLite library has already been initialized.
+ // In that case it will always return SQLITE_MISUSE.
+ //
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_config_none(
+ SQLiteConfigOpsEnum.SQLITE_CONFIG_NONE);
+
+ return (rc == SQLiteErrorCode.Misuse);
+ }
+ finally
+ {
+ SQLiteLog.Enabled = savedEnabled;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+#if USE_INTEROP_DLL && INTEROP_LOG
+ internal static SQLiteErrorCode ConfigureLogForInterop(
+ string className
+ )
+ {
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_config_log_interop();
+
+ if (rc == SQLiteErrorCode.Ok)
+ {
+ UnsafeNativeMethods.sqlite3_log(rc, SQLiteConvert.ToUTF8(
+ HelperMethods.StringFormat(CultureInfo.InvariantCulture,
+ "logging initialized via \"{0}\".", className)));
+ }
+ else if (rc == SQLiteErrorCode.Done)
+ {
+ rc = SQLiteErrorCode.Ok;
+ }
+
+ return rc;
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Helper function to retrieve a column of data from an active statement.
+ ///
+ /// The statement being step()'d through
+ /// The flags associated with the connection.
+ /// The column index to retrieve
+ /// The type of data contained in the column. If Uninitialized, this function will retrieve the datatype information.
+ /// Returns the data in the column
+ internal override object GetValue(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, SQLiteType typ)
+ {
+ TypeAffinity aff = typ.Affinity;
+ if (aff == TypeAffinity.Null) return DBNull.Value;
+ Type t = null;
+
+ if (typ.Type != DbType.Object)
+ {
+ t = SQLiteConvert.SQLiteTypeToType(typ);
+ aff = TypeToAffinity(t, flags);
+ }
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.GetAllAsText))
+ return GetText(stmt, index);
+
+ switch (aff)
+ {
+ case TypeAffinity.Blob:
+ if (typ.Type == DbType.Guid && typ.Affinity == TypeAffinity.Text)
+ return new Guid(GetText(stmt, index));
+
+ int n = (int)GetBytes(stmt, index, 0, null, 0, 0);
+ byte[] b = new byte[n];
+ GetBytes(stmt, index, 0, b, 0, n);
+
+ if (typ.Type == DbType.Guid && n == 16)
+ return new Guid(b);
+
+ return b;
+ case TypeAffinity.DateTime:
+ return GetDateTime(stmt, index);
+ case TypeAffinity.Double:
+ if (t == null) return GetDouble(stmt, index);
+ return Convert.ChangeType(GetDouble(stmt, index), t,
+ HelperMethods.HasFlags(flags, SQLiteConnectionFlags.GetInvariantDouble) ?
+ CultureInfo.InvariantCulture : CultureInfo.CurrentCulture);
+ case TypeAffinity.Int64:
+ if (t == null) return GetInt64(stmt, index);
+ if (t == typeof(Boolean)) return GetBoolean(stmt, index);
+ if (t == typeof(SByte)) return GetSByte(stmt, index);
+ if (t == typeof(Byte)) return GetByte(stmt, index);
+ if (t == typeof(Int16)) return GetInt16(stmt, index);
+ if (t == typeof(UInt16)) return GetUInt16(stmt, index);
+ if (t == typeof(Int32)) return GetInt32(stmt, index);
+ if (t == typeof(UInt32)) return GetUInt32(stmt, index);
+ if (t == typeof(Int64)) return GetInt64(stmt, index);
+ if (t == typeof(UInt64)) return GetUInt64(stmt, index);
+ return Convert.ChangeType(GetInt64(stmt, index), t,
+ HelperMethods.HasFlags(flags, SQLiteConnectionFlags.GetInvariantInt64) ?
+ CultureInfo.InvariantCulture : CultureInfo.CurrentCulture);
+ default:
+ return GetText(stmt, index);
+ }
+ }
+
+ internal override int GetCursorForTable(SQLiteStatement stmt, int db, int rootPage)
+ {
+#if !SQLITE_STANDARD
+ return UnsafeNativeMethods.sqlite3_table_cursor_interop(stmt._sqlite_stmt, db, rootPage);
+#else
+ return -1;
+#endif
+ }
+
+ internal override long GetRowIdForCursor(SQLiteStatement stmt, int cursor)
+ {
+#if !SQLITE_STANDARD
+ long rowid = 0;
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_cursor_rowid_interop(stmt._sqlite_stmt, cursor, ref rowid);
+ if (rc == SQLiteErrorCode.Ok) return rowid;
+
+ return 0;
+#else
+ return 0;
+#endif
+ }
+
+ internal override void GetIndexColumnExtendedInfo(string database, string index, string column, ref int sortMode, ref int onError, ref string collationSequence)
+ {
+#if !SQLITE_STANDARD
+ IntPtr coll = IntPtr.Zero;
+ int colllen = 0;
+ SQLiteErrorCode rc;
+
+ rc = UnsafeNativeMethods.sqlite3_index_column_info_interop(_sql, ToUTF8(database), ToUTF8(index), ToUTF8(column), ref sortMode, ref onError, ref coll, ref colllen);
+ if (rc != SQLiteErrorCode.Ok) throw new SQLiteException(rc, null);
+
+ collationSequence = UTF8ToString(coll, colllen);
+#else
+ sortMode = 0;
+ onError = 2;
+ collationSequence = "BINARY";
+#endif
+ }
+
+ internal override SQLiteErrorCode FileControl(string zDbName, int op, IntPtr pArg)
+ {
+ return UnsafeNativeMethods.sqlite3_file_control(_sql, (zDbName != null) ? ToUTF8(zDbName) : null, op, pArg);
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLite3_UTF16.cs b/Native.Csharp.Tool/SQLite/SQLite3_UTF16.cs
new file mode 100644
index 00000000..67441366
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLite3_UTF16.cs
@@ -0,0 +1,384 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Collections.Generic;
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ using System.Diagnostics;
+#endif
+
+ using System.Globalization;
+ using System.IO;
+ using System.Runtime.InteropServices;
+
+
+ ///
+ /// Alternate SQLite3 object, overriding many text behaviors to support UTF-16 (Unicode)
+ ///
+ internal sealed class SQLite3_UTF16 : SQLite3
+ {
+ ///
+ /// Constructs the object used to interact with the SQLite core library
+ /// using the UTF-8 text encoding.
+ ///
+ ///
+ /// The DateTime format to be used when converting string values to a
+ /// DateTime and binding DateTime parameters.
+ ///
+ ///
+ /// The to be used when creating DateTime
+ /// values.
+ ///
+ ///
+ /// The format string to be used when parsing and formatting DateTime
+ /// values.
+ ///
+ ///
+ /// The native handle to be associated with the database connection.
+ ///
+ ///
+ /// The fully qualified file name associated with .
+ ///
+ ///
+ /// Non-zero if the newly created object instance will need to dispose
+ /// of when it is no longer needed.
+ ///
+ internal SQLite3_UTF16(
+ SQLiteDateFormats fmt,
+ DateTimeKind kind,
+ string fmtString,
+ IntPtr db,
+ string fileName,
+ bool ownHandle
+ )
+ : base(fmt, kind, fmtString, db, fileName, ownHandle)
+ {
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLite3_UTF16).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Overrides SQLiteConvert.ToString() to marshal UTF-16 strings instead of UTF-8
+ ///
+ /// A pointer to a UTF-16 string
+ /// The length (IN BYTES) of the string
+ /// A .NET string
+ public override string ToString(IntPtr b, int nbytelen)
+ {
+ CheckDisposed();
+ return UTF16ToString(b, nbytelen);
+ }
+
+ public static string UTF16ToString(IntPtr b, int nbytelen)
+ {
+ if (nbytelen == 0 || b == IntPtr.Zero) return String.Empty;
+
+ if (nbytelen == -1)
+ return Marshal.PtrToStringUni(b);
+ else
+ return Marshal.PtrToStringUni(b, nbytelen / 2);
+ }
+
+ internal override void Open(string strFilename, string vfsName, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool)
+ {
+ //
+ // NOTE: If the database connection is currently open, attempt to
+ // close it now. This must be done because the file name or
+ // other parameters that may impact the underlying database
+ // connection may have changed.
+ //
+ if (_sql != null) Close(false);
+
+ //
+ // NOTE: If the connection was not closed successfully, throw an
+ // exception now.
+ //
+ if (_sql != null)
+ throw new SQLiteException("connection handle is still active");
+
+ _usePool = usePool;
+ _fileName = strFilename;
+ _flags = connectionFlags;
+
+ if (usePool)
+ {
+ _sql = SQLiteConnectionPool.Remove(strFilename, maxPoolSize, out _poolVersion);
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.OpenedFromPool, null, null,
+ null, null, _sql, strFilename, new object[] {
+ typeof(SQLite3_UTF16), strFilename, vfsName,
+ connectionFlags, openFlags, maxPoolSize, usePool,
+ _poolVersion }));
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Open16 (Pool): {0}",
+ HandleToString()));
+#endif
+ }
+
+ if (_sql == null)
+ {
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ IntPtr db = IntPtr.Zero;
+ SQLiteErrorCode n;
+
+ int extFuncs = HelperMethods.HasFlags(connectionFlags, SQLiteConnectionFlags.NoExtensionFunctions) ? 0 : 1;
+
+#if !SQLITE_STANDARD
+ if ((vfsName != null) || (extFuncs != 0))
+ {
+ n = UnsafeNativeMethods.sqlite3_open16_interop(ToUTF8(strFilename), ToUTF8(vfsName), openFlags, extFuncs, ref db);
+ }
+ else
+#endif
+ {
+ //
+ // NOTE: This flag check is designed to enforce the constraint that opening
+ // a database file that does not already exist requires specifying the
+ // "Create" flag, even when a native API is used that does not accept
+ // a flags parameter.
+ //
+ if (((openFlags & SQLiteOpenFlagsEnum.Create) != SQLiteOpenFlagsEnum.Create) && !File.Exists(strFilename))
+ throw new SQLiteException(SQLiteErrorCode.CantOpen, strFilename);
+
+ if (vfsName != null)
+ {
+ throw new SQLiteException(SQLiteErrorCode.CantOpen, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "cannot open using UTF-16 and VFS \"{0}\": need interop assembly", vfsName));
+ }
+
+ n = UnsafeNativeMethods.sqlite3_open16(strFilename, ref db);
+ }
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Open16: {0}", db));
+#endif
+
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
+ _sql = new SQLiteConnectionHandle(db, true);
+ }
+ lock (_sql) { /* HACK: Force the SyncBlock to be "created" now. */ }
+
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null,
+ null, null, null, _sql, strFilename, new object[] {
+ typeof(SQLite3_UTF16), strFilename, vfsName,
+ connectionFlags, openFlags, maxPoolSize, usePool }));
+ }
+
+ // Bind functions to this connection. If any previous functions of the same name
+ // were already bound, then the new bindings replace the old.
+ if (!HelperMethods.HasFlags(connectionFlags, SQLiteConnectionFlags.NoBindFunctions))
+ {
+ if (_functions == null)
+ _functions = new Dictionary();
+
+ foreach (KeyValuePair pair
+ in SQLiteFunction.BindFunctions(this, connectionFlags))
+ {
+ _functions[pair.Key] = pair.Value;
+ }
+ }
+
+ SetTimeout(0);
+ GC.KeepAlive(_sql);
+ }
+
+ internal override void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt)
+ {
+ switch (_datetimeFormat)
+ {
+ case SQLiteDateFormats.Ticks:
+ case SQLiteDateFormats.JulianDay:
+ case SQLiteDateFormats.UnixEpoch:
+ {
+ base.Bind_DateTime(stmt, flags, index, dt);
+ break;
+ }
+ default:
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (HelperMethods.LogBind(flags))
+ {
+ SQLiteStatementHandle handle =
+ (stmt != null) ? stmt._sqlite_stmt : null;
+
+ LogBind(handle, index, dt);
+ }
+#endif
+
+ Bind_Text(stmt, flags, index, ToString(dt));
+ break;
+ }
+ }
+ }
+
+ internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value)
+ {
+ SQLiteStatementHandle handle = stmt._sqlite_stmt;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (HelperMethods.LogBind(flags))
+ {
+ LogBind(handle, index, value);
+ }
+#endif
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text16(handle, index, value, value.Length * 2, (IntPtr)(-1));
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError());
+ }
+
+ internal override DateTime GetDateTime(SQLiteStatement stmt, int index)
+ {
+ if (_datetimeFormat == SQLiteDateFormats.Ticks)
+ return TicksToDateTime(GetInt64(stmt, index), _datetimeKind);
+ else if (_datetimeFormat == SQLiteDateFormats.JulianDay)
+ return ToDateTime(GetDouble(stmt, index), _datetimeKind);
+ else if (_datetimeFormat == SQLiteDateFormats.UnixEpoch)
+ return UnixEpochToDateTime(GetInt64(stmt, index), _datetimeKind);
+
+ return ToDateTime(GetText(stmt, index));
+ }
+
+ internal override string ColumnName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ IntPtr p = UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, ref len);
+#else
+ IntPtr p = UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index);
+#endif
+ if (p == IntPtr.Zero)
+ throw new SQLiteException(SQLiteErrorCode.NoMem, GetLastError());
+#if !SQLITE_STANDARD
+ return UTF16ToString(p, len);
+#else
+ return UTF16ToString(p, -1);
+#endif
+ }
+
+ internal override string GetText(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_text16(stmt._sqlite_stmt, index),
+ UnsafeNativeMethods.sqlite3_column_bytes16(stmt._sqlite_stmt, index));
+#endif
+ }
+
+ internal override string ColumnOriginalName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_origin_name16(stmt._sqlite_stmt, index), -1);
+#endif
+ }
+
+ internal override string ColumnDatabaseName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_database_name16(stmt._sqlite_stmt, index), -1);
+#endif
+ }
+
+ internal override string ColumnTableName(SQLiteStatement stmt, int index)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16_interop(stmt._sqlite_stmt, index, ref len), len);
+#else
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_column_table_name16(stmt._sqlite_stmt, index), -1);
+#endif
+ }
+
+ internal override string GetParamValueText(IntPtr ptr)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16_interop(ptr, ref len), len);
+#else
+ return UTF16ToString(UnsafeNativeMethods.sqlite3_value_text16(ptr),
+ UnsafeNativeMethods.sqlite3_value_bytes16(ptr));
+#endif
+ }
+
+ internal override void ReturnError(IntPtr context, string value)
+ {
+ UnsafeNativeMethods.sqlite3_result_error16(context, value, value.Length * 2);
+ }
+
+ internal override void ReturnText(IntPtr context, string value)
+ {
+ UnsafeNativeMethods.sqlite3_result_text16(context, value, value.Length * 2, (IntPtr)(-1));
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteBackup.cs b/Native.Csharp.Tool/SQLite/SQLiteBackup.cs
new file mode 100644
index 00000000..ab6d13b5
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteBackup.cs
@@ -0,0 +1,149 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+
+ ///
+ /// Represents a single SQL backup in SQLite.
+ ///
+ internal sealed class SQLiteBackup : IDisposable
+ {
+ ///
+ /// The underlying SQLite object this backup is bound to.
+ ///
+ internal SQLiteBase _sql;
+
+ ///
+ /// The actual backup handle.
+ ///
+ internal SQLiteBackupHandle _sqlite_backup;
+
+ ///
+ /// The destination database for the backup.
+ ///
+ internal IntPtr _destDb;
+
+ ///
+ /// The destination database name for the backup.
+ ///
+ internal byte[] _zDestName;
+
+ ///
+ /// The source database for the backup.
+ ///
+ internal IntPtr _sourceDb;
+
+ ///
+ /// The source database name for the backup.
+ ///
+ internal byte[] _zSourceName;
+
+ ///
+ /// The last result from the StepBackup method of the SQLite3 class.
+ /// This is used to determine if the call to the FinishBackup method of
+ /// the SQLite3 class should throw an exception when it receives a non-Ok
+ /// return code from the core SQLite library.
+ ///
+ internal SQLiteErrorCode _stepResult;
+
+ ///
+ /// Initializes the backup.
+ ///
+ /// The base SQLite object.
+ /// The backup handle.
+ /// The destination database for the backup.
+ /// The destination database name for the backup.
+ /// The source database for the backup.
+ /// The source database name for the backup.
+ internal SQLiteBackup(
+ SQLiteBase sqlbase,
+ SQLiteBackupHandle backup,
+ IntPtr destDb,
+ byte[] zDestName,
+ IntPtr sourceDb,
+ byte[] zSourceName
+ )
+ {
+ _sql = sqlbase;
+ _sqlite_backup = backup;
+ _destDb = destDb;
+ _zDestName = zDestName;
+ _sourceDb = sourceDb;
+ _zSourceName = zSourceName;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes and finalizes the backup.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteBackup).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (_sqlite_backup != null)
+ {
+ _sqlite_backup.Dispose();
+ _sqlite_backup = null;
+ }
+
+ _zSourceName = null;
+ _sourceDb = IntPtr.Zero;
+ _zDestName = null;
+ _destDb = IntPtr.Zero;
+ _sql = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ~SQLiteBackup()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteBase.cs b/Native.Csharp.Tool/SQLite/SQLiteBase.cs
new file mode 100644
index 00000000..e10e64ba
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteBase.cs
@@ -0,0 +1,1656 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Collections.Generic;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ using System.Runtime.InteropServices;
+#endif
+
+ ///
+ /// This internal class provides the foundation of SQLite support. It defines all the abstract members needed to implement
+ /// a SQLite data provider, and inherits from SQLiteConvert which allows for simple translations of string to and from SQLite.
+ ///
+ internal abstract class SQLiteBase : SQLiteConvert, IDisposable
+ {
+ #region Private Constants
+ ///
+ /// The error code used for logging exceptions caught in user-provided
+ /// code.
+ ///
+ internal const int COR_E_EXCEPTION = unchecked((int)0x80131500);
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ internal SQLiteBase(SQLiteDateFormats fmt, DateTimeKind kind, string fmtString)
+ : base(fmt, kind, fmtString) { }
+
+ ///
+ /// Returns a string representing the active version of SQLite
+ ///
+ internal abstract string Version { get; }
+ ///
+ /// Returns an integer representing the active version of SQLite
+ ///
+ internal abstract int VersionNumber { get; }
+ ///
+ /// Returns non-zero if this connection to the database is read-only.
+ ///
+ internal abstract bool IsReadOnly(string name);
+ ///
+ /// Returns the rowid of the most recent successful INSERT into the database from this connection.
+ ///
+ internal abstract long LastInsertRowId { get; }
+ ///
+ /// Returns the number of changes the last executing insert/update caused.
+ ///
+ internal abstract int Changes { get; }
+ ///
+ /// Returns the amount of memory (in bytes) currently in use by the SQLite core library. This is not really a per-connection
+ /// value, it is global to the process.
+ ///
+ internal abstract long MemoryUsed { get; }
+ ///
+ /// Returns the maximum amount of memory (in bytes) used by the SQLite core library since the high-water mark was last reset.
+ /// This is not really a per-connection value, it is global to the process.
+ ///
+ internal abstract long MemoryHighwater { get; }
+ ///
+ /// Returns non-zero if the underlying native connection handle is owned by this instance.
+ ///
+ internal abstract bool OwnHandle { get; }
+ ///
+ /// Returns the logical list of functions associated with this connection.
+ ///
+ internal abstract IDictionary Functions { get; }
+ ///
+ /// Sets the status of the memory usage tracking subsystem in the SQLite core library. By default, this is enabled.
+ /// If this is disabled, memory usage tracking will not be performed. This is not really a per-connection value, it is
+ /// global to the process.
+ ///
+ /// Non-zero to enable memory usage tracking, zero otherwise.
+ /// A standard SQLite return code (i.e. zero for success and non-zero for failure).
+ internal abstract SQLiteErrorCode SetMemoryStatus(bool value);
+ ///
+ /// Attempts to free as much heap memory as possible for the database connection.
+ ///
+ /// A standard SQLite return code (i.e. zero for success and non-zero for failure).
+ internal abstract SQLiteErrorCode ReleaseMemory();
+ ///
+ /// Shutdown the SQLite engine so that it can be restarted with different config options.
+ /// We depend on auto initialization to recover.
+ ///
+ internal abstract SQLiteErrorCode Shutdown();
+ ///
+ /// Determines if the associated native connection handle is open.
+ ///
+ ///
+ /// Non-zero if a database connection is open.
+ ///
+ internal abstract bool IsOpen();
+ ///
+ /// Returns the fully qualified path and file name for the currently open
+ /// database, if any.
+ ///
+ ///
+ /// The name of the attached database to query.
+ ///
+ ///
+ /// The fully qualified path and file name for the currently open database,
+ /// if any.
+ ///
+ internal abstract string GetFileName(string dbName);
+ ///
+ /// Opens a database.
+ ///
+ ///
+ /// Implementers should call SQLiteFunction.BindFunctions() and save the array after opening a connection
+ /// to bind all attributed user-defined functions and collating sequences to the new connection.
+ ///
+ /// The filename of the database to open. SQLite automatically creates it if it doesn't exist.
+ /// The name of the VFS to use -OR- null to use the default VFS.
+ /// The flags associated with the parent connection object
+ /// The open flags to use when creating the connection
+ /// The maximum size of the pool for the given filename
+ /// If true, the connection can be pulled from the connection pool
+ internal abstract void Open(string strFilename, string vfsName, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, int maxPoolSize, bool usePool);
+ ///
+ /// Closes the currently-open database.
+ ///
+ ///
+ /// After the database has been closed implemeters should call SQLiteFunction.UnbindFunctions() to deallocate all interop allocated
+ /// memory associated with the user-defined functions and collating sequences tied to the closed connection.
+ ///
+ /// Non-zero if connection is being disposed, zero otherwise.
+ internal abstract void Close(bool disposing);
+ ///
+ /// Sets the busy timeout on the connection. SQLiteCommand will call this before executing any command.
+ ///
+ /// The number of milliseconds to wait before returning SQLITE_BUSY
+ internal abstract void SetTimeout(int nTimeoutMS);
+ ///
+ /// Returns the text of the last error issued by SQLite
+ ///
+ ///
+ internal abstract string GetLastError();
+
+ ///
+ /// Returns the text of the last error issued by SQLite -OR- the specified default error text if
+ /// none is available from the SQLite core library.
+ ///
+ ///
+ /// The error text to return in the event that one is not available from the SQLite core library.
+ ///
+ ///
+ /// The error text.
+ ///
+ internal abstract string GetLastError(string defValue);
+
+ ///
+ /// When pooling is enabled, force this connection to be disposed rather than returned to the pool
+ ///
+ internal abstract void ClearPool();
+
+ ///
+ /// When pooling is enabled, returns the number of pool entries matching the current file name.
+ ///
+ /// The number of pool entries matching the current file name.
+ internal abstract int CountPool();
+
+ ///
+ /// Prepares a SQL statement for execution.
+ ///
+ /// The source connection preparing the command. Can be null for any caller except LINQ
+ /// The SQL command text to prepare
+ /// The previous statement in a multi-statement command, or null if no previous statement exists
+ /// The timeout to wait before aborting the prepare
+ /// The remainder of the statement that was not processed. Each call to prepare parses the
+ /// SQL up to to either the end of the text or to the first semi-colon delimiter. The remaining text is returned
+ /// here for a subsequent call to Prepare() until all the text has been processed.
+ /// Returns an initialized SQLiteStatement.
+ internal abstract SQLiteStatement Prepare(SQLiteConnection cnn, string strSql, SQLiteStatement previous, uint timeoutMS, ref string strRemain);
+ ///
+ /// Steps through a prepared statement.
+ ///
+ /// The SQLiteStatement to step through
+ /// True if a row was returned, False if not.
+ internal abstract bool Step(SQLiteStatement stmt);
+ ///
+ /// Returns non-zero if the specified statement is read-only in nature.
+ ///
+ /// The statement to check.
+ /// True if the outer query is read-only.
+ internal abstract bool IsReadOnly(SQLiteStatement stmt);
+ ///
+ /// Resets a prepared statement so it can be executed again. If the error returned is SQLITE_SCHEMA,
+ /// transparently attempt to rebuild the SQL statement and throw an error if that was not possible.
+ ///
+ /// The statement to reset
+ /// Returns -1 if the schema changed while resetting, 0 if the reset was sucessful or 6 (SQLITE_LOCKED) if the reset failed due to a lock
+ internal abstract SQLiteErrorCode Reset(SQLiteStatement stmt);
+
+ ///
+ /// Attempts to interrupt the query currently executing on the associated
+ /// native database connection.
+ ///
+ internal abstract void Cancel();
+
+ ///
+ /// This function binds a user-defined function to the connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// The object instance that implements the
+ /// function to be bound.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ internal abstract void BindFunction(SQLiteFunctionAttribute functionAttribute, SQLiteFunction function, SQLiteConnectionFlags flags);
+
+ ///
+ /// This function unbinds a user-defined function from the connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be unbound.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ /// Non-zero if the function was unbound.
+ internal abstract bool UnbindFunction(SQLiteFunctionAttribute functionAttribute, SQLiteConnectionFlags flags);
+
+ internal abstract void Bind_Double(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, double value);
+ internal abstract void Bind_Int32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, Int32 value);
+ internal abstract void Bind_UInt32(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, UInt32 value);
+ internal abstract void Bind_Int64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, Int64 value);
+ internal abstract void Bind_UInt64(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, UInt64 value);
+ internal abstract void Bind_Boolean(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, bool value);
+ internal abstract void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value);
+ internal abstract void Bind_Blob(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, byte[] blobData);
+ internal abstract void Bind_DateTime(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, DateTime dt);
+ internal abstract void Bind_Null(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index);
+
+ internal abstract int Bind_ParamCount(SQLiteStatement stmt, SQLiteConnectionFlags flags);
+ internal abstract string Bind_ParamName(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index);
+ internal abstract int Bind_ParamIndex(SQLiteStatement stmt, SQLiteConnectionFlags flags, string paramName);
+
+ internal abstract int ColumnCount(SQLiteStatement stmt);
+ internal abstract string ColumnName(SQLiteStatement stmt, int index);
+ internal abstract TypeAffinity ColumnAffinity(SQLiteStatement stmt, int index);
+ internal abstract string ColumnType(SQLiteStatement stmt, int index, ref TypeAffinity nAffinity);
+ internal abstract int ColumnIndex(SQLiteStatement stmt, string columnName);
+ internal abstract string ColumnOriginalName(SQLiteStatement stmt, int index);
+ internal abstract string ColumnDatabaseName(SQLiteStatement stmt, int index);
+ internal abstract string ColumnTableName(SQLiteStatement stmt, int index);
+ internal abstract bool DoesTableExist(string dataBase, string table);
+ internal abstract bool ColumnMetaData(string dataBase, string table, string column, bool canThrow, ref string dataType, ref string collateSequence, ref bool notNull, ref bool primaryKey, ref bool autoIncrement);
+ internal abstract void GetIndexColumnExtendedInfo(string database, string index, string column, ref int sortMode, ref int onError, ref string collationSequence);
+
+ internal abstract object GetObject(SQLiteStatement stmt, int index);
+ internal abstract double GetDouble(SQLiteStatement stmt, int index);
+ internal abstract Boolean GetBoolean(SQLiteStatement stmt, int index);
+ internal abstract SByte GetSByte(SQLiteStatement stmt, int index);
+ internal abstract Byte GetByte(SQLiteStatement stmt, int index);
+ internal abstract Int16 GetInt16(SQLiteStatement stmt, int index);
+ internal abstract UInt16 GetUInt16(SQLiteStatement stmt, int index);
+ internal abstract Int32 GetInt32(SQLiteStatement stmt, int index);
+ internal abstract UInt32 GetUInt32(SQLiteStatement stmt, int index);
+ internal abstract Int64 GetInt64(SQLiteStatement stmt, int index);
+ internal abstract UInt64 GetUInt64(SQLiteStatement stmt, int index);
+ internal abstract string GetText(SQLiteStatement stmt, int index);
+ internal abstract long GetBytes(SQLiteStatement stmt, int index, int nDataoffset, byte[] bDest, int nStart, int nLength);
+ internal abstract char GetChar(SQLiteStatement stmt, int index);
+ internal abstract long GetChars(SQLiteStatement stmt, int index, int nDataoffset, char[] bDest, int nStart, int nLength);
+ internal abstract DateTime GetDateTime(SQLiteStatement stmt, int index);
+ internal abstract bool IsNull(SQLiteStatement stmt, int index);
+
+ internal abstract SQLiteErrorCode CreateCollation(string strCollation, SQLiteCollation func, SQLiteCollation func16, bool @throw);
+ internal abstract SQLiteErrorCode CreateFunction(string strFunction, int nArgs, bool needCollSeq, SQLiteCallback func, SQLiteCallback funcstep, SQLiteFinalCallback funcfinal, bool @throw);
+ internal abstract CollationSequence GetCollationSequence(SQLiteFunction func, IntPtr context);
+ internal abstract int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, string s1, string s2);
+ internal abstract int ContextCollateCompare(CollationEncodingEnum enc, IntPtr context, char[] c1, char[] c2);
+
+ internal abstract int AggregateCount(IntPtr context);
+ internal abstract IntPtr AggregateContext(IntPtr context);
+
+ internal abstract long GetParamValueBytes(IntPtr ptr, int nDataOffset, byte[] bDest, int nStart, int nLength);
+ internal abstract double GetParamValueDouble(IntPtr ptr);
+ internal abstract int GetParamValueInt32(IntPtr ptr);
+ internal abstract Int64 GetParamValueInt64(IntPtr ptr);
+ internal abstract string GetParamValueText(IntPtr ptr);
+ internal abstract TypeAffinity GetParamValueType(IntPtr ptr);
+
+ internal abstract void ReturnBlob(IntPtr context, byte[] value);
+ internal abstract void ReturnDouble(IntPtr context, double value);
+ internal abstract void ReturnError(IntPtr context, string value);
+ internal abstract void ReturnInt32(IntPtr context, Int32 value);
+ internal abstract void ReturnInt64(IntPtr context, Int64 value);
+ internal abstract void ReturnNull(IntPtr context);
+ internal abstract void ReturnText(IntPtr context, string value);
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Calls the native SQLite core library in order to create a disposable
+ /// module containing the implementation of a virtual table.
+ ///
+ ///
+ /// The module object to be used when creating the native disposable module.
+ ///
+ ///
+ /// The flags for the associated object instance.
+ ///
+ internal abstract void CreateModule(SQLiteModule module, SQLiteConnectionFlags flags);
+
+ ///
+ /// Calls the native SQLite core library in order to cleanup the resources
+ /// associated with a module containing the implementation of a virtual table.
+ ///
+ ///
+ /// The module object previously passed to the
+ /// method.
+ ///
+ ///
+ /// The flags for the associated object instance.
+ ///
+ internal abstract void DisposeModule(SQLiteModule module, SQLiteConnectionFlags flags);
+
+ ///
+ /// Calls the native SQLite core library in order to declare a virtual table
+ /// in response to a call into the
+ /// or virtual table methods.
+ ///
+ ///
+ /// The virtual table module that is to be responsible for the virtual table
+ /// being declared.
+ ///
+ ///
+ /// The string containing the SQL statement describing the virtual table to
+ /// be declared.
+ ///
+ ///
+ /// Upon success, the contents of this parameter are undefined. Upon failure,
+ /// it should contain an appropriate error message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal abstract SQLiteErrorCode DeclareVirtualTable(SQLiteModule module, string strSql, ref string error);
+
+ ///
+ /// Calls the native SQLite core library in order to declare a virtual table
+ /// function in response to a call into the
+ /// or virtual table methods.
+ ///
+ ///
+ /// The virtual table module that is to be responsible for the virtual table
+ /// function being declared.
+ ///
+ ///
+ /// The number of arguments to the function being declared.
+ ///
+ ///
+ /// The name of the function being declared.
+ ///
+ ///
+ /// Upon success, the contents of this parameter are undefined. Upon failure,
+ /// it should contain an appropriate error message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal abstract SQLiteErrorCode DeclareVirtualFunction(SQLiteModule module, int argumentCount, string name, ref string error);
+#endif
+
+ ///
+ /// Returns the current and/or highwater values for the specified database status parameter.
+ ///
+ ///
+ /// The database status parameter to query.
+ ///
+ ///
+ /// Non-zero to reset the highwater value to the current value.
+ ///
+ ///
+ /// If applicable, receives the current value.
+ ///
+ ///
+ /// If applicable, receives the highwater value.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal abstract SQLiteErrorCode GetStatusParameter(SQLiteStatusOpsEnum option, bool reset, ref int current, ref int highwater);
+ ///
+ /// Change a configuration option value for the database.
+ ///
+ ///
+ /// The database configuration option to change.
+ ///
+ ///
+ /// The new value for the specified configuration option.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ internal abstract SQLiteErrorCode SetConfigurationOption(SQLiteConfigDbOpsEnum option, object value);
+ ///
+ /// Enables or disables extension loading by SQLite.
+ ///
+ ///
+ /// True to enable loading of extensions, false to disable.
+ ///
+ internal abstract void SetLoadExtension(bool bOnOff);
+ ///
+ /// Loads a SQLite extension library from the named file.
+ ///
+ ///
+ /// The name of the dynamic link library file containing the extension.
+ ///
+ ///
+ /// The name of the exported function used to initialize the extension.
+ /// If null, the default "sqlite3_extension_init" will be used.
+ ///
+ internal abstract void LoadExtension(string fileName, string procName);
+ ///
+ /// Enables or disabled extened result codes returned by SQLite
+ ///
+ /// true to enable extended result codes, false to disable.
+ ///
+ internal abstract void SetExtendedResultCodes(bool bOnOff);
+ ///
+ /// Returns the numeric result code for the most recent failed SQLite API call
+ /// associated with the database connection.
+ ///
+ /// Result code
+ internal abstract SQLiteErrorCode ResultCode();
+ ///
+ /// Returns the extended numeric result code for the most recent failed SQLite API call
+ /// associated with the database connection.
+ ///
+ /// Extended result code
+ internal abstract SQLiteErrorCode ExtendedResultCode();
+
+ ///
+ /// Add a log message via the SQLite sqlite3_log interface.
+ ///
+ /// Error code to be logged with the message.
+ /// String to be logged. Unlike the SQLite sqlite3_log()
+ /// interface, this should be pre-formatted. Consider using the
+ /// String.Format() function.
+ ///
+ internal abstract void LogMessage(SQLiteErrorCode iErrCode, string zMessage);
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ internal abstract void SetPassword(byte[] passwordBytes);
+ internal abstract void ChangePassword(byte[] newPasswordBytes);
+#endif
+
+ internal abstract void SetProgressHook(int nOps, SQLiteProgressCallback func);
+ internal abstract void SetAuthorizerHook(SQLiteAuthorizerCallback func);
+ internal abstract void SetUpdateHook(SQLiteUpdateCallback func);
+ internal abstract void SetCommitHook(SQLiteCommitCallback func);
+ internal abstract void SetTraceCallback(SQLiteTraceCallback func);
+ internal abstract void SetTraceCallback2(SQLiteTraceFlags mask, SQLiteTraceCallback2 func);
+ internal abstract void SetRollbackHook(SQLiteRollbackCallback func);
+ internal abstract SQLiteErrorCode SetLogCallback(SQLiteLogCallback func);
+
+ ///
+ /// Checks if the SQLite core library has been initialized in the current process.
+ ///
+ ///
+ /// Non-zero if the SQLite core library has been initialized in the current process,
+ /// zero otherwise.
+ ///
+ internal abstract bool IsInitialized();
+
+ internal abstract int GetCursorForTable(SQLiteStatement stmt, int database, int rootPage);
+ internal abstract long GetRowIdForCursor(SQLiteStatement stmt, int cursor);
+
+ internal abstract object GetValue(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, SQLiteType typ);
+
+ ///
+ /// Returns non-zero if the given database connection is in autocommit mode.
+ /// Autocommit mode is on by default. Autocommit mode is disabled by a BEGIN
+ /// statement. Autocommit mode is re-enabled by a COMMIT or ROLLBACK.
+ ///
+ internal abstract bool AutoCommit
+ {
+ get;
+ }
+
+ internal abstract SQLiteErrorCode FileControl(string zDbName, int op, IntPtr pArg);
+
+ ///
+ /// Creates a new SQLite backup object based on the provided destination
+ /// database connection. The source database connection is the one
+ /// associated with this object. The source and destination database
+ /// connections cannot be the same.
+ ///
+ /// The destination database connection.
+ /// The destination database name.
+ /// The source database name.
+ /// The newly created backup object.
+ internal abstract SQLiteBackup InitializeBackup(
+ SQLiteConnection destCnn, string destName,
+ string sourceName);
+
+ ///
+ /// Copies up to N pages from the source database to the destination
+ /// database associated with the specified backup object.
+ ///
+ /// The backup object to use.
+ ///
+ /// The number of pages to copy or negative to copy all remaining pages.
+ ///
+ ///
+ /// Set to true if the operation needs to be retried due to database
+ /// locking issues.
+ ///
+ ///
+ /// True if there are more pages to be copied, false otherwise.
+ ///
+ internal abstract bool StepBackup(SQLiteBackup backup, int nPage, ref bool retry);
+
+ ///
+ /// Returns the number of pages remaining to be copied from the source
+ /// database to the destination database associated with the specified
+ /// backup object.
+ ///
+ /// The backup object to check.
+ /// The number of pages remaining to be copied.
+ internal abstract int RemainingBackup(SQLiteBackup backup);
+
+ ///
+ /// Returns the total number of pages in the source database associated
+ /// with the specified backup object.
+ ///
+ /// The backup object to check.
+ /// The total number of pages in the source database.
+ internal abstract int PageCountBackup(SQLiteBackup backup);
+
+ ///
+ /// Destroys the backup object, rolling back any backup that may be in
+ /// progess.
+ ///
+ /// The backup object to destroy.
+ internal abstract void FinishBackup(SQLiteBackup backup);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteBase).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ~SQLiteBase()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ // These statics are here for lack of a better place to put them.
+ // They exist here because they are called during the finalization of
+ // a SQLiteStatementHandle, SQLiteConnectionHandle, and SQLiteFunctionCookieHandle.
+ // Therefore these functions have to be static, and have to be low-level.
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static string[] _errorMessages = {
+ /* SQLITE_OK */ "not an error",
+ /* SQLITE_ERROR */ "SQL logic error",
+ /* SQLITE_INTERNAL */ "internal logic error",
+ /* SQLITE_PERM */ "access permission denied",
+ /* SQLITE_ABORT */ "query aborted",
+ /* SQLITE_BUSY */ "database is locked",
+ /* SQLITE_LOCKED */ "database table is locked",
+ /* SQLITE_NOMEM */ "out of memory",
+ /* SQLITE_READONLY */ "attempt to write a readonly database",
+ /* SQLITE_INTERRUPT */ "interrupted",
+ /* SQLITE_IOERR */ "disk I/O error",
+ /* SQLITE_CORRUPT */ "database disk image is malformed",
+ /* SQLITE_NOTFOUND */ "unknown operation",
+ /* SQLITE_FULL */ "database or disk is full",
+ /* SQLITE_CANTOPEN */ "unable to open database file",
+ /* SQLITE_PROTOCOL */ "locking protocol",
+ /* SQLITE_EMPTY */ "table contains no data",
+ /* SQLITE_SCHEMA */ "database schema has changed",
+ /* SQLITE_TOOBIG */ "string or blob too big",
+ /* SQLITE_CONSTRAINT */ "constraint failed",
+ /* SQLITE_MISMATCH */ "datatype mismatch",
+ /* SQLITE_MISUSE */ "bad parameter or other API misuse",
+ /* SQLITE_NOLFS */ "large file support is disabled",
+ /* SQLITE_AUTH */ "authorization denied",
+ /* SQLITE_FORMAT */ "auxiliary database format error",
+ /* SQLITE_RANGE */ "column index out of range",
+ /* SQLITE_NOTADB */ "file is not a database",
+ /* SQLITE_NOTICE */ "notification message",
+ /* SQLITE_WARNING */ "warning message"
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the error message for the specified SQLite return code using
+ /// the internal static lookup table.
+ ///
+ /// The SQLite return code.
+ /// The error message or null if it cannot be found.
+ protected static string FallbackGetErrorString(SQLiteErrorCode rc)
+ {
+ switch (rc)
+ {
+ case SQLiteErrorCode.Abort_Rollback:
+ return "abort due to ROLLBACK";
+ case SQLiteErrorCode.Row:
+ return "another row available";
+ case SQLiteErrorCode.Done:
+ return "no more rows available";
+ }
+
+ if (_errorMessages == null)
+ return null;
+
+ int index = (int)(rc & SQLiteErrorCode.NonExtendedMask);
+
+ if ((index < 0) || (index >= _errorMessages.Length))
+ index = (int)SQLiteErrorCode.Error; /* Make into generic error. */
+
+ return _errorMessages[index];
+ }
+
+ internal static string GetLastError(SQLiteConnectionHandle hdl, IntPtr db)
+ {
+ if ((hdl == null) || (db == IntPtr.Zero))
+ return "null connection or database handle";
+
+ string result = null;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+ if (!hdl.IsInvalid && !hdl.IsClosed)
+ {
+#if !SQLITE_STANDARD
+ int len = 0;
+ result = UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg_interop(db, ref len), len);
+#else
+ result = UTF8ToString(UnsafeNativeMethods.sqlite3_errmsg(db), -1);
+#endif
+ }
+ else
+ {
+ result = "closed or invalid connection handle";
+ }
+ }
+ }
+ GC.KeepAlive(hdl);
+ return result;
+ }
+
+ internal static void FinishBackup(SQLiteConnectionHandle hdl, IntPtr backup)
+ {
+ if ((hdl == null) || (backup == IntPtr.Zero)) return;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+#if !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_backup_finish_interop(backup);
+#else
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_backup_finish(backup);
+#endif
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
+ }
+ }
+ }
+
+ internal static void CloseBlob(SQLiteConnectionHandle hdl, IntPtr blob)
+ {
+ if ((hdl == null) || (blob == IntPtr.Zero)) return;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+#if !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_blob_close_interop(blob);
+#else
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_blob_close(blob);
+#endif
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
+ }
+ }
+ }
+
+ internal static void FinalizeStatement(SQLiteConnectionHandle hdl, IntPtr stmt)
+ {
+ if ((hdl == null) || (stmt == IntPtr.Zero)) return;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+#if !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_finalize_interop(stmt);
+#else
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_finalize(stmt);
+#endif
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, null);
+ }
+ }
+ }
+
+ internal static void CloseConnection(SQLiteConnectionHandle hdl, IntPtr db)
+ {
+ if ((hdl == null) || (db == IntPtr.Zero)) return;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+#if !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_close_interop(db);
+#else
+ ResetConnection(hdl, db, false);
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_close(db);
+#endif
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError(hdl, db));
+ }
+ }
+ }
+
+#if !INTEROP_LEGACY_CLOSE
+ internal static void CloseConnectionV2(SQLiteConnectionHandle hdl, IntPtr db)
+ {
+ if ((hdl == null) || (db == IntPtr.Zero)) return;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+#if !SQLITE_STANDARD
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_close_interop(db);
+#else
+ ResetConnection(hdl, db, false);
+
+ SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_close_v2(db);
+#endif
+ if (n != SQLiteErrorCode.Ok) throw new SQLiteException(n, GetLastError(hdl, db));
+ }
+ }
+ }
+#endif
+
+ internal static bool ResetConnection(SQLiteConnectionHandle hdl, IntPtr db, bool canThrow)
+ {
+ if ((hdl == null) || (db == IntPtr.Zero)) return false;
+
+ bool result = false;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+ if (canThrow && hdl.IsInvalid)
+ throw new InvalidOperationException("The connection handle is invalid.");
+
+ if (canThrow && hdl.IsClosed)
+ throw new InvalidOperationException("The connection handle is closed.");
+
+ if (!hdl.IsInvalid && !hdl.IsClosed)
+ {
+ IntPtr stmt = IntPtr.Zero;
+ SQLiteErrorCode n;
+
+ do
+ {
+ stmt = UnsafeNativeMethods.sqlite3_next_stmt(db, stmt);
+ if (stmt != IntPtr.Zero)
+ {
+#if !SQLITE_STANDARD
+ n = UnsafeNativeMethods.sqlite3_reset_interop(stmt);
+#else
+ n = UnsafeNativeMethods.sqlite3_reset(stmt);
+#endif
+ }
+ } while (stmt != IntPtr.Zero);
+
+ //
+ // NOTE: Is a transaction NOT pending on the connection?
+ //
+ if (IsAutocommit(hdl, db))
+ {
+ result = true;
+ }
+ else
+ {
+ n = UnsafeNativeMethods.sqlite3_exec(
+ db, ToUTF8("ROLLBACK"), IntPtr.Zero, IntPtr.Zero,
+ ref stmt);
+
+ if (n == SQLiteErrorCode.Ok)
+ {
+ result = true;
+ }
+ else if (canThrow)
+ {
+ throw new SQLiteException(n, GetLastError(hdl, db));
+ }
+ }
+ }
+ }
+ }
+ GC.KeepAlive(hdl);
+ return result;
+ }
+
+ internal static bool IsAutocommit(SQLiteConnectionHandle hdl, IntPtr db)
+ {
+ if ((hdl == null) || (db == IntPtr.Zero)) return false;
+
+ bool result = false;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (hdl.syncRoot)
+#else
+ lock (hdl)
+#endif
+ {
+ if (!hdl.IsInvalid && !hdl.IsClosed)
+ result = (UnsafeNativeMethods.sqlite3_get_autocommit(db) == 1);
+ }
+ }
+ GC.KeepAlive(hdl); /* NOTE: Unreachable code. */
+ return result;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public interface ISQLiteSchemaExtensions
+ {
+ ///
+ /// Creates temporary tables on the connection so schema information can be queried.
+ ///
+ ///
+ /// The connection upon which to build the schema tables.
+ ///
+ void BuildTempSchema(SQLiteConnection connection);
+ }
+
+ [Flags]
+ internal enum SQLiteOpenFlagsEnum
+ {
+ None = 0,
+ ReadOnly = 0x1,
+ ReadWrite = 0x2,
+ Create = 0x4,
+ Uri = 0x40,
+ Memory = 0x80,
+ Default = ReadWrite | Create,
+ }
+
+ ///
+ /// The extra behavioral flags that can be applied to a connection.
+ ///
+ [Flags()]
+ public enum SQLiteConnectionFlags : long
+ {
+ ///
+ /// No extra flags.
+ ///
+ None = 0x0,
+
+ ///
+ /// Enable logging of all SQL statements to be prepared.
+ ///
+ LogPrepare = 0x1,
+
+ ///
+ /// Enable logging of all bound parameter types and raw values.
+ ///
+ LogPreBind = 0x2,
+
+ ///
+ /// Enable logging of all bound parameter strongly typed values.
+ ///
+ LogBind = 0x4,
+
+ ///
+ /// Enable logging of all exceptions caught from user-provided
+ /// managed code called from native code via delegates.
+ ///
+ LogCallbackException = 0x8,
+
+ ///
+ /// Enable logging of backup API errors.
+ ///
+ LogBackup = 0x10,
+
+ ///
+ /// Skip adding the extension functions provided by the native
+ /// interop assembly.
+ ///
+ NoExtensionFunctions = 0x20,
+
+ ///
+ /// When binding parameter values with the
+ /// type, use the interop method that accepts an
+ /// value.
+ ///
+ BindUInt32AsInt64 = 0x40,
+
+ ///
+ /// When binding parameter values, always bind them as though they were
+ /// plain text (i.e. no numeric, date/time, or other conversions should
+ /// be attempted).
+ ///
+ BindAllAsText = 0x80,
+
+ ///
+ /// When returning column values, always return them as though they were
+ /// plain text (i.e. no numeric, date/time, or other conversions should
+ /// be attempted).
+ ///
+ GetAllAsText = 0x100,
+
+ ///
+ /// Prevent this object instance from
+ /// loading extensions.
+ ///
+ NoLoadExtension = 0x200,
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Prevent this object instance from
+ /// creating virtual table modules.
+ ///
+ NoCreateModule = 0x400,
+#endif
+
+ ///
+ /// Skip binding any functions provided by other managed assemblies when
+ /// opening the connection.
+ ///
+ NoBindFunctions = 0x800,
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Skip setting the logging related properties of the
+ /// object instance that was passed to
+ /// the method.
+ ///
+ NoLogModule = 0x1000,
+
+ ///
+ /// Enable logging of all virtual table module errors seen by the
+ /// method.
+ ///
+ LogModuleError = 0x2000,
+
+ ///
+ /// Enable logging of certain virtual table module exceptions that cannot
+ /// be easily discovered via other means.
+ ///
+ LogModuleException = 0x4000,
+#endif
+
+ ///
+ /// Enable tracing of potentially important [non-fatal] error conditions
+ /// that cannot be easily reported through other means.
+ ///
+ TraceWarning = 0x8000,
+
+ ///
+ /// When binding parameter values, always use the invariant culture when
+ /// converting their values from strings.
+ ///
+ ConvertInvariantText = 0x10000,
+
+ ///
+ /// When binding parameter values, always use the invariant culture when
+ /// converting their values to strings.
+ ///
+ BindInvariantText = 0x20000,
+
+ ///
+ /// Disable using the connection pool by default. If the "Pooling"
+ /// connection string property is specified, its value will override
+ /// this flag. The precise outcome of combining this flag with the
+ /// flag is unspecified; however,
+ /// one of the flags will be in effect.
+ ///
+ NoConnectionPool = 0x40000,
+
+ ///
+ /// Enable using the connection pool by default. If the "Pooling"
+ /// connection string property is specified, its value will override
+ /// this flag. The precise outcome of combining this flag with the
+ /// flag is unspecified; however,
+ /// one of the flags will be in effect.
+ ///
+ UseConnectionPool = 0x80000,
+
+ ///
+ /// Enable using per-connection mappings between type names and
+ /// values. Also see the
+ /// ,
+ /// , and
+ /// methods. These
+ /// per-connection mappings, when present, override the corresponding
+ /// global mappings.
+ ///
+ UseConnectionTypes = 0x100000,
+
+ ///
+ /// Disable using global mappings between type names and
+ /// values. This may be useful in some very narrow
+ /// cases; however, if there are no per-connection type mappings, the
+ /// fallback defaults will be used for both type names and their
+ /// associated values. Therefore, use of this flag
+ /// is not recommended.
+ ///
+ NoGlobalTypes = 0x200000,
+
+ ///
+ /// When the property is used, it
+ /// should return non-zero if there were ever any rows in the associated
+ /// result sets.
+ ///
+ StickyHasRows = 0x400000,
+
+ ///
+ /// Enable "strict" transaction enlistment semantics. Setting this flag
+ /// will cause an exception to be thrown if an attempt is made to enlist
+ /// in a transaction with an unavailable or unsupported isolation level.
+ /// In the future, more extensive checks may be enabled by this flag as
+ /// well.
+ ///
+ StrictEnlistment = 0x800000,
+
+ ///
+ /// Enable mapping of unsupported transaction isolation levels to the
+ /// closest supported transaction isolation level.
+ ///
+ MapIsolationLevels = 0x1000000,
+
+ ///
+ /// When returning column values, attempt to detect the affinity of
+ /// textual values by checking if they fully conform to those of the
+ /// ,
+ /// ,
+ /// ,
+ /// or types.
+ ///
+ DetectTextAffinity = 0x2000000,
+
+ ///
+ /// When returning column values, attempt to detect the type of
+ /// string values by checking if they fully conform to those of
+ /// the ,
+ /// ,
+ /// ,
+ /// or types.
+ ///
+ DetectStringType = 0x4000000,
+
+ ///
+ /// Skip querying runtime configuration settings for use by the
+ /// class, including the default
+ /// value and default database type name.
+ /// NOTE: If the
+ /// and/or
+ /// properties are not set explicitly nor set via their connection
+ /// string properties and repeated calls to determine these runtime
+ /// configuration settings are seen to be a problem, this flag
+ /// should be set.
+ ///
+ NoConvertSettings = 0x8000000,
+
+ ///
+ /// When binding parameter values with the
+ /// type, take their into account as
+ /// well as that of the associated .
+ ///
+ BindDateTimeWithKind = 0x10000000,
+
+ ///
+ /// If an exception is caught when raising the
+ /// event, the transaction
+ /// should be rolled back. If this is not specified, the transaction
+ /// will continue the commit process instead.
+ ///
+ RollbackOnException = 0x20000000,
+
+ ///
+ /// If an exception is caught when raising the
+ /// event, the action should
+ /// should be denied. If this is not specified, the action will be
+ /// allowed instead.
+ ///
+ DenyOnException = 0x40000000,
+
+ ///
+ /// If an exception is caught when raising the
+ /// event, the operation
+ /// should be interrupted. If this is not specified, the operation
+ /// will simply continue.
+ ///
+ InterruptOnException = 0x80000000,
+
+ ///
+ /// Attempt to unbind all functions provided by other managed assemblies
+ /// when closing the connection.
+ ///
+ UnbindFunctionsOnClose = 0x100000000,
+
+ ///
+ /// When returning column values as a , skip
+ /// verifying their affinity.
+ ///
+ NoVerifyTextAffinity = 0x200000000,
+
+ ///
+ /// Enable using per-connection mappings between type names and
+ /// values. Also see the
+ /// ,
+ /// , and
+ /// methods.
+ ///
+ UseConnectionBindValueCallbacks = 0x400000000,
+
+ ///
+ /// Enable using per-connection mappings between type names and
+ /// values. Also see the
+ /// ,
+ /// , and
+ /// methods.
+ ///
+ UseConnectionReadValueCallbacks = 0x800000000,
+
+ ///
+ /// If the database type name has not been explicitly set for the
+ /// parameter specified, fallback to using the parameter name.
+ ///
+ UseParameterNameForTypeName = 0x1000000000,
+
+ ///
+ /// If the database type name has not been explicitly set for the
+ /// parameter specified, fallback to using the database type name
+ /// associated with the value.
+ ///
+ UseParameterDbTypeForTypeName = 0x2000000000,
+
+ ///
+ /// When returning column values, skip verifying their affinity.
+ ///
+ NoVerifyTypeAffinity = 0x4000000000,
+
+ ///
+ /// Allow transactions to be nested. The outermost transaction still
+ /// controls whether or not any changes are ultimately committed or
+ /// rolled back. All non-outermost transactions are implemented using
+ /// the SAVEPOINT construct.
+ ///
+ AllowNestedTransactions = 0x8000000000,
+
+ ///
+ /// When binding parameter values, always bind
+ /// values as though they were plain text (i.e. not ,
+ /// which is the legacy behavior).
+ ///
+ BindDecimalAsText = 0x10000000000,
+
+ ///
+ /// When returning column values, always return
+ /// values as though they were plain text (i.e. not ,
+ /// which is the legacy behavior).
+ ///
+ GetDecimalAsText = 0x20000000000,
+
+ ///
+ /// When binding parameter values, always use
+ /// the invariant culture when converting their values to strings.
+ ///
+ BindInvariantDecimal = 0x40000000000,
+
+ ///
+ /// When returning column values, always use
+ /// the invariant culture when converting their values from strings.
+ ///
+ GetInvariantDecimal = 0x80000000000,
+
+ ///
+ /// EXPERIMENTAL --
+ /// Enable waiting for the enlistment to be reset prior to attempting
+ /// to create a new enlistment. This may be necessary due to the
+ /// semantics used by distributed transactions, which complete
+ /// asynchronously.
+ ///
+ WaitForEnlistmentReset = 0x100000000000,
+
+ ///
+ /// When returning column values, always use
+ /// the invariant culture when converting their values from strings.
+ ///
+ GetInvariantInt64 = 0x200000000000,
+
+ ///
+ /// When returning column values, always use
+ /// the invariant culture when converting their values from strings.
+ ///
+ GetInvariantDouble = 0x400000000000,
+
+ ///
+ /// EXPERIMENTAL --
+ /// Enable strict conformance to the ADO.NET standard, e.g. use of
+ /// thrown exceptions to indicate common error conditions.
+ ///
+ StrictConformance = 0x800000000000,
+
+ ///
+ /// EXPERIMENTAL --
+ /// When opening a connection, attempt to hide the password from the
+ /// connection string, etc. Given the memory architecture of the CLR,
+ /// (and P/Invoke) this is not 100% reliable and should not be relied
+ /// upon for security critical uses or applications.
+ ///
+ HidePassword = 0x1000000000000,
+
+ ///
+ /// When binding parameter values or returning column values, always
+ /// treat them as though they were plain text (i.e. no numeric,
+ /// date/time, or other conversions should be attempted).
+ ///
+ BindAndGetAllAsText = BindAllAsText | GetAllAsText,
+
+ ///
+ /// When binding parameter values, always use the invariant culture when
+ /// converting their values to strings or from strings.
+ ///
+ ConvertAndBindInvariantText = ConvertInvariantText | BindInvariantText,
+
+ ///
+ /// When binding parameter values or returning column values, always
+ /// treat them as though they were plain text (i.e. no numeric,
+ /// date/time, or other conversions should be attempted) and always
+ /// use the invariant culture when converting their values to strings.
+ ///
+ BindAndGetAllAsInvariantText = BindAndGetAllAsText | BindInvariantText,
+
+ ///
+ /// When binding parameter values or returning column values, always
+ /// treat them as though they were plain text (i.e. no numeric,
+ /// date/time, or other conversions should be attempted) and always
+ /// use the invariant culture when converting their values to strings
+ /// or from strings.
+ ///
+ ConvertAndBindAndGetAllAsInvariantText = BindAndGetAllAsText |
+ ConvertAndBindInvariantText,
+
+ ///
+ /// Enables use of all per-connection value handling callbacks.
+ ///
+ UseConnectionAllValueCallbacks = UseConnectionBindValueCallbacks |
+ UseConnectionReadValueCallbacks,
+
+ ///
+ /// Enables use of all applicable
+ /// properties as fallbacks for the database type name.
+ ///
+ UseParameterAnythingForTypeName = UseParameterNameForTypeName |
+ UseParameterDbTypeForTypeName,
+
+ ///
+ /// Enable all logging.
+ ///
+#if INTEROP_VIRTUAL_TABLE
+ LogAll = LogPrepare | LogPreBind | LogBind |
+ LogCallbackException | LogBackup | LogModuleError |
+ LogModuleException,
+#else
+ LogAll = LogPrepare | LogPreBind | LogBind |
+ LogCallbackException | LogBackup,
+#endif
+
+ ///
+ /// The default logging related flags for new connections.
+ ///
+#if INTEROP_VIRTUAL_TABLE
+ LogDefault = LogCallbackException | LogModuleException,
+#else
+ LogDefault = LogCallbackException,
+#endif
+
+ ///
+ /// The default extra flags for new connections.
+ ///
+ Default = LogDefault | BindInvariantDecimal | GetInvariantDecimal,
+
+ ///
+ /// The default extra flags for new connections with all logging enabled.
+ ///
+ DefaultAndLogAll = Default | LogAll
+ }
+
+ ///
+ /// These are the supported status parameters for use with the native
+ /// SQLite library.
+ ///
+ internal enum SQLiteStatusOpsEnum
+ {
+ ///
+ /// This parameter returns the number of lookaside memory slots
+ /// currently checked out.
+ ///
+ SQLITE_DBSTATUS_LOOKASIDE_USED = 0,
+
+ ///
+ /// This parameter returns the approximate number of bytes of
+ /// heap memory used by all pager caches associated with the
+ /// database connection. The highwater mark associated with
+ /// SQLITE_DBSTATUS_CACHE_USED is always 0.
+ ///
+ SQLITE_DBSTATUS_CACHE_USED = 1,
+
+ ///
+ /// This parameter returns the approximate number of bytes of
+ /// heap memory used to store the schema for all databases
+ /// associated with the connection - main, temp, and any ATTACH-ed
+ /// databases. The full amount of memory used by the schemas is
+ /// reported, even if the schema memory is shared with other
+ /// database connections due to shared cache mode being enabled.
+ /// The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED
+ /// is always 0.
+ ///
+ SQLITE_DBSTATUS_SCHEMA_USED = 2,
+
+ ///
+ /// This parameter returns the number malloc attempts that might
+ /// have been satisfied using lookaside memory but failed due to
+ /// all lookaside memory already being in use. Only the high-water
+ /// value is meaningful; the current value is always zero.
+ ///
+ SQLITE_DBSTATUS_STMT_USED = 3,
+
+ ///
+ /// This parameter returns the number malloc attempts that were
+ /// satisfied using lookaside memory. Only the high-water value
+ /// is meaningful; the current value is always zero.
+ ///
+ SQLITE_DBSTATUS_LOOKASIDE_HIT = 4,
+
+ ///
+ /// This parameter returns the number malloc attempts that might
+ /// have been satisfied using lookaside memory but failed due to
+ /// the amount of memory requested being larger than the lookaside
+ /// slot size. Only the high-water value is meaningful; the current
+ /// value is always zero.
+ ///
+ SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE = 5,
+
+ ///
+ /// This parameter returns the number malloc attempts that might
+ /// have been satisfied using lookaside memory but failed due to
+ /// the amount of memory requested being larger than the lookaside
+ /// slot size. Only the high-water value is meaningful; the current
+ /// value is always zero.
+ ///
+ SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL = 6,
+
+ ///
+ /// This parameter returns the number of pager cache hits that
+ /// have occurred. The highwater mark associated with
+ /// SQLITE_DBSTATUS_CACHE_HIT is always 0.
+ ///
+ SQLITE_DBSTATUS_CACHE_HIT = 7,
+
+ ///
+ /// This parameter returns the number of pager cache misses that
+ /// have occurred. The highwater mark associated with
+ /// SQLITE_DBSTATUS_CACHE_MISS is always 0.
+ ///
+ SQLITE_DBSTATUS_CACHE_MISS = 8,
+
+ ///
+ /// This parameter returns the number of dirty cache entries that
+ /// have been written to disk. Specifically, the number of pages
+ /// written to the wal file in wal mode databases, or the number
+ /// of pages written to the database file in rollback mode
+ /// databases. Any pages written as part of transaction rollback
+ /// or database recovery operations are not included. If an IO or
+ /// other error occurs while writing a page to disk, the effect
+ /// on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is
+ /// undefined. The highwater mark associated with
+ /// SQLITE_DBSTATUS_CACHE_WRITE is always 0.
+ ///
+ SQLITE_DBSTATUS_CACHE_WRITE = 9,
+
+ ///
+ /// This parameter returns zero for the current value if and only
+ /// if all foreign key constraints (deferred or immediate) have
+ /// been resolved. The highwater mark is always 0.
+ ///
+ SQLITE_DBSTATUS_DEFERRED_FKS = 10,
+
+ ///
+ /// This parameter is similar to DBSTATUS_CACHE_USED, except that
+ /// if a pager cache is shared between two or more connections the
+ /// bytes of heap memory used by that pager cache is divided evenly
+ /// between the attached connections. In other words, if none of
+ /// the pager caches associated with the database connection are
+ /// shared, this request returns the same value as DBSTATUS_CACHE_USED.
+ /// Or, if one or more or the pager caches are shared, the value
+ /// returned by this call will be smaller than that returned by
+ /// DBSTATUS_CACHE_USED. The highwater mark associated with
+ /// SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.
+ ///
+ SQLITE_DBSTATUS_CACHE_USED_SHARED = 11
+ }
+
+ ///
+ /// These are the supported configuration verbs for use with the native
+ /// SQLite library. They are used with the
+ /// method.
+ ///
+ public enum SQLiteConfigDbOpsEnum
+ {
+ ///
+ /// This value represents an unknown (or invalid) option, do not use it.
+ ///
+ SQLITE_DBCONFIG_NONE = 0, // nil
+
+ ///
+ /// This option is used to change the name of the "main" database
+ /// schema. The sole argument is a pointer to a constant UTF8 string
+ /// which will become the new schema name in place of "main".
+ ///
+ SQLITE_DBCONFIG_MAINDBNAME = 1000, // char*
+
+ ///
+ /// This option is used to configure the lookaside memory allocator.
+ /// The value must be an array with three elements. The second element
+ /// must be an containing the size of each buffer
+ /// slot. The third element must be an containing
+ /// the number of slots. The first element must be an
+ /// that points to a native memory buffer of bytes equal to or greater
+ /// than the product of the second and third element values.
+ ///
+ SQLITE_DBCONFIG_LOOKASIDE = 1001, // void* int int
+
+ ///
+ /// This option is used to enable or disable the enforcement of
+ /// foreign key constraints.
+ ///
+ SQLITE_DBCONFIG_ENABLE_FKEY = 1002, // int int*
+
+ ///
+ /// This option is used to enable or disable triggers.
+ ///
+ SQLITE_DBCONFIG_ENABLE_TRIGGER = 1003, // int int*
+
+ ///
+ /// This option is used to enable or disable the two-argument version
+ /// of the fts3_tokenizer() function which is part of the FTS3 full-text
+ /// search engine extension.
+ ///
+ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER = 1004, // int int*
+
+ ///
+ /// This option is used to enable or disable the loading of extensions.
+ ///
+ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION = 1005, // int int*
+
+ ///
+ /// This option is used to enable or disable the automatic checkpointing
+ /// when a WAL database is closed.
+ ///
+ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE = 1006, // int int*
+
+ ///
+ /// This option is used to enable or disable the query planner stability
+ /// guarantee (QPSG).
+ ///
+ SQLITE_DBCONFIG_ENABLE_QPSG = 1007, // int int*
+
+ ///
+ /// This option is used to enable or disable the extra EXPLAIN QUERY PLAN
+ /// output for trigger programs.
+ ///
+ SQLITE_DBCONFIG_TRIGGER_EQP = 1008, // int int*
+
+ ///
+ /// This option is used as part of the process to reset a database back
+ /// to an empty state. Because resetting a database is destructive and
+ /// irreversible, the process requires the use of this obscure flag and
+ /// multiple steps to help ensure that it does not happen by accident.
+ ///
+ SQLITE_DBCONFIG_RESET_DATABASE = 1009 // int int*
+ }
+
+ // These are the options to the internal sqlite3_config call.
+ internal enum SQLiteConfigOpsEnum
+ {
+ SQLITE_CONFIG_NONE = 0, // nil
+ SQLITE_CONFIG_SINGLETHREAD = 1, // nil
+ SQLITE_CONFIG_MULTITHREAD = 2, // nil
+ SQLITE_CONFIG_SERIALIZED = 3, // nil
+ SQLITE_CONFIG_MALLOC = 4, // sqlite3_mem_methods*
+ SQLITE_CONFIG_GETMALLOC = 5, // sqlite3_mem_methods*
+ SQLITE_CONFIG_SCRATCH = 6, // void*, int sz, int N
+ SQLITE_CONFIG_PAGECACHE = 7, // void*, int sz, int N
+ SQLITE_CONFIG_HEAP = 8, // void*, int nByte, int min
+ SQLITE_CONFIG_MEMSTATUS = 9, // boolean
+ SQLITE_CONFIG_MUTEX = 10, // sqlite3_mutex_methods*
+ SQLITE_CONFIG_GETMUTEX = 11, // sqlite3_mutex_methods*
+ // previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused
+ SQLITE_CONFIG_LOOKASIDE = 13, // int int
+ SQLITE_CONFIG_PCACHE = 14, // sqlite3_pcache_methods*
+ SQLITE_CONFIG_GETPCACHE = 15, // sqlite3_pcache_methods*
+ SQLITE_CONFIG_LOG = 16, // xFunc, void*
+ SQLITE_CONFIG_URI = 17, // int
+ SQLITE_CONFIG_PCACHE2 = 18, // sqlite3_pcache_methods2*
+ SQLITE_CONFIG_GETPCACHE2 = 19, // sqlite3_pcache_methods2*
+ SQLITE_CONFIG_COVERING_INDEX_SCAN = 20, // int
+ SQLITE_CONFIG_SQLLOG = 21, // xSqllog, void*
+ SQLITE_CONFIG_MMAP_SIZE = 22, // sqlite3_int64, sqlite3_int64
+ SQLITE_CONFIG_WIN32_HEAPSIZE = 23, // int nByte
+ SQLITE_CONFIG_PCACHE_HDRSZ = 24, // int *psz
+ SQLITE_CONFIG_PMASZ = 25 // unsigned int szPma
+ }
+
+ ///
+ /// These constants are used with the sqlite3_trace_v2() API and the
+ /// callbacks registered by it.
+ ///
+ [Flags()]
+ internal enum SQLiteTraceFlags
+ {
+ SQLITE_TRACE_NONE = 0x0, // nil
+ SQLITE_TRACE_STMT = 0x1, // pStmt, zSql
+ SQLITE_TRACE_PROFILE = 0x2, // pStmt, piNsec64
+ SQLITE_TRACE_ROW = 0x4, // pStmt
+ SQLITE_TRACE_CLOSE = 0x8 // pDb
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteBlob.cs b/Native.Csharp.Tool/SQLite/SQLiteBlob.cs
new file mode 100644
index 00000000..3dca96a5
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteBlob.cs
@@ -0,0 +1,410 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+
+ ///
+ /// Represents a single SQL blob in SQLite.
+ ///
+ public sealed class SQLiteBlob : IDisposable
+ {
+ #region Private Data
+ ///
+ /// The underlying SQLite object this blob is bound to.
+ ///
+ internal SQLiteBase _sql;
+
+ ///
+ /// The actual blob handle.
+ ///
+ internal SQLiteBlobHandle _sqlite_blob;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Initializes the blob.
+ ///
+ /// The base SQLite object.
+ /// The blob handle.
+ private SQLiteBlob(
+ SQLiteBase sqlbase,
+ SQLiteBlobHandle blob
+ )
+ {
+ _sql = sqlbase;
+ _sqlite_blob = blob;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Static "Factory" Methods
+ ///
+ /// Creates a object. This will not work
+ /// for tables that were created WITHOUT ROWID -OR- if the query
+ /// does not include the "rowid" column or one of its aliases -OR-
+ /// if the was not created with the
+ /// flag.
+ ///
+ ///
+ /// The instance with a result set
+ /// containing the desired blob column.
+ ///
+ ///
+ /// The index of the blob column.
+ ///
+ ///
+ /// Non-zero to open the blob object for read-only access.
+ ///
+ ///
+ /// The newly created instance -OR- null
+ /// if an error occurs.
+ ///
+ public static SQLiteBlob Create(
+ SQLiteDataReader dataReader,
+ int i,
+ bool readOnly
+ )
+ {
+ if (dataReader == null)
+ throw new ArgumentNullException("dataReader");
+
+ long? rowId = dataReader.GetRowId(i);
+
+ if (rowId == null)
+ throw new InvalidOperationException("No RowId is available");
+
+ return Create(
+ SQLiteDataReader.GetConnection(dataReader),
+ dataReader.GetDatabaseName(i), dataReader.GetTableName(i),
+ dataReader.GetName(i), (long)rowId, readOnly);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates a object. This will not work
+ /// for tables that were created WITHOUT ROWID.
+ ///
+ ///
+ /// The connection to use when opening the blob object.
+ ///
+ ///
+ /// The name of the database containing the blob object.
+ ///
+ ///
+ /// The name of the table containing the blob object.
+ ///
+ ///
+ /// The name of the column containing the blob object.
+ ///
+ ///
+ /// The integer identifier for the row associated with the desired
+ /// blob object.
+ ///
+ ///
+ /// Non-zero to open the blob object for read-only access.
+ ///
+ ///
+ /// The newly created instance -OR- null
+ /// if an error occurs.
+ ///
+ public static SQLiteBlob Create(
+ SQLiteConnection connection,
+ string databaseName,
+ string tableName,
+ string columnName,
+ long rowId,
+ bool readOnly
+ )
+ {
+ if (connection == null)
+ throw new ArgumentNullException("connection");
+
+ SQLite3 sqlite3 = connection._sql as SQLite3;
+
+ if (sqlite3 == null)
+ throw new InvalidOperationException("Connection has no wrapper");
+
+ SQLiteConnectionHandle handle = sqlite3._sql;
+
+ if (handle == null)
+ throw new InvalidOperationException("Connection has an invalid handle.");
+
+ SQLiteBlobHandle blob = null;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ IntPtr ptrBlob = IntPtr.Zero;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_blob_open(
+ handle, SQLiteConvert.ToUTF8(databaseName),
+ SQLiteConvert.ToUTF8(tableName), SQLiteConvert.ToUTF8(
+ columnName), rowId, readOnly ? 0 : 1, ref ptrBlob);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, null);
+
+ blob = new SQLiteBlobHandle(handle, ptrBlob);
+ }
+
+ SQLiteConnection.OnChanged(connection, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null, null,
+ null, null, blob, null, new object[] { typeof(SQLiteBlob),
+ databaseName, tableName, columnName, rowId, readOnly }));
+
+ return new SQLiteBlob(sqlite3, blob);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the blob object does not appear to be open.
+ ///
+ private void CheckOpen()
+ {
+ if (_sqlite_blob == IntPtr.Zero)
+ throw new InvalidOperationException("Blob is not open");
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Throws an exception if an invalid read/write parameter is detected.
+ ///
+ ///
+ /// When reading, this array will be populated with the bytes read from
+ /// the underlying database blob. When writing, this array contains new
+ /// values for the specified portion of the underlying database blob.
+ ///
+ ///
+ /// The number of bytes to read or write.
+ ///
+ ///
+ /// The byte offset, relative to the start of the underlying database
+ /// blob, where the read or write operation will begin.
+ ///
+ private void VerifyParameters(
+ byte[] buffer,
+ int count,
+ int offset
+ )
+ {
+ if (buffer == null)
+ throw new ArgumentNullException("buffer");
+
+ if (offset < 0)
+ throw new ArgumentException("Negative offset not allowed.");
+
+ if (count < 0)
+ throw new ArgumentException("Negative count not allowed.");
+
+ if (count > buffer.Length)
+ throw new ArgumentException("Buffer is too small.");
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Retargets this object to an underlying database blob for a
+ /// different row; the database, table, and column remain exactly
+ /// the same. If this operation fails for any reason, this blob
+ /// object is automatically disposed.
+ ///
+ ///
+ /// The integer identifier for the new row.
+ ///
+ public void Reopen(
+ long rowId
+ )
+ {
+ CheckDisposed();
+ CheckOpen();
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_blob_reopen(
+ _sqlite_blob, rowId);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ Dispose();
+ throw new SQLiteException(rc, null);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries the total number of bytes for the underlying database blob.
+ ///
+ ///
+ /// The total number of bytes for the underlying database blob.
+ ///
+ public int GetCount()
+ {
+ CheckDisposed();
+ CheckOpen();
+
+ return UnsafeNativeMethods.sqlite3_blob_bytes(_sqlite_blob);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Reads data from the underlying database blob.
+ ///
+ ///
+ /// This array will be populated with the bytes read from the
+ /// underlying database blob.
+ ///
+ ///
+ /// The number of bytes to read.
+ ///
+ ///
+ /// The byte offset, relative to the start of the underlying
+ /// database blob, where the read operation will begin.
+ ///
+ public void Read(
+ byte[] buffer,
+ int count,
+ int offset
+ )
+ {
+ CheckDisposed();
+ CheckOpen();
+ VerifyParameters(buffer, count, offset);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_blob_read(
+ _sqlite_blob, buffer, count, offset);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, null);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Writes data into the underlying database blob.
+ ///
+ ///
+ /// This array contains the new values for the specified portion of
+ /// the underlying database blob.
+ ///
+ ///
+ /// The number of bytes to write.
+ ///
+ ///
+ /// The byte offset, relative to the start of the underlying
+ /// database blob, where the write operation will begin.
+ ///
+ public void Write(
+ byte[] buffer,
+ int count,
+ int offset
+ )
+ {
+ CheckDisposed();
+ CheckOpen();
+ VerifyParameters(buffer, count, offset);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_blob_write(
+ _sqlite_blob, buffer, count, offset);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, null);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Closes the blob, freeing the associated resources.
+ ///
+ public void Close()
+ {
+ Dispose();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes and finalizes the blob.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteBlob).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (_sqlite_blob != null)
+ {
+ _sqlite_blob.Dispose();
+ _sqlite_blob = null;
+ }
+
+ _sql = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// The destructor.
+ ///
+ ~SQLiteBlob()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteCommand.cs b/Native.Csharp.Tool/SQLite/SQLiteCommand.cs
new file mode 100644
index 00000000..7c8d7c7a
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteCommand.cs
@@ -0,0 +1,1160 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+ using System.Diagnostics;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+
+ ///
+ /// SQLite implementation of DbCommand.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Designer("SQLite.Designer.SQLiteCommandDesigner, SQLite.Designer, Version=" + SQLite3.DesignerVersion + ", Culture=neutral, PublicKeyToken=db937bc2d44ff139"), ToolboxItem(true)]
+#endif
+ public sealed class SQLiteCommand : DbCommand, ICloneable
+ {
+ ///
+ /// The default connection string to be used when creating a temporary
+ /// connection to execute a command via the static
+ /// or
+ ///
+ /// methods.
+ ///
+ private static readonly string DefaultConnectionString = "Data Source=:memory:;";
+
+ ///
+ /// The command text this command is based on
+ ///
+ private string _commandText;
+ ///
+ /// The connection the command is associated with
+ ///
+ private SQLiteConnection _cnn;
+ ///
+ /// The version of the connection the command is associated with
+ ///
+ private int _version;
+ ///
+ /// Indicates whether or not a DataReader is active on the command.
+ ///
+ private WeakReference _activeReader;
+ ///
+ /// The timeout for the command, kludged because SQLite doesn't support per-command timeout values
+ ///
+ internal int _commandTimeout;
+ ///
+ /// Designer support
+ ///
+ private bool _designTimeVisible;
+ ///
+ /// Used by DbDataAdapter to determine updating behavior
+ ///
+ private UpdateRowSource _updateRowSource;
+ ///
+ /// The collection of parameters for the command
+ ///
+ private SQLiteParameterCollection _parameterCollection;
+ ///
+ /// The SQL command text, broken into individual SQL statements as they are executed
+ ///
+ internal List _statementList;
+ ///
+ /// Unprocessed SQL text that has not been executed
+ ///
+ internal string _remainingText;
+ ///
+ /// Transaction associated with this command
+ ///
+ private SQLiteTransaction _transaction;
+
+ ///
+ /// Constructs a new SQLiteCommand
+ ///
+ ///
+ /// Default constructor
+ ///
+ public SQLiteCommand() :this(null, null)
+ {
+ }
+
+ ///
+ /// Initializes the command with the given command text
+ ///
+ /// The SQL command text
+ public SQLiteCommand(string commandText)
+ : this(commandText, null, null)
+ {
+ }
+
+ ///
+ /// Initializes the command with the given SQL command text and attach the command to the specified
+ /// connection.
+ ///
+ /// The SQL command text
+ /// The connection to associate with the command
+ public SQLiteCommand(string commandText, SQLiteConnection connection)
+ : this(commandText, connection, null)
+ {
+ }
+
+ ///
+ /// Initializes the command and associates it with the specified connection.
+ ///
+ /// The connection to associate with the command
+ public SQLiteCommand(SQLiteConnection connection)
+ : this(null, connection, null)
+ {
+ }
+
+ private SQLiteCommand(SQLiteCommand source) : this(source.CommandText, source.Connection, source.Transaction)
+ {
+ CommandTimeout = source.CommandTimeout;
+ DesignTimeVisible = source.DesignTimeVisible;
+ UpdatedRowSource = source.UpdatedRowSource;
+
+ foreach (SQLiteParameter param in source._parameterCollection)
+ {
+ Parameters.Add(param.Clone());
+ }
+ }
+
+ ///
+ /// Initializes a command with the given SQL, connection and transaction
+ ///
+ /// The SQL command text
+ /// The connection to associate with the command
+ /// The transaction the command should be associated with
+ public SQLiteCommand(string commandText, SQLiteConnection connection, SQLiteTransaction transaction)
+ {
+ _commandTimeout = 30;
+ _parameterCollection = new SQLiteParameterCollection(this);
+ _designTimeVisible = true;
+ _updateRowSource = UpdateRowSource.None;
+
+ if (commandText != null)
+ CommandText = commandText;
+
+ if (connection != null)
+ {
+ DbConnection = connection;
+ _commandTimeout = connection.DefaultTimeout;
+ }
+
+ if (transaction != null)
+ Transaction = transaction;
+
+ SQLiteConnection.OnChanged(connection, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCommand, null, transaction, this,
+ null, null, null, null));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ [Conditional("CHECK_STATE")]
+ internal static void Check(SQLiteCommand command)
+ {
+ if (command == null)
+ throw new ArgumentNullException("command");
+
+ command.CheckDisposed();
+ SQLiteConnection.Check(command._cnn);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteCommand).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of the command and clears all member variables
+ ///
+ /// Whether or not the class is being explicitly or implicitly disposed
+ protected override void Dispose(bool disposing)
+ {
+ SQLiteConnection.OnChanged(_cnn, new ConnectionEventArgs(
+ SQLiteConnectionEventType.DisposingCommand, null, _transaction, this,
+ null, null, null, new object[] { disposing, disposed }));
+
+ bool skippedDispose = false;
+
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ // If a reader is active on this command, don't destroy the command, instead let the reader do it
+ SQLiteDataReader reader = null;
+ if (_activeReader != null)
+ {
+ try
+ {
+ reader = _activeReader.Target as SQLiteDataReader;
+ }
+ catch (InvalidOperationException)
+ {
+ }
+ }
+
+ if (reader != null)
+ {
+ reader._disposeCommand = true;
+ _activeReader = null;
+ skippedDispose = true;
+ return;
+ }
+
+ Connection = null;
+ _parameterCollection.Clear();
+ _commandText = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ if (!skippedDispose)
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method attempts to query the flags associated with the database
+ /// connection in use. If the database connection is disposed, the default
+ /// flags will be returned.
+ ///
+ ///
+ /// The command containing the databse connection to query the flags from.
+ ///
+ ///
+ /// The connection flags value.
+ ///
+ internal static SQLiteConnectionFlags GetFlags(
+ SQLiteCommand command
+ )
+ {
+ try
+ {
+ if (command != null)
+ {
+ SQLiteConnection cnn = command._cnn;
+
+ if (cnn != null)
+ return cnn.Flags;
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // do nothing.
+ }
+
+ return SQLiteConnectionFlags.Default;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void DisposeStatements()
+ {
+ if (_statementList == null) return;
+
+ int x = _statementList.Count;
+
+ for (int n = 0; n < x; n++)
+ {
+ SQLiteStatement stmt = _statementList[n];
+ if (stmt == null) continue;
+ stmt.Dispose();
+ }
+
+ _statementList = null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void ClearDataReader()
+ {
+ if (_activeReader != null)
+ {
+ SQLiteDataReader reader = null;
+
+ try
+ {
+ reader = _activeReader.Target as SQLiteDataReader;
+ }
+ catch(InvalidOperationException)
+ {
+ // do nothing.
+ }
+
+ if (reader != null)
+ reader.Close(); /* Dispose */
+
+ _activeReader = null;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Clears and destroys all statements currently prepared
+ ///
+ internal void ClearCommands()
+ {
+ ClearDataReader();
+ DisposeStatements();
+
+ _parameterCollection.Unbind();
+ }
+
+ ///
+ /// Builds an array of prepared statements for each complete SQL statement in the command text
+ ///
+ internal SQLiteStatement BuildNextCommand()
+ {
+ SQLiteStatement stmt = null;
+
+ try
+ {
+ if ((_cnn != null) && (_cnn._sql != null))
+ {
+ if (_statementList == null)
+ _remainingText = _commandText;
+
+ stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), ref _remainingText);
+
+ if (stmt != null)
+ {
+ stmt._command = this;
+
+ if (_statementList == null)
+ _statementList = new List();
+
+ _statementList.Add(stmt);
+
+ _parameterCollection.MapParameters(stmt);
+ stmt.BindParameters();
+ }
+ }
+ return stmt;
+ }
+ catch (Exception)
+ {
+ if (stmt != null)
+ {
+ if ((_statementList != null) && _statementList.Contains(stmt))
+ _statementList.Remove(stmt);
+
+ stmt.Dispose();
+ }
+
+ // If we threw an error compiling the statement, we cannot continue on so set the remaining text to null.
+ _remainingText = null;
+
+ throw;
+ }
+ }
+
+ internal SQLiteStatement GetStatement(int index)
+ {
+ // Haven't built any statements yet
+ if (_statementList == null) return BuildNextCommand();
+
+ // If we're at the last built statement and want the next unbuilt statement, then build it
+ if (index == _statementList.Count)
+ {
+ if (String.IsNullOrEmpty(_remainingText) == false) return BuildNextCommand();
+ else return null; // No more commands
+ }
+
+ SQLiteStatement stmt = _statementList[index];
+ stmt.BindParameters();
+
+ return stmt;
+ }
+
+ ///
+ /// Not implemented
+ ///
+ public override void Cancel()
+ {
+ CheckDisposed();
+
+ if (_activeReader != null)
+ {
+ SQLiteDataReader reader = _activeReader.Target as SQLiteDataReader;
+ if (reader != null)
+ reader.Cancel();
+ }
+ }
+
+ ///
+ /// The SQL command text associated with the command
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue(""), RefreshProperties(RefreshProperties.All), Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public override string CommandText
+ {
+ get
+ {
+ CheckDisposed();
+
+ return _commandText;
+ }
+ set
+ {
+ CheckDisposed();
+
+ if (_commandText == value) return;
+
+ if (_activeReader != null && _activeReader.IsAlive)
+ {
+ throw new InvalidOperationException("Cannot set CommandText while a DataReader is active");
+ }
+
+ ClearCommands();
+ _commandText = value;
+
+ if (_cnn == null) return;
+ }
+ }
+
+ ///
+ /// The amount of time to wait for the connection to become available before erroring out
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((int)30)]
+#endif
+ public override int CommandTimeout
+ {
+ get
+ {
+ CheckDisposed();
+ return _commandTimeout;
+ }
+ set
+ {
+ CheckDisposed();
+ _commandTimeout = value;
+ }
+ }
+
+ ///
+ /// The type of the command. SQLite only supports CommandType.Text
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [RefreshProperties(RefreshProperties.All), DefaultValue(CommandType.Text)]
+#endif
+ public override CommandType CommandType
+ {
+ get
+ {
+ CheckDisposed();
+ return CommandType.Text;
+ }
+ set
+ {
+ CheckDisposed();
+
+ if (value != CommandType.Text)
+ {
+ throw new NotSupportedException();
+ }
+ }
+ }
+
+ ///
+ /// Forwards to the local CreateParameter() function
+ ///
+ ///
+ protected override DbParameter CreateDbParameter()
+ {
+ return CreateParameter();
+ }
+
+ ///
+ /// Create a new parameter
+ ///
+ ///
+ public new SQLiteParameter CreateParameter()
+ {
+ CheckDisposed();
+ return new SQLiteParameter(this);
+ }
+
+ ///
+ /// The connection associated with this command
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public new SQLiteConnection Connection
+ {
+ get { CheckDisposed(); return _cnn; }
+ set
+ {
+ CheckDisposed();
+
+ if (_activeReader != null && _activeReader.IsAlive)
+ throw new InvalidOperationException("Cannot set Connection while a DataReader is active");
+
+ if (_cnn != null)
+ {
+ ClearCommands();
+ //_cnn.RemoveCommand(this);
+ }
+
+ _cnn = value;
+ if (_cnn != null)
+ _version = _cnn._version;
+
+ //if (_cnn != null)
+ // _cnn.AddCommand(this);
+ }
+ }
+
+ ///
+ /// Forwards to the local Connection property
+ ///
+ protected override DbConnection DbConnection
+ {
+ get
+ {
+ return Connection;
+ }
+ set
+ {
+ Connection = (SQLiteConnection)value;
+ }
+ }
+
+ ///
+ /// Returns the SQLiteParameterCollection for the given command
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
+#endif
+ public new SQLiteParameterCollection Parameters
+ {
+ get { CheckDisposed(); return _parameterCollection; }
+ }
+
+ ///
+ /// Forwards to the local Parameters property
+ ///
+ protected override DbParameterCollection DbParameterCollection
+ {
+ get
+ {
+ return Parameters;
+ }
+ }
+
+ ///
+ /// The transaction associated with this command. SQLite only supports one transaction per connection, so this property forwards to the
+ /// command's underlying connection.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public new SQLiteTransaction Transaction
+ {
+ get { CheckDisposed(); return _transaction; }
+ set
+ {
+ CheckDisposed();
+
+ if (_cnn != null)
+ {
+ if (_activeReader != null && _activeReader.IsAlive)
+ throw new InvalidOperationException("Cannot set Transaction while a DataReader is active");
+
+ if (value != null)
+ {
+ if (value._cnn != _cnn)
+ throw new ArgumentException("Transaction is not associated with the command's connection");
+ }
+ _transaction = value;
+ }
+ else
+ {
+ if (value != null) Connection = value.Connection;
+ _transaction = value;
+ }
+ }
+ }
+
+ ///
+ /// Forwards to the local Transaction property
+ ///
+ protected override DbTransaction DbTransaction
+ {
+ get
+ {
+ return Transaction;
+ }
+ set
+ {
+ Transaction = (SQLiteTransaction)value;
+ }
+ }
+
+ ///
+ /// Verifies that all SQL queries associated with the current command text
+ /// can be successfully compiled. A will be
+ /// raised if any errors occur.
+ ///
+ public void VerifyOnly()
+ {
+ CheckDisposed();
+
+ SQLiteConnection connection = _cnn;
+ SQLiteConnection.Check(connection); /* throw */
+ SQLiteBase sqlBase = connection._sql;
+
+ if ((connection == null) || (sqlBase == null))
+ throw new SQLiteException("invalid or unusable connection");
+
+ List statements = null;
+ SQLiteStatement currentStatement = null;
+
+ try
+ {
+ string text = _commandText;
+ uint timeout = (uint)(_commandTimeout * 1000);
+ SQLiteStatement previousStatement = null;
+
+ while ((text != null) && (text.Length > 0))
+ {
+ currentStatement = sqlBase.Prepare(
+ connection, text, previousStatement, timeout,
+ ref text); /* throw */
+
+ previousStatement = currentStatement;
+
+ if (currentStatement != null)
+ {
+ if (statements == null)
+ statements = new List();
+
+ statements.Add(currentStatement);
+ currentStatement = null;
+ }
+
+ if (text == null) continue;
+ text = text.Trim();
+ }
+ }
+ finally
+ {
+ if (currentStatement != null)
+ {
+ currentStatement.Dispose();
+ currentStatement = null;
+ }
+
+ if (statements != null)
+ {
+ foreach (SQLiteStatement statement in statements)
+ {
+ if (statement == null)
+ continue;
+
+ statement.Dispose();
+ }
+
+ statements.Clear();
+ statements = null;
+ }
+ }
+ }
+
+ ///
+ /// This function ensures there are no active readers, that we have a valid connection,
+ /// that the connection is open, that all statements are prepared and all parameters are assigned
+ /// in preparation for allocating a data reader.
+ ///
+ private void InitializeForReader()
+ {
+ if (_activeReader != null && _activeReader.IsAlive)
+ throw new InvalidOperationException("DataReader already active on this command");
+
+ if (_cnn == null)
+ throw new InvalidOperationException("No connection associated with this command");
+
+ if (_cnn.State != ConnectionState.Open)
+ throw new InvalidOperationException("Database is not open");
+
+ // If the version of the connection has changed, clear out any previous commands before starting
+ if (_cnn._version != _version)
+ {
+ _version = _cnn._version;
+ ClearCommands();
+ }
+
+ // Map all parameters for statements already built
+ _parameterCollection.MapParameters(null);
+
+ //// Set the default command timeout
+ //_cnn._sql.SetTimeout(_commandTimeout * 1000);
+ }
+
+ ///
+ /// Creates a new SQLiteDataReader to execute/iterate the array of SQLite prepared statements
+ ///
+ /// The behavior the data reader should adopt
+ /// Returns a SQLiteDataReader object
+ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
+ {
+ return ExecuteReader(behavior);
+ }
+
+ ///
+ /// This method creates a new connection, executes the query using the given
+ /// execution type, closes the connection, and returns the results. If the
+ /// connection string is null, a temporary in-memory database connection will
+ /// be used.
+ ///
+ ///
+ /// The text of the command to be executed.
+ ///
+ ///
+ /// The execution type for the command. This is used to determine which method
+ /// of the command object to call, which then determines the type of results
+ /// returned, if any.
+ ///
+ ///
+ /// The connection string to the database to be opened, used, and closed. If
+ /// this parameter is null, a temporary in-memory databse will be used.
+ ///
+ ///
+ /// The SQL parameter values to be used when building the command object to be
+ /// executed, if any.
+ ///
+ ///
+ /// The results of the query -OR- null if no results were produced from the
+ /// given execution type.
+ ///
+ public static object Execute(
+ string commandText,
+ SQLiteExecuteType executeType,
+ string connectionString,
+ params object[] args
+ )
+ {
+ return Execute(
+ commandText, executeType, CommandBehavior.Default,
+ connectionString, args);
+ }
+
+ ///
+ /// This method creates a new connection, executes the query using the given
+ /// execution type and command behavior, closes the connection unless a data
+ /// reader is created, and returns the results. If the connection string is
+ /// null, a temporary in-memory database connection will be used.
+ ///
+ ///
+ /// The text of the command to be executed.
+ ///
+ ///
+ /// The execution type for the command. This is used to determine which method
+ /// of the command object to call, which then determines the type of results
+ /// returned, if any.
+ ///
+ ///
+ /// The command behavior flags for the command.
+ ///
+ ///
+ /// The connection string to the database to be opened, used, and closed. If
+ /// this parameter is null, a temporary in-memory databse will be used.
+ ///
+ ///
+ /// The SQL parameter values to be used when building the command object to be
+ /// executed, if any.
+ ///
+ ///
+ /// The results of the query -OR- null if no results were produced from the
+ /// given execution type.
+ ///
+ public static object Execute(
+ string commandText,
+ SQLiteExecuteType executeType,
+ CommandBehavior commandBehavior,
+ string connectionString,
+ params object[] args
+ )
+ {
+ SQLiteConnection connection = null;
+
+ try
+ {
+ if (connectionString == null)
+ connectionString = DefaultConnectionString;
+
+ using (connection = new SQLiteConnection(connectionString))
+ {
+ connection.Open();
+
+ using (SQLiteCommand command = connection.CreateCommand())
+ {
+ command.CommandText = commandText;
+
+ if (args != null)
+ {
+ foreach (object arg in args)
+ {
+ SQLiteParameter parameter = arg as SQLiteParameter;
+
+ if (parameter == null)
+ {
+ parameter = command.CreateParameter();
+ parameter.DbType = DbType.Object;
+ parameter.Value = arg;
+ }
+
+ command.Parameters.Add(parameter);
+ }
+ }
+
+ switch (executeType)
+ {
+ case SQLiteExecuteType.None:
+ {
+ //
+ // NOTE: Do nothing.
+ //
+ break;
+ }
+ case SQLiteExecuteType.NonQuery:
+ {
+ return command.ExecuteNonQuery(commandBehavior);
+ }
+ case SQLiteExecuteType.Scalar:
+ {
+ return command.ExecuteScalar(commandBehavior);
+ }
+ case SQLiteExecuteType.Reader:
+ {
+ bool success = true;
+
+ try
+ {
+ //
+ // NOTE: The CloseConnection flag is being added here.
+ // This should force the returned data reader to
+ // close the connection when it is disposed. In
+ // order to prevent the containing using block
+ // from disposing the connection prematurely,
+ // the innermost finally block sets the internal
+ // no-disposal flag to true. The outer finally
+ // block will reset the internal no-disposal flag
+ // to false so that the data reader will be able
+ // to (eventually) dispose of the connection.
+ //
+ return command.ExecuteReader(
+ commandBehavior | CommandBehavior.CloseConnection);
+ }
+ catch
+ {
+ success = false;
+ throw;
+ }
+ finally
+ {
+ //
+ // NOTE: If an exception was not thrown, that can only
+ // mean the data reader was successfully created
+ // and now owns the connection. Therefore, set
+ // the internal no-disposal flag (temporarily)
+ // in order to exit the containing using block
+ // without disposing it.
+ //
+ if (success)
+ connection._noDispose = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Now that the using block has been exited, reset the
+ // internal disposal flag for the connection. This is
+ // always done if the connection was created because
+ // it will be harmless whether or not the data reader
+ // now owns it.
+ //
+ if (connection != null)
+ connection._noDispose = false;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Overrides the default behavior to return a SQLiteDataReader specialization class
+ ///
+ /// The flags to be associated with the reader.
+ /// A SQLiteDataReader
+ public new SQLiteDataReader ExecuteReader(CommandBehavior behavior)
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ InitializeForReader();
+
+ SQLiteDataReader rd = new SQLiteDataReader(this, behavior);
+ _activeReader = new WeakReference(rd, false);
+
+ return rd;
+ }
+
+ ///
+ /// Overrides the default behavior of DbDataReader to return a specialized SQLiteDataReader class
+ ///
+ /// A SQLiteDataReader
+ public new SQLiteDataReader ExecuteReader()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ return ExecuteReader(CommandBehavior.Default);
+ }
+
+ ///
+ /// Called by the SQLiteDataReader when the data reader is closed.
+ ///
+ internal void ResetDataReader()
+ {
+ _activeReader = null;
+ }
+
+ ///
+ /// Execute the command and return the number of rows inserted/updated affected by it.
+ ///
+ /// The number of rows inserted/updated affected by it.
+ public override int ExecuteNonQuery()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ return ExecuteNonQuery(CommandBehavior.Default);
+ }
+
+ ///
+ /// Execute the command and return the number of rows inserted/updated affected by it.
+ ///
+ /// The flags to be associated with the reader.
+ /// The number of rows inserted/updated affected by it.
+ public int ExecuteNonQuery(
+ CommandBehavior behavior
+ )
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+
+ using (SQLiteDataReader reader = ExecuteReader(behavior |
+ CommandBehavior.SingleRow | CommandBehavior.SingleResult))
+ {
+ while (reader.NextResult()) ;
+ return reader.RecordsAffected;
+ }
+ }
+
+ ///
+ /// Execute the command and return the first column of the first row of the resultset
+ /// (if present), or null if no resultset was returned.
+ ///
+ /// The first column of the first row of the first resultset from the query.
+ public override object ExecuteScalar()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ return ExecuteScalar(CommandBehavior.Default);
+ }
+
+ ///
+ /// Execute the command and return the first column of the first row of the resultset
+ /// (if present), or null if no resultset was returned.
+ ///
+ /// The flags to be associated with the reader.
+ /// The first column of the first row of the first resultset from the query.
+ public object ExecuteScalar(
+ CommandBehavior behavior
+ )
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+
+ using (SQLiteDataReader reader = ExecuteReader(behavior |
+ CommandBehavior.SingleRow | CommandBehavior.SingleResult))
+ {
+ if (reader.Read() && (reader.FieldCount > 0))
+ return reader[0];
+ }
+ return null;
+ }
+
+ ///
+ /// This method resets all the prepared statements held by this instance
+ /// back to their initial states, ready to be re-executed.
+ ///
+ public void Reset()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+
+ Reset(true, false);
+ }
+
+ ///
+ /// This method resets all the prepared statements held by this instance
+ /// back to their initial states, ready to be re-executed.
+ ///
+ ///
+ /// Non-zero if the parameter bindings should be cleared as well.
+ ///
+ ///
+ /// If this is zero, a may be thrown for
+ /// any unsuccessful return codes from the native library; otherwise, a
+ /// will only be thrown if the connection
+ /// or its state is invalid.
+ ///
+ public void Reset(
+ bool clearBindings,
+ bool ignoreErrors
+ )
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+
+ if (clearBindings && (_parameterCollection != null))
+ _parameterCollection.Unbind();
+
+ ClearDataReader();
+
+ if (_statementList == null)
+ return;
+
+ SQLiteBase sqlBase = _cnn._sql;
+ SQLiteErrorCode rc;
+
+ foreach (SQLiteStatement item in _statementList)
+ {
+ if (item == null)
+ continue;
+
+ SQLiteStatementHandle stmt = item._sqlite_stmt;
+
+ if (stmt == null)
+ continue;
+
+ rc = sqlBase.Reset(item);
+
+ if ((rc == SQLiteErrorCode.Ok) && clearBindings &&
+ (SQLite3.SQLiteVersionNumber >= 3003007))
+ {
+ rc = UnsafeNativeMethods.sqlite3_clear_bindings(stmt);
+ }
+
+ if (!ignoreErrors && (rc != SQLiteErrorCode.Ok))
+ throw new SQLiteException(rc, sqlBase.GetLastError());
+ }
+ }
+
+ ///
+ /// Does nothing. Commands are prepared as they are executed the first time, and kept in prepared state afterwards.
+ ///
+ public override void Prepare()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ }
+
+ ///
+ /// Sets the method the SQLiteCommandBuilder uses to determine how to update inserted or updated rows in a DataTable.
+ ///
+ [DefaultValue(UpdateRowSource.None)]
+ public override UpdateRowSource UpdatedRowSource
+ {
+ get
+ {
+ CheckDisposed();
+ return _updateRowSource;
+ }
+ set
+ {
+ CheckDisposed();
+ _updateRowSource = value;
+ }
+ }
+
+ ///
+ /// Determines if the command is visible at design time. Defaults to True.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DesignOnly(true), Browsable(false), DefaultValue(true), EditorBrowsable(EditorBrowsableState.Never)]
+#endif
+ public override bool DesignTimeVisible
+ {
+ get
+ {
+ CheckDisposed();
+ return _designTimeVisible;
+ }
+ set
+ {
+ CheckDisposed();
+
+ _designTimeVisible = value;
+#if !PLATFORM_COMPACTFRAMEWORK
+ TypeDescriptor.Refresh(this);
+#endif
+ }
+ }
+
+ ///
+ /// Clones a command, including all its parameters
+ ///
+ /// A new SQLiteCommand with the same commandtext, connection and parameters
+ public object Clone()
+ {
+ CheckDisposed();
+ return new SQLiteCommand(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Native.Csharp.Tool/SQLite/SQLiteCommandBuilder.cs b/Native.Csharp.Tool/SQLite/SQLiteCommandBuilder.cs
new file mode 100644
index 00000000..8dd35c09
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteCommandBuilder.cs
@@ -0,0 +1,415 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+ using System.Globalization;
+ using System.ComponentModel;
+
+ ///
+ /// SQLite implementation of DbCommandBuilder.
+ ///
+ public sealed class SQLiteCommandBuilder : DbCommandBuilder
+ {
+ ///
+ /// Default constructor
+ ///
+ public SQLiteCommandBuilder() : this(null)
+ {
+ }
+
+ ///
+ /// Initializes the command builder and associates it with the specified data adapter.
+ ///
+ ///
+ public SQLiteCommandBuilder(SQLiteDataAdapter adp)
+ {
+ QuotePrefix = "[";
+ QuoteSuffix = "]";
+ DataAdapter = adp;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteCommandBuilder).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Cleans up resources (native and managed) associated with the current instance.
+ ///
+ ///
+ /// Zero when being disposed via garbage collection; otherwise, non-zero.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Minimal amount of parameter processing. Primarily sets the DbType for the parameter equal to the provider type in the schema
+ ///
+ /// The parameter to use in applying custom behaviors to a row
+ /// The row to apply the parameter to
+ /// The type of statement
+ /// Whether the application of the parameter is part of a WHERE clause
+ protected override void ApplyParameterInfo(DbParameter parameter, DataRow row, StatementType statementType, bool whereClause)
+ {
+ SQLiteParameter param = (SQLiteParameter)parameter;
+ param.DbType = (DbType)row[SchemaTableColumn.ProviderType];
+ }
+
+ ///
+ /// Returns a valid named parameter
+ ///
+ /// The name of the parameter
+ /// Error
+ protected override string GetParameterName(string parameterName)
+ {
+ return HelperMethods.StringFormat(CultureInfo.InvariantCulture, "@{0}", parameterName);
+ }
+
+ ///
+ /// Returns a named parameter for the given ordinal
+ ///
+ /// The i of the parameter
+ /// Error
+ protected override string GetParameterName(int parameterOrdinal)
+ {
+ return HelperMethods.StringFormat(CultureInfo.InvariantCulture, "@param{0}", parameterOrdinal);
+ }
+
+ ///
+ /// Returns a placeholder character for the specified parameter i.
+ ///
+ /// The index of the parameter to provide a placeholder for
+ /// Returns a named parameter
+ protected override string GetParameterPlaceholder(int parameterOrdinal)
+ {
+ return GetParameterName(parameterOrdinal);
+ }
+
+ ///
+ /// Sets the handler for receiving row updating events. Used by the DbCommandBuilder to autogenerate SQL
+ /// statements that may not have previously been generated.
+ ///
+ /// A data adapter to receive events on.
+ protected override void SetRowUpdatingHandler(DbDataAdapter adapter)
+ {
+ if (adapter == base.DataAdapter)
+ {
+ ((SQLiteDataAdapter)adapter).RowUpdating -= new EventHandler(RowUpdatingEventHandler);
+ }
+ else
+ {
+ ((SQLiteDataAdapter)adapter).RowUpdating += new EventHandler(RowUpdatingEventHandler);
+ }
+ }
+
+ private void RowUpdatingEventHandler(object sender, RowUpdatingEventArgs e)
+ {
+ base.RowUpdatingHandler(e);
+ }
+
+ ///
+ /// Gets/sets the DataAdapter for this CommandBuilder
+ ///
+ public new SQLiteDataAdapter DataAdapter
+ {
+ get { CheckDisposed(); return (SQLiteDataAdapter)base.DataAdapter; }
+ set { CheckDisposed(); base.DataAdapter = value; }
+ }
+
+ ///
+ /// Returns the automatically-generated SQLite command to delete rows from the database
+ ///
+ ///
+ public new SQLiteCommand GetDeleteCommand()
+ {
+ CheckDisposed();
+ return (SQLiteCommand)base.GetDeleteCommand();
+ }
+
+ ///
+ /// Returns the automatically-generated SQLite command to delete rows from the database
+ ///
+ ///
+ ///
+ public new SQLiteCommand GetDeleteCommand(bool useColumnsForParameterNames)
+ {
+ CheckDisposed();
+ return (SQLiteCommand)base.GetDeleteCommand(useColumnsForParameterNames);
+ }
+
+ ///
+ /// Returns the automatically-generated SQLite command to update rows in the database
+ ///
+ ///
+ public new SQLiteCommand GetUpdateCommand()
+ {
+ CheckDisposed();
+ return (SQLiteCommand)base.GetUpdateCommand();
+ }
+
+ ///
+ /// Returns the automatically-generated SQLite command to update rows in the database
+ ///
+ ///
+ ///
+ public new SQLiteCommand GetUpdateCommand(bool useColumnsForParameterNames)
+ {
+ CheckDisposed();
+ return (SQLiteCommand)base.GetUpdateCommand(useColumnsForParameterNames);
+ }
+
+ ///
+ /// Returns the automatically-generated SQLite command to insert rows into the database
+ ///
+ ///
+ public new SQLiteCommand GetInsertCommand()
+ {
+ CheckDisposed();
+ return (SQLiteCommand)base.GetInsertCommand();
+ }
+
+ ///
+ /// Returns the automatically-generated SQLite command to insert rows into the database
+ ///
+ ///
+ ///
+ public new SQLiteCommand GetInsertCommand(bool useColumnsForParameterNames)
+ {
+ CheckDisposed();
+ return (SQLiteCommand)base.GetInsertCommand(useColumnsForParameterNames);
+ }
+
+ ///
+ /// Overridden to hide its property from the designer
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false)]
+#endif
+ public override CatalogLocation CatalogLocation
+ {
+ get
+ {
+ CheckDisposed();
+ return base.CatalogLocation;
+ }
+ set
+ {
+ CheckDisposed();
+ base.CatalogLocation = value;
+ }
+ }
+
+ ///
+ /// Overridden to hide its property from the designer
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false)]
+#endif
+ public override string CatalogSeparator
+ {
+ get
+ {
+ CheckDisposed();
+ return base.CatalogSeparator;
+ }
+ set
+ {
+ CheckDisposed();
+ base.CatalogSeparator = value;
+ }
+ }
+
+ ///
+ /// Overridden to hide its property from the designer
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false)]
+#endif
+ [DefaultValue("[")]
+ public override string QuotePrefix
+ {
+ get
+ {
+ CheckDisposed();
+ return base.QuotePrefix;
+ }
+ set
+ {
+ CheckDisposed();
+ base.QuotePrefix = value;
+ }
+ }
+
+ ///
+ /// Overridden to hide its property from the designer
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false)]
+#endif
+ public override string QuoteSuffix
+ {
+ get
+ {
+ CheckDisposed();
+ return base.QuoteSuffix;
+ }
+ set
+ {
+ CheckDisposed();
+ base.QuoteSuffix = value;
+ }
+ }
+
+ ///
+ /// Places brackets around an identifier
+ ///
+ /// The identifier to quote
+ /// The bracketed identifier
+ public override string QuoteIdentifier(string unquotedIdentifier)
+ {
+ CheckDisposed();
+
+ if (String.IsNullOrEmpty(QuotePrefix)
+ || String.IsNullOrEmpty(QuoteSuffix)
+ || String.IsNullOrEmpty(unquotedIdentifier))
+ return unquotedIdentifier;
+
+ return QuotePrefix + unquotedIdentifier.Replace(QuoteSuffix, QuoteSuffix + QuoteSuffix) + QuoteSuffix;
+ }
+
+ ///
+ /// Removes brackets around an identifier
+ ///
+ /// The quoted (bracketed) identifier
+ /// The undecorated identifier
+ public override string UnquoteIdentifier(string quotedIdentifier)
+ {
+ CheckDisposed();
+
+ if (String.IsNullOrEmpty(QuotePrefix)
+ || String.IsNullOrEmpty(QuoteSuffix)
+ || String.IsNullOrEmpty(quotedIdentifier))
+ return quotedIdentifier;
+
+ if (quotedIdentifier.StartsWith(QuotePrefix, StringComparison.OrdinalIgnoreCase) == false
+ || quotedIdentifier.EndsWith(QuoteSuffix, StringComparison.OrdinalIgnoreCase) == false)
+ return quotedIdentifier;
+
+ return quotedIdentifier.Substring(QuotePrefix.Length, quotedIdentifier.Length - (QuotePrefix.Length + QuoteSuffix.Length)).Replace(QuoteSuffix + QuoteSuffix, QuoteSuffix);
+ }
+
+ ///
+ /// Overridden to hide its property from the designer
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false)]
+#endif
+ public override string SchemaSeparator
+ {
+ get
+ {
+ CheckDisposed();
+ return base.SchemaSeparator;
+ }
+ set
+ {
+ CheckDisposed();
+ base.SchemaSeparator = value;
+ }
+ }
+
+ ///
+ /// Override helper, which can help the base command builder choose the right keys for the given query
+ ///
+ ///
+ ///
+ protected override DataTable GetSchemaTable(DbCommand sourceCommand)
+ {
+ using (IDataReader reader = sourceCommand.ExecuteReader(CommandBehavior.KeyInfo | CommandBehavior.SchemaOnly))
+ {
+ DataTable schema = reader.GetSchemaTable();
+
+ // If the query contains a primary key, turn off the IsUnique property
+ // for all the non-key columns
+ if (HasSchemaPrimaryKey(schema))
+ ResetIsUniqueSchemaColumn(schema);
+
+ // if table has no primary key we use unique columns as a fall back
+ return schema;
+ }
+ }
+
+ private bool HasSchemaPrimaryKey(DataTable schema)
+ {
+ DataColumn IsKeyColumn = schema.Columns[SchemaTableColumn.IsKey];
+
+ foreach (DataRow schemaRow in schema.Rows)
+ {
+ if ((bool)schemaRow[IsKeyColumn] == true)
+ return true;
+ }
+
+ return false;
+ }
+
+ private void ResetIsUniqueSchemaColumn(DataTable schema)
+ {
+ DataColumn IsUniqueColumn = schema.Columns[SchemaTableColumn.IsUnique];
+ DataColumn IsKeyColumn = schema.Columns[SchemaTableColumn.IsKey];
+
+ foreach (DataRow schemaRow in schema.Rows)
+ {
+ if ((bool)schemaRow[IsKeyColumn] == false)
+ schemaRow[IsUniqueColumn] = false;
+ }
+
+ schema.AcceptChanges();
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteConnection.cs b/Native.Csharp.Tool/SQLite/SQLiteConnection.cs
new file mode 100644
index 00000000..f0e44eb4
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteConnection.cs
@@ -0,0 +1,7724 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+ using System.Diagnostics;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.ComponentModel;
+ using System.Reflection;
+ using System.Runtime.InteropServices;
+ using System.IO;
+ using System.Text;
+ using System.Threading;
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents a single value to be returned
+ /// from the class via
+ /// its ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// , or
+ /// method. If the value of the
+ /// associated public field of this class is null upon returning from the
+ /// callback, the null value will only be used if the return type for the
+ /// method called is not a value type.
+ /// If the value to be returned from the
+ /// method is unsuitable (e.g. null with a value type), an exception will
+ /// be thrown.
+ ///
+ public sealed class SQLiteDataReaderValue
+ {
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public SQLiteBlob BlobValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public bool? BooleanValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public byte? ByteValue;
+
+ ///
+ /// The value to be returned from the
+ /// method.
+ ///
+ public byte[] BytesValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public char? CharValue;
+
+ ///
+ /// The value to be returned from the
+ /// method.
+ ///
+ public char[] CharsValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public DateTime? DateTimeValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public decimal? DecimalValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public double? DoubleValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public float? FloatValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public Guid? GuidValue;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public short? Int16Value;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public int? Int32Value;
+
+ ///
+ /// The value to be returned from the
+ /// method -OR- null to
+ /// indicate an error.
+ ///
+ public long? Int64Value;
+
+ ///
+ /// The value to be returned from the
+ /// method.
+ ///
+ public string StringValue;
+
+ ///
+ /// The value to be returned from the
+ /// method.
+ ///
+ public object Value;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents the parameters that are provided
+ /// to the methods, with
+ /// the exception of the column index (provided separately).
+ ///
+ public abstract class SQLiteReadEventArgs : EventArgs
+ {
+ // nothing.
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents the parameters that are provided to
+ /// the method, with
+ /// the exception of the column index (provided separately).
+ ///
+ public class SQLiteReadBlobEventArgs : SQLiteReadEventArgs
+ {
+ #region Private Data
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private bool readOnly;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class to pass into a user-defined
+ /// callback associated with the
+ /// method.
+ ///
+ ///
+ /// The value that was originally specified for the "readOnly"
+ /// parameter to the method.
+ ///
+ internal SQLiteReadBlobEventArgs(
+ bool readOnly
+ )
+ {
+ this.readOnly = readOnly;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ ///
+ /// The value that was originally specified for the "readOnly"
+ /// parameter to the method.
+ ///
+ public bool ReadOnly
+ {
+ get { return readOnly; }
+ set { readOnly = value; }
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents the parameters that are provided
+ /// to the and
+ /// methods, with
+ /// the exception of the column index (provided separately).
+ ///
+ public class SQLiteReadArrayEventArgs : SQLiteReadEventArgs
+ {
+ #region Private Data
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private long dataOffset;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private byte[] byteBuffer;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private char[] charBuffer;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private int bufferOffset;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private int length;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class to pass into a user-defined
+ /// callback associated with the
+ /// method.
+ ///
+ ///
+ /// The value that was originally specified for the "dataOffset"
+ /// parameter to the or
+ /// methods.
+ ///
+ ///
+ /// The value that was originally specified for the "buffer"
+ /// parameter to the
+ /// method.
+ ///
+ ///
+ /// The value that was originally specified for the "bufferOffset"
+ /// parameter to the or
+ /// methods.
+ ///
+ ///
+ /// The value that was originally specified for the "length"
+ /// parameter to the or
+ /// methods.
+ ///
+ internal SQLiteReadArrayEventArgs(
+ long dataOffset,
+ byte[] byteBuffer,
+ int bufferOffset,
+ int length
+ )
+ {
+ this.dataOffset = dataOffset;
+ this.byteBuffer = byteBuffer;
+ this.bufferOffset = bufferOffset;
+ this.length = length;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class to pass into a user-defined
+ /// callback associated with the
+ /// method.
+ ///
+ ///
+ /// The value that was originally specified for the "dataOffset"
+ /// parameter to the or
+ /// methods.
+ ///
+ ///
+ /// The value that was originally specified for the "buffer"
+ /// parameter to the
+ /// method.
+ ///
+ ///
+ /// The value that was originally specified for the "bufferOffset"
+ /// parameter to the or
+ /// methods.
+ ///
+ ///
+ /// The value that was originally specified for the "length"
+ /// parameter to the or
+ /// methods.
+ ///
+ internal SQLiteReadArrayEventArgs(
+ long dataOffset,
+ char[] charBuffer,
+ int bufferOffset,
+ int length
+ )
+ {
+ this.dataOffset = dataOffset;
+ this.charBuffer = charBuffer;
+ this.bufferOffset = bufferOffset;
+ this.length = length;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ ///
+ /// The value that was originally specified for the "dataOffset"
+ /// parameter to the or
+ /// methods.
+ ///
+ public long DataOffset
+ {
+ get { return dataOffset; }
+ set { dataOffset = value; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The value that was originally specified for the "buffer"
+ /// parameter to the
+ /// method.
+ ///
+ public byte[] ByteBuffer
+ {
+ get { return byteBuffer; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The value that was originally specified for the "buffer"
+ /// parameter to the
+ /// method.
+ ///
+ public char[] CharBuffer
+ {
+ get { return charBuffer; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The value that was originally specified for the "bufferOffset"
+ /// parameter to the or
+ /// methods.
+ ///
+ public int BufferOffset
+ {
+ get { return bufferOffset; }
+ set { bufferOffset = value; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The value that was originally specified for the "length"
+ /// parameter to the or
+ /// methods.
+ ///
+ public int Length
+ {
+ get { return length; }
+ set { length = value; }
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents the parameters and return values for the
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// , and
+ /// methods.
+ ///
+ public class SQLiteReadValueEventArgs : SQLiteReadEventArgs
+ {
+ #region Private Data
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private string methodName;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private SQLiteReadEventArgs extraEventArgs;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private SQLiteDataReaderValue value;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs a new instance of this class. Depending on the method
+ /// being called, the and/or
+ /// parameters may be null.
+ ///
+ ///
+ /// The name of the method that was
+ /// responsible for invoking this callback.
+ ///
+ ///
+ /// If the or
+ /// method is being called,
+ /// this object will contain the array related parameters for that
+ /// method. If the method is
+ /// being called, this object will contain the blob related parameters
+ /// for that method.
+ ///
+ ///
+ /// This may be used by the callback to set the return value for the
+ /// called method.
+ ///
+ internal SQLiteReadValueEventArgs(
+ string methodName,
+ SQLiteReadEventArgs extraEventArgs,
+ SQLiteDataReaderValue value
+ )
+ {
+ this.methodName = methodName;
+ this.extraEventArgs = extraEventArgs;
+ this.value = value;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ ///
+ /// The name of the method that was
+ /// responsible for invoking this callback.
+ ///
+ public string MethodName
+ {
+ get { return methodName; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// If the or
+ /// method is being called,
+ /// this object will contain the array related parameters for that
+ /// method. If the method is
+ /// being called, this object will contain the blob related parameters
+ /// for that method.
+ ///
+ public SQLiteReadEventArgs ExtraEventArgs
+ {
+ get { return extraEventArgs; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This may be used by the callback to set the return value for the
+ /// called method.
+ ///
+ public SQLiteDataReaderValue Value
+ {
+ get { return value; }
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This represents a method that will be called in response to a request to
+ /// bind a parameter to a command. If an exception is thrown, it will cause
+ /// the parameter binding operation to fail -AND- it will continue to unwind
+ /// the call stack.
+ ///
+ ///
+ /// The instance in use.
+ ///
+ ///
+ /// The instance in use.
+ ///
+ ///
+ /// The flags associated with the instance
+ /// in use.
+ ///
+ ///
+ /// The instance being bound to the command.
+ ///
+ ///
+ /// The database type name associated with this callback.
+ ///
+ ///
+ /// The ordinal of the parameter being bound to the command.
+ ///
+ ///
+ /// The data originally used when registering this callback.
+ ///
+ ///
+ /// Non-zero if the default handling for the parameter binding call should
+ /// be skipped (i.e. the parameter should not be bound at all). Great care
+ /// should be used when setting this to non-zero.
+ ///
+ public delegate void SQLiteBindValueCallback(
+ SQLiteConvert convert,
+ SQLiteCommand command,
+ SQLiteConnectionFlags flags,
+ SQLiteParameter parameter,
+ string typeName,
+ int index,
+ object userData,
+ out bool complete
+ );
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This represents a method that will be called in response to a request
+ /// to read a value from a data reader. If an exception is thrown, it will
+ /// cause the data reader operation to fail -AND- it will continue to unwind
+ /// the call stack.
+ ///
+ ///
+ /// The instance in use.
+ ///
+ ///
+ /// The instance in use.
+ ///
+ ///
+ /// The flags associated with the instance
+ /// in use.
+ ///
+ ///
+ /// The parameter and return type data for the column being read from the
+ /// data reader.
+ ///
+ ///
+ /// The database type name associated with this callback.
+ ///
+ ///
+ /// The zero based index of the column being read from the data reader.
+ ///
+ ///
+ /// The data originally used when registering this callback.
+ ///
+ ///
+ /// Non-zero if the default handling for the data reader call should be
+ /// skipped. If this is set to non-zero and the necessary return value
+ /// is unavailable or unsuitable, an exception will be thrown.
+ ///
+ public delegate void SQLiteReadValueCallback(
+ SQLiteConvert convert,
+ SQLiteDataReader dataReader,
+ SQLiteConnectionFlags flags,
+ SQLiteReadEventArgs eventArgs,
+ string typeName,
+ int index,
+ object userData,
+ out bool complete
+ );
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents the custom data type handling callbacks
+ /// for a single type name.
+ ///
+ public sealed class SQLiteTypeCallbacks
+ {
+ #region Private Data
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private string typeName;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private SQLiteBindValueCallback bindValueCallback;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private SQLiteReadValueCallback readValueCallback;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private object bindValueUserData;
+
+ ///
+ /// Provides the underlying storage for the
+ /// property.
+ ///
+ private object readValueUserData;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The custom paramater binding callback. This parameter may be null.
+ ///
+ ///
+ /// The custom data reader value callback. This parameter may be null.
+ ///
+ ///
+ /// The extra data to pass into the parameter binding callback. This
+ /// parameter may be null.
+ ///
+ ///
+ /// The extra data to pass into the data reader value callback. This
+ /// parameter may be null.
+ ///
+ private SQLiteTypeCallbacks(
+ SQLiteBindValueCallback bindValueCallback,
+ SQLiteReadValueCallback readValueCallback,
+ object bindValueUserData,
+ object readValueUserData
+ )
+ {
+ this.bindValueCallback = bindValueCallback;
+ this.readValueCallback = readValueCallback;
+ this.bindValueUserData = bindValueUserData;
+ this.readValueUserData = readValueUserData;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Static "Factory" Methods
+ ///
+ /// Creates an instance of the class.
+ ///
+ ///
+ /// The custom paramater binding callback. This parameter may be null.
+ ///
+ ///
+ /// The custom data reader value callback. This parameter may be null.
+ ///
+ ///
+ /// The extra data to pass into the parameter binding callback. This
+ /// parameter may be null.
+ ///
+ ///
+ /// The extra data to pass into the data reader value callback. This
+ /// parameter may be null.
+ ///
+ public static SQLiteTypeCallbacks Create(
+ SQLiteBindValueCallback bindValueCallback,
+ SQLiteReadValueCallback readValueCallback,
+ object bindValueUserData,
+ object readValueUserData
+ )
+ {
+ return new SQLiteTypeCallbacks(
+ bindValueCallback, readValueCallback, bindValueUserData,
+ readValueUserData);
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ ///
+ /// The database type name that the callbacks contained in this class
+ /// will apply to. This value may not be null.
+ ///
+ public string TypeName
+ {
+ get { return typeName; }
+ internal set { typeName = value; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The custom paramater binding callback. This value may be null.
+ ///
+ public SQLiteBindValueCallback BindValueCallback
+ {
+ get { return bindValueCallback; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The custom data reader value callback. This value may be null.
+ ///
+ public SQLiteReadValueCallback ReadValueCallback
+ {
+ get { return readValueCallback; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The extra data to pass into the parameter binding callback. This
+ /// value may be null.
+ ///
+ public object BindValueUserData
+ {
+ get { return bindValueUserData; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The extra data to pass into the data reader value callback. This
+ /// value may be null.
+ ///
+ public object ReadValueUserData
+ {
+ get { return readValueUserData; }
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This class represents the mappings between database type names
+ /// and their associated custom data type handling callbacks.
+ ///
+ internal sealed class SQLiteTypeCallbacksMap
+ : Dictionary
+ {
+ ///
+ /// Constructs an (empty) instance of this class.
+ ///
+ public SQLiteTypeCallbacksMap()
+ : base(new TypeNameStringComparer())
+ {
+ // do nothing.
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Event data for connection event handlers.
+ ///
+ public class ConnectionEventArgs : EventArgs
+ {
+ ///
+ /// The type of event being raised.
+ ///
+ public readonly SQLiteConnectionEventType EventType;
+
+ ///
+ /// The associated with this event, if any.
+ ///
+ public readonly StateChangeEventArgs EventArgs;
+
+ ///
+ /// The transaction associated with this event, if any.
+ ///
+ public readonly IDbTransaction Transaction;
+
+ ///
+ /// The command associated with this event, if any.
+ ///
+ public readonly IDbCommand Command;
+
+ ///
+ /// The data reader associated with this event, if any.
+ ///
+ public readonly IDataReader DataReader;
+
+ ///
+ /// The critical handle associated with this event, if any.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ public readonly CriticalHandle CriticalHandle;
+#else
+ public readonly object CriticalHandle;
+#endif
+
+ ///
+ /// Command or message text associated with this event, if any.
+ ///
+ public readonly string Text;
+
+ ///
+ /// Extra data associated with this event, if any.
+ ///
+ public readonly object Data;
+
+ ///
+ /// Constructs the object.
+ ///
+ /// The type of event being raised.
+ /// The base associated
+ /// with this event, if any.
+ /// The transaction associated with this event, if any.
+ /// The command associated with this event, if any.
+ /// The data reader associated with this event, if any.
+ /// The critical handle associated with this event, if any.
+ /// The command or message text, if any.
+ /// The extra data, if any.
+ internal ConnectionEventArgs(
+ SQLiteConnectionEventType eventType,
+ StateChangeEventArgs eventArgs,
+ IDbTransaction transaction,
+ IDbCommand command,
+ IDataReader dataReader,
+#if !PLATFORM_COMPACTFRAMEWORK
+ CriticalHandle criticalHandle,
+#else
+ object criticalHandle,
+#endif
+ string text,
+ object data
+ )
+ {
+ EventType = eventType;
+ EventArgs = eventArgs;
+ Transaction = transaction;
+ Command = command;
+ DataReader = dataReader;
+ CriticalHandle = criticalHandle;
+ Text = text;
+ Data = data;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Raised when an event pertaining to a connection occurs.
+ ///
+ /// The connection involved.
+ /// Extra information about the event.
+ public delegate void SQLiteConnectionEventHandler(object sender, ConnectionEventArgs e);
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// SQLite implentation of DbConnection.
+ ///
+ ///
+ /// The property can contain the following parameter(s), delimited with a semi-colon:
+ ///
+ ///
+ /// Parameter
+ /// Values
+ /// Required
+ /// Default
+ ///
+ /// -
+ /// Data Source
+ ///
+ /// This may be a file name, the string ":memory:", or any supported URI (starting with SQLite 3.7.7).
+ /// Starting with release 1.0.86.0, in order to use more than one consecutive backslash (e.g. for a
+ /// UNC path), each of the adjoining backslash characters must be doubled (e.g. "\\Network\Share\test.db"
+ /// would become "\\\\Network\Share\test.db").
+ ///
+ /// Y
+ ///
+ ///
+ /// -
+ /// Uri
+ ///
+ /// If specified, this must be a file name that starts with "file://", "file:", or "/". Any leading
+ /// "file://" or "file:" prefix will be stripped off and the resulting file name will be used to open
+ /// the database.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// FullUri
+ ///
+ /// If specified, this must be a URI in a format recognized by the SQLite core library (starting with
+ /// SQLite 3.7.7). It will be passed verbatim to the SQLite core library.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// Version
+ /// 3
+ /// N
+ /// 3
+ ///
+ /// -
+ /// UseUTF16Encoding
+ ///
+ /// True - The UTF-16 encoding should be used.
+ ///
+ /// False - The UTF-8 encoding should be used.
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// DefaultDbType
+ ///
+ /// This is the default to use when one cannot be determined based on the
+ /// column metadata and the configured type mappings.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// DefaultTypeName
+ ///
+ /// This is the default type name to use when one cannot be determined based on the column metadata
+ /// and the configured type mappings.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// NoDefaultFlags
+ ///
+ /// True - Do not combine the specified (or existing) connection flags with the value of the
+ /// property.
+ ///
+ /// False - Combine the specified (or existing) connection flags with the value of the
+ /// property.
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// NoSharedFlags
+ ///
+ /// True - Do not combine the specified (or existing) connection flags with the value of the
+ /// property.
+ ///
+ /// False - Combine the specified (or existing) connection flags with the value of the
+ /// property.
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// VfsName
+ ///
+ /// The name of the VFS to use when opening the database connection.
+ /// If this is not specified, the default VFS will be used.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// ZipVfsVersion
+ ///
+ /// If non-null, this is the "version" of ZipVFS to use. This requires
+ /// the System.Data.SQLite interop assembly -AND- primary managed assembly
+ /// to be compiled with the INTEROP_INCLUDE_ZIPVFS option; otherwise, this
+ /// property does nothing. The valid values are "v2" and "v3". Using
+ /// anyother value will cause an exception to be thrown. Please see the
+ /// ZipVFS documentation for more information on how to use this parameter.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// DateTimeFormat
+ ///
+ /// Ticks - Use the value of DateTime.Ticks.
+ /// ISO8601 - Use the ISO-8601 format. Uses the "yyyy-MM-dd HH:mm:ss.FFFFFFFK" format for UTC
+ /// DateTime values and "yyyy-MM-dd HH:mm:ss.FFFFFFF" format for local DateTime values).
+ /// JulianDay - The interval of time in days and fractions of a day since January 1, 4713 BC.
+ /// UnixEpoch - The whole number of seconds since the Unix epoch (January 1, 1970).
+ /// InvariantCulture - Any culture-independent string value that the .NET Framework can interpret as a valid DateTime.
+ /// CurrentCulture - Any string value that the .NET Framework can interpret as a valid DateTime using the current culture.
+ /// N
+ /// ISO8601
+ ///
+ /// -
+ /// DateTimeKind
+ ///
+ /// Unspecified - Not specified as either UTC or local time.
+ ///
+ /// Utc - The time represented is UTC.
+ ///
+ /// Local - The time represented is local time.
+ ///
+ /// N
+ /// Unspecified
+ ///
+ /// -
+ /// DateTimeFormatString
+ ///
+ /// The exact DateTime format string to use for all formatting and parsing of all DateTime
+ /// values for this connection.
+ ///
+ /// N
+ /// null
+ ///
+ /// -
+ /// BaseSchemaName
+ ///
+ /// Some base data classes in the framework (e.g. those that build SQL queries dynamically)
+ /// assume that an ADO.NET provider cannot support an alternate catalog (i.e. database) without supporting
+ /// alternate schemas as well; however, SQLite does not fit into this model. Therefore, this value is used
+ /// as a placeholder and removed prior to preparing any SQL statements that may contain it.
+ ///
+ /// N
+ /// sqlite_default_schema
+ ///
+ /// -
+ /// BinaryGUID
+ ///
+ /// True - Store GUID columns in binary form
+ ///
+ /// False - Store GUID columns as text
+ ///
+ /// N
+ /// True
+ ///
+ /// -
+ /// Cache Size
+ ///
+ /// If the argument N is positive then the suggested cache size is set to N.
+ /// If the argument N is negative, then the number of cache pages is adjusted
+ /// to use approximately abs(N*4096) bytes of memory. Backwards compatibility
+ /// note: The behavior of cache_size with a negative N was different in SQLite
+ /// versions prior to 3.7.10. In version 3.7.9 and earlier, the number of
+ /// pages in the cache was set to the absolute value of N.
+ ///
+ /// N
+ /// -2000
+ ///
+ /// -
+ /// Synchronous
+ ///
+ /// Normal - Normal file flushing behavior
+ ///
+ /// Full - Full flushing after all writes
+ ///
+ /// Off - Underlying OS flushes I/O's
+ ///
+ /// N
+ /// Full
+ ///
+ /// -
+ /// Page Size
+ /// {size in bytes}
+ /// N
+ /// 4096
+ ///
+ /// -
+ /// Password
+ ///
+ /// {password} - Using this parameter requires that the legacy CryptoAPI based
+ /// codec (or the SQLite Encryption Extension) be enabled at compile-time for
+ /// both the native interop assembly and the core managed assemblies; otherwise,
+ /// using this parameter may result in an exception being thrown when attempting
+ /// to open the connection.
+ ///
+ /// N
+ ///
+ ///
+ /// -
+ /// HexPassword
+ ///
+ /// {hexPassword} - Must contain a sequence of zero or more hexadecimal encoded
+ /// byte values without a leading "0x" prefix. Using this parameter requires
+ /// that the legacy CryptoAPI based codec (or the SQLite Encryption Extension)
+ /// be enabled at compile-time for both the native interop assembly and the
+ /// core managed assemblies; otherwise, using this parameter may result in an
+ /// exception being thrown when attempting to open the connection.
+ ///
+ /// N
+ ///
+ ///
+ /// -
+ /// Enlist
+ ///
+ /// Y - Automatically enlist in distributed transactions
+ ///
+ /// N - No automatic enlistment
+ ///
+ /// N
+ /// Y
+ ///
+ /// -
+ /// Pooling
+ ///
+ /// True - Use connection pooling.
+ /// False - Do not use connection pooling.
+ /// WARNING: When using the default connection pool implementation,
+ /// setting this property to True should be avoided by applications that make
+ /// use of COM (either directly or indirectly) due to possible deadlocks that
+ /// can occur during the finalization of some COM objects.
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// FailIfMissing
+ ///
+ /// True - Don't create the database if it does not exist, throw an error instead
+ ///
+ /// False - Automatically create the database if it does not exist
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// Max Page Count
+ /// {size in pages} - Limits the maximum number of pages (limits the size) of the database
+ /// N
+ /// 0
+ ///
+ /// -
+ /// Legacy Format
+ ///
+ /// True - Use the more compatible legacy 3.x database format
+ ///
+ /// False - Use the newer 3.3x database format which compresses numbers more effectively
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// Default Timeout
+ /// {time in seconds}
The default command timeout
+ /// N
+ /// 30
+ ///
+ /// -
+ /// BusyTimeout
+ /// {time in milliseconds}
Sets the busy timeout for the core library.
+ /// N
+ /// 0
+ ///
+ /// -
+ /// WaitTimeout
+ /// {time in milliseconds}
+ /// EXPERIMENTAL -- The wait timeout to use with
+ /// method. This is only used when
+ /// waiting for the enlistment to be reset prior to enlisting in a transaction,
+ /// and then only when the appropriate connection flag is set.
+ /// N
+ /// 30000
+ ///
+ /// -
+ /// Journal Mode
+ ///
+ /// Delete - Delete the journal file after a commit.
+ ///
+ /// Persist - Zero out and leave the journal file on disk after a
+ /// commit.
+ ///
+ /// Off - Disable the rollback journal entirely. This saves disk I/O
+ /// but at the expense of database safety and integrity. If the application
+ /// using SQLite crashes in the middle of a transaction when this journaling
+ /// mode is set, then the database file will very likely go corrupt.
+ ///
+ /// Truncate - Truncate the journal file to zero-length instead of
+ /// deleting it.
+ ///
+ /// Memory - Store the journal in volatile RAM. This saves disk I/O
+ /// but at the expense of database safety and integrity. If the application
+ /// using SQLite crashes in the middle of a transaction when this journaling
+ /// mode is set, then the database file will very likely go corrupt.
+ ///
+ /// Wal - Use a write-ahead log instead of a rollback journal.
+ ///
+ /// N
+ /// Delete
+ ///
+ /// -
+ /// Read Only
+ ///
+ /// True - Open the database for read only access
+ ///
+ /// False - Open the database for normal read/write access
+ ///
+ /// N
+ /// False
+ ///
+ /// -
+ /// Max Pool Size
+ /// The maximum number of connections for the given connection string that can be in the connection pool
+ /// N
+ /// 100
+ ///
+ /// -
+ /// Default IsolationLevel
+ /// The default transaciton isolation level
+ /// N
+ /// Serializable
+ ///
+ /// -
+ /// Foreign Keys
+ /// Enable foreign key constraints
+ /// N
+ /// False
+ ///
+ /// -
+ /// Flags
+ /// Extra behavioral flags for the connection. See the enumeration for possible values.
+ /// N
+ /// Default
+ ///
+ /// -
+ /// SetDefaults
+ ///
+ /// True - Apply the default connection settings to the opened database.
+ /// False - Skip applying the default connection settings to the opened database.
+ ///
+ /// N
+ /// True
+ ///
+ /// -
+ /// ToFullPath
+ ///
+ /// True - Attempt to expand the data source file name to a fully qualified path before opening.
+ ///
+ /// False - Skip attempting to expand the data source file name to a fully qualified path before opening.
+ ///
+ /// N
+ /// True
+ ///
+ /// -
+ /// PrepareRetries
+ ///
+ /// The maximum number of retries when preparing SQL to be executed. This
+ /// normally only applies to preparation errors resulting from the database
+ /// schema being changed.
+ ///
+ /// N
+ /// 3
+ ///
+ /// -
+ /// ProgressOps
+ ///
+ /// The approximate number of virtual machine instructions between progress
+ /// events. In order for progress events to actually fire, the event handler
+ /// must be added to the event as well.
+ ///
+ /// N
+ /// 0
+ ///
+ /// -
+ /// Recursive Triggers
+ ///
+ /// True - Enable the recursive trigger capability.
+ /// False - Disable the recursive trigger capability.
+ ///
+ /// N
+ /// False
+ ///
+ ///
+ ///
+ public sealed partial class SQLiteConnection : DbConnection, ICloneable, IDisposable
+ {
+ #region Private Constants
+ ///
+ /// The "invalid value" for the enumeration used
+ /// by the property. This constant is shared
+ /// by this class and the SQLiteConnectionStringBuilder class.
+ ///
+ internal const DbType BadDbType = (DbType)(-1);
+
+ ///
+ /// The default "stub" (i.e. placeholder) base schema name to use when
+ /// returning column schema information. Used as the initial value of
+ /// the BaseSchemaName property. This should start with "sqlite_*"
+ /// because those names are reserved for use by SQLite (i.e. they cannot
+ /// be confused with the names of user objects).
+ ///
+ internal const string DefaultBaseSchemaName = "sqlite_default_schema";
+
+ private const string MemoryFileName = ":memory:";
+
+ internal const IsolationLevel DeferredIsolationLevel = IsolationLevel.ReadCommitted;
+ internal const IsolationLevel ImmediateIsolationLevel = IsolationLevel.Serializable;
+
+ private const SQLiteConnectionFlags FallbackDefaultFlags = SQLiteConnectionFlags.Default;
+ private const SQLiteSynchronousEnum DefaultSynchronous = SQLiteSynchronousEnum.Default;
+ private const SQLiteJournalModeEnum DefaultJournalMode = SQLiteJournalModeEnum.Default;
+ private const IsolationLevel DefaultIsolationLevel = IsolationLevel.Serializable;
+ internal const SQLiteDateFormats DefaultDateTimeFormat = SQLiteDateFormats.Default;
+ internal const DateTimeKind DefaultDateTimeKind = DateTimeKind.Unspecified;
+ internal const string DefaultDateTimeFormatString = null;
+ private const string DefaultDataSource = null;
+ private const string DefaultUri = null;
+ private const string DefaultFullUri = null;
+ private const string DefaultHexPassword = null;
+ private const string DefaultPassword = null;
+ private const int DefaultVersion = 3;
+ private const int DefaultPageSize = 4096;
+ private const int DefaultMaxPageCount = 0;
+ private const int DefaultCacheSize = -2000;
+ private const int DefaultMaxPoolSize = 100;
+ private const int DefaultConnectionTimeout = 30;
+ private const int DefaultBusyTimeout = 0;
+ private const int DefaultWaitTimeout = 30000;
+ private const bool DefaultNoDefaultFlags = false;
+ private const bool DefaultNoSharedFlags = false;
+ private const bool DefaultFailIfMissing = false;
+ private const bool DefaultReadOnly = false;
+ internal const bool DefaultBinaryGUID = true;
+ private const bool DefaultUseUTF16Encoding = false;
+ private const bool DefaultToFullPath = true;
+ private const bool DefaultPooling = false; // TODO: Maybe promote this to static property?
+ private const bool DefaultLegacyFormat = false;
+ private const bool DefaultForeignKeys = false;
+ private const bool DefaultRecursiveTriggers = false;
+ private const bool DefaultEnlist = true;
+ private const bool DefaultSetDefaults = true;
+ internal const int DefaultPrepareRetries = 3;
+ private const string DefaultVfsName = null;
+ private const int DefaultProgressOps = 0;
+
+#if INTEROP_INCLUDE_ZIPVFS
+ private const string ZipVfs_Automatic = "automatic";
+ private const string ZipVfs_V2 = "v2";
+ private const string ZipVfs_V3 = "v3";
+
+ private const string DefaultZipVfsVersion = null;
+#endif
+
+ private const int SQLITE_FCNTL_CHUNK_SIZE = 6;
+ private const int SQLITE_FCNTL_WIN32_AV_RETRY = 9;
+
+ private const string _dataDirectory = "|DataDirectory|";
+
+ private static string _defaultCatalogName = "main";
+ private static string _defaultMasterTableName = "sqlite_master";
+
+ private static string _temporaryCatalogName = "temp";
+ private static string _temporaryMasterTableName = "sqlite_temp_master";
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Private Static Data
+ ///
+ /// The managed assembly containing this type.
+ ///
+ private static readonly Assembly _assembly = typeof(SQLiteConnection).Assembly;
+
+ ///
+ /// Object used to synchronize access to the static instance data
+ /// for this class.
+ ///
+ private static readonly object _syncRoot = new object();
+
+ ///
+ /// Static variable to store the connection event handlers to call.
+ ///
+ private static event SQLiteConnectionEventHandler _handlers;
+
+ ///
+ /// The extra connection flags to be used for all opened connections.
+ ///
+ private static SQLiteConnectionFlags _sharedFlags;
+
+ ///
+ /// The instance (for this thread) that
+ /// had the most recent call to .
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [ThreadStatic()]
+#endif
+ private static SQLiteConnection _lastConnectionInOpen;
+
+#if SQLITE_STANDARD && !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Used to hold the active library version number of SQLite.
+ ///
+ private static int _versionNumber;
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// State of the current connection
+ ///
+ private ConnectionState _connectionState;
+
+ ///
+ /// The connection string
+ ///
+ private string _connectionString;
+
+#if DEBUG
+ ///
+ /// This string will contain enough information to identify this connection,
+ /// e.g. the database file name, original thread, etc. It is not currently
+ /// exposed via the public interface as it is intended for use only when
+ /// debugging this library.
+ ///
+ private string _debugString;
+#endif
+
+ ///
+ /// Nesting level of the transactions open on the connection
+ ///
+ internal int _transactionLevel;
+
+ ///
+ /// Transaction counter for the connection. Currently, this is only used
+ /// to build SAVEPOINT names.
+ ///
+ internal int _transactionSequence;
+
+ ///
+ /// If this flag is non-zero, the method will have
+ /// no effect; however, the method will continue to
+ /// behave as normal.
+ ///
+ internal bool _noDispose;
+
+ ///
+ /// If set, then the connection is currently being disposed.
+ ///
+ private bool _disposing;
+
+ ///
+ /// The default isolation level for new transactions
+ ///
+ private IsolationLevel _defaultIsolation;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// This object is used with lock statements to synchronize access to the
+ /// field, below.
+ ///
+ internal readonly object _enlistmentSyncRoot = new object();
+
+ ///
+ /// Whether or not the connection is enlisted in a distrubuted transaction
+ ///
+ internal SQLiteEnlistment _enlistment;
+#endif
+
+ ///
+ /// The per-connection mappings between type names and
+ /// values. These mappings override the corresponding global mappings.
+ ///
+ internal SQLiteDbTypeMap _typeNames;
+
+ ///
+ /// The per-connection mappings between type names and optional callbacks
+ /// for parameter binding and value reading.
+ ///
+ private SQLiteTypeCallbacksMap _typeCallbacks;
+
+ ///
+ /// The base SQLite object to interop with
+ ///
+ internal SQLiteBase _sql;
+ ///
+ /// The database filename minus path and extension
+ ///
+ private string _dataSource;
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ ///
+ /// Temporary password storage, emptied after the database has been opened
+ ///
+ private byte[] _password;
+#endif
+
+ ///
+ /// The "stub" (i.e. placeholder) base schema name to use when returning
+ /// column schema information.
+ ///
+ internal string _baseSchemaName;
+
+ ///
+ /// The extra behavioral flags for this connection, if any. See the
+ /// enumeration for a list of
+ /// possible values.
+ ///
+ private SQLiteConnectionFlags _flags;
+
+ ///
+ /// The cached values for all settings that have been fetched on behalf
+ /// of this connection. This cache may be cleared by calling the
+ /// method.
+ ///
+ private Dictionary _cachedSettings;
+
+ ///
+ /// The default databse type for this connection. This value will only
+ /// be used if the
+ /// flag is set.
+ ///
+ private DbType? _defaultDbType;
+
+ ///
+ /// The default databse type name for this connection. This value will only
+ /// be used if the
+ /// flag is set.
+ ///
+ private string _defaultTypeName;
+
+ ///
+ /// The name of the VFS to be used when opening the database connection.
+ ///
+ private string _vfsName;
+
+ ///
+ /// Default command timeout
+ ///
+ private int _defaultTimeout = DefaultConnectionTimeout;
+
+ ///
+ /// The default busy timeout to use with the SQLite core library. This is
+ /// only used when opening a connection.
+ ///
+ private int _busyTimeout = DefaultBusyTimeout;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// The default wait timeout to use with
+ /// method. This is only used when waiting for the enlistment to be reset
+ /// prior to enlisting in a transaction, and then only when the appropriate
+ /// connection flag is set.
+ ///
+ private int _waitTimeout = DefaultWaitTimeout;
+#endif
+
+ ///
+ /// The maximum number of retries when preparing SQL to be executed. This
+ /// normally only applies to preparation errors resulting from the database
+ /// schema being changed.
+ ///
+ internal int _prepareRetries = DefaultPrepareRetries;
+
+ ///
+ /// The approximate number of virtual machine instructions between progress
+ /// events. In order for progress events to actually fire, the event handler
+ /// must be added to the event as
+ /// well. This value will only be used when opening the database.
+ ///
+ private int _progressOps = DefaultProgressOps;
+
+ ///
+ /// Non-zero if the built-in (i.e. framework provided) connection string
+ /// parser should be used when opening the connection.
+ ///
+ private bool _parseViaFramework;
+
+ internal bool _binaryGuid;
+
+ internal int _version;
+
+ private event SQLiteProgressEventHandler _progressHandler;
+ private event SQLiteAuthorizerEventHandler _authorizerHandler;
+ private event SQLiteUpdateEventHandler _updateHandler;
+ private event SQLiteCommitHandler _commitHandler;
+ private event SQLiteTraceEventHandler _traceHandler;
+ private event EventHandler _rollbackHandler;
+
+ private SQLiteProgressCallback _progressCallback;
+ private SQLiteAuthorizerCallback _authorizerCallback;
+ private SQLiteUpdateCallback _updateCallback;
+ private SQLiteCommitCallback _commitCallback;
+ private SQLiteTraceCallback _traceCallback;
+ private SQLiteRollbackCallback _rollbackCallback;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static string GetDefaultCatalogName()
+ {
+ return _defaultCatalogName;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static bool IsDefaultCatalogName(
+ string catalogName
+ )
+ {
+ return String.Compare(catalogName, GetDefaultCatalogName(),
+ StringComparison.OrdinalIgnoreCase) == 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static string GetTemporaryCatalogName()
+ {
+ return _temporaryCatalogName;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static bool IsTemporaryCatalogName(
+ string catalogName
+ )
+ {
+ return String.Compare(catalogName, GetTemporaryCatalogName(),
+ StringComparison.OrdinalIgnoreCase) == 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static string GetMasterTableName(
+ bool temporary
+ )
+ {
+ return temporary ? _temporaryMasterTableName : _defaultMasterTableName;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This event is raised whenever the database is opened or closed.
+ ///
+ public override event StateChangeEventHandler StateChange;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs a new SQLiteConnection object
+ ///
+ ///
+ /// Default constructor
+ ///
+ public SQLiteConnection()
+ : this((string)null)
+ {
+ }
+
+ ///
+ /// Initializes the connection with the specified connection string.
+ ///
+ /// The connection string to use.
+ public SQLiteConnection(string connectionString)
+ : this(connectionString, false)
+ {
+ // do nothing.
+ }
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Initializes the connection with a pre-existing native connection handle.
+ /// This constructor overload is intended to be used only by the private
+ /// method.
+ ///
+ ///
+ /// The native connection handle to use.
+ ///
+ ///
+ /// The file name corresponding to the native connection handle.
+ ///
+ ///
+ /// Non-zero if this instance owns the native connection handle and
+ /// should dispose of it when it is no longer needed.
+ ///
+ internal SQLiteConnection(IntPtr db, string fileName, bool ownHandle)
+ : this()
+ {
+ _sql = new SQLite3(
+ SQLiteDateFormats.Default, DateTimeKind.Unspecified, null,
+ db, fileName, ownHandle);
+
+ _flags = SQLiteConnectionFlags.None;
+
+ _connectionState = (db != IntPtr.Zero) ?
+ ConnectionState.Open : ConnectionState.Closed;
+
+ _connectionString = null; /* unknown */
+
+#if DEBUG
+ _debugString = HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture,
+ "db = {0}, fileName = {1}, ownHandle = {2}",
+ db, fileName, ownHandle);
+#endif
+ }
+#endif
+
+ ///
+ /// Initializes the connection with the specified connection string.
+ ///
+ ///
+ /// The connection string to use.
+ ///
+ ///
+ /// Non-zero to parse the connection string using the built-in (i.e.
+ /// framework provided) parser when opening the connection.
+ ///
+ public SQLiteConnection(string connectionString, bool parseViaFramework)
+ {
+ _noDispose = false;
+
+#if (SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK) && PRELOAD_NATIVE_LIBRARY
+ UnsafeNativeMethods.Initialize();
+#endif
+
+ SQLiteLog.Initialize(typeof(SQLiteConnection).Name);
+
+#if !PLATFORM_COMPACTFRAMEWORK && !INTEROP_LEGACY_CLOSE && SQLITE_STANDARD
+ //
+ // NOTE: Check if the sqlite3_close_v2() native API should be available
+ // to use. This must be done dynamically because the delegate set
+ // here is used by the SQLiteConnectionHandle class, which is a
+ // CriticalHandle derived class (i.e. protected by a constrained
+ // execution region). Therefore, if the underlying native entry
+ // point is unavailable, an exception will be raised even if it is
+ // never actually called (i.e. because the runtime eagerly prepares
+ // all the methods in the call graph of the constrained execution
+ // region).
+ //
+ lock (_syncRoot)
+ {
+ if (_versionNumber == 0)
+ {
+ _versionNumber = SQLite3.SQLiteVersionNumber;
+
+ if (_versionNumber >= 3007014)
+ SQLiteConnectionHandle.closeConnection = SQLiteBase.CloseConnectionV2;
+ }
+ }
+#endif
+
+ _cachedSettings = new Dictionary(
+ new TypeNameStringComparer());
+
+ _typeNames = new SQLiteDbTypeMap();
+ _typeCallbacks = new SQLiteTypeCallbacksMap();
+ _parseViaFramework = parseViaFramework;
+ _flags = SQLiteConnectionFlags.None;
+ _defaultDbType = null;
+ _defaultTypeName = null;
+ _vfsName = null;
+ _connectionState = ConnectionState.Closed;
+ _connectionString = null;
+
+ if (connectionString != null)
+ ConnectionString = connectionString;
+ }
+
+ ///
+ /// Clones the settings and connection string from an existing connection. If the existing connection is already open, this
+ /// function will open its own connection, enumerate any attached databases of the original connection, and automatically
+ /// attach to them.
+ ///
+ /// The connection to copy the settings from.
+ public SQLiteConnection(SQLiteConnection connection)
+ : this(connection.ConnectionString, connection.ParseViaFramework)
+ {
+#if DEBUG
+ _debugString = connection._debugString;
+#endif
+
+ if (connection.State == ConnectionState.Open)
+ {
+ Open();
+
+ // Reattach all attached databases from the existing connection
+ using (DataTable tbl = connection.GetSchema("Catalogs"))
+ {
+ foreach (DataRow row in tbl.Rows)
+ {
+ string str = row[0].ToString();
+
+ if (!IsDefaultCatalogName(str) && !IsTemporaryCatalogName(str))
+ {
+ using (SQLiteCommand cmd = CreateCommand())
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "ATTACH DATABASE '{0}' AS [{1}]", row[1], row[0]);
+ cmd.ExecuteNonQuery();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to lookup the native handle associated with the connection. An exception will
+ /// be thrown if this cannot be accomplished.
+ ///
+ ///
+ /// The connection associated with the desired native handle.
+ ///
+ ///
+ /// The native handle associated with the connection or if it
+ /// cannot be determined.
+ ///
+ private static SQLiteConnectionHandle GetNativeHandle(
+ SQLiteConnection connection
+ )
+ {
+ if (connection == null)
+ throw new ArgumentNullException("connection");
+
+ SQLite3 sqlite3 = connection._sql as SQLite3;
+
+ if (sqlite3 == null)
+ throw new InvalidOperationException("Connection has no wrapper");
+
+ SQLiteConnectionHandle handle = sqlite3._sql;
+
+ if (handle == null)
+ throw new InvalidOperationException("Connection has an invalid handle.");
+
+ IntPtr handlePtr = handle;
+
+ if (handlePtr == IntPtr.Zero)
+ {
+ throw new InvalidOperationException(
+ "Connection has an invalid handle pointer.");
+ }
+
+ return handle;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Raises the event.
+ ///
+ ///
+ /// The connection associated with this event. If this parameter is not
+ /// null and the specified connection cannot raise events, then the
+ /// registered event handlers will not be invoked.
+ ///
+ ///
+ /// A that contains the event data.
+ ///
+ internal static void OnChanged(
+ SQLiteConnection connection,
+ ConnectionEventArgs e
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ if ((connection != null) && !connection.CanRaiseEvents)
+ return;
+#endif
+
+ SQLiteConnectionEventHandler handlers;
+
+ lock (_syncRoot)
+ {
+ if (_handlers != null)
+ handlers = _handlers.Clone() as SQLiteConnectionEventHandler;
+ else
+ handlers = null;
+ }
+
+ if (handlers != null) handlers(connection, e);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This event is raised when events related to the lifecycle of a
+ /// SQLiteConnection object occur.
+ ///
+ public static event SQLiteConnectionEventHandler Changed
+ {
+ add
+ {
+ lock (_syncRoot)
+ {
+ // Remove any copies of this event handler from registered
+ // list. This essentially means that a handler will be
+ // called only once no matter how many times it is added.
+ _handlers -= value;
+
+ // Add this to the list of event handlers.
+ _handlers += value;
+ }
+ }
+ remove
+ {
+ lock (_syncRoot)
+ {
+ _handlers -= value;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This property is used to obtain or set the custom connection pool
+ /// implementation to use, if any. Setting this property to null will
+ /// cause the default connection pool implementation to be used.
+ ///
+ public static ISQLiteConnectionPool ConnectionPool
+ {
+ get { return SQLiteConnectionPool.GetConnectionPool(); }
+ set { SQLiteConnectionPool.SetConnectionPool(value); }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates and returns a new managed database connection handle. This
+ /// method is intended to be used by implementations of the
+ /// interface only. In theory, it
+ /// could be used by other classes; however, that usage is not supported.
+ ///
+ ///
+ /// This must be a native database connection handle returned by the
+ /// SQLite core library and it must remain valid and open during the
+ /// entire duration of the calling method.
+ ///
+ ///
+ /// The new managed database connection handle or null if it cannot be
+ /// created.
+ ///
+ public static object CreateHandle(
+ IntPtr nativeHandle
+ )
+ {
+ SQLiteConnectionHandle result;
+
+ try
+ {
+ // do nothing.
+ }
+ finally /* NOTE: Thread.Abort() protection. */
+ {
+ result = (nativeHandle != IntPtr.Zero) ?
+ new SQLiteConnectionHandle(nativeHandle, true) : null;
+ }
+
+ if (result != null)
+ {
+ SQLiteConnection.OnChanged(null, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewCriticalHandle, null,
+ null, null, null, result, null, new object[] {
+ typeof(SQLiteConnection), nativeHandle }));
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Backup API Members
+ ///
+ /// Backs up the database, using the specified database connection as the
+ /// destination.
+ ///
+ /// The destination database connection.
+ /// The destination database name.
+ /// The source database name.
+ ///
+ /// The number of pages to copy at a time -OR- a negative value to copy all
+ /// pages. When a negative value is used, the
+ /// may never be invoked.
+ ///
+ ///
+ /// The method to invoke between each step of the backup process. This
+ /// parameter may be null (i.e. no callbacks will be performed). If the
+ /// callback returns false -OR- throws an exception, the backup is canceled.
+ ///
+ ///
+ /// The number of milliseconds to sleep after encountering a locking error
+ /// during the backup process. A value less than zero means that no sleep
+ /// should be performed.
+ ///
+ public void BackupDatabase(
+ SQLiteConnection destination,
+ string destinationName,
+ string sourceName,
+ int pages,
+ SQLiteBackupCallback callback,
+ int retryMilliseconds
+ )
+ {
+ CheckDisposed();
+
+ if (_connectionState != ConnectionState.Open)
+ throw new InvalidOperationException(
+ "Source database is not open.");
+
+ if (destination == null)
+ throw new ArgumentNullException("destination");
+
+ if (destination._connectionState != ConnectionState.Open)
+ throw new ArgumentException(
+ "Destination database is not open.", "destination");
+
+ if (destinationName == null)
+ throw new ArgumentNullException("destinationName");
+
+ if (sourceName == null)
+ throw new ArgumentNullException("sourceName");
+
+ SQLiteBase sqliteBase = _sql;
+
+ if (sqliteBase == null)
+ throw new InvalidOperationException(
+ "Connection object has an invalid handle.");
+
+ SQLiteBackup backup = null;
+
+ try
+ {
+ backup = sqliteBase.InitializeBackup(
+ destination, destinationName, sourceName); /* throw */
+
+ bool retry = false;
+
+ while (sqliteBase.StepBackup(backup, pages, ref retry)) /* throw */
+ {
+ //
+ // NOTE: If a callback was supplied by our caller, call it.
+ // If it returns false, halt the backup process.
+ //
+ if ((callback != null) && !callback(this, sourceName,
+ destination, destinationName, pages,
+ sqliteBase.RemainingBackup(backup),
+ sqliteBase.PageCountBackup(backup), retry))
+ {
+ break;
+ }
+
+ //
+ // NOTE: If we need to retry the previous operation, wait for
+ // the number of milliseconds specified by our caller
+ // unless the caller used a negative number, in that case
+ // skip sleeping at all because we do not want to block
+ // this thread forever.
+ //
+ if (retry && (retryMilliseconds >= 0))
+ Thread.Sleep(retryMilliseconds);
+
+ //
+ // NOTE: There is no point in calling the native API to copy
+ // zero pages as it does nothing; therefore, stop now.
+ //
+ if (pages == 0)
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ if (HelperMethods.LogBackup(_flags))
+ {
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Caught exception while backing up database: {0}", e));
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (backup != null)
+ sqliteBase.FinishBackup(backup); /* throw */
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Per-Connection Settings
+ ///
+ /// Clears the per-connection cached settings.
+ ///
+ ///
+ /// The total number of per-connection settings cleared.
+ ///
+ public int ClearCachedSettings()
+ {
+ CheckDisposed();
+
+ int result = -1; /* NO SETTINGS */
+
+ if (_cachedSettings != null)
+ {
+ result = _cachedSettings.Count;
+ _cachedSettings.Clear();
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the value of the specified setting, using the
+ /// cached setting names and values for this connection, when available.
+ ///
+ ///
+ /// The name of the setting.
+ ///
+ ///
+ /// The value to be returned if the setting has not been set explicitly
+ /// or cannot be determined.
+ ///
+ ///
+ /// The value of the cached setting is stored here if found; otherwise,
+ /// the value of is stored here.
+ ///
+ ///
+ /// Non-zero if the cached setting was found; otherwise, zero.
+ ///
+ internal bool TryGetCachedSetting(
+ string name, /* in */
+ object @default, /* in */
+ out object value /* out */
+ )
+ {
+ if ((name == null) || (_cachedSettings == null))
+ {
+ value = @default;
+ return false;
+ }
+
+ return _cachedSettings.TryGetValue(name, out value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adds or sets the cached setting specified by
+ /// to the value specified by .
+ ///
+ ///
+ /// The name of the cached setting to add or replace.
+ ///
+ ///
+ /// The new value of the cached setting.
+ ///
+ internal void SetCachedSetting(
+ string name, /* in */
+ object value /* in */
+ )
+ {
+ if ((name == null) || (_cachedSettings == null))
+ return;
+
+ _cachedSettings[name] = value;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Per-Connection Type Mappings
+ ///
+ /// Clears the per-connection type mappings.
+ ///
+ ///
+ /// The total number of per-connection type mappings cleared.
+ ///
+ public int ClearTypeMappings()
+ {
+ CheckDisposed();
+
+ int result = -1; /* NO MAPPINGS */
+
+ if (_typeNames != null)
+ result = _typeNames.Clear();
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the per-connection type mappings.
+ ///
+ ///
+ /// The per-connection type mappings -OR- null if they are unavailable.
+ ///
+ public Dictionary GetTypeMappings()
+ {
+ CheckDisposed();
+
+ Dictionary result = null;
+
+ if (_typeNames != null)
+ {
+ result = new Dictionary(_typeNames.Count, _typeNames.Comparer);
+
+ foreach (KeyValuePair pair in _typeNames)
+ {
+ SQLiteDbTypeMapping mapping = pair.Value;
+
+ object typeName = null; /* System.String */
+ object dataType = null; /* System.Data.DbType */
+ object primary = null; /* System.Boolean */
+
+ if (mapping != null)
+ {
+ typeName = mapping.typeName;
+ dataType = mapping.dataType;
+ primary = mapping.primary;
+ }
+
+ result.Add(pair.Key, new object[] { typeName, dataType, primary });
+ }
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adds a per-connection type mapping, possibly replacing one or more
+ /// that already exist.
+ ///
+ ///
+ /// The case-insensitive database type name (e.g. "MYDATE"). The value
+ /// of this parameter cannot be null. Using an empty string value (or
+ /// a string value consisting entirely of whitespace) for this parameter
+ /// is not recommended.
+ ///
+ ///
+ /// The value that should be associated with the
+ /// specified type name.
+ ///
+ ///
+ /// Non-zero if this mapping should be considered to be the primary one
+ /// for the specified .
+ ///
+ ///
+ /// A negative value if nothing was done. Zero if no per-connection type
+ /// mappings were replaced (i.e. it was a pure add operation). More than
+ /// zero if some per-connection type mappings were replaced.
+ ///
+ public int AddTypeMapping(
+ string typeName,
+ DbType dataType,
+ bool primary
+ )
+ {
+ CheckDisposed();
+
+ if (typeName == null)
+ throw new ArgumentNullException("typeName");
+
+ int result = -1; /* NO MAPPINGS */
+
+ if (_typeNames != null)
+ {
+ result = 0;
+
+ if (primary && _typeNames.ContainsKey(dataType))
+ result += _typeNames.Remove(dataType) ? 1 : 0;
+
+ if (_typeNames.ContainsKey(typeName))
+ result += _typeNames.Remove(typeName) ? 1 : 0;
+
+ _typeNames.Add(new SQLiteDbTypeMapping(typeName, dataType, primary));
+ }
+
+ return result;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Per-Connection Type Callbacks
+ ///
+ /// Clears the per-connection type callbacks.
+ ///
+ ///
+ /// The total number of per-connection type callbacks cleared.
+ ///
+ public int ClearTypeCallbacks()
+ {
+ CheckDisposed();
+
+ int result = -1; /* NO CALLBACKS */
+
+ if (_typeCallbacks != null)
+ {
+ result = _typeCallbacks.Count;
+ _typeCallbacks.Clear();
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to get the per-connection type callbacks for the specified
+ /// database type name.
+ ///
+ ///
+ /// The database type name.
+ ///
+ ///
+ /// Upon success, this parameter will contain the object holding the
+ /// callbacks for the database type name. Upon failure, this parameter
+ /// will be null.
+ ///
+ ///
+ /// Non-zero upon success; otherwise, zero.
+ ///
+ public bool TryGetTypeCallbacks(
+ string typeName,
+ out SQLiteTypeCallbacks callbacks
+ )
+ {
+ CheckDisposed();
+
+ if (typeName == null)
+ throw new ArgumentNullException("typeName");
+
+ if (_typeCallbacks == null)
+ {
+ callbacks = null;
+ return false;
+ }
+
+ return _typeCallbacks.TryGetValue(typeName, out callbacks);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets, resets, or clears the per-connection type callbacks for the
+ /// specified database type name.
+ ///
+ ///
+ /// The database type name.
+ ///
+ ///
+ /// The object holding the callbacks for the database type name. If
+ /// this parameter is null, any callbacks for the database type name
+ /// will be removed if they are present.
+ ///
+ ///
+ /// Non-zero if callbacks were set or removed; otherwise, zero.
+ ///
+ public bool SetTypeCallbacks(
+ string typeName,
+ SQLiteTypeCallbacks callbacks
+ )
+ {
+ CheckDisposed();
+
+ if (typeName == null)
+ throw new ArgumentNullException("typeName");
+
+ if (_typeCallbacks == null)
+ return false;
+
+ if (callbacks == null)
+ return _typeCallbacks.Remove(typeName);
+
+ callbacks.TypeName = typeName;
+ _typeCallbacks[typeName] = callbacks;
+
+ return true;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to bind the specified object
+ /// instance to this connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// The object instance that implements the
+ /// function to be bound.
+ ///
+ public void BindFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ SQLiteFunction function
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for binding functions.");
+
+ _sql.BindFunction(functionAttribute, function, _flags);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to bind the specified object
+ /// instance to this connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// A object instance that helps implement the
+ /// function to be bound. For scalar functions, this corresponds to the
+ /// type. For aggregate functions,
+ /// this corresponds to the type. For
+ /// collation functions, this corresponds to the
+ /// type.
+ ///
+ ///
+ /// A object instance that helps implement the
+ /// function to be bound. For aggregate functions, this corresponds to the
+ /// type. For other callback types, it
+ /// is not used and must be null.
+ ///
+ public void BindFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ Delegate callback1,
+ Delegate callback2
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for binding functions.");
+
+ _sql.BindFunction(functionAttribute,
+ new SQLiteDelegateFunction(callback1, callback2), _flags);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to unbind the specified object
+ /// instance to this connection.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be unbound.
+ ///
+ /// Non-zero if the function was unbound.
+ public bool UnbindFunction(
+ SQLiteFunctionAttribute functionAttribute
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for unbinding functions.");
+
+ return _sql.UnbindFunction(functionAttribute, _flags);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method unbinds all registered (known) functions -OR- all previously
+ /// bound user-defined functions from this connection.
+ ///
+ ///
+ /// Non-zero to unbind all registered (known) functions -OR- zero to unbind
+ /// all functions currently bound to the connection.
+ ///
+ ///
+ /// Non-zero if all the specified user-defined functions were unbound.
+ ///
+ public bool UnbindAllFunctions(
+ bool registered
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for unbinding functions.");
+
+ return SQLiteFunction.UnbindAllFunctions(_sql, _flags, registered);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ [Conditional("CHECK_STATE")]
+ internal static void Check(SQLiteConnection connection)
+ {
+ if (connection == null)
+ throw new ArgumentNullException("connection");
+
+ connection.CheckDisposed();
+
+ if (connection._connectionState != ConnectionState.Open)
+ throw new InvalidOperationException("The connection is not open.");
+
+ SQLite3 sql = connection._sql as SQLite3;
+
+ if (sql == null)
+ throw new InvalidOperationException("The connection handle wrapper is null.");
+
+ SQLiteConnectionHandle handle = sql._sql;
+
+ if (handle == null)
+ throw new InvalidOperationException("The connection handle is null.");
+
+ if (handle.IsInvalid)
+ throw new InvalidOperationException("The connection handle is invalid.");
+
+ if (handle.IsClosed)
+ throw new InvalidOperationException("The connection handle is closed.");
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Parses a connection string into component parts using the custom
+ /// connection string parser. An exception may be thrown if the syntax
+ /// of the connection string is incorrect.
+ ///
+ ///
+ /// The connection string to parse.
+ ///
+ ///
+ /// Non-zero to parse the connection string using the algorithm provided
+ /// by the framework itself. This is not applicable when running on the
+ /// .NET Compact Framework.
+ ///
+ ///
+ /// Non-zero if names are allowed without values.
+ ///
+ ///
+ /// The list of key/value pairs corresponding to the parameters specified
+ /// within the connection string.
+ ///
+ internal static SortedList ParseConnectionString(
+ string connectionString,
+ bool parseViaFramework,
+ bool allowNameOnly
+ )
+ {
+ return ParseConnectionString(
+ null, connectionString, parseViaFramework, allowNameOnly);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Parses a connection string into component parts using the custom
+ /// connection string parser. An exception may be thrown if the syntax
+ /// of the connection string is incorrect.
+ ///
+ ///
+ /// The connection that will be using the parsed connection string.
+ ///
+ ///
+ /// The connection string to parse.
+ ///
+ ///
+ /// Non-zero to parse the connection string using the algorithm provided
+ /// by the framework itself. This is not applicable when running on the
+ /// .NET Compact Framework.
+ ///
+ ///
+ /// Non-zero if names are allowed without values.
+ ///
+ ///
+ /// The list of key/value pairs corresponding to the parameters specified
+ /// within the connection string.
+ ///
+ private static SortedList ParseConnectionString(
+ SQLiteConnection connection,
+ string connectionString,
+ bool parseViaFramework,
+ bool allowNameOnly
+ )
+ {
+ return parseViaFramework ?
+ ParseConnectionStringViaFramework(connection, connectionString, false) :
+ ParseConnectionString(connection, connectionString, allowNameOnly);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ ///
+ /// Attempts to escape the specified connection string property name or
+ /// value in a way that is compatible with the connection string parser.
+ ///
+ ///
+ /// The connection string property name or value to escape.
+ ///
+ ///
+ /// Non-zero if the equals sign is permitted in the string. If this is
+ /// zero and the string contains an equals sign, an exception will be
+ /// thrown.
+ ///
+ ///
+ /// The original string, with all special characters escaped. If the
+ /// original string contains equals signs, they will not be escaped.
+ /// Instead, they will be preserved verbatim.
+ ///
+ private static string EscapeForConnectionString(
+ string value,
+ bool allowEquals
+ )
+ {
+ if (String.IsNullOrEmpty(value))
+ return value;
+
+ if (value.IndexOfAny(SQLiteConvert.SpecialChars) == -1)
+ return value;
+
+ int length = value.Length;
+ StringBuilder builder = new StringBuilder(length);
+
+ for (int index = 0; index < length; index++)
+ {
+ char character = value[index];
+
+ switch (character)
+ {
+ case SQLiteConvert.QuoteChar:
+ case SQLiteConvert.AltQuoteChar:
+ case SQLiteConvert.PairChar:
+ case SQLiteConvert.EscapeChar:
+ {
+ builder.Append(SQLiteConvert.EscapeChar);
+ builder.Append(character);
+ break;
+ }
+ case SQLiteConvert.ValueChar:
+ {
+ if (allowEquals)
+ {
+ //
+ // HACK: The connection string parser allows
+ // connection string property values
+ // to contain equals signs; however,
+ // they cannot be escaped.
+ //
+ // builder.Append(SQLiteConvert.EscapeChar);
+ builder.Append(character);
+ }
+ else
+ {
+ throw new ArgumentException(
+ "equals sign character is not allowed here");
+ }
+ break;
+ }
+ default:
+ {
+ builder.Append(character);
+ break;
+ }
+ }
+ }
+
+ return builder.ToString();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Builds a connection string from a list of key/value pairs.
+ ///
+ ///
+ /// The list of key/value pairs corresponding to the parameters to be
+ /// specified within the connection string.
+ ///
+ ///
+ /// The connection string. Depending on how the connection string was
+ /// originally parsed, the returned connection string value may not be
+ /// usable in a subsequent call to the method.
+ ///
+ private static string BuildConnectionString(
+ SortedList opts
+ )
+ {
+ if (opts == null) return null;
+ StringBuilder builder = new StringBuilder();
+
+ foreach (KeyValuePair pair in opts)
+ {
+#if NET_COMPACT_20
+ builder.Append(HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture, "{0}{1}{2}{3}",
+ EscapeForConnectionString(pair.Key, false),
+ SQLiteConvert.ValueChar,
+ EscapeForConnectionString(pair.Value, true),
+ SQLiteConvert.PairChar));
+#else
+ builder.AppendFormat("{0}{1}{2}{3}",
+ EscapeForConnectionString(pair.Key, false),
+ SQLiteConvert.ValueChar,
+ EscapeForConnectionString(pair.Value, true),
+ SQLiteConvert.PairChar);
+#endif
+ }
+
+ return builder.ToString();
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void SetupSQLiteBase(SortedList opts)
+ {
+ object enumValue;
+
+ enumValue = TryParseEnum(
+ typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat",
+ DefaultDateTimeFormat.ToString()), true);
+
+ SQLiteDateFormats dateFormat = (enumValue is SQLiteDateFormats) ?
+ (SQLiteDateFormats)enumValue : DefaultDateTimeFormat;
+
+ enumValue = TryParseEnum(
+ typeof(DateTimeKind), FindKey(opts, "DateTimeKind",
+ DefaultDateTimeKind.ToString()), true);
+
+ DateTimeKind kind = (enumValue is DateTimeKind) ?
+ (DateTimeKind)enumValue : DefaultDateTimeKind;
+
+ string dateTimeFormat = FindKey(opts, "DateTimeFormatString",
+ DefaultDateTimeFormatString);
+
+ //
+ // NOTE: SQLite automatically sets the encoding of the database
+ // to UTF16 if called from sqlite3_open16().
+ //
+ if (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding",
+ DefaultUseUTF16Encoding.ToString())))
+ {
+ _sql = new SQLite3_UTF16(
+ dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
+ false);
+ }
+ else
+ {
+ _sql = new SQLite3(
+ dateFormat, kind, dateTimeFormat, IntPtr.Zero, null,
+ false);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes and finalizes the connection, if applicable.
+ ///
+ public new void Dispose()
+ {
+ if (_noDispose)
+ return;
+
+ base.Dispose();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteConnection).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Cleans up resources (native and managed) associated with the current instance.
+ ///
+ ///
+ /// Zero when being disposed via garbage collection; otherwise, non-zero.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+#if !NET_COMPACT_20 && TRACE_WARNING
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.TraceWarning))
+ {
+ if (_noDispose)
+ {
+ System.Diagnostics.Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "WARNING: Disposing of connection \"{0}\" with the no-dispose flag set.",
+ _connectionString));
+ }
+ }
+#endif
+
+ _disposing = true;
+
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ Close();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+#if PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Obsolete
+ ///
+ public override int ConnectionTimeout
+ {
+ get
+ {
+ CheckDisposed();
+ return DefaultConnectionTimeout;
+ }
+ }
+#endif
+
+ ///
+ /// Creates a clone of the connection. All attached databases and user-defined functions are cloned. If the existing connection is open, the cloned connection
+ /// will also be opened.
+ ///
+ ///
+ public object Clone()
+ {
+ CheckDisposed();
+ return new SQLiteConnection(this);
+ }
+
+ ///
+ /// Creates a database file. This just creates a zero-byte file which SQLite
+ /// will turn into a database when the file is opened properly.
+ ///
+ /// The file to create
+ static public void CreateFile(string databaseFileName)
+ {
+ FileStream fs = File.Create(databaseFileName);
+ fs.Close();
+ }
+
+ ///
+ /// Raises the state change event when the state of the connection changes
+ ///
+ /// The new connection state. If this is different
+ /// from the previous state, the event is
+ /// raised.
+ /// The event data created for the raised event, if
+ /// it was actually raised.
+ internal void OnStateChange(
+ ConnectionState newState,
+ ref StateChangeEventArgs eventArgs
+ )
+ {
+ ConnectionState oldState = _connectionState;
+
+ _connectionState = newState;
+
+ if ((StateChange != null) && (newState != oldState))
+ {
+ StateChangeEventArgs localEventArgs =
+ new StateChangeEventArgs(oldState, newState);
+
+ StateChange(this, localEventArgs);
+
+ eventArgs = localEventArgs;
+ }
+ }
+
+ ///
+ /// Determines and returns the fallback default isolation level when one cannot be
+ /// obtained from an existing connection instance.
+ ///
+ ///
+ /// The fallback default isolation level for this connection instance -OR-
+ /// if it cannot be determined.
+ ///
+ private static IsolationLevel GetFallbackDefaultIsolationLevel()
+ {
+ return DefaultIsolationLevel;
+ }
+
+ ///
+ /// Determines and returns the default isolation level for this connection instance.
+ ///
+ ///
+ /// The default isolation level for this connection instance -OR-
+ /// if it cannot be determined.
+ ///
+ internal IsolationLevel GetDefaultIsolationLevel()
+ {
+ return _defaultIsolation;
+ }
+
+ ///
+ /// OBSOLETE. Creates a new SQLiteTransaction if one isn't already active on the connection.
+ ///
+ /// This parameter is ignored.
+ /// When TRUE, SQLite defers obtaining a write lock until a write operation is requested.
+ /// When FALSE, a writelock is obtained immediately. The default is TRUE, but in a multi-threaded multi-writer
+ /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.
+ /// Returns a SQLiteTransaction object.
+ [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]
+ public SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel, bool deferredLock)
+ {
+ CheckDisposed();
+ return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? ImmediateIsolationLevel : DeferredIsolationLevel);
+ }
+
+ ///
+ /// OBSOLETE. Creates a new SQLiteTransaction if one isn't already active on the connection.
+ ///
+ /// When TRUE, SQLite defers obtaining a write lock until a write operation is requested.
+ /// When FALSE, a writelock is obtained immediately. The default is false, but in a multi-threaded multi-writer
+ /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock.
+ /// Returns a SQLiteTransaction object.
+ [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")]
+ public SQLiteTransaction BeginTransaction(bool deferredLock)
+ {
+ CheckDisposed();
+ return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? ImmediateIsolationLevel : DeferredIsolationLevel);
+ }
+
+ ///
+ /// Creates a new if one isn't already active on the connection.
+ ///
+ /// Supported isolation levels are Serializable, ReadCommitted and Unspecified.
+ ///
+ /// Unspecified will use the default isolation level specified in the connection string. If no isolation level is specified in the
+ /// connection string, Serializable is used.
+ /// Serializable transactions are the default. In this mode, the engine gets an immediate lock on the database, and no other threads
+ /// may begin a transaction. Other threads may read from the database, but not write.
+ /// With a ReadCommitted isolation level, locks are deferred and elevated as needed. It is possible for multiple threads to start
+ /// a transaction in ReadCommitted mode, but if a thread attempts to commit a transaction while another thread
+ /// has a ReadCommitted lock, it may timeout or cause a deadlock on both threads until both threads' CommandTimeout's are reached.
+ ///
+ /// Returns a SQLiteTransaction object.
+ public new SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel)
+ {
+ CheckDisposed();
+ return (SQLiteTransaction)BeginDbTransaction(isolationLevel);
+ }
+
+ ///
+ /// Creates a new if one isn't already
+ /// active on the connection.
+ ///
+ /// Returns the new transaction object.
+ public new SQLiteTransaction BeginTransaction()
+ {
+ CheckDisposed();
+ return (SQLiteTransaction)BeginDbTransaction(_defaultIsolation);
+ }
+
+ ///
+ /// Forwards to the local function
+ ///
+ /// Supported isolation levels are Unspecified, Serializable, and ReadCommitted
+ ///
+ protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
+ {
+ if (_connectionState != ConnectionState.Open)
+ throw new InvalidOperationException();
+
+ if (isolationLevel == IsolationLevel.Unspecified) isolationLevel = _defaultIsolation;
+ isolationLevel = GetEffectiveIsolationLevel(isolationLevel);
+
+ if (isolationLevel != ImmediateIsolationLevel && isolationLevel != DeferredIsolationLevel)
+ throw new ArgumentException("isolationLevel");
+
+ SQLiteTransaction transaction;
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.AllowNestedTransactions))
+ {
+ transaction = new SQLiteTransaction2(
+ this, isolationLevel != ImmediateIsolationLevel);
+ }
+ else
+ {
+ transaction = new SQLiteTransaction(
+ this, isolationLevel != ImmediateIsolationLevel);
+ }
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.NewTransaction, null, transaction,
+ null, null, null, null, null));
+
+ return transaction;
+ }
+
+ ///
+ /// This method is not implemented; however, the
+ /// event will still be raised.
+ ///
+ ///
+ public override void ChangeDatabase(string databaseName)
+ {
+ CheckDisposed();
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.ChangeDatabase, null, null, null, null,
+ null, databaseName, null));
+
+ throw new NotImplementedException(); // NOTE: For legacy compatibility.
+ }
+
+ ///
+ /// When the database connection is closed, all commands linked to this connection are automatically reset.
+ ///
+ public override void Close()
+ {
+ CheckDisposed();
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.Closing, null, null, null, null, null,
+ null, null));
+
+ if (_sql != null)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ lock (_enlistmentSyncRoot) /* TRANSACTIONAL */
+ {
+ SQLiteEnlistment enlistment = _enlistment;
+ _enlistment = null;
+
+ if (enlistment != null)
+ {
+ // If the connection is enlisted in a transaction scope and the scope is still active,
+ // we cannot truly shut down this connection until the scope has completed. Therefore make a
+ // hidden connection temporarily to hold open the connection until the scope has completed.
+ SQLiteConnection cnn = new SQLiteConnection();
+
+#if DEBUG
+ cnn._debugString = HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture,
+ "closeThreadId = {0}, {1}{2}{2}{3}",
+ HelperMethods.GetThreadId(), _sql,
+ Environment.NewLine, _debugString);
+#endif
+
+ cnn._sql = _sql;
+ cnn._transactionLevel = _transactionLevel;
+ cnn._transactionSequence = _transactionSequence;
+ cnn._enlistment = enlistment;
+ cnn._connectionState = _connectionState;
+ cnn._version = _version;
+
+ SQLiteTransaction transaction = enlistment._transaction;
+
+ if (transaction != null)
+ transaction._cnn = cnn;
+
+ enlistment._disposeConnection = true;
+
+ _sql = null;
+ }
+ }
+#endif
+ if (_sql != null)
+ {
+ _sql.Close(_disposing);
+ _sql = null;
+ }
+ _transactionLevel = 0;
+ _transactionSequence = 0;
+ }
+
+ StateChangeEventArgs eventArgs = null;
+ OnStateChange(ConnectionState.Closed, ref eventArgs);
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.Closed, eventArgs, null, null, null,
+ null, null, null));
+ }
+
+ ///
+ /// Returns the number of pool entries for the file name associated with this connection.
+ ///
+ public int PoolCount
+ {
+ get
+ {
+ if (_sql == null) return 0;
+ return _sql.CountPool();
+ }
+ }
+
+ ///
+ /// Clears the connection pool associated with the connection. Any other active connections using the same database file
+ /// will be discarded instead of returned to the pool when they are closed.
+ ///
+ ///
+ public static void ClearPool(SQLiteConnection connection)
+ {
+ if (connection._sql == null) return;
+ connection._sql.ClearPool();
+ }
+
+ ///
+ /// Clears all connection pools. Any active connections will be discarded instead of sent to the pool when they are closed.
+ ///
+ public static void ClearAllPools()
+ {
+ SQLiteConnectionPool.ClearAllPools();
+ }
+
+ ///
+ /// The connection string containing the parameters for the connection
+ ///
+ ///
+ /// For the complete list of supported connection string properties,
+ /// please see .
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [RefreshProperties(RefreshProperties.All), DefaultValue("")]
+ [Editor("SQLite.Designer.SQLiteConnectionStringEditor, SQLite.Designer, Version=" + SQLite3.DesignerVersion + ", Culture=neutral, PublicKeyToken=db937bc2d44ff139", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public override string ConnectionString
+ {
+ get
+ {
+ CheckDisposed();
+ return _connectionString;
+ }
+ set
+ {
+ CheckDisposed();
+
+ if (value == null)
+ throw new ArgumentNullException();
+
+ else if (_connectionState != ConnectionState.Closed)
+ throw new InvalidOperationException();
+
+ _connectionString = value;
+ }
+ }
+
+ ///
+ /// Create a new and associate it with this connection.
+ ///
+ /// Returns a new command object already assigned to this connection.
+ public new SQLiteCommand CreateCommand()
+ {
+ CheckDisposed();
+ return new SQLiteCommand(this);
+ }
+
+ ///
+ /// Forwards to the local function.
+ ///
+ ///
+ protected override DbCommand CreateDbCommand()
+ {
+ return CreateCommand();
+ }
+
+#if INTEROP_SESSION_EXTENSION
+ ///
+ /// Attempts to create a new object instance
+ /// using this connection and the specified database name.
+ ///
+ ///
+ /// The name of the database for the newly created session.
+ ///
+ ///
+ /// The newly created session -OR- null if it cannot be created.
+ ///
+ public ISQLiteSession CreateSession(
+ string databaseName
+ )
+ {
+ CheckDisposed();
+
+ return new SQLiteSession(GetNativeHandle(this), _flags, databaseName);
+ }
+
+ ///
+ /// Attempts to create a new object instance
+ /// using this connection and the specified raw data.
+ ///
+ ///
+ /// The raw data that contains a change set (or patch set).
+ ///
+ ///
+ /// The newly created change set -OR- null if it cannot be created.
+ ///
+ public ISQLiteChangeSet CreateChangeSet(
+ byte[] rawData
+ )
+ {
+ CheckDisposed();
+
+ return new SQLiteMemoryChangeSet(rawData, GetNativeHandle(this), _flags);
+ }
+
+ ///
+ /// Attempts to create a new object instance
+ /// using this connection and the specified raw data.
+ ///
+ ///
+ /// The raw data that contains a change set (or patch set).
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ ///
+ /// The newly created change set -OR- null if it cannot be created.
+ ///
+ public ISQLiteChangeSet CreateChangeSet(
+ byte[] rawData,
+ SQLiteChangeSetStartFlags flags
+ )
+ {
+ CheckDisposed();
+
+ return new SQLiteMemoryChangeSet(rawData, GetNativeHandle(this), _flags, flags);
+ }
+
+ ///
+ /// Attempts to create a new object instance
+ /// using this connection and the specified stream.
+ ///
+ ///
+ /// The stream where the raw data that contains a change set (or patch set)
+ /// may be read.
+ ///
+ ///
+ /// The stream where the raw data that contains a change set (or patch set)
+ /// may be written.
+ ///
+ ///
+ /// The newly created change set -OR- null if it cannot be created.
+ ///
+ public ISQLiteChangeSet CreateChangeSet(
+ Stream inputStream,
+ Stream outputStream
+ )
+ {
+ CheckDisposed();
+
+ return new SQLiteStreamChangeSet(
+ inputStream, outputStream, GetNativeHandle(this), _flags);
+ }
+
+ ///
+ /// Attempts to create a new object instance
+ /// using this connection and the specified stream.
+ ///
+ ///
+ /// The stream where the raw data that contains a change set (or patch set)
+ /// may be read.
+ ///
+ ///
+ /// The stream where the raw data that contains a change set (or patch set)
+ /// may be written.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ ///
+ /// The newly created change set -OR- null if it cannot be created.
+ ///
+ public ISQLiteChangeSet CreateChangeSet(
+ Stream inputStream,
+ Stream outputStream,
+ SQLiteChangeSetStartFlags flags
+ )
+ {
+ CheckDisposed();
+
+ return new SQLiteStreamChangeSet(
+ inputStream, outputStream, GetNativeHandle(this), _flags, flags);
+ }
+
+ ///
+ /// Attempts to create a new object
+ /// instance using this connection.
+ ///
+ ///
+ /// The newly created change group -OR- null if it cannot be created.
+ ///
+ public ISQLiteChangeGroup CreateChangeGroup()
+ {
+ CheckDisposed();
+
+ return new SQLiteChangeGroup(_flags);
+ }
+#endif
+
+ ///
+ /// Returns the data source file name without extension or path.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public override string DataSource
+ {
+ get
+ {
+ CheckDisposed();
+ return _dataSource;
+ }
+ }
+
+ ///
+ /// Returns the fully qualified path and file name for the currently open
+ /// database, if any.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public string FileName
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for getting file name.");
+
+ return _sql.GetFileName(GetDefaultCatalogName());
+ }
+ }
+
+ ///
+ /// Returns the string "main".
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public override string Database
+ {
+ get
+ {
+ CheckDisposed();
+ return GetDefaultCatalogName();
+ }
+ }
+
+ internal static string MapUriPath(string path)
+ {
+ if (path.StartsWith ("file://", StringComparison.OrdinalIgnoreCase))
+ return path.Substring (7);
+ else if (path.StartsWith ("file:", StringComparison.OrdinalIgnoreCase))
+ return path.Substring (5);
+ else if (path.StartsWith ("/", StringComparison.OrdinalIgnoreCase))
+ return path;
+ else
+ throw new InvalidOperationException ("Invalid connection string: invalid URI");
+ }
+
+ ///
+ /// Determines if the legacy connection string parser should be used.
+ ///
+ ///
+ /// The connection that will be using the parsed connection string.
+ ///
+ ///
+ /// Non-zero if the legacy connection string parser should be used.
+ ///
+ private static bool ShouldUseLegacyConnectionStringParser(
+ SQLiteConnection connection
+ )
+ {
+ string name = "No_SQLiteConnectionNewParser";
+ object value;
+
+ if ((connection != null) &&
+ connection.TryGetCachedSetting(name, null, out value))
+ {
+ return (value != null);
+ }
+
+ if ((connection == null) &&
+ TryGetLastCachedSetting(name, null, out value))
+ {
+ return (value != null);
+ }
+
+ value = UnsafeNativeMethods.GetSettingValue(name, null);
+
+ if (connection != null)
+ connection.SetCachedSetting(name, value);
+ else
+ SetLastCachedSetting(name, value);
+
+ return (value != null);
+ }
+
+ ///
+ /// Parses a connection string into component parts using the custom
+ /// connection string parser. An exception may be thrown if the syntax
+ /// of the connection string is incorrect.
+ ///
+ ///
+ /// The connection string to parse.
+ ///
+ ///
+ /// Non-zero if names are allowed without values.
+ ///
+ ///
+ /// The list of key/value pairs corresponding to the parameters specified
+ /// within the connection string.
+ ///
+ private static SortedList ParseConnectionString(
+ string connectionString,
+ bool allowNameOnly
+ )
+ {
+ return ParseConnectionString(null, connectionString, allowNameOnly);
+ }
+
+ ///
+ /// Parses a connection string into component parts using the custom
+ /// connection string parser. An exception may be thrown if the syntax
+ /// of the connection string is incorrect.
+ ///
+ ///
+ /// The connection that will be using the parsed connection string.
+ ///
+ ///
+ /// The connection string to parse.
+ ///
+ ///
+ /// Non-zero if names are allowed without values.
+ ///
+ ///
+ /// The list of key/value pairs corresponding to the parameters specified
+ /// within the connection string.
+ ///
+ private static SortedList ParseConnectionString(
+ SQLiteConnection connection,
+ string connectionString,
+ bool allowNameOnly
+ )
+ {
+ string s = connectionString;
+ int n;
+ SortedList ls = new SortedList(StringComparer.OrdinalIgnoreCase);
+
+ // First split into semi-colon delimited values.
+ string error = null;
+ string[] arParts;
+
+ if (ShouldUseLegacyConnectionStringParser(connection))
+ arParts = SQLiteConvert.Split(s, SQLiteConvert.PairChar);
+ else
+ arParts = SQLiteConvert.NewSplit(s, SQLiteConvert.PairChar, true, ref error);
+
+ if (arParts == null)
+ {
+ throw new ArgumentException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Invalid ConnectionString format, cannot parse: {0}", (error != null) ?
+ error : "could not split connection string into properties"));
+ }
+
+ int x = (arParts != null) ? arParts.Length : 0;
+ // For each semi-colon piece, split into key and value pairs by the presence of the = sign
+ for (n = 0; n < x; n++)
+ {
+ if (arParts[n] == null)
+ continue;
+
+ arParts[n] = arParts[n].Trim();
+
+ if (arParts[n].Length == 0)
+ continue;
+
+ int indexOf = arParts[n].IndexOf(SQLiteConvert.ValueChar);
+
+ if (indexOf != -1)
+ ls.Add(UnwrapString(arParts[n].Substring(0, indexOf).Trim()), UnwrapString(arParts[n].Substring(indexOf + 1).Trim()));
+ else if (allowNameOnly)
+ ls.Add(UnwrapString(arParts[n].Trim()), String.Empty);
+ else
+ throw new ArgumentException(HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Invalid ConnectionString format for part \"{0}\", no equal sign found", arParts[n]));
+ }
+ return ls;
+ }
+
+ ///
+ /// Parses a connection string using the built-in (i.e. framework provided)
+ /// connection string parser class and returns the key/value pairs. An
+ /// exception may be thrown if the connection string is invalid or cannot be
+ /// parsed. When compiled for the .NET Compact Framework, the custom
+ /// connection string parser is always used instead because the framework
+ /// provided one is unavailable there.
+ ///
+ ///
+ /// The connection that will be using the parsed connection string.
+ ///
+ ///
+ /// The connection string to parse.
+ ///
+ ///
+ /// Non-zero to throw an exception if any connection string values are not of
+ /// the type. This is not applicable when running on
+ /// the .NET Compact Framework.
+ ///
+ /// The list of key/value pairs.
+ private static SortedList ParseConnectionStringViaFramework(
+ SQLiteConnection connection,
+ string connectionString,
+ bool strict
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ DbConnectionStringBuilder connectionStringBuilder
+ = new DbConnectionStringBuilder();
+
+ connectionStringBuilder.ConnectionString = connectionString; /* throw */
+
+ SortedList result =
+ new SortedList(StringComparer.OrdinalIgnoreCase);
+
+ foreach (string keyName in connectionStringBuilder.Keys)
+ {
+ object value = connectionStringBuilder[keyName];
+ string keyValue = null;
+
+ if (value is string)
+ {
+ keyValue = (string)value;
+ }
+ else if (strict)
+ {
+ throw new ArgumentException(
+ "connection property value is not a string",
+ keyName);
+ }
+ else if (value != null)
+ {
+ keyValue = value.ToString();
+ }
+
+ result.Add(keyName, keyValue);
+ }
+
+ return result;
+#else
+ //
+ // NOTE: On the .NET Compact Framework, always use our custom connection
+ // string parser as the built-in (i.e. framework provided) one is
+ // unavailable.
+ //
+ return ParseConnectionString(connection, connectionString, false);
+#endif
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Manual distributed transaction enlistment support
+ ///
+ /// The distributed transaction to enlist in
+ public override void EnlistTransaction(System.Transactions.Transaction transaction)
+ {
+ CheckDisposed();
+
+ bool waitForEnlistmentReset;
+ int waitTimeout;
+
+ lock (_enlistmentSyncRoot) /* TRANSACTIONAL */
+ {
+ waitForEnlistmentReset = HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.WaitForEnlistmentReset);
+
+ waitTimeout = _waitTimeout;
+ }
+
+ if (waitForEnlistmentReset)
+ /* IGNORED */
+ WaitForEnlistmentReset(waitTimeout, null);
+
+ lock (_enlistmentSyncRoot) /* TRANSACTIONAL */
+ {
+ if (_enlistment != null && transaction == _enlistment._scope)
+ return;
+ else if (_enlistment != null)
+ throw new ArgumentException("Already enlisted in a transaction");
+
+ if (_transactionLevel > 0 && transaction != null)
+ throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists");
+ else if (transaction == null)
+ throw new ArgumentNullException("Unable to enlist in transaction, it is null");
+
+ bool strictEnlistment = HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.StrictEnlistment);
+
+ _enlistment = new SQLiteEnlistment(this, transaction,
+ GetFallbackDefaultIsolationLevel(), strictEnlistment,
+ strictEnlistment);
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.EnlistTransaction, null, null, null, null,
+ null, null, new object[] { _enlistment }));
+ }
+ }
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// EXPERIMENTAL --
+ /// Waits for the enlistment associated with this connection to be reset.
+ /// This method always throws when
+ /// running on the .NET Compact Framework.
+ ///
+ ///
+ /// The approximate maximum number of milliseconds to wait before timing
+ /// out the wait operation.
+ ///
+ ///
+ /// The return value to use if the connection has been disposed; if this
+ /// value is null, will be raised
+ /// if the connection has been disposed.
+ ///
+ ///
+ /// Non-zero if the enlistment assciated with this connection was reset;
+ /// otherwise, zero. It should be noted that this method returning a
+ /// non-zero value does not necessarily guarantee that the connection
+ /// can enlist in a new transaction (i.e. due to potentical race with
+ /// other threads); therefore, callers should generally use try/catch
+ /// when calling the method.
+ ///
+#else
+ ///
+ /// EXPERIMENTAL --
+ /// Waits for the enlistment associated with this connection to be reset.
+ /// This method always throws when
+ /// running on the .NET Compact Framework.
+ ///
+ ///
+ /// The approximate maximum number of milliseconds to wait before timing
+ /// out the wait operation.
+ ///
+ ///
+ /// The return value to use if the connection has been disposed; if this
+ /// value is null, will be raised
+ /// if the connection has been disposed.
+ ///
+ ///
+ /// Non-zero if the enlistment assciated with this connection was reset;
+ /// otherwise, zero. It should be noted that this method returning a
+ /// non-zero value does not necessarily guarantee that the connection
+ /// can enlist in a new transaction (i.e. due to potentical race with
+ /// other threads); therefore, callers should generally use try/catch
+ /// when calling the EnlistTransaction method.
+ ///
+#endif
+ public bool WaitForEnlistmentReset(
+ int timeoutMilliseconds,
+ bool? returnOnDisposed
+ )
+ {
+ if (returnOnDisposed == null)
+ CheckDisposed();
+ else if(disposed)
+ return (bool)returnOnDisposed;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (timeoutMilliseconds < 0)
+ throw new ArgumentException("timeout cannot be negative");
+
+ const int defaultMilliseconds = 100;
+ int sleepMilliseconds;
+
+ if (timeoutMilliseconds == 0)
+ {
+ sleepMilliseconds = 0;
+ }
+ else
+ {
+ sleepMilliseconds = Math.Min(
+ timeoutMilliseconds / 10, defaultMilliseconds);
+
+ if (sleepMilliseconds == 0)
+ sleepMilliseconds = defaultMilliseconds;
+ }
+
+ DateTime start = DateTime.UtcNow;
+
+ while (true)
+ {
+ //
+ // NOTE: Attempt to acquire the necessary lock without blocking.
+ // This method will treat a failure to obtain the lock the
+ // same as the enlistment not being reset yet. Both will
+ // advance toward the timeout.
+ //
+ bool locked = Monitor.TryEnter(_enlistmentSyncRoot);
+
+ try
+ {
+ if (locked)
+ {
+ //
+ // NOTE: Is there still an enlistment? If not, we are
+ // done. There is a potential race condition in
+ // the caller if another thread is able to setup
+ // a new enlistment at any point prior to our
+ // caller fully dealing with the result of this
+ // method. However, that should generally never
+ // happen because this class is not intended to
+ // be used by multiple concurrent threads, with
+ // the notable exception of an active enlistment
+ // being asynchronously committed or rolled back
+ // by the .NET Framework.
+ //
+ if (_enlistment == null)
+ return true;
+ }
+ }
+ finally
+ {
+ if (locked)
+ {
+ Monitor.Exit(_enlistmentSyncRoot);
+ locked = false;
+ }
+ }
+
+ //
+ // NOTE: A timeout value of zero is special. It means never
+ // sleep.
+ //
+ if (sleepMilliseconds == 0)
+ return false;
+
+ //
+ // NOTE: How much time has elapsed since we first starting
+ // waiting?
+ //
+ DateTime now = DateTime.UtcNow;
+ TimeSpan elapsed = now.Subtract(start);
+
+ //
+ // NOTE: Are we done wait?
+ //
+ double totalMilliseconds = elapsed.TotalMilliseconds;
+
+ if ((totalMilliseconds < 0) || /* Time went backward? */
+ (totalMilliseconds >= (double)timeoutMilliseconds))
+ {
+ return false;
+ }
+
+ //
+ // NOTE: Sleep for a bit and then try again.
+ //
+ Thread.Sleep(sleepMilliseconds);
+ }
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ ///
+ /// Looks for a key in the array of key/values of the parameter string. If not found, return the specified default value
+ ///
+ /// The list to look in
+ /// The key to find
+ /// The default value to return if the key is not found
+ /// The value corresponding to the specified key, or the default value if not found.
+ static internal string FindKey(SortedList items, string key, string defValue)
+ {
+ string ret;
+
+ if (String.IsNullOrEmpty(key)) return defValue;
+ if (items.TryGetValue(key, out ret)) return ret;
+ if (items.TryGetValue(key.Replace(" ", String.Empty), out ret)) return ret;
+ if (items.TryGetValue(key.Replace(" ", "_"), out ret)) return ret;
+
+ return defValue;
+ }
+
+ ///
+ /// Attempts to convert the string value to an enumerated value of the specified type.
+ ///
+ /// The enumerated type to convert the string value to.
+ /// The string value to be converted.
+ /// Non-zero to make the conversion case-insensitive.
+ /// The enumerated value upon success or null upon error.
+ internal static object TryParseEnum(
+ Type type,
+ string value,
+ bool ignoreCase
+ )
+ {
+ if (!String.IsNullOrEmpty(value))
+ {
+ try
+ {
+ return Enum.Parse(type, value, ignoreCase);
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Attempts to convert an input string into a byte value.
+ ///
+ ///
+ /// The string value to be converted.
+ ///
+ ///
+ /// The number styles to use for the conversion.
+ ///
+ ///
+ /// Upon sucess, this will contain the parsed byte value.
+ /// Upon failure, the value of this parameter is undefined.
+ ///
+ ///
+ /// Non-zero upon success; zero on failure.
+ ///
+ private static bool TryParseByte(
+ string value,
+ NumberStyles style,
+ out byte result
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return byte.TryParse(value, style, null, out result);
+#else
+ try
+ {
+ result = byte.Parse(value, style);
+ return true;
+ }
+ catch
+ {
+ result = 0;
+ return false;
+ }
+#endif
+ }
+
+ ///
+ /// Change a configuration option value for the database.
+ ///
+ ///
+ /// The database configuration option to change.
+ ///
+ ///
+ /// The new value for the specified configuration option.
+ ///
+ public void SetConfigurationOption(
+ SQLiteConfigDbOpsEnum option,
+ object value
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ {
+ throw new InvalidOperationException(
+ "Database connection not valid for changing a configuration option.");
+ }
+
+ if ((option == SQLiteConfigDbOpsEnum.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION) &&
+ HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoLoadExtension))
+ {
+ throw new SQLiteException("Loading extensions is disabled for this database connection.");
+ }
+
+ SQLiteErrorCode rc = _sql.SetConfigurationOption(option, value);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, null);
+ }
+
+ ///
+ /// Enables or disabled extension loading.
+ ///
+ ///
+ /// True to enable loading of extensions, false to disable.
+ ///
+ public void EnableExtensions(
+ bool enable
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Database connection not valid for {0} extensions.",
+ enable ? "enabling" : "disabling"));
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoLoadExtension))
+ throw new SQLiteException("Loading extensions is disabled for this database connection.");
+
+ _sql.SetLoadExtension(enable);
+ }
+
+ ///
+ /// Loads a SQLite extension library from the named dynamic link library file.
+ ///
+ ///
+ /// The name of the dynamic link library file containing the extension.
+ ///
+ public void LoadExtension(
+ string fileName
+ )
+ {
+ CheckDisposed();
+
+ LoadExtension(fileName, null);
+ }
+
+ ///
+ /// Loads a SQLite extension library from the named dynamic link library file.
+ ///
+ ///
+ /// The name of the dynamic link library file containing the extension.
+ ///
+ ///
+ /// The name of the exported function used to initialize the extension.
+ /// If null, the default "sqlite3_extension_init" will be used.
+ ///
+ public void LoadExtension(
+ string fileName,
+ string procName
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for loading extensions.");
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoLoadExtension))
+ throw new SQLiteException("Loading extensions is disabled for this database connection.");
+
+ _sql.LoadExtension(fileName, procName);
+ }
+
+#if INTEROP_VIRTUAL_TABLE
+ ///
+ /// Creates a disposable module containing the implementation of a virtual
+ /// table.
+ ///
+ ///
+ /// The module object to be used when creating the disposable module.
+ ///
+ public void CreateModule(
+ SQLiteModule module
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException(
+ "Database connection not valid for creating modules.");
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoCreateModule))
+ throw new SQLiteException("Creating modules is disabled for this database connection.");
+
+ _sql.CreateModule(module, _flags);
+ }
+#endif
+
+ ///
+ /// Parses a string containing a sequence of zero or more hexadecimal
+ /// encoded byte values and returns the resulting byte array. The
+ /// "0x" prefix is not allowed on the input string.
+ ///
+ ///
+ /// The input string containing zero or more hexadecimal encoded byte
+ /// values.
+ ///
+ ///
+ /// A byte array containing the parsed byte values or null if an error
+ /// was encountered.
+ ///
+ internal static byte[] FromHexString(
+ string text
+ )
+ {
+ string error = null;
+
+ return FromHexString(text, ref error);
+ }
+
+ ///
+ /// Creates and returns a string containing the hexadecimal encoded byte
+ /// values from the input array.
+ ///
+ ///
+ /// The input array of bytes.
+ ///
+ ///
+ /// The resulting string or null upon failure.
+ ///
+ internal static string ToHexString(
+ byte[] array
+ )
+ {
+ if (array == null)
+ return null;
+
+ StringBuilder result = new StringBuilder();
+
+ int length = array.Length;
+
+ for (int index = 0; index < length; index++)
+#if NET_COMPACT_20
+ result.Append(HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture,
+ "{0:x2}", array[index]));
+#else
+ result.AppendFormat("{0:x2}", array[index]);
+#endif
+
+ return result.ToString();
+ }
+
+ ///
+ /// Parses a string containing a sequence of zero or more hexadecimal
+ /// encoded byte values and returns the resulting byte array. The
+ /// "0x" prefix is not allowed on the input string.
+ ///
+ ///
+ /// The input string containing zero or more hexadecimal encoded byte
+ /// values.
+ ///
+ ///
+ /// Upon failure, this will contain an appropriate error message.
+ ///
+ ///
+ /// A byte array containing the parsed byte values or null if an error
+ /// was encountered.
+ ///
+ private static byte[] FromHexString(
+ string text,
+ ref string error
+ )
+ {
+ if (text == null)
+ {
+ error = "string is null";
+ return null;
+ }
+
+ if (text.Length % 2 != 0)
+ {
+ error = "string contains an odd number of characters";
+ return null;
+ }
+
+ byte[] result = new byte[text.Length / 2];
+
+ for (int index = 0; index < text.Length; index += 2)
+ {
+ string value = text.Substring(index, 2);
+
+ if (!TryParseByte(value,
+ NumberStyles.HexNumber, out result[index / 2]))
+ {
+ error = HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "string contains \"{0}\", which cannot be converted to a byte value",
+ value);
+
+ return null;
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// This method figures out what the default connection pool setting should
+ /// be based on the connection flags. When present, the "Pooling" connection
+ /// string property value always overrides the value returned by this method.
+ ///
+ ///
+ /// Non-zero if the connection pool should be enabled by default; otherwise,
+ /// zero.
+ ///
+ private bool GetDefaultPooling()
+ {
+ bool result = DefaultPooling;
+
+ if (result) /* NOTE: True branch not reached in the default build. */
+ {
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoConnectionPool))
+ result = false;
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionPool))
+ result = true;
+ }
+ else
+ {
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionPool))
+ result = true;
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoConnectionPool))
+ result = false;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Determines the transaction isolation level that should be used by
+ /// the caller, primarily based upon the one specified by the caller.
+ /// If mapping of transaction isolation levels is enabled, the returned
+ /// transaction isolation level may be significantly different than the
+ /// originally specified one.
+ ///
+ ///
+ /// The originally specified transaction isolation level.
+ ///
+ ///
+ /// The transaction isolation level that should be used.
+ ///
+ private IsolationLevel GetEffectiveIsolationLevel(
+ IsolationLevel isolationLevel
+ )
+ {
+ if (!HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.MapIsolationLevels))
+ {
+ return isolationLevel;
+ }
+
+ switch (isolationLevel)
+ {
+ case IsolationLevel.Unspecified:
+ case IsolationLevel.Chaos:
+ case IsolationLevel.ReadUncommitted:
+ case IsolationLevel.ReadCommitted:
+ return DeferredIsolationLevel;
+ case IsolationLevel.RepeatableRead:
+ case IsolationLevel.Serializable:
+ case IsolationLevel.Snapshot:
+ return ImmediateIsolationLevel;
+ default:
+ return GetFallbackDefaultIsolationLevel();
+ }
+ }
+
+ ///
+ /// Opens the connection using the parameters found in the .
+ ///
+ public override void Open()
+ {
+ CheckDisposed();
+
+ _lastConnectionInOpen = this; /* THREAD-SAFE: per-thread datum. */
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.Opening, null, null, null, null, null,
+ null, null));
+
+ if (_connectionState != ConnectionState.Closed)
+ throw new InvalidOperationException();
+
+ Close();
+
+ SortedList opts = ParseConnectionString(
+ this, _connectionString, _parseViaFramework, false);
+
+ object enumValue = TryParseEnum(typeof(SQLiteConnectionFlags), FindKey(opts, "Flags", null), true);
+
+ //
+ // BUGFIX: Always preserve the pre-existing instance flags. This is OK
+ // because when the connection object is initially created, they
+ // are "None"; therefore, OR-ing the connection string property
+ // flags with the instance flags would produce exactly the same
+ // result. If the "Flags" connection string property is absent,
+ // OR-ing the the instance flags with the static DefaultFlags is
+ // done instead. This is OK for the same reason as before: when
+ // the connection object is initially created, they are "None"
+ // by default. If they are different now, they must have been
+ // manually set by the application.
+ //
+ bool noDefaultFlags = SQLiteConvert.ToBoolean(FindKey(opts, "NoDefaultFlags", DefaultNoDefaultFlags.ToString()));
+
+ if (enumValue is SQLiteConnectionFlags)
+ _flags |= (SQLiteConnectionFlags)enumValue;
+ else if (!noDefaultFlags)
+ _flags |= DefaultFlags;
+
+ bool noSharedFlags = SQLiteConvert.ToBoolean(FindKey(opts, "NoSharedFlags", DefaultNoSharedFlags.ToString()));
+ if (!noSharedFlags) { lock (_syncRoot) { _flags |= _sharedFlags; } }
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ bool hidePassword = HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword);
+#endif
+
+ SortedList eventArgOpts = opts;
+ string eventArgConnectionString = _connectionString;
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ if (hidePassword)
+ {
+ eventArgOpts = new SortedList(
+ StringComparer.OrdinalIgnoreCase);
+
+ foreach (KeyValuePair pair in opts)
+ {
+ if (String.Equals(
+ pair.Key, "Password",
+ StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (String.Equals(
+ pair.Key, "HexPassword",
+ StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ eventArgOpts.Add(pair.Key, pair.Value);
+ }
+
+ eventArgConnectionString = BuildConnectionString(
+ eventArgOpts);
+ }
+#endif
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.ConnectionString, null, null, null, null,
+ null, eventArgConnectionString, new object[] { eventArgOpts }));
+
+ enumValue = TryParseEnum(typeof(DbType), FindKey(opts, "DefaultDbType", null), true);
+ _defaultDbType = (enumValue is DbType) ? (DbType)enumValue : (DbType?)null;
+
+ //
+ // NOTE: Nullable values types are not supported by the .NET Framework
+ // ADO.NET support components that work with the connection string
+ // builder; therefore, translate the "invalid value" used by the
+ // SQLiteConnectionStringBuilder.DefaultDbType property to null
+ // here.
+ //
+ if ((_defaultDbType != null) && ((DbType)_defaultDbType == BadDbType))
+ _defaultDbType = null;
+
+ _defaultTypeName = FindKey(opts, "DefaultTypeName", null);
+ _vfsName = FindKey(opts, "VfsName", DefaultVfsName);
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ bool uri = false;
+#endif
+ bool fullUri = false;
+ string fileName;
+
+ if (Convert.ToInt32(FindKey(opts, "Version", SQLiteConvert.ToString(DefaultVersion)), CultureInfo.InvariantCulture) != DefaultVersion)
+ throw new NotSupportedException(HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Only SQLite Version {0} is supported at this time", DefaultVersion));
+
+#if INTEROP_INCLUDE_ZIPVFS
+ bool useZipVfs = false;
+ string zipVfsVersion = FindKey(opts, "ZipVfsVersion", DefaultZipVfsVersion);
+
+ if (zipVfsVersion != null)
+ {
+ if (String.Compare(zipVfsVersion, ZipVfs_Automatic) == 0)
+ {
+ useZipVfs = true;
+ }
+ else if (String.Compare(zipVfsVersion, ZipVfs_V2) == 0)
+ {
+ UnsafeNativeMethods.zipvfsInit_v2();
+ useZipVfs = true;
+ }
+ else if (String.Compare(zipVfsVersion, ZipVfs_V3) == 0)
+ {
+ UnsafeNativeMethods.zipvfsInit_v3(0);
+ useZipVfs = true;
+ }
+ else
+ {
+ throw new NotSupportedException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Only ZipVFS versions {0}, {1}, and {2} are supported at this time",
+ ZipVfs_Automatic, ZipVfs_V2, ZipVfs_V3));
+ }
+ }
+#endif
+
+ fileName = FindKey(opts, "Data Source", DefaultDataSource);
+
+ if (String.IsNullOrEmpty(fileName))
+ {
+ fileName = FindKey(opts, "Uri", DefaultUri);
+ if (String.IsNullOrEmpty(fileName))
+ {
+ fileName = FindKey(opts, "FullUri", DefaultFullUri);
+ if (String.IsNullOrEmpty(fileName))
+ throw new ArgumentException(HelperMethods.StringFormat(CultureInfo.CurrentCulture, "Data Source cannot be empty. Use {0} to open an in-memory database", MemoryFileName));
+ else
+ fullUri = true;
+ }
+ else
+ {
+ fileName = MapUriPath(fileName);
+#if !NET_COMPACT_20 && TRACE_WARNING
+ uri = true;
+#endif
+ }
+ }
+
+ bool isMemory = (String.Compare(fileName, MemoryFileName, StringComparison.OrdinalIgnoreCase) == 0);
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.TraceWarning))
+ {
+ if (!uri && !fullUri && !isMemory && !String.IsNullOrEmpty(fileName) &&
+ fileName.StartsWith("\\", StringComparison.OrdinalIgnoreCase) &&
+ !fileName.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase))
+ {
+ System.Diagnostics.Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "WARNING: Detected a possibly malformed UNC database file name \"{0}\" that " +
+ "may have originally started with two backslashes; however, four leading " +
+ "backslashes may be required, e.g.: \"Data Source=\\\\\\{0};\"",
+ fileName));
+ }
+ }
+#endif
+
+ if (!fullUri)
+ {
+ if (isMemory)
+ fileName = MemoryFileName;
+ else
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ if (fileName.StartsWith("./") || fileName.StartsWith(".\\"))
+ fileName = Path.GetDirectoryName(Assembly.GetCallingAssembly().GetName().CodeBase) + fileName.Substring(1);
+#endif
+ bool toFullPath = SQLiteConvert.ToBoolean(FindKey(opts, "ToFullPath", DefaultToFullPath.ToString()));
+ fileName = ExpandFileName(fileName, toFullPath);
+ }
+ }
+
+ try
+ {
+ bool usePooling = SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", GetDefaultPooling().ToString()));
+ int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", SQLiteConvert.ToString(DefaultMaxPoolSize)), CultureInfo.InvariantCulture);
+
+ _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", SQLiteConvert.ToString(DefaultConnectionTimeout)), CultureInfo.InvariantCulture);
+ _busyTimeout = Convert.ToInt32(FindKey(opts, "BusyTimeout", SQLiteConvert.ToString(DefaultBusyTimeout)), CultureInfo.InvariantCulture);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ _waitTimeout = Convert.ToInt32(FindKey(opts, "WaitTimeout", SQLiteConvert.ToString(DefaultWaitTimeout)), CultureInfo.InvariantCulture);
+#endif
+
+ _prepareRetries = Convert.ToInt32(FindKey(opts, "PrepareRetries", SQLiteConvert.ToString(DefaultPrepareRetries)), CultureInfo.InvariantCulture);
+ _progressOps = Convert.ToInt32(FindKey(opts, "ProgressOps", SQLiteConvert.ToString(DefaultProgressOps)), CultureInfo.InvariantCulture);
+
+ enumValue = TryParseEnum(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", DefaultIsolationLevel.ToString()), true);
+ _defaultIsolation = (enumValue is IsolationLevel) ? (IsolationLevel)enumValue : DefaultIsolationLevel;
+ _defaultIsolation = GetEffectiveIsolationLevel(_defaultIsolation);
+
+ if (_defaultIsolation != ImmediateIsolationLevel && _defaultIsolation != DeferredIsolationLevel)
+ throw new NotSupportedException("Invalid Default IsolationLevel specified");
+
+ _baseSchemaName = FindKey(opts, "BaseSchemaName", DefaultBaseSchemaName);
+
+ if (_sql == null)
+ {
+ SetupSQLiteBase(opts);
+ }
+
+ SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None;
+
+ if (!SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", DefaultFailIfMissing.ToString())))
+ flags |= SQLiteOpenFlagsEnum.Create;
+
+ if (SQLiteConvert.ToBoolean(FindKey(opts, "Read Only", DefaultReadOnly.ToString())))
+ {
+ flags |= SQLiteOpenFlagsEnum.ReadOnly;
+ // SQLite will return SQLITE_MISUSE on ReadOnly and Create
+ flags &= ~SQLiteOpenFlagsEnum.Create;
+ }
+ else
+ {
+ flags |= SQLiteOpenFlagsEnum.ReadWrite;
+ }
+
+ if (fullUri)
+ flags |= SQLiteOpenFlagsEnum.Uri;
+
+ _sql.Open(fileName, _vfsName, _flags, flags, maxPoolSize, usePooling);
+
+ _binaryGuid = SQLiteConvert.ToBoolean(FindKey(opts, "BinaryGUID", DefaultBinaryGUID.ToString()));
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ string hexPassword = FindKey(opts, "HexPassword", DefaultHexPassword);
+
+ if (hexPassword != null)
+ {
+ string error = null;
+ byte[] hexPasswordBytes = FromHexString(hexPassword, ref error);
+
+ if (hexPasswordBytes == null)
+ {
+ throw new FormatException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Cannot parse 'HexPassword' property value into byte values: {0}",
+ error));
+ }
+
+ _sql.SetPassword(hexPasswordBytes);
+ }
+ else
+ {
+ string password = FindKey(opts, "Password", DefaultPassword);
+
+ if (password != null)
+ {
+ byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(
+ password); /* throw */
+
+ _sql.SetPassword(passwordBytes);
+ }
+ else if (_password != null)
+ {
+ _sql.SetPassword(_password);
+ }
+ }
+
+ hexPassword = null; /* IMMUTABLE */
+ _password = null; /* IMMUTABLE */
+
+ if (hidePassword)
+ {
+ if (opts.ContainsKey("HexPassword"))
+ opts["HexPassword"] = String.Empty;
+
+ if (opts.ContainsKey("Password"))
+ opts["Password"] = String.Empty;
+
+ _connectionString = BuildConnectionString(opts);
+ }
+#else
+ if (FindKey(opts, "HexPassword", DefaultHexPassword) != null)
+ {
+ throw new SQLiteException(SQLiteErrorCode.Error,
+ "Cannot use \"HexPassword\" connection string property: " +
+ "library was not built with encryption support, please " +
+ "see \"https://www.sqlite.org/see\" for more information");
+ }
+
+ if (FindKey(opts, "Password", DefaultPassword) != null)
+ {
+ throw new SQLiteException(SQLiteErrorCode.Error,
+ "Cannot use \"Password\" connection string property: " +
+ "library was not built with encryption support, please " +
+ "see \"https://www.sqlite.org/see\" for more information");
+ }
+#endif
+
+ if (!fullUri)
+ _dataSource = Path.GetFileNameWithoutExtension(fileName);
+ else
+ _dataSource = fileName;
+
+ _version++;
+
+ ConnectionState oldstate = _connectionState;
+ _connectionState = ConnectionState.Open;
+
+ try
+ {
+ string strValue;
+ bool boolValue;
+
+ strValue = FindKey(opts, "SetDefaults", DefaultSetDefaults.ToString());
+ boolValue = SQLiteConvert.ToBoolean(strValue);
+
+ if (boolValue)
+ {
+ using (SQLiteCommand cmd = CreateCommand())
+ {
+ if (_busyTimeout != DefaultBusyTimeout)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA busy_timeout={0}", _busyTimeout);
+ cmd.ExecuteNonQuery();
+ }
+
+ int intValue;
+
+ if (!fullUri && !isMemory)
+ {
+ strValue = FindKey(opts, "Page Size", SQLiteConvert.ToString(DefaultPageSize));
+ intValue = Convert.ToInt32(strValue, CultureInfo.InvariantCulture);
+ if (intValue != DefaultPageSize)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA page_size={0}", intValue);
+ cmd.ExecuteNonQuery();
+ }
+ }
+
+ strValue = FindKey(opts, "Max Page Count", SQLiteConvert.ToString(DefaultMaxPageCount));
+ intValue = Convert.ToInt32(strValue, CultureInfo.InvariantCulture);
+ if (intValue != DefaultMaxPageCount)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA max_page_count={0}", intValue);
+ cmd.ExecuteNonQuery();
+ }
+
+ strValue = FindKey(opts, "Legacy Format", DefaultLegacyFormat.ToString());
+ boolValue = SQLiteConvert.ToBoolean(strValue);
+ if (boolValue != DefaultLegacyFormat)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA legacy_file_format={0}", boolValue ? "ON" : "OFF");
+ cmd.ExecuteNonQuery();
+ }
+
+ strValue = FindKey(opts, "Synchronous", DefaultSynchronous.ToString());
+ enumValue = TryParseEnum(typeof(SQLiteSynchronousEnum), strValue, true);
+ if (!(enumValue is SQLiteSynchronousEnum) || ((SQLiteSynchronousEnum)enumValue != DefaultSynchronous))
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA synchronous={0}", strValue);
+ cmd.ExecuteNonQuery();
+ }
+
+ strValue = FindKey(opts, "Cache Size", SQLiteConvert.ToString(DefaultCacheSize));
+ intValue = Convert.ToInt32(strValue, CultureInfo.InvariantCulture);
+ if (intValue != DefaultCacheSize)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA cache_size={0}", intValue);
+ cmd.ExecuteNonQuery();
+ }
+
+ strValue = FindKey(opts, "Journal Mode", DefaultJournalMode.ToString());
+ enumValue = TryParseEnum(typeof(SQLiteJournalModeEnum), strValue, true);
+ if (!(enumValue is SQLiteJournalModeEnum) || ((SQLiteJournalModeEnum)enumValue != DefaultJournalMode))
+ {
+ string pragmaStr = "PRAGMA journal_mode={0}";
+
+#if INTEROP_INCLUDE_ZIPVFS
+ if (useZipVfs)
+ pragmaStr = "PRAGMA zipvfs_journal_mode={0}";
+#endif
+
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, pragmaStr, strValue);
+ cmd.ExecuteNonQuery();
+ }
+
+ strValue = FindKey(opts, "Foreign Keys", DefaultForeignKeys.ToString());
+ boolValue = SQLiteConvert.ToBoolean(strValue);
+ if (boolValue != DefaultForeignKeys)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA foreign_keys={0}", boolValue ? "ON" : "OFF");
+ cmd.ExecuteNonQuery();
+ }
+
+ strValue = FindKey(opts, "Recursive Triggers", DefaultRecursiveTriggers.ToString());
+ boolValue = SQLiteConvert.ToBoolean(strValue);
+ if (boolValue != DefaultRecursiveTriggers)
+ {
+ cmd.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA recursive_triggers={0}", boolValue ? "ON" : "OFF");
+ cmd.ExecuteNonQuery();
+ }
+ }
+ }
+
+ if (_progressHandler != null)
+ _sql.SetProgressHook(_progressOps, _progressCallback);
+
+ if (_authorizerHandler != null)
+ _sql.SetAuthorizerHook(_authorizerCallback);
+
+ if (_commitHandler != null)
+ _sql.SetCommitHook(_commitCallback);
+
+ if (_updateHandler != null)
+ _sql.SetUpdateHook(_updateCallback);
+
+ if (_rollbackHandler != null)
+ _sql.SetRollbackHook(_rollbackCallback);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ System.Transactions.Transaction transaction = Transactions.Transaction.Current;
+
+ if (transaction != null &&
+ SQLiteConvert.ToBoolean(FindKey(opts, "Enlist", DefaultEnlist.ToString())))
+ {
+ EnlistTransaction(transaction);
+ }
+#endif
+
+ _connectionState = oldstate;
+
+ StateChangeEventArgs eventArgs = null;
+ OnStateChange(ConnectionState.Open, ref eventArgs);
+
+ OnChanged(this, new ConnectionEventArgs(
+ SQLiteConnectionEventType.Opened, eventArgs, null, null, null,
+ null, eventArgConnectionString, new object[] { eventArgOpts }));
+
+#if DEBUG
+ _debugString = HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture,
+ "openThreadId = {0}, connectionString = {1}",
+ HelperMethods.GetThreadId(),
+ eventArgConnectionString);
+#endif
+ }
+ catch
+ {
+ _connectionState = oldstate;
+ throw;
+ }
+ }
+ catch (SQLiteException)
+ {
+ Close();
+ throw;
+ }
+ }
+
+ ///
+ /// Opens the connection using the parameters found in the and then returns it.
+ ///
+ /// The current connection object.
+ public SQLiteConnection OpenAndReturn()
+ {
+ CheckDisposed(); Open(); return this;
+ }
+
+ ///
+ /// Gets/sets the default command timeout for newly-created commands. This is especially useful for
+ /// commands used internally such as inside a SQLiteTransaction, where setting the timeout is not possible.
+ /// This can also be set in the ConnectionString with "Default Timeout"
+ ///
+ public int DefaultTimeout
+ {
+ get { CheckDisposed(); return _defaultTimeout; }
+ set { CheckDisposed(); _defaultTimeout = value; }
+ }
+
+ ///
+ /// Gets/sets the default busy timeout to use with the SQLite core library. This is only used when
+ /// opening a connection.
+ ///
+ public int BusyTimeout
+ {
+ get { CheckDisposed(); return _busyTimeout; }
+ set { CheckDisposed(); _busyTimeout = value; }
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// EXPERIMENTAL --
+ /// The wait timeout to use with method.
+ /// This is only used when waiting for the enlistment to be reset prior to
+ /// enlisting in a transaction, and then only when the appropriate connection
+ /// flag is set.
+ ///
+ public int WaitTimeout
+ {
+ get { CheckDisposed(); return _waitTimeout; }
+ set { CheckDisposed(); _waitTimeout = value; }
+ }
+#endif
+
+ ///
+ /// The maximum number of retries when preparing SQL to be executed. This
+ /// normally only applies to preparation errors resulting from the database
+ /// schema being changed.
+ ///
+ public int PrepareRetries
+ {
+ get { CheckDisposed(); return _prepareRetries; }
+ set { CheckDisposed(); _prepareRetries = value; }
+ }
+
+ ///
+ /// The approximate number of virtual machine instructions between progress
+ /// events. In order for progress events to actually fire, the event handler
+ /// must be added to the event as
+ /// well. This value will only be used when the underlying native progress
+ /// callback needs to be changed.
+ ///
+ public int ProgressOps
+ {
+ get { CheckDisposed(); return _progressOps; }
+ set { CheckDisposed(); _progressOps = value; }
+ }
+
+ ///
+ /// Non-zero if the built-in (i.e. framework provided) connection string
+ /// parser should be used when opening the connection.
+ ///
+ public bool ParseViaFramework
+ {
+ get { CheckDisposed(); return _parseViaFramework; }
+ set { CheckDisposed(); _parseViaFramework = value; }
+ }
+
+ ///
+ /// Gets/sets the extra behavioral flags for this connection. See the
+ /// enumeration for a list of
+ /// possible values.
+ ///
+ public SQLiteConnectionFlags Flags
+ {
+ get { CheckDisposed(); return _flags; }
+ set { CheckDisposed(); _flags = value; }
+ }
+
+ ///
+ /// Gets/sets the default database type for this connection. This value
+ /// will only be used when not null.
+ ///
+ public DbType? DefaultDbType
+ {
+ get { CheckDisposed(); return _defaultDbType; }
+ set { CheckDisposed(); _defaultDbType = value; }
+ }
+
+ ///
+ /// Gets/sets the default database type name for this connection. This
+ /// value will only be used when not null.
+ ///
+ public string DefaultTypeName
+ {
+ get { CheckDisposed(); return _defaultTypeName; }
+ set { CheckDisposed(); _defaultTypeName = value; }
+ }
+
+ ///
+ /// Gets/sets the VFS name for this connection. This value will only be
+ /// used when opening the database.
+ ///
+ public string VfsName
+ {
+ get { CheckDisposed(); return _vfsName; }
+ set { CheckDisposed(); _vfsName = value; }
+ }
+
+ ///
+ /// Returns non-zero if the underlying native connection handle is
+ /// owned by this instance.
+ ///
+ public bool OwnHandle
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for checking handle.");
+
+ return _sql.OwnHandle;
+ }
+ }
+
+ ///
+ /// Returns the version of the underlying SQLite database engine
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public override string ServerVersion
+ {
+ get
+ {
+ CheckDisposed();
+ return SQLiteVersion;
+ //if (_connectionState != ConnectionState.Open)
+ // throw new InvalidOperationException();
+
+ //return _sql.Version;
+ }
+ }
+
+ ///
+ /// Returns the rowid of the most recent successful INSERT into the database from this connection.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public long LastInsertRowId
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting last insert rowid.");
+
+ return _sql.LastInsertRowId;
+ }
+ }
+
+ ///
+ /// This method causes any pending database operation to abort and return at
+ /// its earliest opportunity. This routine is typically called in response
+ /// to a user action such as pressing "Cancel" or Ctrl-C where the user wants
+ /// a long query operation to halt immediately. It is safe to call this
+ /// routine from any thread. However, it is not safe to call this routine
+ /// with a database connection that is closed or might close before this method
+ /// returns.
+ ///
+ public void Cancel()
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for query cancellation.");
+
+ _sql.Cancel(); /* throw */
+ }
+
+ ///
+ /// Returns the number of rows changed by the last INSERT, UPDATE, or DELETE statement executed on
+ /// this connection.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public int Changes
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting number of changes.");
+
+ return _sql.Changes;
+ }
+ }
+
+ ///
+ /// Checks if this connection to the specified database should be considered
+ /// read-only. An exception will be thrown if the database name specified
+ /// via cannot be found.
+ ///
+ ///
+ /// The name of a database associated with this connection -OR- null for the
+ /// main database.
+ ///
+ ///
+ /// Non-zero if this connection to the specified database should be considered
+ /// read-only.
+ ///
+ public bool IsReadOnly(
+ string name
+ )
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for checking read-only status.");
+
+ return _sql.IsReadOnly(name);
+ }
+
+ ///
+ /// Returns non-zero if the given database connection is in autocommit mode.
+ /// Autocommit mode is on by default. Autocommit mode is disabled by a BEGIN
+ /// statement. Autocommit mode is re-enabled by a COMMIT or ROLLBACK.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public bool AutoCommit
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting autocommit mode.");
+
+ return _sql.AutoCommit;
+ }
+ }
+
+ ///
+ /// Returns the amount of memory (in bytes) currently in use by the SQLite core library.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public long MemoryUsed
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting memory used.");
+
+ return _sql.MemoryUsed;
+ }
+ }
+
+ ///
+ /// Returns the maximum amount of memory (in bytes) used by the SQLite core library since the high-water mark was last reset.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public long MemoryHighwater
+ {
+ get
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting maximum memory used.");
+
+ return _sql.MemoryHighwater;
+ }
+ }
+
+ ///
+ /// Returns various global memory statistics for the SQLite core library via
+ /// a dictionary of key/value pairs. Currently, only the "MemoryUsed" and
+ /// "MemoryHighwater" keys are returned and they have values that correspond
+ /// to the values that could be obtained via the
+ /// and connection properties.
+ ///
+ ///
+ /// This dictionary will be populated with the global memory statistics. It
+ /// will be created if necessary.
+ ///
+ public static void GetMemoryStatistics(
+ ref IDictionary statistics
+ )
+ {
+ if (statistics == null)
+ statistics = new Dictionary();
+
+ statistics["MemoryUsed"] = SQLite3.StaticMemoryUsed;
+ statistics["MemoryHighwater"] = SQLite3.StaticMemoryHighwater;
+ }
+
+ ///
+ /// Attempts to free as much heap memory as possible for this database connection.
+ ///
+ public void ReleaseMemory()
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for releasing memory.");
+
+ SQLiteErrorCode rc = _sql.ReleaseMemory();
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ throw new SQLiteException(rc,
+ _sql.GetLastError("Could not release connection memory."));
+ }
+ }
+
+ ///
+ /// Attempts to free N bytes of heap memory by deallocating non-essential memory
+ /// allocations held by the database library. Memory used to cache database pages
+ /// to improve performance is an example of non-essential memory. This is a no-op
+ /// returning zero if the SQLite core library was not compiled with the compile-time
+ /// option SQLITE_ENABLE_MEMORY_MANAGEMENT. Optionally, attempts to reset and/or
+ /// compact the Win32 native heap, if applicable.
+ ///
+ ///
+ /// The requested number of bytes to free.
+ ///
+ ///
+ /// Non-zero to attempt a heap reset.
+ ///
+ ///
+ /// Non-zero to attempt heap compaction.
+ ///
+ ///
+ /// The number of bytes actually freed. This value may be zero.
+ ///
+ ///
+ /// This value will be non-zero if the heap reset was successful.
+ ///
+ ///
+ /// The size of the largest committed free block in the heap, in bytes.
+ /// This value will be zero unless heap compaction is enabled.
+ ///
+ ///
+ /// A standard SQLite return code (i.e. zero for success and non-zero
+ /// for failure).
+ ///
+ #pragma warning disable 3001
+ public static SQLiteErrorCode ReleaseMemory(
+ int nBytes,
+ bool reset,
+ bool compact,
+ ref int nFree,
+ ref bool resetOk,
+ ref uint nLargest
+ )
+ {
+ return SQLite3.StaticReleaseMemory(
+ nBytes, reset, compact, ref nFree, ref resetOk, ref nLargest);
+ }
+ #pragma warning restore 3001
+
+ ///
+ /// Sets the status of the memory usage tracking subsystem in the SQLite core library. By default, this is enabled.
+ /// If this is disabled, memory usage tracking will not be performed. This is not really a per-connection value, it is
+ /// global to the process.
+ ///
+ /// Non-zero to enable memory usage tracking, zero otherwise.
+ /// A standard SQLite return code (i.e. zero for success and non-zero for failure).
+ public static SQLiteErrorCode SetMemoryStatus(bool value)
+ {
+ return SQLite3.StaticSetMemoryStatus(value);
+ }
+
+ ///
+ /// Returns a string containing the define constants (i.e. compile-time
+ /// options) used to compile the core managed assembly, delimited with
+ /// spaces.
+ ///
+ public static string DefineConstants
+ {
+ get { return SQLite3.DefineConstants; }
+ }
+
+ ///
+ /// Returns the version of the underlying SQLite core library.
+ ///
+ public static string SQLiteVersion
+ {
+ get { return SQLite3.SQLiteVersion; }
+ }
+
+ ///
+ /// This method returns the string whose value is the same as the
+ /// SQLITE_SOURCE_ID C preprocessor macro used when compiling the
+ /// SQLite core library.
+ ///
+ public static string SQLiteSourceId
+ {
+ get { return SQLite3.SQLiteSourceId; }
+ }
+
+ ///
+ /// Returns a string containing the compile-time options used to
+ /// compile the SQLite core native library, delimited with spaces.
+ ///
+ public static string SQLiteCompileOptions
+ {
+ get { return SQLite3.SQLiteCompileOptions; }
+ }
+
+ ///
+ /// This method returns the version of the interop SQLite assembly
+ /// used. If the SQLite interop assembly is not in use or the
+ /// necessary information cannot be obtained for any reason, a null
+ /// value may be returned.
+ ///
+ public static string InteropVersion
+ {
+ get { return SQLite3.InteropVersion; }
+ }
+
+ ///
+ /// This method returns the string whose value contains the unique
+ /// identifier for the source checkout used to build the interop
+ /// assembly. If the SQLite interop assembly is not in use or the
+ /// necessary information cannot be obtained for any reason, a null
+ /// value may be returned.
+ ///
+ public static string InteropSourceId
+ {
+ get { return SQLite3.InteropSourceId; }
+ }
+
+ ///
+ /// Returns a string containing the compile-time options used to
+ /// compile the SQLite interop assembly, delimited with spaces.
+ ///
+ public static string InteropCompileOptions
+ {
+ get { return SQLite3.InteropCompileOptions; }
+ }
+
+ ///
+ /// This method returns the version of the managed components used
+ /// to interact with the SQLite core library. If the necessary
+ /// information cannot be obtained for any reason, a null value may
+ /// be returned.
+ ///
+ public static string ProviderVersion
+ {
+ get
+ {
+ return (_assembly != null) ?
+ _assembly.GetName().Version.ToString() : null;
+ }
+ }
+
+ ///
+ /// This method returns the string whose value contains the unique
+ /// identifier for the source checkout used to build the managed
+ /// components currently executing. If the necessary information
+ /// cannot be obtained for any reason, a null value may be returned.
+ ///
+ public static string ProviderSourceId
+ {
+ get
+ {
+ if (_assembly == null)
+ return null;
+
+ string sourceId = null;
+
+ if (_assembly.IsDefined(typeof(AssemblySourceIdAttribute), false))
+ {
+ AssemblySourceIdAttribute attribute =
+ (AssemblySourceIdAttribute)_assembly.GetCustomAttributes(
+ typeof(AssemblySourceIdAttribute), false)[0];
+
+ sourceId = attribute.SourceId;
+ }
+
+ string sourceTimeStamp = null;
+
+ if (_assembly.IsDefined(typeof(AssemblySourceTimeStampAttribute), false))
+ {
+ AssemblySourceTimeStampAttribute attribute =
+ (AssemblySourceTimeStampAttribute)_assembly.GetCustomAttributes(
+ typeof(AssemblySourceTimeStampAttribute), false)[0];
+
+ sourceTimeStamp = attribute.SourceTimeStamp;
+ }
+
+ if ((sourceId != null) || (sourceTimeStamp != null))
+ {
+ if (sourceId == null)
+ sourceId = "0000000000000000000000000000000000000000";
+
+ if (sourceTimeStamp == null)
+ sourceTimeStamp = "0000-00-00 00:00:00 UTC";
+
+ return HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture,
+ "{0} {1}", sourceId, sourceTimeStamp);
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the value of the specified setting, using the
+ /// cached setting names and values for the last connection that used
+ /// the method, when available.
+ ///
+ ///
+ /// The name of the setting.
+ ///
+ ///
+ /// The value to be returned if the setting has not been set explicitly
+ /// or cannot be determined.
+ ///
+ ///
+ /// The value of the cached setting is stored here if found; otherwise,
+ /// the value of is stored here.
+ ///
+ ///
+ /// Non-zero if the cached setting was found; otherwise, zero.
+ ///
+ private static bool TryGetLastCachedSetting(
+ string name,
+ object @default,
+ out object value
+ )
+ {
+ if (_lastConnectionInOpen == null)
+ {
+ value = @default;
+ return false;
+ }
+
+ return _lastConnectionInOpen.TryGetCachedSetting(
+ name, @default, out value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adds or sets the cached setting specified by
+ /// to the value specified by using the cached
+ /// setting names and values for the last connection that used the
+ /// method, when available.
+ ///
+ ///
+ /// The name of the cached setting to add or replace.
+ ///
+ ///
+ /// The new value of the cached setting.
+ ///
+ private static void SetLastCachedSetting(
+ string name, /* in */
+ object value /* in */
+ )
+ {
+ if (_lastConnectionInOpen == null)
+ return;
+
+ _lastConnectionInOpen.SetCachedSetting(name, value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The default connection flags to be used for all opened connections
+ /// when they are not present in the connection string.
+ ///
+ public static SQLiteConnectionFlags DefaultFlags
+ {
+ get
+ {
+ string name = "DefaultFlags_SQLiteConnection";
+ object value;
+
+ if (!TryGetLastCachedSetting(name, null, out value))
+ {
+ value = UnsafeNativeMethods.GetSettingValue(name, null);
+ SetLastCachedSetting(name, value);
+ }
+
+ if (value == null)
+ return FallbackDefaultFlags;
+
+ object enumValue = TryParseEnum(
+ typeof(SQLiteConnectionFlags), value.ToString(), true);
+
+ if (enumValue is SQLiteConnectionFlags)
+ return (SQLiteConnectionFlags)enumValue;
+
+ return FallbackDefaultFlags;
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The extra connection flags to be used for all opened connections.
+ ///
+ public static SQLiteConnectionFlags SharedFlags
+ {
+ get { lock (_syncRoot) { return _sharedFlags; } }
+ set { lock (_syncRoot) { _sharedFlags = value; } }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the state of the connection.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+#endif
+ public override ConnectionState State
+ {
+ get
+ {
+ CheckDisposed();
+ return _connectionState;
+ }
+ }
+
+ ///
+ /// Passes a shutdown request to the SQLite core library. Does not throw
+ /// an exception if the shutdown request fails.
+ ///
+ ///
+ /// A standard SQLite return code (i.e. zero for success and non-zero for
+ /// failure).
+ ///
+ public SQLiteErrorCode Shutdown()
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for shutdown.");
+
+ _sql.Close(false); /* NOTE: MUST be closed before shutdown. */
+ SQLiteErrorCode rc = _sql.Shutdown();
+
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ if (rc != SQLiteErrorCode.Ok)
+ System.Diagnostics.Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Shutdown (Instance) Failed: {0}", rc));
+#endif
+
+ return rc;
+ }
+
+ ///
+ /// Passes a shutdown request to the SQLite core library. Throws an
+ /// exception if the shutdown request fails and the no-throw parameter
+ /// is non-zero.
+ ///
+ ///
+ /// Non-zero to reset the database and temporary directories to their
+ /// default values, which should be null for both.
+ ///
+ ///
+ /// When non-zero, throw an exception if the shutdown request fails.
+ ///
+ public static void Shutdown(
+ bool directories,
+ bool noThrow
+ )
+ {
+ SQLiteErrorCode rc = SQLite3.StaticShutdown(directories);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+#if !NET_COMPACT_20 && TRACE_CONNECTION
+ System.Diagnostics.Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Shutdown (Static) Failed: {0}", rc));
+#endif
+
+ if (!noThrow)
+ throw new SQLiteException(rc, null);
+ }
+ }
+
+ /// Enables or disabled extended result codes returned by SQLite
+ public void SetExtendedResultCodes(bool bOnOff)
+ {
+ CheckDisposed();
+
+ if (_sql != null) _sql.SetExtendedResultCodes(bOnOff);
+ }
+ /// Enables or disabled extended result codes returned by SQLite
+ public SQLiteErrorCode ResultCode()
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting result code.");
+ return _sql.ResultCode();
+ }
+ /// Enables or disabled extended result codes returned by SQLite
+ public SQLiteErrorCode ExtendedResultCode()
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for getting extended result code.");
+ return _sql.ExtendedResultCode();
+ }
+
+ /// Add a log message via the SQLite sqlite3_log interface.
+ public void LogMessage(SQLiteErrorCode iErrCode, string zMessage)
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for logging message.");
+
+ _sql.LogMessage(iErrCode, zMessage);
+ }
+
+ /// Add a log message via the SQLite sqlite3_log interface.
+ public void LogMessage(int iErrCode, string zMessage)
+ {
+ CheckDisposed();
+
+ if (_sql == null)
+ throw new InvalidOperationException("Database connection not valid for logging message.");
+
+ _sql.LogMessage((SQLiteErrorCode)iErrCode, zMessage);
+ }
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+ ///
+ /// Change the password (or assign a password) to an open database.
+ ///
+ ///
+ /// No readers or writers may be active for this process. The database must already be open
+ /// and if it already was password protected, the existing password must already have been supplied.
+ ///
+ /// The new password to assign to the database
+ public void ChangePassword(string newPassword)
+ {
+ CheckDisposed();
+
+ if (!String.IsNullOrEmpty(newPassword))
+ {
+ byte[] newPasswordBytes = UTF8Encoding.UTF8.GetBytes(
+ newPassword); /* throw */
+
+ ChangePassword(newPasswordBytes);
+ }
+ else
+ {
+ ChangePassword((byte[])null);
+ }
+ }
+
+ ///
+ /// Change the password (or assign a password) to an open database.
+ ///
+ ///
+ /// No readers or writers may be active for this process. The database must already be open
+ /// and if it already was password protected, the existing password must already have been supplied.
+ ///
+ /// The new password to assign to the database
+ public void ChangePassword(byte[] newPassword)
+ {
+ CheckDisposed();
+
+ if (_connectionState != ConnectionState.Open)
+ throw new InvalidOperationException("Database must be opened before changing the password.");
+
+ _sql.ChangePassword(newPassword);
+ }
+
+ ///
+ /// Sets the password for a password-protected database. A password-protected database is
+ /// unusable for any operation until the password has been set.
+ ///
+ /// The password for the database
+ public void SetPassword(string databasePassword)
+ {
+ CheckDisposed();
+
+ if (!String.IsNullOrEmpty(databasePassword))
+ {
+ byte[] databasePasswordBytes = UTF8Encoding.UTF8.GetBytes(
+ databasePassword); /* throw */
+
+ SetPassword(databasePasswordBytes);
+ }
+ else
+ {
+ SetPassword((byte[])null);
+ }
+ }
+
+ ///
+ /// Sets the password for a password-protected database. A password-protected database is
+ /// unusable for any operation until the password has been set.
+ ///
+ /// The password for the database
+ public void SetPassword(byte[] databasePassword)
+ {
+ CheckDisposed();
+
+ if (_connectionState != ConnectionState.Closed)
+ throw new InvalidOperationException("Password can only be set before the database is opened.");
+
+ if (databasePassword != null)
+ if (databasePassword.Length == 0) databasePassword = null;
+
+ if ((databasePassword != null) &&
+ HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.HidePassword))
+ {
+ throw new InvalidOperationException(
+ "With 'HidePassword' enabled, passwords can only be set via the connection string.");
+ }
+
+ _password = databasePassword;
+ }
+#endif
+
+ ///
+ /// Queries or modifies the number of retries or the retry interval (in milliseconds) for
+ /// certain I/O operations that may fail due to anti-virus software.
+ ///
+ /// The number of times to retry the I/O operation. A negative value
+ /// will cause the current count to be queried and replace that negative value.
+ /// The number of milliseconds to wait before retrying the I/O
+ /// operation. This number is multiplied by the number of retry attempts so far to come
+ /// up with the final number of milliseconds to wait. A negative value will cause the
+ /// current interval to be queried and replace that negative value.
+ /// Zero for success, non-zero for error.
+ public SQLiteErrorCode SetAvRetry(ref int count, ref int interval)
+ {
+ CheckDisposed();
+
+ if (_connectionState != ConnectionState.Open)
+ throw new InvalidOperationException(
+ "Database must be opened before changing the AV retry parameters.");
+
+ SQLiteErrorCode rc;
+ IntPtr pArg = IntPtr.Zero;
+
+ try
+ {
+ pArg = Marshal.AllocHGlobal(sizeof(int) * 2);
+
+ Marshal.WriteInt32(pArg, 0, count);
+ Marshal.WriteInt32(pArg, sizeof(int), interval);
+
+ rc = _sql.FileControl(null, SQLITE_FCNTL_WIN32_AV_RETRY, pArg);
+
+ if (rc == SQLiteErrorCode.Ok)
+ {
+ count = Marshal.ReadInt32(pArg, 0);
+ interval = Marshal.ReadInt32(pArg, sizeof(int));
+ }
+ }
+ finally
+ {
+ if (pArg != IntPtr.Zero)
+ Marshal.FreeHGlobal(pArg);
+ }
+
+ return rc;
+ }
+
+ ///
+ /// Sets the chunk size for the primary file associated with this database
+ /// connection.
+ ///
+ ///
+ /// The new chunk size for the main database, in bytes.
+ ///
+ ///
+ /// Zero for success, non-zero for error.
+ ///
+ public SQLiteErrorCode SetChunkSize(int size)
+ {
+ CheckDisposed();
+
+ if (_connectionState != ConnectionState.Open)
+ throw new InvalidOperationException(
+ "Database must be opened before changing the chunk size.");
+
+ IntPtr pArg = IntPtr.Zero;
+
+ try
+ {
+ pArg = Marshal.AllocHGlobal(sizeof(int) * 1);
+
+ Marshal.WriteInt32(pArg, 0, size);
+
+ return _sql.FileControl(null, SQLITE_FCNTL_CHUNK_SIZE, pArg);
+ }
+ finally
+ {
+ if (pArg != IntPtr.Zero)
+ Marshal.FreeHGlobal(pArg);
+ }
+ }
+
+ ///
+ /// Removes one set of surrounding single -OR- double quotes from the string
+ /// value and returns the resulting string value. If the string is null, empty,
+ /// or contains quotes that are not balanced, nothing is done and the original
+ /// string value will be returned.
+ ///
+ /// The string value to process.
+ ///
+ /// The string value, modified to remove one set of surrounding single -OR-
+ /// double quotes, if applicable.
+ ///
+ private static string UnwrapString(string value)
+ {
+ if (String.IsNullOrEmpty(value))
+ {
+ //
+ // NOTE: The string is null or empty, return it verbatim.
+ //
+ return value;
+ }
+
+ int length = value.Length;
+
+ if ((value[0] == SQLiteConvert.QuoteChar) &&
+ (value[length - 1] == SQLiteConvert.QuoteChar))
+ {
+ //
+ // NOTE: Remove the first and last character, which are
+ // both double quotes.
+ //
+ return value.Substring(1, length - 2);
+ }
+
+ if ((value[0] == SQLiteConvert.AltQuoteChar) &&
+ (value[length - 1] == SQLiteConvert.AltQuoteChar))
+ {
+ //
+ // NOTE: Remove the first and last character, which are
+ // both single quotes.
+ //
+ return value.Substring(1, length - 2);
+ }
+
+ //
+ // NOTE: No match, return the input string verbatim.
+ //
+ return value;
+ }
+
+ ///
+ /// Determines the directory to be used when dealing with the "|DataDirectory|"
+ /// macro in a database file name.
+ ///
+ ///
+ /// The directory to use in place of the "|DataDirectory|" macro -OR- null if it
+ /// cannot be determined.
+ ///
+ private static string GetDataDirectory()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ string result = Path.GetDirectoryName(
+ Assembly.GetCallingAssembly().GetName().CodeBase);
+#else
+ string result = AppDomain.CurrentDomain.GetData(
+ "DataDirectory") as string;
+
+ if (String.IsNullOrEmpty(result))
+ result = AppDomain.CurrentDomain.BaseDirectory;
+#endif
+
+ return result;
+ }
+
+ ///
+ /// Expand the filename of the data source, resolving the |DataDirectory|
+ /// macro as appropriate.
+ ///
+ /// The database filename to expand
+ ///
+ /// Non-zero if the returned file name should be converted to a full path
+ /// (except when using the .NET Compact Framework).
+ ///
+ /// The expanded path and filename of the filename
+ private static string ExpandFileName(string sourceFile, bool toFullPath)
+ {
+ if (String.IsNullOrEmpty(sourceFile)) return sourceFile;
+
+ if (sourceFile.StartsWith(_dataDirectory, StringComparison.OrdinalIgnoreCase))
+ {
+ string dataDirectory = GetDataDirectory();
+
+ if (sourceFile.Length > _dataDirectory.Length)
+ {
+ if (sourceFile[_dataDirectory.Length] == Path.DirectorySeparatorChar ||
+ sourceFile[_dataDirectory.Length] == Path.AltDirectorySeparatorChar)
+ sourceFile = sourceFile.Remove(_dataDirectory.Length, 1);
+ }
+ sourceFile = Path.Combine(dataDirectory, sourceFile.Substring(_dataDirectory.Length));
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (toFullPath)
+ sourceFile = Path.GetFullPath(sourceFile);
+#endif
+
+ return sourceFile;
+ }
+
+ ///
+ /// The following commands are used to extract schema information out of the database. Valid schema types are:
+ ///
+ /// -
+ /// MetaDataCollections
+ ///
+ /// -
+ /// DataSourceInformation
+ ///
+ /// -
+ /// Catalogs
+ ///
+ /// -
+ /// Columns
+ ///
+ /// -
+ /// ForeignKeys
+ ///
+ /// -
+ /// Indexes
+ ///
+ /// -
+ /// IndexColumns
+ ///
+ /// -
+ /// Tables
+ ///
+ /// -
+ /// Views
+ ///
+ /// -
+ /// ViewColumns
+ ///
+ ///
+ ///
+ ///
+ /// Returns the MetaDataCollections schema
+ ///
+ /// A DataTable of the MetaDataCollections schema
+ public override DataTable GetSchema()
+ {
+ CheckDisposed();
+ return GetSchema("MetaDataCollections", null);
+ }
+
+ ///
+ /// Returns schema information of the specified collection
+ ///
+ /// The schema collection to retrieve
+ /// A DataTable of the specified collection
+ public override DataTable GetSchema(string collectionName)
+ {
+ CheckDisposed();
+ return GetSchema(collectionName, new string[0]);
+ }
+
+ ///
+ /// Retrieves schema information using the specified constraint(s) for the specified collection
+ ///
+ /// The collection to retrieve.
+ ///
+ /// The restrictions to impose. Typically, this may include:
+ ///
+ ///
+ /// restrictionValues element index
+ /// usage
+ ///
+ /// -
+ /// 0
+ /// The database (or catalog) name, if applicable.
+ ///
+ /// -
+ /// 1
+ /// The schema name. This is not used by this provider.
+ ///
+ /// -
+ /// 2
+ /// The table name, if applicable.
+ ///
+ /// -
+ /// 3
+ ///
+ /// Depends on .
+ /// When "IndexColumns", it is the index name; otherwise, it is the column name.
+ ///
+ ///
+ /// -
+ /// 4
+ ///
+ /// Depends on .
+ /// When "IndexColumns", it is the column name; otherwise, it is not used.
+ ///
+ ///
+ ///
+ ///
+ /// A DataTable of the specified collection
+ public override DataTable GetSchema(string collectionName, string[] restrictionValues)
+ {
+ CheckDisposed();
+
+ if (_connectionState != ConnectionState.Open)
+ throw new InvalidOperationException();
+
+ string[] parms = new string[5];
+
+ if (restrictionValues == null) restrictionValues = new string[0];
+ restrictionValues.CopyTo(parms, 0);
+
+ switch (collectionName.ToUpper(CultureInfo.InvariantCulture))
+ {
+ case "METADATACOLLECTIONS":
+ return Schema_MetaDataCollections();
+ case "DATASOURCEINFORMATION":
+ return Schema_DataSourceInformation();
+ case "DATATYPES":
+ return Schema_DataTypes();
+ case "COLUMNS":
+ case "TABLECOLUMNS":
+ return Schema_Columns(parms[0], parms[2], parms[3]);
+ case "INDEXES":
+ return Schema_Indexes(parms[0], parms[2], parms[3]);
+ case "TRIGGERS":
+ return Schema_Triggers(parms[0], parms[2], parms[3]);
+ case "INDEXCOLUMNS":
+ return Schema_IndexColumns(parms[0], parms[2], parms[3], parms[4]);
+ case "TABLES":
+ return Schema_Tables(parms[0], parms[2], parms[3]);
+ case "VIEWS":
+ return Schema_Views(parms[0], parms[2]);
+ case "VIEWCOLUMNS":
+ return Schema_ViewColumns(parms[0], parms[2], parms[3]);
+ case "FOREIGNKEYS":
+ return Schema_ForeignKeys(parms[0], parms[2], parms[3]);
+ case "CATALOGS":
+ return Schema_Catalogs(parms[0]);
+ case "RESERVEDWORDS":
+ return Schema_ReservedWords();
+ }
+ throw new NotSupportedException();
+ }
+
+ private static DataTable Schema_ReservedWords()
+ {
+ DataTable tbl = new DataTable("ReservedWords");
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("ReservedWord", typeof(string));
+ tbl.Columns.Add("MaximumVersion", typeof(string));
+ tbl.Columns.Add("MinimumVersion", typeof(string));
+
+ tbl.BeginLoadData();
+ DataRow row;
+ foreach (string word in SR.Keywords.Split(new char[] { ',' }))
+ {
+ row = tbl.NewRow();
+ row[0] = word;
+ tbl.Rows.Add(row);
+ }
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Builds a MetaDataCollections schema datatable
+ ///
+ /// DataTable
+ private static DataTable Schema_MetaDataCollections()
+ {
+ DataTable tbl = new DataTable("MetaDataCollections");
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("CollectionName", typeof(string));
+ tbl.Columns.Add("NumberOfRestrictions", typeof(int));
+ tbl.Columns.Add("NumberOfIdentifierParts", typeof(int));
+
+ tbl.BeginLoadData();
+
+ StringReader reader = new StringReader(SR.MetaDataCollections);
+ tbl.ReadXml(reader);
+ reader.Close();
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Builds a DataSourceInformation datatable
+ ///
+ /// DataTable
+ private DataTable Schema_DataSourceInformation()
+ {
+ DataTable tbl = new DataTable("DataSourceInformation");
+ DataRow row;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add(DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductName, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductVersion, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductVersionNormalized, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.GroupByBehavior, typeof(int));
+ tbl.Columns.Add(DbMetaDataColumnNames.IdentifierPattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.IdentifierCase, typeof(int));
+ tbl.Columns.Add(DbMetaDataColumnNames.OrderByColumnsInSelect, typeof(bool));
+ tbl.Columns.Add(DbMetaDataColumnNames.ParameterMarkerFormat, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.ParameterMarkerPattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.ParameterNameMaxLength, typeof(int));
+ tbl.Columns.Add(DbMetaDataColumnNames.ParameterNamePattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.QuotedIdentifierPattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.QuotedIdentifierCase, typeof(int));
+ tbl.Columns.Add(DbMetaDataColumnNames.StatementSeparatorPattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.StringLiteralPattern, typeof(string));
+ tbl.Columns.Add(DbMetaDataColumnNames.SupportedJoinOperators, typeof(int));
+
+ tbl.BeginLoadData();
+
+ row = tbl.NewRow();
+ row.ItemArray = new object[] {
+ null,
+ "SQLite",
+ _sql.Version,
+ _sql.Version,
+ 3,
+ @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)",
+ 1,
+ false,
+ "{0}",
+ @"@[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)",
+ 255,
+ @"^[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)",
+ @"(([^\[]|\]\])*)",
+ 1,
+ ";",
+ @"'(([^']|'')*)'",
+ 15
+ };
+ tbl.Rows.Add(row);
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Build a Columns schema
+ ///
+ /// The catalog (attached database) to query, can be null
+ /// The table to retrieve schema information for, can be null
+ /// The column to retrieve schema information for, can be null
+ /// DataTable
+ private DataTable Schema_Columns(string strCatalog, string strTable, string strColumn)
+ {
+ DataTable tbl = new DataTable("Columns");
+ DataRow row;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("COLUMN_NAME", typeof(string));
+ tbl.Columns.Add("COLUMN_GUID", typeof(Guid));
+ tbl.Columns.Add("COLUMN_PROPID", typeof(long));
+ tbl.Columns.Add("ORDINAL_POSITION", typeof(int));
+ tbl.Columns.Add("COLUMN_HASDEFAULT", typeof(bool));
+ tbl.Columns.Add("COLUMN_DEFAULT", typeof(string));
+ tbl.Columns.Add("COLUMN_FLAGS", typeof(long));
+ tbl.Columns.Add("IS_NULLABLE", typeof(bool));
+ tbl.Columns.Add("DATA_TYPE", typeof(string));
+ tbl.Columns.Add("TYPE_GUID", typeof(Guid));
+ tbl.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int));
+ tbl.Columns.Add("CHARACTER_OCTET_LENGTH", typeof(int));
+ tbl.Columns.Add("NUMERIC_PRECISION", typeof(int));
+ tbl.Columns.Add("NUMERIC_SCALE", typeof(int));
+ tbl.Columns.Add("DATETIME_PRECISION", typeof(long));
+ tbl.Columns.Add("CHARACTER_SET_CATALOG", typeof(string));
+ tbl.Columns.Add("CHARACTER_SET_SCHEMA", typeof(string));
+ tbl.Columns.Add("CHARACTER_SET_NAME", typeof(string));
+ tbl.Columns.Add("COLLATION_CATALOG", typeof(string));
+ tbl.Columns.Add("COLLATION_SCHEMA", typeof(string));
+ tbl.Columns.Add("COLLATION_NAME", typeof(string));
+ tbl.Columns.Add("DOMAIN_CATALOG", typeof(string));
+ tbl.Columns.Add("DOMAIN_NAME", typeof(string));
+ tbl.Columns.Add("DESCRIPTION", typeof(string));
+ tbl.Columns.Add("PRIMARY_KEY", typeof(bool));
+ tbl.Columns.Add("EDM_TYPE", typeof(string));
+ tbl.Columns.Add("AUTOINCREMENT", typeof(bool));
+ tbl.Columns.Add("UNIQUE", typeof(bool));
+
+ tbl.BeginLoadData();
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ using (SQLiteCommand cmdTables = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table' OR [type] LIKE 'view'", strCatalog, master), this))
+ using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())
+ {
+ while (rdTables.Read())
+ {
+ if (String.IsNullOrEmpty(strTable) || String.Compare(strTable, rdTables.GetString(2), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ try
+ {
+ using (SQLiteCommand cmd = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdTables.GetString(2)), this))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader(CommandBehavior.SchemaOnly))
+ using (DataTable tblSchema = rd.GetSchemaTable(true, true))
+ {
+ foreach (DataRow schemaRow in tblSchema.Rows)
+ {
+ if (String.Compare(schemaRow[SchemaTableColumn.ColumnName].ToString(), strColumn, StringComparison.OrdinalIgnoreCase) == 0
+ || strColumn == null)
+ {
+ row = tbl.NewRow();
+
+ row["NUMERIC_PRECISION"] = schemaRow[SchemaTableColumn.NumericPrecision];
+ row["NUMERIC_SCALE"] = schemaRow[SchemaTableColumn.NumericScale];
+ row["TABLE_NAME"] = rdTables.GetString(2);
+ row["COLUMN_NAME"] = schemaRow[SchemaTableColumn.ColumnName];
+ row["TABLE_CATALOG"] = strCatalog;
+ row["ORDINAL_POSITION"] = schemaRow[SchemaTableColumn.ColumnOrdinal];
+ row["COLUMN_HASDEFAULT"] = (schemaRow[SchemaTableOptionalColumn.DefaultValue] != DBNull.Value);
+ row["COLUMN_DEFAULT"] = schemaRow[SchemaTableOptionalColumn.DefaultValue];
+ row["IS_NULLABLE"] = schemaRow[SchemaTableColumn.AllowDBNull];
+ row["DATA_TYPE"] = schemaRow["DataTypeName"].ToString().ToLower(CultureInfo.InvariantCulture);
+ row["EDM_TYPE"] = SQLiteConvert.DbTypeToTypeName(this, (DbType)schemaRow[SchemaTableColumn.ProviderType], _flags).ToString().ToLower(CultureInfo.InvariantCulture);
+ row["CHARACTER_MAXIMUM_LENGTH"] = schemaRow[SchemaTableColumn.ColumnSize];
+ row["TABLE_SCHEMA"] = schemaRow[SchemaTableColumn.BaseSchemaName];
+ row["PRIMARY_KEY"] = schemaRow[SchemaTableColumn.IsKey];
+ row["AUTOINCREMENT"] = schemaRow[SchemaTableOptionalColumn.IsAutoIncrement];
+ row["COLLATION_NAME"] = schemaRow["CollationType"];
+ row["UNIQUE"] = schemaRow[SchemaTableColumn.IsUnique];
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ }
+ catch(SQLiteException)
+ {
+ }
+ }
+ }
+ }
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Returns index information for the given database and catalog
+ ///
+ /// The catalog (attached database) to query, can be null
+ /// The name of the index to retrieve information for, can be null
+ /// The table to retrieve index information for, can be null
+ /// DataTable
+ private DataTable Schema_Indexes(string strCatalog, string strTable, string strIndex)
+ {
+ DataTable tbl = new DataTable("Indexes");
+ DataRow row;
+ List primaryKeys = new List();
+ bool maybeRowId;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("INDEX_CATALOG", typeof(string));
+ tbl.Columns.Add("INDEX_SCHEMA", typeof(string));
+ tbl.Columns.Add("INDEX_NAME", typeof(string));
+ tbl.Columns.Add("PRIMARY_KEY", typeof(bool));
+ tbl.Columns.Add("UNIQUE", typeof(bool));
+ tbl.Columns.Add("CLUSTERED", typeof(bool));
+ tbl.Columns.Add("TYPE", typeof(int));
+ tbl.Columns.Add("FILL_FACTOR", typeof(int));
+ tbl.Columns.Add("INITIAL_SIZE", typeof(int));
+ tbl.Columns.Add("NULLS", typeof(int));
+ tbl.Columns.Add("SORT_BOOKMARKS", typeof(bool));
+ tbl.Columns.Add("AUTO_UPDATE", typeof(bool));
+ tbl.Columns.Add("NULL_COLLATION", typeof(int));
+ tbl.Columns.Add("ORDINAL_POSITION", typeof(int));
+ tbl.Columns.Add("COLUMN_NAME", typeof(string));
+ tbl.Columns.Add("COLUMN_GUID", typeof(Guid));
+ tbl.Columns.Add("COLUMN_PROPID", typeof(long));
+ tbl.Columns.Add("COLLATION", typeof(short));
+ tbl.Columns.Add("CARDINALITY", typeof(Decimal));
+ tbl.Columns.Add("PAGES", typeof(int));
+ tbl.Columns.Add("FILTER_CONDITION", typeof(string));
+ tbl.Columns.Add("INTEGRATED", typeof(bool));
+ tbl.Columns.Add("INDEX_DEFINITION", typeof(string));
+
+ tbl.BeginLoadData();
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ using (SQLiteCommand cmdTables = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))
+ using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())
+ {
+ while (rdTables.Read())
+ {
+ maybeRowId = false;
+ primaryKeys.Clear();
+ if (String.IsNullOrEmpty(strTable) || String.Compare(rdTables.GetString(2), strTable, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ // First, look for any rowid indexes -- which sqlite defines are INTEGER PRIMARY KEY columns.
+ // Such indexes are not listed in the indexes list but count as indexes just the same.
+ try
+ {
+ using (SQLiteCommand cmdTable = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].table_info([{1}])", strCatalog, rdTables.GetString(2)), this))
+ using (SQLiteDataReader rdTable = cmdTable.ExecuteReader())
+ {
+ while (rdTable.Read())
+ {
+ if (rdTable.GetInt32(5) != 0)
+ {
+ primaryKeys.Add(rdTable.GetInt32(0));
+
+ // If the primary key is of type INTEGER, then its a rowid and we need to make a fake index entry for it.
+ if (String.Compare(rdTable.GetString(2), "INTEGER", StringComparison.OrdinalIgnoreCase) == 0)
+ maybeRowId = true;
+ }
+ }
+ }
+ }
+ catch (SQLiteException)
+ {
+ }
+ if (primaryKeys.Count == 1 && maybeRowId == true)
+ {
+ row = tbl.NewRow();
+
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = rdTables.GetString(2);
+ row["INDEX_CATALOG"] = strCatalog;
+ row["PRIMARY_KEY"] = true;
+ row["INDEX_NAME"] = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "{1}_PK_{0}", rdTables.GetString(2), master);
+ row["UNIQUE"] = true;
+
+ if (String.Compare((string)row["INDEX_NAME"], strIndex, StringComparison.OrdinalIgnoreCase) == 0
+ || strIndex == null)
+ {
+ tbl.Rows.Add(row);
+ }
+
+ primaryKeys.Clear();
+ }
+
+ // Now fetch all the rest of the indexes.
+ try
+ {
+ using (SQLiteCommand cmd = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_list([{1}])", strCatalog, rdTables.GetString(2)), this))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())
+ {
+ while (rd.Read())
+ {
+ if (String.Compare(rd.GetString(1), strIndex, StringComparison.OrdinalIgnoreCase) == 0
+ || strIndex == null)
+ {
+ row = tbl.NewRow();
+
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = rdTables.GetString(2);
+ row["INDEX_CATALOG"] = strCatalog;
+ row["INDEX_NAME"] = rd.GetString(1);
+ row["UNIQUE"] = SQLiteConvert.ToBoolean(rd.GetValue(2), CultureInfo.InvariantCulture, false);
+ row["PRIMARY_KEY"] = false;
+
+ // get the index definition
+ using (SQLiteCommand cmdIndexes = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{2}] WHERE [type] LIKE 'index' AND [name] LIKE '{1}'", strCatalog, rd.GetString(1).Replace("'", "''"), master), this))
+ using (SQLiteDataReader rdIndexes = cmdIndexes.ExecuteReader())
+ {
+ while (rdIndexes.Read())
+ {
+ if (rdIndexes.IsDBNull(4) == false)
+ row["INDEX_DEFINITION"] = rdIndexes.GetString(4);
+ break;
+ }
+ }
+
+ // Now for the really hard work. Figure out which index is the primary key index.
+ // The only way to figure it out is to check if the index was an autoindex and if we have a non-rowid
+ // primary key, and all the columns in the given index match the primary key columns
+ if (primaryKeys.Count > 0 && rd.GetString(1).StartsWith("sqlite_autoindex_" + rdTables.GetString(2), StringComparison.InvariantCultureIgnoreCase) == true)
+ {
+ using (SQLiteCommand cmdDetails = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_info([{1}])", strCatalog, rd.GetString(1)), this))
+ using (SQLiteDataReader rdDetails = cmdDetails.ExecuteReader())
+ {
+ int nMatches = 0;
+ while (rdDetails.Read())
+ {
+ if (primaryKeys.Contains(rdDetails.GetInt32(1)) == false)
+ {
+ nMatches = 0;
+ break;
+ }
+ nMatches++;
+ }
+ if (nMatches == primaryKeys.Count)
+ {
+ row["PRIMARY_KEY"] = true;
+ primaryKeys.Clear();
+ }
+ }
+ }
+
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ }
+ catch (SQLiteException)
+ {
+ }
+ }
+ }
+ }
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ private DataTable Schema_Triggers(string catalog, string table, string triggerName)
+ {
+ DataTable tbl = new DataTable("Triggers");
+ DataRow row;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("TRIGGER_NAME", typeof(string));
+ tbl.Columns.Add("TRIGGER_DEFINITION", typeof(string));
+
+ tbl.BeginLoadData();
+
+ if (String.IsNullOrEmpty(table)) table = null;
+ if (String.IsNullOrEmpty(catalog)) catalog = GetDefaultCatalogName();
+ string master = GetMasterTableName(IsTemporaryCatalogName(catalog));
+
+ using (SQLiteCommand cmd = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT [type], [name], [tbl_name], [rootpage], [sql], [rowid] FROM [{0}].[{1}] WHERE [type] LIKE 'trigger'", catalog, master), this))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())
+ {
+ while (rd.Read())
+ {
+ if (String.Compare(rd.GetString(1), triggerName, StringComparison.OrdinalIgnoreCase) == 0
+ || triggerName == null)
+ {
+ if (table == null || String.Compare(table, rd.GetString(2), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ row = tbl.NewRow();
+
+ row["TABLE_CATALOG"] = catalog;
+ row["TABLE_NAME"] = rd.GetString(2);
+ row["TRIGGER_NAME"] = rd.GetString(1);
+ row["TRIGGER_DEFINITION"] = rd.GetString(4);
+
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ }
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Retrieves table schema information for the database and catalog
+ ///
+ /// The catalog (attached database) to retrieve tables on
+ /// The table to retrieve, can be null
+ /// The table type, can be null
+ /// DataTable
+ private DataTable Schema_Tables(string strCatalog, string strTable, string strType)
+ {
+ DataTable tbl = new DataTable("Tables");
+ DataRow row;
+ string strItem;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("TABLE_TYPE", typeof(string));
+ tbl.Columns.Add("TABLE_ID", typeof(long));
+ tbl.Columns.Add("TABLE_ROOTPAGE", typeof(int));
+ tbl.Columns.Add("TABLE_DEFINITION", typeof(string));
+ tbl.BeginLoadData();
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ using (SQLiteCommand cmd = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT [type], [name], [tbl_name], [rootpage], [sql], [rowid] FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())
+ {
+ while (rd.Read())
+ {
+ strItem = rd.GetString(0);
+ if (String.Compare(rd.GetString(2), 0, "SQLITE_", 0, 7, StringComparison.OrdinalIgnoreCase) == 0)
+ strItem = "SYSTEM_TABLE";
+
+ if (String.Compare(strType, strItem, StringComparison.OrdinalIgnoreCase) == 0
+ || strType == null)
+ {
+ if (String.Compare(rd.GetString(2), strTable, StringComparison.OrdinalIgnoreCase) == 0
+ || strTable == null)
+ {
+ row = tbl.NewRow();
+
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = rd.GetString(2);
+ row["TABLE_TYPE"] = strItem;
+ row["TABLE_ID"] = rd.GetInt64(5);
+ row["TABLE_ROOTPAGE"] = rd.GetInt32(3);
+ row["TABLE_DEFINITION"] = rd.GetString(4);
+
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ }
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Retrieves view schema information for the database
+ ///
+ /// The catalog (attached database) to retrieve views on
+ /// The view name, can be null
+ /// DataTable
+ private DataTable Schema_Views(string strCatalog, string strView)
+ {
+ DataTable tbl = new DataTable("Views");
+ DataRow row;
+ string strItem;
+ int nPos;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("VIEW_DEFINITION", typeof(string));
+ tbl.Columns.Add("CHECK_OPTION", typeof(bool));
+ tbl.Columns.Add("IS_UPDATABLE", typeof(bool));
+ tbl.Columns.Add("DESCRIPTION", typeof(string));
+ tbl.Columns.Add("DATE_CREATED", typeof(DateTime));
+ tbl.Columns.Add("DATE_MODIFIED", typeof(DateTime));
+
+ tbl.BeginLoadData();
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ using (SQLiteCommand cmd = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'view'", strCatalog, master), this))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())
+ {
+ while (rd.Read())
+ {
+ if (String.Compare(rd.GetString(1), strView, StringComparison.OrdinalIgnoreCase) == 0
+ || String.IsNullOrEmpty(strView))
+ {
+ strItem = rd.GetString(4).Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ');
+ nPos = CultureInfo.InvariantCulture.CompareInfo.IndexOf(strItem, " AS ", CompareOptions.IgnoreCase);
+ if (nPos > -1)
+ {
+ strItem = strItem.Substring(nPos + 4).Trim();
+ row = tbl.NewRow();
+
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = rd.GetString(2);
+ row["IS_UPDATABLE"] = false;
+ row["VIEW_DEFINITION"] = strItem;
+
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ }
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Retrieves catalog (attached databases) schema information for the database
+ ///
+ /// The catalog to retrieve, can be null
+ /// DataTable
+ private DataTable Schema_Catalogs(string strCatalog)
+ {
+ DataTable tbl = new DataTable("Catalogs");
+ DataRow row;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("CATALOG_NAME", typeof(string));
+ tbl.Columns.Add("DESCRIPTION", typeof(string));
+ tbl.Columns.Add("ID", typeof(long));
+
+ tbl.BeginLoadData();
+
+ using (SQLiteCommand cmd = new SQLiteCommand("PRAGMA database_list", this))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader())
+ {
+ while (rd.Read())
+ {
+ if (String.Compare(rd.GetString(1), strCatalog, StringComparison.OrdinalIgnoreCase) == 0
+ || strCatalog == null)
+ {
+ row = tbl.NewRow();
+
+ row["CATALOG_NAME"] = rd.GetString(1);
+ row["DESCRIPTION"] = rd.GetString(2);
+ row["ID"] = rd.GetInt64(0);
+
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ private DataTable Schema_DataTypes()
+ {
+ DataTable tbl = new DataTable("DataTypes");
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("TypeName", typeof(String));
+ tbl.Columns.Add("ProviderDbType", typeof(int));
+ tbl.Columns.Add("ColumnSize", typeof(long));
+ tbl.Columns.Add("CreateFormat", typeof(String));
+ tbl.Columns.Add("CreateParameters", typeof(String));
+ tbl.Columns.Add("DataType", typeof(String));
+ tbl.Columns.Add("IsAutoIncrementable", typeof(bool));
+ tbl.Columns.Add("IsBestMatch", typeof(bool));
+ tbl.Columns.Add("IsCaseSensitive", typeof(bool));
+ tbl.Columns.Add("IsFixedLength", typeof(bool));
+ tbl.Columns.Add("IsFixedPrecisionScale", typeof(bool));
+ tbl.Columns.Add("IsLong", typeof(bool));
+ tbl.Columns.Add("IsNullable", typeof(bool));
+ tbl.Columns.Add("IsSearchable", typeof(bool));
+ tbl.Columns.Add("IsSearchableWithLike", typeof(bool));
+ tbl.Columns.Add("IsLiteralSupported", typeof(bool));
+ tbl.Columns.Add("LiteralPrefix", typeof(String));
+ tbl.Columns.Add("LiteralSuffix", typeof(String));
+ tbl.Columns.Add("IsUnsigned", typeof(bool));
+ tbl.Columns.Add("MaximumScale", typeof(short));
+ tbl.Columns.Add("MinimumScale", typeof(short));
+ tbl.Columns.Add("IsConcurrencyType", typeof(bool));
+
+ tbl.BeginLoadData();
+
+ StringReader reader = new StringReader(SR.DataTypes);
+ tbl.ReadXml(reader);
+ reader.Close();
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Returns the base column information for indexes in a database
+ ///
+ /// The catalog to retrieve indexes for (can be null)
+ /// The table to restrict index information by (can be null)
+ /// The index to restrict index information by (can be null)
+ /// The source column to restrict index information by (can be null)
+ /// A DataTable containing the results
+ private DataTable Schema_IndexColumns(string strCatalog, string strTable, string strIndex, string strColumn)
+ {
+ DataTable tbl = new DataTable("IndexColumns");
+ DataRow row;
+ List> primaryKeys = new List>();
+ bool maybeRowId;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("CONSTRAINT_CATALOG", typeof(string));
+ tbl.Columns.Add("CONSTRAINT_SCHEMA", typeof(string));
+ tbl.Columns.Add("CONSTRAINT_NAME", typeof(string));
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("COLUMN_NAME", typeof(string));
+ tbl.Columns.Add("ORDINAL_POSITION", typeof(int));
+ tbl.Columns.Add("INDEX_NAME", typeof(string));
+ tbl.Columns.Add("COLLATION_NAME", typeof(string));
+ tbl.Columns.Add("SORT_MODE", typeof(string));
+ tbl.Columns.Add("CONFLICT_OPTION", typeof(int));
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ tbl.BeginLoadData();
+
+ using (SQLiteCommand cmdTables = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))
+ using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())
+ {
+ while (rdTables.Read())
+ {
+ maybeRowId = false;
+ primaryKeys.Clear();
+ if (String.IsNullOrEmpty(strTable) || String.Compare(rdTables.GetString(2), strTable, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ try
+ {
+ using (SQLiteCommand cmdTable = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].table_info([{1}])", strCatalog, rdTables.GetString(2)), this))
+ using (SQLiteDataReader rdTable = cmdTable.ExecuteReader())
+ {
+ while (rdTable.Read())
+ {
+ if (rdTable.GetInt32(5) == 1) // is a primary key
+ {
+ primaryKeys.Add(new KeyValuePair(rdTable.GetInt32(0), rdTable.GetString(1)));
+ // Is an integer -- could be a rowid if no other primary keys exist in the table
+ if (String.Compare(rdTable.GetString(2), "INTEGER", StringComparison.OrdinalIgnoreCase) == 0)
+ maybeRowId = true;
+ }
+ }
+ }
+ }
+ catch (SQLiteException)
+ {
+ }
+ // This is a rowid row
+ if (primaryKeys.Count == 1 && maybeRowId == true)
+ {
+ row = tbl.NewRow();
+ row["CONSTRAINT_CATALOG"] = strCatalog;
+ row["CONSTRAINT_NAME"] = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "{1}_PK_{0}", rdTables.GetString(2), master);
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = rdTables.GetString(2);
+ row["COLUMN_NAME"] = primaryKeys[0].Value;
+ row["INDEX_NAME"] = row["CONSTRAINT_NAME"];
+ row["ORDINAL_POSITION"] = 0; // primaryKeys[0].Key;
+ row["COLLATION_NAME"] = "BINARY";
+ row["SORT_MODE"] = "ASC";
+ row["CONFLICT_OPTION"] = 2;
+
+ if (String.IsNullOrEmpty(strIndex) || String.Compare(strIndex, (string)row["INDEX_NAME"], StringComparison.OrdinalIgnoreCase) == 0)
+ tbl.Rows.Add(row);
+ }
+
+ using (SQLiteCommand cmdIndexes = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{2}] WHERE [type] LIKE 'index' AND [tbl_name] LIKE '{1}'", strCatalog, rdTables.GetString(2).Replace("'", "''"), master), this))
+ using (SQLiteDataReader rdIndexes = cmdIndexes.ExecuteReader())
+ {
+ while (rdIndexes.Read())
+ {
+ int ordinal = 0;
+ if (String.IsNullOrEmpty(strIndex) || String.Compare(strIndex, rdIndexes.GetString(1), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ try
+ {
+ using (SQLiteCommand cmdIndex = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_info([{1}])", strCatalog, rdIndexes.GetString(1)), this))
+ using (SQLiteDataReader rdIndex = cmdIndex.ExecuteReader())
+ {
+ while (rdIndex.Read())
+ {
+ string columnName = rdIndex.IsDBNull(2) ? null : rdIndex.GetString(2);
+
+ row = tbl.NewRow();
+ row["CONSTRAINT_CATALOG"] = strCatalog;
+ row["CONSTRAINT_NAME"] = rdIndexes.GetString(1);
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = rdIndexes.GetString(2);
+ row["COLUMN_NAME"] = columnName;
+ row["INDEX_NAME"] = rdIndexes.GetString(1);
+ row["ORDINAL_POSITION"] = ordinal; // rdIndex.GetInt32(1);
+
+ string collationSequence = null;
+ int sortMode = 0;
+ int onError = 0;
+
+ if (columnName != null)
+ _sql.GetIndexColumnExtendedInfo(strCatalog, rdIndexes.GetString(1), columnName, ref sortMode, ref onError, ref collationSequence);
+
+ if (String.IsNullOrEmpty(collationSequence) == false)
+ row["COLLATION_NAME"] = collationSequence;
+
+ row["SORT_MODE"] = (sortMode == 0) ? "ASC" : "DESC";
+ row["CONFLICT_OPTION"] = onError;
+
+ ordinal++;
+
+ if ((strColumn == null) || String.Compare(strColumn, columnName, StringComparison.OrdinalIgnoreCase) == 0)
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ catch (SQLiteException)
+ {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ tbl.EndLoadData();
+ tbl.AcceptChanges();
+
+ return tbl;
+ }
+
+ ///
+ /// Returns detailed column information for a specified view
+ ///
+ /// The catalog to retrieve columns for (can be null)
+ /// The view to restrict column information by (can be null)
+ /// The source column to restrict column information by (can be null)
+ /// A DataTable containing the results
+ private DataTable Schema_ViewColumns(string strCatalog, string strView, string strColumn)
+ {
+ DataTable tbl = new DataTable("ViewColumns");
+ DataRow row;
+ string strSql;
+ int n;
+ DataRow schemaRow;
+ DataRow viewRow;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("VIEW_CATALOG", typeof(string));
+ tbl.Columns.Add("VIEW_SCHEMA", typeof(string));
+ tbl.Columns.Add("VIEW_NAME", typeof(string));
+ tbl.Columns.Add("VIEW_COLUMN_NAME", typeof(String));
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("COLUMN_NAME", typeof(string));
+ tbl.Columns.Add("ORDINAL_POSITION", typeof(int));
+ tbl.Columns.Add("COLUMN_HASDEFAULT", typeof(bool));
+ tbl.Columns.Add("COLUMN_DEFAULT", typeof(string));
+ tbl.Columns.Add("COLUMN_FLAGS", typeof(long));
+ tbl.Columns.Add("IS_NULLABLE", typeof(bool));
+ tbl.Columns.Add("DATA_TYPE", typeof(string));
+ tbl.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int));
+ tbl.Columns.Add("NUMERIC_PRECISION", typeof(int));
+ tbl.Columns.Add("NUMERIC_SCALE", typeof(int));
+ tbl.Columns.Add("DATETIME_PRECISION", typeof(long));
+ tbl.Columns.Add("CHARACTER_SET_CATALOG", typeof(string));
+ tbl.Columns.Add("CHARACTER_SET_SCHEMA", typeof(string));
+ tbl.Columns.Add("CHARACTER_SET_NAME", typeof(string));
+ tbl.Columns.Add("COLLATION_CATALOG", typeof(string));
+ tbl.Columns.Add("COLLATION_SCHEMA", typeof(string));
+ tbl.Columns.Add("COLLATION_NAME", typeof(string));
+ tbl.Columns.Add("PRIMARY_KEY", typeof(bool));
+ tbl.Columns.Add("EDM_TYPE", typeof(string));
+ tbl.Columns.Add("AUTOINCREMENT", typeof(bool));
+ tbl.Columns.Add("UNIQUE", typeof(bool));
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ tbl.BeginLoadData();
+
+ using (SQLiteCommand cmdViews = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'view'", strCatalog, master), this))
+ using (SQLiteDataReader rdViews = cmdViews.ExecuteReader())
+ {
+ while (rdViews.Read())
+ {
+ if (String.IsNullOrEmpty(strView) || String.Compare(strView, rdViews.GetString(2), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ using (SQLiteCommand cmdViewSelect = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdViews.GetString(2)), this))
+ {
+ strSql = rdViews.GetString(4).Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' ');
+ n = CultureInfo.InvariantCulture.CompareInfo.IndexOf(strSql, " AS ", CompareOptions.IgnoreCase);
+ if (n < 0)
+ continue;
+
+ strSql = strSql.Substring(n + 4);
+
+ using (SQLiteCommand cmd = new SQLiteCommand(strSql, this))
+ using (SQLiteDataReader rdViewSelect = cmdViewSelect.ExecuteReader(CommandBehavior.SchemaOnly))
+ using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader(CommandBehavior.SchemaOnly))
+ using (DataTable tblSchemaView = rdViewSelect.GetSchemaTable(false, false))
+ using (DataTable tblSchema = rd.GetSchemaTable(false, false))
+ {
+ for (n = 0; n < tblSchema.Rows.Count; n++)
+ {
+ viewRow = tblSchemaView.Rows[n];
+ schemaRow = tblSchema.Rows[n];
+
+ if (String.Compare(viewRow[SchemaTableColumn.ColumnName].ToString(), strColumn, StringComparison.OrdinalIgnoreCase) == 0
+ || strColumn == null)
+ {
+ row = tbl.NewRow();
+
+ row["VIEW_CATALOG"] = strCatalog;
+ row["VIEW_NAME"] = rdViews.GetString(2);
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_SCHEMA"] = schemaRow[SchemaTableColumn.BaseSchemaName];
+ row["TABLE_NAME"] = schemaRow[SchemaTableColumn.BaseTableName];
+ row["COLUMN_NAME"] = schemaRow[SchemaTableColumn.BaseColumnName];
+ row["VIEW_COLUMN_NAME"] = viewRow[SchemaTableColumn.ColumnName];
+ row["COLUMN_HASDEFAULT"] = (viewRow[SchemaTableOptionalColumn.DefaultValue] != DBNull.Value);
+ row["COLUMN_DEFAULT"] = viewRow[SchemaTableOptionalColumn.DefaultValue];
+ row["ORDINAL_POSITION"] = viewRow[SchemaTableColumn.ColumnOrdinal];
+ row["IS_NULLABLE"] = viewRow[SchemaTableColumn.AllowDBNull];
+ row["DATA_TYPE"] = viewRow["DataTypeName"]; // SQLiteConvert.DbTypeToType((DbType)viewRow[SchemaTableColumn.ProviderType]).ToString();
+ row["EDM_TYPE"] = SQLiteConvert.DbTypeToTypeName(this, (DbType)viewRow[SchemaTableColumn.ProviderType], _flags).ToString().ToLower(CultureInfo.InvariantCulture);
+ row["CHARACTER_MAXIMUM_LENGTH"] = viewRow[SchemaTableColumn.ColumnSize];
+ row["TABLE_SCHEMA"] = viewRow[SchemaTableColumn.BaseSchemaName];
+ row["PRIMARY_KEY"] = viewRow[SchemaTableColumn.IsKey];
+ row["AUTOINCREMENT"] = viewRow[SchemaTableOptionalColumn.IsAutoIncrement];
+ row["COLLATION_NAME"] = viewRow["CollationType"];
+ row["UNIQUE"] = viewRow[SchemaTableColumn.IsUnique];
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ tbl.EndLoadData();
+ tbl.AcceptChanges();
+
+ return tbl;
+ }
+
+ ///
+ /// Retrieves foreign key information from the specified set of filters
+ ///
+ /// An optional catalog to restrict results on
+ /// An optional table to restrict results on
+ /// An optional foreign key name to restrict results on
+ /// A DataTable with the results of the query
+ private DataTable Schema_ForeignKeys(string strCatalog, string strTable, string strKeyName)
+ {
+ DataTable tbl = new DataTable("ForeignKeys");
+ DataRow row;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add("CONSTRAINT_CATALOG", typeof(string));
+ tbl.Columns.Add("CONSTRAINT_SCHEMA", typeof(string));
+ tbl.Columns.Add("CONSTRAINT_NAME", typeof(string));
+ tbl.Columns.Add("TABLE_CATALOG", typeof(string));
+ tbl.Columns.Add("TABLE_SCHEMA", typeof(string));
+ tbl.Columns.Add("TABLE_NAME", typeof(string));
+ tbl.Columns.Add("CONSTRAINT_TYPE", typeof(string));
+ tbl.Columns.Add("IS_DEFERRABLE", typeof(bool));
+ tbl.Columns.Add("INITIALLY_DEFERRED", typeof(bool));
+ tbl.Columns.Add("FKEY_ID", typeof(int));
+ tbl.Columns.Add("FKEY_FROM_COLUMN", typeof(string));
+ tbl.Columns.Add("FKEY_FROM_ORDINAL_POSITION", typeof(int));
+ tbl.Columns.Add("FKEY_TO_CATALOG", typeof(string));
+ tbl.Columns.Add("FKEY_TO_SCHEMA", typeof(string));
+ tbl.Columns.Add("FKEY_TO_TABLE", typeof(string));
+ tbl.Columns.Add("FKEY_TO_COLUMN", typeof(string));
+ tbl.Columns.Add("FKEY_ON_UPDATE", typeof(string));
+ tbl.Columns.Add("FKEY_ON_DELETE", typeof(string));
+ tbl.Columns.Add("FKEY_MATCH", typeof(string));
+
+ if (String.IsNullOrEmpty(strCatalog)) strCatalog = GetDefaultCatalogName();
+
+ string master = GetMasterTableName(IsTemporaryCatalogName(strCatalog));
+
+ tbl.BeginLoadData();
+
+ using (SQLiteCommand cmdTables = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this))
+ using (SQLiteDataReader rdTables = cmdTables.ExecuteReader())
+ {
+ while (rdTables.Read())
+ {
+ if (String.IsNullOrEmpty(strTable) || String.Compare(strTable, rdTables.GetString(2), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ try
+ {
+ using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder())
+ using (SQLiteCommand cmdKey = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].foreign_key_list([{1}])", strCatalog, rdTables.GetString(2)), this))
+ using (SQLiteDataReader rdKey = cmdKey.ExecuteReader())
+ {
+ while (rdKey.Read())
+ {
+ row = tbl.NewRow();
+ row["CONSTRAINT_CATALOG"] = strCatalog;
+ row["CONSTRAINT_NAME"] = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "FK_{0}_{1}_{2}", rdTables[2], rdKey.GetInt32(0), rdKey.GetInt32(1));
+ row["TABLE_CATALOG"] = strCatalog;
+ row["TABLE_NAME"] = builder.UnquoteIdentifier(rdTables.GetString(2));
+ row["CONSTRAINT_TYPE"] = "FOREIGN KEY";
+ row["IS_DEFERRABLE"] = false;
+ row["INITIALLY_DEFERRED"] = false;
+ row["FKEY_ID"] = rdKey[0];
+ row["FKEY_FROM_COLUMN"] = builder.UnquoteIdentifier(rdKey[3].ToString());
+ row["FKEY_TO_CATALOG"] = strCatalog;
+ row["FKEY_TO_TABLE"] = builder.UnquoteIdentifier(rdKey[2].ToString());
+ row["FKEY_TO_COLUMN"] = builder.UnquoteIdentifier(rdKey[4].ToString());
+ row["FKEY_FROM_ORDINAL_POSITION"] = rdKey[1];
+ row["FKEY_ON_UPDATE"] = (rdKey.FieldCount > 5) ? rdKey[5] : String.Empty;
+ row["FKEY_ON_DELETE"] = (rdKey.FieldCount > 6) ? rdKey[6] : String.Empty;
+ row["FKEY_MATCH"] = (rdKey.FieldCount > 7) ? rdKey[7] : String.Empty;
+
+ if (String.IsNullOrEmpty(strKeyName) || String.Compare(strKeyName, row["CONSTRAINT_NAME"].ToString(), StringComparison.OrdinalIgnoreCase) == 0)
+ tbl.Rows.Add(row);
+ }
+ }
+ }
+ catch (SQLiteException)
+ {
+ }
+ }
+ }
+ }
+
+ tbl.EndLoadData();
+ tbl.AcceptChanges();
+
+ return tbl;
+ }
+
+ ///
+ /// This event is raised periodically during long running queries. Changing
+ /// the value of the property will
+ /// determine if the operation in progress will continue or be interrupted.
+ /// For the entire duration of the event, the associated connection and
+ /// statement objects must not be modified, either directly or indirectly, by
+ /// the called code.
+ ///
+ public event SQLiteProgressEventHandler Progress
+ {
+ add
+ {
+ CheckDisposed();
+
+ if (_progressHandler == null)
+ {
+ _progressCallback = new SQLiteProgressCallback(ProgressCallback);
+ if (_sql != null) _sql.SetProgressHook(_progressOps, _progressCallback);
+ }
+ _progressHandler += value;
+ }
+ remove
+ {
+ CheckDisposed();
+
+ _progressHandler -= value;
+ if (_progressHandler == null)
+ {
+ if (_sql != null) _sql.SetProgressHook(0, null);
+ _progressCallback = null;
+ }
+ }
+ }
+
+ ///
+ /// This event is raised whenever SQLite encounters an action covered by the
+ /// authorizer during query preparation. Changing the value of the
+ /// property will determine if
+ /// the specific action will be allowed, ignored, or denied. For the entire
+ /// duration of the event, the associated connection and statement objects
+ /// must not be modified, either directly or indirectly, by the called code.
+ ///
+ public event SQLiteAuthorizerEventHandler Authorize
+ {
+ add
+ {
+ CheckDisposed();
+
+ if (_authorizerHandler == null)
+ {
+ _authorizerCallback = new SQLiteAuthorizerCallback(AuthorizerCallback);
+ if (_sql != null) _sql.SetAuthorizerHook(_authorizerCallback);
+ }
+ _authorizerHandler += value;
+ }
+ remove
+ {
+ CheckDisposed();
+
+ _authorizerHandler -= value;
+ if (_authorizerHandler == null)
+ {
+ if (_sql != null) _sql.SetAuthorizerHook(null);
+ _authorizerCallback = null;
+ }
+ }
+ }
+
+ ///
+ /// This event is raised whenever SQLite makes an update/delete/insert into the database on
+ /// this connection. It only applies to the given connection.
+ ///
+ public event SQLiteUpdateEventHandler Update
+ {
+ add
+ {
+ CheckDisposed();
+
+ if (_updateHandler == null)
+ {
+ _updateCallback = new SQLiteUpdateCallback(UpdateCallback);
+ if (_sql != null) _sql.SetUpdateHook(_updateCallback);
+ }
+ _updateHandler += value;
+ }
+ remove
+ {
+ CheckDisposed();
+
+ _updateHandler -= value;
+ if (_updateHandler == null)
+ {
+ if (_sql != null) _sql.SetUpdateHook(null);
+ _updateCallback = null;
+ }
+ }
+ }
+
+ private SQLiteProgressReturnCode ProgressCallback(
+ IntPtr pUserData /* NOT USED: Always IntPtr.Zero. */
+ )
+ {
+ try
+ {
+ ProgressEventArgs eventArgs = new ProgressEventArgs(
+ pUserData, SQLiteProgressReturnCode.Continue);
+
+ if (_progressHandler != null)
+ _progressHandler(this, eventArgs);
+
+ return eventArgs.ReturnCode;
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Progress", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ //
+ // NOTE: Should throwing an exception interrupt the operation?
+ //
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.InterruptOnException))
+ {
+ return SQLiteProgressReturnCode.Interrupt;
+ }
+ else
+ {
+ return SQLiteProgressReturnCode.Continue;
+ }
+ }
+
+ private SQLiteAuthorizerReturnCode AuthorizerCallback(
+ IntPtr pUserData, /* NOT USED: Always IntPtr.Zero. */
+ SQLiteAuthorizerActionCode actionCode,
+ IntPtr pArgument1,
+ IntPtr pArgument2,
+ IntPtr pDatabase,
+ IntPtr pAuthContext)
+ {
+ try
+ {
+ AuthorizerEventArgs eventArgs = new AuthorizerEventArgs(pUserData, actionCode,
+ SQLiteBase.UTF8ToString(pArgument1, -1), SQLiteBase.UTF8ToString(pArgument2, -1),
+ SQLiteBase.UTF8ToString(pDatabase, -1), SQLiteBase.UTF8ToString(pAuthContext, -1),
+ SQLiteAuthorizerReturnCode.Ok);
+
+ if (_authorizerHandler != null)
+ _authorizerHandler(this, eventArgs);
+
+ return eventArgs.ReturnCode;
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Authorize", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ //
+ // NOTE: Should throwing an exception deny the action?
+ //
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.DenyOnException))
+ {
+ return SQLiteAuthorizerReturnCode.Deny;
+ }
+ else
+ {
+ return SQLiteAuthorizerReturnCode.Ok;
+ }
+ }
+
+ private void UpdateCallback(
+ IntPtr puser, /* NOT USED */
+ int type,
+ IntPtr database,
+ IntPtr table,
+ Int64 rowid
+ )
+ {
+ try
+ {
+ _updateHandler(this, new UpdateEventArgs(
+ SQLiteBase.UTF8ToString(database, -1),
+ SQLiteBase.UTF8ToString(table, -1),
+ (UpdateEventType)type,
+ rowid));
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Update", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ }
+
+ ///
+ /// This event is raised whenever SQLite is committing a transaction.
+ /// Return non-zero to trigger a rollback.
+ ///
+ public event SQLiteCommitHandler Commit
+ {
+ add
+ {
+ CheckDisposed();
+
+ if (_commitHandler == null)
+ {
+ _commitCallback = new SQLiteCommitCallback(CommitCallback);
+ if (_sql != null) _sql.SetCommitHook(_commitCallback);
+ }
+ _commitHandler += value;
+ }
+ remove
+ {
+ CheckDisposed();
+
+ _commitHandler -= value;
+ if (_commitHandler == null)
+ {
+ if (_sql != null) _sql.SetCommitHook(null);
+ _commitCallback = null;
+ }
+ }
+ }
+
+ ///
+ /// This event is raised whenever SQLite statement first begins executing on
+ /// this connection. It only applies to the given connection.
+ ///
+ public event SQLiteTraceEventHandler Trace
+ {
+ add
+ {
+ CheckDisposed();
+
+ if (_traceHandler == null)
+ {
+ _traceCallback = new SQLiteTraceCallback(TraceCallback);
+ if (_sql != null) _sql.SetTraceCallback(_traceCallback);
+ }
+ _traceHandler += value;
+ }
+ remove
+ {
+ CheckDisposed();
+
+ _traceHandler -= value;
+ if (_traceHandler == null)
+ {
+ if (_sql != null) _sql.SetTraceCallback(null);
+ _traceCallback = null;
+ }
+ }
+ }
+
+ private void TraceCallback(
+ IntPtr puser, /* NOT USED */
+ IntPtr statement
+ )
+ {
+ try
+ {
+ if (_traceHandler != null)
+ _traceHandler(this, new TraceEventArgs(
+ SQLiteBase.UTF8ToString(statement, -1)));
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Trace", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ }
+
+ ///
+ /// This event is raised whenever SQLite is rolling back a transaction.
+ ///
+ public event EventHandler RollBack
+ {
+ add
+ {
+ CheckDisposed();
+
+ if (_rollbackHandler == null)
+ {
+ _rollbackCallback = new SQLiteRollbackCallback(RollbackCallback);
+ if (_sql != null) _sql.SetRollbackHook(_rollbackCallback);
+ }
+ _rollbackHandler += value;
+ }
+ remove
+ {
+ CheckDisposed();
+
+ _rollbackHandler -= value;
+ if (_rollbackHandler == null)
+ {
+ if (_sql != null) _sql.SetRollbackHook(null);
+ _rollbackCallback = null;
+ }
+ }
+ }
+
+ private int CommitCallback(
+ IntPtr parg /* NOT USED */
+ )
+ {
+ try
+ {
+ CommitEventArgs e = new CommitEventArgs();
+
+ if (_commitHandler != null)
+ _commitHandler(this, e);
+
+ return (e.AbortTransaction == true) ? 1 : 0;
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Commit", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ //
+ // NOTE: Should throwing an exception rollback the transaction?
+ //
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.RollbackOnException))
+ {
+ return 1; // rollback
+ }
+ else
+ {
+ return 0; // commit
+ }
+ }
+
+ private void RollbackCallback(
+ IntPtr parg /* NOT USED */
+ )
+ {
+ try
+ {
+ if (_rollbackHandler != null)
+ _rollbackHandler(this, EventArgs.Empty);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Rollback", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ }
+ }
+
+ ///
+ /// The I/O file cache flushing behavior for the connection
+ ///
+ public enum SynchronizationModes
+ {
+ ///
+ /// Normal file flushing at critical sections of the code
+ ///
+ Normal = 0,
+ ///
+ /// Full file flushing after every write operation
+ ///
+ Full = 1,
+ ///
+ /// Use the default operating system's file flushing, SQLite does not explicitly flush the file buffers after writing
+ ///
+ Off = 2,
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate SQLiteProgressReturnCode SQLiteProgressCallback(IntPtr pUserData);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate SQLiteAuthorizerReturnCode SQLiteAuthorizerCallback(
+ IntPtr pUserData,
+ SQLiteAuthorizerActionCode actionCode,
+ IntPtr pArgument1,
+ IntPtr pArgument2,
+ IntPtr pDatabase,
+ IntPtr pAuthContext
+ );
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteUpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate int SQLiteCommitCallback(IntPtr puser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteTraceCallback(IntPtr puser, IntPtr statement);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteTraceCallback2(SQLiteTraceFlags type, IntPtr puser, IntPtr pCtx1, IntPtr pCtx2);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteRollbackCallback(IntPtr puser);
+
+ ///
+ /// Raised each time the number of virtual machine instructions is
+ /// approximately equal to the value of the
+ /// property.
+ ///
+ /// The connection performing the operation.
+ /// A that contains the
+ /// event data.
+ public delegate void SQLiteProgressEventHandler(object sender, ProgressEventArgs e);
+
+ ///
+ /// Raised when authorization is required to perform an action contained
+ /// within a SQL query.
+ ///
+ /// The connection performing the action.
+ /// A that contains the
+ /// event data.
+ public delegate void SQLiteAuthorizerEventHandler(object sender, AuthorizerEventArgs e);
+
+ ///
+ /// Raised when a transaction is about to be committed. To roll back a transaction, set the
+ /// rollbackTrans boolean value to true.
+ ///
+ /// The connection committing the transaction
+ /// Event arguments on the transaction
+ public delegate void SQLiteCommitHandler(object sender, CommitEventArgs e);
+
+ ///
+ /// Raised when data is inserted, updated and deleted on a given connection
+ ///
+ /// The connection committing the transaction
+ /// The event parameters which triggered the event
+ public delegate void SQLiteUpdateEventHandler(object sender, UpdateEventArgs e);
+
+ ///
+ /// Raised when a statement first begins executing on a given connection
+ ///
+ /// The connection executing the statement
+ /// Event arguments of the trace
+ public delegate void SQLiteTraceEventHandler(object sender, TraceEventArgs e);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Backup API Members
+ ///
+ /// Raised between each backup step.
+ ///
+ ///
+ /// The source database connection.
+ ///
+ ///
+ /// The source database name.
+ ///
+ ///
+ /// The destination database connection.
+ ///
+ ///
+ /// The destination database name.
+ ///
+ ///
+ /// The number of pages copied with each step.
+ ///
+ ///
+ /// The number of pages remaining to be copied.
+ ///
+ ///
+ /// The total number of pages in the source database.
+ ///
+ ///
+ /// Set to true if the operation needs to be retried due to database
+ /// locking issues; otherwise, set to false.
+ ///
+ ///
+ /// True to continue with the backup process or false to halt the backup
+ /// process, rolling back any changes that have been made so far.
+ ///
+ public delegate bool SQLiteBackupCallback(
+ SQLiteConnection source,
+ string sourceName,
+ SQLiteConnection destination,
+ string destinationName,
+ int pages,
+ int remainingPages,
+ int totalPages,
+ bool retry
+ );
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The event data associated with progress reporting events.
+ ///
+ public class ProgressEventArgs : EventArgs
+ {
+ ///
+ /// The user-defined native data associated with this event. Currently,
+ /// this will always contain the value of .
+ ///
+ public readonly IntPtr UserData;
+
+ ///
+ /// The return code for the current call into the progress callback.
+ ///
+ public SQLiteProgressReturnCode ReturnCode;
+
+ ///
+ /// Constructs an instance of this class with default property values.
+ ///
+ private ProgressEventArgs()
+ {
+ this.UserData = IntPtr.Zero;
+ this.ReturnCode = SQLiteProgressReturnCode.Continue;
+ }
+
+ ///
+ /// Constructs an instance of this class with specific property values.
+ ///
+ ///
+ /// The user-defined native data associated with this event.
+ ///
+ ///
+ /// The progress return code.
+ ///
+ internal ProgressEventArgs(
+ IntPtr pUserData,
+ SQLiteProgressReturnCode returnCode
+ )
+ : this()
+ {
+ this.UserData = pUserData;
+ this.ReturnCode = returnCode;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The data associated with a call into the authorizer.
+ ///
+ public class AuthorizerEventArgs : EventArgs
+ {
+ ///
+ /// The user-defined native data associated with this event. Currently,
+ /// this will always contain the value of .
+ ///
+ public readonly IntPtr UserData;
+
+ ///
+ /// The action code responsible for the current call into the authorizer.
+ ///
+ public readonly SQLiteAuthorizerActionCode ActionCode;
+
+ ///
+ /// The first string argument for the current call into the authorizer.
+ /// The exact value will vary based on the action code, see the
+ /// enumeration for possible
+ /// values.
+ ///
+ public readonly string Argument1;
+
+ ///
+ /// The second string argument for the current call into the authorizer.
+ /// The exact value will vary based on the action code, see the
+ /// enumeration for possible
+ /// values.
+ ///
+ public readonly string Argument2;
+
+ ///
+ /// The database name for the current call into the authorizer, if
+ /// applicable.
+ ///
+ public readonly string Database;
+
+ ///
+ /// The name of the inner-most trigger or view that is responsible for
+ /// the access attempt or a null value if this access attempt is directly
+ /// from top-level SQL code.
+ ///
+ public readonly string Context;
+
+ ///
+ /// The return code for the current call into the authorizer.
+ ///
+ public SQLiteAuthorizerReturnCode ReturnCode;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class with default property values.
+ ///
+ private AuthorizerEventArgs()
+ {
+ this.UserData = IntPtr.Zero;
+ this.ActionCode = SQLiteAuthorizerActionCode.None;
+ this.Argument1 = null;
+ this.Argument2 = null;
+ this.Database = null;
+ this.Context = null;
+ this.ReturnCode = SQLiteAuthorizerReturnCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class with specific property values.
+ ///
+ ///
+ /// The user-defined native data associated with this event.
+ ///
+ ///
+ /// The authorizer action code.
+ ///
+ ///
+ /// The first authorizer argument.
+ ///
+ ///
+ /// The second authorizer argument.
+ ///
+ ///
+ /// The database name, if applicable.
+ ///
+ ///
+ /// The name of the inner-most trigger or view that is responsible for
+ /// the access attempt or a null value if this access attempt is directly
+ /// from top-level SQL code.
+ ///
+ ///
+ /// The authorizer return code.
+ ///
+ internal AuthorizerEventArgs(
+ IntPtr pUserData,
+ SQLiteAuthorizerActionCode actionCode,
+ string argument1,
+ string argument2,
+ string database,
+ string context,
+ SQLiteAuthorizerReturnCode returnCode
+ )
+ : this()
+ {
+ this.UserData = pUserData;
+ this.ActionCode = actionCode;
+ this.Argument1 = argument1;
+ this.Argument2 = argument2;
+ this.Database = database;
+ this.Context = context;
+ this.ReturnCode = returnCode;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Whenever an update event is triggered on a connection, this enum will indicate
+ /// exactly what type of operation is being performed.
+ ///
+ public enum UpdateEventType
+ {
+ ///
+ /// A row is being deleted from the given database and table
+ ///
+ Delete = 9,
+ ///
+ /// A row is being inserted into the table.
+ ///
+ Insert = 18,
+ ///
+ /// A row is being updated in the table.
+ ///
+ Update = 23,
+ }
+
+ ///
+ /// Passed during an Update callback, these event arguments detail the type of update operation being performed
+ /// on the given connection.
+ ///
+ public class UpdateEventArgs : EventArgs
+ {
+ ///
+ /// The name of the database being updated (usually "main" but can be any attached or temporary database)
+ ///
+ public readonly string Database;
+
+ ///
+ /// The name of the table being updated
+ ///
+ public readonly string Table;
+
+ ///
+ /// The type of update being performed (insert/update/delete)
+ ///
+ public readonly UpdateEventType Event;
+
+ ///
+ /// The RowId affected by this update.
+ ///
+ public readonly Int64 RowId;
+
+ internal UpdateEventArgs(string database, string table, UpdateEventType eventType, Int64 rowid)
+ {
+ Database = database;
+ Table = table;
+ Event = eventType;
+ RowId = rowid;
+ }
+ }
+
+ ///
+ /// Event arguments raised when a transaction is being committed
+ ///
+ public class CommitEventArgs : EventArgs
+ {
+ internal CommitEventArgs()
+ {
+ }
+
+ ///
+ /// Set to true to abort the transaction and trigger a rollback
+ ///
+ public bool AbortTransaction;
+ }
+
+ ///
+ /// Passed during an Trace callback, these event arguments contain the UTF-8 rendering of the SQL statement text
+ ///
+ public class TraceEventArgs : EventArgs
+ {
+ ///
+ /// SQL statement text as the statement first begins executing
+ ///
+ public readonly string Statement;
+
+ internal TraceEventArgs(string statement)
+ {
+ Statement = statement;
+ }
+ }
+
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteConnectionPool.cs b/Native.Csharp.Tool/SQLite/SQLiteConnectionPool.cs
new file mode 100644
index 00000000..1e140f8e
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteConnectionPool.cs
@@ -0,0 +1,1028 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Collections.Generic;
+
+#if !PLATFORM_COMPACTFRAMEWORK && DEBUG
+ using System.Text;
+#endif
+
+ using System.Threading;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Null Connection Pool Class
+#if !PLATFORM_COMPACTFRAMEWORK && DEBUG
+ ///
+ /// This class implements a connection pool where all methods of the
+ /// interface are NOPs. This class
+ /// is used for testing purposes only.
+ ///
+ internal sealed class NullConnectionPool : ISQLiteConnectionPool
+ {
+ #region Private Data
+ ///
+ /// This field keeps track of all method calls made into the
+ /// interface methods of this
+ /// class.
+ ///
+ private StringBuilder log;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Non-zero to dispose of database connection handles received via the
+ /// method.
+ ///
+ private bool dispose;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs a connection pool object where all methods of the
+ /// interface are NOPs. This
+ /// class is used for testing purposes only.
+ ///
+ private NullConnectionPool()
+ {
+ log = new StringBuilder();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs a connection pool object where all methods of the
+ /// interface are NOPs. This
+ /// class is used for testing purposes only.
+ ///
+ ///
+ /// Non-zero to dispose of database connection handles received via the
+ /// method.
+ ///
+ public NullConnectionPool(
+ bool dispose
+ )
+ : this()
+ {
+ this.dispose = dispose;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteConnectionPool Members
+ ///
+ /// Counts the number of pool entries matching the specified file name.
+ ///
+ ///
+ /// The file name to match or null to match all files.
+ ///
+ ///
+ /// The pool entry counts for each matching file.
+ ///
+ ///
+ /// The total number of connections successfully opened from any pool.
+ ///
+ ///
+ /// The total number of connections successfully closed from any pool.
+ ///
+ ///
+ /// The total number of pool entries for all matching files.
+ ///
+ public void GetCounts(
+ string fileName,
+ ref Dictionary counts,
+ ref int openCount,
+ ref int closeCount,
+ ref int totalCount
+ )
+ {
+ if (log != null)
+ {
+ log.AppendFormat(
+ "GetCounts(\"{0}\", {1}, {2}, {3}, {4}){5}", fileName,
+ counts, openCount, closeCount, totalCount,
+ Environment.NewLine);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of all pooled connections associated with the specified
+ /// database file name.
+ ///
+ ///
+ /// The database file name.
+ ///
+ public void ClearPool(
+ string fileName
+ )
+ {
+ if (log != null)
+ {
+ log.AppendFormat(
+ "ClearPool(\"{0}\"){1}", fileName, Environment.NewLine);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of all pooled connections.
+ ///
+ public void ClearAllPools()
+ {
+ if (log != null)
+ {
+ log.AppendFormat(
+ "ClearAllPools(){0}", Environment.NewLine);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adds a connection to the pool of those associated with the
+ /// specified database file name.
+ ///
+ ///
+ /// The database file name.
+ ///
+ ///
+ /// The database connection handle.
+ ///
+ ///
+ /// The connection pool version at the point the database connection
+ /// handle was received from the connection pool. This is also the
+ /// connection pool version that the database connection handle was
+ /// created under.
+ ///
+ public void Add(
+ string fileName,
+ object handle,
+ int version
+ )
+ {
+ if (log != null)
+ {
+ log.AppendFormat(
+ "Add(\"{0}\", {1}, {2}){3}", fileName, handle, version,
+ Environment.NewLine);
+ }
+
+ //
+ // NOTE: If configured to do so, dispose of the received connection
+ // handle now.
+ //
+ if (dispose)
+ {
+ IDisposable disposable = handle as IDisposable;
+
+ if (disposable != null)
+ disposable.Dispose();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Removes a connection from the pool of those associated with the
+ /// specified database file name with the intent of using it to
+ /// interact with the database.
+ ///
+ ///
+ /// The database file name.
+ ///
+ ///
+ /// The new maximum size of the connection pool for the specified
+ /// database file name.
+ ///
+ ///
+ /// The connection pool version associated with the returned database
+ /// connection handle, if any.
+ ///
+ ///
+ /// The database connection handle associated with the specified
+ /// database file name or null if it cannot be obtained.
+ ///
+ public object Remove(
+ string fileName,
+ int maxPoolSize,
+ out int version
+ )
+ {
+ version = 0;
+
+ if (log != null)
+ {
+ log.AppendFormat(
+ "Remove(\"{0}\", {1}, {2}){3}", fileName, maxPoolSize,
+ version, Environment.NewLine);
+ }
+
+ return null;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region System.Object Overrides
+ ///
+ /// Overrides the default method
+ /// to provide a log of all methods called on the
+ /// interface.
+ ///
+ ///
+ /// A string containing a log of all method calls into the
+ /// interface, along with their
+ /// parameters, delimited by .
+ ///
+ public override string ToString()
+ {
+ return (log != null) ? log.ToString() : String.Empty;
+ }
+ #endregion
+ }
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Public Connection Pool Interface
+ ///
+ /// This interface represents a custom connection pool implementation
+ /// usable by System.Data.SQLite.
+ ///
+ public interface ISQLiteConnectionPool
+ {
+ ///
+ /// Counts the number of pool entries matching the specified file name.
+ ///
+ ///
+ /// The file name to match or null to match all files.
+ ///
+ ///
+ /// The pool entry counts for each matching file.
+ ///
+ ///
+ /// The total number of connections successfully opened from any pool.
+ ///
+ ///
+ /// The total number of connections successfully closed from any pool.
+ ///
+ ///
+ /// The total number of pool entries for all matching files.
+ ///
+ void GetCounts(string fileName, ref Dictionary counts,
+ ref int openCount, ref int closeCount, ref int totalCount);
+
+ ///
+ /// Disposes of all pooled connections associated with the specified
+ /// database file name.
+ ///
+ ///
+ /// The database file name.
+ ///
+ void ClearPool(string fileName);
+
+ ///
+ /// Disposes of all pooled connections.
+ ///
+ void ClearAllPools();
+
+ ///
+ /// Adds a connection to the pool of those associated with the
+ /// specified database file name.
+ ///
+ ///
+ /// The database file name.
+ ///
+ ///
+ /// The database connection handle.
+ ///
+ ///
+ /// The connection pool version at the point the database connection
+ /// handle was received from the connection pool. This is also the
+ /// connection pool version that the database connection handle was
+ /// created under.
+ ///
+ void Add(string fileName, object handle, int version);
+
+ ///
+ /// Removes a connection from the pool of those associated with the
+ /// specified database file name with the intent of using it to
+ /// interact with the database.
+ ///
+ ///
+ /// The database file name.
+ ///
+ ///
+ /// The new maximum size of the connection pool for the specified
+ /// database file name.
+ ///
+ ///
+ /// The connection pool version associated with the returned database
+ /// connection handle, if any.
+ ///
+ ///
+ /// The database connection handle associated with the specified
+ /// database file name or null if it cannot be obtained.
+ ///
+ object Remove(string fileName, int maxPoolSize, out int version);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Connection Pool Subsystem & Default Implementation
+ ///
+ /// This default method implementations in this class should not be used by
+ /// applications that make use of COM (either directly or indirectly) due
+ /// to possible deadlocks that can occur during finalization of some COM
+ /// objects.
+ ///
+ internal static class SQLiteConnectionPool
+ {
+ #region Private Pool Class
+ ///
+ /// Keeps track of connections made on a specified file. The PoolVersion
+ /// dictates whether old objects get returned to the pool or discarded
+ /// when no longer in use.
+ ///
+ private sealed class PoolQueue
+ {
+ #region Private Data
+ ///
+ /// The queue of weak references to the actual database connection
+ /// handles.
+ ///
+ internal readonly Queue Queue =
+ new Queue();
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// This pool version associated with the database connection
+ /// handles in this pool queue.
+ ///
+ internal int PoolVersion;
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// The maximum size of this pool queue.
+ ///
+ internal int MaxPoolSize;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs a connection pool queue using the specified version
+ /// and maximum size. Normally, all the database connection
+ /// handles in this pool are associated with a single database file
+ /// name.
+ ///
+ ///
+ /// The initial pool version for this connection pool queue.
+ ///
+ ///
+ /// The initial maximum size for this connection pool queue.
+ ///
+ internal PoolQueue(
+ int version,
+ int maxSize
+ )
+ {
+ PoolVersion = version;
+ MaxPoolSize = maxSize;
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Static Data
+ ///
+ /// This field is used to synchronize access to the private static data
+ /// in this class.
+ ///
+ private static readonly object _syncRoot = new object();
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// When this field is non-null, it will be used to provide the
+ /// implementation of all the connection pool methods; otherwise,
+ /// the default method implementations will be used.
+ ///
+ private static ISQLiteConnectionPool _connectionPool = null;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The dictionary of connection pools, based on the normalized file
+ /// name of the SQLite database.
+ ///
+ private static SortedList _queueList =
+ new SortedList(StringComparer.OrdinalIgnoreCase);
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The default version number new pools will get.
+ ///
+ private static int _poolVersion = 1;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The number of connections successfully opened from any pool.
+ /// This value is incremented by the Remove method.
+ ///
+ private static int _poolOpened = 0;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The number of connections successfully closed from any pool.
+ /// This value is incremented by the Add method.
+ ///
+ private static int _poolClosed = 0;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteConnectionPool Members (Static, Non-Formal)
+ ///
+ /// Counts the number of pool entries matching the specified file name.
+ ///
+ ///
+ /// The file name to match or null to match all files.
+ ///
+ ///
+ /// The pool entry counts for each matching file.
+ ///
+ ///
+ /// The total number of connections successfully opened from any pool.
+ ///
+ ///
+ /// The total number of connections successfully closed from any pool.
+ ///
+ ///
+ /// The total number of pool entries for all matching files.
+ ///
+ internal static void GetCounts(
+ string fileName,
+ ref Dictionary counts,
+ ref int openCount,
+ ref int closeCount,
+ ref int totalCount
+ )
+ {
+ ISQLiteConnectionPool connectionPool = GetConnectionPool();
+
+ if (connectionPool != null)
+ {
+ connectionPool.GetCounts(
+ fileName, ref counts, ref openCount, ref closeCount,
+ ref totalCount);
+ }
+ else
+ {
+ lock (_syncRoot)
+ {
+ openCount = _poolOpened;
+ closeCount = _poolClosed;
+
+ if (counts == null)
+ {
+ counts = new Dictionary(
+ StringComparer.OrdinalIgnoreCase);
+ }
+
+ if (fileName != null)
+ {
+ PoolQueue queue;
+
+ if (_queueList.TryGetValue(fileName, out queue))
+ {
+ Queue poolQueue = queue.Queue;
+ int count = (poolQueue != null) ? poolQueue.Count : 0;
+
+ counts.Add(fileName, count);
+ totalCount += count;
+ }
+ }
+ else
+ {
+ foreach (KeyValuePair pair in _queueList)
+ {
+ if (pair.Value == null)
+ continue;
+
+ Queue poolQueue = pair.Value.Queue;
+ int count = (poolQueue != null) ? poolQueue.Count : 0;
+
+ counts.Add(pair.Key, count);
+ totalCount += count;
+ }
+ }
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of all pooled connections associated with the specified
+ /// database file name.
+ ///
+ ///
+ /// The database file name.
+ ///
+ internal static void ClearPool(string fileName)
+ {
+ ISQLiteConnectionPool connectionPool = GetConnectionPool();
+
+ if (connectionPool != null)
+ {
+ connectionPool.ClearPool(fileName);
+ }
+ else
+ {
+ lock (_syncRoot)
+ {
+ PoolQueue queue;
+
+ if (_queueList.TryGetValue(fileName, out queue))
+ {
+ queue.PoolVersion++;
+
+ Queue poolQueue = queue.Queue;
+ if (poolQueue == null) return;
+
+ while (poolQueue.Count > 0)
+ {
+ WeakReference connection = poolQueue.Dequeue();
+
+ if (connection == null) continue;
+
+ SQLiteConnectionHandle handle =
+ connection.Target as SQLiteConnectionHandle;
+
+ if (handle != null)
+ handle.Dispose();
+
+ GC.KeepAlive(handle);
+ }
+ }
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of all pooled connections.
+ ///
+ internal static void ClearAllPools()
+ {
+ ISQLiteConnectionPool connectionPool = GetConnectionPool();
+
+ if (connectionPool != null)
+ {
+ connectionPool.ClearAllPools();
+ }
+ else
+ {
+ lock (_syncRoot)
+ {
+ foreach (KeyValuePair pair in _queueList)
+ {
+ if (pair.Value == null)
+ continue;
+
+ Queue poolQueue = pair.Value.Queue;
+
+ while (poolQueue.Count > 0)
+ {
+ WeakReference connection = poolQueue.Dequeue();
+
+ if (connection == null) continue;
+
+ SQLiteConnectionHandle handle =
+ connection.Target as SQLiteConnectionHandle;
+
+ if (handle != null)
+ handle.Dispose();
+
+ GC.KeepAlive(handle);
+ }
+
+ //
+ // NOTE: Keep track of the highest revision so we can
+ // go one higher when we are finished.
+ //
+ if (_poolVersion <= pair.Value.PoolVersion)
+ _poolVersion = pair.Value.PoolVersion + 1;
+ }
+
+ //
+ // NOTE: All pools are cleared and we have a new highest
+ // version number to force all old version active
+ // items to get discarded instead of going back to
+ // the queue when they are closed. We can get away
+ // with this because we have pumped up the pool
+ // version out of range of all active connections,
+ // so they will all get discarded when they try to
+ // put themselves back into their pools.
+ //
+ _queueList.Clear();
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adds a connection to the pool of those associated with the
+ /// specified database file name.
+ ///
+ ///
+ /// The database file name.
+ ///
+ ///
+ /// The database connection handle.
+ ///
+ ///
+ /// The connection pool version at the point the database connection
+ /// handle was received from the connection pool. This is also the
+ /// connection pool version that the database connection handle was
+ /// created under.
+ ///
+ internal static void Add(
+ string fileName,
+ SQLiteConnectionHandle handle,
+ int version
+ )
+ {
+ ISQLiteConnectionPool connectionPool = GetConnectionPool();
+
+ if (connectionPool != null)
+ {
+ connectionPool.Add(fileName, handle, version);
+ }
+ else
+ {
+ lock (_syncRoot)
+ {
+ //
+ // NOTE: If the queue does not exist in the pool, then it
+ // must have been cleared sometime after the
+ // connection was created.
+ //
+ PoolQueue queue;
+
+ if (_queueList.TryGetValue(fileName, out queue) &&
+ (version == queue.PoolVersion))
+ {
+ ResizePool(queue, true);
+
+ Queue poolQueue = queue.Queue;
+ if (poolQueue == null) return;
+
+ poolQueue.Enqueue(new WeakReference(handle, false));
+ Interlocked.Increment(ref _poolClosed);
+ }
+ else
+ {
+ handle.Close();
+ }
+
+ GC.KeepAlive(handle);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Removes a connection from the pool of those associated with the
+ /// specified database file name with the intent of using it to
+ /// interact with the database.
+ ///
+ ///
+ /// The database file name.
+ ///
+ ///
+ /// The new maximum size of the connection pool for the specified
+ /// database file name.
+ ///
+ ///
+ /// The connection pool version associated with the returned database
+ /// connection handle, if any.
+ ///
+ ///
+ /// The database connection handle associated with the specified
+ /// database file name or null if it cannot be obtained.
+ ///
+ internal static SQLiteConnectionHandle Remove(
+ string fileName,
+ int maxPoolSize,
+ out int version
+ )
+ {
+ ISQLiteConnectionPool connectionPool = GetConnectionPool();
+
+ if (connectionPool != null)
+ {
+ return connectionPool.Remove(fileName, maxPoolSize,
+ out version) as SQLiteConnectionHandle;
+ }
+ else
+ {
+ int localVersion;
+ Queue poolQueue;
+
+ //
+ // NOTE: This lock cannot be held while checking the queue for
+ // available connections because other methods of this
+ // class are called from the GC finalizer thread and we
+ // use the WaitForPendingFinalizers method (below).
+ // Holding this lock while calling that method would
+ // therefore result in a deadlock. Instead, this lock
+ // is held only while a temporary copy of the queue is
+ // created, and if necessary, when committing changes
+ // back to that original queue prior to returning from
+ // this method.
+ //
+ lock (_syncRoot)
+ {
+ PoolQueue queue;
+
+ //
+ // NOTE: Default to the highest pool version.
+ //
+ version = _poolVersion;
+
+ //
+ // NOTE: If we didn't find a pool for this file, create one
+ // even though it will be empty. We have to do this
+ // here because otherwise calling ClearPool() on the
+ // file will not work for active connections that have
+ // never seen the pool yet.
+ //
+ if (!_queueList.TryGetValue(fileName, out queue))
+ {
+ queue = new PoolQueue(_poolVersion, maxPoolSize);
+ _queueList.Add(fileName, queue);
+
+ return null;
+ }
+
+ //
+ // NOTE: We found a pool for this file, so use its version
+ // number.
+ //
+ version = localVersion = queue.PoolVersion;
+ queue.MaxPoolSize = maxPoolSize;
+
+ //
+ // NOTE: Now, resize the pool to the new maximum size, if
+ // necessary.
+ //
+ ResizePool(queue, false);
+
+ //
+ // NOTE: Try and get a pooled connection from the queue.
+ //
+ poolQueue = queue.Queue;
+ if (poolQueue == null) return null;
+
+ //
+ // NOTE: Temporarily tranfer the queue for this file into
+ // a local variable. The queue for this file will
+ // be modified and then committed back to the real
+ // pool list (below) prior to returning from this
+ // method.
+ //
+ _queueList.Remove(fileName);
+ poolQueue = new Queue(poolQueue);
+ }
+
+ try
+ {
+ while (poolQueue.Count > 0)
+ {
+ WeakReference connection = poolQueue.Dequeue();
+
+ if (connection == null) continue;
+
+ SQLiteConnectionHandle handle =
+ connection.Target as SQLiteConnectionHandle;
+
+ if (handle == null) continue;
+
+ //
+ // BUGFIX: For ticket [996d13cd87], step #1. After
+ // this point, make sure that the finalizer for
+ // the connection handle just obtained from the
+ // queue cannot START running (i.e. it may
+ // still be pending but it will no longer start
+ // after this point).
+ //
+ GC.SuppressFinalize(handle);
+
+ try
+ {
+ //
+ // BUGFIX: For ticket [996d13cd87], step #2. Now,
+ // we must wait for all pending finalizers
+ // which have STARTED running and have not
+ // yet COMPLETED. This must be done just
+ // in case the finalizer for the connection
+ // handle just obtained from the queue has
+ // STARTED running at some point before
+ // SuppressFinalize was called on it.
+ //
+ // After this point, checking properties of
+ // the connection handle (e.g. IsClosed)
+ // should work reliably without having to
+ // worry that they will (due to the
+ // finalizer) change out from under us.
+ //
+ GC.WaitForPendingFinalizers();
+
+ //
+ // BUGFIX: For ticket [996d13cd87], step #3. Next,
+ // verify that the connection handle is
+ // actually valid and [still?] not closed
+ // prior to actually returning it to our
+ // caller.
+ //
+ if (!handle.IsInvalid && !handle.IsClosed)
+ {
+ Interlocked.Increment(ref _poolOpened);
+ return handle;
+ }
+ }
+ finally
+ {
+ //
+ // BUGFIX: For ticket [996d13cd87], step #4. Next,
+ // we must re-register the connection
+ // handle for finalization now that we have
+ // a strong reference to it (i.e. the
+ // finalizer will not run at least until
+ // the connection is subsequently closed).
+ //
+ GC.ReRegisterForFinalize(handle);
+ }
+
+ GC.KeepAlive(handle);
+ }
+ }
+ finally
+ {
+ //
+ // BUGFIX: For ticket [996d13cd87], step #5. Finally,
+ // commit any changes to the pool/queue for this
+ // database file.
+ //
+ lock (_syncRoot)
+ {
+ //
+ // NOTE: We must check [again] if a pool exists for
+ // this file because one may have been added
+ // while the search for an available connection
+ // was in progress (above).
+ //
+ PoolQueue queue;
+ Queue newPoolQueue;
+ bool addPool;
+
+ if (_queueList.TryGetValue(fileName, out queue))
+ {
+ addPool = false;
+ }
+ else
+ {
+ addPool = true;
+ queue = new PoolQueue(localVersion, maxPoolSize);
+ }
+
+ newPoolQueue = queue.Queue;
+
+ while (poolQueue.Count > 0)
+ newPoolQueue.Enqueue(poolQueue.Dequeue());
+
+ ResizePool(queue, false);
+
+ if (addPool)
+ _queueList.Add(fileName, queue);
+ }
+ }
+
+ return null;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Helper Methods
+ ///
+ /// This method is used to obtain a reference to the custom connection
+ /// pool implementation currently in use, if any.
+ ///
+ ///
+ /// The custom connection pool implementation or null if the default
+ /// connection pool implementation should be used.
+ ///
+ internal static ISQLiteConnectionPool GetConnectionPool()
+ {
+ lock (_syncRoot)
+ {
+ return _connectionPool;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is used to set the reference to the custom connection
+ /// pool implementation to use, if any.
+ ///
+ ///
+ /// The custom connection pool implementation to use or null if the
+ /// default connection pool implementation should be used.
+ ///
+ internal static void SetConnectionPool(
+ ISQLiteConnectionPool connectionPool
+ )
+ {
+ lock (_syncRoot)
+ {
+ _connectionPool = connectionPool;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// We do not have to thread-lock anything in this function, because it
+ /// is only called by other functions above which already take the lock.
+ ///
+ ///
+ /// The pool queue to resize.
+ ///
+ ///
+ /// If a function intends to add to the pool, this is true, which
+ /// forces the resize to take one more than it needs from the pool.
+ ///
+ private static void ResizePool(
+ PoolQueue queue,
+ bool add
+ )
+ {
+ int target = queue.MaxPoolSize;
+
+ if (add && target > 0) target--;
+
+ Queue poolQueue = queue.Queue;
+ if (poolQueue == null) return;
+
+ while (poolQueue.Count > target)
+ {
+ WeakReference connection = poolQueue.Dequeue();
+
+ if (connection == null) continue;
+
+ SQLiteConnectionHandle handle =
+ connection.Target as SQLiteConnectionHandle;
+
+ if (handle != null)
+ handle.Dispose();
+
+ GC.KeepAlive(handle);
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteConnectionStringBuilder.cs b/Native.Csharp.Tool/SQLite/SQLiteConnectionStringBuilder.cs
new file mode 100644
index 00000000..ea0e7f18
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteConnectionStringBuilder.cs
@@ -0,0 +1,991 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data.Common;
+ using System.ComponentModel;
+ using System.Collections;
+ using System.Globalization;
+ using System.Reflection;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// SQLite implementation of DbConnectionStringBuilder.
+ ///
+ [DefaultProperty("DataSource")]
+ [DefaultMember("Item")]
+ public sealed class SQLiteConnectionStringBuilder : DbConnectionStringBuilder
+ {
+ ///
+ /// Properties of this class
+ ///
+ private Hashtable _properties;
+
+ ///
+ /// Constructs a new instance of the class
+ ///
+ ///
+ /// Default constructor
+ ///
+ public SQLiteConnectionStringBuilder()
+ {
+ Initialize(null);
+ }
+
+ ///
+ /// Constructs a new instance of the class using the specified connection string.
+ ///
+ /// The connection string to parse
+ public SQLiteConnectionStringBuilder(string connectionString)
+ {
+ Initialize(connectionString);
+ }
+
+ ///
+ /// Private initializer, which assigns the connection string and resets the builder
+ ///
+ /// The connection string to assign
+ private void Initialize(string cnnString)
+ {
+ _properties = new Hashtable(StringComparer.OrdinalIgnoreCase);
+ try
+ {
+ base.GetProperties(_properties);
+ }
+ catch(NotImplementedException)
+ {
+ FallbackGetProperties(_properties);
+ }
+
+ if (String.IsNullOrEmpty(cnnString) == false)
+ ConnectionString = cnnString;
+ }
+
+ ///
+ /// Gets/Sets the default version of the SQLite engine to instantiate. Currently the only valid value is 3, indicating version 3 of the sqlite library.
+ ///
+ [Browsable(true)]
+ [DefaultValue(3)]
+ public int Version
+ {
+ get
+ {
+ object value;
+ TryGetValue("version", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ if (value != 3)
+ throw new NotSupportedException();
+
+ this["version"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the synchronization mode (file flushing) of the connection string. Default is "Normal".
+ ///
+ [DisplayName("Synchronous")]
+ [Browsable(true)]
+ [DefaultValue(SynchronizationModes.Normal)]
+ public SynchronizationModes SyncMode
+ {
+ get
+ {
+ object value;
+ TryGetValue("synchronous", out value);
+ if (value is string)
+ return (SynchronizationModes)TypeDescriptor.GetConverter(typeof(SynchronizationModes)).ConvertFrom(value);
+ else return (SynchronizationModes)value;
+ }
+ set
+ {
+ this["synchronous"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the encoding for the connection string. The default is "False" which indicates UTF-8 encoding.
+ ///
+ [DisplayName("Use UTF-16 Encoding")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool UseUTF16Encoding
+ {
+ get
+ {
+ object value;
+ TryGetValue("useutf16encoding", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["useutf16encoding"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets whether or not to use connection pooling. The default is "False"
+ ///
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool Pooling
+ {
+ get
+ {
+ object value;
+ TryGetValue("pooling", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["pooling"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets whethor not to store GUID's in binary format. The default is True
+ /// which saves space in the database.
+ ///
+ [DisplayName("Binary GUID")]
+ [Browsable(true)]
+ [DefaultValue(true)]
+ public bool BinaryGUID
+ {
+ get
+ {
+ object value;
+ TryGetValue("binaryguid", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["binaryguid"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the filename to open on the connection string.
+ ///
+ [DisplayName("Data Source")]
+ [Browsable(true)]
+ [DefaultValue("")]
+ public string DataSource
+ {
+ get
+ {
+ object value;
+ TryGetValue("data source", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["data source"] = value;
+ }
+ }
+
+ ///
+ /// An alternate to the data source property
+ ///
+ [DisplayName("URI")]
+ [Browsable(true)]
+ [DefaultValue(null)]
+ public string Uri
+ {
+ get
+ {
+ object value;
+ TryGetValue("uri", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["uri"] = value;
+ }
+ }
+
+ ///
+ /// An alternate to the data source property that uses the SQLite URI syntax.
+ ///
+ [DisplayName("Full URI")]
+ [Browsable(true)]
+ [DefaultValue(null)]
+ public string FullUri
+ {
+ get
+ {
+ object value;
+ TryGetValue("fulluri", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["fulluri"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the default command timeout for newly-created commands. This is especially useful for
+ /// commands used internally such as inside a SQLiteTransaction, where setting the timeout is not possible.
+ ///
+ [DisplayName("Default Timeout")]
+ [Browsable(true)]
+ [DefaultValue(30)]
+ public int DefaultTimeout
+ {
+ get
+ {
+ object value;
+ TryGetValue("default timeout", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["default timeout"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the busy timeout to use with the SQLite core library.
+ ///
+ [DisplayName("Busy Timeout")]
+ [Browsable(true)]
+ [DefaultValue(0)]
+ public int BusyTimeout
+ {
+ get
+ {
+ object value;
+ TryGetValue("busytimeout", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["busytimeout"] = value;
+ }
+ }
+
+ ///
+ /// EXPERIMENTAL --
+ /// The wait timeout to use with
+ /// method.
+ /// This is only used when waiting for the enlistment to be reset
+ /// prior to enlisting in a transaction, and then only when the
+ /// appropriate connection flag is set.
+ ///
+ [DisplayName("Wait Timeout")]
+ [Browsable(true)]
+ [DefaultValue(30000)]
+ public int WaitTimeout
+ {
+ get
+ {
+ object value;
+ TryGetValue("waittimeout", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["waittimeout"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the maximum number of retries when preparing SQL to be executed.
+ /// This normally only applies to preparation errors resulting from the database
+ /// schema being changed.
+ ///
+ [DisplayName("Prepare Retries")]
+ [Browsable(true)]
+ [DefaultValue(3)]
+ public int PrepareRetries
+ {
+ get
+ {
+ object value;
+ TryGetValue("prepareretries", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["prepareretries"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the approximate number of virtual machine instructions between
+ /// progress events. In order for progress events to actually fire, the event
+ /// handler must be added to the event
+ /// as well.
+ ///
+ [DisplayName("Progress Ops")]
+ [Browsable(true)]
+ [DefaultValue(0)]
+ public int ProgressOps
+ {
+ get
+ {
+ object value;
+ TryGetValue("progressops", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["progressops"] = value;
+ }
+ }
+
+ ///
+ /// Determines whether or not the connection will automatically participate
+ /// in the current distributed transaction (if one exists)
+ ///
+ [Browsable(true)]
+ [DefaultValue(true)]
+ public bool Enlist
+ {
+ get
+ {
+ object value;
+ TryGetValue("enlist", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["enlist"] = value;
+ }
+ }
+
+ ///
+ /// If set to true, will throw an exception if the database specified in the connection
+ /// string does not exist. If false, the database will be created automatically.
+ ///
+ [DisplayName("Fail If Missing")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool FailIfMissing
+ {
+ get
+ {
+ object value;
+ TryGetValue("failifmissing", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["failifmissing"] = value;
+ }
+ }
+
+ ///
+ /// If enabled, uses the legacy 3.xx format for maximum compatibility, but results in larger
+ /// database sizes.
+ ///
+ [DisplayName("Legacy Format")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool LegacyFormat
+ {
+ get
+ {
+ object value;
+ TryGetValue("legacy format", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["legacy format"] = value;
+ }
+ }
+
+ ///
+ /// When enabled, the database will be opened for read-only access and writing will be disabled.
+ ///
+ [DisplayName("Read Only")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool ReadOnly
+ {
+ get
+ {
+ object value;
+ TryGetValue("read only", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["read only"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the database encryption password
+ ///
+ [Browsable(true)]
+ [PasswordPropertyText(true)]
+ [DefaultValue("")]
+ public string Password
+ {
+ get
+ {
+ object value;
+ TryGetValue("password", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["password"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the database encryption hexadecimal password
+ ///
+ [DisplayName("Hexadecimal Password")]
+ [Browsable(true)]
+ [PasswordPropertyText(true)]
+ [DefaultValue(null)]
+ public byte[] HexPassword
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("hexpassword", out value))
+ {
+ if (value is string)
+ return SQLiteConnection.FromHexString((string)value);
+ else if (value != null)
+ return (byte[])value;
+ }
+
+ return null;
+ }
+ set
+ {
+ this["hexpassword"] = SQLiteConnection.ToHexString(value);
+ }
+ }
+
+ ///
+ /// Gets/Sets the page size for the connection.
+ ///
+ [DisplayName("Page Size")]
+ [Browsable(true)]
+ [DefaultValue(4096)]
+ public int PageSize
+ {
+ get
+ {
+ object value;
+ TryGetValue("page size", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["page size"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the maximum number of pages the database may hold
+ ///
+ [DisplayName("Maximum Page Count")]
+ [Browsable(true)]
+ [DefaultValue(0)]
+ public int MaxPageCount
+ {
+ get
+ {
+ object value;
+ TryGetValue("max page count", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["max page count"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the cache size for the connection.
+ ///
+ [DisplayName("Cache Size")]
+ [Browsable(true)]
+ [DefaultValue(-2000)]
+ public int CacheSize
+ {
+ get
+ {
+ object value;
+ TryGetValue("cache size", out value);
+ return Convert.ToInt32(value, CultureInfo.CurrentCulture);
+ }
+ set
+ {
+ this["cache size"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the DateTime format for the connection.
+ ///
+ [DisplayName("DateTime Format")]
+ [Browsable(true)]
+ [DefaultValue(SQLiteDateFormats.Default)]
+ public SQLiteDateFormats DateTimeFormat
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("datetimeformat", out value))
+ {
+ if (value is SQLiteDateFormats)
+ return (SQLiteDateFormats)value;
+ else if (value != null)
+ return (SQLiteDateFormats)TypeDescriptor.GetConverter(
+ typeof(SQLiteDateFormats)).ConvertFrom(value);
+ }
+
+ return SQLiteDateFormats.Default;
+ }
+ set
+ {
+ this["datetimeformat"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the DateTime kind for the connection.
+ ///
+ [DisplayName("DateTime Kind")]
+ [Browsable(true)]
+ [DefaultValue(DateTimeKind.Unspecified)]
+ public DateTimeKind DateTimeKind
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("datetimekind", out value))
+ {
+ if (value is DateTimeKind)
+ return (DateTimeKind)value;
+ else if (value != null)
+ return (DateTimeKind)TypeDescriptor.GetConverter(
+ typeof(DateTimeKind)).ConvertFrom(value);
+ }
+
+ return DateTimeKind.Unspecified;
+ }
+ set
+ {
+ this["datetimekind"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the DateTime format string used for formatting
+ /// and parsing purposes.
+ ///
+ [DisplayName("DateTime Format String")]
+ [Browsable(true)]
+ [DefaultValue(null)]
+ public string DateTimeFormatString
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("datetimeformatstring", out value))
+ {
+ if (value is string)
+ return (string)value;
+ else if (value != null)
+ return value.ToString();
+ }
+
+ return null;
+ }
+ set
+ {
+ this["datetimeformatstring"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the placeholder base schema name used for
+ /// .NET Framework compatibility purposes.
+ ///
+ [DisplayName("Base Schema Name")]
+ [Browsable(true)]
+ [DefaultValue(SQLiteConnection.DefaultBaseSchemaName)]
+ public string BaseSchemaName
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("baseschemaname", out value))
+ {
+ if (value is string)
+ return (string)value;
+ else if (value != null)
+ return value.ToString();
+ }
+
+ return null;
+ }
+ set
+ {
+ this["baseschemaname"] = value;
+ }
+ }
+
+ ///
+ /// Determines how SQLite handles the transaction journal file.
+ ///
+ [Browsable(true)]
+ [DefaultValue(SQLiteJournalModeEnum.Default)]
+ [DisplayName("Journal Mode")]
+ public SQLiteJournalModeEnum JournalMode
+ {
+ get
+ {
+ object value;
+ TryGetValue("journal mode", out value);
+ if (value is string)
+ return (SQLiteJournalModeEnum)TypeDescriptor.GetConverter(typeof(SQLiteJournalModeEnum)).ConvertFrom(value);
+ else
+ return (SQLiteJournalModeEnum)value;
+ }
+ set
+ {
+ this["journal mode"] = value;
+ }
+ }
+
+ ///
+ /// Sets the default isolation level for transactions on the connection.
+ ///
+ [Browsable(true)]
+ [DefaultValue(IsolationLevel.Serializable)]
+ [DisplayName("Default Isolation Level")]
+ public IsolationLevel DefaultIsolationLevel
+ {
+ get
+ {
+ object value;
+ TryGetValue("default isolationlevel", out value);
+ if (value is string)
+ return (IsolationLevel)TypeDescriptor.GetConverter(typeof(IsolationLevel)).ConvertFrom(value);
+ else
+ return (IsolationLevel)value;
+ }
+ set
+ {
+ this["default isolationlevel"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the default database type for the connection.
+ ///
+ [DisplayName("Default Database Type")]
+ [Browsable(true)]
+ [DefaultValue(SQLiteConnection.BadDbType)]
+ public DbType DefaultDbType
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("defaultdbtype", out value))
+ {
+ if (value is string)
+ return (DbType)TypeDescriptor.GetConverter(
+ typeof(DbType)).ConvertFrom(value);
+ else if (value != null)
+ return (DbType)value;
+ }
+
+ return SQLiteConnection.BadDbType;
+ }
+ set
+ {
+ this["defaultdbtype"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the default type name for the connection.
+ ///
+ [DisplayName("Default Type Name")]
+ [Browsable(true)]
+ [DefaultValue(null)]
+ public string DefaultTypeName
+ {
+ get
+ {
+ object value;
+ TryGetValue("defaulttypename", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["defaulttypename"] = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the VFS name for the connection.
+ ///
+ [DisplayName("VFS Name")]
+ [Browsable(true)]
+ [DefaultValue(null)]
+ public string VfsName
+ {
+ get
+ {
+ object value;
+ TryGetValue("vfsname", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["vfsname"] = value;
+ }
+ }
+
+ ///
+ /// If enabled, use foreign key constraints
+ ///
+ [DisplayName("Foreign Keys")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool ForeignKeys
+ {
+ get
+ {
+ object value;
+ TryGetValue("foreign keys", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["foreign keys"] = value;
+ }
+ }
+
+ ///
+ /// Enable or disable the recursive trigger capability.
+ ///
+ [DisplayName("Recursive Triggers")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool RecursiveTriggers
+ {
+ get
+ {
+ object value;
+ TryGetValue("recursive triggers", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["recursive triggers"] = value;
+ }
+ }
+
+ ///
+ /// If non-null, this is the version of ZipVFS to use. This requires the
+ /// System.Data.SQLite interop assembly -AND- primary managed assembly to
+ /// be compiled with the INTEROP_INCLUDE_ZIPVFS option; otherwise, this
+ /// property does nothing.
+ ///
+ [DisplayName("ZipVFS Version")]
+ [Browsable(true)]
+ [DefaultValue(null)]
+ public string ZipVfsVersion
+ {
+ get
+ {
+ object value;
+ TryGetValue("zipvfsversion", out value);
+ return (value != null) ? value.ToString() : null;
+ }
+ set
+ {
+ this["zipvfsversion"] = value;
+ }
+ }
+
+ ///
+ /// Gets/Sets the extra behavioral flags.
+ ///
+ [Browsable(true)]
+ [DefaultValue(SQLiteConnectionFlags.Default)]
+ public SQLiteConnectionFlags Flags
+ {
+ get
+ {
+ object value;
+
+ if (TryGetValue("flags", out value))
+ {
+ if (value is SQLiteConnectionFlags)
+ return (SQLiteConnectionFlags)value;
+ else if (value != null)
+ return (SQLiteConnectionFlags)TypeDescriptor.GetConverter(
+ typeof(SQLiteConnectionFlags)).ConvertFrom(value);
+ }
+
+ return SQLiteConnectionFlags.Default;
+ }
+ set
+ {
+ this["flags"] = value;
+ }
+ }
+
+ ///
+ /// If enabled, apply the default connection settings to opened databases.
+ ///
+ [DisplayName("Set Defaults")]
+ [Browsable(true)]
+ [DefaultValue(true)]
+ public bool SetDefaults
+ {
+ get
+ {
+ object value;
+ TryGetValue("setdefaults", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["setdefaults"] = value;
+ }
+ }
+
+ ///
+ /// If enabled, attempt to resolve the provided data source file name to a
+ /// full path before opening.
+ ///
+ [DisplayName("To Full Path")]
+ [Browsable(true)]
+ [DefaultValue(true)]
+ public bool ToFullPath
+ {
+ get
+ {
+ object value;
+ TryGetValue("tofullpath", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["tofullpath"] = value;
+ }
+ }
+
+ ///
+ /// If enabled, skip using the configured default connection flags.
+ ///
+ [DisplayName("No Default Flags")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool NoDefaultFlags
+ {
+ get
+ {
+ object value;
+ TryGetValue("nodefaultflags", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["nodefaultflags"] = value;
+ }
+ }
+
+ ///
+ /// If enabled, skip using the configured shared connection flags.
+ ///
+ [DisplayName("No Shared Flags")]
+ [Browsable(true)]
+ [DefaultValue(false)]
+ public bool NoSharedFlags
+ {
+ get
+ {
+ object value;
+ TryGetValue("nosharedflags", out value);
+ return SQLiteConvert.ToBoolean(value);
+ }
+ set
+ {
+ this["nosharedflags"] = value;
+ }
+ }
+
+ ///
+ /// Helper function for retrieving values from the connectionstring
+ ///
+ /// The keyword to retrieve settings for
+ /// The resulting parameter value
+ /// Returns true if the value was found and returned
+ public override bool TryGetValue(string keyword, out object value)
+ {
+ bool b = base.TryGetValue(keyword, out value);
+
+ if (!_properties.ContainsKey(keyword)) return b;
+
+ PropertyDescriptor pd = _properties[keyword] as PropertyDescriptor;
+
+ if (pd == null) return b;
+
+ // Attempt to coerce the value into something more solid
+ if (b)
+ {
+ if (pd.PropertyType == typeof(Boolean))
+ value = SQLiteConvert.ToBoolean(value);
+ else if (pd.PropertyType != typeof(byte[]))
+ value = TypeDescriptor.GetConverter(pd.PropertyType).ConvertFrom(value);
+ }
+ else
+ {
+ DefaultValueAttribute att = pd.Attributes[typeof(DefaultValueAttribute)] as DefaultValueAttribute;
+ if (att != null)
+ {
+ value = att.Value;
+ b = true;
+ }
+ }
+ return b;
+ }
+
+ ///
+ /// Fallback method for MONO, which doesn't implement DbConnectionStringBuilder.GetProperties()
+ ///
+ /// The hashtable to fill with property descriptors
+ private void FallbackGetProperties(Hashtable propertyList)
+ {
+ foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(this, true))
+ {
+ if (descriptor.Name != "ConnectionString" && propertyList.ContainsKey(descriptor.DisplayName) == false)
+ {
+ propertyList.Add(descriptor.DisplayName, descriptor);
+ }
+ }
+ }
+ }
+#endif
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteConvert.cs b/Native.Csharp.Tool/SQLite/SQLiteConvert.cs
new file mode 100644
index 00000000..a1aadebd
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteConvert.cs
@@ -0,0 +1,3026 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ using System.Diagnostics;
+#endif
+
+ using System.Runtime.InteropServices;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Text;
+
+ ///
+ /// This base class provides datatype conversion services for the SQLite provider.
+ ///
+ public abstract class SQLiteConvert
+ {
+ ///
+ /// This character is used to escape other characters, including itself, in
+ /// connection string property names and values.
+ ///
+ internal const char EscapeChar = '\\';
+
+ ///
+ /// This character can be used to wrap connection string property names and
+ /// values. Normally, it is optional; however, when used, it must be the
+ /// first -AND- last character of that connection string property name -OR-
+ /// value.
+ ///
+ internal const char QuoteChar = '"';
+
+ ///
+ /// This character can be used to wrap connection string property names and
+ /// values. Normally, it is optional; however, when used, it must be the
+ /// first -AND- last character of that connection string property name -OR-
+ /// value.
+ ///
+ internal const char AltQuoteChar = '\'';
+
+ ///
+ /// The character is used to separate the name and value for a connection
+ /// string property. This character cannot be present in any connection
+ /// string property name. This character can be present in a connection
+ /// string property value; however, this should be avoided unless deemed
+ /// absolutely necessary.
+ ///
+ internal const char ValueChar = '=';
+
+ ///
+ /// This character is used to separate connection string properties. When
+ /// the "No_SQLiteConnectionNewParser" setting is enabled, this character
+ /// may not appear in connection string property names -OR- values.
+ ///
+ internal const char PairChar = ';';
+
+ ///
+ /// These are the characters that are special to the connection string
+ /// parser.
+ ///
+ internal static readonly char[] SpecialChars = {
+ QuoteChar, AltQuoteChar, PairChar, ValueChar, EscapeChar
+ };
+
+ ///
+ /// The fallback default database type when one cannot be obtained from an
+ /// existing connection instance.
+ ///
+ private const DbType FallbackDefaultDbType = DbType.Object;
+
+ ///
+ /// The fallback default database type name when one cannot be obtained from
+ /// an existing connection instance.
+ ///
+ private static readonly string FallbackDefaultTypeName = String.Empty;
+
+ ///
+ /// The value for the Unix epoch (e.g. January 1, 1970 at midnight, in UTC).
+ ///
+ protected static readonly DateTime UnixEpoch =
+ new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ #pragma warning disable 414
+ ///
+ /// The value of the OLE Automation epoch represented as a Julian day. This
+ /// field cannot be removed as the test suite relies upon it.
+ ///
+ private static readonly double OleAutomationEpochAsJulianDay = 2415018.5;
+ #pragma warning restore 414
+
+ ///
+ /// The format string for DateTime values when using the InvariantCulture or CurrentCulture formats.
+ ///
+ private const string FullFormat = "yyyy-MM-ddTHH:mm:ss.fffffffK";
+
+ ///
+ /// This is the minimum Julian Day value supported by this library
+ /// (148731163200000).
+ ///
+ private static readonly long MinimumJd = computeJD(DateTime.MinValue);
+
+ ///
+ /// This is the maximum Julian Day value supported by this library
+ /// (464269060799000).
+ ///
+ private static readonly long MaximumJd = computeJD(DateTime.MaxValue);
+
+ ///
+ /// An array of ISO-8601 DateTime formats that we support parsing.
+ ///
+ private static string[] _datetimeFormats = new string[] {
+ "THHmmssK",
+ "THHmmK",
+ "HH:mm:ss.FFFFFFFK",
+ "HH:mm:ssK",
+ "HH:mmK",
+ "yyyy-MM-dd HH:mm:ss.FFFFFFFK", /* NOTE: UTC default (5). */
+ "yyyy-MM-dd HH:mm:ssK",
+ "yyyy-MM-dd HH:mmK",
+ "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
+ "yyyy-MM-ddTHH:mmK",
+ "yyyy-MM-ddTHH:mm:ssK",
+ "yyyyMMddHHmmssK",
+ "yyyyMMddHHmmK",
+ "yyyyMMddTHHmmssFFFFFFFK",
+ "THHmmss",
+ "THHmm",
+ "HH:mm:ss.FFFFFFF",
+ "HH:mm:ss",
+ "HH:mm",
+ "yyyy-MM-dd HH:mm:ss.FFFFFFF", /* NOTE: Non-UTC default (19). */
+ "yyyy-MM-dd HH:mm:ss",
+ "yyyy-MM-dd HH:mm",
+ "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
+ "yyyy-MM-ddTHH:mm",
+ "yyyy-MM-ddTHH:mm:ss",
+ "yyyyMMddHHmmss",
+ "yyyyMMddHHmm",
+ "yyyyMMddTHHmmssFFFFFFF",
+ "yyyy-MM-dd",
+ "yyyyMMdd",
+ "yy-MM-dd"
+ };
+
+ ///
+ /// The internal default format for UTC DateTime values when converting
+ /// to a string.
+ ///
+ private static readonly string _datetimeFormatUtc = _datetimeFormats[5];
+
+ ///
+ /// The internal default format for local DateTime values when converting
+ /// to a string.
+ ///
+ private static readonly string _datetimeFormatLocal = _datetimeFormats[19];
+
+ ///
+ /// An UTF-8 Encoding instance, so we can convert strings to and from UTF-8
+ ///
+ private static Encoding _utf8 = new UTF8Encoding();
+ ///
+ /// The default DateTime format for this instance.
+ ///
+ internal SQLiteDateFormats _datetimeFormat;
+ ///
+ /// The default DateTimeKind for this instance.
+ ///
+ internal DateTimeKind _datetimeKind;
+ ///
+ /// The default DateTime format string for this instance.
+ ///
+ internal string _datetimeFormatString = null;
+ ///
+ /// Initializes the conversion class
+ ///
+ /// The default date/time format to use for this instance
+ /// The DateTimeKind to use.
+ /// The DateTime format string to use.
+ internal SQLiteConvert(
+ SQLiteDateFormats fmt,
+ DateTimeKind kind,
+ string fmtString
+ )
+ {
+ _datetimeFormat = fmt;
+ _datetimeKind = kind;
+ _datetimeFormatString = fmtString;
+ }
+
+ #region UTF-8 Conversion Functions
+ ///
+ /// Converts a string to a UTF-8 encoded byte array sized to include a null-terminating character.
+ ///
+ /// The string to convert to UTF-8
+ /// A byte array containing the converted string plus an extra 0 terminating byte at the end of the array.
+ public static byte[] ToUTF8(string sourceText)
+ {
+ if (sourceText == null) return null;
+ Byte[] byteArray;
+ int nlen = _utf8.GetByteCount(sourceText) + 1;
+
+ byteArray = new byte[nlen];
+ nlen = _utf8.GetBytes(sourceText, 0, sourceText.Length, byteArray, 0);
+ byteArray[nlen] = 0;
+
+ return byteArray;
+ }
+
+ ///
+ /// Convert a DateTime to a UTF-8 encoded, zero-terminated byte array.
+ ///
+ ///
+ /// This function is a convenience function, which first calls ToString() on the DateTime, and then calls ToUTF8() with the
+ /// string result.
+ ///
+ /// The DateTime to convert.
+ /// The UTF-8 encoded string, including a 0 terminating byte at the end of the array.
+ public byte[] ToUTF8(DateTime dateTimeValue)
+ {
+ return ToUTF8(ToString(dateTimeValue));
+ }
+
+ ///
+ /// Converts a UTF-8 encoded IntPtr of the specified length into a .NET string
+ ///
+ /// The pointer to the memory where the UTF-8 string is encoded
+ /// The number of bytes to decode
+ /// A string containing the translated character(s)
+ public virtual string ToString(IntPtr nativestring, int nativestringlen)
+ {
+ return UTF8ToString(nativestring, nativestringlen);
+ }
+
+ ///
+ /// Converts a UTF-8 encoded IntPtr of the specified length into a .NET string
+ ///
+ /// The pointer to the memory where the UTF-8 string is encoded
+ /// The number of bytes to decode
+ /// A string containing the translated character(s)
+ public static string UTF8ToString(IntPtr nativestring, int nativestringlen)
+ {
+ if (nativestring == IntPtr.Zero || nativestringlen == 0) return String.Empty;
+ if (nativestringlen < 0)
+ {
+ nativestringlen = 0;
+
+ while (Marshal.ReadByte(nativestring, nativestringlen) != 0)
+ nativestringlen++;
+
+ if (nativestringlen == 0) return String.Empty;
+ }
+
+ byte[] byteArray = new byte[nativestringlen];
+
+ Marshal.Copy(nativestring, byteArray, 0, nativestringlen);
+
+ return _utf8.GetString(byteArray, 0, nativestringlen);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region DateTime Conversion Functions
+ #region New Julian Day Conversion Methods
+ ///
+ /// Checks if the specified is within the
+ /// supported range for a Julian Day value.
+ ///
+ ///
+ /// The Julian Day value to check.
+ ///
+ ///
+ /// Non-zero if the specified Julian Day value is in the supported
+ /// range; otherwise, zero.
+ ///
+ private static bool isValidJd(
+ long jd
+ )
+ {
+ return ((jd >= MinimumJd) && (jd <= MaximumJd));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a Julian Day value from a to an
+ /// .
+ ///
+ ///
+ /// The Julian Day value to convert.
+ ///
+ ///
+ /// The resulting Julian Day value.
+ ///
+ private static long DoubleToJd(
+ double julianDay
+ )
+ {
+ return (long)Math.Round(julianDay * 86400000.0);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a Julian Day value from an to a
+ /// .
+ ///
+ ///
+ /// The Julian Day value to convert.
+ ///
+ ///
+ /// The resulting Julian Day value.
+ ///
+ private static double JdToDouble(
+ long jd
+ )
+ {
+ return (double)(jd / 86400000.0);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a Julian Day value to a .
+ /// This method was translated from the "computeYMD" function in the
+ /// "date.c" file belonging to the SQLite core library.
+ ///
+ ///
+ /// The Julian Day value to convert.
+ ///
+ ///
+ /// The value to return in the event that the
+ /// Julian Day is out of the supported range. If this value is null,
+ /// an exception will be thrown instead.
+ ///
+ ///
+ /// A value that contains the year, month, and
+ /// day values that are closest to the specified Julian Day value.
+ ///
+ private static DateTime computeYMD(
+ long jd,
+ DateTime? badValue
+ )
+ {
+ if (!isValidJd(jd))
+ {
+ if (badValue == null)
+ {
+ throw new ArgumentException(
+ "Not a supported Julian Day value.");
+ }
+
+ return (DateTime)badValue;
+ }
+
+ int Z, A, B, C, D, E, X1;
+
+ Z = (int)((jd + 43200000) / 86400000);
+ A = (int)((Z - 1867216.25) / 36524.25);
+ A = Z + 1 + A - (A / 4);
+ B = A + 1524;
+ C = (int)((B - 122.1) / 365.25);
+ D = (36525 * C) / 100;
+ E = (int)((B - D) / 30.6001);
+ X1 = (int)(30.6001 * E);
+
+ int day, month, year;
+
+ day = B - D - X1;
+ month = E < 14 ? E - 1 : E - 13;
+ year = month > 2 ? C - 4716 : C - 4715;
+
+ try
+ {
+ return new DateTime(year, month, day);
+ }
+ catch
+ {
+ if (badValue == null)
+ throw;
+
+ return (DateTime)badValue;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a Julian Day value to a .
+ /// This method was translated from the "computeHMS" function in the
+ /// "date.c" file belonging to the SQLite core library.
+ ///
+ ///
+ /// The Julian Day value to convert.
+ ///
+ ///
+ /// The value to return in the event that the
+ /// Julian Day value is out of the supported range. If this value is
+ /// null, an exception will be thrown instead.
+ ///
+ ///
+ /// A value that contains the hour, minute, and
+ /// second, and millisecond values that are closest to the specified
+ /// Julian Day value.
+ ///
+ private static DateTime computeHMS(
+ long jd,
+ DateTime? badValue
+ )
+ {
+ if (!isValidJd(jd))
+ {
+ if (badValue == null)
+ {
+ throw new ArgumentException(
+ "Not a supported Julian Day value.");
+ }
+
+ return (DateTime)badValue;
+ }
+
+ int si;
+
+ si = (int)((jd + 43200000) % 86400000);
+
+ decimal sd;
+
+ sd = si / 1000.0M;
+ si = (int)sd;
+
+ int millisecond = (int)((sd - si) * 1000.0M);
+
+ sd -= si;
+
+ int hour;
+
+ hour = si / 3600;
+ si -= hour * 3600;
+
+ int minute;
+
+ minute = si / 60;
+ sd += si - minute * 60;
+
+ int second = (int)sd;
+
+ try
+ {
+ DateTime minValue = DateTime.MinValue;
+
+ return new DateTime(
+ minValue.Year, minValue.Month, minValue.Day,
+ hour, minute, second, millisecond);
+ }
+ catch
+ {
+ if (badValue == null)
+ throw;
+
+ return (DateTime)badValue;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a to a Julian Day value.
+ /// This method was translated from the "computeJD" function in
+ /// the "date.c" file belonging to the SQLite core library.
+ /// Since the range of Julian Day values supported by this method
+ /// includes all possible (valid) values of a
+ /// value, it should be extremely difficult for this method to
+ /// raise an exception or return an undefined result.
+ ///
+ ///
+ /// The value to convert. This value
+ /// will be within the range of
+ /// (00:00:00.0000000, January 1, 0001) to
+ /// (23:59:59.9999999, December
+ /// 31, 9999).
+ ///
+ ///
+ /// The nearest Julian Day value corresponding to the specified
+ /// value.
+ ///
+ private static long computeJD(
+ DateTime dateTime
+ )
+ {
+ int Y, M, D;
+
+ Y = dateTime.Year;
+ M = dateTime.Month;
+ D = dateTime.Day;
+
+ if (M <= 2)
+ {
+ Y--;
+ M += 12;
+ }
+
+ int A, B, X1, X2;
+
+ A = Y / 100;
+ B = 2 - A + (A / 4);
+ X1 = 36525 * (Y + 4716) / 100;
+ X2 = 306001 * (M + 1) / 10000;
+
+ long jd;
+
+ jd = (long)((X1 + X2 + D + B - 1524.5) * 86400000);
+
+ jd += (dateTime.Hour * 3600000) + (dateTime.Minute * 60000) +
+ (dateTime.Second * 1000) + dateTime.Millisecond;
+
+ return jd;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a string into a DateTime, using the DateTimeFormat, DateTimeKind,
+ /// and DateTimeFormatString specified for the connection when it was opened.
+ ///
+ ///
+ /// Acceptable ISO8601 DateTime formats are:
+ ///
+ /// - THHmmssK
+ /// - THHmmK
+ /// - HH:mm:ss.FFFFFFFK
+ /// - HH:mm:ssK
+ /// - HH:mmK
+ /// - yyyy-MM-dd HH:mm:ss.FFFFFFFK
+ /// - yyyy-MM-dd HH:mm:ssK
+ /// - yyyy-MM-dd HH:mmK
+ /// - yyyy-MM-ddTHH:mm:ss.FFFFFFFK
+ /// - yyyy-MM-ddTHH:mmK
+ /// - yyyy-MM-ddTHH:mm:ssK
+ /// - yyyyMMddHHmmssK
+ /// - yyyyMMddHHmmK
+ /// - yyyyMMddTHHmmssFFFFFFFK
+ /// - THHmmss
+ /// - THHmm
+ /// - HH:mm:ss.FFFFFFF
+ /// - HH:mm:ss
+ /// - HH:mm
+ /// - yyyy-MM-dd HH:mm:ss.FFFFFFF
+ /// - yyyy-MM-dd HH:mm:ss
+ /// - yyyy-MM-dd HH:mm
+ /// - yyyy-MM-ddTHH:mm:ss.FFFFFFF
+ /// - yyyy-MM-ddTHH:mm
+ /// - yyyy-MM-ddTHH:mm:ss
+ /// - yyyyMMddHHmmss
+ /// - yyyyMMddHHmm
+ /// - yyyyMMddTHHmmssFFFFFFF
+ /// - yyyy-MM-dd
+ /// - yyyyMMdd
+ /// - yy-MM-dd
+ ///
+ /// If the string cannot be matched to one of the above formats -OR-
+ /// the DateTimeFormatString if one was provided, an exception will
+ /// be thrown.
+ ///
+ /// The string containing either a long integer number of 100-nanosecond units since
+ /// System.DateTime.MinValue, a Julian day double, an integer number of seconds since the Unix epoch, a
+ /// culture-independent formatted date and time string, a formatted date and time string in the current
+ /// culture, or an ISO8601-format string.
+ /// A DateTime value
+ public DateTime ToDateTime(string dateText)
+ {
+ return ToDateTime(dateText, _datetimeFormat, _datetimeKind, _datetimeFormatString);
+ }
+
+ ///
+ /// Converts a string into a DateTime, using the specified DateTimeFormat,
+ /// DateTimeKind and DateTimeFormatString.
+ ///
+ ///
+ /// Acceptable ISO8601 DateTime formats are:
+ ///
+ /// - THHmmssK
+ /// - THHmmK
+ /// - HH:mm:ss.FFFFFFFK
+ /// - HH:mm:ssK
+ /// - HH:mmK
+ /// - yyyy-MM-dd HH:mm:ss.FFFFFFFK
+ /// - yyyy-MM-dd HH:mm:ssK
+ /// - yyyy-MM-dd HH:mmK
+ /// - yyyy-MM-ddTHH:mm:ss.FFFFFFFK
+ /// - yyyy-MM-ddTHH:mmK
+ /// - yyyy-MM-ddTHH:mm:ssK
+ /// - yyyyMMddHHmmssK
+ /// - yyyyMMddHHmmK
+ /// - yyyyMMddTHHmmssFFFFFFFK
+ /// - THHmmss
+ /// - THHmm
+ /// - HH:mm:ss.FFFFFFF
+ /// - HH:mm:ss
+ /// - HH:mm
+ /// - yyyy-MM-dd HH:mm:ss.FFFFFFF
+ /// - yyyy-MM-dd HH:mm:ss
+ /// - yyyy-MM-dd HH:mm
+ /// - yyyy-MM-ddTHH:mm:ss.FFFFFFF
+ /// - yyyy-MM-ddTHH:mm
+ /// - yyyy-MM-ddTHH:mm:ss
+ /// - yyyyMMddHHmmss
+ /// - yyyyMMddHHmm
+ /// - yyyyMMddTHHmmssFFFFFFF
+ /// - yyyy-MM-dd
+ /// - yyyyMMdd
+ /// - yy-MM-dd
+ ///
+ /// If the string cannot be matched to one of the above formats -OR-
+ /// the DateTimeFormatString if one was provided, an exception will
+ /// be thrown.
+ ///
+ /// The string containing either a long integer number of 100-nanosecond units since
+ /// System.DateTime.MinValue, a Julian day double, an integer number of seconds since the Unix epoch, a
+ /// culture-independent formatted date and time string, a formatted date and time string in the current
+ /// culture, or an ISO8601-format string.
+ /// The SQLiteDateFormats to use.
+ /// The DateTimeKind to use.
+ /// The DateTime format string to use.
+ /// A DateTime value
+ public static DateTime ToDateTime(
+ string dateText,
+ SQLiteDateFormats format,
+ DateTimeKind kind,
+ string formatString
+ )
+ {
+ switch (format)
+ {
+ case SQLiteDateFormats.Ticks:
+ {
+ return TicksToDateTime(Convert.ToInt64(
+ dateText, CultureInfo.InvariantCulture), kind);
+ }
+ case SQLiteDateFormats.JulianDay:
+ {
+ return ToDateTime(Convert.ToDouble(
+ dateText, CultureInfo.InvariantCulture), kind);
+ }
+ case SQLiteDateFormats.UnixEpoch:
+ {
+ return UnixEpochToDateTime(Convert.ToInt64(
+ dateText, CultureInfo.InvariantCulture), kind);
+ }
+ case SQLiteDateFormats.InvariantCulture:
+ {
+ if (formatString != null)
+ return DateTime.SpecifyKind(DateTime.ParseExact(
+ dateText, formatString,
+ DateTimeFormatInfo.InvariantInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ else
+ return DateTime.SpecifyKind(DateTime.Parse(
+ dateText, DateTimeFormatInfo.InvariantInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ }
+ case SQLiteDateFormats.CurrentCulture:
+ {
+ if (formatString != null)
+ return DateTime.SpecifyKind(DateTime.ParseExact(
+ dateText, formatString,
+ DateTimeFormatInfo.CurrentInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ else
+ return DateTime.SpecifyKind(DateTime.Parse(
+ dateText, DateTimeFormatInfo.CurrentInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ }
+ default: /* ISO-8601 */
+ {
+ if (formatString != null)
+ return DateTime.SpecifyKind(DateTime.ParseExact(
+ dateText, formatString,
+ DateTimeFormatInfo.InvariantInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ else
+ return DateTime.SpecifyKind(DateTime.ParseExact(
+ dateText, _datetimeFormats,
+ DateTimeFormatInfo.InvariantInfo,
+ kind == DateTimeKind.Utc ?
+ DateTimeStyles.AdjustToUniversal :
+ DateTimeStyles.None),
+ kind);
+ }
+ }
+ }
+
+ ///
+ /// Converts a julianday value into a DateTime
+ ///
+ /// The value to convert
+ /// A .NET DateTime
+ public DateTime ToDateTime(double julianDay)
+ {
+ return ToDateTime(julianDay, _datetimeKind);
+ }
+
+ ///
+ /// Converts a julianday value into a DateTime
+ ///
+ /// The value to convert
+ /// The DateTimeKind to use.
+ /// A .NET DateTime
+ public static DateTime ToDateTime(
+ double julianDay,
+ DateTimeKind kind
+ )
+ {
+ long jd = DoubleToJd(julianDay);
+ DateTime dateTimeYMD = computeYMD(jd, null);
+ DateTime dateTimeHMS = computeHMS(jd, null);
+
+ return new DateTime(
+ dateTimeYMD.Year, dateTimeYMD.Month, dateTimeYMD.Day,
+ dateTimeHMS.Hour, dateTimeHMS.Minute, dateTimeHMS.Second,
+ dateTimeHMS.Millisecond, kind);
+ }
+
+ ///
+ /// Converts the specified number of seconds from the Unix epoch into a
+ /// value.
+ ///
+ ///
+ /// The number of whole seconds since the Unix epoch.
+ ///
+ ///
+ /// Either Utc or Local time.
+ ///
+ ///
+ /// The new value.
+ ///
+ internal static DateTime UnixEpochToDateTime(long seconds, DateTimeKind kind)
+ {
+ return DateTime.SpecifyKind(UnixEpoch.AddSeconds(seconds), kind);
+ }
+
+ ///
+ /// Converts the specified number of ticks since the epoch into a
+ /// value.
+ ///
+ ///
+ /// The number of whole ticks since the epoch.
+ ///
+ ///
+ /// Either Utc or Local time.
+ ///
+ ///
+ /// The new value.
+ ///
+ internal static DateTime TicksToDateTime(long ticks, DateTimeKind kind)
+ {
+ return new DateTime(ticks, kind);
+ }
+
+ ///
+ /// Converts a DateTime struct to a JulianDay double
+ ///
+ /// The DateTime to convert
+ /// The JulianDay value the Datetime represents
+ public static double ToJulianDay(DateTime value)
+ {
+ return JdToDouble(computeJD(value));
+ }
+
+ ///
+ /// Converts a DateTime struct to the whole number of seconds since the
+ /// Unix epoch.
+ ///
+ /// The DateTime to convert
+ /// The whole number of seconds since the Unix epoch
+ public static long ToUnixEpoch(DateTime value)
+ {
+ return (value.Subtract(UnixEpoch).Ticks / TimeSpan.TicksPerSecond);
+ }
+
+ ///
+ /// Returns the DateTime format string to use for the specified DateTimeKind.
+ /// If is not null, it will be returned verbatim.
+ ///
+ /// The DateTimeKind to use.
+ /// The DateTime format string to use.
+ ///
+ /// The DateTime format string to use for the specified DateTimeKind.
+ ///
+ private static string GetDateTimeKindFormat(
+ DateTimeKind kind,
+ string formatString
+ )
+ {
+ if (formatString != null) return formatString;
+ return (kind == DateTimeKind.Utc) ? _datetimeFormatUtc : _datetimeFormatLocal;
+ }
+
+ ///
+ /// Converts a string into a DateTime, using the DateTimeFormat, DateTimeKind,
+ /// and DateTimeFormatString specified for the connection when it was opened.
+ ///
+ /// The DateTime value to convert
+ /// Either a string containing the long integer number of 100-nanosecond units since System.DateTime.MinValue, a
+ /// Julian day double, an integer number of seconds since the Unix epoch, a culture-independent formatted date and time
+ /// string, a formatted date and time string in the current culture, or an ISO8601-format date/time string.
+ public string ToString(DateTime dateValue)
+ {
+ return ToString(dateValue, _datetimeFormat, _datetimeKind, _datetimeFormatString);
+ }
+
+ ///
+ /// Converts a string into a DateTime, using the DateTimeFormat, DateTimeKind,
+ /// and DateTimeFormatString specified for the connection when it was opened.
+ ///
+ /// The DateTime value to convert
+ /// The SQLiteDateFormats to use.
+ /// The DateTimeKind to use.
+ /// The DateTime format string to use.
+ /// Either a string containing the long integer number of 100-nanosecond units since System.DateTime.MinValue, a
+ /// Julian day double, an integer number of seconds since the Unix epoch, a culture-independent formatted date and time
+ /// string, a formatted date and time string in the current culture, or an ISO8601-format date/time string.
+ public static string ToString(
+ DateTime dateValue,
+ SQLiteDateFormats format,
+ DateTimeKind kind,
+ string formatString
+ )
+ {
+ switch (format)
+ {
+ case SQLiteDateFormats.Ticks:
+ return dateValue.Ticks.ToString(CultureInfo.InvariantCulture);
+ case SQLiteDateFormats.JulianDay:
+ return ToJulianDay(dateValue).ToString(CultureInfo.InvariantCulture);
+ case SQLiteDateFormats.UnixEpoch:
+ return ((long)(dateValue.Subtract(UnixEpoch).Ticks / TimeSpan.TicksPerSecond)).ToString();
+ case SQLiteDateFormats.InvariantCulture:
+ return dateValue.ToString((formatString != null) ?
+ formatString : FullFormat, CultureInfo.InvariantCulture);
+ case SQLiteDateFormats.CurrentCulture:
+ return dateValue.ToString((formatString != null) ?
+ formatString : FullFormat, CultureInfo.CurrentCulture);
+ default:
+ return (dateValue.Kind == DateTimeKind.Unspecified) ?
+ DateTime.SpecifyKind(dateValue, kind).ToString(
+ GetDateTimeKindFormat(kind, formatString),
+ CultureInfo.InvariantCulture) : dateValue.ToString(
+ GetDateTimeKindFormat(dateValue.Kind, formatString),
+ CultureInfo.InvariantCulture);
+ }
+ }
+
+ ///
+ /// Internal function to convert a UTF-8 encoded IntPtr of the specified length to a DateTime.
+ ///
+ ///
+ /// This is a convenience function, which first calls ToString() on the IntPtr to convert it to a string, then calls
+ /// ToDateTime() on the string to return a DateTime.
+ ///
+ /// A pointer to the UTF-8 encoded string
+ /// The length in bytes of the string
+ /// The parsed DateTime value
+ internal DateTime ToDateTime(IntPtr ptr, int len)
+ {
+ return ToDateTime(ToString(ptr, len));
+ }
+ #endregion
+
+ ///
+ /// Smart method of splitting a string. Skips quoted elements, removes the quotes.
+ ///
+ ///
+ /// This split function works somewhat like the String.Split() function in that it breaks apart a string into
+ /// pieces and returns the pieces as an array. The primary differences are:
+ ///
+ /// - Only one character can be provided as a separator character
+ /// - Quoted text inside the string is skipped over when searching for the separator, and the quotes are removed.
+ ///
+ /// Thus, if splitting the following string looking for a comma:
+ /// One,Two, "Three, Four", Five
+ ///
+ /// The resulting array would contain
+ /// [0] One
+ /// [1] Two
+ /// [2] Three, Four
+ /// [3] Five
+ ///
+ /// Note that the leading and trailing spaces were removed from each item during the split.
+ ///
+ /// Source string to split apart
+ /// Separator character
+ /// A string array of the split up elements
+ public static string[] Split(string source, char separator)
+ {
+ char[] toks = new char[2] { QuoteChar, separator };
+ char[] quot = new char[1] { QuoteChar };
+ int n = 0;
+ List ls = new List();
+ string s;
+
+ while (source.Length > 0)
+ {
+ n = source.IndexOfAny(toks, n);
+ if (n == -1) break;
+ if (source[n] == toks[0])
+ {
+ //source = source.Remove(n, 1);
+ n = source.IndexOfAny(quot, n + 1);
+ if (n == -1)
+ {
+ //source = "\"" + source;
+ break;
+ }
+ n++;
+ //source = source.Remove(n, 1);
+ }
+ else
+ {
+ s = source.Substring(0, n).Trim();
+ if (s.Length > 1 && s[0] == quot[0] && s[s.Length - 1] == s[0])
+ s = s.Substring(1, s.Length - 2);
+
+ source = source.Substring(n + 1).Trim();
+ if (s.Length > 0) ls.Add(s);
+ n = 0;
+ }
+ }
+ if (source.Length > 0)
+ {
+ s = source.Trim();
+ if (s.Length > 1 && s[0] == quot[0] && s[s.Length - 1] == s[0])
+ s = s.Substring(1, s.Length - 2);
+ ls.Add(s);
+ }
+
+ string[] ar = new string[ls.Count];
+ ls.CopyTo(ar, 0);
+
+ return ar;
+ }
+
+ ///
+ /// Splits the specified string into multiple strings based on a separator
+ /// and returns the result as an array of strings.
+ ///
+ ///
+ /// The string to split into pieces based on the separator character. If
+ /// this string is null, null will always be returned. If this string is
+ /// empty, an array of zero strings will always be returned.
+ ///
+ ///
+ /// The character used to divide the original string into sub-strings.
+ /// This character cannot be a backslash or a double-quote; otherwise, no
+ /// work will be performed and null will be returned.
+ ///
+ ///
+ /// If this parameter is non-zero, all double-quote characters will be
+ /// retained in the returned list of strings; otherwise, they will be
+ /// dropped.
+ ///
+ ///
+ /// Upon failure, this parameter will be modified to contain an appropriate
+ /// error message.
+ ///
+ ///
+ /// The new array of strings or null if the input string is null -OR- the
+ /// separator character is a backslash or a double-quote -OR- the string
+ /// contains an unbalanced backslash or double-quote character.
+ ///
+ internal static string[] NewSplit(
+ string value,
+ char separator,
+ bool keepQuote,
+ ref string error
+ )
+ {
+ //
+ // NOTE: It is illegal for the separator character to be either a
+ // backslash or a double-quote because both of those characters
+ // are used for escaping other characters (e.g. the separator
+ // character).
+ //
+ if ((separator == EscapeChar) || (separator == QuoteChar))
+ {
+ error = "separator character cannot be the escape or quote characters";
+ return null;
+ }
+
+ if (value == null)
+ {
+ error = "string value to split cannot be null";
+ return null;
+ }
+
+ int length = value.Length;
+
+ if (length == 0)
+ return new string[0];
+
+ List list = new List();
+ StringBuilder element = new StringBuilder();
+ int index = 0;
+ bool escape = false;
+ bool quote = false;
+
+ while (index < length)
+ {
+ char character = value[index++];
+
+ if (escape)
+ {
+ //
+ // HACK: Only consider the escape character to be an actual
+ // "escape" if it is followed by a reserved character;
+ // otherwise, emit the original escape character and
+ // the current character in an effort to help preserve
+ // the original string content.
+ //
+ if ((character != EscapeChar) &&
+ (character != QuoteChar) &&
+ (character != separator))
+ {
+ element.Append(EscapeChar);
+ }
+
+ element.Append(character);
+ escape = false;
+ }
+ else if (character == EscapeChar)
+ {
+ escape = true;
+ }
+ else if (character == QuoteChar)
+ {
+ if (keepQuote)
+ element.Append(character);
+
+ quote = !quote;
+ }
+ else if (character == separator)
+ {
+ if (quote)
+ {
+ element.Append(character);
+ }
+ else
+ {
+ list.Add(element.ToString());
+ element.Length = 0;
+ }
+ }
+ else
+ {
+ element.Append(character);
+ }
+ }
+
+ //
+ // NOTE: An unbalanced escape or quote character in the string is
+ // considered to be a fatal error; therefore, return null.
+ //
+ if (escape || quote)
+ {
+ error = "unbalanced escape or quote character found";
+ return null;
+ }
+
+ if (element.Length > 0)
+ list.Add(element.ToString());
+
+ return list.ToArray();
+ }
+
+ ///
+ /// Queries and returns the string representation for an object, using the
+ /// specified (or current) format provider.
+ ///
+ ///
+ /// The object instance to return the string representation for.
+ ///
+ ///
+ /// The format provider to use -OR- null if the current format provider for
+ /// the thread should be used instead.
+ ///
+ ///
+ /// The string representation for the object instance -OR- null if the
+ /// object instance is also null.
+ ///
+ public static string ToStringWithProvider(
+ object obj,
+ IFormatProvider provider
+ )
+ {
+ if (obj == null)
+ return null; /* null --> null */
+
+ if (obj is string)
+ return (string)obj; /* identity */
+
+ IConvertible convertible = obj as IConvertible;
+
+ if (convertible != null)
+ return convertible.ToString(provider);
+
+ return obj.ToString(); /* not IConvertible */
+ }
+
+ ///
+ /// Attempts to convert an arbitrary object to the Boolean data type.
+ /// Null object values are converted to false. Throws an exception
+ /// upon failure.
+ ///
+ ///
+ /// The object value to convert.
+ ///
+ ///
+ /// The format provider to use.
+ ///
+ ///
+ /// If non-zero, a string value will be converted using the
+ ///
+ /// method; otherwise, the
+ /// method will be used.
+ ///
+ ///
+ /// The converted boolean value.
+ ///
+ internal static bool ToBoolean(
+ object obj,
+ IFormatProvider provider,
+ bool viaFramework
+ )
+ {
+ if (obj == null)
+ return false;
+
+ TypeCode typeCode = Type.GetTypeCode(obj.GetType());
+
+ switch (typeCode)
+ {
+ case TypeCode.Empty:
+ case TypeCode.DBNull:
+ return false;
+ case TypeCode.Boolean:
+ return (bool)obj;
+ case TypeCode.Char:
+ return ((char)obj) != (char)0 ? true : false;
+ case TypeCode.SByte:
+ return ((sbyte)obj) != (sbyte)0 ? true : false;
+ case TypeCode.Byte:
+ return ((byte)obj) != (byte)0 ? true : false;
+ case TypeCode.Int16:
+ return ((short)obj) != (short)0 ? true : false;
+ case TypeCode.UInt16:
+ return ((ushort)obj) != (ushort)0 ? true : false;
+ case TypeCode.Int32:
+ return ((int)obj) != (int)0 ? true : false;
+ case TypeCode.UInt32:
+ return ((uint)obj) != (uint)0 ? true : false;
+ case TypeCode.Int64:
+ return ((long)obj) != (long)0 ? true : false;
+ case TypeCode.UInt64:
+ return ((ulong)obj) != (ulong)0 ? true : false;
+ case TypeCode.Single:
+ return ((float)obj) != (float)0.0 ? true : false;
+ case TypeCode.Double:
+ return ((double)obj) != (double)0.0 ? true : false;
+ case TypeCode.Decimal:
+ return ((decimal)obj) != Decimal.Zero ? true : false;
+ case TypeCode.String:
+ return viaFramework ?
+ Convert.ToBoolean(obj, provider) :
+ ToBoolean(ToStringWithProvider(obj, provider));
+ default:
+ throw new SQLiteException(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Cannot convert type {0} to boolean",
+ typeCode));
+ }
+ }
+
+ ///
+ /// Convert a value to true or false.
+ ///
+ /// A string or number representing true or false
+ ///
+ public static bool ToBoolean(object source)
+ {
+ if (source is bool) return (bool)source;
+
+ return ToBoolean(ToStringWithProvider(
+ source, CultureInfo.InvariantCulture));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts an integer to a string that can be round-tripped using the
+ /// invariant culture.
+ ///
+ ///
+ /// The integer value to return the string representation for.
+ ///
+ ///
+ /// The string representation of the specified integer value, using the
+ /// invariant culture.
+ ///
+ internal static string ToString(int value)
+ {
+ return value.ToString(CultureInfo.InvariantCulture);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to convert a into a .
+ ///
+ ///
+ /// The to convert, cannot be null.
+ ///
+ ///
+ /// The converted value.
+ ///
+ ///
+ /// The supported strings are "yes", "no", "y", "n", "on", "off", "0", "1",
+ /// as well as any prefix of the strings
+ /// and . All strings are treated in a
+ /// case-insensitive manner.
+ ///
+ public static bool ToBoolean(string source)
+ {
+ if (source == null) throw new ArgumentNullException("source");
+ if (String.Compare(source, 0, bool.TrueString, 0, source.Length, StringComparison.OrdinalIgnoreCase) == 0) return true;
+ else if (String.Compare(source, 0, bool.FalseString, 0, source.Length, StringComparison.OrdinalIgnoreCase) == 0) return false;
+
+ switch (source.ToLower(CultureInfo.InvariantCulture))
+ {
+ case "y":
+ case "yes":
+ case "on":
+ case "1":
+ return true;
+ case "n":
+ case "no":
+ case "off":
+ case "0":
+ return false;
+ }
+
+ throw new ArgumentException("source");
+ }
+
+ #region Type Conversions
+ ///
+ /// Converts a SQLiteType to a .NET Type object
+ ///
+ /// The SQLiteType to convert
+ /// Returns a .NET Type object
+ internal static Type SQLiteTypeToType(SQLiteType t)
+ {
+ if (t.Type == DbType.Object)
+ return _affinitytotype[(int)t.Affinity];
+ else
+ return SQLiteConvert.DbTypeToType(t.Type);
+ }
+
+ private static Type[] _affinitytotype = {
+ typeof(object), // Uninitialized (0)
+ typeof(Int64), // Int64 (1)
+ typeof(Double), // Double (2)
+ typeof(string), // Text (3)
+ typeof(byte[]), // Blob (4)
+ typeof(object), // Null (5)
+ null, // Undefined (6)
+ null, // Undefined (7)
+ null, // Undefined (8)
+ null, // Undefined (9)
+ typeof(DateTime), // DateTime (10)
+ typeof(object) // None (11)
+ };
+
+ ///
+ /// For a given intrinsic type, return a DbType
+ ///
+ /// The native type to convert
+ /// The corresponding (closest match) DbType
+ internal static DbType TypeToDbType(Type typ)
+ {
+ TypeCode tc = Type.GetTypeCode(typ);
+ if (tc == TypeCode.Object)
+ {
+ if (typ == typeof(byte[])) return DbType.Binary;
+ if (typ == typeof(Guid)) return DbType.Guid;
+ return DbType.String;
+ }
+ return _typetodbtype[(int)tc];
+ }
+
+ private static DbType[] _typetodbtype = {
+ DbType.Object, // Empty (0)
+ DbType.Binary, // Object (1)
+ DbType.Object, // DBNull (2)
+ DbType.Boolean, // Boolean (3)
+ DbType.SByte, // Char (4)
+ DbType.SByte, // SByte (5)
+ DbType.Byte, // Byte (6)
+ DbType.Int16, // Int16 (7)
+ DbType.UInt16, // UInt16 (8)
+ DbType.Int32, // Int32 (9)
+ DbType.UInt32, // UInt32 (10)
+ DbType.Int64, // Int64 (11)
+ DbType.UInt64, // UInt64 (12)
+ DbType.Single, // Single (13)
+ DbType.Double, // Double (14)
+ DbType.Decimal, // Decimal (15)
+ DbType.DateTime, // DateTime (16)
+ DbType.Object, // ?? (17)
+ DbType.String // String (18)
+ };
+
+ ///
+ /// Returns the ColumnSize for the given DbType
+ ///
+ /// The DbType to get the size of
+ ///
+ internal static int DbTypeToColumnSize(DbType typ)
+ {
+ return _dbtypetocolumnsize[(int)typ];
+ }
+
+ private static int[] _dbtypetocolumnsize = {
+ int.MaxValue, // AnsiString (0)
+ int.MaxValue, // Binary (1)
+ 1, // Byte (2)
+ 1, // Boolean (3)
+ 8, // Currency (4)
+ 8, // Date (5)
+ 8, // DateTime (6)
+ 8, // Decimal (7)
+ 8, // Double (8)
+ 16, // Guid (9)
+ 2, // Int16 (10)
+ 4, // Int32 (11)
+ 8, // Int64 (12)
+ int.MaxValue, // Object (13)
+ 1, // SByte (14)
+ 4, // Single (15)
+ int.MaxValue, // String (16)
+ 8, // Time (17)
+ 2, // UInt16 (18)
+ 4, // UInt32 (19)
+ 8, // UInt64 (20)
+ 8, // VarNumeric (21)
+ int.MaxValue, // AnsiStringFixedLength (22)
+ int.MaxValue, // StringFixedLength (23)
+ int.MaxValue, // ?? (24)
+ int.MaxValue, // Xml (25)
+ 8, // DateTime2 (26)
+ 10 // DateTimeOffset (27)
+ };
+
+ internal static object DbTypeToNumericPrecision(DbType typ)
+ {
+ return _dbtypetonumericprecision[(int)typ];
+ }
+
+ private static object[] _dbtypetonumericprecision = {
+ DBNull.Value, // AnsiString (0)
+ DBNull.Value, // Binary (1)
+ 3, // Byte (2)
+ DBNull.Value, // Boolean (3)
+ 19, // Currency (4)
+ DBNull.Value, // Date (5)
+ DBNull.Value, // DateTime (6)
+ 53, // Decimal (7)
+ 53, // Double (8)
+ DBNull.Value, // Guid (9)
+ 5, // Int16 (10)
+ 10, // Int32 (11)
+ 19, // Int64 (12)
+ DBNull.Value, // Object (13)
+ 3, // SByte (14)
+ 24, // Single (15)
+ DBNull.Value, // String (16)
+ DBNull.Value, // Time (17)
+ 5, // UInt16 (18)
+ 10, // UInt32 (19)
+ 19, // UInt64 (20)
+ 53, // VarNumeric (21)
+ DBNull.Value, // AnsiStringFixedLength (22)
+ DBNull.Value, // StringFixedLength (23)
+ DBNull.Value, // ?? (24)
+ DBNull.Value, // Xml (25)
+ DBNull.Value, // DateTime2 (26)
+ DBNull.Value // DateTimeOffset (27)
+ };
+
+ internal static object DbTypeToNumericScale(DbType typ)
+ {
+ return _dbtypetonumericscale[(int)typ];
+ }
+
+ private static object[] _dbtypetonumericscale = {
+ DBNull.Value, // AnsiString (0)
+ DBNull.Value, // Binary (1)
+ 0, // Byte (2)
+ DBNull.Value, // Boolean (3)
+ 4, // Currency (4)
+ DBNull.Value, // Date (5)
+ DBNull.Value, // DateTime (6)
+ DBNull.Value, // Decimal (7)
+ DBNull.Value, // Double (8)
+ DBNull.Value, // Guid (9)
+ 0, // Int16 (10)
+ 0, // Int32 (11)
+ 0, // Int64 (12)
+ DBNull.Value, // Object (13)
+ 0, // SByte (14)
+ DBNull.Value, // Single (15)
+ DBNull.Value, // String (16)
+ DBNull.Value, // Time (17)
+ 0, // UInt16 (18)
+ 0, // UInt32 (19)
+ 0, // UInt64 (20)
+ 0, // VarNumeric (21)
+ DBNull.Value, // AnsiStringFixedLength (22)
+ DBNull.Value, // StringFixedLength (23)
+ DBNull.Value, // ?? (24)
+ DBNull.Value, // Xml (25)
+ DBNull.Value, // DateTime2 (26)
+ DBNull.Value // DateTimeOffset (27)
+ };
+
+ ///
+ /// Determines the default database type name to be used when a
+ /// per-connection value is not available.
+ ///
+ ///
+ /// The connection context for type mappings, if any.
+ ///
+ ///
+ /// The default database type name to use.
+ ///
+ private static string GetDefaultTypeName(
+ SQLiteConnection connection
+ )
+ {
+ SQLiteConnectionFlags flags = (connection != null) ?
+ connection.Flags : SQLiteConnectionFlags.None;
+
+ if (HelperMethods.HasFlags(
+ flags, SQLiteConnectionFlags.NoConvertSettings))
+ {
+ return FallbackDefaultTypeName;
+ }
+
+ string name = "Use_SQLiteConvert_DefaultTypeName";
+ object value = null;
+ string @default = null;
+
+ if ((connection == null) ||
+ !connection.TryGetCachedSetting(name, @default, out value))
+ {
+ try
+ {
+ value = UnsafeNativeMethods.GetSettingValue(name, @default);
+
+ if (value == null)
+ value = FallbackDefaultTypeName;
+ }
+ finally
+ {
+ if (connection != null)
+ connection.SetCachedSetting(name, value);
+ }
+ }
+
+ return SettingValueToString(value);
+ }
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ ///
+ /// If applicable, issues a trace log message warning about falling back to
+ /// the default database type name.
+ ///
+ ///
+ /// The database value type.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// The textual name of the database type.
+ ///
+ private static void DefaultTypeNameWarning(
+ DbType dbType,
+ SQLiteConnectionFlags flags,
+ string typeName
+ )
+ {
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.TraceWarning))
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "WARNING: Type mapping failed, returning default name \"{0}\" for type {1}.",
+ typeName, dbType));
+ }
+ }
+
+ ///
+ /// If applicable, issues a trace log message warning about falling back to
+ /// the default database value type.
+ ///
+ ///
+ /// The textual name of the database type.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// The database value type.
+ ///
+ private static void DefaultDbTypeWarning(
+ string typeName,
+ SQLiteConnectionFlags flags,
+ DbType? dbType
+ )
+ {
+ if (!String.IsNullOrEmpty(typeName) &&
+ HelperMethods.HasFlags(flags, SQLiteConnectionFlags.TraceWarning))
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "WARNING: Type mapping failed, returning default type {0} for name \"{1}\".",
+ dbType, typeName));
+ }
+ }
+#endif
+
+ ///
+ /// For a given database value type, return the "closest-match" textual database type name.
+ ///
+ /// The connection context for custom type mappings, if any.
+ /// The database value type.
+ /// The flags associated with the parent connection object.
+ /// The type name or an empty string if it cannot be determined.
+ internal static string DbTypeToTypeName(
+ SQLiteConnection connection,
+ DbType dbType,
+ SQLiteConnectionFlags flags
+ )
+ {
+ string defaultTypeName = null;
+
+ if (connection != null)
+ {
+ flags |= connection.Flags;
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.UseConnectionTypes))
+ {
+ SQLiteDbTypeMap connectionTypeNames = connection._typeNames;
+
+ if (connectionTypeNames != null)
+ {
+ SQLiteDbTypeMapping value;
+
+ if (connectionTypeNames.TryGetValue(dbType, out value))
+ return value.typeName;
+ }
+ }
+
+ //
+ // NOTE: Use the default database type name for the connection.
+ //
+ defaultTypeName = connection.DefaultTypeName;
+ }
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.NoGlobalTypes))
+ {
+ if (defaultTypeName != null)
+ return defaultTypeName;
+
+ defaultTypeName = GetDefaultTypeName(connection);
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ DefaultTypeNameWarning(dbType, flags, defaultTypeName);
+#endif
+
+ return defaultTypeName;
+ }
+
+ {
+ SQLiteDbTypeMapping value;
+
+ if ((_typeNames != null) &&
+ _typeNames.TryGetValue(dbType, out value))
+ {
+ return value.typeName;
+ }
+ }
+
+ if (defaultTypeName != null)
+ return defaultTypeName;
+
+ defaultTypeName = GetDefaultTypeName(connection);
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ DefaultTypeNameWarning(dbType, flags, defaultTypeName);
+#endif
+
+ return defaultTypeName;
+ }
+
+ ///
+ /// Convert a DbType to a Type
+ ///
+ /// The DbType to convert from
+ /// The closest-match .NET type
+ internal static Type DbTypeToType(DbType typ)
+ {
+ return _dbtypeToType[(int)typ];
+ }
+
+ private static Type[] _dbtypeToType = {
+ typeof(string), // AnsiString (0)
+ typeof(byte[]), // Binary (1)
+ typeof(byte), // Byte (2)
+ typeof(bool), // Boolean (3)
+ typeof(decimal), // Currency (4)
+ typeof(DateTime), // Date (5)
+ typeof(DateTime), // DateTime (6)
+ typeof(decimal), // Decimal (7)
+ typeof(double), // Double (8)
+ typeof(Guid), // Guid (9)
+ typeof(Int16), // Int16 (10)
+ typeof(Int32), // Int32 (11)
+ typeof(Int64), // Int64 (12)
+ typeof(object), // Object (13)
+ typeof(sbyte), // SByte (14)
+ typeof(float), // Single (15)
+ typeof(string), // String (16)
+ typeof(DateTime), // Time (17)
+ typeof(UInt16), // UInt16 (18)
+ typeof(UInt32), // UInt32 (19)
+ typeof(UInt64), // UInt64 (20)
+ typeof(double), // VarNumeric (21)
+ typeof(string), // AnsiStringFixedLength (22)
+ typeof(string), // StringFixedLength (23)
+ typeof(string), // ?? (24)
+ typeof(string), // Xml (25)
+ typeof(DateTime), // DateTime2 (26)
+#if !PLATFORM_COMPACTFRAMEWORK && (NET_35 || NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47 || NET_471 || NET_472)
+ //
+ // NOTE: This type is only available on the
+ // .NET Framework 2.0 SP1 and later.
+ //
+ typeof(DateTimeOffset) // DateTimeOffset (27)
+#else
+ typeof(DateTime) // DateTimeOffset (27)
+#endif
+ };
+
+ ///
+ /// For a given type, return the closest-match SQLite TypeAffinity, which only understands a very limited subset of types.
+ ///
+ /// The type to evaluate
+ /// The flags associated with the connection.
+ /// The SQLite type affinity for that type.
+ internal static TypeAffinity TypeToAffinity(
+ Type typ,
+ SQLiteConnectionFlags flags
+ )
+ {
+ TypeCode tc = Type.GetTypeCode(typ);
+ if (tc == TypeCode.Object)
+ {
+ if (typ == typeof(byte[]) || typ == typeof(Guid))
+ return TypeAffinity.Blob;
+ else
+ return TypeAffinity.Text;
+ }
+ if ((tc == TypeCode.Decimal) &&
+ HelperMethods.HasFlags(flags, SQLiteConnectionFlags.GetDecimalAsText))
+ {
+ return TypeAffinity.Text;
+ }
+ return _typecodeAffinities[(int)tc];
+ }
+
+ private static TypeAffinity[] _typecodeAffinities = {
+ TypeAffinity.Null, // Empty (0)
+ TypeAffinity.Blob, // Object (1)
+ TypeAffinity.Null, // DBNull (2)
+ TypeAffinity.Int64, // Boolean (3)
+ TypeAffinity.Int64, // Char (4)
+ TypeAffinity.Int64, // SByte (5)
+ TypeAffinity.Int64, // Byte (6)
+ TypeAffinity.Int64, // Int16 (7)
+ TypeAffinity.Int64, // UInt16 (8)
+ TypeAffinity.Int64, // Int32 (9)
+ TypeAffinity.Int64, // UInt32 (10)
+ TypeAffinity.Int64, // Int64 (11)
+ TypeAffinity.Int64, // UInt64 (12)
+ TypeAffinity.Double, // Single (13)
+ TypeAffinity.Double, // Double (14)
+ TypeAffinity.Double, // Decimal (15)
+ TypeAffinity.DateTime, // DateTime (16)
+ TypeAffinity.Null, // ?? (17)
+ TypeAffinity.Text // String (18)
+ };
+
+ ///
+ /// Builds and returns a map containing the database column types
+ /// recognized by this provider.
+ ///
+ ///
+ /// A map containing the database column types recognized by this
+ /// provider.
+ ///
+ private static SQLiteDbTypeMap GetSQLiteDbTypeMap()
+ {
+ return new SQLiteDbTypeMap(new SQLiteDbTypeMapping[] {
+ new SQLiteDbTypeMapping("BIGINT", DbType.Int64, false),
+ new SQLiteDbTypeMapping("BIGUINT", DbType.UInt64, false),
+ new SQLiteDbTypeMapping("BINARY", DbType.Binary, false),
+ new SQLiteDbTypeMapping("BIT", DbType.Boolean, true),
+ new SQLiteDbTypeMapping("BLOB", DbType.Binary, true),
+ new SQLiteDbTypeMapping("BOOL", DbType.Boolean, false),
+ new SQLiteDbTypeMapping("BOOLEAN", DbType.Boolean, false),
+ new SQLiteDbTypeMapping("CHAR", DbType.AnsiStringFixedLength, true),
+ new SQLiteDbTypeMapping("CLOB", DbType.String, false),
+ new SQLiteDbTypeMapping("COUNTER", DbType.Int64, false),
+ new SQLiteDbTypeMapping("CURRENCY", DbType.Decimal, false),
+ new SQLiteDbTypeMapping("DATE", DbType.DateTime, false),
+ new SQLiteDbTypeMapping("DATETIME", DbType.DateTime, true),
+ new SQLiteDbTypeMapping("DECIMAL", DbType.Decimal, true),
+ new SQLiteDbTypeMapping("DECIMALTEXT", DbType.Decimal, false),
+ new SQLiteDbTypeMapping("DOUBLE", DbType.Double, false),
+ new SQLiteDbTypeMapping("FLOAT", DbType.Double, false),
+ new SQLiteDbTypeMapping("GENERAL", DbType.Binary, false),
+ new SQLiteDbTypeMapping("GUID", DbType.Guid, false),
+ new SQLiteDbTypeMapping("IDENTITY", DbType.Int64, false),
+ new SQLiteDbTypeMapping("IMAGE", DbType.Binary, false),
+ new SQLiteDbTypeMapping("INT", DbType.Int32, true),
+ new SQLiteDbTypeMapping("INT8", DbType.SByte, false),
+ new SQLiteDbTypeMapping("INT16", DbType.Int16, false),
+ new SQLiteDbTypeMapping("INT32", DbType.Int32, false),
+ new SQLiteDbTypeMapping("INT64", DbType.Int64, false),
+ new SQLiteDbTypeMapping("INTEGER", DbType.Int64, true),
+ new SQLiteDbTypeMapping("INTEGER8", DbType.SByte, false),
+ new SQLiteDbTypeMapping("INTEGER16", DbType.Int16, false),
+ new SQLiteDbTypeMapping("INTEGER32", DbType.Int32, false),
+ new SQLiteDbTypeMapping("INTEGER64", DbType.Int64, false),
+ new SQLiteDbTypeMapping("LOGICAL", DbType.Boolean, false),
+ new SQLiteDbTypeMapping("LONG", DbType.Int64, false),
+ new SQLiteDbTypeMapping("LONGCHAR", DbType.String, false),
+ new SQLiteDbTypeMapping("LONGTEXT", DbType.String, false),
+ new SQLiteDbTypeMapping("LONGVARCHAR", DbType.String, false),
+ new SQLiteDbTypeMapping("MEMO", DbType.String, false),
+ new SQLiteDbTypeMapping("MONEY", DbType.Decimal, false),
+ new SQLiteDbTypeMapping("NCHAR", DbType.StringFixedLength, true),
+ new SQLiteDbTypeMapping("NOTE", DbType.String, false),
+ new SQLiteDbTypeMapping("NTEXT", DbType.String, false),
+ new SQLiteDbTypeMapping("NUMBER", DbType.Decimal, false),
+ new SQLiteDbTypeMapping("NUMERIC", DbType.Decimal, false),
+ new SQLiteDbTypeMapping("NUMERICTEXT", DbType.Decimal, false),
+ new SQLiteDbTypeMapping("NVARCHAR", DbType.String, true),
+ new SQLiteDbTypeMapping("OLEOBJECT", DbType.Binary, false),
+ new SQLiteDbTypeMapping("RAW", DbType.Binary, false),
+ new SQLiteDbTypeMapping("REAL", DbType.Double, true),
+ new SQLiteDbTypeMapping("SINGLE", DbType.Single, true),
+ new SQLiteDbTypeMapping("SMALLDATE", DbType.DateTime, false),
+ new SQLiteDbTypeMapping("SMALLINT", DbType.Int16, true),
+ new SQLiteDbTypeMapping("SMALLUINT", DbType.UInt16, true),
+ new SQLiteDbTypeMapping("STRING", DbType.String, false),
+ new SQLiteDbTypeMapping("TEXT", DbType.String, false),
+ new SQLiteDbTypeMapping("TIME", DbType.DateTime, false),
+ new SQLiteDbTypeMapping("TIMESTAMP", DbType.DateTime, false),
+ new SQLiteDbTypeMapping("TINYINT", DbType.Byte, true),
+ new SQLiteDbTypeMapping("TINYSINT", DbType.SByte, true),
+ new SQLiteDbTypeMapping("UINT", DbType.UInt32, true),
+ new SQLiteDbTypeMapping("UINT8", DbType.Byte, false),
+ new SQLiteDbTypeMapping("UINT16", DbType.UInt16, false),
+ new SQLiteDbTypeMapping("UINT32", DbType.UInt32, false),
+ new SQLiteDbTypeMapping("UINT64", DbType.UInt64, false),
+ new SQLiteDbTypeMapping("ULONG", DbType.UInt64, false),
+ new SQLiteDbTypeMapping("UNIQUEIDENTIFIER", DbType.Guid, true),
+ new SQLiteDbTypeMapping("UNSIGNEDINTEGER", DbType.UInt64, true),
+ new SQLiteDbTypeMapping("UNSIGNEDINTEGER8", DbType.Byte, false),
+ new SQLiteDbTypeMapping("UNSIGNEDINTEGER16", DbType.UInt16, false),
+ new SQLiteDbTypeMapping("UNSIGNEDINTEGER32", DbType.UInt32, false),
+ new SQLiteDbTypeMapping("UNSIGNEDINTEGER64", DbType.UInt64, false),
+ new SQLiteDbTypeMapping("VARBINARY", DbType.Binary, false),
+ new SQLiteDbTypeMapping("VARCHAR", DbType.AnsiString, true),
+ new SQLiteDbTypeMapping("VARCHAR2", DbType.AnsiString, false),
+ new SQLiteDbTypeMapping("YESNO", DbType.Boolean, false)
+ });
+ }
+
+ ///
+ /// Determines if a database type is considered to be a string.
+ ///
+ ///
+ /// The database type to check.
+ ///
+ ///
+ /// Non-zero if the database type is considered to be a string, zero
+ /// otherwise.
+ ///
+ internal static bool IsStringDbType(
+ DbType type
+ )
+ {
+ switch (type)
+ {
+ case DbType.AnsiString:
+ case DbType.String:
+ case DbType.AnsiStringFixedLength:
+ case DbType.StringFixedLength:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Determines and returns the runtime configuration setting string that
+ /// should be used in place of the specified object value.
+ ///
+ ///
+ /// The object value to convert to a string.
+ ///
+ ///
+ /// Either the string to use in place of the object value -OR- null if it
+ /// cannot be determined.
+ ///
+ private static string SettingValueToString(
+ object value
+ )
+ {
+ if (value is string)
+ return (string)value;
+
+ if (value != null)
+ return value.ToString();
+
+ return null;
+ }
+
+ ///
+ /// Determines the default value to be used when a
+ /// per-connection value is not available.
+ ///
+ ///
+ /// The connection context for type mappings, if any.
+ ///
+ ///
+ /// The default value to use.
+ ///
+ private static DbType GetDefaultDbType(
+ SQLiteConnection connection
+ )
+ {
+ SQLiteConnectionFlags flags = (connection != null) ?
+ connection.Flags : SQLiteConnectionFlags.None;
+
+ if (HelperMethods.HasFlags(
+ flags, SQLiteConnectionFlags.NoConvertSettings))
+ {
+ return FallbackDefaultDbType;
+ }
+
+ bool found = false;
+ string name = "Use_SQLiteConvert_DefaultDbType";
+ object value = null;
+ string @default = null;
+
+ if ((connection == null) ||
+ !connection.TryGetCachedSetting(name, @default, out value))
+ {
+ value = UnsafeNativeMethods.GetSettingValue(name, @default);
+
+ if (value == null)
+ value = FallbackDefaultDbType;
+ }
+ else
+ {
+ found = true;
+ }
+
+ try
+ {
+ if (!(value is DbType))
+ {
+ value = SQLiteConnection.TryParseEnum(
+ typeof(DbType), SettingValueToString(value), true);
+
+ if (!(value is DbType))
+ value = FallbackDefaultDbType;
+ }
+
+ return (DbType)value;
+ }
+ finally
+ {
+ if (!found && (connection != null))
+ connection.SetCachedSetting(name, value);
+ }
+ }
+
+ ///
+ /// Converts the object value, which is assumed to have originated
+ /// from a , to a string value.
+ ///
+ ///
+ /// The value to be converted to a string.
+ ///
+ ///
+ /// A null value will be returned if the original value is null -OR-
+ /// the original value is . Otherwise,
+ /// the original value will be converted to a string, using its
+ /// (possibly overridden) method and
+ /// then returned.
+ ///
+ public static string GetStringOrNull(
+ object value
+ )
+ {
+ if (value == null)
+ return null;
+
+ if (value is string)
+ return (string)value;
+
+ if (value == DBNull.Value)
+ return null;
+
+ return value.ToString();
+ }
+
+ ///
+ /// Determines if the specified textual value appears to be a
+ /// value.
+ ///
+ ///
+ /// The textual value to inspect.
+ ///
+ ///
+ /// Non-zero if the text looks like a value,
+ /// zero otherwise.
+ ///
+ internal static bool LooksLikeNull(
+ string text
+ )
+ {
+ return (text == null);
+ }
+
+ ///
+ /// Determines if the specified textual value appears to be an
+ /// value.
+ ///
+ ///
+ /// The textual value to inspect.
+ ///
+ ///
+ /// Non-zero if the text looks like an value,
+ /// zero otherwise.
+ ///
+ internal static bool LooksLikeInt64(
+ string text
+ )
+ {
+ long longValue;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (!long.TryParse(
+ text, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out longValue))
+ {
+ return false;
+ }
+#else
+ try
+ {
+ longValue = long.Parse(
+ text, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ }
+ catch
+ {
+ return false;
+ }
+#endif
+
+ return String.Equals(
+ longValue.ToString(CultureInfo.InvariantCulture), text,
+ StringComparison.Ordinal);
+ }
+
+ ///
+ /// Determines if the specified textual value appears to be a
+ /// value.
+ ///
+ ///
+ /// The textual value to inspect.
+ ///
+ ///
+ /// Non-zero if the text looks like a value,
+ /// zero otherwise.
+ ///
+ internal static bool LooksLikeDouble(
+ string text
+ )
+ {
+ double doubleValue;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (!double.TryParse(
+ text, NumberStyles.Float | NumberStyles.AllowThousands,
+ CultureInfo.InvariantCulture, out doubleValue))
+ {
+ return false;
+ }
+#else
+ try
+ {
+ doubleValue = double.Parse(text, CultureInfo.InvariantCulture);
+ }
+ catch
+ {
+ return false;
+ }
+#endif
+
+ return String.Equals(
+ doubleValue.ToString(CultureInfo.InvariantCulture), text,
+ StringComparison.Ordinal);
+ }
+
+ ///
+ /// Determines if the specified textual value appears to be a
+ /// value.
+ ///
+ ///
+ /// The object instance configured with
+ /// the chosen format.
+ ///
+ ///
+ /// The textual value to inspect.
+ ///
+ ///
+ /// Non-zero if the text looks like a in the
+ /// configured format, zero otherwise.
+ ///
+ internal static bool LooksLikeDateTime(
+ SQLiteConvert convert,
+ string text
+ )
+ {
+ if (convert == null)
+ return false;
+
+ try
+ {
+ DateTime dateTimeValue = convert.ToDateTime(text);
+
+ if (String.Equals(
+ convert.ToString(dateTimeValue),
+ text, StringComparison.Ordinal))
+ {
+ return true;
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ return false;
+ }
+
+ ///
+ /// For a given textual database type name, return the "closest-match" database type.
+ /// This method is called during query result processing; therefore, its performance
+ /// is critical.
+ ///
+ /// The connection context for custom type mappings, if any.
+ /// The textual name of the database type to match.
+ /// The flags associated with the parent connection object.
+ /// The .NET DBType the text evaluates to.
+ internal static DbType TypeNameToDbType(
+ SQLiteConnection connection,
+ string typeName,
+ SQLiteConnectionFlags flags
+ )
+ {
+ DbType? defaultDbType = null;
+
+ if (connection != null)
+ {
+ flags |= connection.Flags;
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.UseConnectionTypes))
+ {
+ SQLiteDbTypeMap connectionTypeNames = connection._typeNames;
+
+ if (connectionTypeNames != null)
+ {
+ if (typeName != null)
+ {
+ SQLiteDbTypeMapping value;
+
+ if (connectionTypeNames.TryGetValue(typeName, out value))
+ {
+ return value.dataType;
+ }
+ else
+ {
+ int index = typeName.IndexOf('(');
+
+ if ((index > 0) &&
+ connectionTypeNames.TryGetValue(typeName.Substring(0, index).TrimEnd(), out value))
+ {
+ return value.dataType;
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // NOTE: Use the default database type for the connection.
+ //
+ defaultDbType = connection.DefaultDbType;
+ }
+
+ if (HelperMethods.HasFlags(flags, SQLiteConnectionFlags.NoGlobalTypes))
+ {
+ if (defaultDbType != null)
+ return (DbType)defaultDbType;
+
+ defaultDbType = GetDefaultDbType(connection);
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ DefaultDbTypeWarning(typeName, flags, defaultDbType);
+#endif
+
+ return (DbType)defaultDbType;
+ }
+
+ {
+ if ((_typeNames != null) && (typeName != null))
+ {
+ SQLiteDbTypeMapping value;
+
+ if (_typeNames.TryGetValue(typeName, out value))
+ {
+ return value.dataType;
+ }
+ else
+ {
+ int index = typeName.IndexOf('(');
+
+ if ((index > 0) &&
+ _typeNames.TryGetValue(typeName.Substring(0, index).TrimEnd(), out value))
+ {
+ return value.dataType;
+ }
+ }
+ }
+ }
+
+ if (defaultDbType != null)
+ return (DbType)defaultDbType;
+
+ defaultDbType = GetDefaultDbType(connection);
+
+#if !NET_COMPACT_20 && TRACE_WARNING
+ DefaultDbTypeWarning(typeName, flags, defaultDbType);
+#endif
+
+ return (DbType)defaultDbType;
+ }
+ #endregion
+
+ private static readonly SQLiteDbTypeMap _typeNames = GetSQLiteDbTypeMap();
+ }
+
+ ///
+ /// SQLite has very limited types, and is inherently text-based. The first 5 types below represent the sum of all types SQLite
+ /// understands. The DateTime extension to the spec is for internal use only.
+ ///
+ public enum TypeAffinity
+ {
+ ///
+ /// Not used
+ ///
+ Uninitialized = 0,
+ ///
+ /// All integers in SQLite default to Int64
+ ///
+ Int64 = 1,
+ ///
+ /// All floating point numbers in SQLite default to double
+ ///
+ Double = 2,
+ ///
+ /// The default data type of SQLite is text
+ ///
+ Text = 3,
+ ///
+ /// Typically blob types are only seen when returned from a function
+ ///
+ Blob = 4,
+ ///
+ /// Null types can be returned from functions
+ ///
+ Null = 5,
+ ///
+ /// Used internally by this provider
+ ///
+ DateTime = 10,
+ ///
+ /// Used internally by this provider
+ ///
+ None = 11,
+ }
+
+ ///
+ /// These are the event types associated with the
+ ///
+ /// delegate (and its corresponding event) and the
+ /// class.
+ ///
+ public enum SQLiteConnectionEventType
+ {
+ ///
+ /// Not used.
+ ///
+ Invalid = -1,
+
+ ///
+ /// Not used.
+ ///
+ Unknown = 0,
+
+ ///
+ /// The connection is being opened.
+ ///
+ Opening = 1,
+
+ ///
+ /// The connection string has been parsed.
+ ///
+ ConnectionString = 2,
+
+ ///
+ /// The connection was opened.
+ ///
+ Opened = 3,
+
+ ///
+ /// The method was called on the
+ /// connection.
+ ///
+ ChangeDatabase = 4,
+
+ ///
+ /// A transaction was created using the connection.
+ ///
+ NewTransaction = 5,
+
+ ///
+ /// The connection was enlisted into a transaction.
+ ///
+ EnlistTransaction = 6,
+
+ ///
+ /// A command was created using the connection.
+ ///
+ NewCommand = 7,
+
+ ///
+ /// A data reader was created using the connection.
+ ///
+ NewDataReader = 8,
+
+ ///
+ /// An instance of a derived class has
+ /// been created to wrap a native resource.
+ ///
+ NewCriticalHandle = 9,
+
+ ///
+ /// The connection is being closed.
+ ///
+ Closing = 10,
+
+ ///
+ /// The connection was closed.
+ ///
+ Closed = 11,
+
+ ///
+ /// A command is being disposed.
+ ///
+ DisposingCommand = 12,
+
+ ///
+ /// A data reader is being disposed.
+ ///
+ DisposingDataReader = 13,
+
+ ///
+ /// A data reader is being closed.
+ ///
+ ClosingDataReader = 14,
+
+ ///
+ /// A native resource was opened (i.e. obtained) from the pool.
+ ///
+ OpenedFromPool = 15,
+
+ ///
+ /// A native resource was closed (i.e. released) to the pool.
+ ///
+ ClosedToPool = 16
+ }
+
+ ///
+ /// This implementation of SQLite for ADO.NET can process date/time fields in
+ /// databases in one of six formats.
+ ///
+ ///
+ /// ISO8601 format is more compatible, readable, fully-processable, but less
+ /// accurate as it does not provide time down to fractions of a second.
+ /// JulianDay is the numeric format the SQLite uses internally and is arguably
+ /// the most compatible with 3rd party tools. It is not readable as text
+ /// without post-processing. Ticks less compatible with 3rd party tools that
+ /// query the database, and renders the DateTime field unreadable as text
+ /// without post-processing. UnixEpoch is more compatible with Unix systems.
+ /// InvariantCulture allows the configured format for the invariant culture
+ /// format to be used and is human readable. CurrentCulture allows the
+ /// configured format for the current culture to be used and is also human
+ /// readable.
+ ///
+ /// The preferred order of choosing a DateTime format is JulianDay, ISO8601,
+ /// and then Ticks. Ticks is mainly present for legacy code support.
+ ///
+ public enum SQLiteDateFormats
+ {
+ ///
+ /// Use the value of DateTime.Ticks. This value is not recommended and is not well supported with LINQ.
+ ///
+ Ticks = 0,
+ ///
+ /// Use the ISO-8601 format. Uses the "yyyy-MM-dd HH:mm:ss.FFFFFFFK" format for UTC DateTime values and
+ /// "yyyy-MM-dd HH:mm:ss.FFFFFFF" format for local DateTime values).
+ ///
+ ISO8601 = 1,
+ ///
+ /// The interval of time in days and fractions of a day since January 1, 4713 BC.
+ ///
+ JulianDay = 2,
+ ///
+ /// The whole number of seconds since the Unix epoch (January 1, 1970).
+ ///
+ UnixEpoch = 3,
+ ///
+ /// Any culture-independent string value that the .NET Framework can interpret as a valid DateTime.
+ ///
+ InvariantCulture = 4,
+ ///
+ /// Any string value that the .NET Framework can interpret as a valid DateTime using the current culture.
+ ///
+ CurrentCulture = 5,
+ ///
+ /// The default format for this provider.
+ ///
+ Default = ISO8601
+ }
+
+ ///
+ /// This enum determines how SQLite treats its journal file.
+ ///
+ ///
+ /// By default SQLite will create and delete the journal file when needed during a transaction.
+ /// However, for some computers running certain filesystem monitoring tools, the rapid
+ /// creation and deletion of the journal file can cause those programs to fail, or to interfere with SQLite.
+ ///
+ /// If a program or virus scanner is interfering with SQLite's journal file, you may receive errors like "unable to open database file"
+ /// when starting a transaction. If this is happening, you may want to change the default journal mode to Persist.
+ ///
+ public enum SQLiteJournalModeEnum
+ {
+ ///
+ /// The default mode, this causes SQLite to use the existing journaling mode for the database.
+ ///
+ Default = -1,
+ ///
+ /// SQLite will create and destroy the journal file as-needed.
+ ///
+ Delete = 0,
+ ///
+ /// When this is set, SQLite will keep the journal file even after a transaction has completed. It's contents will be erased,
+ /// and the journal re-used as often as needed. If it is deleted, it will be recreated the next time it is needed.
+ ///
+ Persist = 1,
+ ///
+ /// This option disables the rollback journal entirely. Interrupted transactions or a program crash can cause database
+ /// corruption in this mode!
+ ///
+ Off = 2,
+ ///
+ /// SQLite will truncate the journal file to zero-length instead of deleting it.
+ ///
+ Truncate = 3,
+ ///
+ /// SQLite will store the journal in volatile RAM. This saves disk I/O but at the expense of database safety and integrity.
+ /// If the application using SQLite crashes in the middle of a transaction when the MEMORY journaling mode is set, then the
+ /// database file will very likely go corrupt.
+ ///
+ Memory = 4,
+ ///
+ /// SQLite uses a write-ahead log instead of a rollback journal to implement transactions. The WAL journaling mode is persistent;
+ /// after being set it stays in effect across multiple database connections and after closing and reopening the database. A database
+ /// in WAL journaling mode can only be accessed by SQLite version 3.7.0 or later.
+ ///
+ Wal = 5
+ }
+
+ ///
+ /// Possible values for the "synchronous" database setting. This setting determines
+ /// how often the database engine calls the xSync method of the VFS.
+ ///
+ internal enum SQLiteSynchronousEnum
+ {
+ ///
+ /// Use the default "synchronous" database setting. Currently, this should be
+ /// the same as using the FULL mode.
+ ///
+ Default = -1,
+
+ ///
+ /// The database engine continues without syncing as soon as it has handed
+ /// data off to the operating system. If the application running SQLite
+ /// crashes, the data will be safe, but the database might become corrupted
+ /// if the operating system crashes or the computer loses power before that
+ /// data has been written to the disk surface.
+ ///
+ Off = 0,
+
+ ///
+ /// The database engine will still sync at the most critical moments, but
+ /// less often than in FULL mode. There is a very small (though non-zero)
+ /// chance that a power failure at just the wrong time could corrupt the
+ /// database in NORMAL mode.
+ ///
+ Normal = 1,
+
+ ///
+ /// The database engine will use the xSync method of the VFS to ensure that
+ /// all content is safely written to the disk surface prior to continuing.
+ /// This ensures that an operating system crash or power failure will not
+ /// corrupt the database. FULL synchronous is very safe, but it is also
+ /// slower.
+ ///
+ Full = 2
+ }
+
+ ///
+ /// The requested command execution type. This controls which method of the
+ /// object will be called.
+ ///
+ public enum SQLiteExecuteType
+ {
+ ///
+ /// Do nothing. No method will be called.
+ ///
+ None = 0,
+
+ ///
+ /// The command is not expected to return a result -OR- the result is not
+ /// needed. The or
+ /// method
+ /// will be called.
+ ///
+ NonQuery = 1,
+
+ ///
+ /// The command is expected to return a scalar result -OR- the result should
+ /// be limited to a scalar result. The
+ /// or method will
+ /// be called.
+ ///
+ Scalar = 2,
+
+ ///
+ /// The command is expected to return result.
+ /// The or
+ /// method will
+ /// be called.
+ ///
+ Reader = 3,
+
+ ///
+ /// Use the default command execution type. Using this value is the same
+ /// as using the value.
+ ///
+ Default = NonQuery /* TODO: Good default? */
+ }
+
+ ///
+ /// The action code responsible for the current call into the authorizer.
+ ///
+ public enum SQLiteAuthorizerActionCode
+ {
+ ///
+ /// No action is being performed. This value should not be used from
+ /// external code.
+ ///
+ None = -1,
+
+ ///
+ /// No longer used.
+ ///
+ Copy = 0,
+
+ ///
+ /// An index will be created. The action-specific arguments are the
+ /// index name and the table name.
+ ///
+ ///
+ CreateIndex = 1,
+
+ ///
+ /// A table will be created. The action-specific arguments are the
+ /// table name and a null value.
+ ///
+ CreateTable = 2,
+
+ ///
+ /// A temporary index will be created. The action-specific arguments
+ /// are the index name and the table name.
+ ///
+ CreateTempIndex = 3,
+
+ ///
+ /// A temporary table will be created. The action-specific arguments
+ /// are the table name and a null value.
+ ///
+ CreateTempTable = 4,
+
+ ///
+ /// A temporary trigger will be created. The action-specific arguments
+ /// are the trigger name and the table name.
+ ///
+ CreateTempTrigger = 5,
+
+ ///
+ /// A temporary view will be created. The action-specific arguments are
+ /// the view name and a null value.
+ ///
+ CreateTempView = 6,
+
+ ///
+ /// A trigger will be created. The action-specific arguments are the
+ /// trigger name and the table name.
+ ///
+ CreateTrigger = 7,
+
+ ///
+ /// A view will be created. The action-specific arguments are the view
+ /// name and a null value.
+ ///
+ CreateView = 8,
+
+ ///
+ /// A DELETE statement will be executed. The action-specific arguments
+ /// are the table name and a null value.
+ ///
+ Delete = 9,
+
+ ///
+ /// An index will be dropped. The action-specific arguments are the
+ /// index name and the table name.
+ ///
+ DropIndex = 10,
+
+ ///
+ /// A table will be dropped. The action-specific arguments are the tables
+ /// name and a null value.
+ ///
+ DropTable = 11,
+
+ ///
+ /// A temporary index will be dropped. The action-specific arguments are
+ /// the index name and the table name.
+ ///
+ DropTempIndex = 12,
+
+ ///
+ /// A temporary table will be dropped. The action-specific arguments are
+ /// the table name and a null value.
+ ///
+ DropTempTable = 13,
+
+ ///
+ /// A temporary trigger will be dropped. The action-specific arguments
+ /// are the trigger name and the table name.
+ ///
+ DropTempTrigger = 14,
+
+ ///
+ /// A temporary view will be dropped. The action-specific arguments are
+ /// the view name and a null value.
+ ///
+ DropTempView = 15,
+
+ ///
+ /// A trigger will be dropped. The action-specific arguments are the
+ /// trigger name and the table name.
+ ///
+ DropTrigger = 16,
+
+ ///
+ /// A view will be dropped. The action-specific arguments are the view
+ /// name and a null value.
+ ///
+ DropView = 17,
+
+ ///
+ /// An INSERT statement will be executed. The action-specific arguments
+ /// are the table name and a null value.
+ ///
+ Insert = 18,
+
+ ///
+ /// A PRAGMA statement will be executed. The action-specific arguments
+ /// are the name of the PRAGMA and the new value or a null value.
+ ///
+ Pragma = 19,
+
+ ///
+ /// A table column will be read. The action-specific arguments are the
+ /// table name and the column name.
+ ///
+ Read = 20,
+
+ ///
+ /// A SELECT statement will be executed. The action-specific arguments
+ /// are both null values.
+ ///
+ Select = 21,
+
+ ///
+ /// A transaction will be started, committed, or rolled back. The
+ /// action-specific arguments are the name of the operation (BEGIN,
+ /// COMMIT, or ROLLBACK) and a null value.
+ ///
+ Transaction = 22,
+
+ ///
+ /// An UPDATE statement will be executed. The action-specific arguments
+ /// are the table name and the column name.
+ ///
+ Update = 23,
+
+ ///
+ /// A database will be attached to the connection. The action-specific
+ /// arguments are the database file name and a null value.
+ ///
+ Attach = 24,
+
+ ///
+ /// A database will be detached from the connection. The action-specific
+ /// arguments are the database name and a null value.
+ ///
+ Detach = 25,
+
+ ///
+ /// The schema of a table will be altered. The action-specific arguments
+ /// are the database name and the table name.
+ ///
+ AlterTable = 26,
+
+ ///
+ /// An index will be deleted and then recreated. The action-specific
+ /// arguments are the index name and a null value.
+ ///
+ Reindex = 27,
+
+ ///
+ /// A table will be analyzed to gathers statistics about it. The
+ /// action-specific arguments are the table name and a null value.
+ ///
+ Analyze = 28,
+
+ ///
+ /// A virtual table will be created. The action-specific arguments are
+ /// the table name and the module name.
+ ///
+ CreateVtable = 29,
+
+ ///
+ /// A virtual table will be dropped. The action-specific arguments are
+ /// the table name and the module name.
+ ///
+ DropVtable = 30,
+
+ ///
+ /// A SQL function will be called. The action-specific arguments are a
+ /// null value and the function name.
+ ///
+ Function = 31,
+
+ ///
+ /// A savepoint will be created, released, or rolled back. The
+ /// action-specific arguments are the name of the operation (BEGIN,
+ /// RELEASE, or ROLLBACK) and the savepoint name.
+ ///
+ Savepoint = 32,
+
+ ///
+ /// A recursive query will be executed. The action-specific arguments
+ /// are two null values.
+ ///
+ Recursive = 33
+ }
+
+ ///
+ /// The possible return codes for the progress callback.
+ ///
+ public enum SQLiteProgressReturnCode /* int */
+ {
+ ///
+ /// The operation should continue.
+ ///
+ Continue = 0,
+
+ ///
+ /// The operation should be interrupted.
+ ///
+ Interrupt = 1
+ }
+
+ ///
+ /// The return code for the current call into the authorizer.
+ ///
+ public enum SQLiteAuthorizerReturnCode
+ {
+ ///
+ /// The action will be allowed.
+ ///
+ Ok = 0,
+
+ ///
+ /// The overall action will be disallowed and an error message will be
+ /// returned from the query preparation method.
+ ///
+ Deny = 1,
+
+ ///
+ /// The specific action will be disallowed; however, the overall action
+ /// will continue. The exact effects of this return code vary depending
+ /// on the specific action, please refer to the SQLite core library
+ /// documentation for futher details.
+ ///
+ Ignore = 2
+ }
+
+ ///
+ /// Class used internally to determine the datatype of a column in a resultset
+ ///
+ internal sealed class SQLiteType
+ {
+ ///
+ /// The DbType of the column, or DbType.Object if it cannot be determined
+ ///
+ internal DbType Type;
+ ///
+ /// The affinity of a column, used for expressions or when Type is DbType.Object
+ ///
+ internal TypeAffinity Affinity;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs a default instance of this type.
+ ///
+ public SQLiteType()
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this type with the specified field values.
+ ///
+ ///
+ /// The type affinity to use for the new instance.
+ ///
+ ///
+ /// The database type to use for the new instance.
+ ///
+ public SQLiteType(
+ TypeAffinity affinity,
+ DbType type
+ )
+ : this()
+ {
+ this.Affinity = affinity;
+ this.Type = type;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ internal sealed class SQLiteDbTypeMap
+ : Dictionary
+ {
+ #region Private Data
+ private Dictionary reverse;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ public SQLiteDbTypeMap()
+ : base(new TypeNameStringComparer())
+ {
+ reverse = new Dictionary();
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ public SQLiteDbTypeMap(
+ IEnumerable collection
+ )
+ : this()
+ {
+ Add(collection);
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region System.Collections.Generic.Dictionary "Overrides"
+ public new int Clear()
+ {
+ int result = 0;
+
+ if (reverse != null)
+ {
+ result += reverse.Count;
+ reverse.Clear();
+ }
+
+ result += base.Count;
+ base.Clear();
+
+ return result;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteDbTypeMapping Helper Methods
+ public void Add(
+ IEnumerable collection
+ )
+ {
+ if (collection == null)
+ throw new ArgumentNullException("collection");
+
+ foreach (SQLiteDbTypeMapping item in collection)
+ Add(item);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ public void Add(SQLiteDbTypeMapping item)
+ {
+ if (item == null)
+ throw new ArgumentNullException("item");
+
+ if (item.typeName == null)
+ throw new ArgumentException("item type name cannot be null");
+
+ base.Add(item.typeName, item);
+
+ if (item.primary)
+ reverse.Add(item.dataType, item);
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region DbType Helper Methods
+ public bool ContainsKey(DbType key)
+ {
+ if (reverse == null)
+ return false;
+
+ return reverse.ContainsKey(key);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ public bool TryGetValue(DbType key, out SQLiteDbTypeMapping value)
+ {
+ if (reverse == null)
+ {
+ value = null;
+ return false;
+ }
+
+ return reverse.TryGetValue(key, out value);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ public bool Remove(DbType key)
+ {
+ if (reverse == null)
+ return false;
+
+ return reverse.Remove(key);
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ internal sealed class SQLiteDbTypeMapping
+ {
+ internal SQLiteDbTypeMapping(
+ string newTypeName,
+ DbType newDataType,
+ bool newPrimary
+ )
+ {
+ typeName = newTypeName;
+ dataType = newDataType;
+ primary = newPrimary;
+ }
+
+ internal string typeName;
+ internal DbType dataType;
+ internal bool primary;
+ }
+
+ internal sealed class TypeNameStringComparer : IEqualityComparer, IComparer
+ {
+ #region IEqualityComparer Members
+ public bool Equals(
+ string left,
+ string right
+ )
+ {
+ return String.Equals(left, right, StringComparison.OrdinalIgnoreCase);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public int GetHashCode(
+ string value
+ )
+ {
+ //
+ // NOTE: The only thing that we must guarantee here, according
+ // to the MSDN documentation for IEqualityComparer, is
+ // that for two given strings, if Equals return true then
+ // the two strings must hash to the same value.
+ //
+ if (value != null)
+ return StringComparer.OrdinalIgnoreCase.GetHashCode(value);
+ else
+ throw new ArgumentNullException("value");
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region IComparer Members
+ public int Compare(
+ string x,
+ string y
+ )
+ {
+ if ((x == null) && (y == null))
+ return 0;
+ else if (x == null)
+ return -1;
+ else if (y == null)
+ return 1;
+ else
+ return x.CompareTo(y);
+ }
+ #endregion
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteDataAdapter.cs b/Native.Csharp.Tool/SQLite/SQLiteDataAdapter.cs
new file mode 100644
index 00000000..1801c04a
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteDataAdapter.cs
@@ -0,0 +1,328 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data.Common;
+ using System.ComponentModel;
+
+ ///
+ /// SQLite implementation of DbDataAdapter.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultEvent("RowUpdated")]
+ [ToolboxItem("SQLite.Designer.SQLiteDataAdapterToolboxItem, SQLite.Designer, Version=" + SQLite3.DesignerVersion + ", Culture=neutral, PublicKeyToken=db937bc2d44ff139")]
+ [Designer("Microsoft.VSDesigner.Data.VS.SqlDataAdapterDesigner, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public sealed class SQLiteDataAdapter : DbDataAdapter
+ {
+ private bool disposeSelect = true;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private static object _updatingEventPH = new object();
+ private static object _updatedEventPH = new object();
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// This class is just a shell around the DbDataAdapter. Nothing from
+ /// DbDataAdapter is overridden here, just a few constructors are defined.
+ ///
+ ///
+ /// Default constructor.
+ ///
+ public SQLiteDataAdapter()
+ {
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs a data adapter using the specified select command.
+ ///
+ ///
+ /// The select command to associate with the adapter.
+ ///
+ public SQLiteDataAdapter(SQLiteCommand cmd)
+ {
+ SelectCommand = cmd;
+ disposeSelect = false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs a data adapter with the supplied select command text and
+ /// associated with the specified connection.
+ ///
+ ///
+ /// The select command text to associate with the data adapter.
+ ///
+ ///
+ /// The connection to associate with the select command.
+ ///
+ public SQLiteDataAdapter(string commandText, SQLiteConnection connection)
+ {
+ SelectCommand = new SQLiteCommand(commandText, connection);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs a data adapter with the specified select command text,
+ /// and using the specified database connection string.
+ ///
+ ///
+ /// The select command text to use to construct a select command.
+ ///
+ ///
+ /// A connection string suitable for passing to a new SQLiteConnection,
+ /// which is associated with the select command.
+ ///
+ public SQLiteDataAdapter(
+ string commandText,
+ string connectionString
+ )
+ : this(commandText, connectionString, false)
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs a data adapter with the specified select command text,
+ /// and using the specified database connection string.
+ ///
+ ///
+ /// The select command text to use to construct a select command.
+ ///
+ ///
+ /// A connection string suitable for passing to a new SQLiteConnection,
+ /// which is associated with the select command.
+ ///
+ ///
+ /// Non-zero to parse the connection string using the built-in (i.e.
+ /// framework provided) parser when opening the connection.
+ ///
+ public SQLiteDataAdapter(
+ string commandText,
+ string connectionString,
+ bool parseViaFramework
+ )
+ {
+ SQLiteConnection cnn = new SQLiteConnection(
+ connectionString, parseViaFramework);
+
+ SelectCommand = new SQLiteCommand(commandText, cnn);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteDataAdapter).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Cleans up resources (native and managed) associated with the current instance.
+ ///
+ ///
+ /// Zero when being disposed via garbage collection; otherwise, non-zero.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (disposeSelect && (SelectCommand != null))
+ {
+ SelectCommand.Dispose();
+ SelectCommand = null;
+ }
+
+ if (InsertCommand != null)
+ {
+ InsertCommand.Dispose();
+ InsertCommand = null;
+ }
+
+ if (UpdateCommand != null)
+ {
+ UpdateCommand.Dispose();
+ UpdateCommand = null;
+ }
+
+ if (DeleteCommand != null)
+ {
+ DeleteCommand.Dispose();
+ DeleteCommand = null;
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Row updating event handler
+ ///
+ public event EventHandler RowUpdating
+ {
+ add
+ {
+ CheckDisposed();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ EventHandler previous = (EventHandler)base.Events[_updatingEventPH];
+ if ((previous != null) && (value.Target is DbCommandBuilder))
+ {
+ EventHandler handler = (EventHandler)FindBuilder(previous);
+ if (handler != null)
+ {
+ base.Events.RemoveHandler(_updatingEventPH, handler);
+ }
+ }
+#endif
+ base.Events.AddHandler(_updatingEventPH, value);
+ }
+ remove { CheckDisposed(); base.Events.RemoveHandler(_updatingEventPH, value); }
+ }
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ internal static Delegate FindBuilder(MulticastDelegate mcd)
+ {
+ if (mcd != null)
+ {
+ Delegate[] invocationList = mcd.GetInvocationList();
+ for (int i = 0; i < invocationList.Length; i++)
+ {
+ if (invocationList[i].Target is DbCommandBuilder)
+ {
+ return invocationList[i];
+ }
+ }
+ }
+ return null;
+ }
+#endif
+
+ ///
+ /// Row updated event handler
+ ///
+ public event EventHandler RowUpdated
+ {
+ add { CheckDisposed(); base.Events.AddHandler(_updatedEventPH, value); }
+ remove { CheckDisposed(); base.Events.RemoveHandler(_updatedEventPH, value); }
+ }
+
+ ///
+ /// Raised by the underlying DbDataAdapter when a row is being updated
+ ///
+ /// The event's specifics
+ protected override void OnRowUpdating(RowUpdatingEventArgs value)
+ {
+ EventHandler handler = base.Events[_updatingEventPH] as EventHandler;
+
+ if (handler != null)
+ handler(this, value);
+ }
+
+ ///
+ /// Raised by DbDataAdapter after a row is updated
+ ///
+ /// The event's specifics
+ protected override void OnRowUpdated(RowUpdatedEventArgs value)
+ {
+ EventHandler handler = base.Events[_updatedEventPH] as EventHandler;
+
+ if (handler != null)
+ handler(this, value);
+ }
+
+ ///
+ /// Gets/sets the select command for this DataAdapter
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public new SQLiteCommand SelectCommand
+ {
+ get { CheckDisposed(); return (SQLiteCommand)base.SelectCommand; }
+ set { CheckDisposed(); base.SelectCommand = value; }
+ }
+
+ ///
+ /// Gets/sets the insert command for this DataAdapter
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public new SQLiteCommand InsertCommand
+ {
+ get { CheckDisposed(); return (SQLiteCommand)base.InsertCommand; }
+ set { CheckDisposed(); base.InsertCommand = value; }
+ }
+
+ ///
+ /// Gets/sets the update command for this DataAdapter
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public new SQLiteCommand UpdateCommand
+ {
+ get { CheckDisposed(); return (SQLiteCommand)base.UpdateCommand; }
+ set { CheckDisposed(); base.UpdateCommand = value; }
+ }
+
+ ///
+ /// Gets/sets the delete command for this DataAdapter
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((string)null), Editor("Microsoft.VSDesigner.Data.Design.DBCommandEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
+#endif
+ public new SQLiteCommand DeleteCommand
+ {
+ get { CheckDisposed(); return (SQLiteCommand)base.DeleteCommand; }
+ set { CheckDisposed(); base.DeleteCommand = value; }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteDataReader.cs b/Native.Csharp.Tool/SQLite/SQLiteDataReader.cs
new file mode 100644
index 00000000..68941997
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteDataReader.cs
@@ -0,0 +1,2165 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Data;
+ using System.Data.Common;
+ using System.Globalization;
+
+ ///
+ /// SQLite implementation of DbDataReader.
+ ///
+ public sealed class SQLiteDataReader : DbDataReader
+ {
+ ///
+ /// Underlying command this reader is attached to
+ ///
+ private SQLiteCommand _command;
+ ///
+ /// The flags pertaining to the associated connection (via the command).
+ ///
+ private SQLiteConnectionFlags _flags;
+ ///
+ /// Index of the current statement in the command being processed
+ ///
+ private int _activeStatementIndex;
+ ///
+ /// Current statement being Read()
+ ///
+ private SQLiteStatement _activeStatement;
+ ///
+ /// State of the current statement being processed.
+ /// -1 = First Step() executed, so the first Read() will be ignored
+ /// 0 = Actively reading
+ /// 1 = Finished reading
+ /// 2 = Non-row-returning statement, no records
+ ///
+ private int _readingState;
+ ///
+ /// Number of records affected by the insert/update statements executed on the command
+ ///
+ private int _rowsAffected;
+ ///
+ /// Count of fields (columns) in the row-returning statement currently being processed
+ ///
+ private int _fieldCount;
+ ///
+ /// The number of calls to Step() that have returned true (i.e. the number of rows that
+ /// have been read in the current result set).
+ ///
+ private int _stepCount;
+ ///
+ /// Maps the field (column) names to their corresponding indexes within the results.
+ ///
+ private Dictionary _fieldIndexes;
+ ///
+ /// Datatypes of active fields (columns) in the current statement, used for type-restricting data
+ ///
+ private SQLiteType[] _fieldTypeArray;
+
+ ///
+ /// The behavior of the datareader
+ ///
+ private CommandBehavior _commandBehavior;
+
+ ///
+ /// If set, then dispose of the command object when the reader is finished
+ ///
+ internal bool _disposeCommand;
+
+ ///
+ /// If set, then raise an exception when the object is accessed after being disposed.
+ ///
+ internal bool _throwOnDisposed;
+
+ ///
+ /// An array of rowid's for the active statement if CommandBehavior.KeyInfo is specified
+ ///
+ private SQLiteKeyReader _keyInfo;
+
+ ///
+ /// Matches the version of the connection.
+ ///
+ internal int _version;
+
+ ///
+ /// The "stub" (i.e. placeholder) base schema name to use when returning
+ /// column schema information. Matches the base schema name used by the
+ /// associated connection.
+ ///
+ private string _baseSchemaName;
+
+ ///
+ /// Internal constructor, initializes the datareader and sets up to begin executing statements
+ ///
+ /// The SQLiteCommand this data reader is for
+ /// The expected behavior of the data reader
+ internal SQLiteDataReader(SQLiteCommand cmd, CommandBehavior behave)
+ {
+ _throwOnDisposed = true;
+ _command = cmd;
+ _version = _command.Connection._version;
+ _baseSchemaName = _command.Connection._baseSchemaName;
+
+ _commandBehavior = behave;
+ _activeStatementIndex = -1;
+ _rowsAffected = -1;
+
+ RefreshFlags();
+
+ SQLiteConnection.OnChanged(GetConnection(this),
+ new ConnectionEventArgs(SQLiteConnectionEventType.NewDataReader,
+ null, null, _command, this, null, null, new object[] { behave }));
+
+ if (_command != null)
+ NextResult();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed && _throwOnDisposed)
+ throw new ObjectDisposedException(typeof(SQLiteDataReader).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Dispose of all resources used by this datareader.
+ ///
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ SQLiteConnection.OnChanged(GetConnection(this),
+ new ConnectionEventArgs(SQLiteConnectionEventType.DisposingDataReader,
+ null, null, _command, this, null, null, new object[] { disposing,
+ disposed, _commandBehavior, _readingState, _rowsAffected, _stepCount,
+ _fieldCount, _disposeCommand, _throwOnDisposed }));
+
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ //
+ // NOTE: Fix for ticket [e1b2e0f769], do NOT throw exceptions
+ // while we are being disposed.
+ //
+ _throwOnDisposed = false;
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ internal void Cancel()
+ {
+ _version = 0;
+ }
+
+ ///
+ /// Closes the datareader, potentially closing the connection as well if CommandBehavior.CloseConnection was specified.
+ ///
+ public override void Close()
+ {
+ CheckDisposed();
+
+ SQLiteConnection.OnChanged(GetConnection(this),
+ new ConnectionEventArgs(SQLiteConnectionEventType.ClosingDataReader,
+ null, null, _command, this, null, null, new object[] { _commandBehavior,
+ _readingState, _rowsAffected, _stepCount, _fieldCount, _disposeCommand,
+ _throwOnDisposed }));
+
+ try
+ {
+ if (_command != null)
+ {
+ try
+ {
+ try
+ {
+ // Make sure we've not been canceled
+ if (_version != 0)
+ {
+ try
+ {
+ while (NextResult())
+ {
+ }
+ }
+ catch(SQLiteException)
+ {
+ }
+ }
+ _command.ResetDataReader();
+ }
+ finally
+ {
+ // If the datareader's behavior includes closing the connection, then do so here.
+ if ((_commandBehavior & CommandBehavior.CloseConnection) != 0 && _command.Connection != null)
+ _command.Connection.Close();
+ }
+ }
+ finally
+ {
+ if (_disposeCommand)
+ _command.Dispose();
+ }
+ }
+
+ _command = null;
+ _activeStatement = null;
+ _fieldIndexes = null;
+ _fieldTypeArray = null;
+ }
+ finally
+ {
+ if (_keyInfo != null)
+ {
+ _keyInfo.Dispose();
+ _keyInfo = null;
+ }
+ }
+ }
+
+ ///
+ /// Throw an error if the datareader is closed
+ ///
+ private void CheckClosed()
+ {
+ if (!_throwOnDisposed)
+ return;
+
+ if (_command == null)
+ throw new InvalidOperationException("DataReader has been closed");
+
+ if (_version == 0)
+ throw new SQLiteException("Execution was aborted by the user");
+
+ SQLiteConnection connection = _command.Connection;
+
+ if (connection._version != _version || connection.State != ConnectionState.Open)
+ throw new InvalidOperationException("Connection was closed, statement was terminated");
+ }
+
+ ///
+ /// Throw an error if a row is not loaded
+ ///
+ private void CheckValidRow()
+ {
+ if (_readingState != 0)
+ throw new InvalidOperationException("No current row");
+ }
+
+ ///
+ /// Enumerator support
+ ///
+ /// Returns a DbEnumerator object.
+ public override Collections.IEnumerator GetEnumerator()
+ {
+ CheckDisposed();
+ return new DbEnumerator(this, ((_commandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection));
+ }
+
+ ///
+ /// Not implemented. Returns 0
+ ///
+ public override int Depth
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+ return 0;
+ }
+ }
+
+ ///
+ /// Returns the number of columns in the current resultset
+ ///
+ public override int FieldCount
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ if (_keyInfo == null)
+ return _fieldCount;
+
+ return _fieldCount + _keyInfo.Count;
+ }
+ }
+
+ ///
+ /// Forces the connection flags cached by this data reader to be refreshed
+ /// from the underlying connection.
+ ///
+ public void RefreshFlags()
+ {
+ CheckDisposed();
+
+ _flags = SQLiteCommand.GetFlags(_command);
+ }
+
+ ///
+ /// Returns the number of rows seen so far in the current result set.
+ ///
+ public int StepCount
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ return _stepCount;
+ }
+ }
+
+ private int PrivateVisibleFieldCount
+ {
+ get { return _fieldCount; }
+ }
+
+ ///
+ /// Returns the number of visible fields in the current resultset
+ ///
+ public override int VisibleFieldCount
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+ return PrivateVisibleFieldCount;
+ }
+ }
+
+ ///
+ /// This method is used to make sure the result set is open and a row is currently available.
+ ///
+ private void VerifyForGet()
+ {
+ CheckClosed();
+ CheckValidRow();
+ }
+
+ ///
+ /// SQLite is inherently un-typed. All datatypes in SQLite are natively strings. The definition of the columns of a table
+ /// and the affinity of returned types are all we have to go on to type-restrict data in the reader.
+ ///
+ /// This function attempts to verify that the type of data being requested of a column matches the datatype of the column. In
+ /// the case of columns that are not backed into a table definition, we attempt to match up the affinity of a column (int, double, string or blob)
+ /// to a set of known types that closely match that affinity. It's not an exact science, but its the best we can do.
+ ///
+ ///
+ /// This function throws an InvalidTypeCast() exception if the requested type doesn't match the column's definition or affinity.
+ ///
+ /// The index of the column to type-check
+ /// The type we want to get out of the column
+ private TypeAffinity VerifyType(int i, DbType typ)
+ {
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoVerifyTypeAffinity))
+ return TypeAffinity.None;
+
+ TypeAffinity affinity = GetSQLiteType(_flags, i).Affinity;
+
+ switch (affinity)
+ {
+ case TypeAffinity.Int64:
+ if (typ == DbType.Int64) return affinity;
+ if (typ == DbType.Int32) return affinity;
+ if (typ == DbType.Int16) return affinity;
+ if (typ == DbType.Byte) return affinity;
+ if (typ == DbType.SByte) return affinity;
+ if (typ == DbType.Boolean) return affinity;
+ if (typ == DbType.DateTime) return affinity;
+ if (typ == DbType.Double) return affinity;
+ if (typ == DbType.Single) return affinity;
+ if (typ == DbType.Decimal) return affinity;
+ break;
+ case TypeAffinity.Double:
+ if (typ == DbType.Double) return affinity;
+ if (typ == DbType.Single) return affinity;
+ if (typ == DbType.Decimal) return affinity;
+ if (typ == DbType.DateTime) return affinity;
+ break;
+ case TypeAffinity.Text:
+ if (typ == DbType.String) return affinity;
+ if (typ == DbType.Guid) return affinity;
+ if (typ == DbType.DateTime) return affinity;
+ if (typ == DbType.Decimal) return affinity;
+ break;
+ case TypeAffinity.Blob:
+ if (typ == DbType.Guid) return affinity;
+ if (typ == DbType.Binary) return affinity;
+ if (typ == DbType.String) return affinity;
+ break;
+ }
+
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Invokes the data reader value callback configured for the database
+ /// type name associated with the specified column. If no data reader
+ /// value callback is available for the database type name, do nothing.
+ ///
+ ///
+ /// The index of the column being read.
+ ///
+ ///
+ /// The extra event data to pass into the callback.
+ ///
+ ///
+ /// Non-zero if the default handling for the data reader call should be
+ /// skipped. If this is set to non-zero and the necessary return value
+ /// is unavailable or unsuitable, an exception will be thrown.
+ ///
+ private void InvokeReadValueCallback(
+ int index,
+ SQLiteReadEventArgs eventArgs,
+ out bool complete
+ )
+ {
+ complete = false;
+ SQLiteConnectionFlags oldFlags = _flags;
+ _flags &= ~SQLiteConnectionFlags.UseConnectionReadValueCallbacks;
+
+ try
+ {
+ string typeName = GetDataTypeName(index);
+
+ if (typeName == null)
+ return;
+
+ SQLiteConnection connection = GetConnection(this);
+
+ if (connection == null)
+ return;
+
+ SQLiteTypeCallbacks callbacks;
+
+ if (!connection.TryGetTypeCallbacks(typeName, out callbacks) ||
+ (callbacks == null))
+ {
+ return;
+ }
+
+ SQLiteReadValueCallback callback = callbacks.ReadValueCallback;
+
+ if (callback == null)
+ return;
+
+ object userData = callbacks.ReadValueUserData;
+
+ callback(
+ _activeStatement._sql, this, oldFlags, eventArgs, typeName,
+ index, userData, out complete); /* throw */
+ }
+ finally
+ {
+ _flags |= SQLiteConnectionFlags.UseConnectionReadValueCallbacks;
+ }
+ }
+
+ ///
+ /// Attempts to query the integer identifier for the current row. This
+ /// will not work for tables that were created WITHOUT ROWID -OR- if the
+ /// query does not include the "rowid" column or one of its aliases -OR-
+ /// if the was not created with the
+ /// flag.
+ ///
+ ///
+ /// The index of the BLOB column.
+ ///
+ ///
+ /// The integer identifier for the current row -OR- null if it could not
+ /// be determined.
+ ///
+ internal long? GetRowId(
+ int i
+ )
+ {
+ // CheckDisposed();
+ VerifyForGet();
+
+ if (_keyInfo == null)
+ return null;
+
+ string databaseName = GetDatabaseName(i);
+ string tableName = GetTableName(i);
+ int iRowId = _keyInfo.GetRowIdIndex(databaseName, tableName);
+
+ if (iRowId != -1)
+ return GetInt64(iRowId);
+
+ return _keyInfo.GetRowId(databaseName, tableName);
+ }
+
+ ///
+ /// Retrieves the column as a object.
+ /// This will not work for tables that were created WITHOUT ROWID
+ /// -OR- if the query does not include the "rowid" column or one
+ /// of its aliases -OR- if the was
+ /// not created with the
+ /// flag.
+ ///
+ /// The index of the column.
+ ///
+ /// Non-zero to open the blob object for read-only access.
+ ///
+ /// A new object.
+ public SQLiteBlob GetBlob(int i, bool readOnly)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetBlob", new SQLiteReadBlobEventArgs(readOnly), value),
+ out complete);
+
+ if (complete)
+ return (SQLiteBlob)value.BlobValue;
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetBlob(i - PrivateVisibleFieldCount, readOnly);
+
+ return SQLiteBlob.Create(this, i, readOnly);
+ }
+
+ ///
+ /// Retrieves the column as a boolean value
+ ///
+ /// The index of the column.
+ /// bool
+ public override bool GetBoolean(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetBoolean", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.BooleanValue == null)
+ throw new SQLiteException("missing boolean return value");
+
+ return (bool)value.BooleanValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetBoolean(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Boolean);
+ return Convert.ToBoolean(GetValue(i), CultureInfo.CurrentCulture);
+ }
+
+ ///
+ /// Retrieves the column as a single byte value
+ ///
+ /// The index of the column.
+ /// byte
+ public override byte GetByte(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetByte", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.ByteValue == null)
+ throw new SQLiteException("missing byte return value");
+
+ return (byte)value.ByteValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetByte(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Byte);
+ return _activeStatement._sql.GetByte(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves a column as an array of bytes (blob)
+ ///
+ /// The index of the column.
+ /// The zero-based index of where to begin reading the data
+ /// The buffer to write the bytes into
+ /// The zero-based index of where to begin writing into the array
+ /// The number of bytes to retrieve
+ /// The actual number of bytes written into the array
+ ///
+ /// To determine the number of bytes in the column, pass a null value for the buffer. The total length will be returned.
+ ///
+ public override long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteReadArrayEventArgs eventArgs = new SQLiteReadArrayEventArgs(
+ fieldOffset, buffer, bufferoffset, length);
+
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetBytes", eventArgs, value), out complete);
+
+ if (complete)
+ {
+ byte[] bytes = value.BytesValue;
+
+ if (bytes != null)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ Array.Copy(bytes, /* throw */
+ eventArgs.DataOffset, eventArgs.ByteBuffer,
+ eventArgs.BufferOffset, eventArgs.Length);
+#else
+ Array.Copy(bytes, /* throw */
+ (int)eventArgs.DataOffset, eventArgs.ByteBuffer,
+ eventArgs.BufferOffset, eventArgs.Length);
+#endif
+
+ return eventArgs.Length;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetBytes(i - PrivateVisibleFieldCount, fieldOffset, buffer, bufferoffset, length);
+
+ VerifyType(i, DbType.Binary);
+ return _activeStatement._sql.GetBytes(_activeStatement, i, (int)fieldOffset, buffer, bufferoffset, length);
+ }
+
+ ///
+ /// Returns the column as a single character
+ ///
+ /// The index of the column.
+ /// char
+ public override char GetChar(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetChar", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.CharValue == null)
+ throw new SQLiteException("missing character return value");
+
+ return (char)value.CharValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetChar(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.SByte);
+ return _activeStatement._sql.GetChar(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves a column as an array of chars (blob)
+ ///
+ /// The index of the column.
+ /// The zero-based index of where to begin reading the data
+ /// The buffer to write the characters into
+ /// The zero-based index of where to begin writing into the array
+ /// The number of bytes to retrieve
+ /// The actual number of characters written into the array
+ ///
+ /// To determine the number of characters in the column, pass a null value for the buffer. The total length will be returned.
+ ///
+ public override long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteReadArrayEventArgs eventArgs = new SQLiteReadArrayEventArgs(
+ fieldoffset, buffer, bufferoffset, length);
+
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetChars", eventArgs, value), out complete);
+
+ if (complete)
+ {
+ char[] chars = value.CharsValue;
+
+ if (chars != null)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ Array.Copy(chars, /* throw */
+ eventArgs.DataOffset, eventArgs.CharBuffer,
+ eventArgs.BufferOffset, eventArgs.Length);
+#else
+ Array.Copy(chars, /* throw */
+ (int)eventArgs.DataOffset, eventArgs.CharBuffer,
+ eventArgs.BufferOffset, eventArgs.Length);
+#endif
+
+ return eventArgs.Length;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetChars(i - PrivateVisibleFieldCount, fieldoffset, buffer, bufferoffset, length);
+
+ if (!HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoVerifyTextAffinity))
+ VerifyType(i, DbType.String);
+
+ return _activeStatement._sql.GetChars(_activeStatement, i, (int)fieldoffset, buffer, bufferoffset, length);
+ }
+
+ ///
+ /// Retrieves the name of the back-end datatype of the column
+ ///
+ /// The index of the column.
+ /// string
+ public override string GetDataTypeName(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetDataTypeName(i - PrivateVisibleFieldCount);
+
+ TypeAffinity affin = TypeAffinity.Uninitialized;
+ return _activeStatement._sql.ColumnType(_activeStatement, i, ref affin);
+ }
+
+ ///
+ /// Retrieve the column as a date/time value
+ ///
+ /// The index of the column.
+ /// DateTime
+ public override DateTime GetDateTime(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetDateTime", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.DateTimeValue == null)
+ throw new SQLiteException("missing date/time return value");
+
+ return (DateTime)value.DateTimeValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetDateTime(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.DateTime);
+ return _activeStatement._sql.GetDateTime(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieve the column as a decimal value
+ ///
+ /// The index of the column.
+ /// decimal
+ public override decimal GetDecimal(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetDecimal", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.DecimalValue == null)
+ throw new SQLiteException("missing decimal return value");
+
+ return (decimal)value.DecimalValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetDecimal(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Decimal);
+
+ CultureInfo cultureInfo = CultureInfo.CurrentCulture;
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.GetInvariantDecimal))
+ cultureInfo = CultureInfo.InvariantCulture;
+
+ return Decimal.Parse(_activeStatement._sql.GetText(_activeStatement, i), NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign, cultureInfo);
+ }
+
+ ///
+ /// Returns the column as a double
+ ///
+ /// The index of the column.
+ /// double
+ public override double GetDouble(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetDouble", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.DoubleValue == null)
+ throw new SQLiteException("missing double return value");
+
+ return (double)value.DoubleValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetDouble(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Double);
+ return _activeStatement._sql.GetDouble(_activeStatement, i);
+ }
+
+ ///
+ /// Determines and returns the of the
+ /// specified column.
+ ///
+ ///
+ /// The index of the column.
+ ///
+ ///
+ /// The associated with the specified
+ /// column, if any.
+ ///
+ public TypeAffinity GetFieldAffinity(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetFieldAffinity(i - PrivateVisibleFieldCount);
+
+ return GetSQLiteType(_flags, i).Affinity;
+ }
+
+ ///
+ /// Returns the .NET type of a given column
+ ///
+ /// The index of the column.
+ /// Type
+ public override Type GetFieldType(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetFieldType(i - PrivateVisibleFieldCount);
+
+ return SQLiteConvert.SQLiteTypeToType(GetSQLiteType(_flags, i));
+ }
+
+ ///
+ /// Returns a column as a float value
+ ///
+ /// The index of the column.
+ /// float
+ public override float GetFloat(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetFloat", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.FloatValue == null)
+ throw new SQLiteException("missing float return value");
+
+ return (float)value.FloatValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetFloat(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Single);
+ return Convert.ToSingle(_activeStatement._sql.GetDouble(_activeStatement, i));
+ }
+
+ ///
+ /// Returns the column as a Guid
+ ///
+ /// The index of the column.
+ /// Guid
+ public override Guid GetGuid(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetGuid", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.GuidValue == null)
+ throw new SQLiteException("missing guid return value");
+
+ return (Guid)value.GuidValue;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetGuid(i - PrivateVisibleFieldCount);
+
+ TypeAffinity affinity = VerifyType(i, DbType.Guid);
+ if (affinity == TypeAffinity.Blob)
+ {
+ byte[] buffer = new byte[16];
+ _activeStatement._sql.GetBytes(_activeStatement, i, 0, buffer, 0, 16);
+ return new Guid(buffer);
+ }
+ else
+ return new Guid(_activeStatement._sql.GetText(_activeStatement, i));
+ }
+
+ ///
+ /// Returns the column as a short
+ ///
+ /// The index of the column.
+ /// Int16
+ public override Int16 GetInt16(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetInt16", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.Int16Value == null)
+ throw new SQLiteException("missing int16 return value");
+
+ return (Int16)value.Int16Value;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetInt16(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Int16);
+ return _activeStatement._sql.GetInt16(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves the column as an int
+ ///
+ /// The index of the column.
+ /// Int32
+ public override Int32 GetInt32(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetInt32", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.Int32Value == null)
+ throw new SQLiteException("missing int32 return value");
+
+ return (Int32)value.Int32Value;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetInt32(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Int32);
+ return _activeStatement._sql.GetInt32(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves the column as a long
+ ///
+ /// The index of the column.
+ /// Int64
+ public override Int64 GetInt64(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetInt64", null, value), out complete);
+
+ if (complete)
+ {
+ if (value.Int64Value == null)
+ throw new SQLiteException("missing int64 return value");
+
+ return (Int64)value.Int64Value;
+ }
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetInt64(i - PrivateVisibleFieldCount);
+
+ VerifyType(i, DbType.Int64);
+ return _activeStatement._sql.GetInt64(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves the name of the column
+ ///
+ /// The index of the column.
+ /// string
+ public override string GetName(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetName(i - PrivateVisibleFieldCount);
+
+ return _activeStatement._sql.ColumnName(_activeStatement, i);
+ }
+
+ ///
+ /// Returns the name of the database associated with the specified column.
+ ///
+ /// The index of the column.
+ /// string
+ public string GetDatabaseName(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetDatabaseName(i - PrivateVisibleFieldCount);
+
+ return _activeStatement._sql.ColumnDatabaseName(_activeStatement, i);
+ }
+
+ ///
+ /// Returns the name of the table associated with the specified column.
+ ///
+ /// The index of the column.
+ /// string
+ public string GetTableName(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetTableName(i - PrivateVisibleFieldCount);
+
+ return _activeStatement._sql.ColumnTableName(_activeStatement, i);
+ }
+
+ ///
+ /// Returns the original name of the specified column.
+ ///
+ /// The index of the column.
+ /// string
+ public string GetOriginalName(int i)
+ {
+ CheckDisposed();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetName(i - PrivateVisibleFieldCount);
+
+ return _activeStatement._sql.ColumnOriginalName(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves the i of a column, given its name
+ ///
+ /// The name of the column to retrieve
+ /// The int i of the column
+ public override int GetOrdinal(string name)
+ {
+ CheckDisposed();
+
+ if (_throwOnDisposed) SQLiteCommand.Check(_command);
+
+ //
+ // NOTE: First, check if the column name cache has been initialized yet.
+ // If not, do it now.
+ //
+ if (_fieldIndexes == null)
+ {
+ _fieldIndexes = new Dictionary(
+ StringComparer.OrdinalIgnoreCase);
+ }
+
+ //
+ // NOTE: Next, see if the index for the requested column name has been
+ // cached already. If so, return the cached value. Otherwise,
+ // lookup the value and then cache the result for future use.
+ //
+ int r;
+
+ if (!_fieldIndexes.TryGetValue(name, out r))
+ {
+ r = _activeStatement._sql.ColumnIndex(_activeStatement, name);
+
+ if (r == -1 && _keyInfo != null)
+ {
+ r = _keyInfo.GetOrdinal(name);
+ if (r > -1) r += PrivateVisibleFieldCount;
+ }
+
+ _fieldIndexes.Add(name, r);
+ }
+
+ if (r == -1 && HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.StrictConformance))
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ return r;
+ }
+
+ ///
+ /// Schema information in SQLite is difficult to map into .NET conventions, so a lot of work must be done
+ /// to gather the necessary information so it can be represented in an ADO.NET manner.
+ ///
+ /// Returns a DataTable containing the schema information for the active SELECT statement being processed.
+ public override DataTable GetSchemaTable()
+ {
+ CheckDisposed();
+ return GetSchemaTable(true, false);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ColumnParent Class
+ private sealed class ColumnParent : IEqualityComparer
+ {
+ #region Public Fields
+ public string DatabaseName;
+ public string TableName;
+ public string ColumnName;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ public ColumnParent()
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public ColumnParent(
+ string databaseName,
+ string tableName,
+ string columnName
+ )
+ : this()
+ {
+ this.DatabaseName = databaseName;
+ this.TableName = tableName;
+ this.ColumnName = columnName;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEqualityComparer Members
+ public bool Equals(ColumnParent x, ColumnParent y)
+ {
+ if ((x == null) && (y == null))
+ {
+ return true;
+ }
+ else if ((x == null) || (y == null))
+ {
+ return false;
+ }
+ else
+ {
+ if (!String.Equals(x.DatabaseName, y.DatabaseName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (!String.Equals(x.TableName, y.TableName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (!String.Equals(x.ColumnName, y.ColumnName,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public int GetHashCode(ColumnParent obj)
+ {
+ int result = 0;
+
+ if ((obj != null) && (obj.DatabaseName != null))
+ result ^= obj.DatabaseName.GetHashCode();
+
+ if ((obj != null) && (obj.TableName != null))
+ result ^= obj.TableName.GetHashCode();
+
+ if ((obj != null) && (obj.ColumnName != null))
+ result ^= obj.ColumnName.GetHashCode();
+
+ return result;
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static void GetStatementColumnParents(
+ SQLiteBase sql,
+ SQLiteStatement stmt,
+ int fieldCount,
+ ref Dictionary> parentToColumns,
+ ref Dictionary columnToParent
+ )
+ {
+ if (parentToColumns == null)
+ parentToColumns = new Dictionary>(
+ new ColumnParent());
+
+ if (columnToParent == null)
+ columnToParent = new Dictionary();
+
+ for (int n = 0; n < fieldCount; n++)
+ {
+ string databaseName = sql.ColumnDatabaseName(stmt, n);
+ string tableName = sql.ColumnTableName(stmt, n);
+ string columnName = sql.ColumnOriginalName(stmt, n);
+
+ ColumnParent key = new ColumnParent(databaseName, tableName, null);
+ ColumnParent value = new ColumnParent(databaseName, tableName, columnName);
+
+ List indexList;
+
+ if (!parentToColumns.TryGetValue(key, out indexList))
+ parentToColumns.Add(key, new List(new int[] { n }));
+ else if (indexList != null)
+ indexList.Add(n);
+ else
+ parentToColumns[key] = new List(new int[] { n });
+
+ columnToParent.Add(n, value);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static int CountParents(
+ Dictionary> parentToColumns
+ )
+ {
+ int result = 0;
+
+ if (parentToColumns != null)
+ {
+ foreach (ColumnParent key in parentToColumns.Keys)
+ {
+ if (key == null)
+ continue;
+
+ string tableName = key.TableName;
+
+ if (String.IsNullOrEmpty(tableName))
+ continue;
+
+ result++;
+ }
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ internal DataTable GetSchemaTable(bool wantUniqueInfo, bool wantDefaultValue)
+ {
+ CheckClosed();
+ if (_throwOnDisposed) SQLiteCommand.Check(_command);
+
+ //
+ // BUGFIX: We need to quickly scan all the fields in the current
+ // "result set" to see how many distinct tables are actually
+ // involved. This information is necessary so that some
+ // intelligent decisions can be made when constructing the
+ // metadata below. For example, we need to be very careful
+ // about flagging a particular column as "unique" just
+ // because it was in its original underlying database table
+ // if there are now multiple tables involved in the
+ // "result set". See ticket [7e3fa93744] for more detailed
+ // information.
+ //
+ Dictionary> parentToColumns = null;
+ Dictionary columnToParent = null;
+ SQLiteBase sql = _command.Connection._sql;
+
+ GetStatementColumnParents(
+ sql, _activeStatement, _fieldCount,
+ ref parentToColumns, ref columnToParent);
+
+ DataTable tbl = new DataTable("SchemaTable");
+ DataTable tblIndexes = null;
+ DataTable tblIndexColumns;
+ DataRow row;
+ string temp;
+ string strCatalog = String.Empty;
+ string strTable = String.Empty;
+ string strColumn = String.Empty;
+
+ tbl.Locale = CultureInfo.InvariantCulture;
+ tbl.Columns.Add(SchemaTableColumn.ColumnName, typeof(String));
+ tbl.Columns.Add(SchemaTableColumn.ColumnOrdinal, typeof(int));
+ tbl.Columns.Add(SchemaTableColumn.ColumnSize, typeof(int));
+ tbl.Columns.Add(SchemaTableColumn.NumericPrecision, typeof(int));
+ tbl.Columns.Add(SchemaTableColumn.NumericScale, typeof(int));
+ tbl.Columns.Add(SchemaTableColumn.IsUnique, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableColumn.IsKey, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableOptionalColumn.BaseServerName, typeof(string));
+ tbl.Columns.Add(SchemaTableOptionalColumn.BaseCatalogName, typeof(String));
+ tbl.Columns.Add(SchemaTableColumn.BaseColumnName, typeof(String));
+ tbl.Columns.Add(SchemaTableColumn.BaseSchemaName, typeof(String));
+ tbl.Columns.Add(SchemaTableColumn.BaseTableName, typeof(String));
+ tbl.Columns.Add(SchemaTableColumn.DataType, typeof(Type));
+ tbl.Columns.Add(SchemaTableColumn.AllowDBNull, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableColumn.ProviderType, typeof(int));
+ tbl.Columns.Add(SchemaTableColumn.IsAliased, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableColumn.IsExpression, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableOptionalColumn.IsAutoIncrement, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableOptionalColumn.IsRowVersion, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableOptionalColumn.IsHidden, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableColumn.IsLong, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableOptionalColumn.IsReadOnly, typeof(Boolean));
+ tbl.Columns.Add(SchemaTableOptionalColumn.ProviderSpecificDataType, typeof(Type));
+ tbl.Columns.Add(SchemaTableOptionalColumn.DefaultValue, typeof(object));
+ tbl.Columns.Add("DataTypeName", typeof(string));
+ tbl.Columns.Add("CollationType", typeof(string));
+ tbl.BeginLoadData();
+
+ for (int n = 0; n < _fieldCount; n++)
+ {
+ SQLiteType sqlType = GetSQLiteType(_flags, n);
+
+ row = tbl.NewRow();
+
+ DbType typ = sqlType.Type;
+
+ // Default settings for the column
+ row[SchemaTableColumn.ColumnName] = GetName(n);
+ row[SchemaTableColumn.ColumnOrdinal] = n;
+ row[SchemaTableColumn.ColumnSize] = SQLiteConvert.DbTypeToColumnSize(typ);
+ row[SchemaTableColumn.NumericPrecision] = SQLiteConvert.DbTypeToNumericPrecision(typ);
+ row[SchemaTableColumn.NumericScale] = SQLiteConvert.DbTypeToNumericScale(typ);
+ row[SchemaTableColumn.ProviderType] = sqlType.Type;
+ row[SchemaTableColumn.IsLong] = false;
+ row[SchemaTableColumn.AllowDBNull] = true;
+ row[SchemaTableOptionalColumn.IsReadOnly] = false;
+ row[SchemaTableOptionalColumn.IsRowVersion] = false;
+ row[SchemaTableColumn.IsUnique] = false;
+ row[SchemaTableColumn.IsKey] = false;
+ row[SchemaTableOptionalColumn.IsAutoIncrement] = false;
+ row[SchemaTableColumn.DataType] = GetFieldType(n);
+ row[SchemaTableOptionalColumn.IsHidden] = false;
+ row[SchemaTableColumn.BaseSchemaName] = _baseSchemaName;
+
+ strColumn = columnToParent[n].ColumnName;
+ if (String.IsNullOrEmpty(strColumn) == false) row[SchemaTableColumn.BaseColumnName] = strColumn;
+
+ row[SchemaTableColumn.IsExpression] = String.IsNullOrEmpty(strColumn);
+ row[SchemaTableColumn.IsAliased] = (String.Compare(GetName(n), strColumn, StringComparison.OrdinalIgnoreCase) != 0);
+
+ temp = columnToParent[n].TableName;
+ if (String.IsNullOrEmpty(temp) == false) row[SchemaTableColumn.BaseTableName] = temp;
+
+ temp = columnToParent[n].DatabaseName;
+ if (String.IsNullOrEmpty(temp) == false) row[SchemaTableOptionalColumn.BaseCatalogName] = temp;
+
+ string dataType = null;
+ // If we have a table-bound column, extract the extra information from it
+ if (String.IsNullOrEmpty(strColumn) == false)
+ {
+ string baseCatalogName = String.Empty;
+
+ if (row[SchemaTableOptionalColumn.BaseCatalogName] != DBNull.Value)
+ baseCatalogName = (string)row[SchemaTableOptionalColumn.BaseCatalogName];
+
+ string baseTableName = String.Empty;
+
+ if (row[SchemaTableColumn.BaseTableName] != DBNull.Value)
+ baseTableName = (string)row[SchemaTableColumn.BaseTableName];
+
+ if (sql.DoesTableExist(baseCatalogName, baseTableName))
+ {
+ string baseColumnName = String.Empty;
+
+ if (row[SchemaTableColumn.BaseColumnName] != DBNull.Value)
+ baseColumnName = (string)row[SchemaTableColumn.BaseColumnName];
+
+ string collSeq = null;
+ bool bNotNull = false;
+ bool bPrimaryKey = false;
+ bool bAutoIncrement = false;
+ string[] arSize;
+
+ // Get the column meta data
+ _command.Connection._sql.ColumnMetaData(
+ baseCatalogName,
+ baseTableName,
+ strColumn,
+ true,
+ ref dataType, ref collSeq, ref bNotNull, ref bPrimaryKey, ref bAutoIncrement);
+
+ if (bNotNull || bPrimaryKey) row[SchemaTableColumn.AllowDBNull] = false;
+ bool allowDbNull = (bool)row[SchemaTableColumn.AllowDBNull];
+
+ row[SchemaTableColumn.IsKey] = bPrimaryKey && CountParents(parentToColumns) <= 1;
+ row[SchemaTableOptionalColumn.IsAutoIncrement] = bAutoIncrement;
+ row["CollationType"] = collSeq;
+
+ // For types like varchar(50) and such, extract the size
+ arSize = dataType.Split('(');
+ if (arSize.Length > 1)
+ {
+ dataType = arSize[0];
+ arSize = arSize[1].Split(')');
+ if (arSize.Length > 1)
+ {
+ arSize = arSize[0].Split(',', '.');
+ if (sqlType.Type == DbType.Binary || SQLiteConvert.IsStringDbType(sqlType.Type))
+ {
+ row[SchemaTableColumn.ColumnSize] = Convert.ToInt32(arSize[0], CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ row[SchemaTableColumn.NumericPrecision] = Convert.ToInt32(arSize[0], CultureInfo.InvariantCulture);
+ if (arSize.Length > 1)
+ row[SchemaTableColumn.NumericScale] = Convert.ToInt32(arSize[1], CultureInfo.InvariantCulture);
+ }
+ }
+ }
+
+ if (wantDefaultValue)
+ {
+ // Determine the default value for the column, which sucks because we have to query the schema for each column
+ using (SQLiteCommand cmdTable = new SQLiteCommand(HelperMethods.StringFormat(CultureInfo.InvariantCulture, "PRAGMA [{0}].TABLE_INFO([{1}])",
+ baseCatalogName,
+ baseTableName
+ ), _command.Connection))
+ using (DbDataReader rdTable = cmdTable.ExecuteReader())
+ {
+ // Find the matching column
+ while (rdTable.Read())
+ {
+ if (String.Compare(baseColumnName, rdTable.GetString(1), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ if (rdTable.IsDBNull(4) == false)
+ row[SchemaTableOptionalColumn.DefaultValue] = rdTable[4];
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Determine IsUnique properly, which is a pain in the butt!
+ if (wantUniqueInfo)
+ {
+ if (baseCatalogName != strCatalog || baseTableName != strTable)
+ {
+ strCatalog = baseCatalogName;
+ strTable = baseTableName;
+
+ tblIndexes = _command.Connection.GetSchema("Indexes", new string[] {
+ baseCatalogName,
+ null,
+ baseTableName,
+ null
+ });
+ }
+
+ foreach (DataRow rowIndexes in tblIndexes.Rows)
+ {
+ tblIndexColumns = _command.Connection.GetSchema("IndexColumns", new string[] {
+ baseCatalogName,
+ null,
+ baseTableName,
+ (string)rowIndexes["INDEX_NAME"],
+ null
+ });
+ foreach (DataRow rowColumnIndex in tblIndexColumns.Rows)
+ {
+ if (String.Compare(SQLiteConvert.GetStringOrNull(rowColumnIndex["COLUMN_NAME"]), strColumn, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ //
+ // BUGFIX: Make sure that we only flag this column as "unique"
+ // if we are not processing of some kind of multi-table
+ // construct (i.e. a join) because in that case we must
+ // allow duplicate values (refer to ticket [7e3fa93744]).
+ //
+ if (parentToColumns.Count == 1 && tblIndexColumns.Rows.Count == 1 && allowDbNull == false)
+ row[SchemaTableColumn.IsUnique] = rowIndexes["UNIQUE"];
+
+ // If its an integer primary key and the only primary key in the table, then its a rowid alias and is autoincrement
+ // NOTE: Currently commented out because this is not always the desired behavior. For example, a 1:1 relationship with
+ // another table, where the other table is autoincrement, but this one is not, and uses the rowid from the other.
+ // It is safer to only set Autoincrement on tables where we're SURE the user specified AUTOINCREMENT, even if its a rowid column.
+
+ //if (tblIndexColumns.Rows.Count == 1 && (bool)rowIndexes["PRIMARY_KEY"] == true && String.IsNullOrEmpty(dataType) == false &&
+ // String.Compare(dataType, "integer", StringComparison.OrdinalIgnoreCase) == 0)
+ //{
+ // // row[SchemaTableOptionalColumn.IsAutoIncrement] = true;
+ //}
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (String.IsNullOrEmpty(dataType))
+ {
+ TypeAffinity affin = TypeAffinity.Uninitialized;
+ dataType = _activeStatement._sql.ColumnType(_activeStatement, n, ref affin);
+ }
+
+ if (String.IsNullOrEmpty(dataType) == false)
+ row["DataTypeName"] = dataType;
+ }
+
+ tbl.Rows.Add(row);
+ }
+
+ if (_keyInfo != null)
+ _keyInfo.AppendSchemaTable(tbl);
+
+ tbl.AcceptChanges();
+ tbl.EndLoadData();
+
+ return tbl;
+ }
+
+ ///
+ /// Retrieves the column as a string
+ ///
+ /// The index of the column.
+ /// string
+ public override string GetString(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetString", null, value), out complete);
+
+ if (complete)
+ return value.StringValue;
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetString(i - PrivateVisibleFieldCount);
+
+ if (!HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.NoVerifyTextAffinity))
+ VerifyType(i, DbType.String);
+
+ return _activeStatement._sql.GetText(_activeStatement, i);
+ }
+
+ ///
+ /// Retrieves the column as an object corresponding to the underlying datatype of the column
+ ///
+ /// The index of the column.
+ /// object
+ public override object GetValue(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (HelperMethods.HasFlags(_flags, SQLiteConnectionFlags.UseConnectionReadValueCallbacks))
+ {
+ SQLiteDataReaderValue value = new SQLiteDataReaderValue();
+ bool complete;
+
+ InvokeReadValueCallback(i, new SQLiteReadValueEventArgs(
+ "GetValue", null, value), out complete);
+
+ if (complete)
+ return value.Value;
+ }
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.GetValue(i - PrivateVisibleFieldCount);
+
+ SQLiteType typ = GetSQLiteType(_flags, i);
+
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.DetectTextAffinity) &&
+ ((typ == null) || (typ.Affinity == TypeAffinity.Text)))
+ {
+ typ = GetSQLiteType(
+ typ, _activeStatement._sql.GetText(_activeStatement, i));
+ }
+ else if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.DetectStringType) &&
+ ((typ == null) || SQLiteConvert.IsStringDbType(typ.Type)))
+ {
+ typ = GetSQLiteType(
+ typ, _activeStatement._sql.GetText(_activeStatement, i));
+ }
+
+ return _activeStatement._sql.GetValue(_activeStatement, _flags, i, typ);
+ }
+
+ ///
+ /// Retreives the values of multiple columns, up to the size of the supplied array
+ ///
+ /// The array to fill with values from the columns in the current resultset
+ /// The number of columns retrieved
+ public override int GetValues(object[] values)
+ {
+ CheckDisposed();
+
+ int nMax = FieldCount;
+ if (values.Length < nMax) nMax = values.Length;
+
+ for (int n = 0; n < nMax; n++)
+ {
+ values[n] = GetValue(n);
+ }
+
+ return nMax;
+ }
+
+ ///
+ /// Returns a collection containing all the column names and values for the
+ /// current row of data in the current resultset, if any. If there is no
+ /// current row or no current resultset, an exception may be thrown.
+ ///
+ ///
+ /// The collection containing the column name and value information for the
+ /// current row of data in the current resultset or null if this information
+ /// cannot be obtained.
+ ///
+ public NameValueCollection GetValues()
+ {
+ CheckDisposed();
+
+ if ((_activeStatement == null) || (_activeStatement._sql == null))
+ throw new InvalidOperationException();
+
+ int nMax = PrivateVisibleFieldCount;
+ NameValueCollection result = new NameValueCollection(nMax);
+
+ for (int n = 0; n < nMax; n++)
+ {
+ string name = _activeStatement._sql.ColumnName(_activeStatement, n);
+ string value = _activeStatement._sql.GetText(_activeStatement, n);
+
+ result.Add(name, value);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Returns True if the resultset has rows that can be fetched
+ ///
+ public override bool HasRows
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ //
+ // NOTE: If the "sticky" flag has been set, use the new behavior,
+ // which returns non-zero if there were ever any rows in
+ // the associated result sets. Generally, this flag is only
+ // useful when it is necessary to retain compatibility with
+ // other ADO.NET providers that use these same semantics for
+ // the HasRows property.
+ //
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.StickyHasRows))
+ {
+ return ((_readingState != 1) || (_stepCount > 0));
+ }
+
+ //
+ // NOTE: This is the default behavior. It returns non-zero only if
+ // more rows are available (i.e. a call to the Read method is
+ // expected to succeed). Prior to the introduction of the
+ // "sticky" flag, this is how this property has always worked.
+ //
+ return (_readingState != 1);
+ }
+ }
+
+ ///
+ /// Returns True if the data reader is closed
+ ///
+ public override bool IsClosed
+ {
+ get { CheckDisposed(); return (_command == null); }
+ }
+
+ ///
+ /// Returns True if the specified column is null
+ ///
+ /// The index of the column.
+ /// True or False
+ public override bool IsDBNull(int i)
+ {
+ CheckDisposed();
+ VerifyForGet();
+
+ if (i >= PrivateVisibleFieldCount && _keyInfo != null)
+ return _keyInfo.IsDBNull(i - PrivateVisibleFieldCount);
+
+ return _activeStatement._sql.IsNull(_activeStatement, i);
+ }
+
+ ///
+ /// Moves to the next resultset in multiple row-returning SQL command.
+ ///
+ /// True if the command was successful and a new resultset is available, False otherwise.
+ public override bool NextResult()
+ {
+ CheckDisposed();
+ CheckClosed();
+ if (_throwOnDisposed) SQLiteCommand.Check(_command);
+
+ SQLiteStatement stmt = null;
+ int fieldCount;
+ bool schemaOnly = ((_commandBehavior & CommandBehavior.SchemaOnly) != 0);
+
+ while (true)
+ {
+ if (stmt == null && _activeStatement != null && _activeStatement._sql != null && _activeStatement._sql.IsOpen())
+ {
+ // Reset the previously-executed statement
+ if (!schemaOnly) _activeStatement._sql.Reset(_activeStatement);
+
+ // If we're only supposed to return a single rowset, step through all remaining statements once until
+ // they are all done and return false to indicate no more resultsets exist.
+ if ((_commandBehavior & CommandBehavior.SingleResult) != 0)
+ {
+ for (; ; )
+ {
+ stmt = _command.GetStatement(_activeStatementIndex + 1);
+ if (stmt == null) break;
+ _activeStatementIndex++;
+
+ if (!schemaOnly && stmt._sql.Step(stmt)) _stepCount++;
+ if (stmt._sql.ColumnCount(stmt) == 0)
+ {
+ int changes = 0;
+ bool readOnly = false;
+ if (stmt.TryGetChanges(ref changes, ref readOnly))
+ {
+ if (!readOnly)
+ {
+ if (_rowsAffected == -1) _rowsAffected = 0;
+ _rowsAffected += changes;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ if (!schemaOnly) stmt._sql.Reset(stmt); // Gotta reset after every step to release any locks and such!
+ }
+ return false;
+ }
+ }
+
+ // Get the next statement to execute
+ stmt = _command.GetStatement(_activeStatementIndex + 1);
+
+ // If we've reached the end of the statements, return false, no more resultsets
+ if (stmt == null)
+ return false;
+
+ // If we were on a current resultset, set the state to "done reading" for it
+ if (_readingState < 1)
+ _readingState = 1;
+
+ _activeStatementIndex++;
+
+ fieldCount = stmt._sql.ColumnCount(stmt);
+
+ // If the statement is not a select statement or we're not retrieving schema only, then perform the initial step
+ if (!schemaOnly || (fieldCount == 0))
+ {
+ if (!schemaOnly && stmt._sql.Step(stmt))
+ {
+ _stepCount++;
+ _readingState = -1;
+ }
+ else if (fieldCount == 0) // No rows returned, if fieldCount is zero, skip to the next statement
+ {
+ int changes = 0;
+ bool readOnly = false;
+ if (stmt.TryGetChanges(ref changes, ref readOnly))
+ {
+ if (!readOnly)
+ {
+ if (_rowsAffected == -1) _rowsAffected = 0;
+ _rowsAffected += changes;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ if (!schemaOnly) stmt._sql.Reset(stmt);
+ continue; // Skip this command and move to the next, it was not a row-returning resultset
+ }
+ else // No rows, fieldCount is non-zero so stop here
+ {
+ _readingState = 1; // This command returned columns but no rows, so return true, but HasRows = false and Read() returns false
+ }
+ }
+
+ // Ahh, we found a row-returning resultset eligible to be returned!
+ _activeStatement = stmt;
+ _fieldCount = fieldCount;
+ _fieldIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ _fieldTypeArray = new SQLiteType[PrivateVisibleFieldCount];
+
+ if ((_commandBehavior & CommandBehavior.KeyInfo) != 0)
+ LoadKeyInfo();
+
+ return true;
+ }
+ }
+
+ ///
+ /// This method attempts to query the database connection associated with
+ /// the data reader in use. If the underlying command or connection is
+ /// unavailable, a null value will be returned.
+ ///
+ ///
+ /// The connection object -OR- null if it is unavailable.
+ ///
+ internal static SQLiteConnection GetConnection(
+ SQLiteDataReader dataReader
+ )
+ {
+ try
+ {
+ if (dataReader != null)
+ {
+ SQLiteCommand command = dataReader._command;
+
+ if (command != null)
+ {
+ SQLiteConnection connection = command.Connection;
+
+ if (connection != null)
+ return connection;
+ }
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // do nothing.
+ }
+
+ return null;
+ }
+
+ ///
+ /// Retrieves the SQLiteType for a given column and row value.
+ ///
+ ///
+ /// The original SQLiteType structure, based only on the column.
+ ///
+ ///
+ /// The textual value of the column for a given row.
+ ///
+ ///
+ /// The SQLiteType structure.
+ ///
+ private SQLiteType GetSQLiteType(
+ SQLiteType oldType, /* PASS-THROUGH */
+ string text
+ )
+ {
+ if (SQLiteConvert.LooksLikeNull(text))
+ return new SQLiteType(TypeAffinity.Null, DbType.Object);
+
+ if (SQLiteConvert.LooksLikeInt64(text))
+ return new SQLiteType(TypeAffinity.Int64, DbType.Int64);
+
+ if (SQLiteConvert.LooksLikeDouble(text))
+ return new SQLiteType(TypeAffinity.Double, DbType.Double);
+
+ if ((_activeStatement != null) &&
+ SQLiteConvert.LooksLikeDateTime(_activeStatement._sql, text))
+ {
+ return new SQLiteType(TypeAffinity.DateTime, DbType.DateTime);
+ }
+
+ return oldType;
+ }
+
+ ///
+ /// Retrieves the SQLiteType for a given column, and caches it to avoid repetetive interop calls.
+ ///
+ /// The flags associated with the parent connection object.
+ /// The index of the column.
+ /// A SQLiteType structure
+ private SQLiteType GetSQLiteType(SQLiteConnectionFlags flags, int i)
+ {
+ SQLiteType typ = _fieldTypeArray[i];
+
+ if (typ == null)
+ {
+ // Initialize this column's field type instance
+ typ = _fieldTypeArray[i] = new SQLiteType();
+ }
+
+ // If not initialized, then fetch the declared column datatype and attempt to convert it
+ // to a known DbType.
+ if (typ.Affinity == TypeAffinity.Uninitialized)
+ {
+ typ.Type = SQLiteConvert.TypeNameToDbType(
+ GetConnection(this), _activeStatement._sql.ColumnType(
+ _activeStatement, i, ref typ.Affinity), flags);
+ }
+ else
+ {
+ typ.Affinity = _activeStatement._sql.ColumnAffinity(
+ _activeStatement, i);
+ }
+
+ return typ;
+ }
+
+ ///
+ /// Reads the next row from the resultset
+ ///
+ /// True if a new row was successfully loaded and is ready for processing
+ public override bool Read()
+ {
+ CheckDisposed();
+ CheckClosed();
+ if (_throwOnDisposed) SQLiteCommand.Check(_command);
+
+ if ((_commandBehavior & CommandBehavior.SchemaOnly) != 0)
+ return false;
+
+ if (_readingState == -1) // First step was already done at the NextResult() level, so don't step again, just return true.
+ {
+ _readingState = 0;
+ return true;
+ }
+ else if (_readingState == 0) // Actively reading rows
+ {
+ // Don't read a new row if the command behavior dictates SingleRow. We've already read the first row.
+ if ((_commandBehavior & CommandBehavior.SingleRow) == 0)
+ {
+ if (_activeStatement._sql.Step(_activeStatement) == true)
+ {
+ _stepCount++;
+
+ if (_keyInfo != null)
+ _keyInfo.Reset();
+
+ return true;
+ }
+ }
+
+ _readingState = 1; // Finished reading rows
+ }
+
+ return false;
+ }
+
+ ///
+ /// Returns the number of rows affected by the statement being executed.
+ /// The value returned may not be accurate for DDL statements. Also, it
+ /// will be -1 for any statement that does not modify the database (e.g.
+ /// SELECT). If an otherwise read-only statement modifies the database
+ /// indirectly (e.g. via a virtual table or user-defined function), the
+ /// value returned is undefined.
+ ///
+ public override int RecordsAffected
+ {
+ get { CheckDisposed(); return _rowsAffected; }
+ }
+
+ ///
+ /// Indexer to retrieve data from a column given its name
+ ///
+ /// The name of the column to retrieve data for
+ /// The value contained in the column
+ public override object this[string name]
+ {
+ get { CheckDisposed(); return GetValue(GetOrdinal(name)); }
+ }
+
+ ///
+ /// Indexer to retrieve data from a column given its i
+ ///
+ /// The index of the column.
+ /// The value contained in the column
+ public override object this[int i]
+ {
+ get { CheckDisposed(); return GetValue(i); }
+ }
+
+ private void LoadKeyInfo()
+ {
+ if (_keyInfo != null)
+ {
+ _keyInfo.Dispose();
+ _keyInfo = null;
+ }
+
+ _keyInfo = new SQLiteKeyReader(_command.Connection, this, _activeStatement);
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteDefineConstants.cs b/Native.Csharp.Tool/SQLite/SQLiteDefineConstants.cs
new file mode 100644
index 00000000..033ff43a
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteDefineConstants.cs
@@ -0,0 +1,230 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Collections.Generic;
+
+namespace System.Data.SQLite
+{
+ internal static class SQLiteDefineConstants
+ {
+ public static readonly IList OptionList = new List(new string[] {
+#if CHECK_STATE
+ "CHECK_STATE",
+#endif
+
+#if COUNT_HANDLE
+ "COUNT_HANDLE",
+#endif
+
+#if DEBUG
+ "DEBUG",
+#endif
+
+#if INTEROP_CODEC
+ "INTEROP_CODEC",
+#endif
+
+#if INTEROP_DEBUG
+ "INTEROP_DEBUG",
+#endif
+
+#if INTEROP_EXTENSION_FUNCTIONS
+ "INTEROP_EXTENSION_FUNCTIONS",
+#endif
+
+#if INTEROP_FTS5_EXTENSION
+ "INTEROP_FTS5_EXTENSION",
+#endif
+
+#if INTEROP_INCLUDE_CEROD
+ "INTEROP_INCLUDE_CEROD",
+#endif
+
+#if INTEROP_INCLUDE_EXTRA
+ "INTEROP_INCLUDE_EXTRA",
+#endif
+
+#if INTEROP_INCLUDE_SEE
+ "INTEROP_INCLUDE_SEE",
+#endif
+
+#if INTEROP_INCLUDE_ZIPVFS
+ "INTEROP_INCLUDE_ZIPVFS",
+#endif
+
+#if INTEROP_JSON1_EXTENSION
+ "INTEROP_JSON1_EXTENSION",
+#endif
+
+#if INTEROP_LEGACY_CLOSE
+ "INTEROP_LEGACY_CLOSE",
+#endif
+
+#if INTEROP_LOG
+ "INTEROP_LOG",
+#endif
+
+#if INTEROP_PERCENTILE_EXTENSION
+ "INTEROP_PERCENTILE_EXTENSION",
+#endif
+
+#if INTEROP_REGEXP_EXTENSION
+ "INTEROP_REGEXP_EXTENSION",
+#endif
+
+#if INTEROP_SESSION_EXTENSION
+ "INTEROP_SESSION_EXTENSION",
+#endif
+
+#if INTEROP_SHA1_EXTENSION
+ "INTEROP_SHA1_EXTENSION",
+#endif
+
+#if INTEROP_TEST_EXTENSION
+ "INTEROP_TEST_EXTENSION",
+#endif
+
+#if INTEROP_TOTYPE_EXTENSION
+ "INTEROP_TOTYPE_EXTENSION",
+#endif
+
+#if INTEROP_VIRTUAL_TABLE
+ "INTEROP_VIRTUAL_TABLE",
+#endif
+
+#if NET_20
+ "NET_20",
+#endif
+
+#if NET_35
+ "NET_35",
+#endif
+
+#if NET_40
+ "NET_40",
+#endif
+
+#if NET_45
+ "NET_45",
+#endif
+
+#if NET_451
+ "NET_451",
+#endif
+
+#if NET_452
+ "NET_452",
+#endif
+
+#if NET_46
+ "NET_46",
+#endif
+
+#if NET_461
+ "NET_461",
+#endif
+
+#if NET_462
+ "NET_462",
+#endif
+
+#if NET_47
+ "NET_47",
+#endif
+
+#if NET_471
+ "NET_471",
+#endif
+
+#if NET_472
+ "NET_472",
+#endif
+
+#if NET_COMPACT_20
+ "NET_COMPACT_20",
+#endif
+
+#if NET_STANDARD_20
+ "NET_STANDARD_20",
+#endif
+
+#if PLATFORM_COMPACTFRAMEWORK
+ "PLATFORM_COMPACTFRAMEWORK",
+#endif
+
+#if PRELOAD_NATIVE_LIBRARY
+ "PRELOAD_NATIVE_LIBRARY",
+#endif
+
+#if RETARGETABLE
+ "RETARGETABLE",
+#endif
+
+#if SQLITE_STANDARD
+ "SQLITE_STANDARD",
+#endif
+
+#if THROW_ON_DISPOSED
+ "THROW_ON_DISPOSED",
+#endif
+
+#if TRACE
+ "TRACE",
+#endif
+
+#if TRACE_CONNECTION
+ "TRACE_CONNECTION",
+#endif
+
+#if TRACE_DETECTION
+ "TRACE_DETECTION",
+#endif
+
+#if TRACE_HANDLE
+ "TRACE_HANDLE",
+#endif
+
+#if TRACE_PRELOAD
+ "TRACE_PRELOAD",
+#endif
+
+#if TRACE_SHARED
+ "TRACE_SHARED",
+#endif
+
+#if TRACE_STATEMENT
+ "TRACE_STATEMENT",
+#endif
+
+#if TRACE_WARNING
+ "TRACE_WARNING",
+#endif
+
+#if TRACK_MEMORY_BYTES
+ "TRACK_MEMORY_BYTES",
+#endif
+
+#if USE_ENTITY_FRAMEWORK_6
+ "USE_ENTITY_FRAMEWORK_6",
+#endif
+
+#if USE_INTEROP_DLL
+ "USE_INTEROP_DLL",
+#endif
+
+#if USE_PREPARE_V2
+ "USE_PREPARE_V2",
+#endif
+
+#if WINDOWS
+ "WINDOWS",
+#endif
+
+ null
+ });
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteEnlistment.cs b/Native.Csharp.Tool/SQLite/SQLiteEnlistment.cs
new file mode 100644
index 00000000..61c7ffab
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteEnlistment.cs
@@ -0,0 +1,297 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+#if !PLATFORM_COMPACTFRAMEWORK
+namespace System.Data.SQLite
+{
+ using System.Globalization;
+ using System.Transactions;
+
+ internal sealed class SQLiteEnlistment : IDisposable, IEnlistmentNotification
+ {
+ internal SQLiteTransaction _transaction;
+ internal Transaction _scope;
+ internal bool _disposeConnection;
+
+ internal SQLiteEnlistment(
+ SQLiteConnection cnn,
+ Transaction scope,
+ System.Data.IsolationLevel defaultIsolationLevel,
+ bool throwOnUnavailable,
+ bool throwOnUnsupported
+ )
+ {
+ _transaction = cnn.BeginTransaction(GetSystemDataIsolationLevel(
+ cnn, scope, defaultIsolationLevel, throwOnUnavailable,
+ throwOnUnsupported));
+
+ _scope = scope;
+
+ _scope.EnlistVolatile(this, EnlistmentOptions.None);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ private System.Data.IsolationLevel GetSystemDataIsolationLevel(
+ SQLiteConnection connection,
+ Transaction transaction,
+ System.Data.IsolationLevel defaultIsolationLevel,
+ bool throwOnUnavailable,
+ bool throwOnUnsupported
+ )
+ {
+ if (transaction == null)
+ {
+ //
+ // NOTE: If neither the transaction nor connection isolation
+ // level is available, throw an exception if instructed
+ // by the caller.
+ //
+ if (connection != null)
+ return connection.GetDefaultIsolationLevel();
+
+ if (throwOnUnavailable)
+ {
+ throw new InvalidOperationException(
+ "isolation level is unavailable");
+ }
+
+ return defaultIsolationLevel;
+ }
+
+ IsolationLevel isolationLevel = transaction.IsolationLevel;
+
+ //
+ // TODO: Are these isolation level mappings actually correct?
+ //
+ switch (isolationLevel)
+ {
+ case IsolationLevel.Unspecified:
+ return System.Data.IsolationLevel.Unspecified;
+ case IsolationLevel.Chaos:
+ return System.Data.IsolationLevel.Chaos;
+ case IsolationLevel.ReadUncommitted:
+ return System.Data.IsolationLevel.ReadUncommitted;
+ case IsolationLevel.ReadCommitted:
+ return System.Data.IsolationLevel.ReadCommitted;
+ case IsolationLevel.RepeatableRead:
+ return System.Data.IsolationLevel.RepeatableRead;
+ case IsolationLevel.Serializable:
+ return System.Data.IsolationLevel.Serializable;
+ case IsolationLevel.Snapshot:
+ return System.Data.IsolationLevel.Snapshot;
+ }
+
+ //
+ // NOTE: When in "strict" mode, throw an exception if the isolation
+ // level is not recognized; otherwise, fallback to the default
+ // isolation level specified by the caller.
+ //
+ if (throwOnUnsupported)
+ {
+ throw new InvalidOperationException(
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ "unsupported isolation level {0}", isolationLevel));
+ }
+
+ return defaultIsolationLevel;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private void Cleanup(SQLiteConnection cnn)
+ {
+ if (_disposeConnection && (cnn != null))
+ cnn.Dispose();
+
+ _transaction = null;
+ _scope = null;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteEnlistment).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private /* protected virtual */ void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (_transaction != null)
+ {
+ _transaction.Dispose();
+ _transaction = null;
+ }
+
+ if (_scope != null)
+ {
+ // _scope.Dispose(); // NOTE: Not "owned" by us.
+ _scope = null;
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ~SQLiteEnlistment()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region IEnlistmentNotification Members
+ public void Commit(Enlistment enlistment)
+ {
+ CheckDisposed();
+
+ SQLiteConnection cnn = null;
+
+ try
+ {
+ while (true)
+ {
+ cnn = _transaction.Connection;
+
+ if (cnn == null)
+ break;
+
+ lock (cnn._enlistmentSyncRoot) /* TRANSACTIONAL */
+ {
+ //
+ // NOTE: This check is necessary to detect the case where
+ // the SQLiteConnection.Close() method changes the
+ // connection associated with our transaction (i.e.
+ // to avoid a race (condition) between grabbing the
+ // Connection property and locking its enlistment).
+ //
+ if (!Object.ReferenceEquals(cnn, _transaction.Connection))
+ continue;
+
+ cnn._enlistment = null;
+
+ _transaction.IsValid(true); /* throw */
+ cnn._transactionLevel = 1;
+ _transaction.Commit();
+
+ break;
+ }
+ }
+
+ enlistment.Done();
+ }
+ finally
+ {
+ Cleanup(cnn);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void InDoubt(Enlistment enlistment)
+ {
+ CheckDisposed();
+ enlistment.Done();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void Prepare(PreparingEnlistment preparingEnlistment)
+ {
+ CheckDisposed();
+
+ if (_transaction.IsValid(false) == false)
+ preparingEnlistment.ForceRollback();
+ else
+ preparingEnlistment.Prepared();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ public void Rollback(Enlistment enlistment)
+ {
+ CheckDisposed();
+
+ SQLiteConnection cnn = null;
+
+ try
+ {
+ while (true)
+ {
+ cnn = _transaction.Connection;
+
+ if (cnn == null)
+ break;
+
+ lock (cnn._enlistmentSyncRoot) /* TRANSACTIONAL */
+ {
+ //
+ // NOTE: This check is necessary to detect the case where
+ // the SQLiteConnection.Close() method changes the
+ // connection associated with our transaction (i.e.
+ // to avoid a race (condition) between grabbing the
+ // Connection property and locking its enlistment).
+ //
+ if (!Object.ReferenceEquals(cnn, _transaction.Connection))
+ continue;
+
+ cnn._enlistment = null;
+
+ _transaction.Rollback();
+
+ break;
+ }
+ }
+
+ enlistment.Done();
+ }
+ finally
+ {
+ Cleanup(cnn);
+ }
+ }
+ #endregion
+ }
+}
+#endif // !PLATFORM_COMPACT_FRAMEWORK
diff --git a/Native.Csharp.Tool/SQLite/SQLiteException.cs b/Native.Csharp.Tool/SQLite/SQLiteException.cs
new file mode 100644
index 00000000..df51ad2d
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteException.cs
@@ -0,0 +1,853 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data.Common;
+ using System.Globalization;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ using System.Reflection;
+ using System.Runtime.Serialization;
+ using System.Security.Permissions;
+#endif
+
+ ///
+ /// SQLite exception class.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Serializable()]
+ public sealed class SQLiteException : DbException, ISerializable
+#else
+ public sealed class SQLiteException : Exception
+#endif
+ {
+ #region Private Constants
+ ///
+ /// This value was copied from the "WinError.h" file included with the
+ /// Platform SDK for Windows 10.
+ ///
+ private const int FACILITY_SQLITE = 1967;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private SQLiteErrorCode _errorCode;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Private constructor for use with serialization.
+ ///
+ ///
+ /// Holds the serialized object data about the exception being thrown.
+ ///
+ ///
+ /// Contains contextual information about the source or destination.
+ ///
+ private SQLiteException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ _errorCode = (SQLiteErrorCode)info.GetInt32("errorCode");
+
+ Initialize();
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Public constructor for generating a SQLite exception given the error
+ /// code and message.
+ ///
+ ///
+ /// The SQLite return code to report.
+ ///
+ ///
+ /// Message text to go along with the return code message text.
+ ///
+ public SQLiteException(SQLiteErrorCode errorCode, string message)
+ : base(GetStockErrorMessage(errorCode, message))
+ {
+ _errorCode = errorCode;
+
+ Initialize();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Public constructor that uses the base class constructor for the error
+ /// message.
+ ///
+ /// Error message text.
+ public SQLiteException(string message)
+ : this(SQLiteErrorCode.Unknown, message)
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Public constructor that uses the default base class constructor.
+ ///
+ public SQLiteException()
+ : base()
+ {
+ Initialize();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Public constructor that uses the base class constructor for the error
+ /// message and inner exception.
+ ///
+ /// Error message text.
+ /// The original (inner) exception.
+ public SQLiteException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ Initialize();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Adds extra information to the serialized object data specific to this
+ /// class type. This is only used for serialization.
+ ///
+ ///
+ /// Holds the serialized object data about the exception being thrown.
+ ///
+ ///
+ /// Contains contextual information about the source or destination.
+ ///
+ [SecurityPermission(
+ SecurityAction.LinkDemand,
+ Flags = SecurityPermissionFlag.SerializationFormatter)]
+ public override void GetObjectData(
+ SerializationInfo info,
+ StreamingContext context)
+ {
+ if (info != null)
+ info.AddValue("errorCode", _errorCode);
+
+ base.GetObjectData(info, context);
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets the associated SQLite result code for this exception as a
+ /// . This property returns the same
+ /// underlying value as the property.
+ ///
+ public SQLiteErrorCode ResultCode
+ {
+ get { return _errorCode; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets the associated SQLite return code for this exception as an
+ /// . For desktop versions of the .NET Framework,
+ /// this property overrides the property of the same name within the
+ ///
+ /// class. This property returns the same underlying value as the
+ /// property.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ public override int ErrorCode
+#else
+ public int ErrorCode
+#endif
+ {
+ get { return (int)_errorCode; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method performs extra initialization tasks. It may be called by
+ /// any of the constructors of this class. It must not throw exceptions.
+ ///
+ private void Initialize()
+ {
+ if (HResult == unchecked((int)0x80004005)) /* E_FAIL */
+ {
+ int? localHResult = GetHResultForErrorCode(ResultCode);
+
+ if (localHResult != null)
+ HResult = (int)localHResult;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Maps a Win32 error code to an HRESULT.
+ ///
+ ///
+ /// The specified Win32 error code. It must be within the range of zero
+ /// (0) to 0xFFFF (65535).
+ ///
+ ///
+ /// Non-zero if the HRESULT should indicate success; otherwise, zero.
+ ///
+ ///
+ /// The integer value of the HRESULT.
+ ///
+ private static int MakeHResult(
+ int errorCode,
+ bool success
+ )
+ {
+ return (errorCode & 0xFFFF) | FACILITY_SQLITE |
+ (success ? 0 : unchecked((int)0x80000000));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to map the specified onto an
+ /// existing HRESULT -OR- a Win32 error code wrapped in an HRESULT. The
+ /// mappings may not have perfectly matching semantics; however, they do
+ /// have the benefit of being unique within the context of this exception
+ /// type.
+ ///
+ ///
+ /// The to map.
+ ///
+ ///
+ /// The integer HRESULT value -OR- null if there is no known mapping.
+ ///
+ private static int? GetHResultForErrorCode(
+ SQLiteErrorCode errorCode
+ )
+ {
+ switch (errorCode & SQLiteErrorCode.NonExtendedMask)
+ {
+ case SQLiteErrorCode.Ok:
+ {
+ return 0; /* S_OK */
+ }
+ case SQLiteErrorCode.Error:
+ {
+ return MakeHResult(0x001F, false); /* ERROR_GEN_FAILURE */
+ }
+ case SQLiteErrorCode.Internal:
+ {
+ return unchecked((int)0x8000FFFF); /* E_UNEXPECTED */
+ }
+ case SQLiteErrorCode.Perm:
+ {
+ return MakeHResult(0x0005, false); /* ERROR_ACCESS_DENIED */
+ }
+ case SQLiteErrorCode.Abort:
+ {
+ return unchecked((int)0x80004004); /* E_ABORT */
+ }
+ case SQLiteErrorCode.Busy:
+ {
+ return MakeHResult(0x00AA, false); /* ERROR_BUSY */
+ }
+ case SQLiteErrorCode.Locked:
+ {
+ return MakeHResult(0x00D4, false); /* ERROR_LOCKED */
+ }
+ case SQLiteErrorCode.NoMem:
+ {
+ return MakeHResult(0x000E, false); /* ERROR_OUTOFMEMORY */
+ }
+ case SQLiteErrorCode.ReadOnly:
+ {
+ return MakeHResult(0x1779, false); /* ERROR_FILE_READ_ONLY */
+ }
+ case SQLiteErrorCode.Interrupt:
+ {
+ return MakeHResult(0x04C7, false); /* ERROR_CANCELLED */
+ }
+ case SQLiteErrorCode.IoErr:
+ {
+ return MakeHResult(0x045D, false); /* ERROR_IO_DEVICE */
+ }
+ case SQLiteErrorCode.Corrupt:
+ {
+ return MakeHResult(0x054E, false); /* ERROR_INTERNAL_DB_CORRUPTION */
+ }
+ case SQLiteErrorCode.NotFound:
+ {
+ return MakeHResult(0x0032, false); /* ERROR_NOT_SUPPORTED */
+ }
+ case SQLiteErrorCode.Full:
+ {
+ return MakeHResult(0x0070, false); /* ERROR_DISK_FULL */
+ }
+ case SQLiteErrorCode.CantOpen:
+ {
+ return MakeHResult(0x03F3, false); /* ERROR_CANTOPEN */
+ }
+ case SQLiteErrorCode.Protocol:
+ {
+ return MakeHResult(0x05B4, false); /* ERROR_TIMEOUT */
+ }
+ case SQLiteErrorCode.Empty:
+ {
+ return MakeHResult(0x10D2, false); /* ERROR_EMPTY */
+ }
+ case SQLiteErrorCode.Schema:
+ {
+ return MakeHResult(0x078B, false); /* ERROR_CONTEXT_EXPIRED */
+ }
+ case SQLiteErrorCode.TooBig:
+ {
+ return unchecked((int)0x800288C5); /* TYPE_E_SIZETOOBIG */
+ }
+ case SQLiteErrorCode.Constraint:
+ {
+ return MakeHResult(0x202F, false); /* ERROR_DS_CONSTRAINT_VIOLATION */
+ }
+ case SQLiteErrorCode.Mismatch:
+ {
+ return MakeHResult(0x065D, false); /* ERROR_DATATYPE_MISMATCH */
+ }
+ case SQLiteErrorCode.Misuse:
+ {
+ return MakeHResult(0x0649, false); /* ERROR_INVALID_HANDLE_STATE */
+ }
+ case SQLiteErrorCode.NoLfs:
+ {
+ return MakeHResult(0x0646, false); /* ERROR_UNKNOWN_FEATURE */
+ }
+ case SQLiteErrorCode.Auth:
+ {
+ return MakeHResult(0x078F, false); /* ERROR_AUTHENTICATION_FIREWALL_FAILED */
+ }
+ case SQLiteErrorCode.Format:
+ {
+ return MakeHResult(0x000B, false); /* ERROR_BAD_FORMAT */
+ }
+ case SQLiteErrorCode.Range:
+ {
+ return unchecked((int)0x80028CA1); /* TYPE_E_OUTOFBOUNDS */
+ }
+ case SQLiteErrorCode.NotADb:
+ {
+ return MakeHResult(0x0570, false); /* ERROR_FILE_CORRUPT */
+ }
+ case SQLiteErrorCode.Notice:
+ case SQLiteErrorCode.Warning:
+ case SQLiteErrorCode.Row:
+ case SQLiteErrorCode.Done:
+ {
+ //
+ // NOTE: These result codes are not errors, per se;
+ // therefore, mask off all HRESULT bits that
+ // are not part of the "code" portion (e.g.
+ // the severity, facility, etc). This will
+ // have the effect of creating an HRESULT
+ // that indicates success, while (hopefully)
+ // preserving the specified result code. At
+ // the time this method was written (2018-02),
+ // no SQLite result codes were outside of the
+ // supported range for HRESULT codes (e.g.
+ // 0x0000 to 0xFFFF, inclusive), which made
+ // the following masking operation a harmless
+ // NOOP.
+ //
+ return MakeHResult((int)errorCode, true);
+ }
+ }
+
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the error message for the specified SQLite return code.
+ ///
+ /// The SQLite return code.
+ /// The error message or null if it cannot be found.
+ private static string GetErrorString(
+ SQLiteErrorCode errorCode
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // HACK: This must be done via reflection in order to prevent
+ // the RuntimeHelpers.PrepareDelegate method from over-
+ // eagerly attempting to locate the new (and optional)
+ // sqlite3_errstr() function in the SQLite core library
+ // because it happens to be in the static call graph for
+ // the AppDomain.DomainUnload event handler registered
+ // by the SQLiteLog class.
+ //
+ BindingFlags flags = BindingFlags.Static |
+ BindingFlags.NonPublic | BindingFlags.InvokeMethod;
+
+ return typeof(SQLite3).InvokeMember("GetErrorString",
+ flags, null, null, new object[] { errorCode }) as string;
+#else
+ return SQLite3.GetErrorString(errorCode);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the composite error message based on the SQLite return code
+ /// and the optional detailed error message.
+ ///
+ /// The SQLite return code.
+ /// Optional detailed error message.
+ /// Error message text for the return code.
+ private static string GetStockErrorMessage(
+ SQLiteErrorCode errorCode,
+ string message
+ )
+ {
+ return HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "{0}{1}{2}",
+ GetErrorString(errorCode),
+#if !NET_COMPACT_20
+ Environment.NewLine, message).Trim();
+#else
+ "\r\n", message).Trim();
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region System.Object Overrides
+ public override string ToString()
+ {
+ return HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "code = {0} ({1}), message = {2}",
+ _errorCode, (int)_errorCode, base.ToString());
+ }
+ #endregion
+ }
+
+ ///
+ /// SQLite error codes. Actually, this enumeration represents a return code,
+ /// which may also indicate success in one of several ways (e.g. SQLITE_OK,
+ /// SQLITE_ROW, and SQLITE_DONE). Therefore, the name of this enumeration is
+ /// something of a misnomer.
+ ///
+ public enum SQLiteErrorCode
+ {
+ ///
+ /// The error code is unknown. This error code
+ /// is only used by the managed wrapper itself.
+ ///
+ Unknown = -1,
+ ///
+ /// Successful result
+ ///
+ Ok /* 0 */,
+ ///
+ /// SQL error or missing database
+ ///
+ Error /* 1 */,
+ ///
+ /// Internal logic error in SQLite
+ ///
+ Internal /* 2 */,
+ ///
+ /// Access permission denied
+ ///
+ Perm /* 3 */,
+ ///
+ /// Callback routine requested an abort
+ ///
+ Abort /* 4 */,
+ ///
+ /// The database file is locked
+ ///
+ Busy /* 5 */,
+ ///
+ /// A table in the database is locked
+ ///
+ Locked /* 6 */,
+ ///
+ /// A malloc() failed
+ ///
+ NoMem /* 7 */,
+ ///
+ /// Attempt to write a readonly database
+ ///
+ ReadOnly /* 8 */,
+ ///
+ /// Operation terminated by sqlite3_interrupt()
+ ///
+ Interrupt /* 9 */,
+ ///
+ /// Some kind of disk I/O error occurred
+ ///
+ IoErr /* 10 */,
+ ///
+ /// The database disk image is malformed
+ ///
+ Corrupt /* 11 */,
+ ///
+ /// Unknown opcode in sqlite3_file_control()
+ ///
+ NotFound /* 12 */,
+ ///
+ /// Insertion failed because database is full
+ ///
+ Full /* 13 */,
+ ///
+ /// Unable to open the database file
+ ///
+ CantOpen /* 14 */,
+ ///
+ /// Database lock protocol error
+ ///
+ Protocol /* 15 */,
+ ///
+ /// Database is empty
+ ///
+ Empty /* 16 */,
+ ///
+ /// The database schema changed
+ ///
+ Schema /* 17 */,
+ ///
+ /// String or BLOB exceeds size limit
+ ///
+ TooBig /* 18 */,
+ ///
+ /// Abort due to constraint violation
+ ///
+ Constraint /* 19 */,
+ ///
+ /// Data type mismatch
+ ///
+ Mismatch /* 20 */,
+ ///
+ /// Library used incorrectly
+ ///
+ Misuse /* 21 */,
+ ///
+ /// Uses OS features not supported on host
+ ///
+ NoLfs /* 22 */,
+ ///
+ /// Authorization denied
+ ///
+ Auth /* 23 */,
+ ///
+ /// Auxiliary database format error
+ ///
+ Format /* 24 */,
+ ///
+ /// 2nd parameter to sqlite3_bind out of range
+ ///
+ Range /* 25 */,
+ ///
+ /// File opened that is not a database file
+ ///
+ NotADb /* 26 */,
+ ///
+ /// Notifications from sqlite3_log()
+ ///
+ Notice /* 27 */,
+ ///
+ /// Warnings from sqlite3_log()
+ ///
+ Warning /* 28 */,
+ ///
+ /// sqlite3_step() has another row ready
+ ///
+ Row = 100,
+ ///
+ /// sqlite3_step() has finished executing
+ ///
+ Done, /* 101 */
+ ///
+ /// Used to mask off extended result codes
+ ///
+ NonExtendedMask = 0xFF,
+
+ /////////////////////////////////////////////////////////////////////////
+ // BEGIN EXTENDED RESULT CODES
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// A collation sequence was referenced by a schema and it cannot be
+ /// found.
+ ///
+ Error_Missing_CollSeq = (Error | (1 << 8)),
+ ///
+ /// An internal operation failed and it may succeed if retried.
+ ///
+ Error_Retry = (Error | (2 << 8)),
+ ///
+ /// A file read operation failed.
+ ///
+ IoErr_Read = (IoErr | (1 << 8)),
+ ///
+ /// A file read operation returned less data than requested.
+ ///
+ IoErr_Short_Read = (IoErr | (2 << 8)),
+ ///
+ /// A file write operation failed.
+ ///
+ IoErr_Write = (IoErr | (3 << 8)),
+ ///
+ /// A file synchronization operation failed.
+ ///
+ IoErr_Fsync = (IoErr | (4 << 8)),
+ ///
+ /// A directory synchronization operation failed.
+ ///
+ IoErr_Dir_Fsync = (IoErr | (5 << 8)),
+ ///
+ /// A file truncate operation failed.
+ ///
+ IoErr_Truncate = (IoErr | (6 << 8)),
+ ///
+ /// A file metadata operation failed.
+ ///
+ IoErr_Fstat = (IoErr | (7 << 8)),
+ ///
+ /// A file unlock operation failed.
+ ///
+ IoErr_Unlock = (IoErr | (8 << 8)),
+ ///
+ /// A file lock operation failed.
+ ///
+ IoErr_RdLock = (IoErr | (9 << 8)),
+ ///
+ /// A file delete operation failed.
+ ///
+ IoErr_Delete = (IoErr | (10 << 8)),
+ ///
+ /// Not currently used.
+ ///
+ IoErr_Blocked = (IoErr | (11 << 8)),
+ ///
+ /// Out-of-memory during a file operation.
+ ///
+ IoErr_NoMem = (IoErr | (12 << 8)),
+ ///
+ /// A file existence/status operation failed.
+ ///
+ IoErr_Access = (IoErr | (13 << 8)),
+ ///
+ /// A check for a reserved lock failed.
+ ///
+ IoErr_CheckReservedLock = (IoErr | (14 << 8)),
+ ///
+ /// A file lock operation failed.
+ ///
+ IoErr_Lock = (IoErr | (15 << 8)),
+ ///
+ /// A file close operation failed.
+ ///
+ IoErr_Close = (IoErr | (16 << 8)),
+ ///
+ /// A directory close operation failed.
+ ///
+ IoErr_Dir_Close = (IoErr | (17 << 8)),
+ ///
+ /// A shared memory open operation failed.
+ ///
+ IoErr_ShmOpen = (IoErr | (18 << 8)),
+ ///
+ /// A shared memory size operation failed.
+ ///
+ IoErr_ShmSize = (IoErr | (19 << 8)),
+ ///
+ /// A shared memory lock operation failed.
+ ///
+ IoErr_ShmLock = (IoErr | (20 << 8)),
+ ///
+ /// A shared memory map operation failed.
+ ///
+ IoErr_ShmMap = (IoErr | (21 << 8)),
+ ///
+ /// A file seek operation failed.
+ ///
+ IoErr_Seek = (IoErr | (22 << 8)),
+ ///
+ /// A file delete operation failed because it does not exist.
+ ///
+ IoErr_Delete_NoEnt = (IoErr | (23 << 8)),
+ ///
+ /// A file memory mapping operation failed.
+ ///
+ IoErr_Mmap = (IoErr | (24 << 8)),
+ ///
+ /// The temporary directory path could not be obtained.
+ ///
+ IoErr_GetTempPath = (IoErr | (25 << 8)),
+ ///
+ /// A path string conversion operation failed.
+ ///
+ IoErr_ConvPath = (IoErr | (26 << 8)),
+ ///
+ /// Reserved.
+ ///
+ IoErr_VNode = (IoErr | (27 << 8)),
+ ///
+ /// An attempt to authenticate failed.
+ ///
+ IoErr_Auth = (IoErr | (28 << 8)),
+ ///
+ /// An attempt to begin a file system transaction failed.
+ ///
+ IoErr_Begin_Atomic = (IoErr | (29 << 8)),
+ ///
+ /// An attempt to commit a file system transaction failed.
+ ///
+ IoErr_Commit_Atomic = (IoErr | (30 << 8)),
+ ///
+ /// An attempt to rollback a file system transaction failed.
+ ///
+ IoErr_Rollback_Atomic = (IoErr | (31 << 8)),
+ ///
+ /// A database table is locked in shared-cache mode.
+ ///
+ Locked_SharedCache = (Locked | (1 << 8)),
+ ///
+ /// A virtual table in the database is locked.
+ ///
+ Locked_Vtab = (Locked | (2 << 8)),
+ ///
+ /// A database file is locked due to a recovery operation.
+ ///
+ Busy_Recovery = (Busy | (1 << 8)),
+ ///
+ /// A database file is locked due to snapshot semantics.
+ ///
+ Busy_Snapshot = (Busy | (2 << 8)),
+ ///
+ /// A database file cannot be opened because no temporary directory is available.
+ ///
+ CantOpen_NoTempDir = (CantOpen | (1 << 8)),
+ ///
+ /// A database file cannot be opened because its path represents a directory.
+ ///
+ CantOpen_IsDir = (CantOpen | (2 << 8)),
+ ///
+ /// A database file cannot be opened because its full path could not be obtained.
+ ///
+ CantOpen_FullPath = (CantOpen | (3 << 8)),
+ ///
+ /// A database file cannot be opened because a path string conversion operation failed.
+ ///
+ CantOpen_ConvPath = (CantOpen | (4 << 8)),
+ ///
+ /// A virtual table is malformed.
+ ///
+ Corrupt_Vtab = (Corrupt | (1 << 8)),
+ ///
+ /// A required sequence table is missing or corrupt.
+ ///
+ Corrupt_Sequence = (Corrupt | (2 << 8)),
+ ///
+ /// A database file is read-only due to a recovery operation.
+ ///
+ ReadOnly_Recovery = (ReadOnly | (1 << 8)),
+ ///
+ /// A database file is read-only because a lock could not be obtained.
+ ///
+ ReadOnly_CantLock = (ReadOnly | (2 << 8)),
+ ///
+ /// A database file is read-only because it needs rollback processing.
+ ///
+ ReadOnly_Rollback = (ReadOnly | (3 << 8)),
+ ///
+ /// A database file is read-only because it was moved while open.
+ ///
+ ReadOnly_DbMoved = (ReadOnly | (4 << 8)),
+ ///
+ /// The shared-memory file is read-only and it should be read-write.
+ ///
+ ReadOnly_CantInit = (ReadOnly | (5 << 8)),
+ ///
+ /// Unable to create journal file because the directory is read-only.
+ ///
+ ReadOnly_Directory = (ReadOnly | (6 << 8)),
+ ///
+ /// An operation is being aborted due to rollback processing.
+ ///
+ Abort_Rollback = (Abort | (2 << 8)),
+ ///
+ /// A CHECK constraint failed.
+ ///
+ Constraint_Check = (Constraint | (1 << 8)),
+ ///
+ /// A commit hook produced a unsuccessful return code.
+ ///
+ Constraint_CommitHook = (Constraint | (2 << 8)),
+ ///
+ /// A FOREIGN KEY constraint failed.
+ ///
+ Constraint_ForeignKey = (Constraint | (3 << 8)),
+ ///
+ /// Not currently used.
+ ///
+ Constraint_Function = (Constraint | (4 << 8)),
+ ///
+ /// A NOT NULL constraint failed.
+ ///
+ Constraint_NotNull = (Constraint | (5 << 8)),
+ ///
+ /// A PRIMARY KEY constraint failed.
+ ///
+ Constraint_PrimaryKey = (Constraint | (6 << 8)),
+ ///
+ /// The RAISE function was used by a trigger-program.
+ ///
+ Constraint_Trigger = (Constraint | (7 << 8)),
+ ///
+ /// A UNIQUE constraint failed.
+ ///
+ Constraint_Unique = (Constraint | (8 << 8)),
+ ///
+ /// Not currently used.
+ ///
+ Constraint_Vtab = (Constraint | (9 << 8)),
+ ///
+ /// A ROWID constraint failed.
+ ///
+ Constraint_RowId = (Constraint | (10 << 8)),
+ ///
+ /// Frames were recovered from the WAL log file.
+ ///
+ Notice_Recover_Wal = (Notice | (1 << 8)),
+ ///
+ /// Pages were recovered from the journal file.
+ ///
+ Notice_Recover_Rollback = (Notice | (2 << 8)),
+ ///
+ /// An automatic index was created to process a query.
+ ///
+ Warning_AutoIndex = (Warning | (1 << 8)),
+ ///
+ /// User authentication failed.
+ ///
+ Auth_User = (Auth | (1 << 8)),
+ ///
+ /// Success. Prevents the extension from unloading until the process
+ /// terminates.
+ ///
+ Ok_Load_Permanently = (Ok | (1 << 8))
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteFactory.cs b/Native.Csharp.Tool/SQLite/SQLiteFactory.cs
new file mode 100644
index 00000000..db2d7487
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteFactory.cs
@@ -0,0 +1,169 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data.Common;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// SQLite implementation of .
+ ///
+ public sealed partial class SQLiteFactory : DbProviderFactory, IDisposable
+ {
+ ///
+ /// Constructs a new instance.
+ ///
+ public SQLiteFactory()
+ {
+ //
+ // NOTE: Do nothing here now. All the logging setup related code has
+ // been moved to the new SQLiteLog static class.
+ //
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Cleans up resources (native and managed) associated with the current instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteFactory).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Cleans up resources associated with the current instance.
+ ///
+ ~SQLiteFactory()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This event is raised whenever SQLite raises a logging event.
+ /// Note that this should be set as one of the first things in the
+ /// application. This event is provided for backward compatibility only.
+ /// New code should use the class instead.
+ ///
+ public event SQLiteLogEventHandler Log
+ {
+ add { CheckDisposed(); SQLiteLog.Log += value; }
+ remove { CheckDisposed(); SQLiteLog.Log -= value; }
+ }
+
+ ///
+ /// Static instance member which returns an instanced class.
+ ///
+ public static readonly SQLiteFactory Instance = new SQLiteFactory();
+
+ ///
+ /// Creates and returns a new object.
+ ///
+ /// The new object.
+ public override DbCommand CreateCommand()
+ {
+ CheckDisposed();
+ return new SQLiteCommand();
+ }
+
+ ///
+ /// Creates and returns a new object.
+ ///
+ /// The new object.
+ public override DbCommandBuilder CreateCommandBuilder()
+ {
+ CheckDisposed();
+ return new SQLiteCommandBuilder();
+ }
+
+ ///
+ /// Creates and returns a new object.
+ ///
+ /// The new object.
+ public override DbConnection CreateConnection()
+ {
+ CheckDisposed();
+ return new SQLiteConnection();
+ }
+
+ ///
+ /// Creates and returns a new object.
+ ///
+ /// The new object.
+ public override DbConnectionStringBuilder CreateConnectionStringBuilder()
+ {
+ CheckDisposed();
+ return new SQLiteConnectionStringBuilder();
+ }
+
+ ///
+ /// Creates and returns a new object.
+ ///
+ /// The new object.
+ public override DbDataAdapter CreateDataAdapter()
+ {
+ CheckDisposed();
+ return new SQLiteDataAdapter();
+ }
+
+ ///
+ /// Creates and returns a new object.
+ ///
+ /// The new object.
+ public override DbParameter CreateParameter()
+ {
+ CheckDisposed();
+ return new SQLiteParameter();
+ }
+ }
+#endif
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteFunction.cs b/Native.Csharp.Tool/SQLite/SQLiteFunction.cs
new file mode 100644
index 00000000..ab4f5b90
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteFunction.cs
@@ -0,0 +1,1948 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.InteropServices;
+ using System.Globalization;
+
+ ///
+ /// This abstract class is designed to handle user-defined functions easily. An instance of the derived class is made for each
+ /// connection to the database.
+ ///
+ ///
+ /// Although there is one instance of a class derived from SQLiteFunction per database connection, the derived class has no access
+ /// to the underlying connection. This is necessary to deter implementers from thinking it would be a good idea to make database
+ /// calls during processing.
+ ///
+ /// It is important to distinguish between a per-connection instance, and a per-SQL statement context. One instance of this class
+ /// services all SQL statements being stepped through on that connection, and there can be many. One should never store per-statement
+ /// information in member variables of user-defined function classes.
+ ///
+ /// For aggregate functions, always create and store your per-statement data in the contextData object on the 1st step. This data will
+ /// be automatically freed for you (and Dispose() called if the item supports IDisposable) when the statement completes.
+ ///
+ public abstract class SQLiteFunction : IDisposable
+ {
+ private class AggregateData
+ {
+ internal int _count = 1;
+ internal object _data;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The base connection this function is attached to
+ ///
+ internal SQLiteBase _base;
+
+ ///
+ /// Internal array used to keep track of aggregate function context data
+ ///
+ private Dictionary _contextDataList;
+
+ ///
+ /// The connection flags associated with this object (this should be the
+ /// same value as the flags associated with the parent connection object).
+ ///
+ private SQLiteConnectionFlags _flags;
+
+ ///
+ /// Holds a reference to the callback function for user functions
+ ///
+ private SQLiteCallback _InvokeFunc;
+ ///
+ /// Holds a reference to the callbakc function for stepping in an aggregate function
+ ///
+ private SQLiteCallback _StepFunc;
+ ///
+ /// Holds a reference to the callback function for finalizing an aggregate function
+ ///
+ private SQLiteFinalCallback _FinalFunc;
+ ///
+ /// Holds a reference to the callback function for collating sequences
+ ///
+ private SQLiteCollation _CompareFunc;
+
+ private SQLiteCollation _CompareFunc16;
+
+ ///
+ /// Current context of the current callback. Only valid during a callback
+ ///
+ internal IntPtr _context;
+
+ ///
+ /// This static dictionary contains all the registered (known) user-defined
+ /// functions declared using the proper attributes. The contained dictionary
+ /// values are always null and are not currently used.
+ ///
+ private static IDictionary _registeredFunctions;
+
+ ///
+ /// Internal constructor, initializes the function's internal variables.
+ ///
+ protected SQLiteFunction()
+ {
+ _contextDataList = new Dictionary();
+ }
+
+ ///
+ /// Constructs an instance of this class using the specified data-type
+ /// conversion parameters.
+ ///
+ ///
+ /// The DateTime format to be used when converting string values to a
+ /// DateTime and binding DateTime parameters.
+ ///
+ ///
+ /// The to be used when creating DateTime
+ /// values.
+ ///
+ ///
+ /// The format string to be used when parsing and formatting DateTime
+ /// values.
+ ///
+ ///
+ /// Non-zero to create a UTF-16 data-type conversion context; otherwise,
+ /// a UTF-8 data-type conversion context will be created.
+ ///
+ protected SQLiteFunction(
+ SQLiteDateFormats format,
+ DateTimeKind kind,
+ string formatString,
+ bool utf16
+ )
+ : this()
+ {
+ if (utf16)
+ _base = new SQLite3_UTF16(format, kind, formatString, IntPtr.Zero, null, false);
+ else
+ _base = new SQLite3(format, kind, formatString, IntPtr.Zero, null, false);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of any active contextData variables that were not automatically cleaned up. Sometimes this can happen if
+ /// someone closes the connection while a DataReader is open.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteFunction).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Placeholder for a user-defined disposal routine
+ ///
+ /// True if the object is being disposed explicitly
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ IDisposable disp;
+
+ foreach (KeyValuePair kv in _contextDataList)
+ {
+ disp = kv.Value._data as IDisposable;
+ if (disp != null)
+ disp.Dispose();
+ }
+ _contextDataList.Clear();
+ _contextDataList = null;
+
+ _flags = SQLiteConnectionFlags.None;
+
+ _InvokeFunc = null;
+ _StepFunc = null;
+ _FinalFunc = null;
+ _CompareFunc = null;
+ _base = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Cleans up resources associated with the current instance.
+ ///
+ ~SQLiteFunction()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns a reference to the underlying connection's SQLiteConvert class, which can be used to convert
+ /// strings and DateTime's into the current connection's encoding schema.
+ ///
+ public SQLiteConvert SQLiteConvert
+ {
+ get
+ {
+ CheckDisposed();
+ return _base;
+ }
+ }
+
+ ///
+ /// Scalar functions override this method to do their magic.
+ ///
+ ///
+ /// Parameters passed to functions have only an affinity for a certain data type, there is no underlying schema available
+ /// to force them into a certain type. Therefore the only types you will ever see as parameters are
+ /// DBNull.Value, Int64, Double, String or byte[] array.
+ ///
+ /// The arguments for the command to process
+ /// You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or
+ /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error,
+ /// just return it!
+ public virtual object Invoke(object[] args)
+ {
+ CheckDisposed();
+ return null;
+ }
+
+ ///
+ /// Aggregate functions override this method to do their magic.
+ ///
+ ///
+ /// Typically you'll be updating whatever you've placed in the contextData field and returning as quickly as possible.
+ ///
+ /// The arguments for the command to process
+ /// The 1-based step number. This is incrememted each time the step method is called.
+ /// A placeholder for implementers to store contextual data pertaining to the current context.
+ public virtual void Step(object[] args, int stepNumber, ref object contextData)
+ {
+ CheckDisposed();
+ }
+
+ ///
+ /// Aggregate functions override this method to finish their aggregate processing.
+ ///
+ ///
+ /// If you implemented your aggregate function properly,
+ /// you've been recording and keeping track of your data in the contextData object provided, and now at this stage you should have
+ /// all the information you need in there to figure out what to return.
+ /// NOTE: It is possible to arrive here without receiving a previous call to Step(), in which case the contextData will
+ /// be null. This can happen when no rows were returned. You can either return null, or 0 or some other custom return value
+ /// if that is the case.
+ ///
+ /// Your own assigned contextData, provided for you so you can return your final results.
+ /// You may return most simple types as a return value, null or DBNull.Value to return null, DateTime, or
+ /// you may return an Exception-derived class if you wish to return an error to SQLite. Do not actually throw the error,
+ /// just return it!
+ ///
+ public virtual object Final(object contextData)
+ {
+ CheckDisposed();
+ return null;
+ }
+
+ ///
+ /// User-defined collating sequences override this method to provide a custom string sorting algorithm.
+ ///
+ /// The first string to compare.
+ /// The second strnig to compare.
+ /// 1 if param1 is greater than param2, 0 if they are equal, or -1 if param1 is less than param2.
+ public virtual int Compare(string param1, string param2)
+ {
+ CheckDisposed();
+ return 0;
+ }
+
+ ///
+ /// Converts an IntPtr array of context arguments to an object array containing the resolved parameters the pointers point to.
+ ///
+ ///
+ /// Parameters passed to functions have only an affinity for a certain data type, there is no underlying schema available
+ /// to force them into a certain type. Therefore the only types you will ever see as parameters are
+ /// DBNull.Value, Int64, Double, String or byte[] array.
+ ///
+ /// The number of arguments
+ /// A pointer to the array of arguments
+ /// An object array of the arguments once they've been converted to .NET values
+ internal object[] ConvertParams(int nArgs, IntPtr argsptr)
+ {
+ object[] parms = new object[nArgs];
+#if !PLATFORM_COMPACTFRAMEWORK
+ IntPtr[] argint = new IntPtr[nArgs];
+#else
+ int[] argint = new int[nArgs];
+#endif
+ Marshal.Copy(argsptr, argint, 0, nArgs);
+
+ for (int n = 0; n < nArgs; n++)
+ {
+ switch (_base.GetParamValueType((IntPtr)argint[n]))
+ {
+ case TypeAffinity.Null:
+ parms[n] = DBNull.Value;
+ break;
+ case TypeAffinity.Int64:
+ parms[n] = _base.GetParamValueInt64((IntPtr)argint[n]);
+ break;
+ case TypeAffinity.Double:
+ parms[n] = _base.GetParamValueDouble((IntPtr)argint[n]);
+ break;
+ case TypeAffinity.Text:
+ parms[n] = _base.GetParamValueText((IntPtr)argint[n]);
+ break;
+ case TypeAffinity.Blob:
+ {
+ int x;
+ byte[] blob;
+
+ x = (int)_base.GetParamValueBytes((IntPtr)argint[n], 0, null, 0, 0);
+ blob = new byte[x];
+ _base.GetParamValueBytes((IntPtr)argint[n], 0, blob, 0, x);
+ parms[n] = blob;
+ }
+ break;
+ case TypeAffinity.DateTime: // Never happens here but what the heck, maybe it will one day.
+ parms[n] = _base.ToDateTime(_base.GetParamValueText((IntPtr)argint[n]));
+ break;
+ }
+ }
+ return parms;
+ }
+
+ ///
+ /// Takes the return value from Invoke() and Final() and figures out how to return it to SQLite's context.
+ ///
+ /// The context the return value applies to
+ /// The parameter to return to SQLite
+ private void SetReturnValue(IntPtr context, object returnValue)
+ {
+ if (returnValue == null || returnValue == DBNull.Value)
+ {
+ _base.ReturnNull(context);
+ return;
+ }
+
+ Type t = returnValue.GetType();
+ if (t == typeof(DateTime))
+ {
+ _base.ReturnText(context, _base.ToString((DateTime)returnValue));
+ return;
+ }
+ else
+ {
+ Exception r = returnValue as Exception;
+
+ if (r != null)
+ {
+ _base.ReturnError(context, r.Message);
+ return;
+ }
+ }
+
+ switch (SQLiteConvert.TypeToAffinity(t, _flags))
+ {
+ case TypeAffinity.Null:
+ _base.ReturnNull(context);
+ return;
+ case TypeAffinity.Int64:
+ _base.ReturnInt64(context, Convert.ToInt64(returnValue, CultureInfo.CurrentCulture));
+ return;
+ case TypeAffinity.Double:
+ _base.ReturnDouble(context, Convert.ToDouble(returnValue, CultureInfo.CurrentCulture));
+ return;
+ case TypeAffinity.Text:
+ _base.ReturnText(context, returnValue.ToString());
+ return;
+ case TypeAffinity.Blob:
+ _base.ReturnBlob(context, (byte[])returnValue);
+ return;
+ }
+ }
+
+ ///
+ /// Internal scalar callback function, which wraps the raw context pointer and calls the virtual Invoke() method.
+ /// WARNING: Must not throw exceptions.
+ ///
+ /// A raw context pointer
+ /// Number of arguments passed in
+ /// A pointer to the array of arguments
+ internal void ScalarCallback(IntPtr context, int nArgs, IntPtr argsptr)
+ {
+ try
+ {
+ _context = context;
+ SetReturnValue(context,
+ Invoke(ConvertParams(nArgs, argsptr))); /* throw */
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Invoke", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ }
+
+ ///
+ /// Internal collating sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
+ /// WARNING: Must not throw exceptions.
+ ///
+ /// Not used
+ /// Length of the string pv1
+ /// Pointer to the first string to compare
+ /// Length of the string pv2
+ /// Pointer to the second string to compare
+ /// Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater
+ /// than the second. Returns 0 if an exception is caught.
+ internal int CompareCallback(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
+ {
+ try
+ {
+ return Compare(SQLiteConvert.UTF8ToString(ptr1, len1),
+ SQLiteConvert.UTF8ToString(ptr2, len2)); /* throw */
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Compare", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ //
+ // NOTE: This must be done to prevent the core SQLite library from
+ // using our (invalid) result.
+ //
+ if ((_base != null) && _base.IsOpen())
+ _base.Cancel();
+
+ return 0;
+ }
+
+ ///
+ /// Internal collating sequence function, which wraps up the raw string pointers and executes the Compare() virtual function.
+ /// WARNING: Must not throw exceptions.
+ ///
+ /// Not used
+ /// Length of the string pv1
+ /// Pointer to the first string to compare
+ /// Length of the string pv2
+ /// Pointer to the second string to compare
+ /// Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater
+ /// than the second. Returns 0 if an exception is caught.
+ internal int CompareCallback16(IntPtr ptr, int len1, IntPtr ptr1, int len2, IntPtr ptr2)
+ {
+ try
+ {
+ return Compare(SQLite3_UTF16.UTF16ToString(ptr1, len1),
+ SQLite3_UTF16.UTF16ToString(ptr2, len2)); /* throw */
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Compare (UTF16)", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ //
+ // NOTE: This must be done to prevent the core SQLite library from
+ // using our (invalid) result.
+ //
+ if ((_base != null) && _base.IsOpen())
+ _base.Cancel();
+
+ return 0;
+ }
+
+ ///
+ /// The internal aggregate Step function callback, which wraps the raw context pointer and calls the virtual Step() method.
+ /// WARNING: Must not throw exceptions.
+ ///
+ ///
+ /// This function takes care of doing the lookups and getting the important information put together to call the Step() function.
+ /// That includes pulling out the user's contextData and updating it after the call is made. We use a sorted list for this so
+ /// binary searches can be done to find the data.
+ ///
+ /// A raw context pointer
+ /// Number of arguments passed in
+ /// A pointer to the array of arguments
+ internal void StepCallback(IntPtr context, int nArgs, IntPtr argsptr)
+ {
+ try
+ {
+ AggregateData data = null;
+
+ if (_base != null)
+ {
+ IntPtr nAux = _base.AggregateContext(context);
+
+ if ((_contextDataList != null) &&
+ !_contextDataList.TryGetValue(nAux, out data))
+ {
+ data = new AggregateData();
+ _contextDataList[nAux] = data;
+ }
+ }
+
+ if (data == null)
+ data = new AggregateData();
+
+ try
+ {
+ _context = context;
+ Step(ConvertParams(nArgs, argsptr),
+ data._count, ref data._data); /* throw */
+ }
+ finally
+ {
+ data._count++;
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Step", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ }
+
+ ///
+ /// An internal aggregate Final function callback, which wraps the context pointer and calls the virtual Final() method.
+ /// WARNING: Must not throw exceptions.
+ ///
+ /// A raw context pointer
+ internal void FinalCallback(IntPtr context)
+ {
+ try
+ {
+ object obj = null;
+
+ if (_base != null)
+ {
+ IntPtr n = _base.AggregateContext(context);
+ AggregateData aggData;
+
+ if ((_contextDataList != null) &&
+ _contextDataList.TryGetValue(n, out aggData))
+ {
+ obj = aggData._data;
+ _contextDataList.Remove(n);
+ }
+ }
+
+ try
+ {
+ _context = context;
+ SetReturnValue(context, Final(obj)); /* throw */
+ }
+ finally
+ {
+ IDisposable disp = obj as IDisposable;
+ if (disp != null) disp.Dispose(); /* throw */
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(_flags))
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Final", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ }
+
+ ///
+ /// Using reflection, enumerate all assemblies in the current appdomain looking for classes that
+ /// have a SQLiteFunctionAttribute attribute, and registering them accordingly.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK && !NET_STANDARD_20
+ [Security.Permissions.FileIOPermission(Security.Permissions.SecurityAction.Assert, AllFiles = Security.Permissions.FileIOPermissionAccess.PathDiscovery)]
+#endif
+ static SQLiteFunction()
+ {
+ _registeredFunctions = new Dictionary();
+ try
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // NOTE: If the "No_SQLiteFunctions" environment variable is set,
+ // skip all our special code and simply return.
+ //
+ if (UnsafeNativeMethods.GetSettingValue("No_SQLiteFunctions", null) != null)
+ return;
+
+ SQLiteFunctionAttribute at;
+ System.Reflection.Assembly[] arAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();
+ int w = arAssemblies.Length;
+ System.Reflection.AssemblyName sqlite = System.Reflection.Assembly.GetExecutingAssembly().GetName();
+
+ for (int n = 0; n < w; n++)
+ {
+ Type[] arTypes;
+ bool found = false;
+ System.Reflection.AssemblyName[] references;
+ try
+ {
+ // Inspect only assemblies that reference SQLite
+ references = arAssemblies[n].GetReferencedAssemblies();
+ int t = references.Length;
+ for (int z = 0; z < t; z++)
+ {
+ if (references[z].Name == sqlite.Name)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false)
+ continue;
+
+ arTypes = arAssemblies[n].GetTypes();
+ }
+ catch (Reflection.ReflectionTypeLoadException e)
+ {
+ arTypes = e.Types;
+ }
+
+ int v = arTypes.Length;
+ for (int x = 0; x < v; x++)
+ {
+ if (arTypes[x] == null) continue;
+
+ object[] arAtt = arTypes[x].GetCustomAttributes(typeof(SQLiteFunctionAttribute), false);
+ int u = arAtt.Length;
+ for (int y = 0; y < u; y++)
+ {
+ at = arAtt[y] as SQLiteFunctionAttribute;
+ if (at != null)
+ {
+ at.InstanceType = arTypes[x];
+ ReplaceFunction(at, null);
+ }
+ }
+ }
+ }
+#endif
+ }
+ catch // SQLite provider can continue without being able to find built-in functions
+ {
+ }
+ }
+
+ ///
+ /// Manual method of registering a function. The type must still have the SQLiteFunctionAttributes in order to work
+ /// properly, but this is a workaround for the Compact Framework where enumerating assemblies is not currently supported.
+ ///
+ /// The type of the function to register
+ public static void RegisterFunction(Type typ)
+ {
+ object[] arAtt = typ.GetCustomAttributes(
+ typeof(SQLiteFunctionAttribute), false);
+
+ for (int y = 0; y < arAtt.Length; y++)
+ {
+ SQLiteFunctionAttribute at = arAtt[y] as SQLiteFunctionAttribute;
+
+ if (at == null)
+ continue;
+
+ RegisterFunction(
+ at.Name, at.Arguments, at.FuncType, typ,
+ at.Callback1, at.Callback2);
+ }
+ }
+
+ ///
+ /// Alternative method of registering a function. This method
+ /// does not require the specified type to be annotated with
+ /// .
+ ///
+ ///
+ /// The name of the function to register.
+ ///
+ ///
+ /// The number of arguments accepted by the function.
+ ///
+ ///
+ /// The type of SQLite function being resitered (e.g. scalar,
+ /// aggregate, or collating sequence).
+ ///
+ ///
+ /// The that actually implements the function.
+ /// This will only be used if the
+ /// and parameters are null.
+ ///
+ ///
+ /// The to be used for all calls into the
+ /// ,
+ /// ,
+ /// and virtual methods.
+ ///
+ ///
+ /// The to be used for all calls into the
+ /// virtual method. This
+ /// parameter is only necessary for aggregate functions.
+ ///
+ public static void RegisterFunction(
+ string name,
+ int argumentCount,
+ FunctionType functionType,
+ Type instanceType,
+ Delegate callback1,
+ Delegate callback2
+ )
+ {
+ SQLiteFunctionAttribute at = new SQLiteFunctionAttribute(
+ name, argumentCount, functionType);
+
+ at.InstanceType = instanceType;
+ at.Callback1 = callback1;
+ at.Callback2 = callback2;
+
+ ReplaceFunction(at, null);
+ }
+
+ ///
+ /// Replaces a registered function, disposing of the associated (old)
+ /// value if necessary.
+ ///
+ ///
+ /// The attribute that describes the function to replace.
+ ///
+ ///
+ /// The new value to use.
+ ///
+ ///
+ /// Non-zero if an existing registered function was replaced; otherwise,
+ /// zero.
+ ///
+ private static bool ReplaceFunction(
+ SQLiteFunctionAttribute at,
+ object newValue
+ )
+ {
+ object oldValue;
+
+ if (_registeredFunctions.TryGetValue(at, out oldValue))
+ {
+ IDisposable disposable = oldValue as IDisposable;
+
+ if (disposable != null)
+ {
+ disposable.Dispose();
+ disposable = null;
+ }
+
+ _registeredFunctions[at] = newValue;
+ return true;
+ }
+ else
+ {
+ _registeredFunctions.Add(at, newValue);
+ return false;
+ }
+ }
+
+ ///
+ /// Creates a instance based on the specified
+ /// .
+ ///
+ ///
+ /// The containing the metadata about
+ /// the function to create.
+ ///
+ ///
+ /// The created function -OR- null if the function could not be created.
+ ///
+ ///
+ /// Non-zero if the function was created; otherwise, zero.
+ ///
+ private static bool CreateFunction(
+ SQLiteFunctionAttribute functionAttribute,
+ out SQLiteFunction function
+ )
+ {
+ if (functionAttribute == null)
+ {
+ function = null;
+ return false;
+ }
+ else if ((functionAttribute.Callback1 != null) ||
+ (functionAttribute.Callback2 != null))
+ {
+ function = new SQLiteDelegateFunction(
+ functionAttribute.Callback1,
+ functionAttribute.Callback2);
+
+ return true;
+ }
+ else if (functionAttribute.InstanceType != null)
+ {
+ function = (SQLiteFunction)Activator.CreateInstance(
+ functionAttribute.InstanceType);
+
+ return true;
+ }
+ else
+ {
+ function = null;
+ return false;
+ }
+ }
+
+ ///
+ /// Called by the SQLiteBase derived classes, this method binds all registered (known) user-defined functions to a connection.
+ /// It is done this way so that all user-defined functions will access the database using the same encoding scheme
+ /// as the connection (UTF-8 or UTF-16).
+ ///
+ ///
+ /// The wrapper functions that interop with SQLite will create a unique cookie value, which internally is a pointer to
+ /// all the wrapped callback functions. The interop function uses it to map CDecl callbacks to StdCall callbacks.
+ ///
+ /// The base object on which the functions are to bind.
+ /// The flags associated with the parent connection object.
+ /// Returns a logical list of functions which the connection should retain until it is closed.
+ internal static IDictionary BindFunctions(
+ SQLiteBase sqlbase,
+ SQLiteConnectionFlags flags
+ )
+ {
+ IDictionary lFunctions =
+ new Dictionary();
+
+ foreach (KeyValuePair pair
+ in _registeredFunctions)
+ {
+ SQLiteFunctionAttribute pr = pair.Key;
+
+ if (pr == null)
+ continue;
+
+ SQLiteFunction f;
+
+ if (CreateFunction(pr, out f))
+ {
+ BindFunction(sqlbase, pr, f, flags);
+ lFunctions[pr] = f;
+ }
+ else
+ {
+ lFunctions[pr] = null;
+ }
+ }
+
+ return lFunctions;
+ }
+
+ ///
+ /// Called by the SQLiteBase derived classes, this method unbinds all registered (known)
+ /// functions -OR- all previously bound user-defined functions from a connection.
+ ///
+ /// The base object from which the functions are to be unbound.
+ /// The flags associated with the parent connection object.
+ ///
+ /// Non-zero to unbind all registered (known) functions -OR- zero to unbind all functions
+ /// currently bound to the connection.
+ ///
+ /// Non-zero if all the specified user-defined functions were unbound.
+ internal static bool UnbindAllFunctions(
+ SQLiteBase sqlbase,
+ SQLiteConnectionFlags flags,
+ bool registered
+ )
+ {
+ if (sqlbase == null)
+ return false;
+
+ IDictionary lFunctions =
+ sqlbase.Functions;
+
+ if (lFunctions == null)
+ return false;
+
+ bool result = true;
+
+ if (registered)
+ {
+ foreach (KeyValuePair pair
+ in _registeredFunctions)
+ {
+ SQLiteFunctionAttribute pr = pair.Key;
+
+ if (pr == null)
+ continue;
+
+ SQLiteFunction f;
+
+ if (!lFunctions.TryGetValue(pr, out f) ||
+ (f == null) ||
+ !UnbindFunction(sqlbase, pr, f, flags))
+ {
+ result = false;
+ }
+ }
+ }
+ else
+ {
+ //
+ // NOTE: Need to use a copy of the function dictionary in this method
+ // because the dictionary is modified within the UnbindFunction
+ // method, which is called inside the loop.
+ //
+ lFunctions = new Dictionary(
+ lFunctions);
+
+ foreach (KeyValuePair pair
+ in lFunctions)
+ {
+ SQLiteFunctionAttribute pr = pair.Key;
+
+ if (pr == null)
+ continue;
+
+ SQLiteFunction f = pair.Value;
+
+ if ((f != null) &&
+ UnbindFunction(sqlbase, pr, f, flags))
+ {
+ /* IGNORED */
+ sqlbase.Functions.Remove(pr);
+ }
+ else
+ {
+ result = false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// This function binds a user-defined function to a connection.
+ ///
+ ///
+ /// The object instance associated with the
+ /// that the function should be bound to.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// The object instance that implements the
+ /// function to be bound.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ internal static void BindFunction(
+ SQLiteBase sqliteBase,
+ SQLiteFunctionAttribute functionAttribute,
+ SQLiteFunction function,
+ SQLiteConnectionFlags flags
+ )
+ {
+ if (sqliteBase == null)
+ throw new ArgumentNullException("sqliteBase");
+
+ if (functionAttribute == null)
+ throw new ArgumentNullException("functionAttribute");
+
+ if (function == null)
+ throw new ArgumentNullException("function");
+
+ FunctionType functionType = functionAttribute.FuncType;
+
+ function._base = sqliteBase;
+ function._flags = flags;
+
+ function._InvokeFunc = (functionType == FunctionType.Scalar) ?
+ new SQLiteCallback(function.ScalarCallback) : null;
+
+ function._StepFunc = (functionType == FunctionType.Aggregate) ?
+ new SQLiteCallback(function.StepCallback) : null;
+
+ function._FinalFunc = (functionType == FunctionType.Aggregate) ?
+ new SQLiteFinalCallback(function.FinalCallback) : null;
+
+ function._CompareFunc = (functionType == FunctionType.Collation) ?
+ new SQLiteCollation(function.CompareCallback) : null;
+
+ function._CompareFunc16 = (functionType == FunctionType.Collation) ?
+ new SQLiteCollation(function.CompareCallback16) : null;
+
+ string name = functionAttribute.Name;
+
+ if (functionType != FunctionType.Collation)
+ {
+ bool needCollSeq = (function is SQLiteFunctionEx);
+
+ sqliteBase.CreateFunction(
+ name, functionAttribute.Arguments, needCollSeq,
+ function._InvokeFunc, function._StepFunc,
+ function._FinalFunc, true);
+ }
+ else
+ {
+ sqliteBase.CreateCollation(
+ name, function._CompareFunc, function._CompareFunc16,
+ true);
+ }
+ }
+
+ ///
+ /// This function unbinds a user-defined functions from a connection.
+ ///
+ ///
+ /// The object instance associated with the
+ /// that the function should be bound to.
+ ///
+ ///
+ /// The object instance containing
+ /// the metadata for the function to be bound.
+ ///
+ ///
+ /// The object instance that implements the
+ /// function to be bound.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ /// Non-zero if the function was unbound.
+ internal static bool UnbindFunction(
+ SQLiteBase sqliteBase,
+ SQLiteFunctionAttribute functionAttribute,
+ SQLiteFunction function,
+ SQLiteConnectionFlags flags /* NOT USED */
+ )
+ {
+ if (sqliteBase == null)
+ throw new ArgumentNullException("sqliteBase");
+
+ if (functionAttribute == null)
+ throw new ArgumentNullException("functionAttribute");
+
+ if (function == null)
+ throw new ArgumentNullException("function");
+
+ FunctionType functionType = functionAttribute.FuncType;
+ string name = functionAttribute.Name;
+
+ if (functionType != FunctionType.Collation)
+ {
+ bool needCollSeq = (function is SQLiteFunctionEx);
+
+ return sqliteBase.CreateFunction(
+ name, functionAttribute.Arguments, needCollSeq,
+ null, null, null, false) == SQLiteErrorCode.Ok;
+ }
+ else
+ {
+ return sqliteBase.CreateCollation(
+ name, null, null, false) == SQLiteErrorCode.Ok;
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This type is used with the
+ /// method.
+ ///
+ ///
+ /// This is always the string literal "Invoke".
+ ///
+ ///
+ /// The arguments for the scalar function.
+ ///
+ ///
+ /// The result of the scalar function.
+ ///
+ public delegate object SQLiteInvokeDelegate(
+ string param0,
+ object[] args
+ );
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This type is used with the
+ /// method.
+ ///
+ ///
+ /// This is always the string literal "Step".
+ ///
+ ///
+ /// The arguments for the aggregate function.
+ ///
+ ///
+ /// The step number (one based). This is incrememted each time the
+ /// method is called.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ public delegate void SQLiteStepDelegate(
+ string param0,
+ object[] args,
+ int stepNumber,
+ ref object contextData
+ );
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This type is used with the
+ /// method.
+ ///
+ ///
+ /// This is always the string literal "Final".
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// The result of the aggregate function.
+ ///
+ public delegate object SQLiteFinalDelegate(
+ string param0,
+ object contextData
+ );
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This type is used with the
+ /// method.
+ ///
+ ///
+ /// This is always the string literal "Compare".
+ ///
+ ///
+ /// The first string to compare.
+ ///
+ ///
+ /// The second strnig to compare.
+ ///
+ ///
+ /// A positive integer if the parameter is
+ /// greater than the parameter, a negative
+ /// integer if the parameter is less than
+ /// the parameter, or zero if they are
+ /// equal.
+ ///
+ public delegate int SQLiteCompareDelegate(
+ string param0,
+ string param1,
+ string param2
+ );
+
+ /////////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// This class implements a SQLite function using a .
+ /// All the virtual methods of the class are
+ /// implemented using calls to the ,
+ /// , ,
+ /// and strongly typed delegate types
+ /// or via the method.
+ /// The arguments are presented in the same order they appear in
+ /// the associated methods with one exception:
+ /// the first argument is the name of the virtual method being implemented.
+ ///
+#else
+ ///
+ /// This class implements a SQLite function using a .
+ /// All the virtual methods of the class are
+ /// implemented using calls to the ,
+ /// , ,
+ /// and strongly typed delegate types.
+ /// The arguments are presented in the same order they appear in
+ /// the associated methods with one exception:
+ /// the first argument is the name of the virtual method being implemented.
+ ///
+#endif
+ public class SQLiteDelegateFunction : SQLiteFunction
+ {
+ #region Private Constants
+ ///
+ /// This error message is used by the overridden virtual methods when
+ /// a required property (e.g.
+ /// or ) has not been
+ /// set.
+ ///
+ private const string NoCallbackError = "No \"{0}\" callback is set.";
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This error message is used by the overridden
+ /// method when the result does not have a type of .
+ ///
+ private const string ResultInt32Error = "\"{0}\" result must be Int32.";
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an empty instance of this class.
+ ///
+ public SQLiteDelegateFunction()
+ : this(null, null)
+ {
+ // do nothing.
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified
+ /// as the
+ /// implementation.
+ ///
+ ///
+ /// The to be used for all calls into the
+ /// , , and
+ /// virtual methods needed by the
+ /// base class.
+ ///
+ ///
+ /// The to be used for all calls into the
+ /// virtual methods needed by the
+ /// base class.
+ ///
+ public SQLiteDelegateFunction(
+ Delegate callback1,
+ Delegate callback2
+ )
+ {
+ this.callback1 = callback1;
+ this.callback2 = callback2;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Invoke".
+ ///
+ ///
+ /// The original arguments received by the method.
+ ///
+ ///
+ /// Non-zero if the returned arguments are going to be used with the
+ /// type; otherwise, zero.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetInvokeArgs(
+ object[] args,
+ bool earlyBound
+ )
+ {
+ object[] newArgs = new object[] { "Invoke", args };
+
+ if (!earlyBound)
+ newArgs = new object[] { newArgs }; // WRAP
+
+ return newArgs;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Step".
+ ///
+ ///
+ /// The original arguments received by the method.
+ ///
+ ///
+ /// The step number (one based). This is incrememted each time the
+ /// method is called.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// Non-zero if the returned arguments are going to be used with the
+ /// type; otherwise, zero.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetStepArgs(
+ object[] args,
+ int stepNumber,
+ object contextData,
+ bool earlyBound
+ )
+ {
+ object[] newArgs = new object[] {
+ "Step", args, stepNumber, contextData
+ };
+
+ if (!earlyBound)
+ newArgs = new object[] { newArgs }; // WRAP
+
+ return newArgs;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Updates the output arguments for the method,
+ /// using an of . The first
+ /// argument is always the literal string "Step". Currently, only the
+ /// parameter is updated.
+ ///
+ ///
+ /// The original arguments received by the method.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// Non-zero if the returned arguments are going to be used with the
+ /// type; otherwise, zero.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual void UpdateStepArgs(
+ object[] args,
+ ref object contextData,
+ bool earlyBound
+ )
+ {
+ object[] newArgs;
+
+ if (earlyBound)
+ newArgs = args;
+ else
+ newArgs = args[0] as object[];
+
+ if (newArgs == null)
+ return;
+
+ contextData = newArgs[newArgs.Length - 1];
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Final".
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// Non-zero if the returned arguments are going to be used with the
+ /// type; otherwise, zero.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetFinalArgs(
+ object contextData,
+ bool earlyBound
+ )
+ {
+ object[] newArgs = new object[] { "Final", contextData };
+
+ if (!earlyBound)
+ newArgs = new object[] { newArgs }; // WRAP
+
+ return newArgs;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the list of arguments for the method,
+ /// as an of . The first
+ /// argument is always the literal string "Compare".
+ ///
+ ///
+ /// The first string to compare.
+ ///
+ ///
+ /// The second strnig to compare.
+ ///
+ ///
+ /// Non-zero if the returned arguments are going to be used with the
+ /// type; otherwise, zero.
+ ///
+ ///
+ /// The arguments to pass to the configured .
+ ///
+ protected virtual object[] GetCompareArgs(
+ string param1,
+ string param2,
+ bool earlyBound
+ )
+ {
+ object[] newArgs = new object[] { "Compare", param1, param2 };
+
+ if (!earlyBound)
+ newArgs = new object[] { newArgs }; // WRAP
+
+ return newArgs;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private Delegate callback1;
+ ///
+ /// The to be used for all calls into the
+ /// , , and
+ /// virtual methods needed by the
+ /// base class.
+ ///
+ public virtual Delegate Callback1
+ {
+ get { return callback1; }
+ set { callback1 = value; }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private Delegate callback2;
+ ///
+ /// The to be used for all calls into the
+ /// virtual methods needed by the
+ /// base class.
+ ///
+ public virtual Delegate Callback2
+ {
+ get { return callback2; }
+ set { callback2 = value; }
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region System.Data.SQLite.SQLiteFunction Overrides
+ ///
+ /// This virtual method is the implementation for scalar functions.
+ /// See the method for more
+ /// details.
+ ///
+ ///
+ /// The arguments for the scalar function.
+ ///
+ ///
+ /// The result of the scalar function.
+ ///
+ public override object Invoke(
+ object[] args /* in */
+ )
+ {
+ if (callback1 == null)
+ {
+ throw new InvalidOperationException(
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ NoCallbackError, "Invoke"));
+ }
+
+ SQLiteInvokeDelegate invokeDelegate =
+ callback1 as SQLiteInvokeDelegate;
+
+ if (invokeDelegate != null)
+ {
+ return invokeDelegate.Invoke("Invoke", args); /* throw */
+ }
+ else
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return callback1.DynamicInvoke(
+ GetInvokeArgs(args, false)); /* throw */
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This virtual method is part of the implementation for aggregate
+ /// functions. See the method
+ /// for more details.
+ ///
+ ///
+ /// The arguments for the aggregate function.
+ ///
+ ///
+ /// The step number (one based). This is incrememted each time the
+ /// method is called.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ public override void Step(
+ object[] args, /* in */
+ int stepNumber, /* in */
+ ref object contextData /* in, out */
+ )
+ {
+ if (callback1 == null)
+ {
+ throw new InvalidOperationException(
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ NoCallbackError, "Step"));
+ }
+
+ SQLiteStepDelegate stepDelegate = callback1 as SQLiteStepDelegate;
+
+ if (stepDelegate != null)
+ {
+ stepDelegate.Invoke(
+ "Step", args, stepNumber, ref contextData); /* throw */
+ }
+ else
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ object[] newArgs = GetStepArgs(
+ args, stepNumber, contextData, false);
+
+ /* IGNORED */
+ callback1.DynamicInvoke(newArgs); /* throw */
+
+ UpdateStepArgs(newArgs, ref contextData, false);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This virtual method is part of the implementation for aggregate
+ /// functions. See the method
+ /// for more details.
+ ///
+ ///
+ /// A placeholder for implementers to store contextual data pertaining
+ /// to the current context.
+ ///
+ ///
+ /// The result of the aggregate function.
+ ///
+ public override object Final(
+ object contextData /* in */
+ )
+ {
+ if (callback2 == null)
+ {
+ throw new InvalidOperationException(
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ NoCallbackError, "Final"));
+ }
+
+ SQLiteFinalDelegate finalDelegate = callback2 as SQLiteFinalDelegate;
+
+ if (finalDelegate != null)
+ {
+ return finalDelegate.Invoke("Final", contextData); /* throw */
+ }
+ else
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return callback1.DynamicInvoke(GetFinalArgs(
+ contextData, false)); /* throw */
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This virtual method is part of the implementation for collating
+ /// sequences. See the method
+ /// for more details.
+ ///
+ ///
+ /// The first string to compare.
+ ///
+ ///
+ /// The second strnig to compare.
+ ///
+ ///
+ /// A positive integer if the parameter is
+ /// greater than the parameter, a negative
+ /// integer if the parameter is less than
+ /// the parameter, or zero if they are
+ /// equal.
+ ///
+ public override int Compare(
+ string param1, /* in */
+ string param2 /* in */
+ )
+ {
+ if (callback1 == null)
+ {
+ throw new InvalidOperationException(
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ NoCallbackError, "Compare"));
+ }
+
+ SQLiteCompareDelegate compareDelegate =
+ callback1 as SQLiteCompareDelegate;
+
+ if (compareDelegate != null)
+ {
+ return compareDelegate.Invoke(
+ "Compare", param1, param2); /* throw */
+ }
+ else
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ object result = callback1.DynamicInvoke(GetCompareArgs(
+ param1, param2, false)); /* throw */
+
+ if (result is int)
+ return (int)result;
+
+ throw new InvalidOperationException(
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ ResultInt32Error, "Compare"));
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ }
+ #endregion
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Extends SQLiteFunction and allows an inherited class to obtain the collating sequence associated with a function call.
+ ///
+ ///
+ /// User-defined functions can call the GetCollationSequence() method in this class and use it to compare strings and char arrays.
+ ///
+ public class SQLiteFunctionEx : SQLiteFunction
+ {
+ ///
+ /// Obtains the collating sequence in effect for the given function.
+ ///
+ ///
+ protected CollationSequence GetCollationSequence()
+ {
+ return _base.GetCollationSequence(this, _context);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteFunctionEx).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Cleans up resources (native and managed) associated with the current instance.
+ ///
+ ///
+ /// Zero when being disposed via garbage collection; otherwise, non-zero.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+
+ ///
+ /// The type of user-defined function to declare
+ ///
+ public enum FunctionType
+ {
+ ///
+ /// Scalar functions are designed to be called and return a result immediately. Examples include ABS(), Upper(), Lower(), etc.
+ ///
+ Scalar = 0,
+ ///
+ /// Aggregate functions are designed to accumulate data until the end of a call and then return a result gleaned from the accumulated data.
+ /// Examples include SUM(), COUNT(), AVG(), etc.
+ ///
+ Aggregate = 1,
+ ///
+ /// Collating sequences are used to sort textual data in a custom manner, and appear in an ORDER BY clause. Typically text in an ORDER BY is
+ /// sorted using a straight case-insensitive comparison function. Custom collating sequences can be used to alter the behavior of text sorting
+ /// in a user-defined manner.
+ ///
+ Collation = 2,
+ }
+
+ ///
+ /// An internal callback delegate declaration.
+ ///
+ /// Raw native context pointer for the user function.
+ /// Total number of arguments to the user function.
+ /// Raw native pointer to the array of raw native argument pointers.
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate void SQLiteCallback(IntPtr context, int argc, IntPtr argv);
+ ///
+ /// An internal final callback delegate declaration.
+ ///
+ /// Raw context pointer for the user function
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate void SQLiteFinalCallback(IntPtr context);
+ ///
+ /// Internal callback delegate for implementing collating sequences
+ ///
+ /// Not used
+ /// Length of the string pv1
+ /// Pointer to the first string to compare
+ /// Length of the string pv2
+ /// Pointer to the second string to compare
+ /// Returns -1 if the first string is less than the second. 0 if they are equal, or 1 if the first string is greater
+ /// than the second.
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate int SQLiteCollation(IntPtr puser, int len1, IntPtr pv1, int len2, IntPtr pv2);
+
+ ///
+ /// The type of collating sequence
+ ///
+ public enum CollationTypeEnum
+ {
+ ///
+ /// The built-in BINARY collating sequence
+ ///
+ Binary = 1,
+ ///
+ /// The built-in NOCASE collating sequence
+ ///
+ NoCase = 2,
+ ///
+ /// The built-in REVERSE collating sequence
+ ///
+ Reverse = 3,
+ ///
+ /// A custom user-defined collating sequence
+ ///
+ Custom = 0,
+ }
+
+ ///
+ /// The encoding type the collation sequence uses
+ ///
+ public enum CollationEncodingEnum
+ {
+ ///
+ /// The collation sequence is UTF8
+ ///
+ UTF8 = 1,
+ ///
+ /// The collation sequence is UTF16 little-endian
+ ///
+ UTF16LE = 2,
+ ///
+ /// The collation sequence is UTF16 big-endian
+ ///
+ UTF16BE = 3,
+ }
+
+ ///
+ /// A struct describing the collating sequence a function is executing in
+ ///
+ public struct CollationSequence
+ {
+ ///
+ /// The name of the collating sequence
+ ///
+ public string Name;
+ ///
+ /// The type of collating sequence
+ ///
+ public CollationTypeEnum Type;
+
+ ///
+ /// The text encoding of the collation sequence
+ ///
+ public CollationEncodingEnum Encoding;
+
+ ///
+ /// Context of the function that requested the collating sequence
+ ///
+ internal SQLiteFunction _func;
+
+ ///
+ /// Calls the base collating sequence to compare two strings
+ ///
+ /// The first string to compare
+ /// The second string to compare
+ /// -1 if s1 is less than s2, 0 if s1 is equal to s2, and 1 if s1 is greater than s2
+ public int Compare(string s1, string s2)
+ {
+ return _func._base.ContextCollateCompare(Encoding, _func._context, s1, s2);
+ }
+
+ ///
+ /// Calls the base collating sequence to compare two character arrays
+ ///
+ /// The first array to compare
+ /// The second array to compare
+ /// -1 if c1 is less than c2, 0 if c1 is equal to c2, and 1 if c1 is greater than c2
+ public int Compare(char[] c1, char[] c2)
+ {
+ return _func._base.ContextCollateCompare(Encoding, _func._context, c1, c2);
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteFunctionAttribute.cs b/Native.Csharp.Tool/SQLite/SQLiteFunctionAttribute.cs
new file mode 100644
index 00000000..f506bf99
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteFunctionAttribute.cs
@@ -0,0 +1,125 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+
+ ///
+ /// A simple custom attribute to enable us to easily find user-defined functions in
+ /// the loaded assemblies and initialize them in SQLite as connections are made.
+ ///
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
+ public sealed class SQLiteFunctionAttribute : Attribute
+ {
+ private string _name;
+ private int _argumentCount;
+ private FunctionType _functionType;
+ private Type _instanceType;
+ private Delegate _callback1;
+ private Delegate _callback2;
+
+ ///
+ /// Default constructor, initializes the internal variables for the function.
+ ///
+ public SQLiteFunctionAttribute()
+ : this(null, -1, FunctionType.Scalar)
+ {
+ // do nothing.
+ }
+
+ ///
+ /// Constructs an instance of this class. This sets the initial
+ /// , , and
+ /// properties to null.
+ ///
+ ///
+ /// The name of the function, as seen by the SQLite core library.
+ ///
+ ///
+ /// The number of arguments that the function will accept.
+ ///
+ ///
+ /// The type of function being declared. This will either be Scalar,
+ /// Aggregate, or Collation.
+ ///
+ public SQLiteFunctionAttribute(
+ string name,
+ int argumentCount,
+ FunctionType functionType
+ )
+ {
+ _name = name;
+ _argumentCount = argumentCount;
+ _functionType = functionType;
+ _instanceType = null;
+ _callback1 = null;
+ _callback2 = null;
+ }
+
+ ///
+ /// The function's name as it will be used in SQLite command text.
+ ///
+ public string Name
+ {
+ get { return _name; }
+ set { _name = value; }
+ }
+
+ ///
+ /// The number of arguments this function expects. -1 if the number of arguments is variable.
+ ///
+ public int Arguments
+ {
+ get { return _argumentCount; }
+ set { _argumentCount = value; }
+ }
+
+ ///
+ /// The type of function this implementation will be.
+ ///
+ public FunctionType FuncType
+ {
+ get { return _functionType; }
+ set { _functionType = value; }
+ }
+
+ ///
+ /// The object instance that describes the class
+ /// containing the implementation for the associated function. The value of
+ /// this property will not be used if either the or
+ /// property values are set to non-null.
+ ///
+ internal Type InstanceType
+ {
+ get { return _instanceType; }
+ set { _instanceType = value; }
+ }
+
+ ///
+ /// The that refers to the implementation for the
+ /// associated function. If this property value is set to non-null, it will
+ /// be used instead of the property value.
+ ///
+ internal Delegate Callback1
+ {
+ get { return _callback1; }
+ set { _callback1 = value; }
+ }
+
+ ///
+ /// The that refers to the implementation for the
+ /// associated function. If this property value is set to non-null, it will
+ /// be used instead of the property value.
+ ///
+ internal Delegate Callback2
+ {
+ get { return _callback2; }
+ set { _callback2 = value; }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteKeyReader.cs b/Native.Csharp.Tool/SQLite/SQLiteKeyReader.cs
new file mode 100644
index 00000000..38fb126e
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteKeyReader.cs
@@ -0,0 +1,764 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+ using System.Collections.Generic;
+ using System.Globalization;
+
+ ///
+ /// This class provides key info for a given SQLite statement.
+ ///
+ /// Providing key information for a given statement is non-trivial :(
+ ///
+ ///
+ internal sealed class SQLiteKeyReader : IDisposable
+ {
+ private KeyInfo[] _keyInfo;
+ private SQLiteStatement _stmt;
+ private bool _isValid;
+ private RowIdInfo[] _rowIdInfo;
+
+ ///
+ /// Used to support CommandBehavior.KeyInfo
+ ///
+ private struct KeyInfo
+ {
+ internal string databaseName;
+ internal string tableName;
+ internal string columnName;
+ internal int database;
+ internal int rootPage;
+ internal int cursor;
+ internal KeyQuery query;
+ internal int column;
+ }
+
+ ///
+ /// Used to keep track of the per-table RowId column metadata.
+ ///
+ private struct RowIdInfo
+ {
+ internal string databaseName;
+ internal string tableName;
+ internal int column;
+ }
+
+ ///
+ /// A single sub-query for a given table/database.
+ ///
+ private sealed class KeyQuery : IDisposable
+ {
+ private SQLiteCommand _command;
+ internal SQLiteDataReader _reader;
+
+ internal KeyQuery(SQLiteConnection cnn, string database, string table, params string[] columns)
+ {
+ using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder())
+ {
+ _command = cnn.CreateCommand();
+ for (int n = 0; n < columns.Length; n++)
+ {
+ columns[n] = builder.QuoteIdentifier(columns[n]);
+ }
+ }
+ _command.CommandText = HelperMethods.StringFormat(CultureInfo.InvariantCulture, "SELECT {0} FROM [{1}].[{2}] WHERE ROWID = ?", String.Join(",", columns), database, table);
+ _command.Parameters.AddWithValue(null, (long)0);
+ }
+
+ internal bool IsValid
+ {
+ set
+ {
+ if (value != false) throw new ArgumentException();
+ if (_reader != null)
+ {
+ _reader.Dispose();
+ _reader = null;
+ }
+ }
+ }
+
+ internal void Sync(long rowid)
+ {
+ IsValid = false;
+ _command.Parameters[0].Value = rowid;
+ _reader = _command.ExecuteReader();
+ _reader.Read();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(KeyQuery).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ IsValid = false;
+
+ if (_command != null) _command.Dispose();
+ _command = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ~KeyQuery()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+
+ ///
+ /// This function does all the nasty work at determining what keys need to be returned for
+ /// a given statement.
+ ///
+ ///
+ ///
+ ///
+ internal SQLiteKeyReader(SQLiteConnection cnn, SQLiteDataReader reader, SQLiteStatement stmt)
+ {
+ Dictionary catalogs = new Dictionary();
+ Dictionary> tables = new Dictionary>();
+ List list;
+ List keys = new List();
+ List rowIds = new List();
+
+ // Record the statement so we can use it later for sync'ing
+ _stmt = stmt;
+
+ // Fetch all the attached databases on this connection
+ using (DataTable tbl = cnn.GetSchema("Catalogs"))
+ {
+ foreach (DataRow row in tbl.Rows)
+ {
+ catalogs.Add((string)row["CATALOG_NAME"], Convert.ToInt32(row["ID"], CultureInfo.InvariantCulture));
+ }
+ }
+
+ // Fetch all the unique tables and catalogs used by the current statement
+ using (DataTable schema = reader.GetSchemaTable(false, false))
+ {
+ foreach (DataRow row in schema.Rows)
+ {
+ // Check if column is backed to a table
+ if (row[SchemaTableOptionalColumn.BaseCatalogName] == DBNull.Value)
+ continue;
+
+ // Record the unique table so we can look up its keys
+ string catalog = (string)row[SchemaTableOptionalColumn.BaseCatalogName];
+ string table = (string)row[SchemaTableColumn.BaseTableName];
+
+ if (tables.ContainsKey(catalog) == false)
+ {
+ list = new List();
+ tables.Add(catalog, list);
+ }
+ else
+ list = tables[catalog];
+
+ if (list.Contains(table) == false)
+ list.Add(table);
+ }
+
+ // For each catalog and each table, query the indexes for the table.
+ // Find a primary key index if there is one. If not, find a unique index instead
+ foreach (KeyValuePair> pair in tables)
+ {
+ for (int i = 0; i < pair.Value.Count; i++)
+ {
+ string table = pair.Value[i];
+ DataRow preferredRow = null;
+ using (DataTable tbl = cnn.GetSchema("Indexes", new string[] { pair.Key, null, table }))
+ {
+ // Loop twice. The first time looking for a primary key index,
+ // the second time looking for a unique index
+ for (int n = 0; n < 2 && preferredRow == null; n++)
+ {
+ foreach (DataRow row in tbl.Rows)
+ {
+ if (n == 0 && (bool)row["PRIMARY_KEY"] == true)
+ {
+ preferredRow = row;
+ break;
+ }
+ else if (n == 1 && (bool)row["UNIQUE"] == true)
+ {
+ preferredRow = row;
+ break;
+ }
+ }
+ }
+ if (preferredRow == null) // Unable to find any suitable index for this table so remove it
+ {
+ pair.Value.RemoveAt(i);
+ i--;
+ }
+ else // We found a usable index, so fetch the necessary table details
+ {
+ using (DataTable tblTables = cnn.GetSchema("Tables", new string[] { pair.Key, null, table }))
+ {
+ // Find the root page of the table in the current statement and get the cursor that's iterating it
+ int database = catalogs[pair.Key];
+ int rootPage = Convert.ToInt32(tblTables.Rows[0]["TABLE_ROOTPAGE"], CultureInfo.InvariantCulture);
+ int cursor = stmt._sql.GetCursorForTable(stmt, database, rootPage);
+
+ // Now enumerate the members of the index we're going to use
+ using (DataTable indexColumns = cnn.GetSchema("IndexColumns", new string[] { pair.Key, null, table, (string)preferredRow["INDEX_NAME"] }))
+ {
+ //
+ // NOTE: If this is actually a RowId (or alias), record that now. There should
+ // be exactly one index column in that case.
+ //
+ bool isRowId = (string)preferredRow["INDEX_NAME"] == "sqlite_master_PK_" + table;
+ KeyQuery query = null;
+
+ List cols = new List();
+ for (int x = 0; x < indexColumns.Rows.Count; x++)
+ {
+ string columnName = SQLiteConvert.GetStringOrNull(
+ indexColumns.Rows[x]["COLUMN_NAME"]);
+
+ bool addKey = true;
+ // If the column in the index already appears in the query, skip it
+ foreach (DataRow row in schema.Rows)
+ {
+ if (row.IsNull(SchemaTableColumn.BaseColumnName))
+ continue;
+
+ if ((string)row[SchemaTableColumn.BaseColumnName] == columnName &&
+ (string)row[SchemaTableColumn.BaseTableName] == table &&
+ (string)row[SchemaTableOptionalColumn.BaseCatalogName] == pair.Key)
+ {
+ if (isRowId)
+ {
+ RowIdInfo rowId = new RowIdInfo();
+
+ rowId.databaseName = pair.Key;
+ rowId.tableName = table;
+ rowId.column = (int)row[SchemaTableColumn.ColumnOrdinal];
+
+ rowIds.Add(rowId);
+ }
+ indexColumns.Rows.RemoveAt(x);
+ x--;
+ addKey = false;
+ break;
+ }
+ }
+ if (addKey == true)
+ cols.Add(columnName);
+ }
+
+ // If the index is not a rowid alias, record all the columns
+ // needed to make up the unique index and construct a SQL query for it
+ if (!isRowId)
+ {
+ // Whatever remains of the columns we need that make up the index that are not
+ // already in the query need to be queried separately, so construct a subquery
+ if (cols.Count > 0)
+ {
+ string[] querycols = new string[cols.Count];
+ cols.CopyTo(querycols);
+ query = new KeyQuery(cnn, pair.Key, table, querycols);
+ }
+ }
+
+ // Create a KeyInfo struct for each column of the index
+ for (int x = 0; x < indexColumns.Rows.Count; x++)
+ {
+ string columnName = SQLiteConvert.GetStringOrNull(indexColumns.Rows[x]["COLUMN_NAME"]);
+ KeyInfo key = new KeyInfo();
+
+ key.rootPage = rootPage;
+ key.cursor = cursor;
+ key.database = database;
+ key.databaseName = pair.Key;
+ key.tableName = table;
+ key.columnName = columnName;
+ key.query = query;
+ key.column = x;
+
+ keys.Add(key);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Now we have all the additional columns we have to return in order to support
+ // CommandBehavior.KeyInfo
+ _keyInfo = new KeyInfo[keys.Count];
+ keys.CopyTo(_keyInfo);
+
+ _rowIdInfo = new RowIdInfo[rowIds.Count];
+ rowIds.CopyTo(_rowIdInfo);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ internal int GetRowIdIndex(
+ string databaseName,
+ string tableName
+ )
+ {
+ if ((_rowIdInfo != null) &&
+ (databaseName != null) &&
+ (tableName != null))
+ {
+ for (int i = 0; i < _rowIdInfo.Length; i++)
+ {
+ if (_rowIdInfo[i].databaseName == databaseName &&
+ _rowIdInfo[i].tableName == tableName)
+ {
+ return _rowIdInfo[i].column;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ internal long? GetRowId(
+ string databaseName,
+ string tableName
+ )
+ {
+ if ((_keyInfo != null) &&
+ (databaseName != null) &&
+ (tableName != null))
+ {
+ for (int i = 0; i < _keyInfo.Length; i++)
+ {
+ if (_keyInfo[i].databaseName == databaseName &&
+ _keyInfo[i].tableName == tableName)
+ {
+ long rowid = _stmt._sql.GetRowIdForCursor(_stmt, _keyInfo[i].cursor);
+
+ if (rowid != 0)
+ return rowid;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteKeyReader).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ _stmt = null;
+
+ if (_keyInfo != null)
+ {
+ for (int n = 0; n < _keyInfo.Length; n++)
+ {
+ if (_keyInfo[n].query != null)
+ _keyInfo[n].query.Dispose();
+ }
+
+ _keyInfo = null;
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ~SQLiteKeyReader()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// How many additional columns of keyinfo we're holding
+ ///
+ internal int Count
+ {
+ get { return (_keyInfo == null) ? 0 : _keyInfo.Length; }
+ }
+
+ private void Sync(int i)
+ {
+ Sync();
+ if (_keyInfo[i].cursor == -1)
+ throw new InvalidCastException();
+ }
+
+ ///
+ /// Make sure all the subqueries are open and ready and sync'd with the current rowid
+ /// of the table they're supporting
+ ///
+ private void Sync()
+ {
+ if (_isValid == true) return;
+
+ KeyQuery last = null;
+
+ for (int n = 0; n < _keyInfo.Length; n++)
+ {
+ if (_keyInfo[n].query == null || _keyInfo[n].query != last)
+ {
+ last = _keyInfo[n].query;
+
+ if (last != null)
+ {
+ last.Sync(_stmt._sql.GetRowIdForCursor(_stmt, _keyInfo[n].cursor));
+ }
+ }
+ }
+ _isValid = true;
+ }
+
+ ///
+ /// Release any readers on any subqueries
+ ///
+ internal void Reset()
+ {
+ _isValid = false;
+ if (_keyInfo == null) return;
+
+ for (int n = 0; n < _keyInfo.Length; n++)
+ {
+ if (_keyInfo[n].query != null)
+ _keyInfo[n].query.IsValid = false;
+ }
+ }
+
+ internal string GetDataTypeName(int i)
+ {
+ Sync();
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetDataTypeName(_keyInfo[i].column);
+ else return "integer";
+ }
+
+ internal TypeAffinity GetFieldAffinity(int i)
+ {
+ Sync();
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetFieldAffinity(_keyInfo[i].column);
+ else return TypeAffinity.Uninitialized;
+ }
+
+ internal Type GetFieldType(int i)
+ {
+ Sync();
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetFieldType(_keyInfo[i].column);
+ else return typeof(Int64);
+ }
+
+ internal string GetDatabaseName(int i)
+ {
+ return _keyInfo[i].databaseName;
+ }
+
+ internal string GetTableName(int i)
+ {
+ return _keyInfo[i].tableName;
+ }
+
+ internal string GetName(int i)
+ {
+ return _keyInfo[i].columnName;
+ }
+
+ internal int GetOrdinal(string name)
+ {
+ for (int n = 0; n < _keyInfo.Length; n++)
+ {
+ if (String.Compare(name, _keyInfo[n].columnName, StringComparison.OrdinalIgnoreCase) == 0) return n;
+ }
+ return -1;
+ }
+
+ internal SQLiteBlob GetBlob(int i, bool readOnly)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetBlob(_keyInfo[i].column, readOnly);
+ else throw new InvalidCastException();
+ }
+
+ internal bool GetBoolean(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetBoolean(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal byte GetByte(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetByte(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetBytes(_keyInfo[i].column, fieldOffset, buffer, bufferoffset, length);
+ else throw new InvalidCastException();
+ }
+
+ internal char GetChar(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetChar(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal long GetChars(int i, long fieldOffset, char[] buffer, int bufferoffset, int length)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetChars(_keyInfo[i].column, fieldOffset, buffer, bufferoffset, length);
+ else throw new InvalidCastException();
+ }
+
+ internal DateTime GetDateTime(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetDateTime(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal decimal GetDecimal(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetDecimal(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal double GetDouble(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetDouble(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal float GetFloat(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetFloat(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal Guid GetGuid(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetGuid(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal Int16 GetInt16(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetInt16(_keyInfo[i].column);
+ else
+ {
+ long rowid = _stmt._sql.GetRowIdForCursor(_stmt, _keyInfo[i].cursor);
+ if (rowid == 0) throw new InvalidCastException();
+ return Convert.ToInt16(rowid);
+ }
+ }
+
+ internal Int32 GetInt32(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetInt32(_keyInfo[i].column);
+ else
+ {
+ long rowid = _stmt._sql.GetRowIdForCursor(_stmt, _keyInfo[i].cursor);
+ if (rowid == 0) throw new InvalidCastException();
+ return Convert.ToInt32(rowid);
+ }
+ }
+
+ internal Int64 GetInt64(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetInt64(_keyInfo[i].column);
+ else
+ {
+ long rowid = _stmt._sql.GetRowIdForCursor(_stmt, _keyInfo[i].cursor);
+ if (rowid == 0) throw new InvalidCastException();
+ return rowid;
+ }
+ }
+
+ internal string GetString(int i)
+ {
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetString(_keyInfo[i].column);
+ else throw new InvalidCastException();
+ }
+
+ internal object GetValue(int i)
+ {
+ if (_keyInfo[i].cursor == -1) return DBNull.Value;
+
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.GetValue(_keyInfo[i].column);
+
+ if (IsDBNull(i) == true)
+ return DBNull.Value;
+ else return GetInt64(i);
+ }
+
+ internal bool IsDBNull(int i)
+ {
+ if (_keyInfo[i].cursor == -1) return true;
+
+ Sync(i);
+ if (_keyInfo[i].query != null) return _keyInfo[i].query._reader.IsDBNull(_keyInfo[i].column);
+ else return _stmt._sql.GetRowIdForCursor(_stmt, _keyInfo[i].cursor) == 0;
+ }
+
+ ///
+ /// Append all the columns we've added to the original query to the schema
+ ///
+ ///
+ internal void AppendSchemaTable(DataTable tbl)
+ {
+ KeyQuery last = null;
+
+ for (int n = 0; n < _keyInfo.Length; n++)
+ {
+ if (_keyInfo[n].query == null || _keyInfo[n].query != last)
+ {
+ last = _keyInfo[n].query;
+
+ if (last == null) // ROWID aliases are treated special
+ {
+ DataRow row = tbl.NewRow();
+ row[SchemaTableColumn.ColumnName] = _keyInfo[n].columnName;
+ row[SchemaTableColumn.ColumnOrdinal] = tbl.Rows.Count;
+ row[SchemaTableColumn.ColumnSize] = 8;
+ row[SchemaTableColumn.NumericPrecision] = 255;
+ row[SchemaTableColumn.NumericScale] = 255;
+ row[SchemaTableColumn.ProviderType] = DbType.Int64;
+ row[SchemaTableColumn.IsLong] = false;
+ row[SchemaTableColumn.AllowDBNull] = false;
+ row[SchemaTableOptionalColumn.IsReadOnly] = false;
+ row[SchemaTableOptionalColumn.IsRowVersion] = false;
+ row[SchemaTableColumn.IsUnique] = false;
+ row[SchemaTableColumn.IsKey] = true;
+ row[SchemaTableColumn.DataType] = typeof(Int64);
+ row[SchemaTableOptionalColumn.IsHidden] = true;
+ row[SchemaTableColumn.BaseColumnName] = _keyInfo[n].columnName;
+ row[SchemaTableColumn.IsExpression] = false;
+ row[SchemaTableColumn.IsAliased] = false;
+ row[SchemaTableColumn.BaseTableName] = _keyInfo[n].tableName;
+ row[SchemaTableOptionalColumn.BaseCatalogName] = _keyInfo[n].databaseName;
+ row[SchemaTableOptionalColumn.IsAutoIncrement] = true;
+ row["DataTypeName"] = "integer";
+
+ tbl.Rows.Add(row);
+ }
+ else
+ {
+ last.Sync(0);
+ using (DataTable tblSub = last._reader.GetSchemaTable())
+ {
+ foreach (DataRow row in tblSub.Rows)
+ {
+ object[] o = row.ItemArray;
+ DataRow newrow = tbl.Rows.Add(o);
+ newrow[SchemaTableOptionalColumn.IsHidden] = true;
+ newrow[SchemaTableColumn.ColumnOrdinal] = tbl.Rows.Count - 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteLog.cs b/Native.Csharp.Tool/SQLite/SQLiteLog.cs
new file mode 100644
index 00000000..6cca81ec
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteLog.cs
@@ -0,0 +1,652 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data.Common;
+ using System.Diagnostics;
+ using System.Globalization;
+ using System.Threading;
+
+ ///
+ /// Event data for logging event handlers.
+ ///
+ public class LogEventArgs : EventArgs
+ {
+ ///
+ /// The error code. The type of this object value should be
+ /// or .
+ ///
+ public readonly object ErrorCode;
+
+ ///
+ /// SQL statement text as the statement first begins executing
+ ///
+ public readonly string Message;
+
+ ///
+ /// Extra data associated with this event, if any.
+ ///
+ public readonly object Data;
+
+ ///
+ /// Constructs the object.
+ ///
+ /// Should be null.
+ ///
+ /// The error code. The type of this object value should be
+ /// or .
+ ///
+ /// The error message, if any.
+ /// The extra data, if any.
+ internal LogEventArgs(
+ IntPtr pUserData,
+ object errorCode,
+ string message,
+ object data
+ )
+ {
+ ErrorCode = errorCode;
+ Message = message;
+ Data = data;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Raised when a log event occurs.
+ ///
+ /// The current connection
+ /// Event arguments of the trace
+ public delegate void SQLiteLogEventHandler(object sender, LogEventArgs e);
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Manages the SQLite custom logging functionality and the associated
+ /// callback for the whole process.
+ ///
+ public static class SQLiteLog
+ {
+ ///
+ /// Object used to synchronize access to the static instance data
+ /// for this class.
+ ///
+ private static object syncRoot = new object();
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Member variable to store the AppDomain.DomainUnload event handler.
+ ///
+ private static EventHandler _domainUnload;
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Member variable to store the application log handler to call.
+ ///
+ private static event SQLiteLogEventHandler _handlers;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The default log event handler.
+ ///
+ private static SQLiteLogEventHandler _defaultHandler;
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if !USE_INTEROP_DLL || !INTEROP_LOG
+ ///
+ /// The log callback passed to native SQLite engine. This must live
+ /// as long as the SQLite library has a pointer to it.
+ ///
+ private static SQLiteLogCallback _callback;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The base SQLite object to interop with.
+ ///
+ private static SQLiteBase _sql;
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This will be non-zero if an attempt was already made to initialize
+ /// the (managed) logging subsystem.
+ ///
+ private static int _attemptedInitialize;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This will be non-zero if logging is currently enabled.
+ ///
+ private static bool _enabled;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Initializes the SQLite logging facilities.
+ ///
+ public static void Initialize()
+ {
+ Initialize(null);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Initializes the SQLite logging facilities.
+ ///
+ ///
+ /// The name of the managed class that called this method. This
+ /// parameter may be null.
+ ///
+ internal static void Initialize(
+ string className
+ )
+ {
+ //
+ // NOTE: First, check if the managed logging subsystem is always
+ // supposed to at least attempt to initialize itself. In
+ // order to do this, several fairly complex steps must be
+ // taken, including calling a P/Invoke (interop) method;
+ // therefore, by default, attempt to perform these steps
+ // once.
+ //
+ if (UnsafeNativeMethods.GetSettingValue(
+ "Initialize_SQLiteLog", null) == null)
+ {
+ if (Interlocked.Increment(ref _attemptedInitialize) > 1)
+ {
+ Interlocked.Decrement(ref _attemptedInitialize);
+ return;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ //
+ // BUFXIX: We cannot initialize the logging interface if the SQLite
+ // core library has already been initialized anywhere in
+ // the process (see ticket [2ce0870fad]).
+ //
+ if (SQLite3.StaticIsInitialized())
+ return;
+
+ ///////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // BUGFIX: To avoid nasty situations where multiple AppDomains are
+ // attempting to initialize and/or shutdown what is really
+ // a shared native resource (i.e. the SQLite core library
+ // is loaded per-process and has only one logging callback,
+ // not one per-AppDomain, which it knows nothing about),
+ // prevent all non-default AppDomains from registering a
+ // log handler unless the "Force_SQLiteLog" environment
+ // variable is used to manually override this safety check.
+ //
+ if (!AppDomain.CurrentDomain.IsDefaultAppDomain() &&
+ UnsafeNativeMethods.GetSettingValue("Force_SQLiteLog", null) == null)
+ {
+ return;
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////
+
+ lock (syncRoot)
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // NOTE: Add an event handler for the DomainUnload event so
+ // that we can unhook our logging managed function
+ // pointer from the native SQLite code prior to it
+ // being invalidated.
+ //
+ // BUGFIX: Make sure this event handler is only added one
+ // time (per-AppDomain).
+ //
+ if (_domainUnload == null)
+ {
+ _domainUnload = new EventHandler(DomainUnload);
+ AppDomain.CurrentDomain.DomainUnload += _domainUnload;
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////
+
+#if USE_INTEROP_DLL && INTEROP_LOG
+ //
+ // NOTE: Attempt to setup interop assembly log callback.
+ // This may fail, e.g. if the SQLite core library
+ // has somehow been initialized. An exception will
+ // be raised in that case.
+ //
+ SQLiteErrorCode rc = SQLite3.ConfigureLogForInterop(
+ className);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ throw new SQLiteException(rc,
+ "Failed to configure interop assembly logging.");
+ }
+#else
+ //
+ // NOTE: Create an instance of the SQLite wrapper class.
+ //
+ if (_sql == null)
+ {
+ _sql = new SQLite3(
+ SQLiteDateFormats.Default, DateTimeKind.Unspecified,
+ null, IntPtr.Zero, null, false);
+ }
+
+ //
+ // NOTE: Create a single "global" (i.e. per-process) callback
+ // to register with SQLite. This callback will pass the
+ // event on to any registered handler. We only want to
+ // do this once.
+ //
+ if (_callback == null)
+ {
+ _callback = new SQLiteLogCallback(LogCallback);
+
+ SQLiteErrorCode rc = _sql.SetLogCallback(_callback);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ throw new SQLiteException(rc,
+ "Failed to configure managed assembly logging.");
+ }
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////
+
+ //
+ // NOTE: Logging is enabled by default unless the configuration
+ // setting "Disable_SQLiteLog" is present.
+ //
+ if (UnsafeNativeMethods.GetSettingValue(
+ "Disable_SQLiteLog", null) == null)
+ {
+ _enabled = true;
+ }
+
+ ///////////////////////////////////////////////////////////////
+
+ //
+ // NOTE: For now, always setup the default log event handler.
+ //
+ AddDefaultHandler();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Handles the AppDomain being unloaded.
+ ///
+ /// Should be null.
+ /// The data associated with this event.
+ private static void DomainUnload(
+ object sender,
+ EventArgs e
+ )
+ {
+ lock (syncRoot)
+ {
+ //
+ // NOTE: Remove the default log event handler.
+ //
+ RemoveDefaultHandler();
+
+ //
+ // NOTE: Disable logging. If necessary, it can be re-enabled
+ // later by the Initialize method.
+ //
+ _enabled = false;
+
+#if !USE_INTEROP_DLL || !INTEROP_LOG
+ //
+ // BUGBUG: This will cause serious problems if other AppDomains
+ // have any open SQLite connections; however, there is
+ // currently no way around this limitation.
+ //
+ if (_sql != null)
+ {
+ SQLiteErrorCode rc = _sql.Shutdown();
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc,
+ "Failed to shutdown interface.");
+
+ rc = _sql.SetLogCallback(null);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc,
+ "Failed to shutdown logging.");
+ }
+
+ //
+ // BUGFIX: Make sure to reset the callback for next time. This
+ // must be done after it has been succesfully removed
+ // as logging callback by the SQLite core library as we
+ // cannot allow native code to refer to a delegate that
+ // has been garbage collected.
+ //
+ if (_callback != null)
+ {
+ _callback = null;
+ }
+#endif
+
+ //
+ // NOTE: Remove the event handler for the DomainUnload event
+ // that we added earlier.
+ //
+ if (_domainUnload != null)
+ {
+ AppDomain.CurrentDomain.DomainUnload -= _domainUnload;
+ _domainUnload = null;
+ }
+ }
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This event is raised whenever SQLite raises a logging event.
+ /// Note that this should be set as one of the first things in the
+ /// application.
+ ///
+ public static event SQLiteLogEventHandler Log
+ {
+ add
+ {
+ lock (syncRoot)
+ {
+ // Remove any copies of this event handler from registered
+ // list. This essentially means that a handler will be
+ // called only once no matter how many times it is added.
+ _handlers -= value;
+
+ // Add this to the list of event handlers.
+ _handlers += value;
+ }
+ }
+ remove
+ {
+ lock (syncRoot)
+ {
+ _handlers -= value;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// If this property is true, logging is enabled; otherwise, logging is
+ /// disabled. When logging is disabled, no logging events will fire.
+ ///
+ public static bool Enabled
+ {
+ get { lock (syncRoot) { return _enabled; } }
+ set { lock (syncRoot) { _enabled = value; } }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Log a message to all the registered log event handlers without going
+ /// through the SQLite library.
+ ///
+ /// The message to be logged.
+ public static void LogMessage(
+ string message
+ )
+ {
+ LogMessage(null, message);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Log a message to all the registered log event handlers without going
+ /// through the SQLite library.
+ ///
+ /// The SQLite error code.
+ /// The message to be logged.
+ public static void LogMessage(
+ SQLiteErrorCode errorCode,
+ string message
+ )
+ {
+ LogMessage((object)errorCode, message);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Log a message to all the registered log event handlers without going
+ /// through the SQLite library.
+ ///
+ /// The integer error code.
+ /// The message to be logged.
+ public static void LogMessage(
+ int errorCode,
+ string message
+ )
+ {
+ LogMessage((object)errorCode, message);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Log a message to all the registered log event handlers without going
+ /// through the SQLite library.
+ ///
+ ///
+ /// The error code. The type of this object value should be
+ /// System.Int32 or SQLiteErrorCode.
+ ///
+ /// The message to be logged.
+ private static void LogMessage(
+ object errorCode,
+ string message
+ )
+ {
+ bool enabled;
+ SQLiteLogEventHandler handlers;
+
+ lock (syncRoot)
+ {
+ enabled = _enabled;
+
+ if (_handlers != null)
+ handlers = _handlers.Clone() as SQLiteLogEventHandler;
+ else
+ handlers = null;
+ }
+
+ if (enabled && (handlers != null))
+ handlers(null, new LogEventArgs(
+ IntPtr.Zero, errorCode, message, null));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates and initializes the default log event handler.
+ ///
+ private static void InitializeDefaultHandler()
+ {
+ lock (syncRoot)
+ {
+ if (_defaultHandler == null)
+ _defaultHandler = new SQLiteLogEventHandler(LogEventHandler);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adds the default log event handler to the list of handlers.
+ ///
+ public static void AddDefaultHandler()
+ {
+ InitializeDefaultHandler();
+ Log += _defaultHandler;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Removes the default log event handler from the list of handlers.
+ ///
+ public static void RemoveDefaultHandler()
+ {
+ InitializeDefaultHandler();
+ Log -= _defaultHandler;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Internal proxy function that calls any registered application log
+ /// event handlers.
+ ///
+ /// WARNING: This method is used more-or-less directly by native code,
+ /// do not modify its type signature.
+ ///
+ ///
+ /// The extra data associated with this message, if any.
+ ///
+ ///
+ /// The error code associated with this message.
+ ///
+ ///
+ /// The message string to be logged.
+ ///
+ private static void LogCallback(
+ IntPtr pUserData,
+ int errorCode,
+ IntPtr pMessage
+ )
+ {
+ bool enabled;
+ SQLiteLogEventHandler handlers;
+
+ lock (syncRoot)
+ {
+ enabled = _enabled;
+
+ if (_handlers != null)
+ handlers = _handlers.Clone() as SQLiteLogEventHandler;
+ else
+ handlers = null;
+ }
+
+ if (enabled && (handlers != null))
+ handlers(null, new LogEventArgs(pUserData, errorCode,
+ SQLiteBase.UTF8ToString(pMessage, -1), null));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Default logger. Currently, uses the Trace class (i.e. sends events
+ /// to the current trace listeners, if any).
+ ///
+ /// Should be null.
+ /// The data associated with this event.
+ private static void LogEventHandler(
+ object sender,
+ LogEventArgs e
+ )
+ {
+#if !NET_COMPACT_20
+ if (e == null)
+ return;
+
+ string message = e.Message;
+
+ if (message == null)
+ {
+ message = "";
+ }
+ else
+ {
+ message = message.Trim();
+
+ if (message.Length == 0)
+ message = "";
+ }
+
+ object errorCode = e.ErrorCode;
+ string type = "error";
+
+ if ((errorCode is SQLiteErrorCode) || (errorCode is int))
+ {
+ SQLiteErrorCode rc = (SQLiteErrorCode)(int)errorCode;
+
+ rc &= SQLiteErrorCode.NonExtendedMask;
+
+ if (rc == SQLiteErrorCode.Ok)
+ {
+ type = "message";
+ }
+ else if (rc == SQLiteErrorCode.Notice)
+ {
+ type = "notice";
+ }
+ else if (rc == SQLiteErrorCode.Warning)
+ {
+ type = "warning";
+ }
+ else if ((rc == SQLiteErrorCode.Row) ||
+ (rc == SQLiteErrorCode.Done))
+ {
+ type = "data";
+ }
+ }
+ else if (errorCode == null)
+ {
+ type = "trace";
+ }
+
+ if ((errorCode != null) &&
+ !Object.ReferenceEquals(errorCode, String.Empty))
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "SQLite {0} ({1}): {2}",
+ type, errorCode, message));
+ }
+ else
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "SQLite {0}: {1}",
+ type, message));
+ }
+#endif
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteMetaDataCollectionNames.cs b/Native.Csharp.Tool/SQLite/SQLiteMetaDataCollectionNames.cs
new file mode 100644
index 00000000..e1c21341
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteMetaDataCollectionNames.cs
@@ -0,0 +1,52 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ ///
+ /// MetaDataCollections specific to SQLite
+ ///
+ public static class SQLiteMetaDataCollectionNames
+ {
+ ///
+ /// Returns a list of databases attached to the connection
+ ///
+ public static readonly string Catalogs = "Catalogs";
+ ///
+ /// Returns column information for the specified table
+ ///
+ public static readonly string Columns = "Columns";
+ ///
+ /// Returns index information for the optionally-specified table
+ ///
+ public static readonly string Indexes = "Indexes";
+ ///
+ /// Returns base columns for the given index
+ ///
+ public static readonly string IndexColumns = "IndexColumns";
+ ///
+ /// Returns the tables in the given catalog
+ ///
+ public static readonly string Tables = "Tables";
+ ///
+ /// Returns user-defined views in the given catalog
+ ///
+ public static readonly string Views = "Views";
+ ///
+ /// Returns underlying column information on the given view
+ ///
+ public static readonly string ViewColumns = "ViewColumns";
+ ///
+ /// Returns foreign key information for the given catalog
+ ///
+ public static readonly string ForeignKeys = "ForeignKeys";
+ ///
+ /// Returns the triggers on the database
+ ///
+ public static readonly string Triggers = "Triggers";
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteModule.cs b/Native.Csharp.Tool/SQLite/SQLiteModule.cs
new file mode 100644
index 00000000..94489670
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteModule.cs
@@ -0,0 +1,8765 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Collections.Generic;
+using System.Globalization;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+using System.Runtime.CompilerServices;
+#endif
+
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System.Data.SQLite
+{
+ #region SQLiteContext Helper Class
+ ///
+ /// This class represents a context from the SQLite core library that can
+ /// be passed to the sqlite3_result_*() and associated functions.
+ ///
+ public sealed class SQLiteContext : ISQLiteNativeHandle
+ {
+ #region Private Data
+ ///
+ /// The native context handle.
+ ///
+ private IntPtr pContext;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// context handle.
+ ///
+ ///
+ /// The native context handle to use.
+ ///
+ internal SQLiteContext(IntPtr pContext)
+ {
+ this.pContext = pContext;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeHandle Members
+ ///
+ /// Returns the underlying SQLite native handle associated with this
+ /// object instance.
+ ///
+ public IntPtr NativeHandle
+ {
+ get { return pContext; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Sets the context result to NULL.
+ ///
+ public void SetNull()
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ UnsafeNativeMethods.sqlite3_result_null(pContext);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified
+ /// value.
+ ///
+ ///
+ /// The value to use.
+ ///
+ public void SetDouble(double value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ UnsafeNativeMethods.sqlite3_result_double(pContext, value);
+#elif !SQLITE_STANDARD
+ UnsafeNativeMethods.sqlite3_result_double_interop(pContext, ref value);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified
+ /// value.
+ ///
+ ///
+ /// The value to use.
+ ///
+ public void SetInt(int value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ UnsafeNativeMethods.sqlite3_result_int(pContext, value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified
+ /// value.
+ ///
+ ///
+ /// The value to use.
+ ///
+ public void SetInt64(long value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ UnsafeNativeMethods.sqlite3_result_int64(pContext, value);
+#elif !SQLITE_STANDARD
+ UnsafeNativeMethods.sqlite3_result_int64_interop(pContext, ref value);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified
+ /// value.
+ ///
+ ///
+ /// The value to use. This value will be
+ /// converted to the UTF-8 encoding prior to being used.
+ ///
+ public void SetString(string value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ byte[] bytes = SQLiteString.GetUtf8BytesFromString(value);
+
+ if (bytes == null)
+ throw new ArgumentNullException("value");
+
+ UnsafeNativeMethods.sqlite3_result_text(
+ pContext, bytes, bytes.Length, (IntPtr)(-1));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified
+ /// value containing an error message.
+ ///
+ ///
+ /// The value containing the error message text.
+ /// This value will be converted to the UTF-8 encoding prior to being
+ /// used.
+ ///
+ public void SetError(string value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ byte[] bytes = SQLiteString.GetUtf8BytesFromString(value);
+
+ if (bytes == null)
+ throw new ArgumentNullException("value");
+
+ UnsafeNativeMethods.sqlite3_result_error(
+ pContext, bytes, bytes.Length);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified
+ /// value.
+ ///
+ ///
+ /// The value to use.
+ ///
+ public void SetErrorCode(SQLiteErrorCode value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ UnsafeNativeMethods.sqlite3_result_error_code(pContext, value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to contain the error code SQLITE_TOOBIG.
+ ///
+ public void SetErrorTooBig()
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ UnsafeNativeMethods.sqlite3_result_error_toobig(pContext);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to contain the error code SQLITE_NOMEM.
+ ///
+ public void SetErrorNoMemory()
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ UnsafeNativeMethods.sqlite3_result_error_nomem(pContext);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified array
+ /// value.
+ ///
+ ///
+ /// The array value to use.
+ ///
+ public void SetBlob(byte[] value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ UnsafeNativeMethods.sqlite3_result_blob(
+ pContext, value, value.Length, (IntPtr)(-1));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to a BLOB of zeros of the specified size.
+ ///
+ ///
+ /// The number of zero bytes to use for the BLOB context result.
+ ///
+ public void SetZeroBlob(int value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ UnsafeNativeMethods.sqlite3_result_zeroblob(pContext, value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the context result to the specified .
+ ///
+ ///
+ /// The to use.
+ ///
+ public void SetValue(SQLiteValue value)
+ {
+ if (pContext == IntPtr.Zero)
+ throw new InvalidOperationException();
+
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ UnsafeNativeMethods.sqlite3_result_value(
+ pContext, value.NativeHandle);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteValue Helper Class
+ ///
+ /// This class represents a value from the SQLite core library that can be
+ /// passed to the sqlite3_value_*() and associated functions.
+ ///
+ public sealed class SQLiteValue : ISQLiteNativeHandle
+ {
+ #region Private Data
+ ///
+ /// The native value handle.
+ ///
+ private IntPtr pValue;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// value handle.
+ ///
+ ///
+ /// The native value handle to use.
+ ///
+ private SQLiteValue(IntPtr pValue)
+ {
+ this.pValue = pValue;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Invalidates the native value handle, thereby preventing further
+ /// access to it from this object instance.
+ ///
+ private void PreventNativeAccess()
+ {
+ pValue = IntPtr.Zero;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Internal Marshal Helper Methods
+ ///
+ /// Converts a native pointer to a native sqlite3_value structure into
+ /// a managed object instance.
+ ///
+ ///
+ /// The native pointer to a native sqlite3_value structure to convert.
+ ///
+ ///
+ /// The managed object instance or null upon
+ /// failure.
+ ///
+ internal static SQLiteValue FromIntPtr(
+ IntPtr pValue
+ )
+ {
+ if (pValue == IntPtr.Zero) return null;
+ return new SQLiteValue(pValue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a logical array of native pointers to native sqlite3_value
+ /// structures into a managed array of
+ /// object instances.
+ ///
+ ///
+ /// The number of elements in the logical array of native sqlite3_value
+ /// structures.
+ ///
+ ///
+ /// The native pointer to the logical array of native sqlite3_value
+ /// structures to convert.
+ ///
+ ///
+ /// The managed array of object instances or
+ /// null upon failure.
+ ///
+ internal static SQLiteValue[] ArrayFromSizeAndIntPtr(
+ int argc,
+ IntPtr argv
+ )
+ {
+ if (argc < 0)
+ return null;
+
+ if (argv == IntPtr.Zero)
+ return null;
+
+ SQLiteValue[] result = new SQLiteValue[argc];
+
+ for (int index = 0, offset = 0;
+ index < result.Length;
+ index++, offset += IntPtr.Size)
+ {
+ IntPtr pArg = SQLiteMarshal.ReadIntPtr(argv, offset);
+
+ result[index] = (pArg != IntPtr.Zero) ?
+ new SQLiteValue(pArg) : null;
+ }
+
+ return result;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeHandle Members
+ ///
+ /// Returns the underlying SQLite native handle associated with this
+ /// object instance.
+ ///
+ public IntPtr NativeHandle
+ {
+ get { return pValue; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private bool persisted;
+ ///
+ /// Returns non-zero if the native SQLite value has been successfully
+ /// persisted as a managed value within this object instance (i.e. the
+ /// property may then be read successfully).
+ ///
+ public bool Persisted
+ {
+ get { return persisted; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private object value;
+ ///
+ /// If the managed value for this object instance is available (i.e. it
+ /// has been previously persisted via the ) method,
+ /// that value is returned; otherwise, an exception is thrown. The
+ /// returned value may be null.
+ ///
+ public object Value
+ {
+ get
+ {
+ if (!persisted)
+ {
+ throw new InvalidOperationException(
+ "value was not persisted");
+ }
+
+ return value;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Gets and returns the type affinity associated with this value.
+ ///
+ ///
+ /// The type affinity associated with this value.
+ ///
+ public TypeAffinity GetTypeAffinity()
+ {
+ if (pValue == IntPtr.Zero) return TypeAffinity.None;
+ return UnsafeNativeMethods.sqlite3_value_type(pValue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the number of bytes associated with this value, if
+ /// it refers to a UTF-8 encoded string.
+ ///
+ ///
+ /// The number of bytes associated with this value. The returned value
+ /// may be zero.
+ ///
+ public int GetBytes()
+ {
+ if (pValue == IntPtr.Zero) return 0;
+ return UnsafeNativeMethods.sqlite3_value_bytes(pValue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the associated with this
+ /// value.
+ ///
+ ///
+ /// The associated with this value.
+ ///
+ public int GetInt()
+ {
+ if (pValue == IntPtr.Zero) return default(int);
+ return UnsafeNativeMethods.sqlite3_value_int(pValue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the associated with
+ /// this value.
+ ///
+ ///
+ /// The associated with this value.
+ ///
+ public long GetInt64()
+ {
+ if (pValue == IntPtr.Zero) return default(long);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_value_int64(pValue);
+#elif !SQLITE_STANDARD
+ long value = 0;
+ UnsafeNativeMethods.sqlite3_value_int64_interop(pValue, ref value);
+ return value;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the associated with this
+ /// value.
+ ///
+ ///
+ /// The associated with this value.
+ ///
+ public double GetDouble()
+ {
+ if (pValue == IntPtr.Zero) return default(double);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_value_double(pValue);
+#elif !SQLITE_STANDARD
+ double value = 0.0;
+ UnsafeNativeMethods.sqlite3_value_double_interop(pValue, ref value);
+ return value;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the associated with this
+ /// value.
+ ///
+ ///
+ /// The associated with this value. The value is
+ /// converted from the UTF-8 encoding prior to being returned.
+ ///
+ public string GetString()
+ {
+ if (pValue == IntPtr.Zero) return null;
+
+ int length;
+ IntPtr pString;
+
+#if SQLITE_STANDARD
+ length = UnsafeNativeMethods.sqlite3_value_bytes(pValue);
+ pString = UnsafeNativeMethods.sqlite3_value_text(pValue);
+#else
+ length = 0;
+
+ pString = UnsafeNativeMethods.sqlite3_value_text_interop(
+ pValue, ref length);
+#endif
+
+ return SQLiteString.StringFromUtf8IntPtr(pString, length);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the array associated with this
+ /// value.
+ ///
+ ///
+ /// The array associated with this value.
+ ///
+ public byte[] GetBlob()
+ {
+ if (pValue == IntPtr.Zero) return null;
+
+ return SQLiteBytes.FromIntPtr(
+ UnsafeNativeMethods.sqlite3_value_blob(pValue), GetBytes());
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns an instance associated with
+ /// this value.
+ ///
+ ///
+ /// The associated with this value. If the type
+ /// affinity of the object is unknown or cannot be determined, a null
+ /// value will be returned.
+ ///
+ public object GetObject()
+ {
+ switch (GetTypeAffinity())
+ {
+ case TypeAffinity.Uninitialized:
+ {
+ return null;
+ }
+ case TypeAffinity.Int64:
+ {
+ return GetInt64();
+ }
+ case TypeAffinity.Double:
+ {
+ return GetDouble();
+ }
+ case TypeAffinity.Text:
+ {
+ return GetString();
+ }
+ case TypeAffinity.Blob:
+ {
+ return GetBytes();
+ }
+ case TypeAffinity.Null:
+ {
+ return DBNull.Value;
+ }
+ default:
+ {
+ return null;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Uses the native value handle to obtain and store the managed value
+ /// for this object instance, thus saving it for later use. The type
+ /// of the managed value is determined by the type affinity of the
+ /// native value. If the type affinity is not recognized by this
+ /// method, no work is done and false is returned.
+ ///
+ ///
+ /// Non-zero if the native value was persisted successfully.
+ ///
+ public bool Persist()
+ {
+ switch (GetTypeAffinity())
+ {
+ case TypeAffinity.Uninitialized:
+ {
+ value = null;
+ PreventNativeAccess();
+ return (persisted = true);
+ }
+ case TypeAffinity.Int64:
+ {
+ value = GetInt64();
+ PreventNativeAccess();
+ return (persisted = true);
+ }
+ case TypeAffinity.Double:
+ {
+ value = GetDouble();
+ PreventNativeAccess();
+ return (persisted = true);
+ }
+ case TypeAffinity.Text:
+ {
+ value = GetString();
+ PreventNativeAccess();
+ return (persisted = true);
+ }
+ case TypeAffinity.Blob:
+ {
+ value = GetBytes();
+ PreventNativeAccess();
+ return (persisted = true);
+ }
+ case TypeAffinity.Null:
+ {
+ value = DBNull.Value;
+ PreventNativeAccess();
+ return (persisted = true);
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexConstraintOp Enumeration
+ ///
+ /// These are the allowed values for the operators that are part of a
+ /// constraint term in the WHERE clause of a query that uses a virtual
+ /// table.
+ ///
+ public enum SQLiteIndexConstraintOp : byte
+ {
+ ///
+ /// This value represents the equality operator.
+ ///
+ EqualTo = 2,
+
+ ///
+ /// This value represents the greater than operator.
+ ///
+ GreaterThan = 4,
+
+ ///
+ /// This value represents the less than or equal to operator.
+ ///
+ LessThanOrEqualTo = 8,
+
+ ///
+ /// This value represents the less than operator.
+ ///
+ LessThan = 16,
+
+ ///
+ /// This value represents the greater than or equal to operator.
+ ///
+ GreaterThanOrEqualTo = 32,
+
+ ///
+ /// This value represents the MATCH operator.
+ ///
+ Match = 64,
+
+ ///
+ /// This value represents the LIKE operator.
+ ///
+ Like = 65,
+
+ ///
+ /// This value represents the GLOB operator.
+ ///
+ Glob = 66,
+
+ ///
+ /// This value represents the REGEXP operator.
+ ///
+ Regexp = 67,
+
+ ///
+ /// This value represents the inequality operator.
+ ///
+ NotEqualTo = 68,
+
+ ///
+ /// This value represents the IS NOT operator.
+ ///
+ IsNot = 69,
+
+ ///
+ /// This value represents the IS NOT NULL operator.
+ ///
+ IsNotNull = 70,
+
+ ///
+ /// This value represents the IS NULL operator.
+ ///
+ IsNull = 71,
+
+ ///
+ /// This value represents the IS operator.
+ ///
+ Is = 72
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexFlags Enumeration
+ ///
+ /// These are the allowed values for the index flags from the
+ /// method.
+ ///
+ [Flags()]
+ public enum SQLiteIndexFlags
+ {
+ ///
+ /// No special handling. This is the default.
+ ///
+ None = 0x0,
+
+ ///
+ /// This value indicates that the scan of the index will visit at
+ /// most one row.
+ ///
+ ScanUnique = 0x1
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexConstraint Helper Class
+ ///
+ /// This class represents the native sqlite3_index_constraint structure
+ /// from the SQLite core library.
+ ///
+ public sealed class SQLiteIndexConstraint
+ {
+ #region Internal Constructors
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// sqlite3_index_constraint structure.
+ ///
+ ///
+ /// The native sqlite3_index_constraint structure to use.
+ ///
+ internal SQLiteIndexConstraint(
+ UnsafeNativeMethods.sqlite3_index_constraint constraint
+ )
+ : this(constraint.iColumn, constraint.op, constraint.usable,
+ constraint.iTermOffset)
+ {
+ // do nothing.
+ }
+ #endregion
+
+ //////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified field
+ /// values.
+ ///
+ ///
+ /// Column on left-hand side of constraint.
+ ///
+ ///
+ /// Constraint operator ().
+ ///
+ ///
+ /// True if this constraint is usable.
+ ///
+ ///
+ /// Used internally -
+ /// should ignore.
+ ///
+ private SQLiteIndexConstraint(
+ int iColumn,
+ SQLiteIndexConstraintOp op,
+ byte usable,
+ int iTermOffset
+ )
+ {
+ this.iColumn = iColumn;
+ this.op = op;
+ this.usable = usable;
+ this.iTermOffset = iTermOffset;
+ }
+ #endregion
+
+ //////////////////////////////////////////////////////////////////////
+
+ #region Public Fields
+ ///
+ /// Column on left-hand side of constraint.
+ ///
+ public int iColumn;
+
+ //////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constraint operator ().
+ ///
+ public SQLiteIndexConstraintOp op;
+
+ //////////////////////////////////////////////////////////////////////
+
+ ///
+ /// True if this constraint is usable.
+ ///
+ public byte usable;
+
+ //////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Used internally -
+ /// should ignore.
+ ///
+ public int iTermOffset;
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexOrderBy Helper Class
+ ///
+ /// This class represents the native sqlite3_index_orderby structure from
+ /// the SQLite core library.
+ ///
+ public sealed class SQLiteIndexOrderBy
+ {
+ #region Internal Constructors
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// sqlite3_index_orderby structure.
+ ///
+ ///
+ /// The native sqlite3_index_orderby structure to use.
+ ///
+ internal SQLiteIndexOrderBy(
+ UnsafeNativeMethods.sqlite3_index_orderby orderBy
+ )
+ : this(orderBy.iColumn, orderBy.desc)
+ {
+ // do nothing.
+ }
+ #endregion
+
+ //////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified field
+ /// values.
+ ///
+ ///
+ /// Column number.
+ ///
+ ///
+ /// True for DESC. False for ASC.
+ ///
+ private SQLiteIndexOrderBy(
+ int iColumn,
+ byte desc
+ )
+ {
+ this.iColumn = iColumn;
+ this.desc = desc;
+ }
+ #endregion
+
+ //////////////////////////////////////////////////////////////////////
+
+ #region Public Fields
+ ///
+ /// Column number.
+ ///
+ public int iColumn;
+
+ //////////////////////////////////////////////////////////////////////
+
+ ///
+ /// True for DESC. False for ASC.
+ ///
+ public byte desc;
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexConstraintUsage Helper Class
+ ///
+ /// This class represents the native sqlite3_index_constraint_usage
+ /// structure from the SQLite core library.
+ ///
+ public sealed class SQLiteIndexConstraintUsage
+ {
+ #region Internal Constructors
+ ///
+ /// Constructs a default instance of this class.
+ ///
+ internal SQLiteIndexConstraintUsage()
+ {
+ // do nothing.
+ }
+
+ //////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// sqlite3_index_constraint_usage structure.
+ ///
+ ///
+ /// The native sqlite3_index_constraint_usage structure to use.
+ ///
+ internal SQLiteIndexConstraintUsage(
+ UnsafeNativeMethods.sqlite3_index_constraint_usage constraintUsage
+ )
+ : this(constraintUsage.argvIndex, constraintUsage.omit)
+ {
+ // do nothing.
+ }
+ #endregion
+
+ //////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified field
+ /// values.
+ ///
+ ///
+ /// If greater than 0, constraint is part of argv to xFilter.
+ ///
+ ///
+ /// Do not code a test for this constraint.
+ ///
+ private SQLiteIndexConstraintUsage(
+ int argvIndex,
+ byte omit
+ )
+ {
+ this.argvIndex = argvIndex;
+ this.omit = omit;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Fields
+ ///
+ /// If greater than 0, constraint is part of argv to xFilter.
+ ///
+ public int argvIndex;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Do not code a test for this constraint.
+ ///
+ public byte omit;
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexInputs Helper Class
+ ///
+ /// This class represents the various inputs provided by the SQLite core
+ /// library to the method.
+ ///
+ public sealed class SQLiteIndexInputs
+ {
+ #region Internal Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ internal SQLiteIndexInputs(int nConstraint, int nOrderBy)
+ {
+ constraints = new SQLiteIndexConstraint[nConstraint];
+ orderBys = new SQLiteIndexOrderBy[nOrderBy];
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private SQLiteIndexConstraint[] constraints;
+ ///
+ /// An array of object instances,
+ /// each containing information supplied by the SQLite core library.
+ ///
+ public SQLiteIndexConstraint[] Constraints
+ {
+ get { return constraints; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteIndexOrderBy[] orderBys;
+ ///
+ /// An array of object instances,
+ /// each containing information supplied by the SQLite core library.
+ ///
+ public SQLiteIndexOrderBy[] OrderBys
+ {
+ get { return orderBys; }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndexOutputs Helper Class
+ ///
+ /// This class represents the various outputs provided to the SQLite core
+ /// library by the method.
+ ///
+ public sealed class SQLiteIndexOutputs
+ {
+ #region Internal Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The number of instances
+ /// to pre-allocate space for.
+ ///
+ internal SQLiteIndexOutputs(int nConstraint)
+ {
+ constraintUsages = new SQLiteIndexConstraintUsage[nConstraint];
+
+ //
+ // BUGFIX: Create the [empty] constraint usages now so they can be
+ // used by the xBestIndex callback.
+ //
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ constraintUsages[iConstraint] = new SQLiteIndexConstraintUsage();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if the native estimatedRows field can be used, based on
+ /// the available version of the SQLite core library.
+ ///
+ ///
+ /// Non-zero if the property is supported
+ /// by the SQLite core library.
+ ///
+ public bool CanUseEstimatedRows()
+ {
+ if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3008002)
+ return true;
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if the native flags field can be used, based on the
+ /// available version of the SQLite core library.
+ ///
+ ///
+ /// Non-zero if the property is supported by
+ /// the SQLite core library.
+ ///
+ public bool CanUseIndexFlags()
+ {
+ if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3009000)
+ return true;
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if the native flags field can be used, based on the
+ /// available version of the SQLite core library.
+ ///
+ ///
+ /// Non-zero if the property is supported by
+ /// the SQLite core library.
+ ///
+ public bool CanUseColumnsUsed()
+ {
+ if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3010000)
+ return true;
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private SQLiteIndexConstraintUsage[] constraintUsages;
+ ///
+ /// An array of object
+ /// instances, each containing information to be supplied to the SQLite
+ /// core library.
+ ///
+ public SQLiteIndexConstraintUsage[] ConstraintUsages
+ {
+ get { return constraintUsages; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private int indexNumber;
+ ///
+ /// Number used to help identify the selected index. This value will
+ /// later be provided to the
+ /// method.
+ ///
+ public int IndexNumber
+ {
+ get { return indexNumber; }
+ set { indexNumber = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private string indexString;
+ ///
+ /// String used to help identify the selected index. This value will
+ /// later be provided to the
+ /// method.
+ ///
+ public string IndexString
+ {
+ get { return indexString; }
+ set { indexString = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private int needToFreeIndexString;
+ ///
+ /// Non-zero if the index string must be freed by the SQLite core
+ /// library.
+ ///
+ public int NeedToFreeIndexString
+ {
+ get { return needToFreeIndexString; }
+ set { needToFreeIndexString = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private int orderByConsumed;
+ ///
+ /// True if output is already ordered.
+ ///
+ public int OrderByConsumed
+ {
+ get { return orderByConsumed; }
+ set { orderByConsumed = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private double? estimatedCost;
+ ///
+ /// Estimated cost of using this index. Using a null value here
+ /// indicates that a default estimated cost value should be used.
+ ///
+ public double? EstimatedCost
+ {
+ get { return estimatedCost; }
+ set { estimatedCost = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private long? estimatedRows;
+ ///
+ /// Estimated number of rows returned. Using a null value here
+ /// indicates that a default estimated rows value should be used.
+ /// This property has no effect if the SQLite core library is not at
+ /// least version 3.8.2.
+ ///
+ public long? EstimatedRows
+ {
+ get { return estimatedRows; }
+ set { estimatedRows = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteIndexFlags? indexFlags;
+ ///
+ /// The flags that should be used with this index. Using a null value
+ /// here indicates that a default flags value should be used. This
+ /// property has no effect if the SQLite core library is not at least
+ /// version 3.9.0.
+ ///
+ public SQLiteIndexFlags? IndexFlags
+ {
+ get { return indexFlags; }
+ set { indexFlags = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private long? columnsUsed;
+ ///
+ ///
+ /// Indicates which columns of the virtual table may be required by the
+ /// current scan. Virtual table columns are numbered from zero in the
+ /// order in which they appear within the CREATE TABLE statement passed
+ /// to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
+ /// the corresponding bit is set within the bit mask if the column may
+ /// be required by SQLite. If the table has at least 64 columns and
+ /// any column to the right of the first 63 is required, then bit 63 of
+ /// colUsed is also set. In other words, column iCol may be required
+ /// if the expression
+ ///
+ ///
+ /// (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol)))
+ ///
+ ///
+ /// evaluates to non-zero. Using a null value here indicates that a
+ /// default flags value should be used. This property has no effect if
+ /// the SQLite core library is not at least version 3.10.0.
+ ///
+ ///
+ public long? ColumnsUsed
+ {
+ get { return columnsUsed; }
+ set { columnsUsed = value; }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteIndex Helper Class
+ ///
+ /// This class represents the various inputs and outputs used with the
+ /// method.
+ ///
+ public sealed class SQLiteIndex
+ {
+ #region Internal Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The number of (and
+ /// ) instances to
+ /// pre-allocate space for.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ internal SQLiteIndex(
+ int nConstraint,
+ int nOrderBy
+ )
+ {
+ inputs = new SQLiteIndexInputs(nConstraint, nOrderBy);
+ outputs = new SQLiteIndexOutputs(nConstraint);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Marshal Helper Methods (For Test Use Only)
+ ///
+ /// Attempts to determine the structure sizes needed to create and
+ /// populate a native
+ ///
+ /// structure.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ ///
+ /// The size of the native
+ ///
+ /// structure is stored here.
+ ///
+ private static void SizeOfNative(
+ out int sizeOfInfoType,
+ out int sizeOfConstraintType,
+ out int sizeOfOrderByType,
+ out int sizeOfConstraintUsageType
+ )
+ {
+ sizeOfInfoType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_info));
+
+ sizeOfConstraintType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint));
+
+ sizeOfOrderByType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_orderby));
+
+ sizeOfConstraintUsageType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint_usage));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to allocate and initialize a native
+ ///
+ /// structure.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ ///
+ /// The number of instances to
+ /// pre-allocate space for.
+ ///
+ ///
+ /// The newly allocated native
+ /// structure
+ /// -OR- if it could not be fully allocated.
+ ///
+ private static IntPtr AllocateAndInitializeNative(
+ int nConstraint,
+ int nOrderBy
+ )
+ {
+ IntPtr pIndex = IntPtr.Zero;
+ IntPtr pInfo = IntPtr.Zero;
+ IntPtr pConstraint = IntPtr.Zero;
+ IntPtr pOrderBy = IntPtr.Zero;
+ IntPtr pConstraintUsage = IntPtr.Zero;
+
+ try
+ {
+ int sizeOfInfoType;
+ int sizeOfOrderByType;
+ int sizeOfConstraintType;
+ int sizeOfConstraintUsageType;
+
+ SizeOfNative(out sizeOfInfoType, out sizeOfConstraintType,
+ out sizeOfOrderByType, out sizeOfConstraintUsageType);
+
+ if ((sizeOfInfoType > 0) &&
+ (sizeOfConstraintType > 0) &&
+ (sizeOfOrderByType > 0) &&
+ (sizeOfConstraintUsageType > 0))
+ {
+ pInfo = SQLiteMemory.Allocate(sizeOfInfoType);
+
+ pConstraint = SQLiteMemory.Allocate(
+ sizeOfConstraintType * nConstraint);
+
+ pOrderBy = SQLiteMemory.Allocate(
+ sizeOfOrderByType * nOrderBy);
+
+ pConstraintUsage = SQLiteMemory.Allocate(
+ sizeOfConstraintUsageType * nConstraint);
+
+ if ((pInfo != IntPtr.Zero) &&
+ (pConstraint != IntPtr.Zero) &&
+ (pOrderBy != IntPtr.Zero) &&
+ (pConstraintUsage != IntPtr.Zero))
+ {
+ int offset = 0;
+
+ SQLiteMarshal.WriteInt32(
+ pInfo, offset, nConstraint);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(
+ pInfo, offset, pConstraint);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ SQLiteMarshal.WriteInt32(
+ pInfo, offset, nOrderBy);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(
+ pInfo, offset, pOrderBy);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(
+ pInfo, offset, pConstraintUsage);
+
+ pIndex = pInfo; /* NOTE: Success. */
+ }
+ }
+ }
+ finally
+ {
+ if (pIndex == IntPtr.Zero) /* NOTE: Failure? */
+ {
+ if (pConstraintUsage != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraintUsage);
+ pConstraintUsage = IntPtr.Zero;
+ }
+
+ if (pOrderBy != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pOrderBy);
+ pOrderBy = IntPtr.Zero;
+ }
+
+ if (pConstraint != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraint);
+ pConstraint = IntPtr.Zero;
+ }
+
+ if (pInfo != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pInfo);
+ pInfo = IntPtr.Zero;
+ }
+ }
+ }
+
+ return pIndex;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Frees all the memory associated with a native
+ ///
+ /// structure.
+ ///
+ ///
+ /// The native pointer to the native sqlite3_index_info structure to
+ /// free.
+ ///
+ private static void FreeNative(
+ IntPtr pIndex
+ )
+ {
+ if (pIndex == IntPtr.Zero)
+ return;
+
+ int offset = 0;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ int constraintOffset = offset;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ int orderByOffset = offset;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ IntPtr pConstraintUsage = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ int constraintUsageOffset = offset;
+
+ if (pConstraintUsage != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraintUsage);
+ pConstraintUsage = IntPtr.Zero;
+
+ SQLiteMarshal.WriteIntPtr(
+ pIndex, constraintUsageOffset, pConstraintUsage);
+ }
+
+ if (pOrderBy != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pOrderBy);
+ pOrderBy = IntPtr.Zero;
+
+ SQLiteMarshal.WriteIntPtr(
+ pIndex, orderByOffset, pOrderBy);
+ }
+
+ if (pConstraint != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pConstraint);
+ pConstraint = IntPtr.Zero;
+
+ SQLiteMarshal.WriteIntPtr(
+ pIndex, constraintOffset, pConstraint);
+ }
+
+ if (pIndex != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pIndex);
+ pIndex = IntPtr.Zero;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Internal Marshal Helper Methods
+ ///
+ /// Converts a native pointer to a native sqlite3_index_info structure
+ /// into a new object instance.
+ ///
+ ///
+ /// The native pointer to the native sqlite3_index_info structure to
+ /// convert.
+ ///
+ ///
+ /// Non-zero to include fields from the outputs portion of the native
+ /// structure; otherwise, the "output" fields will not be read.
+ ///
+ ///
+ /// Upon success, this parameter will be modified to contain the newly
+ /// created object instance.
+ ///
+ internal static void FromIntPtr(
+ IntPtr pIndex,
+ bool includeOutput,
+ ref SQLiteIndex index
+ )
+ {
+ if (pIndex == IntPtr.Zero)
+ return;
+
+ int offset = 0;
+
+ int nConstraint = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ int nOrderBy = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ IntPtr pConstraintUsage = IntPtr.Zero;
+
+ if (includeOutput)
+ {
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ pConstraintUsage = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+ }
+
+ index = new SQLiteIndex(nConstraint, nOrderBy);
+ SQLiteIndexInputs inputs = index.Inputs;
+
+ if (inputs == null)
+ return;
+
+ SQLiteIndexConstraint[] constraints = inputs.Constraints;
+
+ if (constraints == null)
+ return;
+
+ SQLiteIndexOrderBy[] orderBys = inputs.OrderBys;
+
+ if (orderBys == null)
+ return;
+
+ Type constraintType = typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint);
+
+ int sizeOfConstraintType = Marshal.SizeOf(
+ constraintType);
+
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ {
+ IntPtr pOffset = SQLiteMarshal.IntPtrForOffset(
+ pConstraint, iConstraint * sizeOfConstraintType);
+
+ UnsafeNativeMethods.sqlite3_index_constraint constraint =
+ (UnsafeNativeMethods.sqlite3_index_constraint)
+ Marshal.PtrToStructure(pOffset, constraintType);
+
+ constraints[iConstraint] = new SQLiteIndexConstraint(
+ constraint);
+ }
+
+ Type orderByType = typeof(
+ UnsafeNativeMethods.sqlite3_index_orderby);
+
+ int sizeOfOrderByType = Marshal.SizeOf(orderByType);
+
+ for (int iOrderBy = 0; iOrderBy < nOrderBy; iOrderBy++)
+ {
+ IntPtr pOffset = SQLiteMarshal.IntPtrForOffset(
+ pOrderBy, iOrderBy * sizeOfOrderByType);
+
+ UnsafeNativeMethods.sqlite3_index_orderby orderBy =
+ (UnsafeNativeMethods.sqlite3_index_orderby)
+ Marshal.PtrToStructure(pOffset, orderByType);
+
+ orderBys[iOrderBy] = new SQLiteIndexOrderBy(orderBy);
+ }
+
+ if (includeOutput)
+ {
+ SQLiteIndexOutputs outputs = index.Outputs;
+
+ if (outputs == null)
+ return;
+
+ SQLiteIndexConstraintUsage[] constraintUsages =
+ outputs.ConstraintUsages;
+
+ if (constraintUsages == null)
+ return;
+
+ Type constraintUsageType = typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint_usage);
+
+ int sizeOfConstraintUsageType = Marshal.SizeOf(
+ constraintUsageType);
+
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ {
+ IntPtr pOffset = SQLiteMarshal.IntPtrForOffset(
+ pConstraintUsage, iConstraint * sizeOfConstraintUsageType);
+
+ UnsafeNativeMethods.sqlite3_index_constraint_usage constraintUsage =
+ (UnsafeNativeMethods.sqlite3_index_constraint_usage)
+ Marshal.PtrToStructure(pOffset, constraintUsageType);
+
+ constraintUsages[iConstraint] = new SQLiteIndexConstraintUsage(
+ constraintUsage);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ outputs.IndexNumber = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ outputs.IndexString = SQLiteString.StringFromUtf8IntPtr(
+ SQLiteMarshal.ReadIntPtr(pIndex, offset));
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ outputs.NeedToFreeIndexString = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(int));
+
+ outputs.OrderByConsumed = SQLiteMarshal.ReadInt32(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(double));
+
+ outputs.EstimatedCost = SQLiteMarshal.ReadDouble(
+ pIndex, offset);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(double), sizeof(long));
+
+ if (outputs.CanUseEstimatedRows())
+ {
+ outputs.EstimatedRows = SQLiteMarshal.ReadInt64(
+ pIndex, offset);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(long), sizeof(int));
+
+ if (outputs.CanUseIndexFlags())
+ {
+ outputs.IndexFlags = (SQLiteIndexFlags)
+ SQLiteMarshal.ReadInt32(pIndex, offset);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(long));
+
+ if (outputs.CanUseColumnsUsed())
+ {
+ outputs.ColumnsUsed = SQLiteMarshal.ReadInt64(
+ pIndex, offset);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Populates the outputs of a pre-allocated native sqlite3_index_info
+ /// structure using an existing object
+ /// instance.
+ ///
+ ///
+ /// The existing object instance containing
+ /// the output data to use.
+ ///
+ ///
+ /// The native pointer to the pre-allocated native sqlite3_index_info
+ /// structure.
+ ///
+ ///
+ /// Non-zero to include fields from the inputs portion of the native
+ /// structure; otherwise, the "input" fields will not be written.
+ ///
+ internal static void ToIntPtr(
+ SQLiteIndex index,
+ IntPtr pIndex,
+ bool includeInput
+ )
+ {
+ if (index == null)
+ return;
+
+ SQLiteIndexOutputs outputs = index.Outputs;
+
+ if (outputs == null)
+ return;
+
+ SQLiteIndexConstraintUsage[] constraintUsages =
+ outputs.ConstraintUsages;
+
+ if (constraintUsages == null)
+ return;
+
+ SQLiteIndexInputs inputs = null;
+ SQLiteIndexConstraint[] constraints = null;
+ SQLiteIndexOrderBy[] orderBys = null;
+
+ if (includeInput)
+ {
+ inputs = index.Inputs;
+
+ if (inputs == null)
+ return;
+
+ constraints = inputs.Constraints;
+
+ if (constraints == null)
+ return;
+
+ orderBys = inputs.OrderBys;
+
+ if (orderBys == null)
+ return;
+ }
+
+ if (pIndex == IntPtr.Zero)
+ return;
+
+ int offset = 0;
+
+ int nConstraint = SQLiteMarshal.ReadInt32(pIndex, offset);
+
+ if (includeInput && (nConstraint != constraints.Length))
+ return;
+
+ if (nConstraint != constraintUsages.Length)
+ return;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ if (includeInput)
+ {
+ IntPtr pConstraint = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ int sizeOfConstraintType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint));
+
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ {
+ UnsafeNativeMethods.sqlite3_index_constraint constraint =
+ new UnsafeNativeMethods.sqlite3_index_constraint(
+ constraints[iConstraint]);
+
+ Marshal.StructureToPtr(
+ constraint, SQLiteMarshal.IntPtrForOffset(
+ pConstraint, iConstraint * sizeOfConstraintType),
+ false);
+ }
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ int nOrderBy = includeInput ?
+ SQLiteMarshal.ReadInt32(pIndex, offset) : 0;
+
+ if (includeInput && (nOrderBy != orderBys.Length))
+ return;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ if (includeInput)
+ {
+ IntPtr pOrderBy = SQLiteMarshal.ReadIntPtr(pIndex, offset);
+
+ int sizeOfOrderByType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_orderby));
+
+ for (int iOrderBy = 0; iOrderBy < nOrderBy; iOrderBy++)
+ {
+ UnsafeNativeMethods.sqlite3_index_orderby orderBy =
+ new UnsafeNativeMethods.sqlite3_index_orderby(
+ orderBys[iOrderBy]);
+
+ Marshal.StructureToPtr(
+ orderBy, SQLiteMarshal.IntPtrForOffset(
+ pOrderBy, iOrderBy * sizeOfOrderByType),
+ false);
+ }
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, IntPtr.Size);
+
+ IntPtr pConstraintUsage = SQLiteMarshal.ReadIntPtr(
+ pIndex, offset);
+
+ int sizeOfConstraintUsageType = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_index_constraint_usage));
+
+ for (int iConstraint = 0; iConstraint < nConstraint; iConstraint++)
+ {
+ UnsafeNativeMethods.sqlite3_index_constraint_usage constraintUsage =
+ new UnsafeNativeMethods.sqlite3_index_constraint_usage(
+ constraintUsages[iConstraint]);
+
+ Marshal.StructureToPtr(
+ constraintUsage, SQLiteMarshal.IntPtrForOffset(
+ pConstraintUsage, iConstraint * sizeOfConstraintUsageType),
+ false);
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ SQLiteMarshal.WriteInt32(pIndex, offset,
+ outputs.IndexNumber);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(pIndex, offset,
+ SQLiteString.Utf8IntPtrFromString(
+ outputs.IndexString, false)); /* OK: FREED BY CORE*/
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ //
+ // NOTE: We just allocated the IndexString field; therefore, we
+ // need to set make sure the NeedToFreeIndexString field
+ // is non-zero; however, we are not picky about the exact
+ // value.
+ //
+ int needToFreeIndexString = outputs.NeedToFreeIndexString != 0 ?
+ outputs.NeedToFreeIndexString : 1;
+
+ SQLiteMarshal.WriteInt32(pIndex, offset,
+ needToFreeIndexString);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(int));
+
+ SQLiteMarshal.WriteInt32(pIndex, offset,
+ outputs.OrderByConsumed);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(double));
+
+ if (outputs.EstimatedCost.HasValue)
+ {
+ SQLiteMarshal.WriteDouble(pIndex, offset,
+ outputs.EstimatedCost.GetValueOrDefault());
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(double), sizeof(long));
+
+ if (outputs.CanUseEstimatedRows() &&
+ outputs.EstimatedRows.HasValue)
+ {
+ SQLiteMarshal.WriteInt64(pIndex, offset,
+ outputs.EstimatedRows.GetValueOrDefault());
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(long), sizeof(int));
+
+ if (outputs.CanUseIndexFlags() &&
+ outputs.IndexFlags.HasValue)
+ {
+ SQLiteMarshal.WriteInt32(pIndex, offset,
+ (int)outputs.IndexFlags.GetValueOrDefault());
+ }
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), sizeof(long));
+
+ if (outputs.CanUseColumnsUsed() &&
+ outputs.ColumnsUsed.HasValue)
+ {
+ SQLiteMarshal.WriteInt64(pIndex, offset,
+ outputs.ColumnsUsed.GetValueOrDefault());
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private SQLiteIndexInputs inputs;
+ ///
+ /// The object instance containing
+ /// the inputs to the
+ /// method.
+ ///
+ public SQLiteIndexInputs Inputs
+ {
+ get { return inputs; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteIndexOutputs outputs;
+ ///
+ /// The object instance containing
+ /// the outputs from the
+ /// method.
+ ///
+ public SQLiteIndexOutputs Outputs
+ {
+ get { return outputs; }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteVirtualTable Base Class
+ ///
+ /// This class represents a managed virtual table implementation. It is
+ /// not sealed and should be used as the base class for any user-defined
+ /// virtual table classes implemented in managed code.
+ ///
+ public class SQLiteVirtualTable :
+ ISQLiteNativeHandle, IDisposable /* NOT SEALED */
+ {
+ #region Private Constants
+ ///
+ /// The index within the array of strings provided to the
+ /// and
+ /// methods containing the
+ /// name of the module implementing this virtual table.
+ ///
+ private const int ModuleNameIndex = 0;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The index within the array of strings provided to the
+ /// and
+ /// methods containing the
+ /// name of the database containing this virtual table.
+ ///
+ private const int DatabaseNameIndex = 1;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The index within the array of strings provided to the
+ /// and
+ /// methods containing the
+ /// name of the virtual table.
+ ///
+ private const int TableNameIndex = 2;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The original array of strings provided to the
+ /// and
+ /// methods.
+ ///
+ public SQLiteVirtualTable(
+ string[] arguments
+ )
+ {
+ this.arguments = arguments;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private string[] arguments;
+ ///
+ /// The original array of strings provided to the
+ /// and
+ /// methods.
+ ///
+ public virtual string[] Arguments
+ {
+ get { CheckDisposed(); return arguments; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The name of the module implementing this virtual table.
+ ///
+ public virtual string ModuleName
+ {
+ get
+ {
+ CheckDisposed();
+
+ string[] arguments = Arguments;
+
+ if ((arguments != null) &&
+ (arguments.Length > ModuleNameIndex))
+ {
+ return arguments[ModuleNameIndex];
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The name of the database containing this virtual table.
+ ///
+ public virtual string DatabaseName
+ {
+ get
+ {
+ CheckDisposed();
+
+ string[] arguments = Arguments;
+
+ if ((arguments != null) &&
+ (arguments.Length > DatabaseNameIndex))
+ {
+ return arguments[DatabaseNameIndex];
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The name of the virtual table.
+ ///
+ public virtual string TableName
+ {
+ get
+ {
+ CheckDisposed();
+
+ string[] arguments = Arguments;
+
+ if ((arguments != null) &&
+ (arguments.Length > TableNameIndex))
+ {
+ return arguments[TableNameIndex];
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteIndex index;
+ ///
+ /// The object instance containing all the
+ /// data for the inputs and outputs relating to the most recent index
+ /// selection.
+ ///
+ public virtual SQLiteIndex Index
+ {
+ get { CheckDisposed(); return index; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// This method should normally be used by the
+ /// method in order to
+ /// perform index selection based on the constraints provided by the
+ /// SQLite core library.
+ ///
+ ///
+ /// The object instance containing all the
+ /// data for the inputs and outputs relating to index selection.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ public virtual bool BestIndex(
+ SQLiteIndex index
+ )
+ {
+ CheckDisposed();
+
+ this.index = index;
+
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to record the renaming of the virtual table associated
+ /// with this object instance.
+ ///
+ ///
+ /// The new name for the virtual table.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ public virtual bool Rename(
+ string name
+ )
+ {
+ CheckDisposed();
+
+ if ((arguments != null) &&
+ (arguments.Length > TableNameIndex))
+ {
+ arguments[TableNameIndex] = name;
+ return true;
+ }
+
+ return false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeHandle Members
+ private IntPtr nativeHandle;
+ ///
+ /// Returns the underlying SQLite native handle associated with this
+ /// object instance.
+ ///
+ public virtual IntPtr NativeHandle
+ {
+ get { CheckDisposed(); return nativeHandle; }
+ internal set { nativeHandle = value; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteVirtualTable).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is being called
+ /// from the finalizer.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteVirtualTable()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteVirtualTableCursor Base Class
+ ///
+ /// This class represents a managed virtual table cursor implementation.
+ /// It is not sealed and should be used as the base class for any
+ /// user-defined virtual table cursor classes implemented in managed code.
+ ///
+ public class SQLiteVirtualTableCursor :
+ ISQLiteNativeHandle, IDisposable /* NOT SEALED */
+ {
+ #region Protected Constants
+ ///
+ /// This value represents an invalid integer row sequence number.
+ ///
+ protected static readonly int InvalidRowIndex = 0;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// The field holds the integer row sequence number for the current row
+ /// pointed to by this cursor object instance.
+ ///
+ private int rowIndex;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The object instance associated
+ /// with this object instance.
+ ///
+ public SQLiteVirtualTableCursor(
+ SQLiteVirtualTable table
+ )
+ : this()
+ {
+ this.table = table;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ private SQLiteVirtualTableCursor()
+ {
+ rowIndex = InvalidRowIndex;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ private SQLiteVirtualTable table;
+ ///
+ /// The object instance associated
+ /// with this object instance.
+ ///
+ public virtual SQLiteVirtualTable Table
+ {
+ get { CheckDisposed(); return table; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private int indexNumber;
+ ///
+ /// Number used to help identify the selected index. This value will
+ /// be set via the method.
+ ///
+ public virtual int IndexNumber
+ {
+ get { CheckDisposed(); return indexNumber; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private string indexString;
+ ///
+ /// String used to help identify the selected index. This value will
+ /// be set via the method.
+ ///
+ public virtual string IndexString
+ {
+ get { CheckDisposed(); return indexString; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteValue[] values;
+ ///
+ /// The values used to filter the rows returned via this cursor object
+ /// instance. This value will be set via the
+ /// method.
+ ///
+ public virtual SQLiteValue[] Values
+ {
+ get { CheckDisposed(); return values; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Attempts to persist the specified object
+ /// instances in order to make them available after the
+ /// method returns.
+ ///
+ ///
+ /// The array of object instances to be
+ /// persisted.
+ ///
+ ///
+ /// The number of object instances that were
+ /// successfully persisted.
+ ///
+ protected virtual int TryPersistValues(
+ SQLiteValue[] values
+ )
+ {
+ int result = 0;
+
+ if (values != null)
+ {
+ foreach (SQLiteValue value in values)
+ {
+ if (value == null)
+ continue;
+
+ if (value.Persist())
+ result++;
+ }
+ }
+
+ return result;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// This method should normally be used by the
+ /// method in order to
+ /// perform filtering of the result rows and/or to record the filtering
+ /// criteria provided by the SQLite core library.
+ ///
+ ///
+ /// Number used to help identify the selected index.
+ ///
+ ///
+ /// String used to help identify the selected index.
+ ///
+ ///
+ /// The values corresponding to each column in the selected index.
+ ///
+ public virtual void Filter(
+ int indexNumber,
+ string indexString,
+ SQLiteValue[] values
+ )
+ {
+ CheckDisposed();
+
+ if ((values != null) &&
+ (TryPersistValues(values) != values.Length))
+ {
+ throw new SQLiteException(
+ "failed to persist one or more values");
+ }
+
+ this.indexNumber = indexNumber;
+ this.indexString = indexString;
+ this.values = values;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines the integer row sequence number for the current row.
+ ///
+ ///
+ /// The integer row sequence number for the current row -OR- zero if
+ /// it cannot be determined.
+ ///
+ public virtual int GetRowIndex()
+ {
+ return rowIndex;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adjusts the integer row sequence number so that it refers to the
+ /// next row.
+ ///
+ public virtual void NextRowIndex()
+ {
+ rowIndex++;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeHandle Members
+ private IntPtr nativeHandle;
+ ///
+ /// Returns the underlying SQLite native handle associated with this
+ /// object instance.
+ ///
+ public virtual IntPtr NativeHandle
+ {
+ get { CheckDisposed(); return nativeHandle; }
+ internal set { nativeHandle = value; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteVirtualTableCursor).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is being called
+ /// from the finalizer.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteVirtualTableCursor()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeHandle Interface
+ ///
+ /// This interface represents a native handle provided by the SQLite core
+ /// library.
+ ///
+ public interface ISQLiteNativeHandle
+ {
+ ///
+ /// The native handle value.
+ ///
+ IntPtr NativeHandle { get; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteManagedModule Interface
+ ///
+ /// This interface represents a virtual table implementation written in
+ /// managed code.
+ ///
+ public interface ISQLiteManagedModule
+ {
+ ///
+ /// Returns non-zero if the schema for the virtual table has been
+ /// declared.
+ ///
+ bool Declared { get; }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the name of the module as it was registered with the SQLite
+ /// core library.
+ ///
+ string Name { get; }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// The native user-data pointer associated with this module, as it was
+ /// provided to the SQLite core library when the native module instance
+ /// was created.
+ ///
+ ///
+ /// The module name, database name, virtual table name, and all other
+ /// arguments passed to the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to contain an error
+ /// message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Create(
+ SQLiteConnection connection, /* in */
+ IntPtr pClientData, /* in */
+ string[] arguments, /* in */
+ ref SQLiteVirtualTable table, /* out */
+ ref string error /* out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// The native user-data pointer associated with this module, as it was
+ /// provided to the SQLite core library when the native module instance
+ /// was created.
+ ///
+ ///
+ /// The module name, database name, virtual table name, and all other
+ /// arguments passed to the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to contain an error
+ /// message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Connect(
+ SQLiteConnection connection, /* in */
+ IntPtr pClientData, /* in */
+ string[] arguments, /* in */
+ ref SQLiteVirtualTable table, /* out */
+ ref string error /* out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The object instance containing all the
+ /// data for the inputs and outputs relating to index selection.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode BestIndex(
+ SQLiteVirtualTable table, /* in */
+ SQLiteIndex index /* in, out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Disconnect(
+ SQLiteVirtualTable table /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Destroy(
+ SQLiteVirtualTable table /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance associated
+ /// with the newly opened virtual table cursor.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Open(
+ SQLiteVirtualTable table, /* in */
+ ref SQLiteVirtualTableCursor cursor /* out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Close(
+ SQLiteVirtualTableCursor cursor /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// Number used to help identify the selected index.
+ ///
+ ///
+ /// String used to help identify the selected index.
+ ///
+ ///
+ /// The values corresponding to each column in the selected index.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Filter(
+ SQLiteVirtualTableCursor cursor, /* in */
+ int indexNumber, /* in */
+ string indexString, /* in */
+ SQLiteValue[] values /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Next(
+ SQLiteVirtualTableCursor cursor /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// Non-zero if no more rows are available; zero otherwise.
+ ///
+ bool Eof(
+ SQLiteVirtualTableCursor cursor /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// The object instance to be used for
+ /// returning the specified column value to the SQLite core library.
+ ///
+ ///
+ /// The zero-based index corresponding to the column containing the
+ /// value to be returned.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Column(
+ SQLiteVirtualTableCursor cursor, /* in */
+ SQLiteContext context, /* in */
+ int index /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the unique
+ /// integer row identifier for the current row for the specified cursor.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode RowId(
+ SQLiteVirtualTableCursor cursor, /* in */
+ ref long rowId /* out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The array of object instances containing
+ /// the new or modified column values, if any.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the unique
+ /// integer row identifier for the row that was inserted, if any.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Update(
+ SQLiteVirtualTable table, /* in */
+ SQLiteValue[] values, /* in */
+ ref long rowId /* out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Begin(
+ SQLiteVirtualTable table /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Sync(
+ SQLiteVirtualTable table /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Commit(
+ SQLiteVirtualTable table /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Rollback(
+ SQLiteVirtualTable table /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The number of arguments to the function being sought.
+ ///
+ ///
+ /// The name of the function being sought.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance responsible for
+ /// implementing the specified function.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// native user-data pointer associated with
+ /// .
+ ///
+ ///
+ /// Non-zero if the specified function was found; zero otherwise.
+ ///
+ bool FindFunction(
+ SQLiteVirtualTable table, /* in */
+ int argumentCount, /* in */
+ string name, /* in */
+ ref SQLiteFunction function, /* out */
+ ref IntPtr pClientData /* out */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The new name for the virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Rename(
+ SQLiteVirtualTable table, /* in */
+ string newName /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// This is an integer identifier under which the the current state of
+ /// the virtual table should be saved.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Savepoint(
+ SQLiteVirtualTable table, /* in */
+ int savepoint /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// This is an integer used to indicate that any saved states with an
+ /// identifier greater than or equal to this should be deleted by the
+ /// virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode Release(
+ SQLiteVirtualTable table, /* in */
+ int savepoint /* in */
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// This is an integer identifier used to specify a specific saved
+ /// state for the virtual table for it to restore itself back to, which
+ /// should also have the effect of deleting all saved states with an
+ /// integer identifier greater than this one.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ SQLiteErrorCode RollbackTo(
+ SQLiteVirtualTable table, /* in */
+ int savepoint /* in */
+ );
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteMemory Static Class
+ ///
+ /// This class contains static methods that are used to allocate,
+ /// manipulate, and free native memory provided by the SQLite core library.
+ ///
+ internal static class SQLiteMemory
+ {
+ #region Private Data
+#if TRACK_MEMORY_BYTES
+ ///
+ /// This object instance is used to synchronize access to the other
+ /// static fields of this class.
+ ///
+ private static object syncRoot = new object();
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The total number of outstanding memory bytes allocated by this
+ /// class using the SQLite core library.
+ ///
+ private static ulong bytesAllocated;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The maximum number of outstanding memory bytes ever allocated by
+ /// this class using the SQLite core library.
+ ///
+ private static ulong maximumBytesAllocated;
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Memory Tracking Helper Methods
+#if TRACK_MEMORY_BYTES
+ ///
+ /// Attempts to determine the size of the specified memory block. If
+ /// the method can be used, the returned value
+ /// may be larger than . A message may
+ /// be sent to the logging subsystem if an error is encountered.
+ ///
+ ///
+ /// The native pointer to the memory block previously obtained from
+ /// the , ,
+ /// , or
+ /// methods or directly from the
+ /// SQLite core library.
+ ///
+ ///
+ /// The size of the specified memory block -OR- zero if the 32-bit
+ /// signed value reported from the native API was less than zero.
+ ///
+ private static ulong GetBlockSize(
+ IntPtr pMemory
+ )
+ {
+ ulong ulongSize = 0;
+
+ if (CanUseSize64())
+ {
+ ulongSize = Size64(pMemory);
+ }
+ else
+ {
+ int intSize = Size(pMemory);
+
+ if (intSize > 0)
+ {
+ ulongSize = (ulong)intSize;
+ }
+#if DEBUG
+ else if (intSize < 0)
+ {
+ SQLiteLog.LogMessage(SQLiteErrorCode.Warning,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ "pointer {0} size {1} appears to be negative: {2}",
+ pMemory, intSize,
+#if !PLATFORM_COMPACTFRAMEWORK
+ Environment.StackTrace
+#else
+ null
+#endif
+ ));
+ }
+#endif
+ }
+
+ return ulongSize;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adjusts the total number of (tracked) bytes that are currently
+ /// considered to be allocated by this class. The total number is
+ /// increased by the total size of the memory block pointed to by
+ /// . If the new total number exceeds
+ /// the previously seen maximum, the maximum will be reset.
+ ///
+ ///
+ /// A native pointer to newly allocated memory.
+ ///
+ private static void MemoryWasAllocated(
+ IntPtr pMemory
+ )
+ {
+ if (pMemory != IntPtr.Zero)
+ {
+ ulong blockSize = GetBlockSize(pMemory);
+
+ if (blockSize > 0)
+ {
+ lock (syncRoot)
+ {
+ bytesAllocated += blockSize;
+
+ if (bytesAllocated > maximumBytesAllocated)
+ maximumBytesAllocated = bytesAllocated;
+ }
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Adjusts the total number of (tracked) bytes that are currently
+ /// considered to be allocated by this class. The total number is
+ /// decreased by the total size of the memory block pointed to by
+ /// .
+ ///
+ ///
+ /// A native pointer to allocated memory that is going to be freed.
+ ///
+ private static void MemoryIsBeingFreed(
+ IntPtr pMemory
+ )
+ {
+ if (pMemory != IntPtr.Zero)
+ {
+ ulong blockSize = GetBlockSize(pMemory);
+
+ if (blockSize > 0)
+ {
+ lock (syncRoot)
+ {
+ bytesAllocated -= blockSize;
+ }
+ }
+ }
+ }
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Memory Version Helper Methods
+ ///
+ /// Determines if the native sqlite3_msize() API can be used, based on
+ /// the available version of the SQLite core library.
+ ///
+ ///
+ /// Non-zero if the native sqlite3_msize() API is supported by the
+ /// SQLite core library.
+ ///
+ private static bool CanUseSize64()
+ {
+#if !PLATFORM_COMPACTFRAMEWORK || !SQLITE_STANDARD
+ if (UnsafeNativeMethods.sqlite3_libversion_number() >= 3008007)
+ return true;
+#endif
+
+ return false;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Memory Allocation Helper Methods
+ ///
+ /// Allocates at least the specified number of bytes of native memory
+ /// via the SQLite core library sqlite3_malloc() function and returns
+ /// the resulting native pointer. If the TRACK_MEMORY_BYTES option
+ /// was enabled at compile-time, adjusts the number of bytes currently
+ /// allocated by this class.
+ ///
+ ///
+ /// The number of bytes to allocate.
+ ///
+ ///
+ /// The native pointer that points to a block of memory of at least the
+ /// specified size -OR- if the memory could
+ /// not be allocated.
+ ///
+ public static IntPtr Allocate(int size)
+ {
+ IntPtr pMemory = UnsafeNativeMethods.sqlite3_malloc(size);
+
+#if TRACK_MEMORY_BYTES
+ MemoryWasAllocated(pMemory);
+#endif
+
+ return pMemory;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Allocates at least the specified number of bytes of native memory
+ /// via the SQLite core library sqlite3_malloc64() function and returns
+ /// the resulting native pointer. If the TRACK_MEMORY_BYTES option
+ /// was enabled at compile-time, adjusts the number of bytes currently
+ /// allocated by this class.
+ ///
+ ///
+ /// The number of bytes to allocate.
+ ///
+ ///
+ /// The native pointer that points to a block of memory of at least the
+ /// specified size -OR- if the memory could
+ /// not be allocated.
+ ///
+ public static IntPtr Allocate64(ulong size)
+ {
+ IntPtr pMemory = UnsafeNativeMethods.sqlite3_malloc64(size);
+
+#if TRACK_MEMORY_BYTES
+ MemoryWasAllocated(pMemory);
+#endif
+
+ return pMemory;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Allocates at least the specified number of bytes of native memory
+ /// via the SQLite core library sqlite3_malloc() function and returns
+ /// the resulting native pointer without adjusting the number of
+ /// allocated bytes currently tracked by this class. This is useful
+ /// when dealing with blocks of memory that will be freed directly by
+ /// the SQLite core library.
+ ///
+ ///
+ /// The number of bytes to allocate.
+ ///
+ ///
+ /// The native pointer that points to a block of memory of at least the
+ /// specified size -OR- if the memory could
+ /// not be allocated.
+ ///
+ public static IntPtr AllocateUntracked(int size)
+ {
+ return UnsafeNativeMethods.sqlite3_malloc(size);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Allocates at least the specified number of bytes of native memory
+ /// via the SQLite core library sqlite3_malloc64() function and returns
+ /// the resulting native pointer without adjusting the number of
+ /// allocated bytes currently tracked by this class. This is useful
+ /// when dealing with blocks of memory that will be freed directly by
+ /// the SQLite core library.
+ ///
+ ///
+ /// The number of bytes to allocate.
+ ///
+ ///
+ /// The native pointer that points to a block of memory of at least the
+ /// specified size -OR- if the memory could
+ /// not be allocated.
+ ///
+ public static IntPtr Allocate64Untracked(ulong size)
+ {
+ return UnsafeNativeMethods.sqlite3_malloc64(size);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the actual size of the specified memory block
+ /// that was previously obtained from the ,
+ /// , , or
+ /// methods or directly from the
+ /// SQLite core library.
+ ///
+ ///
+ /// The native pointer to the memory block previously obtained from
+ /// the , ,
+ /// , or
+ /// methods or directly from the
+ /// SQLite core library.
+ ///
+ ///
+ /// The actual size, in bytes, of the memory block specified via the
+ /// native pointer.
+ ///
+ public static int Size(IntPtr pMemory)
+ {
+#if DEBUG
+ SQLiteMarshal.CheckAlignment("Size", pMemory, 0, IntPtr.Size);
+#endif
+
+#if !SQLITE_STANDARD
+ return UnsafeNativeMethods.sqlite3_malloc_size_interop(pMemory);
+#elif TRACK_MEMORY_BYTES
+ //
+ // HACK: Ok, we cannot determine the size of the memory block;
+ // therefore, just track number of allocations instead.
+ //
+ return (pMemory != IntPtr.Zero) ? 1 : 0;
+#else
+ return 0;
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets and returns the actual size of the specified memory block
+ /// that was previously obtained from the ,
+ /// , , or
+ /// methods or directly from the
+ /// SQLite core library.
+ ///
+ ///
+ /// The native pointer to the memory block previously obtained from
+ /// the , ,
+ /// , or
+ /// methods or directly from the
+ /// SQLite core library.
+ ///
+ ///
+ /// The actual size, in bytes, of the memory block specified via the
+ /// native pointer.
+ ///
+ public static ulong Size64(IntPtr pMemory)
+ {
+#if DEBUG
+ SQLiteMarshal.CheckAlignment("Size64", pMemory, 0, IntPtr.Size);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return UnsafeNativeMethods.sqlite3_msize(pMemory);
+#elif !SQLITE_STANDARD
+ ulong size = 0;
+ UnsafeNativeMethods.sqlite3_msize_interop(pMemory, ref size);
+ return size;
+#else
+ throw new NotImplementedException();
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Frees a memory block previously obtained from the
+ /// or methods. If
+ /// the TRACK_MEMORY_BYTES option was enabled at compile-time, adjusts
+ /// the number of bytes currently allocated by this class.
+ ///
+ ///
+ /// The native pointer to the memory block previously obtained from the
+ /// or methods.
+ ///
+ public static void Free(IntPtr pMemory)
+ {
+#if DEBUG
+ SQLiteMarshal.CheckAlignment("Free", pMemory, 0, IntPtr.Size);
+#endif
+
+#if TRACK_MEMORY_BYTES
+ MemoryIsBeingFreed(pMemory);
+#endif
+
+ UnsafeNativeMethods.sqlite3_free(pMemory);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Frees a memory block previously obtained from the SQLite core
+ /// library without adjusting the number of allocated bytes currently
+ /// tracked by this class. This is useful when dealing with blocks of
+ /// memory that were not allocated using this class.
+ ///
+ ///
+ /// The native pointer to the memory block previously obtained from the
+ /// SQLite core library.
+ ///
+ public static void FreeUntracked(IntPtr pMemory)
+ {
+#if DEBUG
+ SQLiteMarshal.CheckAlignment(
+ "FreeUntracked", pMemory, 0, IntPtr.Size);
+#endif
+
+ UnsafeNativeMethods.sqlite3_free(pMemory);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteString Static Class
+ ///
+ /// This class contains static methods that are used to deal with native
+ /// UTF-8 string pointers to be used with the SQLite core library.
+ ///
+ internal static class SQLiteString
+ {
+ #region Private Constants
+ ///
+ /// This is the maximum possible length for the native UTF-8 encoded
+ /// strings used with the SQLite core library.
+ ///
+ private static int ThirtyBits = 0x3fffffff;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This is the object instance used to handle
+ /// conversions from/to UTF-8.
+ ///
+ private static readonly Encoding Utf8Encoding = Encoding.UTF8;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region UTF-8 Encoding Helper Methods
+ ///
+ /// Converts the specified managed string into the UTF-8 encoding and
+ /// returns the array of bytes containing its representation in that
+ /// encoding.
+ ///
+ ///
+ /// The managed string to convert.
+ ///
+ ///
+ /// The array of bytes containing the representation of the managed
+ /// string in the UTF-8 encoding or null upon failure.
+ ///
+ public static byte[] GetUtf8BytesFromString(
+ string value
+ )
+ {
+ if (value == null)
+ return null;
+
+ return Utf8Encoding.GetBytes(value);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified array of bytes representing a string in the
+ /// UTF-8 encoding and returns a managed string.
+ ///
+ ///
+ /// The array of bytes to convert.
+ ///
+ ///
+ /// The managed string or null upon failure.
+ ///
+ public static string GetStringFromUtf8Bytes(
+ byte[] bytes
+ )
+ {
+ if (bytes == null)
+ return null;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return Utf8Encoding.GetString(bytes);
+#else
+ return Utf8Encoding.GetString(bytes, 0, bytes.Length);
+#endif
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region UTF-8 String Helper Methods
+ ///
+ /// Probes a native pointer to a string in the UTF-8 encoding for its
+ /// terminating NUL character, within the specified length limit.
+ ///
+ ///
+ /// The native NUL-terminated string pointer.
+ ///
+ ///
+ /// The maximum length of the native string, in bytes.
+ ///
+ ///
+ /// The length of the native string, in bytes -OR- zero if the length
+ /// could not be determined.
+ ///
+ public static int ProbeForUtf8ByteLength(
+ IntPtr pValue,
+ int limit
+ )
+ {
+ int length = 0;
+
+ if ((pValue != IntPtr.Zero) && (limit > 0))
+ {
+ do
+ {
+ if (Marshal.ReadByte(pValue, length) == 0)
+ break;
+
+ if (length >= limit)
+ break;
+
+ length++;
+ } while (true);
+ }
+
+ return length;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified native NUL-terminated UTF-8 string pointer
+ /// into a managed string.
+ ///
+ ///
+ /// The native NUL-terminated UTF-8 string pointer.
+ ///
+ ///
+ /// The managed string or null upon failure.
+ ///
+ public static string StringFromUtf8IntPtr(
+ IntPtr pValue
+ )
+ {
+ return StringFromUtf8IntPtr(pValue,
+ ProbeForUtf8ByteLength(pValue, ThirtyBits));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified native UTF-8 string pointer of the specified
+ /// length into a managed string.
+ ///
+ ///
+ /// The native UTF-8 string pointer.
+ ///
+ ///
+ /// The length of the native string, in bytes.
+ ///
+ ///
+ /// The managed string or null upon failure.
+ ///
+ public static string StringFromUtf8IntPtr(
+ IntPtr pValue,
+ int length
+ )
+ {
+ if (pValue == IntPtr.Zero)
+ return null;
+
+ if (length > 0)
+ {
+ byte[] bytes = new byte[length];
+
+ Marshal.Copy(pValue, bytes, 0, length);
+
+ return GetStringFromUtf8Bytes(bytes);
+ }
+
+ return String.Empty;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified managed string into a native NUL-terminated
+ /// UTF-8 string pointer using memory obtained from the SQLite core
+ /// library.
+ ///
+ ///
+ /// The managed string to convert.
+ ///
+ ///
+ /// The native NUL-terminated UTF-8 string pointer or
+ /// upon failure.
+ ///
+ public static IntPtr Utf8IntPtrFromString(
+ string value
+ )
+ {
+ return Utf8IntPtrFromString(value, true);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified managed string into a native NUL-terminated
+ /// UTF-8 string pointer using memory obtained from the SQLite core
+ /// library.
+ ///
+ ///
+ /// The managed string to convert.
+ ///
+ ///
+ /// Non-zero to obtain memory from the SQLite core library without
+ /// adjusting the number of allocated bytes currently being tracked
+ /// by the class.
+ ///
+ ///
+ /// The native NUL-terminated UTF-8 string pointer or
+ /// upon failure.
+ ///
+ public static IntPtr Utf8IntPtrFromString(
+ string value,
+ bool tracked
+ )
+ {
+ int length = 0;
+
+ return Utf8IntPtrFromString(value, tracked, ref length);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified managed string into a native NUL-terminated
+ /// UTF-8 string pointer using memory obtained from the SQLite core
+ /// library.
+ ///
+ ///
+ /// The managed string to convert.
+ ///
+ ///
+ /// The length of the native string, in bytes.
+ ///
+ ///
+ /// The native NUL-terminated UTF-8 string pointer or
+ /// upon failure.
+ ///
+ public static IntPtr Utf8IntPtrFromString(
+ string value,
+ ref int length
+ )
+ {
+ return Utf8IntPtrFromString(value, true, ref length);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts the specified managed string into a native NUL-terminated
+ /// UTF-8 string pointer using memory obtained from the SQLite core
+ /// library.
+ ///
+ ///
+ /// The managed string to convert.
+ ///
+ ///
+ /// Non-zero to obtain memory from the SQLite core library without
+ /// adjusting the number of allocated bytes currently being tracked
+ /// by the class.
+ ///
+ ///
+ /// The length of the native string, in bytes.
+ ///
+ ///
+ /// The native NUL-terminated UTF-8 string pointer or
+ /// upon failure.
+ ///
+ public static IntPtr Utf8IntPtrFromString(
+ string value,
+ bool tracked,
+ ref int length
+ )
+ {
+ if (value == null)
+ return IntPtr.Zero;
+
+ IntPtr result = IntPtr.Zero;
+ byte[] bytes = GetUtf8BytesFromString(value);
+
+ if (bytes == null)
+ return IntPtr.Zero;
+
+ length = bytes.Length;
+
+ if (tracked)
+ result = SQLiteMemory.Allocate(length + 1);
+ else
+ result = SQLiteMemory.AllocateUntracked(length + 1);
+
+ if (result == IntPtr.Zero)
+ return IntPtr.Zero;
+
+ Marshal.Copy(bytes, 0, result, length);
+ Marshal.WriteByte(result, length, 0);
+
+ return result;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region UTF-8 String Array Helper Methods
+ ///
+ /// Converts a logical array of native NUL-terminated UTF-8 string
+ /// pointers into an array of managed strings.
+ ///
+ ///
+ /// The number of elements in the logical array of native
+ /// NUL-terminated UTF-8 string pointers.
+ ///
+ ///
+ /// The native pointer to the logical array of native NUL-terminated
+ /// UTF-8 string pointers to convert.
+ ///
+ ///
+ /// The array of managed strings or null upon failure.
+ ///
+ public static string[] StringArrayFromUtf8SizeAndIntPtr(
+ int argc,
+ IntPtr argv
+ )
+ {
+ if (argc < 0)
+ return null;
+
+ if (argv == IntPtr.Zero)
+ return null;
+
+ string[] result = new string[argc];
+
+ for (int index = 0, offset = 0;
+ index < result.Length;
+ index++, offset += IntPtr.Size)
+ {
+ IntPtr pArg = SQLiteMarshal.ReadIntPtr(argv, offset);
+
+ result[index] = (pArg != IntPtr.Zero) ?
+ StringFromUtf8IntPtr(pArg) : null;
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts an array of managed strings into an array of native
+ /// NUL-terminated UTF-8 string pointers.
+ ///
+ ///
+ /// The array of managed strings to convert.
+ ///
+ ///
+ /// Non-zero to obtain memory from the SQLite core library without
+ /// adjusting the number of allocated bytes currently being tracked
+ /// by the class.
+ ///
+ ///
+ /// The array of native NUL-terminated UTF-8 string pointers or null
+ /// upon failure.
+ ///
+ public static IntPtr[] Utf8IntPtrArrayFromStringArray(
+ string[] values,
+ bool tracked
+ )
+ {
+ if (values == null)
+ return null;
+
+ IntPtr[] result = new IntPtr[values.Length];
+
+ for (int index = 0; index < result.Length; index++)
+ result[index] = Utf8IntPtrFromString(values[index], tracked);
+
+ return result;
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteBytes Static Class
+ ///
+ /// This class contains static methods that are used to deal with native
+ /// pointers to memory blocks that logically contain arrays of bytes to be
+ /// used with the SQLite core library.
+ ///
+ internal static class SQLiteBytes
+ {
+ #region Byte Array Helper Methods
+ ///
+ /// Converts a native pointer to a logical array of bytes of the
+ /// specified length into a managed byte array.
+ ///
+ ///
+ /// The native pointer to the logical array of bytes to convert.
+ ///
+ ///
+ /// The length, in bytes, of the logical array of bytes to convert.
+ ///
+ ///
+ /// The managed byte array or null upon failure.
+ ///
+ public static byte[] FromIntPtr(
+ IntPtr pValue,
+ int length
+ )
+ {
+ if (pValue == IntPtr.Zero)
+ return null;
+
+ if (length == 0)
+ return new byte[0];
+
+ byte[] result = new byte[length];
+
+ Marshal.Copy(pValue, result, 0, length);
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a managed byte array into a native pointer to a logical
+ /// array of bytes.
+ ///
+ ///
+ /// The managed byte array to convert.
+ ///
+ ///
+ /// The native pointer to a logical byte array or null upon failure.
+ ///
+ public static IntPtr ToIntPtr(
+ byte[] value
+ )
+ {
+ int length = 0;
+
+ return ToIntPtr(value, ref length);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a managed byte array into a native pointer to a logical
+ /// array of bytes.
+ ///
+ ///
+ /// The managed byte array to convert.
+ ///
+ ///
+ /// The length, in bytes, of the converted logical array of bytes.
+ ///
+ ///
+ /// The native pointer to a logical byte array or null upon failure.
+ ///
+ public static IntPtr ToIntPtr(
+ byte[] value,
+ ref int length
+ )
+ {
+ if (value == null)
+ return IntPtr.Zero;
+
+ length = value.Length;
+
+ if (length == 0)
+ return IntPtr.Zero;
+
+ IntPtr result = SQLiteMemory.Allocate(length);
+
+ if (result == IntPtr.Zero)
+ return IntPtr.Zero;
+
+ Marshal.Copy(value, 0, result, length);
+
+ return result;
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteMarshal Static Class
+ ///
+ /// This class contains static methods that are used to perform several
+ /// low-level data marshalling tasks between native and managed code.
+ ///
+ internal static class SQLiteMarshal
+ {
+ #region IntPtr Helper Methods
+ ///
+ /// Returns a new object instance based on the
+ /// specified object instance and an integer
+ /// offset.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location that the new
+ /// object instance should point to.
+ ///
+ ///
+ /// The new object instance.
+ ///
+ public static IntPtr IntPtrForOffset(
+ IntPtr pointer,
+ int offset
+ )
+ {
+ return new IntPtr(pointer.ToInt64() + offset);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Rounds up an integer size to the next multiple of the alignment.
+ ///
+ ///
+ /// The size, in bytes, to be rounded up.
+ ///
+ ///
+ /// The required alignment for the return value.
+ ///
+ ///
+ /// The size, in bytes, rounded up to the next multiple of the
+ /// alignment. This value may end up being the same as the original
+ /// size.
+ ///
+ public static int RoundUp(
+ int size,
+ int alignment
+ )
+ {
+ int alignmentMinusOne = alignment - 1;
+ return ((size + alignmentMinusOne) & ~alignmentMinusOne);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines the offset, in bytes, of the next structure member.
+ ///
+ ///
+ /// The offset, in bytes, of the current structure member.
+ ///
+ ///
+ /// The size, in bytes, of the current structure member.
+ ///
+ ///
+ /// The alignment, in bytes, of the next structure member.
+ ///
+ ///
+ /// The offset, in bytes, of the next structure member.
+ ///
+ public static int NextOffsetOf(
+ int offset,
+ int size,
+ int alignment
+ )
+ {
+ return RoundUp(offset + size, alignment);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Marshal Read Helper Methods
+ ///
+ /// Reads a value from the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be read is located.
+ ///
+ ///
+ /// The value at the specified memory location.
+ ///
+ public static int ReadInt32(
+ IntPtr pointer,
+ int offset
+ )
+ {
+#if DEBUG
+ CheckAlignment("ReadInt32", pointer, offset, sizeof(int));
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return Marshal.ReadInt32(pointer, offset);
+#else
+ return Marshal.ReadInt32(IntPtrForOffset(pointer, offset));
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Reads a value from the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be read is located.
+ ///
+ ///
+ /// The value at the specified memory location.
+ ///
+ public static long ReadInt64(
+ IntPtr pointer,
+ int offset
+ )
+ {
+#if DEBUG
+ CheckAlignment("ReadInt64", pointer, offset, sizeof(long));
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return Marshal.ReadInt64(pointer, offset);
+#else
+ return Marshal.ReadInt64(IntPtrForOffset(pointer, offset));
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Reads a value from the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// to be read is located.
+ ///
+ ///
+ /// The value at the specified memory location.
+ ///
+ public static double ReadDouble(
+ IntPtr pointer,
+ int offset
+ )
+ {
+#if DEBUG
+ CheckAlignment("ReadDouble", pointer, offset, sizeof(double));
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(
+ pointer, offset));
+#else
+ return BitConverter.ToDouble(BitConverter.GetBytes(
+ Marshal.ReadInt64(IntPtrForOffset(pointer, offset))), 0);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Reads an value from the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be read is located.
+ ///
+ ///
+ /// The value at the specified memory location.
+ ///
+ public static IntPtr ReadIntPtr(
+ IntPtr pointer,
+ int offset
+ )
+ {
+#if DEBUG
+ CheckAlignment("ReadIntPtr", pointer, offset, IntPtr.Size);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ return Marshal.ReadIntPtr(pointer, offset);
+#else
+ return Marshal.ReadIntPtr(IntPtrForOffset(pointer, offset));
+#endif
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Marshal Write Helper Methods
+ ///
+ /// Writes an value to the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be written is located.
+ ///
+ ///
+ /// The value to write.
+ ///
+ public static void WriteInt32(
+ IntPtr pointer,
+ int offset,
+ int value
+ )
+ {
+#if DEBUG
+ CheckAlignment("WriteInt32", pointer, offset, sizeof(int));
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ Marshal.WriteInt32(pointer, offset, value);
+#else
+ Marshal.WriteInt32(IntPtrForOffset(pointer, offset), value);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Writes an value to the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be written is located.
+ ///
+ ///
+ /// The value to write.
+ ///
+ public static void WriteInt64(
+ IntPtr pointer,
+ int offset,
+ long value
+ )
+ {
+#if DEBUG
+ CheckAlignment("WriteInt64", pointer, offset, sizeof(long));
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ Marshal.WriteInt64(pointer, offset, value);
+#else
+ Marshal.WriteInt64(IntPtrForOffset(pointer, offset), value);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Writes a value to the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be written is located.
+ ///
+ ///
+ /// The value to write.
+ ///
+ public static void WriteDouble(
+ IntPtr pointer,
+ int offset,
+ double value
+ )
+ {
+#if DEBUG
+ CheckAlignment("WriteDouble", pointer, offset, sizeof(double));
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ Marshal.WriteInt64(pointer, offset,
+ BitConverter.DoubleToInt64Bits(value));
+#else
+ Marshal.WriteInt64(IntPtrForOffset(pointer, offset),
+ BitConverter.ToInt64(BitConverter.GetBytes(value), 0));
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Writes a value to the specified memory
+ /// location.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the
+ /// value to be written is located.
+ ///
+ ///
+ /// The value to write.
+ ///
+ public static void WriteIntPtr(
+ IntPtr pointer,
+ int offset,
+ IntPtr value
+ )
+ {
+#if DEBUG
+ CheckAlignment(
+ "WriteIntPtr(pointer)", pointer, offset, IntPtr.Size);
+
+ CheckAlignment("WriteIntPtr(value)", value, 0, IntPtr.Size);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ Marshal.WriteIntPtr(pointer, offset, value);
+#else
+ Marshal.WriteIntPtr(IntPtrForOffset(pointer, offset), value);
+#endif
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Object Helper Methods
+ ///
+ /// Generates a hash code value for the object.
+ ///
+ ///
+ /// The object instance used to calculate the hash code.
+ ///
+ ///
+ /// Non-zero if different object instances with the same value should
+ /// generate different hash codes, where applicable. This parameter
+ /// has no effect on the .NET Compact Framework.
+ ///
+ ///
+ /// The hash code value -OR- zero if the object is null.
+ ///
+ public static int GetHashCode(
+ object value,
+ bool identity
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (identity)
+ return RuntimeHelpers.GetHashCode(value);
+#endif
+
+ if (value == null) return 0;
+ return value.GetHashCode();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+#if DEBUG
+ ///
+ /// Attempts to verify that the specified native pointer is properly
+ /// aligned for the size of the data value. If that is not the case,
+ /// a message will be sent to the logging subsystem.
+ ///
+ ///
+ /// The type of operation being performed by the caller. This value
+ /// may be used within diagnostic messages.
+ ///
+ ///
+ /// The object instance representing the base
+ /// memory location.
+ ///
+ ///
+ /// The integer offset from the base memory location where the data
+ /// value to be read or written.
+ ///
+ ///
+ /// The size, in bytes, of the data value.
+ ///
+ internal static void CheckAlignment(
+ string type,
+ IntPtr pointer,
+ int offset,
+ int size
+ )
+ {
+ IntPtr savedPointer = pointer;
+
+ if (offset != 0)
+ pointer = new IntPtr(pointer.ToInt64() + offset);
+
+ if ((pointer.ToInt64() % size) != 0)
+ {
+ SQLiteLog.LogMessage(SQLiteErrorCode.Warning,
+ HelperMethods.StringFormat(CultureInfo.CurrentCulture,
+ "{0}: pointer {1} and offset {2} not aligned to {3}: {4}",
+ type, savedPointer, offset, size,
+#if !PLATFORM_COMPACTFRAMEWORK
+ Environment.StackTrace
+#else
+ null
+#endif
+ ));
+ }
+ }
+#endif
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteModule Base Class
+ ///
+ /// This class represents a managed virtual table module implementation.
+ /// It is not sealed and must be used as the base class for any
+ /// user-defined virtual table module classes implemented in managed code.
+ ///
+ public abstract class SQLiteModule :
+ ISQLiteManagedModule, /*ISQLiteNativeModule,*/
+ IDisposable /* NOT SEALED */
+ {
+ #region SQLiteNativeModule Private Class
+ ///
+ /// This class implements the
+ /// interface by forwarding those method calls to the
+ /// object instance it contains. If the
+ /// contained object instance is null, all
+ /// the methods simply generate an
+ /// error.
+ ///
+ private sealed class SQLiteNativeModule :
+ ISQLiteNativeModule, IDisposable
+ {
+ #region Private Constants
+ ///
+ /// This is the value that is always used for the "logErrors"
+ /// parameter to the various static error handling methods provided
+ /// by the class.
+ ///
+ private const bool DefaultLogErrors = true;
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// This is the value that is always used for the "logExceptions"
+ /// parameter to the various static error handling methods provided
+ /// by the class.
+ ///
+ private const bool DefaultLogExceptions = true;
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// This is the error message text used when the contained
+ /// object instance is not available
+ /// for any reason.
+ ///
+ private const string ModuleNotAvailableErrorMessage =
+ "native module implementation not available";
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// The object instance used to provide
+ /// an implementation of the
+ /// interface.
+ ///
+ private SQLiteModule module;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The object instance used to provide
+ /// an implementation of the
+ /// interface.
+ ///
+ public SQLiteNativeModule(
+ SQLiteModule module
+ )
+ {
+ this.module = module;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region Private Static Methods
+ ///
+ /// Sets the table error message to one that indicates the native
+ /// module implementation is not available.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The value of .
+ ///
+ private static SQLiteErrorCode ModuleNotAvailableTableError(
+ IntPtr pVtab
+ )
+ {
+ SetTableError(null, pVtab, DefaultLogErrors,
+ DefaultLogExceptions, ModuleNotAvailableErrorMessage);
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the table error message to one that indicates the native
+ /// module implementation is not available.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived
+ /// structure.
+ ///
+ ///
+ /// The value of .
+ ///
+ private static SQLiteErrorCode ModuleNotAvailableCursorError(
+ IntPtr pCursor
+ )
+ {
+ SetCursorError(null, pCursor, DefaultLogErrors,
+ DefaultLogExceptions, ModuleNotAvailableErrorMessage);
+
+ return SQLiteErrorCode.Error;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeModule Members
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xCreate(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ {
+ pError = SQLiteString.Utf8IntPtrFromString(
+ ModuleNotAvailableErrorMessage);
+
+ return SQLiteErrorCode.Error;
+ }
+
+ return module.xCreate(
+ pDb, pAux, argc, argv, ref pVtab, ref pError);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xConnect(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ {
+ pError = SQLiteString.Utf8IntPtrFromString(
+ ModuleNotAvailableErrorMessage);
+
+ return SQLiteErrorCode.Error;
+ }
+
+ return module.xConnect(
+ pDb, pAux, argc, argv, ref pVtab, ref pError);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xBestIndex(
+ IntPtr pVtab,
+ IntPtr pIndex
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xBestIndex(pVtab, pIndex);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xDisconnect(
+ IntPtr pVtab
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xDisconnect(pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xDestroy(
+ IntPtr pVtab
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xDestroy(pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xOpen(
+ IntPtr pVtab,
+ ref IntPtr pCursor
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xOpen(pVtab, ref pCursor);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xClose(
+ IntPtr pCursor
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableCursorError(pCursor);
+
+ return module.xClose(pCursor);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xFilter(
+ IntPtr pCursor,
+ int idxNum,
+ IntPtr idxStr,
+ int argc,
+ IntPtr argv
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableCursorError(pCursor);
+
+ return module.xFilter(pCursor, idxNum, idxStr, argc, argv);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xNext(
+ IntPtr pCursor
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableCursorError(pCursor);
+
+ return module.xNext(pCursor);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public int xEof(
+ IntPtr pCursor
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ {
+ ModuleNotAvailableCursorError(pCursor);
+ return 1;
+ }
+
+ return module.xEof(pCursor);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xColumn(
+ IntPtr pCursor,
+ IntPtr pContext,
+ int index
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableCursorError(pCursor);
+
+ return module.xColumn(pCursor, pContext, index);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xRowId(
+ IntPtr pCursor,
+ ref long rowId
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableCursorError(pCursor);
+
+ return module.xRowId(pCursor, ref rowId);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xUpdate(
+ IntPtr pVtab,
+ int argc,
+ IntPtr argv,
+ ref long rowId
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xUpdate(pVtab, argc, argv, ref rowId);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xBegin(
+ IntPtr pVtab
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xBegin(pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xSync(
+ IntPtr pVtab
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xSync(pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xCommit(
+ IntPtr pVtab
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xCommit(pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xRollback(
+ IntPtr pVtab
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xRollback(pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public int xFindFunction(
+ IntPtr pVtab,
+ int nArg,
+ IntPtr zName,
+ ref SQLiteCallback callback,
+ ref IntPtr pClientData
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ {
+ ModuleNotAvailableTableError(pVtab);
+ return 0;
+ }
+
+ return module.xFindFunction(
+ pVtab, nArg, zName, ref callback, ref pClientData);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xRename(
+ IntPtr pVtab,
+ IntPtr zNew
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xRename(pVtab, zNew);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xSavepoint(
+ IntPtr pVtab,
+ int iSavepoint
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xSavepoint(pVtab, iSavepoint);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xRelease(
+ IntPtr pVtab,
+ int iSavepoint
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xRelease(pVtab, iSavepoint);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public SQLiteErrorCode xRollbackTo(
+ IntPtr pVtab,
+ int iSavepoint
+ )
+ {
+ //
+ // NOTE: Called by native code.
+ //
+ // CheckDisposed(); /* EXEMPT */
+
+ if (module == null)
+ return ModuleNotAvailableTableError(pVtab);
+
+ return module.xRollbackTo(pVtab, iSavepoint);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteNativeModule).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is being
+ /// called from the finalizer.
+ ///
+ private /* protected virtual */ void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ //
+ // NOTE: The module is not owned by us; therefore, do not
+ // dispose it.
+ //
+ if (module != null)
+ module = null;
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteNativeModule()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constants
+ ///
+ /// The default version of the native sqlite3_module structure in use.
+ ///
+ private static readonly int DefaultModuleVersion = 2;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// This field is used to store the native sqlite3_module structure
+ /// associated with this object instance.
+ ///
+ private UnsafeNativeMethods.sqlite3_module nativeModule;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This field is used to store the destructor delegate to be passed to
+ /// the SQLite core library via the sqlite3_create_disposable_module()
+ /// function.
+ ///
+ private UnsafeNativeMethods.xDestroyModule destroyModule;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This field is used to store a pointer to the native sqlite3_module
+ /// structure returned by the sqlite3_create_disposable_module
+ /// function.
+ ///
+ private IntPtr disposableModule;
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// This field is used to hold the block of native memory that contains
+ /// the native sqlite3_module structure associated with this object
+ /// instance when running on the .NET Compact Framework.
+ ///
+ private IntPtr pNativeModule;
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This field is used to store the virtual table instances associated
+ /// with this module. The native pointer to the sqlite3_vtab derived
+ /// structure is used to key into this collection.
+ ///
+ private Dictionary tables;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This field is used to store the virtual table cursor instances
+ /// associated with this module. The native pointer to the
+ /// sqlite3_vtab_cursor derived structure is used to key into this
+ /// collection.
+ ///
+ private Dictionary cursors;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This field is used to store the virtual table function instances
+ /// associated with this module. The case-insensitive function name
+ /// and the number of arguments (with -1 meaning "any") are used to
+ /// construct the string that is used to key into this collection.
+ ///
+ private Dictionary functions;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ public SQLiteModule(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ this.name = name;
+ this.tables = new Dictionary();
+ this.cursors = new Dictionary();
+ this.functions = new Dictionary();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Internal Methods
+ ///
+ /// Calls the native SQLite core library in order to create a new
+ /// disposable module containing the implementation of a virtual table.
+ ///
+ ///
+ /// The native database connection pointer to use.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ internal bool CreateDisposableModule(
+ IntPtr pDb
+ )
+ {
+ if (disposableModule != IntPtr.Zero)
+ return true;
+
+ IntPtr pName = IntPtr.Zero;
+
+ try
+ {
+ pName = SQLiteString.Utf8IntPtrFromString(name);
+
+ UnsafeNativeMethods.sqlite3_module nativeModule =
+ AllocateNativeModule();
+
+ destroyModule = new UnsafeNativeMethods.xDestroyModule(
+ xDestroyModule);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ disposableModule =
+ UnsafeNativeMethods.sqlite3_create_disposable_module(
+ pDb, pName, ref nativeModule, IntPtr.Zero, destroyModule);
+
+ return (disposableModule != IntPtr.Zero);
+#elif !SQLITE_STANDARD
+ disposableModule =
+ UnsafeNativeMethods.sqlite3_create_disposable_module_interop(
+ pDb, pName, AllocateNativeModuleInterop(),
+ nativeModule.iVersion, nativeModule.xCreate,
+ nativeModule.xConnect, nativeModule.xBestIndex,
+ nativeModule.xDisconnect, nativeModule.xDestroy,
+ nativeModule.xOpen, nativeModule.xClose,
+ nativeModule.xFilter, nativeModule.xNext,
+ nativeModule.xEof, nativeModule.xColumn,
+ nativeModule.xRowId, nativeModule.xUpdate,
+ nativeModule.xBegin, nativeModule.xSync,
+ nativeModule.xCommit, nativeModule.xRollback,
+ nativeModule.xFindFunction, nativeModule.xRename,
+ nativeModule.xSavepoint, nativeModule.xRelease,
+ nativeModule.xRollbackTo, IntPtr.Zero, destroyModule);
+
+ return (disposableModule != IntPtr.Zero);
+#else
+ throw new NotImplementedException();
+#endif
+ }
+ finally
+ {
+ if (pName != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pName);
+ pName = IntPtr.Zero;
+ }
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// This method is called by the SQLite core library when the native
+ /// module associated with this object instance is being destroyed due
+ /// to its parent connection being closed. It may also be called by
+ /// the "vtshim" module if/when the sqlite3_dispose_module() function
+ /// is called.
+ ///
+ ///
+ /// The native user-data pointer associated with this module, as it was
+ /// provided to the SQLite core library when the native module instance
+ /// was created.
+ ///
+ private void xDestroyModule(
+ IntPtr pClientData /* NOT USED */
+ )
+ {
+ //
+ // NOTE: At this point, just make sure that this native module
+ // handle is not reused, nor passed into the native
+ // sqlite3_dispose_module() function later (i.e. if/when
+ // the Dispose() method of this object instance is called).
+ //
+ disposableModule = IntPtr.Zero;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates and returns the native sqlite_module structure using the
+ /// configured (or default)
+ /// interface implementation.
+ ///
+ ///
+ /// The native sqlite_module structure using the configured (or
+ /// default) interface
+ /// implementation.
+ ///
+ private UnsafeNativeMethods.sqlite3_module AllocateNativeModule()
+ {
+ return AllocateNativeModule(GetNativeModuleImpl());
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Creates and returns a memory block obtained from the SQLite core
+ /// library used to store the native sqlite3_module structure for this
+ /// object instance when running on the .NET Compact Framework.
+ ///
+ ///
+ /// The native pointer to the native sqlite3_module structure.
+ ///
+ private IntPtr AllocateNativeModuleInterop()
+ {
+ if (pNativeModule == IntPtr.Zero)
+ {
+ //
+ // HACK: No easy way to determine the size of the native
+ // sqlite_module structure when running on the .NET
+ // Compact Framework; therefore, just base the size
+ // on what we know:
+ //
+ // There is one integer member.
+ // There are 22 function pointer members.
+ //
+ pNativeModule = SQLiteMemory.Allocate(23 * IntPtr.Size);
+
+ if (pNativeModule == IntPtr.Zero)
+ throw new OutOfMemoryException("sqlite3_module");
+ }
+
+ return pNativeModule;
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates and returns the native sqlite_module structure using the
+ /// specified interface
+ /// implementation.
+ ///
+ ///
+ /// The interface implementation to
+ /// use.
+ ///
+ ///
+ /// The native sqlite_module structure using the specified
+ /// interface implementation.
+ ///
+ private UnsafeNativeMethods.sqlite3_module AllocateNativeModule(
+ ISQLiteNativeModule module
+ )
+ {
+ nativeModule = new UnsafeNativeMethods.sqlite3_module();
+ nativeModule.iVersion = DefaultModuleVersion;
+
+ if (module != null)
+ {
+ nativeModule.xCreate = new UnsafeNativeMethods.xCreate(
+ module.xCreate);
+
+ nativeModule.xConnect = new UnsafeNativeMethods.xConnect(
+ module.xConnect);
+
+ nativeModule.xBestIndex = new UnsafeNativeMethods.xBestIndex(
+ module.xBestIndex);
+
+ nativeModule.xDisconnect = new UnsafeNativeMethods.xDisconnect(
+ module.xDisconnect);
+
+ nativeModule.xDestroy = new UnsafeNativeMethods.xDestroy(
+ module.xDestroy);
+
+ nativeModule.xOpen = new UnsafeNativeMethods.xOpen(
+ module.xOpen);
+
+ nativeModule.xClose = new UnsafeNativeMethods.xClose(
+ module.xClose);
+
+ nativeModule.xFilter = new UnsafeNativeMethods.xFilter(
+ module.xFilter);
+
+ nativeModule.xNext = new UnsafeNativeMethods.xNext(
+ module.xNext);
+
+ nativeModule.xEof = new UnsafeNativeMethods.xEof(module.xEof);
+
+ nativeModule.xColumn = new UnsafeNativeMethods.xColumn(
+ module.xColumn);
+
+ nativeModule.xRowId = new UnsafeNativeMethods.xRowId(
+ module.xRowId);
+
+ nativeModule.xUpdate = new UnsafeNativeMethods.xUpdate(
+ module.xUpdate);
+
+ nativeModule.xBegin = new UnsafeNativeMethods.xBegin(
+ module.xBegin);
+
+ nativeModule.xSync = new UnsafeNativeMethods.xSync(
+ module.xSync);
+
+ nativeModule.xCommit = new UnsafeNativeMethods.xCommit(
+ module.xCommit);
+
+ nativeModule.xRollback = new UnsafeNativeMethods.xRollback(
+ module.xRollback);
+
+ nativeModule.xFindFunction = new UnsafeNativeMethods.xFindFunction(
+ module.xFindFunction);
+
+ nativeModule.xRename = new UnsafeNativeMethods.xRename(
+ module.xRename);
+
+ nativeModule.xSavepoint = new UnsafeNativeMethods.xSavepoint(
+ module.xSavepoint);
+
+ nativeModule.xRelease = new UnsafeNativeMethods.xRelease(
+ module.xRelease);
+
+ nativeModule.xRollbackTo = new UnsafeNativeMethods.xRollbackTo(
+ module.xRollbackTo);
+ }
+ else
+ {
+ nativeModule.xCreate = new UnsafeNativeMethods.xCreate(
+ xCreate);
+
+ nativeModule.xConnect = new UnsafeNativeMethods.xConnect(
+ xConnect);
+
+ nativeModule.xBestIndex = new UnsafeNativeMethods.xBestIndex(
+ xBestIndex);
+
+ nativeModule.xDisconnect = new UnsafeNativeMethods.xDisconnect(
+ xDisconnect);
+
+ nativeModule.xDestroy = new UnsafeNativeMethods.xDestroy(
+ xDestroy);
+
+ nativeModule.xOpen = new UnsafeNativeMethods.xOpen(xOpen);
+ nativeModule.xClose = new UnsafeNativeMethods.xClose(xClose);
+
+ nativeModule.xFilter = new UnsafeNativeMethods.xFilter(
+ xFilter);
+
+ nativeModule.xNext = new UnsafeNativeMethods.xNext(xNext);
+ nativeModule.xEof = new UnsafeNativeMethods.xEof(xEof);
+
+ nativeModule.xColumn = new UnsafeNativeMethods.xColumn(
+ xColumn);
+
+ nativeModule.xRowId = new UnsafeNativeMethods.xRowId(xRowId);
+
+ nativeModule.xUpdate = new UnsafeNativeMethods.xUpdate(
+ xUpdate);
+
+ nativeModule.xBegin = new UnsafeNativeMethods.xBegin(xBegin);
+ nativeModule.xSync = new UnsafeNativeMethods.xSync(xSync);
+
+ nativeModule.xCommit = new UnsafeNativeMethods.xCommit(
+ xCommit);
+
+ nativeModule.xRollback = new UnsafeNativeMethods.xRollback(
+ xRollback);
+
+ nativeModule.xFindFunction = new UnsafeNativeMethods.xFindFunction(
+ xFindFunction);
+
+ nativeModule.xRename = new UnsafeNativeMethods.xRename(
+ xRename);
+
+ nativeModule.xSavepoint = new UnsafeNativeMethods.xSavepoint(
+ xSavepoint);
+
+ nativeModule.xRelease = new UnsafeNativeMethods.xRelease(
+ xRelease);
+
+ nativeModule.xRollbackTo = new UnsafeNativeMethods.xRollbackTo(
+ xRollbackTo);
+ }
+
+ return nativeModule;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates a copy of the specified
+ /// object instance,
+ /// using default implementations for the contained delegates when
+ /// necessary.
+ ///
+ ///
+ /// The object
+ /// instance to copy.
+ ///
+ ///
+ /// The new object
+ /// instance.
+ ///
+ private UnsafeNativeMethods.sqlite3_module CopyNativeModule(
+ UnsafeNativeMethods.sqlite3_module module
+ )
+ {
+ UnsafeNativeMethods.sqlite3_module newModule =
+ new UnsafeNativeMethods.sqlite3_module();
+
+ newModule.iVersion = module.iVersion;
+
+ newModule.xCreate = new UnsafeNativeMethods.xCreate(
+ (module.xCreate != null) ? module.xCreate : xCreate);
+
+ newModule.xConnect = new UnsafeNativeMethods.xConnect(
+ (module.xConnect != null) ? module.xConnect : xConnect);
+
+ newModule.xBestIndex = new UnsafeNativeMethods.xBestIndex(
+ (module.xBestIndex != null) ? module.xBestIndex : xBestIndex);
+
+ newModule.xDisconnect = new UnsafeNativeMethods.xDisconnect(
+ (module.xDisconnect != null) ? module.xDisconnect :
+ xDisconnect);
+
+ newModule.xDestroy = new UnsafeNativeMethods.xDestroy(
+ (module.xDestroy != null) ? module.xDestroy : xDestroy);
+
+ newModule.xOpen = new UnsafeNativeMethods.xOpen(
+ (module.xOpen != null) ? module.xOpen : xOpen);
+
+ newModule.xClose = new UnsafeNativeMethods.xClose(
+ (module.xClose != null) ? module.xClose : xClose);
+
+ newModule.xFilter = new UnsafeNativeMethods.xFilter(
+ (module.xFilter != null) ? module.xFilter : xFilter);
+
+ newModule.xNext = new UnsafeNativeMethods.xNext(
+ (module.xNext != null) ? module.xNext : xNext);
+
+ newModule.xEof = new UnsafeNativeMethods.xEof(
+ (module.xEof != null) ? module.xEof : xEof);
+
+ newModule.xColumn = new UnsafeNativeMethods.xColumn(
+ (module.xColumn != null) ? module.xColumn : xColumn);
+
+ newModule.xRowId = new UnsafeNativeMethods.xRowId(
+ (module.xRowId != null) ? module.xRowId : xRowId);
+
+ newModule.xUpdate = new UnsafeNativeMethods.xUpdate(
+ (module.xUpdate != null) ? module.xUpdate : xUpdate);
+
+ newModule.xBegin = new UnsafeNativeMethods.xBegin(
+ (module.xBegin != null) ? module.xBegin : xBegin);
+
+ newModule.xSync = new UnsafeNativeMethods.xSync(
+ (module.xSync != null) ? module.xSync : xSync);
+
+ newModule.xCommit = new UnsafeNativeMethods.xCommit(
+ (module.xCommit != null) ? module.xCommit : xCommit);
+
+ newModule.xRollback = new UnsafeNativeMethods.xRollback(
+ (module.xRollback != null) ? module.xRollback : xRollback);
+
+ newModule.xFindFunction = new UnsafeNativeMethods.xFindFunction(
+ (module.xFindFunction != null) ? module.xFindFunction :
+ xFindFunction);
+
+ newModule.xRename = new UnsafeNativeMethods.xRename(
+ (module.xRename != null) ? module.xRename : xRename);
+
+ newModule.xSavepoint = new UnsafeNativeMethods.xSavepoint(
+ (module.xSavepoint != null) ? module.xSavepoint : xSavepoint);
+
+ newModule.xRelease = new UnsafeNativeMethods.xRelease(
+ (module.xRelease != null) ? module.xRelease : xRelease);
+
+ newModule.xRollbackTo = new UnsafeNativeMethods.xRollbackTo(
+ (module.xRollbackTo != null) ? module.xRollbackTo :
+ xRollbackTo);
+
+ return newModule;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Calls one of the virtual table initialization methods.
+ ///
+ ///
+ /// Non-zero to call the
+ /// method; otherwise, the
+ /// method will be called.
+ ///
+ ///
+ /// The native database connection handle.
+ ///
+ ///
+ /// The original native pointer value that was provided to the
+ /// sqlite3_create_module(), sqlite3_create_module_v2() or
+ /// sqlite3_create_disposable_module() functions.
+ ///
+ ///
+ /// The number of arguments from the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// The array of string arguments from the CREATE VIRTUAL TABLE
+ /// statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to point to the newly
+ /// created native sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to point to the error
+ /// message, with the underlying memory having been obtained from the
+ /// sqlite3_malloc() function.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ private SQLiteErrorCode CreateOrConnect(
+ bool create,
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ )
+ {
+ try
+ {
+ string fileName = SQLiteString.StringFromUtf8IntPtr(
+ UnsafeNativeMethods.sqlite3_db_filename(pDb, IntPtr.Zero));
+
+ using (SQLiteConnection connection = new SQLiteConnection(
+ pDb, fileName, false))
+ {
+ SQLiteVirtualTable table = null;
+ string error = null;
+
+ if ((create && Create(connection, pAux,
+ SQLiteString.StringArrayFromUtf8SizeAndIntPtr(argc,
+ argv), ref table, ref error) == SQLiteErrorCode.Ok) ||
+ (!create && Connect(connection, pAux,
+ SQLiteString.StringArrayFromUtf8SizeAndIntPtr(argc,
+ argv), ref table, ref error) == SQLiteErrorCode.Ok))
+ {
+ if (table != null)
+ {
+ pVtab = TableToIntPtr(table);
+ return SQLiteErrorCode.Ok;
+ }
+ else
+ {
+ pError = SQLiteString.Utf8IntPtrFromString(
+ "no table was created");
+ }
+ }
+ else
+ {
+ pError = SQLiteString.Utf8IntPtrFromString(error);
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ pError = SQLiteString.Utf8IntPtrFromString(e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Calls one of the virtual table finalization methods.
+ ///
+ ///
+ /// Non-zero to call the
+ /// method; otherwise, the
+ /// method will be
+ /// called.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ private SQLiteErrorCode DestroyOrDisconnect(
+ bool destroy,
+ IntPtr pVtab
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ {
+ if ((destroy && (Destroy(table) == SQLiteErrorCode.Ok)) ||
+ (!destroy && (Disconnect(table) == SQLiteErrorCode.Ok)))
+ {
+ if (tables != null)
+ tables.Remove(pVtab);
+
+ return SQLiteErrorCode.Ok;
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ //
+ // NOTE: At this point, there is no way to report the error
+ // condition back to the caller; therefore, use the
+ // logging facility instead.
+ //
+ try
+ {
+ if (LogExceptionsNoThrow)
+ {
+ /* throw */
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ destroy ? "xDestroy" : "xDisconnect", e));
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ finally
+ {
+ FreeTable(pVtab);
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Static Error Handling Helper Methods
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The object instance to be used.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Non-zero if this error message should also be logged using the
+ /// class.
+ ///
+ ///
+ /// Non-zero if caught exceptions should be logged using the
+ /// class.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ private static bool SetTableError(
+ SQLiteModule module,
+ IntPtr pVtab,
+ bool logErrors,
+ bool logExceptions,
+ string error
+ )
+ {
+ try
+ {
+ if (logErrors && (error != null))
+ {
+ SQLiteLog.LogMessage(SQLiteErrorCode.Error,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Virtual table error: {0}", error)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ bool success = false;
+ IntPtr pNewError = IntPtr.Zero;
+
+ try
+ {
+ if (pVtab == IntPtr.Zero)
+ return false;
+
+ int offset = 0;
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ IntPtr pOldError = SQLiteMarshal.ReadIntPtr(pVtab, offset);
+
+ if (pOldError != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pOldError); pOldError = IntPtr.Zero;
+ SQLiteMarshal.WriteIntPtr(pVtab, offset, pOldError);
+ }
+
+ if (error == null)
+ return true;
+
+ pNewError = SQLiteString.Utf8IntPtrFromString(error);
+ SQLiteMarshal.WriteIntPtr(pVtab, offset, pNewError);
+ success = true;
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ try
+ {
+ if (logExceptions)
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "SetTableError", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+ finally
+ {
+ if (!success && (pNewError != IntPtr.Zero))
+ {
+ SQLiteMemory.Free(pNewError);
+ pNewError = IntPtr.Zero;
+ }
+ }
+
+ return success;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The object instance to be used.
+ ///
+ ///
+ /// The object instance used to
+ /// lookup the native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Non-zero if this error message should also be logged using the
+ /// class.
+ ///
+ ///
+ /// Non-zero if caught exceptions should be logged using the
+ /// class.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ private static bool SetTableError(
+ SQLiteModule module,
+ SQLiteVirtualTable table,
+ bool logErrors,
+ bool logExceptions,
+ string error
+ )
+ {
+ if (table == null)
+ return false;
+
+ IntPtr pVtab = table.NativeHandle;
+
+ if (pVtab == IntPtr.Zero)
+ return false;
+
+ return SetTableError(
+ module, pVtab, logErrors, logExceptions, error);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The object instance to be used.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure
+ /// used to get the native pointer to the sqlite3_vtab derived
+ /// structure.
+ ///
+ ///
+ /// Non-zero if this error message should also be logged using the
+ /// class.
+ ///
+ ///
+ /// Non-zero if caught exceptions should be logged using the
+ /// class.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ private static bool SetCursorError(
+ SQLiteModule module,
+ IntPtr pCursor,
+ bool logErrors,
+ bool logExceptions,
+ string error
+ )
+ {
+ if (pCursor == IntPtr.Zero)
+ return false;
+
+ IntPtr pVtab = TableFromCursor(module, pCursor);
+
+ if (pVtab == IntPtr.Zero)
+ return false;
+
+ return SetTableError(
+ module, pVtab, logErrors, logExceptions, error);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The object instance to be used.
+ ///
+ ///
+ /// The object instance used to
+ /// lookup the native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// Non-zero if this error message should also be logged using the
+ /// class.
+ ///
+ ///
+ /// Non-zero if caught exceptions should be logged using the
+ /// class.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ private static bool SetCursorError(
+ SQLiteModule module,
+ SQLiteVirtualTableCursor cursor,
+ bool logErrors,
+ bool logExceptions,
+ string error
+ )
+ {
+ if (cursor == null)
+ return false;
+
+ IntPtr pCursor = cursor.NativeHandle;
+
+ if (pCursor == IntPtr.Zero)
+ return false;
+
+ return SetCursorError(
+ module, pCursor, logErrors, logExceptions, error);
+ }
+ #endregion
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Members
+ #region Module Helper Methods
+ ///
+ /// Gets and returns the interface
+ /// implementation to be used when creating the native sqlite3_module
+ /// structure. Derived classes may override this method to supply an
+ /// alternate implementation for the
+ /// interface.
+ ///
+ ///
+ /// The interface implementation to
+ /// be used when populating the native sqlite3_module structure. If
+ /// the returned value is null, the private methods provided by the
+ /// class and relating to the
+ /// interface will be used to
+ /// create the necessary delegates.
+ ///
+ protected virtual ISQLiteNativeModule GetNativeModuleImpl()
+ {
+ return null; /* NOTE: Use the built-in default delegates. */
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Creates and returns the
+ /// interface implementation corresponding to the current
+ /// object instance.
+ ///
+ ///
+ /// The interface implementation
+ /// corresponding to the current object
+ /// instance.
+ ///
+ protected virtual ISQLiteNativeModule CreateNativeModuleImpl()
+ {
+ return new SQLiteNativeModule(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Native Table Helper Methods
+ ///
+ /// Allocates a native sqlite3_vtab derived structure and returns a
+ /// native pointer to it.
+ ///
+ ///
+ /// A native pointer to a native sqlite3_vtab derived structure.
+ ///
+ protected virtual IntPtr AllocateTable()
+ {
+ int size = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_vtab));
+
+ return SQLiteMemory.Allocate(size);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Zeros out the fields of a native sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The native pointer to the native sqlite3_vtab derived structure to
+ /// zero.
+ ///
+ protected virtual void ZeroTable(
+ IntPtr pVtab
+ )
+ {
+ if (pVtab == IntPtr.Zero)
+ return;
+
+ int offset = 0;
+
+ SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, IntPtr.Size, sizeof(int));
+
+ SQLiteMarshal.WriteInt32(pVtab, offset, 0);
+
+ offset = SQLiteMarshal.NextOffsetOf(
+ offset, sizeof(int), IntPtr.Size);
+
+ SQLiteMarshal.WriteIntPtr(pVtab, offset, IntPtr.Zero);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Frees a native sqlite3_vtab structure using the provided native
+ /// pointer to it.
+ ///
+ ///
+ /// A native pointer to a native sqlite3_vtab derived structure.
+ ///
+ protected virtual void FreeTable(
+ IntPtr pVtab
+ )
+ {
+ SetTableError(pVtab, null);
+ SQLiteMemory.Free(pVtab);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Native Cursor Helper Methods
+ ///
+ /// Allocates a native sqlite3_vtab_cursor derived structure and
+ /// returns a native pointer to it.
+ ///
+ ///
+ /// A native pointer to a native sqlite3_vtab_cursor derived structure.
+ ///
+ protected virtual IntPtr AllocateCursor()
+ {
+ int size = Marshal.SizeOf(typeof(
+ UnsafeNativeMethods.sqlite3_vtab_cursor));
+
+ return SQLiteMemory.Allocate(size);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Frees a native sqlite3_vtab_cursor structure using the provided
+ /// native pointer to it.
+ ///
+ ///
+ /// A native pointer to a native sqlite3_vtab_cursor derived structure.
+ ///
+ protected virtual void FreeCursor(
+ IntPtr pCursor
+ )
+ {
+ SQLiteMemory.Free(pCursor);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Static Table Lookup Methods
+ ///
+ /// Reads and returns the native pointer to the sqlite3_vtab derived
+ /// structure based on the native pointer to the sqlite3_vtab_cursor
+ /// derived structure.
+ ///
+ ///
+ /// The object instance to be used.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure
+ /// from which to read the native pointer to the sqlite3_vtab derived
+ /// structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure -OR-
+ /// if it cannot be determined.
+ ///
+ private static IntPtr TableFromCursor(
+ SQLiteModule module,
+ IntPtr pCursor
+ )
+ {
+ if (pCursor == IntPtr.Zero)
+ return IntPtr.Zero;
+
+ return Marshal.ReadIntPtr(pCursor);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Table Lookup Methods
+ ///
+ /// Reads and returns the native pointer to the sqlite3_vtab derived
+ /// structure based on the native pointer to the sqlite3_vtab_cursor
+ /// derived structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure
+ /// from which to read the native pointer to the sqlite3_vtab derived
+ /// structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure -OR-
+ /// if it cannot be determined.
+ ///
+ protected virtual IntPtr TableFromCursor(
+ IntPtr pCursor
+ )
+ {
+ return TableFromCursor(this, pCursor);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Looks up and returns the object
+ /// instance based on the native pointer to the sqlite3_vtab derived
+ /// structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The object instance or null if
+ /// the corresponding one cannot be found.
+ ///
+ protected virtual SQLiteVirtualTable TableFromIntPtr(
+ IntPtr pVtab
+ )
+ {
+ if (pVtab == IntPtr.Zero)
+ {
+ SetTableError(pVtab, "invalid native table");
+ return null;
+ }
+
+ SQLiteVirtualTable table;
+
+ if ((tables != null) &&
+ tables.TryGetValue(pVtab, out table))
+ {
+ return table;
+ }
+
+ SetTableError(pVtab, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "managed table for {0} not found", pVtab));
+
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Allocates and returns a native pointer to a sqlite3_vtab derived
+ /// structure and creates an association between it and the specified
+ /// object instance.
+ ///
+ ///
+ /// The object instance to be used
+ /// when creating the association.
+ ///
+ ///
+ /// The native pointer to a sqlite3_vtab derived structure or
+ /// if the method fails for any reason.
+ ///
+ protected virtual IntPtr TableToIntPtr(
+ SQLiteVirtualTable table
+ )
+ {
+ if ((table == null) || (tables == null))
+ return IntPtr.Zero;
+
+ IntPtr pVtab = IntPtr.Zero;
+ bool success = false;
+
+ try
+ {
+ pVtab = AllocateTable();
+
+ if (pVtab != IntPtr.Zero)
+ {
+ ZeroTable(pVtab);
+ table.NativeHandle = pVtab;
+ tables.Add(pVtab, table);
+ success = true;
+ }
+ }
+ finally
+ {
+ if (!success && (pVtab != IntPtr.Zero))
+ {
+ FreeTable(pVtab);
+ pVtab = IntPtr.Zero;
+ }
+ }
+
+ return pVtab;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Cursor Lookup Methods
+ ///
+ /// Looks up and returns the
+ /// object instance based on the native pointer to the
+ /// sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab_cursor derived structure.
+ ///
+ ///
+ /// The object instance or null
+ /// if the corresponding one cannot be found.
+ ///
+ protected virtual SQLiteVirtualTableCursor CursorFromIntPtr(
+ IntPtr pVtab,
+ IntPtr pCursor
+ )
+ {
+ if (pCursor == IntPtr.Zero)
+ {
+ SetTableError(pVtab, "invalid native cursor");
+ return null;
+ }
+
+ SQLiteVirtualTableCursor cursor;
+
+ if ((cursors != null) &&
+ cursors.TryGetValue(pCursor, out cursor))
+ {
+ return cursor;
+ }
+
+ SetTableError(pVtab, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "managed cursor for {0} not found", pCursor));
+
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Allocates and returns a native pointer to a sqlite3_vtab_cursor
+ /// derived structure and creates an association between it and the
+ /// specified object instance.
+ ///
+ ///
+ /// The object instance to be
+ /// used when creating the association.
+ ///
+ ///
+ /// The native pointer to a sqlite3_vtab_cursor derived structure or
+ /// if the method fails for any reason.
+ ///
+ protected virtual IntPtr CursorToIntPtr(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ if ((cursor == null) || (cursors == null))
+ return IntPtr.Zero;
+
+ IntPtr pCursor = IntPtr.Zero;
+ bool success = false;
+
+ try
+ {
+ pCursor = AllocateCursor();
+
+ if (pCursor != IntPtr.Zero)
+ {
+ cursor.NativeHandle = pCursor;
+ cursors.Add(pCursor, cursor);
+ success = true;
+ }
+ }
+ finally
+ {
+ if (!success && (pCursor != IntPtr.Zero))
+ {
+ FreeCursor(pCursor);
+ pCursor = IntPtr.Zero;
+ }
+ }
+
+ return pCursor;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Function Lookup Methods
+ ///
+ /// Deterimines the key that should be used to identify and store the
+ /// object instance for the virtual table
+ /// (i.e. to be returned via the
+ /// method).
+ ///
+ ///
+ /// The number of arguments to the virtual table function.
+ ///
+ ///
+ /// The name of the virtual table function.
+ ///
+ ///
+ /// The object instance associated with
+ /// this virtual table function.
+ ///
+ ///
+ /// The string that should be used to identify and store the virtual
+ /// table function instance. This method cannot return null. If null
+ /// is returned from this method, the behavior is undefined.
+ ///
+ protected virtual string GetFunctionKey(
+ int argumentCount,
+ string name,
+ SQLiteFunction function
+ )
+ {
+ return HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture,
+ "{0}:{1}", argumentCount, name);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Table Declaration Helper Methods
+ ///
+ /// Attempts to declare the schema for the virtual table using the
+ /// specified database connection.
+ ///
+ ///
+ /// The object instance to use when
+ /// declaring the schema of the virtual table. This parameter may not
+ /// be null.
+ ///
+ ///
+ /// The string containing the CREATE TABLE statement that completely
+ /// describes the schema for the virtual table. This parameter may not
+ /// be null.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to contain an error
+ /// message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ protected virtual SQLiteErrorCode DeclareTable(
+ SQLiteConnection connection,
+ string sql,
+ ref string error
+ )
+ {
+ if (connection == null)
+ {
+ error = "invalid connection";
+ return SQLiteErrorCode.Error;
+ }
+
+ SQLiteBase sqliteBase = connection._sql;
+
+ if (sqliteBase == null)
+ {
+ error = "connection has invalid handle";
+ return SQLiteErrorCode.Error;
+ }
+
+ if (sql == null)
+ {
+ error = "invalid SQL statement";
+ return SQLiteErrorCode.Error;
+ }
+
+ return sqliteBase.DeclareVirtualTable(this, sql, ref error);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Function Declaration Helper Methods
+ ///
+ /// Calls the native SQLite core library in order to declare a virtual
+ /// table function in response to a call into the
+ ///
+ /// or virtual table
+ /// methods.
+ ///
+ ///
+ /// The object instance to use when
+ /// declaring the schema of the virtual table.
+ ///
+ ///
+ /// The number of arguments to the function being declared.
+ ///
+ ///
+ /// The name of the function being declared.
+ ///
+ ///
+ /// Upon success, the contents of this parameter are undefined. Upon
+ /// failure, it should contain an appropriate error message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ protected virtual SQLiteErrorCode DeclareFunction(
+ SQLiteConnection connection,
+ int argumentCount,
+ string name,
+ ref string error
+ )
+ {
+ if (connection == null)
+ {
+ error = "invalid connection";
+ return SQLiteErrorCode.Error;
+ }
+
+ SQLiteBase sqliteBase = connection._sql;
+
+ if (sqliteBase == null)
+ {
+ error = "connection has invalid handle";
+ return SQLiteErrorCode.Error;
+ }
+
+ return sqliteBase.DeclareVirtualFunction(
+ this, argumentCount, name, ref error);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Error Handling Properties
+ private bool logErrors;
+ ///
+ /// Returns or sets a boolean value indicating whether virtual table
+ /// errors should be logged using the class.
+ ///
+ protected virtual bool LogErrorsNoThrow
+ {
+ get { return logErrors; }
+ set { logErrors = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private bool logExceptions;
+ ///
+ /// Returns or sets a boolean value indicating whether exceptions
+ /// caught in the
+ /// method,
+ /// the method,
+ /// the method,
+ /// the method,
+ /// and the method should be logged using the
+ /// class.
+ ///
+ protected virtual bool LogExceptionsNoThrow
+ {
+ get { return logExceptions; }
+ set { logExceptions = value; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Error Handling Helper Methods
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetTableError(
+ IntPtr pVtab,
+ string error
+ )
+ {
+ return SetTableError(
+ this, pVtab, LogErrorsNoThrow, LogExceptionsNoThrow, error);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The object instance used to
+ /// lookup the native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetTableError(
+ SQLiteVirtualTable table,
+ string error
+ )
+ {
+ return SetTableError(
+ this, table, LogErrorsNoThrow, LogExceptionsNoThrow, error);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Arranges for the specified error message to be placed into the
+ /// zErrMsg field of a sqlite3_vtab derived structure, freeing the
+ /// existing error message, if any.
+ ///
+ ///
+ /// The object instance used to
+ /// lookup the native pointer to the sqlite3_vtab derived structure.
+ ///
+ ///
+ /// The error message.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetCursorError(
+ SQLiteVirtualTableCursor cursor,
+ string error
+ )
+ {
+ return SetCursorError(
+ this, cursor, LogErrorsNoThrow, LogExceptionsNoThrow, error);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Index Handling Helper Methods
+ ///
+ /// Modifies the specified object instance
+ /// to contain the specified estimated cost.
+ ///
+ ///
+ /// The object instance to modify.
+ ///
+ ///
+ /// The estimated cost value to use. Using a null value means that the
+ /// default value provided by the SQLite core library should be used.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetEstimatedCost(
+ SQLiteIndex index,
+ double? estimatedCost
+ )
+ {
+ if ((index == null) || (index.Outputs == null))
+ return false;
+
+ index.Outputs.EstimatedCost = estimatedCost;
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Modifies the specified object instance
+ /// to contain the default estimated cost.
+ ///
+ ///
+ /// The object instance to modify.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetEstimatedCost(
+ SQLiteIndex index
+ )
+ {
+ return SetEstimatedCost(index, null);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Modifies the specified object instance
+ /// to contain the specified estimated rows.
+ ///
+ ///
+ /// The object instance to modify.
+ ///
+ ///
+ /// The estimated rows value to use. Using a null value means that the
+ /// default value provided by the SQLite core library should be used.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetEstimatedRows(
+ SQLiteIndex index,
+ long? estimatedRows
+ )
+ {
+ if ((index == null) || (index.Outputs == null))
+ return false;
+
+ index.Outputs.EstimatedRows = estimatedRows;
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Modifies the specified object instance
+ /// to contain the default estimated rows.
+ ///
+ ///
+ /// The object instance to modify.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetEstimatedRows(
+ SQLiteIndex index
+ )
+ {
+ return SetEstimatedRows(index, null);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Modifies the specified object instance
+ /// to contain the specified flags.
+ ///
+ ///
+ /// The object instance to modify.
+ ///
+ ///
+ /// The index flags value to use. Using a null value means that the
+ /// default value provided by the SQLite core library should be used.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetIndexFlags(
+ SQLiteIndex index,
+ SQLiteIndexFlags? indexFlags
+ )
+ {
+ if ((index == null) || (index.Outputs == null))
+ return false;
+
+ index.Outputs.IndexFlags = indexFlags;
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Modifies the specified object instance
+ /// to contain the default index flags.
+ ///
+ ///
+ /// The object instance to modify.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetIndexFlags(
+ SQLiteIndex index
+ )
+ {
+ return SetIndexFlags(index, null);
+ }
+ #endregion
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Properties
+ ///
+ /// Returns or sets a boolean value indicating whether virtual table
+ /// errors should be logged using the class.
+ ///
+ public virtual bool LogErrors
+ {
+ get { CheckDisposed(); return LogErrorsNoThrow; }
+ set { CheckDisposed(); LogErrorsNoThrow = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns or sets a boolean value indicating whether exceptions
+ /// caught in the
+ /// method,
+ /// method, and the
+ /// method should be logged using the
+ /// class.
+ ///
+ public virtual bool LogExceptions
+ {
+ get { CheckDisposed(); return LogExceptionsNoThrow; }
+ set { CheckDisposed(); LogExceptionsNoThrow = value; }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteNativeModule Members
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xCreate(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ )
+ {
+ return CreateOrConnect(
+ true, pDb, pAux, argc, argv, ref pVtab, ref pError);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xConnect(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ )
+ {
+ return CreateOrConnect(
+ false, pDb, pAux, argc, argv, ref pVtab, ref pError);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xBestIndex(
+ IntPtr pVtab,
+ IntPtr pIndex
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ {
+ SQLiteIndex index = null;
+
+ SQLiteIndex.FromIntPtr(pIndex, true, ref index);
+
+ if (BestIndex(table, index) == SQLiteErrorCode.Ok)
+ {
+ SQLiteIndex.ToIntPtr(index, pIndex, true);
+ return SQLiteErrorCode.Ok;
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xDisconnect(
+ IntPtr pVtab
+ )
+ {
+ return DestroyOrDisconnect(false, pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xDestroy(
+ IntPtr pVtab
+ )
+ {
+ return DestroyOrDisconnect(true, pVtab);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xOpen(
+ IntPtr pVtab,
+ ref IntPtr pCursor
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ {
+ SQLiteVirtualTableCursor cursor = null;
+
+ if (Open(table, ref cursor) == SQLiteErrorCode.Ok)
+ {
+ if (cursor != null)
+ {
+ pCursor = CursorToIntPtr(cursor);
+
+ if (pCursor != IntPtr.Zero)
+ {
+ return SQLiteErrorCode.Ok;
+ }
+ else
+ {
+ SetTableError(pVtab,
+ "no native cursor was created");
+ }
+ }
+ else
+ {
+ SetTableError(pVtab,
+ "no managed cursor was created");
+ }
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xClose(
+ IntPtr pCursor
+ )
+ {
+ IntPtr pVtab = IntPtr.Zero;
+
+ try
+ {
+ pVtab = TableFromCursor(pCursor);
+
+ SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
+ pVtab, pCursor);
+
+ if (cursor != null)
+ {
+ if (Close(cursor) == SQLiteErrorCode.Ok)
+ {
+ if (cursors != null)
+ cursors.Remove(pCursor);
+
+ return SQLiteErrorCode.Ok;
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+ finally
+ {
+ FreeCursor(pCursor);
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xFilter(
+ IntPtr pCursor,
+ int idxNum,
+ IntPtr idxStr,
+ int argc,
+ IntPtr argv
+ )
+ {
+ IntPtr pVtab = IntPtr.Zero;
+
+ try
+ {
+ pVtab = TableFromCursor(pCursor);
+
+ SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
+ pVtab, pCursor);
+
+ if (cursor != null)
+ {
+ if (Filter(cursor, idxNum,
+ SQLiteString.StringFromUtf8IntPtr(idxStr),
+ SQLiteValue.ArrayFromSizeAndIntPtr(argc,
+ argv)) == SQLiteErrorCode.Ok)
+ {
+ return SQLiteErrorCode.Ok;
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xNext(
+ IntPtr pCursor
+ )
+ {
+ IntPtr pVtab = IntPtr.Zero;
+
+ try
+ {
+ pVtab = TableFromCursor(pCursor);
+
+ SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
+ pVtab, pCursor);
+
+ if (cursor != null)
+ {
+ if (Next(cursor) == SQLiteErrorCode.Ok)
+ return SQLiteErrorCode.Ok;
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private int xEof(
+ IntPtr pCursor
+ )
+ {
+ IntPtr pVtab = IntPtr.Zero;
+
+ try
+ {
+ pVtab = TableFromCursor(pCursor);
+
+ SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
+ pVtab, pCursor);
+
+ if (cursor != null)
+ return Eof(cursor) ? 1 : 0;
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return 1; /* NOTE: On any error, return "no more rows". */
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xColumn(
+ IntPtr pCursor,
+ IntPtr pContext,
+ int index
+ )
+ {
+ IntPtr pVtab = IntPtr.Zero;
+
+ try
+ {
+ pVtab = TableFromCursor(pCursor);
+
+ SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
+ pVtab, pCursor);
+
+ if (cursor != null)
+ {
+ SQLiteContext context = new SQLiteContext(pContext);
+
+ return Column(cursor, context, index);
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xRowId(
+ IntPtr pCursor,
+ ref long rowId
+ )
+ {
+ IntPtr pVtab = IntPtr.Zero;
+
+ try
+ {
+ pVtab = TableFromCursor(pCursor);
+
+ SQLiteVirtualTableCursor cursor = CursorFromIntPtr(
+ pVtab, pCursor);
+
+ if (cursor != null)
+ return RowId(cursor, ref rowId);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xUpdate(
+ IntPtr pVtab,
+ int argc,
+ IntPtr argv,
+ ref long rowId
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ {
+ return Update(table,
+ SQLiteValue.ArrayFromSizeAndIntPtr(argc, argv),
+ ref rowId);
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xBegin(
+ IntPtr pVtab
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return Begin(table);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xSync(
+ IntPtr pVtab
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return Sync(table);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xCommit(
+ IntPtr pVtab
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return Commit(table);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xRollback(
+ IntPtr pVtab
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return Rollback(table);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private int xFindFunction(
+ IntPtr pVtab,
+ int nArg,
+ IntPtr zName,
+ ref SQLiteCallback callback,
+ ref IntPtr pClientData
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ {
+ string name = SQLiteString.StringFromUtf8IntPtr(zName);
+ SQLiteFunction function = null;
+
+ if (FindFunction(
+ table, nArg, name, ref function, ref pClientData))
+ {
+ if (function != null)
+ {
+ string key = GetFunctionKey(nArg, name, function);
+
+ functions[key] = function;
+ callback = function.ScalarCallback;
+
+ return 1;
+ }
+ else
+ {
+ SetTableError(pVtab, "no function was created");
+ }
+ }
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return 0; /* NOTE: On any error, return "no such function". */
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xRename(
+ IntPtr pVtab,
+ IntPtr zNew
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ {
+ return Rename(table,
+ SQLiteString.StringFromUtf8IntPtr(zNew));
+ }
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xSavepoint(
+ IntPtr pVtab,
+ int iSavepoint
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return Savepoint(table, iSavepoint);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xRelease(
+ IntPtr pVtab,
+ int iSavepoint
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return Release(table, iSavepoint);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ private SQLiteErrorCode xRollbackTo(
+ IntPtr pVtab,
+ int iSavepoint
+ )
+ {
+ try
+ {
+ SQLiteVirtualTable table = TableFromIntPtr(pVtab);
+
+ if (table != null)
+ return RollbackTo(table, iSavepoint);
+ }
+ catch (Exception e) /* NOTE: Must catch ALL. */
+ {
+ SetTableError(pVtab, e.ToString());
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteManagedModule Members
+ private bool declared;
+ ///
+ /// Returns non-zero if the schema for the virtual table has been
+ /// declared.
+ ///
+ public virtual bool Declared
+ {
+ get { CheckDisposed(); return declared; }
+ internal set { declared = value; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private string name;
+ ///
+ /// Returns the name of the module as it was registered with the SQLite
+ /// core library.
+ ///
+ public virtual string Name
+ {
+ get { CheckDisposed(); return name; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// The native user-data pointer associated with this module, as it was
+ /// provided to the SQLite core library when the native module instance
+ /// was created.
+ ///
+ ///
+ /// The module name, database name, virtual table name, and all other
+ /// arguments passed to the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to contain an error
+ /// message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Create(
+ SQLiteConnection connection,
+ IntPtr pClientData,
+ string[] arguments,
+ ref SQLiteVirtualTable table,
+ ref string error
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// The native user-data pointer associated with this module, as it was
+ /// provided to the SQLite core library when the native module instance
+ /// was created.
+ ///
+ ///
+ /// The module name, database name, virtual table name, and all other
+ /// arguments passed to the CREATE VIRTUAL TABLE statement.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance associated with
+ /// the virtual table.
+ ///
+ ///
+ /// Upon failure, this parameter must be modified to contain an error
+ /// message.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Connect(
+ SQLiteConnection connection,
+ IntPtr pClientData,
+ string[] arguments,
+ ref SQLiteVirtualTable table,
+ ref string error
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The object instance containing all the
+ /// data for the inputs and outputs relating to index selection.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode BestIndex(
+ SQLiteVirtualTable table,
+ SQLiteIndex index
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Disconnect(
+ SQLiteVirtualTable table
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Destroy(
+ SQLiteVirtualTable table
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance associated
+ /// with the newly opened virtual table cursor.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Open(
+ SQLiteVirtualTable table,
+ ref SQLiteVirtualTableCursor cursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Close(
+ SQLiteVirtualTableCursor cursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// Number used to help identify the selected index.
+ ///
+ ///
+ /// String used to help identify the selected index.
+ ///
+ ///
+ /// The values corresponding to each column in the selected index.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Filter(
+ SQLiteVirtualTableCursor cursor,
+ int indexNumber,
+ string indexString,
+ SQLiteValue[] values
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Next(
+ SQLiteVirtualTableCursor cursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// Non-zero if no more rows are available; zero otherwise.
+ ///
+ public abstract bool Eof(
+ SQLiteVirtualTableCursor cursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// The object instance to be used for
+ /// returning the specified column value to the SQLite core library.
+ ///
+ ///
+ /// The zero-based index corresponding to the column containing the
+ /// value to be returned.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Column(
+ SQLiteVirtualTableCursor cursor,
+ SQLiteContext context,
+ int index
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the unique
+ /// integer row identifier for the current row for the specified cursor.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode RowId(
+ SQLiteVirtualTableCursor cursor,
+ ref long rowId
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The array of object instances containing
+ /// the new or modified column values, if any.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the unique
+ /// integer row identifier for the row that was inserted, if any.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Update(
+ SQLiteVirtualTable table,
+ SQLiteValue[] values,
+ ref long rowId
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Begin(
+ SQLiteVirtualTable table
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Sync(
+ SQLiteVirtualTable table
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Commit(
+ SQLiteVirtualTable table
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Rollback(
+ SQLiteVirtualTable table
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The number of arguments to the function being sought.
+ ///
+ ///
+ /// The name of the function being sought.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// object instance responsible for
+ /// implementing the specified function.
+ ///
+ ///
+ /// Upon success, this parameter must be modified to contain the
+ /// native user-data pointer associated with
+ /// .
+ ///
+ ///
+ /// Non-zero if the specified function was found; zero otherwise.
+ ///
+ public abstract bool FindFunction(
+ SQLiteVirtualTable table,
+ int argumentCount,
+ string name,
+ ref SQLiteFunction function,
+ ref IntPtr pClientData
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// The new name for the virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Rename(
+ SQLiteVirtualTable table,
+ string newName
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// This is an integer identifier under which the the current state of
+ /// the virtual table should be saved.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Savepoint(
+ SQLiteVirtualTable table,
+ int savepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// This is an integer used to indicate that any saved states with an
+ /// identifier greater than or equal to this should be deleted by the
+ /// virtual table.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode Release(
+ SQLiteVirtualTable table,
+ int savepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is called in response to the
+ /// method.
+ ///
+ ///
+ /// The object instance associated
+ /// with this virtual table.
+ ///
+ ///
+ /// This is an integer identifier used to specify a specific saved
+ /// state for the virtual table for it to restore itself back to, which
+ /// should also have the effect of deleting all saved states with an
+ /// integer identifier greater than this one.
+ ///
+ ///
+ /// A standard SQLite return code.
+ ///
+ public abstract SQLiteErrorCode RollbackTo(
+ SQLiteVirtualTable table,
+ int savepoint
+ );
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteModule).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is being
+ /// called from the finalizer.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (functions != null)
+ functions.Clear();
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ try
+ {
+ if (disposableModule != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3_dispose_module(
+ disposableModule);
+
+ disposableModule = IntPtr.Zero;
+ }
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if (LogExceptionsNoThrow)
+ {
+ SQLiteLog.LogMessage(SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "Dispose", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+#if PLATFORM_COMPACTFRAMEWORK
+ finally
+ {
+ if (pNativeModule != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pNativeModule);
+ pNativeModule = IntPtr.Zero;
+ }
+ }
+#endif
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteModule()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteModuleCommon.cs b/Native.Csharp.Tool/SQLite/SQLiteModuleCommon.cs
new file mode 100644
index 00000000..897c1721
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteModuleCommon.cs
@@ -0,0 +1,286 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Globalization;
+
+#region Non-Generic Classes
+namespace System.Data.SQLite
+{
+ #region SQLiteModuleCommon Class
+ ///
+ /// This class contains some virtual methods that may be useful for other
+ /// virtual table classes. It specifically does NOT implement any of the
+ /// interface methods.
+ ///
+ public class SQLiteModuleCommon : SQLiteModuleNoop /* NOT SEALED */
+ {
+ #region Private Constants
+ ///
+ /// The CREATE TABLE statement used to declare the schema for the
+ /// virtual table.
+ ///
+ private static readonly string declareSql =
+ HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture, "CREATE TABLE {0}(x);",
+ typeof(SQLiteModuleCommon).Name);
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// Non-zero if different object instances with the same value should
+ /// generate different row identifiers, where applicable. This has no
+ /// effect on the .NET Compact Framework.
+ ///
+ private bool objectIdentity;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ public SQLiteModuleCommon(
+ string name
+ )
+ : this(name, false)
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ ///
+ /// Non-zero if different object instances with the same value should
+ /// generate different row identifiers, where applicable. This
+ /// parameter has no effect on the .NET Compact Framework.
+ ///
+ public SQLiteModuleCommon(
+ string name,
+ bool objectIdentity
+ )
+ : base(name)
+ {
+ this.objectIdentity = objectIdentity;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Determines the SQL statement used to declare the virtual table.
+ /// This method should be overridden in derived classes if they require
+ /// a custom virtual table schema.
+ ///
+ ///
+ /// The SQL statement used to declare the virtual table -OR- null if it
+ /// cannot be determined.
+ ///
+ protected virtual string GetSqlForDeclareTable()
+ {
+ return declareSql;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the table error message to one that indicates the virtual
+ /// table cursor is of the wrong type.
+ ///
+ ///
+ /// The object instance.
+ ///
+ ///
+ /// The that the virtual table cursor should be.
+ ///
+ ///
+ /// The value of .
+ ///
+ protected virtual SQLiteErrorCode CursorTypeMismatchError(
+ SQLiteVirtualTableCursor cursor,
+ Type type
+ )
+ {
+ if (type != null)
+ {
+ SetCursorError(cursor, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "not a \"{0}\" cursor",
+ type));
+ }
+ else
+ {
+ SetCursorError(cursor, "cursor type mismatch");
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines the string to return as the column value for the object
+ /// instance value.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// The object instance to return a string representation for.
+ ///
+ ///
+ /// The string representation of the specified object instance or null
+ /// upon failure.
+ ///
+ protected virtual string GetStringFromObject(
+ SQLiteVirtualTableCursor cursor,
+ object value
+ )
+ {
+ if (value == null)
+ return null;
+
+ if (value is string)
+ return (string)value;
+
+ return value.ToString();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an unique row identifier from two
+ /// values. The first value
+ /// must contain the row sequence number for the current row and the
+ /// second value must contain the hash code of the key column value
+ /// for the current row.
+ ///
+ ///
+ /// The integer row sequence number for the current row.
+ ///
+ ///
+ /// The hash code of the key column value for the current row.
+ ///
+ ///
+ /// The unique row identifier or zero upon failure.
+ ///
+ protected virtual long MakeRowId(
+ int rowIndex,
+ int hashCode
+ )
+ {
+ long result = rowIndex;
+
+ result <<= 32; /* typeof(int) bits */
+ result |= (long)(uint)hashCode;
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines the unique row identifier for the current row.
+ ///
+ ///
+ /// The object instance
+ /// associated with the previously opened virtual table cursor to be
+ /// used.
+ ///
+ ///
+ /// The object instance to return a unique row identifier for.
+ ///
+ ///
+ /// The unique row identifier or zero upon failure.
+ ///
+ protected virtual long GetRowIdFromObject(
+ SQLiteVirtualTableCursor cursor,
+ object value
+ )
+ {
+ int rowIndex = (cursor != null) ? cursor.GetRowIndex() : 0;
+ int hashCode = SQLiteMarshal.GetHashCode(value, objectIdentity);
+
+ return MakeRowId(rowIndex, hashCode);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteModuleCommon).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is
+ /// being called from the finalizer.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
+#endregion
diff --git a/Native.Csharp.Tool/SQLite/SQLiteModuleEnumerable.cs b/Native.Csharp.Tool/SQLite/SQLiteModuleEnumerable.cs
new file mode 100644
index 00000000..dc10bcb9
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteModuleEnumerable.cs
@@ -0,0 +1,1270 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+
+#region Non-Generic Classes
+namespace System.Data.SQLite
+{
+ #region SQLiteVirtualTableCursorEnumerator Class
+ ///
+ /// This class represents a virtual table cursor to be used with the
+ /// class. It is not sealed and may
+ /// be used as the base class for any user-defined virtual table cursor
+ /// class that wraps an object instance.
+ ///
+ public class SQLiteVirtualTableCursorEnumerator :
+ SQLiteVirtualTableCursor, IEnumerator /* NOT SEALED */
+ {
+ #region Private Data
+ ///
+ /// The instance provided when this cursor
+ /// was created.
+ ///
+ private IEnumerator enumerator;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This value will be non-zero if false has been returned from the
+ /// method.
+ ///
+ private bool endOfEnumerator;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The object instance associated
+ /// with this object instance.
+ ///
+ ///
+ /// The instance to expose as a virtual
+ /// table cursor.
+ ///
+ public SQLiteVirtualTableCursorEnumerator(
+ SQLiteVirtualTable table,
+ IEnumerator enumerator
+ )
+ : base(table)
+ {
+ this.enumerator = enumerator;
+ this.endOfEnumerator = true;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Members
+ ///
+ /// Advances to the next row of the virtual table cursor using the
+ /// method of the
+ /// object instance.
+ ///
+ ///
+ /// Non-zero if the current row is valid; zero otherwise. If zero is
+ /// returned, no further rows are available.
+ ///
+ public virtual bool MoveNext()
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ if (enumerator == null)
+ return false;
+
+ endOfEnumerator = !enumerator.MoveNext();
+
+ if (!endOfEnumerator)
+ NextRowIndex();
+
+ return !endOfEnumerator;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the value for the current row of the virtual table cursor
+ /// using the property of the
+ /// object instance.
+ ///
+ public virtual object Current
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ if (enumerator == null)
+ return null;
+
+ return enumerator.Current;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Resets the virtual table cursor position, also invalidating the
+ /// current row, using the method of
+ /// the object instance.
+ ///
+ public virtual void Reset()
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ if (enumerator == null)
+ return;
+
+ enumerator.Reset();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns non-zero if the end of the virtual table cursor has been
+ /// seen (i.e. no more rows are available, including the current one).
+ ///
+ public virtual bool EndOfEnumerator
+ {
+ get { CheckDisposed(); CheckClosed(); return endOfEnumerator; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns non-zero if the virtual table cursor is open.
+ ///
+ public virtual bool IsOpen
+ {
+ get { CheckDisposed(); return (enumerator != null); }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Closes the virtual table cursor. This method must not throw any
+ /// exceptions.
+ ///
+ public virtual void Close()
+ {
+ // CheckDisposed();
+ // CheckClosed();
+
+ if (enumerator != null)
+ enumerator = null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Throws an if the virtual
+ /// table cursor has been closed.
+ ///
+ public virtual void CheckClosed()
+ {
+ CheckDisposed();
+
+ if (!IsOpen)
+ {
+ throw new InvalidOperationException(
+ "virtual table cursor is closed");
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteVirtualTableCursorEnumerator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is
+ /// being called from the finalizer.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ Close();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteModuleEnumerable Class
+ ///
+ /// This class implements a virtual table module that exposes an
+ /// object instance as a read-only virtual
+ /// table. It is not sealed and may be used as the base class for any
+ /// user-defined virtual table class that wraps an
+ /// object instance. The following short
+ /// example shows it being used to treat an array of strings as a table
+ /// data source:
+ ///
+ /// public static class Sample
+ /// {
+ /// public static void Main()
+ /// {
+ /// using (SQLiteConnection connection = new SQLiteConnection(
+ /// "Data Source=:memory:;"))
+ /// {
+ /// connection.Open();
+ ///
+ /// connection.CreateModule(new SQLiteModuleEnumerable(
+ /// "sampleModule", new string[] { "one", "two", "three" }));
+ ///
+ /// using (SQLiteCommand command = connection.CreateCommand())
+ /// {
+ /// command.CommandText =
+ /// "CREATE VIRTUAL TABLE t1 USING sampleModule;";
+ ///
+ /// command.ExecuteNonQuery();
+ /// }
+ ///
+ /// using (SQLiteCommand command = connection.CreateCommand())
+ /// {
+ /// command.CommandText = "SELECT * FROM t1;";
+ ///
+ /// using (SQLiteDataReader dataReader = command.ExecuteReader())
+ /// {
+ /// while (dataReader.Read())
+ /// Console.WriteLine(dataReader[0].ToString());
+ /// }
+ /// }
+ ///
+ /// connection.Close();
+ /// }
+ /// }
+ /// }
+ ///
+ ///
+ public class SQLiteModuleEnumerable : SQLiteModuleCommon /* NOT SEALED */
+ {
+ #region Private Data
+ ///
+ /// The instance containing the backing data
+ /// for the virtual table.
+ ///
+ private IEnumerable enumerable;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Non-zero if different object instances with the same value should
+ /// generate different row identifiers, where applicable. This has no
+ /// effect on the .NET Compact Framework.
+ ///
+ private bool objectIdentity;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ ///
+ /// The instance to expose as a virtual
+ /// table. This parameter cannot be null.
+ ///
+ public SQLiteModuleEnumerable(
+ string name,
+ IEnumerable enumerable
+ )
+ : this(name, enumerable, false)
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ ///
+ /// The instance to expose as a virtual
+ /// table. This parameter cannot be null.
+ ///
+ ///
+ /// Non-zero if different object instances with the same value should
+ /// generate different row identifiers, where applicable. This
+ /// parameter has no effect on the .NET Compact Framework.
+ ///
+ public SQLiteModuleEnumerable(
+ string name,
+ IEnumerable enumerable,
+ bool objectIdentity
+ )
+ : base(name)
+ {
+ if (enumerable == null)
+ throw new ArgumentNullException("enumerable");
+
+ this.enumerable = enumerable;
+ this.objectIdentity = objectIdentity;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Sets the table error message to one that indicates the virtual
+ /// table cursor has no current row.
+ ///
+ ///
+ /// The object instance.
+ ///
+ ///
+ /// The value of .
+ ///
+ protected virtual SQLiteErrorCode CursorEndOfEnumeratorError(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ SetCursorError(cursor, "already hit end of enumerator");
+ return SQLiteErrorCode.Error;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteManagedModule Members
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Create(
+ SQLiteConnection connection,
+ IntPtr pClientData,
+ string[] arguments,
+ ref SQLiteVirtualTable table,
+ ref string error
+ )
+ {
+ CheckDisposed();
+
+ if (DeclareTable(
+ connection, GetSqlForDeclareTable(),
+ ref error) == SQLiteErrorCode.Ok)
+ {
+ table = new SQLiteVirtualTable(arguments);
+ return SQLiteErrorCode.Ok;
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Connect(
+ SQLiteConnection connection,
+ IntPtr pClientData,
+ string[] arguments,
+ ref SQLiteVirtualTable table,
+ ref string error
+ )
+ {
+ CheckDisposed();
+
+ if (DeclareTable(
+ connection, GetSqlForDeclareTable(),
+ ref error) == SQLiteErrorCode.Ok)
+ {
+ table = new SQLiteVirtualTable(arguments);
+ return SQLiteErrorCode.Ok;
+ }
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode BestIndex(
+ SQLiteVirtualTable table,
+ SQLiteIndex index
+ )
+ {
+ CheckDisposed();
+
+ if (!table.BestIndex(index))
+ {
+ SetTableError(table, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "failed to select best index for virtual table \"{0}\"",
+ table.TableName));
+
+ return SQLiteErrorCode.Error;
+ }
+
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Disconnect(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ table.Dispose();
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Destroy(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ table.Dispose();
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Open(
+ SQLiteVirtualTable table,
+ ref SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ cursor = new SQLiteVirtualTableCursorEnumerator(
+ table, enumerable.GetEnumerator());
+
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Close(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return CursorTypeMismatchError(cursor,
+ typeof(SQLiteVirtualTableCursorEnumerator));
+ }
+
+ enumeratorCursor.Close();
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Filter(
+ SQLiteVirtualTableCursor cursor,
+ int indexNumber,
+ string indexString,
+ SQLiteValue[] values
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return CursorTypeMismatchError(cursor,
+ typeof(SQLiteVirtualTableCursorEnumerator));
+ }
+
+ enumeratorCursor.Filter(indexNumber, indexString, values);
+ enumeratorCursor.Reset(); /* NO RESULT */
+ enumeratorCursor.MoveNext(); /* IGNORED */
+
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Next(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return CursorTypeMismatchError(cursor,
+ typeof(SQLiteVirtualTableCursorEnumerator));
+ }
+
+ if (enumeratorCursor.EndOfEnumerator)
+ return CursorEndOfEnumeratorError(cursor);
+
+ enumeratorCursor.MoveNext(); /* IGNORED */
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override bool Eof(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return ResultCodeToEofResult(CursorTypeMismatchError(
+ cursor, typeof(SQLiteVirtualTableCursorEnumerator)));
+ }
+
+ return enumeratorCursor.EndOfEnumerator;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Column(
+ SQLiteVirtualTableCursor cursor,
+ SQLiteContext context,
+ int index
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return CursorTypeMismatchError(cursor,
+ typeof(SQLiteVirtualTableCursorEnumerator));
+ }
+
+ if (enumeratorCursor.EndOfEnumerator)
+ return CursorEndOfEnumeratorError(cursor);
+
+ object current = enumeratorCursor.Current;
+
+ if (current != null)
+ context.SetString(GetStringFromObject(cursor, current));
+ else
+ context.SetNull();
+
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode RowId(
+ SQLiteVirtualTableCursor cursor,
+ ref long rowId
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return CursorTypeMismatchError(cursor,
+ typeof(SQLiteVirtualTableCursorEnumerator));
+ }
+
+ if (enumeratorCursor.EndOfEnumerator)
+ return CursorEndOfEnumeratorError(cursor);
+
+ object current = enumeratorCursor.Current;
+
+ rowId = GetRowIdFromObject(cursor, current);
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Update(
+ SQLiteVirtualTable table,
+ SQLiteValue[] values,
+ ref long rowId
+ )
+ {
+ CheckDisposed();
+
+ SetTableError(table, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "virtual table \"{0}\" is read-only", table.TableName));
+
+ return SQLiteErrorCode.Error;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Rename(
+ SQLiteVirtualTable table,
+ string newName
+ )
+ {
+ CheckDisposed();
+
+ if (!table.Rename(newName))
+ {
+ SetTableError(table, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "failed to rename virtual table from \"{0}\" to \"{1}\"",
+ table.TableName, newName));
+
+ return SQLiteErrorCode.Error;
+ }
+
+ return SQLiteErrorCode.Ok;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteModuleEnumerable).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is
+ /// being called from the finalizer.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
+#endregion
+
+///////////////////////////////////////////////////////////////////////////////
+
+#region Generic Classes
+namespace System.Data.SQLite.Generic
+{
+ #region SQLiteVirtualTableCursorEnumerator Class
+ ///
+ /// This class represents a virtual table cursor to be used with the
+ /// class. It is not sealed and may
+ /// be used as the base class for any user-defined virtual table cursor
+ /// class that wraps an object instance.
+ ///
+ public class SQLiteVirtualTableCursorEnumerator :
+ SQLiteVirtualTableCursorEnumerator, IEnumerator /* NOT SEALED */
+ {
+ #region Private Data
+ ///
+ /// The instance provided when this
+ /// cursor was created.
+ ///
+ private IEnumerator enumerator;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The object instance associated
+ /// with this object instance.
+ ///
+ ///
+ /// The instance to expose as a virtual
+ /// table cursor.
+ ///
+ public SQLiteVirtualTableCursorEnumerator(
+ SQLiteVirtualTable table,
+ IEnumerator enumerator
+ )
+ : base(table, enumerator as IEnumerator)
+ {
+ this.enumerator = enumerator;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Members
+ ///
+ /// Returns the value for the current row of the virtual table cursor
+ /// using the property of the
+ /// object instance.
+ ///
+ T IEnumerator.Current
+ {
+ get
+ {
+ CheckDisposed();
+ CheckClosed();
+
+ if (enumerator == null)
+ return default(T);
+
+ return enumerator.Current;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Closes the virtual table cursor. This method must not throw any
+ /// exceptions.
+ ///
+ public override void Close()
+ {
+ // CheckDisposed();
+ // CheckClosed();
+
+ if (enumerator != null)
+ enumerator = null;
+
+ base.Close();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteVirtualTableCursorEnumerator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is
+ /// being called from the finalizer.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ Close();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteModuleEnumerable Class
+ ///
+ /// This class implements a virtual table module that exposes an
+ /// object instance as a read-only virtual
+ /// table. It is not sealed and may be used as the base class for any
+ /// user-defined virtual table class that wraps an
+ /// object instance.
+ ///
+ public class SQLiteModuleEnumerable :
+ SQLiteModuleEnumerable /* NOT SEALED */
+ {
+ #region Private Data
+ ///
+ /// The instance containing the backing
+ /// data for the virtual table.
+ ///
+ private IEnumerable enumerable;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ ///
+ /// The instance to expose as a virtual
+ /// table. This parameter cannot be null.
+ ///
+ public SQLiteModuleEnumerable(
+ string name,
+ IEnumerable enumerable
+ )
+ : base(name, enumerable as IEnumerable)
+ {
+ this.enumerable = enumerable;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteManagedModule Members
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Open(
+ SQLiteVirtualTable table,
+ ref SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ cursor = new SQLiteVirtualTableCursorEnumerator(
+ table, enumerable.GetEnumerator());
+
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Column(
+ SQLiteVirtualTableCursor cursor,
+ SQLiteContext context,
+ int index
+ )
+ {
+ CheckDisposed();
+
+ SQLiteVirtualTableCursorEnumerator enumeratorCursor =
+ cursor as SQLiteVirtualTableCursorEnumerator;
+
+ if (enumeratorCursor == null)
+ {
+ return CursorTypeMismatchError(cursor,
+ typeof(SQLiteVirtualTableCursorEnumerator));
+ }
+
+ if (enumeratorCursor.EndOfEnumerator)
+ return CursorEndOfEnumeratorError(cursor);
+
+ T current = ((IEnumerator)enumeratorCursor).Current;
+
+ if (current != null)
+ context.SetString(GetStringFromObject(cursor, current));
+ else
+ context.SetNull();
+
+ return SQLiteErrorCode.Ok;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteModuleEnumerable).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is
+ /// being called from the finalizer.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
+#endregion
diff --git a/Native.Csharp.Tool/SQLite/SQLiteModuleNoop.cs b/Native.Csharp.Tool/SQLite/SQLiteModuleNoop.cs
new file mode 100644
index 00000000..32a967e3
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteModuleNoop.cs
@@ -0,0 +1,786 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Collections.Generic;
+
+namespace System.Data.SQLite
+{
+ ///
+ /// This class implements a virtual table module that does nothing by
+ /// providing "empty" implementations for all of the
+ /// interface methods. The result
+ /// codes returned by these "empty" method implementations may be
+ /// controlled on a per-method basis by using and/or overriding the
+ /// ,
+ /// ,
+ /// ,
+ /// , and
+ /// methods from within derived classes.
+ ///
+ public class SQLiteModuleNoop : SQLiteModule /* NOT SEALED */
+ {
+ #region Private Data
+ ///
+ /// This field is used to store the
+ /// values to return, on a per-method basis, for all methods that are
+ /// part of the interface.
+ ///
+ private Dictionary resultCodes;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class.
+ ///
+ ///
+ /// The name of the module. This parameter cannot be null.
+ ///
+ public SQLiteModuleNoop(
+ string name
+ )
+ : base(name)
+ {
+ resultCodes = new Dictionary();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Determines the default value to be
+ /// returned by methods of the
+ /// interface that lack an overridden implementation in all classes
+ /// derived from the class.
+ ///
+ ///
+ /// The value that should be returned
+ /// by all interface methods unless
+ /// a more specific result code has been set for that interface method.
+ ///
+ protected virtual SQLiteErrorCode GetDefaultResultCode()
+ {
+ return SQLiteErrorCode.Ok;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a value into a boolean
+ /// return value for use with the
+ /// method.
+ ///
+ ///
+ /// The value to convert.
+ ///
+ ///
+ /// The value.
+ ///
+ protected virtual bool ResultCodeToEofResult(
+ SQLiteErrorCode resultCode
+ )
+ {
+ return (resultCode == SQLiteErrorCode.Ok) ? false : true;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Converts a value into a boolean
+ /// return value for use with the
+ /// method.
+ ///
+ ///
+ /// The value to convert.
+ ///
+ ///
+ /// The value.
+ ///
+ protected virtual bool ResultCodeToFindFunctionResult(
+ SQLiteErrorCode resultCode
+ )
+ {
+ return (resultCode == SQLiteErrorCode.Ok) ? true : false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines the value that should be
+ /// returned by the specified
+ /// interface method if it lack an overridden implementation. If no
+ /// specific value is available (or set)
+ /// for the specified method, the value
+ /// returned by the method will be
+ /// returned instead.
+ ///
+ ///
+ /// The name of the method. Currently, this method must be part of
+ /// the interface.
+ ///
+ ///
+ /// The value that should be returned
+ /// by the interface method.
+ ///
+ protected virtual SQLiteErrorCode GetMethodResultCode(
+ string methodName
+ )
+ {
+ if ((methodName == null) || (resultCodes == null))
+ return GetDefaultResultCode();
+
+ SQLiteErrorCode resultCode;
+
+ if ((resultCodes != null) &&
+ resultCodes.TryGetValue(methodName, out resultCode))
+ {
+ return resultCode;
+ }
+
+ return GetDefaultResultCode();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the value that should be
+ /// returned by the specified
+ /// interface method if it lack an overridden implementation.
+ ///
+ ///
+ /// The name of the method. Currently, this method must be part of
+ /// the interface.
+ ///
+ ///
+ /// The value that should be returned
+ /// by the interface method.
+ ///
+ ///
+ /// Non-zero upon success.
+ ///
+ protected virtual bool SetMethodResultCode(
+ string methodName,
+ SQLiteErrorCode resultCode
+ )
+ {
+ if ((methodName == null) || (resultCodes == null))
+ return false;
+
+ resultCodes[methodName] = resultCode;
+ return true;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteManagedModule Members
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Create(
+ SQLiteConnection connection,
+ IntPtr pClientData,
+ string[] arguments,
+ ref SQLiteVirtualTable table,
+ ref string error
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Create");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Connect(
+ SQLiteConnection connection,
+ IntPtr pClientData,
+ string[] arguments,
+ ref SQLiteVirtualTable table,
+ ref string error
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Connect");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode BestIndex(
+ SQLiteVirtualTable table,
+ SQLiteIndex index
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("BestIndex");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Disconnect(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Disconnect");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Destroy(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Destroy");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Open(
+ SQLiteVirtualTable table,
+ ref SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Open");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Close(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Close");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Filter(
+ SQLiteVirtualTableCursor cursor,
+ int indexNumber,
+ string indexString,
+ SQLiteValue[] values
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Filter");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Next(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Next");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override bool Eof(
+ SQLiteVirtualTableCursor cursor
+ )
+ {
+ CheckDisposed();
+
+ return ResultCodeToEofResult(GetMethodResultCode("Eof"));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Column(
+ SQLiteVirtualTableCursor cursor,
+ SQLiteContext context,
+ int index
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Column");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode RowId(
+ SQLiteVirtualTableCursor cursor,
+ ref long rowId
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("RowId");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Update(
+ SQLiteVirtualTable table,
+ SQLiteValue[] values,
+ ref long rowId
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Update");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Begin(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Begin");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Sync(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Sync");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Commit(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Commit");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Rollback(
+ SQLiteVirtualTable table
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Rollback");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override bool FindFunction(
+ SQLiteVirtualTable table,
+ int argumentCount,
+ string name,
+ ref SQLiteFunction function,
+ ref IntPtr pClientData
+ )
+ {
+ CheckDisposed();
+
+ return ResultCodeToFindFunctionResult(GetMethodResultCode(
+ "FindFunction"));
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Rename(
+ SQLiteVirtualTable table,
+ string newName
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Rename");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Savepoint(
+ SQLiteVirtualTable table,
+ int savepoint
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Savepoint");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode Release(
+ SQLiteVirtualTable table,
+ int savepoint
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("Release");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ ///
+ /// See the method.
+ ///
+ public override SQLiteErrorCode RollbackTo(
+ SQLiteVirtualTable table,
+ int savepoint
+ )
+ {
+ CheckDisposed();
+
+ return GetMethodResultCode("RollbackTo");
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ ///
+ /// Throws an if this object
+ /// instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteModuleNoop).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of this object instance.
+ ///
+ ///
+ /// Non-zero if this method is being called from the
+ /// method. Zero if this method is
+ /// being called from the finalizer.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteParameter.cs b/Native.Csharp.Tool/SQLite/SQLiteParameter.cs
new file mode 100644
index 00000000..40ca6517
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteParameter.cs
@@ -0,0 +1,511 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+ using System.ComponentModel;
+
+ ///
+ /// SQLite implementation of DbParameter.
+ ///
+ public sealed class SQLiteParameter : DbParameter, ICloneable
+ {
+ ///
+ /// This value represents an "unknown" .
+ ///
+ private const DbType UnknownDbType = (DbType)(-1);
+
+ ///
+ /// The command associated with this parameter.
+ ///
+ private IDbCommand _command;
+ ///
+ /// The data type of the parameter
+ ///
+ internal DbType _dbType;
+ ///
+ /// The version information for mapping the parameter
+ ///
+ private DataRowVersion _rowVersion;
+ ///
+ /// The value of the data in the parameter
+ ///
+ private Object _objValue;
+ ///
+ /// The source column for the parameter
+ ///
+ private string _sourceColumn;
+ ///
+ /// The column name
+ ///
+ private string _parameterName;
+ ///
+ /// The data size, unused by SQLite
+ ///
+ private int _dataSize;
+
+ private bool _nullable;
+ private bool _nullMapping;
+
+ ///
+ /// The database type name associated with this parameter, if any.
+ ///
+ private string _typeName;
+
+ ///
+ /// Constructor used when creating for use with a specific command.
+ ///
+ ///
+ /// The command associated with this parameter.
+ ///
+ internal SQLiteParameter(
+ IDbCommand command
+ )
+ : this()
+ {
+ _command = command;
+ }
+
+ ///
+ /// Default constructor
+ ///
+ public SQLiteParameter()
+ : this(null, UnknownDbType, 0, null, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter given the specified parameter name
+ ///
+ /// The parameter name
+ public SQLiteParameter(string parameterName)
+ : this(parameterName, UnknownDbType, 0, null, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter given the specified parameter name and initial value
+ ///
+ /// The parameter name
+ /// The initial value of the parameter
+ public SQLiteParameter(string parameterName, object value)
+ : this(parameterName, UnknownDbType, 0, null, DataRowVersion.Current)
+ {
+ Value = value;
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type
+ ///
+ /// The parameter name
+ /// The datatype of the parameter
+ public SQLiteParameter(string parameterName, DbType dbType)
+ : this(parameterName, dbType, 0, null, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type and source column reference
+ ///
+ /// The parameter name
+ /// The data type
+ /// The source column
+ public SQLiteParameter(string parameterName, DbType dbType, string sourceColumn)
+ : this(parameterName, dbType, 0, sourceColumn, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type, source column and row version
+ ///
+ /// The parameter name
+ /// The data type
+ /// The source column
+ /// The row version information
+ public SQLiteParameter(string parameterName, DbType dbType, string sourceColumn, DataRowVersion rowVersion)
+ : this(parameterName, dbType, 0, sourceColumn, rowVersion)
+ {
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified data type
+ ///
+ /// The datatype of the parameter
+ public SQLiteParameter(DbType dbType)
+ : this(null, dbType, 0, null, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified data type and sets the initial value
+ ///
+ /// The datatype of the parameter
+ /// The initial value of the parameter
+ public SQLiteParameter(DbType dbType, object value)
+ : this(null, dbType, 0, null, DataRowVersion.Current)
+ {
+ Value = value;
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified data type and source column
+ ///
+ /// The datatype of the parameter
+ /// The source column
+ public SQLiteParameter(DbType dbType, string sourceColumn)
+ : this(null, dbType, 0, sourceColumn, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified data type, source column and row version
+ ///
+ /// The data type
+ /// The source column
+ /// The row version information
+ public SQLiteParameter(DbType dbType, string sourceColumn, DataRowVersion rowVersion)
+ : this(null, dbType, 0, sourceColumn, rowVersion)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type and size
+ ///
+ /// The parameter name
+ /// The data type
+ /// The size of the parameter
+ public SQLiteParameter(string parameterName, DbType parameterType, int parameterSize)
+ : this(parameterName, parameterType, parameterSize, null, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type, size and source column
+ ///
+ /// The name of the parameter
+ /// The data type
+ /// The size of the parameter
+ /// The source column
+ public SQLiteParameter(string parameterName, DbType parameterType, int parameterSize, string sourceColumn)
+ : this(parameterName, parameterType, parameterSize, sourceColumn, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type, size, source column and row version
+ ///
+ /// The name of the parameter
+ /// The data type
+ /// The size of the parameter
+ /// The source column
+ /// The row version information
+ public SQLiteParameter(string parameterName, DbType parameterType, int parameterSize, string sourceColumn, DataRowVersion rowVersion)
+ {
+ _parameterName = parameterName;
+ _dbType = parameterType;
+ _sourceColumn = sourceColumn;
+ _rowVersion = rowVersion;
+ _dataSize = parameterSize;
+ _nullable = true;
+ }
+
+ private SQLiteParameter(SQLiteParameter source)
+ : this(source.ParameterName, source._dbType, 0, source.Direction, source.IsNullable, 0, 0, source.SourceColumn, source.SourceVersion, source.Value)
+ {
+ _nullMapping = source._nullMapping;
+ }
+
+ ///
+ /// Constructs a named parameter of the specified type, size, source column and row version
+ ///
+ /// The name of the parameter
+ /// The data type
+ /// The size of the parameter
+ /// Only input parameters are supported in SQLite
+ /// Ignored
+ /// Ignored
+ /// Ignored
+ /// The source column
+ /// The row version information
+ /// The initial value to assign the parameter
+#if !PLATFORM_COMPACTFRAMEWORK
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+#endif
+ public SQLiteParameter(string parameterName, DbType parameterType, int parameterSize, ParameterDirection direction, bool isNullable, byte precision, byte scale, string sourceColumn, DataRowVersion rowVersion, object value)
+ : this(parameterName, parameterType, parameterSize, sourceColumn, rowVersion)
+ {
+ Direction = direction;
+ IsNullable = isNullable;
+ Value = value;
+ }
+
+ ///
+ /// Constructs a named parameter, yet another flavor
+ ///
+ /// The name of the parameter
+ /// The data type
+ /// The size of the parameter
+ /// Only input parameters are supported in SQLite
+ /// Ignored
+ /// Ignored
+ /// The source column
+ /// The row version information
+ /// Whether or not this parameter is for comparing NULL's
+ /// The intial value to assign the parameter
+#if !PLATFORM_COMPACTFRAMEWORK
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+#endif
+ public SQLiteParameter(string parameterName, DbType parameterType, int parameterSize, ParameterDirection direction, byte precision, byte scale, string sourceColumn, DataRowVersion rowVersion, bool sourceColumnNullMapping, object value)
+ : this(parameterName, parameterType, parameterSize, sourceColumn, rowVersion)
+ {
+ Direction = direction;
+ SourceColumnNullMapping = sourceColumnNullMapping;
+ Value = value;
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified type and size
+ ///
+ /// The data type
+ /// The size of the parameter
+ public SQLiteParameter(DbType parameterType, int parameterSize)
+ : this(null, parameterType, parameterSize, null, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified type, size, and source column
+ ///
+ /// The data type
+ /// The size of the parameter
+ /// The source column
+ public SQLiteParameter(DbType parameterType, int parameterSize, string sourceColumn)
+ : this(null, parameterType, parameterSize, sourceColumn, DataRowVersion.Current)
+ {
+ }
+
+ ///
+ /// Constructs an unnamed parameter of the specified type, size, source column and row version
+ ///
+ /// The data type
+ /// The size of the parameter
+ /// The source column
+ /// The row version information
+ public SQLiteParameter(DbType parameterType, int parameterSize, string sourceColumn, DataRowVersion rowVersion)
+ : this(null, parameterType, parameterSize, sourceColumn, rowVersion)
+ {
+ }
+
+ ///
+ /// The command associated with this parameter.
+ ///
+ public IDbCommand Command
+ {
+ get
+ {
+ return _command;
+ }
+ set
+ {
+ _command = value;
+ }
+ }
+
+ ///
+ /// Whether or not the parameter can contain a null value
+ ///
+ public override bool IsNullable
+ {
+ get
+ {
+ return _nullable;
+ }
+ set
+ {
+ _nullable = value;
+ }
+ }
+
+ ///
+ /// Returns the datatype of the parameter
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DbProviderSpecificTypeProperty(true)]
+ [RefreshProperties(RefreshProperties.All)]
+#endif
+ public override DbType DbType
+ {
+ get
+ {
+ if (_dbType == UnknownDbType)
+ {
+ if (_objValue != null && _objValue != DBNull.Value)
+ {
+ return SQLiteConvert.TypeToDbType(_objValue.GetType());
+ }
+ return DbType.String; // Unassigned default value is String
+ }
+ return _dbType;
+ }
+ set
+ {
+ _dbType = value;
+ }
+ }
+
+ ///
+ /// Supports only input parameters
+ ///
+ public override ParameterDirection Direction
+ {
+ get
+ {
+ return ParameterDirection.Input;
+ }
+ set
+ {
+ if (value != ParameterDirection.Input)
+ throw new NotSupportedException();
+ }
+ }
+
+ ///
+ /// Returns the parameter name
+ ///
+ public override string ParameterName
+ {
+ get
+ {
+ return _parameterName;
+ }
+ set
+ {
+ _parameterName = value;
+ }
+ }
+
+ ///
+ /// Resets the DbType of the parameter so it can be inferred from the value
+ ///
+ public override void ResetDbType()
+ {
+ _dbType = UnknownDbType;
+ }
+
+ ///
+ /// Returns the size of the parameter
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DefaultValue((int)0)]
+#endif
+ public override int Size
+ {
+ get
+ {
+ return _dataSize;
+ }
+ set
+ {
+ _dataSize = value;
+ }
+ }
+
+ ///
+ /// Gets/sets the source column
+ ///
+ public override string SourceColumn
+ {
+ get
+ {
+ return _sourceColumn;
+ }
+ set
+ {
+ _sourceColumn = value;
+ }
+ }
+
+ ///
+ /// Used by DbCommandBuilder to determine the mapping for nullable fields
+ ///
+ public override bool SourceColumnNullMapping
+ {
+ get
+ {
+ return _nullMapping;
+ }
+ set
+ {
+ _nullMapping = value;
+ }
+ }
+
+ ///
+ /// Gets and sets the row version
+ ///
+ public override DataRowVersion SourceVersion
+ {
+ get
+ {
+ return _rowVersion;
+ }
+ set
+ {
+ _rowVersion = value;
+ }
+ }
+
+ ///
+ /// Gets and sets the parameter value. If no datatype was specified, the datatype will assume the type from the value given.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [TypeConverter(typeof(StringConverter)), RefreshProperties(RefreshProperties.All)]
+#endif
+ public override object Value
+ {
+ get
+ {
+ return _objValue;
+ }
+ set
+ {
+ _objValue = value;
+ if (_dbType == UnknownDbType && _objValue != null && _objValue != DBNull.Value) // If the DbType has never been assigned, try to glean one from the value's datatype
+ _dbType = SQLiteConvert.TypeToDbType(_objValue.GetType());
+ }
+ }
+
+ ///
+ /// The database type name associated with this parameter, if any.
+ ///
+ public string TypeName
+ {
+ get
+ {
+ return _typeName;
+ }
+ set
+ {
+ _typeName = value;
+ }
+ }
+
+ ///
+ /// Clones a parameter
+ ///
+ /// A new, unassociated SQLiteParameter
+ public object Clone()
+ {
+ SQLiteParameter newparam = new SQLiteParameter(this);
+
+ return newparam;
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteParameterCollection.cs b/Native.Csharp.Tool/SQLite/SQLiteParameterCollection.cs
new file mode 100644
index 00000000..2945cd47
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteParameterCollection.cs
@@ -0,0 +1,477 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+ using System.Collections.Generic;
+ using System.Globalization;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ using System.ComponentModel;
+#endif
+
+ using System.Reflection;
+
+ ///
+ /// SQLite implementation of DbParameterCollection.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [Editor("Microsoft.VSDesigner.Data.Design.DBParametersEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), ListBindable(false)]
+#endif
+ public sealed class SQLiteParameterCollection : DbParameterCollection
+ {
+ ///
+ /// The underlying command to which this collection belongs
+ ///
+ private SQLiteCommand _command;
+ ///
+ /// The internal array of parameters in this collection
+ ///
+ private List _parameterList;
+ ///
+ /// Determines whether or not all parameters have been bound to their statement(s)
+ ///
+ private bool _unboundFlag;
+
+ ///
+ /// Initializes the collection
+ ///
+ /// The command to which the collection belongs
+ internal SQLiteParameterCollection(SQLiteCommand cmd)
+ {
+ _command = cmd;
+ _parameterList = new List();
+ _unboundFlag = true;
+ }
+
+ ///
+ /// Returns false
+ ///
+ public override bool IsSynchronized
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Returns false
+ ///
+ public override bool IsFixedSize
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Returns false
+ ///
+ public override bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Returns null
+ ///
+ public override object SyncRoot
+ {
+ get { return null; }
+ }
+
+ ///
+ /// Retrieves an enumerator for the collection
+ ///
+ /// An enumerator for the underlying array
+ public override System.Collections.IEnumerator GetEnumerator()
+ {
+ return _parameterList.GetEnumerator();
+ }
+
+ ///
+ /// Adds a parameter to the collection
+ ///
+ /// The parameter name
+ /// The data type
+ /// The size of the value
+ /// The source column
+ /// A SQLiteParameter object
+ public SQLiteParameter Add(string parameterName, DbType parameterType, int parameterSize, string sourceColumn)
+ {
+ SQLiteParameter param = new SQLiteParameter(parameterName, parameterType, parameterSize, sourceColumn);
+ Add(param);
+
+ return param;
+ }
+
+ ///
+ /// Adds a parameter to the collection
+ ///
+ /// The parameter name
+ /// The data type
+ /// The size of the value
+ /// A SQLiteParameter object
+ public SQLiteParameter Add(string parameterName, DbType parameterType, int parameterSize)
+ {
+ SQLiteParameter param = new SQLiteParameter(parameterName, parameterType, parameterSize);
+ Add(param);
+
+ return param;
+ }
+
+ ///
+ /// Adds a parameter to the collection
+ ///
+ /// The parameter name
+ /// The data type
+ /// A SQLiteParameter object
+ public SQLiteParameter Add(string parameterName, DbType parameterType)
+ {
+ SQLiteParameter param = new SQLiteParameter(parameterName, parameterType);
+ Add(param);
+
+ return param;
+ }
+
+ ///
+ /// Adds a parameter to the collection
+ ///
+ /// The parameter to add
+ /// A zero-based index of where the parameter is located in the array
+ public int Add(SQLiteParameter parameter)
+ {
+ int n = -1;
+
+ if (String.IsNullOrEmpty(parameter.ParameterName) == false)
+ {
+ n = IndexOf(parameter.ParameterName);
+ }
+
+ if (n == -1)
+ {
+ n = _parameterList.Count;
+ _parameterList.Add((SQLiteParameter)parameter);
+ }
+
+ SetParameter(n, parameter);
+
+ return n;
+ }
+
+ ///
+ /// Adds a parameter to the collection
+ ///
+ /// The parameter to add
+ /// A zero-based index of where the parameter is located in the array
+#if !PLATFORM_COMPACTFRAMEWORK
+ [EditorBrowsable(EditorBrowsableState.Never)]
+#endif
+ public override int Add(object value)
+ {
+ return Add((SQLiteParameter)value);
+ }
+
+ ///
+ /// Adds a named/unnamed parameter and its value to the parameter collection.
+ ///
+ /// Name of the parameter, or null to indicate an unnamed parameter
+ /// The initial value of the parameter
+ /// Returns the SQLiteParameter object created during the call.
+ public SQLiteParameter AddWithValue(string parameterName, object value)
+ {
+ SQLiteParameter param = new SQLiteParameter(parameterName, value);
+ Add(param);
+
+ return param;
+ }
+
+ ///
+ /// Adds an array of parameters to the collection
+ ///
+ /// The array of parameters to add
+ public void AddRange(SQLiteParameter[] values)
+ {
+ int x = values.Length;
+ for (int n = 0; n < x; n++)
+ Add(values[n]);
+ }
+
+ ///
+ /// Adds an array of parameters to the collection
+ ///
+ /// The array of parameters to add
+ public override void AddRange(Array values)
+ {
+ int x = values.Length;
+ for (int n = 0; n < x; n++)
+ Add((SQLiteParameter)(values.GetValue(n)));
+ }
+
+ ///
+ /// Clears the array and resets the collection
+ ///
+ public override void Clear()
+ {
+ _unboundFlag = true;
+ _parameterList.Clear();
+ }
+
+ ///
+ /// Determines if the named parameter exists in the collection
+ ///
+ /// The name of the parameter to check
+ /// True if the parameter is in the collection
+ public override bool Contains(string parameterName)
+ {
+ return (IndexOf(parameterName) != -1);
+ }
+
+ ///
+ /// Determines if the parameter exists in the collection
+ ///
+ /// The SQLiteParameter to check
+ /// True if the parameter is in the collection
+ public override bool Contains(object value)
+ {
+ return _parameterList.Contains((SQLiteParameter)value);
+ }
+
+ ///
+ /// Not implemented
+ ///
+ ///
+ ///
+ public override void CopyTo(Array array, int index)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Returns a count of parameters in the collection
+ ///
+ public override int Count
+ {
+ get { return _parameterList.Count; }
+ }
+
+ ///
+ /// Overloaded to specialize the return value of the default indexer
+ ///
+ /// Name of the parameter to get/set
+ /// The specified named SQLite parameter
+ public new SQLiteParameter this[string parameterName]
+ {
+ get
+ {
+ return (SQLiteParameter)GetParameter(parameterName);
+ }
+ set
+ {
+ SetParameter(parameterName, value);
+ }
+ }
+
+ ///
+ /// Overloaded to specialize the return value of the default indexer
+ ///
+ /// The index of the parameter to get/set
+ /// The specified SQLite parameter
+ public new SQLiteParameter this[int index]
+ {
+ get
+ {
+ return (SQLiteParameter)GetParameter(index);
+ }
+ set
+ {
+ SetParameter(index, value);
+ }
+ }
+ ///
+ /// Retrieve a parameter by name from the collection
+ ///
+ /// The name of the parameter to fetch
+ /// A DbParameter object
+ protected override DbParameter GetParameter(string parameterName)
+ {
+ return GetParameter(IndexOf(parameterName));
+ }
+
+ ///
+ /// Retrieves a parameter by its index in the collection
+ ///
+ /// The index of the parameter to retrieve
+ /// A DbParameter object
+ protected override DbParameter GetParameter(int index)
+ {
+ return _parameterList[index];
+ }
+
+ ///
+ /// Returns the index of a parameter given its name
+ ///
+ /// The name of the parameter to find
+ /// -1 if not found, otherwise a zero-based index of the parameter
+ public override int IndexOf(string parameterName)
+ {
+ int x = _parameterList.Count;
+ for (int n = 0; n < x; n++)
+ {
+ if (String.Compare(parameterName, _parameterList[n].ParameterName, StringComparison.OrdinalIgnoreCase) == 0)
+ return n;
+ }
+ return -1;
+ }
+
+ ///
+ /// Returns the index of a parameter
+ ///
+ /// The parameter to find
+ /// -1 if not found, otherwise a zero-based index of the parameter
+ public override int IndexOf(object value)
+ {
+ return _parameterList.IndexOf((SQLiteParameter)value);
+ }
+
+ ///
+ /// Inserts a parameter into the array at the specified location
+ ///
+ /// The zero-based index to insert the parameter at
+ /// The parameter to insert
+ public override void Insert(int index, object value)
+ {
+ _unboundFlag = true;
+ _parameterList.Insert(index, (SQLiteParameter)value);
+ }
+
+ ///
+ /// Removes a parameter from the collection
+ ///
+ /// The parameter to remove
+ public override void Remove(object value)
+ {
+ _unboundFlag = true;
+ _parameterList.Remove((SQLiteParameter)value);
+ }
+
+ ///
+ /// Removes a parameter from the collection given its name
+ ///
+ /// The name of the parameter to remove
+ public override void RemoveAt(string parameterName)
+ {
+ RemoveAt(IndexOf(parameterName));
+ }
+
+ ///
+ /// Removes a parameter from the collection given its index
+ ///
+ /// The zero-based parameter index to remove
+ public override void RemoveAt(int index)
+ {
+ _unboundFlag = true;
+ _parameterList.RemoveAt(index);
+ }
+
+ ///
+ /// Re-assign the named parameter to a new parameter object
+ ///
+ /// The name of the parameter to replace
+ /// The new parameter
+ protected override void SetParameter(string parameterName, DbParameter value)
+ {
+ SetParameter(IndexOf(parameterName), value);
+ }
+
+ ///
+ /// Re-assign a parameter at the specified index
+ ///
+ /// The zero-based index of the parameter to replace
+ /// The new parameter
+ protected override void SetParameter(int index, DbParameter value)
+ {
+ _unboundFlag = true;
+ _parameterList[index] = (SQLiteParameter)value;
+ }
+
+ ///
+ /// Un-binds all parameters from their statements
+ ///
+ internal void Unbind()
+ {
+ _unboundFlag = true;
+ }
+
+ ///
+ /// This function attempts to map all parameters in the collection to all statements in a Command.
+ /// Since named parameters may span multiple statements, this function makes sure all statements are bound
+ /// to the same named parameter. Unnamed parameters are bound in sequence.
+ ///
+ internal void MapParameters(SQLiteStatement activeStatement)
+ {
+ if (_unboundFlag == false || _parameterList.Count == 0 || _command._statementList == null) return;
+
+ int nUnnamed = 0;
+ string s;
+ int n;
+ int y = -1;
+ SQLiteStatement stmt;
+
+ foreach(SQLiteParameter p in _parameterList)
+ {
+ y ++;
+ s = p.ParameterName;
+ if (s == null)
+ {
+ s = HelperMethods.StringFormat(CultureInfo.InvariantCulture, ";{0}", nUnnamed);
+ nUnnamed++;
+ }
+
+ int x;
+ bool isMapped = false;
+
+ if (activeStatement == null)
+ x = _command._statementList.Count;
+ else
+ x = 1;
+
+ stmt = activeStatement;
+ for (n = 0; n < x; n++)
+ {
+ isMapped = false;
+ if (stmt == null) stmt = _command._statementList[n];
+ if (stmt._paramNames != null)
+ {
+ if (stmt.MapParameter(s, p) == true)
+ isMapped = true;
+ }
+ stmt = null;
+ }
+
+ // If the parameter has a name, but the SQL statement uses unnamed references, this can happen -- attempt to map
+ // the parameter by its index in the collection
+ if (isMapped == false)
+ {
+ s = HelperMethods.StringFormat(CultureInfo.InvariantCulture, ";{0}", y);
+
+ stmt = activeStatement;
+ for (n = 0; n < x; n++)
+ {
+ if (stmt == null) stmt = _command._statementList[n];
+ if (stmt._paramNames != null)
+ {
+ if (stmt.MapParameter(s, p) == true)
+ isMapped = true;
+ }
+ stmt = null;
+ }
+ }
+ }
+ if (activeStatement == null) _unboundFlag = false;
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLitePatchLevel.cs b/Native.Csharp.Tool/SQLite/SQLitePatchLevel.cs
new file mode 100644
index 00000000..1e13a589
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLitePatchLevel.cs
@@ -0,0 +1,16 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Data.SQLite;
+
+///////////////////////////////////////////////////////////////////////////////
+
+[assembly: AssemblySourceId(null)]
+
+///////////////////////////////////////////////////////////////////////////////
+
+[assembly: AssemblySourceTimeStamp(null)]
diff --git a/Native.Csharp.Tool/SQLite/SQLiteSession.cs b/Native.Csharp.Tool/SQLite/SQLiteSession.cs
new file mode 100644
index 00000000..a02dd2c3
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteSession.cs
@@ -0,0 +1,5568 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Joe Mistachkin (joe@mistachkin.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+using System.Collections;
+using System.Collections.Generic;
+
+#if DEBUG
+using System.Diagnostics;
+#endif
+
+using System.IO;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace System.Data.SQLite
+{
+ #region Session Extension Enumerations
+ ///
+ /// This enumerated type represents a type of conflict seen when apply
+ /// changes from a change set or patch set.
+ ///
+ public enum SQLiteChangeSetConflictType
+ {
+ ///
+ /// This value is seen when processing a DELETE or UPDATE change if a
+ /// row with the required PRIMARY KEY fields is present in the
+ /// database, but one or more other (non primary-key) fields modified
+ /// by the update do not contain the expected "before" values.
+ ///
+ Data = 1,
+
+ ///
+ /// This value is seen when processing a DELETE or UPDATE change if a
+ /// row with the required PRIMARY KEY fields is not present in the
+ /// database. There is no conflicting row in this case.
+ ///
+ /// The results of invoking the
+ ///
+ /// method are undefined.
+ ///
+ NotFound = 2,
+
+ ///
+ /// This value is seen when processing an INSERT change if the
+ /// operation would result in duplicate primary key values.
+ /// The conflicting row in this case is the database row with the
+ /// matching primary key.
+ ///
+ Conflict = 3,
+
+ ///
+ /// If a non-foreign key constraint violation occurs while applying a
+ /// change (i.e. a UNIQUE, CHECK or NOT NULL constraint), the conflict
+ /// callback will see this value.
+ ///
+ /// There is no conflicting row in this case. The results of invoking
+ /// the
+ /// method are undefined.
+ ///
+ Constraint = 4,
+
+ ///
+ /// If foreign key handling is enabled, and applying a changes leaves
+ /// the database in a state containing foreign key violations, this
+ /// value will be seen exactly once before the changes are committed.
+ /// If the conflict handler
+ /// , the changes,
+ /// including those that caused the foreign key constraint violation,
+ /// are committed. Or, if it returns
+ /// , the changes are
+ /// rolled back.
+ ///
+ /// No current or conflicting row information is provided. The only
+ /// method it is possible to call on the supplied
+ /// object is
+ /// .
+ ///
+ ForeignKey = 5
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This enumerated type represents the result of a user-defined conflict
+ /// resolution callback.
+ ///
+ public enum SQLiteChangeSetConflictResult
+ {
+ ///
+ /// If a conflict callback returns this value no special action is
+ /// taken. The change that caused the conflict is not applied. The
+ /// application of changes continues with the next change.
+ ///
+ Omit = 0,
+
+ ///
+ /// This value may only be returned from a conflict callback if the
+ /// type of conflict was
+ /// or . If this is
+ /// not the case, any changes applied so far are rolled back and the
+ /// call to
+ ///
+ /// will raise a with an error code of
+ /// .
+ ///
+ /// If this value is returned for a
+ /// conflict, then the
+ /// conflicting row is either updated or deleted, depending on the type
+ /// of change.
+ ///
+ /// If this value is returned for a
+ /// conflict, then
+ /// the conflicting row is removed from the database and a second
+ /// attempt to apply the change is made. If this second attempt fails,
+ /// the original row is restored to the database before continuing.
+ ///
+ Replace = 1,
+
+ ///
+ /// If this value is returned, any changes applied so far are rolled
+ /// back and the call to
+ ///
+ /// will raise a with an error code of
+ /// .
+ ///
+ Abort = 2
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This enumerated type represents possible flags that may be passed
+ /// to the appropriate overloads of various change set creation methods.
+ ///
+ public enum SQLiteChangeSetStartFlags
+ {
+ ///
+ /// No special handling.
+ ///
+ None = 0x0,
+
+ ///
+ /// Invert the change set while iterating through it.
+ /// This is equivalent to inverting a change set using
+ /// before
+ /// applying it. It is an error to specify this flag
+ /// with a patch set.
+ ///
+ Invert = 0x2
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Session Extension Delegates
+ ///
+ /// This callback is invoked when a determination must be made about
+ /// whether changes to a specific table should be tracked -OR- applied.
+ /// It will not be called for tables that are already attached to a
+ /// .
+ ///
+ ///
+ /// The optional application-defined context data that was originally
+ /// passed to the or
+ ///
+ /// methods. This value may be null.
+ ///
+ ///
+ /// The name of the table.
+ ///
+ ///
+ /// Non-zero if changes to the table should be considered; otherwise,
+ /// zero. Throwing an exception from this callback will result in
+ /// undefined behavior.
+ ///
+ public delegate bool SessionTableFilterCallback(
+ object clientData,
+ string name
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This callback is invoked when there is a conflict while apply changes
+ /// to a database.
+ ///
+ ///
+ /// The optional application-defined context data that was originally
+ /// passed to the
+ ///
+ /// method. This value may be null.
+ ///
+ ///
+ /// The type of this conflict.
+ ///
+ ///
+ /// The object associated with
+ /// this conflict. This value may not be null; however, only properties
+ /// that are applicable to the conflict type will be available. Further
+ /// information on this is available within the descriptions of the
+ /// available values.
+ ///
+ ///
+ /// A value that indicates the
+ /// action to be taken in order to resolve the conflict. Throwing an
+ /// exception from this callback will result in undefined behavior.
+ ///
+ public delegate SQLiteChangeSetConflictResult SessionConflictCallback(
+ object clientData,
+ SQLiteChangeSetConflictType type,
+ ISQLiteChangeSetMetadataItem item
+ );
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeSet Interface
+ ///
+ /// This interface contains methods used to manipulate a set of changes for
+ /// a database.
+ ///
+ public interface ISQLiteChangeSet :
+ IEnumerable, IDisposable
+ {
+ ///
+ /// This method "inverts" the set of changes within this instance.
+ /// Applying an inverted set of changes to a database reverses the
+ /// effects of applying the uninverted changes. Specifically:
+ /// ]]>]]>
+ /// Each DELETE change is changed to an INSERT, and
+ /// ]]>]]>
+ /// Each INSERT change is changed to a DELETE, and
+ /// ]]>]]>
+ /// For each UPDATE change, the old.* and new.* values are exchanged.
+ /// ]]>]]>
+ /// This method does not change the order in which changes appear
+ /// within the set of changes. It merely reverses the sense of each
+ /// individual change.
+ ///
+ ///
+ /// The new instance that represents
+ /// the resulting set of changes -OR- null if it is not available.
+ ///
+ ISQLiteChangeSet Invert();
+
+ ///
+ /// This method combines the specified set of changes with the ones
+ /// contained in this instance.
+ ///
+ ///
+ /// The changes to be combined with those in this instance.
+ ///
+ ///
+ /// The new instance that represents
+ /// the resulting set of changes -OR- null if it is not available.
+ ///
+ ISQLiteChangeSet CombineWith(ISQLiteChangeSet changeSet);
+
+ ///
+ /// Attempts to apply the set of changes in this instance to the
+ /// associated database.
+ ///
+ ///
+ /// The delegate that will need
+ /// to handle any conflicting changes that may arise.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ void Apply(
+ SessionConflictCallback conflictCallback,
+ object clientData
+ );
+
+ ///
+ /// Attempts to apply the set of changes in this instance to the
+ /// associated database.
+ ///
+ ///
+ /// The delegate that will need
+ /// to handle any conflicting changes that may arise.
+ ///
+ ///
+ /// The optional delegate
+ /// that can be used to filter the list of tables impacted by the set
+ /// of changes.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ void Apply(
+ SessionConflictCallback conflictCallback,
+ SessionTableFilterCallback tableFilterCallback,
+ object clientData
+ );
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeGroup Interface
+ ///
+ /// This interface contains methods used to manipulate multiple sets of
+ /// changes for a database.
+ ///
+ public interface ISQLiteChangeGroup : IDisposable
+ {
+ ///
+ /// Attempts to add a change set (or patch set) to this change group
+ /// instance. The underlying data must be contained entirely within
+ /// the byte array.
+ ///
+ ///
+ /// The raw byte data for the specified change set (or patch set).
+ ///
+ void AddChangeSet(byte[] rawData);
+
+ ///
+ /// Attempts to add a change set (or patch set) to this change group
+ /// instance. The underlying data will be read from the specified
+ /// .
+ ///
+ ///
+ /// The instance containing the raw change set
+ /// (or patch set) data to read.
+ ///
+ void AddChangeSet(Stream stream);
+
+ ///
+ /// Attempts to create and return, via , the
+ /// combined set of changes represented by this change group instance.
+ ///
+ ///
+ /// Upon success, this will contain the raw byte data for all the
+ /// changes in this change group instance.
+ ///
+ void CreateChangeSet(ref byte[] rawData);
+
+ ///
+ /// Attempts to create and write, via , the
+ /// combined set of changes represented by this change group instance.
+ ///
+ ///
+ /// Upon success, the raw byte data for all the changes in this change
+ /// group instance will be written to this .
+ ///
+ void CreateChangeSet(Stream stream);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeSetMetadataItem Interface
+ ///
+ /// This interface contains properties and methods used to fetch metadata
+ /// about one change within a set of changes for a database.
+ ///
+ public interface ISQLiteChangeSetMetadataItem : IDisposable
+ {
+ ///
+ /// The name of the table the change was made to.
+ ///
+ string TableName { get; }
+
+ ///
+ /// The number of columns impacted by this change. This value can be
+ /// used to determine the highest valid column index that may be used
+ /// with the , ,
+ /// and methods of this interface. It
+ /// will be this value minus one.
+ ///
+ int NumberOfColumns { get; }
+
+ ///
+ /// This will contain the value
+ /// ,
+ /// , or
+ /// , corresponding to
+ /// the overall type of change this item represents.
+ ///
+ SQLiteAuthorizerActionCode OperationCode { get; }
+
+ ///
+ /// Non-zero if this change is considered to be indirect (i.e. as
+ /// though they were made via a trigger or foreign key action).
+ ///
+ bool Indirect { get; }
+
+ ///
+ /// This array contains a for each column in
+ /// the table associated with this change. The element will be zero
+ /// if the column is not part of the primary key; otherwise, it will
+ /// be non-zero.
+ ///
+ bool[] PrimaryKeyColumns { get; }
+
+ ///
+ /// This method may only be called from within a
+ /// delegate when the conflict
+ /// type is . It
+ /// returns the total number of known foreign key violations in the
+ /// destination database.
+ ///
+ int NumberOfForeignKeyConflicts { get; }
+
+ ///
+ /// Queries and returns the original value of a given column for this
+ /// change. This method may only be called when the
+ /// has a value of
+ /// or
+ /// .
+ ///
+ ///
+ /// The index for the column. This value must be between zero and one
+ /// less than the total number of columns for this table.
+ ///
+ ///
+ /// The original value of a given column for this change.
+ ///
+ SQLiteValue GetOldValue(int columnIndex);
+
+ ///
+ /// Queries and returns the updated value of a given column for this
+ /// change. This method may only be called when the
+ /// has a value of
+ /// or
+ /// .
+ ///
+ ///
+ /// The index for the column. This value must be between zero and one
+ /// less than the total number of columns for this table.
+ ///
+ ///
+ /// The updated value of a given column for this change.
+ ///
+ SQLiteValue GetNewValue(int columnIndex);
+
+ ///
+ /// Queries and returns the conflicting value of a given column for
+ /// this change. This method may only be called from within a
+ /// delegate when the conflict
+ /// type is or
+ /// .
+ ///
+ ///
+ /// The index for the column. This value must be between zero and one
+ /// less than the total number of columns for this table.
+ ///
+ ///
+ /// The conflicting value of a given column for this change.
+ ///
+ SQLiteValue GetConflictValue(int columnIndex);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteSession Interface
+ ///
+ /// This interface contains methods to query and manipulate the state of a
+ /// change tracking session for a database.
+ ///
+ public interface ISQLiteSession : IDisposable
+ {
+ ///
+ /// Determines if this session is currently tracking changes to its
+ /// associated database.
+ ///
+ ///
+ /// Non-zero if changes to the associated database are being trakced;
+ /// otherwise, zero.
+ ///
+ bool IsEnabled();
+
+ ///
+ /// Enables tracking of changes to the associated database.
+ ///
+ void SetToEnabled();
+
+ ///
+ /// Disables tracking of changes to the associated database.
+ ///
+ void SetToDisabled();
+
+ ///
+ /// Determines if this session is currently set to mark changes as
+ /// indirect (i.e. as though they were made via a trigger or foreign
+ /// key action).
+ ///
+ ///
+ /// Non-zero if changes to the associated database are being marked as
+ /// indirect; otherwise, zero.
+ ///
+ bool IsIndirect();
+
+ ///
+ /// Sets the indirect flag for this session. Subsequent changes will
+ /// be marked as indirect until this flag is changed again.
+ ///
+ void SetToIndirect();
+
+ ///
+ /// Clears the indirect flag for this session. Subsequent changes will
+ /// be marked as direct until this flag is changed again.
+ ///
+ void SetToDirect();
+
+ ///
+ /// Determines if there are any tracked changes currently within the
+ /// data for this session.
+ ///
+ ///
+ /// Non-zero if there are no changes within the data for this session;
+ /// otherwise, zero.
+ ///
+ bool IsEmpty();
+
+ ///
+ /// Upon success, causes changes to the specified table(s) to start
+ /// being tracked. Any tables impacted by calls to this method will
+ /// not cause the callback
+ /// to be invoked.
+ ///
+ ///
+ /// The name of the table to be tracked -OR- null to track all
+ /// applicable tables within this database.
+ ///
+ void AttachTable(string name);
+
+ ///
+ /// This method is used to set the table filter for this instance.
+ ///
+ ///
+ /// The table filter callback -OR- null to clear any existing table
+ /// filter callback.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ void SetTableFilter(
+ SessionTableFilterCallback callback,
+ object clientData
+ );
+
+ ///
+ /// Attempts to create and return, via , the
+ /// combined set of changes represented by this session instance.
+ ///
+ ///
+ /// Upon success, this will contain the raw byte data for all the
+ /// changes in this session instance.
+ ///
+ void CreateChangeSet(ref byte[] rawData);
+
+ ///
+ /// Attempts to create and write, via , the
+ /// combined set of changes represented by this session instance.
+ ///
+ ///
+ /// Upon success, the raw byte data for all the changes in this session
+ /// instance will be written to this .
+ ///
+ void CreateChangeSet(Stream stream);
+
+ ///
+ /// Attempts to create and return, via , the
+ /// combined set of changes represented by this session instance as a
+ /// patch set.
+ ///
+ ///
+ /// Upon success, this will contain the raw byte data for all the
+ /// changes in this session instance.
+ ///
+ void CreatePatchSet(ref byte[] rawData);
+
+ ///
+ /// Attempts to create and write, via , the
+ /// combined set of changes represented by this session instance as a
+ /// patch set.
+ ///
+ ///
+ /// Upon success, the raw byte data for all the changes in this session
+ /// instance will be written to this .
+ ///
+ void CreatePatchSet(Stream stream);
+
+ ///
+ /// This method loads the differences between two tables [with the same
+ /// name, set of columns, and primary key definition] into this session
+ /// instance.
+ ///
+ ///
+ /// The name of the database containing the table with the original
+ /// data (i.e. it will need updating in order to be identical to the
+ /// one within the database associated with this session instance).
+ ///
+ ///
+ /// The name of the table.
+ ///
+ void LoadDifferencesFromTable(
+ string fromDatabaseName,
+ string tableName
+ );
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteSessionHelpers Class
+ ///
+ /// This class contains some static helper methods for use within this
+ /// subsystem.
+ ///
+ internal static class SQLiteSessionHelpers
+ {
+ #region Public Methods
+ ///
+ /// This method checks the byte array specified by the caller to make
+ /// sure it will be usable.
+ ///
+ ///
+ /// A byte array provided by the caller into one of the public methods
+ /// for the classes that belong to this subsystem. This value cannot
+ /// be null or represent an empty array; otherwise, an appropriate
+ /// exception will be thrown.
+ ///
+ public static void CheckRawData(
+ byte[] rawData
+ )
+ {
+ if (rawData == null)
+ throw new ArgumentNullException("rawData");
+
+ if (rawData.Length == 0)
+ {
+ throw new ArgumentException(
+ "empty change set data", "rawData");
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteConnectionLock Class
+ ///
+ /// This class is used to hold the native connection handle associated with
+ /// a open until this subsystem is totally
+ /// done with it. This class is for internal use by this subsystem only.
+ ///
+ internal abstract class SQLiteConnectionLock : IDisposable
+ {
+ #region Private Constants
+ ///
+ /// The SQL statement used when creating the native statement handle.
+ /// There are no special requirements for this other than counting as
+ /// an "open statement handle".
+ ///
+ private const string LockNopSql = "SELECT 1;";
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The format of the error message used when reporting, during object
+ /// disposal, that the statement handle is still open (i.e. because
+ /// this situation is considered a fairly serious programming error).
+ ///
+ private const string StatementMessageFormat =
+ "Connection lock object was {0} with statement {1}";
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// The wrapped native connection handle associated with this lock.
+ ///
+ private SQLiteConnectionHandle handle;
+
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ private SQLiteConnectionFlags flags;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The native statement handle for this lock. The garbage collector
+ /// cannot cause this statement to be finalized; therefore, it will
+ /// serve to hold the associated native connection open until it is
+ /// freed manually using the method.
+ ///
+ private IntPtr statement;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs a new instance of this class using the specified wrapped
+ /// native connection handle and associated flags.
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// lock.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ ///
+ /// Non-zero if the method should be called prior
+ /// to returning from this constructor.
+ ///
+ public SQLiteConnectionLock(
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags flags,
+ bool autoLock
+ )
+ {
+ this.handle = handle;
+ this.flags = flags;
+
+ if (autoLock)
+ Lock();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Queries and returns the wrapped native connection handle for this
+ /// instance.
+ ///
+ ///
+ /// The wrapped native connection handle for this instance -OR- null
+ /// if it is unavailable.
+ ///
+ protected SQLiteConnectionHandle GetHandle()
+ {
+ return handle;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the flags associated with the connection for
+ /// this instance.
+ ///
+ ///
+ /// The value. There is no return
+ /// value reserved to indicate an error.
+ ///
+ protected SQLiteConnectionFlags GetFlags()
+ {
+ return flags;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the native connection handle for this instance.
+ ///
+ ///
+ /// The native connection handle for this instance. If this value is
+ /// unavailable or invalid an exception will be thrown.
+ ///
+ protected IntPtr GetIntPtr()
+ {
+ if (handle == null)
+ {
+ throw new InvalidOperationException(
+ "Connection lock object has an invalid handle.");
+ }
+
+ IntPtr handlePtr = handle;
+
+ if (handlePtr == IntPtr.Zero)
+ {
+ throw new InvalidOperationException(
+ "Connection lock object has an invalid handle pointer.");
+ }
+
+ return handlePtr;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// This method attempts to "lock" the associated native connection
+ /// handle by preparing a SQL statement that will not be finalized
+ /// until the method is called (i.e. and which
+ /// cannot be done by the garbage collector). If the statement is
+ /// already prepared, nothing is done. If the statement cannot be
+ /// prepared for any reason, an exception will be thrown.
+ ///
+ public void Lock()
+ {
+ CheckDisposed();
+
+ if (statement != IntPtr.Zero)
+ return;
+
+ IntPtr pSql = IntPtr.Zero;
+
+ try
+ {
+ int nSql = 0;
+
+ pSql = SQLiteString.Utf8IntPtrFromString(LockNopSql, ref nSql);
+
+ IntPtr pRemain = IntPtr.Zero;
+
+#if !SQLITE_STANDARD
+ int nRemain = 0;
+ string functionName = "sqlite3_prepare_interop";
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_prepare_interop(
+ GetIntPtr(), pSql, nSql, ref statement, ref pRemain,
+ ref nRemain);
+#else
+#if USE_PREPARE_V2
+ string functionName = "sqlite3_prepare_v2";
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_prepare_v2(
+ GetIntPtr(), pSql, nSql, ref statement, ref pRemain);
+#else
+ string functionName = "sqlite3_prepare";
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_prepare(
+ GetIntPtr(), pSql, nSql, ref statement, ref pRemain);
+#endif
+#endif
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, functionName);
+ }
+ finally
+ {
+ if (pSql != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pSql);
+ pSql = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method attempts to "unlock" the associated native connection
+ /// handle by finalizing the previously prepared statement. If the
+ /// statement is already finalized, nothing is done. If the statement
+ /// cannot be finalized for any reason, an exception will be thrown.
+ ///
+ public void Unlock()
+ {
+ CheckDisposed();
+
+ if (statement == IntPtr.Zero)
+ return;
+
+#if !SQLITE_STANDARD
+ string functionName = "sqlite3_finalize_interop";
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_finalize_interop(
+ statement);
+#else
+ string functionName = "sqlite3_finalize";
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3_finalize(
+ statement);
+#endif
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, functionName);
+
+ statement = IntPtr.Zero;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteConnectionLock).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ if (statement != IntPtr.Zero)
+ {
+ //
+ // NOTE: This should never happen. This object was
+ // disposed (or finalized) without the Unlock
+ // method being called first.
+ //
+ try
+ {
+ if (HelperMethods.LogPrepare(GetFlags()))
+ {
+ /* throw */
+ SQLiteLog.LogMessage(
+ SQLiteErrorCode.Misuse,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ StatementMessageFormat, disposing ?
+ "disposed" : "finalized",
+ statement));
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+#if DEBUG
+ Debugger.Break();
+#endif
+ }
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteConnectionLock()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteChangeSetIterator Class
+ ///
+ /// This class manages the native change set iterator. It is used as the
+ /// base class for the and
+ /// classes. It knows how to
+ /// advance the native iterator handle as well as finalize it.
+ ///
+ internal class SQLiteChangeSetIterator : IDisposable
+ {
+ #region Private Data
+ ///
+ /// The native change set (a.k.a. iterator) handle.
+ ///
+ private IntPtr iterator;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Non-zero if this instance owns the native iterator handle in the
+ /// field. In that case, this instance will
+ /// finalize the native iterator handle upon being disposed or
+ /// finalized.
+ ///
+ private bool ownHandle;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Constructors
+ ///
+ /// Constructs a new instance of this class using the specified native
+ /// iterator handle.
+ ///
+ ///
+ /// The native iterator handle to use.
+ ///
+ ///
+ /// Non-zero if this instance is to take ownership of the native
+ /// iterator handle specified by .
+ ///
+ protected SQLiteChangeSetIterator(
+ IntPtr iterator,
+ bool ownHandle
+ )
+ {
+ this.iterator = iterator;
+ this.ownHandle = ownHandle;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the native iterator handle is invalid.
+ ///
+ internal void CheckHandle()
+ {
+ if (iterator == IntPtr.Zero)
+ throw new InvalidOperationException("iterator is not open");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Used to query the native iterator handle. This method is only used
+ /// by the class.
+ ///
+ ///
+ /// The native iterator handle -OR- if it
+ /// is not available.
+ ///
+ internal IntPtr GetIntPtr()
+ {
+ return iterator;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Attempts to advance the native iterator handle to its next item.
+ ///
+ ///
+ /// Non-zero if the native iterator handle was advanced and contains
+ /// more data; otherwise, zero. If the underlying native API returns
+ /// an unexpected value then an exception will be thrown.
+ ///
+ public bool Next()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_next(
+ iterator);
+
+ switch (rc)
+ {
+ case SQLiteErrorCode.Ok:
+ {
+ throw new SQLiteException(SQLiteErrorCode.Ok,
+ "sqlite3changeset_next: unexpected result Ok");
+ }
+ case SQLiteErrorCode.Row:
+ {
+ return true;
+ }
+ case SQLiteErrorCode.Done:
+ {
+ return false;
+ }
+ default:
+ {
+ throw new SQLiteException(rc, "sqlite3changeset_next");
+ }
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Static "Factory" Methods
+ ///
+ /// Attempts to create an instance of this class that is associated
+ /// with the specified native iterator handle. Ownership of the
+ /// native iterator handle is NOT transferred to the new instance of
+ /// this class.
+ ///
+ ///
+ /// The native iterator handle to use.
+ ///
+ ///
+ /// The new instance of this class. No return value is reserved to
+ /// indicate an error; however, if the native iterator handle is not
+ /// valid, any subsequent attempt to make use of it via the returned
+ /// instance of this class may throw exceptions.
+ ///
+ public static SQLiteChangeSetIterator Attach(
+ IntPtr iterator
+ )
+ {
+ return new SQLiteChangeSetIterator(iterator, false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteChangeSetIterator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ if (iterator != IntPtr.Zero)
+ {
+ if (ownHandle)
+ {
+ UnsafeNativeMethods.sqlite3changeset_finalize(
+ iterator);
+ }
+
+ iterator = IntPtr.Zero;
+ }
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteChangeSetIterator()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteMemoryChangeSetIterator Class
+ ///
+ /// This class manages the native change set iterator for a set of changes
+ /// contained entirely in memory.
+ ///
+ internal sealed class SQLiteMemoryChangeSetIterator :
+ SQLiteChangeSetIterator
+ {
+ #region Private Data
+ ///
+ /// The native memory buffer allocated to contain the set of changes
+ /// associated with this instance. This will always be freed when this
+ /// instance is disposed or finalized.
+ ///
+ private IntPtr pData;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// memory buffer and native iterator handle.
+ ///
+ ///
+ /// The native memory buffer to use.
+ ///
+ ///
+ /// The native iterator handle to use.
+ ///
+ ///
+ /// Non-zero if this instance is to take ownership of the native
+ /// iterator handle specified by .
+ ///
+ private SQLiteMemoryChangeSetIterator(
+ IntPtr pData,
+ IntPtr iterator,
+ bool ownHandle
+ )
+ : base(iterator, ownHandle)
+ {
+ this.pData = pData;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Static "Factory" Methods
+ ///
+ /// Attempts to create an instance of this class using the specified
+ /// raw byte data.
+ ///
+ ///
+ /// The raw byte data containing the set of changes for this native
+ /// iterator.
+ ///
+ ///
+ /// The new instance of this class -OR- null if it cannot be created.
+ ///
+ public static SQLiteMemoryChangeSetIterator Create(
+ byte[] rawData
+ )
+ {
+ SQLiteSessionHelpers.CheckRawData(rawData);
+
+ SQLiteMemoryChangeSetIterator result = null;
+ IntPtr pData = IntPtr.Zero;
+ IntPtr iterator = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ pData = SQLiteBytes.ToIntPtr(rawData, ref nData);
+
+ if (pData == IntPtr.Zero)
+ throw new SQLiteException(SQLiteErrorCode.NoMem, null);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start(
+ ref iterator, nData, pData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_start");
+
+ result = new SQLiteMemoryChangeSetIterator(
+ pData, iterator, true);
+ }
+ finally
+ {
+ if (result == null)
+ {
+ if (iterator != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3changeset_finalize(
+ iterator);
+
+ iterator = IntPtr.Zero;
+ }
+
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create an instance of this class using the specified
+ /// raw byte data.
+ ///
+ ///
+ /// The raw byte data containing the set of changes for this native
+ /// iterator.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ ///
+ /// The new instance of this class -OR- null if it cannot be created.
+ ///
+ public static SQLiteMemoryChangeSetIterator Create(
+ byte[] rawData,
+ SQLiteChangeSetStartFlags flags
+ )
+ {
+ SQLiteSessionHelpers.CheckRawData(rawData);
+
+ SQLiteMemoryChangeSetIterator result = null;
+ IntPtr pData = IntPtr.Zero;
+ IntPtr iterator = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ pData = SQLiteBytes.ToIntPtr(rawData, ref nData);
+
+ if (pData == IntPtr.Zero)
+ throw new SQLiteException(SQLiteErrorCode.NoMem, null);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start_v2(
+ ref iterator, nData, pData, flags);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_start_v2");
+
+ result = new SQLiteMemoryChangeSetIterator(
+ pData, iterator, true);
+ }
+ finally
+ {
+ if (result == null)
+ {
+ if (iterator != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3changeset_finalize(
+ iterator);
+
+ iterator = IntPtr.Zero;
+ }
+
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+
+ return result;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteMemoryChangeSetIterator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ //
+ // NOTE: Must dispose of the base class first (leaky abstraction)
+ // because it contains the iterator handle, which must be
+ // closed *prior* to freeing the underlying memory.
+ //
+ base.Dispose(disposing);
+
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteStreamChangeSetIterator Class
+ ///
+ /// This class manages the native change set iterator for a set of changes
+ /// backed by a instance.
+ ///
+ internal sealed class SQLiteStreamChangeSetIterator :
+ SQLiteChangeSetIterator
+ {
+ #region Private Data
+ ///
+ /// The instance that is managing
+ /// the underlying used as the backing store for
+ /// the set of changes associated with this native change set iterator.
+ ///
+ private SQLiteStreamAdapter streamAdapter;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified native
+ /// iterator handle and .
+ ///
+ ///
+ /// The instance to use.
+ ///
+ ///
+ /// The native iterator handle to use.
+ ///
+ ///
+ /// Non-zero if this instance is to take ownership of the native
+ /// iterator handle specified by .
+ ///
+ private SQLiteStreamChangeSetIterator(
+ SQLiteStreamAdapter streamAdapter,
+ IntPtr iterator,
+ bool ownHandle
+ )
+ : base(iterator, ownHandle)
+ {
+ this.streamAdapter = streamAdapter;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Static "Factory" Methods
+ ///
+ /// Attempts to create an instance of this class using the specified
+ /// .
+ ///
+ ///
+ /// The where the raw byte data for the set of
+ /// changes may be read.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ ///
+ /// The new instance of this class -OR- null if it cannot be created.
+ ///
+ public static SQLiteStreamChangeSetIterator Create(
+ Stream stream,
+ SQLiteConnectionFlags connectionFlags
+ )
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ SQLiteStreamAdapter streamAdapter = null;
+ SQLiteStreamChangeSetIterator result = null;
+ IntPtr iterator = IntPtr.Zero;
+
+ try
+ {
+ streamAdapter = new SQLiteStreamAdapter(stream, connectionFlags);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start_strm(
+ ref iterator, streamAdapter.GetInputDelegate(), IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ throw new SQLiteException(
+ rc, "sqlite3changeset_start_strm");
+ }
+
+ result = new SQLiteStreamChangeSetIterator(
+ streamAdapter, iterator, true);
+ }
+ finally
+ {
+ if (result == null)
+ {
+ if (iterator != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3changeset_finalize(
+ iterator);
+
+ iterator = IntPtr.Zero;
+ }
+
+ if (streamAdapter != null)
+ {
+ streamAdapter.Dispose();
+ streamAdapter = null;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create an instance of this class using the specified
+ /// .
+ ///
+ ///
+ /// The where the raw byte data for the set of
+ /// changes may be read.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ ///
+ /// The new instance of this class -OR- null if it cannot be created.
+ ///
+ public static SQLiteStreamChangeSetIterator Create(
+ Stream stream,
+ SQLiteConnectionFlags connectionFlags,
+ SQLiteChangeSetStartFlags startFlags
+ )
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ SQLiteStreamAdapter streamAdapter = null;
+ SQLiteStreamChangeSetIterator result = null;
+ IntPtr iterator = IntPtr.Zero;
+
+ try
+ {
+ streamAdapter = new SQLiteStreamAdapter(stream, connectionFlags);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_start_v2_strm(
+ ref iterator, streamAdapter.GetInputDelegate(), IntPtr.Zero,
+ startFlags);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ throw new SQLiteException(
+ rc, "sqlite3changeset_start_v2_strm");
+ }
+
+ result = new SQLiteStreamChangeSetIterator(
+ streamAdapter, iterator, true);
+ }
+ finally
+ {
+ if (result == null)
+ {
+ if (iterator != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3changeset_finalize(
+ iterator);
+
+ iterator = IntPtr.Zero;
+ }
+
+ if (streamAdapter != null)
+ {
+ streamAdapter.Dispose();
+ streamAdapter = null;
+ }
+ }
+ }
+
+ return result;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteStreamChangeSetIterator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ //if (disposing)
+ //{
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ //}
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteStreamAdapter Class
+ ///
+ /// This class is used to act as a bridge between a
+ /// instance and the delegates used with the native streaming API.
+ ///
+ internal sealed class SQLiteStreamAdapter : IDisposable
+ {
+ #region Private Data
+ ///
+ /// The managed stream instance used to in order to service the native
+ /// delegates for both input and output.
+ ///
+ private Stream stream;
+
+ ///
+ /// The flags associated with the connection.
+ ///
+ private SQLiteConnectionFlags flags;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The delegate used to provide input to the native streaming API.
+ /// It will be null -OR- point to the method.
+ ///
+ private UnsafeNativeMethods.xSessionInput xInput;
+
+ ///
+ /// The delegate used to provide output to the native streaming API.
+ /// It will be null -OR- point to the method.
+ ///
+ private UnsafeNativeMethods.xSessionOutput xOutput;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs a new instance of this class using the specified managed
+ /// stream and connection flags.
+ ///
+ ///
+ /// The managed stream instance to be used in order to service the
+ /// native delegates for both input and output.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ public SQLiteStreamAdapter(
+ Stream stream,
+ SQLiteConnectionFlags flags
+ )
+ {
+ this.stream = stream;
+ this.flags = flags;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Queries and returns the flags associated with the connection for
+ /// this instance.
+ ///
+ ///
+ /// The value. There is no return
+ /// value reserved to indicate an error.
+ ///
+ private SQLiteConnectionFlags GetFlags()
+ {
+ return flags;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Returns a delegate that wraps the method,
+ /// creating it first if necessary.
+ ///
+ ///
+ /// A delegate that refers to the method.
+ ///
+ public UnsafeNativeMethods.xSessionInput GetInputDelegate()
+ {
+ CheckDisposed();
+
+ if (xInput == null)
+ xInput = new UnsafeNativeMethods.xSessionInput(Input);
+
+ return xInput;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns a delegate that wraps the method,
+ /// creating it first if necessary.
+ ///
+ ///
+ /// A delegate that refers to the method.
+ ///
+ public UnsafeNativeMethods.xSessionOutput GetOutputDelegate()
+ {
+ CheckDisposed();
+
+ if (xOutput == null)
+ xOutput = new UnsafeNativeMethods.xSessionOutput(Output);
+
+ return xOutput;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Native Callback Methods
+ ///
+ /// This method attempts to read bytes from
+ /// the managed stream, writing them to the
+ /// buffer.
+ ///
+ ///
+ /// Optional extra context information. Currently, this will always
+ /// have a value of .
+ ///
+ ///
+ /// A preallocated native buffer to receive the requested input bytes.
+ /// It must be at least bytes in size.
+ ///
+ ///
+ /// Upon entry, the number of bytes to read. Upon exit, the number of
+ /// bytes actually read. This value may be zero upon exit.
+ ///
+ ///
+ /// The value upon success -OR- an
+ /// appropriate error code upon failure.
+ ///
+ private SQLiteErrorCode Input(
+ IntPtr context,
+ IntPtr pData,
+ ref int nData
+ )
+ {
+ try
+ {
+ Stream localStream = stream;
+
+ if (localStream == null)
+ return SQLiteErrorCode.Misuse;
+
+ if (nData > 0)
+ {
+ byte[] bytes = new byte[nData];
+ int nRead = localStream.Read(bytes, 0, nData);
+
+ if ((nRead > 0) && (pData != IntPtr.Zero))
+ Marshal.Copy(bytes, 0, pData, nRead);
+
+ nData = nRead;
+ }
+
+ return SQLiteErrorCode.Ok;
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(GetFlags()))
+ {
+ SQLiteLog.LogMessage(
+ SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "xSessionInput", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ return SQLiteErrorCode.IoErr_Read;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method attempts to write bytes to
+ /// the managed stream, reading them from the
+ /// buffer.
+ ///
+ ///
+ /// Optional extra context information. Currently, this will always
+ /// have a value of .
+ ///
+ ///
+ /// A preallocated native buffer containing the requested output
+ /// bytes. It must be at least bytes in
+ /// size.
+ ///
+ ///
+ /// The number of bytes to write.
+ ///
+ ///
+ /// The value upon success -OR- an
+ /// appropriate error code upon failure.
+ ///
+ private SQLiteErrorCode Output(
+ IntPtr context,
+ IntPtr pData,
+ int nData
+ )
+ {
+ try
+ {
+ Stream localStream = stream;
+
+ if (localStream == null)
+ return SQLiteErrorCode.Misuse;
+
+ if (nData > 0)
+ {
+ byte[] bytes = new byte[nData];
+
+ if (pData != IntPtr.Zero)
+ Marshal.Copy(pData, bytes, 0, nData);
+
+ localStream.Write(bytes, 0, nData);
+ }
+
+ localStream.Flush();
+
+ return SQLiteErrorCode.Ok;
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(GetFlags()))
+ {
+ SQLiteLog.LogMessage(
+ SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "xSessionOutput", e)); /* throw */
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ return SQLiteErrorCode.IoErr_Write;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteStreamAdapter).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ private /* protected virtual */ void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (xInput != null)
+ xInput = null;
+
+ if (xOutput != null)
+ xOutput = null;
+
+ if (stream != null)
+ stream = null; /* NOT OWNED */
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteStreamAdapter()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteSessionStreamManager Class
+ ///
+ /// This class manages a collection of
+ /// instances. When used, it takes responsibility for creating, returning,
+ /// and disposing of its instances.
+ ///
+ internal sealed class SQLiteSessionStreamManager : IDisposable
+ {
+ #region Private Data
+ ///
+ /// The managed collection of
+ /// instances, keyed by their associated
+ /// instance.
+ ///
+ private Dictionary streamAdapters;
+
+ ///
+ /// The flags associated with the connection.
+ ///
+ private SQLiteConnectionFlags flags;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs a new instance of this class using the specified
+ /// connection flags.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ public SQLiteSessionStreamManager(
+ SQLiteConnectionFlags flags
+ )
+ {
+ this.flags = flags;
+
+ InitializeStreamAdapters();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Makes sure the collection of
+ /// is created.
+ ///
+ private void InitializeStreamAdapters()
+ {
+ if (streamAdapters != null)
+ return;
+
+ streamAdapters = new Dictionary();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Makes sure the collection of
+ /// is disposed.
+ ///
+ private void DisposeStreamAdapters()
+ {
+ if (streamAdapters == null)
+ return;
+
+ foreach (KeyValuePair pair
+ in streamAdapters)
+ {
+ SQLiteStreamAdapter streamAdapter = pair.Value;
+
+ if (streamAdapter == null)
+ continue;
+
+ streamAdapter.Dispose();
+ }
+
+ streamAdapters.Clear();
+ streamAdapters = null;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Attempts to return a instance
+ /// suitable for the specified .
+ ///
+ ///
+ /// The instance. If this value is null, a null
+ /// value will be returned.
+ ///
+ ///
+ /// A instance. Typically, these
+ /// are always freshly created; however, this method is designed to
+ /// return the existing instance
+ /// associated with the specified stream, should one exist.
+ ///
+ public SQLiteStreamAdapter GetAdapter(
+ Stream stream
+ )
+ {
+ CheckDisposed();
+
+ if (stream == null)
+ return null;
+
+ SQLiteStreamAdapter streamAdapter;
+
+ if (streamAdapters.TryGetValue(stream, out streamAdapter))
+ return streamAdapter;
+
+ streamAdapter = new SQLiteStreamAdapter(stream, flags);
+ streamAdapters.Add(stream, streamAdapter);
+
+ return streamAdapter;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteSessionStreamManager).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ private /* protected virtual */ void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ DisposeStreamAdapters();
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteSessionStreamManager()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteChangeGroup Class
+ ///
+ /// This class represents a group of change sets (or patch sets).
+ ///
+ internal sealed class SQLiteChangeGroup : ISQLiteChangeGroup
+ {
+ #region Private Data
+ ///
+ /// The instance associated
+ /// with this change group.
+ ///
+ private SQLiteSessionStreamManager streamManager;
+
+ ///
+ /// The flags associated with the connection.
+ ///
+ private SQLiteConnectionFlags flags;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The native handle for this change group. This will be deleted when
+ /// this instance is disposed or finalized.
+ ///
+ private IntPtr changeGroup;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs a new instance of this class using the specified
+ /// connection flags.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ public SQLiteChangeGroup(
+ SQLiteConnectionFlags flags
+ )
+ {
+ this.flags = flags;
+
+ InitializeHandle();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the native change group handle is invalid.
+ ///
+ private void CheckHandle()
+ {
+ if (changeGroup == IntPtr.Zero)
+ throw new InvalidOperationException("change group not open");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Makes sure the native change group handle is valid, creating it if
+ /// necessary.
+ ///
+ private void InitializeHandle()
+ {
+ if (changeGroup != IntPtr.Zero)
+ return;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_new(
+ ref changeGroup);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changegroup_new");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Makes sure the instance
+ /// is available, creating it if necessary.
+ ///
+ private void InitializeStreamManager()
+ {
+ if (streamManager != null)
+ return;
+
+ streamManager = new SQLiteSessionStreamManager(flags);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to return a instance
+ /// suitable for the specified .
+ ///
+ ///
+ /// The instance. If this value is null, a null
+ /// value will be returned.
+ ///
+ ///
+ /// A instance. Typically, these
+ /// are always freshly created; however, this method is designed to
+ /// return the existing instance
+ /// associated with the specified stream, should one exist.
+ ///
+ private SQLiteStreamAdapter GetStreamAdapter(
+ Stream stream
+ )
+ {
+ InitializeStreamManager();
+
+ return streamManager.GetAdapter(stream);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeGroup Members
+ ///
+ /// Attempts to add a change set (or patch set) to this change group
+ /// instance. The underlying data must be contained entirely within
+ /// the byte array.
+ ///
+ ///
+ /// The raw byte data for the specified change set (or patch set).
+ ///
+ public void AddChangeSet(
+ byte[] rawData
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ SQLiteSessionHelpers.CheckRawData(rawData);
+
+ IntPtr pData = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ pData = SQLiteBytes.ToIntPtr(rawData, ref nData);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_add(
+ changeGroup, nData, pData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changegroup_add");
+ }
+ finally
+ {
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to add a change set (or patch set) to this change group
+ /// instance. The underlying data will be read from the specified
+ /// .
+ ///
+ ///
+ /// The instance containing the raw change set
+ /// (or patch set) data to read.
+ ///
+ public void AddChangeSet(
+ Stream stream
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
+
+ if (streamAdapter == null)
+ {
+ throw new SQLiteException(
+ "could not get or create adapter for input stream");
+ }
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_add_strm(
+ changeGroup, streamAdapter.GetInputDelegate(), IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changegroup_add_strm");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create and return, via , the
+ /// combined set of changes represented by this change group instance.
+ ///
+ ///
+ /// Upon success, this will contain the raw byte data for all the
+ /// changes in this change group instance.
+ ///
+ public void CreateChangeSet(
+ ref byte[] rawData
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ IntPtr pData = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_output(
+ changeGroup, ref nData, ref pData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changegroup_output");
+
+ rawData = SQLiteBytes.FromIntPtr(pData, nData);
+ }
+ finally
+ {
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.FreeUntracked(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create and write, via , the
+ /// combined set of changes represented by this change group instance.
+ ///
+ ///
+ /// Upon success, the raw byte data for all the changes in this change
+ /// group instance will be written to this .
+ ///
+ public void CreateChangeSet(
+ Stream stream
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
+
+ if (streamAdapter == null)
+ {
+ throw new SQLiteException(
+ "could not get or create adapter for output stream");
+ }
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changegroup_output_strm(
+ changeGroup, streamAdapter.GetOutputDelegate(), IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changegroup_output_strm");
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteChangeGroup).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ private /* protected virtual */ void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (streamManager != null)
+ {
+ streamManager.Dispose();
+ streamManager = null;
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ if (changeGroup != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3changegroup_delete(
+ changeGroup);
+
+ changeGroup = IntPtr.Zero;
+ }
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteChangeGroup()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteSession Class
+ ///
+ /// This class represents the change tracking session associated with a
+ /// database.
+ ///
+ internal sealed class SQLiteSession : SQLiteConnectionLock, ISQLiteSession
+ {
+ #region Private Data
+ ///
+ /// The instance associated
+ /// with this session.
+ ///
+ private SQLiteSessionStreamManager streamManager;
+
+ ///
+ /// The name of the database (e.g. "main") for this session.
+ ///
+ private string databaseName;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The native handle for this session. This will be deleted when
+ /// this instance is disposed or finalized.
+ ///
+ private IntPtr session;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// The delegate used to provide table filtering to the native API.
+ /// It will be null -OR- point to the method.
+ ///
+ private UnsafeNativeMethods.xSessionFilter xFilter;
+
+ ///
+ /// The managed callback used to filter tables for this session. Set
+ /// via the method.
+ ///
+ private SessionTableFilterCallback tableFilterCallback;
+
+ ///
+ /// The optional application-defined context data that was passed to
+ /// the method. This value may be null.
+ ///
+ private object tableFilterClientData;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs a new instance of this class using the specified wrapped
+ /// native connection handle and associated flags.
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// session.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ ///
+ /// The name of the database (e.g. "main") for this session.
+ ///
+ public SQLiteSession(
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags flags,
+ string databaseName
+ )
+ : base(handle, flags, true)
+ {
+ this.databaseName = databaseName;
+
+ InitializeHandle();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the native session handle is invalid.
+ ///
+ private void CheckHandle()
+ {
+ if (session == IntPtr.Zero)
+ throw new InvalidOperationException("session is not open");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Makes sure the native session handle is valid, creating it if
+ /// necessary.
+ ///
+ private void InitializeHandle()
+ {
+ if (session != IntPtr.Zero)
+ return;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_create(
+ GetIntPtr(), SQLiteString.GetUtf8BytesFromString(databaseName),
+ ref session);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3session_create");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method sets up the internal table filtering associated state
+ /// of this instance.
+ ///
+ ///
+ /// The table filter callback -OR- null to clear any existing table
+ /// filter callback.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ ///
+ /// The native
+ /// delegate -OR- null to clear any existing table filter.
+ ///
+ private UnsafeNativeMethods.xSessionFilter ApplyTableFilter(
+ SessionTableFilterCallback callback, /* in: NULL OK */
+ object clientData /* in: NULL OK */
+ )
+ {
+ tableFilterCallback = callback;
+ tableFilterClientData = clientData;
+
+ if (callback == null)
+ {
+ if (xFilter != null)
+ xFilter = null;
+
+ return null;
+ }
+
+ if (xFilter == null)
+ xFilter = new UnsafeNativeMethods.xSessionFilter(Filter);
+
+ return xFilter;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Makes sure the instance
+ /// is available, creating it if necessary.
+ ///
+ private void InitializeStreamManager()
+ {
+ if (streamManager != null)
+ return;
+
+ streamManager = new SQLiteSessionStreamManager(GetFlags());
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to return a instance
+ /// suitable for the specified .
+ ///
+ ///
+ /// The instance. If this value is null, a null
+ /// value will be returned.
+ ///
+ ///
+ /// A instance. Typically, these
+ /// are always freshly created; however, this method is designed to
+ /// return the existing instance
+ /// associated with the specified stream, should one exist.
+ ///
+ private SQLiteStreamAdapter GetStreamAdapter(
+ Stream stream
+ )
+ {
+ InitializeStreamManager();
+
+ return streamManager.GetAdapter(stream);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Native Callback Methods
+ ///
+ /// This method is called when determining if a table needs to be
+ /// included in the tracked changes for the associated database.
+ ///
+ ///
+ /// Optional extra context information. Currently, this will always
+ /// have a value of .
+ ///
+ ///
+ /// The native pointer to the name of the table.
+ ///
+ ///
+ /// Non-zero if changes to the specified table should be considered;
+ /// otherwise, zero.
+ ///
+ private int Filter(
+ IntPtr context, /* NOT USED */
+ IntPtr pTblName
+ )
+ {
+ try
+ {
+ return tableFilterCallback(tableFilterClientData,
+ SQLiteString.StringFromUtf8IntPtr(pTblName)) ? 1 : 0;
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(GetFlags()))
+ {
+ SQLiteLog.LogMessage( /* throw */
+ SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "xSessionFilter", e));
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ return 0;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteSession Members
+ ///
+ /// Determines if this session is currently tracking changes to its
+ /// associated database.
+ ///
+ ///
+ /// Non-zero if changes to the associated database are being trakced;
+ /// otherwise, zero.
+ ///
+ public bool IsEnabled()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ return UnsafeNativeMethods.sqlite3session_enable(session, -1) != 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Enables tracking of changes to the associated database.
+ ///
+ public void SetToEnabled()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ UnsafeNativeMethods.sqlite3session_enable(session, 1);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disables tracking of changes to the associated database.
+ ///
+ public void SetToDisabled()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ UnsafeNativeMethods.sqlite3session_enable(session, 0);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if this session is currently set to mark changes as
+ /// indirect (i.e. as though they were made via a trigger or foreign
+ /// key action).
+ ///
+ ///
+ /// Non-zero if changes to the associated database are being marked as
+ /// indirect; otherwise, zero.
+ ///
+ public bool IsIndirect()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ return UnsafeNativeMethods.sqlite3session_indirect(session, -1) != 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the indirect flag for this session. Subsequent changes will
+ /// be marked as indirect until this flag is changed again.
+ ///
+ public void SetToIndirect()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ UnsafeNativeMethods.sqlite3session_indirect(session, 1);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Clears the indirect flag for this session. Subsequent changes will
+ /// be marked as direct until this flag is changed again.
+ ///
+ public void SetToDirect()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ UnsafeNativeMethods.sqlite3session_indirect(session, 0);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if there are any tracked changes currently within the
+ /// data for this session.
+ ///
+ ///
+ /// Non-zero if there are no changes within the data for this session;
+ /// otherwise, zero.
+ ///
+ public bool IsEmpty()
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ return UnsafeNativeMethods.sqlite3session_isempty(session) != 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Upon success, causes changes to the specified table(s) to start
+ /// being tracked. Any tables impacted by calls to this method will
+ /// not cause the callback
+ /// to be invoked.
+ ///
+ ///
+ /// The name of the table to be tracked -OR- null to track all
+ /// applicable tables within this database.
+ ///
+ public void AttachTable(
+ string name /* in: NULL OK */
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_attach(
+ session, SQLiteString.GetUtf8BytesFromString(name));
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3session_attach");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method is used to set the table filter for this instance.
+ ///
+ ///
+ /// The table filter callback -OR- null to clear any existing table
+ /// filter callback.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ public void SetTableFilter(
+ SessionTableFilterCallback callback, /* in: NULL OK */
+ object clientData /* in: NULL OK */
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ UnsafeNativeMethods.sqlite3session_table_filter(
+ session, ApplyTableFilter(callback, clientData), IntPtr.Zero);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create and return, via , the
+ /// set of changes represented by this session instance.
+ ///
+ ///
+ /// Upon success, this will contain the raw byte data for all the
+ /// changes in this session instance.
+ ///
+ public void CreateChangeSet(
+ ref byte[] rawData
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ IntPtr pData = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_changeset(
+ session, ref nData, ref pData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3session_changeset");
+
+ rawData = SQLiteBytes.FromIntPtr(pData, nData);
+ }
+ finally
+ {
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.FreeUntracked(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create and write, via , the
+ /// set of changes represented by this session instance.
+ ///
+ ///
+ /// Upon success, the raw byte data for all the changes in this session
+ /// instance will be written to this .
+ ///
+ public void CreateChangeSet(
+ Stream stream
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
+
+ if (streamAdapter == null)
+ {
+ throw new SQLiteException(
+ "could not get or create adapter for output stream");
+ }
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_changeset_strm(
+ session, streamAdapter.GetOutputDelegate(), IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3session_changeset_strm");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create and return, via , the
+ /// set of changes represented by this session instance as a patch set.
+ ///
+ ///
+ /// Upon success, this will contain the raw byte data for all the
+ /// changes in this session instance.
+ ///
+ public void CreatePatchSet(
+ ref byte[] rawData
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ IntPtr pData = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_patchset(
+ session, ref nData, ref pData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3session_patchset");
+
+ rawData = SQLiteBytes.FromIntPtr(pData, nData);
+ }
+ finally
+ {
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.FreeUntracked(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create and write, via , the
+ /// set of changes represented by this session instance as a patch set.
+ ///
+ ///
+ /// Upon success, the raw byte data for all the changes in this session
+ /// instance will be written to this .
+ ///
+ public void CreatePatchSet(
+ Stream stream
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ SQLiteStreamAdapter streamAdapter = GetStreamAdapter(stream);
+
+ if (streamAdapter == null)
+ {
+ throw new SQLiteException(
+ "could not get or create adapter for output stream");
+ }
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_patchset_strm(
+ session, streamAdapter.GetOutputDelegate(), IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3session_patchset_strm");
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method loads the differences between two tables [with the same
+ /// name, set of columns, and primary key definition] into this session
+ /// instance.
+ ///
+ ///
+ /// The name of the database containing the table with the original
+ /// data (i.e. it will need updating in order to be identical to the
+ /// one within the database associated with this session instance).
+ ///
+ ///
+ /// The name of the table.
+ ///
+ public void LoadDifferencesFromTable(
+ string fromDatabaseName,
+ string tableName
+ )
+ {
+ CheckDisposed();
+ CheckHandle();
+
+ if (fromDatabaseName == null)
+ throw new ArgumentNullException("fromDatabaseName");
+
+ if (tableName == null)
+ throw new ArgumentNullException("tableName");
+
+ IntPtr pError = IntPtr.Zero;
+
+ try
+ {
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3session_diff(
+ session, SQLiteString.GetUtf8BytesFromString(fromDatabaseName),
+ SQLiteString.GetUtf8BytesFromString(tableName), ref pError);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ string error = null;
+
+ if (pError != IntPtr.Zero)
+ {
+ error = SQLiteString.StringFromUtf8IntPtr(pError);
+
+ if (!String.IsNullOrEmpty(error))
+ {
+ error = HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, ": {0}", error);
+ }
+ }
+
+ throw new SQLiteException(rc, HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "{0}{1}",
+ "sqlite3session_diff", error));
+ }
+ }
+ finally
+ {
+ if (pError != IntPtr.Zero)
+ {
+ SQLiteMemory.FreeUntracked(pError);
+ pError = IntPtr.Zero;
+ }
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteSession).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (xFilter != null)
+ xFilter = null;
+
+ if (streamManager != null)
+ {
+ streamManager.Dispose();
+ streamManager = null;
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ if (session != IntPtr.Zero)
+ {
+ UnsafeNativeMethods.sqlite3session_delete(session);
+ session = IntPtr.Zero;
+ }
+
+ Unlock();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteChangeSetBase Class
+ ///
+ /// This class represents the abstract concept of a set of changes. It
+ /// acts as the base class for the
+ /// and classes. It derives from
+ /// the class, which is used to hold
+ /// the underlying native connection handle open until the instances of
+ /// this class are disposed or finalized. It also provides the ability
+ /// to construct wrapped native delegates of the
+ /// and
+ /// types.
+ ///
+ internal class SQLiteChangeSetBase : SQLiteConnectionLock
+ {
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified wrapped
+ /// native connection handle.
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// change set.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ internal SQLiteChangeSetBase(
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags flags
+ )
+ : base(handle, flags, true)
+ {
+ // do nothing.
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Creates and returns a concrete implementation of the
+ /// interface.
+ ///
+ ///
+ /// The native iterator handle to use.
+ ///
+ ///
+ /// An instance of the
+ /// interface, which can be used to fetch metadata associated with
+ /// the current item in this set of changes.
+ ///
+ private ISQLiteChangeSetMetadataItem CreateMetadataItem(
+ IntPtr iterator
+ )
+ {
+ return new SQLiteChangeSetMetadataItem(
+ SQLiteChangeSetIterator.Attach(iterator));
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Attempts to create a
+ /// native delegate
+ /// that invokes the specified
+ /// delegate.
+ ///
+ ///
+ /// The to invoke when the
+ /// native delegate
+ /// is called. If this value is null then null is returned.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ ///
+ /// The created
+ /// native delegate -OR- null if it cannot be created.
+ ///
+ protected UnsafeNativeMethods.xSessionFilter GetDelegate(
+ SessionTableFilterCallback tableFilterCallback,
+ object clientData
+ )
+ {
+ if (tableFilterCallback == null)
+ return null;
+
+ UnsafeNativeMethods.xSessionFilter xFilter;
+
+ xFilter = new UnsafeNativeMethods.xSessionFilter(
+ delegate(IntPtr context, IntPtr pTblName)
+ {
+ try
+ {
+ string name = SQLiteString.StringFromUtf8IntPtr(
+ pTblName);
+
+ return tableFilterCallback(clientData, name) ? 1 : 0;
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(GetFlags()))
+ {
+ SQLiteLog.LogMessage( /* throw */
+ SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "xSessionFilter", e));
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ return 0;
+ });
+
+ return xFilter;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to create a
+ /// native delegate
+ /// that invokes the specified
+ /// delegate.
+ ///
+ ///
+ /// The to invoke when the
+ /// native delegate
+ /// is called. If this value is null then null is returned.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ ///
+ /// The created
+ /// native delegate -OR- null if it cannot be created.
+ ///
+ protected UnsafeNativeMethods.xSessionConflict GetDelegate(
+ SessionConflictCallback conflictCallback,
+ object clientData
+ )
+ {
+ if (conflictCallback == null)
+ return null;
+
+ UnsafeNativeMethods.xSessionConflict xConflict;
+
+ xConflict = new UnsafeNativeMethods.xSessionConflict(
+ delegate(IntPtr context,
+ SQLiteChangeSetConflictType type,
+ IntPtr iterator)
+ {
+ try
+ {
+ ISQLiteChangeSetMetadataItem item = CreateMetadataItem(
+ iterator);
+
+ if (item == null)
+ {
+ throw new SQLiteException(
+ "could not create metadata item");
+ }
+
+ return conflictCallback(clientData, type, item);
+ }
+ catch (Exception e)
+ {
+ try
+ {
+ if (HelperMethods.LogCallbackExceptions(GetFlags()))
+ {
+ SQLiteLog.LogMessage( /* throw */
+ SQLiteBase.COR_E_EXCEPTION,
+ HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ UnsafeNativeMethods.ExceptionMessageFormat,
+ "xSessionConflict", e));
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+ }
+
+ return SQLiteChangeSetConflictResult.Abort;
+ });
+
+ return xConflict;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteChangeSetBase).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ Unlock();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteMemoryChangeSet Class
+ ///
+ /// This class represents a set of changes contained entirely in memory.
+ ///
+ internal sealed class SQLiteMemoryChangeSet :
+ SQLiteChangeSetBase, ISQLiteChangeSet
+ {
+ #region Private Data
+ ///
+ /// The raw byte data for this set of changes. Since this data must
+ /// be marshalled to a native memory buffer before being used, there
+ /// must be enough memory available to store at least two times the
+ /// amount of data contained within it.
+ ///
+ private byte[] rawData;
+
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ private SQLiteChangeSetStartFlags startFlags;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified raw byte
+ /// data and wrapped native connection handle.
+ ///
+ ///
+ /// The raw byte data for the specified change set (or patch set).
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// set of changes.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ internal SQLiteMemoryChangeSet(
+ byte[] rawData,
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags connectionFlags
+ )
+ : base(handle, connectionFlags)
+ {
+ this.rawData = rawData;
+ this.startFlags = SQLiteChangeSetStartFlags.None;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified raw byte
+ /// data and wrapped native connection handle.
+ ///
+ ///
+ /// The raw byte data for the specified change set (or patch set).
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// set of changes.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ internal SQLiteMemoryChangeSet(
+ byte[] rawData,
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags connectionFlags,
+ SQLiteChangeSetStartFlags startFlags
+ )
+ : base(handle, connectionFlags)
+ {
+ this.rawData = rawData;
+ this.startFlags = startFlags;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeSet Members
+ ///
+ /// This method "inverts" the set of changes within this instance.
+ /// Applying an inverted set of changes to a database reverses the
+ /// effects of applying the uninverted changes. Specifically:
+ /// ]]>]]>
+ /// Each DELETE change is changed to an INSERT, and
+ /// ]]>]]>
+ /// Each INSERT change is changed to a DELETE, and
+ /// ]]>]]>
+ /// For each UPDATE change, the old.* and new.* values are exchanged.
+ /// ]]>]]>
+ /// This method does not change the order in which changes appear
+ /// within the set of changes. It merely reverses the sense of each
+ /// individual change.
+ ///
+ ///
+ /// The new instance that represents
+ /// the resulting set of changes.
+ ///
+ public ISQLiteChangeSet Invert()
+ {
+ CheckDisposed();
+
+ SQLiteSessionHelpers.CheckRawData(rawData);
+
+ IntPtr pInData = IntPtr.Zero;
+ IntPtr pOutData = IntPtr.Zero;
+
+ try
+ {
+ int nInData = 0;
+
+ pInData = SQLiteBytes.ToIntPtr(rawData, ref nInData);
+
+ int nOutData = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_invert(
+ nInData, pInData, ref nOutData, ref pOutData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_invert");
+
+ byte[] newData = SQLiteBytes.FromIntPtr(pOutData, nOutData);
+
+ return new SQLiteMemoryChangeSet(
+ newData, GetHandle(), GetFlags());
+ }
+ finally
+ {
+ if (pOutData != IntPtr.Zero)
+ {
+ SQLiteMemory.FreeUntracked(pOutData);
+ pOutData = IntPtr.Zero;
+ }
+
+ if (pInData != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pInData);
+ pInData = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method combines the specified set of changes with the ones
+ /// contained in this instance.
+ ///
+ ///
+ /// The changes to be combined with those in this instance.
+ ///
+ ///
+ /// The new instance that represents
+ /// the resulting set of changes.
+ ///
+ public ISQLiteChangeSet CombineWith(
+ ISQLiteChangeSet changeSet
+ )
+ {
+ CheckDisposed();
+
+ SQLiteSessionHelpers.CheckRawData(rawData);
+
+ SQLiteMemoryChangeSet memoryChangeSet =
+ changeSet as SQLiteMemoryChangeSet;
+
+ if (memoryChangeSet == null)
+ {
+ throw new ArgumentException(
+ "not a memory based change set", "changeSet");
+ }
+
+ SQLiteSessionHelpers.CheckRawData(memoryChangeSet.rawData);
+
+ IntPtr pInData1 = IntPtr.Zero;
+ IntPtr pInData2 = IntPtr.Zero;
+ IntPtr pOutData = IntPtr.Zero;
+
+ try
+ {
+ int nInData1 = 0;
+
+ pInData1 = SQLiteBytes.ToIntPtr(rawData, ref nInData1);
+
+ int nInData2 = 0;
+
+ pInData2 = SQLiteBytes.ToIntPtr(
+ memoryChangeSet.rawData, ref nInData2);
+
+ int nOutData = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_concat(
+ nInData1, pInData1, nInData2, pInData2, ref nOutData,
+ ref pOutData);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_concat");
+
+ byte[] newData = SQLiteBytes.FromIntPtr(pOutData, nOutData);
+
+ return new SQLiteMemoryChangeSet(
+ newData, GetHandle(), GetFlags());
+ }
+ finally
+ {
+ if (pOutData != IntPtr.Zero)
+ {
+ SQLiteMemory.FreeUntracked(pOutData);
+ pOutData = IntPtr.Zero;
+ }
+
+ if (pInData2 != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pInData2);
+ pInData2 = IntPtr.Zero;
+ }
+
+ if (pInData1 != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pInData1);
+ pInData1 = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to apply the set of changes in this instance to the
+ /// associated database.
+ ///
+ ///
+ /// The delegate that will need
+ /// to handle any conflicting changes that may arise.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ public void Apply(
+ SessionConflictCallback conflictCallback,
+ object clientData
+ )
+ {
+ CheckDisposed();
+
+ Apply(conflictCallback, null, clientData);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to apply the set of changes in this instance to the
+ /// associated database.
+ ///
+ ///
+ /// The delegate that will need
+ /// to handle any conflicting changes that may arise.
+ ///
+ ///
+ /// The optional delegate
+ /// that can be used to filter the list of tables impacted by the set
+ /// of changes.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ public void Apply(
+ SessionConflictCallback conflictCallback,
+ SessionTableFilterCallback tableFilterCallback,
+ object clientData
+ )
+ {
+ CheckDisposed();
+
+ SQLiteSessionHelpers.CheckRawData(rawData);
+
+ if (conflictCallback == null)
+ throw new ArgumentNullException("conflictCallback");
+
+ UnsafeNativeMethods.xSessionFilter xFilter = GetDelegate(
+ tableFilterCallback, clientData);
+
+ UnsafeNativeMethods.xSessionConflict xConflict = GetDelegate(
+ conflictCallback, clientData);
+
+ IntPtr pData = IntPtr.Zero;
+
+ try
+ {
+ int nData = 0;
+
+ pData = SQLiteBytes.ToIntPtr(rawData, ref nData);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_apply(
+ GetIntPtr(), nData, pData, xFilter, xConflict, IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_apply");
+ }
+ finally
+ {
+ if (pData != IntPtr.Zero)
+ {
+ SQLiteMemory.Free(pData);
+ pData = IntPtr.Zero;
+ }
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerable Members
+ ///
+ /// Creates an capable of iterating over the
+ /// items within this set of changes.
+ ///
+ ///
+ /// The new
+ /// instance.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ if (startFlags != SQLiteChangeSetStartFlags.None)
+ {
+ return new SQLiteMemoryChangeSetEnumerator(
+ rawData, startFlags);
+ }
+ else
+ {
+ return new SQLiteMemoryChangeSetEnumerator(rawData);
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerable Members
+ ///
+ /// Creates an capable of iterating over the
+ /// items within this set of changes.
+ ///
+ ///
+ /// The new instance.
+ ///
+ IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteMemoryChangeSet).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (rawData != null)
+ rawData = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteStreamChangeSet Class
+ ///
+ /// This class represents a set of changes that are backed by a
+ /// instance.
+ ///
+ internal sealed class SQLiteStreamChangeSet :
+ SQLiteChangeSetBase, ISQLiteChangeSet
+ {
+ #region Private Data
+ ///
+ /// The instance that is managing
+ /// the underlying input used as the backing
+ /// store for the set of changes associated with this instance.
+ ///
+ private SQLiteStreamAdapter inputStreamAdapter;
+
+ ///
+ /// The instance that is managing
+ /// the underlying output used as the backing
+ /// store for the set of changes generated by the
+ /// or methods.
+ ///
+ private SQLiteStreamAdapter outputStreamAdapter;
+
+ ///
+ /// The instance used as the backing store for
+ /// the set of changes associated with this instance.
+ ///
+ private Stream inputStream;
+
+ ///
+ /// The instance used as the backing store for
+ /// the set of changes generated by the or
+ /// methods.
+ ///
+ private Stream outputStream;
+
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ private SQLiteChangeSetStartFlags startFlags;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Constructors
+ ///
+ /// Constructs an instance of this class using the specified streams
+ /// and wrapped native connection handle.
+ ///
+ ///
+ /// The where the raw byte data for the set of
+ /// changes may be read.
+ ///
+ ///
+ /// The where the raw byte data for resulting
+ /// sets of changes may be written.
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// set of changes.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ internal SQLiteStreamChangeSet(
+ Stream inputStream,
+ Stream outputStream,
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags connectionFlags
+ )
+ : base(handle, connectionFlags)
+ {
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified streams
+ /// and wrapped native connection handle.
+ ///
+ ///
+ /// The where the raw byte data for the set of
+ /// changes may be read.
+ ///
+ ///
+ /// The where the raw byte data for resulting
+ /// sets of changes may be written.
+ ///
+ ///
+ /// The wrapped native connection handle to be associated with this
+ /// set of changes.
+ ///
+ ///
+ /// The flags associated with the connection represented by the
+ /// value.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ internal SQLiteStreamChangeSet(
+ Stream inputStream,
+ Stream outputStream,
+ SQLiteConnectionHandle handle,
+ SQLiteConnectionFlags connectionFlags,
+ SQLiteChangeSetStartFlags startFlags
+ )
+ : base(handle, connectionFlags)
+ {
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ this.startFlags = startFlags;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the input stream or its associated stream
+ /// adapter are invalid.
+ ///
+ private void CheckInputStream()
+ {
+ if (inputStream == null)
+ {
+ throw new InvalidOperationException(
+ "input stream unavailable");
+ }
+
+ if (inputStreamAdapter == null)
+ {
+ inputStreamAdapter = new SQLiteStreamAdapter(
+ inputStream, GetFlags());
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Throws an exception if the output stream or its associated stream
+ /// adapter are invalid.
+ ///
+ private void CheckOutputStream()
+ {
+ if (outputStream == null)
+ {
+ throw new InvalidOperationException(
+ "output stream unavailable");
+ }
+
+ if (outputStreamAdapter == null)
+ {
+ outputStreamAdapter = new SQLiteStreamAdapter(
+ outputStream, GetFlags());
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeSet Members
+ ///
+ /// This method "inverts" the set of changes within this instance.
+ /// Applying an inverted set of changes to a database reverses the
+ /// effects of applying the uninverted changes. Specifically:
+ /// ]]>]]>
+ /// Each DELETE change is changed to an INSERT, and
+ /// ]]>]]>
+ /// Each INSERT change is changed to a DELETE, and
+ /// ]]>]]>
+ /// For each UPDATE change, the old.* and new.* values are exchanged.
+ /// ]]>]]>
+ /// This method does not change the order in which changes appear
+ /// within the set of changes. It merely reverses the sense of each
+ /// individual change.
+ ///
+ ///
+ /// Since the resulting set of changes is written to the output stream,
+ /// this method always returns null.
+ ///
+ public ISQLiteChangeSet Invert()
+ {
+ CheckDisposed();
+ CheckInputStream();
+ CheckOutputStream();
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_invert_strm(
+ inputStreamAdapter.GetInputDelegate(), IntPtr.Zero,
+ outputStreamAdapter.GetOutputDelegate(), IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_invert_strm");
+
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This method combines the specified set of changes with the ones
+ /// contained in this instance.
+ ///
+ ///
+ /// The changes to be combined with those in this instance.
+ ///
+ ///
+ /// Since the resulting set of changes is written to the output stream,
+ /// this method always returns null.
+ ///
+ public ISQLiteChangeSet CombineWith(
+ ISQLiteChangeSet changeSet
+ )
+ {
+ CheckDisposed();
+ CheckInputStream();
+ CheckOutputStream();
+
+ SQLiteStreamChangeSet streamChangeSet =
+ changeSet as SQLiteStreamChangeSet;
+
+ if (streamChangeSet == null)
+ {
+ throw new ArgumentException(
+ "not a stream based change set", "changeSet");
+ }
+
+ streamChangeSet.CheckInputStream();
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_concat_strm(
+ inputStreamAdapter.GetInputDelegate(), IntPtr.Zero,
+ streamChangeSet.inputStreamAdapter.GetInputDelegate(),
+ IntPtr.Zero, outputStreamAdapter.GetOutputDelegate(),
+ IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_concat_strm");
+
+ return null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to apply the set of changes in this instance to the
+ /// associated database.
+ ///
+ ///
+ /// The delegate that will need
+ /// to handle any conflicting changes that may arise.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ public void Apply(
+ SessionConflictCallback conflictCallback,
+ object clientData
+ )
+ {
+ CheckDisposed();
+
+ Apply(conflictCallback, null, clientData);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to apply the set of changes in this instance to the
+ /// associated database.
+ ///
+ ///
+ /// The delegate that will need
+ /// to handle any conflicting changes that may arise.
+ ///
+ ///
+ /// The optional delegate
+ /// that can be used to filter the list of tables impacted by the set
+ /// of changes.
+ ///
+ ///
+ /// The optional application-defined context data. This value may be
+ /// null.
+ ///
+ public void Apply(
+ SessionConflictCallback conflictCallback,
+ SessionTableFilterCallback tableFilterCallback,
+ object clientData
+ )
+ {
+ CheckDisposed();
+ CheckInputStream();
+
+ if (conflictCallback == null)
+ throw new ArgumentNullException("conflictCallback");
+
+ UnsafeNativeMethods.xSessionFilter xFilter = GetDelegate(
+ tableFilterCallback, clientData);
+
+ UnsafeNativeMethods.xSessionConflict xConflict = GetDelegate(
+ conflictCallback, clientData);
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_apply_strm(
+ GetIntPtr(), inputStreamAdapter.GetInputDelegate(), IntPtr.Zero,
+ xFilter, xConflict, IntPtr.Zero);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_apply_strm");
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerable Members
+ ///
+ /// Creates an capable of iterating over the
+ /// items within this set of changes.
+ ///
+ ///
+ /// The new
+ /// instance.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ if (startFlags != SQLiteChangeSetStartFlags.None)
+ {
+ return new SQLiteStreamChangeSetEnumerator(
+ inputStream, GetFlags(), startFlags);
+ }
+ else
+ {
+ return new SQLiteStreamChangeSetEnumerator(
+ inputStream, GetFlags());
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerable Members
+ ///
+ /// Creates an capable of iterating over the
+ /// items within this set of changes.
+ ///
+ ///
+ /// The new instance.
+ ///
+ IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteStreamChangeSet).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (outputStreamAdapter != null)
+ {
+ outputStreamAdapter.Dispose();
+ outputStreamAdapter = null;
+ }
+
+ if (inputStreamAdapter != null)
+ {
+ inputStreamAdapter.Dispose();
+ inputStreamAdapter = null;
+ }
+
+ if (outputStream != null)
+ outputStream = null; /* NOT OWNED */
+
+ if (inputStream != null)
+ inputStream = null; /* NOT OWNED */
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteChangeSetEnumerator Class
+ ///
+ /// This class represents an that is capable of
+ /// enumerating over a set of changes. It serves as the base class for the
+ /// and
+ /// classes. It manages and
+ /// owns an instance of the class.
+ ///
+ internal abstract class SQLiteChangeSetEnumerator :
+ IEnumerator
+ {
+ #region Private Data
+ ///
+ /// This managed change set iterator is managed and owned by this
+ /// class. It will be disposed when this class is disposed.
+ ///
+ private SQLiteChangeSetIterator iterator;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class using the specified managed
+ /// change set iterator.
+ ///
+ ///
+ /// The managed iterator instance to use.
+ ///
+ public SQLiteChangeSetEnumerator(
+ SQLiteChangeSetIterator iterator
+ )
+ {
+ SetIterator(iterator);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the managed iterator instance is invalid.
+ ///
+ private void CheckIterator()
+ {
+ if (iterator == null)
+ throw new InvalidOperationException("iterator unavailable");
+
+ iterator.CheckHandle();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Sets the managed iterator instance to a new value.
+ ///
+ ///
+ /// The new managed iterator instance to use.
+ ///
+ private void SetIterator(
+ SQLiteChangeSetIterator iterator
+ )
+ {
+ this.iterator = iterator;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes of the managed iterator instance and sets its value to
+ /// null.
+ ///
+ private void CloseIterator()
+ {
+ if (iterator != null)
+ {
+ iterator.Dispose();
+ iterator = null;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Protected Methods
+ ///
+ /// Disposes of the existing managed iterator instance and then sets it
+ /// to a new value.
+ ///
+ ///
+ /// The new managed iterator instance to use.
+ ///
+ protected void ResetIterator(
+ SQLiteChangeSetIterator iterator
+ )
+ {
+ CloseIterator();
+ SetIterator(iterator);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerator Members
+ ///
+ /// Returns the current change within the set of changes, represented
+ /// by a instance.
+ ///
+ public ISQLiteChangeSetMetadataItem Current
+ {
+ get
+ {
+ CheckDisposed();
+
+ return new SQLiteChangeSetMetadataItem(iterator);
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerator Members
+ ///
+ /// Returns the current change within the set of changes, represented
+ /// by a instance.
+ ///
+ object Collections.IEnumerator.Current
+ {
+ get
+ {
+ CheckDisposed();
+
+ return Current;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to advance to the next item in the set of changes.
+ ///
+ ///
+ /// Non-zero if more items are available; otherwise, zero.
+ ///
+ public bool MoveNext()
+ {
+ CheckDisposed();
+ CheckIterator();
+
+ return iterator.Next();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Throws because not all the
+ /// derived classes are able to support reset functionality.
+ ///
+ public virtual void Reset()
+ {
+ CheckDisposed();
+
+ throw new NotImplementedException();
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteChangeSetEnumerator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ CloseIterator();
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteChangeSetEnumerator()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteMemoryChangeSetEnumerator Class
+ ///
+ /// This class represents an that is capable of
+ /// enumerating over a set of changes contained entirely in memory.
+ ///
+ internal sealed class SQLiteMemoryChangeSetEnumerator :
+ SQLiteChangeSetEnumerator
+ {
+ #region Private Data
+ ///
+ /// The raw byte data for this set of changes. Since this data must
+ /// be marshalled to a native memory buffer before being used, there
+ /// must be enough memory available to store at least two times the
+ /// amount of data contained within it.
+ ///
+ private byte[] rawData;
+
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ private SQLiteChangeSetStartFlags flags;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class using the specified raw byte
+ /// data.
+ ///
+ ///
+ /// The raw byte data containing the set of changes for this
+ /// enumerator.
+ ///
+ public SQLiteMemoryChangeSetEnumerator(
+ byte[] rawData
+ )
+ : base(SQLiteMemoryChangeSetIterator.Create(rawData))
+ {
+ this.rawData = rawData;
+ this.flags = SQLiteChangeSetStartFlags.None;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified raw byte
+ /// data.
+ ///
+ ///
+ /// The raw byte data containing the set of changes for this
+ /// enumerator.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ public SQLiteMemoryChangeSetEnumerator(
+ byte[] rawData,
+ SQLiteChangeSetStartFlags flags
+ )
+ : base(SQLiteMemoryChangeSetIterator.Create(rawData, flags))
+ {
+ this.rawData = rawData;
+ this.flags = flags;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IEnumerator Overrides
+ ///
+ /// Resets the enumerator to its initial position.
+ ///
+ public override void Reset()
+ {
+ CheckDisposed();
+
+ SQLiteMemoryChangeSetIterator result;
+
+ if (flags != SQLiteChangeSetStartFlags.None)
+ result = SQLiteMemoryChangeSetIterator.Create(rawData, flags);
+ else
+ result = SQLiteMemoryChangeSetIterator.Create(rawData);
+
+ ResetIterator(result);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteMemoryChangeSetEnumerator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteStreamChangeSetEnumerator Class
+ ///
+ /// This class represents an that is capable of
+ /// enumerating over a set of changes backed by a
+ /// instance.
+ ///
+ internal sealed class SQLiteStreamChangeSetEnumerator :
+ SQLiteChangeSetEnumerator
+ {
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class using the specified stream.
+ ///
+ ///
+ /// The where the raw byte data for the set of
+ /// changes may be read.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ public SQLiteStreamChangeSetEnumerator(
+ Stream stream,
+ SQLiteConnectionFlags connectionFlags
+ )
+ : base(SQLiteStreamChangeSetIterator.Create(
+ stream, connectionFlags))
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs an instance of this class using the specified stream.
+ ///
+ ///
+ /// The where the raw byte data for the set of
+ /// changes may be read.
+ ///
+ ///
+ /// The flags associated with the parent connection.
+ ///
+ ///
+ /// The flags used to create the change set iterator.
+ ///
+ public SQLiteStreamChangeSetEnumerator(
+ Stream stream,
+ SQLiteConnectionFlags connectionFlags,
+ SQLiteChangeSetStartFlags startFlags
+ )
+ : base(SQLiteStreamChangeSetIterator.Create(
+ stream, connectionFlags, startFlags))
+ {
+ // do nothing.
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteStreamChangeSetEnumerator).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ //if (!disposed)
+ //{
+ // if (disposing)
+ // {
+ // ////////////////////////////////////
+ // // dispose managed resources here...
+ // ////////////////////////////////////
+ // }
+
+ // //////////////////////////////////////
+ // // release unmanaged resources here...
+ // //////////////////////////////////////
+ //}
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteChangeSetMetadataItem Class
+ ///
+ /// This interface implements properties and methods used to fetch metadata
+ /// about one change within a set of changes for a database.
+ ///
+ internal sealed class SQLiteChangeSetMetadataItem :
+ ISQLiteChangeSetMetadataItem
+ {
+ #region Private Data
+ ///
+ /// The instance to use. This
+ /// will NOT be owned by this class and will not be disposed upon this
+ /// class being disposed or finalized.
+ ///
+ private SQLiteChangeSetIterator iterator;
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Public Constructors
+ ///
+ /// Constructs an instance of this class using the specified iterator
+ /// instance.
+ ///
+ ///
+ /// The managed iterator instance to use.
+ ///
+ public SQLiteChangeSetMetadataItem(
+ SQLiteChangeSetIterator iterator
+ )
+ {
+ this.iterator = iterator;
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Throws an exception if the managed iterator instance is invalid.
+ ///
+ private void CheckIterator()
+ {
+ if (iterator == null)
+ throw new InvalidOperationException("iterator unavailable");
+
+ iterator.CheckHandle();
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Populates the underlying data for the ,
+ /// , , and
+ /// properties, using the appropriate native
+ /// API.
+ ///
+ private void PopulateOperationMetadata()
+ {
+ if ((tableName == null) || (numberOfColumns == null) ||
+ (operationCode == null) || (indirect == null))
+ {
+ CheckIterator();
+
+ IntPtr pTblName = IntPtr.Zero;
+ SQLiteAuthorizerActionCode op = SQLiteAuthorizerActionCode.None;
+ int bIndirect = 0;
+ int nColumns = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_op(
+ iterator.GetIntPtr(), ref pTblName, ref nColumns, ref op,
+ ref bIndirect);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_op");
+
+ tableName = SQLiteString.StringFromUtf8IntPtr(pTblName);
+ numberOfColumns = nColumns;
+ operationCode = op;
+ indirect = (bIndirect != 0);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Populates the underlying data for the
+ /// property using the appropriate
+ /// native API.
+ ///
+ private void PopulatePrimaryKeyColumns()
+ {
+ if (primaryKeyColumns == null)
+ {
+ CheckIterator();
+
+ IntPtr pPrimaryKeys = IntPtr.Zero;
+ int nColumns = 0;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_pk(
+ iterator.GetIntPtr(), ref pPrimaryKeys, ref nColumns);
+
+ if (rc != SQLiteErrorCode.Ok)
+ throw new SQLiteException(rc, "sqlite3changeset_pk");
+
+ byte[] bytes = SQLiteBytes.FromIntPtr(pPrimaryKeys, nColumns);
+
+ if (bytes != null)
+ {
+ primaryKeyColumns = new bool[nColumns];
+
+ for (int index = 0; index < bytes.Length; index++)
+ primaryKeyColumns[index] = (bytes[index] != 0);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Populates the underlying data for the
+ /// property using the
+ /// appropriate native API.
+ ///
+ private void PopulateNumberOfForeignKeyConflicts()
+ {
+ if (numberOfForeignKeyConflicts == null)
+ {
+ CheckIterator();
+
+ int conflicts = 0;
+
+ SQLiteErrorCode rc =
+ UnsafeNativeMethods.sqlite3changeset_fk_conflicts(
+ iterator.GetIntPtr(), ref conflicts);
+
+ if (rc != SQLiteErrorCode.Ok)
+ {
+ throw new SQLiteException(rc,
+ "sqlite3changeset_fk_conflicts");
+ }
+
+ numberOfForeignKeyConflicts = conflicts;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region ISQLiteChangeSetMetadataItem Members
+ ///
+ /// Backing field for the property. This value
+ /// will be null if this field has not yet been populated via the
+ /// underlying native API.
+ ///
+ private string tableName;
+
+ ///
+ /// The name of the table the change was made to.
+ ///
+ public string TableName
+ {
+ get
+ {
+ CheckDisposed();
+ PopulateOperationMetadata();
+
+ return tableName;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Backing field for the property. This
+ /// value will be null if this field has not yet been populated via the
+ /// underlying native API.
+ ///
+ private int? numberOfColumns;
+
+ ///
+ /// The number of columns impacted by this change. This value can be
+ /// used to determine the highest valid column index that may be used
+ /// with the , ,
+ /// and methods of this interface. It
+ /// will be this value minus one.
+ ///
+ public int NumberOfColumns
+ {
+ get
+ {
+ CheckDisposed();
+ PopulateOperationMetadata();
+
+ return (int)numberOfColumns;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Backing field for the property. This
+ /// value will be null if this field has not yet been populated via the
+ /// underlying native API.
+ ///
+ private SQLiteAuthorizerActionCode? operationCode;
+
+ ///
+ /// This will contain the value
+ /// ,
+ /// , or
+ /// , corresponding to
+ /// the overall type of change this item represents.
+ ///
+ public SQLiteAuthorizerActionCode OperationCode
+ {
+ get
+ {
+ CheckDisposed();
+ PopulateOperationMetadata();
+
+ return (SQLiteAuthorizerActionCode)operationCode;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Backing field for the property. This value
+ /// will be null if this field has not yet been populated via the
+ /// underlying native API.
+ ///
+ private bool? indirect;
+
+ ///
+ /// Non-zero if this change is considered to be indirect (i.e. as
+ /// though they were made via a trigger or foreign key action).
+ ///
+ public bool Indirect
+ {
+ get
+ {
+ CheckDisposed();
+ PopulateOperationMetadata();
+
+ return (bool)indirect;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Backing field for the property.
+ /// This value will be null if this field has not yet been populated
+ /// via the underlying native API.
+ ///
+ private bool[] primaryKeyColumns;
+
+ ///
+ /// This array contains a for each column in
+ /// the table associated with this change. The element will be zero
+ /// if the column is not part of the primary key; otherwise, it will
+ /// be non-zero.
+ ///
+ public bool[] PrimaryKeyColumns
+ {
+ get
+ {
+ CheckDisposed();
+ PopulatePrimaryKeyColumns();
+
+ return primaryKeyColumns;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Backing field for the
+ /// property. This value will be null if this field has not yet been
+ /// populated via the underlying native API.
+ ///
+ private int? numberOfForeignKeyConflicts;
+
+ ///
+ /// This method may only be called from within a
+ /// delegate when the conflict
+ /// type is . It
+ /// returns the total number of known foreign key violations in the
+ /// destination database.
+ ///
+ public int NumberOfForeignKeyConflicts
+ {
+ get
+ {
+ CheckDisposed();
+ PopulateNumberOfForeignKeyConflicts();
+
+ return (int)numberOfForeignKeyConflicts;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the original value of a given column for this
+ /// change. This method may only be called when the
+ /// has a value of
+ /// or
+ /// .
+ ///
+ ///
+ /// The index for the column. This value must be between zero and one
+ /// less than the total number of columns for this table.
+ ///
+ ///
+ /// The original value of a given column for this change.
+ ///
+ public SQLiteValue GetOldValue(
+ int columnIndex
+ )
+ {
+ CheckDisposed();
+ CheckIterator();
+
+ IntPtr pValue = IntPtr.Zero;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_old(
+ iterator.GetIntPtr(), columnIndex, ref pValue);
+
+ return SQLiteValue.FromIntPtr(pValue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the updated value of a given column for this
+ /// change. This method may only be called when the
+ /// has a value of
+ /// or
+ /// .
+ ///
+ ///
+ /// The index for the column. This value must be between zero and one
+ /// less than the total number of columns for this table.
+ ///
+ ///
+ /// The updated value of a given column for this change.
+ ///
+ public SQLiteValue GetNewValue(
+ int columnIndex
+ )
+ {
+ CheckDisposed();
+ CheckIterator();
+
+ IntPtr pValue = IntPtr.Zero;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_new(
+ iterator.GetIntPtr(), columnIndex, ref pValue);
+
+ return SQLiteValue.FromIntPtr(pValue);
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the conflicting value of a given column for
+ /// this change. This method may only be called from within a
+ /// delegate when the conflict
+ /// type is or
+ /// .
+ ///
+ ///
+ /// The index for the column. This value must be between zero and one
+ /// less than the total number of columns for this table.
+ ///
+ ///
+ /// The conflicting value of a given column for this change.
+ ///
+ public SQLiteValue GetConflictValue(
+ int columnIndex
+ )
+ {
+ CheckDisposed();
+ CheckIterator();
+
+ IntPtr pValue = IntPtr.Zero;
+
+ SQLiteErrorCode rc = UnsafeNativeMethods.sqlite3changeset_conflict(
+ iterator.GetIntPtr(), columnIndex, ref pValue);
+
+ return SQLiteValue.FromIntPtr(pValue);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes of this object instance.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ ///
+ /// Non-zero if this object instance has been disposed.
+ ///
+ private bool disposed;
+
+ ///
+ /// Throws an exception if this object instance has been disposed.
+ ///
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteChangeSetMetadataItem).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes or finalizes this object instance.
+ ///
+ ///
+ /// Non-zero if this object is being disposed; otherwise, this object
+ /// is being finalized.
+ ///
+ private /* protected virtual */ void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (iterator != null)
+ iterator = null; /* NOT OWNED */
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ///
+ /// Finalizes this object instance.
+ ///
+ ~SQLiteChangeSetMetadataItem()
+ {
+ Dispose(false);
+ }
+ #endregion
+ }
+ #endregion
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteStatement.cs b/Native.Csharp.Tool/SQLite/SQLiteStatement.cs
new file mode 100644
index 00000000..9c8f36a0
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteStatement.cs
@@ -0,0 +1,558 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Globalization;
+
+ ///
+ /// Represents a single SQL statement in SQLite.
+ ///
+ internal sealed class SQLiteStatement : IDisposable
+ {
+ ///
+ /// The underlying SQLite object this statement is bound to
+ ///
+ internal SQLiteBase _sql;
+ ///
+ /// The command text of this SQL statement
+ ///
+ internal string _sqlStatement;
+ ///
+ /// The actual statement pointer
+ ///
+ internal SQLiteStatementHandle _sqlite_stmt;
+ ///
+ /// An index from which unnamed parameters begin
+ ///
+ internal int _unnamedParameters;
+ ///
+ /// Names of the parameters as SQLite understands them to be
+ ///
+ internal string[] _paramNames;
+ ///
+ /// Parameters for this statement
+ ///
+ internal SQLiteParameter[] _paramValues;
+ ///
+ /// Command this statement belongs to (if any)
+ ///
+ internal SQLiteCommand _command;
+
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ private SQLiteConnectionFlags _flags;
+
+ private string[] _types;
+
+ ///
+ /// Initializes the statement and attempts to get all information about parameters in the statement
+ ///
+ /// The base SQLite object
+ /// The flags associated with the parent connection object
+ /// The statement
+ /// The command text for this statement
+ /// The previous command in a multi-statement command
+ internal SQLiteStatement(SQLiteBase sqlbase, SQLiteConnectionFlags flags, SQLiteStatementHandle stmt, string strCommand, SQLiteStatement previous)
+ {
+ _sql = sqlbase;
+ _sqlite_stmt = stmt;
+ _sqlStatement = strCommand;
+ _flags = flags;
+
+ // Determine parameters for this statement (if any) and prepare space for them.
+ int nCmdStart = 0;
+ int n = _sql.Bind_ParamCount(this, _flags);
+ int x;
+ string s;
+
+ if (n > 0)
+ {
+ if (previous != null)
+ nCmdStart = previous._unnamedParameters;
+
+ _paramNames = new string[n];
+ _paramValues = new SQLiteParameter[n];
+
+ for (x = 0; x < n; x++)
+ {
+ s = _sql.Bind_ParamName(this, _flags, x + 1);
+ if (String.IsNullOrEmpty(s))
+ {
+ s = HelperMethods.StringFormat(CultureInfo.InvariantCulture, ";{0}", nCmdStart);
+ nCmdStart++;
+ _unnamedParameters++;
+ }
+ _paramNames[x] = s;
+ _paramValues[x] = null;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable Members
+ ///
+ /// Disposes and finalizes the statement
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteStatement).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ private void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (_sqlite_stmt != null)
+ {
+ _sqlite_stmt.Dispose();
+ _sqlite_stmt = null;
+ }
+
+ _paramNames = null;
+ _paramValues = null;
+ _sql = null;
+ _sqlStatement = null;
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region Destructor
+ ~SQLiteStatement()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// If the underlying database connection is open, fetches the number of changed rows
+ /// resulting from the most recent query; otherwise, does nothing.
+ ///
+ ///
+ /// The number of changes when true is returned.
+ /// Undefined if false is returned.
+ ///
+ ///
+ /// The read-only flag when true is returned.
+ /// Undefined if false is returned.
+ ///
+ /// Non-zero if the number of changed rows was fetched.
+ internal bool TryGetChanges(
+ ref int changes,
+ ref bool readOnly
+ )
+ {
+ if ((_sql != null) && _sql.IsOpen())
+ {
+ changes = _sql.Changes;
+ readOnly = _sql.IsReadOnly(this);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Called by SQLiteParameterCollection, this function determines if the specified parameter name belongs to
+ /// this statement, and if so, keeps a reference to the parameter so it can be bound later.
+ ///
+ /// The parameter name to map
+ /// The parameter to assign it
+ internal bool MapParameter(string s, SQLiteParameter p)
+ {
+ if (_paramNames == null) return false;
+
+ int startAt = 0;
+ if (s.Length > 0)
+ {
+ if (":$@;".IndexOf(s[0]) == -1)
+ startAt = 1;
+ }
+
+ int x = _paramNames.Length;
+ for (int n = 0; n < x; n++)
+ {
+ if (String.Compare(_paramNames[n], startAt, s, 0, Math.Max(_paramNames[n].Length - startAt, s.Length), StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ _paramValues[n] = p;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Bind all parameters, making sure the caller didn't miss any
+ ///
+ internal void BindParameters()
+ {
+ if (_paramNames == null) return;
+
+ int x = _paramNames.Length;
+ for (int n = 0; n < x; n++)
+ {
+ BindParameter(n + 1, _paramValues[n]);
+ }
+ }
+
+ ///
+ /// This method attempts to query the database connection associated with
+ /// the statement in use. If the underlying command or connection is
+ /// unavailable, a null value will be returned.
+ ///
+ ///
+ /// The connection object -OR- null if it is unavailable.
+ ///
+ private static SQLiteConnection GetConnection(
+ SQLiteStatement statement
+ )
+ {
+ try
+ {
+ if (statement != null)
+ {
+ SQLiteCommand command = statement._command;
+
+ if (command != null)
+ {
+ SQLiteConnection connection = command.Connection;
+
+ if (connection != null)
+ return connection;
+ }
+ }
+ }
+ catch (ObjectDisposedException)
+ {
+ // do nothing.
+ }
+
+ return null;
+ }
+
+ ///
+ /// Invokes the parameter binding callback configured for the database
+ /// type name associated with the specified column. If no parameter
+ /// binding callback is available for the database type name, do
+ /// nothing.
+ ///
+ ///
+ /// The index of the column being read.
+ ///
+ ///
+ /// The instance being bound to the
+ /// command.
+ ///
+ ///
+ /// Non-zero if the default handling for the parameter binding call
+ /// should be skipped (i.e. the parameter should not be bound at all).
+ /// Great care should be used when setting this to non-zero.
+ ///
+ private void InvokeBindValueCallback(
+ int index,
+ SQLiteParameter parameter,
+ out bool complete
+ )
+ {
+ complete = false;
+ SQLiteConnectionFlags oldFlags = _flags;
+ _flags &= ~SQLiteConnectionFlags.UseConnectionBindValueCallbacks;
+
+ try
+ {
+ if (parameter == null)
+ return;
+
+ SQLiteConnection connection = GetConnection(this);
+
+ if (connection == null)
+ return;
+
+ //
+ // NOTE: First, always look for an explicitly set database type
+ // name.
+ //
+ string typeName = parameter.TypeName;
+
+ if (typeName == null)
+ {
+ //
+ // NOTE: Are we allowed to fallback to using the parameter name
+ // as the basis for looking up the binding callback?
+ //
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.UseParameterNameForTypeName))
+ {
+ typeName = parameter.ParameterName;
+ }
+ }
+
+ if (typeName == null)
+ {
+ //
+ // NOTE: Are we allowed to fallback to using the database type
+ // name translated from the DbType as the basis for looking
+ // up the binding callback?
+ //
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.UseParameterDbTypeForTypeName))
+ {
+ typeName = SQLiteConvert.DbTypeToTypeName(
+ connection, parameter.DbType, _flags);
+ }
+ }
+
+ if (typeName == null)
+ return;
+
+ SQLiteTypeCallbacks callbacks;
+
+ if (!connection.TryGetTypeCallbacks(typeName, out callbacks) ||
+ (callbacks == null))
+ {
+ return;
+ }
+
+ SQLiteBindValueCallback callback = callbacks.BindValueCallback;
+
+ if (callback == null)
+ return;
+
+ object userData = callbacks.BindValueUserData;
+
+ callback(
+ _sql, _command, oldFlags, parameter, typeName, index,
+ userData, out complete); /* throw */
+ }
+ finally
+ {
+ _flags |= SQLiteConnectionFlags.UseConnectionBindValueCallbacks;
+ }
+ }
+
+ ///
+ /// Perform the bind operation for an individual parameter
+ ///
+ /// The index of the parameter to bind
+ /// The parameter we're binding
+ private void BindParameter(int index, SQLiteParameter param)
+ {
+ if (param == null)
+ throw new SQLiteException("Insufficient parameters supplied to the command");
+
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.UseConnectionBindValueCallbacks))
+ {
+ bool complete;
+
+ InvokeBindValueCallback(index, param, out complete);
+
+ if (complete)
+ return;
+ }
+
+ object obj = param.Value;
+ DbType objType = param.DbType;
+
+ if ((obj != null) && (objType == DbType.Object))
+ objType = SQLiteConvert.TypeToDbType(obj.GetType());
+
+ if (SQLite3.ForceLogPrepare() || HelperMethods.LogPreBind(_flags))
+ {
+ IntPtr handle = _sqlite_stmt;
+
+ SQLiteLog.LogMessage(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Binding statement {0} paramter #{1} with database type {2} and raw value {{{3}}}...",
+ handle, index, objType, obj));
+ }
+
+ if ((obj == null) || Convert.IsDBNull(obj))
+ {
+ _sql.Bind_Null(this, _flags, index);
+ return;
+ }
+
+ CultureInfo invariantCultureInfo = CultureInfo.InvariantCulture;
+
+ bool invariantText = HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.BindInvariantText);
+
+ CultureInfo cultureInfo = CultureInfo.CurrentCulture;
+
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.ConvertInvariantText))
+ {
+ cultureInfo = invariantCultureInfo;
+ }
+
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.BindAllAsText))
+ {
+ if (obj is DateTime)
+ {
+ _sql.Bind_DateTime(this, _flags, index, (DateTime)obj);
+ }
+ else
+ {
+ _sql.Bind_Text(this, _flags, index, invariantText ?
+ SQLiteConvert.ToStringWithProvider(obj, invariantCultureInfo) :
+ SQLiteConvert.ToStringWithProvider(obj, cultureInfo));
+ }
+
+ return;
+ }
+
+ bool invariantDecimal = HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.BindInvariantDecimal);
+
+ if (HelperMethods.HasFlags(
+ _flags, SQLiteConnectionFlags.BindDecimalAsText))
+ {
+ if (obj is Decimal)
+ {
+ _sql.Bind_Text(this, _flags, index, invariantText || invariantDecimal ?
+ SQLiteConvert.ToStringWithProvider(obj, invariantCultureInfo) :
+ SQLiteConvert.ToStringWithProvider(obj, cultureInfo));
+
+ return;
+ }
+ }
+
+ switch (objType)
+ {
+ case DbType.Date:
+ case DbType.Time:
+ case DbType.DateTime:
+ //
+ // NOTE: The old method (commented below) does not honor the selected date format
+ // for the connection.
+ // _sql.Bind_DateTime(this, index, Convert.ToDateTime(obj, cultureInfo));
+ _sql.Bind_DateTime(this, _flags, index, (obj is string) ?
+ _sql.ToDateTime((string)obj) : Convert.ToDateTime(obj, cultureInfo));
+ break;
+ case DbType.Boolean:
+ _sql.Bind_Boolean(this, _flags, index, SQLiteConvert.ToBoolean(obj, cultureInfo, true));
+ break;
+ case DbType.SByte:
+ _sql.Bind_Int32(this, _flags, index, Convert.ToSByte(obj, cultureInfo));
+ break;
+ case DbType.Int16:
+ _sql.Bind_Int32(this, _flags, index, Convert.ToInt16(obj, cultureInfo));
+ break;
+ case DbType.Int32:
+ _sql.Bind_Int32(this, _flags, index, Convert.ToInt32(obj, cultureInfo));
+ break;
+ case DbType.Int64:
+ _sql.Bind_Int64(this, _flags, index, Convert.ToInt64(obj, cultureInfo));
+ break;
+ case DbType.Byte:
+ _sql.Bind_UInt32(this, _flags, index, Convert.ToByte(obj, cultureInfo));
+ break;
+ case DbType.UInt16:
+ _sql.Bind_UInt32(this, _flags, index, Convert.ToUInt16(obj, cultureInfo));
+ break;
+ case DbType.UInt32:
+ _sql.Bind_UInt32(this, _flags, index, Convert.ToUInt32(obj, cultureInfo));
+ break;
+ case DbType.UInt64:
+ _sql.Bind_UInt64(this, _flags, index, Convert.ToUInt64(obj, cultureInfo));
+ break;
+ case DbType.Single:
+ case DbType.Double:
+ case DbType.Currency:
+ _sql.Bind_Double(this, _flags, index, Convert.ToDouble(obj, cultureInfo));
+ break;
+ case DbType.Binary:
+ _sql.Bind_Blob(this, _flags, index, (byte[])obj);
+ break;
+ case DbType.Guid:
+ if (_command.Connection._binaryGuid == true)
+ {
+ _sql.Bind_Blob(this, _flags, index, ((Guid)obj).ToByteArray());
+ }
+ else
+ {
+ _sql.Bind_Text(this, _flags, index, invariantText ?
+ SQLiteConvert.ToStringWithProvider(obj, invariantCultureInfo) :
+ SQLiteConvert.ToStringWithProvider(obj, cultureInfo));
+ }
+ break;
+ case DbType.Decimal: // Dont store decimal as double ... loses precision
+ _sql.Bind_Text(this, _flags, index, invariantText || invariantDecimal ?
+ SQLiteConvert.ToStringWithProvider(Convert.ToDecimal(obj, cultureInfo), invariantCultureInfo) :
+ SQLiteConvert.ToStringWithProvider(Convert.ToDecimal(obj, cultureInfo), cultureInfo));
+ break;
+ default:
+ _sql.Bind_Text(this, _flags, index, invariantText ?
+ SQLiteConvert.ToStringWithProvider(obj, invariantCultureInfo) :
+ SQLiteConvert.ToStringWithProvider(obj, cultureInfo));
+ break;
+ }
+ }
+
+ internal string[] TypeDefinitions
+ {
+ get { return _types; }
+ }
+
+ internal void SetTypes(string typedefs)
+ {
+ int pos = typedefs.IndexOf("TYPES", 0, StringComparison.OrdinalIgnoreCase);
+ if (pos == -1) throw new ArgumentOutOfRangeException();
+
+ string[] types = typedefs.Substring(pos + 6).Replace(" ", String.Empty).Replace(";", String.Empty).Replace("\"", String.Empty).Replace("[", String.Empty).Replace("]", String.Empty).Replace("`", String.Empty).Split(',', '\r', '\n', '\t');
+
+ int n;
+ for (n = 0; n < types.Length; n++)
+ {
+ if (String.IsNullOrEmpty(types[n]) == true)
+ types[n] = null;
+ }
+ _types = types;
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteTransaction.cs b/Native.Csharp.Tool/SQLite/SQLiteTransaction.cs
new file mode 100644
index 00000000..4549c87c
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteTransaction.cs
@@ -0,0 +1,175 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Threading;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// SQLite implementation of DbTransaction that does not support nested transactions.
+ ///
+ public class SQLiteTransaction : SQLiteTransactionBase
+ {
+ ///
+ /// Constructs the transaction object, binding it to the supplied connection
+ ///
+ /// The connection to open a transaction on
+ /// TRUE to defer the writelock, or FALSE to lock immediately
+ internal SQLiteTransaction(SQLiteConnection connection, bool deferredLock)
+ : base(connection, deferredLock)
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteTransaction).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes the transaction. If it is currently active, any changes are rolled back.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (IsValid(false))
+ {
+ IssueRollback(false);
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Commits the current transaction.
+ ///
+ public override void Commit()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ IsValid(true);
+
+ if (_cnn._transactionLevel - 1 == 0)
+ {
+ using (SQLiteCommand cmd = _cnn.CreateCommand())
+ {
+ cmd.CommandText = "COMMIT;";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ _cnn._transactionLevel--;
+ _cnn = null;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to start a transaction. An exception will be thrown if the transaction cannot
+ /// be started for any reason.
+ ///
+ /// TRUE to defer the writelock, or FALSE to lock immediately
+ protected override void Begin(
+ bool deferredLock
+ )
+ {
+ if (_cnn._transactionLevel++ == 0)
+ {
+ try
+ {
+ using (SQLiteCommand cmd = _cnn.CreateCommand())
+ {
+ if (!deferredLock)
+ cmd.CommandText = "BEGIN IMMEDIATE;";
+ else
+ cmd.CommandText = "BEGIN;";
+
+ cmd.ExecuteNonQuery();
+ }
+ }
+ catch (SQLiteException)
+ {
+ _cnn._transactionLevel--;
+ _cnn = null;
+
+ throw;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Issue a ROLLBACK command against the database connection,
+ /// optionally re-throwing any caught exception.
+ ///
+ ///
+ /// Non-zero to re-throw caught exceptions.
+ ///
+ protected override void IssueRollback(
+ bool throwError
+ )
+ {
+ SQLiteConnection cnn = Interlocked.Exchange(ref _cnn, null);
+
+ if (cnn != null)
+ {
+ try
+ {
+ using (SQLiteCommand cmd = cnn.CreateCommand())
+ {
+ cmd.CommandText = "ROLLBACK;";
+ cmd.ExecuteNonQuery();
+ }
+ }
+ catch
+ {
+ if (throwError)
+ throw;
+ }
+ cnn._transactionLevel = 0;
+ }
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteTransaction2.cs b/Native.Csharp.Tool/SQLite/SQLiteTransaction2.cs
new file mode 100644
index 00000000..044395bc
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteTransaction2.cs
@@ -0,0 +1,276 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Threading;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// SQLite implementation of DbTransaction that does support nested transactions.
+ ///
+ public sealed class SQLiteTransaction2 : SQLiteTransaction
+ {
+ ///
+ /// The original transaction level for the associated connection
+ /// when this transaction was created (i.e. begun).
+ ///
+ private int _beginLevel;
+
+ ///
+ /// The SAVEPOINT name for this transaction, if any. This will
+ /// only be non-null if this transaction is a nested one.
+ ///
+ private string _savePointName;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs the transaction object, binding it to the supplied connection
+ ///
+ /// The connection to open a transaction on
+ /// TRUE to defer the writelock, or FALSE to lock immediately
+ internal SQLiteTransaction2(SQLiteConnection connection, bool deferredLock)
+ : base(connection, deferredLock)
+ {
+ // do nothing.
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ throw new ObjectDisposedException(typeof(SQLiteTransaction2).Name);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes the transaction. If it is currently active, any changes are rolled back.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (IsValid(false))
+ {
+ IssueRollback(false);
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Commits the current transaction.
+ ///
+ public override void Commit()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ IsValid(true);
+
+ if (_beginLevel == 0)
+ {
+ using (SQLiteCommand cmd = _cnn.CreateCommand())
+ {
+ cmd.CommandText = "COMMIT;";
+ cmd.ExecuteNonQuery();
+ }
+
+ _cnn._transactionLevel = 0;
+ _cnn = null;
+ }
+ else
+ {
+ using (SQLiteCommand cmd = _cnn.CreateCommand())
+ {
+ if (String.IsNullOrEmpty(_savePointName))
+ throw new SQLiteException("Cannot commit, unknown SAVEPOINT");
+
+ cmd.CommandText = String.Format(
+ "RELEASE {0};", _savePointName);
+
+ cmd.ExecuteNonQuery();
+ }
+
+ _cnn._transactionLevel--;
+ _cnn = null;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to start a transaction. An exception will be thrown if the transaction cannot
+ /// be started for any reason.
+ ///
+ /// TRUE to defer the writelock, or FALSE to lock immediately
+ protected override void Begin(
+ bool deferredLock
+ )
+ {
+ int transactionLevel;
+
+ if ((transactionLevel = _cnn._transactionLevel++) == 0)
+ {
+ try
+ {
+ using (SQLiteCommand cmd = _cnn.CreateCommand())
+ {
+ if (!deferredLock)
+ cmd.CommandText = "BEGIN IMMEDIATE;";
+ else
+ cmd.CommandText = "BEGIN;";
+
+ cmd.ExecuteNonQuery();
+
+ _beginLevel = transactionLevel;
+ }
+ }
+ catch (SQLiteException)
+ {
+ _cnn._transactionLevel--;
+ _cnn = null;
+
+ throw;
+ }
+ }
+ else
+ {
+ try
+ {
+ using (SQLiteCommand cmd = _cnn.CreateCommand())
+ {
+ _savePointName = GetSavePointName();
+
+ cmd.CommandText = String.Format(
+ "SAVEPOINT {0};", _savePointName);
+
+ cmd.ExecuteNonQuery();
+
+ _beginLevel = transactionLevel;
+ }
+ }
+ catch (SQLiteException)
+ {
+ _cnn._transactionLevel--;
+ _cnn = null;
+
+ throw;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Issue a ROLLBACK command against the database connection,
+ /// optionally re-throwing any caught exception.
+ ///
+ ///
+ /// Non-zero to re-throw caught exceptions.
+ ///
+ protected override void IssueRollback(bool throwError)
+ {
+ SQLiteConnection cnn = Interlocked.Exchange(ref _cnn, null);
+
+ if (cnn != null)
+ {
+ if (_beginLevel == 0)
+ {
+ try
+ {
+ using (SQLiteCommand cmd = cnn.CreateCommand())
+ {
+ cmd.CommandText = "ROLLBACK;";
+ cmd.ExecuteNonQuery();
+ }
+
+ cnn._transactionLevel = 0;
+ }
+ catch
+ {
+ if (throwError)
+ throw;
+ }
+ }
+ else
+ {
+ try
+ {
+ using (SQLiteCommand cmd = cnn.CreateCommand())
+ {
+ if (String.IsNullOrEmpty(_savePointName))
+ throw new SQLiteException("Cannot rollback, unknown SAVEPOINT");
+
+ cmd.CommandText = String.Format(
+ "ROLLBACK TO {0};", _savePointName);
+
+ cmd.ExecuteNonQuery();
+ }
+
+ cnn._transactionLevel--;
+ }
+ catch
+ {
+ if (throwError)
+ throw;
+ }
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs the name of a new savepoint for this transaction. It
+ /// should only be called from the constructor of this class.
+ ///
+ ///
+ /// The name of the new savepoint -OR- null if it cannot be constructed.
+ ///
+ private string GetSavePointName()
+ {
+ int sequence = ++_cnn._transactionSequence;
+
+ return String.Format(
+ "sqlite_dotnet_savepoint_{0}", sequence);
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/SQLiteTransactionBase.cs b/Native.Csharp.Tool/SQLite/SQLiteTransactionBase.cs
new file mode 100644
index 00000000..724a843a
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/SQLiteTransactionBase.cs
@@ -0,0 +1,214 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Data;
+ using System.Data.Common;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Base class used by to implement DbTransaction for SQLite.
+ ///
+ public abstract class SQLiteTransactionBase : DbTransaction
+ {
+ ///
+ /// The connection to which this transaction is bound.
+ ///
+ internal SQLiteConnection _cnn;
+
+ ///
+ /// Matches the version of the connection.
+ ///
+ internal int _version;
+
+ ///
+ /// The isolation level for this transaction.
+ ///
+ private IsolationLevel _level;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Constructs the transaction object, binding it to the supplied connection
+ ///
+ /// The connection to open a transaction on
+ /// TRUE to defer the writelock, or FALSE to lock immediately
+ internal SQLiteTransactionBase(SQLiteConnection connection, bool deferredLock)
+ {
+ _cnn = connection;
+ _version = _cnn._version;
+
+ _level = (deferredLock == true) ?
+ SQLiteConnection.DeferredIsolationLevel :
+ SQLiteConnection.ImmediateIsolationLevel;
+
+ Begin(deferredLock);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Gets the isolation level of the transaction. SQLite only supports Serializable transactions.
+ ///
+ public override IsolationLevel IsolationLevel
+ {
+ get { CheckDisposed(); return _level; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ #region IDisposable "Pattern" Members
+ private bool disposed;
+ private void CheckDisposed() /* throw */
+ {
+#if THROW_ON_DISPOSED
+ if (disposed)
+ {
+ throw new ObjectDisposedException(
+ typeof(SQLiteTransactionBase).Name);
+ }
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Disposes the transaction. If it is currently active, any changes are rolled back.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ ////////////////////////////////////
+ // dispose managed resources here...
+ ////////////////////////////////////
+
+ if (IsValid(false))
+ {
+ IssueRollback(false);
+ }
+ }
+
+ //////////////////////////////////////
+ // release unmanaged resources here...
+ //////////////////////////////////////
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+
+ //
+ // NOTE: Everything should be fully disposed at this point.
+ //
+ disposed = true;
+ }
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Returns the underlying connection to which this transaction applies.
+ ///
+ public new SQLiteConnection Connection
+ {
+ get { CheckDisposed(); return _cnn; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Forwards to the local Connection property
+ ///
+ protected override DbConnection DbConnection
+ {
+ get { return Connection; }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Rolls back the active transaction.
+ ///
+ public override void Rollback()
+ {
+ CheckDisposed();
+ SQLiteConnection.Check(_cnn);
+ IsValid(true);
+ IssueRollback(true);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to start a transaction. An exception will be thrown if the transaction cannot
+ /// be started for any reason.
+ ///
+ /// TRUE to defer the writelock, or FALSE to lock immediately
+ protected abstract void Begin(bool deferredLock);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Issue a ROLLBACK command against the database connection,
+ /// optionally re-throwing any caught exception.
+ ///
+ ///
+ /// Non-zero to re-throw caught exceptions.
+ ///
+ protected abstract void IssueRollback(bool throwError);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Checks the state of this transaction, optionally throwing an exception if a state
+ /// inconsistency is found.
+ ///
+ ///
+ /// Non-zero to throw an exception if a state inconsistency is found.
+ ///
+ ///
+ /// Non-zero if this transaction is valid; otherwise, false.
+ ///
+ internal bool IsValid(bool throwError)
+ {
+ if (_cnn == null)
+ {
+ if (throwError == true) throw new ArgumentNullException("No connection associated with this transaction");
+ else return false;
+ }
+
+ if (_cnn._version != _version)
+ {
+ if (throwError == true) throw new SQLiteException("The connection was closed and re-opened, changes were already rolled back");
+ else return false;
+ }
+ if (_cnn.State != ConnectionState.Open)
+ {
+ if (throwError == true) throw new SQLiteException("Connection was closed");
+ else return false;
+ }
+
+ if (_cnn._transactionLevel == 0 || _cnn._sql.AutoCommit == true)
+ {
+ _cnn._transactionLevel = 0; // Make sure the transaction level is reset before returning
+ if (throwError == true) throw new SQLiteException("No transaction is active on this connection");
+ else return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/Native.Csharp.Tool/SQLite/UnsafeNativeMethods.cs b/Native.Csharp.Tool/SQLite/UnsafeNativeMethods.cs
new file mode 100644
index 00000000..f5db906f
--- /dev/null
+++ b/Native.Csharp.Tool/SQLite/UnsafeNativeMethods.cs
@@ -0,0 +1,6013 @@
+/********************************************************
+ * ADO.NET 2.0 Data Provider for SQLite Version 3.X
+ * Written by Robert Simpson (robert@blackcastlesoft.com)
+ *
+ * Released to the public domain, use at your own risk!
+ ********************************************************/
+
+namespace System.Data.SQLite
+{
+ using System;
+ using System.Globalization;
+
+#if TRACE_DETECTION || TRACE_SHARED || TRACE_PRELOAD || TRACE_HANDLE
+ using System.Diagnostics;
+#endif
+
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Reflection;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ using System.Security;
+#endif
+
+ using System.Runtime.InteropServices;
+
+#if (NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47 || NET_471 || NET_472) && !PLATFORM_COMPACTFRAMEWORK
+ using System.Runtime.Versioning;
+#endif
+
+ using System.Text;
+
+#if !PLATFORM_COMPACTFRAMEWORK || COUNT_HANDLE
+ using System.Threading;
+#endif
+
+ using System.Xml;
+
+ #region Debug Data Static Class
+#if COUNT_HANDLE || DEBUG
+ ///
+ /// This class encapsulates some tracking data that is used for debugging
+ /// and testing purposes.
+ ///
+ internal static class DebugData
+ {
+ #region Private Data
+#if DEBUG
+ ///
+ /// This lock is used to protect several static fields.
+ ///
+ private static readonly object staticSyncRoot = new object();
+#endif
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Critical Handle Counts (Debug Build Only)
+#if COUNT_HANDLE
+ //
+ // NOTE: These counts represent the total number of outstanding
+ // (non-disposed) CriticalHandle derived object instances
+ // created by this library and are primarily for use by
+ // the test suite. These counts are incremented by the
+ // associated constructors and are decremented upon the
+ // successful completion of the associated ReleaseHandle
+ // methods.
+ //
+ internal static int connectionCount;
+ internal static int statementCount;
+ internal static int backupCount;
+ internal static int blobCount;
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Settings Read Counts (Debug Build Only)
+#if DEBUG
+ ///
+ /// This dictionary stores the read counts for the runtime configuration
+ /// settings. This information is only recorded when compiled in the
+ /// "Debug" build configuration.
+ ///
+ private static Dictionary settingReadCounts;
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This dictionary stores the read counts for the runtime configuration
+ /// settings via the XML configuration file. This information is only
+ /// recorded when compiled in the "Debug" build configuration.
+ ///
+ private static Dictionary settingFileReadCounts;
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Other Counts (Debug Build Only)
+#if DEBUG
+ ///
+ /// This dictionary stores miscellaneous counts used for debugging
+ /// purposes. This information is only recorded when compiled in the
+ /// "Debug" build configuration.
+ ///
+ private static Dictionary otherCounts;
+#endif
+ #endregion
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+#if DEBUG
+ ///
+ /// Creates dictionaries used to store the read counts for each of
+ /// the runtime configuration settings. These numbers are used for
+ /// debugging and testing purposes only.
+ ///
+ public static void Initialize()
+ {
+ lock (staticSyncRoot)
+ {
+ //
+ // NOTE: Create the dictionaries of statistics that will
+ // contain the number of times each setting value
+ // has been read.
+ //
+ if (settingReadCounts == null)
+ settingReadCounts = new Dictionary();
+
+ if (settingFileReadCounts == null)
+ settingFileReadCounts = new Dictionary();
+
+ if (otherCounts == null)
+ otherCounts = new Dictionary();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries the read counts for the runtime configuration settings.
+ /// These numbers are used for debugging and testing purposes only.
+ ///
+ ///
+ /// Non-zero if the specified settings were read from the XML
+ /// configuration file.
+ ///
+ ///
+ /// A copy of the statistics for the specified runtime configuration
+ /// settings -OR- null if they are not available.
+ ///
+ public static object GetSettingReadCounts(
+ bool viaFile
+ )
+ {
+ lock (staticSyncRoot)
+ {
+ if (viaFile)
+ {
+ if (settingFileReadCounts == null)
+ return null;
+
+ return new Dictionary(settingFileReadCounts);
+ }
+ else
+ {
+ if (settingReadCounts == null)
+ return null;
+
+ return new Dictionary(settingReadCounts);
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Clears the read counts for the runtime configuration settings.
+ /// These numbers are used for debugging and testing purposes only.
+ ///
+ ///
+ /// Non-zero if the specified settings were read from the XML
+ /// configuration file.
+ ///
+ public static void ClearSettingReadCounts(
+ bool viaFile
+ )
+ {
+ lock (staticSyncRoot)
+ {
+ if (viaFile)
+ {
+ if (settingFileReadCounts != null)
+ settingFileReadCounts.Clear();
+ }
+ else
+ {
+ if (settingReadCounts != null)
+ settingReadCounts.Clear();
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Increments the read count for the specified runtime configuration
+ /// setting. These numbers are used for debugging and testing purposes
+ /// only.
+ ///
+ ///
+ /// The name of the setting being read.
+ ///
+ ///
+ /// Non-zero if the specified setting is being read from the XML
+ /// configuration file.
+ ///
+ public static void IncrementSettingReadCount(
+ string name,
+ bool viaFile
+ )
+ {
+ lock (staticSyncRoot)
+ {
+ //
+ // NOTE: Update statistics for this setting value.
+ //
+ if (viaFile)
+ {
+ if (settingFileReadCounts != null)
+ {
+ int count;
+
+ if (settingFileReadCounts.TryGetValue(name, out count))
+ settingFileReadCounts[name] = count + 1;
+ else
+ settingFileReadCounts.Add(name, 1);
+ }
+ }
+ else
+ {
+ if (settingReadCounts != null)
+ {
+ int count;
+
+ if (settingReadCounts.TryGetValue(name, out count))
+ settingReadCounts[name] = count + 1;
+ else
+ settingReadCounts.Add(name, 1);
+ }
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries the counters. These numbers are used for debugging and
+ /// testing purposes only.
+ ///
+ ///
+ /// A copy of the counters -OR- null if they are not available.
+ ///
+ public static object GetOtherCounts()
+ {
+ lock (staticSyncRoot)
+ {
+ if (otherCounts == null)
+ return null;
+
+ return new Dictionary(otherCounts);
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Clears the counters. These numbers are used for debugging and
+ /// testing purposes only.
+ ///
+ public static void ClearOtherCounts()
+ {
+ lock (staticSyncRoot)
+ {
+ if (otherCounts != null)
+ otherCounts.Clear();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Increments the specified counter.
+ ///
+ ///
+ /// The name of the counter being incremented.
+ ///
+ public static void IncrementOtherCount(
+ string name
+ )
+ {
+ lock (staticSyncRoot)
+ {
+ if (otherCounts != null)
+ {
+ int count;
+
+ if (otherCounts.TryGetValue(name, out count))
+ otherCounts[name] = count + 1;
+ else
+ otherCounts.Add(name, 1);
+ }
+ }
+ }
+#endif
+ #endregion
+ }
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ #region Helper Methods Static Class
+ ///
+ /// This static class provides some methods that are shared between the
+ /// native library pre-loader and other classes.
+ ///
+ internal static class HelperMethods
+ {
+ #region Private Constants
+ private const string DisplayNullObject = "";
+ private const string DisplayEmptyString = "";
+ private const string DisplayStringFormat = "\"{0}\"";
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private const string DisplayNullArray = "";
+ private const string DisplayEmptyArray = "";
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private const char ArrayOpen = '[';
+ private const string ElementSeparator = ", ";
+ private const char ArrayClose = ']';
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private static readonly char[] SpaceChars = {
+ '\t', '\n', '\r', '\v', '\f', ' '
+ };
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// This lock is used to protect the static and
+ /// fields.
+ ///
+ private static readonly object staticSyncRoot = new object();
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This type is only present when running on Mono.
+ ///
+ private static readonly string MonoRuntimeType = "Mono.Runtime";
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This type is only present when running on .NET Core.
+ ///
+ private static readonly string DotNetCoreLibType = "System.CoreLib";
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Keeps track of whether we are running on Mono. Initially null, it is
+ /// set by the method on its first call. Later, it
+ /// is returned verbatim by the method.
+ ///
+ private static bool? isMono = null;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Keeps track of whether we are running on .NET Core. Initially null,
+ /// it is set by the method on its first
+ /// call. Later, it is returned verbatim by the
+ /// method.
+ ///
+ private static bool? isDotNetCore = null;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Keeps track of whether we successfully invoked the
+ /// method. Initially null, it is set by
+ /// the method on its first call.
+ ///
+ private static bool? debuggerBreak = null;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Determines the ID of the current process. Only used for debugging.
+ ///
+ ///
+ /// The ID of the current process -OR- zero if it cannot be determined.
+ ///
+ private static int GetProcessId()
+ {
+ Process process = Process.GetCurrentProcess();
+
+ if (process == null)
+ return 0;
+
+ return process.Id;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines whether or not this assembly is running on Mono.
+ ///
+ ///
+ /// Non-zero if this assembly is running on Mono.
+ ///
+ private static bool IsMono()
+ {
+ try
+ {
+ lock (staticSyncRoot)
+ {
+ if (isMono == null)
+ isMono = (Type.GetType(MonoRuntimeType) != null);
+
+ return (bool)isMono;
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ return false;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines whether or not this assembly is running on .NET Core.
+ ///
+ ///
+ /// Non-zero if this assembly is running on .NET Core.
+ ///
+ public static bool IsDotNetCore()
+ {
+ try
+ {
+ lock (staticSyncRoot)
+ {
+ if (isDotNetCore == null)
+ {
+ isDotNetCore = (Type.GetType(
+ DotNetCoreLibType) != null);
+ }
+
+ return (bool)isDotNetCore;
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ return false;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Internal Methods
+ ///
+ /// Resets the cached value for the "PreLoadSQLite_BreakIntoDebugger"
+ /// configuration setting.
+ ///
+ internal static void ResetBreakIntoDebugger()
+ {
+ lock (staticSyncRoot)
+ {
+ debuggerBreak = null;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// If the "PreLoadSQLite_BreakIntoDebugger" configuration setting is
+ /// present (e.g. via the environment), give the interactive user an
+ /// opportunity to attach a debugger to the current process; otherwise,
+ /// do nothing.
+ ///
+ internal static void MaybeBreakIntoDebugger()
+ {
+ lock (staticSyncRoot)
+ {
+ if (debuggerBreak != null)
+ return;
+ }
+
+ if (UnsafeNativeMethods.GetSettingValue(
+ "PreLoadSQLite_BreakIntoDebugger", null) != null)
+ {
+ //
+ // NOTE: Attempt to use the Console in order to prompt the
+ // interactive user (if any). This may fail for any
+ // number of reasons. Even in those cases, we still
+ // want to issue the actual request to break into the
+ // debugger.
+ //
+ try
+ {
+ Console.WriteLine(StringFormat(
+ CultureInfo.CurrentCulture,
+ "Attach a debugger to process {0} " +
+ "and press any key to continue.",
+ GetProcessId()));
+
+#if PLATFORM_COMPACTFRAMEWORK
+ Console.ReadLine();
+#else
+ Console.ReadKey();
+#endif
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Failed to issue debugger prompt, " +
+ "{0} may be unusable: {1}",
+ typeof(Console), e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+
+ try
+ {
+ Debugger.Break();
+
+ lock (staticSyncRoot)
+ {
+ debuggerBreak = true;
+ }
+ }
+ catch
+ {
+ lock (staticSyncRoot)
+ {
+ debuggerBreak = false;
+ }
+
+ throw;
+ }
+ }
+ else
+ {
+ //
+ // BUGFIX: There is (almost) no point in checking for the
+ // associated configuration setting repeatedly.
+ // Prevent that here by setting the cached value
+ // to false.
+ //
+ lock (staticSyncRoot)
+ {
+ debuggerBreak = false;
+ }
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines the ID of the current thread. Only used for debugging.
+ ///
+ ///
+ /// The ID of the current thread -OR- zero if it cannot be determined.
+ ///
+ internal static int GetThreadId()
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ return AppDomain.GetCurrentThreadId();
+#else
+ return 0;
+#endif
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if the specified flags are present within the flags
+ /// associated with the parent connection object.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// The flags to check for.
+ ///
+ ///
+ /// Non-zero if the specified flag or flags were present; otherwise,
+ /// zero.
+ ///
+ internal static bool HasFlags(
+ SQLiteConnectionFlags flags,
+ SQLiteConnectionFlags hasFlags
+ )
+ {
+ return ((flags & hasFlags) == hasFlags);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Determines if preparing a query should be logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the query preparation should be logged; otherwise, zero.
+ ///
+ internal static bool LogPrepare(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogPrepare);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if pre-parameter binding should be logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the pre-parameter binding should be logged; otherwise,
+ /// zero.
+ ///
+ internal static bool LogPreBind(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogPreBind);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if parameter binding should be logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the parameter binding should be logged; otherwise, zero.
+ ///
+ internal static bool LogBind(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogBind);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if an exception in a native callback should be logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the exception should be logged; otherwise, zero.
+ ///
+ internal static bool LogCallbackExceptions(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogCallbackException);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if backup API errors should be logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the backup API error should be logged; otherwise, zero.
+ ///
+ internal static bool LogBackup(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogBackup);
+ }
+
+#if INTEROP_VIRTUAL_TABLE
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if logging for the class is
+ /// disabled.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if logging for the class is
+ /// disabled; otherwise, zero.
+ ///
+ internal static bool NoLogModule(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.NoLogModule);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if errors should be logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the error should be logged;
+ /// otherwise, zero.
+ ///
+ internal static bool LogModuleError(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogModuleError);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if exceptions should be
+ /// logged.
+ ///
+ ///
+ /// The flags associated with the parent connection object.
+ ///
+ ///
+ /// Non-zero if the exception should be
+ /// logged; otherwise, zero.
+ ///
+ internal static bool LogModuleException(
+ SQLiteConnectionFlags flags
+ )
+ {
+ return HasFlags(flags, SQLiteConnectionFlags.LogModuleException);
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if the current process is running on one of the Windows
+ /// [sub-]platforms.
+ ///
+ ///
+ /// Non-zero when running on Windows; otherwise, zero.
+ ///
+ internal static bool IsWindows()
+ {
+ PlatformID platformId = Environment.OSVersion.Platform;
+
+ if ((platformId == PlatformID.Win32S) ||
+ (platformId == PlatformID.Win32Windows) ||
+ (platformId == PlatformID.Win32NT) ||
+ (platformId == PlatformID.WinCE))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is a wrapper around the
+ /// method.
+ /// On Mono, it has to call the method overload without the
+ /// parameter, due to a bug in Mono.
+ ///
+ ///
+ /// This is used for culture-specific formatting.
+ ///
+ ///
+ /// The format string.
+ ///
+ ///
+ /// An array the objects to format.
+ ///
+ ///
+ /// The resulting string.
+ ///
+ internal static string StringFormat(
+ IFormatProvider provider,
+ string format,
+ params object[] args
+ )
+ {
+ if (IsMono())
+ return String.Format(format, args);
+ else
+ return String.Format(provider, format, args);
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ public static string ToDisplayString(
+ object value
+ )
+ {
+ if (value == null)
+ return DisplayNullObject;
+
+ string stringValue = value.ToString();
+
+ if (stringValue.Length == 0)
+ return DisplayEmptyString;
+
+ if (stringValue.IndexOfAny(SpaceChars) < 0)
+ return stringValue;
+
+ return HelperMethods.StringFormat(
+ CultureInfo.InvariantCulture, DisplayStringFormat,
+ stringValue);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ public static string ToDisplayString(
+ Array array
+ )
+ {
+ if (array == null)
+ return DisplayNullArray;
+
+ if (array.Length == 0)
+ return DisplayEmptyArray;
+
+ StringBuilder result = new StringBuilder();
+
+ foreach (object value in array)
+ {
+ if (result.Length > 0)
+ result.Append(ElementSeparator);
+
+ result.Append(ToDisplayString(value));
+ }
+
+ if (result.Length > 0)
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ result.Insert(0, ArrayOpen.ToString());
+#else
+ result.Insert(0, ArrayOpen);
+#endif
+
+ result.Append(ArrayClose);
+ }
+
+ return result.ToString();
+ }
+ #endregion
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ #region Native Library Helper Class
+ ///
+ /// This static class provides a thin wrapper around the native library
+ /// loading features of the underlying platform.
+ ///
+ internal static class NativeLibraryHelper
+ {
+ #region Private Delegates
+ ///
+ /// This delegate is used to wrap the concept of loading a native
+ /// library, based on a file name, and returning the loaded module
+ /// handle.
+ ///
+ ///
+ /// The file name of the native library to load.
+ ///
+ ///
+ /// The native module handle upon success -OR- IntPtr.Zero on failure.
+ ///
+ private delegate IntPtr LoadLibraryCallback(
+ string fileName
+ );
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This delegate is used to wrap the concept of querying the machine
+ /// name of the current process.
+ ///
+ ///
+ /// The machine name for the current process -OR- null on failure.
+ ///
+ private delegate string GetMachineCallback();
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// Attempts to load the specified native library file using the Win32
+ /// API.
+ ///
+ ///
+ /// The file name of the native library to load.
+ ///
+ ///
+ /// The native module handle upon success -OR- IntPtr.Zero on failure.
+ ///
+ private static IntPtr LoadLibraryWin32(
+ string fileName
+ )
+ {
+ return UnsafeNativeMethodsWin32.LoadLibrary(fileName);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to determine the machine name of the current process using
+ /// the Win32 API.
+ ///
+ ///
+ /// The machine name for the current process -OR- null on failure.
+ ///
+ private static string GetMachineWin32()
+ {
+ //
+ // NOTE: When running on Windows, attempt to use the native Win32
+ // API function (via P/Invoke) that can provide us with the
+ // processor architecture.
+ //
+ try
+ {
+ UnsafeNativeMethodsWin32.SYSTEM_INFO systemInfo;
+
+ //
+ // NOTE: Query the system information via P/Invoke, thus
+ // filling the structure.
+ //
+ UnsafeNativeMethodsWin32.GetSystemInfo(out systemInfo);
+
+ //
+ // NOTE: Return the processor architecture value as a string.
+ //
+ return systemInfo.wProcessorArchitecture.ToString();
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ return null;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Attempts to load the specified native library file using the POSIX
+ /// API.
+ ///
+ ///
+ /// The file name of the native library to load.
+ ///
+ ///
+ /// The native module handle upon success -OR- IntPtr.Zero on failure.
+ ///
+ private static IntPtr LoadLibraryPosix(
+ string fileName
+ )
+ {
+ return UnsafeNativeMethodsPosix.dlopen(
+ fileName, UnsafeNativeMethodsPosix.RTLD_DEFAULT);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to determine the machine name of the current process using
+ /// the POSIX API.
+ ///
+ ///
+ /// The machine name for the current process -OR- null on failure.
+ ///
+ private static string GetMachinePosix()
+ {
+ //
+ // NOTE: When running on POSIX (non-Windows), attempt to query the
+ // machine from the operating system via uname().
+ //
+ try
+ {
+ UnsafeNativeMethodsPosix.utsname utsName = null;
+
+ if (UnsafeNativeMethodsPosix.GetOsVersionInfo(ref utsName) &&
+ (utsName != null))
+ {
+ return utsName.machine;
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ return null;
+ }
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Public Methods
+ ///
+ /// Attempts to load the specified native library file.
+ ///
+ ///
+ /// The file name of the native library to load.
+ ///
+ ///
+ /// The native module handle upon success -OR- IntPtr.Zero on failure.
+ ///
+ public static IntPtr LoadLibrary(
+ string fileName
+ )
+ {
+ LoadLibraryCallback callback = LoadLibraryWin32;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (!HelperMethods.IsWindows())
+ callback = LoadLibraryPosix;
+#endif
+
+ return callback(fileName);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Attempts to determine the machine name of the current process.
+ ///
+ ///
+ /// The machine name for the current process -OR- null on failure.
+ ///
+ public static string GetMachine()
+ {
+ GetMachineCallback callback = GetMachineWin32;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (!HelperMethods.IsWindows())
+ callback = GetMachinePosix;
+#endif
+
+ return callback();
+ }
+ #endregion
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ #region Unmanaged Interop Methods Static Class (POSIX)
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// This class declares P/Invoke methods to call native POSIX APIs.
+ ///
+ [SuppressUnmanagedCodeSecurity]
+ internal static class UnsafeNativeMethodsPosix
+ {
+ ///
+ /// This structure is used when running on POSIX operating systems
+ /// to store information about the current machine, including the
+ /// human readable name of the operating system as well as that of
+ /// the underlying hardware.
+ ///
+ internal sealed class utsname
+ {
+ public string sysname; /* Name of this implementation of
+ * the operating system. */
+ public string nodename; /* Name of this node within the
+ * communications network to which
+ * this node is attached, if any. */
+ public string release; /* Current release level of this
+ * implementation. */
+ public string version; /* Current version level of this
+ * release. */
+ public string machine; /* Name of the hardware type on
+ * which the system is running. */
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This structure is passed directly to the P/Invoke method to
+ /// obtain the information about the current machine, including
+ /// the human readable name of the operating system as well as
+ /// that of the underlying hardware.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ private struct utsname_interop
+ {
+ //
+ // NOTE: The following string fields should be present in
+ // this buffer, all of which will be zero-terminated:
+ //
+ // sysname
+ // nodename
+ // release
+ // version
+ // machine
+ //
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4096)]
+ public byte[] buffer;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This is the P/Invoke method that wraps the native Unix uname
+ /// function. See the POSIX documentation for full details on what it
+ /// does.
+ ///
+ ///
+ /// Structure containing a preallocated byte buffer to fill with the
+ /// requested information.
+ ///
+ ///
+ /// Zero for success and less than zero upon failure.
+ ///
+#if NET_STANDARD_20
+ [DllImport("libc",
+#else
+ [DllImport("__Internal",
+#endif
+ CallingConvention = CallingConvention.Cdecl)]
+ private static extern int uname(out utsname_interop name);
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the P/Invoke method that wraps the native Unix dlopen
+ /// function. See the POSIX documentation for full details on what it
+ /// does.
+ ///
+ ///
+ /// The name of the executable library.
+ ///
+ ///
+ /// This must be a combination of the individual bit flags RTLD_LAZY,
+ /// RTLD_NOW, RTLD_GLOBAL, and/or RTLD_LOCAL.
+ ///
+ ///
+ /// The native module handle upon success -OR- IntPtr.Zero on failure.
+ ///
+#if NET_STANDARD_20
+ [DllImport("libdl",
+#else
+ [DllImport("__Internal",
+#endif
+ EntryPoint = "dlopen",
+ CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi,
+ BestFitMapping = false, ThrowOnUnmappableChar = true,
+ SetLastError = true)]
+ internal static extern IntPtr dlopen(string fileName, int mode);
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the P/Invoke method that wraps the native Unix dlclose
+ /// function. See the POSIX documentation for full details on what it
+ /// does.
+ ///
+ ///
+ /// The handle to the loaded native library.
+ ///
+ ///
+ /// Zero upon success -OR- non-zero on failure.
+ ///
+#if NET_STANDARD_20
+ [DllImport("libdl",
+#else
+ [DllImport("__Internal",
+#endif
+ EntryPoint = "dlclose",
+ CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ internal static extern int dlclose(IntPtr module);
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Constants
+ ///
+ /// For use with dlopen(), bind function calls lazily.
+ ///
+ internal const int RTLD_LAZY = 0x1;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// For use with dlopen(), bind function calls immediately.
+ ///
+ internal const int RTLD_NOW = 0x2;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// For use with dlopen(), make symbols globally available.
+ ///
+ internal const int RTLD_GLOBAL = 0x100;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// For use with dlopen(), opposite of RTLD_GLOBAL, and the default.
+ ///
+ internal const int RTLD_LOCAL = 0x000;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// For use with dlopen(), the defaults used by this class.
+ ///
+ internal const int RTLD_DEFAULT = RTLD_NOW | RTLD_GLOBAL;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// These are the characters used to separate the string fields within
+ /// the raw buffer returned by the P/Invoke method.
+ ///
+ private static readonly char[] utsNameSeparators = { '\0' };
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Methods
+ ///
+ /// This method is a wrapper around the P/Invoke
+ /// method that extracts and returns the human readable strings from
+ /// the raw buffer.
+ ///
+ ///
+ /// This structure, which contains strings, will be filled based on the
+ /// data placed in the raw buffer returned by the
+ /// P/Invoke method.
+ ///
+ ///
+ /// Non-zero upon success; otherwise, zero.
+ ///
+ internal static bool GetOsVersionInfo(
+ ref utsname utsName
+ )
+ {
+ try
+ {
+ utsname_interop utfNameInterop;
+
+ if (uname(out utfNameInterop) < 0)
+ return false;
+
+ if (utfNameInterop.buffer == null)
+ return false;
+
+ string bufferAsString = Encoding.UTF8.GetString(
+ utfNameInterop.buffer);
+
+ if ((bufferAsString == null) || (utsNameSeparators == null))
+ return false;
+
+ bufferAsString = bufferAsString.Trim(utsNameSeparators);
+
+ string[] parts = bufferAsString.Split(
+ utsNameSeparators, StringSplitOptions.RemoveEmptyEntries);
+
+ if (parts == null)
+ return false;
+
+ utsname localUtsName = new utsname();
+
+ if (parts.Length >= 1)
+ localUtsName.sysname = parts[0];
+
+ if (parts.Length >= 2)
+ localUtsName.nodename = parts[1];
+
+ if (parts.Length >= 3)
+ localUtsName.release = parts[2];
+
+ if (parts.Length >= 4)
+ localUtsName.version = parts[3];
+
+ if (parts.Length >= 5)
+ localUtsName.machine = parts[4];
+
+ utsName = localUtsName;
+ return true;
+ }
+ catch
+ {
+ // do nothing.
+ }
+
+ return false;
+ }
+ #endregion
+ }
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ #region Unmanaged Interop Methods Static Class (Win32)
+ ///
+ /// This class declares P/Invoke methods to call native Win32 APIs.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [SuppressUnmanagedCodeSecurity]
+#endif
+ internal static class UnsafeNativeMethodsWin32
+ {
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the P/Invoke method that wraps the native Win32 LoadLibrary
+ /// function. See the MSDN documentation for full details on what it
+ /// does.
+ ///
+ ///
+ /// The name of the executable library.
+ ///
+ ///
+ /// The native module handle upon success -OR- IntPtr.Zero on failure.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport("kernel32",
+#else
+ [DllImport("coredll",
+#endif
+ CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto,
+#if !PLATFORM_COMPACTFRAMEWORK
+ BestFitMapping = false, ThrowOnUnmappableChar = true,
+#endif
+ SetLastError = true)]
+ internal static extern IntPtr LoadLibrary(string fileName);
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// This is the P/Invoke method that wraps the native Win32 GetSystemInfo
+ /// function. See the MSDN documentation for full details on what it
+ /// does.
+ ///
+ ///
+ /// The system information structure to be filled in by the function.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport("kernel32",
+#else
+ [DllImport("coredll",
+#endif
+ CallingConvention = CallingConvention.Winapi)]
+ internal static extern void GetSystemInfo(out SYSTEM_INFO systemInfo);
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This enumeration contains the possible values for the processor
+ /// architecture field of the system information structure.
+ ///
+ internal enum ProcessorArchitecture : ushort /* COMPAT: Win32. */
+ {
+ Intel = 0,
+ MIPS = 1,
+ Alpha = 2,
+ PowerPC = 3,
+ SHx = 4,
+ ARM = 5,
+ IA64 = 6,
+ Alpha64 = 7,
+ MSIL = 8,
+ AMD64 = 9,
+ IA32_on_Win64 = 10,
+ Unknown = 0xFFFF
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This structure contains information about the current computer. This
+ /// includes the processor type, page size, memory addresses, etc.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct SYSTEM_INFO
+ {
+ public ProcessorArchitecture wProcessorArchitecture;
+ public ushort wReserved; /* NOT USED */
+ public uint dwPageSize; /* NOT USED */
+ public IntPtr lpMinimumApplicationAddress; /* NOT USED */
+ public IntPtr lpMaximumApplicationAddress; /* NOT USED */
+#if PLATFORM_COMPACTFRAMEWORK
+ public uint dwActiveProcessorMask; /* NOT USED */
+#else
+ public IntPtr dwActiveProcessorMask; /* NOT USED */
+#endif
+ public uint dwNumberOfProcessors; /* NOT USED */
+ public uint dwProcessorType; /* NOT USED */
+ public uint dwAllocationGranularity; /* NOT USED */
+ public ushort wProcessorLevel; /* NOT USED */
+ public ushort wProcessorRevision; /* NOT USED */
+ }
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ #region Unmanaged Interop Methods Static Class (SQLite)
+ ///
+ /// This class declares P/Invoke methods to call native SQLite APIs.
+ ///
+#if !PLATFORM_COMPACTFRAMEWORK
+ [SuppressUnmanagedCodeSecurity]
+#endif
+ internal static class UnsafeNativeMethods
+ {
+ public const string ExceptionMessageFormat =
+ "Caught exception in \"{0}\" method: {1}";
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Shared Native SQLite Library Pre-Loading Code
+ #region Private Constants
+ ///
+ /// The file extension used for dynamic link libraries.
+ ///
+ private static readonly string DllFileExtension = ".dll";
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// The file extension used for the XML configuration file.
+ ///
+ private static readonly string ConfigFileExtension = ".config";
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the name of the XML configuration file specific to the
+ /// System.Data.SQLite assembly.
+ ///
+ private static readonly string XmlConfigFileName =
+ typeof(UnsafeNativeMethods).Namespace + DllFileExtension +
+ ConfigFileExtension;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the XML configuratrion file token that will be replaced with
+ /// the qualified path to the directory containing the XML configuration
+ /// file.
+ ///
+ private static readonly string XmlConfigDirectoryToken =
+ "%PreLoadSQLite_XmlConfigDirectory%";
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Constants (Desktop Framework Only)
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// This is the environment variable token that will be replaced with
+ /// the qualified path to the directory containing this assembly.
+ ///
+ private static readonly string AssemblyDirectoryToken =
+ "%PreLoadSQLite_AssemblyDirectory%";
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the environment variable token that will be replaced with an
+ /// abbreviation of the target framework attribute value associated with
+ /// this assembly.
+ ///
+ private static readonly string TargetFrameworkToken =
+ "%PreLoadSQLite_TargetFramework%";
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// This lock is used to protect the static _SQLiteNativeModuleFileName,
+ /// _SQLiteNativeModuleHandle, and processorArchitecturePlatforms fields.
+ ///
+ private static readonly object staticSyncRoot = new object();
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This dictionary stores the mappings between processor architecture
+ /// names and platform names. These mappings are now used for two
+ /// purposes. First, they are used to determine if the assembly code
+ /// base should be used instead of the location, based upon whether one
+ /// or more of the named sub-directories exist within the assembly code
+ /// base. Second, they are used to assist in loading the appropriate
+ /// SQLite interop assembly into the current process.
+ ///
+ private static Dictionary processorArchitecturePlatforms;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the cached return value from the
+ /// method -OR- null if that method
+ /// has never returned a valid value.
+ ///
+ private static string cachedAssemblyDirectory;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// When this field is non-zero, it indicates the
+ /// method was not able to locate a
+ /// suitable assembly directory. The
+ /// method will check this
+ /// field and skips calls into the
+ /// method whenever it is non-zero.
+ ///
+ private static bool noAssemblyDirectory;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// This is the cached return value from the
+ /// method -OR- null if that method
+ /// has never returned a valid value.
+ ///
+ private static string cachedXmlConfigFileName;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// When this field is non-zero, it indicates the
+ /// method was not able to locate a
+ /// suitable XML configuration file name. The
+ /// method will check this
+ /// field and skips calls into the
+ /// method whenever it is non-zero.
+ ///
+ private static bool noXmlConfigFileName;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// For now, this method simply calls the Initialize method.
+ ///
+ static UnsafeNativeMethods()
+ {
+ Initialize();
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Attempts to initialize this class by pre-loading the native SQLite
+ /// library for the processor architecture of the current process.
+ ///
+ internal static void Initialize()
+ {
+ #region Debug Build Only
+#if DEBUG
+ //
+ // NOTE: Create the lists of statistics that will contain
+ // various counts used in debugging, including the
+ // number of times each setting value has been read.
+ //
+ DebugData.Initialize();
+#endif
+ #endregion
+
+ //
+ // NOTE: Check if a debugger needs to be attached before doing any
+ // real work.
+ //
+ HelperMethods.MaybeBreakIntoDebugger();
+
+#if SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK
+#if PRELOAD_NATIVE_LIBRARY
+ //
+ // NOTE: If the "No_PreLoadSQLite" environment variable is set (to
+ // anything), skip all of our special code and simply return.
+ //
+ if (GetSettingValue("No_PreLoadSQLite", null) != null)
+ return;
+#endif
+#endif
+
+ lock (staticSyncRoot)
+ {
+ //
+ // TODO: Make sure this list is updated if the supported
+ // processor architecture names and/or platform names
+ // changes.
+ //
+ if (processorArchitecturePlatforms == null)
+ {
+ //
+ // NOTE: Create the map of processor architecture names
+ // to platform names using a case-insensitive string
+ // comparer.
+ //
+ processorArchitecturePlatforms =
+ new Dictionary(
+ StringComparer.OrdinalIgnoreCase);
+
+ //
+ // NOTE: Setup the list of platform names associated with
+ // the supported processor architectures.
+ //
+ processorArchitecturePlatforms.Add("x86", "Win32");
+ processorArchitecturePlatforms.Add("x86_64", "x64");
+ processorArchitecturePlatforms.Add("AMD64", "x64");
+ processorArchitecturePlatforms.Add("IA64", "Itanium");
+ processorArchitecturePlatforms.Add("ARM", "WinCE");
+ }
+
+#if SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK
+#if PRELOAD_NATIVE_LIBRARY
+ //
+ // BUGBUG: What about other application domains?
+ //
+ if (_SQLiteNativeModuleHandle == IntPtr.Zero)
+ {
+ string baseDirectory = null;
+ string processorArchitecture = null;
+ bool allowBaseDirectoryOnly = false;
+
+ /* IGNORED */
+ SearchForDirectory(
+ ref baseDirectory, ref processorArchitecture,
+ ref allowBaseDirectoryOnly);
+
+ //
+ // NOTE: Attempt to pre-load the SQLite core library (or
+ // interop assembly) and store both the file name
+ // and native module handle for later usage.
+ //
+ /* IGNORED */
+ PreLoadSQLiteDll(baseDirectory,
+ processorArchitecture, allowBaseDirectoryOnly,
+ ref _SQLiteNativeModuleFileName,
+ ref _SQLiteNativeModuleHandle);
+ }
+#endif
+#endif
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Combines two path strings.
+ ///
+ ///
+ /// The first path -OR- null.
+ ///
+ ///
+ /// The second path -OR- null.
+ ///
+ ///
+ /// The combined path string -OR- null if both of the original path
+ /// strings are null.
+ ///
+ private static string MaybeCombinePath(
+ string path1,
+ string path2
+ )
+ {
+ if (path1 != null)
+ {
+ if (path2 != null)
+ return Path.Combine(path1, path2);
+ else
+ return path1;
+ }
+ else
+ {
+ if (path2 != null)
+ return path2;
+ else
+ return null;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Resets the cached XML configuration file name value, thus forcing the
+ /// next call to method to rely
+ /// upon the method to fetch the
+ /// XML configuration file name.
+ ///
+ private static void ResetCachedXmlConfigFileName()
+ {
+ #region Debug Build Only
+#if DEBUG
+ DebugData.IncrementOtherCount("Method_ResetCachedXmlConfigFileName");
+#endif
+ #endregion
+
+ lock (staticSyncRoot)
+ {
+ cachedXmlConfigFileName = null;
+ noXmlConfigFileName = false;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the cached XML configuration file name for the
+ /// assembly containing the managed System.Data.SQLite components, if
+ /// available. If the cached XML configuration file name value is not
+ /// available, the method will
+ /// be used to obtain the XML configuration file name.
+ ///
+ ///
+ /// The XML configuration file name -OR- null if it cannot be determined
+ /// or does not exist.
+ ///
+ private static string GetCachedXmlConfigFileName()
+ {
+ #region Debug Build Only
+#if DEBUG
+ DebugData.IncrementOtherCount("Method_GetCachedXmlConfigFileName");
+#endif
+ #endregion
+
+ lock (staticSyncRoot)
+ {
+ if (cachedXmlConfigFileName != null)
+ return cachedXmlConfigFileName;
+
+ if (noXmlConfigFileName)
+ return null;
+ }
+
+ return GetXmlConfigFileName();
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the XML configuration file name for the assembly
+ /// containing the managed System.Data.SQLite components.
+ ///
+ ///
+ /// The XML configuration file name -OR- null if it cannot be determined
+ /// or does not exist.
+ ///
+ private static string GetXmlConfigFileName()
+ {
+ #region Debug Build Only
+#if DEBUG
+ DebugData.IncrementOtherCount("Method_GetXmlConfigFileName");
+#endif
+ #endregion
+
+ string directory;
+ string fileName;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ directory = AppDomain.CurrentDomain.BaseDirectory;
+ fileName = MaybeCombinePath(directory, XmlConfigFileName);
+
+ if (File.Exists(fileName))
+ {
+ lock (staticSyncRoot)
+ {
+ cachedXmlConfigFileName = fileName;
+ }
+
+ return fileName;
+ }
+#endif
+
+ directory = GetCachedAssemblyDirectory();
+ fileName = MaybeCombinePath(directory, XmlConfigFileName);
+
+ if (File.Exists(fileName))
+ {
+ lock (staticSyncRoot)
+ {
+ cachedXmlConfigFileName = fileName;
+ }
+
+ return fileName;
+ }
+
+ lock (staticSyncRoot)
+ {
+ noXmlConfigFileName = true;
+ }
+
+ return null;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// If necessary, replaces all supported XML configuration file tokens
+ /// with their associated values.
+ ///
+ ///
+ /// The name of the XML configuration file being read.
+ ///
+ ///
+ /// A setting value read from the XML configuration file.
+ ///
+ ///
+ /// The value of the will all supported XML
+ /// configuration file tokens replaced. No return value is reserved
+ /// to indicate an error. This method cannot fail.
+ ///
+ private static string ReplaceXmlConfigFileTokens(
+ string fileName,
+ string value
+ )
+ {
+ if (!String.IsNullOrEmpty(value))
+ {
+ if (!String.IsNullOrEmpty(fileName))
+ {
+ try
+ {
+ string directory = Path.GetDirectoryName(fileName);
+
+ if (!String.IsNullOrEmpty(directory))
+ {
+ value = value.Replace(
+ XmlConfigDirectoryToken, directory);
+ }
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Native library " +
+ "pre-loader failed to replace XML " +
+ "configuration file \"{0}\" tokens: {1}",
+ fileName, e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+ }
+ }
+
+ return value;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Queries and returns the value of the specified setting, using the
+ /// specified XML configuration file.
+ ///
+ ///
+ /// The name of the XML configuration file to read.
+ ///
+ ///
+ /// The name of the setting.
+ ///
+ ///
+ /// The value to be returned if the setting has not been set explicitly
+ /// or cannot be determined.
+ ///
+ ///
+ /// Non-zero to expand any environment variable references contained in
+ /// the setting value to be returned. This has no effect on the .NET
+ /// Compact Framework.
+ ///
+ ///
+ /// The value of the setting -OR- the default value specified by
+ /// if it has not been set explicitly or
+ /// cannot be determined.
+ ///
+ private static string GetSettingValueViaXmlConfigFile(
+ string fileName, /* in */
+ string name, /* in */
+ string @default, /* in */
+ bool expand /* in */
+ )
+ {
+ try
+ {
+ if ((fileName == null) || (name == null))
+ return @default;
+
+ XmlDocument document = new XmlDocument();
+
+ document.Load(fileName); /* throw */
+
+ XmlElement element = document.SelectSingleNode(
+ HelperMethods.StringFormat(CultureInfo.InvariantCulture,
+ "/configuration/appSettings/add[@key='{0}']", name)) as
+ XmlElement; /* throw */
+
+ if (element != null)
+ {
+ string value = null;
+
+ if (element.HasAttribute("value"))
+ value = element.GetAttribute("value");
+
+ if (!String.IsNullOrEmpty(value))
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ if (expand)
+ value = Environment.ExpandEnvironmentVariables(value);
+
+ value = ReplaceEnvironmentVariableTokens(value);
+#endif
+
+ value = ReplaceXmlConfigFileTokens(fileName, value);
+ }
+
+ if (value != null)
+ return value;
+ }
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Native library " +
+ "pre-loader failed to get setting \"{0}\" value " +
+ "from XML configuration file \"{1}\": {2}", name,
+ fileName, e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+
+ return @default;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ ///
+ /// Attempts to determine the target framework attribute value that is
+ /// associated with the specified managed assembly, if applicable.
+ ///
+ ///
+ /// The managed assembly to read the target framework attribute value
+ /// from.
+ ///
+ ///
+ /// The value of the target framework attribute value for the specified
+ /// managed assembly -OR- null if it cannot be determined. If this
+ /// assembly was compiled with a version of the .NET Framework prior to
+ /// version 4.0, the value returned MAY reflect that version of the .NET
+ /// Framework instead of the one associated with the specified managed
+ /// assembly.
+ ///
+ private static string GetAssemblyTargetFramework(
+ Assembly assembly
+ )
+ {
+ if (assembly != null)
+ {
+#if NET_40 || NET_45 || NET_451 || NET_452 || NET_46 || NET_461 || NET_462 || NET_47 || NET_471 || NET_472 || NET_STANDARD_20
+ try
+ {
+ if (assembly.IsDefined(
+ typeof(TargetFrameworkAttribute), false))
+ {
+ TargetFrameworkAttribute targetFramework =
+ (TargetFrameworkAttribute)
+ assembly.GetCustomAttributes(
+ typeof(TargetFrameworkAttribute), false)[0];
+
+ return targetFramework.FrameworkName;
+ }
+ }
+ catch
+ {
+ // do nothing.
+ }
+#elif NET_35
+ return ".NETFramework,Version=v3.5";
+#elif NET_20
+ return ".NETFramework,Version=v2.0";
+#endif
+ }
+
+ return null;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// Accepts a long target framework attribute value and makes it into a
+ /// much shorter version, suitable for use with NuGet packages.
+ ///
+ ///
+ /// The long target framework attribute value to convert.
+ ///
+ ///
+ /// The short target framework attribute value -OR- null if it cannot
+ /// be determined or converted.
+ ///
+ private static string AbbreviateTargetFramework(
+ string value
+ )
+ {
+ if (String.IsNullOrEmpty(value))
+ return value;
+
+ value = value.Replace(".NETFramework,Version=v", "net");
+ value = value.Replace(".", String.Empty);
+
+ int index = value.IndexOf(',');
+
+ if (index != -1)
+ value = value.Substring(0, index);
+
+ return value;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ ///
+ /// If necessary, replaces all supported environment variable tokens
+ /// with their associated values.
+ ///
+ ///
+ /// A setting value read from an environment variable.
+ ///
+ ///
+ /// The value of the will all supported
+ /// environment variable tokens replaced. No return value is reserved
+ /// to indicate an error. This method cannot fail.
+ ///
+ private static string ReplaceEnvironmentVariableTokens(
+ string value
+ )
+ {
+ if (!String.IsNullOrEmpty(value))
+ {
+ string directory = GetCachedAssemblyDirectory();
+
+ if (!String.IsNullOrEmpty(directory))
+ {
+ try
+ {
+ value = value.Replace(
+ AssemblyDirectoryToken, directory);
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Native library " +
+ "pre-loader failed to replace assembly " +
+ "directory token: {0}", e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+ }
+
+ Assembly assembly = null;
+
+ try
+ {
+ assembly = Assembly.GetExecutingAssembly();
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Native library " +
+ "pre-loader failed to obtain executing " +
+ "assembly: {0}", e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+
+ string targetFramework = AbbreviateTargetFramework(
+ GetAssemblyTargetFramework(assembly));
+
+ if (!String.IsNullOrEmpty(targetFramework))
+ {
+ try
+ {
+ value = value.Replace(
+ TargetFrameworkToken, targetFramework);
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture, "Native library " +
+ "pre-loader failed to replace target " +
+ "framework token: {0}", e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+ }
+ }
+
+ return value;
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the value of the specified setting, using the XML
+ /// configuration file and/or the environment variables for the current
+ /// process and/or the current system, when available.
+ ///
+ ///
+ /// The name of the setting.
+ ///
+ ///
+ /// The value to be returned if the setting has not been set explicitly
+ /// or cannot be determined.
+ ///
+ ///
+ /// The value of the setting -OR- the default value specified by
+ /// if it has not been set explicitly or
+ /// cannot be determined. By default, all references to existing
+ /// environment variables will be expanded to their corresponding values
+ /// within the value to be returned unless either the "No_Expand" or
+ /// "No_Expand_" environment variable is set [to
+ /// anything].
+ ///
+ internal static string GetSettingValue(
+ string name, /* in */
+ string @default /* in */
+ )
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // NOTE: If the special "No_SQLiteGetSettingValue" environment
+ // variable is set [to anything], this method will always
+ // return the default value.
+ //
+ if (Environment.GetEnvironmentVariable(
+ "No_SQLiteGetSettingValue") != null)
+ {
+ return @default;
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////
+
+ if (name == null)
+ return @default;
+
+ /////////////////////////////////////////////////////////////////////
+
+ #region Debug Build Only
+#if DEBUG
+ //
+ // NOTE: We are about to read a setting value from the environment
+ // or possibly from the XML configuration file; create or
+ // increment the appropriate statistic now.
+ //
+ DebugData.IncrementSettingReadCount(name, false);
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////
+
+ bool expand = true; /* SHARED: Environment -AND- XML config file. */
+
+ /////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ string value = null;
+
+ if (Environment.GetEnvironmentVariable("No_Expand") != null)
+ {
+ expand = false;
+ }
+ else if (Environment.GetEnvironmentVariable(
+ HelperMethods.StringFormat(CultureInfo.InvariantCulture,
+ "No_Expand_{0}", name)) != null)
+ {
+ expand = false;
+ }
+
+ value = Environment.GetEnvironmentVariable(name);
+
+ if (!String.IsNullOrEmpty(value))
+ {
+ if (expand)
+ value = Environment.ExpandEnvironmentVariables(value);
+
+ value = ReplaceEnvironmentVariableTokens(value);
+ }
+
+ if (value != null)
+ return value;
+
+ //
+ // NOTE: If the "No_SQLiteXmlConfigFile" environment variable is
+ // set [to anything], this method will NEVER read from the
+ // XML configuration file.
+ //
+ if (Environment.GetEnvironmentVariable(
+ "No_SQLiteXmlConfigFile") != null)
+ {
+ return @default;
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////
+
+ #region Debug Build Only
+#if DEBUG
+ //
+ // NOTE: We are about to read a setting value from the XML
+ // configuration file; create or increment the appropriate
+ // statistic now.
+ //
+ DebugData.IncrementSettingReadCount(name, true);
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////
+
+ return GetSettingValueViaXmlConfigFile(
+ GetCachedXmlConfigFileName(), name, @default, expand);
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ private static string ListToString(IList list)
+ {
+ if (list == null)
+ return null;
+
+ StringBuilder result = new StringBuilder();
+
+ foreach (string element in list)
+ {
+ if (element == null)
+ continue;
+
+ if (result.Length > 0)
+ result.Append(' ');
+
+ result.Append(element);
+ }
+
+ return result.ToString();
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private static int CheckForArchitecturesAndPlatforms(
+ string directory,
+ ref List matches
+ )
+ {
+ int result = 0;
+
+ if (matches == null)
+ matches = new List();
+
+ lock (staticSyncRoot)
+ {
+ if (!String.IsNullOrEmpty(directory) &&
+ (processorArchitecturePlatforms != null))
+ {
+ foreach (KeyValuePair pair
+ in processorArchitecturePlatforms)
+ {
+ if (Directory.Exists(MaybeCombinePath(directory, pair.Key)))
+ {
+ matches.Add(pair.Key);
+ result++;
+ }
+
+ string value = pair.Value;
+
+ if (value == null)
+ continue;
+
+ if (Directory.Exists(MaybeCombinePath(directory, value)))
+ {
+ matches.Add(value);
+ result++;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+
+ private static bool CheckAssemblyCodeBase(
+ Assembly assembly,
+ ref string fileName
+ )
+ {
+ try
+ {
+ if (assembly == null)
+ return false;
+
+ string codeBase = assembly.CodeBase;
+
+ if (String.IsNullOrEmpty(codeBase))
+ return false;
+
+ Uri uri = new Uri(codeBase);
+ string localFileName = uri.LocalPath;
+
+ if (!File.Exists(localFileName))
+ return false;
+
+ string directory = Path.GetDirectoryName(
+ localFileName); /* throw */
+
+ string xmlConfigFileName = MaybeCombinePath(
+ directory, XmlConfigFileName);
+
+ if (File.Exists(xmlConfigFileName))
+ {
+#if !NET_COMPACT_20 && TRACE_DETECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader found XML configuration file " +
+ "via code base for currently executing assembly: \"{0}\"",
+ xmlConfigFileName)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ fileName = localFileName;
+ return true;
+ }
+
+ List matches = null;
+
+ if (CheckForArchitecturesAndPlatforms(directory, ref matches) > 0)
+ {
+#if !NET_COMPACT_20 && TRACE_DETECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader found native sub-directories " +
+ "via code base for currently executing assembly: \"{0}\"",
+ ListToString(matches))); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ fileName = localFileName;
+ return true;
+ }
+
+ return false;
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader failed to check code base " +
+ "for currently executing assembly: {0}", e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+
+ return false;
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Resets the cached assembly directory value, thus forcing the next
+ /// call to method to rely
+ /// upon the method to fetch the
+ /// assembly directory.
+ ///
+ private static void ResetCachedAssemblyDirectory()
+ {
+ #region Debug Build Only
+#if DEBUG
+ DebugData.IncrementOtherCount("Method_ResetCachedAssemblyDirectory");
+#endif
+ #endregion
+
+ lock (staticSyncRoot)
+ {
+ cachedAssemblyDirectory = null;
+ noAssemblyDirectory = false;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the cached directory for the assembly currently
+ /// being executed, if available. If the cached assembly directory value
+ /// is not available, the method will
+ /// be used to obtain the assembly directory.
+ ///
+ ///
+ /// The directory for the assembly currently being executed -OR- null if
+ /// it cannot be determined.
+ ///
+ private static string GetCachedAssemblyDirectory()
+ {
+ #region Debug Build Only
+#if DEBUG
+ DebugData.IncrementOtherCount("Method_GetCachedAssemblyDirectory");
+#endif
+ #endregion
+
+ lock (staticSyncRoot)
+ {
+ if (cachedAssemblyDirectory != null)
+ return cachedAssemblyDirectory;
+
+ if (noAssemblyDirectory)
+ return null;
+ }
+
+ return GetAssemblyDirectory();
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the directory for the assembly currently being
+ /// executed.
+ ///
+ ///
+ /// The directory for the assembly currently being executed -OR- null if
+ /// it cannot be determined.
+ ///
+ private static string GetAssemblyDirectory()
+ {
+ #region Debug Build Only
+#if DEBUG
+ DebugData.IncrementOtherCount("Method_GetAssemblyDirectory");
+#endif
+ #endregion
+
+ try
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+
+ if (assembly == null)
+ {
+ lock (staticSyncRoot)
+ {
+ noAssemblyDirectory = true;
+ }
+
+ return null;
+ }
+
+ string fileName = null;
+
+#if PLATFORM_COMPACTFRAMEWORK
+ AssemblyName assemblyName = assembly.GetName();
+
+ if (assemblyName == null)
+ {
+ lock (staticSyncRoot)
+ {
+ noAssemblyDirectory = true;
+ }
+
+ return null;
+ }
+
+ fileName = assemblyName.CodeBase;
+#else
+ if (!CheckAssemblyCodeBase(assembly, ref fileName))
+ fileName = assembly.Location;
+#endif
+
+ if (String.IsNullOrEmpty(fileName))
+ {
+ lock (staticSyncRoot)
+ {
+ noAssemblyDirectory = true;
+ }
+
+ return null;
+ }
+
+ string directory = Path.GetDirectoryName(fileName);
+
+ if (String.IsNullOrEmpty(directory))
+ {
+ lock (staticSyncRoot)
+ {
+ noAssemblyDirectory = true;
+ }
+
+ return null;
+ }
+
+ lock (staticSyncRoot)
+ {
+ cachedAssemblyDirectory = directory;
+ }
+
+ return directory;
+ }
+#if !NET_COMPACT_20 && TRACE_SHARED
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_SHARED
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader failed to get directory " +
+ "for currently executing assembly: {0}", e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+
+ lock (staticSyncRoot)
+ {
+ noAssemblyDirectory = true;
+ }
+
+ return null;
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Optional Native SQLite Library Pre-Loading Code
+ //
+ // NOTE: If we are looking for the standard SQLite DLL ("sqlite3.dll"),
+ // the interop DLL ("SQLite.Interop.dll"), or we are running on the
+ // .NET Compact Framework, we should include this code (only if the
+ // feature has actually been enabled). This code would be totally
+ // redundant if this module has been bundled into the mixed-mode
+ // assembly.
+ //
+#if SQLITE_STANDARD || USE_INTEROP_DLL || PLATFORM_COMPACTFRAMEWORK
+
+ //
+ // NOTE: Only compile in the native library pre-load code if the feature
+ // has been enabled for this build.
+ //
+#if PRELOAD_NATIVE_LIBRARY
+ ///
+ /// The name of the environment variable containing the processor
+ /// architecture of the current process.
+ ///
+ private static readonly string PROCESSOR_ARCHITECTURE =
+ "PROCESSOR_ARCHITECTURE";
+
+ /////////////////////////////////////////////////////////////////////////
+
+ #region Private Data
+ ///
+ /// The native module file name for the native SQLite library or null.
+ ///
+ internal static string _SQLiteNativeModuleFileName = null;
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// The native module handle for the native SQLite library or the value
+ /// IntPtr.Zero.
+ ///
+ private static IntPtr _SQLiteNativeModuleHandle = IntPtr.Zero;
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines the base file name (without any directory information)
+ /// for the native SQLite library to be pre-loaded by this class.
+ ///
+ ///
+ /// The base file name for the native SQLite library to be pre-loaded by
+ /// this class -OR- null if its value cannot be determined.
+ ///
+ internal static string GetNativeLibraryFileNameOnly()
+ {
+ string fileNameOnly = GetSettingValue(
+ "PreLoadSQLite_LibraryFileNameOnly", null);
+
+ if (fileNameOnly != null)
+ return fileNameOnly;
+
+ return SQLITE_DLL; /* COMPAT */
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Searches for the native SQLite library in the directory containing
+ /// the assembly currently being executed as well as the base directory
+ /// for the current application domain.
+ ///
+ ///
+ /// Upon success, this parameter will be modified to refer to the base
+ /// directory containing the native SQLite library.
+ ///
+ ///
+ /// Upon success, this parameter will be modified to refer to the name
+ /// of the immediate directory (i.e. the offset from the base directory)
+ /// containing the native SQLite library.
+ ///
+ ///
+ /// Upon success, this parameter will be modified to non-zero only if
+ /// the base directory itself should be allowed for loading the native
+ /// library.
+ ///
+ ///
+ /// Non-zero (success) if the native SQLite library was found; otherwise,
+ /// zero (failure).
+ ///
+ private static bool SearchForDirectory(
+ ref string baseDirectory, /* out */
+ ref string processorArchitecture, /* out */
+ ref bool allowBaseDirectoryOnly /* out */
+ )
+ {
+ if (GetSettingValue(
+ "PreLoadSQLite_NoSearchForDirectory", null) != null)
+ {
+ return false; /* DISABLED */
+ }
+
+ //
+ // NOTE: Determine the base file name for the native SQLite library.
+ // If this is not known by this class, we cannot continue.
+ //
+ string fileNameOnly = GetNativeLibraryFileNameOnly();
+
+ if (fileNameOnly == null)
+ return false;
+
+ //
+ // NOTE: Build the list of base directories and processor/platform
+ // names. These lists will be used to help locate the native
+ // SQLite core library (or interop assembly) to pre-load into
+ // this process.
+ //
+ string[] directories = {
+ GetAssemblyDirectory(),
+#if !PLATFORM_COMPACTFRAMEWORK
+ AppDomain.CurrentDomain.BaseDirectory,
+#endif
+ };
+
+ string extraSubDirectory = null;
+
+ if ((GetSettingValue(
+ "PreLoadSQLite_AllowBaseDirectoryOnly", null) != null) ||
+ (HelperMethods.IsDotNetCore() && !HelperMethods.IsWindows()))
+ {
+ extraSubDirectory = String.Empty; /* .NET Core on POSIX */
+ }
+
+ string[] subDirectories = {
+ GetProcessorArchitecture(), /* e.g. "x86" */
+ GetPlatformName(null), /* e.g. "Win32" */
+ extraSubDirectory /* base directory only? */
+ };
+
+ foreach (string directory in directories)
+ {
+ if (directory == null)
+ continue;
+
+ foreach (string subDirectory in subDirectories)
+ {
+ if (subDirectory == null)
+ continue;
+
+ string fileName = FixUpDllFileName(MaybeCombinePath(
+ MaybeCombinePath(directory, subDirectory),
+ fileNameOnly));
+
+ //
+ // NOTE: If the SQLite DLL file exists, return success.
+ // Prior to returning, set the base directory and
+ // processor architecture to reflect the location
+ // where it was found.
+ //
+ if (File.Exists(fileName))
+ {
+#if !NET_COMPACT_20 && TRACE_DETECTION
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader found native file " +
+ "name \"{0}\", returning directory \"{1}\" and " +
+ "sub-directory \"{2}\"...", fileName, directory,
+ subDirectory)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ baseDirectory = directory;
+ processorArchitecture = subDirectory;
+ allowBaseDirectoryOnly = (subDirectory.Length == 0);
+
+ return true; /* FOUND */
+ }
+ }
+ }
+
+ return false; /* NOT FOUND */
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the base directory of the current application
+ /// domain.
+ ///
+ ///
+ /// The base directory for the current application domain -OR- null if it
+ /// cannot be determined.
+ ///
+ private static string GetBaseDirectory()
+ {
+ //
+ // NOTE: If the "PreLoadSQLite_BaseDirectory" environment variable
+ // is set, use it verbatim for the base directory.
+ //
+ string directory = GetSettingValue("PreLoadSQLite_BaseDirectory",
+ null);
+
+ if (directory != null)
+ return directory;
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // NOTE: If the "PreLoadSQLite_UseAssemblyDirectory" environment
+ // variable is set (to anything), then attempt to use the
+ // directory containing the currently executing assembly
+ // (i.e. System.Data.SQLite) intsead of the application
+ // domain base directory.
+ //
+ if (GetSettingValue(
+ "PreLoadSQLite_UseAssemblyDirectory", null) != null)
+ {
+ directory = GetAssemblyDirectory();
+
+ if (directory != null)
+ return directory;
+ }
+
+ //
+ // NOTE: Otherwise, fallback on using the base directory of the
+ // current application domain.
+ //
+ return AppDomain.CurrentDomain.BaseDirectory;
+#else
+ //
+ // NOTE: Otherwise, fallback on using the directory containing
+ // the currently executing assembly.
+ //
+ return GetAssemblyDirectory();
+#endif
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Determines if the dynamic link library file name requires a suffix
+ /// and adds it if necessary.
+ ///
+ ///
+ /// The original dynamic link library file name to inspect.
+ ///
+ ///
+ /// The dynamic link library file name, possibly modified to include an
+ /// extension.
+ ///
+ private static string FixUpDllFileName(
+ string fileName /* in */
+ )
+ {
+ if (!String.IsNullOrEmpty(fileName))
+ {
+ if (HelperMethods.IsWindows())
+ {
+ if (!fileName.EndsWith(DllFileExtension,
+ StringComparison.OrdinalIgnoreCase))
+ {
+ return fileName + DllFileExtension;
+ }
+ }
+ }
+
+ return fileName;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Queries and returns the processor architecture of the current
+ /// process.
+ ///
+ ///
+ /// The processor architecture of the current process -OR- null if it
+ /// cannot be determined.
+ ///
+ private static string GetProcessorArchitecture()
+ {
+ //
+ // NOTE: If the "PreLoadSQLite_ProcessorArchitecture" environment
+ // variable is set, use it verbatim for the current processor
+ // architecture.
+ //
+ string processorArchitecture = GetSettingValue(
+ "PreLoadSQLite_ProcessorArchitecture", null);
+
+ if (processorArchitecture != null)
+ return processorArchitecture;
+
+ //
+ // BUGBUG: Will this always be reliable?
+ //
+ processorArchitecture = GetSettingValue(PROCESSOR_ARCHITECTURE, null);
+
+ /////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ //
+ // HACK: Check for an "impossible" situation. If the pointer size
+ // is 32-bits, the processor architecture cannot be "AMD64".
+ // In that case, we are almost certainly hitting a bug in the
+ // operating system and/or Visual Studio that causes the
+ // PROCESSOR_ARCHITECTURE environment variable to contain the
+ // wrong value in some circumstances. Please refer to ticket
+ // [9ac9862611] for further information.
+ //
+ if ((IntPtr.Size == sizeof(int)) &&
+ String.Equals(processorArchitecture, "AMD64",
+ StringComparison.OrdinalIgnoreCase))
+ {
+#if !NET_COMPACT_20 && TRACE_DETECTION
+ //
+ // NOTE: When tracing is enabled, save the originally detected
+ // processor architecture before changing it.
+ //
+ string savedProcessorArchitecture = processorArchitecture;
+#endif
+
+ //
+ // NOTE: We know that operating systems that return "AMD64" as
+ // the processor architecture are actually a superset of
+ // the "x86" processor architecture; therefore, return
+ // "x86" when the pointer size is 32-bits.
+ //
+ processorArchitecture = "x86";
+
+#if !NET_COMPACT_20 && TRACE_DETECTION
+ try
+ {
+ //
+ // NOTE: Show that we hit a fairly unusual situation (i.e.
+ // the "wrong" processor architecture was detected).
+ //
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader detected {0}-bit pointer " +
+ "size with processor architecture \"{1}\", using " +
+ "processor architecture \"{2}\" instead...",
+ IntPtr.Size * 8 /* bits */, savedProcessorArchitecture,
+ processorArchitecture)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+#endif
+
+ /////////////////////////////////////////////////////////////////////
+
+ if (processorArchitecture == null)
+ {
+ //
+ // NOTE: Default to the processor architecture reported by the
+ // appropriate native operating system API, if any.
+ //
+ processorArchitecture = NativeLibraryHelper.GetMachine();
+
+ //
+ // NOTE: Upon failure, return empty string. This will prevent
+ // the calling method from considering this method call
+ // a "failure".
+ //
+ if (processorArchitecture == null)
+ processorArchitecture = String.Empty;
+ }
+
+ /////////////////////////////////////////////////////////////////////
+
+ return processorArchitecture;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Given the processor architecture, returns the name of the platform.
+ ///
+ ///
+ /// The processor architecture to be translated to a platform name.
+ ///
+ ///
+ /// The platform name for the specified processor architecture -OR- null
+ /// if it cannot be determined.
+ ///
+ private static string GetPlatformName(
+ string processorArchitecture /* in */
+ )
+ {
+ if (processorArchitecture == null)
+ processorArchitecture = GetProcessorArchitecture();
+
+ if (String.IsNullOrEmpty(processorArchitecture))
+ return null;
+
+ lock (staticSyncRoot)
+ {
+ if (processorArchitecturePlatforms == null)
+ return null;
+
+ string platformName;
+
+ if (processorArchitecturePlatforms.TryGetValue(
+ processorArchitecture, out platformName))
+ {
+ return platformName;
+ }
+ }
+
+ return null;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ ///
+ /// Attempts to load the native SQLite library based on the specified
+ /// directory and processor architecture.
+ ///
+ ///
+ /// The base directory to use, null for default (the base directory of
+ /// the current application domain). This directory should contain the
+ /// processor architecture specific sub-directories.
+ ///
+ ///
+ /// The requested processor architecture, null for default (the
+ /// processor architecture of the current process). This caller should
+ /// almost always specify null for this parameter.
+ ///
+ ///
+ /// Non-zero indicates that the native SQLite library can be loaded
+ /// from the base directory itself.
+ ///
+ ///
+ /// The candidate native module file name to load will be stored here,
+ /// if necessary.
+ ///
+ ///
+ /// The native module handle as returned by LoadLibrary will be stored
+ /// here, if necessary. This value will be IntPtr.Zero if the call to
+ /// LoadLibrary fails.
+ ///
+ ///
+ /// Non-zero if the native module was loaded successfully; otherwise,
+ /// zero.
+ ///
+ private static bool PreLoadSQLiteDll(
+ string baseDirectory, /* in */
+ string processorArchitecture, /* in */
+ bool allowBaseDirectoryOnly, /* in */
+ ref string nativeModuleFileName, /* out */
+ ref IntPtr nativeModuleHandle /* out */
+ )
+ {
+ //
+ // NOTE: If the specified base directory is null, use the default
+ // (i.e. attempt to automatically detect it).
+ //
+ if (baseDirectory == null)
+ baseDirectory = GetBaseDirectory();
+
+ //
+ // NOTE: If we failed to query the base directory, stop now.
+ //
+ if (baseDirectory == null)
+ return false;
+
+ //
+ // NOTE: Determine the base file name for the native SQLite library.
+ // If this is not known by this class, we cannot continue.
+ //
+ string fileNameOnly = GetNativeLibraryFileNameOnly();
+
+ if (fileNameOnly == null)
+ return false;
+
+ //
+ // NOTE: If the native SQLite library exists in the base directory
+ // itself, possibly stop now.
+ //
+ string fileName = FixUpDllFileName(MaybeCombinePath(baseDirectory,
+ fileNameOnly));
+
+ if (File.Exists(fileName))
+ {
+ //
+ // NOTE: If the caller is allowing the base directory itself
+ // to be used, also make sure a processor architecture
+ // was not specified; if either condition is false just
+ // stop now and return failure.
+ //
+ if (allowBaseDirectoryOnly &&
+ String.IsNullOrEmpty(processorArchitecture))
+ {
+ goto baseDirOnly;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ //
+ // NOTE: If the specified processor architecture is null, use the
+ // default.
+ //
+ if (processorArchitecture == null)
+ processorArchitecture = GetProcessorArchitecture();
+
+ //
+ // NOTE: If we failed to query the processor architecture, stop now.
+ //
+ if (processorArchitecture == null)
+ return false;
+
+ //
+ // NOTE: Build the full path and file name for the native SQLite
+ // library using the processor architecture name.
+ //
+ fileName = FixUpDllFileName(MaybeCombinePath(MaybeCombinePath(
+ baseDirectory, processorArchitecture), fileNameOnly));
+
+ //
+ // NOTE: If the file name based on the processor architecture name
+ // is not found, try using the associated platform name.
+ //
+ if (!File.Exists(fileName))
+ {
+ //
+ // NOTE: Attempt to translate the processor architecture to a
+ // platform name.
+ //
+ string platformName = GetPlatformName(processorArchitecture);
+
+ //
+ // NOTE: If we failed to translate the platform name, stop now.
+ //
+ if (platformName == null)
+ return false;
+
+ //
+ // NOTE: Build the full path and file name for the native SQLite
+ // library using the platform name.
+ //
+ fileName = FixUpDllFileName(MaybeCombinePath(MaybeCombinePath(
+ baseDirectory, platformName), fileNameOnly));
+
+ //
+ // NOTE: If the file does not exist, skip trying to load it.
+ //
+ if (!File.Exists(fileName))
+ return false;
+ }
+
+ baseDirOnly:
+
+ try
+ {
+#if !NET_COMPACT_20 && TRACE_PRELOAD
+ try
+ {
+ //
+ // NOTE: Show exactly where we are trying to load the native
+ // SQLite library from.
+ //
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader is trying to load native " +
+ "SQLite library \"{0}\"...", fileName)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+
+ //
+ // NOTE: Attempt to load the native library. This will either
+ // return a valid native module handle, return IntPtr.Zero,
+ // or throw an exception. This must use the appropriate
+ // P/Invoke method for the current operating system.
+ //
+ nativeModuleFileName = fileName;
+ nativeModuleHandle = NativeLibraryHelper.LoadLibrary(fileName);
+
+ return (nativeModuleHandle != IntPtr.Zero);
+ }
+#if !NET_COMPACT_20 && TRACE_PRELOAD
+ catch (Exception e)
+#else
+ catch (Exception)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_PRELOAD
+ try
+ {
+ //
+ // NOTE: First, grab the last Win32 error number.
+ //
+ int lastError = Marshal.GetLastWin32Error(); /* throw */
+
+ //
+ // NOTE: Show where we failed to load the native SQLite
+ // library from along with the Win32 error code and
+ // exception information.
+ //
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "Native library pre-loader failed to load native " +
+ "SQLite library \"{0}\" (getLastError = {1}): {2}",
+ fileName, lastError, e)); /* throw */
+ }
+ catch
+ {
+ // do nothing.
+ }
+#endif
+ }
+
+ return false;
+ }
+#endif
+#endif
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////
+
+#if PLATFORM_COMPACTFRAMEWORK
+ //
+ // NOTE: On the .NET Compact Framework, the native interop assembly must
+ // be used because it provides several workarounds to .NET Compact
+ // Framework limitations important for proper operation of the core
+ // System.Data.SQLite functionality (e.g. being able to bind
+ // parameters and handle column values of types Int64 and Double).
+ //
+ internal const string SQLITE_DLL = "SQLite.Interop.110.dll";
+#elif SQLITE_STANDARD
+ //
+ // NOTE: Otherwise, if the standard SQLite library is enabled, use it.
+ //
+ internal const string SQLITE_DLL = "sqlite3";
+#elif USE_INTEROP_DLL
+ //
+ // NOTE: Otherwise, if the native SQLite interop assembly is enabled,
+ // use it.
+ //
+ internal const string SQLITE_DLL = "SQLite.Interop.dll";
+#else
+ //
+ // NOTE: Finally, assume that the mixed-mode assembly is being used.
+ //
+ internal const string SQLITE_DLL = "System.Data.SQLite.dll";
+#endif
+
+ // This section uses interop calls that also fetch text length to optimize conversion.
+ // When using the standard dll, we can replace these calls with normal sqlite calls and
+ // do unoptimized conversions instead afterwards
+ #region interop added textlength calls
+
+#if !SQLITE_STANDARD
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_bind_parameter_name_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_database_name_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_database_name16_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_decltype_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_decltype16_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_name_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_name16_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_origin_name_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_origin_name16_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_table_name_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_table_name16_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_text_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_column_text16_interop(IntPtr stmt, int index, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_errmsg_interop(IntPtr db, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_prepare_interop(IntPtr db, IntPtr pSql, int nBytes, ref IntPtr stmt, ref IntPtr ptrRemain, ref int nRemain);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_table_column_metadata_interop(IntPtr db, byte[] dbName, byte[] tblName, byte[] colName, ref IntPtr ptrDataType, ref IntPtr ptrCollSeq, ref int notNull, ref int primaryKey, ref int autoInc, ref int dtLen, ref int csLen);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_value_text_interop(IntPtr p, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_value_text16_interop(IntPtr p, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern int sqlite3_malloc_size_interop(IntPtr p);
+
+#if INTEROP_LOG
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_config_log_interop();
+#endif
+#endif
+// !SQLITE_STANDARD
+
+ #endregion
+
+ // These functions add existing functionality on top of SQLite and require a little effort to
+ // get working when using the standard SQLite library.
+ #region interop added functionality
+
+#if !SQLITE_STANDARD
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr interop_libversion();
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr interop_sourceid();
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern int interop_compileoption_used(IntPtr zOptName);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr interop_compileoption_get(int N);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_close_interop(IntPtr db);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_create_function_interop(IntPtr db, byte[] strName, int nArgs, int nType, IntPtr pvUser, SQLiteCallback func, SQLiteCallback fstep, SQLiteFinalCallback ffinal, int needCollSeq);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_finalize_interop(IntPtr stmt);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_backup_finish_interop(IntPtr backup);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_blob_close_interop(IntPtr blob);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_open_interop(byte[] utf8Filename, byte[] vfsName, SQLiteOpenFlagsEnum flags, int extFuncs, ref IntPtr db);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_open16_interop(byte[] utf8Filename, byte[] vfsName, SQLiteOpenFlagsEnum flags, int extFuncs, ref IntPtr db);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_reset_interop(IntPtr stmt);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern int sqlite3_changes_interop(IntPtr db);
+#endif
+// !SQLITE_STANDARD
+
+ #endregion
+
+ // The standard api call equivalents of the above interop calls
+ #region standard versions of interop functions
+
+#if SQLITE_STANDARD
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_close(IntPtr db);
+
+#if !INTEROP_LEGACY_CLOSE
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_close_v2(IntPtr db); /* 3.7.14+ */
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_create_function(IntPtr db, byte[] strName, int nArgs, int nType, IntPtr pvUser, SQLiteCallback func, SQLiteCallback fstep, SQLiteFinalCallback ffinal);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_finalize(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_backup_finish(IntPtr backup);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_reset(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_bind_parameter_name(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_database_name(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_database_name16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_decltype(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_decltype16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_name(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_name16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_origin_name(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_origin_name16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_table_name(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_table_name16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_text(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_text16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_errmsg(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_prepare(IntPtr db, IntPtr pSql, int nBytes, ref IntPtr stmt, ref IntPtr ptrRemain);
+
+#if USE_PREPARE_V2
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_prepare_v2(IntPtr db, IntPtr pSql, int nBytes, ref IntPtr stmt, ref IntPtr ptrRemain);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_table_column_metadata(IntPtr db, byte[] dbName, byte[] tblName, byte[] colName, ref IntPtr ptrDataType, ref IntPtr ptrCollSeq, ref int notNull, ref int primaryKey, ref int autoInc);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_value_text(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_value_text16(IntPtr p);
+
+#endif
+ // SQLITE_STANDARD
+
+ #endregion
+
+ // These functions are custom and have no equivalent standard library method.
+ // All of them are "nice to haves" and not necessarily "need to haves".
+ #region no equivalent standard method
+
+#if !SQLITE_STANDARD
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_context_collseq_interop(IntPtr context, ref int type, ref int enc, ref int len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern int sqlite3_context_collcompare_interop(IntPtr context, byte[] p1, int p1len, byte[] p2, int p2len);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_cursor_rowid_interop(IntPtr stmt, int cursor, ref long rowid);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_index_column_info_interop(IntPtr db, byte[] catalog, byte[] IndexName, byte[] ColumnName, ref int sortOrder, ref int onError, ref IntPtr Collation, ref int colllen);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern int sqlite3_table_cursor_interop(IntPtr stmt, int db, int tableRootPage);
+
+#endif
+// !SQLITE_STANDARD
+
+ #endregion
+
+ // Standard API calls global across versions. There are a few instances of interop calls
+ // scattered in here, but they are only active when PLATFORM_COMPACTFRAMEWORK is declared.
+ #region standard sqlite api calls
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_libversion();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_libversion_number();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_sourceid();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_compileoption_used(IntPtr zOptName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_compileoption_get(int N);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_enable_shared_cache(
+ int enable);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_enable_load_extension(
+ IntPtr db, int enable);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_load_extension(
+ IntPtr db, byte[] fileName, byte[] procName, ref IntPtr pError);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_overload_function(IntPtr db, IntPtr zName, int nArgs);
+
+#if WINDOWS
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+ [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
+#endif
+ //
+ // NOTE: The "sqlite3_win32_set_directory" SQLite core library function is
+ // only supported on Windows.
+ //
+ internal static extern SQLiteErrorCode sqlite3_win32_set_directory(uint type, string value);
+
+#if !DEBUG // NOTE: Should be "WIN32HEAP && !MEMDEBUG"
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ //
+ // NOTE: The "sqlite3_win32_reset_heap" SQLite core library function is
+ // only supported on Windows when the Win32 native allocator is in
+ // use (i.e. by default, in "Release" builds of System.Data.SQLite
+ // only). By default, in "Debug" builds of System.Data.SQLite, the
+ // MEMDEBUG allocator is used.
+ //
+ internal static extern SQLiteErrorCode sqlite3_win32_reset_heap();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ //
+ // NOTE: The "sqlite3_win32_compact_heap" SQLite core library function is
+ // only supported on Windows when the Win32 native allocator is in
+ // use (i.e. by default, in "Release" builds of System.Data.SQLite
+ // only). By default, in "Debug" builds of System.Data.SQLite, the
+ // MEMDEBUG allocator is used.
+ //
+ internal static extern SQLiteErrorCode sqlite3_win32_compact_heap(ref uint largest);
+#endif
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_malloc(int n);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_malloc64(ulong n);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_realloc(IntPtr p, int n);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_realloc64(IntPtr p, ulong n);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern ulong sqlite3_msize(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_free(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_open_v2(byte[] utf8Filename, ref IntPtr db, SQLiteOpenFlagsEnum flags, byte[] vfsName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+ [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_open16(string fileName, ref IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_interrupt(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern long sqlite3_last_insert_rowid(IntPtr db);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_changes(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern long sqlite3_memory_used();
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern long sqlite3_memory_highwater(int resetFlag);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_shutdown();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_busy_timeout(IntPtr db, int ms);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_clear_bindings(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_bind_blob(IntPtr stmt, int index, Byte[] value, int nSize, IntPtr nTransient);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern SQLiteErrorCode sqlite3_bind_double(IntPtr stmt, int index, double value);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_bind_int(IntPtr stmt, int index, int value);
+
+ //
+ // NOTE: This really just calls "sqlite3_bind_int"; however, it has the
+ // correct type signature for an unsigned (32-bit) integer.
+ //
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_bind_uint(IntPtr stmt, int index, uint value);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern SQLiteErrorCode sqlite3_bind_int64(IntPtr stmt, int index, long value);
+#endif
+
+ //
+ // NOTE: This really just calls "sqlite3_bind_int64"; however, it has the
+ // correct type signature for an unsigned long (64-bit) integer.
+ //
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int64", CallingConvention = CallingConvention.Cdecl)]
+ internal static extern SQLiteErrorCode sqlite3_bind_uint64(IntPtr stmt, int index, ulong value);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_bind_null(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_bind_text(IntPtr stmt, int index, byte[] value, int nlen, IntPtr pvReserved);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_bind_parameter_count(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_bind_parameter_index(IntPtr stmt, byte[] strName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_column_count(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_step(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_stmt_readonly(IntPtr stmt); /* 3.7.4+ */
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double sqlite3_column_double(IntPtr stmt, int index);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_column_int(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern long sqlite3_column_int64(IntPtr stmt, int index);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_column_blob(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_column_bytes(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_column_bytes16(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern TypeAffinity sqlite3_column_type(IntPtr stmt, int index);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_create_collation(IntPtr db, byte[] strName, int nType, IntPtr pvUser, SQLiteCollation func);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_aggregate_count(IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_value_blob(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_value_bytes(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_value_bytes16(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern double sqlite3_value_double(IntPtr p);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_value_int(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern long sqlite3_value_int64(IntPtr p);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern TypeAffinity sqlite3_value_type(IntPtr p);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_blob(IntPtr context, byte[] value, int nSize, IntPtr pvReserved);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void sqlite3_result_double(IntPtr context, double value);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_error(IntPtr context, byte[] strErr, int nLen);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_error_code(IntPtr context, SQLiteErrorCode value);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_error_toobig(IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_error_nomem(IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_value(IntPtr context, IntPtr value);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_zeroblob(IntPtr context, int nLen);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_int(IntPtr context, int value);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void sqlite3_result_int64(IntPtr context, long value);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_null(IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_result_text(IntPtr context, byte[] value, int nLen, IntPtr pvReserved);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_aggregate_context(IntPtr context, int nBytes);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+ [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_bind_text16(IntPtr stmt, int index, string value, int nlen, IntPtr pvReserved);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+ [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
+#endif
+ internal static extern void sqlite3_result_error16(IntPtr context, string strName, int nLen);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+ [DllImport(SQLITE_DLL, CharSet = CharSet.Unicode)]
+#endif
+ internal static extern void sqlite3_result_text16(IntPtr context, string strName, int nLen, IntPtr pvReserved);
+
+#if INTEROP_CODEC || INTEROP_INCLUDE_SEE
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_key(IntPtr db, byte[] key, int keylen);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_rekey(IntPtr db, byte[] key, int keylen);
+#endif
+
+#if INTEROP_INCLUDE_ZIPVFS
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void zipvfsInit_v2();
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void zipvfsInit_v3(int regDflt);
+#endif
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_progress_handler(IntPtr db, int ops, SQLiteProgressCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_set_authorizer(IntPtr db, SQLiteAuthorizerCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_update_hook(IntPtr db, SQLiteUpdateCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_commit_hook(IntPtr db, SQLiteCommitCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_trace(IntPtr db, SQLiteTraceCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_trace_v2(IntPtr db, SQLiteTraceFlags mask, SQLiteTraceCallback2 func, IntPtr pvUser);
+
+ // Since sqlite3_config() takes a variable argument list, we have to overload declarations
+ // for all possible calls that we want to use.
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_config_none(SQLiteConfigOpsEnum op);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_config_int(SQLiteConfigOpsEnum op, int value);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_config")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_config_log(SQLiteConfigOpsEnum op, SQLiteLogCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_db_config_charptr(IntPtr db, SQLiteConfigDbOpsEnum op, IntPtr charPtr);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_db_config_int_refint(IntPtr db, SQLiteConfigDbOpsEnum op, int value, ref int result);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_config")]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_db_config_intptr_two_ints(IntPtr db, SQLiteConfigDbOpsEnum op, IntPtr ptr, int int0, int int1);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_db_status(IntPtr db, SQLiteStatusOpsEnum op, ref int current, ref int highwater, int resetFlag);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_rollback_hook(IntPtr db, SQLiteRollbackCallback func, IntPtr pvUser);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_db_handle(IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_db_release_memory(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_db_filename(IntPtr db, IntPtr dbName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_db_readonly(IntPtr db, IntPtr dbName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_filename", CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_db_filename")]
+#endif
+ internal static extern IntPtr sqlite3_db_filename_bytes(IntPtr db, byte[] dbName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_next_stmt(IntPtr db, IntPtr stmt);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_exec(IntPtr db, byte[] strSql, IntPtr pvCallback, IntPtr pvParam, ref IntPtr errMsg);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_release_memory(int nBytes);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_get_autocommit(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_extended_result_codes(IntPtr db, int onoff);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_errcode(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_extended_errcode(IntPtr db);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_errstr(SQLiteErrorCode rc); /* 3.7.15+ */
+
+ // Since sqlite3_log() takes a variable argument list, we have to overload declarations
+ // for all possible calls. For now, we are only exposing a single string, and
+ // depend on the caller to format the string.
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_log(SQLiteErrorCode iErrCode, byte[] zFormat);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_file_control(IntPtr db, byte[] zDbName, int op, IntPtr pArg);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_backup_init(IntPtr destDb, byte[] zDestName, IntPtr sourceDb, byte[] zSourceName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_backup_step(IntPtr backup, int nPage);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_backup_remaining(IntPtr backup);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_backup_pagecount(IntPtr backup);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_blob_close(IntPtr blob);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3_blob_bytes(IntPtr blob);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_blob_open(IntPtr db, byte[] dbName, byte[] tblName, byte[] colName, long rowId, int flags, ref IntPtr ptrBlob);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_blob_read(IntPtr blob, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, int count, int offset);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_blob_reopen(IntPtr blob, long rowId);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_blob_write(IntPtr blob, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, int count, int offset);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3_declare_vtab(IntPtr db, IntPtr zSQL);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_mprintf(IntPtr format, __arglist);
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ // SQLite API calls that are provided by "well-known" extensions that may be statically
+ // linked with the SQLite core native library currently in use.
+ #region extension sqlite api calls
+ #region virtual table
+#if INTEROP_VIRTUAL_TABLE
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern IntPtr sqlite3_create_disposable_module(IntPtr db, IntPtr name, ref sqlite3_module module, IntPtr pClientData, xDestroyModule xDestroy);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3_dispose_module(IntPtr pModule);
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region session extension
+#if INTEROP_SESSION_EXTENSION
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate int xSessionFilter(IntPtr context, IntPtr pTblName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate SQLiteChangeSetConflictResult xSessionConflict(IntPtr context, SQLiteChangeSetConflictType type, IntPtr iterator);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate SQLiteErrorCode xSessionInput(IntPtr context, IntPtr pData, ref int nData);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ internal delegate SQLiteErrorCode xSessionOutput(IntPtr context, IntPtr pData, int nData);
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_create(IntPtr db, byte[] dbName, ref IntPtr session);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3session_delete(IntPtr session);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3session_enable(IntPtr session, int enable);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3session_indirect(IntPtr session, int indirect);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_attach(IntPtr session, byte[] tblName);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3session_table_filter(IntPtr session, xSessionFilter xFilter, IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_changeset(IntPtr session, ref int nChangeSet, ref IntPtr pChangeSet);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_diff(IntPtr session, byte[] fromDbName, byte[] tblName, ref IntPtr errMsg);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_patchset(IntPtr session, ref int nPatchSet, ref IntPtr pPatchSet);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern int sqlite3session_isempty(IntPtr session);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_start(ref IntPtr iterator, int nChangeSet, IntPtr pChangeSet);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_start_v2(ref IntPtr iterator, int nChangeSet, IntPtr pChangeSet, SQLiteChangeSetStartFlags flags);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_next(IntPtr iterator);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_op(IntPtr iterator, ref IntPtr pTblName, ref int nColumns, ref SQLiteAuthorizerActionCode op, ref int bIndirect);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_pk(IntPtr iterator, ref IntPtr pPrimaryKeys, ref int nColumns);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_old(IntPtr iterator, int columnIndex, ref IntPtr pValue);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_new(IntPtr iterator, int columnIndex, ref IntPtr pValue);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_conflict(IntPtr iterator, int columnIndex, ref IntPtr pValue);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_fk_conflicts(IntPtr iterator, ref int conflicts);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_finalize(IntPtr iterator);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_invert(int nIn, IntPtr pIn, ref int nOut, ref IntPtr pOut);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_concat(int nA, IntPtr pA, int nB, IntPtr pB, ref int nOut, ref IntPtr pOut);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changegroup_new(ref IntPtr changeGroup);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changegroup_add(IntPtr changeGroup, int nData, IntPtr pData);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changegroup_output(IntPtr changeGroup, ref int nData, ref IntPtr pData);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern void sqlite3changegroup_delete(IntPtr changeGroup);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_apply(IntPtr db, int nChangeSet, IntPtr pChangeSet, xSessionFilter xFilter, xSessionConflict xConflict, IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_apply_strm(IntPtr db, xSessionInput xInput, IntPtr pIn, xSessionFilter xFilter, xSessionConflict xConflict, IntPtr context);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_concat_strm(xSessionInput xInputA, IntPtr pInA, xSessionInput xInputB, IntPtr pInB, xSessionOutput xOutput, IntPtr pOut);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_invert_strm(xSessionInput xInput, IntPtr pIn, xSessionOutput xOutput, IntPtr pOut);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_start_strm(ref IntPtr iterator, xSessionInput xInput, IntPtr pIn);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changeset_start_v2_strm(ref IntPtr iterator, xSessionInput xInput, IntPtr pIn, SQLiteChangeSetStartFlags flags);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_changeset_strm(IntPtr session, xSessionOutput xOutput, IntPtr pOut);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3session_patchset_strm(IntPtr session, xSessionOutput xOutput, IntPtr pOut);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changegroup_add_strm(IntPtr changeGroup, xSessionInput xInput, IntPtr pIn);
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [DllImport(SQLITE_DLL, CallingConvention = CallingConvention.Cdecl)]
+#else
+ [DllImport(SQLITE_DLL)]
+#endif
+ internal static extern SQLiteErrorCode sqlite3changegroup_output_strm(IntPtr changeGroup, xSessionOutput xOutput, IntPtr pOut);
+#endif
+ #endregion
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region sqlite interop api calls (.NET Compact Framework only)
+#if PLATFORM_COMPACTFRAMEWORK && !SQLITE_STANDARD
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_last_insert_rowid_interop(IntPtr db, ref long rowId);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_memory_used_interop(ref long bytes);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_memory_highwater_interop(int resetFlag, ref long bytes);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_bind_double_interop(IntPtr stmt, int index, ref double value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern SQLiteErrorCode sqlite3_bind_int64_interop(IntPtr stmt, int index, ref long value);
+
+ [DllImport(SQLITE_DLL, EntryPoint = "sqlite3_bind_int64_interop")]
+ internal static extern SQLiteErrorCode sqlite3_bind_uint64_interop(IntPtr stmt, int index, ref ulong value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_column_double_interop(IntPtr stmt, int index, ref double value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_column_int64_interop(IntPtr stmt, int index, ref long value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_value_double_interop(IntPtr p, ref double value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_value_int64_interop(IntPtr p, ref Int64 value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_result_double_interop(IntPtr context, ref double value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_result_int64_interop(IntPtr context, ref Int64 value);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern void sqlite3_msize_interop(IntPtr p, ref ulong size);
+
+ [DllImport(SQLITE_DLL)]
+ internal static extern IntPtr sqlite3_create_disposable_module_interop(
+ IntPtr db, IntPtr name, IntPtr pModule, int iVersion, xCreate xCreate,
+ xConnect xConnect, xBestIndex xBestIndex, xDisconnect xDisconnect,
+ xDestroy xDestroy, xOpen xOpen, xClose xClose, xFilter xFilter,
+ xNext xNext, xEof xEof, xColumn xColumn, xRowId xRowId, xUpdate xUpdate,
+ xBegin xBegin, xSync xSync, xCommit xCommit, xRollback xRollback,
+ xFindFunction xFindFunction, xRename xRename, xSavepoint xSavepoint,
+ xRelease xRelease, xRollbackTo xRollbackTo, IntPtr pClientData,
+ xDestroyModule xDestroyModule);
+#endif
+ // PLATFORM_COMPACTFRAMEWORK && !SQLITE_STANDARD
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Native Delegates
+#if INTEROP_VIRTUAL_TABLE
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xCreate(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xConnect(
+ IntPtr pDb,
+ IntPtr pAux,
+ int argc,
+ IntPtr argv,
+ ref IntPtr pVtab,
+ ref IntPtr pError
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xBestIndex(
+ IntPtr pVtab,
+ IntPtr pIndex
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xDisconnect(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xDestroy(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xOpen(
+ IntPtr pVtab,
+ ref IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xClose(
+ IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xFilter(
+ IntPtr pCursor,
+ int idxNum,
+ IntPtr idxStr,
+ int argc,
+ IntPtr argv
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xNext(
+ IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate int xEof(
+ IntPtr pCursor
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xColumn(
+ IntPtr pCursor,
+ IntPtr pContext,
+ int index
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xRowId(
+ IntPtr pCursor,
+ ref long rowId
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xUpdate(
+ IntPtr pVtab,
+ int argc,
+ IntPtr argv,
+ ref long rowId
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xBegin(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xSync(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xCommit(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xRollback(
+ IntPtr pVtab
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate int xFindFunction(
+ IntPtr pVtab,
+ int nArg,
+ IntPtr zName,
+ ref SQLiteCallback callback,
+ ref IntPtr pUserData
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xRename(
+ IntPtr pVtab,
+ IntPtr zNew
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xSavepoint(
+ IntPtr pVtab,
+ int iSavepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xRelease(
+ IntPtr pVtab,
+ int iSavepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate SQLiteErrorCode xRollbackTo(
+ IntPtr pVtab,
+ int iSavepoint
+ );
+
+ ///////////////////////////////////////////////////////////////////////////
+
+#if !PLATFORM_COMPACTFRAMEWORK
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+#endif
+ public delegate void xDestroyModule(IntPtr pClientData);
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region Native Structures
+#if INTEROP_VIRTUAL_TABLE
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_module
+ {
+ /* 0 */ public int iVersion;
+ /* 8 */ public xCreate xCreate;
+ /* 16 */ public xConnect xConnect;
+ /* 24 */ public xBestIndex xBestIndex;
+ /* 32 */ public xDisconnect xDisconnect;
+ /* 40 */ public xDestroy xDestroy;
+ /* 48 */ public xOpen xOpen;
+ /* 56 */ public xClose xClose;
+ /* 64 */ public xFilter xFilter;
+ /* 72 */ public xNext xNext;
+ /* 80 */ public xEof xEof;
+ /* 88 */ public xColumn xColumn;
+ /* 96 */ public xRowId xRowId;
+ /* 104 */ public xUpdate xUpdate;
+ /* 112 */ public xBegin xBegin;
+ /* 120 */ public xSync xSync;
+ /* 128 */ public xCommit xCommit;
+ /* 136 */ public xRollback xRollback;
+ /* 144 */ public xFindFunction xFindFunction;
+ /* 152 */ public xRename xRename;
+ /* The methods above are in version 1 of the sqlite3_module
+ * object. Those below are for version 2 and greater. */
+ /* 160 */ public xSavepoint xSavepoint;
+ /* 168 */ public xRelease xRelease;
+ /* 176 */ public xRollbackTo xRollbackTo;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_vtab
+ {
+ /* 0 */ public IntPtr pModule;
+ /* 8 */ public int nRef; /* NO LONGER USED */
+ /* 16 */ public IntPtr zErrMsg;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_vtab_cursor
+ {
+ /* 0 */ public IntPtr pVTab;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_index_constraint
+ {
+ public sqlite3_index_constraint(
+ SQLiteIndexConstraint constraint
+ )
+ : this()
+ {
+ if (constraint != null)
+ {
+ iColumn = constraint.iColumn;
+ op = constraint.op;
+ usable = constraint.usable;
+ iTermOffset = constraint.iTermOffset;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ /* 0 */ public int iColumn;
+ /* 4 */ public SQLiteIndexConstraintOp op;
+ /* 5 */ public byte usable;
+ /* 8 */ public int iTermOffset;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_index_orderby
+ {
+ public sqlite3_index_orderby(
+ SQLiteIndexOrderBy orderBy
+ )
+ : this()
+ {
+ if (orderBy != null)
+ {
+ iColumn = orderBy.iColumn;
+ desc = orderBy.desc;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ /* 0 */ public int iColumn; /* Column number */
+ /* 4 */ public byte desc; /* True for DESC. False for ASC. */
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_index_constraint_usage
+ {
+ public sqlite3_index_constraint_usage(
+ SQLiteIndexConstraintUsage constraintUsage
+ )
+ : this()
+ {
+ if (constraintUsage != null)
+ {
+ argvIndex = constraintUsage.argvIndex;
+ omit = constraintUsage.omit;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public int argvIndex; /* if >0, constraint is part of argv to xFilter */
+ public byte omit; /* Do not code a test for this constraint */
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct sqlite3_index_info
+ {
+ /* Inputs */
+ /* 0 */ public int nConstraint; /* Number of entries in aConstraint */
+ /* 8 */ public IntPtr aConstraint;
+ /* 16 */ public int nOrderBy; /* Number of entries in aOrderBy */
+ /* 24 */ public IntPtr aOrderBy;
+ /* Outputs */
+ /* 32 */ public IntPtr aConstraintUsage;
+ /* 40 */ public int idxNum; /* Number used to identify the index */
+ /* 48 */ public string idxStr; /* String, possibly obtained from sqlite3_malloc */
+ /* 56 */ public int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
+ /* 60 */ public int orderByConsumed; /* True if output is already ordered */
+ /* 64 */ public double estimatedCost; /* Estimated cost of using this index */
+ /* 72 */ public long estimatedRows; /* Estimated number of rows returned */
+ /* 80 */ public SQLiteIndexFlags idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ /* 88 */ public long colUsed; /* Input: Mask of columns used by statement */
+ }
+#endif
+ #endregion
+ }
+ #endregion
+
+ /////////////////////////////////////////////////////////////////////////////
+
+ #region .NET Compact Framework (only) CriticalHandle Class
+#if PLATFORM_COMPACTFRAMEWORK
+ internal abstract class CriticalHandle : IDisposable
+ {
+ private bool _isClosed;
+ protected IntPtr handle;
+
+ protected CriticalHandle(IntPtr invalidHandleValue)
+ {
+ handle = invalidHandleValue;
+ _isClosed = false;
+ }
+
+ ~CriticalHandle()
+ {
+ Dispose(false);
+ }
+
+ private void Cleanup()
+ {
+ if (!IsClosed)
+ {
+ this._isClosed = true;
+ if (!IsInvalid)
+ {
+ ReleaseHandle();
+ GC.SuppressFinalize(this);
+ }
+ }
+ }
+
+ public void Close()
+ {
+ Dispose(true);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ Cleanup();
+ }
+
+ protected abstract bool ReleaseHandle();
+
+ protected void SetHandle(IntPtr value)
+ {
+ handle = value;
+ }
+
+ public void SetHandleAsInvalid()
+ {
+ _isClosed = true;
+ GC.SuppressFinalize(this);
+ }
+
+ public bool IsClosed
+ {
+ get { return _isClosed; }
+ }
+
+ public abstract bool IsInvalid
+ {
+ get;
+ }
+
+ }
+#endif
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteConnectionHandle Class
+ // Handles the unmanaged database pointer, and provides finalization
+ // support for it.
+ internal sealed class SQLiteConnectionHandle : CriticalHandle
+ {
+#if SQLITE_STANDARD && !PLATFORM_COMPACTFRAMEWORK
+ internal delegate void CloseConnectionCallback(
+ SQLiteConnectionHandle hdl, IntPtr db);
+
+ internal static CloseConnectionCallback closeConnection =
+ SQLiteBase.CloseConnection;
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if PLATFORM_COMPACTFRAMEWORK
+ internal readonly object syncRoot = new object();
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private bool ownHandle;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public static implicit operator IntPtr(SQLiteConnectionHandle db)
+ {
+ if (db != null)
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (db.syncRoot)
+#endif
+ {
+ return db.handle;
+ }
+ }
+ return IntPtr.Zero;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ internal SQLiteConnectionHandle(IntPtr db, bool ownHandle)
+ : this(ownHandle)
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ this.ownHandle = ownHandle;
+ SetHandle(db);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteConnectionHandle(bool ownHandle)
+ : base(IntPtr.Zero)
+ {
+#if COUNT_HANDLE
+ if (ownHandle)
+ Interlocked.Increment(ref DebugData.connectionCount);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ protected override bool ReleaseHandle()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ if (!ownHandle) return true;
+ }
+
+ try
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ IntPtr localHandle = Interlocked.Exchange(
+ ref handle, IntPtr.Zero);
+
+#if SQLITE_STANDARD
+ if (localHandle != IntPtr.Zero)
+ closeConnection(this, localHandle);
+#else
+ if (localHandle != IntPtr.Zero)
+ SQLiteBase.CloseConnection(this, localHandle);
+#endif
+
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "CloseConnection: {0}", localHandle)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+#else
+ lock (syncRoot)
+ {
+ if (handle != IntPtr.Zero)
+ {
+ SQLiteBase.CloseConnection(this, handle);
+ SetHandle(IntPtr.Zero);
+ }
+ }
+#endif
+#if COUNT_HANDLE
+ Interlocked.Decrement(ref DebugData.connectionCount);
+#endif
+#if DEBUG
+ return true;
+#endif
+ }
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ catch (SQLiteException e)
+#else
+ catch (SQLiteException)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "CloseConnection: {0}, exception: {1}",
+ handle, e)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+ }
+ finally
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ SetHandleAsInvalid();
+ }
+ }
+#if DEBUG
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if COUNT_HANDLE
+ public int WasReleasedOk()
+ {
+ return Interlocked.Decrement(ref DebugData.connectionCount);
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public bool OwnHandle
+ {
+ get
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return ownHandle;
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public override bool IsInvalid
+ {
+ get
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return (handle == IntPtr.Zero);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if DEBUG
+ public override string ToString()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return handle.ToString();
+ }
+ }
+#endif
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteStatementHandle Class
+ // Provides finalization support for unmanaged SQLite statements.
+ internal sealed class SQLiteStatementHandle : CriticalHandle
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ internal readonly object syncRoot = new object();
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteConnectionHandle cnn;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public static implicit operator IntPtr(SQLiteStatementHandle stmt)
+ {
+ if (stmt != null)
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (stmt.syncRoot)
+#endif
+ {
+ return stmt.handle;
+ }
+ }
+ return IntPtr.Zero;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ internal SQLiteStatementHandle(SQLiteConnectionHandle cnn, IntPtr stmt)
+ : this()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ this.cnn = cnn;
+ SetHandle(stmt);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteStatementHandle()
+ : base(IntPtr.Zero)
+ {
+#if COUNT_HANDLE
+ Interlocked.Increment(ref DebugData.statementCount);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ protected override bool ReleaseHandle()
+ {
+ try
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ IntPtr localHandle = Interlocked.Exchange(
+ ref handle, IntPtr.Zero);
+
+ if (localHandle != IntPtr.Zero)
+ SQLiteBase.FinalizeStatement(cnn, localHandle);
+
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "FinalizeStatement: {0}", localHandle)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+#else
+ lock (syncRoot)
+ {
+ if (handle != IntPtr.Zero)
+ {
+ SQLiteBase.FinalizeStatement(cnn, handle);
+ SetHandle(IntPtr.Zero);
+ }
+ }
+#endif
+#if COUNT_HANDLE
+ Interlocked.Decrement(ref DebugData.statementCount);
+#endif
+#if DEBUG
+ return true;
+#endif
+ }
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ catch (SQLiteException e)
+#else
+ catch (SQLiteException)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "FinalizeStatement: {0}, exception: {1}",
+ handle, e)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+ }
+ finally
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ SetHandleAsInvalid();
+ }
+ }
+#if DEBUG
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if COUNT_HANDLE
+ public int WasReleasedOk()
+ {
+ return Interlocked.Decrement(ref DebugData.statementCount);
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public override bool IsInvalid
+ {
+ get
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return (handle == IntPtr.Zero);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if DEBUG
+ public override string ToString()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return handle.ToString();
+ }
+ }
+#endif
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteBackupHandle Class
+ // Provides finalization support for unmanaged SQLite backup objects.
+ internal sealed class SQLiteBackupHandle : CriticalHandle
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ internal readonly object syncRoot = new object();
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteConnectionHandle cnn;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public static implicit operator IntPtr(SQLiteBackupHandle backup)
+ {
+ if (backup != null)
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (backup.syncRoot)
+#endif
+ {
+ return backup.handle;
+ }
+ }
+ return IntPtr.Zero;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ internal SQLiteBackupHandle(SQLiteConnectionHandle cnn, IntPtr backup)
+ : this()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ this.cnn = cnn;
+ SetHandle(backup);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteBackupHandle()
+ : base(IntPtr.Zero)
+ {
+#if COUNT_HANDLE
+ Interlocked.Increment(ref DebugData.backupCount);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ protected override bool ReleaseHandle()
+ {
+ try
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ IntPtr localHandle = Interlocked.Exchange(
+ ref handle, IntPtr.Zero);
+
+ if (localHandle != IntPtr.Zero)
+ SQLiteBase.FinishBackup(cnn, localHandle);
+
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "FinishBackup: {0}", localHandle)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+#else
+ lock (syncRoot)
+ {
+ if (handle != IntPtr.Zero)
+ {
+ SQLiteBase.FinishBackup(cnn, handle);
+ SetHandle(IntPtr.Zero);
+ }
+ }
+#endif
+#if COUNT_HANDLE
+ Interlocked.Decrement(ref DebugData.backupCount);
+#endif
+#if DEBUG
+ return true;
+#endif
+ }
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ catch (SQLiteException e)
+#else
+ catch (SQLiteException)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "FinishBackup: {0}, exception: {1}",
+ handle, e)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+ }
+ finally
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ SetHandleAsInvalid();
+ }
+ }
+#if DEBUG
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if COUNT_HANDLE
+ public int WasReleasedOk()
+ {
+ return Interlocked.Decrement(ref DebugData.backupCount);
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public override bool IsInvalid
+ {
+ get
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return (handle == IntPtr.Zero);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if DEBUG
+ public override string ToString()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return handle.ToString();
+ }
+ }
+#endif
+ }
+ #endregion
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ #region SQLiteBlobHandle Class
+ // Provides finalization support for unmanaged SQLite blob objects.
+ internal sealed class SQLiteBlobHandle : CriticalHandle
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ internal readonly object syncRoot = new object();
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteConnectionHandle cnn;
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public static implicit operator IntPtr(SQLiteBlobHandle blob)
+ {
+ if (blob != null)
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (blob.syncRoot)
+#endif
+ {
+ return blob.handle;
+ }
+ }
+ return IntPtr.Zero;
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ internal SQLiteBlobHandle(SQLiteConnectionHandle cnn, IntPtr blob)
+ : this()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ this.cnn = cnn;
+ SetHandle(blob);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ private SQLiteBlobHandle()
+ : base(IntPtr.Zero)
+ {
+#if COUNT_HANDLE
+ Interlocked.Increment(ref DebugData.blobCount);
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+ protected override bool ReleaseHandle()
+ {
+ try
+ {
+#if !PLATFORM_COMPACTFRAMEWORK
+ IntPtr localHandle = Interlocked.Exchange(
+ ref handle, IntPtr.Zero);
+
+ if (localHandle != IntPtr.Zero)
+ SQLiteBase.CloseBlob(cnn, localHandle);
+
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "CloseBlob: {0}", localHandle)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+#else
+ lock (syncRoot)
+ {
+ if (handle != IntPtr.Zero)
+ {
+ SQLiteBase.CloseBlob(cnn, handle);
+ SetHandle(IntPtr.Zero);
+ }
+ }
+#endif
+#if COUNT_HANDLE
+ Interlocked.Decrement(ref DebugData.blobCount);
+#endif
+#if DEBUG
+ return true;
+#endif
+ }
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ catch (SQLiteException e)
+#else
+ catch (SQLiteException)
+#endif
+ {
+#if !NET_COMPACT_20 && TRACE_HANDLE
+ try
+ {
+ Trace.WriteLine(HelperMethods.StringFormat(
+ CultureInfo.CurrentCulture,
+ "CloseBlob: {0}, exception: {1}",
+ handle, e)); /* throw */
+ }
+ catch
+ {
+ }
+#endif
+ }
+ finally
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ SetHandleAsInvalid();
+ }
+ }
+#if DEBUG
+ return false;
+#else
+ return true;
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if COUNT_HANDLE
+ public int WasReleasedOk()
+ {
+ return Interlocked.Decrement(ref DebugData.blobCount);
+ }
+#endif
+
+ ///////////////////////////////////////////////////////////////////////
+
+ public override bool IsInvalid
+ {
+ get
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return (handle == IntPtr.Zero);
+ }
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////
+
+#if DEBUG
+ public override string ToString()
+ {
+#if PLATFORM_COMPACTFRAMEWORK
+ lock (syncRoot)
+#endif
+ {
+ return handle.ToString();
+ }
+ }
+#endif
+ }
+ #endregion
+}
diff --git a/Native.Csharp/App/Core/LibExport.cs b/Native.Csharp/App/Core/LibExport.cs
index c21da702..920b7491 100644
--- a/Native.Csharp/App/Core/LibExport.cs
+++ b/Native.Csharp/App/Core/LibExport.cs
@@ -15,13 +15,15 @@
using Native.Csharp.App.Interface;
using Native.Csharp.Sdk.Cqp;
using Native.Csharp.Sdk.Cqp.Enum;
-using Native.Csharp.Tool;
+using Native.Csharp.Sdk.Cqp.Other;
using Native.Csharp.Repair;
namespace Native.Csharp.App.Core
{
public class LibExport
{
+ private static Encoding _defaultEncoding = null;
+
#region --构造函数--
///
/// 静态构造函数, 注册依赖注入回调
@@ -29,6 +31,8 @@ public class LibExport
///
static LibExport ()
{
+ _defaultEncoding = Encoding.GetEncoding ("GB18030");
+
// 初始化 Costura
CosturaUtility.Initialize ();
@@ -334,7 +338,7 @@ private static int EventPrivateMsg (int subType, int msgId, long fromQQ, IntPtr
PrivateMessageEventArgs args = new PrivateMessageEventArgs ();
args.MsgId = msgId;
args.FromQQ = fromQQ;
- args.Msg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.Msg = msg.ToString (_defaultEncoding);
args.Handled = false;
if (subType == 11) // 来自好友
@@ -368,7 +372,7 @@ private static int EventGroupMsg (int subType, int msgId, long fromGroup, long f
args.MsgId = msgId;
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
- args.Msg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.Msg = msg.ToString (_defaultEncoding);
args.FromAnonymous = null;
args.IsAnonymousMsg = false;
args.Handled = false;
@@ -398,7 +402,7 @@ private static int EventDiscussMsg (int subType, int msgId, long fromDiscuss, lo
args.MsgId = msgId;
args.FromDiscuss = fromDiscuss;
args.FromQQ = fromQQ;
- args.Msg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.Msg = msg.ToString (_defaultEncoding);
args.Handled = false;
if (subType == 1) // 讨论组消息
@@ -417,7 +421,7 @@ private static int EventDiscussMsg (int subType, int msgId, long fromDiscuss, lo
private static int EventGroupUpload (int subType, int sendTime, long fromGroup, long fromQQ, string file)
{
FileUploadMessageEventArgs args = new FileUploadMessageEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
args.File = Common.CqApi.GetFile (file);
@@ -429,7 +433,7 @@ private static int EventGroupUpload (int subType, int sendTime, long fromGroup,
private static int EventSystemGroupAdmin (int subType, int sendTime, long fromGroup, long beingOperateQQ)
{
GroupManageAlterEventArgs args = new GroupManageAlterEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.BeingOperateQQ = beingOperateQQ;
args.Handled = false;
@@ -454,7 +458,7 @@ private static int EventSystemGroupAdmin (int subType, int sendTime, long fromGr
private static int EventSystemGroupMemberDecrease (int subType, int sendTime, long fromGroup, long fromQQ, long beingOperateQQ)
{
GroupMemberAlterEventArgs args = new GroupMemberAlterEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
args.BeingOperateQQ = beingOperateQQ;
@@ -481,7 +485,7 @@ private static int EventSystemGroupMemberDecrease (int subType, int sendTime, lo
private static int EventSystemGroupMemberIncrease (int subType, int sendTime, long fromGroup, long fromQQ, long beingOperateQQ)
{
GroupMemberAlterEventArgs args = new GroupMemberAlterEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
args.BeingOperateQQ = beingOperateQQ;
@@ -507,7 +511,7 @@ private static int EventSystemGroupMemberIncrease (int subType, int sendTime, lo
private static int EventFriendAdd (int subType, int sendTime, long fromQQ)
{
FriendIncreaseEventArgs args = new FriendIncreaseEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromQQ = fromQQ;
args.Handled = false;
@@ -527,9 +531,9 @@ private static int EventFriendAdd (int subType, int sendTime, long fromQQ)
private static int EventRequestAddFriend (int subType, int sendTime, long fromQQ, IntPtr msg, string responseFlag)
{
FriendAddRequestEventArgs args = new FriendAddRequestEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromQQ = fromQQ;
- args.AppendMsg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.AppendMsg = msg.ToString (_defaultEncoding);
args.Tag = responseFlag;
args.Handled = false;
@@ -549,10 +553,10 @@ private static int EventRequestAddFriend (int subType, int sendTime, long fromQQ
private static int EventRequestAddGroup (int subType, int sendTime, long fromGroup, long fromQQ, IntPtr msg, string responseFlag)
{
GroupAddRequestEventArgs args = new GroupAddRequestEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
- args.AppendMsg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.AppendMsg = msg.ToString (_defaultEncoding);
args.Tag = responseFlag;
args.Handled = false;
diff --git a/Native.Csharp/App/Core/LibExport.tt b/Native.Csharp/App/Core/LibExport.tt
index c075351f..5d0d7610 100644
--- a/Native.Csharp/App/Core/LibExport.tt
+++ b/Native.Csharp/App/Core/LibExport.tt
@@ -21,13 +21,15 @@ using Native.Csharp.App.Model;
using Native.Csharp.App.Interface;
using Native.Csharp.Sdk.Cqp;
using Native.Csharp.Sdk.Cqp.Enum;
-using Native.Csharp.Tool;
+using Native.Csharp.Sdk.Cqp.Other;
using Native.Csharp.Repair;
namespace Native.Csharp.App.Core
{
public class LibExport
{
+ private static Encoding _defaultEncoding = null;
+
#region --构造函数--
///
/// 静态构造函数, 注册依赖注入回调
@@ -35,6 +37,8 @@ namespace Native.Csharp.App.Core
///
static LibExport ()
{
+ _defaultEncoding = Encoding.GetEncoding ("GB18030");
+
// 初始化 Costura
CosturaUtility.Initialize ();
@@ -340,7 +344,7 @@ namespace Native.Csharp.App.Core
PrivateMessageEventArgs args = new PrivateMessageEventArgs ();
args.MsgId = msgId;
args.FromQQ = fromQQ;
- args.Msg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.Msg = msg.ToString (_defaultEncoding);
args.Handled = false;
if (subType == 11) // 来自好友
@@ -374,7 +378,7 @@ namespace Native.Csharp.App.Core
args.MsgId = msgId;
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
- args.Msg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.Msg = msg.ToString (_defaultEncoding);
args.FromAnonymous = null;
args.IsAnonymousMsg = false;
args.Handled = false;
@@ -404,7 +408,7 @@ namespace Native.Csharp.App.Core
args.MsgId = msgId;
args.FromDiscuss = fromDiscuss;
args.FromQQ = fromQQ;
- args.Msg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.Msg = msg.ToString (_defaultEncoding);
args.Handled = false;
if (subType == 1) // 讨论组消息
@@ -423,7 +427,7 @@ namespace Native.Csharp.App.Core
private static int EventGroupUpload (int subType, int sendTime, long fromGroup, long fromQQ, string file)
{
FileUploadMessageEventArgs args = new FileUploadMessageEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
args.File = Common.CqApi.GetFile (file);
@@ -435,7 +439,7 @@ namespace Native.Csharp.App.Core
private static int EventSystemGroupAdmin (int subType, int sendTime, long fromGroup, long beingOperateQQ)
{
GroupManageAlterEventArgs args = new GroupManageAlterEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.BeingOperateQQ = beingOperateQQ;
args.Handled = false;
@@ -460,7 +464,7 @@ namespace Native.Csharp.App.Core
private static int EventSystemGroupMemberDecrease (int subType, int sendTime, long fromGroup, long fromQQ, long beingOperateQQ)
{
GroupMemberAlterEventArgs args = new GroupMemberAlterEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
args.BeingOperateQQ = beingOperateQQ;
@@ -487,7 +491,7 @@ namespace Native.Csharp.App.Core
private static int EventSystemGroupMemberIncrease (int subType, int sendTime, long fromGroup, long fromQQ, long beingOperateQQ)
{
GroupMemberAlterEventArgs args = new GroupMemberAlterEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
args.BeingOperateQQ = beingOperateQQ;
@@ -513,7 +517,7 @@ namespace Native.Csharp.App.Core
private static int EventFriendAdd (int subType, int sendTime, long fromQQ)
{
FriendIncreaseEventArgs args = new FriendIncreaseEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromQQ = fromQQ;
args.Handled = false;
@@ -533,9 +537,9 @@ namespace Native.Csharp.App.Core
private static int EventRequestAddFriend (int subType, int sendTime, long fromQQ, IntPtr msg, string responseFlag)
{
FriendAddRequestEventArgs args = new FriendAddRequestEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromQQ = fromQQ;
- args.AppendMsg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.AppendMsg = msg.ToString (_defaultEncoding);
args.Tag = responseFlag;
args.Handled = false;
@@ -555,10 +559,10 @@ namespace Native.Csharp.App.Core
private static int EventRequestAddGroup (int subType, int sendTime, long fromGroup, long fromQQ, IntPtr msg, string responseFlag)
{
GroupAddRequestEventArgs args = new GroupAddRequestEventArgs ();
- args.SendTime = NativeConvert.FotmatUnixTime (sendTime.ToString ());
+ args.SendTime = sendTime.ToDateTime ();
args.FromGroup = fromGroup;
args.FromQQ = fromQQ;
- args.AppendMsg = NativeConvert.ToPtrString (msg, Encoding.GetEncoding ("GB18030"));
+ args.AppendMsg = msg.ToString (_defaultEncoding);
args.Tag = responseFlag;
args.Handled = false;
diff --git a/Native.Csharp/Native.Csharp.csproj b/Native.Csharp/Native.Csharp.csproj
index 312cf64f..5d96cecf 100644
--- a/Native.Csharp/Native.Csharp.csproj
+++ b/Native.Csharp/Native.Csharp.csproj
@@ -129,10 +129,6 @@
{797eaebc-4d5b-4eef-87f4-a508fda2cb6a}
Native.Csharp.Sdk
-
- {9db94cbf-6843-4ea3-9241-769124416fe9}
- Native.Csharp.Tool
-
diff --git a/Native.Csharp/Properties/AssemblyInfo.cs b/Native.Csharp/Properties/AssemblyInfo.cs
index b622e213..fc50a463 100644
--- a/Native.Csharp/Properties/AssemblyInfo.cs
+++ b/Native.Csharp/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion ("3.0.6.0525")]
-[assembly: AssemblyFileVersion ("3.0.6.0525")]
+[assembly: AssemblyVersion ("3.0.7.0607")]
+[assembly: AssemblyFileVersion ("3.0.7.0607")]
diff --git a/README.md b/README.md
index edb36fe9..c689bfde 100644
--- a/README.md
+++ b/README.md
@@ -43,6 +43,18 @@
> 4. ~~对于接收消息时, 颜文字表情, 特殊符号乱码, 当前正在寻找转换方式~~ (已修复)
## Native.SDK 更新日志
+> 2019年06月07日 版本: V3.0.7.0607
+
+ 由于 酷Q 停止对 Windows XP/Vista 系统的支持, 所以 Native.SDK 将停止继续使用 .Net 4.0
+ 并将此版本作为最终发布版归档处理, 下个版本开始仅对 .Net 4.5+ 更新
+
+ 1. 修复 悬浮窗数据转换错误 (由 Pack -> BinaryWriter)
+ 2. 优化 部分 Api 接口的数据处理效率 (由 UnPack -> BinaryReader)
+ 3. 优化 分离 Native.Csharp.Tool 项目, 使 SDK 更轻量
+ 4. 优化 调整 Native.Csharp.Tool 项目结构, 每个模块为一个根文件夹. 排除即可在编译时移除功能
+ 5. 优化 新增 HttpTool (位于 Native.Csharp.Tool.Http)
+ 6. 新增 SQLite 操作类 (不包含EF, 需要可自行添加), 完全移植自 System.Data.SQLite (.Net 4.0)
+
> 2019年05月25日 版本: V3.0.6.0525
1. 修复 HttpWebClient 类在请求 Internet 资源时响应重定向的部分代码错误