From 722fccf763b6f0e5b3b613cd3eb931d53193e5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erdo=C4=9Fan=20K=C3=BCrt=C3=BCr?= Date: Tue, 25 Sep 2018 01:02:45 +0300 Subject: [PATCH 1/2] fixes #50 --- QueryBuilder.Tests/QueryBuilderTest.cs | 98 ++++++++++++++++++++++++++ QueryBuilder/Compilers/Compiler.cs | 58 ++++++++------- QueryBuilder/Compilers/CteFinder.cs | 56 +++++++++++++++ 3 files changed, 186 insertions(+), 26 deletions(-) create mode 100644 QueryBuilder/Compilers/CteFinder.cs diff --git a/QueryBuilder.Tests/QueryBuilderTest.cs b/QueryBuilder.Tests/QueryBuilderTest.cs index 08a5e9d1..79d053b3 100644 --- a/QueryBuilder.Tests/QueryBuilderTest.cs +++ b/QueryBuilder.Tests/QueryBuilderTest.cs @@ -163,6 +163,104 @@ public void ColumnsEscaping() Assert.Equal("SELECT [mycol[isthis]]] FROM [users]", c[0]); } + // test for issue #50 + [Fact] + public void CascadedCteAndBindings() + { + var cte1 = new Query("Table1"); + cte1.Select("Column1", "Column2"); + cte1.Where("Column2", 1); + + var cte2 = new Query("Table2"); + cte2.With("cte1", cte1); + cte2.Select("Column3", "Column4"); + cte2.Join("cte1", join => join.On("Column1", "Column3")); + cte2.Where("Column4", 2); + + var mainQuery = new Query("Table3"); + mainQuery.With("cte2", cte2); + mainQuery.Select("*"); + mainQuery.From("cte2"); + mainQuery.Where("Column3", 5); + + var sql = Compile(mainQuery); + + Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1)\nWITH [cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nSELECT * FROM [cte2] WHERE [Column3] = 5", sql[0]); + Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1)\nWITH `cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nSELECT * FROM `cte2` WHERE `Column3` = 5", sql[1]); + Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1)\nWITH \"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nSELECT * FROM \"cte2\" WHERE \"Column3\" = 5", sql[2]); + Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1)\nWITH \"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nSELECT * FROM \"CTE2\" WHERE \"COLUMN3\" = 5", sql[3]); + } + + // test for issue #50 + [Fact] + public void CascadedAndMultiReferencedCteAndBindings() + { + var cte1 = new Query("Table1"); + cte1.Select("Column1", "Column2"); + cte1.Where("Column2", 1); + + var cte2 = new Query("Table2"); + cte2.With("cte1", cte1); + cte2.Select("Column3", "Column4"); + cte2.Join("cte1", join => join.On("Column1", "Column3")); + cte2.Where("Column4", 2); + + var cte3 = new Query("Table3"); + cte3.With("cte1", cte1); + cte3.Select("Column3_3", "Column3_4"); + cte3.Join("cte1", join => join.On("Column1", "Column3_3")); + cte3.Where("Column3_4", 33); + + var mainQuery = new Query("Table3"); + mainQuery.With("cte2", cte2); + mainQuery.With("cte3", cte3); + mainQuery.Select("*"); + mainQuery.From("cte2"); + mainQuery.Where("Column3", 5); + + var sql = Compile(mainQuery); + + Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1)\nWITH [cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nWITH [cte3] AS (SELECT [Column3_3], [Column3_4] FROM [Table3] \nINNER JOIN [cte1] ON ([Column1] = [Column3_3]) WHERE [Column3_4] = 33)\nSELECT * FROM [cte2] WHERE [Column3] = 5", sql[0]); + Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1)\nWITH `cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nWITH `cte3` AS (SELECT `Column3_3`, `Column3_4` FROM `Table3` \nINNER JOIN `cte1` ON (`Column1` = `Column3_3`) WHERE `Column3_4` = 33)\nSELECT * FROM `cte2` WHERE `Column3` = 5", sql[1]); + Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1)\nWITH \"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nWITH \"cte3\" AS (SELECT \"Column3_3\", \"Column3_4\" FROM \"Table3\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3_3\") WHERE \"Column3_4\" = 33)\nSELECT * FROM \"cte2\" WHERE \"Column3\" = 5", sql[2]); + Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1)\nWITH \"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nWITH \"CTE3\" AS (SELECT \"COLUMN3_3\", \"COLUMN3_4\" FROM \"TABLE3\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3_3\") WHERE \"COLUMN3_4\" = 33)\nSELECT * FROM \"CTE2\" WHERE \"COLUMN3\" = 5", sql[3]); + } + + // test for issue #50 + [Fact] + public void MultipleCtesAndBindings() + { + var cte1 = new Query("Table1"); + cte1.Select("Column1", "Column2"); + cte1.Where("Column2", 1); + + var cte2 = new Query("Table2"); + cte2.Select("Column3", "Column4"); + cte2.Join("cte1", join => join.On("Column1", "Column3")); + cte2.Where("Column4", 2); + + var cte3 = new Query("Table3"); + cte3.Select("Column3_3", "Column3_4"); + cte3.Join("cte1", join => join.On("Column1", "Column3_3")); + cte3.Where("Column3_4", 33); + + var mainQuery = new Query("Table3"); + mainQuery.With("cte1", cte1); + mainQuery.With("cte2", cte2); + mainQuery.With("cte3", cte3); + mainQuery.Select("*"); + mainQuery.From("cte3"); + mainQuery.Where("Column3_4", 5); + + var sql = Compile(mainQuery); + + Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1)\nWITH [cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nWITH [cte3] AS (SELECT [Column3_3], [Column3_4] FROM [Table3] \nINNER JOIN [cte1] ON ([Column1] = [Column3_3]) WHERE [Column3_4] = 33)\nSELECT * FROM [cte3] WHERE [Column3_4] = 5", sql[0]); + Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1)\nWITH `cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nWITH `cte3` AS (SELECT `Column3_3`, `Column3_4` FROM `Table3` \nINNER JOIN `cte1` ON (`Column1` = `Column3_3`) WHERE `Column3_4` = 33)\nSELECT * FROM `cte3` WHERE `Column3_4` = 5", sql[1]); + Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1)\nWITH \"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nWITH \"cte3\" AS (SELECT \"Column3_3\", \"Column3_4\" FROM \"Table3\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3_3\") WHERE \"Column3_4\" = 33)\nSELECT * FROM \"cte3\" WHERE \"Column3_4\" = 5", sql[2]); + Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1)\nWITH \"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nWITH \"CTE3\" AS (SELECT \"COLUMN3_3\", \"COLUMN3_4\" FROM \"TABLE3\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3_3\") WHERE \"COLUMN3_4\" = 33)\nSELECT * FROM \"CTE3\" WHERE \"COLUMN3_4\" = 5", sql[3]); + } + + [Fact] public void CteAndBindings() { diff --git a/QueryBuilder/Compilers/Compiler.cs b/QueryBuilder/Compilers/Compiler.cs index 458d510b..47c330a2 100644 --- a/QueryBuilder/Compilers/Compiler.cs +++ b/QueryBuilder/Compilers/Compiler.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; namespace SqlKata.Compilers { @@ -54,11 +55,26 @@ public virtual SqlResult Compile(Query query) // handle CTEs if (query.HasComponent("cte", EngineCode)) { - var cteCtx = CompileCte(query.GetComponents("cte", EngineCode)); - ctx.Bindings.InsertRange(0, cteCtx.Bindings); - ctx.RawSql = cteCtx.RawSql.Trim() + "\n" + ctx.RawSql; - } + var cteFinder = new CteFinder(query, EngineCode); + var cteSearchResult = cteFinder.Find(); + + var rawSql = new StringBuilder(); + var cteBindings = new List(); + + foreach (var cte in cteSearchResult) + { + var cteCtx = CompileCte(cte); + + cteBindings.AddRange(cteCtx.Bindings); + rawSql.Append(cteCtx.RawSql.Trim()); + rawSql.Append('\n'); + } + + rawSql.Append(ctx.RawSql); + ctx.Bindings.InsertRange(0, cteBindings); + ctx.RawSql = rawSql.ToString(); + } return ctx; } @@ -277,35 +293,28 @@ public virtual string OnAfterCompile(string sql) return sql; } - public virtual SqlResult CompileCte(List cteClauses) + public virtual SqlResult CompileCte(AbstractFrom cte) { + var ctx = new SqlResult(); - var ctx = new SqlResult { }; - - if (!cteClauses.Any()) + if (null == cte) { return ctx; } - var sql = new List(); - - foreach (var cte in cteClauses) + if (cte is RawFromClause raw) { - if (cte is RawFromClause raw) - { - ctx.Bindings.AddRange(raw.Bindings); - sql.Add($"{WrapValue(raw.Alias)} AS ({WrapIdentifiers(raw.Expression)})"); - } - else if (cte is QueryFromClause queryFromClause) - { - var subCtx = CompileSelectQuery(queryFromClause.Query); - ctx.Bindings.AddRange(subCtx.Bindings); + ctx.Bindings.AddRange(raw.Bindings); + ctx.RawSql = $"WITH {WrapValue(raw.Alias)} AS ({WrapIdentifiers(raw.Expression)}) "; + } + else if (cte is QueryFromClause queryFromClause) + { + var subCtx = CompileSelectQuery(queryFromClause.Query); + ctx.Bindings.AddRange(subCtx.Bindings); - sql.Add($"{WrapValue(queryFromClause.Alias)} AS ({subCtx.RawSql})"); - } + ctx.RawSql = $"WITH {WrapValue(queryFromClause.Alias)} AS ({subCtx.RawSql}) "; } - ctx.RawSql = "WITH " + string.Join(",\n", sql) + " "; return ctx; } @@ -697,7 +706,4 @@ public virtual string WrapIdentifiers(string input) } } - - - } diff --git a/QueryBuilder/Compilers/CteFinder.cs b/QueryBuilder/Compilers/CteFinder.cs new file mode 100644 index 00000000..17b8724d --- /dev/null +++ b/QueryBuilder/Compilers/CteFinder.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; + +namespace SqlKata.Compilers +{ + public class CteFinder + { + private readonly Query query; + private readonly string engineCode; + private HashSet namesOfPreviousCtes; + private List orderedCteList; + + public CteFinder(Query query, string engineCode) + { + this.query = query; + this.engineCode = engineCode; + } + + public List Find() + { + if (null != orderedCteList) + return orderedCteList; + + namesOfPreviousCtes = new HashSet(); + + orderedCteList = FindInternal(query); + + namesOfPreviousCtes.Clear(); + namesOfPreviousCtes = null; + + return orderedCteList; + } + + private List FindInternal(Query queryToSearch) + { + var cteList = queryToSearch.GetComponents("cte", engineCode); + + var resultList = new List(); + + foreach (var cte in cteList) + { + if (namesOfPreviousCtes.Contains(cte.Alias)) + continue; + + namesOfPreviousCtes.Add(cte.Alias); + resultList.Add(cte); + + if (cte is QueryFromClause queryFromClause) + { + resultList.InsertRange(0, FindInternal(queryFromClause.Query)); + } + } + + return resultList; + } + } +} From 28029540f21d4d5cb68e98b7ad34909a8e24f84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erdo=C4=9Fan=20K=C3=BCrt=C3=BCr?= Date: Tue, 25 Sep 2018 01:26:06 +0300 Subject: [PATCH 2/2] Fixes syntax error introduced in previous commit --- QueryBuilder.Tests/QueryBuilderTest.cs | 24 ++++++++++++------------ QueryBuilder/Compilers/Compiler.cs | 10 ++++++---- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/QueryBuilder.Tests/QueryBuilderTest.cs b/QueryBuilder.Tests/QueryBuilderTest.cs index 79d053b3..5ab35edb 100644 --- a/QueryBuilder.Tests/QueryBuilderTest.cs +++ b/QueryBuilder.Tests/QueryBuilderTest.cs @@ -185,10 +185,10 @@ public void CascadedCteAndBindings() var sql = Compile(mainQuery); - Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1)\nWITH [cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nSELECT * FROM [cte2] WHERE [Column3] = 5", sql[0]); - Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1)\nWITH `cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nSELECT * FROM `cte2` WHERE `Column3` = 5", sql[1]); - Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1)\nWITH \"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nSELECT * FROM \"cte2\" WHERE \"Column3\" = 5", sql[2]); - Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1)\nWITH \"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nSELECT * FROM \"CTE2\" WHERE \"COLUMN3\" = 5", sql[3]); + Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1),\n[cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nSELECT * FROM [cte2] WHERE [Column3] = 5", sql[0]); + Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1),\n`cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nSELECT * FROM `cte2` WHERE `Column3` = 5", sql[1]); + Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1),\n\"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nSELECT * FROM \"cte2\" WHERE \"Column3\" = 5", sql[2]); + Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1),\n\"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nSELECT * FROM \"CTE2\" WHERE \"COLUMN3\" = 5", sql[3]); } // test for issue #50 @@ -220,10 +220,10 @@ public void CascadedAndMultiReferencedCteAndBindings() var sql = Compile(mainQuery); - Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1)\nWITH [cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nWITH [cte3] AS (SELECT [Column3_3], [Column3_4] FROM [Table3] \nINNER JOIN [cte1] ON ([Column1] = [Column3_3]) WHERE [Column3_4] = 33)\nSELECT * FROM [cte2] WHERE [Column3] = 5", sql[0]); - Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1)\nWITH `cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nWITH `cte3` AS (SELECT `Column3_3`, `Column3_4` FROM `Table3` \nINNER JOIN `cte1` ON (`Column1` = `Column3_3`) WHERE `Column3_4` = 33)\nSELECT * FROM `cte2` WHERE `Column3` = 5", sql[1]); - Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1)\nWITH \"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nWITH \"cte3\" AS (SELECT \"Column3_3\", \"Column3_4\" FROM \"Table3\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3_3\") WHERE \"Column3_4\" = 33)\nSELECT * FROM \"cte2\" WHERE \"Column3\" = 5", sql[2]); - Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1)\nWITH \"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nWITH \"CTE3\" AS (SELECT \"COLUMN3_3\", \"COLUMN3_4\" FROM \"TABLE3\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3_3\") WHERE \"COLUMN3_4\" = 33)\nSELECT * FROM \"CTE2\" WHERE \"COLUMN3\" = 5", sql[3]); + Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1),\n[cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2),\n[cte3] AS (SELECT [Column3_3], [Column3_4] FROM [Table3] \nINNER JOIN [cte1] ON ([Column1] = [Column3_3]) WHERE [Column3_4] = 33)\nSELECT * FROM [cte2] WHERE [Column3] = 5", sql[0]); + Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1),\n`cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2),\n`cte3` AS (SELECT `Column3_3`, `Column3_4` FROM `Table3` \nINNER JOIN `cte1` ON (`Column1` = `Column3_3`) WHERE `Column3_4` = 33)\nSELECT * FROM `cte2` WHERE `Column3` = 5", sql[1]); + Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1),\n\"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2),\n\"cte3\" AS (SELECT \"Column3_3\", \"Column3_4\" FROM \"Table3\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3_3\") WHERE \"Column3_4\" = 33)\nSELECT * FROM \"cte2\" WHERE \"Column3\" = 5", sql[2]); + Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1),\n\"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2),\n\"CTE3\" AS (SELECT \"COLUMN3_3\", \"COLUMN3_4\" FROM \"TABLE3\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3_3\") WHERE \"COLUMN3_4\" = 33)\nSELECT * FROM \"CTE2\" WHERE \"COLUMN3\" = 5", sql[3]); } // test for issue #50 @@ -254,10 +254,10 @@ public void MultipleCtesAndBindings() var sql = Compile(mainQuery); - Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1)\nWITH [cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2)\nWITH [cte3] AS (SELECT [Column3_3], [Column3_4] FROM [Table3] \nINNER JOIN [cte1] ON ([Column1] = [Column3_3]) WHERE [Column3_4] = 33)\nSELECT * FROM [cte3] WHERE [Column3_4] = 5", sql[0]); - Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1)\nWITH `cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2)\nWITH `cte3` AS (SELECT `Column3_3`, `Column3_4` FROM `Table3` \nINNER JOIN `cte1` ON (`Column1` = `Column3_3`) WHERE `Column3_4` = 33)\nSELECT * FROM `cte3` WHERE `Column3_4` = 5", sql[1]); - Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1)\nWITH \"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2)\nWITH \"cte3\" AS (SELECT \"Column3_3\", \"Column3_4\" FROM \"Table3\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3_3\") WHERE \"Column3_4\" = 33)\nSELECT * FROM \"cte3\" WHERE \"Column3_4\" = 5", sql[2]); - Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1)\nWITH \"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2)\nWITH \"CTE3\" AS (SELECT \"COLUMN3_3\", \"COLUMN3_4\" FROM \"TABLE3\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3_3\") WHERE \"COLUMN3_4\" = 33)\nSELECT * FROM \"CTE3\" WHERE \"COLUMN3_4\" = 5", sql[3]); + Assert.Equal("WITH [cte1] AS (SELECT [Column1], [Column2] FROM [Table1] WHERE [Column2] = 1),\n[cte2] AS (SELECT [Column3], [Column4] FROM [Table2] \nINNER JOIN [cte1] ON ([Column1] = [Column3]) WHERE [Column4] = 2),\n[cte3] AS (SELECT [Column3_3], [Column3_4] FROM [Table3] \nINNER JOIN [cte1] ON ([Column1] = [Column3_3]) WHERE [Column3_4] = 33)\nSELECT * FROM [cte3] WHERE [Column3_4] = 5", sql[0]); + Assert.Equal("WITH `cte1` AS (SELECT `Column1`, `Column2` FROM `Table1` WHERE `Column2` = 1),\n`cte2` AS (SELECT `Column3`, `Column4` FROM `Table2` \nINNER JOIN `cte1` ON (`Column1` = `Column3`) WHERE `Column4` = 2),\n`cte3` AS (SELECT `Column3_3`, `Column3_4` FROM `Table3` \nINNER JOIN `cte1` ON (`Column1` = `Column3_3`) WHERE `Column3_4` = 33)\nSELECT * FROM `cte3` WHERE `Column3_4` = 5", sql[1]); + Assert.Equal("WITH \"cte1\" AS (SELECT \"Column1\", \"Column2\" FROM \"Table1\" WHERE \"Column2\" = 1),\n\"cte2\" AS (SELECT \"Column3\", \"Column4\" FROM \"Table2\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3\") WHERE \"Column4\" = 2),\n\"cte3\" AS (SELECT \"Column3_3\", \"Column3_4\" FROM \"Table3\" \nINNER JOIN \"cte1\" ON (\"Column1\" = \"Column3_3\") WHERE \"Column3_4\" = 33)\nSELECT * FROM \"cte3\" WHERE \"Column3_4\" = 5", sql[2]); + Assert.Equal("WITH \"CTE1\" AS (SELECT \"COLUMN1\", \"COLUMN2\" FROM \"TABLE1\" WHERE \"COLUMN2\" = 1),\n\"CTE2\" AS (SELECT \"COLUMN3\", \"COLUMN4\" FROM \"TABLE2\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3\") WHERE \"COLUMN4\" = 2),\n\"CTE3\" AS (SELECT \"COLUMN3_3\", \"COLUMN3_4\" FROM \"TABLE3\" \nINNER JOIN \"CTE1\" ON (\"COLUMN1\" = \"COLUMN3_3\") WHERE \"COLUMN3_4\" = 33)\nSELECT * FROM \"CTE3\" WHERE \"COLUMN3_4\" = 5", sql[3]); } diff --git a/QueryBuilder/Compilers/Compiler.cs b/QueryBuilder/Compilers/Compiler.cs index 47c330a2..d31d13dc 100644 --- a/QueryBuilder/Compilers/Compiler.cs +++ b/QueryBuilder/Compilers/Compiler.cs @@ -58,7 +58,7 @@ public virtual SqlResult Compile(Query query) var cteFinder = new CteFinder(query, EngineCode); var cteSearchResult = cteFinder.Find(); - var rawSql = new StringBuilder(); + var rawSql = new StringBuilder("WITH "); var cteBindings = new List(); foreach (var cte in cteSearchResult) @@ -67,9 +67,11 @@ public virtual SqlResult Compile(Query query) cteBindings.AddRange(cteCtx.Bindings); rawSql.Append(cteCtx.RawSql.Trim()); - rawSql.Append('\n'); + rawSql.Append(",\n"); } + rawSql.Length -= 2; // remove last comma + rawSql.Append('\n'); rawSql.Append(ctx.RawSql); ctx.Bindings.InsertRange(0, cteBindings); @@ -305,14 +307,14 @@ public virtual SqlResult CompileCte(AbstractFrom cte) if (cte is RawFromClause raw) { ctx.Bindings.AddRange(raw.Bindings); - ctx.RawSql = $"WITH {WrapValue(raw.Alias)} AS ({WrapIdentifiers(raw.Expression)}) "; + ctx.RawSql = $"{WrapValue(raw.Alias)} AS ({WrapIdentifiers(raw.Expression)})"; } else if (cte is QueryFromClause queryFromClause) { var subCtx = CompileSelectQuery(queryFromClause.Query); ctx.Bindings.AddRange(subCtx.Bindings); - ctx.RawSql = $"WITH {WrapValue(queryFromClause.Alias)} AS ({subCtx.RawSql}) "; + ctx.RawSql = $"{WrapValue(queryFromClause.Alias)} AS ({subCtx.RawSql})"; } return ctx;