/// <summary> /// Initializes the statement and attempts to get all information about parameters in the statement /// </summary> /// <param name="sqlbase">The base SQLite object</param> /// <param name="flags">The flags associated with the parent connection object</param> /// <param name="stmt">The statement</param> /// <param name="strCommand">The command text for this statement</param> /// <param name="previous">The previous command in a multi-statement command</param> internal SQLiteStatement(SQLiteBase sqlbase, SQLiteConnectionFlags flags, SQLiteStatementHandle stmt, string strCommand, SQLiteStatement previous) { _sql = sqlbase; _sqlite_stmt = stmt; _sqlStatement = strCommand; _flags = flags; // Determine parameters for this statement (if any) and prepare space for them. int nCmdStart = 0; int n = _sql.Bind_ParamCount(this, _flags); int x; string s; if (n > 0) { if (previous != null) nCmdStart = previous._unnamedParameters; _paramNames = new string[n]; _paramValues = new SQLiteParameter[n]; for (x = 0; x < n; x++) { s = _sql.Bind_ParamName(this, _flags, x + 1); if (String.IsNullOrEmpty(s)) { s = String.Format(CultureInfo.InvariantCulture, ";{0}", nCmdStart); nCmdStart++; _unnamedParameters++; } _paramNames[x] = s; _paramValues[x] = null; } } }
internal override long GetInt64(SQLiteStatement stmt, int index) { long value; #if !PLATFORM_COMPACTFRAMEWORK value = UnsafeNativeMethods.sqlite3_column_int64(stmt._sqlite_stmt, index); #else UnsafeNativeMethods.sqlite3_column_int64_interop(stmt._sqlite_stmt, index, out value); #endif return(value); }
internal override double GetDouble(SQLiteStatement stmt, int index) { double value; #if !PLATFORM_COMPACTFRAMEWORK value = UnsafeNativeMethods.sqlite3_column_double(stmt._sqlite_stmt, index); #else UnsafeNativeMethods.sqlite3_column_double_interop(stmt._sqlite_stmt, index, out value); #endif return(value); }
internal override void FinalizeStatement(SQLiteStatement stmt) { if (stmt._sqlite_stmt != IntPtr.Zero) { int n = UnsafeNativeMethods.sqlite3_finalize_interop(stmt._sqlite_stmt); if (n > 0) { throw new SQLiteException(n, SQLiteLastError()); } } stmt._sqlite_stmt = IntPtr.Zero; }
/// <summary> /// Internal constructor, initializes the datareader and sets up to begin executing statements /// </summary> /// <param name="cmd">The SQLiteCommand this data reader is for</param> /// <param name="behave">The expected behavior of the data reader</param> internal SQLiteDataReader(SQLiteCommand cmd, CommandBehavior behave) { _command = cmd; _commandBehavior = behave; _activeStatementIndex = -1; _activeStatement = null; _rowsAffected = -1; _fieldCount = -1; if (_command != null) NextResult(); }
internal override void Bind_Int64(SQLiteStatement stmt, int index, long value) { #if !PLATFORM_COMPACTFRAMEWORK int n = UnsafeNativeMethods.sqlite3_bind_int64(stmt._sqlite_stmt, index, value); #else int n = UnsafeNativeMethods.sqlite3_bind_int64_interop(stmt._sqlite_stmt, index, ref value); #endif if (n > 0) { throw new SQLiteException(n, SQLiteLastError()); } }
public override void Close() { try { if (this._command != null) { try { try { if (this._version != 0L) { try { while (this.NextResult()) { } } catch { } } this._command.ClearDataReader(); } finally { if (((this._commandBehavior & CommandBehavior.CloseConnection) != CommandBehavior.Default) && (this._command.Connection != null)) { this._command.Connection.Close(); } } } finally { if (this._disposeCommand) { this._command.Dispose(); } } } this._command = null; this._activeStatement = null; this._fieldTypeArray = null; } finally { if (this._keyInfo != null) { this._keyInfo.Dispose(); this._keyInfo = null; } } }
internal override int ColumnIndex(SQLiteStatement stmt, string columnName) { int num = this.ColumnCount(stmt); for (int i = 0; i < num; i++) { if (string.Compare(columnName, this.ColumnName(stmt, i), true, CultureInfo.InvariantCulture) == 0) { return(i); } } return(-1); }
internal override int ColumnIndex(SQLiteStatement stmt, string columnName) { int x = ColumnCount(stmt); for (int n = 0; n < x; n++) { if (String.Compare(columnName, ColumnName(stmt, n), true, CultureInfo.InvariantCulture) == 0) { return(n); } } return(-1); }
internal override int ColumnIndex(SQLiteStatement stmt, string columnName) { int x = ColumnCount(stmt); for (int n = 0; n < x; n++) { if (String.Compare(columnName, ColumnName(stmt, n), StringComparison.OrdinalIgnoreCase) == 0) { return(n); } } return(-1); }
/// <summary> /// Internal constructor, initializes the datareader and sets up to begin executing statements /// </summary> /// <param name="cmd">The SQLiteCommand this data reader is for</param> /// <param name="behave">The expected behavior of the data reader</param> internal SQLiteDataReader(SQLiteCommand cmd, CommandBehavior behave) { _command = cmd; _commandBehavior = behave; _activeStatementIndex = -1; _activeStatement = null; _rowsAffected = -1; _fieldCount = -1; if (_command != null) { NextResult(); } }
/// <summary> /// Builds an array of prepared statements for each complete SQL statement in the command text /// </summary> internal SQLiteStatement BuildNextCommand() { SQLiteStatement stmt = null; try { if ((_cnn != null) && (_cnn._sql != null)) { if (_statementList == null) { _remainingText = _commandText; } stmt = _cnn._sql.Prepare(_cnn, _remainingText, (_statementList == null) ? null : _statementList[_statementList.Count - 1], (uint)(_commandTimeout * 1000), out _remainingText); if (stmt != null) { stmt._command = this; if (_statementList == null) { _statementList = new List <SQLiteStatement>(); } _statementList.Add(stmt); _parameterCollection.MapParameters(stmt); stmt.BindParameters(); } } return(stmt); } catch (Exception) { if (stmt != null) { if ((_statementList != null) && _statementList.Contains(stmt)) { _statementList.Remove(stmt); } stmt.Dispose(); } // If we threw an error compiling the statement, we cannot continue on so set the remaining text to null. _remainingText = null; throw; } }
internal SQLiteDataReader(SQLiteCommand cmd, CommandBehavior behave) { this._command = cmd; this._version = this._command.Connection._version; this._commandBehavior = behave; this._activeStatementIndex = -1; this._activeStatement = null; this._rowsAffected = -1; this._fieldCount = 0; if (this._command != null) { this.NextResult(); } }
internal override object GetValue(SQLiteStatement stmt, int index, SQLiteType typ) { if (this.IsNull(stmt, index)) { return(DBNull.Value); } TypeAffinity affinity = typ.Affinity; Type type = null; if (typ.Type != DbType.Object) { type = SQLiteConvert.SQLiteTypeToType(typ); affinity = SQLiteConvert.TypeToAffinity(type); } switch (affinity) { case TypeAffinity.Int64: if (type != null) { return(Convert.ChangeType(this.GetInt64(stmt, index), type, null)); } return(this.GetInt64(stmt, index)); case TypeAffinity.Double: if (type != null) { return(Convert.ChangeType(this.GetDouble(stmt, index), type, null)); } return(this.GetDouble(stmt, index)); case TypeAffinity.Blob: if ((typ.Type != DbType.Guid) || (typ.Affinity != TypeAffinity.Text)) { int nLength = (int)this.GetBytes(stmt, index, 0, null, 0, 0); byte[] bDest = new byte[nLength]; this.GetBytes(stmt, index, 0, bDest, 0, nLength); if ((typ.Type == DbType.Guid) && (nLength == 0x10)) { return(new Guid(bDest)); } return(bDest); } return(new Guid(this.GetText(stmt, index))); case TypeAffinity.DateTime: return(this.GetDateTime(stmt, index)); } return(this.GetText(stmt, index)); }
public void Dispose() { this._stmt = null; if (this._keyInfo != null) { for (int i = 0; i < this._keyInfo.Length; i++) { if (this._keyInfo[i].query != null) { this._keyInfo[i].query.Dispose(); } } this._keyInfo = null; } }
internal override long GetRowIdForCursor(SQLiteStatement stmt, int cursor) { #if !SQLITE_STANDARD long rowid; int rc = UnsafeNativeMethods.sqlite3_cursor_rowid(stmt._sqlite_stmt, cursor, out rowid); if (rc == 0) { return(rowid); } return(0); #else return(0); #endif }
internal override void Bind_Text(SQLiteStatement stmt, SQLiteConnectionFlags flags, int index, string value) { SQLiteStatementHandle handle = stmt._sqlite_stmt; #if !PLATFORM_COMPACTFRAMEWORK if ((flags & SQLiteConnectionFlags.LogBind) == SQLiteConnectionFlags.LogBind) { LogBind(handle, index, value); } #endif SQLiteErrorCode n = UnsafeNativeMethods.sqlite3_bind_text16(handle, index, value, value.Length * 2, (IntPtr)(-1)); if (n != SQLiteErrorCode.Ok) { throw new SQLiteException(n, GetLastError()); } }
internal override DateTime GetDateTime(SQLiteStatement stmt, int index) { if (_datetimeFormat == SQLiteDateFormats.Ticks) { return(ToDateTime(GetInt64(stmt, index), _datetimeKind)); } else if (_datetimeFormat == SQLiteDateFormats.JulianDay) { return(ToDateTime(GetDouble(stmt, index), _datetimeKind)); } else if (_datetimeFormat == SQLiteDateFormats.UnixEpoch) { return(ToDateTime(GetInt32(stmt, index), _datetimeKind)); } return(ToDateTime(GetText(stmt, index))); }
internal override string ColumnType(SQLiteStatement stmt, int index, out TypeAffinity nAffinity) { int num; IntPtr nativestring = UnsafeNativeMethods.sqlite3_column_decltype_interop((IntPtr)stmt._sqlite_stmt, index, out num); nAffinity = this.ColumnAffinity(stmt, index); if (nativestring != IntPtr.Zero) { return(SQLiteConvert.UTF8ToString(nativestring, num)); } string[] typeDefinitions = stmt.TypeDefinitions; if (((typeDefinitions != null) && (index < typeDefinitions.Length)) && (typeDefinitions[index] != null)) { return(typeDefinitions[index]); } return(string.Empty); }
/// <summary> /// Helper function to retrieve a column of data from an active statement. /// </summary> /// <param name="stmt">The statement being step()'d through</param> /// <param name="index">The column index to retrieve</param> /// <param name="typ">The type of data contained in the column. If Uninitialized, this function will retrieve the datatype information.</param> /// <returns>Returns the data in the column</returns> internal virtual object GetValue(SQLiteStatement stmt, int index, ref SQLiteType typ) { if (typ.Affinity == 0) { typ = SQLiteConvert.ColumnToType(stmt, index); } if (IsNull(stmt, index)) { return(DBNull.Value); } Type t = SQLiteConvert.SQLiteTypeToType(typ); switch (TypeToAffinity(t)) { case TypeAffinity.Blob: if (typ.Type == DbType.Guid && typ.Affinity == TypeAffinity.Text) { return(new Guid(GetText(stmt, index))); } int n = (int)GetBytes(stmt, index, 0, null, 0, 0); byte[] b = new byte[n]; GetBytes(stmt, index, 0, b, 0, n); if (typ.Type == DbType.Guid && n == 16) { return(new Guid(b)); } return(b); case TypeAffinity.DateTime: return(GetDateTime(stmt, index)); case TypeAffinity.Double: return(Convert.ChangeType(GetDouble(stmt, index), t, null)); case TypeAffinity.Int64: return(Convert.ChangeType(GetInt64(stmt, index), t, null)); default: return(GetText(stmt, index)); } }
public void Dispose() { _stmt = null; if (_keyInfo == null) { return; } for (int n = 0; n < _keyInfo.Length; n++) { if (_keyInfo[n].query != null) { _keyInfo[n].query.Dispose(); } } _keyInfo = null; }
internal override string ColumnName(SQLiteStatement stmt, int index) { #if !SQLITE_STANDARD int len = 0; IntPtr p = UnsafeNativeMethods.sqlite3_column_name16_interop(stmt._sqlite_stmt, index, ref len); #else IntPtr p = UnsafeNativeMethods.sqlite3_column_name16(stmt._sqlite_stmt, index); #endif if (p == IntPtr.Zero) { throw new SQLiteException(SQLiteErrorCode.NoMem, GetLastError()); } #if !SQLITE_STANDARD return(UTF16ToString(p, len)); #else return(UTF16ToString(p, -1)); #endif }
internal SQLiteStatement GetStatement(int index) { if (this._statementList == null) { return(this.BuildNextCommand()); } if (index == this._statementList.Count) { if (!string.IsNullOrEmpty(this._remainingText)) { return(this.BuildNextCommand()); } return(null); } SQLiteStatement statement = this._statementList[index]; statement.BindParameters(); return(statement); }
internal override string ColumnType(SQLiteStatement stmt, int index, out TypeAffinity nAffinity) { int len; #if !SQLITE_STANDARD IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype_interop(stmt._sqlite_stmt, index, out len); #else len = -1; IntPtr p = UnsafeNativeMethods.sqlite3_column_decltype(stmt._sqlite_stmt, index); #endif nAffinity = ColumnAffinity(stmt, index); if (p != IntPtr.Zero) { return(UTF8ToString(p, len)); } else { string[] ar = stmt.TypeDefinitions; if (ar != null) { if (index < ar.Length && ar[index] != null) { return(ar[index]); } } return(String.Empty); //switch (nAffinity) //{ // case TypeAffinity.Int64: // return "BIGINT"; // case TypeAffinity.Double: // return "DOUBLE"; // case TypeAffinity.Blob: // return "BLOB"; // default: // return "TEXT"; //} } }
/////////////////////////////////////////////////////////////////////////////////////////////// private void DisposeStatements() { if (_statementList == null) { return; } int x = _statementList.Count; for (int n = 0; n < x; n++) { SQLiteStatement stmt = _statementList[n]; if (stmt == null) { continue; } stmt.Dispose(); } _statementList = null; }
internal SQLiteStatement BuildNextCommand() { SQLiteStatement item = null; SQLiteStatement statement2; try { if (this._statementList == null) { this._remainingText = this._commandText; } item = this._cnn._sql.Prepare(this._cnn, this._remainingText, (this._statementList == null) ? null : this._statementList[this._statementList.Count - 1], (uint)(this._commandTimeout * 0x3e8), out this._remainingText); if (item != null) { item._command = this; if (this._statementList == null) { this._statementList = new List <SQLiteStatement>(); } this._statementList.Add(item); this._parameterCollection.MapParameters(item); item.BindParameters(); } statement2 = item; } catch (Exception) { if (item != null) { if (this._statementList.Contains(item)) { this._statementList.Remove(item); } item.Dispose(); } this._remainingText = null; throw; } return(statement2); }
internal override int Reset(SQLiteStatement stmt) { int n; #if !SQLITE_STANDARD n = UnsafeNativeMethods.sqlite3_reset_interop(stmt._sqlite_stmt); #else n = UnsafeNativeMethods.sqlite3_reset(stmt._sqlite_stmt); #endif // If the schema changed, try and re-prepare it if (n == 17) // SQLITE_SCHEMA { // Recreate a dummy statement string str; using (SQLiteStatement tmp = Prepare(null, stmt._sqlStatement, null, (uint)(stmt._command._commandTimeout * 1000), out str)) { // Finalize the existing statement stmt._sqlite_stmt.Dispose(); // Reassign a new statement pointer to the old statement and clear the temporary one stmt._sqlite_stmt = tmp._sqlite_stmt; tmp._sqlite_stmt = null; // Reapply parameters stmt.BindParameters(); } return(-1); // Reset was OK, with schema change } else if (n == 6 || n == 5) // SQLITE_LOCKED || SQLITE_BUSY { return(n); } if (n > 0) { throw new SQLiteException(n, SQLiteLastError()); } return(0); // We reset OK, no schema changes }
internal override bool Step(SQLiteStatement stmt) { Random random = null; uint tickCount = (uint)Environment.TickCount; uint num3 = (uint)(stmt._command._commandTimeout * 0x3e8); while (true) { int errorCode = UnsafeNativeMethods.sqlite3_step((IntPtr)stmt._sqlite_stmt); switch (errorCode) { case 100: return(true); case 0x65: return(false); } if (errorCode > 0) { int num4 = this.Reset(stmt); if (num4 == 0) { throw new SQLiteException(errorCode, this.SQLiteLastError()); } if (((num4 == 6) || (num4 == 5)) && (stmt._command != null)) { if (random == null) { random = new Random(); } if ((Environment.TickCount - tickCount) > num3) { throw new SQLiteException(num4, this.SQLiteLastError()); } Thread.Sleep(random.Next(1, 150)); } } } }
internal override SQLiteStatement Prepare(string strSql, SQLiteStatement previous, out string strRemain) { IntPtr stmt = IntPtr.Zero; IntPtr ptr = IntPtr.Zero; int len = 0; int n = 17; int retries = 0; byte[] b = ToUTF8(strSql); unsafe { fixed(byte *psql = &b[0]) { while (n == 17 && retries < 3) { n = UnsafeNativeMethods.sqlite3_prepare_interop(_sql, (IntPtr)psql, b.Length - 1, out stmt, out ptr, out len); retries++; } if (n > 0) { throw new SQLiteException(n, SQLiteLastError()); } strRemain = UTF8ToString(ptr, len); SQLiteStatement cmd = null; if (stmt != IntPtr.Zero) { cmd = new SQLiteStatement(this, stmt, strSql.Substring(0, strSql.Length - strRemain.Length), previous); } return(cmd); } } }
internal override int Reset(SQLiteStatement stmt) { int n; n = UnsafeNativeMethods.sqlite3_reset_interop(stmt._sqlite_stmt); // If the schema changed, try and re-prepare it if (n == 17) // SQLITE_SCHEMA { // Recreate a dummy statement string str; using (SQLiteStatement tmp = Prepare(stmt._sqlStatement, null, out str)) { // Finalize the existing statement FinalizeStatement(stmt); // Reassign a new statement pointer to the old statement and clear the temporary one stmt._sqlite_stmt = tmp._sqlite_stmt; tmp._sqlite_stmt = IntPtr.Zero; // Reapply parameters stmt.BindParameters(); } return(-1); // Reset was OK, with schema change } else if (n == 6) // SQLITE_LOCKED { return(n); } if (n > 0) { throw new SQLiteException(n, SQLiteLastError()); } return(0); // We reset OK, no schema changes }
/// <summary> /// Initializes the statement and attempts to get all information about parameters in the statement /// </summary> /// <param name="sqlbase">The base SQLite object</param> /// <param name="stmt">The statement</param> /// <param name="strCommand">The command text for this statement</param> /// <param name="previous">The previous command in a multi-statement command</param> internal SQLiteStatement(SQLiteBase sqlbase, IntPtr stmt, string strCommand, SQLiteStatement previous) { _sql = sqlbase; _sqlite_stmt = stmt; _sqlStatement = strCommand; // Determine parameters for this statement (if any) and prepare space for them. int nCmdStart = 0; int n = _sql.Bind_ParamCount(this); int x; string s; if (n > 0) { if (previous != null) { nCmdStart = previous._unnamedParameters; } _paramNames = new string[n]; _paramValues = new SQLiteParameter[n]; for (x = 0; x < n; x++) { s = _sql.Bind_ParamName(this, x + 1); if (String.IsNullOrEmpty(s)) { s = String.Format(CultureInfo.InvariantCulture, ";{0}", nCmdStart); nCmdStart++; _unnamedParameters++; } _paramNames[x] = s; _paramValues[x] = null; } } }
internal abstract TypeAffinity ColumnAffinity(SQLiteStatement stmt, int index);
internal abstract int ColumnCount(SQLiteStatement stmt);
internal abstract Int32 GetInt32(SQLiteStatement stmt, int index);
internal abstract string GetText(SQLiteStatement stmt, int index);
internal abstract long GetChars(SQLiteStatement stmt, int index, int nDataoffset, char[] bDest, int nStart, int nLength);
internal abstract long GetBytes(SQLiteStatement stmt, int index, int nDataoffset, byte[] bDest, int nStart, int nLength);
internal abstract double GetDouble(SQLiteStatement stmt, int index);
/////////////////////////////////////////////////////////////////////////////////////////////// private void Dispose(bool disposing) { if (!disposed) { if (disposing) { //////////////////////////////////// // dispose managed resources here... //////////////////////////////////// _stmt = null; if (_keyInfo != null) { for (int n = 0; n < _keyInfo.Length; n++) { if (_keyInfo[n].query != null) _keyInfo[n].query.Dispose(); } _keyInfo = null; } } ////////////////////////////////////// // release unmanaged resources here... ////////////////////////////////////// disposed = true; } }
public override bool NextResult() { this.CheckClosed(); SQLiteStatement stmt = null; Label_0008: if ((this._activeStatement != null) && (stmt == null)) { this._activeStatement._sql.Reset(this._activeStatement); if ((this._commandBehavior & CommandBehavior.SingleResult) != CommandBehavior.Default) { while (true) { stmt = this._command.GetStatement(this._activeStatementIndex + 1); if (stmt == null) { return(false); } this._activeStatementIndex++; stmt._sql.Step(stmt); if (stmt._sql.ColumnCount(stmt) == 0) { if (this._rowsAffected == -1) { this._rowsAffected = 0; } this._rowsAffected += stmt._sql.Changes; } stmt._sql.Reset(stmt); } } } stmt = this._command.GetStatement(this._activeStatementIndex + 1); if (stmt == null) { return(false); } if (this._readingState < 1) { this._readingState = 1; } this._activeStatementIndex++; int num = stmt._sql.ColumnCount(stmt); if (((this._commandBehavior & CommandBehavior.SchemaOnly) == CommandBehavior.Default) || (num == 0)) { if (stmt._sql.Step(stmt)) { this._readingState = -1; } else { if (num == 0) { if (this._rowsAffected == -1) { this._rowsAffected = 0; } this._rowsAffected += stmt._sql.Changes; stmt._sql.Reset(stmt); goto Label_0008; } this._readingState = 1; } } this._activeStatement = stmt; this._fieldCount = num; this._fieldTypeArray = null; if ((this._commandBehavior & CommandBehavior.KeyInfo) != CommandBehavior.Default) { this.LoadKeyInfo(); } return(true); }
internal abstract string ColumnDatabaseName(SQLiteStatement stmt, int index);
internal abstract void Bind_Blob(SQLiteStatement stmt, int index, byte[] blobData);
/// <summary> /// Steps through a prepared statement. /// </summary> /// <param name="stmt">The SQLiteStatement to step through</param> /// <returns>True if a row was returned, False if not.</returns> internal abstract bool Step(SQLiteStatement stmt);
/// <summary> /// Resets a prepared statement so it can be executed again. If the error returned is SQLITE_SCHEMA, /// transparently attempt to rebuild the SQL statement and throw an error if that was not possible. /// </summary> /// <param name="stmt">The statement to reset</param> /// <returns>Returns -1 if the schema changed while resetting, 0 if the reset was sucessful or 6 (SQLITE_LOCKED) if the reset failed due to a lock</returns> internal abstract int Reset(SQLiteStatement stmt);
/// <summary> /// Prepares a SQL statement for execution. /// </summary> /// <param name="strSql">The SQL command text to prepare</param> /// <param name="previous">The previous statement in a multi-statement command, or null if no previous statement exists</param> /// <param name="strRemain">The remainder of the statement that was not processed. Each call to prepare parses the /// SQL up to to either the end of the text or to the first semi-colon delimiter. The remaining text is returned /// here for a subsequent call to Prepare() until all the text has been processed.</param> /// <returns>Returns an initialized SQLiteStatement.</returns> internal abstract SQLiteStatement Prepare(string strSql, SQLiteStatement previous, out string strRemain);
internal abstract bool IsNull(SQLiteStatement stmt, int index);
internal abstract int ColumnIndex(SQLiteStatement stmt, string columnName);
internal abstract void Bind_Int64(SQLiteStatement stmt, int index, Int64 value);
/// <summary> /// This function does all the nasty work at determining what keys need to be returned for /// a given statement. /// </summary> /// <param name="cnn"></param> /// <param name="reader"></param> /// <param name="stmt"></param> internal SQLiteKeyReader(SQLiteConnection cnn, SQLiteDataReader reader, SQLiteStatement stmt) { Dictionary<string, int> catalogs = new Dictionary<string, int>(); Dictionary<string, List<string>> tables = new Dictionary<string, List<string>>(); List<string> list; List<KeyInfo> keys = new List<KeyInfo>(); // Record the statement so we can use it later for sync'ing _stmt = stmt; // Fetch all the attached databases on this connection using (DataTable tbl = cnn.GetSchema("Catalogs")) { foreach (DataRow row in tbl.Rows) { catalogs.Add((string)row["CATALOG_NAME"], Convert.ToInt32(row["ID"], CultureInfo.InvariantCulture)); } } // Fetch all the unique tables and catalogs used by the current statement using (DataTable schema = reader.GetSchemaTable(false, false)) { foreach (DataRow row in schema.Rows) { // Check if column is backed to a table if (row[SchemaTableOptionalColumn.BaseCatalogName] == DBNull.Value) continue; // Record the unique table so we can look up its keys string catalog = (string)row[SchemaTableOptionalColumn.BaseCatalogName]; string table = (string)row[SchemaTableColumn.BaseTableName]; if (tables.ContainsKey(catalog) == false) { list = new List<string>(); tables.Add(catalog, list); } else list = tables[catalog]; if (list.Contains(table) == false) list.Add(table); } // For each catalog and each table, query the indexes for the table. // Find a primary key index if there is one. If not, find a unique index instead foreach (KeyValuePair<string, List<string>> pair in tables) { for (int i = 0; i < pair.Value.Count; i++) { string table = pair.Value[i]; DataRow preferredRow = null; using (DataTable tbl = cnn.GetSchema("Indexes", new string[] { pair.Key, null, table })) { // Loop twice. The first time looking for a primary key index, // the second time looking for a unique index for (int n = 0; n < 2 && preferredRow == null; n++) { foreach (DataRow row in tbl.Rows) { if (n == 0 && (bool)row["PRIMARY_KEY"] == true) { preferredRow = row; break; } else if (n == 1 && (bool)row["UNIQUE"] == true) { preferredRow = row; break; } } } if (preferredRow == null) // Unable to find any suitable index for this table so remove it { pair.Value.RemoveAt(i); i--; } else // We found a usable index, so fetch the necessary table details { using (DataTable tblTables = cnn.GetSchema("Tables", new string[] { pair.Key, null, table })) { // Find the root page of the table in the current statement and get the cursor that's iterating it int database = catalogs[pair.Key]; int rootPage = Convert.ToInt32(tblTables.Rows[0]["TABLE_ROOTPAGE"], CultureInfo.InvariantCulture); int cursor = stmt._sql.GetCursorForTable(stmt, database, rootPage); // Now enumerate the members of the index we're going to use using (DataTable indexColumns = cnn.GetSchema("IndexColumns", new string[] { pair.Key, null, table, (string)preferredRow["INDEX_NAME"] })) { KeyQuery query = null; List<string> cols = new List<string>(); for (int x = 0; x < indexColumns.Rows.Count; x++) { bool addKey = true; // If the column in the index already appears in the query, skip it foreach (DataRow row in schema.Rows) { if (row.IsNull(SchemaTableColumn.BaseColumnName)) continue; if ((string)row[SchemaTableColumn.BaseColumnName] == (string)indexColumns.Rows[x]["COLUMN_NAME"] && (string)row[SchemaTableColumn.BaseTableName] == table && (string)row[SchemaTableOptionalColumn.BaseCatalogName] == pair.Key) { indexColumns.Rows.RemoveAt(x); x--; addKey = false; break; } } if (addKey == true) cols.Add((string)indexColumns.Rows[x]["COLUMN_NAME"]); } // If the index is not a rowid alias, record all the columns // needed to make up the unique index and construct a SQL query for it if ((string)preferredRow["INDEX_NAME"] != "sqlite_master_PK_" + table) { // Whatever remains of the columns we need that make up the index that are not // already in the query need to be queried separately, so construct a subquery if (cols.Count > 0) { string[] querycols = new string[cols.Count]; cols.CopyTo(querycols); query = new KeyQuery(cnn, pair.Key, table, querycols); } } // Create a KeyInfo struct for each column of the index for (int x = 0; x < indexColumns.Rows.Count; x++) { string columnName = (string)indexColumns.Rows[x]["COLUMN_NAME"]; KeyInfo key = new KeyInfo(); key.rootPage = rootPage; key.cursor = cursor; key.database = database; key.databaseName = pair.Key; key.tableName = table; key.columnName = columnName; key.query = query; key.column = x; keys.Add(key); } } } } } } } } // Now we have all the additional columns we have to return in order to support // CommandBehavior.KeyInfo _keyInfo = new KeyInfo[keys.Count]; keys.CopyTo(_keyInfo); }
internal abstract void Bind_Null(SQLiteStatement stmt, int index);
internal abstract DateTime GetDateTime(SQLiteStatement stmt, int index);
internal abstract int Bind_ParamCount(SQLiteStatement stmt);
/// <summary> /// Determines the data type of a column in a statement /// </summary> /// <param name="stmt">The statement to retrieve information for</param> /// <param name="i">The column to retrieve type information on</param> /// <returns>Returns a SQLiteType struct</returns> internal static SQLiteType ColumnToType(SQLiteStatement stmt, int i) { SQLiteType typ; typ.Type = TypeNameToDbType(stmt._sql.ColumnType(stmt, i, out typ.Affinity)); return typ; }
internal abstract int Bind_ParamIndex(SQLiteStatement stmt, string paramName);
internal abstract string Bind_ParamName(SQLiteStatement stmt, int index);
internal abstract void Bind_Text(SQLiteStatement stmt, int index, string value);
internal abstract void Bind_DateTime(SQLiteStatement stmt, int index, DateTime dt);
internal abstract Int64 GetInt64(SQLiteStatement stmt, int index);
/// <summary> /// Helper function to retrieve a column of data from an active statement. /// </summary> /// <param name="stmt">The statement being step()'d through</param> /// <param name="index">The column index to retrieve</param> /// <param name="typ">The type of data contained in the column. If Uninitialized, this function will retrieve the datatype information.</param> /// <returns>Returns the data in the column</returns> internal virtual object GetValue(SQLiteStatement stmt, int index, ref SQLiteType typ) { if (typ.Affinity == 0) typ = SQLiteConvert.ColumnToType(stmt, index); if (IsNull(stmt, index)) return DBNull.Value; Type t = SQLiteConvert.SQLiteTypeToType(typ); switch (TypeToAffinity(t)) { case TypeAffinity.Blob: if (typ.Type == DbType.Guid && typ.Affinity == TypeAffinity.Text) return new Guid(GetText(stmt, index)); int n = (int)GetBytes(stmt, index, 0, null, 0, 0); byte[] b = new byte[n]; GetBytes(stmt, index, 0, b, 0, n); if (typ.Type == DbType.Guid && n == 16) return new Guid(b); return b; case TypeAffinity.DateTime: return GetDateTime(stmt, index); case TypeAffinity.Double: return Convert.ChangeType(GetDouble(stmt, index), t, null); case TypeAffinity.Int64: return Convert.ChangeType(GetInt64(stmt, index), t, null); default: return GetText(stmt, index); } }
internal abstract void Bind_Double(SQLiteStatement stmt, int index, double value);