Hi Everybody,
I noticed that there are some (actually a lot!) tests broken for Firebird
and I decided to tackle each Problem one by one.
So far I have found three Issues wich are (roughly description):
1) Decimal data type: Firebird does'nt support precisions larger than 18
and the default of nh is 19.
I extended the FirebirdDialect to override the "GetTypeName" method and
swap larger Decimal precisions with 18.
2) Parameters Within a select clause: Firebird needs a typecast if a
parameter is used within a select clause.
I extended the FirebirdClientDriver to override the AdjustCommand method
and typecast the parameter.
3) Temporary Tables: Firebird supports temporary Tables, which must be
created in an isolated transaction, and so I thought it is just a matter of
extending the dialect and overriding the related properties and methods.
Running the tests afterwards I discovered that this was'nt the case.
After digging deeper in the code I think I now know what's going on and I
think the issue is a "nasty" one and not necessarily related to Firebird.
This is going to be a rather long Description but please bear with me since
I really need some guidance here!
I'll start with one failing test which is found in
*AbstractEntityWithOneToManyTests.OneToManyCollectionOptimisticLockingWithUpdate()*
.
The relevant Part is at the end of the test:
s = OpenSession();
*t = s.BeginTransaction();* <== keep this in mind.
c = s.CreateCriteria<Contract>().UniqueResult<Contract>();
*s.CreateQuery("delete from Party").ExecuteUpdate();*
An Exception is thrown at s.CreateQuery().ExecuteUpdate().
Debuging and "Following the dots" you reach
*MultiTableDeleteExecutor.Execute(parameters,
session)* with:
CoordinateSharedCacheCleanup(session);
*CreateTemporaryTableIfNecessary(persister, session);*
the implementation of CreateTemporarayTableIfNecessary() in
AbstractStatementExecutor is:
IIsolatedWork *work = new
TmpIdTableCreationIsolatedWork(persister, log, session);*
if (ShouldIsolateTemporaryTableDDL())
{
*if
(Factory.Settings.IsDataDefinitionInTransactionSupported)*
{
*Isolater.DoIsolatedWork(work, session);*
}
else
{
Isolater.DoNonTransactedWork(work, session);
}
}
else
{
work.DoWork(session.ConnectionManager.GetConnection(),
null);
session.ConnectionManager.AfterStatement();
}
The bold if Statement is absolutly useless! The Property
IsDataDefinitionInTransactionSupported is never used (let alone set to
true!) anywhere in the entire code base and hence it is always false.
I commented out the whole thing except *Isolator.DoIsolatedWork(work,
session)* - because this is what firebird requires - just to see what will
happen which was unfortunatly nothing. The test still fails with the same
exception.
Digging further starting from *Isolator.DoIsolatedWork(work, session) *you
reach AdoNetTransactionFactory.ExecuteWorkInIsolation(ISessionImplementor
session, IIsolatedWork work, bool transacted) with the relevant
implementaion part of (i removed the comments for brevity):
if (session.Factory.Dialect is SQLiteDialect)
connection = session.Connection;
else
connection =
session.Factory.ConnectionProvider.GetConnection();
if (transacted)
{
trans = connection.BeginTransaction();
}
work.DoWork(connection, trans);
if (transacted)
{
trans.Commit();
}
Notice how It's asking for a new connection and starting a new transaction
(wich is precisly what it should do) if requested. It then passes both to
work.DoWork. In our case here the "work" is
TmpIdTableCreationIsolatedWork.DoWork(*IDbConnection connection*,
*IDbTransaction
transaction*) with the relevant implementation:
stmnt = *session.ConnectionManager.CreateCommand()*;
stmnt.CommandText = persister.TemporaryIdTableDDL;
stmnt.ExecuteNonQuery();
session.Factory.Settings.SqlStatementLogger.LogCommand(stmnt,
FormatStyle.Ddl);
The code is not only *ignoring* the passed in connection and transaction
but is asking for a command from the ConnectionManager which will return a
command assigned to a *different* connection from that passed in!
Further more the ConnectionManager.CreateCommand() will assign the command
the very first transaction created by the test and the whole thing blows up!
Here ist the implementation of ConnectionManager.CreateCommand():
var result = *GetConnection().CreateCommand();*
*Transaction.Enlist(result);*
return result;
Now I think the solution is actually simple, namly refactor the
TmpIdTableCreationIsolatedWork.DoWork to use the passed in parameters, but
I need to make sure that other dialects will not be affected.
(After all there must be a reason why the original author(s) ignored the
passed in parameters right?)
1) Does any dialect support DDL statements within the same transaction as
the one which actually usese them (such as selects or inserts) at all?
2) If yes, will they be broken if we did the refactoring or do they also
support DDL in separate transactions?
3) Can we remove the *if
(Factory.Settings.IsDataDefinitionInTransactionSupported) *and instead just
stick with *Isolater.DoIsolatedWork(work, session);*?
Sorry if the post was rather long and Merry Christmas to all of you.
--
---
You received this message because you are subscribed to the Google Groups
"nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.