From eca2881fcf68f7227a6a06cd24fb1ab6571d864f Mon Sep 17 00:00:00 2001 From: Fabio Maulo Date: Sat, 23 Aug 2008 23:52:09 +0000 Subject: [PATCH] Fix of NH-1034 (parameters in HQL functions) SVN: branches/2.0.x@3730 --- .../HQLFunctionTest/HQLFunctions.cs | 69 ++++++++- .../SQLFunctionTemplateTest.cs | 22 +-- .../HQLFunctionTest/SimpleFunctionsTest.cs | 67 ++++---- .../Criterion/SqlFunctionProjection.cs | 2 +- src/NHibernate/Dialect/FirebirdDialect.cs | 15 +- .../Dialect/Function/AnsiSubstringFunction.cs | 23 +-- .../Function/AnsiTrimEmulationFunction.cs | 23 +-- .../Dialect/Function/CastFunction.cs | 15 +- .../Dialect/Function/CharIndexFunction.cs | 31 ++-- .../Function/ClassicAggregateFunction.cs | 25 +-- .../Dialect/Function/ISQLFunction.cs | 5 +- .../Dialect/Function/NoArgSQLFunction.cs | 17 +- .../Dialect/Function/NvlFunction.cs | 16 +- .../Function/PositionSubstringFunction.cs | 35 +++-- .../Dialect/Function/SQLFunctionTemplate.cs | 20 ++- .../Dialect/Function/StandardSQLFunction.cs | 25 ++- .../Function/StandardSafeSQLFunction.cs | 3 +- .../Dialect/Function/VarArgsSQLFunction.cs | 13 +- src/NHibernate/Driver/SqlStringFormatter.cs | 7 +- src/NHibernate/Hql/Classic/GroupByParser.cs | 7 +- src/NHibernate/Hql/Classic/QueryTranslator.cs | 145 ++++++++++-------- src/NHibernate/Hql/Classic/SelectParser.cs | 26 ++-- .../SqlCommand/ISqlStringVisitor.cs | 3 +- src/NHibernate/SqlCommand/QuerySelect.cs | 20 +-- src/NHibernate/SqlCommand/SqlString.cs | 19 ++- src/NHibernate/SqlCommand/SqlStringBuilder.cs | 7 +- 26 files changed, 404 insertions(+), 256 deletions(-) diff --git a/src/NHibernate.Test/HQLFunctionTest/HQLFunctions.cs b/src/NHibernate.Test/HQLFunctionTest/HQLFunctions.cs index 10e69458a8d..8d01b123695 100644 --- a/src/NHibernate.Test/HQLFunctionTest/HQLFunctions.cs +++ b/src/NHibernate.Test/HQLFunctionTest/HQLFunctions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using NHibernate.Dialect; using NHibernate.Dialect.Function; @@ -17,7 +18,7 @@ public class HQLFunctions : TestCase static HQLFunctions() { notSupportedStandardFunction.Add("locate", - new System.Type[] { typeof(MsSql2000Dialect), typeof(MsSql2005Dialect), typeof(FirebirdDialect) }); + new System.Type[] { typeof(MsSql2000Dialect), typeof(MsSql2005Dialect), typeof(FirebirdDialect), typeof(PostgreSQLDialect) }); notSupportedStandardFunction.Add("bit_length", new System.Type[] { typeof(MsSql2000Dialect), typeof(MsSql2005Dialect) }); notSupportedStandardFunction.Add("extract", @@ -222,6 +223,36 @@ public void SubString() Animal result = (Animal) s.CreateQuery(hql).UniqueResult(); Assert.AreEqual("abcdef", result.Description); + hql = "from Animal a where substring(a.Description, 2, 3) = ?"; + result = (Animal)s.CreateQuery(hql) + .SetParameter(0, "bcd") + .UniqueResult(); + Assert.AreEqual("abcdef", result.Description); + + + hql = "from Animal a where substring(a.Description, 2, ?) = 'bcd'"; + result = (Animal)s.CreateQuery(hql) + .SetParameter(0, 3) + .UniqueResult(); + Assert.AreEqual("abcdef", result.Description); + + + hql = "from Animal a where substring(a.Description, ?, ?) = ?"; + result = (Animal)s.CreateQuery(hql) + .SetParameter(0, 2) + .SetParameter(1, 3) + .SetParameter(2, "bcd") + .UniqueResult(); + Assert.AreEqual("abcdef", result.Description); + + hql = "select substring(a.Description, ?, ?) from Animal a"; + IList results = s.CreateQuery(hql) + .SetParameter(0, 2) + .SetParameter(1, 3) + .List(); + Assert.AreEqual(1, results.Count); + Assert.AreEqual("bcd", results[0]); + if (twoArgSubstringSupported) { hql = "from Animal a where substring(a.Description, 4) = 'def'"; @@ -304,6 +335,7 @@ public void Trim() Animal a = new Animal(" abc", 20); s.Save(a); + s.Flush(); hql = "from Animal a where trim(both from a.Description) = 'abc'"; lresult = s.CreateQuery(hql).List(); Assert.AreEqual(2, lresult.Count); @@ -536,7 +568,7 @@ public void Cast() Assert.AreEqual(1.3f, l[0]); // Rendered in SELECT using a property in an operation with costant - hql = "select cast(7+123-5*a.BodyWeight as double) from Animal a"; + hql = "select cast(7+123-5*a.BodyWeight as Double) from Animal a"; l = s.CreateQuery(hql).List(); Assert.AreEqual(1, l.Count); Assert.AreEqual(7f + 123f - 5f * 1.3f, l[0]); @@ -559,6 +591,13 @@ public void Cast() result = (Animal)s.CreateQuery(hql).UniqueResult(); Assert.AreEqual("abcdef", result.Description); + // Rendered in WHERE using a property and named param + hql = "from Animal a where cast(:aParam+a.BodyWeight as Double)>0"; + result = (Animal)s.CreateQuery(hql) + .SetDouble("aParam", 2D) + .UniqueResult(); + Assert.AreEqual("abcdef", result.Description); + // Rendered in WHERE using a property and nested functions hql = "from Animal a where cast(cast(cast(a.BodyWeight as string) as double) as int) = 1"; result = (Animal)s.CreateQuery(hql).UniqueResult(); @@ -594,6 +633,30 @@ public void Cast() Assert.AreEqual(1, l.Count); Assert.AreEqual(129, l[0]); + // Rendered in HAVING using a property and named param (NOT SUPPORTED) + try + { + hql = "select cast(:aParam+a.BodyWeight as int) from Animal a group by cast(:aParam+a.BodyWeight as int) having cast(:aParam+a.BodyWeight as int)>0"; + l = s.CreateQuery(hql).SetInt32("aParam", 10).List(); + Assert.AreEqual(1, l.Count); + Assert.AreEqual(11, l[0]); + } + catch (QueryException ex) + { + if (!(ex.InnerException is NotSupportedException)) + throw; + } + catch (ADOException ex) + { + // This test raises an exception in SQL Server because named + // parameters internally are always positional (@p0, @p1, etc.) + // and named differently hence they mismatch between GROUP BY and HAVING clauses. + if (!ex.InnerException.Message.Equals( + "Column 'Animal.BodyWeight' is invalid in the HAVING clause " + + "because it is not contained in either an aggregate function or the GROUP BY clause.")) + throw; + } + // Rendered in HAVING using a property and nested functions string castExpr = "cast(cast(cast(a.BodyWeight as string) as double) as int)"; hql = string.Format("select {0} from Animal a group by {0} having {0} = 1", castExpr); @@ -788,4 +851,4 @@ public void ParameterLikeArgument() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/HQLFunctionTest/SQLFunctionTemplateTest.cs b/src/NHibernate.Test/HQLFunctionTest/SQLFunctionTemplateTest.cs index 944ccdc608a..a0795a9620a 100644 --- a/src/NHibernate.Test/HQLFunctionTest/SQLFunctionTemplateTest.cs +++ b/src/NHibernate.Test/HQLFunctionTest/SQLFunctionTemplateTest.cs @@ -16,19 +16,19 @@ public void Simple() Assert.IsTrue(ft.HasArguments); IList args = new ArrayList(); args.Add("'abcd <'"); - Assert.AreEqual("ltrim( 'abcd <' )", ft.Render(args, factoryImpl)); + Assert.AreEqual("ltrim( 'abcd <' )", ft.Render(args, factoryImpl).ToString()); ft = new SQLFunctionTemplate(NHibernateUtil.String, "ltrim( Az?ab )"); Assert.IsFalse(ft.HasArguments); - Assert.AreEqual("ltrim( Az?ab )", ft.Render(args, factoryImpl)); + Assert.AreEqual("ltrim( Az?ab )", ft.Render(args, factoryImpl).ToString()); ft = new SQLFunctionTemplate(NHibernateUtil.String, "function( ?1 )? 5:6"); Assert.IsTrue(ft.HasArguments); - Assert.AreEqual("function( 'abcd <' )? 5:6", ft.Render(args, factoryImpl)); + Assert.AreEqual("function( 'abcd <' )? 5:6", ft.Render(args, factoryImpl).ToString()); ft = new SQLFunctionTemplate(NHibernateUtil.String, "????????1?"); Assert.IsTrue(ft.HasArguments); - Assert.AreEqual("???????'abcd <'?", ft.Render(args, factoryImpl)); + Assert.AreEqual("???????'abcd <'?", ft.Render(args, factoryImpl).ToString()); } [Test] @@ -44,14 +44,14 @@ public void RepetedParams() args.Add("'param2 ab '"); Assert.AreEqual( "replace( replace( rtrim( replace( replace( 'param1 ', ' ', '${space}$' ), 'param2 ab ', ' ' ) ), ' ', 'param2 ab ' ), '${space}$', ' ' )", - ft.Render(args, factoryImpl)); + ft.Render(args, factoryImpl).ToString()); args.Clear(); ft = new SQLFunctionTemplate(NHibernateUtil.String, "?1 ?3 ?2 ?3 ?1"); args.Add(1); args.Add(2); args.Add(3); - Assert.AreEqual("1 3 2 3 1", ft.Render(args, factoryImpl)); + Assert.AreEqual("1 3 2 3 1", ft.Render(args, factoryImpl).ToString()); } //[Test] not required @@ -68,7 +68,7 @@ public void NoStringArguments() DateTime.Today.ToString(DateTimeFormatInfo.InvariantInfo), (125.6D).ToString(NumberFormatInfo.InvariantInfo), (0910.123456m).ToString(NumberFormatInfo.InvariantInfo)); - Assert.AreEqual(expected, ft.Render(args, factoryImpl)); + Assert.AreEqual(expected, ft.Render(args, factoryImpl).ToString()); } [Test] @@ -79,13 +79,13 @@ public void ArgsDiffParams() // No Args; 2 params ft = new SQLFunctionTemplate(NHibernateUtil.String, "func(?1,?2)"); - Assert.AreEqual("func(,)", ft.Render(args, factoryImpl)); + Assert.AreEqual("func(,)", ft.Render(args, factoryImpl).ToString()); // Argsparams args.Clear(); @@ -93,7 +93,7 @@ public void ArgsDiffParams() args.Add(1); args.Add(2); args.Add(3); - Assert.AreEqual("func(1,3)", ft.Render(args, factoryImpl)); + Assert.AreEqual("func(1,3)", ft.Render(args, factoryImpl).ToString()); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/HQLFunctionTest/SimpleFunctionsTest.cs b/src/NHibernate.Test/HQLFunctionTest/SimpleFunctionsTest.cs index 87beda25d68..f303108da6f 100644 --- a/src/NHibernate.Test/HQLFunctionTest/SimpleFunctionsTest.cs +++ b/src/NHibernate.Test/HQLFunctionTest/SimpleFunctionsTest.cs @@ -3,6 +3,7 @@ using NHibernate.Dialect.Function; using NHibernate.SqlTypes; using NUnit.Framework; +using NHibernate.SqlCommand; namespace NHibernate.Test.HQLFunctionTest { @@ -15,16 +16,16 @@ public void NoArgFunction() IList args = new ArrayList(); NoArgSQLFunction nf = new NoArgSQLFunction("noArgs", NHibernateUtil.String); Assert.IsTrue(nf.HasParenthesesIfNoArguments); - Assert.AreEqual("noArgs()", nf.Render(args, factoryImpl)); + Assert.AreEqual("noArgs()", nf.Render(args, factoryImpl).ToString()); nf = new NoArgSQLFunction("noArgs", NHibernateUtil.String, false); Assert.IsFalse(nf.HasParenthesesIfNoArguments); - Assert.AreEqual("noArgs", nf.Render(args, factoryImpl)); + Assert.AreEqual("noArgs", nf.Render(args, factoryImpl).ToString()); args.Add("aparam"); try { - string t = nf.Render(args, factoryImpl); + SqlString t = nf.Render(args, factoryImpl); Assert.Fail("No exception if has argument"); } catch (QueryException) @@ -39,11 +40,11 @@ public void StandardFunction() IList args = new ArrayList(); StandardSQLFunction sf = new StandardSQLFunction("fname"); - Assert.AreEqual("fname()", sf.Render(args, factoryImpl)); + Assert.AreEqual("fname()", sf.Render(args, factoryImpl).ToString()); args.Add(1); args.Add(2); - Assert.AreEqual("fname(1, 2)", sf.Render(args, factoryImpl)); + Assert.AreEqual("fname(1, 2)", sf.Render(args, factoryImpl).ToString()); } [Test] @@ -54,7 +55,7 @@ public void CastFunc() CastFunction cf = new CastFunction(); try { - string t = cf.Render(args, factoryImpl); + SqlString t = cf.Render(args, factoryImpl); Assert.Fail("No exception if no argument"); } catch (QueryException) @@ -66,14 +67,14 @@ public void CastFunc() args.Add("long"); string expected = string.Format("cast({0} as {1})", args[0], factoryImpl.Dialect.GetCastTypeName(SqlTypeFactory.Int64)); - Assert.AreEqual(expected, cf.Render(args, factoryImpl)); + Assert.AreEqual(expected, cf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("'123'"); args.Add("NO_TYPE"); try { - string t = cf.Render(args, factoryImpl); + SqlString t = cf.Render(args, factoryImpl); Assert.Fail("Ivalid type accepted"); } catch (QueryException) @@ -88,16 +89,16 @@ public void VarArgsFunction() IList args = new ArrayList(); VarArgsSQLFunction vf = new VarArgsSQLFunction("(", " || ", ")"); - Assert.AreEqual("()", vf.Render(args, factoryImpl)); + Assert.AreEqual("()", vf.Render(args, factoryImpl).ToString()); args.Add("va1"); - Assert.AreEqual("(va1)", vf.Render(args, factoryImpl)); + Assert.AreEqual("(va1)", vf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("va1"); args.Add("va2"); args.Add("va3"); - Assert.AreEqual("(va1 || va2 || va3)", vf.Render(args, factoryImpl)); + Assert.AreEqual("(va1 || va2 || va3)", vf.Render(args, factoryImpl).ToString()); } [Test] @@ -107,13 +108,13 @@ public void Nvl() NvlFunction nf = new NvlFunction(); args.Add("va1"); - Assert.AreEqual("va1", nf.Render(args, factoryImpl)); + Assert.AreEqual("va1", nf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("va1"); args.Add("va2"); args.Add("va3"); - Assert.AreEqual("nvl(va1, nvl(va2, va3))", nf.Render(args, factoryImpl)); + Assert.AreEqual("nvl(va1, nvl(va2, va3))", nf.Render(args, factoryImpl).ToString()); } [Test] @@ -124,13 +125,13 @@ public void PositionSubstring() PositionSubstringFunction psf = new PositionSubstringFunction(); args.Add("'a'"); args.Add("va2"); - Assert.AreEqual("position('a' in va2)", psf.Render(args, factoryImpl)); + Assert.AreEqual("position('a' in va2)", psf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("'a'"); args.Add("va2"); args.Add("2"); - Assert.AreEqual("(position('a' in substring(va2, 2))+2-1)", psf.Render(args, factoryImpl)); + Assert.AreEqual("(position('a' in substring(va2, 2))+2-1)", psf.Render(args, factoryImpl).ToString()); } [Test] @@ -145,19 +146,19 @@ public void ClassicSum() ClassicSumFunction csf = new ClassicSumFunction(); args.Add("va1"); - Assert.AreEqual("sum(va1)", csf.Render(args, factoryImpl)); + Assert.AreEqual("sum(va1)", csf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("distinct"); args.Add("va2"); - Assert.AreEqual("sum(distinct va2)", csf.Render(args, factoryImpl)); + Assert.AreEqual("sum(distinct va2)", csf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("va1"); args.Add("va2"); try { - string t = csf.Render(args, factoryImpl); + SqlString t = csf.Render(args, factoryImpl); Assert.Fail("No exception 2 argument without :" + t); } catch (QueryException) @@ -175,18 +176,18 @@ public void ClassicCount() ClassicCountFunction ccf = new ClassicCountFunction(); args.Add("va1"); - Assert.AreEqual("count(va1)", ccf.Render(args, factoryImpl)); + Assert.AreEqual("count(va1)", ccf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("*"); - Assert.AreEqual("count(*)", ccf.Render(args, factoryImpl)); + Assert.AreEqual("count(*)", ccf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("va1"); args.Add("va2"); try { - string t = ccf.Render(args, factoryImpl); + SqlString t = ccf.Render(args, factoryImpl); Assert.Fail("No exception 2 argument without :" + t); } catch (QueryException) @@ -207,19 +208,19 @@ public void ClassicAvg() ClassicAvgFunction caf = new ClassicAvgFunction(); args.Add("va1"); - Assert.AreEqual("avg(va1)", caf.Render(args, factoryImpl)); + Assert.AreEqual("avg(va1)", caf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("distinct"); args.Add("va2"); - Assert.AreEqual("avg(distinct va2)", caf.Render(args, factoryImpl)); + Assert.AreEqual("avg(distinct va2)", caf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("va1"); args.Add("va2"); try { - string t = caf.Render(args, factoryImpl); + SqlString t = caf.Render(args, factoryImpl); Assert.Fail("No exception 2 argument without :" + t); } catch (QueryException) @@ -235,19 +236,19 @@ public void ClassicAggregate() ClassicAggregateFunction caf = new ClassicAggregateFunction("max", false); args.Add("va1"); - Assert.AreEqual("max(va1)", caf.Render(args, factoryImpl)); + Assert.AreEqual("max(va1)", caf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("distinct"); args.Add("va2"); - Assert.AreEqual("max(distinct va2)", caf.Render(args, factoryImpl)); + Assert.AreEqual("max(distinct va2)", caf.Render(args, factoryImpl).ToString()); args.Clear(); args.Add("va1"); args.Add("va2"); try { - string t = caf.Render(args, factoryImpl); + SqlString t = caf.Render(args, factoryImpl); Assert.Fail("No exception 2 argument without :" + t); } catch (QueryException) @@ -259,7 +260,7 @@ public void ClassicAggregate() args.Add("*"); try { - string t = caf.Render(args, factoryImpl); + SqlString t = caf.Render(args, factoryImpl); Assert.Fail("No exception '*' :" + t); } catch (QueryException) @@ -280,15 +281,15 @@ public void AnsiSubstring() AnsiSubstringFunction asf = new AnsiSubstringFunction(); args.Add("var1"); args.Add("3"); - Assert.AreEqual("substring(var1 from 3)", asf.Render(args, factoryImpl)); + Assert.AreEqual("substring(var1 from 3)", asf.Render(args, factoryImpl).ToString()); args.Add("4"); - Assert.AreEqual("substring(var1 from 3 for 4)", asf.Render(args, factoryImpl)); + Assert.AreEqual("substring(var1 from 3 for 4)", asf.Render(args, factoryImpl).ToString()); args.Clear(); try { - string t = asf.Render(args, factoryImpl); + SqlString t = asf.Render(args, factoryImpl); Assert.Fail("Not threw 'Not enough parameters' exception:" + t); } catch (QueryException) @@ -302,7 +303,7 @@ public void AnsiSubstring() args.Add("4"); try { - string t = asf.Render(args, factoryImpl); + SqlString t = asf.Render(args, factoryImpl); Assert.Fail("Not threw 'Not enough parameters' exception:" + t); } catch (QueryException) @@ -311,4 +312,4 @@ public void AnsiSubstring() } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Criterion/SqlFunctionProjection.cs b/src/NHibernate/Criterion/SqlFunctionProjection.cs index fc29711c0a0..fa5857e49b4 100644 --- a/src/NHibernate/Criterion/SqlFunctionProjection.cs +++ b/src/NHibernate/Criterion/SqlFunctionProjection.cs @@ -41,7 +41,7 @@ public override SqlString ToSqlString(ICriteria criteria, int position, ICriteri { tokens.Add(replacemenToken); } - string functionStatement = sqlFunction.Render(tokens, criteriaQuery.Factory); + string functionStatement = sqlFunction.Render(tokens, criteriaQuery.Factory).ToString(); string[] splitted = functionStatement.Split(new string[] { replacemenToken }, StringSplitOptions.RemoveEmptyEntries); SqlStringBuilder sb = new SqlStringBuilder(); diff --git a/src/NHibernate/Dialect/FirebirdDialect.cs b/src/NHibernate/Dialect/FirebirdDialect.cs index 43f33f88a65..bb9d11396c4 100644 --- a/src/NHibernate/Dialect/FirebirdDialect.cs +++ b/src/NHibernate/Dialect/FirebirdDialect.cs @@ -219,10 +219,15 @@ public CastedFunction(string name, IType returnType) { } - public override string Render(IList args, ISessionFactoryImplementor factory) + public override SqlString Render(IList args, ISessionFactoryImplementor factory) { - base.Render(args, factory); - return string.Format("cast('{0}' as {1})", Name, FunctionReturnType.SqlTypes(factory)[0]); + return new SqlStringBuilder() + .Add("cast('") + .Add(name) + .Add("' as ") + .Add(returnType.SqlTypes(factory)[0].ToString()) + .Add(")") + .ToSqlString(); } } @@ -232,9 +237,9 @@ public CurrentTimeStamp() : base("current_timestamp", NHibernateUtil.DateTime, true) { } - public override string Render(IList args, ISessionFactoryImplementor factory) + public override SqlString Render(IList args, ISessionFactoryImplementor factory) { - return Name; + return new SqlString(name); } } } diff --git a/src/NHibernate/Dialect/Function/AnsiSubstringFunction.cs b/src/NHibernate/Dialect/Function/AnsiSubstringFunction.cs index 92acc1d09f3..08edc20c91f 100644 --- a/src/NHibernate/Dialect/Function/AnsiSubstringFunction.cs +++ b/src/NHibernate/Dialect/Function/AnsiSubstringFunction.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -39,26 +40,26 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { if (args.Count < 2 || args.Count > 3) { throw new QueryException("substring(): Incorrect number of parameters (expected 2 or 3, got " + args.Count + ")"); } - StringBuilder cmd = new StringBuilder(); - cmd.Append("substring(") - .Append(args[0]) - .Append(" from ") - .Append(args[1]); + SqlStringBuilder cmd = new SqlStringBuilder(); + cmd.Add("substring(") + .AddObject(args[0]) + .Add(" from ") + .AddObject(args[1]); if (args.Count > 2) { - cmd.Append(" for ") - .Append(args[2]); + cmd.Add(" for ") + .AddObject(args[2]); } - cmd.Append(')'); - return cmd.ToString(); + cmd.Add(")"); + return cmd.ToSqlString(); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/AnsiTrimEmulationFunction.cs b/src/NHibernate/Dialect/Function/AnsiTrimEmulationFunction.cs index a386785fc66..4c0e9587193 100644 --- a/src/NHibernate/Dialect/Function/AnsiTrimEmulationFunction.cs +++ b/src/NHibernate/Dialect/Function/AnsiTrimEmulationFunction.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; using NHibernate.Util; using System.Text.RegularExpressions; @@ -79,14 +80,14 @@ public bool HasParenthesesIfNoArguments /// If only trim specification is omitted, BOTH is assumed; /// if trim character is omitted, space is assumed /// - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { if (args.Count < 1 || args.Count > 4) { throw new QueryException("function takes between 1 and 4 arguments"); } - string firstArg = (string) args[0]; + string firstArg = args[0].ToString(); if (args.Count == 1) { @@ -108,7 +109,7 @@ public string Render(IList args, ISessionFactoryImplementor factory) bool leading = true; // should leading trim-characters be trimmed? bool trailing = true; // should trailing trim-characters be trimmed? string trimCharacter = null; // the trim-character - string trimSource = null; // the trim-source + object trimSource = null; // the trim-source // potentialTrimCharacterArgIndex = 1 assumes that a // trim-specification has been specified. we handle the @@ -130,11 +131,11 @@ public string Render(IList args, ISessionFactoryImplementor factory) potentialTrimCharacterArgIndex = 0; } - string potentialTrimCharacter = (string) args[potentialTrimCharacterArgIndex]; - if (StringHelper.EqualsCaseInsensitive("from", potentialTrimCharacter)) + object potentialTrimCharacter = args[potentialTrimCharacterArgIndex]; + if (StringHelper.EqualsCaseInsensitive("from", potentialTrimCharacter.ToString())) { trimCharacter = "' '"; - trimSource = (string) args[potentialTrimCharacterArgIndex + 1]; + trimSource = args[potentialTrimCharacterArgIndex + 1]; } else if (potentialTrimCharacterArgIndex + 1 >= args.Count) { @@ -143,14 +144,14 @@ public string Render(IList args, ISessionFactoryImplementor factory) } else { - trimCharacter = potentialTrimCharacter; - if (StringHelper.EqualsCaseInsensitive("from", (string) args[potentialTrimCharacterArgIndex + 1])) + trimCharacter = potentialTrimCharacter.ToString(); + if (StringHelper.EqualsCaseInsensitive("from", args[potentialTrimCharacterArgIndex + 1].ToString())) { - trimSource = (string) args[potentialTrimCharacterArgIndex + 2]; + trimSource = args[potentialTrimCharacterArgIndex + 2]; } else { - trimSource = (string) args[potentialTrimCharacterArgIndex + 1]; + trimSource = args[potentialTrimCharacterArgIndex + 1]; } } @@ -208,4 +209,4 @@ bool IFunctionGrammar.IsKnownArgument(string token) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/CastFunction.cs b/src/NHibernate/Dialect/Function/CastFunction.cs index da86db8c40e..d6a98d7eba8 100644 --- a/src/NHibernate/Dialect/Function/CastFunction.cs +++ b/src/NHibernate/Dialect/Function/CastFunction.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.SqlTypes; using NHibernate.Type; @@ -34,13 +35,13 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { if (args.Count != 2) { throw new QueryException("cast() requires two arguments"); } - string typeName = (string) args[1]; + string typeName = args[1].ToString(); string sqlType = string.Empty; IType hqlType = TypeFactory.HeuristicType(typeName); if (hqlType != null) @@ -70,7 +71,13 @@ public string Render(IList args, ISessionFactoryImplementor factory) { throw new QueryException(string.Format("invalid Hibernate type for cast(): type {0} not found", typeName)); } - return String.Format("cast({0} as {1})", args[0], sqlType); + return new SqlStringBuilder() + .Add("cast(") + .AddObject(args[0]) + .Add(" as ") + .Add(sqlType) + .Add(")") + .ToSqlString(); } #endregion @@ -89,4 +96,4 @@ bool IFunctionGrammar.IsKnownArgument(string token) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/CharIndexFunction.cs b/src/NHibernate/Dialect/Function/CharIndexFunction.cs index 2777c36a19f..bc1a061cbbe 100644 --- a/src/NHibernate/Dialect/Function/CharIndexFunction.cs +++ b/src/NHibernate/Dialect/Function/CharIndexFunction.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -32,7 +33,7 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { // TODO: QueryException if args.Count<2 (not present in H3.2) bool threeArgs = args.Count > 2; @@ -40,27 +41,27 @@ public string Render(IList args, ISessionFactoryImplementor factory) object orgString = args[1]; object start = threeArgs ? args[2] : null; - StringBuilder buf = new StringBuilder(); - buf.Append("charindex(") - .Append(pattern) - .Append(", "); + SqlStringBuilder buf = new SqlStringBuilder(); + buf.Add("charindex(") + .AddObject(pattern) + .Add(", "); if (threeArgs) { - buf.Append("right("); + buf.Add("right("); } - buf.Append(orgString); + buf.AddObject(orgString); if (threeArgs) { - buf.Append(", char_length(") - .Append(orgString) - .Append(")-(") - .Append(start) - .Append("-1))"); + buf.Add(", char_length(") + .AddObject(orgString) + .Add(")-(") + .AddObject(start) + .Add("-1))"); } - buf.Append(')'); - return buf.ToString(); + buf.Add(")"); + return buf.ToSqlString(); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs b/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs index 4d2dcc100a6..bd0e500b07d 100644 --- a/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs +++ b/src/NHibernate/Dialect/Function/ClassicAggregateFunction.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; using NHibernate.Util; @@ -53,7 +54,7 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { //ANSI-SQL92 definition // ::= @@ -69,22 +70,22 @@ public string Render(IList args, ISessionFactoryImplementor factory) { throw new QueryException(string.Format("Aggregate {0}(): invalid argument '*'.", name)); } - StringBuilder cmd = new StringBuilder(); - cmd.Append(name) - .Append("("); + SqlStringBuilder cmd = new SqlStringBuilder(); + cmd.Add(name) + .Add("("); if (args.Count > 1) { - string firstArg = args[0].ToString(); - if (!StringHelper.EqualsCaseInsensitive("distinct", firstArg) && - !StringHelper.EqualsCaseInsensitive("all", firstArg)) + object firstArg = args[0]; + if (!StringHelper.EqualsCaseInsensitive("distinct", firstArg.ToString()) && + !StringHelper.EqualsCaseInsensitive("all", firstArg.ToString())) { throw new QueryException(string.Format("Aggregate {0}(): token unknow {1}.", name, firstArg)); } - cmd.Append(firstArg).Append(' '); + cmd.AddObject(firstArg).Add(" "); } - cmd.Append(args[args.Count - 1]) - .Append(')'); - return cmd.ToString(); + cmd.AddObject(args[args.Count - 1]) + .Add(")"); + return cmd.ToSqlString(); } #endregion @@ -110,4 +111,4 @@ bool IFunctionGrammar.IsKnownArgument(string token) #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/ISQLFunction.cs b/src/NHibernate/Dialect/Function/ISQLFunction.cs index 4c97145b278..74b57ee4a9d 100644 --- a/src/NHibernate/Dialect/Function/ISQLFunction.cs +++ b/src/NHibernate/Dialect/Function/ISQLFunction.cs @@ -1,5 +1,6 @@ using System.Collections; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -38,6 +39,6 @@ public interface ISQLFunction /// List of arguments /// /// SQL fragment for the fuction. - string Render(IList args, ISessionFactoryImplementor factory); + SqlString Render(IList args, ISessionFactoryImplementor factory); } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/NoArgSQLFunction.cs b/src/NHibernate/Dialect/Function/NoArgSQLFunction.cs index 71caf129ba0..5a11e7ccfe6 100644 --- a/src/NHibernate/Dialect/Function/NoArgSQLFunction.cs +++ b/src/NHibernate/Dialect/Function/NoArgSQLFunction.cs @@ -1,5 +1,6 @@ using System.Collections; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -9,8 +10,8 @@ namespace NHibernate.Dialect.Function /// public class NoArgSQLFunction : ISQLFunction { - private readonly IType returnType = null; - private readonly string name; + protected readonly IType returnType = null; + protected readonly string name; private readonly bool hasParenthesesIfNoArguments; public NoArgSQLFunction(string name, IType returnType) : this(name, returnType, true) @@ -51,15 +52,21 @@ public bool HasParenthesesIfNoArguments get { return hasParenthesesIfNoArguments; } } - public virtual string Render(IList args, ISessionFactoryImplementor factory) + public virtual SqlString Render(IList args, ISessionFactoryImplementor factory) { if (args.Count > 0) { throw new QueryException("function takes no arguments: " + name); } - return hasParenthesesIfNoArguments ? name + "()" : name; + SqlStringBuilder buf = new SqlStringBuilder(2); + buf.Add(name); + if (hasParenthesesIfNoArguments) + { + buf.Add("()"); + } + return buf.ToSqlString(); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/NvlFunction.cs b/src/NHibernate/Dialect/Function/NvlFunction.cs index 292b4995a7b..627537a1a0b 100644 --- a/src/NHibernate/Dialect/Function/NvlFunction.cs +++ b/src/NHibernate/Dialect/Function/NvlFunction.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -31,7 +32,7 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { // DONE: QueryException if args.Count==0 (not present in H3.2) if (args.Count == 0) @@ -43,14 +44,19 @@ public string Render(IList args, ISessionFactoryImplementor factory) args.RemoveAt(lastIndex); if (lastIndex == 0) { - return last.ToString(); + return new SqlString(last); } object secondLast = args[lastIndex - 1]; - string nvl = "nvl(" + secondLast + ", " + last + ")"; - args[lastIndex - 1] = nvl; + SqlStringBuilder nvl = new SqlStringBuilder(5) + .Add("nvl(") + .AddObject(secondLast) + .Add(", ") + .AddObject(last) + .Add(")"); + args[lastIndex - 1] = nvl.ToSqlString(); return Render(args, factory); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/PositionSubstringFunction.cs b/src/NHibernate/Dialect/Function/PositionSubstringFunction.cs index 17cde5c5489..d67f9e25b20 100644 --- a/src/NHibernate/Dialect/Function/PositionSubstringFunction.cs +++ b/src/NHibernate/Dialect/Function/PositionSubstringFunction.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -32,7 +33,7 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { // DONE: QueryException if args.Count<2 (not present in H3.2) if (args.Count < 2) @@ -44,35 +45,35 @@ public string Render(IList args, ISessionFactoryImplementor factory) object orgString = args[1]; object start = threeArgs ? args[2] : null; - StringBuilder buf = new StringBuilder(); + SqlStringBuilder buf = new SqlStringBuilder(); if (threeArgs) { - buf.Append('('); + buf.Add("("); } - buf.Append("position(") - .Append(pattern) - .Append(" in "); + buf.Add("position(") + .AddObject(pattern) + .Add(" in "); if (threeArgs) { - buf.Append("substring("); + buf.Add("substring("); } - buf.Append(orgString); + buf.AddObject(orgString); if (threeArgs) { - buf.Append(", ") - .Append(start) - .Append(')'); + buf.Add(", ") + .AddObject(start) + .Add(")"); } - buf.Append(')'); + buf.Add(")"); if (threeArgs) { - buf.Append('+') - .Append(start) - .Append("-1)"); + buf.Add("+") + .AddObject(start) + .Add("-1)"); } - return buf.ToString(); + return buf.ToSqlString(); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/SQLFunctionTemplate.cs b/src/NHibernate/Dialect/Function/SQLFunctionTemplate.cs index 71af1c6a259..e6f96d6d7e4 100644 --- a/src/NHibernate/Dialect/Function/SQLFunctionTemplate.cs +++ b/src/NHibernate/Dialect/Function/SQLFunctionTemplate.cs @@ -3,6 +3,7 @@ using System.Text; using System.Text.RegularExpressions; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -98,9 +99,9 @@ public bool HasParenthesesIfNoArguments /// args function arguments /// generated SQL function call /// - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { - StringBuilder buf = new StringBuilder(); + SqlStringBuilder buf = new SqlStringBuilder(); foreach (TemplateChunk tc in chunks) { if (tc.ArgumentIndex != InvalidArgumentIndex) @@ -110,15 +111,22 @@ public string Render(IList args, ISessionFactoryImplementor factory) // TODO: if (arg == null) QueryException is better ? if (arg != null) { - buf.Append(arg); + if (arg is Parameter || arg is SqlString) + { + buf.AddObject(arg); + } + else + { + buf.Add(arg.ToString()); + } } } else { - buf.Append(tc.Text); + buf.Add(tc.Text); } } - return buf.ToString(); + return buf.ToSqlString(); } #endregion @@ -128,4 +136,4 @@ public override string ToString() return template; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/StandardSQLFunction.cs b/src/NHibernate/Dialect/Function/StandardSQLFunction.cs index 56445394807..21077ef46c7 100644 --- a/src/NHibernate/Dialect/Function/StandardSQLFunction.cs +++ b/src/NHibernate/Dialect/Function/StandardSQLFunction.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -55,17 +56,25 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public virtual string Render(IList args, ISessionFactoryImplementor factory) + public virtual SqlString Render(IList args, ISessionFactoryImplementor factory) { - StringBuilder buf = new StringBuilder(); - buf.Append(name) - .Append('('); + SqlStringBuilder buf = new SqlStringBuilder(); + buf.Add(name) + .Add("("); for (int i = 0; i < args.Count; i++) { - buf.Append(args[i]); - if (i < (args.Count - 1)) buf.Append(", "); + object arg = args[i]; + if (arg is Parameter || arg is SqlString) + { + buf.AddObject(arg); + } + else + { + buf.Add(arg.ToString()); + } + if (i < (args.Count - 1)) buf.Add(", "); } - return buf.Append(')').ToString(); + return buf.Add(")").ToSqlString(); } #endregion @@ -75,4 +84,4 @@ public override string ToString() return name; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Dialect/Function/StandardSafeSQLFunction.cs b/src/NHibernate/Dialect/Function/StandardSafeSQLFunction.cs index 2df3521995f..92b2fa1aa6a 100644 --- a/src/NHibernate/Dialect/Function/StandardSafeSQLFunction.cs +++ b/src/NHibernate/Dialect/Function/StandardSafeSQLFunction.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -39,7 +40,7 @@ public StandardSafeSQLFunction(string name, IType typeValue, int allowedArgsCoun this.allowedArgsCount = allowedArgsCount; } - public override string Render(System.Collections.IList args, NHibernate.Engine.ISessionFactoryImplementor factory) + public override SqlString Render(System.Collections.IList args, NHibernate.Engine.ISessionFactoryImplementor factory) { if (args.Count!= allowedArgsCount) { diff --git a/src/NHibernate/Dialect/Function/VarArgsSQLFunction.cs b/src/NHibernate/Dialect/Function/VarArgsSQLFunction.cs index c4ca53d6896..ce582c98261 100644 --- a/src/NHibernate/Dialect/Function/VarArgsSQLFunction.cs +++ b/src/NHibernate/Dialect/Function/VarArgsSQLFunction.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Text; using NHibernate.Engine; +using NHibernate.SqlCommand; using NHibernate.Type; namespace NHibernate.Dialect.Function @@ -47,17 +48,17 @@ public bool HasParenthesesIfNoArguments get { return true; } } - public string Render(IList args, ISessionFactoryImplementor factory) + public SqlString Render(IList args, ISessionFactoryImplementor factory) { - StringBuilder buf = new StringBuilder().Append(begin); + SqlStringBuilder buf = new SqlStringBuilder().Add(begin); for (int i = 0; i < args.Count; i++) { - buf.Append(args[i]); - if (i < args.Count - 1) buf.Append(sep); + buf.AddObject(args[i]); + if (i < args.Count - 1) buf.Add(sep); } - return buf.Append(end).ToString(); + return buf.Add(end).ToSqlString(); } #endregion } -} \ No newline at end of file +} diff --git a/src/NHibernate/Driver/SqlStringFormatter.cs b/src/NHibernate/Driver/SqlStringFormatter.cs index 205617330fa..2538a649281 100644 --- a/src/NHibernate/Driver/SqlStringFormatter.cs +++ b/src/NHibernate/Driver/SqlStringFormatter.cs @@ -30,6 +30,11 @@ void ISqlStringVisitor.String(string text) result.Append(text); } + void ISqlStringVisitor.String(SqlString sqlString) + { + result.Append(sqlString.ToString()); + } + void ISqlStringVisitor.Parameter() { string name = formatter.GetParameterName(parameterIndex); @@ -37,4 +42,4 @@ void ISqlStringVisitor.Parameter() result.Append(name); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Hql/Classic/GroupByParser.cs b/src/NHibernate/Hql/Classic/GroupByParser.cs index ae2df91ac66..4b3a5cef95b 100644 --- a/src/NHibernate/Hql/Classic/GroupByParser.cs +++ b/src/NHibernate/Hql/Classic/GroupByParser.cs @@ -27,6 +27,11 @@ public void Token(string token, QueryTranslator q) q.AppendGroupByToken(pathExpressionParser.WhereColumn); pathExpressionParser.AddAssociation(q); } + else if (token.StartsWith(ParserHelper.HqlVariablePrefix)) + { + q.AddNamedParameter(token.Substring(1)); + q.AppendGroupByParameter(); + } else { q.AppendGroupByToken(token); @@ -46,4 +51,4 @@ public GroupByParser() pathExpressionParser.UseThetaStyleJoin = true; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Hql/Classic/QueryTranslator.cs b/src/NHibernate/Hql/Classic/QueryTranslator.cs index 433ea8433c1..cfa9ea035af 100644 --- a/src/NHibernate/Hql/Classic/QueryTranslator.cs +++ b/src/NHibernate/Hql/Classic/QueryTranslator.cs @@ -19,7 +19,6 @@ using NHibernate.Type; using NHibernate.Util; using NHibernate.Dialect.Function; -using System.Collections.Specialized; using System.Collections.Generic; namespace NHibernate.Hql.Classic @@ -45,7 +44,7 @@ public class QueryTranslator : BasicLoader, IFilterTranslator private readonly IDictionary uniqueKeyOwnerReferences = new Dictionary(); private readonly IDictionary decoratedPropertyMappings = new Dictionary(); - private readonly IList scalarSelectTokens = new ArrayList(); // contains a List of strings + private readonly IList scalarSelectTokens = new List(); // contains a List of strings private readonly IList whereTokens = new List(); private readonly IList havingTokens = new List(); private readonly IDictionary joins = new LinkedHashMap(); @@ -174,7 +173,9 @@ public void AddSelectFragmentString(QuerySelect sql) for (int i = 0; i < count; i++) { - sql.AddSelectFragmentString(((IQueryableCollection) persisters[i]).SelectFragment(names[i], suffixes[i])); + sql.AddSelectFragmentString(new SqlString( + ((IQueryableCollection) persisters[i]).SelectFragment( + (string) names[i], (string) suffixes[i]))); } } @@ -634,14 +635,24 @@ internal void AppendGroupByToken(string token) groupByTokens.Add(new SqlString(token)); } + internal void AppendGroupByParameter() + { + groupByTokens.Add(SqlString.Parameter); + } + internal void AppendScalarSelectToken(string token) { - scalarSelectTokens.Add(token); + scalarSelectTokens.Add(new SqlString(token)); } internal void AppendScalarSelectTokens(string[] tokens) { - scalarSelectTokens.Add(tokens); + scalarSelectTokens.Add(new SqlString(tokens)); + } + + internal void AppendScalarSelectParameter() + { + scalarSelectTokens.Add(SqlString.Parameter); } internal void AddJoin(string name, JoinSequence joinSequence) @@ -749,7 +760,7 @@ private void RenderSql() owners = null; } - string scalarSelect = RenderScalarSelect(); //Must be done here because of side-effect! yuck... + SqlString scalarSelect = RenderScalarSelect(); //Must be done here because of side-effect! yuck... int scalarSize = scalarTypes.Count; hasScalars = scalarTypes.Count != rtsize; @@ -848,7 +859,7 @@ private void RenderIdentifierSelect(QuerySelect sql) { string name = returnedTypes[k]; string suffix = size == 1 ? String.Empty : k.ToString() + StringHelper.Underscore; - sql.AddSelectFragmentString(persisters[k].IdentifierSelectFragment(name, suffix)); + sql.AddSelectFragmentString(new SqlString(persisters[k].IdentifierSelectFragment(name, suffix))); } } @@ -858,19 +869,19 @@ private void RenderPropertiesSelect(QuerySelect sql) for (int k = 0; k < size; k++) { string suffix = (size == 1) ? String.Empty : k.ToString() + StringHelper.Underscore; - string name = returnedTypes[k]; - sql.AddSelectFragmentString(persisters[k].PropertySelectFragment(name, suffix, false)); + string name = (string) returnedTypes[k]; + sql.AddSelectFragmentString(new SqlString(persisters[k].PropertySelectFragment(name, suffix, false))); } } /// /// WARNING: side-effecty /// - private string RenderScalarSelect() + private SqlString RenderScalarSelect() { bool isSubselect = superQuery != null; - StringBuilder buf = new StringBuilder(20); + SqlStringBuilder buf = new SqlStringBuilder(); if (scalarTypes.Count == 0) { @@ -883,14 +894,14 @@ private string RenderScalarSelect() string[] _names = persisters[k].IdentifierColumnNames; for (int i = 0; i < _names.Length; i++) { - buf.Append(returnedTypes[k]).Append(StringHelper.Dot).Append(_names[i]); + buf.Add(returnedTypes[k].ToString()).Add(StringHelper.Dot.ToString()).Add(_names[i]); if (!isSubselect) { - buf.Append(" as ").Append(ScalarName(k, i)); + buf.Add(" as ").Add(ScalarName(k, i)); } if (i != _names.Length - 1 || k != size - 1) { - buf.Append(StringHelper.CommaSpace); + buf.Add(StringHelper.CommaSpace); } } } @@ -903,17 +914,17 @@ private string RenderScalarSelect() int parenCount = 0; // used to count the nesting of parentheses for (int tokenIdx = 0; tokenIdx < scalarSelectTokens.Count; tokenIdx++) { - object next = scalarSelectTokens[tokenIdx]; - if (next is string) + SqlString next = scalarSelectTokens[tokenIdx]; + if (next.Count == 1) { - string token = (string)next; + string token = next.ToString(); string lc = token.ToLowerInvariant(); ISQLFunction func = Factory.SQLFunctionRegistry.FindSQLFunction(lc); if (func != null) { // Render the HQL function - string renderedFunction = RenderFunctionClause(func, scalarSelectTokens, ref tokenIdx); - buf.Append(renderedFunction); + SqlString renderedFunction = RenderFunctionClause(func, scalarSelectTokens, ref tokenIdx); + buf.Add(renderedFunction); } else { @@ -935,47 +946,47 @@ private string RenderScalarSelect() { if (!isSubselect && parenCount == 0) { - buf.Append(" as ").Append(ScalarName(c++, 0)); + buf.Add(" as ").Add(ScalarName(c++, 0)); } } } - buf.Append(token); + buf.Add(token); if (lc.Equals("distinct") || lc.Equals("all")) { - buf.Append(' '); + buf.Add(" "); } } } else { nolast = true; - string[] tokens = (string[])next; - for (int i = 0; i < tokens.Length; i++) + int i = 0; + foreach (object token in next.Parts) { - buf.Append(tokens[i]); + buf.AddObject(token); if (!isSubselect) { - buf.Append(" as ").Append(ScalarName(c, i)); + buf.Add(" as ").Add(ScalarName(c, i)); } - if (i != tokens.Length - 1) + if (i != next.Count - 1) { - buf.Append(StringHelper.CommaSpace); + buf.Add(StringHelper.CommaSpace); } + i++; } c++; } } if (!isSubselect && !nolast) { - buf.Append(" as ").Append(ScalarName(c, 0)); + buf.Add(" as ").Add(ScalarName(c++, 0)); } } - return buf.ToString(); + return buf.ToSqlString(); } - // Parameters inside function are not supported private void RenderFunctions(IList tokens) { for (int tokenIdx = 0; tokenIdx < tokens.Count; tokenIdx++) @@ -985,10 +996,10 @@ private void RenderFunctions(IList tokens) if (func != null) { int flTokenIdx = tokenIdx; - string renderedFunction = RenderFunctionClause(func, (IList)tokens, ref flTokenIdx); - // At this point we have the trunk that represent the function with it's parameters enclosed - // in paren. Now all token in the tokens list can be removed from original list because they must - // be replased with the rendered function. + SqlString renderedFunction = RenderFunctionClause(func, tokens, ref flTokenIdx); + // At this point we have the trunk that represents the function with its + // arguments enclosed in parens. Now all token in the tokens list will be + // removed from the original list and replaced with the rendered function. for (int i = 0; i < flTokenIdx - tokenIdx; i++) { tokens.RemoveAt(tokenIdx + 1); @@ -1005,30 +1016,32 @@ private void RenderFunctions(IList tokens) /// The index of the list that represent the founded function. /// String trepresentation of each token. /// Each token can be string or SqlString - private StringCollection ExtractFunctionClause(IList tokens, ref int tokenIdx) + private IList ExtractFunctionClause(IList tokens, ref int tokenIdx) { - string funcName = tokens[tokenIdx].ToString(); - StringCollection functionTokens = new StringCollection(); + SqlString funcName = tokens[tokenIdx]; + IList functionTokens = new List(); functionTokens.Add(funcName); tokenIdx++; if (tokenIdx >= tokens.Count || !StringHelper.OpenParen.Equals(tokens[tokenIdx].ToString())) { - // All function with parameters have the syntax - // + // All function with arguments have the syntax + // throw new QueryException("'(' expected after function " + funcName); } - functionTokens.Add(StringHelper.OpenParen); + functionTokens.Add(new SqlString(StringHelper.OpenParen)); tokenIdx++; int parenCount = 1; for (; tokenIdx < tokens.Count && parenCount > 0; tokenIdx++) { - if (tokens[tokenIdx].ToString().StartsWith(ParserHelper.HqlVariablePrefix) || tokens[tokenIdx].ToString().Equals(StringHelper.SqlParameter)) + if (tokens[tokenIdx].StartsWithCaseInsensitive(ParserHelper.HqlVariablePrefix) || tokens[tokenIdx].ToString().Equals(StringHelper.SqlParameter)) + { + functionTokens.Add(SqlString.Parameter); + } + else { - throw new QueryException(string.Format("Parameters inside function are not supported (function '{0}').", funcName), - new NotSupportedException()); + functionTokens.Add(tokens[tokenIdx]); } - functionTokens.Add(tokens[tokenIdx].ToString()); if (StringHelper.OpenParen.Equals(tokens[tokenIdx].ToString())) { parenCount++; @@ -1046,17 +1059,17 @@ private StringCollection ExtractFunctionClause(IList tokens, ref int tokenIdx) return functionTokens; } - private string RenderFunctionClause(ISQLFunction func, IList tokens, ref int tokenIdx) + private SqlString RenderFunctionClause(ISQLFunction func, IList tokens, ref int tokenIdx) { - StringCollection functionTokens; + IList functionTokens; if (!func.HasArguments) { - // The function don't work with arguments. + // The function doesn't work with arguments. if (func.HasParenthesesIfNoArguments) ExtractFunctionClause(tokens, ref tokenIdx); - // The function render simply translate is't name for a specific dialect. - return func.Render(CollectionHelper.EmptyList, Factory); + // The function render simply translate its name for a specific dialect. + return func.Render(new ArrayList(), Factory); } functionTokens = ExtractFunctionClause(tokens, ref tokenIdx); @@ -1064,8 +1077,8 @@ private string RenderFunctionClause(ISQLFunction func, IList tokens, ref int tok if (fg == null) fg = new CommonGrammar(); - StringCollection args = new StringCollection(); - StringBuilder argBuf = new StringBuilder(20); + IList args = new ArrayList(); + SqlStringBuilder argBuf = new SqlStringBuilder(); // Extract args spliting first 2 token because are: FuncName( // last token is ')' // To allow expressions like arg (ex:5+5) all tokens between 'argument separator' or @@ -1075,44 +1088,44 @@ private string RenderFunctionClause(ISQLFunction func, IList tokens, ref int tok // Ex: sum(a.Prop+10), cast(yesterday-1 as date) for (int argIdx = 2; argIdx < functionTokens.Count - 1; argIdx++) { - string token = functionTokens[argIdx]; - if(fg.IsKnownArgument(token)) + object token = functionTokens[argIdx]; + if (fg.IsKnownArgument(token.ToString())) { - if (argBuf.Length > 0) + if (argBuf.Count > 0) { // end of the previous argument - args.Add(argBuf.ToString()); - argBuf = new StringBuilder(20); + args.Add(argBuf.ToSqlString()); + argBuf = new SqlStringBuilder(); } args.Add(token); } - else if (fg.IsSeparator(token)) + else if (fg.IsSeparator(token.ToString())) { // argument end - if (argBuf.Length > 0) + if (argBuf.Count > 0) { - args.Add(argBuf.ToString()); - argBuf = new StringBuilder(20); + args.Add(argBuf.ToSqlString()); + argBuf = new SqlStringBuilder(); } } else { - ISQLFunction nfunc = Factory.SQLFunctionRegistry.FindSQLFunction(token.ToLowerInvariant()); + ISQLFunction nfunc = Factory.SQLFunctionRegistry.FindSQLFunction(token.ToString().ToLowerInvariant()); if (nfunc != null) { // the token is a nested function call - argBuf.Append(RenderFunctionClause(nfunc, functionTokens, ref argIdx)); + argBuf.Add(RenderFunctionClause(nfunc, functionTokens, ref argIdx)); } else { // the token is a part of an argument (every thing else) - argBuf.Append(token); + argBuf.AddObject(token); } } } // Add the last arg - if (argBuf.Length > 0) - args.Add(argBuf.ToString()); + if (argBuf.Count > 0) + args.Add(argBuf.ToSqlString()); return func.Render(args, Factory); } diff --git a/src/NHibernate/Hql/Classic/SelectParser.cs b/src/NHibernate/Hql/Classic/SelectParser.cs index 769b4844d76..fafc57dc615 100644 --- a/src/NHibernate/Hql/Classic/SelectParser.cs +++ b/src/NHibernate/Hql/Classic/SelectParser.cs @@ -118,7 +118,7 @@ public void Token(string token, QueryTranslator q) readyForAliasOrExpression = false; // if all functions were parsed add the type of the first function in stack - if(!funcStack.HasFunctions) + if (!funcStack.HasFunctions) q.AddSelectScalar(scalarType); } } @@ -166,7 +166,12 @@ public void Token(string token, QueryTranslator q) constantToken = true; } - if (constantToken) + if (token.StartsWith(ParserHelper.HqlVariablePrefix)) + { + q.AddNamedParameter(token.Substring(1)); + q.AppendScalarSelectParameter(); + } + else if (constantToken) { q.AppendScalarSelectToken(token); } @@ -207,7 +212,7 @@ public void Token(string token, QueryTranslator q) } q.AppendScalarSelectTokens(pathExpressionParser.WhereColumns); q.AddSelectScalar(pathExpressionParser.WhereColumnType); - pathExpressionParser.AddAssociation(q); + pathExpressionParser.AddAssociation(q); } catch (QueryException) { @@ -228,11 +233,10 @@ public void Token(string token, QueryTranslator q) q.AppendScalarSelectToken(token); q.AddSelectScalar(GetFloatingPointConstantType()); } - else if (IsParameter(token)) + else if (token.StartsWith(ParserHelper.HqlVariablePrefix)) { - //q.AddNamedParameter(token.Substring(1)); - //q.AppendScalarSelectToken(token); - throw new QueryException("parameters are not supported in SELECT.", new NotSupportedException()); + q.AddNamedParameter(token.Substring(1)); + q.AppendScalarSelectParameter(); } else throw; @@ -244,9 +248,6 @@ public void Token(string token, QueryTranslator q) #region RegExs private static readonly Regex pathExpressionRegEx = new Regex(@"\A[A-Za-z_][A-Za-z_0-9]*[.][A-Za-z_][A-Za-z_0-9]*\z", RegexOptions.Singleline | RegexOptions.Compiled); private static readonly Regex stringCostantRegEx = new Regex(@"\A'('{2})*([^'\r\n]*)('{2})*([^'\r\n]*)('{2})*'\z", RegexOptions.Singleline | RegexOptions.Compiled); - - private static readonly string paramMatcher = string.Format("\\A([{0}][A-Za-z_][A-Za-z_0-9]*)|[{1}]\\z", ParserHelper.HqlVariablePrefix, StringHelper.SqlParameter); - private static readonly Regex parameterRegEx = new Regex(paramMatcher, RegexOptions.Singleline | RegexOptions.Compiled); #endregion private static bool IsPathExpression(string token) @@ -271,11 +272,6 @@ private static bool IsFloatingPointConstant(string token) return double.TryParse(token, NumberStyles.Number, CultureInfo.InvariantCulture, out d); } - private static bool IsParameter(string token) - { - return parameterRegEx.IsMatch(token); - } - private static IType GetIntegerConstantType(string token) { int i; diff --git a/src/NHibernate/SqlCommand/ISqlStringVisitor.cs b/src/NHibernate/SqlCommand/ISqlStringVisitor.cs index 9ca63c32876..0b86c3f177f 100644 --- a/src/NHibernate/SqlCommand/ISqlStringVisitor.cs +++ b/src/NHibernate/SqlCommand/ISqlStringVisitor.cs @@ -5,6 +5,7 @@ namespace NHibernate.SqlCommand public interface ISqlStringVisitor { void String(string text); + void String(SqlString sqlString); void Parameter(); } -} \ No newline at end of file +} diff --git a/src/NHibernate/SqlCommand/QuerySelect.cs b/src/NHibernate/SqlCommand/QuerySelect.cs index 61e7324f094..0598608ec0a 100644 --- a/src/NHibernate/SqlCommand/QuerySelect.cs +++ b/src/NHibernate/SqlCommand/QuerySelect.cs @@ -12,7 +12,7 @@ public class QuerySelect { private readonly JoinFragment joins; - private readonly StringBuilder selectBuilder = new StringBuilder(); + private readonly SqlStringBuilder selectBuilder = new SqlStringBuilder(); private readonly SqlStringBuilder whereBuilder = new SqlStringBuilder(); // groupBy, orderBy, and having will for sure have no parameters. @@ -111,9 +111,9 @@ public JoinFragment JoinFragment /// /// /// - public void AddSelectFragmentString(string fragment) + public void AddSelectFragmentString(SqlString fragment) { - if (fragment.StartsWith(",")) + if (fragment.StartsWithCaseInsensitive(",")) { fragment = fragment.Substring(1); } @@ -122,12 +122,12 @@ public void AddSelectFragmentString(string fragment) if (fragment.Length > 0) { - if (selectBuilder.Length > 0) + if (selectBuilder.Count > 0) { - selectBuilder.Append(StringHelper.CommaSpace); + selectBuilder.Add(StringHelper.CommaSpace); } - selectBuilder.Append(fragment); + selectBuilder.Add(fragment); } } @@ -138,7 +138,7 @@ public void AddSelectFragmentString(string fragment) /// public void AddSelectColumn(string columnName, string alias) { - AddSelectFragmentString(columnName + ' ' + alias); + AddSelectFragmentString(new SqlString(columnName + ' ' + alias)); } /// @@ -224,12 +224,12 @@ public SqlString ToQuerySqlString() from = from.Substring(11); } - builder.Add(selectBuilder.ToString()) + builder.Add(selectBuilder.ToSqlString()) .Add(" from") .Add(from); SqlString part1 = joins.ToWhereFragmentString.Trim(); - SqlString part2 = whereBuilder.ToSqlString().Trim(); + SqlString part2 = whereBuilder.ToSqlString(); bool hasPart1 = part1.Count > 0; bool hasPart2 = part2.Count > 0; @@ -330,4 +330,4 @@ private void AppendTokens(SqlStringBuilder builder, ICollection iter) } } } -} \ No newline at end of file +} diff --git a/src/NHibernate/SqlCommand/SqlString.cs b/src/NHibernate/SqlCommand/SqlString.cs index fc354384aae..e57ec697905 100644 --- a/src/NHibernate/SqlCommand/SqlString.cs +++ b/src/NHibernate/SqlCommand/SqlString.cs @@ -42,7 +42,7 @@ public SqlString(params object[] sqlParts) #if DEBUG foreach (object obj in sqlParts) { - Debug.Assert(obj is string || obj is Parameter); + Debug.Assert(obj is string || obj is SqlString || obj is Parameter); } #endif this.sqlParts = sqlParts; @@ -108,9 +108,13 @@ private SqlString Compact() foreach (object part in sqlParts) { + SqlString sqlStringPart = part as SqlString; string stringPart = part as string; - - if (stringPart != null) + if (sqlStringPart != null) + { + sqlBuilder.Add(sqlStringPart.Compact()); + } + else if (stringPart != null) { builder.Append(stringPart); } @@ -123,7 +127,7 @@ private SqlString Compact() } builder.Length = 0; - sqlBuilder.Add((Parameter) part); + sqlBuilder.Add((Parameter)part); } } @@ -648,10 +652,15 @@ public void Visit(ISqlStringVisitor visitor) foreach (object part in sqlParts) { string partString = part as string; + SqlString partSqlString = part as SqlString; if (partString != null) { visitor.String(partString); } + else if (partSqlString != null && !SqlString.Parameter.Equals(partSqlString)) + { + visitor.String(partSqlString); + } else { visitor.Parameter(); @@ -726,4 +735,4 @@ public SqlString GetSubselectString() return new SubselectClauseExtractor(Compact().sqlParts).GetSqlString(); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/SqlCommand/SqlStringBuilder.cs b/src/NHibernate/SqlCommand/SqlStringBuilder.cs index c2985a81f5f..cfba896811a 100644 --- a/src/NHibernate/SqlCommand/SqlStringBuilder.cs +++ b/src/NHibernate/SqlCommand/SqlStringBuilder.cs @@ -333,6 +333,11 @@ public void String(string text) parent.Add(text); } + public void String(SqlString sqlString) + { + parent.Add(sqlString); + } + public void Parameter() { parent.AddParameter(); @@ -344,4 +349,4 @@ public void Clear() sqlParts.Clear(); } } -} \ No newline at end of file +}