Using new features of Firebird 2.1 in ADO.NET
Keywords: Firebird 2.1, Firebird, ADO .Net, c#, .Net провайдер, Firebird 2.
Beta version of the Firebird 2.1 database was published at the end of June, 2007. I suppose, most interesting features are common table expressions, global database triggers, temporary tables, database monitoring through the SQL, canceling running queries and new SQL statements.
We implemented new Firebird 2.1 features in IBProvider and glad to announce new version of IBProvider Professional Edition
RETURNING Clause
UPDATE, INSERT and UPDATE OR INSERT now can be used with RETURNING clause. In the previous Firebird 2.0 release this feature was implemented only for INSERT statement, now it is possible for other statements. Let’s write C# sample:
using (OleDbConnection con = ConnectionProvider.GetConnection()) { // Description: UPDATE OR INSERT … RETURNING, UPDATE .. RETURNING, DELETE .. RETURNING, Firebird 2.1 ADO .Net (c#) con.Open(); OleDbTransaction trans = con.BeginTransaction(); // 1. inserting new record using the UPDATE OR INSERT .. RETURNING statement OleDbCommand cmd = new OleDbCommand( «UPDATE OR INSERT INTO country (country, currency) VALUES (?, ?) RETURNING country, currency», con, trans); // in-parameters cmd.Parameters.AddWithValue(«country», «Russia»); cmd.Parameters.AddWithValue(«currency», «Kopec»); // out-parameters for RETURNING clause OleDbParameter outpCountry = new OleDbParameter(«out_country», OleDbType.BSTR); outpCountry.Direction = ParameterDirection.Output; cmd.Parameters.Add(outpCountry); OleDbParameter outpCurrency = new OleDbParameter(«out_currency», OleDbType.BSTR); outpCurrency.Direction = ParameterDirection.Output; cmd.Parameters.Add(outpCurrency); Assert.AreEqual(1, cmd.ExecuteNonQuery(), «inserting record. FAIL»); Assert.AreEqual(«Russia», outpCountry.Value, «getting out parameter ‘COUNTRY’ from UPDATE or INSERT command. FAIL»); Assert.AreEqual(«Kopec», outpCurrency.Value, «getting out parameter ‘CURRENCY’ from UPDATE or INSERT command. FAIL»); // 2. updating record using the UPDATE .. RETURNING statement cmd = new OleDbCommand(«UPDATE country SET currency = ? WHERE country = ? RETURNING country, currency», con, trans); // in-parameters cmd.Parameters.AddWithValue(«currency», «Rouble»); cmd.Parameters.AddWithValue(«country», «Russia»); // out-parameters outpCountry = new OleDbParameter(«out_country», OleDbType.BSTR); outpCountry.Direction = ParameterDirection.Output; cmd.Parameters.Add(outpCountry); outpCurrency = new OleDbParameter(«out_currency», OleDbType.BSTR); outpCurrency.Direction = ParameterDirection.Output; cmd.Parameters.Add(outpCurrency); Assert.AreEqual(1, cmd.ExecuteNonQuery(), «updating record. FAIL»); Assert.AreEqual(«Russia», outpCountry.Value, «getting out parameter ‘COUNTRY’ from UPDATE command. FAIL»); Assert.AreEqual(«Rouble», outpCurrency.Value, «getting out parameter ‘CURRENCY’ from UPDATE command. FAIL»); // 3. DELETE .. RETURNING // input parameter required because ADO .Net will ignore parameters for commands which not contains // explicit symbol ‘?’ (or :param_name) cmd = new OleDbCommand(«DELETE FROM country WHERE country =:country RETURNING country, currency», con, trans); // in-parameters cmd.Parameters.AddWithValue(«country», «Russia»); // out-parameters outpCountry = new OleDbParameter(«out_country», OleDbType.BSTR); outpCountry.Direction = ParameterDirection.Output; cmd.Parameters.Add(outpCountry); outpCurrency = new OleDbParameter(«out_currency», OleDbType.BSTR); outpCurrency.Direction = ParameterDirection.Output; cmd.Parameters.Add(outpCurrency); Assert.AreEqual(1, cmd.ExecuteNonQuery(), «deleting record. FAIL»); Assert.AreEqual(«Russia», outpCountry.Value, «getting out parameter ‘COUNTRY’ from DELETE command. FAIL»); Assert.AreEqual(«Rouble», outpCurrency.Value, «getting out parameter ‘CURRENCY’ from DELETE command. FAIL»); trans.Commit(); } |
When you will be using the RETURNING clause, you must define at least one input parameter (like the named parameter :country in the previous code sample):
DELETE FROM country WHERE country =:country RETURNING country, currency |
If you define SQL query without input parameters ADO.Net will ignore your output parameters and Firebird will throw the error:
DELETE FROM country WHERE country =‘Russia’ RETURNING country, currency |
UPDATE OR INSERT
It is very helpful statement, which allow defining single expression for INSERT and UPDATE operations. Server compares records by MATCHING (column) clause and automatically detects which action must be performed. If MATCHING (column) skipped, server will use primary key:
using (OleDbConnection con = ConnectionProvider.GetConnection()) { // Description: UPDATE OR INSERT, Firebird 2.1 ADO .Net (c#) con.Open(); OleDbTransaction trans = con.BeginTransaction(); // insert ne wrecord // matching by primary key «country» OleDbCommand cmd = new OleDbCommand( «UPDATE OR INSERT into COUNTRY (country, currency) values (?, ?)», con, trans); cmd.Parameters.AddWithValue(«country», «Russia»); cmd.Parameters.AddWithValue(«currency», «Kopec»); Assert.AreEqual(1, cmd.ExecuteNonQuery(), «inserting record. FAIL»); // update record cmd.Parameters[«country»].Value = «Russia»; cmd.Parameters[«currency»].Value = «Rouble»; // setting new value Assert.AreEqual(1, cmd.ExecuteNonQuery(), «updating record. FAIL»); // updating record which matched by ‘Currency’ field cmd = new OleDbCommand( «UPDATE OR INSERT into COUNTRY (country, currency) values (?, ?) MATCHING (currency) returning country, currency», con, trans); cmd.Parameters.AddWithValue(«country», «RF»); cmd.Parameters.AddWithValue(«currency», «Rouble»); Assert.AreEqual(1, cmd.ExecuteNonQuery(), «updating record fail.»); // delete record OleDbCommand cmd_delete = new OleDbCommand(string.Format( «delete from COUNTRY where country ='{0}'», «Russian Federaion»), con, trans); Assert.AreEqual(1, cmd_delete.ExecuteNonQuery(), «deleting record fail»); trans.Commit(); } |
The LIST() Function
LIST(column, delimiter) is the new aggregate function which build string from result set using delimiter.
<list function> ::= LIST ‘(‘ [ {ALL | DISTINCT} ] <value expression> [‘,’ <delimiter value>] ‘)’ <delimiter value> ::= { <string literal> | <parameter> | <variable> } |
The simple sample of getting customers list as single value:
using (OleDbConnection con = ConnectionProvider.GetConnection()) { // Description: LIST function, Firebird 2.1 c# (ADO .Net) con.Open(); OleDbTransaction trans = con.BeginTransaction(); // select all customers into the string with separators OleDbCommand cmd = new OleDbCommand( string.Format(«SELECT LIST(CUSTOMER, ‘,{0}’) FROM CUSTOMER», Environment.NewLine) , con, trans); // query return single value string customer_list = cmd.ExecuteScalar().ToString(); Console.WriteLine(customer_list); trans.Commit(); } |
Database monitoring through the SQL
Database statistic
New Firebird 2.1 feature is the Database Monitoring Tables. It’s allowed to get info about active connections, queries, and transactions, collect database statistic, cancel executing queries, etc.
The first Sample shows how to get general database info:
using (OleDbConnection con = ConnectionProvider.GetConnection()) { // Description: MONITORING TABLES Firebird 2.1 c# (ADO .Net) con.Open(); OleDbTransaction trans = con.BeginTransaction(); OleDbCommand cmd = new OleDbCommand(«select * from MON$DATABASE», con, trans); using(OleDbDataReader rdr = cmd.ExecuteReader()) while (rdr.Read()) { Console.WriteLine(«[DATABASE INFO]»); Console.WriteLine(«Database name: {0}», rdr[«MON$DATABASE_NAME»]); Console.WriteLine(«Dialect: {0}», rdr[«MON$SQL_DIALECT»]); Console.WriteLine(«Page size: {0}», rdr[«MON$PAGE_SIZE»]); Console.WriteLine(«Pages: {0}», rdr[«MON$PAGES»]); Console.WriteLine(«Creation date: {0}», rdr[«MON$CREATION_DATE»]); Console.WriteLine(«Read only database: {0}», Convert.ToBoolean(rdr[«MON$READ_ONLY»])); Console.WriteLine(«ODS Version: {0}.{1}», rdr[«MON$ODS_MAJOR»], rdr[«MON$ODS_MINOR»]); Console.WriteLine(«Oldest transaction: {0}», rdr[«MON$OLDEST_TRANSACTION»]); Console.WriteLine(«Oldest active: {0}», rdr[«MON$OLDEST_ACTIVE»]); Console.WriteLine(«Oldest snapshot: {0}», rdr[«MON$OLDEST_SNAPSHOT»]); // enumeration from RDB$TYPES WHERE RDB$FIELD_NAME =’MON$SHUTDOWN_MODE’ Console.WriteLine(«Shutdown mode: {0}», rdr[«MON$SHUTDOWN_MODE»]); Console.WriteLine(«Sweep interval: {0}», rdr[«MON$SWEEP_INTERVAL»]); } trans.Commit(); } |
How to get the isolation level of the current transaction?
The simple way to get the isolation level of the current transaction is use the table MON$TRANSACTIONS. The SQL query below:
select CURRENT_TRANSACTION, trans.MON$ISOLATION_MODE, types.RDB$TYPE_NAME from MON$TRANSACTIONS trans join RDB$TYPES types on types.RDB$FIELD_NAME = ‘MON$ISOLATION_MODE’ and types.RDB$TYPE = trans.MON$ISOLATION_MODE where MON$TRANSACTION_ID = CURRENT_TRANSACTION |
Results of using all ADO .Net IsolationLevel enum values:
ADO .Net Isolation level | Firebird Isolation level |
---|---|
Chaos | READ_COMMITTED_NO_VERSION |
ReadUncommitted | Not supported |
ReadCommitted | READ_COMMITTED_NO_VERSION |
RepeatableRead | CONCURRENCY (or SNAPHOT) |
Serializable | CONSISTENCY |
Snapshot | Not supported |
Unspecified | READ_COMMITTED_NO_VERSION |
The isolation level READ_COMMITTED_NO_VERSION is setted by default. Firebird supports one more isolation level which not supported by ADO .Net — it is the READ_COMMITTED_VERSION. You can set it through the SET TRANSACTION operator.
Cancel executing query
Information about executing queries is available from the table MON$STATEMENTS. If you want cancel any query you must delete record from the MON$STATEMENTS, for example:
delete from mon$statements where mon$sql_text = :sql_text |
Sometimes query text doesn’t available, however you can detect required query by alternate parameters like MON$STATE (query activity [1 — active, 0 — idle]) or MON$TIMESTAMP (query start time).
Conclusion
Firebird is very progressive and absolutely free database. More info about Firebird 2.1 features you will find in official
What do you need for quick start?
- Download Firebird 2.1 database or above and install it.
- Run tool which supports OLE DB technology (like Visual Studio, Borland C++ Builder, Delphi, WSH, etc).
- Install IBProvider Professional Edition.
Useful links:
- Quick Start with Firebird + ADO .Net Provider (c#). 26 and 1 samples for ADO .Net 2.0. and Firebird 2.0.
- Quick Start with Firebird Delphi and InterBase Delphi.
- The differences between data access technologies: ODBC Firebird driver, ODBC InterBase or OLE DB provider?
- ODBC Escape Sequences guide.