/******************************************************** * ADO.NET 2.0 Data Provider for SQLite Version 3.X * Written by Robert Simpson (robert@blackcastlesoft.com) * * Released to the public domain, use at your own risk! ********************************************************/ namespace System.Data.SQLite { using System; using System.Data; using System.Data.Common; using System.Collections.Generic; using System.Globalization; using System.ComponentModel; using System.Text; using System.Runtime.InteropServices; using System.IO; /// /// SQLite implentation of DbConnection. /// /// /// The ConnectionString property of the SQLiteConnection class can contain the following parameter(s), delimited with a semi-colon: /// /// /// Parameter /// Values /// Required /// Default /// /// /// Data Source /// {filename} /// Y /// /// /// /// Version /// 3 /// N /// 3 /// /// /// UseUTF16Encoding /// True
False
/// N /// False ///
/// /// DateTimeFormat /// Ticks - Use DateTime.Ticks
ISO8601 - Use ISO8601 DateTime format
/// N /// ISO8601 ///
/// /// BinaryGUID /// True - Store GUID columns in binary form
False - Store GUID columns as text
/// N /// True ///
/// /// Cache Size /// {size in bytes} /// N /// 2000 /// /// /// Synchronous /// Normal - Normal file flushing behavior
Full - Full flushing after all writes
Off - Underlying OS flushes I/O's
/// N /// Normal ///
/// /// Page Size /// {size in bytes} /// N /// 1024 /// /// /// Password /// {password} /// N /// /// /// /// Enlist /// Y - Automatically enlist in distributed transactions
N - No automatic enlistment
/// N /// Y ///
/// /// Pooling /// True - Use connection pooling
False - Do not use connection pooling
/// N /// False ///
/// /// FailIfMissing /// True - Don't create the database if it does not exist, throw an error instead
False - Automatically create the database if it does not exist
/// N /// False ///
/// /// Max Page Count /// {size in pages} - Limits the maximum number of pages (limits the size) of the database /// N /// 0 /// /// /// Legacy Format /// True - Use the more compatible legacy 3.x database format
False - Use the newer 3.3x database format which compresses numbers more effectively
/// N /// False ///
/// /// Default Timeout /// {time in seconds}
The default command timeout
/// N /// 30 ///
/// /// Journal Mode /// Delete - Delete the journal file after a commit
Persist - Zero out and leave the journal file on disk after a commit
Off - Disable the rollback journal entirely
/// N /// Delete ///
/// /// Read Only /// True - Open the database for read only access
False - Open the database for normal read/write access
/// N /// False ///
/// /// Max Pool Size /// The maximum number of connections for the given connection string that can be in the connection pool /// N /// 100 /// /// /// Default IsolationLevel /// The default transaciton isolation level /// N /// Serializable /// /// /// Foreign Keys /// Enable foreign key constraints /// N /// False /// ///
///
public sealed partial class SQLiteConnection : DbConnection, ICloneable { private const string _dataDirectory = "|DataDirectory|"; private const string _masterdb = "sqlite_master"; private const string _tempmasterdb = "sqlite_temp_master"; /// /// State of the current connection /// private ConnectionState _connectionState; /// /// The connection string /// private string _connectionString; /// /// Nesting level of the transactions open on the connection /// internal int _transactionLevel; /// /// The default isolation level for new transactions /// private IsolationLevel _defaultIsolation; #if !PLATFORM_COMPACTFRAMEWORK /// /// Whether or not the connection is enlisted in a distrubuted transaction /// internal SQLiteEnlistment _enlistment; #endif /// /// The base SQLite object to interop with /// internal SQLiteBase _sql; /// /// The database filename minus path and extension /// private string _dataSource; /// /// Temporary password storage, emptied after the database has been opened /// private byte[] _password; /// /// Default command timeout /// private int _defaultTimeout = 30; internal bool _binaryGuid; internal long _version; private event SQLiteUpdateEventHandler _updateHandler; private event SQLiteCommitHandler _commitHandler; private event SQLiteTraceEventHandler _traceHandler; private event EventHandler _rollbackHandler; private SQLiteUpdateCallback _updateCallback; private SQLiteCommitCallback _commitCallback; private SQLiteTraceCallback _traceCallback; private SQLiteRollbackCallback _rollbackCallback; /// /// This event is raised whenever the database is opened or closed. /// public override event StateChangeEventHandler StateChange; /// /// Constructs a new SQLiteConnection object /// /// /// Default constructor /// public SQLiteConnection() : this("") { } /// /// Initializes the connection with the specified connection string /// /// The connection string to use on the connection public SQLiteConnection(string connectionString) { _connectionState = ConnectionState.Closed; _connectionString = ""; //_commandList = new List(); if (connectionString != null) ConnectionString = connectionString; } /// /// Clones the settings and connection string from an existing connection. If the existing connection is already open, this /// function will open its own connection, enumerate any attached databases of the original connection, and automatically /// attach to them. /// /// public SQLiteConnection(SQLiteConnection connection) : this(connection.ConnectionString) { string str; if (connection.State == ConnectionState.Open) { Open(); // Reattach all attached databases from the existing connection using (DataTable tbl = connection.GetSchema("Catalogs")) { foreach (DataRow row in tbl.Rows) { str = row[0].ToString(); if (String.Compare(str, "main", StringComparison.OrdinalIgnoreCase) != 0 && String.Compare(str, "temp", StringComparison.OrdinalIgnoreCase) != 0) { using (SQLiteCommand cmd = CreateCommand()) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "ATTACH DATABASE '{0}' AS [{1}]", row[1], row[0]); cmd.ExecuteNonQuery(); } } } } } } #if PLATFORM_COMPACTFRAMEWORK /// /// Obsolete /// public override int ConnectionTimeout { get { return 30; } } #endif /// /// Creates a clone of the connection. All attached databases and user-defined functions are cloned. If the existing connection is open, the cloned connection /// will also be opened. /// /// public object Clone() { return new SQLiteConnection(this); } /// /// Disposes of the SQLiteConnection, closing it if it is active. /// /// True if the connection is being explicitly closed. protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) Close(); } /// /// Creates a database file. This just creates a zero-byte file which SQLite /// will turn into a database when the file is opened properly. /// /// The file to create static public void CreateFile(string databaseFileName) { FileStream fs = File.Create(databaseFileName); fs.Close(); } /// /// Raises the state change event when the state of the connection changes /// /// The new state. If it is different from the previous state, an event is raised. internal void OnStateChange(ConnectionState newState) { ConnectionState oldState = _connectionState; _connectionState = newState; if (StateChange != null && oldState != newState) { StateChangeEventArgs e = new StateChangeEventArgs(oldState, newState); StateChange(this, e); } } /// /// OBSOLETE. Creates a new SQLiteTransaction if one isn't already active on the connection. /// /// This parameter is ignored. /// When TRUE, SQLite defers obtaining a write lock until a write operation is requested. /// When FALSE, a writelock is obtained immediately. The default is TRUE, but in a multi-threaded multi-writer /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock. /// Returns a SQLiteTransaction object. [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")] public SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel, bool deferredLock) { return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted); } /// /// OBSOLETE. Creates a new SQLiteTransaction if one isn't already active on the connection. /// /// When TRUE, SQLite defers obtaining a write lock until a write operation is requested. /// When FALSE, a writelock is obtained immediately. The default is false, but in a multi-threaded multi-writer /// environment, one may instead choose to lock the database immediately to avoid any possible writer deadlock. /// Returns a SQLiteTransaction object. [Obsolete("Use one of the standard BeginTransaction methods, this one will be removed soon")] public SQLiteTransaction BeginTransaction(bool deferredLock) { return (SQLiteTransaction)BeginDbTransaction(deferredLock == false ? IsolationLevel.Serializable : IsolationLevel.ReadCommitted); } /// /// Creates a new SQLiteTransaction if one isn't already active on the connection. /// /// Supported isolation levels are Serializable, ReadCommitted and Unspecified. /// /// Unspecified will use the default isolation level specified in the connection string. If no isolation level is specified in the /// connection string, Serializable is used. /// Serializable transactions are the default. In this mode, the engine gets an immediate lock on the database, and no other threads /// may begin a transaction. Other threads may read from the database, but not write. /// With a ReadCommitted isolation level, locks are deferred and elevated as needed. It is possible for multiple threads to start /// a transaction in ReadCommitted mode, but if a thread attempts to commit a transaction while another thread /// has a ReadCommitted lock, it may timeout or cause a deadlock on both threads until both threads' CommandTimeout's are reached. /// /// Returns a SQLiteTransaction object. public new SQLiteTransaction BeginTransaction(IsolationLevel isolationLevel) { return (SQLiteTransaction)BeginDbTransaction(isolationLevel); } /// /// Creates a new SQLiteTransaction if one isn't already active on the connection. /// /// Returns a SQLiteTransaction object. public new SQLiteTransaction BeginTransaction() { return (SQLiteTransaction)BeginDbTransaction(_defaultIsolation); } /// /// Forwards to the local BeginTransaction() function /// /// Supported isolation levels are Unspecified, Serializable, and ReadCommitted /// protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) { if (_connectionState != ConnectionState.Open) throw new InvalidOperationException(); if (isolationLevel == IsolationLevel.Unspecified) isolationLevel = _defaultIsolation; if (isolationLevel != IsolationLevel.Serializable && isolationLevel != IsolationLevel.ReadCommitted) throw new ArgumentException("isolationLevel"); return new SQLiteTransaction(this, isolationLevel != IsolationLevel.Serializable); } /// /// Not implemented /// /// public override void ChangeDatabase(string databaseName) { throw new NotImplementedException(); } /// /// When the database connection is closed, all commands linked to this connection are automatically reset. /// public override void Close() { if (_sql != null) { #if !PLATFORM_COMPACTFRAMEWORK if (_enlistment != null) { // If the connection is enlisted in a transaction scope and the scope is still active, // we cannot truly shut down this connection until the scope has completed. Therefore make a // hidden connection temporarily to hold open the connection until the scope has completed. SQLiteConnection cnn = new SQLiteConnection(); cnn._sql = _sql; cnn._transactionLevel = _transactionLevel; cnn._enlistment = _enlistment; cnn._connectionState = _connectionState; cnn._version = _version; cnn._enlistment._transaction._cnn = cnn; cnn._enlistment._disposeConnection = true; _sql = null; _enlistment = null; } #endif if (_sql != null) { _sql.Close(); } _sql = null; _transactionLevel = 0; } OnStateChange(ConnectionState.Closed); } /// /// Clears the connection pool associated with the connection. Any other active connections using the same database file /// will be discarded instead of returned to the pool when they are closed. /// /// public static void ClearPool(SQLiteConnection connection) { if (connection._sql == null) return; connection._sql.ClearPool(); } /// /// Clears all connection pools. Any active connections will be discarded instead of sent to the pool when they are closed. /// public static void ClearAllPools() { SQLiteConnectionPool.ClearAllPools(); } /// /// The connection string containing the parameters for the connection /// /// /// /// /// Parameter /// Values /// Required /// Default /// /// /// Data Source /// {filename} /// Y /// /// /// /// Version /// 3 /// N /// 3 /// /// /// UseUTF16Encoding /// True
False
/// N /// False ///
/// /// DateTimeFormat /// Ticks - Use DateTime.Ticks
ISO8601 - Use ISO8601 DateTime format
JulianDay - Use JulianDay format
/// N /// ISO8601 ///
/// /// BinaryGUID /// Yes/On/1 - Store GUID columns in binary form
No/Off/0 - Store GUID columns as text
/// N /// On ///
/// /// Cache Size /// {size in bytes} /// N /// 2000 /// /// /// Synchronous /// Normal - Normal file flushing behavior
Full - Full flushing after all writes
Off - Underlying OS flushes I/O's
/// N /// Normal ///
/// /// Page Size /// {size in bytes} /// N /// 1024 /// /// /// Password /// {password} /// N /// /// /// /// Enlist /// Y - Automatically enlist in distributed transactions
N - No automatic enlistment
/// N /// Y ///
/// /// Pooling /// True - Use connection pooling
False - Do not use connection pooling
/// N /// False ///
/// /// FailIfMissing /// True - Don't create the database if it does not exist, throw an error instead
False - Automatically create the database if it does not exist
/// N /// False ///
/// /// Max Page Count /// {size in pages} - Limits the maximum number of pages (limits the size) of the database /// N /// 0 /// /// /// Legacy Format /// True - Use the more compatible legacy 3.x database format
False - Use the newer 3.3x database format which compresses numbers more effectively
/// N /// False ///
/// /// Default Timeout /// {time in seconds}
The default command timeout
/// N /// 30 ///
/// /// Journal Mode /// Delete - Delete the journal file after a commit
Persist - Zero out and leave the journal file on disk after a commit
Off - Disable the rollback journal entirely
/// N /// Delete ///
/// /// Read Only /// True - Open the database for read only access
False - Open the database for normal read/write access
/// N /// False ///
/// /// Max Pool Size /// The maximum number of connections for the given connection string that can be in the connection pool /// N /// 100 /// /// /// Default IsolationLevel /// The default transaciton isolation level /// N /// Serializable /// ///
///
#if !PLATFORM_COMPACTFRAMEWORK [RefreshProperties(RefreshProperties.All), DefaultValue("")] [Editor("SQLite.Designer.SQLiteConnectionStringEditor, SQLite.Designer, Version=1.0.38.1, Culture=neutral, PublicKeyToken=db937bc2d44ff139", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] #endif public override string ConnectionString { get { return _connectionString; } set { if (value == null) throw new ArgumentNullException(); else if (_connectionState != ConnectionState.Closed) throw new InvalidOperationException(); _connectionString = value; } } /// /// Create a new SQLiteCommand and associate it with this connection. /// /// Returns an instantiated SQLiteCommand object already assigned to this connection. public new SQLiteCommand CreateCommand() { return new SQLiteCommand(this); } /// /// Forwards to the local CreateCommand() function /// /// protected override DbCommand CreateDbCommand() { return CreateCommand(); } /// /// Returns the filename without extension or path /// #if !PLATFORM_COMPACTFRAMEWORK [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] #endif public override string DataSource { get { return _dataSource; } } /// /// Returns an empty string /// #if !PLATFORM_COMPACTFRAMEWORK [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] #endif public override string Database { get { return "main"; } } internal static string MapUriPath(string path) { if (path.StartsWith ("file://", StringComparison.OrdinalIgnoreCase)) return path.Substring (7); else if (path.StartsWith ("file:", StringComparison.OrdinalIgnoreCase)) return path.Substring (5); else if (path.StartsWith ("/", StringComparison.OrdinalIgnoreCase)) return path; else throw new InvalidOperationException ("Invalid connection string: invalid URI"); } /// /// Parses the connection string into component parts /// /// The connection string to parse /// An array of key-value pairs representing each parameter of the connection string internal static SortedList ParseConnectionString(string connectionString) { string s = connectionString; int n; SortedList ls = new SortedList(StringComparer.OrdinalIgnoreCase); // First split into semi-colon delimited values. The Split() function of SQLiteBase accounts for and properly // skips semi-colons in quoted strings string[] arParts = SQLiteConvert.Split(s, ';'); string[] arPiece; int x = arParts.Length; // For each semi-colon piece, split into key and value pairs by the presence of the = sign for (n = 0; n < x; n++) { arPiece = SQLiteConvert.Split(arParts[n], '='); if (arPiece.Length == 2) { ls.Add(arPiece[0], arPiece[1]); } else throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Invalid ConnectionString format for parameter \"{0}\"", (arPiece.Length > 0) ? arPiece[0] : "null")); } return ls; } #if !PLATFORM_COMPACTFRAMEWORK /// /// Manual distributed transaction enlistment support /// /// The distributed transaction to enlist in public override void EnlistTransaction(System.Transactions.Transaction transaction) { if (_transactionLevel > 0 && transaction != null) throw new ArgumentException("Unable to enlist in transaction, a local transaction already exists"); if (_enlistment != null && transaction == _enlistment._scope) return; else if (_enlistment != null) throw new ArgumentException("Already enlisted in a transaction"); _enlistment = new SQLiteEnlistment(this, transaction); } #endif /// /// Looks for a key in the array of key/values of the parameter string. If not found, return the specified default value /// /// The list to look in /// The key to find /// The default value to return if the key is not found /// The value corresponding to the specified key, or the default value if not found. static internal string FindKey(SortedList items, string key, string defValue) { string ret; if (items.TryGetValue(key, out ret)) return ret; return defValue; } /// /// Opens the connection using the parameters found in the ConnectionString /// public override void Open() { if (_connectionState != ConnectionState.Closed) throw new InvalidOperationException(); Close(); SortedList opts = ParseConnectionString(_connectionString); string fileName; if (Convert.ToInt32(FindKey(opts, "Version", "3"), CultureInfo.InvariantCulture) != 3) throw new NotSupportedException("Only SQLite Version 3 is supported at this time"); fileName = FindKey(opts, "Data Source", ""); if (String.IsNullOrEmpty(fileName)) { fileName = FindKey(opts, "Uri", ""); if (String.IsNullOrEmpty(fileName)) throw new ArgumentException("Data Source cannot be empty. Use :memory: to open an in-memory database"); else fileName = MapUriPath(fileName); } if (String.Compare(fileName, ":MEMORY:", StringComparison.OrdinalIgnoreCase) == 0) fileName = ":memory:"; else { #if PLATFORM_COMPACTFRAMEWORK if (fileName.StartsWith(".\\")) fileName = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase) + fileName.Substring(1); #endif fileName = ExpandFileName(fileName); } try { bool usePooling = (SQLiteConvert.ToBoolean(FindKey(opts, "Pooling", Boolean.FalseString)) == true); bool bUTF16 = (SQLiteConvert.ToBoolean(FindKey(opts, "UseUTF16Encoding", Boolean.FalseString)) == true); int maxPoolSize = Convert.ToInt32(FindKey(opts, "Max Pool Size", "100"), CultureInfo.InvariantCulture); _defaultTimeout = Convert.ToInt32(FindKey(opts, "Default Timeout", "30"), CultureInfo.CurrentCulture); _defaultIsolation = (IsolationLevel)Enum.Parse(typeof(IsolationLevel), FindKey(opts, "Default IsolationLevel", "Serializable"), true); if (_defaultIsolation != IsolationLevel.Serializable && _defaultIsolation != IsolationLevel.ReadCommitted) throw new NotSupportedException("Invalid Default IsolationLevel specified"); SQLiteDateFormats dateFormat = (SQLiteDateFormats)Enum.Parse(typeof(SQLiteDateFormats), FindKey(opts, "DateTimeFormat", "ISO8601"), true); //string temp = FindKey(opts, "DateTimeFormat", "ISO8601"); //if (String.Compare(temp, "ticks", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.Ticks; //else if (String.Compare(temp, "julianday", StringComparison.OrdinalIgnoreCase) == 0) dateFormat = SQLiteDateFormats.JulianDay; if (bUTF16) // SQLite automatically sets the encoding of the database to UTF16 if called from sqlite3_open16() _sql = new SQLite3_UTF16(dateFormat); else _sql = new SQLite3(dateFormat); SQLiteOpenFlagsEnum flags = SQLiteOpenFlagsEnum.None; if (SQLiteConvert.ToBoolean(FindKey(opts, "FailIfMissing", Boolean.FalseString)) == false) flags |= SQLiteOpenFlagsEnum.Create; if (SQLiteConvert.ToBoolean(FindKey(opts, "Read Only", Boolean.FalseString)) == true) { flags |= SQLiteOpenFlagsEnum.ReadOnly; // SQLite will return SQLITE_MISUSE on ReadOnly and Create flags &= ~SQLiteOpenFlagsEnum.Create; } else { flags |= SQLiteOpenFlagsEnum.ReadWrite; } _sql.Open(fileName, flags, maxPoolSize, usePooling); _binaryGuid = (SQLiteConvert.ToBoolean(FindKey(opts, "BinaryGUID", Boolean.TrueString)) == true); string password = FindKey(opts, "Password", null); if (String.IsNullOrEmpty(password) == false) _sql.SetPassword(System.Text.UTF8Encoding.UTF8.GetBytes(password)); else if (_password != null) _sql.SetPassword(_password); _password = null; _dataSource = Path.GetFileNameWithoutExtension(fileName); _version++; ConnectionState oldstate = _connectionState; _connectionState = ConnectionState.Open; try { using (SQLiteCommand cmd = CreateCommand()) { string defValue; if (fileName != ":memory:") { defValue = FindKey(opts, "Page Size", "1024"); if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 1024) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA page_size={0}", defValue); cmd.ExecuteNonQuery(); } } defValue = FindKey(opts, "Max Page Count", "0"); if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 0) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA max_page_count={0}", defValue); cmd.ExecuteNonQuery(); } defValue = FindKey(opts, "Legacy Format", Boolean.FalseString); cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA legacy_file_format={0}", SQLiteConvert.ToBoolean(defValue) == true ? "ON" : "OFF"); cmd.ExecuteNonQuery(); defValue = FindKey(opts, "Synchronous", "Normal"); if (String.Compare(defValue, "Full", StringComparison.OrdinalIgnoreCase) != 0) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA synchronous={0}", defValue); cmd.ExecuteNonQuery(); } defValue = FindKey(opts, "Cache Size", "2000"); if (Convert.ToInt32(defValue, CultureInfo.InvariantCulture) != 2000) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA cache_size={0}", defValue); cmd.ExecuteNonQuery(); } defValue = FindKey(opts, "Journal Mode", "Delete"); if (String.Compare(defValue, "Default", StringComparison.OrdinalIgnoreCase) != 0) { cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA journal_mode={0}", defValue); cmd.ExecuteNonQuery(); } defValue = FindKey(opts, "Foreign Keys", Boolean.FalseString); cmd.CommandText = String.Format(CultureInfo.InvariantCulture, "PRAGMA foreign_keys={0}", SQLiteConvert.ToBoolean(defValue) == true ? "ON" : "OFF"); cmd.ExecuteNonQuery(); } if (_commitHandler != null) _sql.SetCommitHook(_commitCallback); if (_updateHandler != null) _sql.SetUpdateHook(_updateCallback); if (_rollbackHandler != null) _sql.SetRollbackHook(_rollbackCallback); #if !PLATFORM_COMPACTFRAMEWORK if (Transactions.Transaction.Current != null && SQLiteConvert.ToBoolean(FindKey(opts, "Enlist", Boolean.TrueString)) == true) EnlistTransaction(Transactions.Transaction.Current); #endif _connectionState = oldstate; OnStateChange(ConnectionState.Open); } catch { _connectionState = oldstate; throw; } } catch (SQLiteException) { Close(); throw; } } /// /// Gets/sets the default command timeout for newly-created commands. This is especially useful for /// commands used internally such as inside a SQLiteTransaction, where setting the timeout is not possible. /// This can also be set in the ConnectionString with "Default Timeout" /// public int DefaultTimeout { get { return _defaultTimeout; } set { _defaultTimeout = value; } } /// /// Returns the version of the underlying SQLite database engine /// #if !PLATFORM_COMPACTFRAMEWORK [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] #endif public override string ServerVersion { get { return SQLiteVersion; //if (_connectionState != ConnectionState.Open) // throw new InvalidOperationException(); //return _sql.Version; } } /// /// Returns the version of the underlying SQLite database engine /// public static string SQLiteVersion { get { return SQLite3.SQLiteVersion; } } /// /// Returns the state of the connection. /// #if !PLATFORM_COMPACTFRAMEWORK [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] #endif public override ConnectionState State { get { return _connectionState; } } /// /// Change the password (or assign a password) to an open database. /// /// /// No readers or writers may be active for this process. The database must already be open /// and if it already was password protected, the existing password must already have been supplied. /// /// The new password to assign to the database public void ChangePassword(string newPassword) { ChangePassword(String.IsNullOrEmpty(newPassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(newPassword)); } /// /// Change the password (or assign a password) to an open database. /// /// /// No readers or writers may be active for this process. The database must already be open /// and if it already was password protected, the existing password must already have been supplied. /// /// The new password to assign to the database public void ChangePassword(byte[] newPassword) { if (_connectionState != ConnectionState.Open) throw new InvalidOperationException("Database must be opened before changing the password."); _sql.ChangePassword(newPassword); } /// /// Sets the password for a password-protected database. A password-protected database is /// unusable for any operation until the password has been set. /// /// The password for the database public void SetPassword(string databasePassword) { SetPassword(String.IsNullOrEmpty(databasePassword) ? null : System.Text.UTF8Encoding.UTF8.GetBytes(databasePassword)); } /// /// Sets the password for a password-protected database. A password-protected database is /// unusable for any operation until the password has been set. /// /// The password for the database public void SetPassword(byte[] databasePassword) { if (_connectionState != ConnectionState.Closed) throw new InvalidOperationException("Password can only be set before the database is opened."); if (databasePassword != null) if (databasePassword.Length == 0) databasePassword = null; _password = databasePassword; } /// /// Expand the filename of the data source, resolving the |DataDirectory| macro as appropriate. /// /// The database filename to expand /// The expanded path and filename of the filename private string ExpandFileName(string sourceFile) { if (String.IsNullOrEmpty(sourceFile)) return sourceFile; if (sourceFile.StartsWith(_dataDirectory, StringComparison.OrdinalIgnoreCase)) { string dataDirectory; #if PLATFORM_COMPACTFRAMEWORK dataDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase); #else dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory") as string; if (String.IsNullOrEmpty(dataDirectory)) dataDirectory = AppDomain.CurrentDomain.BaseDirectory; #endif if (sourceFile.Length > _dataDirectory.Length) { if (sourceFile[_dataDirectory.Length] == Path.DirectorySeparatorChar || sourceFile[_dataDirectory.Length] == Path.AltDirectorySeparatorChar) sourceFile = sourceFile.Remove(_dataDirectory.Length, 1); } sourceFile = Path.Combine(dataDirectory, sourceFile.Substring(_dataDirectory.Length)); } #if !PLATFORM_COMPACTFRAMEWORK sourceFile = Path.GetFullPath(sourceFile); #endif return sourceFile; } /// /// The following commands are used to extract schema information out of the database. Valid schema types are: /// /// /// MetaDataCollections /// /// /// DataSourceInformation /// /// /// Catalogs /// /// /// Columns /// /// /// ForeignKeys /// /// /// Indexes /// /// /// IndexColumns /// /// /// Tables /// /// /// Views /// /// /// ViewColumns /// /// /// /// /// Returns the MetaDataCollections schema /// /// A DataTable of the MetaDataCollections schema public override DataTable GetSchema() { return GetSchema("MetaDataCollections", null); } /// /// Returns schema information of the specified collection /// /// The schema collection to retrieve /// A DataTable of the specified collection public override DataTable GetSchema(string collectionName) { return GetSchema(collectionName, new string[0]); } /// /// Retrieves schema information using the specified constraint(s) for the specified collection /// /// The collection to retrieve /// The restrictions to impose /// A DataTable of the specified collection public override DataTable GetSchema(string collectionName, string[] restrictionValues) { if (_connectionState != ConnectionState.Open) throw new InvalidOperationException(); string[] parms = new string[5]; if (restrictionValues == null) restrictionValues = new string[0]; restrictionValues.CopyTo(parms, 0); switch (collectionName.ToUpper(CultureInfo.InvariantCulture)) { case "METADATACOLLECTIONS": return Schema_MetaDataCollections(); case "DATASOURCEINFORMATION": return Schema_DataSourceInformation(); case "DATATYPES": return Schema_DataTypes(); case "COLUMNS": case "TABLECOLUMNS": return Schema_Columns(parms[0], parms[2], parms[3]); case "INDEXES": return Schema_Indexes(parms[0], parms[2], parms[3]); case "TRIGGERS": return Schema_Triggers(parms[0], parms[2], parms[3]); case "INDEXCOLUMNS": return Schema_IndexColumns(parms[0], parms[2], parms[3], parms[4]); case "TABLES": return Schema_Tables(parms[0], parms[2], parms[3]); case "VIEWS": return Schema_Views(parms[0], parms[2]); case "VIEWCOLUMNS": return Schema_ViewColumns(parms[0], parms[2], parms[3]); case "FOREIGNKEYS": return Schema_ForeignKeys(parms[0], parms[2], parms[3]); case "CATALOGS": return Schema_Catalogs(parms[0]); case "RESERVEDWORDS": return Schema_ReservedWords(); } throw new NotSupportedException(); } private static DataTable Schema_ReservedWords() { DataTable tbl = new DataTable("MetaDataCollections"); tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("ReservedWord", typeof(string)); tbl.Columns.Add("MaximumVersion", typeof(string)); tbl.Columns.Add("MinimumVersion", typeof(string)); tbl.BeginLoadData(); DataRow row; foreach (string word in SR.Keywords.Split(new char[] { ',' })) { row = tbl.NewRow(); row[0] = word; tbl.Rows.Add(row); } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Builds a MetaDataCollections schema datatable /// /// DataTable private static DataTable Schema_MetaDataCollections() { DataTable tbl = new DataTable("MetaDataCollections"); tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("CollectionName", typeof(string)); tbl.Columns.Add("NumberOfRestrictions", typeof(int)); tbl.Columns.Add("NumberOfIdentifierParts", typeof(int)); tbl.BeginLoadData(); StringReader reader = new StringReader(SR.MetaDataCollections); tbl.ReadXml(reader); reader.Close(); tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Builds a DataSourceInformation datatable /// /// DataTable private DataTable Schema_DataSourceInformation() { DataTable tbl = new DataTable("DataSourceInformation"); DataRow row; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add(DbMetaDataColumnNames.CompositeIdentifierSeparatorPattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductName, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductVersion, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.DataSourceProductVersionNormalized, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.GroupByBehavior, typeof(int)); tbl.Columns.Add(DbMetaDataColumnNames.IdentifierPattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.IdentifierCase, typeof(int)); tbl.Columns.Add(DbMetaDataColumnNames.OrderByColumnsInSelect, typeof(bool)); tbl.Columns.Add(DbMetaDataColumnNames.ParameterMarkerFormat, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.ParameterMarkerPattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.ParameterNameMaxLength, typeof(int)); tbl.Columns.Add(DbMetaDataColumnNames.ParameterNamePattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.QuotedIdentifierPattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.QuotedIdentifierCase, typeof(int)); tbl.Columns.Add(DbMetaDataColumnNames.StatementSeparatorPattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.StringLiteralPattern, typeof(string)); tbl.Columns.Add(DbMetaDataColumnNames.SupportedJoinOperators, typeof(int)); tbl.BeginLoadData(); row = tbl.NewRow(); row.ItemArray = new object[] { null, "SQLite", _sql.Version, _sql.Version, 3, @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)", 1, false, "{0}", @"@[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)", 255, @"^[\p{Lo}\p{Lu}\p{Ll}\p{Lm}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Lm}\p{Nd}\uff3f_@#\$]*(?=\s+|$)", @"(([^\[]|\]\])*)", 1, ";", @"'(([^']|'')*)'", 15 }; tbl.Rows.Add(row); tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Build a Columns schema /// /// The catalog (attached database) to query, can be null /// The table to retrieve schema information for, must not be null /// The column to retrieve schema information for, can be null /// DataTable private DataTable Schema_Columns(string strCatalog, string strTable, string strColumn) { DataTable tbl = new DataTable("Columns"); DataRow row; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("COLUMN_NAME", typeof(string)); tbl.Columns.Add("COLUMN_GUID", typeof(Guid)); tbl.Columns.Add("COLUMN_PROPID", typeof(long)); tbl.Columns.Add("ORDINAL_POSITION", typeof(int)); tbl.Columns.Add("COLUMN_HASDEFAULT", typeof(bool)); tbl.Columns.Add("COLUMN_DEFAULT", typeof(string)); tbl.Columns.Add("COLUMN_FLAGS", typeof(long)); tbl.Columns.Add("IS_NULLABLE", typeof(bool)); tbl.Columns.Add("DATA_TYPE", typeof(string)); tbl.Columns.Add("TYPE_GUID", typeof(Guid)); tbl.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int)); tbl.Columns.Add("CHARACTER_OCTET_LENGTH", typeof(int)); tbl.Columns.Add("NUMERIC_PRECISION", typeof(int)); tbl.Columns.Add("NUMERIC_SCALE", typeof(int)); tbl.Columns.Add("DATETIME_PRECISION", typeof(long)); tbl.Columns.Add("CHARACTER_SET_CATALOG", typeof(string)); tbl.Columns.Add("CHARACTER_SET_SCHEMA", typeof(string)); tbl.Columns.Add("CHARACTER_SET_NAME", typeof(string)); tbl.Columns.Add("COLLATION_CATALOG", typeof(string)); tbl.Columns.Add("COLLATION_SCHEMA", typeof(string)); tbl.Columns.Add("COLLATION_NAME", typeof(string)); tbl.Columns.Add("DOMAIN_CATALOG", typeof(string)); tbl.Columns.Add("DOMAIN_NAME", typeof(string)); tbl.Columns.Add("DESCRIPTION", typeof(string)); tbl.Columns.Add("PRIMARY_KEY", typeof(bool)); tbl.Columns.Add("EDM_TYPE", typeof(string)); tbl.Columns.Add("AUTOINCREMENT", typeof(bool)); tbl.Columns.Add("UNIQUE", typeof(bool)); tbl.BeginLoadData(); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table' OR [type] LIKE 'view'", strCatalog, master), this)) using (SQLiteDataReader rdTables = cmdTables.ExecuteReader()) { while (rdTables.Read()) { if (String.IsNullOrEmpty(strTable) || String.Compare(strTable, rdTables.GetString(2), StringComparison.OrdinalIgnoreCase) == 0) { try { using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdTables.GetString(2)), this)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader(CommandBehavior.SchemaOnly)) using (DataTable tblSchema = rd.GetSchemaTable(true, true)) { foreach (DataRow schemaRow in tblSchema.Rows) { if (String.Compare(schemaRow[SchemaTableColumn.ColumnName].ToString(), strColumn, StringComparison.OrdinalIgnoreCase) == 0 || strColumn == null) { row = tbl.NewRow(); row["NUMERIC_PRECISION"] = schemaRow[SchemaTableColumn.NumericPrecision]; row["NUMERIC_SCALE"] = schemaRow[SchemaTableColumn.NumericScale]; row["TABLE_NAME"] = rdTables.GetString(2); row["COLUMN_NAME"] = schemaRow[SchemaTableColumn.ColumnName]; row["TABLE_CATALOG"] = strCatalog; row["ORDINAL_POSITION"] = schemaRow[SchemaTableColumn.ColumnOrdinal]; row["COLUMN_HASDEFAULT"] = (schemaRow[SchemaTableOptionalColumn.DefaultValue] != DBNull.Value); row["COLUMN_DEFAULT"] = schemaRow[SchemaTableOptionalColumn.DefaultValue]; row["IS_NULLABLE"] = schemaRow[SchemaTableColumn.AllowDBNull]; row["DATA_TYPE"] = schemaRow["DataTypeName"].ToString().ToLower(CultureInfo.InvariantCulture); row["EDM_TYPE"] = SQLiteConvert.DbTypeToTypeName((DbType)schemaRow[SchemaTableColumn.ProviderType]).ToString().ToLower(CultureInfo.InvariantCulture); row["CHARACTER_MAXIMUM_LENGTH"] = schemaRow[SchemaTableColumn.ColumnSize]; row["TABLE_SCHEMA"] = schemaRow[SchemaTableColumn.BaseSchemaName]; row["PRIMARY_KEY"] = schemaRow[SchemaTableColumn.IsKey]; row["AUTOINCREMENT"] = schemaRow[SchemaTableOptionalColumn.IsAutoIncrement]; row["COLLATION_NAME"] = schemaRow["CollationType"]; row["UNIQUE"] = schemaRow[SchemaTableColumn.IsUnique]; tbl.Rows.Add(row); } } } } catch(SQLiteException) { } } } } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Returns index information for the given database and catalog /// /// The catalog (attached database) to query, can be null /// The name of the index to retrieve information for, can be null /// The table to retrieve index information for, can be null /// DataTable private DataTable Schema_Indexes(string strCatalog, string strTable, string strIndex) { DataTable tbl = new DataTable("Indexes"); DataRow row; List primaryKeys = new List(); bool maybeRowId; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("INDEX_CATALOG", typeof(string)); tbl.Columns.Add("INDEX_SCHEMA", typeof(string)); tbl.Columns.Add("INDEX_NAME", typeof(string)); tbl.Columns.Add("PRIMARY_KEY", typeof(bool)); tbl.Columns.Add("UNIQUE", typeof(bool)); tbl.Columns.Add("CLUSTERED", typeof(bool)); tbl.Columns.Add("TYPE", typeof(int)); tbl.Columns.Add("FILL_FACTOR", typeof(int)); tbl.Columns.Add("INITIAL_SIZE", typeof(int)); tbl.Columns.Add("NULLS", typeof(int)); tbl.Columns.Add("SORT_BOOKMARKS", typeof(bool)); tbl.Columns.Add("AUTO_UPDATE", typeof(bool)); tbl.Columns.Add("NULL_COLLATION", typeof(int)); tbl.Columns.Add("ORDINAL_POSITION", typeof(int)); tbl.Columns.Add("COLUMN_NAME", typeof(string)); tbl.Columns.Add("COLUMN_GUID", typeof(Guid)); tbl.Columns.Add("COLUMN_PROPID", typeof(long)); tbl.Columns.Add("COLLATION", typeof(short)); tbl.Columns.Add("CARDINALITY", typeof(Decimal)); tbl.Columns.Add("PAGES", typeof(int)); tbl.Columns.Add("FILTER_CONDITION", typeof(string)); tbl.Columns.Add("INTEGRATED", typeof(bool)); tbl.Columns.Add("INDEX_DEFINITION", typeof(string)); tbl.BeginLoadData(); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this)) using (SQLiteDataReader rdTables = cmdTables.ExecuteReader()) { while (rdTables.Read()) { maybeRowId = false; primaryKeys.Clear(); if (String.IsNullOrEmpty(strTable) || String.Compare(rdTables.GetString(2), strTable, StringComparison.OrdinalIgnoreCase) == 0) { // First, look for any rowid indexes -- which sqlite defines are INTEGER PRIMARY KEY columns. // Such indexes are not listed in the indexes list but count as indexes just the same. try { using (SQLiteCommand cmdTable = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].table_info([{1}])", strCatalog, rdTables.GetString(2)), this)) using (SQLiteDataReader rdTable = cmdTable.ExecuteReader()) { while (rdTable.Read()) { if (rdTable.GetInt32(5) == 1) { primaryKeys.Add(rdTable.GetInt32(0)); // If the primary key is of type INTEGER, then its a rowid and we need to make a fake index entry for it. if (String.Compare(rdTable.GetString(2), "INTEGER", StringComparison.OrdinalIgnoreCase) == 0) maybeRowId = true; } } } } catch (SQLiteException) { } if (primaryKeys.Count == 1 && maybeRowId == true) { row = tbl.NewRow(); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = rdTables.GetString(2); row["INDEX_CATALOG"] = strCatalog; row["PRIMARY_KEY"] = true; row["INDEX_NAME"] = String.Format(CultureInfo.InvariantCulture, "{1}_PK_{0}", rdTables.GetString(2), master); row["UNIQUE"] = true; if (String.Compare((string)row["INDEX_NAME"], strIndex, StringComparison.OrdinalIgnoreCase) == 0 || strIndex == null) { tbl.Rows.Add(row); } primaryKeys.Clear(); } // Now fetch all the rest of the indexes. try { using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_list([{1}])", strCatalog, rdTables.GetString(2)), this)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader()) { while (rd.Read()) { if (String.Compare(rd.GetString(1), strIndex, StringComparison.OrdinalIgnoreCase) == 0 || strIndex == null) { row = tbl.NewRow(); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = rdTables.GetString(2); row["INDEX_CATALOG"] = strCatalog; row["INDEX_NAME"] = rd.GetString(1); row["UNIQUE"] = rd.GetBoolean(2); row["PRIMARY_KEY"] = false; // get the index definition using (SQLiteCommand cmdIndexes = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{2}] WHERE [type] LIKE 'index' AND [name] LIKE '{1}'", strCatalog, rd.GetString(1).Replace("'", "''"), master), this)) using (SQLiteDataReader rdIndexes = cmdIndexes.ExecuteReader()) { while (rdIndexes.Read()) { if (rdIndexes.IsDBNull(4) == false) row["INDEX_DEFINITION"] = rdIndexes.GetString(4); break; } } // Now for the really hard work. Figure out which index is the primary key index. // The only way to figure it out is to check if the index was an autoindex and if we have a non-rowid // primary key, and all the columns in the given index match the primary key columns if (primaryKeys.Count > 0 && rd.GetString(1).StartsWith("sqlite_autoindex_" + rdTables.GetString(2), StringComparison.InvariantCultureIgnoreCase) == true) { using (SQLiteCommand cmdDetails = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_info([{1}])", strCatalog, rd.GetString(1)), this)) using (SQLiteDataReader rdDetails = cmdDetails.ExecuteReader()) { int nMatches = 0; while (rdDetails.Read()) { if (primaryKeys.Contains(rdDetails.GetInt32(1)) == false) { nMatches = 0; break; } nMatches++; } if (nMatches == primaryKeys.Count) { row["PRIMARY_KEY"] = true; primaryKeys.Clear(); } } } tbl.Rows.Add(row); } } } } catch (SQLiteException) { } } } } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } private DataTable Schema_Triggers(string catalog, string table, string triggerName) { DataTable tbl = new DataTable("Triggers"); DataRow row; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("TRIGGER_NAME", typeof(string)); tbl.Columns.Add("TRIGGER_DEFINITION", typeof(string)); tbl.BeginLoadData(); if (String.IsNullOrEmpty(table)) table = null; if (String.IsNullOrEmpty(catalog)) catalog = "main"; string master = (String.Compare(catalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT [type], [name], [tbl_name], [rootpage], [sql], [rowid] FROM [{0}].[{1}] WHERE [type] LIKE 'trigger'", catalog, master), this)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader()) { while (rd.Read()) { if (String.Compare(rd.GetString(1), triggerName, StringComparison.OrdinalIgnoreCase) == 0 || triggerName == null) { if (table == null || String.Compare(table, rd.GetString(2), StringComparison.OrdinalIgnoreCase) == 0) { row = tbl.NewRow(); row["TABLE_CATALOG"] = catalog; row["TABLE_NAME"] = rd.GetString(2); row["TRIGGER_NAME"] = rd.GetString(1); row["TRIGGER_DEFINITION"] = rd.GetString(4); tbl.Rows.Add(row); } } } } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Retrieves table schema information for the database and catalog /// /// The catalog (attached database) to retrieve tables on /// The table to retrieve, can be null /// The table type, can be null /// DataTable private DataTable Schema_Tables(string strCatalog, string strTable, string strType) { DataTable tbl = new DataTable("Tables"); DataRow row; string strItem; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("TABLE_TYPE", typeof(string)); tbl.Columns.Add("TABLE_ID", typeof(long)); tbl.Columns.Add("TABLE_ROOTPAGE", typeof(int)); tbl.Columns.Add("TABLE_DEFINITION", typeof(string)); tbl.BeginLoadData(); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT [type], [name], [tbl_name], [rootpage], [sql], [rowid] FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader()) { while (rd.Read()) { strItem = rd.GetString(0); if (String.Compare(rd.GetString(2), 0, "SQLITE_", 0, 7, StringComparison.OrdinalIgnoreCase) == 0) strItem = "SYSTEM_TABLE"; if (String.Compare(strType, strItem, StringComparison.OrdinalIgnoreCase) == 0 || strType == null) { if (String.Compare(rd.GetString(2), strTable, StringComparison.OrdinalIgnoreCase) == 0 || strTable == null) { row = tbl.NewRow(); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = rd.GetString(2); row["TABLE_TYPE"] = strItem; row["TABLE_ID"] = rd.GetInt64(5); row["TABLE_ROOTPAGE"] = rd.GetInt32(3); row["TABLE_DEFINITION"] = rd.GetString(4); tbl.Rows.Add(row); } } } } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Retrieves view schema information for the database /// /// The catalog (attached database) to retrieve views on /// The view name, can be null /// DataTable private DataTable Schema_Views(string strCatalog, string strView) { DataTable tbl = new DataTable("Views"); DataRow row; string strItem; int nPos; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("VIEW_DEFINITION", typeof(string)); tbl.Columns.Add("CHECK_OPTION", typeof(bool)); tbl.Columns.Add("IS_UPDATABLE", typeof(bool)); tbl.Columns.Add("DESCRIPTION", typeof(string)); tbl.Columns.Add("DATE_CREATED", typeof(DateTime)); tbl.Columns.Add("DATE_MODIFIED", typeof(DateTime)); tbl.BeginLoadData(); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; using (SQLiteCommand cmd = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'view'", strCatalog, master), this)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader()) { while (rd.Read()) { if (String.Compare(rd.GetString(1), strView, StringComparison.OrdinalIgnoreCase) == 0 || String.IsNullOrEmpty(strView)) { strItem = rd.GetString(4).Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' '); nPos = CultureInfo.InvariantCulture.CompareInfo.IndexOf(strItem, " AS ", CompareOptions.IgnoreCase); if (nPos > -1) { strItem = strItem.Substring(nPos + 4).Trim(); row = tbl.NewRow(); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = rd.GetString(2); row["IS_UPDATABLE"] = false; row["VIEW_DEFINITION"] = strItem; tbl.Rows.Add(row); } } } } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Retrieves catalog (attached databases) schema information for the database /// /// The catalog to retrieve, can be null /// DataTable private DataTable Schema_Catalogs(string strCatalog) { DataTable tbl = new DataTable("Catalogs"); DataRow row; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("CATALOG_NAME", typeof(string)); tbl.Columns.Add("DESCRIPTION", typeof(string)); tbl.Columns.Add("ID", typeof(long)); tbl.BeginLoadData(); using (SQLiteCommand cmd = new SQLiteCommand("PRAGMA database_list", this)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader()) { while (rd.Read()) { if (String.Compare(rd.GetString(1), strCatalog, StringComparison.OrdinalIgnoreCase) == 0 || strCatalog == null) { row = tbl.NewRow(); row["CATALOG_NAME"] = rd.GetString(1); row["DESCRIPTION"] = rd.GetString(2); row["ID"] = rd.GetInt64(0); tbl.Rows.Add(row); } } } tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } private DataTable Schema_DataTypes() { DataTable tbl = new DataTable("DataTypes"); tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("TypeName", typeof(String)); tbl.Columns.Add("ProviderDbType", typeof(int)); tbl.Columns.Add("ColumnSize", typeof(long)); tbl.Columns.Add("CreateFormat", typeof(String)); tbl.Columns.Add("CreateParameters", typeof(String)); tbl.Columns.Add("DataType", typeof(String)); tbl.Columns.Add("IsAutoIncrementable", typeof(bool)); tbl.Columns.Add("IsBestMatch", typeof(bool)); tbl.Columns.Add("IsCaseSensitive", typeof(bool)); tbl.Columns.Add("IsFixedLength", typeof(bool)); tbl.Columns.Add("IsFixedPrecisionScale", typeof(bool)); tbl.Columns.Add("IsLong", typeof(bool)); tbl.Columns.Add("IsNullable", typeof(bool)); tbl.Columns.Add("IsSearchable", typeof(bool)); tbl.Columns.Add("IsSearchableWithLike", typeof(bool)); tbl.Columns.Add("IsLiteralSupported", typeof(bool)); tbl.Columns.Add("LiteralPrefix", typeof(String)); tbl.Columns.Add("LiteralSuffix", typeof(String)); tbl.Columns.Add("IsUnsigned", typeof(bool)); tbl.Columns.Add("MaximumScale", typeof(short)); tbl.Columns.Add("MinimumScale", typeof(short)); tbl.Columns.Add("IsConcurrencyType", typeof(bool)); tbl.BeginLoadData(); StringReader reader = new StringReader(SR.DataTypes); tbl.ReadXml(reader); reader.Close(); tbl.AcceptChanges(); tbl.EndLoadData(); return tbl; } /// /// Returns the base column information for indexes in a database /// /// The catalog to retrieve indexes for (can be null) /// The table to restrict index information by (can be null) /// The index to restrict index information by (can be null) /// The source column to restrict index information by (can be null) /// A DataTable containing the results private DataTable Schema_IndexColumns(string strCatalog, string strTable, string strIndex, string strColumn) { DataTable tbl = new DataTable("IndexColumns"); DataRow row; List> primaryKeys = new List>(); bool maybeRowId; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("CONSTRAINT_CATALOG", typeof(string)); tbl.Columns.Add("CONSTRAINT_SCHEMA", typeof(string)); tbl.Columns.Add("CONSTRAINT_NAME", typeof(string)); tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("COLUMN_NAME", typeof(string)); tbl.Columns.Add("ORDINAL_POSITION", typeof(int)); tbl.Columns.Add("INDEX_NAME", typeof(string)); tbl.Columns.Add("COLLATION_NAME", typeof(string)); tbl.Columns.Add("SORT_MODE", typeof(string)); tbl.Columns.Add("CONFLICT_OPTION", typeof(int)); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; tbl.BeginLoadData(); using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this)) using (SQLiteDataReader rdTables = cmdTables.ExecuteReader()) { while (rdTables.Read()) { maybeRowId = false; primaryKeys.Clear(); if (String.IsNullOrEmpty(strTable) || String.Compare(rdTables.GetString(2), strTable, StringComparison.OrdinalIgnoreCase) == 0) { try { using (SQLiteCommand cmdTable = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].table_info([{1}])", strCatalog, rdTables.GetString(2)), this)) using (SQLiteDataReader rdTable = cmdTable.ExecuteReader()) { while (rdTable.Read()) { if (rdTable.GetInt32(5) == 1) // is a primary key { primaryKeys.Add(new KeyValuePair(rdTable.GetInt32(0), rdTable.GetString(1))); // Is an integer -- could be a rowid if no other primary keys exist in the table if (String.Compare(rdTable.GetString(2), "INTEGER", StringComparison.OrdinalIgnoreCase) == 0) maybeRowId = true; } } } } catch (SQLiteException) { } // This is a rowid row if (primaryKeys.Count == 1 && maybeRowId == true) { row = tbl.NewRow(); row["CONSTRAINT_CATALOG"] = strCatalog; row["CONSTRAINT_NAME"] = String.Format(CultureInfo.InvariantCulture, "{1}_PK_{0}", rdTables.GetString(2), master); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = rdTables.GetString(2); row["COLUMN_NAME"] = primaryKeys[0].Value; row["INDEX_NAME"] = row["CONSTRAINT_NAME"]; row["ORDINAL_POSITION"] = 0; // primaryKeys[0].Key; row["COLLATION_NAME"] = "BINARY"; row["SORT_MODE"] = "ASC"; row["CONFLICT_OPTION"] = 2; if (String.IsNullOrEmpty(strIndex) || String.Compare(strIndex, (string)row["INDEX_NAME"], StringComparison.OrdinalIgnoreCase) == 0) tbl.Rows.Add(row); } using (SQLiteCommand cmdIndexes = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{2}] WHERE [type] LIKE 'index' AND [tbl_name] LIKE '{1}'", strCatalog, rdTables.GetString(2).Replace("'", "''"), master), this)) using (SQLiteDataReader rdIndexes = cmdIndexes.ExecuteReader()) { while (rdIndexes.Read()) { int ordinal = 0; if (String.IsNullOrEmpty(strIndex) || String.Compare(strIndex, rdIndexes.GetString(1), StringComparison.OrdinalIgnoreCase) == 0) { try { using (SQLiteCommand cmdIndex = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].index_info([{1}])", strCatalog, rdIndexes.GetString(1)), this)) using (SQLiteDataReader rdIndex = cmdIndex.ExecuteReader()) { while (rdIndex.Read()) { row = tbl.NewRow(); row["CONSTRAINT_CATALOG"] = strCatalog; row["CONSTRAINT_NAME"] = rdIndexes.GetString(1); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = rdIndexes.GetString(2); row["COLUMN_NAME"] = rdIndex.GetString(2); row["INDEX_NAME"] = rdIndexes.GetString(1); row["ORDINAL_POSITION"] = ordinal; // rdIndex.GetInt32(1); string collationSequence; int sortMode; int onError; _sql.GetIndexColumnExtendedInfo(strCatalog, rdIndexes.GetString(1), rdIndex.GetString(2), out sortMode, out onError, out collationSequence); if (String.IsNullOrEmpty(collationSequence) == false) row["COLLATION_NAME"] = collationSequence; row["SORT_MODE"] = (sortMode == 0) ? "ASC" : "DESC"; row["CONFLICT_OPTION"] = onError; ordinal++; if (String.IsNullOrEmpty(strColumn) || String.Compare(strColumn, row["COLUMN_NAME"].ToString(), StringComparison.OrdinalIgnoreCase) == 0) tbl.Rows.Add(row); } } } catch (SQLiteException) { } } } } } } } tbl.EndLoadData(); tbl.AcceptChanges(); return tbl; } /// /// Returns detailed column information for a specified view /// /// The catalog to retrieve columns for (can be null) /// The view to restrict column information by (can be null) /// The source column to restrict column information by (can be null) /// A DataTable containing the results private DataTable Schema_ViewColumns(string strCatalog, string strView, string strColumn) { DataTable tbl = new DataTable("ViewColumns"); DataRow row; string strSql; int n; DataRow schemaRow; DataRow viewRow; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("VIEW_CATALOG", typeof(string)); tbl.Columns.Add("VIEW_SCHEMA", typeof(string)); tbl.Columns.Add("VIEW_NAME", typeof(string)); tbl.Columns.Add("VIEW_COLUMN_NAME", typeof(String)); tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("COLUMN_NAME", typeof(string)); tbl.Columns.Add("ORDINAL_POSITION", typeof(int)); tbl.Columns.Add("COLUMN_HASDEFAULT", typeof(bool)); tbl.Columns.Add("COLUMN_DEFAULT", typeof(string)); tbl.Columns.Add("COLUMN_FLAGS", typeof(long)); tbl.Columns.Add("IS_NULLABLE", typeof(bool)); tbl.Columns.Add("DATA_TYPE", typeof(string)); tbl.Columns.Add("CHARACTER_MAXIMUM_LENGTH", typeof(int)); tbl.Columns.Add("NUMERIC_PRECISION", typeof(int)); tbl.Columns.Add("NUMERIC_SCALE", typeof(int)); tbl.Columns.Add("DATETIME_PRECISION", typeof(long)); tbl.Columns.Add("CHARACTER_SET_CATALOG", typeof(string)); tbl.Columns.Add("CHARACTER_SET_SCHEMA", typeof(string)); tbl.Columns.Add("CHARACTER_SET_NAME", typeof(string)); tbl.Columns.Add("COLLATION_CATALOG", typeof(string)); tbl.Columns.Add("COLLATION_SCHEMA", typeof(string)); tbl.Columns.Add("COLLATION_NAME", typeof(string)); tbl.Columns.Add("PRIMARY_KEY", typeof(bool)); tbl.Columns.Add("EDM_TYPE", typeof(string)); tbl.Columns.Add("AUTOINCREMENT", typeof(bool)); tbl.Columns.Add("UNIQUE", typeof(bool)); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; tbl.BeginLoadData(); using (SQLiteCommand cmdViews = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'view'", strCatalog, master), this)) using (SQLiteDataReader rdViews = cmdViews.ExecuteReader()) { while (rdViews.Read()) { if (String.IsNullOrEmpty(strView) || String.Compare(strView, rdViews.GetString(2), StringComparison.OrdinalIgnoreCase) == 0) { using (SQLiteCommand cmdViewSelect = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdViews.GetString(2)), this)) { strSql = rdViews.GetString(4).Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' '); n = CultureInfo.InvariantCulture.CompareInfo.IndexOf(strSql, " AS ", CompareOptions.IgnoreCase); if (n < 0) continue; strSql = strSql.Substring(n + 4); using (SQLiteCommand cmd = new SQLiteCommand(strSql, this)) using (SQLiteDataReader rdViewSelect = cmdViewSelect.ExecuteReader(CommandBehavior.SchemaOnly)) using (SQLiteDataReader rd = (SQLiteDataReader)cmd.ExecuteReader(CommandBehavior.SchemaOnly)) using (DataTable tblSchemaView = rdViewSelect.GetSchemaTable(false, false)) using (DataTable tblSchema = rd.GetSchemaTable(false, false)) { for (n = 0; n < tblSchema.Rows.Count; n++) { viewRow = tblSchemaView.Rows[n]; schemaRow = tblSchema.Rows[n]; if (String.Compare(viewRow[SchemaTableColumn.ColumnName].ToString(), strColumn, StringComparison.OrdinalIgnoreCase) == 0 || strColumn == null) { row = tbl.NewRow(); row["VIEW_CATALOG"] = strCatalog; row["VIEW_NAME"] = rdViews.GetString(2); row["TABLE_CATALOG"] = strCatalog; row["TABLE_SCHEMA"] = schemaRow[SchemaTableColumn.BaseSchemaName]; row["TABLE_NAME"] = schemaRow[SchemaTableColumn.BaseTableName]; row["COLUMN_NAME"] = schemaRow[SchemaTableColumn.BaseColumnName]; row["VIEW_COLUMN_NAME"] = viewRow[SchemaTableColumn.ColumnName]; row["COLUMN_HASDEFAULT"] = (viewRow[SchemaTableOptionalColumn.DefaultValue] != DBNull.Value); row["COLUMN_DEFAULT"] = viewRow[SchemaTableOptionalColumn.DefaultValue]; row["ORDINAL_POSITION"] = viewRow[SchemaTableColumn.ColumnOrdinal]; row["IS_NULLABLE"] = viewRow[SchemaTableColumn.AllowDBNull]; row["DATA_TYPE"] = viewRow["DataTypeName"]; // SQLiteConvert.DbTypeToType((DbType)viewRow[SchemaTableColumn.ProviderType]).ToString(); row["EDM_TYPE"] = SQLiteConvert.DbTypeToTypeName((DbType)viewRow[SchemaTableColumn.ProviderType]).ToString().ToLower(CultureInfo.InvariantCulture); row["CHARACTER_MAXIMUM_LENGTH"] = viewRow[SchemaTableColumn.ColumnSize]; row["TABLE_SCHEMA"] = viewRow[SchemaTableColumn.BaseSchemaName]; row["PRIMARY_KEY"] = viewRow[SchemaTableColumn.IsKey]; row["AUTOINCREMENT"] = viewRow[SchemaTableOptionalColumn.IsAutoIncrement]; row["COLLATION_NAME"] = viewRow["CollationType"]; row["UNIQUE"] = viewRow[SchemaTableColumn.IsUnique]; tbl.Rows.Add(row); } } } } } } } tbl.EndLoadData(); tbl.AcceptChanges(); return tbl; } /// /// Retrieves foreign key information from the specified set of filters /// /// An optional catalog to restrict results on /// An optional table to restrict results on /// An optional foreign key name to restrict results on /// A DataTable with the results of the query private DataTable Schema_ForeignKeys(string strCatalog, string strTable, string strKeyName) { DataTable tbl = new DataTable("ForeignKeys"); DataRow row; tbl.Locale = CultureInfo.InvariantCulture; tbl.Columns.Add("CONSTRAINT_CATALOG", typeof(string)); tbl.Columns.Add("CONSTRAINT_SCHEMA", typeof(string)); tbl.Columns.Add("CONSTRAINT_NAME", typeof(string)); tbl.Columns.Add("TABLE_CATALOG", typeof(string)); tbl.Columns.Add("TABLE_SCHEMA", typeof(string)); tbl.Columns.Add("TABLE_NAME", typeof(string)); tbl.Columns.Add("CONSTRAINT_TYPE", typeof(string)); tbl.Columns.Add("IS_DEFERRABLE", typeof(bool)); tbl.Columns.Add("INITIALLY_DEFERRED", typeof(bool)); tbl.Columns.Add("FKEY_FROM_COLUMN", typeof(string)); tbl.Columns.Add("FKEY_FROM_ORDINAL_POSITION", typeof(int)); tbl.Columns.Add("FKEY_TO_CATALOG", typeof(string)); tbl.Columns.Add("FKEY_TO_SCHEMA", typeof(string)); tbl.Columns.Add("FKEY_TO_TABLE", typeof(string)); tbl.Columns.Add("FKEY_TO_COLUMN", typeof(string)); if (String.IsNullOrEmpty(strCatalog)) strCatalog = "main"; string master = (String.Compare(strCatalog, "temp", StringComparison.OrdinalIgnoreCase) == 0) ? _tempmasterdb : _masterdb; tbl.BeginLoadData(); using (SQLiteCommand cmdTables = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}] WHERE [type] LIKE 'table'", strCatalog, master), this)) using (SQLiteDataReader rdTables = cmdTables.ExecuteReader()) { while (rdTables.Read()) { if (String.IsNullOrEmpty(strTable) || String.Compare(strTable, rdTables.GetString(2), StringComparison.OrdinalIgnoreCase) == 0) { try { using (SQLiteCommandBuilder builder = new SQLiteCommandBuilder()) //using (SQLiteCommand cmdTable = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM [{0}].[{1}]", strCatalog, rdTables.GetString(2)), this)) //using (SQLiteDataReader rdTable = cmdTable.ExecuteReader(CommandBehavior.SchemaOnly)) using (SQLiteCommand cmdKey = new SQLiteCommand(String.Format(CultureInfo.InvariantCulture, "PRAGMA [{0}].foreign_key_list([{1}])", strCatalog, rdTables.GetString(2)), this)) using (SQLiteDataReader rdKey = cmdKey.ExecuteReader()) { while (rdKey.Read()) { row = tbl.NewRow(); row["CONSTRAINT_CATALOG"] = strCatalog; row["CONSTRAINT_NAME"] = String.Format(CultureInfo.InvariantCulture, "FK_{0}_{1}", rdTables[2], rdKey.GetInt32(0)); row["TABLE_CATALOG"] = strCatalog; row["TABLE_NAME"] = builder.UnquoteIdentifier(rdTables.GetString(2)); row["CONSTRAINT_TYPE"] = "FOREIGN KEY"; row["IS_DEFERRABLE"] = false; row["INITIALLY_DEFERRED"] = false; row["FKEY_FROM_COLUMN"] = builder.UnquoteIdentifier(rdKey[3].ToString()); row["FKEY_TO_CATALOG"] = strCatalog; row["FKEY_TO_TABLE"] = builder.UnquoteIdentifier(rdKey[2].ToString()); row["FKEY_TO_COLUMN"] = builder.UnquoteIdentifier(rdKey[4].ToString()); row["FKEY_FROM_ORDINAL_POSITION"] = rdKey[1]; if (String.IsNullOrEmpty(strKeyName) || String.Compare(strKeyName, row["CONSTRAINT_NAME"].ToString(), StringComparison.OrdinalIgnoreCase) == 0) tbl.Rows.Add(row); } } } catch (SQLiteException) { } } } } tbl.EndLoadData(); tbl.AcceptChanges(); return tbl; } /// /// This event is raised whenever SQLite makes an update/delete/insert into the database on /// this connection. It only applies to the given connection. /// public event SQLiteUpdateEventHandler Update { add { if (_updateHandler == null) { _updateCallback = new SQLiteUpdateCallback(UpdateCallback); if (_sql != null) _sql.SetUpdateHook(_updateCallback); } _updateHandler += value; } remove { _updateHandler -= value; if (_updateHandler == null) { if (_sql != null) _sql.SetUpdateHook(null); _updateCallback = null; } } } private void UpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid) { _updateHandler(this, new UpdateEventArgs( SQLiteBase.UTF8ToString(database, -1), SQLiteBase.UTF8ToString(table, -1), (UpdateEventType)type, rowid)); } /// /// This event is raised whenever SQLite is committing a transaction. /// Return non-zero to trigger a rollback /// public event SQLiteCommitHandler Commit { add { if (_commitHandler == null) { _commitCallback = new SQLiteCommitCallback(CommitCallback); if (_sql != null) _sql.SetCommitHook(_commitCallback); } _commitHandler += value; } remove { _commitHandler -= value; if (_commitHandler == null) { if (_sql != null) _sql.SetCommitHook(null); _commitCallback = null; } } } /// /// This event is raised whenever SQLite statement first begins executing on /// this connection. It only applies to the given connection. /// public event SQLiteTraceEventHandler Trace { add { if (_traceHandler == null) { _traceCallback = new SQLiteTraceCallback(TraceCallback); if (_sql != null) _sql.SetTraceCallback(_traceCallback); } _traceHandler += value; } remove { _traceHandler -= value; if (_traceHandler == null) { if (_sql != null) _sql.SetTraceCallback(null); _traceCallback = null; } } } private void TraceCallback(IntPtr puser, IntPtr statement) { _traceHandler(this, new TraceEventArgs( SQLiteBase.UTF8ToString(statement, -1))); } /// /// This event is raised whenever SQLite is committing a transaction. /// Return non-zero to trigger a rollback /// public event EventHandler RollBack { add { if (_rollbackHandler == null) { _rollbackCallback = new SQLiteRollbackCallback(RollbackCallback); if (_sql != null) _sql.SetRollbackHook(_rollbackCallback); } _rollbackHandler += value; } remove { _rollbackHandler -= value; if (_rollbackHandler == null) { if (_sql != null) _sql.SetRollbackHook(null); _rollbackCallback = null; } } } private int CommitCallback(IntPtr parg) { CommitEventArgs e = new CommitEventArgs(); _commitHandler(this, e); return (e.AbortTransaction == true) ? 1 : 0; } private void RollbackCallback(IntPtr parg) { _rollbackHandler(this, EventArgs.Empty); } } /// /// The I/O file cache flushing behavior for the connection /// public enum SynchronizationModes { /// /// Normal file flushing at critical sections of the code /// Normal = 0, /// /// Full file flushing after every write operation /// Full = 1, /// /// Use the default operating system's file flushing, SQLite does not explicitly flush the file buffers after writing /// Off = 2, } #if !PLATFORM_COMPACTFRAMEWORK [UnmanagedFunctionPointer(CallingConvention.Cdecl)] #endif internal delegate void SQLiteUpdateCallback(IntPtr puser, int type, IntPtr database, IntPtr table, Int64 rowid); #if !PLATFORM_COMPACTFRAMEWORK [UnmanagedFunctionPointer(CallingConvention.Cdecl)] #endif internal delegate int SQLiteCommitCallback(IntPtr puser); #if !PLATFORM_COMPACTFRAMEWORK [UnmanagedFunctionPointer(CallingConvention.Cdecl)] #endif internal delegate void SQLiteTraceCallback(IntPtr puser, IntPtr statement); #if !PLATFORM_COMPACTFRAMEWORK [UnmanagedFunctionPointer(CallingConvention.Cdecl)] #endif internal delegate void SQLiteRollbackCallback(IntPtr puser); /// /// Raised when a transaction is about to be committed. To roll back a transaction, set the /// rollbackTrans boolean value to true. /// /// The connection committing the transaction /// Event arguments on the transaction public delegate void SQLiteCommitHandler(object sender, CommitEventArgs e); /// /// Raised when data is inserted, updated and deleted on a given connection /// /// The connection committing the transaction /// The event parameters which triggered the event public delegate void SQLiteUpdateEventHandler(object sender, UpdateEventArgs e); /// /// Raised when a statement first begins executing on a given connection /// /// The connection executing the statement /// Event arguments on the trace public delegate void SQLiteTraceEventHandler(object sender, TraceEventArgs e); /// /// Whenever an update event is triggered on a connection, this enum will indicate /// exactly what type of operation is being performed. /// public enum UpdateEventType { /// /// A row is being deleted from the given database and table /// Delete = 9, /// /// A row is being inserted into the table. /// Insert = 18, /// /// A row is being updated in the table. /// Update = 23, } /// /// Passed during an Update callback, these event arguments detail the type of update operation being performed /// on the given connection. /// public class UpdateEventArgs : EventArgs { /// /// The name of the database being updated (usually "main" but can be any attached or temporary database) /// public readonly string Database; /// /// The name of the table being updated /// public readonly string Table; /// /// The type of update being performed (insert/update/delete) /// public readonly UpdateEventType Event; /// /// The RowId affected by this update. /// public readonly Int64 RowId; internal UpdateEventArgs(string database, string table, UpdateEventType eventType, Int64 rowid) { Database = database; Table = table; Event = eventType; RowId = rowid; } } /// /// Event arguments raised when a transaction is being committed /// public class CommitEventArgs : EventArgs { internal CommitEventArgs() { } /// /// Set to true to abort the transaction and trigger a rollback /// public bool AbortTransaction; } /// /// Passed during an Trace callback, these event arguments contain the UTF-8 rendering of the SQL statement text /// public class TraceEventArgs : EventArgs { /// /// SQL statement text as the statement first begins executing /// public readonly string Statement; internal TraceEventArgs(string statement) { Statement = statement; } } }