摘自编译后的.cs文件,版权归原作者所有:
// Decompiled with JetBrains decompiler
// Type: SQLite.SQLiteConnection
// Assembly: SQLite-net, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E6176266-E10E-4143-A13A-15E20E5B743C
// Assembly location: ...\packages\sqlite-net-pcl.1.4.118\lib\netstandard1.1\SQLite-net.dll
using SQLitePCL;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
namespace SQLite
{
/// <summary>Represents an open connection to a SQLite database.</summary>
public class SQLiteConnection : IDisposable
{
private Random _rand = new Random();
private bool _open;
private TimeSpan _busyTimeout;
private Dictionary<string, TableMapping> _mappings;
private Dictionary<string, TableMapping> _tables;
private Stopwatch _sw;
private long _elapsedMilliseconds;
private int _transactionDepth;
internal static readonly sqlite3 NullHandle;
public sqlite3 Handle { get; private set; }
public string DatabasePath { 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; }
static SQLiteConnection()
{
Batteries_V2.Init();
}
/// <summary>
/// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
/// </summary>
/// <param name="databasePath">
/// Specifies the path to the database file.
/// </param>
/// <param name="storeDateTimeAsTicks">
/// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
/// absolutely do want to store them as Ticks in all new projects. The value of false is
/// only here for backwards compatibility. There is a *significant* speed advantage, with no
/// down sides, when setting storeDateTimeAsTicks = true.
/// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
/// the storeDateTimeAsTicks parameter.
/// </param>
public SQLiteConnection(string databasePath, bool storeDateTimeAsTicks = true)
: this(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
{
}
/// <summary>
/// Constructs a new SQLiteConnection and opens a SQLite database specified by databasePath.
/// </summary>
/// <param name="databasePath">
/// Specifies the path to the database file.
/// </param>
/// <param name="openFlags">
/// Flags controlling how the connection should be opened.
/// </param>
/// <param name="storeDateTimeAsTicks">
/// Specifies whether to store DateTime properties as ticks (true) or strings (false). You
/// absolutely do want to store them as Ticks in all new projects. The value of false is
/// only here for backwards compatibility. There is a *significant* speed advantage, with no
/// down sides, when setting storeDateTimeAsTicks = true.
/// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
/// the storeDateTimeAsTicks parameter.
/// </param>
public SQLiteConnection(string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
{
if (string.IsNullOrEmpty(databasePath))
throw new ArgumentException("Must be specified", nameof (databasePath));
this.DatabasePath = databasePath;
sqlite3 db;
SQLite3.Result r = SQLite3.Open(databasePath, out db, (int) openFlags, IntPtr.Zero);
this.Handle = db;
if (r != SQLite3.Result.OK)
throw SQLiteException.New(r, string.Format("Could not open database file: {0} ({1})", new object[2]
{
(object) this.DatabasePath,
(object) r
}));
this._open = true;
this.StoreDateTimeAsTicks = storeDateTimeAsTicks;
this.BusyTimeout = TimeSpan.FromSeconds(0.1);
this.Tracer = (Action<string>) (line => {});
}
/// <summary>
/// Sets a busy handler to sleep the specified amount of time when a table is locked.
/// The handler will sleep multiple times until a total time of <see cref="P:SQLite.SQLiteConnection.BusyTimeout" /> has accumulated.
/// </summary>
public TimeSpan BusyTimeout
{
get
{
return this._busyTimeout;
}
set
{
this._busyTimeout = value;
if (this.Handle == SQLiteConnection.NullHandle)
return;
int num = (int) SQLite3.BusyTimeout(this.Handle, (int) this._busyTimeout.TotalMilliseconds);
}
}
/// <summary>
/// Returns the mappings from types to tables that the connection
/// currently understands.
/// </summary>
public IEnumerable<TableMapping> TableMappings
{
get
{
if (this._tables == null)
return Enumerable.Empty<TableMapping>();
return (IEnumerable<TableMapping>) this._tables.Values;
}
}
/// <summary>
/// Retrieves the mapping that is automatically generated for the given type.
/// </summary>
/// <param name="type">
/// The type whose mapping to the database is returned.
/// </param>
/// <param name="createFlags">
/// Optional flags allowing implicit PK and indexes based on naming conventions
/// </param>
/// <returns>
/// The mapping represents the schema of the columns of the database and contains
/// methods to set and get properties of objects.
/// </returns>
public TableMapping GetMapping(Type type, CreateFlags createFlags = CreateFlags.None)
{
if (this._mappings == null)
this._mappings = new Dictionary<string, TableMapping>();
TableMapping tableMapping;
if (!this._mappings.TryGetValue(type.FullName, out tableMapping))
{
tableMapping = new TableMapping(type, createFlags);
this._mappings[type.FullName] = tableMapping;
}
return tableMapping;
}
/// <summary>
/// Retrieves the mapping that is automatically generated for the given type.
/// </summary>
/// <param name="createFlags">
/// Optional flags allowing implicit PK and indexes based on naming conventions
/// </param>
/// <returns>
/// The mapping represents the schema of the columns of the database and contains
/// methods to set and get properties of objects.
/// </returns>
public TableMapping GetMapping<T>(CreateFlags createFlags = CreateFlags.None)
{
return this.GetMapping(typeof (T), createFlags);
}
/// <summary>
/// Executes a "drop table" on the database. This is non-recoverable.
/// </summary>
public int DropTable<T>()
{
return this.DropTable(this.GetMapping(typeof (T), CreateFlags.None));
}
/// <summary>
/// Executes a "drop table" on the database. This is non-recoverable.
/// </summary>
/// <param name="map">The TableMapping used to identify the table.</param>
public int DropTable(TableMapping map)
{
return this.Execute(string.Format("drop table if exists \"{0}\"", new object[1]
{
(object) map.TableName
}));
}
/// <summary>
/// Executes a "create table if not exists" on the database. It also
/// creates any specified indexes on the columns of the table. It uses
/// a schema automatically generated from the specified type. You can
/// later access this schema by calling GetMapping.
/// </summary>
/// <returns>The number of entries added to the database schema.</returns>
public int CreateTable<T>(CreateFlags createFlags = CreateFlags.None)
{
return this.CreateTable(typeof (T), createFlags);
}
/// <summary>
/// Executes a "create table if not exists" on the database. It also
/// creates any specified indexes on the columns of the table. It uses
/// a schema automatically generated from the specified type. You can
/// later access this schema by calling GetMapping.
/// </summary>
/// <param name="ty">Type to reflect to a database table.</param>
/// <param name="createFlags">Optional flags allowing implicit PK and indexes based on naming conventions.</param>
/// <returns>The number of entries added to the database schema.</returns>
public int CreateTable(Type ty, CreateFlags createFlags = CreateFlags.None)
{
if (this._tables == null)
this._tables = new Dictionary<string, TableMapping>();
TableMapping mapping;
if (!this._tables.TryGetValue(ty.FullName, out mapping))
{
mapping = this.GetMapping(ty, createFlags);
this._tables.Add(ty.FullName, mapping);
}
if (mapping.Columns.Length == 0)
throw new Exception(string.Format("Cannot create a table with zero columns (does '{0}' have public properties?)", new object[1]
{
(object) ty.FullName
}));
bool flag1 = (uint) (createFlags & CreateFlags.FullTextSearch3) > 0U;
bool flag2 = (uint) (createFlags & CreateFlags.FullTextSearch4) > 0U;
string str1 = flag1 | flag2 ? "virtual " : string.Empty;
string str2 = flag1 ? "using fts3 " : (flag2 ? "using fts4 " : string.Empty);
int num = this.Execute("create " + str1 + "table if not exists \"" + mapping.TableName + "\" " + str2 + "(\n" + string.Join(",\n", ((IEnumerable<TableMapping.Column>) mapping.Columns).Select<TableMapping.Column, string>((Func<TableMapping.Column, string>) (p => Orm.SqlDecl(p, this.StoreDateTimeAsTicks))).ToArray<string>()) + ")");
if (num == 0)
this.MigrateTable(mapping);
Dictionary<string, SQLiteConnection.IndexInfo> dictionary = new Dictionary<string, SQLiteConnection.IndexInfo>();
foreach (TableMapping.Column column in mapping.Columns)
{
foreach (IndexedAttribute index in column.Indices)
{
string key = index.Name ?? mapping.TableName + "_" + column.Name;
SQLiteConnection.IndexInfo indexInfo;
if (!dictionary.TryGetValue(key, out indexInfo))
{
indexInfo = new SQLiteConnection.IndexInfo()
{
IndexName = key,
TableName = mapping.TableName,
Unique = index.Unique,
Columns = new List<SQLiteConnection.IndexedColumn>()
};
dictionary.Add(key, indexInfo);
}
if (index.Unique != indexInfo.Unique)
throw new Exception("All the columns in an index must have the same value for their Unique property");
indexInfo.Columns.Add(new SQLiteConnection.IndexedColumn()
{
Order = index.Order,
ColumnName = column.Name
});
}
}
foreach (string key in dictionary.Keys)
{
SQLiteConnection.IndexInfo indexInfo = dictionary[key];
string[] array = indexInfo.Columns.OrderBy<SQLiteConnection.IndexedColumn, int>((Func<SQLiteConnection.IndexedColumn, int>) (i => i.Order)).Select<SQLiteConnection.IndexedColumn, string>((Func<SQLiteConnection.IndexedColumn, string>) (i => i.ColumnName)).ToArray<string>();
num += this.CreateIndex(key, indexInfo.TableName, array, indexInfo.Unique);
}
return num;
}
/// <summary>Creates an index for the specified table and columns.</summary>
/// <param name="indexName">Name of the index to create</param>
/// <param name="tableName">Name of the database table</param>
/// <param name="columnNames">An array of column names to index</param>
/// <param name="unique">Whether the index should be unique</param>
public int CreateIndex(string indexName, string tableName, string[] columnNames, bool unique = false)
{
return this.Execute(string.Format("create {2} index if not exists \"{3}\" on \"{0}\"(\"{1}\")", (object) tableName, (object) string.Join("\", \"", columnNames), unique ? (object) nameof (unique) : (object) "", (object) indexName));
}
/// <summary>Creates an index for the specified table and column.</summary>
/// <param name="indexName">Name of the index to create</param>
/// <param name="tableName">Name of the database table</param>
/// <param name="columnName">Name of the column to index</param>
/// <param name="unique">Whether the index should be unique</param>
public int CreateIndex(string indexName, string tableName, string columnName, bool unique = false)
{
return this.CreateIndex(indexName, tableName, new string[1]
{
columnName
}, (unique ? 1 : 0) != 0);
}
/// <summary>Creates an index for the specified table and column.</summary>
/// <param name="tableName">Name of the database table</param>
/// <param name="columnName">Name of the column to index</param>
/// <param name="unique">Whether the index should be unique</param>
public int CreateIndex(string tableName, string columnName, bool unique = false)
{
return this.CreateIndex(tableName + "_" + columnName, tableName, columnName, unique);
}
/// <summary>Creates an index for the specified table and columns.</summary>
/// <param name="tableName">Name of the database table</param>
/// <param name="columnNames">An array of column names to index</param>
/// <param name="unique">Whether the index should be unique</param>
public int CreateIndex(string tableName, string[] columnNames, bool unique = false)
{
return this.CreateIndex(tableName + "_" + string.Join("_", columnNames), tableName, columnNames, unique);
}
/// <summary>
/// Creates an index for the specified object property.
/// e.g. CreateIndex<Client>(c => c.Name);
/// </summary>
/// <typeparam name="T">Type to reflect to a database table.</typeparam>
/// <param name="property">Property to index</param>
/// <param name="unique">Whether the index should be unique</param>
public void CreateIndex<T>(Expression<Func<T, object>> property, bool unique = false)
{
PropertyInfo member = (property.Body.NodeType != ExpressionType.Convert ? property.Body as MemberExpression : ((UnaryExpression) property.Body).Operand as MemberExpression).Member as PropertyInfo;
if ((object) member == null)
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
string name1 = member.Name;
TableMapping mapping = this.GetMapping<T>(CreateFlags.None);
string name2 = mapping.FindColumnWithPropertyName(name1).Name;
this.CreateIndex(mapping.TableName, name2, unique);
}
public List<SQLiteConnection.ColumnInfo> GetTableInfo(string tableName)
{
return this.Query<SQLiteConnection.ColumnInfo>("pragma table_info(\"" + tableName + "\")");
}
private void MigrateTable(TableMapping map)
{
List<SQLiteConnection.ColumnInfo> tableInfo = this.GetTableInfo(map.TableName);
List<TableMapping.Column> columnList = new List<TableMapping.Column>();
foreach (TableMapping.Column column in map.Columns)
{
bool flag = false;
foreach (SQLiteConnection.ColumnInfo columnInfo in tableInfo)
{
flag = string.Compare(column.Name, columnInfo.Name, StringComparison.OrdinalIgnoreCase) == 0;
if (flag)
break;
}
if (!flag)
columnList.Add(column);
}
foreach (TableMapping.Column p in columnList)
this.Execute("alter table \"" + map.TableName + "\" add column " + Orm.SqlDecl(p, this.StoreDateTimeAsTicks));
}
/// <summary>
/// Creates a new SQLiteCommand. Can be overridden to provide a sub-class.
/// </summary>
/// <seealso cref="M:SQLite.SQLiteCommand.OnInstanceCreated(System.Object)" />
protected virtual SQLiteCommand NewCommand()
{
return new SQLiteCommand(this);
}
/// <summary>
/// Creates a new SQLiteCommand given the command text with arguments. Place a '?'
/// in the command text for each of the arguments.
/// </summary>
/// <param name="cmdText">The fully escaped SQL.</param>
/// <param name="ps">
/// Arguments to substitute for the occurences of '?' in the command text.
/// </param>
/// <returns>
/// A <see cref="T:SQLite.SQLiteCommand" />
/// </returns>
public SQLiteCommand CreateCommand(string cmdText, params object[] ps)
{
if (!this._open)
throw SQLiteException.New(SQLite3.Result.Error, "Cannot create commands from unopened database");
SQLiteCommand sqLiteCommand = this.NewCommand();
sqLiteCommand.CommandText = cmdText;
foreach (object p in ps)
sqLiteCommand.Bind(p);
return sqLiteCommand;
}
/// <summary>
/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
/// in the command text for each of the arguments and then executes that command.
/// Use this method instead of Query when you don't expect rows back. Such cases include
/// INSERTs, UPDATEs, and DELETEs.
/// You can set the Trace or TimeExecution properties of the connection
/// to profile execution.
/// </summary>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// The number of rows modified in the database as a result of this execution.
/// </returns>
public int Execute(string query, params object[] args)
{
SQLiteCommand command = this.CreateCommand(query, args);
if (this.TimeExecution)
{
if (this._sw == null)
this._sw = new Stopwatch();
this._sw.Reset();
this._sw.Start();
}
int num = command.ExecuteNonQuery();
if (this.TimeExecution)
{
this._sw.Stop();
this._elapsedMilliseconds += this._sw.ElapsedMilliseconds;
}
return num;
}
public T ExecuteScalar<T>(string query, params object[] args)
{
SQLiteCommand command = this.CreateCommand(query, args);
if (this.TimeExecution)
{
if (this._sw == null)
this._sw = new Stopwatch();
this._sw.Reset();
this._sw.Start();
}
T obj = command.ExecuteScalar<T>();
if (this.TimeExecution)
{
this._sw.Stop();
this._elapsedMilliseconds += this._sw.ElapsedMilliseconds;
}
return obj;
}
/// <summary>
/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
/// in the command text for each of the arguments and then executes that command.
/// It returns each row of the result using the mapping automatically generated for
/// the given type.
/// </summary>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// An enumerable with one result for each row returned by the query.
/// </returns>
public List<T> Query<T>(string query, params object[] args) where T : new()
{
return this.CreateCommand(query, args).ExecuteQuery<T>();
}
/// <summary>
/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
/// in the command text for each of the arguments and then executes that command.
/// It returns each row of the result using the mapping automatically generated for
/// the given type.
/// </summary>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// An enumerable with one result for each row returned by the query.
/// The enumerator will call sqlite3_step on each call to MoveNext, so the database
/// connection must remain open for the lifetime of the enumerator.
/// </returns>
public IEnumerable<T> DeferredQuery<T>(string query, params object[] args) where T : new()
{
return this.CreateCommand(query, args).ExecuteDeferredQuery<T>();
}
/// <summary>
/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
/// in the command text for each of the arguments and then executes that command.
/// It returns each row of the result using the specified mapping. This function is
/// only used by libraries in order to query the database via introspection. It is
/// normally not used.
/// </summary>
/// <param name="map">
/// A <see cref="T:SQLite.TableMapping" /> to use to convert the resulting rows
/// into objects.
/// </param>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// An enumerable with one result for each row returned by the query.
/// </returns>
public List<object> Query(TableMapping map, string query, params object[] args)
{
return this.CreateCommand(query, args).ExecuteQuery<object>(map);
}
/// <summary>
/// Creates a SQLiteCommand given the command text (SQL) with arguments. Place a '?'
/// in the command text for each of the arguments and then executes that command.
/// It returns each row of the result using the specified mapping. This function is
/// only used by libraries in order to query the database via introspection. It is
/// normally not used.
/// </summary>
/// <param name="map">
/// A <see cref="T:SQLite.TableMapping" /> to use to convert the resulting rows
/// into objects.
/// </param>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// An enumerable with one result for each row returned by the query.
/// The enumerator will call sqlite3_step on each call to MoveNext, so the database
/// connection must remain open for the lifetime of the enumerator.
/// </returns>
public IEnumerable<object> DeferredQuery(TableMapping map, string query, params object[] args)
{
return this.CreateCommand(query, args).ExecuteDeferredQuery<object>(map);
}
/// <summary>
/// Returns a queryable interface to the table represented by the given type.
/// </summary>
/// <returns>
/// A queryable object that is able to translate Where, OrderBy, and Take
/// queries into native SQL.
/// </returns>
public TableQuery<T> Table<T>() where T : new()
{
return new TableQuery<T>(this);
}
/// <summary>
/// Attempts to retrieve an object with the given primary key from the table
/// associated with the specified type. Use of this method requires that
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">The primary key.</param>
/// <returns>
/// The object with the given primary key. Throws a not found exception
/// if the object is not found.
/// </returns>
public T Get<T>(object pk) where T : new()
{
return this.Query<T>(this.GetMapping(typeof (T), CreateFlags.None).GetByPrimaryKeySql, pk).First<T>();
}
/// <summary>
/// Attempts to retrieve an object with the given primary key from the table
/// associated with the specified type. Use of this method requires that
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">The primary key.</param>
/// <param name="map">The TableMapping used to identify the table.</param>
/// <returns>
/// The object with the given primary key. Throws a not found exception
/// if the object is not found.
/// </returns>
public object Get(object pk, TableMapping map)
{
return this.Query(map, map.GetByPrimaryKeySql, pk).First<object>();
}
/// <summary>
/// Attempts to retrieve the first object that matches the predicate from the table
/// associated with the specified type.
/// </summary>
/// <param name="predicate">A predicate for which object to find.</param>
/// <returns>
/// The object that matches the given predicate. Throws a not found exception
/// if the object is not found.
/// </returns>
public T Get<T>(Expression<Func<T, bool>> predicate) where T : new()
{
return this.Table<T>().Where(predicate).First();
}
/// <summary>
/// Attempts to retrieve an object with the given primary key from the table
/// associated with the specified type. Use of this method requires that
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">The primary key.</param>
/// <returns>
/// The object with the given primary key or null
/// if the object is not found.
/// </returns>
public T Find<T>(object pk) where T : new()
{
return this.Query<T>(this.GetMapping(typeof (T), CreateFlags.None).GetByPrimaryKeySql, pk).FirstOrDefault<T>();
}
/// <summary>
/// Attempts to retrieve an object with the given primary key from the table
/// associated with the specified type. Use of this method requires that
/// the given type have a designated PrimaryKey (using the PrimaryKeyAttribute).
/// </summary>
/// <param name="pk">The primary key.</param>
/// <param name="map">The TableMapping used to identify the table.</param>
/// <returns>
/// The object with the given primary key or null
/// if the object is not found.
/// </returns>
public object Find(object pk, TableMapping map)
{
return this.Query(map, map.GetByPrimaryKeySql, pk).FirstOrDefault<object>();
}
/// <summary>
/// Attempts to retrieve the first object that matches the predicate from the table
/// associated with the specified type.
/// </summary>
/// <param name="predicate">A predicate for which object to find.</param>
/// <returns>
/// The object that matches the given predicate or null
/// if the object is not found.
/// </returns>
public T Find<T>(Expression<Func<T, bool>> predicate) where T : new()
{
return this.Table<T>().Where(predicate).FirstOrDefault();
}
/// <summary>
/// Attempts to retrieve the first object that matches the query from the table
/// associated with the specified type.
/// </summary>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// The object that matches the given predicate or null
/// if the object is not found.
/// </returns>
public T FindWithQuery<T>(string query, params object[] args) where T : new()
{
return this.Query<T>(query, args).FirstOrDefault<T>();
}
/// <summary>
/// Attempts to retrieve the first object that matches the query from the table
/// associated with the specified type.
/// </summary>
/// <param name="map">The TableMapping used to identify the table.</param>
/// <param name="query">The fully escaped SQL.</param>
/// <param name="args">
/// Arguments to substitute for the occurences of '?' in the query.
/// </param>
/// <returns>
/// The object that matches the given predicate or null
/// if the object is not found.
/// </returns>
public object FindWithQuery(TableMapping map, string query, params object[] args)
{
return this.Query(map, query, args).FirstOrDefault<object>();
}
/// <summary>
/// Whether <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> has been called and the database is waiting for a <see cref="M:SQLite.SQLiteConnection.Commit" />.
/// </summary>
public bool IsInTransaction
{
get
{
return this._transactionDepth > 0;
}
}
/// <summary>
/// Begins a new transaction. Call <see cref="M:SQLite.SQLiteConnection.Commit" /> to end the transaction.
/// </summary>
/// <example cref="T:System.InvalidOperationException">Throws if a transaction has already begun.</example>
public void BeginTransaction()
{
if (Interlocked.CompareExchange(ref this._transactionDepth, 1, 0) != 0)
throw new InvalidOperationException("Cannot begin a transaction while already in a transaction.");
try
{
this.Execute("begin transaction");
}
catch (Exception ex)
{
SQLiteException sqLiteException = ex as SQLiteException;
if (sqLiteException != null)
{
switch (sqLiteException.Result)
{
case SQLite3.Result.Busy:
case SQLite3.Result.NoMem:
case SQLite3.Result.Interrupt:
case SQLite3.Result.IOError:
case SQLite3.Result.Full:
this.RollbackTo((string) null, true);
break;
}
}
else
Interlocked.Decrement(ref this._transactionDepth);
throw;
}
}
/// <summary>
/// Creates a savepoint in the database at the current point in the transaction timeline.
/// Begins a new transaction if one is not in progress.
///
/// Call <see cref="M:SQLite.SQLiteConnection.RollbackTo(System.String)" /> to undo transactions since the returned savepoint.
/// Call <see cref="M:SQLite.SQLiteConnection.Release(System.String)" /> to commit transactions after the savepoint returned here.
/// Call <see cref="M:SQLite.SQLiteConnection.Commit" /> to end the transaction, committing all changes.
/// </summary>
/// <returns>A string naming the savepoint.</returns>
public string SaveTransactionPoint()
{
string str = "S" + (object) this._rand.Next((int) short.MaxValue) + "D" + (object) (Interlocked.Increment(ref this._transactionDepth) - 1);
try
{
this.Execute("savepoint " + str);
}
catch (Exception ex)
{
SQLiteException sqLiteException = ex as SQLiteException;
if (sqLiteException != null)
{
switch (sqLiteException.Result)
{
case SQLite3.Result.Busy:
case SQLite3.Result.NoMem:
case SQLite3.Result.Interrupt:
case SQLite3.Result.IOError:
case SQLite3.Result.Full:
this.RollbackTo((string) null, true);
break;
}
}
else
Interlocked.Decrement(ref this._transactionDepth);
throw;
}
return str;
}
/// <summary>
/// Rolls back the transaction that was begun by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> or <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />.
/// </summary>
public void Rollback()
{
this.RollbackTo((string) null, false);
}
/// <summary>
/// Rolls back the savepoint created by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> or SaveTransactionPoint.
/// </summary>
/// <param name="savepoint">The name of the savepoint to roll back to, as returned by <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />. If savepoint is null or empty, this method is equivalent to a call to <see cref="M:SQLite.SQLiteConnection.Rollback" /></param>
public void RollbackTo(string savepoint)
{
this.RollbackTo(savepoint, false);
}
/// <summary>
/// Rolls back the transaction that was begun by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" />.
/// </summary>
/// <param name="savepoint">The name of the savepoint to roll back to, as returned by <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />. If savepoint is null or empty, this method is equivalent to a call to <see cref="M:SQLite.SQLiteConnection.Rollback" /></param>
/// <param name="noThrow">true to avoid throwing exceptions, false otherwise</param>
private void RollbackTo(string savepoint, bool noThrow)
{
try
{
if (string.IsNullOrEmpty(savepoint))
{
if (Interlocked.Exchange(ref this._transactionDepth, 0) <= 0)
return;
this.Execute("rollback");
}
else
this.DoSavePointExecute(savepoint, "rollback to ");
}
catch (SQLiteException ex)
{
if (noThrow)
return;
throw;
}
}
/// <summary>
/// Releases a savepoint returned from <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" />. Releasing a savepoint
/// makes changes since that savepoint permanent if the savepoint began the transaction,
/// or otherwise the changes are permanent pending a call to <see cref="M:SQLite.SQLiteConnection.Commit" />.
///
/// The RELEASE command is like a COMMIT for a SAVEPOINT.
/// </summary>
/// <param name="savepoint">The name of the savepoint to release. The string should be the result of a call to <see cref="M:SQLite.SQLiteConnection.SaveTransactionPoint" /></param>
public void Release(string savepoint)
{
this.DoSavePointExecute(savepoint, "release ");
}
private void DoSavePointExecute(string savepoint, string cmd)
{
int num = savepoint.IndexOf('D');
int result;
if (num < 2 || savepoint.Length <= num + 1 || (!int.TryParse(savepoint.Substring(num + 1), out result) || 0 > result) || result >= this._transactionDepth)
throw new ArgumentException("savePoint is not valid, and should be the result of a call to SaveTransactionPoint.", "savePoint");
Volatile.Write(ref this._transactionDepth, result);
this.Execute(cmd + savepoint);
}
/// <summary>
/// Commits the transaction that was begun by <see cref="M:SQLite.SQLiteConnection.BeginTransaction" />.
/// </summary>
public void Commit()
{
if (Interlocked.Exchange(ref this._transactionDepth, 0) == 0)
return;
this.Execute("commit");
}
/// <summary>
/// Executes <paramref name="action" /> within a (possibly nested) transaction by wrapping it in a SAVEPOINT. If an
/// exception occurs the whole transaction is rolled back, not just the current savepoint. The exception
/// is rethrown.
/// </summary>
/// <param name="action">
/// The <see cref="T:System.Action" /> to perform within a transaction. <paramref name="action" /> can contain any number
/// of operations on the connection but should never call <see cref="M:SQLite.SQLiteConnection.BeginTransaction" /> or
/// <see cref="M:SQLite.SQLiteConnection.Commit" />.
/// </param>
public void RunInTransaction(Action action)
{
try
{
string savepoint = this.SaveTransactionPoint();
action();
this.Release(savepoint);
}
catch (Exception ex)
{
this.Rollback();
throw;
}
}
/// <summary>Inserts all specified objects.</summary>
/// <param name="objects">
/// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
/// <param name="runInTransaction" />
/// A boolean indicating if the inserts should be wrapped in a transaction.
/// </param>
/// <returns>The number of rows added to the table.</returns>
public int InsertAll(IEnumerable objects, bool runInTransaction = true)
{
int c = 0;
if (runInTransaction)
{
this.RunInTransaction((Action) (() =>
{
foreach (object obj in objects)
c += this.Insert(obj);
}));
}
else
{
foreach (object obj in objects)
c += this.Insert(obj);
}
return c;
}
/// <summary>Inserts all specified objects.</summary>
/// <param name="objects">
/// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
/// </param>
/// <param name="extra">
/// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
/// </param>
/// <param name="runInTransaction">
/// A boolean indicating if the inserts should be wrapped in a transaction.
/// </param>
/// <returns>The number of rows added to the table.</returns>
public int InsertAll(IEnumerable objects, string extra, bool runInTransaction = true)
{
int c = 0;
if (runInTransaction)
{
this.RunInTransaction((Action) (() =>
{
foreach (object obj in objects)
c += this.Insert(obj, extra);
}));
}
else
{
foreach (object obj in objects)
c += this.Insert(obj);
}
return c;
}
/// <summary>Inserts all specified objects.</summary>
/// <param name="objects">
/// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
/// </param>
/// <param name="objType">The type of object to insert.</param>
/// <param name="runInTransaction">
/// A boolean indicating if the inserts should be wrapped in a transaction.
/// </param>
/// <returns>The number of rows added to the table.</returns>
public int InsertAll(IEnumerable objects, Type objType, bool runInTransaction = true)
{
int c = 0;
if (runInTransaction)
{
this.RunInTransaction((Action) (() =>
{
foreach (object obj in objects)
c += this.Insert(obj, objType);
}));
}
else
{
foreach (object obj in objects)
c += this.Insert(obj, objType);
}
return c;
}
/// <summary>
/// Inserts the given object and retrieves its
/// auto incremented primary key if it has one.
/// </summary>
/// <param name="obj">The object to insert.</param>
/// <returns>The number of rows added to the table.</returns>
public int Insert(object obj)
{
if (obj == null)
return 0;
return this.Insert(obj, "", Orm.GetType(obj));
}
/// <summary>
/// Inserts the given object and retrieves its
/// auto incremented primary key if it has one.
/// If a UNIQUE constraint violation occurs with
/// some pre-existing object, this function deletes
/// the old object.
/// </summary>
/// <param name="obj">The object to insert.</param>
/// <returns>The number of rows modified.</returns>
public int InsertOrReplace(object obj)
{
if (obj == null)
return 0;
return this.Insert(obj, "OR REPLACE", Orm.GetType(obj));
}
/// <summary>
/// Inserts the given object and retrieves its
/// auto incremented primary key if it has one.
/// </summary>
/// <param name="obj">The object to insert.</param>
/// <param name="objType">The type of object to insert.</param>
/// <returns>The number of rows added to the table.</returns>
public int Insert(object obj, Type objType)
{
return this.Insert(obj, "", objType);
}
/// <summary>
/// Inserts the given object and retrieves its
/// auto incremented primary key if it has one.
/// If a UNIQUE constraint violation occurs with
/// some pre-existing object, this function deletes
/// the old object.
/// </summary>
/// <param name="obj">The object to insert.</param>
/// <param name="objType">The type of object to insert.</param>
/// <returns>The number of rows modified.</returns>
public int InsertOrReplace(object obj, Type objType)
{
return this.Insert(obj, "OR REPLACE", objType);
}
/// <summary>
/// Inserts the given object and retrieves its
/// auto incremented primary key if it has one.
/// </summary>
/// <param name="obj">The object to insert.</param>
/// <param name="extra">
/// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
/// </param>
/// <returns>The number of rows added to the table.</returns>
public int Insert(object obj, string extra)
{
if (obj == null)
return 0;
return this.Insert(obj, extra, Orm.GetType(obj));
}
/// <summary>
/// Inserts the given object and retrieves its
/// auto incremented primary key if it has one.
/// </summary>
/// <param name="obj">The object to insert.</param>
/// <param name="extra">
/// Literal SQL code that gets placed into the command. INSERT {extra} INTO ...
/// </param>
/// <param name="objType">The type of object to insert.</param>
/// <returns>The number of rows added to the table.</returns>
public int Insert(object obj, string extra, Type objType)
{
if (obj == null || (object) objType == null)
return 0;
TableMapping mapping = this.GetMapping(objType, CreateFlags.None);
if (mapping.PK != null && mapping.PK.IsAutoGuid && mapping.PK.GetValue(obj).Equals((object) Guid.Empty))
mapping.PK.SetValue(obj, (object) Guid.NewGuid());
TableMapping.Column[] columnArray = string.Compare(extra, "OR REPLACE", StringComparison.OrdinalIgnoreCase) == 0 ? mapping.InsertOrReplaceColumns : mapping.InsertColumns;
object[] source = new object[columnArray.Length];
for (int index = 0; index < source.Length; ++index)
source[index] = columnArray[index].GetValue(obj);
PreparedSqlLiteInsertCommand insertCommand = mapping.GetInsertCommand(this, extra);
int num;
lock (insertCommand)
{
try
{
num = insertCommand.ExecuteNonQuery(source);
}
catch (SQLiteException ex)
{
if (SQLite3.ExtendedErrCode(this.Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
throw NotNullConstraintViolationException.New(ex.Result, ex.Message, mapping, obj);
throw;
}
if (mapping.HasAutoIncPK)
{
long id = SQLite3.LastInsertRowid(this.Handle);
mapping.SetAutoIncPK(obj, id);
}
}
if (num > 0)
this.OnTableChanged(mapping, NotifyTableChangedAction.Insert);
return num;
}
/// <summary>
/// Updates all of the columns of a table using the specified object
/// except for its primary key.
/// The object is required to have a primary key.
/// </summary>
/// <param name="obj">
/// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
/// </param>
/// <returns>The number of rows updated.</returns>
public int Update(object obj)
{
if (obj == null)
return 0;
return this.Update(obj, Orm.GetType(obj));
}
/// <summary>
/// Updates all of the columns of a table using the specified object
/// except for its primary key.
/// The object is required to have a primary key.
/// </summary>
/// <param name="obj">
/// The object to update. It must have a primary key designated using the PrimaryKeyAttribute.
/// </param>
/// <param name="objType">The type of object to insert.</param>
/// <returns>The number of rows updated.</returns>
public int Update(object obj, Type objType)
{
if (obj == null || (object) objType == null)
return 0;
TableMapping mapping = this.GetMapping(objType, CreateFlags.None);
TableMapping.Column pk = mapping.PK;
if (pk == null)
throw new NotSupportedException("Cannot update " + mapping.TableName + ": it has no PK");
IEnumerable<TableMapping.Column> source = ((IEnumerable<TableMapping.Column>) mapping.Columns).Where<TableMapping.Column>((Func<TableMapping.Column, bool>) (p => p != pk));
List<object> objectList = new List<object>(source.Select<TableMapping.Column, object>((Func<TableMapping.Column, object>) (c => c.GetValue(obj))));
objectList.Add(pk.GetValue(obj));
string query = string.Format("update \"{0}\" set {1} where {2} = ? ", new object[3]
{
(object) mapping.TableName,
(object) string.Join(",", source.Select<TableMapping.Column, string>((Func<TableMapping.Column, string>) (c => "\"" + c.Name + "\" = ? ")).ToArray<string>()),
(object) pk.Name
});
int num;
try
{
num = this.Execute(query, objectList.ToArray());
}
catch (SQLiteException ex)
{
if (ex.Result == SQLite3.Result.Constraint && SQLite3.ExtendedErrCode(this.Handle) == SQLite3.ExtendedResult.ConstraintNotNull)
throw NotNullConstraintViolationException.New(ex, mapping, obj);
throw ex;
}
if (num > 0)
this.OnTableChanged(mapping, NotifyTableChangedAction.Update);
return num;
}
/// <summary>Updates all specified objects.</summary>
/// <param name="objects">
/// An <see cref="T:System.Collections.IEnumerable" /> of the objects to insert.
/// </param>
/// <param name="runInTransaction">
/// A boolean indicating if the inserts should be wrapped in a transaction
/// </param>
/// <returns>The number of rows modified.</returns>
public int UpdateAll(IEnumerable objects, bool runInTransaction = true)
{
int c = 0;
if (runInTransaction)
{
this.RunInTransaction((Action) (() =>
{
foreach (object obj in objects)
c += this.Update(obj);
}));
}
else
{
foreach (object obj in objects)
c += this.Update(obj);
}
return c;
}
/// <summary>
/// Deletes the given object from the database using its primary key.
/// </summary>
/// <param name="objectToDelete">
/// The object to delete. It must have a primary key designated using the PrimaryKeyAttribute.
/// </param>
/// <returns>The number of rows deleted.</returns>
public int Delete(object objectToDelete)
{
TableMapping mapping = this.GetMapping(Orm.GetType(objectToDelete), CreateFlags.None);
TableMapping.Column pk = mapping.PK;
if (pk == null)
throw new NotSupportedException("Cannot delete " + mapping.TableName + ": it has no PK");
int num = this.Execute(string.Format("delete from \"{0}\" where \"{1}\" = ?", new object[2]
{
(object) mapping.TableName,
(object) pk.Name
}), pk.GetValue(objectToDelete));
if (num > 0)
this.OnTableChanged(mapping, NotifyTableChangedAction.Delete);
return num;
}
/// <summary>Deletes the object with the specified primary key.</summary>
/// <param name="primaryKey">The primary key of the object to delete.</param>
/// <returns>The number of objects deleted.</returns>
/// <typeparam name="T">The type of object.</typeparam>
public int Delete<T>(object primaryKey)
{
return this.Delete(primaryKey, this.GetMapping(typeof (T), CreateFlags.None));
}
/// <summary>Deletes the object with the specified primary key.</summary>
/// <param name="primaryKey">The primary key of the object to delete.</param>
/// <param name="map">The TableMapping used to identify the table.</param>
/// <returns>The number of objects deleted.</returns>
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");
int num = this.Execute(string.Format("delete from \"{0}\" where \"{1}\" = ?", new object[2]
{
(object) map.TableName,
(object) pk.Name
}), primaryKey);
if (num > 0)
this.OnTableChanged(map, NotifyTableChangedAction.Delete);
return num;
}
/// <summary>
/// Deletes all the objects from the specified table.
/// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
/// specified table. Do you really want to do that?
/// </summary>
/// <returns>The number of objects deleted.</returns>
/// <typeparam name="T">The type of objects to delete.</typeparam>
public int DeleteAll<T>()
{
return this.DeleteAll(this.GetMapping(typeof (T), CreateFlags.None));
}
/// <summary>
/// Deletes all the objects from the specified table.
/// WARNING WARNING: Let me repeat. It deletes ALL the objects from the
/// specified table. Do you really want to do that?
/// </summary>
/// <param name="map">The TableMapping used to identify the table.</param>
/// <returns>The number of objects deleted.</returns>
public int DeleteAll(TableMapping map)
{
int num = this.Execute(string.Format("delete from \"{0}\"", new object[1]
{
(object) map.TableName
}));
if (num > 0)
this.OnTableChanged(map, NotifyTableChangedAction.Delete);
return num;
}
~SQLiteConnection()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize((object) this);
}
public void Close()
{
this.Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!this._open)
return;
if (this.Handle == SQLiteConnection.NullHandle)
return;
try
{
if (disposing)
{
if (this._mappings != null)
{
foreach (TableMapping tableMapping in this._mappings.Values)
tableMapping.Dispose();
}
SQLite3.Result r = SQLite3.Close2(this.Handle);
if (r != SQLite3.Result.OK)
{
string errmsg = SQLite3.GetErrmsg(this.Handle);
throw SQLiteException.New(r, errmsg);
}
}
else
{
int num = (int) SQLite3.Close2(this.Handle);
}
}
finally
{
this.Handle = SQLiteConnection.NullHandle;
this._open = false;
}
}
private void OnTableChanged(TableMapping table, NotifyTableChangedAction action)
{
// ISSUE: reference to a compiler-generated field
EventHandler<NotifyTableChangedEventArgs> tableChanged = this.TableChanged;
if (tableChanged == null)
return;
tableChanged((object) this, new NotifyTableChangedEventArgs(table, action));
}
public event EventHandler<NotifyTableChangedEventArgs> TableChanged;
private struct IndexedColumn
{
public int Order;
public string ColumnName;
}
private struct IndexInfo
{
public string IndexName;
public string TableName;
public bool Unique;
public List<SQLiteConnection.IndexedColumn> Columns;
}
public class ColumnInfo
{
[Column("name")]
public string Name { get; set; }
public int notnull { get; set; }
public override string ToString()
{
return this.Name;
}
}
}
}