Decompiled source of AsyncLoggers v1.6.2

BepInEx/patchers/AsyncLoggers.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AsyncLoggers.BepInExListeners;
using AsyncLoggers.Cecil;
using AsyncLoggers.DBAPI;
using AsyncLoggers.Patches;
using AsyncLoggers.StaticContexts;
using AsyncLoggers.Wrappers;
using AsyncLoggers.Wrappers.Unity;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DisruptorUnity3d;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Mono.Collections.Generic;
using SQLite;
using Unity.Jobs;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("BepInEx")]
[assembly: IgnoresAccessChecksTo("UnityEngine.CoreModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine")]
[assembly: AssemblyCompany("AsyncLoggers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.6.2.0")]
[assembly: AssemblyInformationalVersion("1.6.2+b63427043efbe65e65144bef8b774d02e51a67a7")]
[assembly: AssemblyProduct("Async Loggers")]
[assembly: AssemblyTitle("AsyncLoggers")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.6.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SQLite
{
	public class SQLiteException : Exception
	{
		public SQLite3.Result Result { get; private set; }

		protected SQLiteException(SQLite3.Result r, string message)
			: base(message)
		{
			Result = r;
		}

		public static SQLiteException New(SQLite3.Result r, string message)
		{
			return new SQLiteException(r, message);
		}
	}
	public class NotNullConstraintViolationException : SQLiteException
	{
		public IEnumerable<TableMapping.Column> Columns { get; protected set; }

		protected NotNullConstraintViolationException(SQLite3.Result r, string message)
			: this(r, message, null, null)
		{
		}

		protected NotNullConstraintViolationException(SQLite3.Result r, string message, TableMapping mapping, object obj)
			: base(r, message)
		{
			if (mapping != null && obj != null)
			{
				Columns = mapping.Columns.Where((TableMapping.Column c) => !c.IsNullable && c.GetValue(obj) == null);
			}
		}

		public new static NotNullConstraintViolationException New(SQLite3.Result r, string message)
		{
			return new NotNullConstraintViolationException(r, message);
		}

		public static NotNullConstraintViolationException New(SQLite3.Result r, string message, TableMapping mapping, object obj)
		{
			return new NotNullConstraintViolationException(r, message, mapping, obj);
		}

		public static NotNullConstraintViolationException New(SQLiteException exception, TableMapping mapping, object obj)
		{
			return new NotNullConstraintViolationException(exception.Result, exception.Message, mapping, obj);
		}
	}
	[Flags]
	public enum SQLiteOpenFlags
	{
		ReadOnly = 1,
		ReadWrite = 2,
		Create = 4,
		NoMutex = 0x8000,
		FullMutex = 0x10000,
		SharedCache = 0x20000,
		PrivateCache = 0x40000,
		ProtectionComplete = 0x100000,
		ProtectionCompleteUnlessOpen = 0x200000,
		ProtectionCompleteUntilFirstUserAuthentication = 0x300000,
		ProtectionNone = 0x400000
	}
	[Flags]
	public enum CreateFlags
	{
		None = 0,
		ImplicitPK = 1,
		ImplicitIndex = 2,
		AllImplicit = 3,
		AutoIncPK = 4,
		FullTextSearch3 = 0x100,
		FullTextSearch4 = 0x200
	}
	public interface ISQLiteConnection
	{
		IntPtr Handle { get; }

		string DatabasePath { get; }

		int LibVersionNumber { get; }

		bool TimeExecution { get; set; }

		bool Trace { get; set; }

		Action<string> Tracer { get; set; }

		bool StoreDateTimeAsTicks { get; }

		bool StoreTimeSpanAsTicks { get; }

		string DateTimeStringFormat { get; }

		TimeSpan BusyTimeout { get; set; }

		IEnumerable<TableMapping> TableMappings { get; }

		bool IsInTransaction { get; }

		event EventHandler<NotifyTableChangedEventArgs> TableChanged;

		void Backup(string destinationDatabasePath, string databaseName = "main");

		void BeginTransaction();

		void Close();

		void Commit();

		SQLiteCommand CreateCommand(string cmdText, params object[] ps);

		SQLiteCommand CreateCommand(string cmdText, Dictionary<string, object> args);

		int CreateIndex(string indexName, string tableName, string[] columnNames, bool unique = false);

		int CreateIndex(string indexName, string tableName, string columnName, bool unique = false);

		int CreateIndex(string tableName, string columnName, bool unique = false);

		int CreateIndex(string tableName, string[] columnNames, bool unique = false);

		int CreateIndex<T>(Expression<Func<T, object>> property, bool unique = false);

		CreateTableResult CreateTable<T>(CreateFlags createFlags = CreateFlags.None);

		CreateTableResult CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None);

		CreateTablesResult CreateTables<T, T2>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new();

		CreateTablesResult CreateTables<T, T2, T3>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new() where T3 : new();

		CreateTablesResult CreateTables<T, T2, T3, T4>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new() where T3 : new() where T4 : new();

		CreateTablesResult CreateTables<T, T2, T3, T4, T5>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new() where T3 : new() where T4 : new() where T5 : new();

		CreateTablesResult CreateTables(CreateFlags createFlags = CreateFlags.None, params Type[] types);

		IEnumerable<T> DeferredQuery<T>(string query, params object[] args) where T : new();

		IEnumerable<object> DeferredQuery(TableMapping map, string query, params object[] args);

		int Delete(object objectToDelete);

		int Delete<T>(object primaryKey);

		int Delete(object primaryKey, TableMapping map);

		int DeleteAll<T>();

		int DeleteAll(TableMapping map);

		void Dispose();

		int DropTable<T>();

		int DropTable(TableMapping map);

		void EnableLoadExtension(bool enabled);

		void EnableWriteAheadLogging();

		int Execute(string query, params object[] args);

		T ExecuteScalar<T>(string query, params object[] args);

		T Find<T>(object pk) where T : new();

		object Find(object pk, TableMapping map);

		T Find<T>(Expression<Func<T, bool>> predicate) where T : new();

		T FindWithQuery<T>(string query, params object[] args) where T : new();

		object FindWithQuery(TableMapping map, string query, params object[] args);

		T Get<T>(object pk) where T : new();

		object Get(object pk, TableMapping map);

		T Get<T>(Expression<Func<T, bool>> predicate) where T : new();

		TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None);

		TableMapping GetMapping<T>(CreateFlags createFlags = CreateFlags.None);

		List<SQLiteConnection.ColumnInfo> GetTableInfo(string tableName);

		int Insert(object obj);

		int Insert(object obj, Type objType);

		int Insert(object obj, string extra);

		int Insert(object obj, string extra, Type objType);

		int InsertAll(IEnumerable objects, bool runInTransaction = true);

		int InsertAll(IEnumerable objects, string extra, bool runInTransaction = true);

		int InsertAll(IEnumerable objects, Type objType, bool runInTransaction = true);

		int InsertOrReplace(object obj);

		int InsertOrReplace(object obj, Type objType);

		List<T> Query<T>(string query, params object[] args) where T : new();

		List<object> Query(TableMapping map, string query, params object[] args);

		List<T> QueryScalars<T>(string query, params object[] args);

		void Release(string savepoint);

		void Rollback();

		void RollbackTo(string savepoint);

		void RunInTransaction(Action action);

		string SaveTransactionPoint();

		TableQuery<T> Table<T>() where T : new();

		int Update(object obj);

		int Update(object obj, Type objType);

		int UpdateAll(IEnumerable objects, bool runInTransaction = true);
	}
	[Preserve(AllMembers = true)]
	public class SQLiteConnection : IDisposable, ISQLiteConnection
	{
		private struct IndexedColumn
		{
			public int Order;

			public string ColumnName;
		}

		private struct IndexInfo
		{
			public string IndexName;

			public string TableName;

			public bool Unique;

			public List<IndexedColumn> Columns;
		}

		[Preserve(AllMembers = true)]
		public class ColumnInfo
		{
			[Column("name")]
			public string Name { get; set; }

			public int notnull { get; set; }

			public override string ToString()
			{
				return Name;
			}
		}

		private bool _open;

		private TimeSpan _busyTimeout;

		private static readonly Dictionary<string, TableMapping> _mappings = new Dictionary<string, TableMapping>();

		private Stopwatch _sw;

		private long _elapsedMilliseconds;

		private int _transactionDepth;

		private Random _rand = new Random();

		private static readonly IntPtr NullHandle = default(IntPtr);

		private static readonly IntPtr NullBackupHandle = default(IntPtr);

		private readonly Dictionary<Tuple<string, string>, PreparedSqlLiteInsertCommand> _insertCommandMap = new Dictionary<Tuple<string, string>, PreparedSqlLiteInsertCommand>();

		public IntPtr Handle { get; private set; }

		public string DatabasePath { get; private set; }

		public int LibVersionNumber { get; private set; }

		public bool TimeExecution { get; set; }

		public bool Trace { get; set; }

		public Action<string> Tracer { get; set; }

		public bool StoreDateTimeAsTicks { get; private set; }

		public bool StoreTimeSpanAsTicks { get; private set; }

		public string DateTimeStringFormat { get; private set; }

		internal DateTimeStyles DateTimeStyle { get; private set; }

		public TimeSpan BusyTimeout
		{
			get
			{
				return _busyTimeout;
			}
			set
			{
				_busyTimeout = value;
				if (Handle != NullHandle)
				{
					SQLite3.BusyTimeout(Handle, (int)_busyTimeout.TotalMilliseconds);
				}
			}
		}

		public IEnumerable<TableMapping> TableMappings
		{
			get
			{
				lock (_mappings)
				{
					return new List<TableMapping>(_mappings.Values);
				}
			}
		}

		public bool IsInTransaction => _transactionDepth > 0;

		public event EventHandler<NotifyTableChangedEventArgs> TableChanged;

		public SQLiteConnection(string databasePath, bool storeDateTimeAsTicks = true)
			: this(new SQLiteConnectionString(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks))
		{
		}

		public SQLiteConnection(string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
			: this(new SQLiteConnectionString(databasePath, openFlags, storeDateTimeAsTicks))
		{
		}

		public SQLiteConnection(SQLiteConnectionString connectionString)
		{
			if (connectionString == null)
			{
				throw new ArgumentNullException("connectionString");
			}
			if (connectionString.DatabasePath == null)
			{
				throw new InvalidOperationException("DatabasePath must be specified");
			}
			DatabasePath = connectionString.DatabasePath;
			LibVersionNumber = SQLite3.LibVersionNumber();
			byte[] nullTerminatedUtf = GetNullTerminatedUtf8(connectionString.DatabasePath);
			IntPtr db;
			SQLite3.Result result = SQLite3.Open(nullTerminatedUtf, out db, (int)connectionString.OpenFlags, connectionString.VfsName);
			Handle = db;
			if (result != 0)
			{
				throw SQLiteException.New(result, $"Could not open database file: {DatabasePath} ({result})");
			}
			_open = true;
			StoreDateTimeAsTicks = connectionString.StoreDateTimeAsTicks;
			StoreTimeSpanAsTicks = connectionString.StoreTimeSpanAsTicks;
			DateTimeStringFormat = connectionString.DateTimeStringFormat;
			DateTimeStyle = connectionString.DateTimeStyle;
			BusyTimeout = TimeSpan.FromSeconds(1.0);
			Tracer = delegate
			{
			};
			connectionString.PreKeyAction?.Invoke(this);
			if (connectionString.Key is string key)
			{
				SetKey(key);
			}
			else if (connectionString.Key is byte[] key2)
			{
				SetKey(key2);
			}
			else if (connectionString.Key != null)
			{
				throw new InvalidOperationException("Encryption keys must be strings or byte arrays");
			}
			connectionString.PostKeyAction?.Invoke(this);
		}

		public void EnableWriteAheadLogging()
		{
			ExecuteScalar<string>("PRAGMA journal_mode=WAL", Array.Empty<object>());
		}

		private static string Quote(string unsafeString)
		{
			if (unsafeString == null)
			{
				return "NULL";
			}
			string text = unsafeString.Replace("'", "''");
			return "'" + text + "'";
		}

		private void SetKey(string key)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}
			string text = Quote(key);
			ExecuteScalar<string>("pragma key = " + text, Array.Empty<object>());
		}

		private void SetKey(byte[] key)
		{
			if (key == null)
			{
				throw new ArgumentNullException("key");
			}
			if (key.Length != 32 && key.Length != 48)
			{
				throw new ArgumentException("Key must be 32 bytes (256-bit) or 48 bytes (384-bit)", "key");
			}
			string text = string.Join("", key.Select((byte x) => x.ToString("X2")));
			ExecuteScalar<string>("pragma key = \"x'" + text + "'\"", Array.Empty<object>());
		}

		public void EnableLoadExtension(bool enabled)
		{
			SQLite3.Result result = SQLite3.EnableLoadExtension(Handle, enabled ? 1 : 0);
			if (result != 0)
			{
				string errmsg = SQLite3.GetErrmsg(Handle);
				throw SQLiteException.New(result, errmsg);
			}
		}

		private static byte[] GetNullTerminatedUtf8(string s)
		{
			int byteCount = Encoding.UTF8.GetByteCount(s);
			byte[] array = new byte[byteCount + 1];
			byteCount = Encoding.UTF8.GetBytes(s, 0, s.Length, array, 0);
			return array;
		}

		public TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None)
		{
			string fullName = type.FullName;
			TableMapping value;
			lock (_mappings)
			{
				if (_mappings.TryGetValue(fullName, out value))
				{
					if (createFlags != 0 && createFlags != value.CreateFlags)
					{
						value = new TableMapping(type, createFlags);
						_mappings[fullName] = value;
					}
				}
				else
				{
					value = new TableMapping(type, createFlags);
					_mappings.Add(fullName, value);
				}
			}
			return value;
		}

		public TableMapping GetMapping<T>(CreateFlags createFlags = CreateFlags.None)
		{
			return GetMapping(typeof(T), createFlags);
		}

		public int DropTable<T>()
		{
			return DropTable(GetMapping(typeof(T)));
		}

		public int DropTable(TableMapping map)
		{
			string query = $"drop table if exists \"{map.TableName}\"";
			return Execute(query);
		}

		public CreateTableResult CreateTable<T>(CreateFlags createFlags = CreateFlags.None)
		{
			return CreateTable(typeof(T), createFlags);
		}

		public CreateTableResult CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None)
		{
			TableMapping mapping = GetMapping(ty, createFlags);
			if (mapping.Columns.Length == 0)
			{
				throw new Exception($"Cannot create a table without columns (does '{ty.FullName}' have public properties?)");
			}
			CreateTableResult result = CreateTableResult.Created;
			List<ColumnInfo> tableInfo = GetTableInfo(mapping.TableName);
			if (tableInfo.Count == 0)
			{
				bool flag = (createFlags & CreateFlags.FullTextSearch3) != 0;
				bool flag2 = (createFlags & CreateFlags.FullTextSearch4) != 0;
				string text = ((flag || flag2) ? "virtual " : string.Empty);
				string text2 = (flag ? "using fts3 " : (flag2 ? "using fts4 " : string.Empty));
				string text3 = "create " + text + "table if not exists \"" + mapping.TableName + "\" " + text2 + "(\n";
				IEnumerable<string> source = mapping.Columns.Select((TableMapping.Column p) => Orm.SqlDecl(p, StoreDateTimeAsTicks, StoreTimeSpanAsTicks));
				string text4 = string.Join(",\n", source.ToArray());
				text3 += text4;
				text3 += ")";
				if (mapping.WithoutRowId)
				{
					text3 += " without rowid";
				}
				Execute(text3);
			}
			else
			{
				result = CreateTableResult.Migrated;
				MigrateTable(mapping, tableInfo);
			}
			Dictionary<string, IndexInfo> dictionary = new Dictionary<string, IndexInfo>();
			TableMapping.Column[] columns = mapping.Columns;
			foreach (TableMapping.Column column in columns)
			{
				foreach (IndexedAttribute index in column.Indices)
				{
					string text5 = index.Name ?? (mapping.TableName + "_" + column.Name);
					if (!dictionary.TryGetValue(text5, out var value))
					{
						IndexInfo indexInfo = default(IndexInfo);
						indexInfo.IndexName = text5;
						indexInfo.TableName = mapping.TableName;
						indexInfo.Unique = index.Unique;
						indexInfo.Columns = new List<IndexedColumn>();
						value = indexInfo;
						dictionary.Add(text5, value);
					}
					if (index.Unique != value.Unique)
					{
						throw new Exception("All the columns in an index must have the same value for their Unique property");
					}
					value.Columns.Add(new IndexedColumn
					{
						Order = index.Order,
						ColumnName = column.Name
					});
				}
			}
			foreach (string key in dictionary.Keys)
			{
				IndexInfo indexInfo2 = dictionary[key];
				string[] columnNames = (from i in indexInfo2.Columns
					orderby i.Order
					select i.ColumnName).ToArray();
				CreateIndex(key, indexInfo2.TableName, columnNames, indexInfo2.Unique);
			}
			return result;
		}

		public CreateTablesResult CreateTables<T, T2>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new()
		{
			return CreateTables(createFlags, typeof(T), typeof(T2));
		}

		public CreateTablesResult CreateTables<T, T2, T3>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new() where T3 : new()
		{
			return CreateTables(createFlags, typeof(T), typeof(T2), typeof(T3));
		}

		public CreateTablesResult CreateTables<T, T2, T3, T4>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new() where T3 : new() where T4 : new()
		{
			return CreateTables(createFlags, typeof(T), typeof(T2), typeof(T3), typeof(T4));
		}

		public CreateTablesResult CreateTables<T, T2, T3, T4, T5>(CreateFlags createFlags = CreateFlags.None) where T : new() where T2 : new() where T3 : new() where T4 : new() where T5 : new()
		{
			return CreateTables(createFlags, typeof(T), typeof(T2), typeof(T3), typeof(T4), typeof(T5));
		}

		public CreateTablesResult CreateTables(CreateFlags createFlags = CreateFlags.None, params Type[] types)
		{
			CreateTablesResult createTablesResult = new CreateTablesResult();
			foreach (Type type in types)
			{
				CreateTableResult value = CreateTable(type, createFlags);
				createTablesResult.Results[type] = value;
			}
			return createTablesResult;
		}

		public int CreateIndex(string indexName, string tableName, string[] columnNames, bool unique = false)
		{
			string query = string.Format("create {2} index if not exists \"{3}\" on \"{0}\"(\"{1}\")", tableName, string.Join("\", \"", columnNames), unique ? "unique" : "", indexName);
			return Execute(query);
		}

		public int CreateIndex(string indexName, string tableName, string columnName, bool unique = false)
		{
			return CreateIndex(indexName, tableName, new string[1] { columnName }, unique);
		}

		public int CreateIndex(string tableName, string columnName, bool unique = false)
		{
			return CreateIndex(tableName + "_" + columnName, tableName, columnName, unique);
		}

		public int CreateIndex(string tableName, string[] columnNames, bool unique = false)
		{
			return CreateIndex(tableName + "_" + string.Join("_", columnNames), tableName, columnNames, unique);
		}

		public int CreateIndex<T>(Expression<Func<T, object>> property, bool unique = false)
		{
			MemberExpression memberExpression = ((property.Body.NodeType != ExpressionType.Convert) ? (property.Body as MemberExpression) : (((UnaryExpression)property.Body).Operand as MemberExpression));
			PropertyInfo propertyInfo = memberExpression.Member as PropertyInfo;
			if (propertyInfo == null)
			{
				throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
			}
			string name = propertyInfo.Name;
			TableMapping mapping = GetMapping<T>();
			string name2 = mapping.FindColumnWithPropertyName(name).Name;
			return CreateIndex(mapping.TableName, name2, unique);
		}

		public List<ColumnInfo> GetTableInfo(string tableName)
		{
			string query = "pragma table_info(\"" + tableName + "\")";
			return Query<ColumnInfo>(query, Array.Empty<object>());
		}

		private void MigrateTable(TableMapping map, List<ColumnInfo> existingCols)
		{
			List<TableMapping.Column> list = new List<TableMapping.Column>();
			TableMapping.Column[] columns = map.Columns;
			foreach (TableMapping.Column column in columns)
			{
				bool flag = false;
				foreach (ColumnInfo existingCol in existingCols)
				{
					flag = string.Compare(column.Name, existingCol.Name, StringComparison.OrdinalIgnoreCase) == 0;
					if (flag)
					{
						break;
					}
				}
				if (!flag)
				{
					list.Add(column);
				}
			}
			foreach (TableMapping.Column item in list)
			{
				string query = "alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl(item, StoreDateTimeAsTicks, StoreTimeSpanAsTicks);
				Execute(query);
			}
		}

		protected virtual SQLiteCommand NewCommand()
		{
			return new SQLiteCommand(this);
		}

		public SQLiteCommand CreateCommand(string cmdText, params object[] ps)
		{
			if (!_open)
			{
				throw SQLiteException.New(SQLite3.Result.Error, "Cannot create commands from unopened database");
			}
			SQLiteCommand sQLiteCommand = NewCommand();
			sQLiteCommand.CommandText = cmdText;
			foreach (object val in ps)
			{
				sQLiteCommand.Bind(val);
			}
			return sQLiteCommand;
		}

		public SQLiteCommand CreateCommand(string cmdText, Dictionary<string, object> args)
		{
			if (!_open)
			{
				throw SQLiteException.New(SQLite3.Result.Error, "Cannot create commands from unopened database");
			}
			SQLiteCommand sQLiteCommand = NewCommand();
			sQLiteCommand.CommandText = cmdText;
			foreach (KeyValuePair<string, object> arg in args)
			{
				sQLiteCommand.Bind(arg.Key, arg.Value);
			}
			return sQLiteCommand;
		}

		public int Execute(string query, params object[] args)
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			if (TimeExecution)
			{
				if (_sw == null)
				{
					_sw = new Stopwatch();
				}
				_sw.Reset();
				_sw.Start();
			}
			int result = sQLiteCommand.ExecuteNonQuery();
			if (TimeExecution)
			{
				_sw.Stop();
				_elapsedMilliseconds += _sw.ElapsedMilliseconds;
				Tracer?.Invoke($"Finished in {_sw.ElapsedMilliseconds} ms ({(double)_elapsedMilliseconds / 1000.0:0.0} s total)");
			}
			return result;
		}

		public T ExecuteScalar<T>(string query, params object[] args)
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			if (TimeExecution)
			{
				if (_sw == null)
				{
					_sw = new Stopwatch();
				}
				_sw.Reset();
				_sw.Start();
			}
			T result = sQLiteCommand.ExecuteScalar<T>();
			if (TimeExecution)
			{
				_sw.Stop();
				_elapsedMilliseconds += _sw.ElapsedMilliseconds;
				Tracer?.Invoke($"Finished in {_sw.ElapsedMilliseconds} ms ({(double)_elapsedMilliseconds / 1000.0:0.0} s total)");
			}
			return result;
		}

		public List<T> Query<T>(string query, params object[] args) where T : new()
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			return sQLiteCommand.ExecuteQuery<T>();
		}

		public List<T> QueryScalars<T>(string query, params object[] args)
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			return sQLiteCommand.ExecuteQueryScalars<T>().ToList();
		}

		public IEnumerable<T> DeferredQuery<T>(string query, params object[] args) where T : new()
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			return sQLiteCommand.ExecuteDeferredQuery<T>();
		}

		public List<object> Query(TableMapping map, string query, params object[] args)
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			return sQLiteCommand.ExecuteQuery<object>(map);
		}

		public IEnumerable<object> DeferredQuery(TableMapping map, string query, params object[] args)
		{
			SQLiteCommand sQLiteCommand = CreateCommand(query, args);
			return sQLiteCommand.ExecuteDeferredQuery<object>(map);
		}

		public TableQuery<T> Table<T>() where T : new()
		{
			return new TableQuery<T>(this);
		}

		public T Get<T>(object pk) where T : new()
		{
			TableMapping mapping = GetMapping(typeof(T));
			return Query<T>(mapping.GetByPrimaryKeySql, new object[1] { pk }).First();
		}

		public object Get(object pk, TableMapping map)
		{
			return Query(map, map.GetByPrimaryKeySql, pk).First();
		}

		public T Get<T>(Expression<Func<T, bool>> predicate) where T : new()
		{
			return Table<T>().Where(predicate).First();
		}

		public T Find<T>(object pk) where T : new()
		{
			TableMapping mapping = GetMapping(typeof(T));
			return Query<T>(mapping.GetByPrimaryKeySql, new object[1] { pk }).FirstOrDefault();
		}

		public object Find(object pk, TableMapping map)
		{
			return Query(map, map.GetByPrimaryKeySql, pk).FirstOrDefault();
		}

		public T Find<T>(Expression<Func<T, bool>> predicate) where T : new()
		{
			return Table<T>().Where(predicate).FirstOrDefault();
		}

		public T FindWithQuery<T>(string query, params object[] args) where T : new()
		{
			return Query<T>(query, args).FirstOrDefault();
		}

		public object FindWithQuery(TableMapping map, string query, params object[] args)
		{
			return Query(map, query, args).FirstOrDefault();
		}

		public void BeginTransaction()
		{
			if (Interlocked.CompareExchange(ref _transactionDepth, 1, 0) == 0)
			{
				try
				{
					Execute("begin transaction");
					return;
				}
				catch (Exception ex)
				{
					if (ex is SQLiteException ex2)
					{
						switch (ex2.Result)
						{
						case SQLite3.Result.Busy:
						case SQLite3.Result.NoMem:
						case SQLite3.Result.Interrupt:
						case SQLite3.Result.IOError:
						case SQLite3.Result.Full:
							RollbackTo(null, noThrow: true);
							break;
						}
					}
					else
					{
						Interlocked.Decrement(ref _transactionDepth);
					}
					throw;
				}
			}
			throw new InvalidOperationException("Cannot begin a transaction while already in a transaction.");
		}

		public string SaveTransactionPoint()
		{
			int num = Interlocked.Increment(ref _transactionDepth) - 1;
			string text = "S" + _rand.Next(32767) + "D" + num;
			try
			{
				Execute("savepoint " + text);
				return text;
			}
			catch (Exception ex)
			{
				if (ex is SQLiteException ex2)
				{
					switch (ex2.Result)
					{
					case SQLite3.Result.Busy:
					case SQLite3.Result.NoMem:
					case SQLite3.Result.Interrupt:
					case SQLite3.Result.IOError:
					case SQLite3.Result.Full:
						RollbackTo(null, noThrow: true);
						break;
					}
				}
				else
				{
					Interlocked.Decrement(ref _transactionDepth);
				}
				throw;
			}
		}

		public void Rollback()
		{
			RollbackTo(null, noThrow: false);
		}

		public void RollbackTo(string savepoint)
		{
			RollbackTo(savepoint, noThrow: false);
		}

		private void RollbackTo(string savepoint, bool noThrow)
		{
			try
			{
				if (string.IsNullOrEmpty(savepoint))
				{
					if (Interlocked.Exchange(ref _transactionDepth, 0) > 0)
					{
						Execute("rollback");
					}
				}
				else
				{
					DoSavePointExecute(savepoint, "rollback to ");
				}
			}
			catch (SQLiteException)
			{
				if (!noThrow)
				{
					throw;
				}
			}
		}

		public void Release(string savepoint)
		{
			try
			{
				DoSavePointExecute(savepoint, "release ");
			}
			catch (SQLiteException ex)
			{
				if (ex.Result == SQLite3.Result.Busy)
				{
					try
					{
						Execute("rollback");
					}
					catch
					{
					}
				}
				throw;
			}
		}

		private void DoSavePointExecute(string savepoint, string cmd)
		{
			int num = savepoint.IndexOf('D');
			if (num >= 2 && savepoint.Length > num + 1 && int.TryParse(savepoint.Substring(num + 1), out var result) && 0 <= result && result < _transactionDepth)
			{
				Thread.VolatileWrite(ref _transactionDepth, result);
				Execute(cmd + savepoint);
				return;
			}
			throw new ArgumentException("savePoint is not valid, and should be the result of a call to SaveTransactionPoint.", "savePoint");
		}

		public void Commit()
		{
			if (Interlocked.Exchange(ref _transactionDepth, 0) == 0)
			{
				return;
			}
			try
			{
				Execute("commit");
			}
			catch
			{
				try
				{
					Execute("rollback");
				}
				catch
				{
				}
				throw;
			}
		}

		public void RunInTransaction(Action action)
		{
			try
			{
				string savepoint = SaveTransactionPoint();
				action();
				Release(savepoint);
			}
			catch (Exception)
			{
				Rollback();
				throw;
			}
		}

		public int InsertAll(IEnumerable objects, bool runInTransaction = true)
		{
			int c = 0;
			if (runInTransaction)
			{
				RunInTransaction(delegate
				{
					foreach (object @object in objects)
					{
						c += Insert(@object);
					}
				});
			}
			else
			{
				foreach (object object2 in objects)
				{
					c += Insert(object2);
				}
			}
			return c;
		}

		public int InsertAll(IEnumerable objects, string extra, bool runInTransaction = true)
		{
			int c = 0;
			if (runInTransaction)
			{
				RunInTransaction(delegate
				{
					foreach (object @object in objects)
					{
						c += Insert(@object, extra);
					}
				});
			}
			else
			{
				foreach (object object2 in objects)
				{
					c += Insert(object2, extra);
				}
			}
			return c;
		}

		public int InsertAll(IEnumerable objects, Type objType, bool runInTransaction = true)
		{
			int c = 0;
			if (runInTransaction)
			{
				RunInTransaction(delegate
				{
					foreach (object @object in objects)
					{
						c += Insert(@object, objType);
					}
				});
			}
			else
			{
				foreach (object object2 in objects)
				{
					c += Insert(object2, objType);
				}
			}
			return c;
		}

		public int Insert(object obj)
		{
			if (obj == null)
			{
				return 0;
			}
			return Insert(obj, "", Orm.GetType(obj));
		}

		public int InsertOrReplace(object obj)
		{
			if (obj == null)
			{
				return 0;
			}
			return Insert(obj, "OR REPLACE", Orm.GetType(obj));
		}

		public int Insert(object obj, Type objType)
		{
			return Insert(obj, "", objType);
		}

		public int InsertOrReplace(object obj, Type objType)
		{
			return Insert(obj, "OR REPLACE", objType);
		}

		public int Insert(object obj, string extra)
		{
			if (obj == null)
			{
				return 0;
			}
			return Insert(obj, extra, Orm.GetType(obj));
		}

		public int Insert(object obj, string extra, Type objType)
		{
			if (obj == null || objType == null)
			{
				return 0;
			}
			TableMapping mapping = GetMapping(objType);
			if (mapping.PK != null && mapping.PK.IsAutoGuid && mapping.PK.GetValue(obj).Equals(Guid.Empty))
			{
				mapping.PK.SetValue(obj, Guid.NewGuid());
			}
			TableMapping.Column[] array = ((string.Compare(extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0) ? mapping.InsertOrReplaceColumns : mapping.InsertColumns);
			object[] array2 = new object[array.Length];
			for (int i = 0; i < array2.Length; i++)
			{
				array2[i] = array[i].GetValue(obj);
			}
			PreparedSqlLiteInsertCommand insertCommand = GetInsertCommand(mapping, extra);
			int num;
			lock (insertCommand)
			{
				try
				{
					num = insertCommand.ExecuteNonQuery(array2);
				}
				catch (SQLiteException ex)
				{
					if (SQLite3.ExtendedErrCode(Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
					{
						throw NotNullConstraintViolationException.New(ex.Result, ex.Message, mapping, obj);
					}
					throw;
				}
				if (mapping.HasAutoIncPK)
				{
					long id = SQLite3.LastInsertRowid(Handle);
					mapping.SetAutoIncPK(obj, id);
				}
			}
			if (num > 0)
			{
				OnTableChanged(mapping, NotifyTableChangedAction.Insert);
			}
			return num;
		}

		private PreparedSqlLiteInsertCommand GetInsertCommand(TableMapping map, string extra)
		{
			Tuple<string, string> key = Tuple.Create(map.MappedType.FullName, extra);
			PreparedSqlLiteInsertCommand value;
			lock (_insertCommandMap)
			{
				if (_insertCommandMap.TryGetValue(key, out value))
				{
					return value;
				}
			}
			value = CreateInsertCommand(map, extra);
			lock (_insertCommandMap)
			{
				if (_insertCommandMap.TryGetValue(key, out var value2))
				{
					value.Dispose();
					return value2;
				}
				_insertCommandMap.Add(key, value);
				return value;
			}
		}

		private PreparedSqlLiteInsertCommand CreateInsertCommand(TableMapping map, string extra)
		{
			TableMapping.Column[] array = map.InsertColumns;
			string commandText;
			if (array.Length == 0 && map.Columns.Length == 1 && map.Columns[0].IsAutoInc)
			{
				commandText = string.Format("insert {1} into \"{0}\" default values", map.TableName, extra);
			}
			else
			{
				if (string.Compare(extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0)
				{
					array = map.InsertOrReplaceColumns;
				}
				commandText = string.Format("insert {3} into \"{0}\"({1}) values ({2})", map.TableName, string.Join(",", array.Select((TableMapping.Column c) => "\"" + c.Name + "\"").ToArray()), string.Join(",", array.Select((TableMapping.Column c) => "?").ToArray()), extra);
			}
			return new PreparedSqlLiteInsertCommand(this, commandText);
		}

		public int Update(object obj)
		{
			if (obj == null)
			{
				return 0;
			}
			return Update(obj, Orm.GetType(obj));
		}

		public int Update(object obj, Type objType)
		{
			int num = 0;
			if (obj == null || objType == null)
			{
				return 0;
			}
			TableMapping mapping = GetMapping(objType);
			TableMapping.Column pk = mapping.PK;
			if (pk == null)
			{
				throw new NotSupportedException("Cannot update " + mapping.TableName + ": it has no PK");
			}
			IEnumerable<TableMapping.Column> source = mapping.Columns.Where((TableMapping.Column p) => p != pk);
			IEnumerable<object> collection = source.Select((TableMapping.Column c) => c.GetValue(obj));
			List<object> list = new List<object>(collection);
			if (list.Count == 0)
			{
				source = mapping.Columns;
				collection = source.Select((TableMapping.Column c) => c.GetValue(obj));
				list = new List<object>(collection);
			}
			list.Add(pk.GetValue(obj));
			string query = string.Format("update \"{0}\" set {1} where \"{2}\" = ? ", mapping.TableName, string.Join(",", source.Select((TableMapping.Column c) => "\"" + c.Name + "\" = ? ").ToArray()), pk.Name);
			try
			{
				num = Execute(query, list.ToArray());
			}
			catch (SQLiteException ex)
			{
				if (ex.Result == SQLite3.Result.Constraint && SQLite3.ExtendedErrCode(Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
				{
					throw NotNullConstraintViolationException.New(ex, mapping, obj);
				}
				throw ex;
			}
			if (num > 0)
			{
				OnTableChanged(mapping, NotifyTableChangedAction.Update);
			}
			return num;
		}

		public int UpdateAll(IEnumerable objects, bool runInTransaction = true)
		{
			int c = 0;
			if (runInTransaction)
			{
				RunInTransaction(delegate
				{
					foreach (object @object in objects)
					{
						c += Update(@object);
					}
				});
			}
			else
			{
				foreach (object object2 in objects)
				{
					c += Update(object2);
				}
			}
			return c;
		}

		public int Delete(object objectToDelete)
		{
			TableMapping mapping = GetMapping(Orm.GetType(objectToDelete));
			TableMapping.Column pK = mapping.PK;
			if (pK == null)
			{
				throw new NotSupportedException("Cannot delete " + mapping.TableName + ": it has no PK");
			}
			string query = $"delete from \"{mapping.TableName}\" where \"{pK.Name}\" = ?";
			int num = Execute(query, pK.GetValue(objectToDelete));
			if (num > 0)
			{
				OnTableChanged(mapping, NotifyTableChangedAction.Delete);
			}
			return num;
		}

		public int Delete<T>(object primaryKey)
		{
			return Delete(primaryKey, GetMapping(typeof(T)));
		}

		public int Delete(object primaryKey, TableMapping map)
		{
			TableMapping.Column pK = map.PK;
			if (pK == null)
			{
				throw new NotSupportedException("Cannot delete " + map.TableName + ": it has no PK");
			}
			string query = $"delete from \"{map.TableName}\" where \"{pK.Name}\" = ?";
			int num = Execute(query, primaryKey);
			if (num > 0)
			{
				OnTableChanged(map, NotifyTableChangedAction.Delete);
			}
			return num;
		}

		public int DeleteAll<T>()
		{
			TableMapping mapping = GetMapping(typeof(T));
			return DeleteAll(mapping);
		}

		public int DeleteAll(TableMapping map)
		{
			string query = $"delete from \"{map.TableName}\"";
			int num = Execute(query);
			if (num > 0)
			{
				OnTableChanged(map, NotifyTableChangedAction.Delete);
			}
			return num;
		}

		public void Backup(string destinationDatabasePath, string databaseName = "main")
		{
			SQLite3.Result result = SQLite3.Open(destinationDatabasePath, out var db);
			if (result != 0)
			{
				throw SQLiteException.New(result, "Failed to open destination database");
			}
			IntPtr intPtr = SQLite3.BackupInit(db, databaseName, Handle, databaseName);
			if (intPtr == NullBackupHandle)
			{
				SQLite3.Close(db);
				throw new Exception("Failed to create backup");
			}
			SQLite3.BackupStep(intPtr, -1);
			SQLite3.BackupFinish(intPtr);
			result = SQLite3.GetResult(db);
			string message = "";
			if (result != 0)
			{
				message = SQLite3.GetErrmsg(db);
			}
			SQLite3.Close(db);
			if (result != 0)
			{
				throw SQLiteException.New(result, message);
			}
		}

		~SQLiteConnection()
		{
			Dispose(disposing: false);
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		public void Close()
		{
			Dispose(disposing: true);
		}

		protected virtual void Dispose(bool disposing)
		{
			bool flag = LibVersionNumber >= 3007014;
			if (!_open || !(Handle != NullHandle))
			{
				return;
			}
			try
			{
				if (disposing)
				{
					lock (_insertCommandMap)
					{
						foreach (PreparedSqlLiteInsertCommand value in _insertCommandMap.Values)
						{
							value.Dispose();
						}
						_insertCommandMap.Clear();
					}
					SQLite3.Result result = (flag ? SQLite3.Close2(Handle) : SQLite3.Close(Handle));
					if (result != 0)
					{
						string errmsg = SQLite3.GetErrmsg(Handle);
						throw SQLiteException.New(result, errmsg);
					}
				}
				else
				{
					SQLite3.Result result2 = (flag ? SQLite3.Close2(Handle) : SQLite3.Close(Handle));
				}
			}
			finally
			{
				Handle = NullHandle;
				_open = false;
			}
		}

		private void OnTableChanged(TableMapping table, NotifyTableChangedAction action)
		{
			this.TableChanged?.Invoke(this, new NotifyTableChangedEventArgs(table, action));
		}
	}
	public class NotifyTableChangedEventArgs : EventArgs
	{
		public TableMapping Table { get; private set; }

		public NotifyTableChangedAction Action { get; private set; }

		public NotifyTableChangedEventArgs(TableMapping table, NotifyTableChangedAction action)
		{
			Table = table;
			Action = action;
		}
	}
	public enum NotifyTableChangedAction
	{
		Insert,
		Update,
		Delete
	}
	public class SQLiteConnectionString
	{
		private const string DateTimeSqliteDefaultFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff";

		public string UniqueKey { get; }

		public string DatabasePath { get; }

		public bool StoreDateTimeAsTicks { get; }

		public bool StoreTimeSpanAsTicks { get; }

		public string DateTimeStringFormat { get; }

		public DateTimeStyles DateTimeStyle { get; }

		public object Key { get; }

		public SQLiteOpenFlags OpenFlags { get; }

		public Action<SQLiteConnection> PreKeyAction { get; }

		public Action<SQLiteConnection> PostKeyAction { get; }

		public string VfsName { get; }

		public SQLiteConnectionString(string databasePath, bool storeDateTimeAsTicks = true)
			: this(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
		{
		}

		public SQLiteConnectionString(string databasePath, bool storeDateTimeAsTicks, object key = null, Action<SQLiteConnection> preKeyAction = null, Action<SQLiteConnection> postKeyAction = null, string vfsName = null)
			: this(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks, key, preKeyAction, postKeyAction, vfsName)
		{
		}

		public SQLiteConnectionString(string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks, object key = null, Action<SQLiteConnection> preKeyAction = null, Action<SQLiteConnection> postKeyAction = null, string vfsName = null, string dateTimeStringFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff", bool storeTimeSpanAsTicks = true)
		{
			if (key != null && !(key is byte[]) && !(key is string))
			{
				throw new ArgumentException("Encryption keys must be strings or byte arrays", "key");
			}
			UniqueKey = $"{databasePath}_{(uint)openFlags:X8}";
			StoreDateTimeAsTicks = storeDateTimeAsTicks;
			StoreTimeSpanAsTicks = storeTimeSpanAsTicks;
			DateTimeStringFormat = dateTimeStringFormat;
			DateTimeStyle = (("o".Equals(DateTimeStringFormat, StringComparison.OrdinalIgnoreCase) || "r".Equals(DateTimeStringFormat, StringComparison.OrdinalIgnoreCase)) ? DateTimeStyles.RoundtripKind : DateTimeStyles.None);
			Key = key;
			PreKeyAction = preKeyAction;
			PostKeyAction = postKeyAction;
			OpenFlags = openFlags;
			VfsName = vfsName;
			DatabasePath = databasePath;
		}
	}
	[AttributeUsage(AttributeTargets.Class)]
	public class TableAttribute : Attribute
	{
		public string Name { get; set; }

		public bool WithoutRowId { get; set; }

		public TableAttribute(string name)
		{
			Name = name;
		}
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class ColumnAttribute : Attribute
	{
		public string Name { get; set; }

		public ColumnAttribute(string name)
		{
			Name = name;
		}
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class PrimaryKeyAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class AutoIncrementAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class IndexedAttribute : Attribute
	{
		public string Name { get; set; }

		public int Order { get; set; }

		public virtual bool Unique { get; set; }

		public IndexedAttribute()
		{
		}

		public IndexedAttribute(string name, int order)
		{
			Name = name;
			Order = order;
		}
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class IgnoreAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class UniqueAttribute : IndexedAttribute
	{
		public override bool Unique
		{
			get
			{
				return true;
			}
			set
			{
			}
		}
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class MaxLengthAttribute : Attribute
	{
		public int Value { get; private set; }

		public MaxLengthAttribute(int length)
		{
			Value = length;
		}
	}
	public sealed class PreserveAttribute : Attribute
	{
		public bool AllMembers;

		public bool Conditional;
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class CollationAttribute : Attribute
	{
		public string Value { get; private set; }

		public CollationAttribute(string collation)
		{
			Value = collation;
		}
	}
	[AttributeUsage(AttributeTargets.Property)]
	public class NotNullAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Enum)]
	public class StoreAsTextAttribute : Attribute
	{
	}
	public class TableMapping
	{
		public class Column
		{
			private MemberInfo _member;

			public string Name { get; private set; }

			public PropertyInfo PropertyInfo => _member as PropertyInfo;

			public string PropertyName => _member.Name;

			public Type ColumnType { get; private set; }

			public string Collation { get; private set; }

			public bool IsAutoInc { get; private set; }

			public bool IsAutoGuid { get; private set; }

			public bool IsPK { get; private set; }

			public IEnumerable<IndexedAttribute> Indices { get; set; }

			public bool IsNullable { get; private set; }

			public int? MaxStringLength { get; private set; }

			public bool StoreAsText { get; private set; }

			public Column(MemberInfo member, CreateFlags createFlags = CreateFlags.None)
			{
				_member = member;
				Type memberType = GetMemberType(member);
				CustomAttributeData customAttributeData = member.CustomAttributes.FirstOrDefault((CustomAttributeData x) => x.AttributeType == typeof(ColumnAttribute));
				Name = ((customAttributeData == null || customAttributeData.ConstructorArguments.Count <= 0) ? member.Name : customAttributeData.ConstructorArguments[0].Value?.ToString());
				ColumnType = Nullable.GetUnderlyingType(memberType) ?? memberType;
				Collation = Orm.Collation(member);
				IsPK = Orm.IsPK(member) || ((createFlags & CreateFlags.ImplicitPK) == CreateFlags.ImplicitPK && string.Compare(member.Name, "Id", StringComparison.OrdinalIgnoreCase) == 0);
				bool flag = Orm.IsAutoInc(member) || (IsPK && (createFlags & CreateFlags.AutoIncPK) == CreateFlags.AutoIncPK);
				IsAutoGuid = flag && ColumnType == typeof(Guid);
				IsAutoInc = flag && !IsAutoGuid;
				Indices = Orm.GetIndices(member);
				if (!Indices.Any() && !IsPK && (createFlags & CreateFlags.ImplicitIndex) == CreateFlags.ImplicitIndex && Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase))
				{
					Indices = new IndexedAttribute[1]
					{
						new IndexedAttribute()
					};
				}
				IsNullable = !IsPK && !Orm.IsMarkedNotNull(member);
				MaxStringLength = Orm.MaxStringLength(member);
				StoreAsText = memberType.GetTypeInfo().CustomAttributes.Any((CustomAttributeData x) => x.AttributeType == typeof(StoreAsTextAttribute));
			}

			public Column(PropertyInfo member, CreateFlags createFlags = CreateFlags.None)
				: this((MemberInfo)member, createFlags)
			{
			}

			public void SetValue(object obj, object val)
			{
				if (_member is PropertyInfo propertyInfo)
				{
					if (val != null && ColumnType.GetTypeInfo().IsEnum)
					{
						propertyInfo.SetValue(obj, Enum.ToObject(ColumnType, val));
					}
					else
					{
						propertyInfo.SetValue(obj, val);
					}
					return;
				}
				if (_member is FieldInfo fieldInfo)
				{
					if (val != null && ColumnType.GetTypeInfo().IsEnum)
					{
						fieldInfo.SetValue(obj, Enum.ToObject(ColumnType, val));
					}
					else
					{
						fieldInfo.SetValue(obj, val);
					}
					return;
				}
				throw new InvalidProgramException("unreachable condition");
			}

			public object GetValue(object obj)
			{
				if (_member is PropertyInfo propertyInfo)
				{
					return propertyInfo.GetValue(obj);
				}
				if (_member is FieldInfo fieldInfo)
				{
					return fieldInfo.GetValue(obj);
				}
				throw new InvalidProgramException("unreachable condition");
			}

			private static Type GetMemberType(MemberInfo m)
			{
				return m.MemberType switch
				{
					MemberTypes.Property => ((PropertyInfo)m).PropertyType, 
					MemberTypes.Field => ((FieldInfo)m).FieldType, 
					_ => throw new InvalidProgramException("TableMapping supports properties or fields only."), 
				};
			}
		}

		internal enum MapMethod
		{
			ByName,
			ByPosition
		}

		private readonly Column _autoPk;

		private readonly Column[] _insertColumns;

		private readonly Column[] _insertOrReplaceColumns;

		public Type MappedType { get; private set; }

		public string TableName { get; private set; }

		public bool WithoutRowId { get; private set; }

		public Column[] Columns { get; private set; }

		public Column PK { get; private set; }

		public string GetByPrimaryKeySql { get; private set; }

		public CreateFlags CreateFlags { get; private set; }

		internal MapMethod Method { get; private set; }

		public bool HasAutoIncPK { get; private set; }

		public Column[] InsertColumns => _insertColumns;

		public Column[] InsertOrReplaceColumns => _insertOrReplaceColumns;

		public TableMapping(Type type, CreateFlags createFlags = CreateFlags.None)
		{
			MappedType = type;
			CreateFlags = createFlags;
			TypeInfo typeInfo = type.GetTypeInfo();
			TableAttribute tableAttribute = (from x in typeInfo.CustomAttributes
				where x.AttributeType == typeof(TableAttribute)
				select (TableAttribute)Orm.InflateAttribute(x)).FirstOrDefault();
			TableName = ((tableAttribute != null && !string.IsNullOrEmpty(tableAttribute.Name)) ? tableAttribute.Name : MappedType.Name);
			WithoutRowId = tableAttribute?.WithoutRowId ?? false;
			IReadOnlyCollection<MemberInfo> publicMembers = GetPublicMembers(type);
			List<Column> list = new List<Column>(publicMembers.Count);
			foreach (MemberInfo item in publicMembers)
			{
				if (!item.IsDefined(typeof(IgnoreAttribute), inherit: true))
				{
					list.Add(new Column(item, createFlags));
				}
			}
			Columns = list.ToArray();
			Column[] columns = Columns;
			foreach (Column column in columns)
			{
				if (column.IsAutoInc && column.IsPK)
				{
					_autoPk = column;
				}
				if (column.IsPK)
				{
					PK = column;
				}
			}
			HasAutoIncPK = _autoPk != null;
			if (PK != null)
			{
				GetByPrimaryKeySql = $"select * from \"{TableName}\" where \"{PK.Name}\" = ?";
			}
			else
			{
				GetByPrimaryKeySql = $"select * from \"{TableName}\" limit 1";
			}
			_insertColumns = Columns.Where((Column c) => !c.IsAutoInc).ToArray();
			_insertOrReplaceColumns = Columns.ToArray();
		}

		private IReadOnlyCollection<MemberInfo> GetPublicMembers(Type type)
		{
			if (type.Name.StartsWith("ValueTuple`"))
			{
				return GetFieldsFromValueTuple(type);
			}
			List<MemberInfo> list = new List<MemberInfo>();
			HashSet<string> memberNames = new HashSet<string>();
			List<MemberInfo> list2 = new List<MemberInfo>();
			do
			{
				TypeInfo typeInfo = type.GetTypeInfo();
				list2.Clear();
				list2.AddRange(typeInfo.DeclaredProperties.Where((PropertyInfo p) => !memberNames.Contains(p.Name) && p.CanRead && p.CanWrite && p.GetMethod != null && p.SetMethod != null && p.GetMethod.IsPublic && p.SetMethod.IsPublic && !p.GetMethod.IsStatic && !p.SetMethod.IsStatic));
				list.AddRange(list2);
				foreach (MemberInfo item in list2)
				{
					memberNames.Add(item.Name);
				}
				type = typeInfo.BaseType;
			}
			while (type != typeof(object));
			return list;
		}

		private IReadOnlyCollection<MemberInfo> GetFieldsFromValueTuple(Type type)
		{
			Method = MapMethod.ByPosition;
			FieldInfo[] fields = type.GetFields();
			if (fields.Length >= 8)
			{
				throw new NotSupportedException("ValueTuple with more than 7 members not supported due to nesting; see https://docs.microsoft.com/en-us/dotnet/api/system.valuetuple-8.rest");
			}
			return (IReadOnlyCollection<MemberInfo>)(object)fields;
		}

		public void SetAutoIncPK(object obj, long id)
		{
			if (_autoPk != null)
			{
				_autoPk.SetValue(obj, Convert.ChangeType(id, _autoPk.ColumnType, null));
			}
		}

		public Column FindColumnWithPropertyName(string propertyName)
		{
			return Columns.FirstOrDefault((Column c) => c.PropertyName == propertyName);
		}

		public Column FindColumn(string columnName)
		{
			if (Method != 0)
			{
				throw new InvalidOperationException(string.Format("This {0} is not mapped by name, but {1}.", "TableMapping", Method));
			}
			return Columns.FirstOrDefault((Column c) => c.Name.ToLower() == columnName.ToLower());
		}
	}
	internal class EnumCacheInfo
	{
		public bool IsEnum { get; private set; }

		public bool StoreAsText { get; private set; }

		public Dictionary<int, string> EnumValues { get; private set; }

		public EnumCacheInfo(Type type)
		{
			TypeInfo typeInfo = type.GetTypeInfo();
			IsEnum = typeInfo.IsEnum;
			if (!IsEnum)
			{
				return;
			}
			StoreAsText = typeInfo.CustomAttributes.Any((CustomAttributeData x) => x.AttributeType == typeof(StoreAsTextAttribute));
			if (!StoreAsText)
			{
				return;
			}
			EnumValues = new Dictionary<int, string>();
			foreach (object value in Enum.GetValues(type))
			{
				EnumValues[Convert.ToInt32(value)] = value.ToString();
			}
		}
	}
	internal static class EnumCache
	{
		private static readonly Dictionary<Type, EnumCacheInfo> Cache = new Dictionary<Type, EnumCacheInfo>();

		public static EnumCacheInfo GetInfo<T>()
		{
			return GetInfo(typeof(T));
		}

		public static EnumCacheInfo GetInfo(Type type)
		{
			lock (Cache)
			{
				EnumCacheInfo value = null;
				if (!Cache.TryGetValue(type, out value))
				{
					value = new EnumCacheInfo(type);
					Cache[type] = value;
				}
				return value;
			}
		}
	}
	public static class Orm
	{
		public const int DefaultMaxStringLength = 140;

		public const string ImplicitPkName = "Id";

		public const string ImplicitIndexSuffix = "Id";

		public static Type GetType(object obj)
		{
			if (obj == null)
			{
				return typeof(object);
			}
			if (obj is IReflectableType reflectableType)
			{
				return reflectableType.GetTypeInfo().AsType();
			}
			return obj.GetType();
		}

		public static string SqlDecl(TableMapping.Column p, bool storeDateTimeAsTicks, bool storeTimeSpanAsTicks)
		{
			string text = "\"" + p.Name + "\" " + SqlType(p, storeDateTimeAsTicks, storeTimeSpanAsTicks) + " ";
			if (p.IsPK)
			{
				text += "primary key ";
			}
			if (p.IsAutoInc)
			{
				text += "autoincrement ";
			}
			if (!p.IsNullable)
			{
				text += "not null ";
			}
			if (!string.IsNullOrEmpty(p.Collation))
			{
				text = text + "collate " + p.Collation + " ";
			}
			return text;
		}

		public static string SqlType(TableMapping.Column p, bool storeDateTimeAsTicks, bool storeTimeSpanAsTicks)
		{
			Type columnType = p.ColumnType;
			if (columnType == typeof(bool) || columnType == typeof(byte) || columnType == typeof(ushort) || columnType == typeof(sbyte) || columnType == typeof(short) || columnType == typeof(int) || columnType == typeof(uint) || columnType == typeof(long))
			{
				return "integer";
			}
			if (columnType == typeof(float) || columnType == typeof(double) || columnType == typeof(decimal))
			{
				return "float";
			}
			if (columnType == typeof(string) || columnType == typeof(StringBuilder) || columnType == typeof(Uri) || columnType == typeof(UriBuilder))
			{
				int? maxStringLength = p.MaxStringLength;
				if (maxStringLength.HasValue)
				{
					return "varchar(" + maxStringLength.Value + ")";
				}
				return "varchar";
			}
			if (columnType == typeof(TimeSpan))
			{
				if (!storeTimeSpanAsTicks)
				{
					return "time";
				}
				return "bigint";
			}
			if (columnType == typeof(DateTime))
			{
				if (!storeDateTimeAsTicks)
				{
					return "datetime";
				}
				return "bigint";
			}
			if (columnType == typeof(DateTimeOffset))
			{
				return "bigint";
			}
			if (columnType.GetTypeInfo().IsEnum)
			{
				if (p.StoreAsText)
				{
					return "varchar";
				}
				return "integer";
			}
			if (columnType == typeof(byte[]))
			{
				return "blob";
			}
			if (columnType == typeof(Guid))
			{
				return "varchar(36)";
			}
			throw new NotSupportedException("Don't know about " + columnType);
		}

		public static bool IsPK(MemberInfo p)
		{
			return p.CustomAttributes.Any((CustomAttributeData x) => x.AttributeType == typeof(PrimaryKeyAttribute));
		}

		public static string Collation(MemberInfo p)
		{
			return p.CustomAttributes.Where((CustomAttributeData x) => typeof(CollationAttribute) == x.AttributeType).Select(delegate(CustomAttributeData x)
			{
				IList<CustomAttributeTypedArgument> constructorArguments = x.ConstructorArguments;
				return (constructorArguments.Count <= 0) ? "" : ((constructorArguments[0].Value as string) ?? "");
			}).FirstOrDefault() ?? "";
		}

		public static bool IsAutoInc(MemberInfo p)
		{
			return p.CustomAttributes.Any((CustomAttributeData x) => x.AttributeType == typeof(AutoIncrementAttribute));
		}

		public static FieldInfo GetField(TypeInfo t, string name)
		{
			FieldInfo declaredField = t.GetDeclaredField(name);
			if (declaredField != null)
			{
				return declaredField;
			}
			return GetField(t.BaseType.GetTypeInfo(), name);
		}

		public static PropertyInfo GetProperty(TypeInfo t, string name)
		{
			PropertyInfo declaredProperty = t.GetDeclaredProperty(name);
			if (declaredProperty != null)
			{
				return declaredProperty;
			}
			return GetProperty(t.BaseType.GetTypeInfo(), name);
		}

		public static object InflateAttribute(CustomAttributeData x)
		{
			Type attributeType = x.AttributeType;
			TypeInfo typeInfo = attributeType.GetTypeInfo();
			object[] args = x.ConstructorArguments.Select((CustomAttributeTypedArgument a) => a.Value).ToArray();
			object obj = Activator.CreateInstance(x.AttributeType, args);
			foreach (CustomAttributeNamedArgument namedArgument in x.NamedArguments)
			{
				if (namedArgument.IsField)
				{
					GetField(typeInfo, namedArgument.MemberName).SetValue(obj, namedArgument.TypedValue.Value);
				}
				else
				{
					GetProperty(typeInfo, namedArgument.MemberName).SetValue(obj, namedArgument.TypedValue.Value);
				}
			}
			return obj;
		}

		public static IEnumerable<IndexedAttribute> GetIndices(MemberInfo p)
		{
			TypeInfo indexedInfo = typeof(IndexedAttribute).GetTypeInfo();
			return from x in p.CustomAttributes
				where indexedInfo.IsAssignableFrom(x.AttributeType.GetTypeInfo())
				select (IndexedAttribute)InflateAttribute(x);
		}

		public static int? MaxStringLength(MemberInfo p)
		{
			CustomAttributeData customAttributeData = p.CustomAttributes.FirstOrDefault((CustomAttributeData x) => x.AttributeType == typeof(MaxLengthAttribute));
			if (customAttributeData != null)
			{
				MaxLengthAttribute maxLengthAttribute = (MaxLengthAttribute)InflateAttribute(customAttributeData);
				return maxLengthAttribute.Value;
			}
			return null;
		}

		public static int? MaxStringLength(PropertyInfo p)
		{
			return MaxStringLength((MemberInfo)p);
		}

		public static bool IsMarkedNotNull(MemberInfo p)
		{
			return p.CustomAttributes.Any((CustomAttributeData x) => x.AttributeType == typeof(NotNullAttribute));
		}
	}
	public class SQLiteCommand
	{
		private class Binding
		{
			public string Name { get; set; }

			public object Value { get; set; }

			public int Index { get; set; }
		}

		private SQLiteConnection _conn;

		private List<Binding> _bindings;

		private static IntPtr NegativePointer = new IntPtr(-1);

		public string CommandText { get; set; }

		public SQLiteCommand(SQLiteConnection conn)
		{
			_conn = conn;
			_bindings = new List<Binding>();
			CommandText = "";
		}

		public int ExecuteNonQuery()
		{
			if (_conn.Trace)
			{
				_conn.Tracer?.Invoke("Executing: " + this);
			}
			SQLite3.Result result = SQLite3.Result.OK;
			IntPtr stmt = Prepare();
			result = SQLite3.Step(stmt);
			Finalize(stmt);
			switch (result)
			{
			case SQLite3.Result.Done:
				return SQLite3.Changes(_conn.Handle);
			case SQLite3.Result.Error:
			{
				string errmsg = SQLite3.GetErrmsg(_conn.Handle);
				throw SQLiteException.New(result, errmsg);
			}
			case SQLite3.Result.Constraint:
				if (SQLite3.ExtendedErrCode(_conn.Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
				{
					throw NotNullConstraintViolationException.New(result, SQLite3.GetErrmsg(_conn.Handle));
				}
				break;
			}
			throw SQLiteException.New(result, SQLite3.GetErrmsg(_conn.Handle));
		}

		public IEnumerable<T> ExecuteDeferredQuery<T>()
		{
			return ExecuteDeferredQuery<T>(_conn.GetMapping(typeof(T)));
		}

		public List<T> ExecuteQuery<T>()
		{
			return ExecuteDeferredQuery<T>(_conn.GetMapping(typeof(T))).ToList();
		}

		public List<T> ExecuteQuery<T>(TableMapping map)
		{
			return ExecuteDeferredQuery<T>(map).ToList();
		}

		protected virtual void OnInstanceCreated(object obj)
		{
		}

		public IEnumerable<T> ExecuteDeferredQuery<T>(TableMapping map)
		{
			if (_conn.Trace)
			{
				_conn.Tracer?.Invoke("Executing Query: " + this);
			}
			IntPtr stmt = Prepare();
			try
			{
				TableMapping.Column[] cols = new TableMapping.Column[SQLite3.ColumnCount(stmt)];
				Action<object, IntPtr, int>[] fastColumnSetters = new Action<object, IntPtr, int>[SQLite3.ColumnCount(stmt)];
				if (map.Method == TableMapping.MapMethod.ByPosition)
				{
					Array.Copy(map.Columns, cols, Math.Min(cols.Length, map.Columns.Length));
				}
				else if (map.Method == TableMapping.MapMethod.ByName)
				{
					MethodInfo methodInfo = null;
					if (typeof(T) != map.MappedType)
					{
						methodInfo = typeof(FastColumnSetter).GetMethod("GetFastSetter", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(map.MappedType);
					}
					for (int i = 0; i < cols.Length; i++)
					{
						string columnName = SQLite3.ColumnName16(stmt, i);
						cols[i] = map.FindColumn(columnName);
						if (cols[i] != null)
						{
							if (methodInfo != null)
							{
								fastColumnSetters[i] = (Action<object, IntPtr, int>)methodInfo.Invoke(null, new object[2]
								{
									_conn,
									cols[i]
								});
							}
							else
							{
								fastColumnSetters[i] = FastColumnSetter.GetFastSetter<T>(_conn, cols[i]);
							}
						}
					}
				}
				while (SQLite3.Step(stmt) == SQLite3.Result.Row)
				{
					object obj = Activator.CreateInstance(map.MappedType);
					for (int j = 0; j < cols.Length; j++)
					{
						if (cols[j] != null)
						{
							if (fastColumnSetters[j] != null)
							{
								fastColumnSetters[j](obj, stmt, j);
								continue;
							}
							SQLite3.ColType type = SQLite3.ColumnType(stmt, j);
							object val = ReadCol(stmt, j, type, cols[j].ColumnType);
							cols[j].SetValue(obj, val);
						}
					}
					OnInstanceCreated(obj);
					yield return (T)obj;
				}
			}
			finally
			{
				SQLite3.Finalize(stmt);
			}
		}

		public T ExecuteScalar<T>()
		{
			if (_conn.Trace)
			{
				_conn.Tracer?.Invoke("Executing Query: " + this);
			}
			T result = default(T);
			IntPtr stmt = Prepare();
			try
			{
				SQLite3.Result result2 = SQLite3.Step(stmt);
				switch (result2)
				{
				case SQLite3.Result.Row:
				{
					SQLite3.ColType type = SQLite3.ColumnType(stmt, 0);
					object obj = ReadCol(stmt, 0, type, typeof(T));
					if (obj != null)
					{
						result = (T)obj;
						return result;
					}
					break;
				}
				case SQLite3.Result.Done:
					break;
				default:
					throw SQLiteException.New(result2, SQLite3.GetErrmsg(_conn.Handle));
				}
			}
			finally
			{
				Finalize(stmt);
			}
			return result;
		}

		public IEnumerable<T> ExecuteQueryScalars<T>()
		{
			if (_conn.Trace)
			{
				_conn.Tracer?.Invoke("Executing Query: " + this);
			}
			IntPtr stmt = Prepare();
			try
			{
				if (SQLite3.ColumnCount(stmt) < 1)
				{
					throw new InvalidOperationException("QueryScalars should return at least one column");
				}
				while (SQLite3.Step(stmt) == SQLite3.Result.Row)
				{
					SQLite3.ColType type = SQLite3.ColumnType(stmt, 0);
					object obj = ReadCol(stmt, 0, type, typeof(T));
					if (obj == null)
					{
						yield return default(T);
					}
					else
					{
						yield return (T)obj;
					}
				}
			}
			finally
			{
				Finalize(stmt);
			}
		}

		public void Bind(string name, object val)
		{
			_bindings.Add(new Binding
			{
				Name = name,
				Value = val
			});
		}

		public void Bind(object val)
		{
			Bind(null, val);
		}

		public override string ToString()
		{
			string[] array = new string[1 + _bindings.Count];
			array[0] = CommandText;
			int num = 1;
			foreach (Binding binding in _bindings)
			{
				array[num] = $"  {num - 1}: {binding.Value}";
				num++;
			}
			return string.Join(Environment.NewLine, array);
		}

		private IntPtr Prepare()
		{
			IntPtr intPtr = SQLite3.Prepare2(_conn.Handle, CommandText);
			BindAll(intPtr);
			return intPtr;
		}

		private void Finalize(IntPtr stmt)
		{
			SQLite3.Finalize(stmt);
		}

		private void BindAll(IntPtr stmt)
		{
			int num = 1;
			foreach (Binding binding in _bindings)
			{
				if (binding.Name != null)
				{
					binding.Index = SQLite3.BindParameterIndex(stmt, binding.Name);
				}
				else
				{
					binding.Index = num++;
				}
				BindParameter(stmt, binding.Index, binding.Value, _conn.StoreDateTimeAsTicks, _conn.DateTimeStringFormat, _conn.StoreTimeSpanAsTicks);
			}
		}

		internal static void BindParameter(IntPtr stmt, int index, object value, bool storeDateTimeAsTicks, string dateTimeStringFormat, bool storeTimeSpanAsTicks)
		{
			if (value == null)
			{
				SQLite3.BindNull(stmt, index);
				return;
			}
			if (value is int)
			{
				SQLite3.BindInt(stmt, index, (int)value);
				return;
			}
			if (value is string)
			{
				SQLite3.BindText(stmt, index, (string)value, -1, NegativePointer);
				return;
			}
			if (value is byte || value is ushort || value is sbyte || value is short)
			{
				SQLite3.BindInt(stmt, index, Convert.ToInt32(value));
				return;
			}
			if (value is bool)
			{
				SQLite3.BindInt(stmt, index, ((bool)value) ? 1 : 0);
				return;
			}
			if (value is uint || value is long)
			{
				SQLite3.BindInt64(stmt, index, Convert.ToInt64(value));
				return;
			}
			if (value is float || value is double || value is decimal)
			{
				SQLite3.BindDouble(stmt, index, Convert.ToDouble(value));
				return;
			}
			if (value is TimeSpan)
			{
				if (storeTimeSpanAsTicks)
				{
					SQLite3.BindInt64(stmt, index, ((TimeSpan)value).Ticks);
				}
				else
				{
					SQLite3.BindText(stmt, index, ((TimeSpan)value).ToString(), -1, NegativePointer);
				}
				return;
			}
			if (value is DateTime)
			{
				if (storeDateTimeAsTicks)
				{
					SQLite3.BindInt64(stmt, index, ((DateTime)value).Ticks);
				}
				else
				{
					SQLite3.BindText(stmt, index, ((DateTime)value).ToString(dateTimeStringFormat, CultureInfo.InvariantCulture), -1, NegativePointer);
				}
				return;
			}
			if (value is DateTimeOffset)
			{
				SQLite3.BindInt64(stmt, index, ((DateTimeOffset)value).UtcTicks);
				return;
			}
			if (value is byte[])
			{
				SQLite3.BindBlob(stmt, index, (byte[])value, ((byte[])value).Length, NegativePointer);
				return;
			}
			if (value is Guid)
			{
				SQLite3.BindText(stmt, index, ((Guid)value).ToString(), 72, NegativePointer);
				return;
			}
			if (value is Uri)
			{
				SQLite3.BindText(stmt, index, ((Uri)value).ToString(), -1, NegativePointer);
				return;
			}
			if (value is StringBuilder)
			{
				SQLite3.BindText(stmt, index, ((StringBuilder)value).ToString(), -1, NegativePointer);
				return;
			}
			if (value is UriBuilder)
			{
				SQLite3.BindText(stmt, index, ((UriBuilder)value).ToString(), -1, NegativePointer);
				return;
			}
			Type type = value.GetType();
			EnumCacheInfo info = EnumCache.GetInfo(type);
			if (info.IsEnum)
			{
				int num = Convert.ToInt32(value);
				if (info.StoreAsText)
				{
					SQLite3.BindText(stmt, index, info.EnumValues[num], -1, NegativePointer);
				}
				else
				{
					SQLite3.BindInt(stmt, index, num);
				}
				return;
			}
			throw new NotSupportedException("Cannot store type: " + Orm.GetType(value));
		}

		private object ReadCol(IntPtr stmt, int index, SQLite3.ColType type, Type clrType)
		{
			if (type == SQLite3.ColType.Null)
			{
				return null;
			}
			TypeInfo typeInfo = clrType.GetTypeInfo();
			if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
			{
				clrType = typeInfo.GenericTypeArguments[0];
				typeInfo = clrType.GetTypeInfo();
			}
			if (clrType == typeof(string))
			{
				return SQLite3.ColumnString(stmt, index);
			}
			if (clrType == typeof(int))
			{
				return SQLite3.ColumnInt(stmt, index);
			}
			if (clrType == typeof(bool))
			{
				return SQLite3.ColumnInt(stmt, index) == 1;
			}
			if (clrType == typeof(double))
			{
				return SQLite3.ColumnDouble(stmt, index);
			}
			if (clrType == typeof(float))
			{
				return (float)SQLite3.ColumnDouble(stmt, index);
			}
			if (clrType == typeof(TimeSpan))
			{
				if (_conn.StoreTimeSpanAsTicks)
				{
					return new TimeSpan(SQLite3.ColumnInt64(stmt, index));
				}
				string text = SQLite3.ColumnString(stmt, index);
				if (!TimeSpan.TryParseExact(text, "c", CultureInfo.InvariantCulture, TimeSpanStyles.None, out var result))
				{
					result = TimeSpan.Parse(text);
				}
				return result;
			}
			if (clrType == typeof(DateTime))
			{
				if (_conn.StoreDateTimeAsTicks)
				{
					return new DateTime(SQLite3.ColumnInt64(stmt, index));
				}
				string s = SQLite3.ColumnString(stmt, index);
				if (!DateTime.TryParseExact(s, _conn.DateTimeStringFormat, CultureInfo.InvariantCulture, _conn.DateTimeStyle, out var result2))
				{
					result2 = DateTime.Parse(s);
				}
				return result2;
			}
			if (clrType == typeof(DateTimeOffset))
			{
				return new DateTimeOffset(SQLite3.ColumnInt64(stmt, index), TimeSpan.Zero);
			}
			if (typeInfo.IsEnum)
			{
				if (type == SQLite3.ColType.Text)
				{
					string text2 = SQLite3.ColumnString(stmt, index);
					return Enum.Parse(clrType, text2.ToString(), ignoreCase: true);
				}
				return SQLite3.ColumnInt(stmt, index);
			}
			if (clrType == typeof(long))
			{
				return SQLite3.ColumnInt64(stmt, index);
			}
			if (clrType == typeof(uint))
			{
				return (uint)SQLite3.ColumnInt64(stmt, index);
			}
			if (clrType == typeof(decimal))
			{
				return (decimal)SQLite3.ColumnDouble(stmt, index);
			}
			if (clrType == typeof(byte))
			{
				return (byte)SQLite3.ColumnInt(stmt, index);
			}
			if (clrType == typeof(ushort))
			{
				return (ushort)SQLite3.ColumnInt(stmt, index);
			}
			if (clrType == typeof(short))
			{
				return (short)SQLite3.ColumnInt(stmt, index);
			}
			if (clrType == typeof(sbyte))
			{
				return (sbyte)SQLite3.ColumnInt(stmt, index);
			}
			if (clrType == typeof(byte[]))
			{
				return SQLite3.ColumnByteArray(stmt, index);
			}
			if (clrType == typeof(Guid))
			{
				string g = SQLite3.ColumnString(stmt, index);
				return new Guid(g);
			}
			if (clrType == typeof(Uri))
			{
				string uriString = SQLite3.ColumnString(stmt, index);
				return new Uri(uriString);
			}
			if (clrType == typeof(StringBuilder))
			{
				string value = SQLite3.ColumnString(stmt, index);
				return new StringBuilder(value);
			}
			if (clrType == typeof(UriBuilder))
			{
				string uri = SQLite3.ColumnString(stmt, index);
				return new UriBuilder(uri);
			}
			throw new NotSupportedException("Don't know how to read " + clrType);
		}
	}
	internal class FastColumnSetter
	{
		internal static Action<object, IntPtr, int> GetFastSetter<T>(SQLiteConnection conn, TableMapping.Column column)
		{
			Action<object, IntPtr, int> result = null;
			Type type = column.PropertyInfo.PropertyType;
			TypeInfo typeInfo = type.GetTypeInfo();
			if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
			{
				type = typeInfo.GenericTypeArguments[0];
				typeInfo = type.GetTypeInfo();
			}
			if (type == typeof(string))
			{
				result = CreateTypedSetterDelegate<T, string>(column, (IntPtr stmt, int index) => SQLite3.ColumnString(stmt, index));
			}
			else if (type == typeof(int))
			{
				result = CreateNullableTypedSetterDelegate<T, int>(column, (IntPtr stmt, int index) => SQLite3.ColumnInt(stmt, index));
			}
			else if (type == typeof(bool))
			{
				result = CreateNullableTypedSetterDelegate<T, bool>(column, (IntPtr stmt, int index) => SQLite3.ColumnInt(stmt, index) == 1);
			}
			else if (type == typeof(double))
			{
				result = CreateNullableTypedSetterDelegate<T, double>(column, (IntPtr stmt, int index) => SQLite3.ColumnDouble(stmt, index));
			}
			else if (type == typeof(float))
			{
				result = CreateNullableTypedSetterDelegate<T, float>(column, (IntPtr stmt, int index) => (float)SQLite3.ColumnDouble(stmt, index));
			}
			else if (type == typeof(TimeSpan))
			{
				result = ((!conn.StoreTimeSpanAsTicks) ? CreateNullableTypedSetterDelegate<T, TimeSpan>(column, delegate(IntPtr stmt, int index)
				{
					string text = SQLite3.ColumnString(stmt, index);
					TimeSpan result3;
					return (!TimeSpan.TryParseExact(text, "c", CultureInfo.InvariantCulture, TimeSpanStyles.None, out result3)) ? TimeSpan.Parse(text) : result3;
				}) : CreateNullableTypedSetterDelegate<T, TimeSpan>(column, (IntPtr stmt, int index) => new TimeSpan(SQLite3.ColumnInt64(stmt, index))));
			}
			else if (type == typeof(DateTime))
			{
				result = ((!conn.StoreDateTimeAsTicks) ? CreateNullableTypedSetterDelegate<T, DateTime>(column, delegate(IntPtr stmt, int index)
				{
					string s = SQLite3.ColumnString(stmt, index);
					DateTime result2;
					return (!DateTime.TryParseExact(s, conn.DateTimeStringFormat, CultureInfo.InvariantCulture, conn.DateTimeStyle, out result2)) ? DateTime.Parse(s) : result2;
				}) : CreateNullableTypedSetterDelegate<T, DateTime>(column, (IntPtr stmt, int index) => new DateTime(SQLite3.ColumnInt64(stmt, index))));
			}
			else if (type == typeof(DateTimeOffset))
			{
				result = CreateNullableTypedSetterDelegate<T, DateTimeOffset>(column, (IntPtr stmt, int index) => new DateTimeOffset(SQLite3.ColumnInt64(stmt, index), TimeSpan.Zero));
			}
			else if (!typeInfo.IsEnum)
			{
				if (type == typeof(long))
				{
					result = CreateNullableTypedSetterDelegate<T, long>(column, (IntPtr stmt, int index) => SQLite3.ColumnInt64(stmt, index));
				}
				else if (type == typeof(uint))
				{
					result = CreateNullableTypedSetterDelegate<T, uint>(column, (IntPtr stmt, int index) => (uint)SQLite3.ColumnInt64(stmt, index));
				}
				else if (type == typeof(decimal))
				{
					result = CreateNullableTypedSetterDelegate<T, decimal>(column, (IntPtr stmt, int index) => (decimal)SQLite3.ColumnDouble(stmt, index));
				}
				else if (type == typeof(byte))
				{
					result = CreateNullableTypedSetterDelegate<T, byte>(column, (IntPtr stmt, int index) => (byte)SQLite3.ColumnInt(stmt, index));
				}
				else if (type == typeof(ushort))
				{
					result = CreateNullableTypedSetterDelegate<T, ushort>(column, (IntPtr stmt, int index) => (ushort)SQLite3.ColumnInt(stmt, index));
				}
				else if (type == typeof(short))
				{
					result = CreateNullableTypedSetterDelegate<T, short>(column, (IntPtr stmt, int index) => (short)SQLite3.ColumnInt(stmt, index));
				}
				else if (type == typeof(sbyte))
				{
					result = CreateNullableTypedSetterDelegate<T, sbyte>(column, (IntPtr stmt, int index) => (sbyte)SQLite3.ColumnInt(stmt, index));
				}
				else if (type == typeof(byte[]))
				{
					result = CreateTypedSetterDelegate<T, byte[]>(column, (IntPtr stmt, int index) => SQLite3.ColumnByteArray(stmt, index));
				}
				else if (type == typeof(Guid))
				{
					result = CreateNullableTypedSetterDelegate<T, Guid>(column, delegate(IntPtr stmt, int index)
					{
						string g = SQLite3.ColumnString(stmt, index);
						return new Guid(g);
					});
				}
				else if (type == typeof(Uri))
				{
					result = CreateTypedSetterDelegate<T, Uri>(column, delegate(IntPtr stmt, int index)
					{
						string uriString = SQLite3.ColumnString(stmt, index);
						return new Uri(uriString);
					});
				}
				else if (type == typeof(StringBuilder))
				{
					result = CreateTypedSetterDelegate<T, StringBuilder>(column, delegate(IntPtr stmt, int index)
					{
						string value = SQLite3.ColumnString(stmt, index);
						return new StringBuilder(value);
					});
				}
				else if (type == typeof(UriBuilder))
				{
					result = CreateTypedSetterDelegate<T, UriBuilder>(column, delegate(IntPtr stmt, int index)
					{
						string uri = SQLite3.ColumnString(stmt, index);
						return new UriBuilder(uri);
					});
				}
			}
			return result;
		}

		private static Action<object, IntPtr, int> CreateNullableTypedSetterDelegate<ObjectType, ColumnMemberType>(TableMapping.Column column, Func<IntPtr, int, ColumnMemberType> getColumnValue) where ColumnMemberType : struct
		{
			TypeInfo typeInfo = column.PropertyInfo.PropertyType.GetTypeInfo();
			bool flag = false;
			if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
			{
				flag = true;
			}
			if (flag)
			{
				Action<ObjectType, ColumnMemberType?> setProperty = (Action<ObjectType, ColumnMemberType?>)Delegate.CreateDelegate(typeof(Action<ObjectType, ColumnMemberType?>), null, column.PropertyInfo.GetSetMethod());
				return delegate(object o, IntPtr stmt, int i)
				{
					SQLite3.ColType colType = SQLite3.ColumnType(stmt, i);
					if (colType != SQLite3.ColType.Null)
					{
						setProperty((ObjectType)o, getColumnValue(stmt, i));
					}
				};
			}
			return CreateTypedSetterDelegate<ObjectType, ColumnMemberType>(column, getColumnValue);
		}

		private static Action<object, IntPtr, int> CreateTypedSetterDelegate<ObjectType, ColumnMemberType>(TableMapping.Column column, Func<IntPtr, int, ColumnMemberType> getColumnValue)
		{
			Action<ObjectType, ColumnMemberType> setProperty = (Action<ObjectType, ColumnMemberType>)Delegate.CreateDelegate(typeof(Action<ObjectType, ColumnMemberType>), null, column.PropertyInfo.GetSetMethod());
			return delegate(object o, IntPtr stmt, int i)
			{
				SQLite3.ColType colType = SQLite3.ColumnType(stmt, i);
				if (colType != SQLite3.ColType.Null)
				{
					setProperty((ObjectType)o, getColumnValue(stmt, i));
				}
			};
		}
	}
	internal class PreparedSqlLiteInsertCommand : IDisposable
	{
		private bool Initialized;

		private SQLiteConnection Connection;

		private string CommandText;

		private IntPtr Statement;

		private static readonly IntPtr NullStatement;

		public PreparedSqlLiteInsertCommand(SQLiteConnection conn, string commandText)
		{
			Connection = conn;
			CommandText = commandText;
		}

		public int ExecuteNonQuery(object[] source)
		{
			if (Initialized && Statement == NullStatement)
			{
				throw new ObjectDisposedException("PreparedSqlLiteInsertCommand");
			}
			if (Connection.Trace)
			{
				Connection.Tracer?.Invoke("Executing: " + CommandText);
			}
			SQLite3.Result result = SQLite3.Result.OK;
			if (!Initialized)
			{
				Statement = SQLite3.Prepare2(Connection.Handle, CommandText);
				Initialized = true;
			}
			if (source != null)
			{
				for (int i = 0; i < source.Length; i++)
				{
					SQLiteCommand.BindParameter(Statement, i + 1, source[i], Connection.StoreDateTimeAsTicks, Connection.DateTimeStringFormat, Connection.StoreTimeSpanAsTicks);
				}
			}
			result = SQLite3.Step(Statement);
			switch (result)
			{
			case SQLite3.Result.Done:
			{
				int result2 = SQLite3.Changes(Connection.Handle);
				SQLite3.Reset(Statement);
				return result2;
			}
			case SQLite3.Result.Error:
			{
				string errmsg = SQLite3.GetErrmsg(Connection.Handle);
				SQLite3.Reset(Statement);
				throw SQLiteException.New(result, errmsg);
			}
			case SQLite3.Result.Constraint:
				if (SQLite3.ExtendedErrCode(Connection.Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
				{
					SQLite3.Reset(Statement);
					throw NotNullConstraintViolationException.New(result, SQLite3.GetErrmsg(Connection.Handle));
				}
				break;
			}
			SQLite3.Reset(Statement);
			throw SQLiteException.New(result, SQLite3.GetErrmsg(Connection.Handle));
		}

		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			IntPtr statement = Statement;
			Statement = NullStatement;
			Connection = null;
			if (statement != NullStatement)
			{
				SQLite3.Finalize(statement);
			}
		}

		~PreparedSqlLiteInsertCommand()
		{
			Dispose(disposing: false);
		}
	}
	public enum CreateTableResult
	{
		Created,
		Migrated
	}
	public class CreateTablesResult
	{
		public Dictionary<Type, CreateTableResult> Results { get; private set; }

		public CreateTablesResult()
		{
			Results = new Dictionary<Type, CreateTableResult>();
		}
	}
	public abstract class BaseTableQuery
	{
		protected class Ordering
		{
			public string ColumnName { get; set; }

			public bool Ascending { get; set; }
		}
	}
	public class TableQuery<T> : BaseTableQuery, IEnumerable<T>, IEnumerable
	{
		private class CompileResult
		{
			public string CommandText { get; set; }

			public object Value { get; set; }
		}

		private Expression _where;

		private List<Ordering> _orderBys;

		private int? _limit;

		private int? _offset;

		private BaseTableQuery _joinInner;

		private Expression _joinInnerKeySelector;

		private BaseTableQuery _joinOuter;

		private Expression _joinOuterKeySelector;

		private Expression _joinSelector;

		private Expression _selector;

		private bool _deferred;

		public SQLiteConnection Connection { get; private set; }

		public TableMapping Table { get; private set; }

		private TableQuery(SQLiteConnection conn, TableMapping table)
		{
			Connection = conn;
			Table = table;
		}

		public TableQuery(SQLiteConnection conn)
		{
			Connection = conn;
			Table = Connection.GetMapping(typeof(T));
		}

		public TableQuery<U> Clone<U>()
		{
			TableQuery<U> tableQuery = new TableQuery<U>(Connection, Table);
			tableQuery._where = _where;
			tableQuery._deferred = _deferred;
			if (_orderBys != null)
			{
				tableQuery._orderBys = new List<Ordering>(_orderBys);
			}
			tableQuery._limit = _limit;
			tableQuery._offset = _offset;
			tableQuery._joinInner = _joinInner;
			tableQuery._joinInnerKeySelector = _joinInnerKeySelector;
			tableQuery._joinOuter = _joinOuter;
			tableQuery._joinOuterKeySelector = _joinOuterKeySelector;
			tableQuery._joinSelector = _joinSelector;
			tableQuery._selector = _selector;
			return tableQuery;
		}

		public TableQuery<T> Where(Expression<Func<T, bool>> predExpr)
		{
			if (predExpr.NodeType == ExpressionType.Lambda)
			{
				Expression body = predExpr.Body;
				TableQuery<T> tableQuery = Clone<T>();
				tableQuery.AddWhere(body);
				return tableQuery;
			}
			throw new NotSupportedException("Must be a predicate");
		}

		public int Delete()
		{
			return Delete(null);
		}

		public int Delete(Expression<Func<T, bool>> predExpr)
		{
			if (_limit.HasValue || _offset.HasValue)
			{
				throw new InvalidOperationException("Cannot delete with limits or offsets");
			}
			if (_where == null && predExpr == null)
			{
				throw new InvalidOperationException("No condition specified");
			}
			Expression expression = _where;
			if (predExpr != null && predExpr.NodeType == ExpressionType.Lambda)
			{
				expression = ((expression != null) ? Expression.AndAlso(expression, predExpr.Body) : predExpr.Body);
			}
			List<object> list = new List<object>();
			string text = "delete from \"" + Table.TableName + "\"";
			CompileResult compileResult = CompileExpr(expression, list);
			text = text + " where " + compileResult.CommandText;
			SQLiteCommand sQLiteCommand = Connection.CreateCommand(text, list.ToArray());
			return sQLiteCommand.ExecuteNonQuery();
		}

		public TableQuery<T> Take(int n)
		{
			TableQuery<T> tableQuery = Clone<T>();
			tableQuery._limit = n;
			return tableQuery;
		}

		public TableQuery<T> Skip(int n)
		{
			TableQuery<T> tableQuery = Clone<T>();
			tableQuery._offset = n;
			return tableQuery;
		}

		public T ElementAt(int index)
		{
			return Skip(index).Take(1).First();
		}

		public TableQuery<T> Deferred()
		{
			TableQuery<T> tableQuery = Clone<T>();
			tableQuery._deferred = true;
			return tableQuery;
		}

		public TableQuery<T> OrderBy<U>(Expression<Func<T, U>> orderExpr)
		{
			return AddOrderBy(orderExpr, asc: true);
		}

		public TableQuery<T> OrderByDescending<U>(Expression<Func<T, U>> orderExpr)
		{
			return AddOrderBy(orderExpr, asc: false);
		}

		public TableQuery<T> ThenBy<U>(Expression<Func<T, U>> orderExpr)
		{
			return AddOrderBy(orderExpr, asc: true);
		}

		public TableQuery<T> ThenByDescending<U>(Expression<Func<T, U>> orderExpr)
		{
			return AddOrderBy(orderExpr, asc: false);
		}

		private TableQuery<T> AddOrderBy<U>(Expression<Func<T, U>> orderExpr, bool asc)
		{
			if (orderExpr.NodeType == ExpressionType.Lambda)
			{
				MemberExpression memberExpression = null;
				memberExpression = ((!(orderExpr.Body is UnaryExpression unaryExpression) || unaryExpression.NodeType != ExpressionType.Convert) ? (orderExpr.Body as MemberExpression) : (unaryExpression.Operand as MemberExpression));
				if (memberExpression != null && memberExpression.Expression.NodeType == ExpressionType.Parameter)
				{
					TableQuery<T> tableQuery = Clone<T>();
					if (tableQuery._orderBys == null)
					{
						tableQuery._orderBys = new List<Ordering>();
					}
					tableQuery._orderBys.Add(new Ordering
					{
						ColumnName = Table.FindColumnWithPropertyName(memberExpression.Member.Name).Name,
						Ascending = asc
					});
					return tableQuery;
				}
				throw new NotSupportedException("Order By does not support: " + orderExpr);
			}
			throw new NotSupportedException("Must be a predicate");
		}

		private void AddWhere(Expression pred)
		{
			if (_where == null)
			{
				_where = pred;
			}
			else
			{
				_where = Expression.AndAlso(_where, pred);
			}
		}

		private SQLiteCommand GenerateCommand(string selectionList)
		{
			if (_joinInner != null && _joinOuter != null)
			{
				throw new NotSupportedException("Joins are not supported.");
			}
			string text = "select " + selectionList + " from \"" + Table.TableName + "\"";
			List<object> list = new List<object>();
			if (_where != null)
			{
				CompileResult compileResult = CompileExpr(_where, list);
				text = text + " where " + compileResult.CommandText;
			}
			if (_orderBys != null && _orderBys.Count > 0)
			{
				string text2 = string.Join(", ", _orderBys.Select((Ordering o) => "\"" + o.ColumnName + "\"" + (o.Ascending ? "" : " desc")).ToArray());
				text = text + " order by " + text2;
			}
			if (_limit.HasValue)
			{
				text = text + " limit " + _limit.Value;
			}
			if (_offset.HasValue)
			{
				if (!_limit.HasValue)
				{
					text += " limit -1 ";
				}
				text = text + " offset " + _offset.Value;
			}
			return Connection.CreateCommand(text, list.ToArray());
		}

		private CompileResult CompileExpr(Expression expr, List<object> queryArgs)
		{
			if (expr == null)
			{
				throw new NotSupportedException("Expression is NULL");
			}
			if (expr is BinaryExpression)
			{
				BinaryExpression binaryExpression = (BinaryExpression)expr;
				if (binaryExpression.Left.NodeType == ExpressionType.Call)
				{
					MethodCallExpression methodCallExpression = (MethodCallExpression)binaryExpression.Left;
					if (methodCallExpression.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators" && methodCallExpression.Method.Name == "CompareString")
					{
						binaryExpression = Expression.MakeBinary(binaryExpression.NodeType, methodCallExpression.Arguments[0], methodCallExpression.Arguments[1]);
					}
				}
				CompileResult compileResult = CompileExpr(binaryExpression.Left, queryArgs);
				CompileResult compileResult2 = CompileExpr(binaryExpression.Right, queryArgs);
				string commandText = ((compileResult.CommandText == "?" && compileResult.Value == null) ? CompileNullBinaryExpression(binaryExpression, compileResult2) : ((!(compileResult2.CommandText == "?") || compileResult2.Value != null) ? ("(" + compileResult.CommandText + " " + GetSqlName(binaryExpression) + " " + compileResult2.CommandText + ")") : CompileNullBinaryExpression(binaryExpression, compileResult)));
				return new CompileResult
				{
					CommandText = commandText
				};
			}
			if (expr.NodeType == ExpressionType.Not)
			{
				Expression operand = ((UnaryExpression)expr).Operand;
				CompileResult compileResult3 = CompileExpr(operand, queryArgs);
				object obj = compileResult3.Value;
				if (obj is bool)
				{
					obj = !(bool)obj;
				}
				return new CompileResult
				{
					CommandText = "NOT(" + compileResult3.CommandText + ")",
					Value = obj
				};
			}
			if (expr.NodeType == ExpressionType.Call)
			{
				MethodCallExpression methodCallExpression2 = (MethodCallExpression)expr;
				CompileResult[] array = new CompileResult[methodCallExpression2.Arguments.Count];
				CompileResult compileResult4 = ((methodCallExpression2.Object != null) ? CompileExpr(methodCallExpression2.Object, queryArgs) : null);
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = CompileExpr(methodCallExpression2.Arguments[i], queryArgs);
				}
				string commandText2 = "";
				if (methodCallExpression2.Method.Name == "Like" && array.Length == 2)
				{
					commandText2 = "(" + array[0].CommandText + " like " + array[1].CommandText + ")";
				}
				else if (methodCallExpression2.Method.Name == "Contains" && array.Length == 2)
				{
					commandText2 = "(" + array[1].CommandText + " in " + array[0].CommandText + ")";
				}
				else if (methodCallExpression2.Method.Name == "Contains" && array.Length == 1)
				{
					commandText2 = ((methodCallExpression2.Object == null || !(methodCallExpression2.Object.Type == typeof(string))) ? ("(" + array[0].CommandText + " in " + compileResult4.CommandText + ")") : ("( instr(" + compileResult4.CommandText + "," + array[0].CommandText + ") >0 )"));
				}
				else if (methodCallExpression2.Method.Name == "StartsWith" && array.Length >= 1)
				{
					StringComparison stringComparison = StringComparison.CurrentCulture;
					if (array.Length == 2)
					{
						stringComparison = (StringComparison)array[1].Value;
					}
					switch (stringComparison)
					{
					case StringComparison.CurrentCulture:
					case StringComparison.Ordinal:
						commandText2 = "( substr(" + compileResult4.CommandText + ", 1, " + array[0].Value.ToString().Length + ") =  " + array[0].CommandText + ")";
						break;
					case StringComparison.CurrentCultureIgnoreCase:
					case StringComparison.OrdinalIgnoreCase:
						commandText2 = "(" + compileResult4.CommandText + " like (" + array[0].CommandText + " || '%'))";
						break;
					}
				}
				else if (!(methodCallExpression2.Method.Name == "EndsWith") || array.Length < 1)
				{
					commandText2 = ((methodCallExpression2.Method.Name == "Equals" && array.Length == 1) ? ("(" + compileResult4.CommandText + " = (" + array[0].CommandText + "))") : ((methodCallExpression2.Method.Name == "ToLower") ? ("(lower(" + compileResult4.CommandText + "))") : ((methodCallExpression2.Method.Name == "ToUpper") ? ("(upper(" + compileResult4.CommandText + "))") : ((methodCallExpression2.Method.Name == "Replace" && array.Length == 2) ? ("(replace(" + compileResult4.CommandText + "," + array[0].CommandText + "," + array[1].CommandText + "))") : ((!(methodCallExpression2.Method.Name == "IsNullOrEmpty") || array.Length != 1) ? (methodCallExpression2.Method.Name.ToLower() + "(" + string.Join(",", array.Select((CompileResult a) => a.CommandText).ToArray()) + ")") : ("(" + array[0].CommandText + " is null or" + array[0].CommandText + " ='' )"))))));
				}
				else
				{
					StringComparison stringComparison2 = StringComparison.CurrentCulture;
					if (array.Length == 2)
					{
						stringComparison2 = (StringComparison)array[1].Value;
					}
					switch (stringComparison2)
					{
					case StringComparison.CurrentCulture:
					case StringComparison.Ordinal:
						commandText2 = "( substr(" + compileResult4.CommandText + ", length(" + compileResult4.CommandText + ") - " + array[0].Value.ToString().Length + "+1, " + array[0].Value.ToString().Length + ") =  " + array[0].CommandText + ")";
						break;
					case StringComparison.CurrentCultureIgnoreCase:
					case StringComparison.OrdinalIgnoreCase:
						commandText2 = "(" + compileResult4.CommandText + " like ('%' || " + array[0].CommandText + "))";
						break;
					}
				}
				return new CompileResult
				{
					CommandText = commandText2
				};
			}
			if (expr.NodeType == ExpressionType.Constant)
			{
				ConstantExpression constantExpression = (ConstantExpression)expr;
				queryArgs.Add(constantExpression.Value);
				return new CompileResult
				{
					CommandText = "?",
					Value = constantExpression.Value
				};
			}
			if (expr.NodeType == ExpressionType.Convert)
			{
				UnaryExpression unaryExpression = (UnaryExpression)expr;
				Type type = unaryExpression.Type;
				CompileResult compileResult5 = CompileExpr(unaryExpression.Operand, queryArgs);
				return new CompileResult
				{
					CommandText = compileResult5.CommandText,
					Value = ((compileResult5.Value != null) ? ConvertTo(compileResult5.Value, type) : null)
				};
			}
			if (expr.NodeType == ExpressionType.MemberAccess)
			{
				MemberExpression memberExpression = (MemberExpression)expr;
				ParameterExpression parameterExpression = memberExpression.Expression as ParameterExpression;
				if (parameterExpression == null && memberExpression.Expression is UnaryExpression unaryExpression2 && unaryExpression2.NodeType == ExpressionType.Convert)
				{
					parameterExpression = unaryExpression2.Operand as ParameterExpression;
				}
				if (parameterExpression != null)
				{
					string name = Table.FindColumnWithPropertyName(memberExpression.Member.Name).Name;
					return new CompileResult
					{
						CommandText = "\"" + name + "\""
					};
				}
				object obj2 = null;
				if (memberExpression.Expression != null)
				{
					CompileResult compileResult6 = CompileExpr(memberExpression.Expression, queryArgs);
					if (compileResult6.Value == null)
					{
						throw new NotSupportedException("Member access failed to compile expression");
					}
					if (compileResult6.CommandText == "?")
					{
						queryArgs.RemoveAt(queryArgs.Count - 1);
					}
					obj2 = compileResult6.Value;
				}
				object obj3 = null;
				if (memberExpression.Member is PropertyInfo)
				{
					PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;
					obj3 = propertyInfo.GetValue(obj2, null);
				}
				else
				{
					if (!(memberExpression.Member is FieldInfo))
					{
						throw new NotSupportedException("MemberExpr: " + memberExpression.Member.GetType());
					}
					FieldInfo fieldInfo = (FieldInfo)memberExpression.Member;
					obj3 = fieldInfo.GetValue(obj2);
				}
				if (obj3 != null && obj3 is IEnumerable && !(obj3 is string) && !(obj3 is IEnumerable<byte>))
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.Append("(");
					string value = "";
					foreach (object item in (IEnumerable)obj3)
					{
						queryArgs.Add(item);
						stringBuilder.Append(value);
						stringBuilder.Append("?");
						value = ",";
					}
					stringBuilder.Append(")");
					return new CompileResult
					{
						CommandText = stringBuilder.ToString(),
						Value = obj3
					};
				}
				queryArgs.Add(obj3);
				return new CompileResult
				{
					CommandText = "?",
					Value = obj3
				};
			}
			throw new NotSupportedException("Cannot compile: " + expr.NodeType);
		}

		private static object ConvertTo(object obj, Type t)
		{
			Type underlyingType = Nullable.GetUnderlyingType(t);
			if (underlyingType != null)
			{
				if (obj == null)
				{
					return null;
				}
				return Convert.ChangeType(obj, underlyingType);
			}
			return Convert.ChangeType(obj, t);
		}

		private string CompileNullBinaryExpression(BinaryExpression expression, CompileResult parameter)
		{
			if (expression.NodeType == ExpressionType.Equal)
			{
				return "(" + parameter.CommandText + " is ?)";
			}
			if (expression.NodeType == ExpressionType.NotEqual)
			{
				return "(" + parameter.CommandText + " is not ?)";
			}
			if (expression.NodeType == ExpressionType.GreaterThan || expression.NodeType == ExpressionType.GreaterThanOrEqual || expression.NodeType == ExpressionType.LessThan || expression.NodeType == ExpressionType.LessThanOrEqual)
			{
				return "(" + parameter.CommandText + " < ?)";
			}
			throw new NotSupportedException("Cannot compile Null-BinaryExpression with type " + expression.NodeType);
		}

		private string GetSqlName(Expression expr)
		{
			ExpressionType nodeType = expr.NodeType;
			return nodeType switch
			{
				ExpressionType.GreaterThan => ">", 
				ExpressionType.GreaterThanOrEqual => ">=", 
				ExpressionType.LessThan => "<", 
				ExpressionType.LessThanOrEqual => "<=", 
				ExpressionType.And => "&", 
				ExpressionType.AndAlso => "and", 
				ExpressionType.Or => "|", 
				ExpressionType.OrElse => "or", 
				ExpressionType.Equal => "=", 
				ExpressionType.NotEqual => "!=", 
				_ => throw new NotSupportedException("Cannot get SQL for: " + nodeType), 
			};
		}

		public int Count()
		{
			return GenerateCommand("count(*)").ExecuteScalar<int>();
		}

		public int Count(Expression<Func<T, bool>> predExpr)
		{
			return Where(predExpr).Count();
		}

		public IEnumerator<T> GetEnumerator()
		{
			if (!_deferred)
			{
				return GenerateCommand("*").ExecuteQuery<T>().GetEnumerator();
			}
			return GenerateCommand("*").ExecuteDeferredQuery<T>().GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		public List<T> ToList()
		{
			return GenerateCommand("*").ExecuteQuery<T>();
		}

		public T[] ToArray()
		{
			return GenerateCommand("*").ExecuteQuery<T>().ToArray();
		}

		public T First()
		{
			TableQuery<T> tableQuery = Take(1);
			return tableQuery.ToList().First();
		}

		public T FirstOrDefault()
		{
			TableQuery<T> tableQuery = Take(1);
			return tableQuery.ToList().FirstOrDefault();
		}

		public T First(Expression<Func<T, bool>> predExpr)
		{
			return Where(predExpr).First();
		}

		public T FirstOrDefault(Expression<Func<T, bool>> predExpr)
		{
			return Where(predExpr).FirstOrDefault();
		}
	}
	public static class SQLite3
	{
		public enum Result
		{
			OK = 0,
			Error = 1,
			Internal = 2,
			Perm = 3,
			Abort = 4,
			Busy = 5,
			Locked = 6,
			NoMem = 7,
			ReadOnly = 8,
			Interrupt = 9,
			IOError = 10,
			Corrupt = 11,
			NotFound = 12,
			Full = 13,
			CannotOpen = 14,
			LockErr = 15,
			Empty = 16,
			SchemaChngd = 17,
			TooBig = 18,
			Constraint = 19,
			Mismatch = 20,
			Misuse = 21,
			NotImplementedLFS = 22,
			AccessDenied = 23,
			Format = 24,
			Range = 25,
			NonDBFile = 26,
			Notice = 27,
			Warning = 28,
			Row = 100,
			Done = 101
		}

		public enum ExtendedResult
		{
			IOErrorRead = 266,
			IOErrorShortRead = 522,
			IOErrorWrite = 778,
			IOErrorFsync = 1034,
			IOErrorDirFSync = 1290,
			IOErrorTruncate = 1546,
			IOErrorFStat = 1802,
			IOErrorUnlock = 2058,
			IOErrorRdlock = 2314,
			IOErrorDelete = 2570,
			IOErrorBlocked = 2826,
			IOErrorNoMem = 3082,
			IOErrorAccess = 3338,
			IOErrorCheckReservedLock = 3594,
			IOErrorLock = 3850,
			IOErrorClose = 4106,
			IOErrorDirClose = 4362,
			IOErrorSHMOpen = 4618,
			IOErrorSHMSize = 4874,
			IOErrorSHMLock = 5130,
			IOErrorSHMMap = 5386,
			IOErrorSeek = 5642,
			IOErrorDeleteNoEnt = 5898,
			IOErrorMMap = 6154,
			LockedSharedcache = 262,
			BusyRecovery = 261,
			CannottOpenNoTempDir = 270,
			CannotOpenIsDir = 526,
			CannotOpenFullPath = 782,
			CorruptVTab = 267,
			ReadonlyRecovery = 264,
			ReadonlyCannotLock = 520,
			ReadonlyRollback = 776,
			AbortRollback = 516,
			ConstraintCheck = 275,
			ConstraintCommitHook = 531,
			ConstraintForeignKey = 787,
			ConstraintFunction = 1043,
			ConstraintNotNull = 1299,
			ConstraintPrimaryKey = 1555,
			ConstraintTrigger = 1811,
			ConstraintUnique = 2067,
			ConstraintVTab = 2323,
			NoticeRecoverWAL = 283,
			NoticeRecoverRollback = 539
		}

		public enum ConfigOption
		{
			SingleThread = 1,
			MultiThread,
			Serialized
		}

		public enum ColType
		{
			Integer = 1,
			Float,
			Text,
			Blob,
			Null
		}

		private const string LibraryPath = "sqlite3";

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_threadsafe")]
		public static extern int Threadsafe();

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_open")]
		public static extern Result Open([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_open_v2")]
		public static extern Result Open([MarshalAs(UnmanagedType.LPStr)] string filename, out IntPtr db, int flags, [MarshalAs(UnmanagedType.LPStr)] string zvfs);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_open_v2")]
		public static extern Result Open(byte[] filename, out IntPtr db, int flags, [MarshalAs(UnmanagedType.LPStr)] string zvfs);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_open16")]
		public static extern Result Open16([MarshalAs(UnmanagedType.LPWStr)] string filename, out IntPtr db);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_enable_load_extension")]
		public static extern Result EnableLoadExtension(IntPtr db, int onoff);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_close")]
		public static extern Result Close(IntPtr db);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_close_v2")]
		public static extern Result Close2(IntPtr db);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_initialize")]
		public static extern Result Initialize();

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_shutdown")]
		public static extern Result Shutdown();

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_config")]
		public static extern Result Config(ConfigOption option);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "sqlite3_win32_set_directory")]
		public static extern int SetDirectory(uint directoryType, string directoryPath);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_busy_timeout")]
		public static extern Result BusyTimeout(IntPtr db, int milliseconds);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_changes")]
		public static extern int Changes(IntPtr db);

		[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl, EntryPoint = "sqlite3_prepare_v2")]
		public