📄 fastdbcommand.cs
字号:
using System;using System.Collections;using System.Text.RegularExpressions;namespace FastDbNet{ //------------------------------------------------------------------------- /// <summary> /// A class that implements a select/insert query that can be executed in /// FastDb database. A new command is created using the following code: /// <code> /// FastDbCommand command = connection.CreateCommand("select * from Table"); /// </code> /// <seealso cref="FastDbConnection.CreateCommand"/> /// </summary> public class FastDbCommand: IDisposable { private FastDbConnection connection; private int statement; private string sql; private bool described; private bool sql_changed; private bool bof; private bool eof; private int rec_no; private string table_name; private bool read_only; private int row_count; private FastDbFields fields; private FastDbParameters vars; internal FastDbCommand(FastDbConnection connection, string sql): this(connection) { this.sql = sql; } internal FastDbCommand(FastDbConnection connection) { this.connection = connection; statement = -1; sql = ""; described = false; sql_changed = false; bof = false; eof = false; rec_no = -1; table_name = ""; read_only = true; row_count = 0; fields = new FastDbFields(); vars = new FastDbParameters(); } ~FastDbCommand() { Dispose(false); } public void Free() { this.Free(true); } public void Free(bool CheckError) { if (statement == -1) return; if (connection.Threaded) connection.Detach(); connection.RemoveCommand(this); int rc; if (connection.Session == -1) rc = (int)CLI.ErrorCode.cli_ok; else rc = CLI.cli_free(statement); statement = -1; if (CheckError) CLI.CliCheck(rc, "cli_free failed"); Fields.Clear(); Parameters.Clear(); table_name = ""; described = false; } /// <summary> /// A reference to the FastDb connection where this command is defined. /// </summary> public FastDbConnection Connection { get { return connection; } } /// <summary> /// Internal statement handle. /// </summary> public int Statement { get { return statement; } } /// <summary> /// A container of fields defined in current command. /// </summary> public FastDbFields Fields { get { return fields; } } /// <summary> /// A container of parameters defined in current command. /// </summary> public FastDbParameters Parameters { get { return vars; } } /// <summary> /// An SQL string representing a query against the database. /// </summary> public string SQL { get { return sql; } set { sql = value; sql_changed = true; } } /// <summary> /// A property indicating whether current query is read-only or updatable. /// </summary> public bool ReadOnly { get { return read_only; } } /// <summary> /// If true - the cursor is at the beginning of the record-set. /// </summary> public bool Bof { get { return bof; } } /// <summary> /// If true - the cursor is at the end of the record-set. /// </summary> public bool Eof { get { return eof; } } /// <summary> /// Current number of record within the record-set: 0 ... RowCount /// </summary> public int RecNo { get { return rec_no; } } /// <summary> /// Total number of records within the record-set. /// </summary> public int RowCount { get { return row_count; } } /// <summary> /// Name of the table parsed out of the SQL property. /// </summary> public string TableName { get { return table_name; } } private bool IsSelectQuery() { return String.Compare(sql.Substring(0, 6), "select", true) == 0; } private string GetTableName() { if (!sql_changed && table_name != "") return table_name; if (sql == "") return ""; // The tablename is the first identifier after FROM Match m = Regex.Match(sql, @"insert\s+into\s+([a-zA-Z0-9_]+)\s*"); if (m.Success) return table_name = m.Groups[1].ToString(); m = Regex.Match(sql, @"select\s+\*?\s*from\s+([a-zA-Z0-9_]+)\s*"); if (m.Success) return table_name = m.Groups[1].ToString(); else throw new CliError((int)CLI.ErrorCode.cli_table_not_found); } /// <summary> /// Fetch definition of fields from the database. SQL property must be assigned /// </summary> public void Describe() { if (described && !sql_changed) return; else if (statement != -1) Free(false); if (SQL == "") throw new CliError("SQL not assigned!"); string s = GetTableName(); if (SQL == "") throw new CliError("Error parsing table name from the SQL!"); if (fields.Count == 0) fields.Assign(connection.DescribeTable(GetTableName())); statement = CLI.CliCheck(CLI.cli_statement(connection.Session, sql), "cli_statement failed"); fields.Bind(statement); vars.Bind(statement); described = true; sql_changed = false; } public int Execute() { return this.Execute(false, true); } public int Execute(bool Updatable) { return this.Execute(Updatable, true); } /// <summary> /// Executes an SQL command agains a FastDB connection. /// </summary> /// <param name="Updatable">Determines if records can be updated (optional, default: false).</param> /// <param name="FetchFirst">Instructs to fetch the first record (optional, default: false). /// If false, the user must call First() method after Execute().</param> /// <returns>Number of records in the record-set fetched from the database.</returns> public int Execute(bool Updatable, bool FetchFirst) { if (connection.Threaded) connection.Attach(); Describe(); read_only = !Updatable; row_count = CLI.CliCheck( CLI.cli_fetch(statement, (Updatable) ? CLI.QueryType.cli_for_update : CLI.QueryType.cli_view_only)); rec_no = (FetchFirst) ? 0 : -1; eof = row_count <= 0; if (FetchFirst && !eof) First(); else bof = FetchFirst; return row_count; } public Cursor Select() { return Select(false); } public Cursor Select(bool Updatable) { Execute(Updatable, false); return new Cursor(this); } /// <summary> /// Inserts a record in the database. Requires the SQL to be in the form: /// "insert into TableName". /// </summary> /// <returns>An OID of the newly inserted record.</returns> public uint Insert() { uint oid = 0; if (connection.Threaded) connection.Attach(); Describe(); CLI.CliCheck(CLI.cli_insert(statement, ref oid)); if (connection.Threaded) connection.Detach(); return oid; } /// <summary> /// Updates a record in the database. Requires the SQL to be in the form: /// "select * from TableName ...", and the command must be updatable: /// command.Execute(true). /// </summary> public void Update() { if (read_only) throw new CliError((int)CLI.ErrorCode.cli_not_update_mode); CLI.CliCheck(CLI.cli_update(statement)); } /// <summary> /// Deletes all records in the current record-set returned by the select statement. /// Requires the SQL to be in the form: /// "select * from TableName ...", and the command must be updatable: /// command.Execute(true); /// command.Delete(); /// </summary> public void Delete() { CLI.CliCheck(CLI.cli_remove(statement)); } /// <summary> /// Freeze cursor. Make it possible to reuse cursor after commit of /// the current transaction. /// </summary> public void Freeze() { CLI.CliCheck(CLI.cli_freeze(statement)); } /// <summary> /// Unfreeze cursor. Reuse previously frozen cursor. /// </summary> public void UnFreeze() { CLI.CliCheck(CLI.cli_unfreeze(statement)); } /// <summary> /// Return the OID of the currently fetched record in the cursor. /// </summary> public uint OID { get { return CLI.cli_get_oid(statement); } } /// <summary> /// Refresh the selected record the cursor points to. /// </summary> public void RefreshRecord() { CLI.CliCheck(CLI.cli_skip(statement, 0)); } /// <summary> /// Determines if the current command has an open cursor. /// </summary> /// <returns>true - the current query is open.</returns> public bool IsOpen() { return statement != -1; } /// <summary> /// Go to the first row of the current record-set. /// </summary> public void First() { CLI.CliCheck(CLI.cli_get_first(statement)); rec_no = 0; } /// <summary> /// Go to the last row of the current record-set. /// </summary> public void Last() { CLI.CliCheck(CLI.cli_get_last(statement)); rec_no = row_count; } /// <summary> /// Go to the next row of the current record-set. /// </summary> public bool Next() { int n = CLI.cli_get_next(statement); eof = n != (int)CLI.ErrorCode.cli_ok; bof = false; if (!eof) rec_no++; return !eof; } /// <summary> /// Go to the previous row of the current record-set. /// </summary> public bool Prev() { int n = CLI.cli_get_prev(statement); bof = n != (int)CLI.ErrorCode.cli_ok; eof = false; if (!bof) rec_no--; return !bof; } public void Skip(int Records) { Skip(Records, false); } /// <summary> /// Skip N rows of the current record-set. /// </summary> /// <param name="Records">Number of records to skip</param> /// <param name="RefreshOnNoSkip">Optional parameter (default: false). If true, forces a refresh when parameter Records=0.</param> public void Skip(int Records, bool RefreshOnNoSkip) { if ((Records == 0) && !RefreshOnNoSkip) return; int n = CLI.cli_skip(statement, Records); if (n == (int)CLI.ErrorCode.cli_not_found) { if (Records >= 0) { eof = true; bof = false; rec_no = row_count; } else { bof = true; eof = false; rec_no = 0; } } else { CLI.CliCheck(n, "cli_skip failed"); rec_no += Records; } } /// <summary> /// Seeks for a given OID in the current record-set. /// </summary> /// <param name="oid">OID to search for.</param> /// <returns>Position of the record in the selection.</returns> public int Seek(uint oid) { return rec_no = CLI.CliCheck(CLI.cli_seek(statement, oid)); } /// <summary> /// Clears the current command, empties fields/parameters, and frees unmanaged resources. /// </summary> public void Clear() { Free(); } /// <summary> /// This class is used to iterate over a record set using foreach() statement. /// Example: /// <code> /// FastDbCommand command = connection.CreateCommand("select * from persons"); /// foreach(FastDbCommand.Cursor cur in command.Select()) { /// foreach(FastDbField f in cur.Fields) /// Console.WriteLine(f.asString); /// } /// </code> /// </summary> public class Cursor: IEnumerable { FastDbCommand command; public Cursor(FastDbCommand command) { this.command = command; } public FastDbFields Fields { get { return command.Fields; } } public FastDbCommand Command { get { return command; } } #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public Iterator GetEnumerator() { return new Iterator(this); } /// <summary> /// This class implements IEnumerator interface. Use its methods within a /// foreach() loop. /// </summary> public class Iterator: IEnumerator { #region Private Fields Cursor cursor; #endregion #region Constructors public Iterator(Cursor cursor) { this.cursor = cursor; } #endregion #region Methods public void Reset() { cursor.command.First(); } public bool MoveNext() { if (cursor.Command.RecNo == -1) { Reset(); return !cursor.command.Eof; } else { return cursor.command.Next(); } } public Cursor Current { get { return cursor; } } // The current property on the IEnumerator interface: object IEnumerator.Current { get { return Current; } } #endregion } #endregion } #region IDisposable Members // Implement IDisposable. // This method is not virtual. A derived class should not be able to override this method. public void Dispose() { Dispose(true); // Take yourself off the Finalization queue // to prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } // Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. protected virtual void Dispose(bool disposing) { if(this.statement != -1) { // Check to see if Dispose has already been called. if(disposing) {} // Dispose managed resources here. Free(); // Release unmanaged resources. } } #endregion }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -