From: <ric...@us...> - 2009-10-26 11:02:36
|
Revision: 4802 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4802&view=rev Author: ricbrown Date: 2009-10-26 11:02:27 +0000 (Mon, 26 Oct 2009) Log Message: ----------- Merge r4801 (Fix NH-847, Stored Procedures support with OracleDataClient) Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Driver/DriverBase.cs trunk/nhibernate/src/NHibernate/Driver/IDriver.cs trunk/nhibernate/src/NHibernate/Driver/OracleClientDriver.cs trunk/nhibernate/src/NHibernate/Driver/OracleDataClientDriver.cs trunk/nhibernate/src/NHibernate/Engine/Query/ParamLocationRecognizer.cs trunk/nhibernate/src/NHibernate/Engine/Query/ParameterMetadata.cs trunk/nhibernate/src/NHibernate/Engine/Query/QueryPlanCache.cs trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs trunk/nhibernate/src/NHibernate/Impl/SqlQueryImpl.cs trunk/nhibernate/src/NHibernate/Loader/Loader.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandInfo.cs trunk/nhibernate/src/NHibernate.Test/EngineTest/ParameterParserFixture.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/Mappings.hbm.xml trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/OracleCustomSQLFixture.cs trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/StoredProcedures.hbm.xml Added Paths: ----------- trunk/nhibernate/src/NHibernate/Engine/Query/CallableParser.cs trunk/nhibernate/src/NHibernate.Test/EngineTest/CallableParserFixture.cs Modified: trunk/nhibernate/src/NHibernate/Driver/DriverBase.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Driver/DriverBase.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Driver/DriverBase.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -136,6 +136,11 @@ return cmd; } + public virtual int RegisterResultSetOutParameter(IDbCommand command, int position, bool hasReturnValue) + { + throw new NotImplementedException(GetType().Name + " does not support resultsets via stored procedures"); + } + private void SetCommandTimeout(IDbCommand cmd, object envTimeout) { if (commandTimeout >= 0) Modified: trunk/nhibernate/src/NHibernate/Driver/IDriver.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Driver/IDriver.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Driver/IDriver.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -80,6 +80,17 @@ IDbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes); /// <summary> + /// Registers an OUT parameter which will be returing a + /// <see cref="IDataReader"/>. How this is accomplished varies greatly + /// from DB to DB, hence its inclusion here. + /// </summary> + /// <param name="command">The <see cref="IDbCommand"/> with CommandType.StoredProcedure.</param> + /// <param name="position">The bind position at which to register the OUT param.</param> + /// <param name="hasReturnValue">Whether the out parameter is a return value, or an out parameter.</param> + /// <returns>The number of (contiguous) bind positions used.</returns> + int RegisterResultSetOutParameter(IDbCommand command, int position, bool hasReturnValue); + + /// <summary> /// Prepare the <paramref name="command" /> by calling <see cref="IDbCommand.Prepare()" />. /// May be a no-op if the driver does not support preparing commands, or for any other reason. /// </summary> Modified: trunk/nhibernate/src/NHibernate/Driver/OracleClientDriver.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Driver/OracleClientDriver.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Driver/OracleClientDriver.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -47,5 +47,12 @@ base.InitializeParameter(dbParam, name, sqlType); } } + + public override int RegisterResultSetOutParameter(IDbCommand command, int position, bool hasReturnValue) + { + throw new System.NotImplementedException(GetType().Name + + " does not support resultsets via stored procedures." + + " Consider using OracleDataClientDriver instead."); + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Driver/OracleDataClientDriver.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Driver/OracleDataClientDriver.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Driver/OracleDataClientDriver.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -1,6 +1,8 @@ using System.Data; +using System.Reflection; using NHibernate.AdoNet; using NHibernate.SqlTypes; +using NHibernate.Util; namespace NHibernate.Driver { @@ -14,7 +16,13 @@ /// </remarks> public class OracleDataClientDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider { + private const string driverAssemblyName = "Oracle.DataAccess"; + private const string connectionTypeName = "Oracle.DataAccess.Client.OracleConnection"; + private const string commandTypeName = "Oracle.DataAccess.Client.OracleCommand"; private static readonly SqlType GuidSqlType = new SqlType(DbType.Binary, 16); + private readonly PropertyInfo oracleDbType; + private readonly object oracleDbTypeRefCursor; + /// <summary> /// Initializes a new instance of <see cref="OracleDataClientDriver"/>. /// </summary> @@ -23,10 +31,15 @@ /// </exception> public OracleDataClientDriver() : base( - "Oracle.DataAccess", - "Oracle.DataAccess.Client.OracleConnection", - "Oracle.DataAccess.Client.OracleCommand") + driverAssemblyName, + connectionTypeName, + commandTypeName) { + System.Type parameterType = ReflectHelper.TypeFromAssembly("Oracle.DataAccess.Client.OracleParameter", driverAssemblyName, false); + oracleDbType = parameterType.GetProperty("OracleDbType"); + + System.Type oracleDbTypeEnum = ReflectHelper.TypeFromAssembly("Oracle.DataAccess.Client.OracleDbType", driverAssemblyName, false); + oracleDbTypeRefCursor = System.Enum.Parse(oracleDbTypeEnum, "RefCursor"); } /// <summary></summary> @@ -69,6 +82,19 @@ } } + public override int RegisterResultSetOutParameter(IDbCommand command, int position, bool hasReturnValue) + { + IDbDataParameter outCursor = command.CreateParameter(); + outCursor.ParameterName = ""; + oracleDbType.SetValue(outCursor, oracleDbTypeRefCursor, null); + + outCursor.Direction = hasReturnValue ? ParameterDirection.ReturnValue : ParameterDirection.Output; + + command.Parameters.Insert(position, outCursor); + + return 1; + } + #region IEmbeddedBatcherFactoryProvider Members System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass Added: trunk/nhibernate/src/NHibernate/Engine/Query/CallableParser.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Query/CallableParser.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/Engine/Query/CallableParser.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -0,0 +1,33 @@ +using System; +using System.Text.RegularExpressions; +using NHibernate.Hql.Classic; +using NHibernate.Util; +using NHibernate.SqlCommand; + +namespace NHibernate.Engine.Query +{ + public static class CallableParser + { + private static readonly Regex functionNameFinder = new Regex(@"\{[\S\s]*call[\s]+([\w]+)[^\w]"); + private static readonly int NewLineLength = Environment.NewLine.Length; + + public static SqlString Parse(string sqlString) + { + bool isCallableSyntax = sqlString.IndexOf("{") == 0 && + sqlString.IndexOf("}") == (sqlString.Length - 1) && + sqlString.IndexOf("call") > 0; + + if (!isCallableSyntax) + throw new ParserException("Expected callable syntax {? = call procedure_name[(?, ?, ...)]} but got: " + sqlString); + + + Match functionMatch = functionNameFinder.Match(sqlString); + + if ((!functionMatch.Success) || (functionMatch.Groups.Count < 2)) + throw new HibernateException("Could not determine function name for callable SQL: " + sqlString); + + string function = functionMatch.Groups[1].Value; + return new SqlString(function); + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Engine/Query/ParamLocationRecognizer.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Query/ParamLocationRecognizer.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Engine/Query/ParamLocationRecognizer.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -13,6 +13,8 @@ private readonly List<int> ordinalParameterLocationList = new List<int>(); + private bool hasReturnValue = false; + /// <summary> /// Convenience method for creating a param location recognizer and /// initiating the parse. @@ -47,11 +49,16 @@ get { return ordinalParameterLocationList; } } + public bool HasReturnValue + { + get { return hasReturnValue; } + } + #region IRecognizer Members public void OutParameter(int position) { - // don't care... + hasReturnValue = true; } public void OrdinalParameter(int position) Modified: trunk/nhibernate/src/NHibernate/Engine/Query/ParameterMetadata.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Query/ParameterMetadata.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Engine/Query/ParameterMetadata.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -12,8 +12,17 @@ private static readonly OrdinalParameterDescriptor[] EmptyOrdinals = new OrdinalParameterDescriptor[0]; private readonly OrdinalParameterDescriptor[] ordinalDescriptors; private readonly Dictionary<string, NamedParameterDescriptor> namedDescriptorMap; + private readonly bool hasReturnValue = false; public ParameterMetadata(OrdinalParameterDescriptor[] ordinalDescriptors, + IDictionary<string, NamedParameterDescriptor> namedDescriptorMap, + bool hasReturnValue) + : this (ordinalDescriptors, namedDescriptorMap) + { + this.hasReturnValue = hasReturnValue; + } + + public ParameterMetadata(OrdinalParameterDescriptor[] ordinalDescriptors, IDictionary<string, NamedParameterDescriptor> namedDescriptorMap) { if (ordinalDescriptors == null) @@ -43,6 +52,11 @@ get { return namedDescriptorMap.Keys; } } + public bool HasReturnValue + { + get { return hasReturnValue; } + } + public OrdinalParameterDescriptor GetOrdinalParameterDescriptor(int position) { if (position < 1 || position > ordinalDescriptors.Length) Modified: trunk/nhibernate/src/NHibernate/Engine/Query/QueryPlanCache.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/Query/QueryPlanCache.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Engine/Query/QueryPlanCache.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -171,7 +171,7 @@ new NamedParameterDescriptor(name, null, description.BuildPositionsArray(), description.JpaStyle); } - return new ParameterMetadata(ordinalDescriptors, namedParamDescriptorMap); + return new ParameterMetadata(ordinalDescriptors, namedParamDescriptorMap, recognizer.HasReturnValue); } [Serializable] Modified: trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Engine/QueryParameters.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -279,6 +279,8 @@ public bool Callable { get; set; } + public bool HasReturnValue { get; set; } + public bool ReadOnly { get { return _readOnly; } Modified: trunk/nhibernate/src/NHibernate/Impl/SqlQueryImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Impl/SqlQueryImpl.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Impl/SqlQueryImpl.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -27,6 +27,7 @@ private readonly IList<INativeSQLQueryReturn> queryReturns; private readonly ICollection<string> querySpaces; private readonly bool callable; + private readonly bool hasReturnValue; private bool autoDiscoverTypes; /// <summary> Constructs a SQLQueryImpl given a sql query defined in the mappings. </summary> @@ -52,16 +53,9 @@ querySpaces = queryDef.QuerySpaces; callable = queryDef.IsCallable; + hasReturnValue = parameterMetadata.HasReturnValue; } - internal SqlQueryImpl(string sql, IList<INativeSQLQueryReturn> queryReturns, ICollection<string> querySpaces, FlushMode flushMode, bool callable, ISessionImplementor session, ParameterMetadata parameterMetadata) - : base(sql, flushMode, session, parameterMetadata) - { - this.queryReturns = queryReturns; - this.querySpaces = querySpaces; - this.callable = callable; - } - internal SqlQueryImpl(string sql, string[] returnAliases, System.Type[] returnClasses, LockMode[] lockModes, ISessionImplementor session, ICollection<string> querySpaces, FlushMode flushMode, ParameterMetadata parameterMetadata) : base(sql, flushMode, session, parameterMetadata) { @@ -180,6 +174,7 @@ { QueryParameters qp = base.GetQueryParameters(namedParams); qp.Callable = callable; + qp.HasReturnValue = hasReturnValue; qp.HasAutoDiscoverScalarTypes = autoDiscoverTypes; return qp; } Modified: trunk/nhibernate/src/NHibernate/Loader/Loader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -11,6 +11,7 @@ using NHibernate.Cache; using NHibernate.Collection; using NHibernate.Engine; +using NHibernate.Engine.Query; using NHibernate.Event; using NHibernate.Exceptions; using NHibernate.Hql.Util; @@ -1104,7 +1105,8 @@ bool useLimit = UseLimit(selection, dialect); bool hasFirstRow = GetFirstRow(selection) > 0; bool useOffset = hasFirstRow && useLimit && dialect.SupportsLimitOffset; - // TODO NH bool callable = queryParameters.Callable; + bool isCallable = queryParameters.Callable; + bool hasReturnValue = queryParameters.HasReturnValue; if (useLimit) { @@ -1114,10 +1116,19 @@ sqlString = PreprocessSQL(sqlString, queryParameters, dialect); - // TODO NH: Callable for SP -> PrepareCallableQueryCommand - IDbCommand command = - session.Batcher.PrepareQueryCommand(CommandType.Text, sqlString, - GetParameterTypes(queryParameters, useLimit, useOffset)); + IDbCommand command = null; + if (isCallable) + { + command = + session.Batcher.PrepareQueryCommand(CommandType.StoredProcedure, CallableParser.Parse(sqlString.ToString()), + GetParameterTypes(queryParameters, useLimit, useOffset)); + } + else + { + command = + session.Batcher.PrepareQueryCommand(CommandType.Text, sqlString, + GetParameterTypes(queryParameters, useLimit, useOffset)); + } try { @@ -1133,12 +1144,13 @@ { colIndex += BindLimitParameters(command, colIndex, selection, session); } - // TODO NH - //if (callable) - //{ - // colIndex = dialect.RegisterResultSetOutParameter(command, col); - //} + if (isCallable) + { + colIndex += + session.Factory.ConnectionProvider.Driver.RegisterResultSetOutParameter(command, colIndex, hasReturnValue); + } + colIndex += BindParameterValues(command, queryParameters, colIndex, session); if (useLimit && !dialect.BindLimitParametersFirst) @@ -1263,7 +1275,7 @@ // NH Different behavior: // The responsibility of parameter binding was entirely moved to QueryParameters // to deal with positionslParameter+NamedParameter+ParameterOfFilters - return queryParameters.BindParameters(statement, GetNamedParameterLocs, 0, session); + return queryParameters.BindParameters(statement, GetNamedParameterLocs, startIndex, session); } public virtual int[] GetNamedParameterLocs(string name) Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2009-10-26 11:02:27 UTC (rev 4802) @@ -524,6 +524,7 @@ <Compile Include="Dialect\SybaseASA9Dialect.cs" /> <Compile Include="Driver\IfxDriver.cs" /> <Compile Include="Driver\OracleLiteDataClientDriver.cs" /> + <Compile Include="Engine\Query\CallableParser.cs" /> <Compile Include="EntityModeEqualityComparer.cs" /> <Compile Include="Event\AbstractPostDatabaseOperationEvent.cs" /> <Compile Include="Event\AbstractPreDatabaseOperationEvent.cs" /> Modified: trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -3332,20 +3332,20 @@ SqlCommandInfo defaultDelete = GenerateDeleteString(j); sqlInsertStrings[j] = customSQLInsert[j] != null - ? new SqlCommandInfo(customSQLInsert[j], defaultInsert.ParameterTypes) + ? new SqlCommandInfo(customSQLInsert[j], insertCallable[j], defaultInsert.ParameterTypes) : defaultInsert; sqlUpdateStrings[j] = customSQLUpdate[j] != null - ? new SqlCommandInfo(customSQLUpdate[j], defaultUpdate.ParameterTypes) + ? new SqlCommandInfo(customSQLUpdate[j], updateCallable[j], defaultUpdate.ParameterTypes) : defaultUpdate; // NH: in practice for lazy update de update sql is the same any way. sqlLazyUpdateStrings[j] = customSQLUpdate[j] != null - ? new SqlCommandInfo(customSQLUpdate[j], defaultUpdate.ParameterTypes) + ? new SqlCommandInfo(customSQLUpdate[j], updateCallable[j], defaultUpdate.ParameterTypes) : GenerateUpdateString(NonLazyPropertyUpdateability, j, false); sqlDeleteStrings[j] = customSQLDelete[j] != null - ? new SqlCommandInfo(customSQLDelete[j], defaultDelete.ParameterTypes) + ? new SqlCommandInfo(customSQLDelete[j], deleteCallable[j], defaultDelete.ParameterTypes) : defaultDelete; } Modified: trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandInfo.cs =================================================================== --- trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandInfo.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate/SqlCommand/SqlCommandInfo.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -1,10 +1,12 @@ using System.Data; +using NHibernate.Engine.Query; using NHibernate.SqlTypes; namespace NHibernate.SqlCommand { public class SqlCommandInfo { + private readonly CommandType commandType; private readonly SqlString text; private readonly SqlType[] parameterTypes; @@ -12,12 +14,22 @@ { this.text = text; this.parameterTypes = parameterTypes; + this.commandType = CommandType.Text; } + public SqlCommandInfo(SqlString text, bool isStoredProcedure, SqlType[] parameterTypes) + : this(text, parameterTypes) + { + if (isStoredProcedure) + { + this.commandType = CommandType.StoredProcedure; + this.text = CallableParser.Parse(text.ToString()); + } + } + public CommandType CommandType { - // Always Text for now - get { return CommandType.Text; } + get { return commandType; } } public SqlString Text Added: trunk/nhibernate/src/NHibernate.Test/EngineTest/CallableParserFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/EngineTest/CallableParserFixture.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/EngineTest/CallableParserFixture.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -0,0 +1,40 @@ +using NUnit.Framework; +using NHibernate.Engine.Query; +using NHibernate.SqlCommand; +using NHibernate.Util; + +namespace NHibernate.Test.EngineTest +{ + [TestFixture] + public class CallableParserFixture + { + [Test] + public void CanFindCallableFunctionName() + { + string query = @"{ call myFunction(:name) }"; + + SqlString sqlFunction = CallableParser.Parse(query); + Assert.That(sqlFunction.ToString(), Is.EqualTo("myFunction")); + } + + [Test] + public void CanDetermineIsNotCallable() + { + string query = @"SELECT id FROM mytable"; + + Assert.Throws<ParserException>(() => + { + SqlString sqlFunction = CallableParser.Parse(query); + }); + } + + [Test] + public void CanFindCallableFunctionNameWithoutParameters() + { + string query = @"{ call myFunction }"; + + SqlString sqlFunction = CallableParser.Parse(query); + Assert.That(sqlFunction.ToString(), Is.EqualTo("myFunction")); + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/EngineTest/ParameterParserFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/EngineTest/ParameterParserFixture.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate.Test/EngineTest/ParameterParserFixture.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -59,5 +59,22 @@ Assert.DoesNotThrow(() => p = recognizer.NamedParameterDescriptionMap["pizza"]); } + [Test] + public void CanRecogniseNoReturnValueParameter() + { + string query = "{ call myFunction(?) }"; + ParamLocationRecognizer recognizer = new ParamLocationRecognizer(); + ParameterParser.Parse(query, recognizer); + Assert.That(recognizer.HasReturnValue, Is.False); + } + + [Test] + public void CanRecogniseReturnValueParameter() + { + string query = "{ ? = call myFunction(?) }"; + ParamLocationRecognizer recognizer = new ParamLocationRecognizer(); + ParameterParser.Parse(query, recognizer); + Assert.That(recognizer.HasReturnValue, Is.True); + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-10-26 11:02:27 UTC (rev 4802) @@ -165,6 +165,7 @@ <Compile Include="Criteria\Reptile.cs" /> <Compile Include="DriverTest\SqlClientDriverFixture.cs" /> <Compile Include="DriverTest\SqlServerCeDriverFixture.cs" /> + <Compile Include="EngineTest\CallableParserFixture.cs" /> <Compile Include="EngineTest\NativeSQLQueryNonScalarReturnTest.cs" /> <Compile Include="EngineTest\NativeSQLQueryScalarReturnTest.cs" /> <Compile Include="EngineTest\NativeSQLQuerySpecificationTest.cs" /> Modified: trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/Mappings.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/Mappings.hbm.xml 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/Mappings.hbm.xml 2009-10-26 11:02:27 UTC (rev 4802) @@ -39,7 +39,7 @@ </id> <property name="name" not-null="true"/> <loader query-ref="person"/> - <sql-insert callable="true" check="none">call createPerson(?,?)</sql-insert> + <sql-insert callable="true" check="none">{ call createPerson(?, ?) }</sql-insert> <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE PERID=?</sql-update> <sql-delete>DELETE FROM PERSON WHERE PERID=?</sql-delete> </class> @@ -187,6 +187,23 @@ <database-object> <create> + CREATE OR REPLACE PROCEDURE employmentsForRegion(rows OUT SYS_REFCURSOR, p_regionCode EMPLOYMENT.REGIONCODE%TYPE) + AS + BEGIN + OPEN rows FOR + SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE, + REGIONCODE, EMPID, VALUE, CURRENCY + FROM EMPLOYMENT + WHERE REGIONCODE = p_regionCode; + END; + </create> + <drop> + DROP PROCEDURE employmentsForRegion + </drop> + </database-object> + + <database-object> + <create> CREATE OR REPLACE PROCEDURE createPerson(p_name PERSON.NAME%TYPE, p_id PERSON.PERID%TYPE) AS rowcount INTEGER; @@ -201,7 +218,7 @@ END; </create> <drop> - DROP PROCEDURE createPerson; + DROP PROCEDURE createPerson </drop> </database-object> Modified: trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/OracleCustomSQLFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/OracleCustomSQLFixture.cs 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/OracleCustomSQLFixture.cs 2009-10-26 11:02:27 UTC (rev 4802) @@ -4,7 +4,7 @@ namespace NHibernate.Test.SqlTest.Custom.Oracle { - [TestFixture, Ignore("Not supported yet.")] + [TestFixture] public class OracleCustomSQLFixture : CustomStoredProcSupportTest { protected override IList Mappings @@ -12,9 +12,49 @@ get { return new[] { "SqlTest.Custom.Oracle.Mappings.hbm.xml", "SqlTest.Custom.Oracle.StoredProcedures.hbm.xml" }; } } + protected override bool AppliesTo(NHibernate.Engine.ISessionFactoryImplementor factory) + { + return factory.ConnectionProvider.Driver is Driver.OracleDataClientDriver; + } + protected override bool AppliesTo(Dialect.Dialect dialect) { return dialect is Oracle8iDialect; } + + [Test] + public void RefCursorOutStoredProcedure() + { + ISession s = OpenSession(); + ITransaction t = s.BeginTransaction(); + + Organization ifa = new Organization("IFA"); + Organization jboss = new Organization("JBoss"); + Person gavin = new Person("Gavin"); + Person kevin = new Person("Kevin"); + Employment emp = new Employment(gavin, jboss, "AU"); + Employment emp2 = new Employment(kevin, ifa, "EU"); + s.Save(ifa); + s.Save(jboss); + s.Save(gavin); + s.Save(kevin); + s.Save(emp); + s.Save(emp2); + + IQuery namedQuery = s.GetNamedQuery("selectEmploymentsForRegion"); + namedQuery.SetString("regionCode", "EU"); + IList list = namedQuery.List(); + Assert.That(list.Count, Is.EqualTo(1)); + s.Delete(emp2); + s.Delete(emp); + s.Delete(ifa); + s.Delete(jboss); + s.Delete(kevin); + s.Delete(gavin); + + t.Commit(); + s.Close(); + } + } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/StoredProcedures.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/StoredProcedures.hbm.xml 2009-10-26 11:01:27 UTC (rev 4801) +++ trunk/nhibernate/src/NHibernate.Test/SqlTest/Custom/Oracle/StoredProcedures.hbm.xml 2009-10-26 11:02:27 UTC (rev 4802) @@ -1,10 +1,6 @@ <?xml version="1.0" encoding="utf-8" ?> <!-- This version is for Oracle drivers handling of stored procedures/functions. - - - NOTE: so far this is the JAVA syntax, probably we will do something different in .NET - or we can use the same syntax and solve the problem in each Oracle drive --> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" @@ -15,19 +11,19 @@ <sql-query name="simpleScalar" callable="true"> <return-scalar column="name" type="string"/> <return-scalar column="value" type="long"/> - call simpleScalar(:number) + { ? = call simpleScalar(:number) } </sql-query> <sql-query name="paramhandling" callable="true"> <return-scalar column="value" type="long"/> <return-scalar column="value2" type="long"/> - call testParamHandling(?,?) + { ? = call testParamHandling(?, ?) } </sql-query> <sql-query name="paramhandling_mixed" callable="true"> <return-scalar column="value" type="long"/> <return-scalar column="value2" type="long"/> - call testParamHandling(?,:second) + { ? = call testParamHandling(?,:second) } </sql-query> <sql-query name="selectAllEmployments" callable="true"> @@ -43,7 +39,23 @@ <return-column name="CURRENCY"/> </return-property> </return> - call allEmployments() + { ? = call allEmployments } </sql-query> + <sql-query name="selectEmploymentsForRegion" callable="true"> + <return alias="emp" class="Employment"> + <return-property name="employee" column="EMPLOYEE"/> + <return-property name="employer" column="EMPLOYER"/> + <return-property name="startDate" column="STARTDATE"/> + <return-property name="endDate" column="ENDDATE"/> + <return-property name="regionCode" column="REGIONCODE"/> + <return-property name="employmentId" column="EMPID"/> + <return-property name="salary"> + <return-column name="VALUE"/> + <return-column name="CURRENCY"/> + </return-property> + </return> + { call employmentsForRegion(:regionCode) } + </sql-query> + </hibernate-mapping> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |