From: <fab...@us...> - 2009-05-05 18:07:11
|
Revision: 4246 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4246&view=rev Author: fabiomaulo Date: 2009-05-05 18:07:06 +0000 (Tue, 05 May 2009) Log Message: ----------- Continue port of AST tests for Updates Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate.Test/HQL/Ast/BulkManipulation.cs Added Paths: ----------- trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/MultiTableUpdateExecutor.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -22,7 +22,7 @@ private readonly SqlString[] updates; private readonly IParameterSpecification[][] hqlParameters; - public MultiTableUpdateExecutor(IStatement statement, ILog log) : base(statement, log) + public MultiTableUpdateExecutor(IStatement statement) : base(statement, log) { if (!Factory.Dialect.SupportsTemporaryTables) { Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -11,6 +11,7 @@ using NHibernate.Persister.Entity; using NHibernate.SqlCommand; using NHibernate.Type; +using NHibernate.UserTypes; using NHibernate.Util; namespace NHibernate.Hql.Ast.ANTLR @@ -167,12 +168,69 @@ void PrepareVersioned(IASTNode updateNode, IASTNode versioned) { - throw new NotImplementedException(); // DML + var updateStatement = (UpdateStatement)updateNode; + FromClause fromClause = updateStatement.FromClause; + if (versioned != null) + { + // Make sure that the persister is versioned + IQueryable persister = fromClause.GetFromElement().Queryable; + if (!persister.IsVersioned) + { + throw new SemanticException("increment option specified for update of non-versioned entity"); + } + + IVersionType versionType = persister.VersionType; + if (versionType is IUserVersionType) + { + throw new SemanticException("user-defined version types not supported for increment option"); + } + + IASTNode eq = ASTFactory.CreateNode(EQ, "="); + IASTNode versionPropertyNode = GenerateVersionPropertyNode(persister); + + eq.SetFirstChild(versionPropertyNode); + + IASTNode versionIncrementNode; + if (typeof(DateTime).IsAssignableFrom(versionType.ReturnedClass)) + { + versionIncrementNode = ASTFactory.CreateNode(PARAM, "?"); + IParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification(versionType); + ((ParameterNode)versionIncrementNode).HqlParameterSpecification = paramSpec; + Parameters.Insert(0, paramSpec); + } + else + { + // Not possible to simply re-use the versionPropertyNode here as it causes + // OOM errors due to circularity :( + versionIncrementNode = ASTFactory.CreateNode(PLUS, "+"); + versionIncrementNode.SetFirstChild(GenerateVersionPropertyNode(persister)); + versionIncrementNode.AddChild(ASTFactory.CreateNode(IDENT, "1")); + } + + eq.AddChild(versionIncrementNode); + + EvaluateAssignment(eq, persister, 0); + + IASTNode setClause = updateStatement.SetClause; + IASTNode currentFirstSetElement = setClause.GetFirstChild(); + setClause.SetFirstChild(eq); + eq.NextSibling= currentFirstSetElement; + } } + private IASTNode GenerateVersionPropertyNode(IQueryable persister) + { + string versionPropertyName = persister.PropertyNames[persister.VersionProperty]; + var versionPropertyRef = ASTFactory.CreateNode(IDENT, versionPropertyName); + var versionPropertyNode = LookupNonQualifiedProperty(versionPropertyRef); + Resolve(versionPropertyNode); + return versionPropertyNode; + } + void PostProcessUpdate(IASTNode update) { - throw new NotImplementedException(); // DML + var updateStatement = (UpdateStatement)update; + PostProcessDML(updateStatement); } void PostProcessDelete(IASTNode delete) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -242,10 +242,12 @@ var list = new List<string>(); if (IsManipulationStatement) { - SqlString[] sqlStatements = statementExecutor.SqlStatements; - foreach (var sqlStatement in sqlStatements) + foreach (var sqlStatement in statementExecutor.SqlStatements) { - list.Add(sqlStatement.ToString()); + if (sqlStatement != null) + { + list.Add(sqlStatement.ToString()); + } } } else @@ -419,7 +421,7 @@ // even here, if only properties mapped to the "base table" are referenced // in the set and where clauses, this could be handled by the BasicDelegate. // TODO : decide if it is better performance-wise to perform that check, or to simply use the MultiTableUpdateDelegate - return new MultiTableDeleteExecutor(statement); + return new MultiTableUpdateExecutor(statement); } else { Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -262,6 +262,24 @@ return null; } + set + { + if (_parent != null) + { + if (_parent.ChildCount > (ChildIndex + 1)) + { + _parent.SetChild(ChildIndex + 1, value); + } + else + { + AddSibling(value); + } + } + else + { + throw new InvalidOperationException("Trying set NextSibling without a parent."); + } + } } public IASTNode GetChild(int index) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -15,7 +15,7 @@ int ChildCount { get; } int ChildIndex { get; } IASTNode Parent { get; set; } - IASTNode NextSibling { get; } + IASTNode NextSibling { get; set; } IASTNode GetChild(int index); IASTNode GetFirstChild(); void SetFirstChild(IASTNode newChild); Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2009-05-05 18:07:06 UTC (rev 4246) @@ -589,6 +589,7 @@ <Compile Include="Hql\Ast\ANTLR\Util\JoinProcessor.cs" /> <Compile Include="Hql\Ast\ANTLR\Util\LiteralProcessor.cs" /> <Compile Include="Hql\Ast\ANTLR\Util\NodeTraverser.cs" /> + <Compile Include="Param\VersionTypeSeedParameterSpecification.cs" /> <Compile Include="SqlCommand\InsertSelect.cs" /> <Compile Include="Util\NullableDictionary.cs" /> <Compile Include="Hql\Ast\ANTLR\Util\PathHelper.cs" /> Added: trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/Param/VersionTypeSeedParameterSpecification.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -0,0 +1,36 @@ +using System.Data; +using NHibernate.Engine; +using NHibernate.Type; + +namespace NHibernate.Param +{ + public class VersionTypeSeedParameterSpecification : IParameterSpecification + { + private readonly IVersionType type; + + public VersionTypeSeedParameterSpecification(IVersionType type) + { + this.type = type; + } + + public int Bind(IDbCommand statement, QueryParameters qp, ISessionImplementor session, int position) + { + type.NullSafeSet(statement, type.Seed(session), position, session); + return 1; + } + + public IType ExpectedType + { + get { return type; } + set + { + // expected type is intrinsic here... + } + } + + public string RenderDisplayInfo() + { + return "version-seed, type=" + type; + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/HQL/Ast/BulkManipulation.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/HQL/Ast/BulkManipulation.cs 2009-05-05 15:38:45 UTC (rev 4245) +++ trunk/nhibernate/src/NHibernate.Test/HQL/Ast/BulkManipulation.cs 2009-05-05 18:07:06 UTC (rev 4246) @@ -44,6 +44,81 @@ } } + [Test] + public void UpdateWithWhereExistsSubquery() + { + // multi-table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ISession s = OpenSession(); + ITransaction t = s.BeginTransaction(); + var joe = new Human { Name = new Name { First = "Joe", Initial = 'Q', Last = "Public" } }; + s.Save(joe); + var doll = new Human {Name = new Name {First = "Kyu", Initial = 'P', Last = "Doll"}, Friends = new[] {joe}}; + s.Save(doll); + t.Commit(); + s.Close(); + + s = OpenSession(); + t = s.BeginTransaction(); + string updateQryString = "update Human h " + + "set h.description = 'updated' " + + "where exists (" + + " select f.id " + + " from h.friends f " + + " where f.name.last = 'Public' " + + ")"; + int count = s.CreateQuery(updateQryString).ExecuteUpdate(); + Assert.That(count, Is.EqualTo(1)); + s.Delete(doll); + s.Delete(joe); + t.Commit(); + s.Close(); + + // single-table (one-to-many & many-to-many) ~~~~~~~~~~~~~~~~~~~~~~~~~~ + s = OpenSession(); + t = s.BeginTransaction(); + var entity = new SimpleEntityWithAssociation(); + var other = new SimpleEntityWithAssociation(); + entity.Name= "main"; + other.Name= "many-to-many-association"; + entity.ManyToManyAssociatedEntities.Add(other); + entity.AddAssociation("one-to-many-association"); + s.Save(entity); + t.Commit(); + s.Close(); + + s = OpenSession(); + t = s.BeginTransaction(); + // one-to-many test + updateQryString = "update SimpleEntityWithAssociation e " + + "set e.Name = 'updated' " + + "where exists (" + + " select a.id " + + " from e.AssociatedEntities a " + + " where a.Name = 'one-to-many-association' " + + ")"; + count = s.CreateQuery(updateQryString).ExecuteUpdate(); + Assert.That(count, Is.EqualTo(1)); + // many-to-many test + if (Dialect.SupportsSubqueryOnMutatingTable) + { + updateQryString = "update SimpleEntityWithAssociation e " + + "set e.Name = 'updated' " + + "where exists (" + + " select a.id " + + " from e.ManyToManyAssociatedEntities a " + + " where a.Name = 'many-to-many-association' " + + ")"; + count = s.CreateQuery(updateQryString).ExecuteUpdate(); + Assert.That(count, Is.EqualTo(1)); + } + var mtm = entity.ManyToManyAssociatedEntities.GetEnumerator(); + mtm.MoveNext(); + s.Delete(mtm.Current); + s.Delete(entity); + t.Commit(); + s.Close(); + } + #endregion #region DELETES This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |