From: <fab...@us...> - 2009-05-02 05:10:05
|
Revision: 4223 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4223&view=rev Author: fabiomaulo Date: 2009-05-02 05:10:00 +0000 (Sat, 02 May 2009) Log Message: ----------- First test for executable HQL passed Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.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/FromClause.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/HqlSqlWalkerTreeAdapter.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/ASTIterator.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/CollectingNodeVisitor.cs trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/SyntheticAndFactory.cs trunk/nhibernate/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs trunk/nhibernate/src/NHibernate.Test/BulkManipulation/SimpleClass.hbm.xml Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Exec/BasicExecutor.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -6,9 +6,11 @@ using log4net; using NHibernate.Engine; using NHibernate.Exceptions; +using NHibernate.Hql.Ast.ANTLR.Tree; using NHibernate.Param; using NHibernate.Persister.Entity; using NHibernate.SqlCommand; +using NHibernate.SqlTypes; namespace NHibernate.Hql.Ast.ANTLR.Exec { @@ -17,18 +19,19 @@ { private readonly IQueryable persister; private static readonly ILog log = LogManager.GetLogger(typeof(QueryTranslatorImpl)); - private SqlString sql; + private readonly SqlString sql; - public BasicExecutor(HqlSqlWalker walker, IQueryable persister) : base(walker, log) + public BasicExecutor(IStatement statement, ITokenStream tokenStream, IQueryable persister) + : base(statement.Walker, log) { this.persister = persister; try { + var generator = new HqlSqlGenerator(statement, tokenStream, Factory); + generator.Generate(); - //SqlGenerator gen = new SqlGenerator(Factory); - //gen.statement(walker.getAST()); - //sql = gen.GetSQL(); - //gen.ParseErrorHandler.ThrowQueryException(); + sql = generator.Sql; + Parameters = generator.CollectionParameters; } catch (RecognitionException e) { @@ -36,6 +39,8 @@ } } + protected IList<IParameterSpecification> Parameters{get;private set;} + protected ISessionFactoryImplementor Factory { get @@ -60,8 +65,13 @@ { try { - //st = session.Batcher.PrepareCommand(CommandType.Text, sql, parameterTypes); - IEnumerator<IParameterSpecification> paramSpecifications = Walker.Parameters.GetEnumerator(); + var parameterTypes = new List<SqlType>(Parameters.Count); + foreach (var parameterSpecification in Parameters) + { + parameterTypes.AddRange(parameterSpecification.ExpectedType.SqlTypes(Factory)); + } + st = session.Batcher.PrepareCommand(CommandType.Text, sql, parameterTypes.ToArray()); + IEnumerator<IParameterSpecification> paramSpecifications = Parameters.GetEnumerator(); int pos = 1; while (paramSpecifications.MoveNext()) { Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -165,7 +165,7 @@ void PostProcessDelete(IASTNode delete) { - throw new NotImplementedException(); // DML + PostProcessDML((DeleteStatement)delete); } void PostProcessInsert(IASTNode insert) @@ -173,6 +173,24 @@ throw new NotImplementedException(); // DML } + private void PostProcessDML(IRestrictableStatement statement) + { + statement.FromClause.Resolve(); + + var fromElement = (FromElement)statement.FromClause.GetFromElements()[0]; + IQueryable persister = fromElement.Queryable; + // Make #@%$^#^&# sure no alias is applied to the table name + fromElement.Text = persister.TableName; + + // append any filter fragments; the EMPTY_MAP is used under the assumption that + // currently enabled filters should not affect this process + if (persister.DiscriminatorType != null) + { + new SyntheticAndFactory(this) + .AddDiscriminatorWhereFragment(statement, persister, new CollectionHelper.EmptyMapClass<string, IFilter>(), fromElement.TableAlias); + } + } + void AfterStatementCompletion(string statementName) { if (log.IsDebugEnabled) Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -166,9 +166,18 @@ public int ExecuteUpdate(QueryParameters queryParameters, ISessionImplementor session) { - throw new System.NotImplementedException(); // DML + ErrorIfSelect(); + return statementExecutor.Execute(queryParameters, session); } + private void ErrorIfSelect() + { + if (!sqlAst.NeedsExecutor) + { + throw new QueryExecutionRequestException("Not supported for select queries:", _hql); + } + } + public NHibernate.Loader.Loader Loader { get { return _queryLoader; } @@ -229,17 +238,14 @@ { get { - List<string> list = new List<string>(); + var list = new List<string>(); if (IsManipulationStatement) { - throw new NotImplementedException(); // DML - /* - String[] sqlStatements = statementExecutor.getSqlStatements(); - for (int i = 0; i < sqlStatements.length; i++) + SqlString[] sqlStatements = statementExecutor.SqlStatements; + foreach (var sqlStatement in sqlStatements) { - list.Add(sqlStatements[i]); + list.Add(sqlStatement.ToString()); } - */ } else { @@ -340,7 +346,7 @@ // PHASE 2 : Analyze the HQL AST, and produce an SQL AST. HqlSqlWalker w = Analyze(parser, collectionRole); - sqlAst = (IStatement)w.statement().Tree; + sqlAst = _translator.SqlStatement; // at some point the generate phase needs to be moved out of here, // because a single object-level DML might spawn multiple SQL DML @@ -353,14 +359,14 @@ // QueryLoader currently even has a dependency on this at all; does // it need it? Ideally like to see the walker itself given to the delegates directly... - if (_translator.SqlStatement.NeedsExecutor) + if (sqlAst.NeedsExecutor) { - statementExecutor = BuildAppropriateStatementExecutor(w); + statementExecutor = BuildAppropriateStatementExecutor(sqlAst); } else { // PHASE 3 : Generate the SQL. - _generator = new HqlSqlGenerator(_translator.SqlStatement, parser.Tokens, _factory); + _generator = new HqlSqlGenerator(sqlAst, parser.Tokens, _factory); _generator.Generate(); _queryLoader = new QueryLoader(this, _factory, w.SelectClause); @@ -387,8 +393,9 @@ _enabledFilters = null; //only needed during compilation phase... } - private IStatementExecutor BuildAppropriateStatementExecutor(HqlSqlWalker walker) + private IStatementExecutor BuildAppropriateStatementExecutor(IStatement statement) { + HqlSqlWalker walker = statement.Walker; if (walker.StatementType == HqlSqlWalker.DELETE) { FromElement fromElement = walker.GetFinalFromClause().GetFromElement(); @@ -400,7 +407,7 @@ } else { - return new BasicExecutor(walker, persister); + return new BasicExecutor(statement, _parser.Tokens, persister); } } else if (walker.StatementType == HqlSqlWalker.UPDATE) @@ -417,7 +424,7 @@ } else { - return new BasicExecutor(walker, persister); + return new BasicExecutor(statement, _parser.Tokens, persister); } } else if (walker.StatementType == HqlSqlWalker.INSERT) @@ -645,12 +652,12 @@ private static readonly ILog log = LogManager.GetLogger(typeof(HqlSqlGenerator)); private readonly IASTNode _ast; - private readonly CommonTokenStream _tokens; + private readonly ITokenStream _tokens; private readonly ISessionFactoryImplementor _sfi; private SqlString _sql; private IList<IParameterSpecification> _parameters; - public HqlSqlGenerator(IStatement ast, CommonTokenStream tokens, ISessionFactoryImplementor sfi) + public HqlSqlGenerator(IStatement ast, ITokenStream tokens, ISessionFactoryImplementor sfi) { _ast = (IASTNode) ast; _tokens = tokens; Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/ASTNode.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -273,7 +273,17 @@ return _children[index]; } - public void SetChild(int index, IASTNode newChild) + public IASTNode GetFirstChild() + { + return GetChild(0); + } + + public void SetFirstChild(IASTNode newChild) + { + SetChild(0, newChild); + } + + public void SetChild(int index, IASTNode newChild) { if ((_children == null) || _children.Count <= index) { Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/FromClause.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -360,5 +360,23 @@ { return "FromClause{" + "level=" + _level + "}"; } + + public virtual void Resolve() + { + // Make sure that all from elements registered with this FROM clause are actually in the AST. + var iter = (new ASTIterator(GetFirstChild())).GetEnumerator(); + var childrenInTree = new HashedSet<IASTNode>(); + while (iter.MoveNext()) + { + childrenInTree.Add(iter.Current); + } + foreach (var fromElement in _fromElements) + { + if (!childrenInTree.Contains(fromElement)) + { + throw new SemanticException("Element not in AST: " + fromElement); + } + } + } } } Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/HqlSqlWalkerTreeAdapter.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/HqlSqlWalkerTreeAdapter.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/HqlSqlWalkerTreeAdapter.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -33,8 +33,7 @@ ret = new SqlNode(payload); break; case HqlSqlWalker.DELETE: - //return DeleteStatement.class; - ret = new SqlNode(payload); + ret = new DeleteStatement(payload); break; case HqlSqlWalker.INSERT: //return InsertStatement.class; Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Tree/IASTNode.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -17,7 +17,9 @@ IASTNode Parent { get; set; } IASTNode NextSibling { get; } IASTNode GetChild(int index); - void SetChild(int index, IASTNode newChild); + IASTNode GetFirstChild(); + void SetFirstChild(IASTNode newChild); + void SetChild(int index, IASTNode newChild); IASTNode AddChild(IASTNode childNode); IASTNode InsertChild(int index, IASTNode child); IASTNode AddSibling(IASTNode newSibling); @@ -30,7 +32,7 @@ // TODO - not sure we need this... IToken Token { get; } - + string ToStringTree(); } } Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/ASTIterator.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/ASTIterator.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/ASTIterator.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -13,14 +13,16 @@ [CLSCompliant(false)] public class ASTIterator : IEnumerable<IASTNode> { + private readonly Stack<IASTNode> _stack = new Stack<IASTNode>(); private IASTNode _current; - private readonly Stack<IASTNode> _stack = new Stack<IASTNode>(); public ASTIterator(IASTNode tree) { _current = tree; } + #region IEnumerable<IASTNode> Members + public IEnumerator<IASTNode> GetEnumerator() { Down(); @@ -45,17 +47,20 @@ } } - private void Down() { - while ( _current != null && _current.ChildCount > 0) + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + private void Down() + { + while (_current != null && _current.ChildCount > 0) { _stack.Push(_current); _current = _current.GetChild(0); } } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } } -} +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/CollectingNodeVisitor.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/CollectingNodeVisitor.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/CollectingNodeVisitor.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -4,6 +4,11 @@ namespace NHibernate.Hql.Ast.ANTLR.Util { + /// <summary> + /// Filters nodes in/out of a tree. + /// </summary> + /// <param name="node">The node to check.</param> + /// <returns>true to keep the node, false if the node should be filtered out.</returns> [CLSCompliant(false)] public delegate bool FilterPredicate(IASTNode node); Modified: trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/SyntheticAndFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/SyntheticAndFactory.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate/Hql/Ast/ANTLR/Util/SyntheticAndFactory.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -1,11 +1,12 @@ using System; -using Antlr.Runtime.Tree; using log4net; using NHibernate.Hql.Ast.ANTLR.Tree; using NHibernate.Param; using NHibernate.SqlCommand; using NHibernate.Type; using NHibernate.Util; +using NHibernate.Persister.Entity; +using System.Collections.Generic; namespace NHibernate.Hql.Ast.ANTLR.Util { @@ -143,5 +144,45 @@ { return _hqlSqlWalker.ASTFactory.CreateNode(tokenType, text); } + + public virtual void AddDiscriminatorWhereFragment(IRestrictableStatement statement, IQueryable persister, IDictionary<string, IFilter> enabledFilters, string alias) + { + string whereFragment = persister.FilterFragment(alias, enabledFilters).Trim(); + if (string.Empty.Equals(whereFragment)) + { + return; + } + if (whereFragment.StartsWith("and")) + { + whereFragment = whereFragment.Substring(4); + } + + // Need to parse off the column qualifiers; this is assuming (which is true as of now) + // that this is only used from update and delete HQL statement parsing + whereFragment = StringHelper.Replace(whereFragment, persister.GenerateFilterConditionAlias(alias) + ".", ""); + + // Note: this simply constructs a "raw" SQL_TOKEN representing the + // where fragment and injects this into the tree. This "works"; + // however it is probably not the best long-term solution. + // + // At some point we probably want to apply an additional grammar to + // properly tokenize this where fragment into constituent parts + // focused on the operators embedded within the fragment. + IASTNode discrimNode = Create(HqlSqlWalker.SQL_TOKEN, whereFragment); + + if (statement.WhereClause.ChildCount == 0) + { + statement.WhereClause.SetFirstChild(discrimNode); + } + else + { + IASTNode and = Create(HqlSqlWalker.AND, "{and}"); + IASTNode currentFirstChild = statement.WhereClause.GetFirstChild(); + and.SetFirstChild(discrimNode); + and.AddChild(currentFirstChild); + statement.WhereClause.SetFirstChild(and); + } + } + } } Modified: trunk/nhibernate/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate.Test/BulkManipulation/HQLBulkOperations.cs 2009-05-02 05:10:00 UTC (rev 4223) @@ -6,7 +6,7 @@ [TestFixture] public class HqlBulkOperations: BaseFixture { - [Test, Ignore("Not supported yet.")] + [Test] public void SimpleDelete() { using (var s = OpenSession()) Modified: trunk/nhibernate/src/NHibernate.Test/BulkManipulation/SimpleClass.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/BulkManipulation/SimpleClass.hbm.xml 2009-05-01 14:55:28 UTC (rev 4222) +++ trunk/nhibernate/src/NHibernate.Test/BulkManipulation/SimpleClass.hbm.xml 2009-05-02 05:10:00 UTC (rev 4223) @@ -3,7 +3,7 @@ assembly="NHibernate.Test" namespace="NHibernate.Test.BulkManipulation"> - <class name="SimpleClass"> + <class name="SimpleClass" table="TSIMPLE"> <id type="int"> <generator class="native" /> </id> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |